Pi Agent

Trace a Pi Agent with Neatlogs (TypeScript).

Wrap a Pi Agent with piAgentHooks to capture its runs, model calls, and tool calls.

Prerequisites

npm install neatlogs@latest @mariozechner/pi-agent-core @mariozechner/pi-ai

Usage

import { init, shutdown } from 'neatlogs';
import { piAgentHooks } from 'neatlogs/pi-agent';
import { Agent } from '@mariozechner/pi-agent-core';
import { getModel } from '@mariozechner/pi-ai';

async function main() {
  await init({ apiKey: process.env.NEATLOGS_API_KEY, workflowName: 'pi-agent-demo' });

  const agent = piAgentHooks(
    new Agent({
      initialState: {
        systemPrompt: 'Be concise.',
        model: getModel('openai', 'gpt-4o'),
        tools: [],
        messages: [],
      },
    }),
  );

  await agent.prompt('In one sentence, what is Pi Agent?');

  await shutdown();
}

main().catch(console.error);

Sessions & end-user

Say a user keeps chatting with your Pi agent across a handful of turns and you'd like the whole exchange to read as one session — piAgentHooks opens each run's span as the trace root, so wrap a turn in identify() and that root plus every span beneath it picks up the session and end-user. Bind the same values on each turn and the conversation stitches itself together:

// Same sessionId every turn → one session; endUserId attributes it to your user.
await identify(
  { sessionId: `conv_${conversationId}`, endUserId: userId },
  async () => {
    await agent.prompt(message);
  },
);

Not every run is a conversation. A standalone workflow — a one-off job, not a back-and-forth — is a single trace with no turns to group. You still attribute it to the customer it ran for, sourcing the id and any metadata from your own user or request object:

Warning

A standalone workflow must not carry a sessionId. Omit it entirely — Neatlogs sets sessionId = traceId, so the run is its own single-turn session, fully attributed to the end-user. Reusing one sessionId across unrelated runs wrongly folds them into a single multi-turn conversation.

async function summarize(document, user) {
  // Standalone workflow — attribute to the end-user; do NOT pass sessionId.
  return identify(
    { endUserId: String(user.id), endUserMetadata: { plan: user.plan } },
    async () => agent.prompt(`Summarize:\n${document}`),
  );
}

The full picture lives in Sessions and End-User Identity.

On this page

Ask Neatlogs AI

Answers from the docs

How can I help?

Ask anything about instrumenting, tracing, or the Neatlogs dashboard.