Skip to content

Commit 50dbd17

Browse files
sabrennerBridgeAR
authored andcommitted
fix(ai): fix infinite recursion when using BedrockChatLanguageModel (#9487)
1 parent 7861b3f commit 50dbd17

3 files changed

Lines changed: 107 additions & 1 deletion

File tree

packages/datadog-instrumentations/src/ai.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,13 @@ for (const hook of getHooks('ai')) {
230230
// generateObject, streamObject)
231231
tracingChannel('orchestrion:ai:resolveLanguageModel').subscribe({
232232
end (ctx) {
233-
wrapModelWithLifecycle(ctx.result)
233+
const model = ctx.arguments[0]
234+
if (typeof model !== 'string' && model !== ctx.result) {
235+
wrapModelWithLifecycle(model)
236+
wrappedModels.add(ctx.result)
237+
} else {
238+
wrapModelWithLifecycle(ctx.result)
239+
}
234240
},
235241
})
236242

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import assert from 'node:assert/strict'
2+
import { webcrypto } from 'node:crypto'
3+
4+
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
5+
import { generateText } from 'ai'
6+
7+
globalThis.crypto ??= webcrypto
8+
9+
const response = {
10+
stopReason: 'end_turn',
11+
output: {
12+
message: {
13+
role: 'assistant',
14+
content: [{ type: 'text', text: 'Reproduction completed.' }],
15+
},
16+
},
17+
usage: {
18+
inputTokens: 3,
19+
outputTokens: 3,
20+
totalTokens: 6,
21+
},
22+
metrics: { latencyMs: 1 },
23+
}
24+
25+
const bedrock = createAmazonBedrock({
26+
region: 'us-east-1',
27+
fetch: () => Promise.resolve(new Response(JSON.stringify(response), {
28+
status: 200,
29+
headers: { 'Content-Type': 'application/json' },
30+
})),
31+
})
32+
33+
const result = await generateText({
34+
model: bedrock('anthropic.claude-3-haiku-20240307-v1:0'),
35+
prompt: 'Run the Bedrock recursion reproduction.',
36+
})
37+
38+
assert.strictEqual(result.text, 'Reproduction completed.')
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict'
2+
3+
const assert = require('node:assert/strict')
4+
const { inspect } = require('node:util')
5+
6+
const {
7+
FakeAgent,
8+
sandboxCwd,
9+
useSandbox,
10+
spawnPluginIntegrationTestProcAndExpectExit,
11+
stopProc,
12+
} = require('../../../../integration-tests/helpers')
13+
const { withVersions } = require('../../../dd-trace/test/setup/mocha')
14+
15+
describe('Bedrock recursion regression', () => {
16+
let agent
17+
let proc
18+
19+
withVersions('ai', 'ai', '>=6.0.0 <7.0.0', aiVersion => {
20+
withVersions('ai', '@ai-sdk/amazon-bedrock', '^3.0.0', bedrockVersion => {
21+
useSandbox([
22+
`ai@${aiVersion}`,
23+
`@ai-sdk/amazon-bedrock@${bedrockVersion}`,
24+
], false, [
25+
'./packages/datadog-plugin-ai/test/integration-test/bedrock-recursion.mjs',
26+
])
27+
28+
beforeEach(async () => {
29+
agent = await new FakeAgent().start()
30+
})
31+
32+
afterEach(async () => {
33+
await stopProc(proc)
34+
await agent.stop()
35+
})
36+
37+
it('does not recurse when AI SDK adapts a v2 Bedrock model', async () => {
38+
const received = agent.assertMessageReceived(({ payload }) => {
39+
assert.ok(Array.isArray(payload), `Expected array, got ${inspect(payload)}`)
40+
assert.ok(payload.flat().some(span => span.name === 'ai.generateText'))
41+
})
42+
43+
proc = await spawnPluginIntegrationTestProcAndExpectExit(
44+
sandboxCwd(),
45+
'bedrock-recursion.mjs',
46+
agent.port,
47+
{
48+
AWS_ACCESS_KEY_ID: 'test-access-key',
49+
AWS_SECRET_ACCESS_KEY: 'test-secret-key',
50+
AWS_REGION: 'us-east-1',
51+
DD_LLMOBS_ENABLED: '1',
52+
DD_LLMOBS_ML_APP: 'test',
53+
NODE_OPTIONS: '--import dd-trace/initialize.mjs',
54+
},
55+
['--stack-size=128']
56+
)
57+
58+
await received
59+
}).timeout(20000)
60+
})
61+
})
62+
})

0 commit comments

Comments
 (0)