t.llm / t.tool / t.span helpers record the steps.
Mode 1 — callback-scoped (the default path)
Wrap a run inglassray.trace(name, meta?, fn). The callback receives a trace handle t; the return value becomes the trace’s output, and a throw is recorded as an error (the trace still sends).
meta object takes { customer?, sessionId?, flow?, traceId?, environment? } — see Metadata for what each field does. environment is accepted for backward compatibility but ignored since 0.1.3 (the ingest key selects the project).
Mode 2 — wrap once, trace every call
For an agent with a single entry function, wrap it once and every invocation becomes a trace:Mode 3 — manual lifecycle
When a run doesn’t fit inside one callback — batch pipelines, event-driven steps — drive the handles yourself:s.setInput(x), s.setOutput(y), s.setUsage({ inputTokens, outputTokens }) (add an optional cost to override the computed estimate, see Cost), s.setError(err), s.end(). The trace sends when t.end() is called.
Recording steps
Inside any trace, three helpers cover the step kinds Glassray understands:t.tool() calls (Promise.all) become siblings. Spans still open when the root settles are auto-closed and flagged, so a forgotten end() can’t wedge a trace.
Usage capture
Fort.llm, token usage is extracted automatically from the two common response shapes — Anthropic (usage.input_tokens / output_tokens) and OpenAI (usage.prompt_tokens / completion_tokens). For anything else, set it explicitly:
Cost
Glassray fills in each LLM step’s cost for you: it multiplies the step’s token usage by the price of itsmodel. So you usually don’t do anything, just make sure the model you pass is the provider’s canonical model id (the exact string you send to the provider’s API). We price ~2,300 models from the public LiteLLM price list, refreshed daily.
gpt-5.6-luna, claude-opus-4-8, gemini-2.5-pro. A custom or internal alias (for example Luna56LowPriority) won’t match a price, so its cost is left blank. Either pass the canonical id, or set the cost yourself.
The estimate prices total input and output tokens at the model’s base rate; it does not apply the cheaper prompt-cache read rate, so for cache-heavy calls the computed figure can run higher than the provider actually charges. When exact cost matters, set it yourself.
Overriding the cost. When you already know the exact figure — a custom alias, a batch or priority tier, a cache-heavy call, or a gateway that returns a cost — pass cost (in USD) on setUsage. An explicit cost always wins over the computed estimate:
t.llm(...) callback the SDK also picks up a cost your provider returns on the response usage (for example OpenRouter’s usage.cost) automatically.
The
cost field on setUsage needs @glassray/tracing 0.1.4+. Passing a canonical model (auto-computed cost) works on any version.Errors
A throw anywhere — in the root callback or any helper — is recorded on the corresponding span and rethrown unchanged. You never need to try/catch for Glassray’s benefit. To record an error without throwing, uses.setError(err) in manual mode.
Escape hatches
Automatic capture reads arguments and return values. When those aren’t the interesting payload, set trace or span I/O explicitly — explicit setters always win:Sessions
Group multi-turn conversations by passing the samesessionId on each turn’s trace: