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,148 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Cohere chat token-usage enrichment complement.
|
|
4
|
-
//
|
|
5
|
-
// The cohere-ai v1 `CohereClient` is stubbed with an internal `__chat` method
|
|
6
|
-
// that mirrors the SDK shape — resolving to `{ data: { meta: { billedUnits } } }`
|
|
7
|
-
// — so the complement can be exercised without network access or the dependency
|
|
8
|
-
// installed. The complement enriches whichever span is active when `__chat` runs
|
|
9
|
-
// (in production, the span traceloop opens around the public `chat`), so the
|
|
10
|
-
// tests start a span and make it active before invoking `__chat`.
|
|
11
|
-
|
|
12
|
-
const test = require('node:test');
|
|
13
|
-
const assert = require('node:assert/strict');
|
|
14
|
-
const Module = require('node:module');
|
|
15
|
-
|
|
16
|
-
const { trace, context } = require('@opentelemetry/api');
|
|
17
|
-
const {
|
|
18
|
-
BasicTracerProvider,
|
|
19
|
-
InMemorySpanExporter,
|
|
20
|
-
SimpleSpanProcessor,
|
|
21
|
-
} = require('@opentelemetry/sdk-trace-base');
|
|
22
|
-
const { AsyncLocalStorageContextManager } = require('@opentelemetry/context-async-hooks');
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
-
CohereChatTokenUsageInstrumentor,
|
|
26
|
-
} = require('../../../src/instrumentation/cohere/get_chat_telemetry');
|
|
27
|
-
|
|
28
|
-
const CLIENT_MODULE = 'cohere-ai/Client';
|
|
29
|
-
|
|
30
|
-
const exporter = new InMemorySpanExporter();
|
|
31
|
-
trace.setGlobalTracerProvider(
|
|
32
|
-
new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
|
|
33
|
-
);
|
|
34
|
-
context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable());
|
|
35
|
-
const tracer = trace.getTracer('test');
|
|
36
|
-
|
|
37
|
-
class StubCohereClient {
|
|
38
|
-
__chat(request = {}) {
|
|
39
|
-
if (request.model === 'boom') return Promise.reject(new Error('api error'));
|
|
40
|
-
return Promise.resolve({
|
|
41
|
-
data: { text: 'hi', meta: { billedUnits: { inputTokens: 7, outputTokens: 3 } } },
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function instrumented() {
|
|
47
|
-
const stubs = { [CLIENT_MODULE]: { CohereClient: StubCohereClient } };
|
|
48
|
-
const origResolve = Module._resolveFilename;
|
|
49
|
-
Module._resolveFilename = function patched(request, ...rest) {
|
|
50
|
-
if (request in stubs) return request;
|
|
51
|
-
return origResolve.call(this, request, ...rest);
|
|
52
|
-
};
|
|
53
|
-
for (const [moduleId, exports] of Object.entries(stubs)) {
|
|
54
|
-
require.cache[moduleId] = { id: moduleId, filename: moduleId, loaded: true, exports };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const instrumentor = new CohereChatTokenUsageInstrumentor();
|
|
58
|
-
instrumentor.instrument();
|
|
59
|
-
|
|
60
|
-
return () => {
|
|
61
|
-
instrumentor.uninstrument();
|
|
62
|
-
Module._resolveFilename = origResolve;
|
|
63
|
-
for (const moduleId of Object.keys(stubs)) delete require.cache[moduleId];
|
|
64
|
-
exporter.reset();
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Runs `__chat` inside an active span, mirroring the context traceloop sets up
|
|
69
|
-
// around the public `chat`, and returns the finished span.
|
|
70
|
-
async function callChatWithinSpan(client, request) {
|
|
71
|
-
const span = tracer.startSpan('cohere.chat');
|
|
72
|
-
await context.with(trace.setSpan(context.active(), span), () => client.__chat(request));
|
|
73
|
-
span.end();
|
|
74
|
-
return exporter.getFinishedSpans().find((finished) => finished.name === 'cohere.chat');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
test('enriches the active chat span with input and output tokens', async () => {
|
|
78
|
-
const teardown = instrumented();
|
|
79
|
-
try {
|
|
80
|
-
const client = new StubCohereClient();
|
|
81
|
-
const span = await callChatWithinSpan(client, { model: 'command-r-08-2024', message: 'hi' });
|
|
82
|
-
|
|
83
|
-
assert.equal(span.attributes['gen_ai.usage.input_tokens'], 7);
|
|
84
|
-
assert.equal(span.attributes['gen_ai.usage.output_tokens'], 3);
|
|
85
|
-
} finally {
|
|
86
|
-
teardown();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test('leaves the response unchanged', async () => {
|
|
91
|
-
const teardown = instrumented();
|
|
92
|
-
try {
|
|
93
|
-
const client = new StubCohereClient();
|
|
94
|
-
let result;
|
|
95
|
-
const span = tracer.startSpan('cohere.chat');
|
|
96
|
-
await context.with(trace.setSpan(context.active(), span), async () => {
|
|
97
|
-
result = await client.__chat({ model: 'command-r-08-2024', message: 'hi' });
|
|
98
|
-
});
|
|
99
|
-
span.end();
|
|
100
|
-
|
|
101
|
-
assert.equal(result.data.text, 'hi');
|
|
102
|
-
assert.equal(result.data.meta.billedUnits.inputTokens, 7);
|
|
103
|
-
} finally {
|
|
104
|
-
teardown();
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('does nothing when no span is active', async () => {
|
|
109
|
-
const teardown = instrumented();
|
|
110
|
-
try {
|
|
111
|
-
const client = new StubCohereClient();
|
|
112
|
-
// No active span — must not throw, just pass the response through.
|
|
113
|
-
const result = await client.__chat({ model: 'command-r-08-2024', message: 'hi' });
|
|
114
|
-
assert.equal(result.data.meta.billedUnits.outputTokens, 3);
|
|
115
|
-
assert.equal(exporter.getFinishedSpans().length, 0);
|
|
116
|
-
} finally {
|
|
117
|
-
teardown();
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('propagates errors without enriching the span', async () => {
|
|
122
|
-
const teardown = instrumented();
|
|
123
|
-
try {
|
|
124
|
-
const client = new StubCohereClient();
|
|
125
|
-
const span = tracer.startSpan('cohere.chat');
|
|
126
|
-
await assert.rejects(
|
|
127
|
-
() =>
|
|
128
|
-
context.with(trace.setSpan(context.active(), span), () =>
|
|
129
|
-
client.__chat({ model: 'boom', message: 'x' })
|
|
130
|
-
),
|
|
131
|
-
/api error/
|
|
132
|
-
);
|
|
133
|
-
span.end();
|
|
134
|
-
|
|
135
|
-
const finished = exporter.getFinishedSpans().find((each) => each.name === 'cohere.chat');
|
|
136
|
-
assert.equal(finished.attributes['gen_ai.usage.input_tokens'], undefined);
|
|
137
|
-
} finally {
|
|
138
|
-
teardown();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test('uninstrument restores the original method', async () => {
|
|
143
|
-
const teardown = instrumented();
|
|
144
|
-
const { CohereClient } = require(CLIENT_MODULE);
|
|
145
|
-
const wrapped = CohereClient.prototype.__chat;
|
|
146
|
-
teardown();
|
|
147
|
-
assert.notEqual(CohereClient.prototype.__chat, wrapped);
|
|
148
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the bundled Cohere embeddings instrumentor.
|
|
4
|
-
//
|
|
5
|
-
// The cohere-ai v1 `CohereClient` and v2 `V2Client` classes are stubbed with an
|
|
6
|
-
// `embed` method that mirrors the SDK shape — resolving to a response carrying
|
|
7
|
-
// `meta.billedUnits.inputTokens` — so the instrumentor can be exercised without
|
|
8
|
-
// network access or the dependency installed.
|
|
9
|
-
|
|
10
|
-
const test = require('node:test');
|
|
11
|
-
const assert = require('node:assert/strict');
|
|
12
|
-
const Module = require('node:module');
|
|
13
|
-
|
|
14
|
-
const { trace, SpanStatusCode } = require('@opentelemetry/api');
|
|
15
|
-
const {
|
|
16
|
-
BasicTracerProvider,
|
|
17
|
-
InMemorySpanExporter,
|
|
18
|
-
SimpleSpanProcessor,
|
|
19
|
-
} = require('@opentelemetry/sdk-trace-base');
|
|
20
|
-
|
|
21
|
-
const {
|
|
22
|
-
CohereEmbeddingsInstrumentor,
|
|
23
|
-
} = require('../../../src/instrumentation/cohere/get_embeddings_telemetry');
|
|
24
|
-
|
|
25
|
-
const V1_MODULE = 'cohere-ai/Client';
|
|
26
|
-
const V2_MODULE = 'cohere-ai/api/resources/v2/client/Client';
|
|
27
|
-
|
|
28
|
-
const exporter = new InMemorySpanExporter();
|
|
29
|
-
trace.setGlobalTracerProvider(
|
|
30
|
-
new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
class StubCohereClient {
|
|
34
|
-
embed(request = {}) {
|
|
35
|
-
if (request.model === 'boom') return Promise.reject(new Error('api error'));
|
|
36
|
-
return Promise.resolve({ embeddings: [[0.1]], meta: { billedUnits: { inputTokens: 8 } } });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class StubV2Client {
|
|
41
|
-
embed(_request = {}) {
|
|
42
|
-
return Promise.resolve({
|
|
43
|
-
embeddings: { float: [[0.2]] },
|
|
44
|
-
meta: { billedUnits: { inputTokens: 5 } },
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function instrumented() {
|
|
50
|
-
const stubs = {
|
|
51
|
-
[V1_MODULE]: { CohereClient: StubCohereClient },
|
|
52
|
-
[V2_MODULE]: { V2Client: StubV2Client },
|
|
53
|
-
};
|
|
54
|
-
const origResolve = Module._resolveFilename;
|
|
55
|
-
Module._resolveFilename = function patched(request, ...rest) {
|
|
56
|
-
if (request in stubs) return request;
|
|
57
|
-
return origResolve.call(this, request, ...rest);
|
|
58
|
-
};
|
|
59
|
-
for (const [moduleId, exports] of Object.entries(stubs)) {
|
|
60
|
-
require.cache[moduleId] = { id: moduleId, filename: moduleId, loaded: true, exports };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const instrumentor = new CohereEmbeddingsInstrumentor();
|
|
64
|
-
instrumentor.instrument();
|
|
65
|
-
|
|
66
|
-
return () => {
|
|
67
|
-
instrumentor.uninstrument();
|
|
68
|
-
Module._resolveFilename = origResolve;
|
|
69
|
-
for (const moduleId of Object.keys(stubs)) delete require.cache[moduleId];
|
|
70
|
-
exporter.reset();
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
test('v1 embed emits genai span with input tokens', async () => {
|
|
75
|
-
const teardown = instrumented();
|
|
76
|
-
try {
|
|
77
|
-
const client = new StubCohereClient();
|
|
78
|
-
const result = await client.embed({ model: 'embed-english-v3.0', texts: ['hi'] });
|
|
79
|
-
assert.equal(result.meta.billedUnits.inputTokens, 8);
|
|
80
|
-
|
|
81
|
-
const [span] = exporter.getFinishedSpans();
|
|
82
|
-
assert.equal(span.name, 'embeddings');
|
|
83
|
-
assert.equal(span.attributes['gen_ai.system'], 'Cohere');
|
|
84
|
-
assert.equal(span.attributes['gen_ai.operation.name'], 'embeddings');
|
|
85
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'embed-english-v3.0');
|
|
86
|
-
assert.equal(span.attributes['gen_ai.usage.input_tokens'], 8);
|
|
87
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.input.messages']), [
|
|
88
|
-
{ role: 'user', parts: [{ type: 'text', content: 'hi' }] },
|
|
89
|
-
]);
|
|
90
|
-
assert.equal(span.status.code, SpanStatusCode.UNSET);
|
|
91
|
-
} finally {
|
|
92
|
-
teardown();
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('v2 embed emits genai span with input tokens', async () => {
|
|
97
|
-
const teardown = instrumented();
|
|
98
|
-
try {
|
|
99
|
-
const client = new StubV2Client();
|
|
100
|
-
await client.embed({ model: 'embed-v4.0', texts: ['hi'] });
|
|
101
|
-
|
|
102
|
-
const [span] = exporter.getFinishedSpans();
|
|
103
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'embed-v4.0');
|
|
104
|
-
assert.equal(span.attributes['gen_ai.usage.input_tokens'], 5);
|
|
105
|
-
} finally {
|
|
106
|
-
teardown();
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test('embed records error status on failure', async () => {
|
|
111
|
-
const teardown = instrumented();
|
|
112
|
-
try {
|
|
113
|
-
const client = new StubCohereClient();
|
|
114
|
-
await assert.rejects(() => client.embed({ model: 'boom', texts: ['x'] }), /api error/);
|
|
115
|
-
|
|
116
|
-
const [span] = exporter.getFinishedSpans();
|
|
117
|
-
assert.equal(span.status.code, SpanStatusCode.ERROR);
|
|
118
|
-
} finally {
|
|
119
|
-
teardown();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('uninstrument restores original methods', async () => {
|
|
124
|
-
const teardown = instrumented();
|
|
125
|
-
try {
|
|
126
|
-
const client = new StubCohereClient();
|
|
127
|
-
await client.embed({ model: 'embed-english-v3.0', texts: ['x'] });
|
|
128
|
-
assert.equal(exporter.getFinishedSpans().length, 1);
|
|
129
|
-
} finally {
|
|
130
|
-
teardown();
|
|
131
|
-
}
|
|
132
|
-
});
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the bundled ElevenLabs audio instrumentor.
|
|
4
|
-
//
|
|
5
|
-
// The real `@elevenlabs/elevenlabs-js` clients are stubbed with methods that
|
|
6
|
-
// mirror the SDK's shapes — the streaming TTS entrypoints (`convert` / `stream`)
|
|
7
|
-
// resolve to a ReadableStream, `streamWithTimestamps` to an async-iterable, and
|
|
8
|
-
// the unary `convertWithTimestamps` and Speech-to-Text `convert` to a response
|
|
9
|
-
// object — so the instrumentor can be exercised without network access or the
|
|
10
|
-
// dependency installed.
|
|
11
|
-
|
|
12
|
-
const test = require('node:test');
|
|
13
|
-
const assert = require('node:assert/strict');
|
|
14
|
-
const Module = require('node:module');
|
|
15
|
-
|
|
16
|
-
const { trace, SpanStatusCode } = require('@opentelemetry/api');
|
|
17
|
-
const {
|
|
18
|
-
BasicTracerProvider,
|
|
19
|
-
InMemorySpanExporter,
|
|
20
|
-
SimpleSpanProcessor,
|
|
21
|
-
} = require('@opentelemetry/sdk-trace-base');
|
|
22
|
-
|
|
23
|
-
const {
|
|
24
|
-
ElevenLabsInstrumentor,
|
|
25
|
-
} = require('../../../src/instrumentation/elevenlabs/full_instrumentation');
|
|
26
|
-
|
|
27
|
-
const TTS_CLIENT_MODULE = '@elevenlabs/elevenlabs-js/api/resources/textToSpeech/client/Client';
|
|
28
|
-
const STT_CLIENT_MODULE = '@elevenlabs/elevenlabs-js/api/resources/speechToText/client/Client';
|
|
29
|
-
|
|
30
|
-
// The instrumentor emits via the global tracer provider. node's test runner
|
|
31
|
-
// isolates each test file in its own process, so registering one provider +
|
|
32
|
-
// in-memory exporter for the whole file is safe.
|
|
33
|
-
const exporter = new InMemorySpanExporter();
|
|
34
|
-
trace.setGlobalTracerProvider(
|
|
35
|
-
new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// The exact chunk objects the streaming stubs yield. Shared so the passthrough
|
|
39
|
-
// assertions can compare object identity — proof our proxies re-emit the user's
|
|
40
|
-
// chunks without copying or mutating them.
|
|
41
|
-
const TTS_BYTE_CHUNKS = [Buffer.from('a'), Buffer.from('b')];
|
|
42
|
-
const TTS_OBJECT_CHUNKS = [{ audioBase64: 'eA==' }, { audioBase64: 'YmI=' }];
|
|
43
|
-
|
|
44
|
-
function readableOf(chunks) {
|
|
45
|
-
let index = 0;
|
|
46
|
-
return new ReadableStream({
|
|
47
|
-
pull(controller) {
|
|
48
|
-
if (index < chunks.length) controller.enqueue(chunks[index++]);
|
|
49
|
-
else controller.close();
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function failingReadable(error) {
|
|
55
|
-
return new ReadableStream({
|
|
56
|
-
pull() {
|
|
57
|
-
throw error;
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function asyncIterableOf(chunks) {
|
|
63
|
-
return {
|
|
64
|
-
async *[Symbol.asyncIterator]() {
|
|
65
|
-
for (const chunk of chunks) yield chunk;
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
class StubTextToSpeechClient {
|
|
71
|
-
convert(_voiceId, request = {}) {
|
|
72
|
-
if (request.text === 'boom') return Promise.resolve(failingReadable(new Error('api error')));
|
|
73
|
-
return Promise.resolve(readableOf(TTS_BYTE_CHUNKS));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
stream(_voiceId, _request = {}) {
|
|
77
|
-
return Promise.resolve(readableOf([Buffer.from('x')]));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
streamWithTimestamps(_voiceId, _request = {}) {
|
|
81
|
-
return Promise.resolve(asyncIterableOf(TTS_OBJECT_CHUNKS));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
convertWithTimestamps(_voiceId, _request = {}) {
|
|
85
|
-
return Promise.resolve({ audioBase64: 'YQ==' });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
class StubSpeechToTextClient {
|
|
90
|
-
convert(request = {}) {
|
|
91
|
-
if (request.modelId === 'boom') return Promise.reject(new Error('api error'));
|
|
92
|
-
return Promise.resolve({ audioDurationSecs: 12.4, text: 'hello' });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function drain(stream) {
|
|
97
|
-
const reader = stream.getReader();
|
|
98
|
-
const chunks = [];
|
|
99
|
-
for (;;) {
|
|
100
|
-
const { done, value } = await reader.read();
|
|
101
|
-
if (done) break;
|
|
102
|
-
chunks.push(value);
|
|
103
|
-
}
|
|
104
|
-
return chunks;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async function drainIterable(iterable) {
|
|
108
|
-
const chunks = [];
|
|
109
|
-
for await (const chunk of iterable) chunks.push(chunk);
|
|
110
|
-
return chunks;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Stub the elevenlabs client modules, install the instrumentor + the in-memory
|
|
114
|
-
// exporter, and return a teardown that restores everything.
|
|
115
|
-
function instrumented() {
|
|
116
|
-
const stubs = {
|
|
117
|
-
[TTS_CLIENT_MODULE]: { TextToSpeechClient: StubTextToSpeechClient },
|
|
118
|
-
[STT_CLIENT_MODULE]: { SpeechToTextClient: StubSpeechToTextClient },
|
|
119
|
-
};
|
|
120
|
-
const origResolve = Module._resolveFilename;
|
|
121
|
-
Module._resolveFilename = function patched(request, ...rest) {
|
|
122
|
-
if (request in stubs) return request;
|
|
123
|
-
return origResolve.call(this, request, ...rest);
|
|
124
|
-
};
|
|
125
|
-
for (const [moduleId, exports] of Object.entries(stubs)) {
|
|
126
|
-
require.cache[moduleId] = { id: moduleId, filename: moduleId, loaded: true, exports };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const instrumentor = new ElevenLabsInstrumentor();
|
|
130
|
-
instrumentor.instrument();
|
|
131
|
-
|
|
132
|
-
return () => {
|
|
133
|
-
instrumentor.uninstrument();
|
|
134
|
-
Module._resolveFilename = origResolve;
|
|
135
|
-
for (const moduleId of Object.keys(stubs)) delete require.cache[moduleId];
|
|
136
|
-
exporter.reset();
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
test('convert emits genai span with char count', async () => {
|
|
141
|
-
const teardown = instrumented();
|
|
142
|
-
try {
|
|
143
|
-
const client = new StubTextToSpeechClient();
|
|
144
|
-
const stream = await client.convert('voice', { text: 'hello world', modelId: 'eleven_v2' });
|
|
145
|
-
// Same count, order, and object identity: the byte-stream proxy re-emits each
|
|
146
|
-
// chunk by reference — no telemetry collision with the caller's audio.
|
|
147
|
-
const collected = await drain(stream);
|
|
148
|
-
assert.equal(collected.length, TTS_BYTE_CHUNKS.length);
|
|
149
|
-
collected.forEach((chunk, index) => assert.equal(chunk, TTS_BYTE_CHUNKS[index]));
|
|
150
|
-
|
|
151
|
-
const [span] = exporter.getFinishedSpans();
|
|
152
|
-
assert.equal(span.name, 'text_to_speech');
|
|
153
|
-
assert.equal(span.attributes['gen_ai.system'], 'elevenlabs');
|
|
154
|
-
assert.equal(span.attributes['gen_ai.operation.name'], 'text_to_speech');
|
|
155
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'eleven_v2');
|
|
156
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], 'hello world'.length);
|
|
157
|
-
assert.equal(span.status.code, SpanStatusCode.UNSET);
|
|
158
|
-
} finally {
|
|
159
|
-
teardown();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test('convert records error status on failure', async () => {
|
|
164
|
-
const teardown = instrumented();
|
|
165
|
-
try {
|
|
166
|
-
const client = new StubTextToSpeechClient();
|
|
167
|
-
const stream = await client.convert('voice', { text: 'boom', modelId: 'm' });
|
|
168
|
-
await assert.rejects(() => drain(stream), /api error/);
|
|
169
|
-
|
|
170
|
-
const [span] = exporter.getFinishedSpans();
|
|
171
|
-
assert.equal(span.status.code, SpanStatusCode.ERROR);
|
|
172
|
-
} finally {
|
|
173
|
-
teardown();
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test('convert with timestamps emits span with char count', async () => {
|
|
178
|
-
const teardown = instrumented();
|
|
179
|
-
try {
|
|
180
|
-
const client = new StubTextToSpeechClient();
|
|
181
|
-
// The unary timestamp variant resolves to a single response, not a stream.
|
|
182
|
-
const result = await client.convertWithTimestamps('voice', { text: 'hello', modelId: 'm' });
|
|
183
|
-
assert.equal(result.audioBase64, 'YQ==');
|
|
184
|
-
|
|
185
|
-
const [span] = exporter.getFinishedSpans();
|
|
186
|
-
assert.equal(span.name, 'text_to_speech');
|
|
187
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], 'hello'.length);
|
|
188
|
-
assert.equal(span.attributes['gen_ai.usage.input_seconds'], undefined);
|
|
189
|
-
} finally {
|
|
190
|
-
teardown();
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test('stream with timestamps emits span with char count', async () => {
|
|
195
|
-
const teardown = instrumented();
|
|
196
|
-
try {
|
|
197
|
-
const client = new StubTextToSpeechClient();
|
|
198
|
-
const stream = await client.streamWithTimestamps('voice', { text: 'hello', modelId: 'm' });
|
|
199
|
-
// Same count, order, and object identity: the object-stream proxy re-yields
|
|
200
|
-
// each chunk by reference — no telemetry collision with the caller's payload.
|
|
201
|
-
const collected = await drainIterable(stream);
|
|
202
|
-
assert.equal(collected.length, TTS_OBJECT_CHUNKS.length);
|
|
203
|
-
collected.forEach((chunk, index) => assert.equal(chunk, TTS_OBJECT_CHUNKS[index]));
|
|
204
|
-
|
|
205
|
-
const [span] = exporter.getFinishedSpans();
|
|
206
|
-
assert.equal(span.name, 'text_to_speech');
|
|
207
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], 'hello'.length);
|
|
208
|
-
} finally {
|
|
209
|
-
teardown();
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test('uninstrument restores original methods', async () => {
|
|
214
|
-
const teardown = instrumented();
|
|
215
|
-
try {
|
|
216
|
-
const client = new StubTextToSpeechClient();
|
|
217
|
-
assert.deepEqual(await drain(await client.stream('voice', { text: 'x' })), [Buffer.from('x')]);
|
|
218
|
-
assert.equal(exporter.getFinishedSpans().length, 1);
|
|
219
|
-
} finally {
|
|
220
|
-
teardown();
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
test('speech to text emits span with audio duration', async () => {
|
|
225
|
-
const teardown = instrumented();
|
|
226
|
-
try {
|
|
227
|
-
const client = new StubSpeechToTextClient();
|
|
228
|
-
const result = await client.convert({ modelId: 'scribe_v1', file: Buffer.from('audio') });
|
|
229
|
-
assert.equal(result.audioDurationSecs, 12.4);
|
|
230
|
-
|
|
231
|
-
const [span] = exporter.getFinishedSpans();
|
|
232
|
-
assert.equal(span.name, 'speech_to_text');
|
|
233
|
-
assert.equal(span.attributes['gen_ai.system'], 'elevenlabs');
|
|
234
|
-
assert.equal(span.attributes['gen_ai.operation.name'], 'speech_to_text');
|
|
235
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'scribe_v1');
|
|
236
|
-
assert.equal(span.attributes['gen_ai.usage.input_seconds'], 12.4);
|
|
237
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], undefined);
|
|
238
|
-
assert.equal(span.status.code, SpanStatusCode.UNSET);
|
|
239
|
-
} finally {
|
|
240
|
-
teardown();
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
test('speech to text records error status on failure', async () => {
|
|
245
|
-
const teardown = instrumented();
|
|
246
|
-
try {
|
|
247
|
-
const client = new StubSpeechToTextClient();
|
|
248
|
-
await assert.rejects(
|
|
249
|
-
() => client.convert({ modelId: 'boom', file: Buffer.from('audio') }),
|
|
250
|
-
/api error/
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
const [span] = exporter.getFinishedSpans();
|
|
254
|
-
assert.equal(span.status.code, SpanStatusCode.ERROR);
|
|
255
|
-
} finally {
|
|
256
|
-
teardown();
|
|
257
|
-
}
|
|
258
|
-
});
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the bundled Google GenAI (Gemini) embeddings telemetry.
|
|
4
|
-
//
|
|
5
|
-
// The @google/genai `Models` class is stubbed with the prototype method
|
|
6
|
-
// `embedContentInternal` (the stable patch point that the public `embedContent`
|
|
7
|
-
// arrow delegates to). The Gemini Developer API returns no token usage, so the
|
|
8
|
-
// billable unit is the input character count. `instrumentEmbeddings(Models,
|
|
9
|
-
// patched)` wraps the method and pushes its restore tuple onto `patched`.
|
|
10
|
-
|
|
11
|
-
const test = require('node:test');
|
|
12
|
-
const assert = require('node:assert/strict');
|
|
13
|
-
|
|
14
|
-
const { trace, SpanStatusCode } = require('@opentelemetry/api');
|
|
15
|
-
const {
|
|
16
|
-
BasicTracerProvider,
|
|
17
|
-
InMemorySpanExporter,
|
|
18
|
-
SimpleSpanProcessor,
|
|
19
|
-
} = require('@opentelemetry/sdk-trace-base');
|
|
20
|
-
|
|
21
|
-
const {
|
|
22
|
-
instrumentEmbeddings,
|
|
23
|
-
} = require('../../../src/instrumentation/google/get_embeddings_telemetry');
|
|
24
|
-
|
|
25
|
-
const exporter = new InMemorySpanExporter();
|
|
26
|
-
trace.setGlobalTracerProvider(
|
|
27
|
-
new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
class StubModels {
|
|
31
|
-
embedContentInternal(params = {}) {
|
|
32
|
-
if (params.model === 'boom') return Promise.reject(new Error('api error'));
|
|
33
|
-
return Promise.resolve({ embeddings: [{ values: [0.1] }] });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function instrumented() {
|
|
38
|
-
const patched = [];
|
|
39
|
-
instrumentEmbeddings(StubModels, patched);
|
|
40
|
-
return () => {
|
|
41
|
-
for (const [owner, methodName, original] of patched) owner[methodName] = original;
|
|
42
|
-
exporter.reset();
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
test('embeddings emits genai span with character count and stripped model', async () => {
|
|
47
|
-
const teardown = instrumented();
|
|
48
|
-
try {
|
|
49
|
-
const client = new StubModels();
|
|
50
|
-
await client.embedContentInternal({
|
|
51
|
-
model: 'models/text-embedding-004',
|
|
52
|
-
contents: 'hello world',
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const [span] = exporter.getFinishedSpans();
|
|
56
|
-
assert.equal(span.name, 'embeddings');
|
|
57
|
-
// No apiClient on the stub → Gemini Developer API.
|
|
58
|
-
assert.equal(span.attributes['gen_ai.system'], 'gemini');
|
|
59
|
-
assert.equal(span.attributes['gen_ai.operation.name'], 'embeddings');
|
|
60
|
-
// The "models/" prefix is stripped to match the chat path.
|
|
61
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'text-embedding-004');
|
|
62
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], 'hello world'.length);
|
|
63
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.input.messages']), [
|
|
64
|
-
{ role: 'user', parts: [{ type: 'text', content: 'hello world' }] },
|
|
65
|
-
]);
|
|
66
|
-
assert.equal(span.status.code, SpanStatusCode.UNSET);
|
|
67
|
-
} finally {
|
|
68
|
-
teardown();
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('embeddings counts characters across list/part contents', async () => {
|
|
73
|
-
const teardown = instrumented();
|
|
74
|
-
try {
|
|
75
|
-
const client = new StubModels();
|
|
76
|
-
await client.embedContentInternal({
|
|
77
|
-
model: 'text-embedding-004',
|
|
78
|
-
contents: ['ab', { text: 'cde' }, { parts: [{ text: 'fg' }] }],
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const [span] = exporter.getFinishedSpans();
|
|
82
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], 7);
|
|
83
|
-
} finally {
|
|
84
|
-
teardown();
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('embeddings system is vertex_ai when client targets vertex', async () => {
|
|
89
|
-
const teardown = instrumented();
|
|
90
|
-
try {
|
|
91
|
-
const client = new StubModels();
|
|
92
|
-
// The Vertex backend is read off the api client.
|
|
93
|
-
client.apiClient = { isVertexAI: () => true };
|
|
94
|
-
await client.embedContentInternal({ model: 'text-embedding-004', contents: 'hi' });
|
|
95
|
-
|
|
96
|
-
const [span] = exporter.getFinishedSpans();
|
|
97
|
-
assert.equal(span.attributes['gen_ai.system'], 'vertex_ai');
|
|
98
|
-
} finally {
|
|
99
|
-
teardown();
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('embeddings records error status on failure', async () => {
|
|
104
|
-
const teardown = instrumented();
|
|
105
|
-
try {
|
|
106
|
-
const client = new StubModels();
|
|
107
|
-
await assert.rejects(
|
|
108
|
-
() => client.embedContentInternal({ model: 'boom', contents: 'x' }),
|
|
109
|
-
/api error/
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
const [span] = exporter.getFinishedSpans();
|
|
113
|
-
assert.equal(span.status.code, SpanStatusCode.ERROR);
|
|
114
|
-
} finally {
|
|
115
|
-
teardown();
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test('uninstrument restores the original method', async () => {
|
|
120
|
-
const teardown = instrumented();
|
|
121
|
-
const client = new StubModels();
|
|
122
|
-
await client.embedContentInternal({ model: 'text-embedding-004', contents: 'x' });
|
|
123
|
-
assert.equal(exporter.getFinishedSpans().length, 1);
|
|
124
|
-
teardown();
|
|
125
|
-
// After teardown the prototype method is restored (no new span is recorded).
|
|
126
|
-
await client.embedContentInternal({ model: 'text-embedding-004', contents: 'y' });
|
|
127
|
-
assert.equal(exporter.getFinishedSpans().length, 0);
|
|
128
|
-
});
|