feat(aiguard): evaluating anthropic calls with AI guard automatically - #9563
feat(aiguard): evaluating anthropic calls with AI guard automatically#9563IlyasShabi wants to merge 5 commits into
Conversation
…#9219) * feat(aiguard): evaluating anthropic calls with AI guard automatically
Overall package sizeSelf size: 7.61 MB Dependency sizes| name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.3.2 | 124.41 kB | 440.65 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | dc-polyfill | 0.1.11 | 25.74 kB | 25.74 kB |🤖 This report was automatically generated by heaviest-objects-in-the-universe |
🎉 All green!🧪 All tests passed 🔄 Datadog retried 1 test - 1 passed on retry 🎯 Code Coverage (details) 🔗 Commit SHA: cabca2b | Docs | Datadog PR Page | Give us feedback! |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #9563 +/- ##
==========================================
+ Coverage 98.50% 98.51% +0.01%
==========================================
Files 952 956 +4
Lines 131108 132480 +1372
Branches 11128 11425 +297
==========================================
+ Hits 129145 130515 +1370
- Misses 1963 1965 +2 Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 199b4e4c53
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
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".
BenchmarksBenchmark execution time: 2026-07-31 08:51:16 Comparing candidate commit cabca2b in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 2325 metrics, 33 unstable metrics.
|
|
It looks like this is a WIP so I converted it into a draft. |
|
@tlhunter I enabled it for codex reviews requests |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d743fc770d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
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".
d743fc7 to
5bf825e
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! 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". |
|
@IlyasShabi TIL Codex doesn't review drafts. Maybe we need a better flow then; ideally Codex can perform a review without having GitHub ping folks on slack for a review. |
|
Well we can use #9563 (comment) to trigger a review even on draft :D |
46d3c0e to
459ca4e
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 459ca4ee6b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cabca2bc9b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
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".
| try { | ||
| return finishResult(ctx, JSON.parse(body), getVerdict, body) | ||
| } catch { | ||
| finish(ctx) |
There was a problem hiding this comment.
Avoid finalizing nested text reads as success
When asResponse() returns a node-fetch Response, response.json() delegates to this.text(). Because both readers are wrapped, malformed JSON enters the inner text wrapper, this catch calls finish(ctx) as a success, and only afterward the outer json() rejects; its catch skips error publication because ctx.finished is already true. This records anthropic.request as successful even though the reader failed. Avoid finalizing from the nested text call, and cover invalid JSON through the real node-fetch response path.
AGENTS.md reference: AGENTS.md:L128-L129
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This is not a bug on the supported node-fetch. node-fetch 2.7 consumes the body directly; it never calls this.text().
| env: | ||
| PLUGINS: anthropic|anthropic-lifecycle | ||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
There was a problem hiding this comment.
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
BridgeAR
left a comment
There was a problem hiding this comment.
Just blocking since it got an approval and I think the comments are still important
| const input = { messages: options.messages } | ||
| if (options.system !== undefined) input.system = options.system | ||
|
|
||
| const snapshot = [...args] |
There was a problem hiding this comment.
I think we should move this into the try to make sure copying works, since it is not guaranteed to be an iterable.
There was a problem hiding this comment.
args is guaranteed to be an array because it comes directly from wrapCreate, also make sense to keep it outside and reserve the try/catch for structuredClone failure only.
| */ | ||
| function snapshotLifecycleArgs (args) { | ||
| const options = args[0] | ||
| if (!options || typeof options !== 'object') return args |
There was a problem hiding this comment.
This keeps the actual arguments and I think we should skip inspection if something is wrong.
There was a problem hiding this comment.
This will not cause an evaluation, when we have no valid messages array we're going to skip evaluations
| .then(response => { | ||
| // Raw output evaluation supports the common json() and text() readers only. | ||
| if (!stream && | ||
| (anthropicTracingChannel.start.hasSubscribers || | ||
| afterVerdict || | ||
| messagesAfterChannel.hasSubscribers) && | ||
| wrappedResponse !== response) { | ||
| wrappedResponse = response | ||
| wrapResponseReader(response, 'json', ctx, getAfterVerdict) | ||
| wrapResponseReader(response, 'text', ctx, getAfterVerdict) | ||
| } | ||
|
|
||
| if (afterVerdict) return afterVerdict.then(() => response) | ||
| return response | ||
| }) |
There was a problem hiding this comment.
| .then(response => { | |
| // Raw output evaluation supports the common json() and text() readers only. | |
| if (!stream && | |
| (anthropicTracingChannel.start.hasSubscribers || | |
| afterVerdict || | |
| messagesAfterChannel.hasSubscribers) && | |
| wrappedResponse !== response) { | |
| wrappedResponse = response | |
| wrapResponseReader(response, 'json', ctx, getAfterVerdict) | |
| wrapResponseReader(response, 'text', ctx, getAfterVerdict) | |
| } | |
| if (afterVerdict) return afterVerdict.then(() => response) | |
| return response | |
| }) |
I believe this is the issue about asResponse being complained about by the AI findings.
What about removing this for now so that we can land partial support right away and land support for this afterwards as follow-up? :)
There was a problem hiding this comment.
I added asResponse support in the initial PR and addressed some AI reviews such as #9492 (comment) as you noted.
For now, Im adding support only the common json() and text() and plan to add support for the remaining methods in a follow-up PR. If you think it's too large to review, I can limit it to parse() and move asResponse() to a separate PR.
What does this PR do?
This PR adds automatic AI Guard integration for the Anthropic SDK via auto-instrumentation. Inside the wrapped
parse()andasResponse()calls, we evaluate the input (before the model runs) and the input/output (after the model responds) and hand them off to AI Guard for evaluation. If AI Guard denies, the call is aborted before the response reaches user.Adds a new integration file
packages/dd-trace/src/aiguard/integrations/anthropic.jsthat subscribes to the Anthropic lifecycle channels and normalizes anthropicmessages into AI Guard's style.Streaming is out of scope.
Motivation
AI Guard already integrates with the OpenAI SDK and Vercel AI SDK. Anthropic is also a major LLM provider in the tracer supported set without AI Guard coverage.
Additional Notes
JIRA: https://datadoghq.atlassian.net/browse/APPSEC-62251