Recording what the model did
Every model call emits a span carrying the provider, the model id, the prompt version, tokens, latency and the session. The application's own logs record that a call succeeded. The traces record which model answered and how many attempts it took.
Langfuse, OpenTelemetry and the AI SDK
Why each component is here.
Langfuse
OpenTelemetry
Vercel AI SDK
With no credentials present the span processor is inert and the application runs unchanged. An unreachable backend cannot fail a request.
Visitor traffic is traced up to 300 model calls a day, then not at all
A public demo that traces everything spends an observability quota the audit evidence also draws on, and a percentage sample sets no ceiling, because it scales with whatever traffic arrives.
The budget is an absolute count held in one row of the database, claimed with a single statement so 2 requests arriving together cannot both decide there is room. Once the day is spent, tracing switches off and the application carries on. It is claimed once per model call rather than once per failover attempt, so a retry does not spend the budget several times over.
Evidence runs are never counted against it. They set CADENCE_TRACE_ENV=audit and are the reason the budget exists.
This deployment reports environment demo and tracing is connected.
Real traces, readable without an account
The tool is behind a login and the deployed site carries no keys, so this work could be described here without anyone being able to see it. These rows come from actual runs, and each links to the trace itself.
Exported 2026-09-07 from runs of this system. The statements inside are synthetic, written by the Demo Bank generator. A trace is readable without a Langfuse account where it was declared public as it was written. The rows that open carry a link, and the rest are marked as not shared.
| Attachment | Answered by | Prompt | Tokens in / out | Latency | Cost recorded | Trace |
|---|---|---|---|---|---|---|
decide-application 2026-09-07 | groq, gcp.gemini openai/gpt-oss-120b 1 failover | rat-2026-09-05.1 | 5,409 / 14,683 6 model calls | 67.1s | $0.00000 | not shared |
decide-application 2026-09-07 | groq, gcp.gemini openai/gpt-oss-120b 1 failover | cat-2026-09-05.1 | 5,376 / 17,168 6 model calls | 82.4s | $0.00000 | not shared |
judge-rationale 2026-09-07 | groq openai/gpt-oss-120b | judge-2026-09-05.1 | 839 / 401 1 model call | 8.2s | $0.00000 | Open |
build-rationale 2026-09-07 | groq openai/gpt-oss-120b | rat-2026-09-05.1 | 398 / 349 1 model call | 10.5s | $0.00000 | Open |
categorise-transactions 2026-09-07 | groq openai/gpt-oss-120b | cat-2026-09-05.1 | 881 / 1,132 1 model call | 10.0s | $0.00000 | Open |
judge-rationale 2026-09-07 | groq openai/gpt-oss-120b | judge-2026-09-05.1 | 782 / 231 1 model call | 0.8s | $0.00000 | not shared |
build-rationale 2026-09-07 | groq openai/gpt-oss-120b | rat-2026-09-05.1 | 398 / 324 1 model call | 1.0s | $0.00000 | not shared |
categorise-transactions 2026-09-07 | groq openai/gpt-oss-120b | cat-2026-09-05.1 | 881 / 1,448 1 model call | 3.3s | $0.00000 | not shared |
regenerate-rationale 2026-09-06 | groq openai/gpt-oss-120b | not versioned yet | 6,921 / 13,449 6 model calls | 65.0s | no price registered | not shared |
live-eval 2026-09-05 | groq openai/gpt-oss-120b | not versioned yet | 1,442 / 3,072 1 model call | 6.8s | no price registered | not shared |
The cost column reports a price setting. Both providers run on free tiers, so no run in this table was billed. The column reports 3 different things: 2 rows where no price is registered for the model, 8 where one is and it is zero, and 0 where a published list price was applied to a request that was never charged.
Costs are computed when a trace is written, so a row keeps whatever price was registered at that moment and no later edit reaches it. Both models now carry an explicit zero, which is why the recent rows read $0.00000 and the older ones do not. The underlying point stands: cost is the wrong axis for ranking these 2 providers while both tiers are free, and what actually binds each one is a quota. Recorded as F5.
What each field records
All 3 model call sites emit the same 5 fields.
gen_ai.provider.name, with the model id beneath it. The chain fails over, so the answering provider varies per call and cannot be read off the configuration.
A function id per call site, so categorisation, rationale drafting and the judge stay in separate groups.
The change record. Without it a stored label cannot be tied to the prompt that produced it.
Cost and behaviour. Temperature is recorded because it explains why repeated runs return different answers.
The same identifier the application scopes its writes by, so a trace joins back to the application, decision and audit rows.
What was settled before collection started
Evidence is separated from demo traffic
The override is recorded as a score
What the traces caught that nothing else did
A run the application called a success had failed 24 times
One evaluation over the labelled set produced 24 error observations out of 88, all the same cause: the primary provider hit a rate limit of 8,000 tokens per minute and the work passed to the paid fallback. The run completed, reported an accuracy, and said nothing. A single evaluation had therefore been answered by 2 different models, so the headline figure was a blend of both. F6
Every row claimed a provider that had not answered
The trace recorded one provider while the application stamped another on every row it returned, in the same response. The field had been a hardcoded literal rather than the provider that actually replied. F1
Where this stops short
Tracing is off in this deployment. No credentials are set here. Public traffic would consume the 50,000 unit monthly quota and mix demo activity into the evidence set. Everything described above runs locally.
A trace records that a model returned a category. Whether the category was correct is measured by the evaluation harness.
Cost attribution reports a price setting. Both models now carry an explicit price of zero, so a new generation records $0.00000, and the older rows keep the price that was registered when they were written. Neither tier has ever charged, and what binds each provider is a quota. Recorded as F5.