Skip to main content
The Moda SDK for Python (moda-ai) auto-instruments the OpenAI and Anthropic client libraries: after one moda.init() call, every LLM request, response, streamed completion, and tool call is captured and sent to Moda without changes to your provider code. This page covers installation, configuration, provider coverage, and conversation context.

Prerequisites

  • Python 3.10 or later
  • A Moda API key, created at Settings → Ingestion keys (see Authentication)
  • The openai or anthropic package your app already uses

Set up

1

Install the SDK

The package installs as moda-ai but imports as moda. Instrumentation for OpenAI and Anthropic is bundled — no extra packages are needed for these two providers.
2

Initialize and make a call

Call moda.init() once at startup, before your app makes LLM calls. Initialization is synchronous.
app.py
3

Verify in the dashboard

Open Conversations in the dashboard. Within minutes you should see a conversation with ID session_8f2a containing the user message and the assistant response, with the model name and token counts attached.

Configuration

moda.init() options

For custom OpenTelemetry pipelines, moda.init() also accepts exporter (a custom SpanExporter), processor (a SpanProcessor or list of them), propagator, sampler, and span_postprocess_callback. When exporter or processor is set, spans are exported through your components instead of Moda’s default OTLP exporter.

Environment variables

Environment variables take precedence over arguments passed to moda.init(). If MODA_API_KEY or MODA_BASE_URL is set in the process environment, it overrides the api_key and endpoint arguments.

What gets instrumented

moda.init() patches the provider client libraries. Anything your app does through them is captured, including streaming. OpenAI (openai package, sync and async clients):
  • Chat Completions — create and parse, including streaming and tool calls
  • Responses API — create, retrieve, cancel
  • Legacy Completions
  • Embeddings
  • Image generation
  • Assistants API (assistants, threads, runs)
  • Realtime API — sessions opened through beta.realtime.connect()
Anthropic (anthropic package, sync and async clients):
  • Messages — create and stream, including tool use and extended thinking
  • Beta Messages
  • Legacy Completions
  • The AnthropicBedrock client, so Claude called through Amazon Bedrock is captured

Provider detection for OpenAI-compatible endpoints

When you point the OpenAI client at a different base_url, the SDK detects the vendor from the URL and records it as the provider: Other OpenAI-compatible endpoints (for example Groq) are still fully captured — requests, responses, and token usage — but are recorded with provider openai.
openrouter.py

Supported providers

Conversation and user context

Set a conversation ID to group related calls into one conversation, and a user ID to attribute them to a user. Context is stored in contextvars, so it is safe in async code and threaded request handlers.

Module properties

Context managers

set_conversation_id and set_user_id scope the context to a block and restore the previous value on exit — use them in concurrent request handlers:
handler.py

Setters and getters

Set the conversation ID before the first model call of a session — from your session, thread, or run ID — so every call in the session lands in one conversation.

Selecting instrumentations

By default all instrumentations are enabled, but each activates only when its target package is installed. Restrict them with instruments (allowlist) or block_instruments (blocklist):
Available Instruments values: Only the OpenAI and Anthropic instrumentation packages ship with moda-ai. All other values take effect only if you install the corresponding opentelemetry-instrumentation-<name> package yourself.

Flushing

Spans are batched and exported in the background. Call moda.flush() to force-export pending spans:
  • Always flush before a script or worker exits — unexported spans are lost when the process ends.
  • In long-running servers, flushing on shutdown is enough; the batch exporter sends data continuously while the process runs.
  • For very short-lived processes, pass disable_batch=True to moda.init() so each span is sent immediately.
There is no separate shutdown function in the Python SDK; moda.flush() is the only lifecycle call you need.

Troubleshooting

Next steps