weflayr 0.24.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 +86 -12
- package/dist/{api → src/api}/generated/facade.gen.js +4 -2
- package/dist/src/api/generated/index.d.ts +2 -0
- package/dist/{api → src/api}/generated/index.js +3 -1
- package/dist/{api → src/api}/generated/sdk.gen.d.ts +11 -3
- package/dist/{api → src/api}/generated/sdk.gen.js +34 -4
- package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +436 -153
- 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/{src → dist/src}/instrumentation/google/get_cache_telemetry.js +18 -19
- 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 -1005
- package/eslint.config.mjs +0 -36
- package/index.d.ts +0 -170
- 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 -104
- package/src/api/generated/index.ts +0 -4
- package/src/api/generated/sdk.gen.ts +0 -175
- package/src/api/index.ts +0 -45
- package/src/api/makeEndpoint.ts +0 -54
- package/src/auto-instrument.js +0 -172
- package/src/index.js +0 -19
- package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -118
- package/src/instrumentation/anthropic/get_cache_config_telemetry.js +0 -90
- package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
- package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -55
- package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -39
- package/src/instrumentation/bedrock/get_cache_config_telemetry.js +0 -106
- package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -109
- 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 -40
- 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_operations_telemetry.js +0 -171
- package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
- package/src/instrumentation/google/set_telemetry_attributes.js +0 -104
- 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_cache_config_telemetry.js +0 -63
- 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 -41
- package/src/instrumentation/vercel/full_instrumentation.js +0 -52
- package/src/instrumentation/vercel/get_cache_creation_telemetry.js +0 -70
- package/src/logger.js +0 -8
- package/src/otel/exporter.js +0 -108
- package/src/otel/propagation.js +0 -146
- package/src/otel/span-filter.js +0 -69
- package/src/otel/span-processor.js +0 -170
- 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 -884
- package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -181
- package/tests/instrumentation/anthropic/get_cache_config_telemetry.test.js +0 -92
- package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
- package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -97
- package/tests/instrumentation/bedrock/get_cache_config_telemetry.test.js +0 -107
- package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -118
- 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_cache_operations_telemetry.test.js +0 -130
- 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_cache_config_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/instrumentation/vercel/get_cache_creation_telemetry.test.js +0 -96
- 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,219 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the Gemini generate_content enrichment complement.
|
|
4
|
-
//
|
|
5
|
-
// It stamps `gen_ai.system` (vertex_ai/gemini) and audio token details onto the
|
|
6
|
-
// ACTIVE span (traceloop's chat span) when `generateContent{,Stream}Internal`
|
|
7
|
-
// runs. The @google/genai `Models` class is stubbed; the test plays traceloop by
|
|
8
|
-
// starting a span and making it active around the call.
|
|
9
|
-
|
|
10
|
-
const test = require('node:test');
|
|
11
|
-
const assert = require('node:assert/strict');
|
|
12
|
-
const Module = require('node:module');
|
|
13
|
-
|
|
14
|
-
const { trace, context } = require('@opentelemetry/api');
|
|
15
|
-
const {
|
|
16
|
-
BasicTracerProvider,
|
|
17
|
-
InMemorySpanExporter,
|
|
18
|
-
SimpleSpanProcessor,
|
|
19
|
-
} = require('@opentelemetry/sdk-trace-base');
|
|
20
|
-
const { AsyncLocalStorageContextManager } = require('@opentelemetry/context-async-hooks');
|
|
21
|
-
|
|
22
|
-
const {
|
|
23
|
-
GoogleTelemetryInstrumentor,
|
|
24
|
-
} = require('../../../src/instrumentation/google/set_telemetry_attributes');
|
|
25
|
-
|
|
26
|
-
const GENAI_MODULE = '@google/genai';
|
|
27
|
-
|
|
28
|
-
// The exact chunk objects the streaming stub yields. Shared so the passthrough
|
|
29
|
-
// test can assert object identity — proof our wrapper re-yields the user's chunks
|
|
30
|
-
// without copying or mutating them. The last chunk carries the streamed usage.
|
|
31
|
-
const STREAM_CHUNKS = [
|
|
32
|
-
{ text: 'hello ' },
|
|
33
|
-
{ text: 'world', usageMetadata: { cachedContentTokenCount: 32 } },
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const exporter = new InMemorySpanExporter();
|
|
37
|
-
trace.setGlobalTracerProvider(
|
|
38
|
-
new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
|
|
39
|
-
);
|
|
40
|
-
context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable());
|
|
41
|
-
|
|
42
|
-
class StubModels {
|
|
43
|
-
generateContentInternal(params = {}) {
|
|
44
|
-
return Promise.resolve({
|
|
45
|
-
text: 'ok',
|
|
46
|
-
usageMetadata: {
|
|
47
|
-
promptTokensDetails: params.audioInput
|
|
48
|
-
? [{ modality: 'AUDIO', tokenCount: 9 }]
|
|
49
|
-
: [{ modality: 'TEXT', tokenCount: 2 }],
|
|
50
|
-
candidatesTokensDetails: params.audioOutput
|
|
51
|
-
? [{ modality: 'AUDIO', tokenCount: 17 }]
|
|
52
|
-
: [{ modality: 'TEXT', tokenCount: 5 }],
|
|
53
|
-
cachedContentTokenCount: params.cached || 0,
|
|
54
|
-
cacheTokensDetails: params.cacheDetails,
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
generateContentStreamInternal() {
|
|
60
|
-
return Promise.resolve(
|
|
61
|
-
(async function* () {
|
|
62
|
-
for (const chunk of STREAM_CHUNKS) yield chunk;
|
|
63
|
-
})()
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function instrumented() {
|
|
69
|
-
const stubs = { [GENAI_MODULE]: { Models: StubModels } };
|
|
70
|
-
const origResolve = Module._resolveFilename;
|
|
71
|
-
Module._resolveFilename = function patched(request, ...rest) {
|
|
72
|
-
if (request in stubs) return request;
|
|
73
|
-
return origResolve.call(this, request, ...rest);
|
|
74
|
-
};
|
|
75
|
-
for (const [moduleId, exports] of Object.entries(stubs)) {
|
|
76
|
-
require.cache[moduleId] = { id: moduleId, filename: moduleId, loaded: true, exports };
|
|
77
|
-
}
|
|
78
|
-
const instrumentor = new GoogleTelemetryInstrumentor();
|
|
79
|
-
instrumentor.instrument();
|
|
80
|
-
return () => {
|
|
81
|
-
instrumentor.uninstrument();
|
|
82
|
-
Module._resolveFilename = origResolve;
|
|
83
|
-
for (const moduleId of Object.keys(stubs)) delete require.cache[moduleId];
|
|
84
|
-
exporter.reset();
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function withActiveSpan(fn) {
|
|
89
|
-
const span = trace.getTracer('test').startSpan('generate_content');
|
|
90
|
-
await context.with(trace.setSpan(context.active(), span), fn);
|
|
91
|
-
span.end();
|
|
92
|
-
return exporter.getFinishedSpans().at(-1);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
test('stamps provider gemini and audio output tokens on the active span', async () => {
|
|
96
|
-
const teardown = instrumented();
|
|
97
|
-
try {
|
|
98
|
-
const client = new StubModels();
|
|
99
|
-
client.apiClient = { isVertexAI: () => false };
|
|
100
|
-
const span = await withActiveSpan(() =>
|
|
101
|
-
client.generateContentInternal({ model: 'tts', audioOutput: true })
|
|
102
|
-
);
|
|
103
|
-
assert.equal(span.attributes['gen_ai.system'], 'gemini');
|
|
104
|
-
assert.equal(span.attributes['gen_ai.usage.output_tokens_details.audio_tokens'], 17);
|
|
105
|
-
assert.equal(span.attributes['gen_ai.usage.input_tokens_details.audio_tokens'], undefined);
|
|
106
|
-
} finally {
|
|
107
|
-
teardown();
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('stamps provider vertex_ai and audio input tokens', async () => {
|
|
112
|
-
const teardown = instrumented();
|
|
113
|
-
try {
|
|
114
|
-
const client = new StubModels();
|
|
115
|
-
client.apiClient = { isVertexAI: () => true };
|
|
116
|
-
const span = await withActiveSpan(() =>
|
|
117
|
-
client.generateContentInternal({ model: 'm', audioInput: true })
|
|
118
|
-
);
|
|
119
|
-
assert.equal(span.attributes['gen_ai.system'], 'vertex_ai');
|
|
120
|
-
assert.equal(span.attributes['gen_ai.usage.input_tokens_details.audio_tokens'], 9);
|
|
121
|
-
} finally {
|
|
122
|
-
teardown();
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test('no audio token attributes for a text-only call', async () => {
|
|
127
|
-
const teardown = instrumented();
|
|
128
|
-
try {
|
|
129
|
-
const client = new StubModels();
|
|
130
|
-
client.apiClient = { isVertexAI: () => false };
|
|
131
|
-
const span = await withActiveSpan(() => client.generateContentInternal({ model: 'm' }));
|
|
132
|
-
assert.equal(span.attributes['gen_ai.usage.output_tokens_details.audio_tokens'], undefined);
|
|
133
|
-
assert.equal(span.attributes['gen_ai.usage.cache_read_input_tokens'], undefined);
|
|
134
|
-
assert.equal(span.attributes['gen_ai.system'], 'gemini');
|
|
135
|
-
} finally {
|
|
136
|
-
teardown();
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('stamps cached-content tokens on the active span', async () => {
|
|
141
|
-
const teardown = instrumented();
|
|
142
|
-
try {
|
|
143
|
-
const client = new StubModels();
|
|
144
|
-
client.apiClient = { isVertexAI: () => false };
|
|
145
|
-
const span = await withActiveSpan(() =>
|
|
146
|
-
client.generateContentInternal({ model: 'm', cached: 128 })
|
|
147
|
-
);
|
|
148
|
-
assert.equal(span.attributes['gen_ai.usage.cache_read_input_tokens'], 128);
|
|
149
|
-
assert.equal(
|
|
150
|
-
span.attributes['gen_ai.usage.input_tokens_details.cached_audio_tokens'],
|
|
151
|
-
undefined
|
|
152
|
-
);
|
|
153
|
-
} finally {
|
|
154
|
-
teardown();
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test('splits cached tokens into audio and text remainder by modality', async () => {
|
|
159
|
-
const teardown = instrumented();
|
|
160
|
-
try {
|
|
161
|
-
const client = new StubModels();
|
|
162
|
-
client.apiClient = { isVertexAI: () => false };
|
|
163
|
-
// 200 cached tokens, 50 of them audio: audio is billed separately, the rest
|
|
164
|
-
// stays as text cache-read so the two never double-count.
|
|
165
|
-
const span = await withActiveSpan(() =>
|
|
166
|
-
client.generateContentInternal({
|
|
167
|
-
model: 'm',
|
|
168
|
-
cached: 200,
|
|
169
|
-
cacheDetails: [
|
|
170
|
-
{ modality: 'TEXT', tokenCount: 150 },
|
|
171
|
-
{ modality: 'AUDIO', tokenCount: 50 },
|
|
172
|
-
],
|
|
173
|
-
})
|
|
174
|
-
);
|
|
175
|
-
assert.equal(span.attributes['gen_ai.usage.input_tokens_details.cached_audio_tokens'], 50);
|
|
176
|
-
assert.equal(span.attributes['gen_ai.usage.cache_read_input_tokens'], 150);
|
|
177
|
-
} finally {
|
|
178
|
-
teardown();
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test('streaming sets provider only and uninstrument restores methods', async () => {
|
|
183
|
-
const teardown = instrumented();
|
|
184
|
-
try {
|
|
185
|
-
const client = new StubModels();
|
|
186
|
-
client.apiClient = { isVertexAI: () => true };
|
|
187
|
-
const span = await withActiveSpan(() => client.generateContentStreamInternal({ model: 'm' }));
|
|
188
|
-
assert.equal(span.attributes['gen_ai.system'], 'vertex_ai');
|
|
189
|
-
} finally {
|
|
190
|
-
teardown();
|
|
191
|
-
}
|
|
192
|
-
// After teardown the prototype method is restored.
|
|
193
|
-
const client = new StubModels();
|
|
194
|
-
assert.equal(typeof client.generateContentInternal, 'function');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test('streaming re-yields the user chunks unchanged and stamps cache off the stream', async () => {
|
|
198
|
-
const teardown = instrumented();
|
|
199
|
-
try {
|
|
200
|
-
const client = new StubModels();
|
|
201
|
-
client.apiClient = { isVertexAI: () => false };
|
|
202
|
-
const collected = [];
|
|
203
|
-
const span = await withActiveSpan(async () => {
|
|
204
|
-
const stream = await client.generateContentStreamInternal({ model: 'm' });
|
|
205
|
-
for await (const chunk of stream) collected.push(chunk);
|
|
206
|
-
});
|
|
207
|
-
// Same count, order, and object identity: observeCache re-yields each chunk
|
|
208
|
-
// by reference — no telemetry collision with the caller's payload.
|
|
209
|
-
assert.equal(collected.length, STREAM_CHUNKS.length);
|
|
210
|
-
collected.forEach((chunk, index) => {
|
|
211
|
-
assert.equal(chunk, STREAM_CHUNKS[index]);
|
|
212
|
-
assert.equal(chunk.text, STREAM_CHUNKS[index].text);
|
|
213
|
-
});
|
|
214
|
-
// The wrapper still ran: cache tokens from the final chunk landed on the span.
|
|
215
|
-
assert.equal(span.attributes['gen_ai.usage.cache_read_input_tokens'], 32);
|
|
216
|
-
} finally {
|
|
217
|
-
teardown();
|
|
218
|
-
}
|
|
219
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the bundled OpenAI audio instrumentor.
|
|
4
|
-
//
|
|
5
|
-
// The real `openai` audio resource classes are stubbed with `create` methods
|
|
6
|
-
// that mirror the SDK's shapes — Text-to-Speech resolves to the audio bytes and
|
|
7
|
-
// Speech-to-Text to a transcription carrying `duration` — so the instrumentor
|
|
8
|
-
// can be exercised without 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
|
-
OpenAIAudioInstrumentor,
|
|
23
|
-
} = require('../../../src/instrumentation/openai/get_audio_telemetry');
|
|
24
|
-
|
|
25
|
-
const SPEECH_MODULE = 'openai/resources/audio/speech';
|
|
26
|
-
const TRANSCRIPTIONS_MODULE = 'openai/resources/audio/transcriptions';
|
|
27
|
-
|
|
28
|
-
const exporter = new InMemorySpanExporter();
|
|
29
|
-
trace.setGlobalTracerProvider(
|
|
30
|
-
new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
class StubSpeech {
|
|
34
|
-
create(body = {}) {
|
|
35
|
-
if (body.input === 'boom') return Promise.reject(new Error('api error'));
|
|
36
|
-
return Promise.resolve(Buffer.from('audio-bytes'));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class StubTranscriptions {
|
|
41
|
-
create(body = {}) {
|
|
42
|
-
if (body.model === 'boom') return Promise.reject(new Error('api error'));
|
|
43
|
-
return Promise.resolve({ duration: 12.4, text: 'hello' });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Stub the openai audio resource modules, install the instrumentor + the
|
|
48
|
-
// in-memory exporter, and return a teardown that restores everything.
|
|
49
|
-
function instrumented() {
|
|
50
|
-
const stubs = {
|
|
51
|
-
[SPEECH_MODULE]: { Speech: StubSpeech },
|
|
52
|
-
[TRANSCRIPTIONS_MODULE]: { Transcriptions: StubTranscriptions },
|
|
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 OpenAIAudioInstrumentor();
|
|
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('speech emits genai span with char count', async () => {
|
|
75
|
-
const teardown = instrumented();
|
|
76
|
-
try {
|
|
77
|
-
const client = new StubSpeech();
|
|
78
|
-
assert.deepEqual(
|
|
79
|
-
await client.create({ input: 'hello world', model: 'tts-1', voice: 'alloy' }),
|
|
80
|
-
Buffer.from('audio-bytes')
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const [span] = exporter.getFinishedSpans();
|
|
84
|
-
assert.equal(span.name, 'text_to_speech');
|
|
85
|
-
assert.equal(span.attributes['gen_ai.system'], 'openai');
|
|
86
|
-
assert.equal(span.attributes['gen_ai.operation.name'], 'text_to_speech');
|
|
87
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'tts-1');
|
|
88
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], 'hello world'.length);
|
|
89
|
-
assert.equal(span.attributes['gen_ai.usage.input_seconds'], undefined);
|
|
90
|
-
assert.equal(span.status.code, SpanStatusCode.UNSET);
|
|
91
|
-
} finally {
|
|
92
|
-
teardown();
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('speech records error status on failure', async () => {
|
|
97
|
-
const teardown = instrumented();
|
|
98
|
-
try {
|
|
99
|
-
const client = new StubSpeech();
|
|
100
|
-
await assert.rejects(() => client.create({ input: 'boom', model: 'tts-1' }), /api error/);
|
|
101
|
-
|
|
102
|
-
const [span] = exporter.getFinishedSpans();
|
|
103
|
-
assert.equal(span.status.code, SpanStatusCode.ERROR);
|
|
104
|
-
} finally {
|
|
105
|
-
teardown();
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('transcriptions emits span with audio duration', async () => {
|
|
110
|
-
const teardown = instrumented();
|
|
111
|
-
try {
|
|
112
|
-
const client = new StubTranscriptions();
|
|
113
|
-
const result = await client.create({ file: Buffer.from('audio'), model: 'whisper-1' });
|
|
114
|
-
assert.equal(result.duration, 12.4);
|
|
115
|
-
|
|
116
|
-
const [span] = exporter.getFinishedSpans();
|
|
117
|
-
assert.equal(span.name, 'speech_to_text');
|
|
118
|
-
assert.equal(span.attributes['gen_ai.system'], 'openai');
|
|
119
|
-
assert.equal(span.attributes['gen_ai.operation.name'], 'speech_to_text');
|
|
120
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'whisper-1');
|
|
121
|
-
assert.equal(span.attributes['gen_ai.usage.input_seconds'], 12.4);
|
|
122
|
-
assert.equal(span.attributes['gen_ai.usage.input_characters'], undefined);
|
|
123
|
-
assert.equal(span.status.code, SpanStatusCode.UNSET);
|
|
124
|
-
} finally {
|
|
125
|
-
teardown();
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test('transcriptions records error status on failure', async () => {
|
|
130
|
-
const teardown = instrumented();
|
|
131
|
-
try {
|
|
132
|
-
const client = new StubTranscriptions();
|
|
133
|
-
await assert.rejects(
|
|
134
|
-
() => client.create({ file: Buffer.from('audio'), model: 'boom' }),
|
|
135
|
-
/api error/
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const [span] = exporter.getFinishedSpans();
|
|
139
|
-
assert.equal(span.status.code, SpanStatusCode.ERROR);
|
|
140
|
-
} finally {
|
|
141
|
-
teardown();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test('uninstrument restores original methods', async () => {
|
|
146
|
-
const teardown = instrumented();
|
|
147
|
-
try {
|
|
148
|
-
const client = new StubSpeech();
|
|
149
|
-
await client.create({ input: 'x', model: 'tts-1' });
|
|
150
|
-
assert.equal(exporter.getFinishedSpans().length, 1);
|
|
151
|
-
} finally {
|
|
152
|
-
teardown();
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
test('system is azure when client targets azure', async () => {
|
|
157
|
-
const teardown = instrumented();
|
|
158
|
-
try {
|
|
159
|
-
const client = new StubSpeech();
|
|
160
|
-
// Azure is reached via the same openai SDK; the vendor is read off the
|
|
161
|
-
// client base URL so the span is attributed to azure.ai.openai, not openai.
|
|
162
|
-
client._client = { baseURL: 'https://acme.openai.azure.com/' };
|
|
163
|
-
await client.create({ input: 'hi', model: 'tts-1' });
|
|
164
|
-
|
|
165
|
-
const [span] = exporter.getFinishedSpans();
|
|
166
|
-
assert.equal(span.attributes['gen_ai.system'], 'azure.ai.openai');
|
|
167
|
-
} finally {
|
|
168
|
-
teardown();
|
|
169
|
-
}
|
|
170
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the OpenAI non-streaming cached-prompt token complement. It wraps the
|
|
4
|
-
// primary instrumentor's `_endSpan({ span, type, result })` to stamp
|
|
5
|
-
// `gen_ai.usage.cache_read_input_tokens` from `usage.prompt_tokens_details.cached_tokens`.
|
|
6
|
-
|
|
7
|
-
const test = require('node:test');
|
|
8
|
-
const assert = require('node:assert/strict');
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
OpenAIPromptCachingInstrumentor,
|
|
12
|
-
} = require('../../../src/instrumentation/openai/get_cache_and_reasoning_telemetry');
|
|
13
|
-
|
|
14
|
-
function fakeSpan() {
|
|
15
|
-
const attrs = {};
|
|
16
|
-
return {
|
|
17
|
-
attrs,
|
|
18
|
-
isRecording: () => true,
|
|
19
|
-
setAttribute(key, value) {
|
|
20
|
-
attrs[key] = value;
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
test('stamps cache_read_input_tokens and still calls the original _endSpan', () => {
|
|
26
|
-
const calls = [];
|
|
27
|
-
const instrumentation = {
|
|
28
|
-
_endSpan(args) {
|
|
29
|
-
calls.push(args);
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
new OpenAIPromptCachingInstrumentor().instrument(instrumentation);
|
|
33
|
-
|
|
34
|
-
const span = fakeSpan();
|
|
35
|
-
instrumentation._endSpan({
|
|
36
|
-
span,
|
|
37
|
-
type: 'chat',
|
|
38
|
-
result: { usage: { prompt_tokens: 1600, prompt_tokens_details: { cached_tokens: 1536 } } },
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], 1536);
|
|
42
|
-
assert.equal(calls.length, 1);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('sets nothing when there are no cached tokens', () => {
|
|
46
|
-
const instrumentation = { _endSpan() {} };
|
|
47
|
-
new OpenAIPromptCachingInstrumentor().instrument(instrumentation);
|
|
48
|
-
|
|
49
|
-
const span = fakeSpan();
|
|
50
|
-
instrumentation._endSpan({ span, type: 'chat', result: { usage: { prompt_tokens: 10 } } });
|
|
51
|
-
|
|
52
|
-
assert.equal(span.attrs['gen_ai.usage.cache_read_input_tokens'], undefined);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('stamps reasoning_tokens from completion_tokens_details', () => {
|
|
56
|
-
const instrumentation = { _endSpan() {} };
|
|
57
|
-
new OpenAIPromptCachingInstrumentor().instrument(instrumentation);
|
|
58
|
-
|
|
59
|
-
const span = fakeSpan();
|
|
60
|
-
instrumentation._endSpan({
|
|
61
|
-
span,
|
|
62
|
-
type: 'chat',
|
|
63
|
-
result: { usage: { output_tokens: 115, completion_tokens_details: { reasoning_tokens: 64 } } },
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
assert.equal(span.attrs['gen_ai.usage.reasoning_tokens'], 64);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('is a no-op when the instrumentor has no _endSpan', () => {
|
|
70
|
-
const instrumentation = {};
|
|
71
|
-
new OpenAIPromptCachingInstrumentor().instrument(instrumentation);
|
|
72
|
-
assert.equal(instrumentation._endSpan, undefined);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('uninstrument restores the original _endSpan', () => {
|
|
76
|
-
const original = () => {};
|
|
77
|
-
const instrumentation = { _endSpan: original };
|
|
78
|
-
const instrumentor = new OpenAIPromptCachingInstrumentor();
|
|
79
|
-
instrumentor.instrument(instrumentation);
|
|
80
|
-
assert.notEqual(instrumentation._endSpan, original);
|
|
81
|
-
instrumentor.uninstrument();
|
|
82
|
-
assert.equal(instrumentation._endSpan, original);
|
|
83
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the OpenAI request-side prompt-cache param capture.
|
|
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
|
-
OpenAICacheConfigInstrumentor,
|
|
13
|
-
} = require('../../../src/instrumentation/openai/get_cache_config_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() {
|
|
28
|
-
return makeFakeSpan();
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
test('stamps prompt cache key and retention from the request params', () => {
|
|
34
|
-
const instrumentation = makeStubInstrumentation();
|
|
35
|
-
new OpenAICacheConfigInstrumentor().instrument(instrumentation);
|
|
36
|
-
|
|
37
|
-
const span = instrumentation.startSpan({
|
|
38
|
-
type: 'chat',
|
|
39
|
-
params: {
|
|
40
|
-
model: 'gpt-4o-mini',
|
|
41
|
-
prompt_cache_key: 'session-42',
|
|
42
|
-
prompt_cache_retention: '24h',
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
assert.equal(span.attributes['weflayr.cache.key'], 'session-42');
|
|
47
|
-
assert.equal(span.attributes['weflayr.cache.retention'], '24h');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('stamps prompt cache options ttl as the retention', () => {
|
|
51
|
-
const instrumentation = makeStubInstrumentation();
|
|
52
|
-
new OpenAICacheConfigInstrumentor().instrument(instrumentation);
|
|
53
|
-
|
|
54
|
-
const span = instrumentation.startSpan({
|
|
55
|
-
type: 'chat',
|
|
56
|
-
params: {
|
|
57
|
-
model: 'gpt-5.6',
|
|
58
|
-
prompt_cache_options: { ttl: '30m' },
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
assert.equal(span.attributes['weflayr.cache.retention'], '30m');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('no attributes without cache params', () => {
|
|
66
|
-
const instrumentation = makeStubInstrumentation();
|
|
67
|
-
new OpenAICacheConfigInstrumentor().instrument(instrumentation);
|
|
68
|
-
|
|
69
|
-
const span = instrumentation.startSpan({ type: 'chat', params: { model: 'gpt-4o-mini' } });
|
|
70
|
-
|
|
71
|
-
assert.ok(!('weflayr.cache.key' in span.attributes));
|
|
72
|
-
assert.ok(!('weflayr.cache.retention' in span.attributes));
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('uninstrument restores the original startSpan', () => {
|
|
76
|
-
const instrumentation = makeStubInstrumentation();
|
|
77
|
-
const original = instrumentation.startSpan;
|
|
78
|
-
const instrumentor = new OpenAICacheConfigInstrumentor();
|
|
79
|
-
instrumentor.instrument(instrumentation);
|
|
80
|
-
assert.notEqual(instrumentation.startSpan, original);
|
|
81
|
-
instrumentor.uninstrument();
|
|
82
|
-
assert.equal(instrumentation.startSpan, original);
|
|
83
|
-
});
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the bundled OpenAI embeddings instrumentor.
|
|
4
|
-
//
|
|
5
|
-
// The real `openai` Embeddings resource class is stubbed with a `create` method
|
|
6
|
-
// that mirrors the SDK's shape — resolving to a response carrying `model` and a
|
|
7
|
-
// `usage.prompt_tokens` count — 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
|
-
OpenAIEmbeddingsInstrumentor,
|
|
23
|
-
} = require('../../../src/instrumentation/openai/get_embeddings_telemetry');
|
|
24
|
-
|
|
25
|
-
const EMBEDDINGS_MODULE = 'openai/resources/embeddings';
|
|
26
|
-
|
|
27
|
-
const exporter = new InMemorySpanExporter();
|
|
28
|
-
trace.setGlobalTracerProvider(
|
|
29
|
-
new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
class StubEmbeddings {
|
|
33
|
-
create(body = {}) {
|
|
34
|
-
if (body.model === 'boom') return Promise.reject(new Error('api error'));
|
|
35
|
-
return Promise.resolve({
|
|
36
|
-
object: 'list',
|
|
37
|
-
data: [{ embedding: [0.1, 0.2] }],
|
|
38
|
-
model: 'text-embedding-3-small',
|
|
39
|
-
usage: { prompt_tokens: 8, total_tokens: 8 },
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Stub the openai embeddings resource module, install the instrumentor + the
|
|
45
|
-
// in-memory exporter, and return a teardown that restores everything.
|
|
46
|
-
function instrumented() {
|
|
47
|
-
const stubs = { [EMBEDDINGS_MODULE]: { Embeddings: StubEmbeddings } };
|
|
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 OpenAIEmbeddingsInstrumentor();
|
|
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
|
-
test('embeddings emits genai span with prompt tokens', async () => {
|
|
69
|
-
const teardown = instrumented();
|
|
70
|
-
try {
|
|
71
|
-
const client = new StubEmbeddings();
|
|
72
|
-
const result = await client.create({ input: 'hello', model: 'text-embedding-3-small' });
|
|
73
|
-
assert.equal(result.usage.prompt_tokens, 8);
|
|
74
|
-
|
|
75
|
-
const [span] = exporter.getFinishedSpans();
|
|
76
|
-
assert.equal(span.name, 'embeddings');
|
|
77
|
-
assert.equal(span.attributes['gen_ai.system'], 'openai');
|
|
78
|
-
assert.equal(span.attributes['gen_ai.operation.name'], 'embeddings');
|
|
79
|
-
assert.equal(span.attributes['gen_ai.request.model'], 'text-embedding-3-small');
|
|
80
|
-
assert.equal(span.attributes['gen_ai.response.model'], 'text-embedding-3-small');
|
|
81
|
-
assert.equal(span.attributes['gen_ai.usage.input_tokens'], 8);
|
|
82
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.input.messages']), [
|
|
83
|
-
{ role: 'user', parts: [{ type: 'text', content: 'hello' }] },
|
|
84
|
-
]);
|
|
85
|
-
assert.equal(span.status.code, SpanStatusCode.UNSET);
|
|
86
|
-
} finally {
|
|
87
|
-
teardown();
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('embeddings emits one input message per element of a list input', async () => {
|
|
92
|
-
const teardown = instrumented();
|
|
93
|
-
try {
|
|
94
|
-
const client = new StubEmbeddings();
|
|
95
|
-
await client.create({ input: ['a', 'b'], model: 'text-embedding-3-small' });
|
|
96
|
-
|
|
97
|
-
const [span] = exporter.getFinishedSpans();
|
|
98
|
-
assert.deepEqual(JSON.parse(span.attributes['gen_ai.input.messages']), [
|
|
99
|
-
{ role: 'user', parts: [{ type: 'text', content: 'a' }] },
|
|
100
|
-
{ role: 'user', parts: [{ type: 'text', content: 'b' }] },
|
|
101
|
-
]);
|
|
102
|
-
} finally {
|
|
103
|
-
teardown();
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test('embeddings records error status on failure', async () => {
|
|
108
|
-
const teardown = instrumented();
|
|
109
|
-
try {
|
|
110
|
-
const client = new StubEmbeddings();
|
|
111
|
-
await assert.rejects(() => client.create({ input: 'x', model: 'boom' }), /api error/);
|
|
112
|
-
|
|
113
|
-
const [span] = exporter.getFinishedSpans();
|
|
114
|
-
assert.equal(span.status.code, SpanStatusCode.ERROR);
|
|
115
|
-
} finally {
|
|
116
|
-
teardown();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test('uninstrument restores original methods', async () => {
|
|
121
|
-
const teardown = instrumented();
|
|
122
|
-
try {
|
|
123
|
-
const client = new StubEmbeddings();
|
|
124
|
-
await client.create({ input: 'x', model: 'text-embedding-3-small' });
|
|
125
|
-
assert.equal(exporter.getFinishedSpans().length, 1);
|
|
126
|
-
} finally {
|
|
127
|
-
teardown();
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test('system is azure when client targets azure', async () => {
|
|
132
|
-
const teardown = instrumented();
|
|
133
|
-
try {
|
|
134
|
-
const client = new StubEmbeddings();
|
|
135
|
-
// Azure is reached via the same openai SDK; the vendor is read off the
|
|
136
|
-
// client base URL so the span is attributed to azure.ai.openai, not openai.
|
|
137
|
-
client._client = { baseURL: 'https://acme.openai.azure.com/' };
|
|
138
|
-
await client.create({ input: 'hi', model: 'text-embedding-3-small' });
|
|
139
|
-
|
|
140
|
-
const [span] = exporter.getFinishedSpans();
|
|
141
|
-
assert.equal(span.attributes['gen_ai.system'], 'azure.ai.openai');
|
|
142
|
-
} finally {
|
|
143
|
-
teardown();
|
|
144
|
-
}
|
|
145
|
-
});
|