From 59275ed4f5a69ed5f1c97fabe779fe1b708e76f4 Mon Sep 17 00:00:00 2001 From: Brent Rager Date: Tue, 18 Aug 2026 19:33:18 -0400 Subject: [PATCH] changeset: release polyglot tool-span parity Fifth in a row on this ticket to merge without one (#470, #471, #474, #488, #490). Adding it so the parity work actually publishes. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LkCz96UfUxcai5RU4LwPnG --- .changeset/polyglot-tool-span-parity.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .changeset/polyglot-tool-span-parity.md diff --git a/.changeset/polyglot-tool-span-parity.md b/.changeset/polyglot-tool-span-parity.md new file mode 100644 index 00000000..d6469035 --- /dev/null +++ b/.changeset/polyglot-tool-span-parity.md @@ -0,0 +1,16 @@ +--- +'@smooai/smooth-operator': patch +--- + +Make Go, Python, TypeScript and .NET tool spans self-identifying, and record cost on all four. + +All four engines omitted `gen_ai.system` from the tool span. OTLP consumers that gate on that attribute — SmooAI's does — therefore discarded every tool span those engines ever emitted, exactly as the Rust engine did. Each now carries its own `gen_ai.system`, `gen_ai.operation.name` (literally `chat` / `tool`, taken verbatim by ingest), `gen_ai.conversation.id`, and `smooai.org_id` where the engine has one. Child spans need their own copies: an ingest merges resource attributes with *that span's* attributes and does not inherit from the parent. + +Cost reaches the span for the first time in all four: `gen_ai.usage.cost_usd` when positive, otherwise `smooai.gen_ai.cost_unavailable` = `"unpriced"`. A gateway zero means the model is unpriced, not free, so it is never recorded as a cost. + +Two fabricated values found and removed rather than ported: + +- .NET returned a literal `TurnUsage(0, …)` on the cost fallback path, under an XML doc note reading *"0 means 'nothing priced it', not 'free'"* — the comment was correct and the code shipped the zero anyway. +- .NET published `input_tokens = 0` because it guarded on `sawUsage` alone; a usage chunk with null counts sets that flag while both totals collapse to `0`. The other three engines guard on the counts themselves. + +Each engine has a test that fails without its change, verified by reverting and restoring.