Skip to content

Inference traces

Outside training there is no run and no policy step. A tracer anchored to the project alone records inference: each request becomes a trace owned by the project, and the portal and query SDK read them with no run named.

import metrana.tracing as tracing
tracing.init(workspace_name="my-team", project_name="agents", run_name=None) # project-anchored
def handle(messages):
with tracing.model_trace("chat", prompt=messages, attributes={"model": "claude-y", "tier": "pro"}) as trace:
reply = model(messages)
trace.completion(reply.content)
trace.token_count = reply.usage.total_tokens
return reply
tracing.close() # at shutdown

An inference trace that is one model call roots at an LLM_CALL node, and a root carries the bodies of its own kind: model_trace takes the prompt on its open and trace.completion(...) on its close, exactly as an llm_call span would. Tool calls the model made, retries and delegations nest beneath it as usual:

with tracing.model_trace("chat", prompt=messages) as trace:
with tracing.tool_call("lookup", input={"doc": "report.pdf"}) as tool:
tool.output(page)
trace.completion(answer)

A served agent, rather than a single call, roots at a SUB_AGENT with trace(name, kind=SpanKind.SUB_AGENT, input=task), whose input and output are the task and final output. A decorated request handler roots a FUNCTION trace on its own:

@tracing.op
def handle(request: dict) -> dict: # top-level call, project-anchored: a trace with no step
tracing.current_span().set_attributes({"route": request["path"]})
...

Whether traces carry a step is the tracer’s, not the trace’s. A tracer built for a run requires step= (or a tracing.step block) on every trace; one built for a project refuses it. The project must exist — a project is created by its first run, or through the portal.

tracing.init(...) takes the same credentials and delivery settings as metrana.init (see Configuration); its API key comes from METRANA_API_KEY like everything else. In a process that also has a run open, run_name=None is what makes the tracer project-anchored rather than the run’s.

With no run, the query SDK’s runs= is left empty and run=None names the project’s own traces:

client.fetch_spans(filter=AttrFilter("tier") == "pro") # inference traces of the project
client.fetch_span_payloads(trace_id, run=None) # prompt and completion on span 0

See Searching traces.