Observability and OpenTelemetry
On this page
Updated November 2, 2026 based on
mistralaiSDK 3.7.0: trace methods are located underclient.workflows.executions; their asynchronous versions carry the_asyncsuffix (awaiting the synchronous version fails),execution_idis passed as a keyword argument, and responses exposeotel_trace_data,span_tree, andevents(thespans,activity_count, orerror_countfields read by the older version no longer exist);traceparentis passed as an execution parameter, as the HTTP header is deprecated. Sources: Workflow observability · client-python.
Why observe your workflows
A workflow in production can chain dozens of activities, call multiple external services, and run from a few seconds to several days. Without observability, diagnosing a problem is like finding a needle in a haystack: you know the execution failed, but not at which step, after how many attempts, or whether the culprit is your code or a third-party API. Mistral’s Workflows platform integrates OpenTelemetry natively to answer this question: every activity, every error, and every duration is recorded as a span in a distributed trace, with no instrumentation required on your part.
Two concepts structure this model. The trace represents the entire journey of an execution, from the initial trigger to the final result. The span, on the other hand, is an individual operation within that trace: each activity automatically generates one, carrying its name, start and end times, execution duration, status (success or error), and any custom attributes. These spans form a hierarchy—the workflow span is the parent, and the activity spans are its children—which reproduces the complete structure of the execution.
Name activities for readability
The name parameter of the activity decorator determines the label that appears in traces. It’s a ten-second investment while writing that will save you a lot of time during an incident.
import mistralai.workflows as workflows
@workflows.activity(name="Extract source text")
async def extraire_texte(url: str) -> dict:
async with httpx.AsyncClient() as client:
response = await client.get(url)
return {"texte": response.text}
@workflows.activity(name="Mistral sentiment analysis")
async def analyser_sentiment(texte: str) -> dict:
client = Mistral()
response = await client.chat.complete_async(
model="mistral-large-latest",
messages=[
{"role": "system", "content": "Analyze the sentiment."},
{"role": "user", "content": texte}
]
)
return {"sentiment": response.choices[0].message.content}
@workflows.activity(name="Send email notification")
async def envoyer_notification(destinataire: str, contenu: str) -> dict:
# ...
return {"status": "sent"}
Put yourself in the shoes of the on-call engineer opening a trace view at 3 a.m. Between fetch_user_data and “Retrieve user profile,” the latter is immediately understandable, even to someone who has never read this code.
Retrieve traces programmatically
The SDK exposes three access methods under client.workflows.executions; their asynchronous versions carry the _async suffix. The first returns raw OpenTelemetry data—spans, durations, attributes, parent-child relationships—allowing you to re-ingest it into your own observability tool (Jaeger, Grafana Tempo, etc.).
from mistralai.client import Mistral
client = Mistral()
# Raw OpenTelemetry data for the execution
trace = await client.workflows.executions.get_workflow_execution_trace_otel_async(
execution_id="exec_abc123def456"
)
print(f"OTel Trace: {trace.otel_trace_id}")
otel_data = trace.otel_trace_data # Full trace, to re-export to your backend
When you mainly want to know if the execution succeeded and how long it took, the summary is enough. It includes the status, total duration, and the span tree, which you can traverse recursively:
summary = await client.workflows.executions.get_workflow_execution_trace_summary_async(
execution_id="exec_abc123def456"
)
print(f"Workflow: {summary.workflow_name} — status {summary.status}")
print(f"Total duration: {summary.total_duration_ms} ms")
def display(span, depth=0):
end = span.end_time_unix_nano or span.start_time_unix_nano
duration_ms = (end - span.start_time_unix_nano) / 1e6
print(f"{' ' * depth}{span.name}: {duration_ms:.0f} ms, {len(span.events)} event(s)")
for child in span.children or []:
display(child, depth + 1)
if summary.span_tree:
display(summary.span_tree)
The third method returns a chronological list of execution events:
response = await client.workflows.executions.get_workflow_execution_trace_events_async(
execution_id="exec_abc123def456",
include_internal_events=False, # Exclude internal platform events
)
for event in response.events or []:
print(event)
The include_internal_events=False parameter filters out technical platform events, keeping only those from your workflow and activities. On a slightly long workflow, the difference in readability is considerable. For the logs themselves—those displayed in Studio—get_workflow_execution_logs returns them page by page.
Master sampling
By default, all traces are exported: the worker samples at 100%. To set the trace ID or control sampling for an execution, pass a W3C-format traceparent in the body of the execution request. The worker uses a parent-based sampler: it follows your decision, carried by the last field (01 to collect, 00 not to collect).
execution = await client.workflows.execute_workflow_async(
workflow_identifier="my_workflow",
input={"data": "test"},
traceparent="00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
)
Mistral SDKs do this automatically: with each execution, they transmit the traceparent of the current span. Passing traceparent in an HTTP header is still accepted but deprecated, because a header can be rewritten in transit; use the parameter instead.
Monitor in production
Five indicators are enough to keep a workflow under control: the success rate, i.e., the percentage of executions that finish without error; the average duration of a complete execution; the P95 and P99 durations, which reveal the slowest executions that the average hides; the retry rate per activity, whose sudden rise betrays an unstable external service well before the success rate degrades; and finally, the identification of the slowest activity, your bottleneck.
Since traces respect the OpenTelemetry standard, nothing forces you to consult them from the SDK: they can be exported to Datadog via its OTel collector, to Grafana and Tempo by the same mechanism, to Jaeger for distributed trace visualization, or to New Relic via its dedicated exporter. Your workflows thus join the dashboard where the rest of your infrastructure already lives.
Debug a failed workflow
Faced with a failure, a methodical procedure avoids guessing.
- Retrieve the execution ID from logs or the API response
- Consult the trace summary to know the status and spot the faulty activity in the span tree
- Examine detailed events to understand the sequence that led to the error
- Check retries: was the activity restarted? How many times?
- Analyze the error message: timeout, network error, business error?
The first two steps can be scripted in a few lines, using the display function seen above, giving you a reusable diagnostic tool.
async def diagnose_failure(execution_id: str):
client = Mistral()
executions = client.workflows.executions
# Step 1: Summary and span tree
summary = await executions.get_workflow_execution_trace_summary_async(execution_id=execution_id)
print(f"Status: {summary.status}")
if summary.span_tree:
display(summary.span_tree)
# Step 2: Events from your code, in order
response = await executions.get_workflow_execution_trace_events_async(
execution_id=execution_id,
include_internal_events=False,
)
for event in response.events or []:
print(event)
The number of attempts deserves special attention: an activity that fails on the first attempt and one that fails after five retries tell different stories and call for different fixes.
Course recap
Congratulations, you have finished this course on Mistral Workflows. The journey took you from durable orchestration and the event sourcing mechanism to installing the SDK (lessons 1 to 3), then to creating activities, workflows, and workers, triggering and testing them (lessons 4 to 7). Lessons 8 to 11 laid out the structuring constraints—determinism, Pydantic inputs, timeouts, limitations—before opening up to advanced patterns of parallelism and continuation. The last three covered retry policies, the requirement for async I/O, and observability with OpenTelemetry.
The platform is in Public Preview: APIs and features may evolve. Check the official documentation regularly at docs.mistral.ai/workflows to follow updates.
Key takeaways
- Each activity automatically generates OpenTelemetry spans with name, duration, and status
- Name your activities with the
nameparameter to facilitate debugging - Three methods to access traces, under
client.workflows.executions: raw OTel, summary (status, duration, span tree), and detailed events - Sampling is controlled by the
traceparentparameter of the execution (100% by default; the HTTP header is deprecated) - In production, monitor the success rate, duration, and retry rate
- Standard OTel traces are exportable to Datadog, Grafana, Jaeger, or New Relic
Test your knowledge
Durable workflows: engine rules before production.
1. What does the Mistral Workflows platform guarantee?
Answer: Durability: a workflow survives failures and resumes where it left off—long-duration orchestration becomes reliable by design.
2. Activity, workflow, worker: who does what?
Answer: The activity is the unit of work (external call, computation), the workflow orchestrates activities, the worker executes—the separation that enables resumption and scaling.
3. What does the determinism rule impose?
Answer: Workflow code must be replayable identically: no randomness or direct clock access in orchestration—non-deterministic effects live in activities.
4. How are transient failures handled?
Answer: Via the activity retry policy (attempts, backoff) and timeouts—failure is a normal case, handled declaratively rather than manually.
5. What does OpenTelemetry integration bring?
Answer: Standard observability: execution traces, metrics, and correlation with the rest of your system—essential for operating workflows in production.
Durability, determinism, retry, observability: four rules that hold long orchestrations together—the engine handles the rest.