feat(obs): record the request body on failed chat/messages/responses requests - #749
Conversation
…requests Content capture was structurally success-only: DispatchFailure carried no content and every failure-path emit passed None, so a 4xx/5xx SLS row showed status + error class but never WHAT was sent (AISIX-Cloud#1013). Failed requests on the three routing endpoints (/v1/chat/completions, /v1/messages, /v1/responses) now capture the (post-mask) request body under the same opt-in gate and cap as the success path: - pre-dispatch terminal events (guardrail block, validation, budget, model-not-found) carry the prompt; - when every target failed there is no terminal event, so the prompt rides the LAST failed attempt (the status the caller saw); other attempts stay content-less to avoid duplicating large prompts; - the billed-then-blocked (output guardrail) terminal event carries the prompt; the blocked model output intentionally stays out; - 401/403 stay body-less — the body adds nothing to an authorization failure and callers probing keys shouldn't get payloads archived. The remaining handlers (completions/embeddings/rerank/images/audio) emit no usage events at all on failure — with or without content — which is a separate gap, filed as a follow-up. Fixes api7/AISIX-Cloud#1013
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 3 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
…e exports post-mask text Audit findings on the initial revision: - HIGH: the mask-action rewrite ran only after the input block check passed, so the new failure-path capture exported the PRE-mask body for blocked (422) requests — the one case the success path would have masked. The three endpoints now run the mask rewrite before returning the block error. (Remote Bedrock segment masks stay skipped on the dead request — those entities are never locally detected; noted in the block-arm comment.) - /v1/responses billed-then-blocked (output guardrail) now carries the prompt like chat/messages; the blocked output itself stays out. - e2e additions: post-mask capture on a blocked request (raw email must never reach the logstore), all-targets-failed places the prompt on exactly the LAST attempt record, fallback-then-success keeps the failed attempt content-less, and the metadata_only assertion now has a vacuous-pass guard.
Problem
Content capture was structurally success-only:
DispatchFailurecarried no content field and every failure-path emit passedNone. A 4xx/5xx row in SLS showed status, error class and attempt metadata — but never what was sent, so triaging "is this a caller problem or a gateway/upstream problem" required reproducing the call (AISIX-Cloud#1013).Change
Failed requests on the three routing endpoints (
/v1/chat/completions,/v1/messages,/v1/responses) now capture the (post-mask) request body into thepromptcontent field, under exactly the same rules as the success path — only when acontent_mode: fullexporter is enabled, capped atcontent_max_bytes, body-only (headers/credentials are never part of captured content):Masking note: PII mask guardrails rewrite the request in place before dispatch, so captured failure bodies are post-mask whenever the failure occurred at or after the guardrail stage; failures before that stage (e.g. malformed-parameter 400s) log the body as received — same semantics as the success path, where capture is also post-mask.
Deliberately out of scope
completions/embeddings/rerank/images/audioemit no usage events at all on failure today (success-only emitters) — there is no record to attach content to. That's a separate, pre-existing gap: filed as #748.count_tokensemits no usage events by design (free endpoint).Tests
New e2e
sls-failure-content-e2e.test.ts(realaisix+ etcd + mock SLS, decodes the delivered protobuf):guardrail_blocked=truemetadata_onlyexporter running alongside never receives any failed-request promptVerified fail-before/pass-after: on unpatched main, tests 1/2/4/5 fail with "no SLS log matching … with prompt"; with this change all six pass. Neighboring content-capture and guardrail e2es pass unchanged.
Baseline: mainstream LLM proxies log the request messages on failures as well (failure logging payloads carry the input alongside structured error info), with redaction — not omission — as the sensitive-data control. This change brings failure logs to parity while keeping capture opt-in and capped.
Fixes api7/AISIX-Cloud#1013
🤖 Generated with Claude Code