m.instruct(), a @generative classifier, a @generative entity extractor,
and a multi-turn m.chat() — while OpenTelemetry instrumentation records each
step. Two independent trace scopes control what gets recorded: the application
trace covers Mellea-level operations, and the backend trace covers raw LLM
calls.
Source file: docs/examples/telemetry/telemetry_example.py
Concepts covered
- The two independent trace scopes:
mellea.applicationandmellea.backend - Controlling tracing with
MELLEA_TRACE_APPLICATIONandMELLEA_TRACE_BACKENDenvironment variables - Using
start_session()as a context manager so session lifecycle is spanned - Exporting spans to an OTLP endpoint (Jaeger)
- Using
mellea.stdlib.requirements.reqto attach constraints tom.instruct()
Prerequisites
- Quick Start complete
- Ollama running locally with
granite4:micropulled - (Optional) Jaeger running locally for span visualisation — see the Jaeger section below
Trace scopes
Mellea defines two independent OpenTelemetry trace scopes.| Scope | Env var | What it records |
|---|---|---|
| Application | MELLEA_TRACE_APPLICATION | Session lifecycle, @generative calls, aact, sampling, requirement validation |
| Backend | MELLEA_TRACE_BACKEND | Raw model generation calls, context-based generation, backend-specific operations |
false. Enable either or both independently depending on what
you need to observe.
Performance impact
| Configuration | Overhead |
|---|---|
| Both disabled (default) | Near-zero |
| Application only | ~1–2 % |
| Backend only | ~1–2 % |
| Both enabled | ~2–5 % |
Running the example
No tracing (baseline)
Application tracing only
Backend tracing only
Both scopes with console output for debugging
Export to an OTLP endpoint (Jaeger)
Starting Jaeger
Run Jaeger in Docker to receive and visualise spans:http://localhost:16686, select the
mellea-example service, and browse the trace timeline.
The full example
Generative function declarations
@generative wires them up
to the runtime; no implementation is needed. Each call site below passes a
session m as the first argument, which binds the call to the current trace
context.
Session as a context manager and introspection
is_application_tracing_enabled() and is_backend_tracing_enabled() reflect
the current environment variable state at runtime. Use these guards in your own
code when you want to conditionally add tracing context (for example, adding
custom span attributes only when tracing is on).
Operation 1: instruct with requirements
start_session() as a context manager (with start_session() as m:)
means the session open and close events are recorded as the root span when
application tracing is enabled. All child operations appear nested under this
root.
req("Must be formal") attaches a soft requirement to the generation.
Requirements appear as span attributes in the trace so you can see which
constraints were applied and whether they triggered a retry.
Operation 2: @generative sentiment classifier
@generative call produces its own child span in the application trace.
The span includes the function name, parameter names, and the inferred return
type.
Operation 3: @generative entity extractor
@generative calls inside the same with block keeps them
all under the same root span. In Jaeger you can see the sequence and duration of
each call on a single timeline.
Operation 4: multi-turn chat
m.chat() is a stateful multi-turn method. The session accumulates turn
history, so response2 can refer back to the result of response1 without
repeating the context. Both turns appear as sibling spans under the root session
span.
Full file
Span attributes
Each span in the application trace includes the following attributes where applicable:| Attribute | Description |
|---|---|
model_id | Model identifier used for the call |
backend | Backend class name (e.g. OllamaBackend) |
action_type | Component type (e.g. generative, instruct) |
context_size | Number of context items passed |
has_requirements | Whether requirements were specified |
strategy_type | Sampling strategy used |
tool_calls | Whether tool calling was enabled |
format_type | Response format class |
What to try next
- Set
OTEL_SERVICE_NAME=my-appto customise the service name in your trace backend. - See OpenTelemetry Tracing for attribute schemas and advanced configuration.
- Add
MELLEA_TRACE_CONSOLE=truealongside an OTLP endpoint to confirm spans are generated even when the remote collector is unavailable.