Skip to content

Commit a18c971

Browse files
authored
feat(llmobs): add prompt tracking telemetry tags for OpenAI (#7106)
* feat(llmobs): add prompt tracking telemetry tags for OpenAI - Add prompt_tracking_source:auto tag for auto-instrumented prompts - Add prompt_multimodal:true tag for prompts with image/file inputs - Add hasMultimodalInputs helper function in utils - Note: LangChain prompt tracking deferred to separate PR * docs :docstrings * refactor(llmobs): rename prompt tracking source tag to instrumentation method - Update prompt tracking source to prompt_tracking_instrumentation_method in OpenAI plugin - Adjust related documentation and test assertions to reflect the new naming convention * refactor tagging logic * refactor constant logic * re-organize constants
1 parent 7ec09e7 commit a18c971

File tree

6 files changed

+76
-10
lines changed

6 files changed

+76
-10
lines changed

packages/dd-trace/src/llmobs/constants/tags.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,11 @@ module.exports = {
3636
CACHE_WRITE_INPUT_TOKENS_METRIC_KEY: 'cache_write_input_tokens',
3737
REASONING_OUTPUT_TOKENS_METRIC_KEY: 'reasoning_output_tokens',
3838

39-
DROPPED_IO_COLLECTION_ERROR: 'dropped_io'
39+
DROPPED_IO_COLLECTION_ERROR: 'dropped_io',
40+
41+
PROMPT_TRACKING_INSTRUMENTATION_METHOD: 'prompt_tracking_instrumentation_method',
42+
PROMPT_MULTIMODAL: 'prompt_multimodal',
43+
INSTRUMENTATION_METHOD_AUTO: 'auto',
44+
INSTRUMENTATION_METHOD_ANNOTATED: 'annotated',
45+
INSTRUMENTATION_METHOD_UNKNOWN: 'unknown'
4046
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict'
2+
3+
const INPUT_TYPE_IMAGE = 'input_image'
4+
const INPUT_TYPE_FILE = 'input_file'
5+
const INPUT_TYPE_TEXT = 'input_text'
6+
7+
const IMAGE_FALLBACK = '[image]'
8+
const FILE_FALLBACK = '[file]'
9+
10+
module.exports = {
11+
INPUT_TYPE_IMAGE,
12+
INPUT_TYPE_FILE,
13+
INPUT_TYPE_TEXT,
14+
IMAGE_FALLBACK,
15+
FILE_FALLBACK
16+
}

packages/dd-trace/src/llmobs/plugins/openai/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
'use strict'
22

33
const LLMObsPlugin = require('../base')
4-
const { extractChatTemplateFromInstructions, normalizePromptVariables, extractTextFromContentItem } = require('./utils')
4+
const {
5+
extractChatTemplateFromInstructions,
6+
normalizePromptVariables,
7+
extractTextFromContentItem,
8+
hasMultimodalInputs
9+
} = require('./utils')
10+
const {
11+
PROMPT_TRACKING_INSTRUMENTATION_METHOD,
12+
PROMPT_MULTIMODAL,
13+
INSTRUMENTATION_METHOD_AUTO
14+
} = require('../../constants/tags')
515

616
const allowedParamKeys = new Set([
717
'max_output_tokens',
@@ -422,6 +432,11 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
422432
variables: normalizedVariables,
423433
chat_template: chatTemplate
424434
})
435+
const tags = { [PROMPT_TRACKING_INSTRUMENTATION_METHOD]: INSTRUMENTATION_METHOD_AUTO }
436+
if (hasMultimodalInputs(inputs.prompt.variables)) {
437+
tags[PROMPT_MULTIMODAL] = 'true'
438+
}
439+
this._tagger.tagSpanTags(span, tags)
425440
}
426441
}
427442

packages/dd-trace/src/llmobs/plugins/openai/utils.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
'use strict'
22

3-
const IMAGE_FALLBACK = '[image]'
4-
const FILE_FALLBACK = '[file]'
3+
const {
4+
INPUT_TYPE_IMAGE,
5+
INPUT_TYPE_FILE,
6+
IMAGE_FALLBACK,
7+
FILE_FALLBACK
8+
} = require('./constants')
59

610
const REGEX_SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g
711

@@ -77,11 +81,11 @@ function extractTextFromContentItem (contentItem) {
7781
}
7882

7983
// For image/file items, extract the actual reference value
80-
if (contentItem.type === 'input_image') {
84+
if (contentItem.type === INPUT_TYPE_IMAGE) {
8185
return contentItem.image_url || contentItem.file_id || IMAGE_FALLBACK
8286
}
8387

84-
if (contentItem.type === 'input_file') {
88+
if (contentItem.type === INPUT_TYPE_FILE) {
8589
return contentItem.file_id || contentItem.file_url || contentItem.filename || FILE_FALLBACK
8690
}
8791

@@ -107,8 +111,16 @@ function normalizePromptVariables (variables) {
107111
)
108112
}
109113

114+
function hasMultimodalInputs (variables) {
115+
if (!variables) return false
116+
return Object.values(variables).some(value =>
117+
value?.type === INPUT_TYPE_IMAGE || value?.type === INPUT_TYPE_FILE
118+
)
119+
}
120+
110121
module.exports = {
111122
extractChatTemplateFromInstructions,
112123
normalizePromptVariables,
113-
extractTextFromContentItem
124+
extractTextFromContentItem,
125+
hasMultimodalInputs
114126
}

packages/dd-trace/test/llmobs/plugins/openai/openaiv4.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ describe('integrations', () => {
919919
'Image reference 2: file-BCuhT1HQ24kmtsuuzF1mh2\n\n' +
920920
'Please provide a comprehensive analysis.'
921921
}
922-
])
922+
], { promptMultimodal: true })
923923
})
924924

925925
it('submits a response span with prompt tracking - mixed input types (url preserved)', async function () {
@@ -969,7 +969,7 @@ describe('integrations', () => {
969969
'Image reference 2: file-BCuhT1HQ24kmtsuuzF1mh2\n\n' +
970970
'Please provide a comprehensive analysis.'
971971
}
972-
])
972+
], { promptMultimodal: true })
973973
})
974974
})
975975

packages/dd-trace/test/llmobs/util.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,18 @@ function getLlmObsSpansFromRequests (llmobsSpanEventsRequests) {
366366
* @param {object} spanEvent - The LLMObs span event to verify
367367
* @param {object} expectedPrompt - Expected prompt metadata (id, version, variables, chat_template)
368368
* @param {Array<{role: string, content: string}>} expectedInputMessages - Expected input messages
369+
* @param {object} options - Optional configuration
370+
* @param {string} options.promptTrackingInstrumentationMethod - Expected prompt tracking instrumentation method
371+
* ('auto' or 'manual'), defaults to 'auto'
372+
* @param {boolean} options.promptMultimodal - Whether prompt contains multimodal inputs,
373+
* defaults to false
369374
*/
370-
function assertPromptTracking (spanEvent, expectedPrompt, expectedInputMessages) {
375+
function assertPromptTracking (
376+
spanEvent,
377+
expectedPrompt,
378+
expectedInputMessages,
379+
{ promptTrackingInstrumentationMethod = 'auto', promptMultimodal = false } = {}
380+
) {
371381
// Verify input messages are captured from instructions
372382
assert(spanEvent.meta.input.messages, 'Input messages should be present')
373383
assert(Array.isArray(spanEvent.meta.input.messages), 'Input messages should be an array')
@@ -385,6 +395,13 @@ function assertPromptTracking (spanEvent, expectedPrompt, expectedInputMessages)
385395
assert.strictEqual(prompt.version, expectedPrompt.version)
386396
assert.deepStrictEqual(prompt.variables, expectedPrompt.variables)
387397
assert.deepStrictEqual(prompt.chat_template, expectedPrompt.chat_template)
398+
399+
// Verify tags
400+
assert(spanEvent.tags, 'Span event should include tags')
401+
assert(spanEvent.tags.includes(`prompt_tracking_instrumentation_method:${promptTrackingInstrumentationMethod}`))
402+
if (promptMultimodal) {
403+
assert(spanEvent.tags.includes('prompt_multimodal:true'))
404+
}
388405
}
389406

390407
module.exports = {

0 commit comments

Comments
 (0)