- Add prompt_audit_events.full_prompt (migration 182) so admins can review
the exact unredacted prompt that triggered a finding; blocking mode writes
it from the snapshot, async mode reconstructs it from the Redis scan
payload so jobs rows stay redaction-only
- Event detail API returns full_prompt (list endpoint stays lean); text is
NUL-stripped and capped at 65536 runes
- Detail dialog shows the full prompt in a scrollable pane with fallback to
the legacy redacted preview; page copy updated to match the new behavior
- Rework filter deletion into a dedicated dialog with time-range presets and
criteria-change preview invalidation; localize decision/risk/category
labels across the events workspace
- Fix pre-existing i18n message-compile spec by declaring the
@intlify/message-compiler dev dependency
Let admins configure private/intranet Guard endpoints without destination-class blocking, and fix prompt-audit switch layout so thumbs and labels no longer overlap.
Co-authored-by: Cursor <cursoragent@cursor.com>
The Anthropic→Responses streaming converter omits two things the OpenAI
Responses wire format requires, which breaks clients that reconstruct the
response from the event stream (rather than just reading deltas).
1. response.content_part.added is never emitted.
A message item is opened with content: [], and the OpenAI SDK's
accumulating stream helper (client.responses.stream) only appends a
content part when it sees content_part.added. Without it, the next
output_text.delta indexes output.content[content_index] and raises:
File "openai/lib/streaming/responses/_responses.py", line 352,
in accumulate_event
content = output.content[event.content_index]
IndexError: list index out of range
Raw iteration (responses.create(stream=True)) does not accumulate and is
unaffected, which is why this went unnoticed.
2. response.completed carries Output: []ResponsesOutput{}.
get_final_response() and tracing integrations parse the terminal event's
response directly, so callers see an empty output_text even though the
deltas streamed correctly. This one is invisible when only watching the
stream render.
Also carries the full text on output_text.done / content_part.done (deltas
carry increments only, done events carry the whole part) and fills in
content/arguments/summary on output_item.done, which had the same
empty-payload issue.
Reproduced against a live Anthropic-platform group with the openai Python
SDK 2.46.0; Arize Phoenix's playground hits the same path. Verified before
(IndexError) and after (full text via get_final_response()).
Adds regression tests covering event ordering, done-event payloads, and the
terminal event's output for both text and tool calls.
Behind a reverse proxy (e.g. nginx with X-Real-IP), admin audit logs and
session IP/UA binding always recorded 127.0.0.1 because they hardcoded
the gin trusted_proxies chain, while API key IP restriction already
honored the "trust forwarded client IP" system setting.
- add ip.GetSecurityClientIP(c, trustForwarded) as the single source of
truth for security-sensitive client IP selection; API key auth
middlewares (main + google) refactored onto it with zero behavior change
- SessionBindingContext(cfg) now resolves the client IP via the same
toggle and injects it into the request context; token issuance,
binding enforcement and its mismatch audit record all read the
injected value, so issue/verify can never diverge
- audit log middleware and audit-log clear trace record the same
security client IP (middleware.SecurityClientIP), falling back to the
trusted proxy chain when the injection is absent
- settings UI hint (zh/en) documents the broadened toggle scope and the
one-time re-login after toggling while session binding is enabled
With the toggle off (default) behavior is byte-for-byte unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>