diff --git a/packages/dd-trace/src/llmobs/index.js b/packages/dd-trace/src/llmobs/index.js index 9901d96b08..ff53db8669 100644 --- a/packages/dd-trace/src/llmobs/index.js +++ b/packages/dd-trace/src/llmobs/index.js @@ -163,12 +163,30 @@ function handleLLMObsInjection ({ carrier }) { // tags, so it may be undefined here — coalesce before appending. const existing = readDatadogTags(carrier) let tags = existing || '' - if (parentId) tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}` - if (mlApp) tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}` - if (sessionId) tags += `${tags ? ',' : ''}${PROPAGATED_SESSION_ID_KEY}=${sessionId}` - if (sampleRate != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}` - if (samplingDecision != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}` - if (propagatedTraceId != null) tags += `${tags ? ',' : ''}${PROPAGATED_TRACE_ID_KEY}=${propagatedTraceId}` + if (parentId) { + tags = stripTagsetEntry(tags, PROPAGATED_PARENT_ID_KEY) + tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}` + } + if (mlApp) { + tags = stripTagsetEntry(tags, PROPAGATED_ML_APP_KEY) + tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}` + } + if (sessionId) { + tags = stripTagsetEntry(tags, PROPAGATED_SESSION_ID_KEY) + tags += `${tags ? ',' : ''}${PROPAGATED_SESSION_ID_KEY}=${sessionId}` + } + if (sampleRate != null) { + tags = stripTagsetEntry(tags, PROPAGATED_SAMPLE_RATE_KEY) + tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}` + } + if (samplingDecision != null) { + tags = stripTagsetEntry(tags, PROPAGATED_SAMPLING_DECISION_KEY) + tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}` + } + if (propagatedTraceId != null) { + tags = stripTagsetEntry(tags, PROPAGATED_TRACE_ID_KEY) + tags += `${tags ? ',' : ''}${PROPAGATED_TRACE_ID_KEY}=${propagatedTraceId}` + } // When a local agent attribution is resolved, strip any stale upstream pagent entries that // `_injectTags` may have already written into the carrier (it propagates all `_dd.p.*` from // `_trace.tags`). This ensures the downstream sees a consistent id-only or id+name pair diff --git a/packages/dd-trace/test/llmobs/index.spec.js b/packages/dd-trace/test/llmobs/index.spec.js index da3020bfa6..56539ad1e7 100644 --- a/packages/dd-trace/test/llmobs/index.spec.js +++ b/packages/dd-trace/test/llmobs/index.spec.js @@ -283,6 +283,49 @@ describe('module', () => { ) }) + it('does not duplicate _dd.p.llmobs_ml_app when already present in x-datadog-tags', () => { + llmobsModule.enable({ llmobs: { mlApp: 'test', agentlessEnabled: false } }) + + const carrier = { + 'x-datadog-tags': '_dd.p.tid=69fe014200000000,_dd.p.dm=-0,_dd.p.llmobs_ml_app=test', + } + injectCh.publish({ carrier }) + + assert.strictEqual( + carrier['x-datadog-tags'], + '_dd.p.tid=69fe014200000000,_dd.p.dm=-0,_dd.p.llmobs_ml_app=test' + ) + }) + + it('updates existing LLMObs tags in x-datadog-tags without duplicating keys', () => { + llmobsModule.enable({ llmobs: { mlApp: 'test', agentlessEnabled: false } }) + store.span = { + context () { + return { + toSpanId () { + return 'new-parent-id' + }, + } + }, + } + LLMObsTagger.tagMap.set(store.span, { + [SESSION_ID]: 'new-session', + [SAMPLE_RATE]: '0.8', + [SAMPLING_DECISION]: '1', + }) + + const carrier = { + 'x-datadog-tags': + '_dd.p.tid=69fe014200000000,_dd.p.llmobs_parent_id=old-id,_dd.p.llmobs_ml_app=old-app,_dd.p.llmobs_sid=old-session', + } + injectCh.publish({ carrier }) + + assert.strictEqual( + carrier['x-datadog-tags'], + '_dd.p.tid=69fe014200000000,_dd.p.llmobs_parent_id=new-parent-id,_dd.p.llmobs_ml_app=test,_dd.p.llmobs_sid=new-session,_dd.p.llmobs_sr=0.8,_dd.p.llmobs_sd=1' + ) + }) + describe('with DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH=0', () => { let config