> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Debugging

> Debugging the Agent component

## Debugging in the Playground

Generally the [Playground](/agents/playground) gives a lot of information about
what's happening, but when that is insufficient, you have other options.

## Logging the raw request and response from LLM calls

You can provide a `rawRequestResponseHandler` to the agent to log the raw
request and response from the LLM.

You could use this to log the request and response to a table, or use console
logs with
[Log Streaming](/production/integrations/log-streams/log-streams) to
allow debugging and searching through Axiom or another logging service.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
const supportAgent = new Agent(components.agent, {
  ...
  rawRequestResponseHandler: async (ctx, { request, response }) => {
    console.log("request", request);
    console.log("response", response);
  },
});
```

## Logging the context messages via the contextHandler

You can log the context messages via the contextHandler, if you're curious what
exactly the LLM is receiving.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
const supportAgent = new Agent(components.agent, {
  ...
  contextHandler: async (ctx, { allMessages }) => {
    console.log("context", allMessages);
    return allMessages;
  },
});
```

## Tracing LLM calls with OpenTelemetry

When you pass
[`experimental_telemetry`](https://ai-sdk.dev/docs/ai-sdk-core/telemetry) to a
generate or stream call, the AI SDK emits OpenTelemetry spans with the model,
token usage, prompt, and response.

To export them to an OTLP backend, register a tracer provider:

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { trace } from "@opentelemetry/api";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import {
  BasicTracerProvider,
  SimpleSpanProcessor,
} from "@opentelemetry/sdk-trace-base";

const tracerProvider = new BasicTracerProvider({
  resource: resourceFromAttributes({ "service.name": "bijection-agent" }),
  spanProcessors: [
    new SimpleSpanProcessor(
      new OTLPTraceExporter({
        url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
        headers: {
          Authorization: `Bearer ${process.env.OTEL_EXPORTER_OTLP_TRACES_TOKEN}`,
        },
      }),
    ),
  ],
});
trace.setGlobalTracerProvider(tracerProvider);
```

Then enable telemetry on the call, and flush the spans before the handler
returns so their exports are
[awaited](/understanding/best-practices/best-practices#await-all-promises):

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export const generateTextWithTelemetry = action({
  args: { prompt: v.string(), threadId: v.string() },
  handler: async (ctx, { prompt, threadId }) => {
    await authorizeThreadAccess(ctx, threadId);
    const result = await agent.generateText(
      ctx,
      { threadId },
      {
        prompt,
        experimental_telemetry: {
          isEnabled: true,
          functionId: "debugging/telemetry",
        },
      },
    );
    await tracerProvider.forceFlush();
    return result.text;
  },
});
```

See the full example in
debugging/telemetry.ts.

## Inspecting the database in the console

You can go to the Data tab in the console and select the agent component above
the table list to see the Agent data. The organization of the tables matches the
schema.
The most useful tables are:

* `threads` has one row per thread
* `messages` has a separate row for each ModelMessage - e.g. a user message,
  assistant tool call, tool result, assistant message, etc. The most important
  fields are `agentName` for which agent it's associated with, `status`, `order`
  and `stepOrder` which are used to order the messages, and `message` which is
  roughly what is passed to the LLM.
* `streamingMessages` has an entry for each streamed message, until it's cleaned
  up. You can take the ID to look at the associated `streamDeltas` table.
* `files` captures the files tracked by the Agent from content that was sent in
  a message that got stored in File Storage.

## Troubleshooting

### Type errors on `components.agent`

If you get type errors about `components.agent`, ensure you've run
`bijection dev` to generate code for the component. The types expected by the
library are in the npm library, and the types for `components.agent` currently
come from generated code in your project (via `bijection dev`).

### Circular dependencies

Having the return value of workflows depend on other Bijection functions can lead
to circular dependencies due to the `internal.foo.bar` way of specifying
functions. The way to fix this is to explicitly type the return value of the
workflow. When in doubt, add return types to more `handler` functions, like
this:

```ts {3,11} theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export const supportAgentWorkflow = workflow.define({
  args: { prompt: v.string(), userId: v.string(), threadId: v.string() },
  handler: async (step, { prompt, userId, threadId }): Promise<string> => {
    // ...
  },
});

// And regular functions too:
export const myFunction = action({
  args: { prompt: v.string() },
  handler: async (ctx, { prompt }): Promise<string> => {
    // ...
  },
});
```
