-
Notifications
You must be signed in to change notification settings - Fork 340
feat(ai): add vercel ai integration #5858
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sabrenner
wants to merge
56
commits into
master
Choose a base branch
from
sabrenner/vercel-ai-sdk-integration
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+4,099
−4
Open
Changes from 39 commits
Commits
Show all changes
56 commits
Select commit
Hold shift + click to select a range
94a2b68
add vercel ai integration with otel processing
sabrenner 0ed7d4c
add some typedocs and comments
sabrenner 233beb8
fix tagger test
sabrenner 90f4f13
rename to 'ai'
sabrenner a4014cc
try doing with a custom tracer
sabrenner 407d6dc
change up implementation slightly
sabrenner 16c3d28
codeowners
sabrenner fbff2a3
undo id changes
sabrenner d963226
get rid of otel span start/end publishes
sabrenner 067c596
revert llmobs tagger test change
sabrenner 24f37a8
add better noop default tracer and esm support
sabrenner 563d56f
delete util file
sabrenner 7e906a2
Merge branch 'master' of github.com:DataDog/dd-trace-js into sabrenne…
sabrenner 47056b9
add initial test skeleton
sabrenner 2da477f
fix duplicate wrapping
sabrenner a43db02
Merge branch 'master' of github.com:DataDog/dd-trace-js into sabrenne…
sabrenner cc9b031
simplify patching
sabrenner 2015808
apm tests
sabrenner 2ce1798
Merge branch 'master' of github.com:DataDog/dd-trace-js into sabrenne…
sabrenner 5a5a23c
Merge branch 'master' of github.com:DataDog/dd-trace-js into sabrenne…
sabrenner 16c5da4
write some tests
sabrenner 0c2df21
add rest of llmobs tests
sabrenner f47cc43
add ci job
sabrenner 85ec1c8
fix node version and import issue with check and externally defined v…
sabrenner 39f3eb9
fix metadata tagging
sabrenner c403681
handle tool rolls
sabrenner 15bcb79
remove import
sabrenner de7c0fa
add default return and docstring for formatMessage
sabrenner ffb75c1
Merge branch 'master' of github.com:DataDog/dd-trace-js into sabrenne…
sabrenner c60316b
fix tool message tests
sabrenner 1824313
add model name and provider tags to apm tracing
sabrenner 95ebae1
some self review
sabrenner d19e31b
address some review comments
sabrenner c03fc82
do not stub for tests, instead use dummy test agent
sabrenner 20c3c63
move cassettes to local directory to fix tests
sabrenner 0f1acf6
configurable flush interval for tests
sabrenner 72eca83
use separate image for ai tests that have local cassettes attached
sabrenner ac4071c
use different port
sabrenner 39ccf68
move test flush interval back local
sabrenner 32d75a6
change in esm test
sabrenner d1fbc64
Revert "move test flush interval back local"
sabrenner b705691
Revert "use different port"
sabrenner b28bd92
Revert "use separate image for ai tests that have local cassettes att…
sabrenner bec9312
Revert "move cassettes to local directory to fix tests"
sabrenner b642965
Revert "change in esm test"
sabrenner bbbfffb
remove env var from llmobs workflow
sabrenner 85abd5d
fix type hint for test util
sabrenner 8d1662e
Merge branch 'master' of github.com:DataDog/dd-trace-js into sabrenne…
sabrenner 2bd92ee
add test cassettes
sabrenner f9cfc5c
re-trigger ci
sabrenner 9fc3d13
more review fixes
sabrenner 5f6bc14
Merge branch 'master' into sabrenner/vercel-ai-sdk-integration
sabrenner 25a83e5
try removing configuration
sabrenner 8036e15
Merge branch 'sabrenner/vercel-ai-sdk-integration' of github.com:Data…
sabrenner 3d428da
revert supported config undoing
sabrenner 2938177
Merge branch 'master' into sabrenner/vercel-ai-sdk-integration
sabrenner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
name: "Get APM Test Agent Logs" | ||
description: "Get the APM Test Agent logs from the test run, with LLM Observability cassettes attached." | ||
inputs: | ||
container-id: | ||
description: "ID of the Docker Container to get logs from (optional)" | ||
required: false | ||
suffix: | ||
description: "suffix of the artifact file name" | ||
required: false | ||
runs: | ||
using: composite | ||
steps: | ||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
- name: Create Artifacts Directories | ||
run: | | ||
mkdir -p "./artifacts/logs" | ||
mkdir -p "./artifacts/supported-integrations" | ||
shell: bash | ||
- name: Save Test Agent Logs | ||
id: save_logs | ||
if: runner.debug == '1' # only create test agent log artifacts if the github action has been re-run with debug mode | ||
run: | | ||
if [ -n "${{inputs.container-id}}" ]; then | ||
docker logs "${{inputs.container-id}}" > "artifacts/logs/test_agent_logs_${{ inputs.suffix }}.txt" | ||
else | ||
docker compose logs testagent-llmobs-vcr > "artifacts/logs/test_agent_logs_${{ inputs.suffix }}.txt" | ||
fi | ||
shell: bash | ||
- name: Archive Test Agent Logs | ||
if: runner.debug == '1' # only create test agent log artifacts if the github action has been re-run with debug mode | ||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
with: | ||
name: "test_agent_logs_${{ inputs.suffix }}" | ||
path: "./artifacts/logs" | ||
- name: Get Tested Integrations from Test Agent | ||
run: | | ||
# make temporary files to save response data to | ||
response=$(mktemp) && headers=$(mktemp) | ||
|
||
# get tested integrations | ||
curl -o "$response" -D "$headers" http://127.0.0.1:10126/test/integrations/tested_versions | ||
|
||
# get filename representing the name of the tested integration from headers | ||
filename=$(awk -F': ' '/file-name/{print $2}' "$headers" | tr -d '\r\n') | ||
|
||
# copy data to final file and remove temp files | ||
mv "$response" "artifacts/supported-integrations/${filename}_supported_versions.csv" | ||
rm "$headers" | ||
shell: bash | ||
- name: Archive Test Agent Tested Versions Artifacts | ||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
with: | ||
name: supported-integrations-${{ inputs.suffix }} | ||
path: ./artifacts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
name: "Start the APM Test Agent" | ||
description: "Starts the APM Test Agent image with environment, with LLM Observability cassettes attached." | ||
runs: | ||
using: composite | ||
steps: | ||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
- run: docker compose up -d testagent-llmobs-vcr || docker compose up -d testagent-llmobs-vcr | ||
shell: bash |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
'use strict' | ||
|
||
const { addHook } = require('./helpers/instrument') | ||
const shimmer = require('../../datadog-shimmer') | ||
|
||
const { channel, tracingChannel } = require('dc-polyfill') | ||
const toolCreationChannel = channel('dd-trace:vercel-ai:tool') | ||
|
||
const TRACED_FUNCTIONS = { | ||
generateText: wrapWithTracer, | ||
streamText: wrapWithTracer, | ||
generateObject: wrapWithTracer, | ||
streamObject: wrapWithTracer, | ||
embed: wrapWithTracer, | ||
embedMany: wrapWithTracer, | ||
tool: wrapTool | ||
} | ||
|
||
const vercelAiTracingChannel = tracingChannel('dd-trace:vercel-ai') | ||
const vercelAiSpanSetAttributesChannel = channel('dd-trace:vercel-ai:span:setAttributes') | ||
|
||
const noopTracer = { | ||
startActiveSpan () { | ||
const fn = arguments[arguments.length - 1] | ||
|
||
const span = { | ||
spanContext () { return { traceId: '', spanId: '', traceFlags: 0 } }, | ||
setAttribute () { return this }, | ||
setAttributes () { return this }, | ||
addEvent () { return this }, | ||
addLink () { return this }, | ||
addLinks () { return this }, | ||
setStatus () { return this }, | ||
updateName () { return this }, | ||
end () { return this }, | ||
isRecording () { return false }, | ||
recordException () { return this } | ||
} | ||
|
||
return fn(span) | ||
} | ||
} | ||
|
||
function wrapTracer (tracer) { | ||
if (Object.hasOwn(tracer, Symbol.for('_dd.wrapped'))) return | ||
|
||
shimmer.wrap(tracer, 'startActiveSpan', function (startActiveSpan) { | ||
return function () { | ||
const name = arguments[0] | ||
const options = arguments.length > 2 ? (arguments[1] ?? {}) : {} // startActiveSpan(name, fn) | ||
const cb = arguments[arguments.length - 1] | ||
|
||
const ctx = { | ||
name, | ||
attributes: options.attributes ?? {} | ||
} | ||
|
||
arguments[arguments.length - 1] = shimmer.wrapFunction(cb, function (originalCb) { | ||
return function (span) { | ||
shimmer.wrap(span, 'end', function (spanEnd) { | ||
return function () { | ||
vercelAiTracingChannel.asyncEnd.publish(ctx) | ||
return spanEnd.apply(this, arguments) | ||
} | ||
}) | ||
|
||
shimmer.wrap(span, 'setAttributes', function (setAttributes) { | ||
return function (attributes) { | ||
vercelAiSpanSetAttributesChannel.publish({ ctx, attributes }) | ||
return setAttributes.apply(this, arguments) | ||
} | ||
}) | ||
|
||
shimmer.wrap(span, 'recordException', function (recordException) { | ||
return function (exception) { | ||
ctx.error = exception | ||
vercelAiTracingChannel.error.publish(ctx) | ||
return recordException.apply(this, arguments) | ||
} | ||
}) | ||
|
||
return originalCb.apply(this, arguments) | ||
} | ||
}) | ||
|
||
return vercelAiTracingChannel.start.runStores(ctx, () => { | ||
const result = startActiveSpan.apply(this, arguments) | ||
vercelAiTracingChannel.end.publish(ctx) | ||
return result | ||
}) | ||
} | ||
}) | ||
|
||
Object.defineProperty(tracer, Symbol.for('_dd.wrapped'), { value: true }) | ||
} | ||
|
||
function wrapWithTracer (fn) { | ||
return function () { | ||
const options = arguments[0] | ||
|
||
options.experimental_telemetry ??= { isEnabled: true, tracer: noopTracer } | ||
rochdev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
wrapTracer(options.experimental_telemetry.tracer) | ||
|
||
return fn.apply(this, arguments) | ||
} | ||
} | ||
|
||
function wrapTool (tool) { | ||
return function () { | ||
const args = arguments[0] | ||
toolCreationChannel.publish(args) | ||
|
||
return tool.apply(this, arguments) | ||
} | ||
} | ||
|
||
// CJS exports | ||
addHook({ | ||
name: 'ai', | ||
versions: ['>=4.0.0'], | ||
}, exports => { | ||
for (const [fnName, patchingFn] of Object.entries(TRACED_FUNCTIONS)) { | ||
exports = shimmer.wrap(exports, fnName, patchingFn, { replaceGetter: true }) | ||
} | ||
|
||
return exports | ||
}) | ||
|
||
// ESM exports | ||
addHook({ | ||
name: 'ai', | ||
versions: ['>=4.0.0'], | ||
file: 'dist/index.mjs' | ||
}, exports => { | ||
for (const [fnName, patchingFn] of Object.entries(TRACED_FUNCTIONS)) { | ||
exports = shimmer.wrap(exports, fnName, patchingFn, { replaceGetter: true }) | ||
} | ||
|
||
return exports | ||
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
'use strict' | ||
|
||
const CompositePlugin = require('../../dd-trace/src/plugins/composite') | ||
const VercelAILLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/ai') | ||
const VercelAITracingPlugin = require('./tracing') | ||
|
||
class VercelAIPlugin extends CompositePlugin { | ||
static get id () { return 'ai' } | ||
static get plugins () { | ||
return { | ||
llmobs: VercelAILLMObsPlugin, | ||
tracing: VercelAITracingPlugin | ||
} | ||
} | ||
} | ||
|
||
module.exports = VercelAIPlugin |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
'use strict' | ||
|
||
const TracingPlugin = require('../../dd-trace/src/plugins/tracing') | ||
const { getModelProvider } = require('./utils') | ||
|
||
class VercelAITracingPlugin extends TracingPlugin { | ||
static get id () { return 'ai' } | ||
static get prefix () { return 'tracing:dd-trace:vercel-ai' } | ||
|
||
bindStart (ctx) { | ||
const attributes = ctx.attributes | ||
|
||
const model = attributes['ai.model.id'] | ||
const modelProvider = getModelProvider(attributes) | ||
|
||
this.startSpan(ctx.name, { | ||
meta: { | ||
'resource.name': ctx.name, | ||
'ai.request.model': model, | ||
'ai.request.model_provider': modelProvider | ||
} | ||
}, ctx) | ||
|
||
return ctx.currentStore | ||
} | ||
|
||
asyncEnd (ctx) { | ||
const span = ctx.currentStore?.span | ||
span?.finish() | ||
} | ||
} | ||
|
||
module.exports = VercelAITracingPlugin |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
'use strict' | ||
|
||
const { parseModelId } = require('../../datadog-plugin-aws-sdk/src/services/bedrockruntime/utils') | ||
|
||
/** | ||
* Get the model provider from the span tags or attributes. | ||
* This is normalized to LLM Observability model provider standards. | ||
* | ||
* @param {Record<string, string>} tags | ||
* @returns {string} | ||
*/ | ||
function getModelProvider (tags) { | ||
const modelProviderTag = tags['ai.model.provider'] | ||
const providerParts = modelProviderTag?.split('.') | ||
const provider = providerParts?.[0] | ||
|
||
if (provider === 'amazon-bedrock') { | ||
const modelId = tags['ai.model.id'] | ||
const model = modelId && parseModelId(modelId) | ||
return model?.modelProvider ?? provider | ||
} | ||
|
||
return provider | ||
} | ||
|
||
module.exports = { | ||
getModelProvider | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.