Quickstart
Tracing ships in the metrana package; nothing extra to install. Set METRANA_API_KEY as for
logging. A tracer is anchored either to a training run,
whose traces carry the policy step, or to a project alone, for inference. The two snippets below are the two
cases.
In a training run
Section titled “In a training run”import metranaimport metrana.tracing as tracing
metrana.init(organization="my-org", workspace_name="my-team", project_name="agents", run_name="grpo-7b-v2")tracing.init() # traces of that run, with its credentials and settings
messages = [{"role": "user", "content": "How many legs do three spiders have?"}]
with tracing.step(100): # the policy step every trace in the block belongs to with tracing.agent_trace("rollout", task_input=messages[-1]["content"]) as trace: with tracing.llm_call("policy", prompt=messages) as llm: reply = model(messages) # your call llm.completion(reply.content) llm.token_count = reply.usage.total_tokens with tracing.tool_call("calculator", input={"expr": "3 * 8"}) as tool: tool.output({"value": 24}) trace.final_output("24") trace.set_attributes({"reward": 1.0, "correct": True})
tracing.close()metrana.close()What happened, in order:
tracing.init()built a tracer for the runmetrana.initopened.agent_traceopened a trace rooted at aSUB_AGENTnode namedrollout, at policy step 100. Its task input was sent with the open.llm_callopened a child span with its prompt on the open; leaving its block sent the completion and the token count on the close.tool_calldid the same with its input and output.- Leaving the trace’s block sent its close: the final output, the attributes, status OK, and the totals — 2 spans, and the LLM call’s tokens.
tracing.close()flushed the stream and stopped the tracer;metrana.close()did the same for the run.
Had an exception escaped any block, that span would have closed with status ERROR and an exception
attribute, and the exception would still have propagated to your code.
At inference, with no run
Section titled “At inference, with no run”A serving process has no run and no step, so there is no metrana.init: the tracer is initialized against the
project, and each request roots at the model call.
import metrana.tracing as tracing
tracing.init(workspace_name="my-team", project_name="agents", run_name=None) # project only
def handle(messages): with tracing.model_trace("chat", prompt=messages, attributes={"model": "claude-y"}) as trace: reply = model(messages) trace.completion(reply.content) trace.token_count = reply.usage.total_tokens return reply
...
tracing.close() # at shutdownThe root is the model call itself, so its prompt and completion are the trace’s own bodies. Tool calls the model made nest beneath it as in the training case. See Inference traces.
Read it back
Section titled “Read it back”Traces land asynchronously. A few seconds later, with the query extra installed:
from metrana.query import QueryClient, AttrFilter
client = QueryClient(organization="my-org", workspace_name="my-team", project_name="agents")
# The run's traces ...traces = client.fetch_spans(runs="grpo-7b-v2", filter=AttrFilter("reward") >= 1.0, attributes=["reward"])trace_id = traces.iloc[0]["trace_id"]tree = client.fetch_trace_spans(trace_id, run="grpo-7b-v2") # root first, then spans by idbodies = client.fetch_span_payloads(trace_id, tree["span_id"].tolist(), run="grpo-7b-v2")
# ... and the project's own inference traces: no run named.chats = client.fetch_spans(filter=AttrFilter("model") == "claude-y")prompt = client.fetch_span_payloads(chats.iloc[0]["trace_id"], run=None)See Searching traces and Reading a trace.