th-bfc65e: OTel gen_ai agent spans on the production streaming path (SMOODEV-2328)#182
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: 4c29158 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
…SMOODEV-2328) The reference server drives every real turn through `runner::run_streaming_turn`, which emitted NO `gen_ai.*` spans — only the secondary `KnowledgeChatRuntime::run_turn` was instrumented, so in production nothing reached the observability studio. Both paths now emit the identical span shape. Why: SMOODEV-2328 (agent observability) needs every agent turn + tool call in the Rust core to flow via OTLP → otel-collector → ClickHouse → the smooai observability studio, grouped by org the same way the monorepo's TS chat handler groups. - Turn `gen_ai.chat` span: adds `gen_ai.agent.name` and (streaming path) `smooai.org_id` (matches the TS handler's attribute exactly for org grouping), plus the existing system/model/conversation.id and aggregated token usage. - Tool `gen_ai.tool` span: adds `gen_ai.tool.call.arguments` (redacted via new `telemetry::redact_tool_arguments`, which scrubs secret-named JSON keys + caps length) and an `otel.status_code=ERROR` + message on failure. - Streaming-path spans are materialized in the main body from the collected AgentEvent stream (not the spawned translator task) so they flow under the process-global OTLP subscriber. OTLP export was already wired end-to-end (`init_telemetry()` in both server and lambda mains, gated on `OTEL_EXPORTER_OTLP_ENDPOINT`) — confirmed, not changed. No per-LLM-call inference span yet: that needs smooth-operator-core to emit per-call usage + finish-reason (separate core→server release). Tests: extends `smooth-operator/tests/telemetry.rs` (agent name + tool args) and adds `smooth-operator-server/tests/telemetry.rs` driving a real `run_streaming_turn` and asserting `smooai.org_id` + redacted tool args. Redaction unit tests in telemetry.rs. Docs updated in `docs/Operations/Observability.md`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6f47246 to
4c29158
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The reference server drives every real turn through
runner::run_streaming_turn(WS service + lambda), which emitted nogen_ai.*spans — only the secondaryKnowledgeChatRuntime::run_turnwas instrumented. So in production, nothing reached the observability studio despite OTLP being wired.Epic SMOODEV-2328 (agent observability): every agent turn + tool call in the Rust core should flow via OTLP → otel-collector → ClickHouse → the smooai observability studio.
What changed
Both turn paths now emit the identical span shape.
Turn
gen_ai.chatspan — adds:gen_ai.agent.name(smooth-agent-chat)smooai.org_id(streaming path) — matches the monorepo TS chat handler's attribute exactly, so the studio groups Rust + TS turns by orgTool
gen_ai.toolchild span — adds:gen_ai.tool.call.arguments, redacted via newtelemetry::redact_tool_arguments(scrubs secret-named JSON keys, caps length)otel.status_code=ERROR+ message on failureStreaming-path spans are materialized in the main body from the collected
AgentEventstream (not the spawned translator task) so they flow under the process-global OTLP subscriber.Audit result (already done vs added)
telemetry.rs, gated onOTEL_EXPORTER_OTLP_ENDPOINT;init_telemetry()called in both server + lambdamain.rs. Confirmed end-to-end, unchanged.gen_ai.chat+gen_ai.toolon the non-streamingrun_turn.run_streaming_turn;gen_ai.agent.name+smooai.org_id; redacted tool arguments + error status; server-side telemetry test.Not in scope (flagged)
chat {model}with per-callgen_ai.usage.*+ finish-reason). The engine'sAgentEventstream only reports usage once (aggregated, onCompleted) and carries no finish-reason. A real inference span needs asmooth-operator-corechange to emit per-call usage/finish-reason → separate core→server release. Do not publish core unilaterally.smooth-operator-server/tests/{suggested_replies,interactions}.rsreferenceTurnRequest.identity_intake, removed by commit85e5643. They fail to compile onmaintoday (likely unnoticed — rust/ tests lack PR CI). Worth a follow-up pearl.Tests
smooth-operator/tests/telemetry.rsextended: assertsgen_ai.agent.name+gen_ai.tool.call.arguments.smooth-operator-server/tests/telemetry.rs: drives a realrun_streaming_turn, assertssmooai.org_id+ redacted tool args on the streaming path.telemetry.rs.cargo fmt --check,cargo clippy(clean on changed files), and all runtime + server telemetry/lib/integration tests pass (validated against the core commitmaintargets — see note below).🤖 Generated with Claude Code