AgentPeek
Developer documentation · SDK v0.2.1Browse sections ⌘ K
Log inGet API key

GET STARTED

See an agent run, not another logging chore

Pass the provider response you already have. AgentPeek extracts supported output, model, and token fields, calculates a versioned catalog estimate when usage and billing context are known, redacts sensitive values, and attaches request context automatically.

Keep the key server-side

Create a project, copy its one-time API key, and set AGENTPEEK_API_KEY in your backend environment.

1. Install

JavaScript / TypeScript

npm install https://agentpeek.62.238.63.198.sslip.io/sdk/agentpeek-sdk-0.2.1.tgz

Python 3.9+

pip install https://agentpeek.62.238.63.198.sslip.io/sdk/agentpeek_sdk-0.2.1-py3-none-any.whl

2. Track a provider response

typescript
import { AgentPeek } from "@agentpeek/sdk";

const peek = new AgentPeek({
  apiKey: process.env.AGENTPEEK_API_KEY!,
});

const started = performance.now();
const response = await openai.responses.create({
  model: "gpt-5-mini",
  input: userMessage,
});

// model, typed output, reported token classes, and known catalog cost are extracted
peek.trackOpenAI(response, {
  input: userMessage,
  latencyMs: Math.round(performance.now() - started),
});

track() and provider tracking methods return synchronously after queueing. Use await peek.flush() only during graceful shutdown or at the end of a short script.

NORMALIZATION

OpenAI, Anthropic, and Gemini adapters

The adapters accept raw OpenAI Responses and Chat Completions, Anthropic Messages, and Gemini GenerateContent and Interactions results. A single text-only result becomes a string. Multiple choices or candidates, Interaction steps, tool and function calls, thinking and signature parts, and multimodal content remain typed JSON.

typescript
const createResponse = peek.wrapProvider(
  "openai",
  { input: ([request]) => request },
  openai.responses.create.bind(openai.responses),
);

const message = peek.wrapProvider(
  "anthropic",
  { input: ([request]) => request },
  anthropic.messages.create.bind(anthropic.messages),
);

// Raw adapters are also exported for pipelines that only normalize.
import { fromOpenAI, fromAnthropic, fromGemini } from "@agentpeek/sdk";
// fromGemini accepts both GenerateContent and Interactions results.
ProviderAccepted usagePreserved output
OpenAIinput/prompt, cached, cache-write, output/completion, reasoningResponses items; Chat choices, messages, and tool calls
Anthropicuncached input, cache read, 5m/1h cache write, output, thinkingtext, thinking/signature, and tool_use blocks
Geminiinput/prompt, cached, output/candidate, thoughtGenerateContent candidates/parts; Interaction steps and function calls
Missing is not zero. If a response has no usage object, token fields and catalog cost stay unknown. Unknown models and unsupported billing modifiers also keep cost unknown.

AUTOMATIC COST

Versioned pricing and billing units

Known model aliases resolve against the built-in catalog dated 2026-08-30. Estimates select the applicable long-context tier, subtract cache reads and writes from regular input, distinguish Anthropic 5-minute and 1-hour cache writes, and bill output once—reasoning or thoughts remain an output subset.

Normalized billing context applies supported modifiers, including Anthropic US inference geography and Gemini Flex or supported Priority service tiers. An unrecognized model, missing usage, or unsupported service-tier modifier produces unknown cost instead of silently using the standard rate.

Automatic

Built-in rates plus normalized provider usage and billing context.

cost_source: catalog · pricing_version: 2026-08-30
Override

Send a provider-reported or contract-specific USD cost.

cost_source: reported · pricing_version: null

For a custom JavaScript catalog, pass both pricing and a stable pricingVersion provenance label; without a label, catalog-derived events use pricing_version: custom. The server authoritatively fills missing costs from its built-in catalog, so direct HTTP events receive the same built-in provenance.

PROPAGATION

Request and session context

Context uses AsyncLocalStorage in Node.js and contextvars in Python, so concurrent requests do not leak IDs or metadata. Inner context and explicit event values override outer defaults.

typescript
await peek.withContext(
  {
    userId: user.id,
    sessionId: conversation.id,
    metadata: { environment: "production" },
  },
  async () => {
    // Every track(), stream(), and span below inherits this context.
    await answerQuestion();
  },
);

Express middleware reads authenticated user fields, x-agentpeek-session-id, W3C traceparent, and request IDs. ASGI middleware offers the same scope for FastAPI and Starlette.

STREAMING

Pass chunks through, capture the result

Streaming helpers are lazy async iterables, and every chunk keeps its original identity and order. Successful exhaustion records success, a recognized provider terminal failure or thrown iterable records an error, and breaking consumption early records cancellation. In every case, any assembled typed output and observed time to first token are retained.

typescript
const stream = await openai.chat.completions.create({
  model: "gpt-5-mini",
  messages,
  stream: true,
  stream_options: { include_usage: true },
});

for await (const chunk of peek.stream("openai", stream, { input: messages })) {
  yield chunk; // exact provider chunk; AgentPeek never changes it
}
// Output and TTFT are tracked; usage and cost require provider-reported usage.
Terminal usage matters. OpenAI Chat needs stream_options.include_usage; Responses reports usage on its terminal response, Anthropic in the final message_delta, GenerateContent in usageMetadata, and Gemini Interactions on interaction.completed. If that usage never arrives, token fields remain absent and catalog cost stays unknown—AgentPeek does not substitute zero.

TRACES & SPANS

Nested spans for LLM calls, tools, and workflows

Use trace() for the root run and span() for agent, chain, workflow, LLM, tool, retriever, or custom work. Parent and trace IDs propagate automatically across async boundaries.

typescript
await peek.trace("support.answer", async () => {
  const documents = await peek.span(
    "knowledge.search",
    { kind: "retriever", input: { query } },
    () => searchDocuments(query),
  );

  return peek.span("policy.check", { kind: "tool" }, () =>
    checkPolicy(documents)
  );
});

Provider interactions created inside a span become trace leaves. Dashboard trace trees do not roll generic span durations or costs into interaction analytics twice.

FRAMEWORKS

Express, ASGI, LangChain, and LangGraph

typescript
// Express-compatible request context
app.use(peek.requestMiddleware());

// LangChain / LangGraph nested run callbacks
const callbacks = [peek.langchain()];
await chain.invoke({ question }, { callbacks });
python
from agentpeek import AgentPeekASGIMiddleware

# FastAPI / Starlette / any ASGI application
app.add_middleware(AgentPeekASGIMiddleware, client=peek)

# LangChain / LangGraph
result = await chain.ainvoke(
    {"question": question},
    config={"callbacks": [peek.langchain()]},
)

The callback handlers map framework run IDs and parent run IDs into AgentPeek spans without importing LangChain as a required dependency.

PYTHON

Sync and async are both first-class

python
from agentpeek import AgentPeek

peek = AgentPeek()  # reads AGENTPEEK_API_KEY

response = client.messages.create(
    model="claude-sonnet-5",
    messages=messages,
)
peek.track_anthropic(response, input=messages, latency_ms=842)

Async decorator, context, trace, and stream

python
@peek.track(model="gpt-5-mini", provider="openai")
async def answer(question: str):
    return await run_agent(question)

with peek.context(user_id=user.id, session_id=session.id):
    result = await peek.trace("support.answer", answer, question)

async for chunk in peek.astream("openai", provider_stream, input=question):
    yield chunk

Decorated functions preserve return values and re-raise the original exception object. stream() handles synchronous iterables; astream() handles provider async iterables.

PRIVACY

Redaction runs before the queue

Default hooks recursively mask authorization, cookies, passwords, API keys, access/refresh tokens, secrets, private keys, email addresses, payment-card-like values, and common provider key formats. Source request and response objects are never mutated.

typescript
const peek = new AgentPeek({
  redaction: {
    sensitiveKeys: ["ssn", /customer_secret/i],
    patterns: [/acct_[a-z0-9]+/gi],
    replacement: "[REDACTED]",
    hook: (value, field) => tenantPolicy(value, field),
  },
});

Customize keys and patternsAdd strings or regular expressions for your domain.

Add policy hooksHooks receive the already-redacted JSON and its input/output/error/metadata field.

Fail closedIf a custom hook throws, built-in redaction still protects the queued value.

API REFERENCE

Direct HTTPS ingestion

Use direct HTTP when an SDK does not fit your runtime. Both endpoints use the same project key, rate limit, project isolation, and payload-size boundary.

POSThttps://agentpeek.62.238.63.198.sslip.io/v1/interactions
POSThttps://agentpeek.62.238.63.198.sslip.io/v1/spans
bash
curl -X POST https://agentpeek.62.238.63.198.sslip.io/v1/interactions \
  -H "Authorization: Bearer $AGENTPEEK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "evt_01J...",
    "model": "gpt-5-mini",
    "provider": "openai",
    "input": "Where is my order?",
    "output": "It arrives tomorrow.",
    "input_tokens": 120,
    "cached_input_tokens": 40,
    "output_tokens": 28,
    "latency_ms": 842,
    "status": "success",
    "user_id": "usr_123",
    "session_id": "ses_abc",
    "trace_id": "trace_01J...",
    "span_id": "span_01J..."
  }'

Idempotency

Provide a stable id. Replaying the same project ID and identical payload returns duplicate: true without changing session or usage totals. Reusing it for a changed payload returns 409. The same event ID remains independent across projects.

json
HTTP/1.1 202 Accepted

{
  "accepted": true,
  "id": "server-uuid",
  "external_id": "evt_01J...",
  "duplicate": false
}

API REFERENCE

Interaction event schema

FieldTypeDescription
modelstringrequiredResolved model identifier
inputJSONrequiredPrompt, messages, or structured input
outputJSONoptionalString for text-only; typed provider JSON otherwise
latency_msintegerrequiredEnd-to-end milliseconds
time_to_first_token_msintegeroptionalStreaming TTFT
statussuccess | error | cancelledoptionalIncludes terminal failure and consumer cancellation
providerstringfor pricingopenai, anthropic, gemini/google
user_id / session_idstringoptionalInherited request context
input_tokensintegeroptionalTotal input when provider usage is available
cached_input_tokensintegeroptionalSubset of input
cache_write_tokensintegeroptionalSubset of input
output_tokensintegeroptionalTotal output including reasoning
reasoning_tokensintegeroptionalSubset of output
costUSD numberoptionalReported cost or versioned catalog estimate
cost_source / pricing_versionstringoptionalreported, catalog plus provenance, or unknown
trace_id / span_idstringoptionalNested execution identifiers
errorJSONfor errorsRequired for error status
metadataobjectoptionalSearchable redacted context

Span-only fields

trace_idRequired trace identifier
span_idRequired identifier unique inside the project
parent_span_idOptional parent, including out-of-order delivery
nameHuman-readable operation name
kindagent, chain, workflow, llm, tool, retriever, or custom

OPERATIONS

Reliability, security, and limits

  • Tracking queues locally and stays off the application response path.
  • Network, 429, and 5xx failures retry with bounded backoff.
  • Queues are bounded to protect memory; overflow reports through onError.
  • API keys are hashed at rest and dashboard access is organization/project scoped.
  • Retention deletes content in bounded batches while usage history remains available.
Request body1 MB
Ingestion rate300 requests/minute per project key
IDs160 characters
Default retention30 days, configurable per project
Unknown pricenull / unknown, never zero
Ready to inspect a real trace?

Create a project, set the key, and paste either provider example.

Start for free