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

# Pi Agent Traces

> Trace current Pi Agent model turns, streams, tool calls, usage, costs, and agent identity through Catalyst.

<Info>
  Using legacy `@mariozechner/pi-ai`? Use the
  [PI AI integration](/integrations/traces/pi-ai).
</Info>

Catalyst instruments current Pi Agent applications built with
`@earendil-works/pi-agent-core` and `@earendil-works/pi-ai`. Each Pi `Models`
collection owns its providers. Pass that collection to Catalyst so each model
turn emits an OpenInference LLM span.

This guide is tested with `@earendil-works/pi-agent-core@0.84.1` and
`@earendil-works/pi-ai@0.84.1`.

Pi Agent is available for TypeScript. There is no Python equivalent for this
integration.

## What Is Captured

* One LLM span per model turn, named like `pi-agent.<provider>.turn`
* Calls through `stream`, `streamSimple`, `complete`, and `completeSimple`
* System prompts, input messages, assistant output, model name, and provider
* Tool call IDs, names, and JSON arguments from assistant messages
* Token usage, prompt cache read/write counts, finish reason, and total cost
* Errors, aborts, and exception details
* Active `agentSpan()` identity, including `agent.id`, `agent.name`,
  `agent.role`, and `session.id`

## Install

<Metadata text="integrations/traces/pi-agent-install" />

```bash TypeScript theme={"system"}
bun add @inference/tracing@0.1.9 @earendil-works/pi-agent-core@0.84.1 @earendil-works/pi-ai@0.84.1
```

## Configure Export

Set the Catalyst endpoint and token before your app starts. Generate a token at
[API Keys](https://inference.net/dashboard/api-keys).

<Metadata text="integrations/traces/pi-agent-env" />

```bash theme={"system"}
export CATALYST_OTLP_ENDPOINT="https://telemetry.inference.net"
export CATALYST_OTLP_TOKEN="<your-token>"
export CATALYST_SERVICE_NAME="pi-agent"

export ANTHROPIC_API_KEY="<your-anthropic-api-key>"
```

## Initialize Tracing

Create the Pi `Models` collection before tracing setup. Pass the collection as
`piAgent`. You can add providers before or after setup.

<Metadata text="integrations/traces/pi-agent-init[series=pi_agent_setup]" />

```typescript TypeScript theme={"system"}
import { createModels } from "@earendil-works/pi-ai";
import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic";
import { setup } from "@inference/tracing";

const models = createModels();
const tracing = await setup({
  serviceName: process.env.CATALYST_SERVICE_NAME ?? "pi-agent",
  autoInstrument: false,
  modules: { piAgent: models },
});

models.setProvider(anthropicProvider());
```

<Warning>
  Pi uses a separate `Models` collection for each application. Catalyst cannot
  find that collection through package auto-detection. Pass it as
  `modules.piAgent` or call `instrumentPiAgent(models, tracing)`.
</Warning>

For manual initialization, use the Pi Agent subpath helper before the agent
makes its first model call.

<Metadata text="integrations/traces/pi-agent-manual-init[series=pi_agent_manual]" />

```typescript TypeScript theme={"system"}
import { createModels } from "@earendil-works/pi-ai";
import { setup } from "@inference/tracing";
import { instrumentPiAgent } from "@inference/tracing/pi-agent";

const models = createModels();
const tracing = await setup({ autoInstrument: false });
instrumentPiAgent(models, tracing);
```

## Run An Agent

Pass `models.streamSimple.bind(models)` to the current Pi `Agent`. Wrap the run
in `agentSpan()` to group all model turns under one stable agent identity.

The following example uses an Anthropic model. It assumes you ran the setup
block above.

<Metadata text="integrations/traces/pi-agent-run[series=pi_agent_setup]" />

```typescript TypeScript theme={"system"}
import { Agent } from "@earendil-works/pi-agent-core";
import { agentSpan } from "@inference/tracing";

const model = models.getModel("anthropic", "claude-sonnet-4-6");
if (!model) throw new Error("Pi model not found");

const agent = new Agent({
  initialState: {
    systemPrompt: "You answer order questions in one short sentence.",
    model,
  },
  sessionId: "order-abc-123",
  streamFn: models.streamSimple.bind(models),
});

await agentSpan(
  {
    agentId: "pi-support-agent",
    agentName: "Pi Support Agent",
    spanName: "pi-support-agent.run",
    sessionId: "order-abc-123",
    role: "support",
    system: "pi-agent",
  },
  async (span) => {
    const input = "Summarize order ABC-123.";
    span.setInput(input);
    await agent.prompt(input);
    span.setOutput(agent.state.messages.at(-1));
  },
);

await tracing.shutdown();
```

Expected spans:

* `pi-support-agent.run` AGENT span
* One or more `pi-agent.anthropic.turn` LLM child spans

## Trace Tool Execution

Pi returns model tool calls in assistant messages and executes `AgentTool`
functions locally. Catalyst records the requested tool name, ID, and arguments
on the LLM span. Wrap local execution with `manualSpan()` when you also want a
TOOL span for the work.

<Metadata text="integrations/traces/pi-agent-tool[series=pi_agent_tool]" />

```typescript TypeScript theme={"system"}
import { type AgentTool } from "@earendil-works/pi-agent-core";
import { Type } from "@earendil-works/pi-ai";
import { manualSpan, SpanKindValues } from "@inference/tracing";

const parameters = Type.Object({ orderId: Type.String() });

const lookupOrder: AgentTool<typeof parameters> = {
  name: "lookup_order",
  label: "Look up order",
  description: "Look up an order by ID.",
  parameters,
  execute: async (toolCallId, { orderId }) =>
    manualSpan(
      {
        spanName: "lookup_order",
        spanKind: SpanKindValues.TOOL,
        toolName: "lookup_order",
        toolCallId,
        input: { orderId },
      },
      async (span) => {
        const order = { orderId, status: "shipped", eta: "Friday" };
        span.setOutput(order);
        return {
          content: [{ type: "text" as const, text: JSON.stringify(order) }],
          details: order,
        };
      },
    ),
};
```

Add `lookupOrder` to `initialState.tools`. A tool round trip then produces:

* A `pi-agent.<provider>.turn` LLM span with the requested tool call
* A `lookup_order` TOOL span for local execution
* Another `pi-agent.<provider>.turn` LLM span for the final answer

## Verify In Catalyst

Filter traces by your `service.name`, for example `pi-agent`. A successful run
shows the AGENT span with nested Pi Agent LLM spans. Each LLM span includes
input/output, model metadata, usage, finish reason, and tool call attributes.

If no Pi Agent spans appear:

* Pass the `Models` collection as `modules: { piAgent: models }`.
* Instrument the collection before the agent makes its first model call.
* Add providers through the instrumented collection's `setProvider()` method.
* Consume streaming results or await `agent.prompt()` before shutdown.
* Call `await tracing.shutdown()` before a short-lived process exits.
