PrompticPromptic

Tracing

Promptic uses OpenTelemetry to automatically capture every LLM call your application makes. Once set up, you get full visibility into requests, responses, token usage, cost, and latency — with zero manual instrumentation.

Setup

import promptic_sdk

promptic_sdk.init()

init() installs OpenTelemetry instrumentation for all detected LLM libraries and exports spans to the Promptic platform.

Supported providers and frameworks

Install the SDK with the extras for your stack:

CategoryItemInstall command
LLM providerOpenAIpip install promptic-sdk[openai]
LLM providerAnthropicpip install promptic-sdk[anthropic]
LLM providerAWS Bedrockpip install promptic-sdk[bedrock]
LLM providerGoogle Vertex AIpip install promptic-sdk[vertexai]
LLM providerMistralpip install promptic-sdk[mistralai]
Agent frameworkLangChain / LangGraph / create_agent / deepagentspip install promptic-sdk[langchain]
Agent frameworkOpenAI Agents SDKpip install promptic-sdk[openai-agents]
Agent frameworkClaude Agent SDKpip install promptic-sdk[claude-agent]
Google Generative AI and Cohere(included by default)pip install promptic-sdk
EverythingAll of the abovepip install promptic-sdk[all]

Pydantic AI is also supported without any extra: it ships its own OpenTelemetry emitter. Enable it with Agent(..., instrument=True) and its spans will flow to Promptic through the same TracerProvider that init() configures.

Configuration

promptic_sdk.init(
    api_key="ptc_...",          # default: PROMPTIC_API_KEY env var
    endpoint="https://...",      # default: https://promptic.eu
    auto_instrument=True,        # default: True — auto-detect and instrument LLM libs
    service_name="my-service",   # optional — sets the OTel service name
)

AI Components

AI Components are logical groupings for your LLM-powered features. Use them to separate traces for different parts of your application:

with promptic_sdk.ai_component("email-classifier"):
    # All LLM calls inside this block are tagged with "email-classifier"
    response = client.chat.completions.create(...)

with promptic_sdk.ai_component("support-agent"):
    # These traces are tagged with "support-agent"
    response = client.chat.completions.create(...)

Components are auto-created in Promptic when the first trace arrives. You can also create them explicitly via the SDK client or API.

Datasets and runs

Tag traces with a dataset and run to group them for evaluation:

with promptic_sdk.ai_component("support-agent", dataset="eval-set", run="v2"):
    for query in test_queries:
        agent.run(query)

This creates a dataset called "eval-set" with a run called "v2" under the "support-agent" component. All traces from this block are automatically added to that run.

You can also use the dataset context manager for more granular control:

with promptic_sdk.ai_component("support-agent"):
    # Production traffic — no dataset tagging
    agent.run(user_query)

    # Evaluation run — tagged
    with promptic_sdk.dataset("nightly-eval"):
        for query in test_queries:
            agent.run(query)

Tracing workflows with custom spans

Most users don't need this. With the right [extras] installed, auto-instrumentation already creates spans for every LLM and tool call. Reach for custom spans only when you have meaningful non-LLM workflow logic (retrieval, normalization, business rules, control flow) you want represented in the trace.

When you do need it, wrap your workflow stages in custom OpenTelemetry spans. Auto-instrumented LLM and tool spans automatically nest under whichever custom span is active.

The recommended pattern:

  1. Wrap the whole run in one root workflow span inside ai_component(...).
  2. Add a child task span for each meaningful stage of the pipeline.
  3. Record each stage's input and output as span attributes so the trace reads as a transformation, not just a list of LLM calls.
import json
import promptic_sdk
from opentelemetry import trace

promptic_sdk.init()
tracer = trace.get_tracer(__name__)

with promptic_sdk.ai_component("support-agent"):
    with tracer.start_as_current_span("answer_question") as root:
        root.set_attribute("traceloop.span.kind", "workflow")
        root.set_attribute("traceloop.entity.input", json.dumps(user_input))

        with tracer.start_as_current_span("retrieve_context") as span:
            span.set_attribute("traceloop.span.kind", "task")
            span.set_attribute("traceloop.entity.input", json.dumps(query))
            context = retrieve(query)
            span.set_attribute("traceloop.entity.output", json.dumps(context))

        with tracer.start_as_current_span("generate_answer") as span:
            span.set_attribute("traceloop.span.kind", "task")
            # The auto-instrumented LLM call nests under this task span
            answer = llm_call(context)

        root.set_attribute("traceloop.entity.output", json.dumps(answer))

Span attribute conventions:

AttributeUse
traceloop.span.kind="workflow"The top-level run
traceloop.span.kind="task"An internal pipeline stage
traceloop.entity.input / traceloop.entity.outputJSON-serialized stage payloads, surfaced in the Promptic UI

File and media artifacts

Promptic automatically keeps large inline file content out of trace rows. If an auto-instrumented provider emits base64 media in span attributes, the SDK and ingestion layer upload the bytes as trace artifacts and replace the original payload with a lightweight promptic-artifact://... reference. The trace UI uses that reference to render images and files on demand, while the trace API stays small and reliable.

This covers common multimodal payloads such as OpenAI data-URI images, Anthropic base64 media sources, Gemini inline data, Bedrock byte fields, and large text/markdown/json fields. Existing external https://... media URLs are left as URLs and rendered directly.

Local filesystem paths are intentionally not read automatically. A span attribute like /tmp/report.pdf is only a string, and silently uploading it could leak files the application did not intend to trace. Attach local files explicitly when you want their contents available from the trace:

import promptic_sdk

file_ref = promptic_sdk.artifact("/tmp/report.pdf")
span.set_attribute("retrieval.input_file", file_ref.uri)

Fetch artifacts through the SDK or CLI:

from promptic_sdk import PrompticClient

with PrompticClient() as client:
    artifacts = client.list_trace_artifacts(trace_id)
    client.download_artifact(artifacts["data"][0]["id"], "artifact.bin")
promptic traces artifacts <trace-id>
promptic artifacts get <artifact-id> --output artifact.bin

SDKs can also avoid routing artifact bytes through Promptic by uploading to a private storage_object first and then registering the artifact metadata. The trace still stores only the promptic-artifact://... reference.

Direct artifact uploads are always scoped to the authenticated workspace in object storage. When the artifact is registered, Promptic verifies that the storage object belongs to the same workspace, is private, exists in the trace-artifacts prefix, and has the expected size and content type without downloading the file body.

A few tips:

  • Use semantic span names (retrieve_context, rerank_results) rather than generic function names, so repeated stages stay distinguishable in the trace view.

  • For huge collections, still prefer logging a small preview plus a count. Artifacts are for file-like content, not for turning every trace into a data dump:

    span.set_attribute(
        "traceloop.entity.output",
        json.dumps({
            "items": items[:5],
            "item_count": len(items),
            "additional_item_count": max(len(items) - 5, 0),
        }),
    )

The result is a single trace where the root workflow span carries the structured input and output, task spans appear as its children, and every auto-instrumented LLM or tool call nests under the stage that triggered it.

LangGraph and deepagents support

pip install promptic-sdk[langchain] installs OpenLLMetry's opentelemetry-instrumentation-langchain (≥0.60), which covers LangChain chains, LangGraph (create_agent), and deepagents with subagents. It emits the official OpenTelemetry GenAI semantic conventions (gen_ai.operation.name, gen_ai.tool.definitions, gen_ai.tool.name, gen_ai.usage.*) — the same schema Promptic's trace parser uses for every framework, so agent-evaluation insights (loops, tool errors, unused tools) work identically for flat agents and multi-agent graphs.

No extra configuration required.

Alternative: LangSmith OTel bridge

If you prefer LangSmith's built-in OTel exporter (for hybrid setups where traces also flow to LangSmith), set these env vars before calling init():

LANGSMITH_TRACING=true
LANGSMITH_OTEL_ENABLED=true

The SDK will propagate Promptic tags (promptic.ai_component, promptic.dataset, promptic.run) through LangSmith's run metadata so bridged spans are still linked to the right component. Note that the LangSmith bridge does not emit tool definitions, so the "unused tools" evaluator insight will not fire on LangSmith-bridged traces.

Custom OpenTelemetry instrumentors

Since Promptic uses standard OpenTelemetry, you can add any OTel instrumentor alongside the built-in ones:

import promptic_sdk
from opentelemetry.instrumentation.httpx import HTTPXInstrumentor

promptic_sdk.init()
HTTPXInstrumentor().instrument()  # Also trace HTTP calls

What's captured

Each trace includes:

FieldDescription
ModelThe model used (e.g., gpt-4o, claude-sonnet-4-20250514)
ProviderOpenAI, Anthropic, Google, etc.
Input/OutputFull request and response content
TokensInput, output, and total token counts
CostEstimated cost in USD
LatencyDuration in milliseconds
Statusok or error
SpansIndividual API calls within the trace