Design: Responses content extraction in ai-statistics (#3581)
- Proposal Issue: 4721
Design: Responses content extraction in ai-statistics
Tracking issue: #3581. Baseline: faccaad586a3cdc9e85dc7fa39358ff31a6453b2. Reviewable design only; not implemented or approved.
Integration boundary
Keep token/error framing in onHttpStreamingBody unchanged. Built-in content attributes currently consume the existing accumulated response buffer once at end of stream. Add Responses-aware extraction to that path in getBuiltinAttributeValue, preserving precedence of existing Chat Completions/Claude branches. Do not process content both per-event and at end-of-stream.
Use small event-aware extraction helpers and reuse existing SSE parsing where its semantics fit. Do not add another persistent callback-tail buffer. Respect complete event boundaries, LF/CRLF and JSON escaping; do not parse an unterminated tail as a complete event or join unrelated event metadata.
For each complete SSE event, prefer nonempty JSON type over SSE event-name fallback. Append string delta only for response.output_text.delta (answer) or response.reasoning_summary_text.delta (public reasoning summary). Ignore done/completed snapshots, unknown types, malformed JSON and non-string deltas. Preserve the existing observability panic recovery and client bytes.
For non-stream JSON, iterate output[] in response order. Message items contribute only content[] type output_text string text; reasoning items contribute only summary[] type summary_text string text. Do not expose tool arguments or encrypted/private reasoning. Missing content retains existing fallback behavior.
No path defaults, metrics, provider conversion, dependency bump or full-buffer memory-policy change. Explicit /responses configuration keeps this separate from #4206.
Verification Plan
- Unit regressions: mixed/separate answer-summary events, JSON type vs event fallback/conflict, done/completed duplication, tool/unknown/malformed events, escaped and multibyte text, LF/CRLF, multiple output/content/summary entries, disabled attributes and existing formats.
- Host-path tests: split a short SSE fixture at every byte offset, plus targeted UTF-8/JSON/event boundaries in a longer fixture; include multiple events per callback and empty final callback. Assert exact single-copy output and unchanged payload. Preserve all current SSE-framer tests.
- Plugin commands: go test ./... -count=1; focused race tests; go vet ./...; GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared. Record actual toolchain/results rather than predeclaring success.
- Runtime red/green: pinned gateway/Envoy with separate baseline/fixed Wasm modules, identical explicit /responses and built-in-attribute config, deterministic local mock JSON/SSE upstream. Compare exact AI-log answer/summary, token metrics, client status/headers/body SHA-256 and logs. Repeat fragmented cases at least three times.
- Publish exact source SHAs, both Wasm hashes, image digest/Envoy version, configs/fixtures, machine assertions and sanitized raw results. Include rerun/cleanup commands and prove no test containers/ports/processes remain.
Execution
After explicit Proposal/Design approval, one non-Coordinator worker owns the bounded plugin implementation/tests. An independent read-only reviewer checks exact base/head; every P0/P1 goes back to the writer and is rechecked. Tasks remain draft and no worker is authorized yet. July provenance is not retroactive approval.
Source: higress-group/higress