Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Mistral OTLP Observability

Manual, OTLP-first observability helpers for GenAI applications.

The package installs into mistralai.observability.otel and owns private OpenTelemetry providers for traces, metrics, and logs. It does not install global tracer or meter providers and does not configure Python logging.

Install

pip install mistralai-otel-observability

Install the optional Mistral client adapter when using operation.mistral_completion(...) or running the live completion example:

pip install "mistralai-otel-observability[mistral]"

The top-level package exports the normal SDK surface: observability, ObservabilityConfig, Operation, Features, the explicit telemetry payload dataclasses, ChatCompletionDetails, and ChatCompletionAdapter. Specialized utilities such as Mistral message conversion stay in their scoped modules.

Usage

from mistralai.observability.otel import ObservabilityConfig, observability
from mistralai.observability.otel import semconv_constants as genai

observability.configure(
    ObservabilityConfig(
        service_name="my-app",
        endpoint="http://localhost:4318",
        protocol="http/protobuf",
    )
)

with observability.operation(
    genai.OPERATION_NAME_CHAT,
    model="mistral-large-latest",
    provider=genai.PROVIDER_NAME_MISTRAL_AI,
    include_content=True,
) as chat:
    chat.completion(
        response_model="mistral-large-latest",
        output_messages=[{"role": "assistant", "content": "Bonjour!"}],
        input_tokens=12,
        output_tokens=4,
    )

observability.shutdown()

The first argument to observability.operation(...) is the GenAI operation name from the semantic conventions. For a chat completion, genai.OPERATION_NAME_CHAT is recorded as gen_ai.operation.name on the operation span, inference detail log, token usage metric, and operation duration metric. The high-level Mistral helper keeps using that same operation context:

with observability.operation(
    genai.OPERATION_NAME_CHAT,
    model="mistral-large-latest",
    provider=genai.PROVIDER_NAME_MISTRAL_AI,
    include_content=True,
) as chat:
    response = client.chat.complete(model="mistral-large-latest", messages=messages)
    chat.mistral_completion(response, messages=messages)

In that example:

  • The span is named chat mistral-large-latest and has gen_ai.operation.name="chat", gen_ai.provider.name="mistral_ai", and gen_ai.request.model="mistral-large-latest".
  • chat.mistral_completion(...) enriches the same span with response metadata, token counts, and finish reasons. Because this operation was created with include_content=True, it also records gen_ai.input.messages and gen_ai.output.messages on the span.
  • Inference detail logs are explicit. Calling chat.inference_details(...) emits event name gen_ai.client.inference.operation.details with the operation/model/provider attributes and any messages, prompt, or completion content passed to that method.
  • The token usage metric uses gen_ai.client.token.usage with gen_ai.operation.name="chat" and gen_ai.token.type="input" / "output".
  • The context manager records gen_ai.client.operation.duration when the operation ends, again with gen_ai.operation.name="chat".
  • chat.evaluation_result(...) emits gen_ai.evaluation.result. That event is correlated to the operation through the operation trace/span context, and through gen_ai.response.id when provided; the semantic convention for evaluation results does not require duplicating gen_ai.operation.name on the event.
  • chat.log(...) and chat.event(...) are operation-scoped application logs. They inherit the operation trace/span context, but they only carry GenAI attributes that you pass explicitly.

By default, completion helpers are metadata-only on the span: gen_ai.input.messages and gen_ai.output.messages are omitted unless the operation is created with include_content=True. operation.inference_details(...) is different: it is always explicit and emits the messages, prompt, or completion content that you pass to it.

The same Operation object can also be managed explicitly:

from mistralai.observability.otel import observability
from mistralai.observability.otel import semconv_constants as genai

chat = observability.operation(
    genai.OPERATION_NAME_CHAT,
    model="mistral-large-latest",
    provider=genai.PROVIDER_NAME_MISTRAL_AI,
)
try:
    chat.start()
    chat.evaluation_result("quality", score_label="pass")
finally:
    chat.end()

Mistral observability feature flags become regular telemetry attributes. Pass a Features object when creating the operation to add span attributes, or when emitting operation logs to add log attributes:

from mistralai.observability.otel import Features

with observability.operation(
    genai.OPERATION_NAME_CHAT,
    features=Features(sensitive_data=True, moderation=True, judges=["toxicity", "groundedness"]),
) as chat:
    chat.set_attribute("custom.operation.flag", "enabled")
    chat.log("moderation queued", features=Features(moderation=True, judges=["quality"]))
    chat.event(
        "mistral.obs.operation.checkpoint",
        "moderation queued",
        features=Features(moderation=True, judges=["quality"]),
    )

When an evaluation runs asynchronously after the original operation span has already been exported, start a child operation from the original trace and span identifiers:

from mistralai.observability.otel import Operation, observability
from mistralai.observability.otel import semconv_constants as genai

parent = Operation(
    name=genai.OPERATION_NAME_CHAT,
    trace_id="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    span_id="bbbbbbbbbbbbbbbb",
    request_model="mistral-large-latest",
    provider_name=genai.PROVIDER_NAME_MISTRAL_AI,
)

with observability.operation(
    "evaluation",
    parent=parent,
    model="mistral-large-latest",
    provider=genai.PROVIDER_NAME_MISTRAL_AI,
) as evaluation:
    evaluation.evaluation_result(
        "moderation",
        score_label="pass",
        response_id="cmpl_123",
    )

This emits a real operation span whose parent is the provided span id. Logs and evaluations recorded on the operation use the new child span context, so they remain discoverable in the same trace while preserving the original parent span.

Agent, tool, and plan spans are specialized Operation helpers:

with observability.agent("Support Agent", model="mistral-large-latest") as agent:
    with observability.plan("Support Agent", parent=agent):
        pass

    with observability.tool(
        "lookup_customer",
        parent=agent,
        tool_type=genai.TOOL_TYPE_FUNCTION,
        arguments={"customer_id": "cus_123"},
        result={"tier": "enterprise"},
    ):
        pass
  • observability.agent(...) records an invoke_agent span. By default it is INTERNAL, which matches in-process agent frameworks. Pass remote=True for remote hosted agent calls, which records a CLIENT span.
  • observability.create_agent(...) records a create_agent CLIENT span for hosted agent creation.
  • observability.tool(...) records an execute_tool INTERNAL span with tool name, call id, type, arguments, result, description, and the executing agent name when provided.
  • observability.plan(...) records a plan INTERNAL span for an agent planning or task-decomposition phase. It does not emit a duration metric because the GenAI semantic conventions do not define one for plan spans.
  • Workflow spans are intentionally not exposed as a high-level helper yet. The semantic convention says to use invoke_workflow only when instrumentation can reliably distinguish a coordinated multi-agent workflow from an agent invocation, and that boundary is application/framework-specific.

Use observability.span(...) for arbitrary local work that should appear in traces but is not a GenAI operation:

with observability.operation(genai.OPERATION_NAME_CHAT, model="mistral-large-latest") as chat:
    with chat.span("cache.lookup", attributes={"cache.hit": True}) as cache_span:
        cached_response = cache.get("customer:123")
        cache_span.set_attribute("cache.backend", "redis")

    if cached_response is None:
        response = client.chat.complete(...)
        chat.mistral_completion(response)

These spans are exported through the SDK's isolated tracer provider, use the current OpenTelemetry context by default, and intentionally do not add gen_ai.* attributes or GenAI duration metrics. Use operation.span(...) for child spans, or pass parent=operation, context=..., or trace_id=... plus parent_span_id=... to observability.span(...) when the span needs an explicit parent.

Structured-data redaction

To prevent known secrets and PII from reaching OpenTelemetry accidentally, the SDK enables RegexRedactionPolicy by default. It inspects operation data before it is serialized into span attributes or passed to any other OpenTelemetry recording API. Hosts can replace it with another policy:

from mistralai.observability.otel import (
    ObservabilityConfig,
    AttributeRedactionPolicy,
    observability,
)

observability.configure(
    ObservabilityConfig(service_name="my-app"),
    operation_data_transformers=[AttributeRedactionPolicy()],
)

Pass an explicit empty sequence to disable redaction:

observability.configure(
    ObservabilityConfig(service_name="my-app"),
    operation_data_transformers=[],
)

The built-in policies:

Policy Strategy Trade-off
RegexRedactionPolicy Preserves structure and redacts known secret tokens plus emails, card-like sequences, and IPv4 addresses. Keeps the most observability value, but cannot recognize arbitrary free-form secrets.
AttributeRedactionPolicy Redacts values wholesale for sensitive keys, key fragments, and non-primitive values, then scans retained values for known tokens. Safer for prompts and responses, but deliberately removes most content.
CallbackRedactionPolicy Invokes a custom `(key, value) -> value Nonemasker for each field;None` drops it.

The default pattern and key sets are exported as DEFAULT_PII_SECRET_PATTERNS, DEFAULT_TOKEN_PATTERNS, DEFAULT_SENSITIVE_ATTRIBUTE_KEYS, DEFAULT_SENSITIVE_ATTRIBUTE_FRAGMENTS, and DEFAULT_SAFE_ATTRIBUTE_KEYS, so hosts can extend the shared catalogue instead of replacing it.

Policies receive structured values before the SDK serializes them into span attributes or creates OTel logs. Policy failures omit only the affected value. The chain applies to operation spans, child spans, logs, events, metrics, and exception details. It does not apply to standalone low-level observability.span(...) or ObservabilitySDK.record_* calls that are not scoped to an Operation.

Sending Telemetry to Mistral

Configure the SDK with the Mistral telemetry OTLP HTTP endpoint:

from mistralai.observability.otel import observability

observability.configure_mistral(
    service_name="my-genai-service",
    service_version="1.2.3",
)

By default, observability.configure_mistral(...) reads the API key from MISTRAL_API_KEY. Pass api_key="..." when the credential comes from another secret store. Use observability.configure(ObservabilityConfig(...)) directly when a custom endpoint or internal deployment requires explicit OTLP headers.

With protocol="http/protobuf", the SDK sends the three OTLP signals to:

  • https://api.mistral.ai/telemetry/v1/traces
  • https://api.mistral.ai/telemetry/v1/metrics
  • https://api.mistral.ai/telemetry/v1/logs

The base package installs HTTP/protobuf export support. For custom collectors that require OTLP/gRPC, install the grpc extra and set protocol="grpc" in ObservabilityConfig.

Call observability.shutdown() when the application is done with the SDK. It performs a final flush before closing the owned OpenTelemetry providers. Use observability.flush() directly only when the process should keep running but you need telemetry exported at a specific checkpoint.

The helper is equivalent to this explicit OTLP configuration:

import os

from mistralai.observability.otel import ObservabilityConfig, observability

observability.configure(
    ObservabilityConfig(
        service_name="my-genai-service",
        service_version="1.2.3",
        endpoint="https://api.mistral.ai/telemetry",
        protocol="http/protobuf",
        headers={"Authorization": f"Bearer {os.environ['MISTRAL_API_KEY']}"},
    )
)

Run a live Mistral completion and record it through this SDK:

MISTRAL_API_KEY=... uv run --extra mistral python examples/mistral_completion.py

Explicit dataclasses such as Operation, InferenceDetails, and TokenUsage are also available for code that needs the full OpenTelemetry shape, including distinct request and response model attributes.

Use standard OpenTelemetry propagation helpers, such as opentelemetry.propagate.extract(...), when reading inbound trace context. When no explicit context is passed to SDK methods, the current OpenTelemetry context is used so upstream traceparent values are preserved.

Publishing

Releases run from the OTLP Observability SDK workflow via Run workflow (workflow_dispatch), not by pushing a tag. The package version is not stored in code: it is derived from the release tag through uv-dynamic-versioning (tag prefix otel-observability-sdk/) and stamped onto the build at release time, so cutting a release never needs a version-bump PR.

To release:

  1. Open the OTLP Observability SDK workflow and choose Run workflow.
  2. Select a main or mais-* branch as the ref.
  3. Enter the version to publish (for example 0.1.0 or 1.0.0rc1).
  4. Leave publish_pypi / publish_gemfury at their defaults, or untick one to publish to a single index.

The workflow validates the ref and version, fails fast if otel-observability-sdk/v<version> already exists, then builds the package once. A reviewer must approve the observability-publish GitHub Environment before the same artifact is published to the selected indexes (Gemfury and/or public PyPI); PyPI publishes from a clean job via trusted publishing. Once at least one selected index publishes successfully, the workflow creates the otel-observability-sdk/v<version> tag to record the released commit.

For dogfooding, publish an rc version (for example 1.0.0rc1) to Gemfury only by unticking publish_pypi; the rc is still tagged so the build is traceable to a commit. The otel-observability-sdk/vX.Y.Z tag prefix is intentionally distinct from the older observability-sdk/vX.Y.Z release line used by the AI Studio observability SDK, and this package does not publish to Cloudsmith.

SDK Shape

The SDK is deliberately manual and explicit:

  • observability.configure(...) creates the process-wide SDK instance with isolated OpenTelemetry trace, metric, and log providers. Recording before configuration raises a clear error.
  • It never calls global set_tracer_provider, set_meter_provider, or logging.basicConfig.
  • Context propagation uses the shared OpenTelemetry context only for parenting spans and logs.
  • observability.operation(...) returns an Operation. Use it as a context manager, or call start() and end() explicitly. Record details, evaluations, token usage, and duration on that same object.
  • observability.span(...) starts a plain OpenTelemetry span through the same isolated tracer for non-GenAI local work. operation.span(...) does the same while using that operation as the parent.
  • Completion helpers omit input and output message content from spans by default. Pass include_content=True to observability.operation(...) to opt in for span content on operation.completion(...) and operation.mistral_completion(...).
  • Inference detail logs are explicit: operation.inference_details(...) and InferenceDetails(...) emit the content passed to them and are not gated by include_content.
  • Operation can carry trace_id and span_id for an already-exported span. Pass it as observability.operation(..., parent=parent_operation) to create a child operation for delayed telemetry, such as asynchronous evaluations. For raw identifiers, observability.operation(...) also accepts trace_id and parent_span_id.
  • Operation spans accept arbitrary OpenTelemetry attributes through attributes=..., operation.set_attribute(...), and operation.set_attributes(...).
  • Agent, tool, and plan spans are created with observability.agent(...), observability.create_agent(...), observability.tool(...), and observability.plan(...). They are normal Operation objects, so they support the same context management, parenting, logs, evaluations, and custom attributes as inference operations.
  • Operation-scoped logs can be emitted with operation.log(...). Named event logs, useful as a span-event replacement, can be emitted with operation.event(...). Both keep the operation trace/span context and support arbitrary log attributes plus the same Features object.
  • Chat completion details can be recorded directly with operation.completion(...). Raw completion payloads can use ChatCompletionDetails, and provider-specific payloads can implement ChatCompletionAdapter; both are recorded by the low-level SDK with record_completion(operation, completion).
  • The dataclasses, such as Operation, ChatCompletionDetails, InferenceDetails, EvaluationResult, TokenUsage, OperationDuration, and StreamingChunk, expose the fuller semantic-convention shape.
  • GenAI semantic-convention strings live in mistralai.observability.otel.semconv_constants; do not duplicate gen_ai.* strings in the SDK code.
  • Mistral chat payload conversion lives in mistralconv.py and is intentionally separate from signal emission.
  • Mistral SDK response extraction is available on Operation through mistral_completion(...). The Mistral adapter lives in mistralai.observability.otel.mistral and is imported lazily, so the core package does not import the optional Mistral SDK unless that helper is called. It maps the response id, response model, finish reasons, output messages, and prompt/completion token counts exposed by the installed Mistral SDK. Response fields such as object, created, usage.total_tokens, and usage.prompt_audio_seconds are not emitted today because the current GenAI semantic conventions do not define matching standard attributes for them.

Signals emitted today:

  • Spans: GenAI inference operation spans through observability.operation(...), plus agent creation, agent invocation, tool execution, and plan spans through their dedicated helpers.
  • Logs: gen_ai.client.inference.operation.details and gen_ai.evaluation.result.
  • Metrics: gen_ai.client.operation.duration, gen_ai.client.token.usage, gen_ai.client.operation.time_to_first_chunk, gen_ai.client.operation.time_per_output_chunk, gen_ai.invoke_agent.duration, and gen_ai.execute_tool.duration.

GenAI semantic-convention coverage:

  • Covered on spans and inference-detail logs: common provider/model/operation attrs, server address/port, request parameters, response id/model/finish reasons/time-to-first-chunk, token usage including cache/reasoning breakdowns, prompt/conversation attrs, opt-in system instructions, tool definitions, and input/output messages.
  • Covered as separate logs: evaluation results.
  • Covered as metrics: client operation duration, token usage, time to first chunk, and time per output chunk.
  • Covered as first-class spans: agent creation, agent invocation, tool execution, and plan.
  • Covered as agent/tool metrics: in-process agent invocation duration and tool execution duration.
  • Not yet modeled as first-class SDK APIs: embeddings, retrieval, memory, workflow spans, server metrics, and gen_ai.client.operation.exception logs.

Mistral Message Conversion

The mistralconv module includes JSON-dict converters between Mistral chat completion messages and the OpenTelemetry GenAI gen_ai.input.messages / gen_ai.output.messages shape:

from mistralai.observability.otel import mistralconv

otel_input_messages = mistralconv.mistral_messages_to_otel_input_messages(
    [{"role": "user", "content": "hello"}],
)

Mistral-only fields are preserved through mistral.* extension fields where OpenTelemetry has no direct equivalent. Plain string content is normalized back to structured Mistral text chunks on reverse conversion.

Release files for mistralai-otel-observability 0.2.0rc1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mistralai-otel-observability 0.2.0rc1
File Size Uploaded
mistralai_otel_observability-0.2.0rc1.tar.gz 109.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mistralai-otel-observability 0.2.0rc1
File Interpreter ABI Platform
mistralai_otel_observability-0.2.0rc1-py3-none-any.whl Python 3 none any Details

Total release size: 154.0 kB

Release files / mistralai_otel_observability-0.2.0rc1.tar.gz

Download URL mistralai_otel_observability-0.2.0rc1.tar.gz
Size 109.9 kB
Tags Source
SHA-256 checksum
How to use checksums
3a876ba1769272f14b26f876c0c96550eb4a736845e40dec6ae56816201dde8b
BLAKE2b-256 checksum
How to use checksums
d5a32fed607dfe4224fa01ab4d060b14d8d122b1a35c0566bf20ae5bc5a84e34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / mistralai_otel_observability-0.2.0rc1-py3-none-any.whl

Download URL mistralai_otel_observability-0.2.0rc1-py3-none-any.whl
Size 44.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7f3ddf6346ee06b6f2d70780f3679979008bbefc4bf259d3e9fd9156d3e73b41
BLAKE2b-256 checksum
How to use checksums
b1cd9d50a76ff5ec5840de42b8889dec165c70428acd9f8f1793f6fac8d19614
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0rc1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page