Skip to content

Commit 43df1ad

Browse files
committed
test(llmobs): cover google-genai metrics and streaming output formatting
The `extractMetrics` token fallback (derive the total from prompt + candidate counts when `totalTokenCount` is absent) and the streaming special-case branch in `formatOutputMessages` (functionCall / executableCode / codeExecutionResult parts routed through non-streaming formatting) had no direct coverage. Add a unit spec pinning both branches and their token-count siblings.
1 parent c2a5cd1 commit 43df1ad

1 file changed

Lines changed: 65 additions & 0 deletions

File tree

  • packages/dd-trace/test/llmobs/plugins/google-genai
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use strict'
2+
3+
const assert = require('node:assert')
4+
const { describe, it } = require('mocha')
5+
6+
const { extractMetrics, formatOutputMessages } = require('../../../../src/llmobs/plugins/genai/util')
7+
8+
describe('google-genai llmobs util', () => {
9+
describe('extractMetrics', () => {
10+
it('derives totalTokens from prompt and candidate counts when totalTokenCount is absent', () => {
11+
const metrics = extractMetrics({ usageMetadata: { promptTokenCount: 5, candidatesTokenCount: 7 } })
12+
assert.deepStrictEqual(metrics, { inputTokens: 5, outputTokens: 7, totalTokens: 12 })
13+
})
14+
15+
it('prefers totalTokenCount over the derived sum when present', () => {
16+
const metrics = extractMetrics({
17+
usageMetadata: { promptTokenCount: 5, candidatesTokenCount: 7, totalTokenCount: 20 },
18+
})
19+
assert.deepStrictEqual(metrics, { inputTokens: 5, outputTokens: 7, totalTokens: 20 })
20+
})
21+
22+
it('omits totalTokens when no token counts are present', () => {
23+
assert.deepStrictEqual(extractMetrics({ usageMetadata: {} }), {})
24+
})
25+
26+
it('returns no metrics when usageMetadata is missing', () => {
27+
assert.deepStrictEqual(extractMetrics({}), {})
28+
})
29+
})
30+
31+
describe('formatOutputMessages with streaming special cases', () => {
32+
function streamingResponse (part) {
33+
return { candidates: [{ content: { parts: [part] } }] }
34+
}
35+
36+
it('routes a streaming functionCall part through non-streaming formatting', () => {
37+
const part = { functionCall: { name: 'getWeather', args: { city: 'NYC' }, id: 'call_1' } }
38+
assert.deepStrictEqual(formatOutputMessages(streamingResponse(part), true), [{
39+
role: 'assistant',
40+
toolCalls: [{ name: 'getWeather', arguments: { city: 'NYC' }, toolId: 'call_1', type: 'function_call' }],
41+
}])
42+
})
43+
44+
it('routes a streaming executableCode part through non-streaming formatting', () => {
45+
const part = { executableCode: { language: 'PYTHON', code: 'print(1)' } }
46+
assert.deepStrictEqual(formatOutputMessages(streamingResponse(part), true), [{
47+
role: 'assistant',
48+
content: JSON.stringify({ language: 'PYTHON', code: 'print(1)' }),
49+
}])
50+
})
51+
52+
it('routes a streaming codeExecutionResult part through non-streaming formatting', () => {
53+
const part = { codeExecutionResult: { outcome: 'OUTCOME_OK', output: '1\n' } }
54+
assert.deepStrictEqual(formatOutputMessages(streamingResponse(part), true), [{
55+
role: 'assistant',
56+
content: JSON.stringify({ outcome: 'OUTCOME_OK', output: '1\n' }),
57+
}])
58+
})
59+
60+
it('accumulates plain text streaming parts by role', () => {
61+
const response = { candidates: [{ content: { parts: [{ text: 'Hello, ' }, { text: 'world!' }] } }] }
62+
assert.deepStrictEqual(formatOutputMessages(response, true), [{ role: 'assistant', content: 'Hello, world!' }])
63+
})
64+
})
65+
})

0 commit comments

Comments
 (0)