weflayr 0.23.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{api → src/api}/generated/facade.gen.d.ts +163 -12
- package/dist/{api → src/api}/generated/facade.gen.js +10 -2
- package/dist/src/api/generated/index.d.ts +2 -0
- package/dist/{api → src/api}/generated/index.js +9 -1
- package/dist/{api → src/api}/generated/sdk.gen.d.ts +35 -3
- package/dist/{api → src/api}/generated/sdk.gen.js +88 -4
- package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +692 -116
- package/dist/{api → src/api}/makeEndpoint.js +1 -3
- package/dist/src/auto-instrument.d.ts +42 -0
- package/dist/src/auto-instrument.js +156 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +49 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.d.ts +20 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +114 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.js +92 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +141 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.js +17 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +50 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +39 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +97 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +102 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.js +91 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +21 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +54 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.d.ts +19 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.js +81 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +126 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.d.ts +17 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.js +85 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +68 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.js +77 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +88 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.js +18 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.d.ts +31 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.js +106 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.js +75 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +44 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.d.ts +28 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.js +209 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.js +28 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.js +154 -0
- package/dist/src/instrumentation/google/get_cache_telemetry.d.ts +16 -0
- package/dist/src/instrumentation/google/get_cache_telemetry.js +38 -0
- package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_embeddings_telemetry.js +85 -0
- package/dist/src/instrumentation/google/get_provider_name.d.ts +11 -0
- package/{src → dist/src}/instrumentation/google/get_provider_name.js +11 -12
- package/{src/instrumentation/google/get_reasoning_telemetry.js → dist/src/instrumentation/google/get_reasoning_telemetry.d.ts} +1 -11
- package/dist/src/instrumentation/google/get_reasoning_telemetry.js +21 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.js +19 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.js +98 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.js +123 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +148 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +72 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +91 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +86 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.d.ts +21 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.js +74 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +20 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +76 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +3 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.js +54 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +66 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +24 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +84 -0
- package/dist/src/logger.d.ts +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/otel/exporter.d.ts +19 -0
- package/dist/src/otel/exporter.js +94 -0
- package/dist/src/otel/propagation.d.ts +46 -0
- package/dist/src/otel/propagation.js +110 -0
- package/dist/src/otel/span-filter.d.ts +3 -0
- package/dist/src/otel/span-filter.js +61 -0
- package/dist/src/otel/span-processor.d.ts +55 -0
- package/dist/src/otel/span-processor.js +169 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +12 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.js +88 -0
- package/dist/src/setup.d.ts +50 -0
- package/dist/src/setup.js +155 -0
- package/package.json +11 -5
- package/.prettierignore +0 -2
- package/.prettierrc.json +0 -7
- package/LICENSE-MIT +0 -21
- package/NOTICE +0 -12
- package/dist/api/generated/index.d.ts +0 -2
- package/dist/api/generated/types.gen.d.ts +0 -749
- package/eslint.config.mjs +0 -36
- package/index.d.ts +0 -167
- package/openapi-ts.config.mjs +0 -9
- package/scripts/generate_facade.mjs +0 -86
- package/src/api/caseConversion.ts +0 -58
- package/src/api/generated/client/client.gen.ts +0 -277
- package/src/api/generated/client/index.ts +0 -27
- package/src/api/generated/client/types.gen.ts +0 -218
- package/src/api/generated/client/utils.gen.ts +0 -316
- package/src/api/generated/client.gen.ts +0 -16
- package/src/api/generated/core/auth.gen.ts +0 -48
- package/src/api/generated/core/bodySerializer.gen.ts +0 -82
- package/src/api/generated/core/params.gen.ts +0 -178
- package/src/api/generated/core/pathSerializer.gen.ts +0 -171
- package/src/api/generated/core/queryKeySerializer.gen.ts +0 -117
- package/src/api/generated/core/serverSentEvents.gen.ts +0 -242
- package/src/api/generated/core/types.gen.ts +0 -110
- package/src/api/generated/core/utils.gen.ts +0 -140
- package/src/api/generated/facade.gen.ts +0 -68
- package/src/api/generated/index.ts +0 -4
- package/src/api/generated/sdk.gen.ts +0 -121
- package/src/api/index.ts +0 -45
- package/src/api/makeEndpoint.ts +0 -54
- package/src/auto-instrument.js +0 -171
- package/src/index.js +0 -19
- package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -107
- package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
- package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -50
- package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -37
- package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -104
- package/src/instrumentation/bedrock/get_input_message_content_telemetry.js +0 -94
- package/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +0 -87
- package/src/instrumentation/bedrock/set_telemetry_attributes.js +0 -36
- package/src/instrumentation/cohere/get_chat_telemetry.js +0 -88
- package/src/instrumentation/cohere/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/cohere/set_telemetry_attributes.js +0 -32
- package/src/instrumentation/elevenlabs/full_instrumentation.js +0 -236
- package/src/instrumentation/google/get_audio_telemetry.js +0 -30
- package/src/instrumentation/google/get_cache_telemetry.js +0 -25
- package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
- package/src/instrumentation/google/set_telemetry_attributes.js +0 -98
- package/src/instrumentation/openai/get_audio_telemetry.js +0 -149
- package/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +0 -71
- package/src/instrumentation/openai/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/openai/get_streaming_usage.js +0 -81
- package/src/instrumentation/openai/set_telemetry_attributes.js +0 -39
- package/src/instrumentation/vercel/full_instrumentation.js +0 -44
- package/src/logger.js +0 -8
- package/src/otel/exporter.js +0 -108
- package/src/otel/propagation.js +0 -133
- package/src/otel/span-filter.js +0 -69
- package/src/otel/span-processor.js +0 -162
- package/src/otel/vercel-ai-sdk-span-filter.js +0 -79
- package/src/setup.js +0 -167
- package/tests/api/index.test.js +0 -437
- package/tests/index.test.js +0 -824
- package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -158
- package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
- package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -82
- package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -102
- package/tests/instrumentation/bedrock/get_input_message_content_telemetry.test.js +0 -89
- package/tests/instrumentation/bedrock/get_streaming_output_message_telemetry.test.js +0 -87
- package/tests/instrumentation/cohere/get_chat_telemetry.test.js +0 -148
- package/tests/instrumentation/cohere/get_embeddings_telemetry.test.js +0 -132
- package/tests/instrumentation/elevenlabs/full_instrumentation.test.js +0 -258
- package/tests/instrumentation/google/get_embeddings_telemetry.test.js +0 -128
- package/tests/instrumentation/google/get_reasoning_telemetry.test.js +0 -41
- package/tests/instrumentation/google/set_telemetry_attributes.test.js +0 -219
- package/tests/instrumentation/openai/get_audio_telemetry.test.js +0 -170
- package/tests/instrumentation/openai/get_cache_and_reasoning_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_embeddings_telemetry.test.js +0 -145
- package/tests/instrumentation/openai/get_streaming_usage.test.js +0 -105
- package/tests/instrumentation/vercel/full_instrumentation.test.js +0 -16
- package/tests/otel/vercel-ai-sdk-span-filter.test.js +0 -159
- package/tsconfig.json +0 -18
- /package/dist/{api → src/api}/caseConversion.d.ts +0 -0
- /package/dist/{api → src/api}/caseConversion.js +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/index.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/index.js +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/types.gen.js +0 -0
- /package/dist/{api → src/api}/index.d.ts +0 -0
- /package/dist/{api → src/api}/index.js +0 -0
- /package/dist/{api → src/api}/makeEndpoint.d.ts +0 -0
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Anthropic cached-prompt token complement. It wraps the primary
|
|
4
|
-
// instrumentor's `_endSpan({ span, type, result })` to stamp
|
|
5
|
-
// gen_ai.usage.cache_read_input_tokens / cache_creation_input_tokens from the
|
|
6
|
-
// response's `usage.cache_read_input_tokens` / `cache_creation_input_tokens`.
|
|
7
|
-
|
|
8
|
-
const test = require('node:test');
|
|
9
|
-
const assert = require('node:assert/strict');
|
|
10
|
-
|
|
11
|
-
const {
|
|
12
|
-
AnthropicPromptCachingInstrumentor,
|
|
13
|
-
} = require('../../../src/instrumentation/anthropic/get_cache_and_reasoning_telemetry');
|
|
14
|
-
|
|
15
|
-
function fakeSpan() {
|
|
16
|
-
const attrs = {};
|
|
17
|
-
return {
|
|
18
|
-
attrs,
|
|
19
|
-
isRecording: () => true,
|
|
20
|
-
setAttribute(key, value) {
|
|
21
|
-
attrs[key] = value;
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
test('stamps cache write and still calls the original _endSpan', () => {
|
|
27
|
-
const calls = [];
|
|
28
|
-
const instrumentation = {
|
|
29
|
-
_endSpan(args) {
|
|
30
|
-
calls.push(args);
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
new AnthropicPromptCachingInstrumentor().instrument(instrumentation);
|
|
34
|
-
|
|
35
|
-
const span = fakeSpan();
|
|
36
|
-
instrumentation._endSpan({
|
|
37
|
-
span,
|
|
38
|
-
type: 'completion',
|
|
39
|
-
result: {
|
|
40
|
-
usage: { input_tokens: 10, cache_creation_input_tokens: 5269, cache_read_input_tokens: 0 },
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
assert.equal(span.attrs['gen_ai.usage.cache_creation_input_tokens'], 5269);
|
|
45
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], undefined);
|
|
46
|
-
assert.equal(calls.length, 1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('stamps cache read on a warm call', () => {
|
|
50
|
-
const instrumentation = { _endSpan() {} };
|
|
51
|
-
new AnthropicPromptCachingInstrumentor().instrument(instrumentation);
|
|
52
|
-
|
|
53
|
-
const span = fakeSpan();
|
|
54
|
-
instrumentation._endSpan({
|
|
55
|
-
span,
|
|
56
|
-
type: 'completion',
|
|
57
|
-
result: {
|
|
58
|
-
usage: { input_tokens: 10, cache_creation_input_tokens: 0, cache_read_input_tokens: 5271 },
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], 5271);
|
|
63
|
-
assert.equal(span.attrs['gen_ai.usage.cache_creation_input_tokens'], undefined);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('sets nothing for an uncached call', () => {
|
|
67
|
-
const instrumentation = { _endSpan() {} };
|
|
68
|
-
new AnthropicPromptCachingInstrumentor().instrument(instrumentation);
|
|
69
|
-
|
|
70
|
-
const span = fakeSpan();
|
|
71
|
-
instrumentation._endSpan({ span, type: 'completion', result: { usage: { input_tokens: 13 } } });
|
|
72
|
-
|
|
73
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], undefined);
|
|
74
|
-
assert.equal(span.attrs['gen_ai.usage.cache_creation_input_tokens'], undefined);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('stamps extended thinking tokens from output_tokens_details', () => {
|
|
78
|
-
const instrumentation = { _endSpan() {} };
|
|
79
|
-
new AnthropicPromptCachingInstrumentor().instrument(instrumentation);
|
|
80
|
-
|
|
81
|
-
const span = fakeSpan();
|
|
82
|
-
instrumentation._endSpan({
|
|
83
|
-
span,
|
|
84
|
-
type: 'completion',
|
|
85
|
-
result: { usage: { input_tokens: 10, output_tokens_details: { thinking_tokens: 150 } } },
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
assert.equal(span.attrs['gen_ai.usage.output_tokens_details.thinking_tokens'], 150);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('stamps cache + raw input from the streamed message_start usage', async () => {
|
|
92
|
-
const instrumentation = {
|
|
93
|
-
_endSpan() {},
|
|
94
|
-
_streamingWrapPromise() {
|
|
95
|
-
return (async function* () {
|
|
96
|
-
yield {
|
|
97
|
-
type: 'message_start',
|
|
98
|
-
message: {
|
|
99
|
-
usage: {
|
|
100
|
-
input_tokens: 10,
|
|
101
|
-
cache_read_input_tokens: 5270,
|
|
102
|
-
cache_creation_input_tokens: 0,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
yield { type: 'content_block_delta', delta: { text: 'OK' } };
|
|
107
|
-
})();
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
new AnthropicPromptCachingInstrumentor().instrument(instrumentation);
|
|
111
|
-
|
|
112
|
-
const span = fakeSpan();
|
|
113
|
-
for await (const _chunk of instrumentation._streamingWrapPromise('client', 'mod', { span })) {
|
|
114
|
-
void _chunk;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], 5270);
|
|
118
|
-
assert.equal(span.attrs['gen_ai.usage.input_tokens'], 10);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('re-yields every stream chunk unchanged — no telemetry collision with the payload', async () => {
|
|
122
|
-
const emitted = [
|
|
123
|
-
{
|
|
124
|
-
type: 'message_start',
|
|
125
|
-
message: { usage: { input_tokens: 10, cache_read_input_tokens: 5270 } },
|
|
126
|
-
},
|
|
127
|
-
{ type: 'content_block_delta', delta: { text: 'O' } },
|
|
128
|
-
{ type: 'content_block_delta', delta: { text: 'K' } },
|
|
129
|
-
];
|
|
130
|
-
const instrumentation = {
|
|
131
|
-
_endSpan() {},
|
|
132
|
-
_streamingWrapPromise() {
|
|
133
|
-
return (async function* () {
|
|
134
|
-
for (const chunk of emitted) yield chunk;
|
|
135
|
-
})();
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
new AnthropicPromptCachingInstrumentor().instrument(instrumentation);
|
|
139
|
-
|
|
140
|
-
const span = fakeSpan();
|
|
141
|
-
const collected = [];
|
|
142
|
-
for await (const chunk of instrumentation._streamingWrapPromise('client', 'mod', { span })) {
|
|
143
|
-
collected.push(chunk);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Same count, order, and object identity: observeCache re-yields each chunk by
|
|
147
|
-
// reference without copying or mutating it.
|
|
148
|
-
assert.equal(collected.length, emitted.length);
|
|
149
|
-
collected.forEach((chunk, index) => assert.equal(chunk, emitted[index]));
|
|
150
|
-
// …while still stamping cache + raw input off the message_start chunk.
|
|
151
|
-
assert.equal(span.attrs['gen_ai.usage.input_tokens'], 10);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('is a no-op when the instrumentor has no _endSpan', () => {
|
|
155
|
-
const instrumentation = {};
|
|
156
|
-
new AnthropicPromptCachingInstrumentor().instrument(instrumentation);
|
|
157
|
-
assert.equal(instrumentation._endSpan, undefined);
|
|
158
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Anthropic streamed stop-reason complement.
|
|
4
|
-
//
|
|
5
|
-
// The traceloop instrumentation instance is stubbed the way the real one works:
|
|
6
|
-
// `_streamingWrapPromise` returns `new moduleExports.APIPromise(...)` whose
|
|
7
|
-
// parse step wraps the SDK stream with an accumulating iterator that calls
|
|
8
|
-
// `_endSpan` when exhausted — so the test exercises the complement's APIPromise
|
|
9
|
-
// interposition end to end.
|
|
10
|
-
|
|
11
|
-
const test = require('node:test');
|
|
12
|
-
const assert = require('node:assert/strict');
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
AnthropicStreamingStopReasonInstrumentor,
|
|
16
|
-
} = require('../../../src/instrumentation/anthropic/get_streaming_stop_reason_telemetry');
|
|
17
|
-
|
|
18
|
-
class StubStream {
|
|
19
|
-
constructor(iteratorFactory, controller) {
|
|
20
|
-
this._iteratorFactory = iteratorFactory;
|
|
21
|
-
this.controller = controller;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
[Symbol.asyncIterator]() {
|
|
25
|
-
return this._iteratorFactory();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
class StubAPIPromise {
|
|
30
|
-
constructor(client, responsePromise, parseResponse) {
|
|
31
|
-
this.parsed = parseResponse(client, responsePromise);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function makeStubInstrumentation() {
|
|
36
|
-
const finalized = [];
|
|
37
|
-
return {
|
|
38
|
-
finalized,
|
|
39
|
-
_streamingWrapPromise(client, moduleExports, { span, promise }) {
|
|
40
|
-
const self = this;
|
|
41
|
-
return new moduleExports.APIPromise(
|
|
42
|
-
client,
|
|
43
|
-
promise,
|
|
44
|
-
async (parseClient, realStreamPromise) => {
|
|
45
|
-
const realStream = await realStreamPromise;
|
|
46
|
-
// Mimics traceloop: re-wrap the stream with an accumulating iterator
|
|
47
|
-
// that finalizes the span once exhausted.
|
|
48
|
-
return new realStream.constructor(
|
|
49
|
-
() =>
|
|
50
|
-
(async function* accumulate() {
|
|
51
|
-
const result = { stop_reason: null };
|
|
52
|
-
for await (const chunk of realStream) yield chunk;
|
|
53
|
-
self._endSpan({ span, result });
|
|
54
|
-
})(),
|
|
55
|
-
realStream.controller
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
},
|
|
60
|
-
_endSpan(args) {
|
|
61
|
-
finalized.push(args.result);
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function chunkStream() {
|
|
67
|
-
return new StubStream(async function* chunks() {
|
|
68
|
-
yield { type: 'message_start', message: { usage: { input_tokens: 5 } } };
|
|
69
|
-
yield { type: 'content_block_delta', delta: { type: 'text_delta', text: 'Hello' } };
|
|
70
|
-
yield {
|
|
71
|
-
type: 'message_delta',
|
|
72
|
-
delta: { stop_reason: 'end_turn' },
|
|
73
|
-
usage: { output_tokens: 3 },
|
|
74
|
-
};
|
|
75
|
-
yield { type: 'message_stop' };
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
test('injects the streamed stop reason into the result before endSpan', async () => {
|
|
80
|
-
const instrumentation = makeStubInstrumentation();
|
|
81
|
-
new AnthropicStreamingStopReasonInstrumentor().instrument(instrumentation);
|
|
82
|
-
|
|
83
|
-
const span = { name: 'anthropic.chat' };
|
|
84
|
-
const apiPromise = instrumentation._streamingWrapPromise(
|
|
85
|
-
{},
|
|
86
|
-
{ APIPromise: StubAPIPromise },
|
|
87
|
-
{ span, type: 'chat', promise: Promise.resolve(chunkStream()) }
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const stream = await apiPromise.parsed;
|
|
91
|
-
const seen = [];
|
|
92
|
-
for await (const chunk of stream) seen.push(chunk.type);
|
|
93
|
-
|
|
94
|
-
assert.deepEqual(seen, ['message_start', 'content_block_delta', 'message_delta', 'message_stop']);
|
|
95
|
-
assert.equal(instrumentation.finalized.length, 1);
|
|
96
|
-
assert.equal(instrumentation.finalized[0].stop_reason, 'end_turn');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('leaves an already-set stop reason untouched', () => {
|
|
100
|
-
const instrumentation = makeStubInstrumentation();
|
|
101
|
-
new AnthropicStreamingStopReasonInstrumentor().instrument(instrumentation);
|
|
102
|
-
|
|
103
|
-
const span = { name: 'anthropic.chat' };
|
|
104
|
-
instrumentation._endSpan({ span, result: { stop_reason: 'max_tokens' } });
|
|
105
|
-
assert.equal(instrumentation.finalized[0].stop_reason, 'max_tokens');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('uninstrument restores the original methods', () => {
|
|
109
|
-
const instrumentation = makeStubInstrumentation();
|
|
110
|
-
const streamOriginal = instrumentation._streamingWrapPromise;
|
|
111
|
-
const endSpanOriginal = instrumentation._endSpan;
|
|
112
|
-
const instrumentor = new AnthropicStreamingStopReasonInstrumentor();
|
|
113
|
-
instrumentor.instrument(instrumentation);
|
|
114
|
-
instrumentor.uninstrument();
|
|
115
|
-
assert.equal(instrumentation._streamingWrapPromise, streamOriginal);
|
|
116
|
-
assert.equal(instrumentation._endSpan, endSpanOriginal);
|
|
117
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Anthropic tool-definitions complement.
|
|
4
|
-
//
|
|
5
|
-
// The traceloop instrumentation instance is stubbed with a `startSpan` that
|
|
6
|
-
// returns a fake span, so the wrap can be exercised without the real SDK.
|
|
7
|
-
|
|
8
|
-
const test = require('node:test');
|
|
9
|
-
const assert = require('node:assert/strict');
|
|
10
|
-
|
|
11
|
-
const {
|
|
12
|
-
AnthropicToolDefinitionsInstrumentor,
|
|
13
|
-
} = require('../../../src/instrumentation/anthropic/get_tool_definitions_telemetry');
|
|
14
|
-
|
|
15
|
-
function makeFakeSpan() {
|
|
16
|
-
const attributes = {};
|
|
17
|
-
return {
|
|
18
|
-
attributes,
|
|
19
|
-
setAttribute(key, value) {
|
|
20
|
-
attributes[key] = value;
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function makeStubInstrumentation() {
|
|
26
|
-
return {
|
|
27
|
-
startSpan(request) {
|
|
28
|
-
const span = makeFakeSpan();
|
|
29
|
-
span.attributes['gen_ai.request.model'] = request.params.model;
|
|
30
|
-
return span;
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const WEATHER_TOOL = {
|
|
36
|
-
name: 'get_weather',
|
|
37
|
-
description: 'Get the current weather for a city.',
|
|
38
|
-
input_schema: { type: 'object', properties: { city: { type: 'string' } } },
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
test('stamps tool definitions from the request params', () => {
|
|
42
|
-
const instrumentation = makeStubInstrumentation();
|
|
43
|
-
const instrumentor = new AnthropicToolDefinitionsInstrumentor();
|
|
44
|
-
instrumentor.instrument(instrumentation);
|
|
45
|
-
|
|
46
|
-
const span = instrumentation.startSpan({
|
|
47
|
-
type: 'chat',
|
|
48
|
-
params: { model: 'claude-haiku-4-5', tools: [WEATHER_TOOL] },
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'claude-haiku-4-5');
|
|
52
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.tool.definitions']), [WEATHER_TOOL]);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('no attribute when the request has no tools', () => {
|
|
56
|
-
const instrumentation = makeStubInstrumentation();
|
|
57
|
-
new AnthropicToolDefinitionsInstrumentor().instrument(instrumentation);
|
|
58
|
-
|
|
59
|
-
const span = instrumentation.startSpan({
|
|
60
|
-
type: 'chat',
|
|
61
|
-
params: { model: 'claude-haiku-4-5' },
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
assert.ok(!('gen_ai.tool.definitions' in span.attributes));
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('uninstrument restores the original startSpan', () => {
|
|
68
|
-
const instrumentation = makeStubInstrumentation();
|
|
69
|
-
const original = instrumentation.startSpan;
|
|
70
|
-
const instrumentor = new AnthropicToolDefinitionsInstrumentor();
|
|
71
|
-
instrumentor.instrument(instrumentation);
|
|
72
|
-
assert.notEqual(instrumentation.startSpan, original);
|
|
73
|
-
instrumentor.uninstrument();
|
|
74
|
-
assert.equal(instrumentation.startSpan, original);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('is a no-op when the instrumentor has no startSpan', () => {
|
|
78
|
-
const instrumentor = new AnthropicToolDefinitionsInstrumentor();
|
|
79
|
-
instrumentor.instrument({});
|
|
80
|
-
instrumentor.instrument(undefined);
|
|
81
|
-
instrumentor.uninstrument();
|
|
82
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Bedrock cached-prompt token complement. It wraps the primary
|
|
4
|
-
// instrumentor's `_wrapPromise(span, promise, modelId)` to read Nova's cache-token
|
|
5
|
-
// counts off the decoded InvokeModel response body and stamp them as
|
|
6
|
-
// gen_ai.usage.cache_* attributes.
|
|
7
|
-
|
|
8
|
-
const test = require('node:test');
|
|
9
|
-
const assert = require('node:assert/strict');
|
|
10
|
-
|
|
11
|
-
const {
|
|
12
|
-
BedrockPromptCachingInstrumentor,
|
|
13
|
-
} = require('../../../src/instrumentation/bedrock/get_cache_telemetry');
|
|
14
|
-
|
|
15
|
-
function fakeSpan() {
|
|
16
|
-
const attrs = {};
|
|
17
|
-
return {
|
|
18
|
-
attrs,
|
|
19
|
-
isRecording: () => true,
|
|
20
|
-
setAttribute(key, value) {
|
|
21
|
-
attrs[key] = value;
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function encode(obj) {
|
|
27
|
-
return new TextEncoder().encode(JSON.stringify(obj));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Pass-through original: resolve to whatever promise it was handed.
|
|
31
|
-
function passthroughInstrumentation() {
|
|
32
|
-
return { _wrapPromise: (_span, promise) => Promise.resolve(promise) };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
test('stamps cache read/write from a buffered InvokeModel body', async () => {
|
|
36
|
-
const instrumentation = passthroughInstrumentation();
|
|
37
|
-
new BedrockPromptCachingInstrumentor().instrument(instrumentation);
|
|
38
|
-
|
|
39
|
-
const span = fakeSpan();
|
|
40
|
-
const body = encode({
|
|
41
|
-
usage: { cacheReadInputTokenCount: 100, cacheWriteInputTokenCount: 1300 },
|
|
42
|
-
});
|
|
43
|
-
await instrumentation._wrapPromise(span, Promise.resolve({ body }), 'amazon.nova-micro-v1:0');
|
|
44
|
-
|
|
45
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], 100);
|
|
46
|
-
assert.equal(span.attrs['gen_ai.usage.cache_creation_input_tokens'], 1300);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('sets nothing when cache counts are zero', async () => {
|
|
50
|
-
const instrumentation = passthroughInstrumentation();
|
|
51
|
-
new BedrockPromptCachingInstrumentor().instrument(instrumentation);
|
|
52
|
-
|
|
53
|
-
const span = fakeSpan();
|
|
54
|
-
const body = encode({ usage: { cacheReadInputTokenCount: 0, cacheWriteInputTokenCount: 0 } });
|
|
55
|
-
await instrumentation._wrapPromise(span, Promise.resolve({ body }), 'm');
|
|
56
|
-
|
|
57
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], undefined);
|
|
58
|
-
assert.equal(span.attrs['gen_ai.usage.cache_creation_input_tokens'], undefined);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('skips a streamed (async-iterable) body without consuming it', async () => {
|
|
62
|
-
const instrumentation = passthroughInstrumentation();
|
|
63
|
-
new BedrockPromptCachingInstrumentor().instrument(instrumentation);
|
|
64
|
-
|
|
65
|
-
const span = fakeSpan();
|
|
66
|
-
let consumed = false;
|
|
67
|
-
const body = (async function* () {
|
|
68
|
-
consumed = true;
|
|
69
|
-
yield {};
|
|
70
|
-
})();
|
|
71
|
-
await instrumentation._wrapPromise(span, Promise.resolve({ body }), 'm');
|
|
72
|
-
|
|
73
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], undefined);
|
|
74
|
-
assert.equal(consumed, false);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('stamps cache from the streamed metadata chunk and calls the original', () => {
|
|
78
|
-
const calls = [];
|
|
79
|
-
const instrumentation = {
|
|
80
|
-
_handleNovaStreamingMetadata(span, parsed) {
|
|
81
|
-
calls.push(parsed);
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
new BedrockPromptCachingInstrumentor().instrument(instrumentation);
|
|
85
|
-
|
|
86
|
-
const span = fakeSpan();
|
|
87
|
-
instrumentation._handleNovaStreamingMetadata(span, {
|
|
88
|
-
metadata: {
|
|
89
|
-
usage: { inputTokens: 3, cacheReadInputTokenCount: 1458, cacheWriteInputTokenCount: 0 },
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], 1458);
|
|
94
|
-
assert.equal(span.attrs['gen_ai.usage.cache_creation_input_tokens'], undefined);
|
|
95
|
-
assert.equal(calls.length, 1);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('is a no-op when the instrumentor has no _wrapPromise', () => {
|
|
99
|
-
const instrumentation = {};
|
|
100
|
-
new BedrockPromptCachingInstrumentor().instrument(instrumentation);
|
|
101
|
-
assert.equal(instrumentation._wrapPromise, undefined);
|
|
102
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Bedrock input-message-content complement.
|
|
4
|
-
//
|
|
5
|
-
// The traceloop instrumentation instance is stubbed with a `_startSpan` that
|
|
6
|
-
// returns a fake span — with or without instrumentor-set input messages — so
|
|
7
|
-
// the wrap's "only when missing" behaviour can be exercised.
|
|
8
|
-
|
|
9
|
-
const test = require('node:test');
|
|
10
|
-
const assert = require('node:assert/strict');
|
|
11
|
-
|
|
12
|
-
const {
|
|
13
|
-
BedrockInputMessageContentInstrumentor,
|
|
14
|
-
} = require('../../../src/instrumentation/bedrock/get_input_message_content_telemetry');
|
|
15
|
-
|
|
16
|
-
function makeFakeSpan(attributes = {}) {
|
|
17
|
-
return {
|
|
18
|
-
attributes,
|
|
19
|
-
setAttribute(key, value) {
|
|
20
|
-
attributes[key] = value;
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function makeStubInstrumentation(spanAttributes) {
|
|
26
|
-
return {
|
|
27
|
-
_startSpan() {
|
|
28
|
-
return makeFakeSpan(spanAttributes);
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function request(body) {
|
|
34
|
-
return { params: { input: { modelId: 'model-that-does-not-exist', body } } };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
test('stamps input messages when the instrumentor set none', () => {
|
|
38
|
-
const instrumentation = makeStubInstrumentation({});
|
|
39
|
-
new BedrockInputMessageContentInstrumentor().instrument(instrumentation);
|
|
40
|
-
|
|
41
|
-
const span = instrumentation._startSpan(
|
|
42
|
-
request(JSON.stringify({ messages: [{ role: 'user', content: [{ text: 'Hello' }] }] }))
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.input.messages']), [
|
|
46
|
-
{ role: 'user', parts: [{ type: 'text', content: 'Hello' }] },
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('supports the flat inputText schema', () => {
|
|
51
|
-
const instrumentation = makeStubInstrumentation({});
|
|
52
|
-
new BedrockInputMessageContentInstrumentor().instrument(instrumentation);
|
|
53
|
-
|
|
54
|
-
const span = instrumentation._startSpan(request(JSON.stringify({ inputText: 'Hello' })));
|
|
55
|
-
|
|
56
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.input.messages']), [
|
|
57
|
-
{ role: 'user', parts: [{ type: 'text', content: 'Hello' }] },
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('leaves instrumentor-set input messages untouched', () => {
|
|
62
|
-
const existing = '[{"role":"user","parts":[{"type":"text","content":"from-traceloop"}]}]';
|
|
63
|
-
const instrumentation = makeStubInstrumentation({ 'gen_ai.input.messages': existing });
|
|
64
|
-
new BedrockInputMessageContentInstrumentor().instrument(instrumentation);
|
|
65
|
-
|
|
66
|
-
const span = instrumentation._startSpan(
|
|
67
|
-
request(JSON.stringify({ messages: [{ role: 'user', content: [{ text: 'other' }] }] }))
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
assert.equal(span.attributes['gen_ai.input.messages'], existing);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('no attribute for an unparseable or empty body', () => {
|
|
74
|
-
const instrumentation = makeStubInstrumentation({});
|
|
75
|
-
new BedrockInputMessageContentInstrumentor().instrument(instrumentation);
|
|
76
|
-
|
|
77
|
-
assert.ok(
|
|
78
|
-
!('gen_ai.input.messages' in instrumentation._startSpan(request('not-json')).attributes)
|
|
79
|
-
);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('uninstrument restores the original startSpan', () => {
|
|
83
|
-
const instrumentation = makeStubInstrumentation({});
|
|
84
|
-
const original = instrumentation._startSpan;
|
|
85
|
-
const instrumentor = new BedrockInputMessageContentInstrumentor();
|
|
86
|
-
instrumentor.instrument(instrumentation);
|
|
87
|
-
instrumentor.uninstrument();
|
|
88
|
-
assert.equal(instrumentation._startSpan, original);
|
|
89
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Bedrock streamed-output message complement.
|
|
4
|
-
//
|
|
5
|
-
// The traceloop instrumentation instance is stubbed with a
|
|
6
|
-
// `_handleNovaStreamingMetadata` no-op; chunks are replayed through the wrap the
|
|
7
|
-
// way the instrumentation's stream loop calls it.
|
|
8
|
-
|
|
9
|
-
const test = require('node:test');
|
|
10
|
-
const assert = require('node:assert/strict');
|
|
11
|
-
|
|
12
|
-
const {
|
|
13
|
-
BedrockStreamingOutputMessageInstrumentor,
|
|
14
|
-
} = require('../../../src/instrumentation/bedrock/get_streaming_output_message_telemetry');
|
|
15
|
-
|
|
16
|
-
function makeFakeSpan() {
|
|
17
|
-
const attributes = {};
|
|
18
|
-
return {
|
|
19
|
-
attributes,
|
|
20
|
-
setAttribute(key, value) {
|
|
21
|
-
attributes[key] = value;
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function instrumented() {
|
|
27
|
-
const instrumentation = { _handleNovaStreamingMetadata() {} };
|
|
28
|
-
const instrumentor = new BedrockStreamingOutputMessageInstrumentor();
|
|
29
|
-
instrumentor.instrument(instrumentation);
|
|
30
|
-
return { instrumentation, instrumentor };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
test('re-stamps the output message from accumulated stream chunks', () => {
|
|
34
|
-
const { instrumentation } = instrumented();
|
|
35
|
-
const span = makeFakeSpan();
|
|
36
|
-
|
|
37
|
-
instrumentation._handleNovaStreamingMetadata(span, {
|
|
38
|
-
contentBlockDelta: { delta: { text: 'Hel' } },
|
|
39
|
-
});
|
|
40
|
-
instrumentation._handleNovaStreamingMetadata(span, {
|
|
41
|
-
contentBlockDelta: { delta: { text: 'lo!' } },
|
|
42
|
-
});
|
|
43
|
-
instrumentation._handleNovaStreamingMetadata(span, {
|
|
44
|
-
messageStop: { stopReason: 'end_turn' },
|
|
45
|
-
});
|
|
46
|
-
assert.ok(!('gen_ai.output.messages' in span.attributes), 'set only on the metadata chunk');
|
|
47
|
-
|
|
48
|
-
instrumentation._handleNovaStreamingMetadata(span, { metadata: { usage: {} } });
|
|
49
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.output.messages']), [
|
|
50
|
-
{
|
|
51
|
-
role: 'assistant',
|
|
52
|
-
parts: [{ type: 'text', content: 'Hello!' }],
|
|
53
|
-
finish_reason: 'stop',
|
|
54
|
-
},
|
|
55
|
-
]);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('maps max_tokens to length', () => {
|
|
59
|
-
const { instrumentation } = instrumented();
|
|
60
|
-
const span = makeFakeSpan();
|
|
61
|
-
instrumentation._handleNovaStreamingMetadata(span, {
|
|
62
|
-
contentBlockDelta: { delta: { text: 'OK' } },
|
|
63
|
-
});
|
|
64
|
-
instrumentation._handleNovaStreamingMetadata(span, {
|
|
65
|
-
messageStop: { stopReason: 'max_tokens' },
|
|
66
|
-
});
|
|
67
|
-
instrumentation._handleNovaStreamingMetadata(span, { metadata: {} });
|
|
68
|
-
assert.equal(JSON.parse(span.attributes['gen_ai.output.messages'])[0].finish_reason, 'length');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('no attribute when no text was streamed', () => {
|
|
72
|
-
const { instrumentation } = instrumented();
|
|
73
|
-
const span = makeFakeSpan();
|
|
74
|
-
instrumentation._handleNovaStreamingMetadata(span, {
|
|
75
|
-
messageStop: { stopReason: 'end_turn' },
|
|
76
|
-
});
|
|
77
|
-
instrumentation._handleNovaStreamingMetadata(span, { metadata: {} });
|
|
78
|
-
assert.ok(!('gen_ai.output.messages' in span.attributes));
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('uninstrument restores the original handler', () => {
|
|
82
|
-
const { instrumentation, instrumentor } = instrumented();
|
|
83
|
-
instrumentor.uninstrument();
|
|
84
|
-
const span = makeFakeSpan();
|
|
85
|
-
instrumentation._handleNovaStreamingMetadata(span, { metadata: {} });
|
|
86
|
-
assert.deepEqual(span.attributes, {});
|
|
87
|
-
});
|