weflayr 0.24.0 → 0.26.1
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/package.json +51 -0
- 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 +12 -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
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Reasoning (thinking) token enrichment for the Google GenAI (Gemini) chat span.
|
|
4
|
+
*
|
|
5
|
+
* Neither instrumentor surfaces the thinking budget. Gemini reports thinking
|
|
6
|
+
* under `usageMetadata.thoughtsTokenCount`, which is SEPARATE from
|
|
7
|
+
* `candidatesTokenCount` (the visible output) and excluded from the totals, so
|
|
8
|
+
* without this the thinking tokens are lost. Stamp them as
|
|
9
|
+
* `gen_ai.usage.thoughts_token_count` so the normalizer can fold them into the
|
|
10
|
+
* completion count.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.setReasoningTelemetry = setReasoningTelemetry;
|
|
14
|
+
function setReasoningTelemetry(span, response) {
|
|
15
|
+
const usage = response && response.usageMetadata;
|
|
16
|
+
if (!usage)
|
|
17
|
+
return;
|
|
18
|
+
const thoughts = usage.thoughtsTokenCount || 0;
|
|
19
|
+
if (thoughts)
|
|
20
|
+
span.setAttribute('gen_ai.usage.thoughts_token_count', thoughts);
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured-output schema capture for the Google GenAI (Gemini) chat span.
|
|
3
|
+
*
|
|
4
|
+
* Gemini pins an answer to a JSON schema with `config.responseJsonSchema` (a
|
|
5
|
+
* plain JSON schema) or `config.responseSchema` (the SDK's own `Schema` object).
|
|
6
|
+
* The instrumentor records only that the output is JSON, never the schema.
|
|
7
|
+
*
|
|
8
|
+
* Google is wrapped at its own call site rather than through a traceloop
|
|
9
|
+
* `startSpan`, so this stamps directly instead of supplying a reader.
|
|
10
|
+
*/
|
|
11
|
+
import type { Span } from '@opentelemetry/api';
|
|
12
|
+
export declare function setInputStructuredOutputSchemaTelemetry(span: Span, config: any): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Structured-output schema capture for the Google GenAI (Gemini) chat span.
|
|
4
|
+
*
|
|
5
|
+
* Gemini pins an answer to a JSON schema with `config.responseJsonSchema` (a
|
|
6
|
+
* plain JSON schema) or `config.responseSchema` (the SDK's own `Schema` object).
|
|
7
|
+
* The instrumentor records only that the output is JSON, never the schema.
|
|
8
|
+
*
|
|
9
|
+
* Google is wrapped at its own call site rather than through a traceloop
|
|
10
|
+
* `startSpan`, so this stamps directly instead of supplying a reader.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.setInputStructuredOutputSchemaTelemetry = setInputStructuredOutputSchemaTelemetry;
|
|
14
|
+
const get_structured_output_schema_telemetry_1 = require("../shared/get_structured_output_schema_telemetry");
|
|
15
|
+
function setInputStructuredOutputSchemaTelemetry(span, config) {
|
|
16
|
+
if (!config)
|
|
17
|
+
return;
|
|
18
|
+
(0, get_structured_output_schema_telemetry_1.setStructuredOutputSchemaTelemetry)(span, config.responseJsonSchema || config.responseSchema);
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google GenAI (Gemini) telemetry complement — the registered orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Wires every weflayr-supplied patch the traceloop Google instrumentor misses:
|
|
5
|
+
* - chat span enrichment (one wrap of generateContent{,Stream}Internal that
|
|
6
|
+
* stamps provider name + audio tokens + cache tokens — see the get_*_telemetry
|
|
7
|
+
* files), and
|
|
8
|
+
* - the embeddings endpoint it doesn't cover at all.
|
|
9
|
+
*
|
|
10
|
+
* The public arrows delegate to these private prototype methods while the
|
|
11
|
+
* traceloop span is the active span, so enrichment lands on that span.
|
|
12
|
+
*/
|
|
13
|
+
export declare class GoogleTelemetryInstrumentor {
|
|
14
|
+
private _patched;
|
|
15
|
+
instrument(): void;
|
|
16
|
+
private _wrapChat;
|
|
17
|
+
uninstrument(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Google GenAI (Gemini) telemetry complement — the registered orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* Wires every weflayr-supplied patch the traceloop Google instrumentor misses:
|
|
6
|
+
* - chat span enrichment (one wrap of generateContent{,Stream}Internal that
|
|
7
|
+
* stamps provider name + audio tokens + cache tokens — see the get_*_telemetry
|
|
8
|
+
* files), and
|
|
9
|
+
* - the embeddings endpoint it doesn't cover at all.
|
|
10
|
+
*
|
|
11
|
+
* The public arrows delegate to these private prototype methods while the
|
|
12
|
+
* traceloop span is the active span, so enrichment lands on that span.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.GoogleTelemetryInstrumentor = void 0;
|
|
16
|
+
const api_1 = require("@opentelemetry/api");
|
|
17
|
+
const get_provider_name_1 = require("./get_provider_name");
|
|
18
|
+
const get_audio_telemetry_1 = require("./get_audio_telemetry");
|
|
19
|
+
const get_cache_telemetry_1 = require("./get_cache_telemetry");
|
|
20
|
+
const get_reasoning_telemetry_1 = require("./get_reasoning_telemetry");
|
|
21
|
+
const get_structured_output_schema_telemetry_1 = require("./get_structured_output_schema_telemetry");
|
|
22
|
+
const get_embeddings_telemetry_1 = require("./get_embeddings_telemetry");
|
|
23
|
+
const get_cache_operations_telemetry_1 = require("./get_cache_operations_telemetry");
|
|
24
|
+
class GoogleTelemetryInstrumentor {
|
|
25
|
+
// [ownerPrototype, methodName, originalFunction] for uninstrument().
|
|
26
|
+
_patched = [];
|
|
27
|
+
instrument() {
|
|
28
|
+
let Models;
|
|
29
|
+
let Caches;
|
|
30
|
+
try {
|
|
31
|
+
({ Models, Caches } = require('@google/genai'));
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new Error('weflayr.autoInstrument(GOOGLE_GENAI): the @google/genai client is not installed. ' +
|
|
35
|
+
'Install it with: npm install @google/genai\n' +
|
|
36
|
+
`Underlying error: ${err.message}`);
|
|
37
|
+
}
|
|
38
|
+
// Non-streaming: stamp provider up front, audio + cache tokens off the response.
|
|
39
|
+
this._wrapChat(Models.prototype, 'generateContentInternal', { stream: false });
|
|
40
|
+
// Streaming: stamp provider, then cache tokens off the final chunk's usageMetadata.
|
|
41
|
+
this._wrapChat(Models.prototype, 'generateContentStreamInternal', { stream: true });
|
|
42
|
+
// Embeddings: an endpoint the traceloop instrumentor doesn't cover at all.
|
|
43
|
+
(0, get_embeddings_telemetry_1.instrumentEmbeddings)(Models, this._patched);
|
|
44
|
+
// Explicit-cache lifecycle (caches.create/update/delete): endpoints the
|
|
45
|
+
// traceloop instrumentor doesn't cover at all.
|
|
46
|
+
if (Caches)
|
|
47
|
+
(0, get_cache_operations_telemetry_1.instrumentCacheOperations)(Caches, this._patched);
|
|
48
|
+
}
|
|
49
|
+
_wrapChat(owner, methodName, { stream }) {
|
|
50
|
+
const original = owner[methodName];
|
|
51
|
+
function wrapper(...args) {
|
|
52
|
+
const span = api_1.trace.getActiveSpan();
|
|
53
|
+
if (!span || !span.isRecording || !span.isRecording()) {
|
|
54
|
+
return original.apply(this, args);
|
|
55
|
+
}
|
|
56
|
+
span.setAttribute('gen_ai.system', (0, get_provider_name_1.getProviderName)(this));
|
|
57
|
+
const requestConfig = args[0] && args[0].config;
|
|
58
|
+
(0, get_cache_telemetry_1.setCacheConfigTelemetry)(span, requestConfig);
|
|
59
|
+
(0, get_structured_output_schema_telemetry_1.setInputStructuredOutputSchemaTelemetry)(span, requestConfig);
|
|
60
|
+
if (!stream) {
|
|
61
|
+
return Promise.resolve(original.apply(this, args)).then((response) => {
|
|
62
|
+
(0, get_audio_telemetry_1.setAudioTelemetry)(span, response);
|
|
63
|
+
(0, get_cache_telemetry_1.setCacheTelemetry)(span, response);
|
|
64
|
+
(0, get_reasoning_telemetry_1.setReasoningTelemetry)(span, response);
|
|
65
|
+
return response;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Streamed: usage (incl. cachedContentTokenCount) lands on the final chunk;
|
|
69
|
+
// observe the chunks as they pass through. The instrumentor keeps the span
|
|
70
|
+
// open until the stream is exhausted.
|
|
71
|
+
return Promise.resolve(original.apply(this, args)).then((stream_) => {
|
|
72
|
+
if (!stream_ || typeof stream_[Symbol.asyncIterator] !== 'function')
|
|
73
|
+
return stream_;
|
|
74
|
+
return (async function* observeCache() {
|
|
75
|
+
for await (const chunk of stream_) {
|
|
76
|
+
try {
|
|
77
|
+
(0, get_cache_telemetry_1.setCacheTelemetry)(span, chunk);
|
|
78
|
+
(0, get_reasoning_telemetry_1.setReasoningTelemetry)(span, chunk);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Never break the caller's stream over an enrichment failure.
|
|
82
|
+
}
|
|
83
|
+
yield chunk;
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
owner[methodName] = wrapper;
|
|
89
|
+
this._patched.push([owner, methodName, original]);
|
|
90
|
+
}
|
|
91
|
+
uninstrument() {
|
|
92
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
93
|
+
owner[methodName] = original;
|
|
94
|
+
}
|
|
95
|
+
this._patched = [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.GoogleTelemetryInstrumentor = GoogleTelemetryInstrumentor;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-written OTel instrumentor for the OpenAI audio endpoints.
|
|
3
|
+
*
|
|
4
|
+
* The `@traceloop/instrumentation-openai` package wraps OpenAI's chat,
|
|
5
|
+
* completions, responses and image endpoints, but not the audio ones, so this
|
|
6
|
+
* ships inside weflayr to cover them. It wraps the Text-to-Speech and
|
|
7
|
+
* Speech-to-Text entrypoints to emit one `gen_ai.*` span per call, which flows
|
|
8
|
+
* through the standard span processor -> exporter with no new transport code. It
|
|
9
|
+
* is registered alongside the traceloop instrumentor whenever
|
|
10
|
+
* `autoInstrument({ aiSdks: 'openai' })` runs.
|
|
11
|
+
*
|
|
12
|
+
* The billable unit differs by API, so each span records the one OpenAI bills:
|
|
13
|
+
* - Text-to-Speech is billed per input character -> `gen_ai.usage.input_characters`.
|
|
14
|
+
* - Speech-to-Text is billed per second of audio -> `gen_ai.usage.input_seconds`.
|
|
15
|
+
*
|
|
16
|
+
* Both endpoints are request/response (unary): `audio.speech.create` resolves to
|
|
17
|
+
* the full audio and `audio.transcriptions.create` to the transcription. The STT
|
|
18
|
+
* audio length is only present when the call requests `response_format: 'verbose_json'`.
|
|
19
|
+
*/
|
|
20
|
+
export declare class OpenAIAudioInstrumentor {
|
|
21
|
+
private _patched;
|
|
22
|
+
instrument(): void;
|
|
23
|
+
uninstrument(): void;
|
|
24
|
+
private _wrap;
|
|
25
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hand-written OTel instrumentor for the OpenAI audio endpoints.
|
|
4
|
+
*
|
|
5
|
+
* The `@traceloop/instrumentation-openai` package wraps OpenAI's chat,
|
|
6
|
+
* completions, responses and image endpoints, but not the audio ones, so this
|
|
7
|
+
* ships inside weflayr to cover them. It wraps the Text-to-Speech and
|
|
8
|
+
* Speech-to-Text entrypoints to emit one `gen_ai.*` span per call, which flows
|
|
9
|
+
* through the standard span processor -> exporter with no new transport code. It
|
|
10
|
+
* is registered alongside the traceloop instrumentor whenever
|
|
11
|
+
* `autoInstrument({ aiSdks: 'openai' })` runs.
|
|
12
|
+
*
|
|
13
|
+
* The billable unit differs by API, so each span records the one OpenAI bills:
|
|
14
|
+
* - Text-to-Speech is billed per input character -> `gen_ai.usage.input_characters`.
|
|
15
|
+
* - Speech-to-Text is billed per second of audio -> `gen_ai.usage.input_seconds`.
|
|
16
|
+
*
|
|
17
|
+
* Both endpoints are request/response (unary): `audio.speech.create` resolves to
|
|
18
|
+
* the full audio and `audio.transcriptions.create` to the transcription. The STT
|
|
19
|
+
* audio length is only present when the call requests `response_format: 'verbose_json'`.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.OpenAIAudioInstrumentor = void 0;
|
|
23
|
+
const api_1 = require("@opentelemetry/api");
|
|
24
|
+
const TRACER_NAME = 'weflayr.instrumentation.openai_audio';
|
|
25
|
+
const TTS_OPERATION = 'text_to_speech';
|
|
26
|
+
const STT_OPERATION = 'speech_to_text';
|
|
27
|
+
// Return the gen_ai.system for this call, matching how the OpenAI chat
|
|
28
|
+
// instrumentor attributes the vendor: Azure clients (reached via the same openai
|
|
29
|
+
// SDK) report `azure.ai.openai`, everything else `openai`.
|
|
30
|
+
function _detectSystem(instance) {
|
|
31
|
+
const baseURL = (instance && instance._client && instance._client.baseURL) || '';
|
|
32
|
+
return baseURL.toLowerCase().includes('openai.azure.com') ? 'azure.ai.openai' : 'openai';
|
|
33
|
+
}
|
|
34
|
+
function _startSpan(operation, body, system) {
|
|
35
|
+
const span = api_1.trace.getTracer(TRACER_NAME).startSpan(operation);
|
|
36
|
+
span.setAttribute('gen_ai.system', system);
|
|
37
|
+
span.setAttribute('gen_ai.operation.name', operation);
|
|
38
|
+
// `model` lives on the request body passed to both audio entrypoints.
|
|
39
|
+
const model = body && body.model;
|
|
40
|
+
if (model) {
|
|
41
|
+
span.setAttribute('gen_ai.request.model', model);
|
|
42
|
+
}
|
|
43
|
+
return span;
|
|
44
|
+
}
|
|
45
|
+
function _setInputCharacters(span, body) {
|
|
46
|
+
// `input` is the text passed to text_to_speech; the billable unit is its length.
|
|
47
|
+
span.setAttribute('gen_ai.usage.input_characters', ((body && body.input) || '').length);
|
|
48
|
+
}
|
|
49
|
+
function _setAudioDuration(span, response) {
|
|
50
|
+
// `duration` (seconds) is only present on a verbose transcription response;
|
|
51
|
+
// it is the billable unit for speech-to-text.
|
|
52
|
+
const durationSecs = response && response.duration;
|
|
53
|
+
if (durationSecs != null) {
|
|
54
|
+
span.setAttribute('gen_ai.usage.input_seconds', durationSecs);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function _recordError(span, error) {
|
|
58
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error?.message ?? String(error) });
|
|
59
|
+
span.recordException(error);
|
|
60
|
+
}
|
|
61
|
+
// Wrap a request/response entrypoint: the span spans the single call, and any
|
|
62
|
+
// response-derived billable unit is read off the result.
|
|
63
|
+
function _consumeUnary(span, responsePromise, setResponseAttributes) {
|
|
64
|
+
return responsePromise.then((response) => {
|
|
65
|
+
if (setResponseAttributes) {
|
|
66
|
+
setResponseAttributes(span, response);
|
|
67
|
+
}
|
|
68
|
+
span.end();
|
|
69
|
+
return response;
|
|
70
|
+
}, (error) => {
|
|
71
|
+
_recordError(span, error);
|
|
72
|
+
span.end();
|
|
73
|
+
throw error;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
class OpenAIAudioInstrumentor {
|
|
77
|
+
// [ownerPrototype, methodName, originalFunction] for uninstrument().
|
|
78
|
+
_patched = [];
|
|
79
|
+
instrument() {
|
|
80
|
+
let Speech;
|
|
81
|
+
let Transcriptions;
|
|
82
|
+
try {
|
|
83
|
+
({ Speech } = require('openai/resources/audio/speech'));
|
|
84
|
+
({ Transcriptions } = require('openai/resources/audio/transcriptions'));
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
throw new Error('weflayr.autoInstrument(OPENAI): the openai client is not installed. ' +
|
|
88
|
+
'Install it with: npm install openai\n' +
|
|
89
|
+
`Underlying error: ${err.message}`);
|
|
90
|
+
}
|
|
91
|
+
// TTS — request body is the 1st arg; billed per character.
|
|
92
|
+
this._wrap(Speech.prototype, {
|
|
93
|
+
operation: TTS_OPERATION,
|
|
94
|
+
setRequestAttributes: _setInputCharacters,
|
|
95
|
+
setResponseAttributes: null,
|
|
96
|
+
});
|
|
97
|
+
// STT — request body is the 1st arg; billed per second of audio.
|
|
98
|
+
this._wrap(Transcriptions.prototype, {
|
|
99
|
+
operation: STT_OPERATION,
|
|
100
|
+
setRequestAttributes: null,
|
|
101
|
+
setResponseAttributes: _setAudioDuration,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
uninstrument() {
|
|
105
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
106
|
+
owner[methodName] = original;
|
|
107
|
+
}
|
|
108
|
+
this._patched = [];
|
|
109
|
+
}
|
|
110
|
+
_wrap(owner, { operation, setRequestAttributes, setResponseAttributes, }) {
|
|
111
|
+
const original = owner.create;
|
|
112
|
+
function wrapper(body, ...rest) {
|
|
113
|
+
const span = _startSpan(operation, body, _detectSystem(this));
|
|
114
|
+
if (setRequestAttributes) {
|
|
115
|
+
setRequestAttributes(span, body);
|
|
116
|
+
}
|
|
117
|
+
return _consumeUnary(span, Promise.resolve(original.call(this, body, ...rest)), setResponseAttributes);
|
|
118
|
+
}
|
|
119
|
+
owner.create = wrapper;
|
|
120
|
+
this._patched.push([owner, 'create', original]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.OpenAIAudioInstrumentor = OpenAIAudioInstrumentor;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bedrock Mantle fixes for the OpenAI chat path.
|
|
3
|
+
*
|
|
4
|
+
* Amazon Bedrock Mantle serves Bedrock models over an OpenAI-compatible API, so it
|
|
5
|
+
* is reached with the stock `openai` SDK pointed at
|
|
6
|
+
* `https://bedrock-mantle.<region>.api.aws/v1`. Two things come out wrong there:
|
|
7
|
+
*
|
|
8
|
+
* - **The call is attributed to OpenAI.** The instrumentor derives the provider
|
|
9
|
+
* from the client base URL, but its chat patch hands `_detectVendorFromURL`
|
|
10
|
+
* the *resource* (`this` inside `Chat.Completions.create`) instead of the
|
|
11
|
+
* client, so `baseURL` is always undefined and every call falls back to
|
|
12
|
+
* `openai`. Resolving the resource's `_client` before delegating restores the
|
|
13
|
+
* detection the instrumentor already implements — for Mantle that is
|
|
14
|
+
* `aws.bedrock`, which the normalizer prices as Bedrock. The Python
|
|
15
|
+
* OpenLLMetry instrumentor passes the client correctly, so this gap is
|
|
16
|
+
* Node-only.
|
|
17
|
+
*
|
|
18
|
+
* - **The chain of thought is dropped.** Mantle's open-weight models (gpt-oss,
|
|
19
|
+
* qwen, ...) return their reasoning under `reasoning`, which neither the
|
|
20
|
+
* output-message builder nor the stream accumulator knows about. Without it a
|
|
21
|
+
* call that spends its whole budget reasoning records an empty output message.
|
|
22
|
+
* The reasoning part is inserted ahead of the tool calls, matching the order
|
|
23
|
+
* the Python SDK emits.
|
|
24
|
+
*/
|
|
25
|
+
export declare class OpenAIBedrockMantleInstrumentor {
|
|
26
|
+
private _patched;
|
|
27
|
+
instrument(instrumentation: any): void;
|
|
28
|
+
/** Hand the vendor detection the real client, not the resource it is called with. */
|
|
29
|
+
private _patchVendorDetection;
|
|
30
|
+
/** Accumulate `delta.reasoning` across the streamed chunks. */
|
|
31
|
+
private _patchStreamedReasoning;
|
|
32
|
+
/** Add the reasoning part as the output message is written. */
|
|
33
|
+
private _patchOutputMessages;
|
|
34
|
+
uninstrument(): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bedrock Mantle fixes for the OpenAI chat path.
|
|
4
|
+
*
|
|
5
|
+
* Amazon Bedrock Mantle serves Bedrock models over an OpenAI-compatible API, so it
|
|
6
|
+
* is reached with the stock `openai` SDK pointed at
|
|
7
|
+
* `https://bedrock-mantle.<region>.api.aws/v1`. Two things come out wrong there:
|
|
8
|
+
*
|
|
9
|
+
* - **The call is attributed to OpenAI.** The instrumentor derives the provider
|
|
10
|
+
* from the client base URL, but its chat patch hands `_detectVendorFromURL`
|
|
11
|
+
* the *resource* (`this` inside `Chat.Completions.create`) instead of the
|
|
12
|
+
* client, so `baseURL` is always undefined and every call falls back to
|
|
13
|
+
* `openai`. Resolving the resource's `_client` before delegating restores the
|
|
14
|
+
* detection the instrumentor already implements — for Mantle that is
|
|
15
|
+
* `aws.bedrock`, which the normalizer prices as Bedrock. The Python
|
|
16
|
+
* OpenLLMetry instrumentor passes the client correctly, so this gap is
|
|
17
|
+
* Node-only.
|
|
18
|
+
*
|
|
19
|
+
* - **The chain of thought is dropped.** Mantle's open-weight models (gpt-oss,
|
|
20
|
+
* qwen, ...) return their reasoning under `reasoning`, which neither the
|
|
21
|
+
* output-message builder nor the stream accumulator knows about. Without it a
|
|
22
|
+
* call that spends its whole budget reasoning records an empty output message.
|
|
23
|
+
* The reasoning part is inserted ahead of the tool calls, matching the order
|
|
24
|
+
* the Python SDK emits.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.OpenAIBedrockMantleInstrumentor = void 0;
|
|
28
|
+
const OUTPUT_MESSAGES_ATTR = 'gen_ai.output.messages';
|
|
29
|
+
// Reasoning streamed so far, per open span. Streamed deltas arrive while the
|
|
30
|
+
// stream is consumed, but the output message is only built when the span ends,
|
|
31
|
+
// so the two hooks below meet here.
|
|
32
|
+
const _streamedReasoningBySpan = new WeakMap();
|
|
33
|
+
function _completeResponseReasoning(result) {
|
|
34
|
+
const reasoning = result?.choices?.[0]?.message?.reasoning;
|
|
35
|
+
return typeof reasoning === 'string' && reasoning ? reasoning : undefined;
|
|
36
|
+
}
|
|
37
|
+
/** The parts array with the reasoning inserted ahead of the first non-text part
|
|
38
|
+
* (tool calls, audio, ...), so text and refusal keep leading it. */
|
|
39
|
+
function _withReasoningPart(parts, reasoning) {
|
|
40
|
+
const reasoningPart = { type: 'reasoning', content: reasoning };
|
|
41
|
+
const firstNonTextIndex = parts.findIndex((part) => part?.type !== 'text' && part?.type !== 'refusal');
|
|
42
|
+
if (firstNonTextIndex === -1)
|
|
43
|
+
return [...parts, reasoningPart];
|
|
44
|
+
return [...parts.slice(0, firstNonTextIndex), reasoningPart, ...parts.slice(firstNonTextIndex)];
|
|
45
|
+
}
|
|
46
|
+
/** The serialized output messages with the reasoning part added, or null when the
|
|
47
|
+
* value isn't the expected JSON array of messages. */
|
|
48
|
+
function _addReasoningToOutputMessages(rawOutputMessages, reasoning) {
|
|
49
|
+
if (typeof rawOutputMessages !== 'string')
|
|
50
|
+
return null;
|
|
51
|
+
let messages;
|
|
52
|
+
try {
|
|
53
|
+
messages = JSON.parse(rawOutputMessages);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (!Array.isArray(messages) || messages.length === 0)
|
|
59
|
+
return null;
|
|
60
|
+
const [first, ...rest] = messages;
|
|
61
|
+
if (!first || !Array.isArray(first.parts))
|
|
62
|
+
return null;
|
|
63
|
+
return JSON.stringify([{ ...first, parts: _withReasoningPart(first.parts, reasoning) }, ...rest]);
|
|
64
|
+
}
|
|
65
|
+
class OpenAIBedrockMantleInstrumentor {
|
|
66
|
+
// [owner, methodName, originalFunction] for uninstrument().
|
|
67
|
+
_patched = [];
|
|
68
|
+
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
69
|
+
// the worst case is an OpenAI-attributed span without its chain of thought,
|
|
70
|
+
// never a crash.
|
|
71
|
+
instrument(instrumentation) {
|
|
72
|
+
if (!instrumentation)
|
|
73
|
+
return;
|
|
74
|
+
this._patchVendorDetection(instrumentation);
|
|
75
|
+
this._patchStreamedReasoning(instrumentation);
|
|
76
|
+
this._patchOutputMessages(instrumentation);
|
|
77
|
+
}
|
|
78
|
+
/** Hand the vendor detection the real client, not the resource it is called with. */
|
|
79
|
+
_patchVendorDetection(instrumentation) {
|
|
80
|
+
const original = instrumentation._detectVendorFromURL;
|
|
81
|
+
if (typeof original !== 'function')
|
|
82
|
+
return;
|
|
83
|
+
instrumentation._detectVendorFromURL = function detectVendorFromResolvedClient(client) {
|
|
84
|
+
const resolved = client && !client.baseURL && client._client ? client._client : client;
|
|
85
|
+
return original.call(this, resolved);
|
|
86
|
+
};
|
|
87
|
+
this._patched.push([instrumentation, '_detectVendorFromURL', original]);
|
|
88
|
+
}
|
|
89
|
+
/** Accumulate `delta.reasoning` across the streamed chunks. */
|
|
90
|
+
_patchStreamedReasoning(instrumentation) {
|
|
91
|
+
const original = instrumentation._streamingWrapPromise;
|
|
92
|
+
if (typeof original !== 'function')
|
|
93
|
+
return;
|
|
94
|
+
instrumentation._streamingWrapPromise = function streamingWrapWithReasoning(streamArgs) {
|
|
95
|
+
const span = streamArgs && streamArgs.span;
|
|
96
|
+
const wrappedStream = original.call(this, streamArgs);
|
|
97
|
+
if (!span)
|
|
98
|
+
return wrappedStream;
|
|
99
|
+
return (async function* accumulateReasoning() {
|
|
100
|
+
for await (const chunk of wrappedStream) {
|
|
101
|
+
const reasoning = chunk?.choices?.[0]?.delta?.reasoning;
|
|
102
|
+
if (typeof reasoning === 'string' && reasoning) {
|
|
103
|
+
_streamedReasoningBySpan.set(span, (_streamedReasoningBySpan.get(span) ?? '') + reasoning);
|
|
104
|
+
}
|
|
105
|
+
yield chunk;
|
|
106
|
+
}
|
|
107
|
+
})();
|
|
108
|
+
};
|
|
109
|
+
this._patched.push([instrumentation, '_streamingWrapPromise', original]);
|
|
110
|
+
}
|
|
111
|
+
/** Add the reasoning part as the output message is written. */
|
|
112
|
+
_patchOutputMessages(instrumentation) {
|
|
113
|
+
const original = instrumentation._endSpan;
|
|
114
|
+
if (typeof original !== 'function')
|
|
115
|
+
return;
|
|
116
|
+
instrumentation._endSpan = function endSpanWithReasoning(args) {
|
|
117
|
+
const span = args && args.span;
|
|
118
|
+
const reasoning = _completeResponseReasoning(args && args.result) ??
|
|
119
|
+
(span ? _streamedReasoningBySpan.get(span) : undefined);
|
|
120
|
+
if (!span || !reasoning)
|
|
121
|
+
return original.call(this, args);
|
|
122
|
+
// The original builds the output messages and then ends the span, so the
|
|
123
|
+
// part is grafted on as the attribute is written rather than afterwards.
|
|
124
|
+
const previousSetAttribute = span.setAttribute;
|
|
125
|
+
span.setAttribute = function setAttributeWithReasoning(key, value) {
|
|
126
|
+
if (key === OUTPUT_MESSAGES_ATTR) {
|
|
127
|
+
value = _addReasoningToOutputMessages(value, reasoning) ?? value;
|
|
128
|
+
}
|
|
129
|
+
return previousSetAttribute.call(this, key, value);
|
|
130
|
+
};
|
|
131
|
+
try {
|
|
132
|
+
return original.call(this, args);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
span.setAttribute = previousSetAttribute;
|
|
136
|
+
_streamedReasoningBySpan.delete(span);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
this._patched.push([instrumentation, '_endSpan', original]);
|
|
140
|
+
}
|
|
141
|
+
uninstrument() {
|
|
142
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
143
|
+
owner[methodName] = original;
|
|
144
|
+
}
|
|
145
|
+
this._patched = [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.OpenAIBedrockMantleInstrumentor = OpenAIBedrockMantleInstrumentor;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cached-prompt token enrichment complement for the non-streamed OpenAI chat path.
|
|
3
|
+
*
|
|
4
|
+
* `@traceloop/instrumentation-openai` records `gen_ai.usage.{input,output}_tokens`
|
|
5
|
+
* for non-streamed calls but never reads `usage.prompt_tokens_details.cached_tokens`
|
|
6
|
+
* or `.cache_write_tokens`, so spans for prompt-cache hits/writes carry no
|
|
7
|
+
* cached-token count. The Python OpenLLMetry instrumentor sets
|
|
8
|
+
* `gen_ai.usage.cache_read_input_tokens` from the former, so this brings the Node
|
|
9
|
+
* SDK to parity; the latter (GPT-5.6+'s cache-write count) has no OpenLLMetry
|
|
10
|
+
* equivalent on either side, so this is its only source.
|
|
11
|
+
*
|
|
12
|
+
* It wraps the instrumentor instance's `_endSpan({ span, type, result })`, which
|
|
13
|
+
* finalizes the span after the response resolves, letting us stamp the
|
|
14
|
+
* cached-token counts off `result.usage` while the span is still open.
|
|
15
|
+
* `cache_write_tokens` is stamped as `gen_ai.usage.cache_creation_input_tokens`,
|
|
16
|
+
* the attribute name the normalizer already reads for Anthropic/Bedrock/Vercel.
|
|
17
|
+
*
|
|
18
|
+
* The same hook surfaces the reasoning-token count: reasoning models report it as
|
|
19
|
+
* `usage.completion_tokens_details.reasoning_tokens` (a subset of output_tokens)
|
|
20
|
+
* which the Node instrumentor drops, so this stamps the flat `reasoning_tokens`
|
|
21
|
+
* attribute (the form the Python instrumentor emits and the normalizer reads).
|
|
22
|
+
*/
|
|
23
|
+
export declare class OpenAIPromptCachingInstrumentor {
|
|
24
|
+
private _patched;
|
|
25
|
+
instrument(instrumentation: any): void;
|
|
26
|
+
uninstrument(): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cached-prompt token enrichment complement for the non-streamed OpenAI chat path.
|
|
4
|
+
*
|
|
5
|
+
* `@traceloop/instrumentation-openai` records `gen_ai.usage.{input,output}_tokens`
|
|
6
|
+
* for non-streamed calls but never reads `usage.prompt_tokens_details.cached_tokens`
|
|
7
|
+
* or `.cache_write_tokens`, so spans for prompt-cache hits/writes carry no
|
|
8
|
+
* cached-token count. The Python OpenLLMetry instrumentor sets
|
|
9
|
+
* `gen_ai.usage.cache_read_input_tokens` from the former, so this brings the Node
|
|
10
|
+
* SDK to parity; the latter (GPT-5.6+'s cache-write count) has no OpenLLMetry
|
|
11
|
+
* equivalent on either side, so this is its only source.
|
|
12
|
+
*
|
|
13
|
+
* It wraps the instrumentor instance's `_endSpan({ span, type, result })`, which
|
|
14
|
+
* finalizes the span after the response resolves, letting us stamp the
|
|
15
|
+
* cached-token counts off `result.usage` while the span is still open.
|
|
16
|
+
* `cache_write_tokens` is stamped as `gen_ai.usage.cache_creation_input_tokens`,
|
|
17
|
+
* the attribute name the normalizer already reads for Anthropic/Bedrock/Vercel.
|
|
18
|
+
*
|
|
19
|
+
* The same hook surfaces the reasoning-token count: reasoning models report it as
|
|
20
|
+
* `usage.completion_tokens_details.reasoning_tokens` (a subset of output_tokens)
|
|
21
|
+
* which the Node instrumentor drops, so this stamps the flat `reasoning_tokens`
|
|
22
|
+
* attribute (the form the Python instrumentor emits and the normalizer reads).
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.OpenAIPromptCachingInstrumentor = void 0;
|
|
26
|
+
const CACHE_READ_INPUT_TOKENS_ATTR = 'gen_ai.usage.cache_read_input_tokens';
|
|
27
|
+
const CACHE_CREATION_INPUT_TOKENS_ATTR = 'gen_ai.usage.cache_creation_input_tokens';
|
|
28
|
+
const REASONING_TOKENS_ATTR = 'gen_ai.usage.reasoning_tokens';
|
|
29
|
+
class OpenAIPromptCachingInstrumentor {
|
|
30
|
+
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
31
|
+
_patched = [];
|
|
32
|
+
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
33
|
+
// the worst case is a missing cached-token count, never a crash.
|
|
34
|
+
instrument(instrumentation) {
|
|
35
|
+
const original = instrumentation && instrumentation._endSpan;
|
|
36
|
+
if (typeof original !== 'function')
|
|
37
|
+
return;
|
|
38
|
+
instrumentation._endSpan = function endSpanWithCache(args) {
|
|
39
|
+
try {
|
|
40
|
+
const span = args && args.span;
|
|
41
|
+
const usage = args && args.result && args.result.usage;
|
|
42
|
+
const recording = span && span.isRecording && span.isRecording();
|
|
43
|
+
const cachedTokens = usage && usage.prompt_tokens_details && usage.prompt_tokens_details.cached_tokens;
|
|
44
|
+
if (cachedTokens && recording) {
|
|
45
|
+
span.setAttribute(CACHE_READ_INPUT_TOKENS_ATTR, cachedTokens);
|
|
46
|
+
}
|
|
47
|
+
const cacheWriteTokens = usage && usage.prompt_tokens_details && usage.prompt_tokens_details.cache_write_tokens;
|
|
48
|
+
if (cacheWriteTokens && recording) {
|
|
49
|
+
span.setAttribute(CACHE_CREATION_INPUT_TOKENS_ATTR, cacheWriteTokens);
|
|
50
|
+
}
|
|
51
|
+
const reasoningTokens = usage &&
|
|
52
|
+
usage.completion_tokens_details &&
|
|
53
|
+
usage.completion_tokens_details.reasoning_tokens;
|
|
54
|
+
if (reasoningTokens && recording) {
|
|
55
|
+
span.setAttribute(REASONING_TOKENS_ATTR, reasoningTokens);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Never break span finalization over an enrichment failure.
|
|
60
|
+
}
|
|
61
|
+
return original.call(this, args);
|
|
62
|
+
};
|
|
63
|
+
this._patched.push([instrumentation, '_endSpan', original]);
|
|
64
|
+
}
|
|
65
|
+
uninstrument() {
|
|
66
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
67
|
+
owner[methodName] = original;
|
|
68
|
+
}
|
|
69
|
+
this._patched = [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.OpenAIPromptCachingInstrumentor = OpenAIPromptCachingInstrumentor;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-side prompt-caching config capture for OpenAI chat spans.
|
|
3
|
+
*
|
|
4
|
+
* OpenAI callers influence the (automatic, server-side) prompt cache with
|
|
5
|
+
* request parameters the traceloop instrumentor never records:
|
|
6
|
+
* `prompt_cache_key` (a client-chosen routing key — requests sharing a key are
|
|
7
|
+
* routed to the same cache shard), the retention tuning —
|
|
8
|
+
* `prompt_cache_retention` ("in_memory" / "24h") on older models, replaced by
|
|
9
|
+
* `prompt_cache_options: { ttl }` ("30m") on GPT-5.6+ — and, also on GPT-5.6+,
|
|
10
|
+
* explicit breakpoint placement: a `prompt_cache_breakpoint` marker on a
|
|
11
|
+
* message content block (`prompt_cache_options: { mode: "explicit" }`
|
|
12
|
+
* disables the automatic breakpoint, requiring these to cache anything).
|
|
13
|
+
*
|
|
14
|
+
* Wrap `startSpan({ type, params })` (covering the streamed and non-streamed
|
|
15
|
+
* paths) to stamp the values as `weflayr.cache.key` / `weflayr.cache.retention`
|
|
16
|
+
* (raw retention/ttl string) and, when any content block carries the marker,
|
|
17
|
+
* `weflayr.cache.breakpoints` (a JSON list of `{"location": "messages[i]"}`
|
|
18
|
+
* entries — the marker itself carries no TTL, unlike Anthropic's
|
|
19
|
+
* `cache_control`; the normalizer owns the vocabulary).
|
|
20
|
+
* `prompt_cache_options: { mode: "explicit" }` is stamped as
|
|
21
|
+
* `weflayr.cache.mode` so the normalizer knows not to also synthesize the
|
|
22
|
+
* automatic "auto"-location breakpoint OpenAI does not create in this mode.
|
|
23
|
+
*/
|
|
24
|
+
export declare class OpenAICacheConfigInstrumentor {
|
|
25
|
+
private _patched;
|
|
26
|
+
instrument(instrumentation: any): void;
|
|
27
|
+
uninstrument(): void;
|
|
28
|
+
}
|