Skip to main content
For advanced use cases, you can work directly with the Client class. This gives you full control over initialization, trace submission, scoring, and lifecycle management.

Programmatic initialization

import pandaprobe

client = pandaprobe.init(
    api_key="your-api-key",
    project_name="my-project",
    endpoint="https://your-instance.com",
    environment="production",
    release="v1.2.3",
    enabled=True,
    batch_size=10,
    flush_interval=5.0,
    max_queue_size=1000,
    debug=False,
)
pandaprobe.init() parameters:
ParameterTypeDefaultDescription
api_keystr | NoneFrom envPandaProbe API key
project_namestr | NoneFrom envProject name
endpointstr | Nonehttps://api.pandaprobe.comBackend URL
environmentstr | NoneFrom envEnvironment tag (e.g., "production")
releasestr | NoneFrom envRelease/version tag
enabledbool | NoneTrueEnable/disable SDK
batch_sizeint | None10Traces per flush batch
flush_intervalfloat | None5.0Seconds between automatic flushes
max_queue_sizeint | None1000Max queued items before dropping
debugbool | NoneFalseEnable verbose debug logging
Returns the configured Client instance and sets it as the global singleton.
If init() is called when a client already exists, the existing client is shut down and replaced.

Creating traces via Client

client.trace() has the same signature as pandaprobe.start_trace().
import pandaprobe

with pandaprobe.start_trace("my-agent", input={"messages": [...]}) as t:
    with t.span("llm-call", kind="LLM") as s:
        ...
Use get_client() after init() when you inject dependencies in tests or pass the client through your application layer.

Submitting pre-built traces

from pandaprobe.schemas import TraceData, SpanData, SpanKind
from datetime import datetime, timezone

trace = TraceData(
    name="my-trace",
    input={"messages": [{"role": "user", "content": "Hello"}]},
    output={"messages": [{"role": "assistant", "content": "Hi!"}]},
    started_at=datetime.now(timezone.utc),
    ended_at=datetime.now(timezone.utc),
    session_id="session-1",
    spans=[
        SpanData(
            name="llm-call",
            kind=SpanKind.LLM,
            model="gpt-4o",
            started_at=datetime.now(timezone.utc),
            ended_at=datetime.now(timezone.utc),
        ),
    ],
)

client.log_trace(trace)
Use pre-built TraceData when traces are assembled offline, replayed from logs, or produced by frameworks that already materialize a full trace object.

Scoring

client.score(
    trace_id="your-trace-id",
    name="accuracy",
    value="0.95",
    data_type="NUMERIC",
    reason="Matched expected output",
)

Lifecycle management

client.flush(timeout=30.0)  # Block until all queued items are sent
client.shutdown()            # Flush remaining items and release resources
Always call flush() before shutdown() in short-lived scripts. For long-running services, the SDK flushes automatically via a background thread and atexit handler.

Error handling

Register callbacks to be notified of transport errors:
def on_error(exc: Exception):
    print(f"PandaProbe error: {exc}")

client.on_error(on_error)

Properties

  • client.enabledbool, whether the SDK is active
  • client.configSdkConfig, the resolved configuration (frozen dataclass)

Auto-initialization

The global client auto-initializes from environment variables on first use. You only need to call pandaprobe.init() if you want to configure the SDK programmatically or override environment variables.
# These all trigger auto-init if needed:
pandaprobe.start_trace(...)
pandaprobe.flush()
pandaprobe.score(...)
pandaprobe.get_client() returns None if auto-init fails (e.g., missing API key). pandaprobe.start_trace() raises RuntimeError if no client is available.