Auto-instrumentation handles the LLM API boundary automatically, but most agents contain custom logic — routing, retrieval, business rules, multi-step orchestration — that lives outside any SDK call. Manual tracing lets you annotate that logic precisely: wrap a function with a decorator to capture its inputs and outputs as a span, or open a context manager to record only the code block you care about. Manual traces compose naturally with auto-instrumentation; model spans created byDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/sidmanale643/northstar/llms.txt
Use this file to discover all available pages before exploring further.
auto_instrument() are nested inside whatever trace or span is active in the current context.
Decorator API
@northstar.trace — Top-Level Trace
northstar.trace() opens a Run and sets it as the current trace context. Use it on the entry-point function of an agent or workflow. Function arguments are captured as the run’s input, and the return value is captured as the run’s output.
@northstar.observe — Child Span
northstar.observe() opens a Span inside the current trace. It records the function’s arguments as TOOL_ARGUMENTS events and its return value as a TOOL_RESULT event, subject to the global capture_inputs / capture_outputs settings.
Combined Example
This pattern is taken directly from theexamples/agent_run.py file and the README:
Both
@northstar.trace and @northstar.observe support sync and async functions transparently. The decorator inspects the function with inspect.iscoroutinefunction() and wraps it accordingly — no changes to your function signature are needed.Context Manager API
with northstar.trace(...) — Explicit Input/Output Control
When you need to control exactly what is recorded as the trace input and output — for example, when the input is not the function’s arguments — use the context manager form. The returned handle exposes set_output() for recording the result.
with northstar.span(...) — Nested Spans
northstar.span() opens a child Span inside the current trace context. Spans can be nested arbitrarily; each call reads _current_span from the context variable and uses it as the parent.
SpanKind values (from northstar.models.SpanKind):
| Value | String | Use for |
|---|---|---|
SpanKind.AGENT | "agent" | A sub-agent or nested agent call |
SpanKind.WORKFLOW | "workflow" | A multi-step workflow or pipeline stage |
SpanKind.MODEL | "model" | An LLM API call (used internally by model_call) |
SpanKind.TOOL | "tool" | A tool or function called by the agent |
SpanKind.CUSTOM | "custom" | Any other instrumented block |
model_call Context Manager
Use northstar.model_call() when you want to record token usage, cost, and messages for an LLM call that is not covered by auto-instrumentation (for example, a custom HTTP call or a provider not yet patched). It opens a span of kind MODEL and returns a ModelSpan handle.
ModelSpan handle exposes three recording methods:
Records the list of input messages and estimates input token count via litellm.
Records the assistant’s output message and estimates output token count. If input tokens were already recorded, USD cost is computed automatically.
Records exact token counts and triggers cost computation when you have authoritative usage numbers from the API response.
examples/cost_tracking.py):
Logging Within Traces
Three utility functions are available inside any active trace or span context:Records a named custom event attached to the current span (or the current run if no span is active).
Records a named numeric metric.
value must be a real number (not a boolean).Merges a dictionary of key/value pairs into the current span’s attributes, or into the current run’s metadata if no span is active.
Correlating Application Logs
Callnorthstar.current_trace_id() anywhere inside an active trace to get the run’s UUID as a string. Use it to tag your application log lines so they can be joined with the NorthStar trace in the dashboard.
current_trace_id() returns None when called outside of an active trace, so it is safe to call unconditionally.