Skip to content

Tracing SDK (potato_trace)

potato_trace is a lightweight, dependency-light SDK that captures your agent's runs and ships them to Potato's trace-ingestion pipeline — so Potato sits in the runtime path and you evaluate real runs as they happen, instead of only importing offline dumps.

It is a top-level package (importing it never pulls in Flask or the ML stack) and uses only the standard library plus requests at send time.

Install

potato_trace ships with Potato:

pip install -e .        # from the repo, makes `potato_trace` importable anywhere
# or, running from the repo without installing:
PYTHONPATH=. python your_agent.py

Quick start

import potato_trace

potato_trace.configure(
    potato_url="http://localhost:8000",   # or env POTATO_TRACE_URL
    api_key="",                            # or env POTATO_TRACE_API_KEY
    project_name="my-agent",
)

@potato_trace.traceable(run_type="tool")
def search(query):
    return run_search(query)

@potato_trace.traceable(run_type="llm")
def summarize(text):
    potato_trace.add_metadata(prompt_tokens=120, completion_tokens=40)
    return call_llm(text)

@potato_trace.traceable           # the outermost call is the trace root
def agent(task):
    return summarize(search(task))

agent("weather in NYC")
potato_trace.flush()              # ensure sends finish before a short script exits

Nested @traceable calls form a run tree; when the root returns, the whole tree is POSTed to /api/traces/webhook in a background thread (tracing never blocks or crashes your agent). If no potato_url is configured, tracing is a safe no-op.

API

Symbol Purpose
configure(potato_url=, api_key=, project_name=) Set the global client
@traceable / @traceable(run_type=, name=, tags=) Trace a function (sync or async). run_type: chain (default), llm, tool, retriever
trace(name, run_type=...) Context-manager form: with trace("step"): ...
set_outputs({...}) Set/merge outputs on the current run
add_metadata(**kw) Attach extra metadata (e.g. token usage) to the current run
current_run() The Run currently executing (or None)
flush(timeout=30) Wait for pending background sends
PotatoTraceClient(...) Construct a client explicitly (advanced)

OpenTelemetry interop (optional)

If your stack emits OpenTelemetry spans (GenAI semantic conventions), export them to Potato directly. opentelemetry-sdk is an optional extra:

from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from potato_trace.otel_exporter import build_exporter

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(build_exporter(project_name="my-agent")))

build_exporter() maps spans (using gen_ai.* attributes where present — prompt, completion, token usage, model) to Potato run trees and submits them. potato_trace.otel_exporter.is_available() reports whether opentelemetry is installed.

Live OTLP / OpenInference ingest endpoint

You don't need the Potato SDK at all if your agent framework already emits OpenTelemetry. Point any OTLP/JSON exporter at POST /api/traces/otel and Potato converts the spans into traces. This accepts both the OTel GenAI semantic conventions and OpenInference (Arize/Phoenix) conventions (input.value / output.value / llm.model_name / tool.parameters), so frameworks instrumented with OpenInference or OpenLLMetry — LangGraph, CrewAI, OpenAI Agents SDK, Pydantic AI, LlamaIndex, … — work without any Potato-specific code:

curl -X POST localhost:8000/api/traces/otel \
  -H "Content-Type: application/json" -H "X-API-Key: <key>" \
  -d @otlp-spans.json
# -> {"status": "accepted", "ingested": <n>, "trace_ids": [...]}

GenAI/OpenInference LLM and tool spans become conversation turns; token counts and model land in the trace metadata (so they flow into Trace Analytics). Spans with no recognizable GenAI/OpenInference attributes are skipped ("ingested": 0).

Payload format

The SDK emits the LangSmith-format run payload ({"runs": [...], "project_name"}) that Potato's webhook already normalizes — so no server-side configuration is needed beyond enabling trace ingestion:

trace_ingestion:
  enabled: true
  api_key: ""    # set a key in production; the SDK sends it as a Bearer token