Skip to main content
AgentMark uses OpenTelemetry to collect distributed traces and metrics for your prompt executions. This page covers setup and the tracing patterns built on top of it.
Already instrumenting with a framework? Apps traced by OpenInference (LangChain, LlamaIndex, CrewAI, …) or OpenLLMetry (Traceloop, OpenLIT) can send traces to AgentMark without the SDK. Point their OTLP exporter at AgentMark. See Tracing integrations.

Install the SDK

Initialize tracing

TypeScript: pass registerGlobally: true, or your model-call spans silently vanish.The AI SDK emits the model-call (“generation”) span through the global OpenTelemetry tracer. AgentMark’s tracer stays isolated by default so it never clobbers an existing OTel setup in your app. Without registerGlobally: true, your custom span() / observe() wrappers still arrive, so it looks like “tracing works”, but every model span goes to a no-op tracer and never shows up. The result is a trace tree with no model data, no tokens, and no cost, which is easy to miss because the rest of the trace looks healthy.
Omit registerGlobally only when your app already registers its own global OTel provider.Python: init_tracing() always registers the provider globally, with no separate flag. This is the equivalent of TypeScript’s registerGlobally: true, handled for you.
This is the single most common reason traces look like they’re working but carry no model data. If model spans are missing, this is the first thing to check. For the other silent failures that drop spans (telemetry not enabled, env vars not loaded, or a script exiting before the batch flushes), see Troubleshooting traces.
For local development with agentmark dev, the SDK sends traces to http://localhost:9418 automatically. Pass disableBatch: true for short-running scripts:
Once spans are flowing, AgentMark classifies each one by type (GENERATION for model calls, SPAN for everything else) and reads attributes like model, tokens, and response off it. For the full list of span types, attributes, how traces link to a prompt version, and how AgentMark derives trace-level input/output, see the Span and attribute reference.

Attributing traces to an environment

By default, AgentMark pins an API key to one environment, and every trace it sends lands there. A key scoped instead to environment kinds (see API key environment scope) carries no pin, so it lets the SDK pick the target environment per request. Pass environment or prNumber to initTracing():
  • environment sets the X-Agentmark-Environment header. The gateway authorizes the named environment against the key’s allowed kinds; if the key can’t write to that environment’s kind, the gateway drops the traces rather than writing them to the wrong environment.
  • prNumber sets the X-Agentmark-Pr-Number header. The gateway maps it to that pull request’s preview environment. When you set both, prNumber wins.
Omitting both falls back to the key’s pinned environment, so a pinned key never needs these options.

Zero-config selection in CI

For CI runs where adding code isn’t convenient, initTracing() reads two environment variable fallbacks:
  • AGENTMARK_ENVIRONMENT, the fallback for environment.
  • AGENTMARK_PR_NUMBER, the fallback for prNumber.
The precedence is: an explicit option, then the matching AGENTMARK_* variable, then the Vercel auto-derivation below. The eval Action and component set these variables for you, so eval traces from a pull request land in that PR’s preview environment with no configuration.

Automatic selection on Vercel

When your app runs on Vercel, initTracing() derives the environment from the system variables Vercel already exposes, so a Vercel deploy needs no selector configuration. A Preview deployment for an open pull request attributes traces to that PR’s preview environment, a branch Preview deployment with no open PR uses the branch ref as the environment name, and a Development deployment uses the dev environment. A Production deployment sends to the key’s pinned environment. The derived selector is the lowest-precedence source, so an explicit option or AGENTMARK_* variable still overrides it. See Vercel integration for the full mapping.

Grouping operations into a span

Use span() (TypeScript) or span_context() (Python) to wrap a block of work as a single parent span. Nested SDK calls automatically attach as children.
In TypeScript, span() returns { result, traceId } where result is Promise<T>, not T. You need to await it to get the resolved value. In Python, span_context() is an async context manager that exposes ctx.trace_id immediately.
SpanOptions accept far more than name: a sessionId to group traces into a session, a userId, metadata, and the dataset/experiment fields used by experiment runs. For every field and its Python snake_case equivalent, see SpanOptions in the reference.

Creating child spans

Use ctx.span() inside a callback to create child spans under the current parent:
ctx.span() accepts { name: string; metadata?: Record<string, string> }. Use observe() (below) if you need to set a SpanKind on a span.

Wrapping functions with observe()

observe() wraps an async function with automatic input/output capture AND lets you set a SpanKind. Unlike span() / ctx.span() which create inline spans, observe() produces a reusable function so every call is automatically traced.
Observed functions automatically attach to the active trace context, so they nest correctly inside span() / span_context() without extra wiring. You can also redact arguments or return values before they’re recorded with the processInputs / processOutputs options. For every observe() option and the full list of SpanKind values, see the Span and attribute reference.

Using SpanKind in a pipeline

To set SpanKind on individual steps of a pipeline, wrap each step with observe() and call the wrapped functions inside span():

Scoring traces

Use sdk.score() to attach quality scores to traces or spans:

Best practices

  • Use meaningful function IDs: "customer-support-greeting", not "func1"
  • Add relevant metadata: user_id, environment, query parameters
  • Always enable telemetry in production: monitor performance and set up alerts
  • Shutdown tracer for short scripts: call tracer.shutdown() before the process exits

Next steps

Span reference

Span types, attributes, SpanOptions, and SpanKind values

Sessions

Group related traces together

Metadata

Add custom context to traces

Tags

Categorize traces with labels

PII masking

Redact sensitive data from traces

Troubleshooting

Fix traces that aren’t showing up

Have questions?

Reach out any time: