Repository navigation
chore(aiguard): add backend-provided tags to AI Guard spans - #21145
gh-worker-dd-mergequeue-cf854d[bot] merged 3 commits into
Conversation
The evaluate response may carry an extra_tags object. Scalar values are set as tags on the ai_guard span and structured values are added to the ai_guard meta_struct, so new evaluation details reach spans without a tracer change per tag. Tags computed by the tracer and span control keys are never overridden, and malformed payloads are skipped without affecting the evaluation. APPSEC-70768 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codeowners resolved asResolved from the full PR diff against |
Dependency direction analysis
|
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Backend-provided tags are an internal mechanism to add span tags without a tracer release, so no customer-facing release note is needed. APPSEC-70768 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
More details
Reserved span controls and tracer-owned AI Guard fields remain protected, and extra tags are applied before the block decision. Validation used source inspection and isolated checks; the test suite was not run.
🤖 Bits Code Review · Commit 247e644
Type the span as SpanData and write extra tags through _set_attribute, and type the payload as object so the isinstance checks narrow it. APPSEC-70768 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…alue lists (#21252) ## Description Jira: [APPSEC-70768](https://datadoghq.atlassian.net/browse/APPSEC-70768) Follow-up to #21145, which has not been released yet. The AI Guard evaluate response now carries backend-provided span data in two separate attributes, each a list of key/value objects. The keys are dotted (for example `ai_guard.evaluate_id`), which are not valid JSON:API member names, so they cannot be object keys: ```json "extra_tags": [{ "key": "ai_guard.evaluate_id", "value": "<same UUID as data.id>" }], "extra_metastruct": [{ "key": "policy_match", "value": { "rule": "credit_card", "categories": ["pii"] } }] ``` Today the response only sends `ai_guard.evaluate_id` in `extra_tags`; `extra_metastruct` is not sent yet (the entry above only illustrates its shape). - `extra_tags` (string values): each entry is set as a tag on the `ai_guard` span under its key exactly as returned. Non-string values are skipped with a debug log; structured values here no longer go to the meta_struct. - `extra_metastruct` (any value): each non-null entry is added to the `ai_guard` meta_struct under its key exactly as returned, scalars included. - A payload that is not a list, or an entry that is not an object or has a missing, non-string or empty `key`, is skipped with a debug log. On duplicate keys the last entry wins. - Unchanged from #21145: reserved tracer and span-control keys are never overridden, the tracer adds no `ai_guard.` prefix, and a malformed payload never changes the evaluation result or telemetry. No public API change. ## Testing - `tests/aiguard/api/test_extra_tags.py` updated for the list shape: tags on the span, `extra_metastruct` values (structured and scalar) in the meta_struct, a full response document with `ai_guard.evaluate_id` in `extra_tags` only, equal to `data.id`, next to attributes the tracer does not read, duplicate keys, structured `extra_tags` kept out of the meta_struct, reserved keys for both fields, malformed containers and entries, and null payloads for each field. - `ai_guard_api` suite passes on py3.13 (430 tests); `test_extra_tags.py` passes on py3.9. - Lint (format, typing, spelling): clean. ## Risks Low. Neither field has shipped in a release, and both code paths only run when the response contains them. ## Additional Notes No release note: #21145 is unreleased and this does not change customer-facing behaviour. 🤖 Generated with [Claude Code](https://claude.com/claude-code) [APPSEC-70768]: https://datadoghq.atlassian.net/browse/APPSEC-70768?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Co-authored-by: alberto.vara <alberto.vara@datadoghq.com>
Description
Jira: APPSEC-70768
The AI Guard evaluate response can now carry an optional
extra_tagsobject (data.attributes.extra_tags), so new evaluation details reach AI Guard spans without a tracer change per tag.ai_guardspan with the key unchanged. Booleans are written as"true"/"false", matching the other AI Guard tags.ai_guardmeta_struct under their key.ai_guard.action,reason,target,tool_name,blocked,redacted,event, the anomaly-detection copies, and the meta_struct keysmessages,attack_categories,sds,tag_probs.Span.set_tagtreats as span controls are also reserved:manual.keep,manual.drop,service.name,service.version,_dd.measured,version,env.tags=parameter.No public API change.
Testing
tests/aiguard/api/test_extra_tags.py(15 tests): scalars, structured values, reserved tracer and control keys, anomaly-tag reservation, customer-tag override, blocking path, malformed and null payloads, absent field, and an exception raised while applying tags.ai_guard_apisuite passes on py3.9, 3.11 and 3.13;ai_guard_openaisuite passes on py3.13.Risks
Low. The new path only runs when the response contains
extra_tags, and it is wrapped so that it cannot fail the evaluation. The response field name follows the expected backend contract; if that changes, the parsing is a one-line update.Additional Notes
Backend values are not size-capped; they come from decoded JSON returned by the AI Guard service.
🤖 Generated with Claude Code