AI Tracing

Trace every LLM call, agent step, and tool execution in your AI application. Temps ingests standard OpenTelemetry GenAI spans — no proprietary SDK required.


Overview

Temps provides a built-in AI Activity dashboard that visualizes your AI application's behavior: which models you're calling, token usage, latency, error rates, and full conversation replay.

Instead of building a custom tracing library, Temps uses the OpenTelemetry standard — specifically the gen_ai.* semantic conventions. This means you can use existing instrumentation libraries from the OTel ecosystem with zero vendor lock-in.

What gets captured

  • Model name and provider
  • Input/output token counts
  • Request/response latency
  • Conversation messages (opt-in)
  • Tool calls and results
  • Agent workflow traces
  • Error states and types

Supported providers

  • OpenAI / Azure OpenAI
  • Anthropic (Claude)
  • Google (Gemini)
  • xAI (Grok)
  • Any OpenAI-compatible API
  • Cohere, Mistral, Bedrock
  • LangChain, LlamaIndex

How It Works

Your App  →  OTel SDK + Auto-Instrumentation  →  OTLP/HTTP  →  Temps  →  AI Activity Dashboard
  1. Your app uses an AI SDK (OpenAI, Anthropic, Vercel AI SDK, etc.)
  2. An auto-instrumentation library patches the SDK to emit gen_ai.* spans automatically
  3. The OTel exporter sends spans to Temps via standard OTLP/HTTP protocol
  4. Temps stores, indexes, and displays them in the AI Activity tab

No proprietary SDK. No custom span code. Just standard OpenTelemetry.


Authentication

Temps supports two token types for the OTLP ingest endpoint:

API Keys (tk_)

User-scoped tokens. Requires the X-Temps-Project-Id header to identify the target project.

Generate one with the CLI:

temps api-key --name "ai-tracing" --role admin

When using an API key, pass both the token and project ID:

Authorization: Bearer tk_your_api_key_here
X-Temps-Project-Id: 2

Deployment Tokens (dt_)

Project-scoped tokens automatically created for each deployment. The project, environment, and deployment are inferred from the token — no extra headers needed.

Authorization: Bearer dt_your_deployment_token_here

OTLP Endpoint

The ingest endpoint accepts standard OTLP/HTTP Protobuf payloads:

POST https://your-temps-instance.com/api/otel/v1/traces
POST https://your-temps-instance.com/api/otel/v1/metrics
POST https://your-temps-instance.com/api/otel/v1/logs

Or with IDs in the URL path (useful for deployment tokens):

POST https://your-temps-instance.com/api/otel/v1/{project_id}/{environment_id}/{deployment_id}/traces

Python — Auto-Instrumented (OpenAI)

The easiest way to trace OpenAI SDK calls. The opentelemetry-instrumentation-openai-v2 library patches the SDK and emits gen_ai.* spans automatically.

Install

pip install openai \
  opentelemetry-sdk \
  opentelemetry-exporter-otlp-proto-http \
  opentelemetry-instrumentation-openai-v2

Setup

import openai
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor

# Configure OTel to export to Temps
resource = Resource.create({"service.name": "my-ai-app"})
provider = TracerProvider(resource=resource)

exporter = OTLPSpanExporter(
    endpoint="https://your-temps-instance.com/api/otel/v1/traces",
    headers={
        "Authorization": "Bearer tk_your_api_key",
        "X-Temps-Project-Id": "2",
    },
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

# One line to auto-instrument all OpenAI calls
OpenAIInstrumentor().instrument()

Now use OpenAI as normal — spans are emitted automatically:

client = openai.OpenAI()
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}],
)
# Traces appear in your Temps AI Activity dashboard

Enable conversation capture

By default, message content is not captured for privacy. To see full conversations in the AI Activity dashboard, set:

export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true

Works with the Temps AI Gateway

If you're using the Temps AI Gateway (OpenAI-compatible endpoint for multiple providers), the same auto-instrumentation works for all providers — Anthropic, Google, xAI, etc.:

client = openai.OpenAI(
    api_key="tk_your_api_key",
    base_url="https://your-temps-instance.com/api/ai/v1",
)

# All of these are auto-traced with gen_ai.* attributes
client.chat.completions.create(model="claude-haiku-4-5", ...)
client.chat.completions.create(model="gpt-4o", ...)
client.chat.completions.create(model="gemini-2.5-flash", ...)

Python — Anthropic SDK

Use the community OpenLLMetry instrumentation for native Anthropic SDK calls.

Install

pip install anthropic \
  opentelemetry-sdk \
  opentelemetry-exporter-otlp-proto-http \
  opentelemetry-instrumentation-anthropic

Setup

import anthropic
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor

# Configure OTel exporter
resource = Resource.create({"service.name": "my-ai-app"})
provider = TracerProvider(resource=resource)

exporter = OTLPSpanExporter(
    endpoint="https://your-temps-instance.com/api/otel/v1/traces",
    headers={
        "Authorization": "Bearer tk_your_api_key",
        "X-Temps-Project-Id": "2",
    },
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

# Auto-instrument Anthropic SDK
AnthropicInstrumentor().instrument()

Now use Anthropic as normal:

client = anthropic.Anthropic()
message = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)
# Spans are emitted automatically

Enable conversation capture

export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true

Node.js — Vercel AI SDK

The Vercel AI SDK has built-in telemetry via the experimental_telemetry option. No extra instrumentation library needed.

Install

npm install ai @ai-sdk/openai @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http

Setup

Create tracing.ts and import it before your app starts:

import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'https://your-temps-instance.com/api/otel/v1/traces',
    headers: {
      'Authorization': 'Bearer tk_your_api_key',
      'X-Temps-Project-Id': '2',
    },
  }),
  serviceName: 'my-ai-app',
})

sdk.start()

Usage

Pass experimental_telemetry to enable tracing on each call:

import { generateText } from 'ai'
import { openai } from '@ai-sdk/openai'

const result = await generateText({
  model: openai('gpt-4o'),
  prompt: 'Explain quantum computing in simple terms.',
  experimental_telemetry: {
    isEnabled: true,
    recordInputs: true,   // capture prompts
    recordOutputs: true,  // capture responses
  },
})

Every generateText, streamText, generateObject, and streamObject call emits gen_ai.* spans automatically when experimental_telemetry is enabled.

Enable globally with middleware

To avoid passing experimental_telemetry to every call:

import { wrapLanguageModel } from 'ai'
import { openai } from '@ai-sdk/openai'

const model = wrapLanguageModel({
  model: openai('gpt-4o'),
  middleware: {
    experimental_telemetry: {
      isEnabled: true,
      recordInputs: true,
      recordOutputs: true,
    },
  },
})

// All calls through this model are traced
const result = await generateText({ model, prompt: '...' })

Node.js — Auto-Instrumented (OpenAI)

Use the OpenLLMetry JS library to auto-instrument the OpenAI Node.js SDK.

Install

npm install openai @traceloop/instrumentation-openai @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http

Setup

Create tracing.ts — must be imported before any other module:

import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { OpenAIInstrumentation } from '@traceloop/instrumentation-openai'

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'https://your-temps-instance.com/api/otel/v1/traces',
    headers: {
      'Authorization': 'Bearer tk_your_api_key',
      'X-Temps-Project-Id': '2',
    },
  }),
  instrumentations: [new OpenAIInstrumentation()],
  serviceName: 'my-ai-app',
})

sdk.start()
// app.ts — tracing must be imported first
import './tracing'
import OpenAI from 'openai'

const client = new OpenAI()
const response = await client.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Hello!' }],
})
// Spans are emitted automatically

Manual Instrumentation

If you need full control over span attributes, or you're using a provider without auto-instrumentation support, you can create gen_ai.* spans manually.

Python

import json
from opentelemetry import trace
from opentelemetry.trace import SpanKind, StatusCode

tracer = trace.get_tracer("my-ai-app")

with tracer.start_as_current_span(
    "chat gpt-4o",
    kind=SpanKind.CLIENT,
) as span:
    # Required attributes
    span.set_attribute("gen_ai.operation.name", "chat")
    span.set_attribute("gen_ai.system", "openai")
    span.set_attribute("gen_ai.request.model", "gpt-4o")

    # Make your API call
    response = call_llm(...)

    # Record response
    span.set_attribute("gen_ai.response.model", "gpt-4o-2024-08-06")
    span.set_attribute("gen_ai.usage.input_tokens", 150)
    span.set_attribute("gen_ai.usage.output_tokens", 320)
    span.set_attribute("gen_ai.response.finish_reasons", ["stop"])

    # Optional: capture messages (privacy-sensitive)
    span.set_attribute("gen_ai.input.messages", json.dumps([
        {"role": "user", "content": "Hello!"}
    ]))
    span.set_attribute("gen_ai.output.messages", json.dumps([
        {"role": "assistant", "content": "Hi there!"}
    ]))

    span.set_status(StatusCode.OK)

Node.js / TypeScript

import { trace, SpanKind, SpanStatusCode } from '@opentelemetry/api'

const tracer = trace.getTracer('my-ai-app')

await tracer.startActiveSpan(
  'chat gpt-4o',
  { kind: SpanKind.CLIENT },
  async (span) => {
    span.setAttribute('gen_ai.operation.name', 'chat')
    span.setAttribute('gen_ai.system', 'openai')
    span.setAttribute('gen_ai.request.model', 'gpt-4o')

    const response = await callLLM(...)

    span.setAttribute('gen_ai.response.model', 'gpt-4o-2024-08-06')
    span.setAttribute('gen_ai.usage.input_tokens', 150)
    span.setAttribute('gen_ai.usage.output_tokens', 320)
    span.setAttribute('gen_ai.response.finish_reasons', ['stop'])

    span.setStatus({ code: SpanStatusCode.OK })
    span.end()
  }
)

Tracing agent workflows

Nest spans to represent agent steps:

with tracer.start_as_current_span("invoke_agent research-assistant") as agent_span:
    agent_span.set_attribute("gen_ai.operation.name", "invoke_agent")
    agent_span.set_attribute("gen_ai.agent.name", "research-assistant")

    # Child span: first LLM call (decides to use tools)
    with tracer.start_as_current_span("chat gpt-4o", kind=SpanKind.CLIENT):
        span.set_attribute("gen_ai.operation.name", "chat")
        span.set_attribute("gen_ai.request.model", "gpt-4o")
        # ... make LLM call, set response attributes

    # Child span: tool execution
    with tracer.start_as_current_span("execute_tool search_web"):
        span.set_attribute("gen_ai.operation.name", "execute_tool")
        span.set_attribute("gen_ai.tool.name", "search_web")
        span.set_attribute("gen_ai.tool.call.arguments", '{"query": "..."}')
        span.set_attribute("gen_ai.tool.call.result", '{"results": [...]}')

    # Child span: final LLM call with tool results
    with tracer.start_as_current_span("chat gpt-4o", kind=SpanKind.CLIENT):
        # ... make LLM call with tool results

This produces a trace tree in the AI Activity dashboard:

invoke_agent research-assistant
  ├── chat gpt-4o (initial request)
  ├── execute_tool search_web
  └── chat gpt-4o (final response)

Viewing Traces

Once instrumented, traces appear in the AI Activity tab of your project:

  • Trace list — All AI traces with model, provider, token counts, latency, and status
  • Trace detail — Full span tree showing each step of an agent workflow
  • Conversation view — Input/output messages for each LLM call (requires content capture enabled)
  • Filters — Filter by provider (anthropic, openai, google), model, status, or time range

Semantic Conventions

Temps follows the OpenTelemetry GenAI Semantic Conventions. Key attributes:

AttributeDescriptionExample
gen_ai.operation.nameOperation typechat, embeddings, execute_tool, invoke_agent
gen_ai.systemProvider nameopenai, anthropic, google
gen_ai.request.modelRequested modelgpt-4o, claude-sonnet-4-20250514
gen_ai.response.modelActual model usedgpt-4o-2024-08-06
gen_ai.usage.input_tokensInput token count150
gen_ai.usage.output_tokensOutput token count320
gen_ai.response.finish_reasonsWhy generation stopped["stop"], ["tool_calls"]
gen_ai.response.idProvider response IDchatcmpl-abc123
gen_ai.request.temperatureTemperature setting0.7
gen_ai.request.max_tokensMax tokens requested1024
gen_ai.input.messagesInput messages (JSON)Opt-in, contains prompts
gen_ai.output.messagesOutput messages (JSON)Opt-in, contains responses
gen_ai.agent.nameAgent identifierresearch-assistant
gen_ai.tool.nameTool being executedsearch_web
gen_ai.tool.call.argumentsTool call arguments (JSON){"query": "..."}
gen_ai.tool.call.resultTool call result (JSON){"results": [...]}

Temps also supports the deprecated gen_ai.usage.prompt_tokens / gen_ai.usage.completion_tokens and gen_ai.provider.name (alias for gen_ai.system) for backward compatibility.


Environment Variables

VariableDescriptionDefault
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENTCapture prompt/response content in spansfalse
OTEL_EXPORTER_OTLP_ENDPOINTOTLP exporter base endpoint
OTEL_EXPORTER_OTLP_HEADERSHeaders for OTLP exporter (comma-separated key=value)
OTEL_SERVICE_NAMEService name for the OTel resource

Minimal env-based configuration

Some OTel SDKs can be configured entirely via environment variables, avoiding any setup code:

export OTEL_EXPORTER_OTLP_ENDPOINT=https://your-temps-instance.com/api
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer tk_your_api_key,X-Temps-Project-Id=2"
export OTEL_SERVICE_NAME=my-ai-app
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true

With these set, many auto-instrumentation libraries will pick up the configuration automatically without any code changes.

Was this page helpful?