weflayr 0.23.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{api → src/api}/generated/facade.gen.d.ts +163 -12
- package/dist/{api → src/api}/generated/facade.gen.js +10 -2
- package/dist/src/api/generated/index.d.ts +2 -0
- package/dist/{api → src/api}/generated/index.js +9 -1
- package/dist/{api → src/api}/generated/sdk.gen.d.ts +35 -3
- package/dist/{api → src/api}/generated/sdk.gen.js +88 -4
- package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +692 -116
- package/dist/{api → src/api}/makeEndpoint.js +1 -3
- package/dist/src/auto-instrument.d.ts +42 -0
- package/dist/src/auto-instrument.js +156 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +49 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.d.ts +20 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +114 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.js +92 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +141 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.js +17 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +50 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +39 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +97 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +102 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.js +91 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +21 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +54 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.d.ts +19 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.js +81 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +126 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.d.ts +17 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.js +85 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +68 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.js +77 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +88 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.js +18 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.d.ts +31 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.js +106 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.js +75 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +44 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.d.ts +28 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.js +209 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.js +28 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.js +154 -0
- package/dist/src/instrumentation/google/get_cache_telemetry.d.ts +16 -0
- package/dist/src/instrumentation/google/get_cache_telemetry.js +38 -0
- package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_embeddings_telemetry.js +85 -0
- package/dist/src/instrumentation/google/get_provider_name.d.ts +11 -0
- package/{src → dist/src}/instrumentation/google/get_provider_name.js +11 -12
- package/{src/instrumentation/google/get_reasoning_telemetry.js → dist/src/instrumentation/google/get_reasoning_telemetry.d.ts} +1 -11
- package/dist/src/instrumentation/google/get_reasoning_telemetry.js +21 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.js +19 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.js +98 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.js +123 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +148 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +72 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +91 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +86 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.d.ts +21 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.js +74 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +20 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +76 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +3 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.js +54 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +66 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +24 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +84 -0
- package/dist/src/logger.d.ts +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/otel/exporter.d.ts +19 -0
- package/dist/src/otel/exporter.js +94 -0
- package/dist/src/otel/propagation.d.ts +46 -0
- package/dist/src/otel/propagation.js +110 -0
- package/dist/src/otel/span-filter.d.ts +3 -0
- package/dist/src/otel/span-filter.js +61 -0
- package/dist/src/otel/span-processor.d.ts +55 -0
- package/dist/src/otel/span-processor.js +169 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +12 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.js +88 -0
- package/dist/src/setup.d.ts +50 -0
- package/dist/src/setup.js +155 -0
- package/package.json +11 -5
- package/.prettierignore +0 -2
- package/.prettierrc.json +0 -7
- package/LICENSE-MIT +0 -21
- package/NOTICE +0 -12
- package/dist/api/generated/index.d.ts +0 -2
- package/dist/api/generated/types.gen.d.ts +0 -749
- package/eslint.config.mjs +0 -36
- package/index.d.ts +0 -167
- package/openapi-ts.config.mjs +0 -9
- package/scripts/generate_facade.mjs +0 -86
- package/src/api/caseConversion.ts +0 -58
- package/src/api/generated/client/client.gen.ts +0 -277
- package/src/api/generated/client/index.ts +0 -27
- package/src/api/generated/client/types.gen.ts +0 -218
- package/src/api/generated/client/utils.gen.ts +0 -316
- package/src/api/generated/client.gen.ts +0 -16
- package/src/api/generated/core/auth.gen.ts +0 -48
- package/src/api/generated/core/bodySerializer.gen.ts +0 -82
- package/src/api/generated/core/params.gen.ts +0 -178
- package/src/api/generated/core/pathSerializer.gen.ts +0 -171
- package/src/api/generated/core/queryKeySerializer.gen.ts +0 -117
- package/src/api/generated/core/serverSentEvents.gen.ts +0 -242
- package/src/api/generated/core/types.gen.ts +0 -110
- package/src/api/generated/core/utils.gen.ts +0 -140
- package/src/api/generated/facade.gen.ts +0 -68
- package/src/api/generated/index.ts +0 -4
- package/src/api/generated/sdk.gen.ts +0 -121
- package/src/api/index.ts +0 -45
- package/src/api/makeEndpoint.ts +0 -54
- package/src/auto-instrument.js +0 -171
- package/src/index.js +0 -19
- package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -107
- package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
- package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -50
- package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -37
- package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -104
- package/src/instrumentation/bedrock/get_input_message_content_telemetry.js +0 -94
- package/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +0 -87
- package/src/instrumentation/bedrock/set_telemetry_attributes.js +0 -36
- package/src/instrumentation/cohere/get_chat_telemetry.js +0 -88
- package/src/instrumentation/cohere/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/cohere/set_telemetry_attributes.js +0 -32
- package/src/instrumentation/elevenlabs/full_instrumentation.js +0 -236
- package/src/instrumentation/google/get_audio_telemetry.js +0 -30
- package/src/instrumentation/google/get_cache_telemetry.js +0 -25
- package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
- package/src/instrumentation/google/set_telemetry_attributes.js +0 -98
- package/src/instrumentation/openai/get_audio_telemetry.js +0 -149
- package/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +0 -71
- package/src/instrumentation/openai/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/openai/get_streaming_usage.js +0 -81
- package/src/instrumentation/openai/set_telemetry_attributes.js +0 -39
- package/src/instrumentation/vercel/full_instrumentation.js +0 -44
- package/src/logger.js +0 -8
- package/src/otel/exporter.js +0 -108
- package/src/otel/propagation.js +0 -133
- package/src/otel/span-filter.js +0 -69
- package/src/otel/span-processor.js +0 -162
- package/src/otel/vercel-ai-sdk-span-filter.js +0 -79
- package/src/setup.js +0 -167
- package/tests/api/index.test.js +0 -437
- package/tests/index.test.js +0 -824
- package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -158
- package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
- package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -82
- package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -102
- package/tests/instrumentation/bedrock/get_input_message_content_telemetry.test.js +0 -89
- package/tests/instrumentation/bedrock/get_streaming_output_message_telemetry.test.js +0 -87
- package/tests/instrumentation/cohere/get_chat_telemetry.test.js +0 -148
- package/tests/instrumentation/cohere/get_embeddings_telemetry.test.js +0 -132
- package/tests/instrumentation/elevenlabs/full_instrumentation.test.js +0 -258
- package/tests/instrumentation/google/get_embeddings_telemetry.test.js +0 -128
- package/tests/instrumentation/google/get_reasoning_telemetry.test.js +0 -41
- package/tests/instrumentation/google/set_telemetry_attributes.test.js +0 -219
- package/tests/instrumentation/openai/get_audio_telemetry.test.js +0 -170
- package/tests/instrumentation/openai/get_cache_and_reasoning_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_embeddings_telemetry.test.js +0 -145
- package/tests/instrumentation/openai/get_streaming_usage.test.js +0 -105
- package/tests/instrumentation/vercel/full_instrumentation.test.js +0 -16
- package/tests/otel/vercel-ai-sdk-span-filter.test.js +0 -159
- package/tsconfig.json +0 -18
- /package/dist/{api → src/api}/caseConversion.d.ts +0 -0
- /package/dist/{api → src/api}/caseConversion.js +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/index.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/index.js +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/types.gen.js +0 -0
- /package/dist/{api → src/api}/index.d.ts +0 -0
- /package/dist/{api → src/api}/index.js +0 -0
- /package/dist/{api → src/api}/makeEndpoint.d.ts +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Request-side prompt-caching config capture for OpenAI chat spans.
|
|
4
|
+
*
|
|
5
|
+
* OpenAI callers influence the (automatic, server-side) prompt cache with
|
|
6
|
+
* request parameters the traceloop instrumentor never records:
|
|
7
|
+
* `prompt_cache_key` (a client-chosen routing key — requests sharing a key are
|
|
8
|
+
* routed to the same cache shard), the retention tuning —
|
|
9
|
+
* `prompt_cache_retention` ("in_memory" / "24h") on older models, replaced by
|
|
10
|
+
* `prompt_cache_options: { ttl }` ("30m") on GPT-5.6+ — and, also on GPT-5.6+,
|
|
11
|
+
* explicit breakpoint placement: a `prompt_cache_breakpoint` marker on a
|
|
12
|
+
* message content block (`prompt_cache_options: { mode: "explicit" }`
|
|
13
|
+
* disables the automatic breakpoint, requiring these to cache anything).
|
|
14
|
+
*
|
|
15
|
+
* Wrap `startSpan({ type, params })` (covering the streamed and non-streamed
|
|
16
|
+
* paths) to stamp the values as `weflayr.cache.key` / `weflayr.cache.retention`
|
|
17
|
+
* (raw retention/ttl string) and, when any content block carries the marker,
|
|
18
|
+
* `weflayr.cache.breakpoints` (a JSON list of `{"location": "messages[i]"}`
|
|
19
|
+
* entries — the marker itself carries no TTL, unlike Anthropic's
|
|
20
|
+
* `cache_control`; the normalizer owns the vocabulary).
|
|
21
|
+
* `prompt_cache_options: { mode: "explicit" }` is stamped as
|
|
22
|
+
* `weflayr.cache.mode` so the normalizer knows not to also synthesize the
|
|
23
|
+
* automatic "auto"-location breakpoint OpenAI does not create in this mode.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.OpenAICacheConfigInstrumentor = void 0;
|
|
27
|
+
const CACHE_KEY_ATTR = 'weflayr.cache.key';
|
|
28
|
+
const CACHE_RETENTION_ATTR = 'weflayr.cache.retention';
|
|
29
|
+
const CACHE_MODE_ATTR = 'weflayr.cache.mode';
|
|
30
|
+
const BREAKPOINTS_ATTR = 'weflayr.cache.breakpoints';
|
|
31
|
+
function explicitBreakpoints(params) {
|
|
32
|
+
const breakpoints = [];
|
|
33
|
+
const messages = (params && params.messages) || [];
|
|
34
|
+
messages.forEach((message, index) => {
|
|
35
|
+
const content = message && message.content;
|
|
36
|
+
if (!Array.isArray(content))
|
|
37
|
+
return;
|
|
38
|
+
for (const block of content) {
|
|
39
|
+
if (block && typeof block === 'object' && 'prompt_cache_breakpoint' in block) {
|
|
40
|
+
breakpoints.push({ location: `messages[${index}]` });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return breakpoints;
|
|
45
|
+
}
|
|
46
|
+
class OpenAICacheConfigInstrumentor {
|
|
47
|
+
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
48
|
+
_patched = [];
|
|
49
|
+
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
50
|
+
// the worst case is missing cache-config capture, never a crash.
|
|
51
|
+
instrument(instrumentation) {
|
|
52
|
+
if (!instrumentation)
|
|
53
|
+
return;
|
|
54
|
+
const startSpanOriginal = instrumentation.startSpan;
|
|
55
|
+
if (typeof startSpanOriginal !== 'function')
|
|
56
|
+
return;
|
|
57
|
+
instrumentation.startSpan = function startSpanWithCacheConfig(request) {
|
|
58
|
+
const span = startSpanOriginal.call(this, request);
|
|
59
|
+
try {
|
|
60
|
+
const params = (request && request.params) || {};
|
|
61
|
+
if (params.prompt_cache_key) {
|
|
62
|
+
span.setAttribute(CACHE_KEY_ATTR, String(params.prompt_cache_key));
|
|
63
|
+
}
|
|
64
|
+
const retention = params.prompt_cache_retention ||
|
|
65
|
+
(params.prompt_cache_options && params.prompt_cache_options.ttl);
|
|
66
|
+
if (retention) {
|
|
67
|
+
span.setAttribute(CACHE_RETENTION_ATTR, String(retention));
|
|
68
|
+
}
|
|
69
|
+
if (params.prompt_cache_options && params.prompt_cache_options.mode === 'explicit') {
|
|
70
|
+
span.setAttribute(CACHE_MODE_ATTR, 'explicit');
|
|
71
|
+
}
|
|
72
|
+
const breakpoints = explicitBreakpoints(params);
|
|
73
|
+
if (breakpoints.length) {
|
|
74
|
+
span.setAttribute(BREAKPOINTS_ATTR, JSON.stringify(breakpoints));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Never break span creation over an enrichment failure.
|
|
79
|
+
}
|
|
80
|
+
return span;
|
|
81
|
+
};
|
|
82
|
+
this._patched.push([instrumentation, 'startSpan', startSpanOriginal]);
|
|
83
|
+
}
|
|
84
|
+
uninstrument() {
|
|
85
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
86
|
+
owner[methodName] = original;
|
|
87
|
+
}
|
|
88
|
+
this._patched = [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.OpenAICacheConfigInstrumentor = OpenAICacheConfigInstrumentor;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-written OTel instrumentor for the OpenAI embeddings endpoint.
|
|
3
|
+
*
|
|
4
|
+
* `@traceloop/instrumentation-openai` (the package weflayr uses for OpenAI on
|
|
5
|
+
* Node) does not wrap the embeddings endpoint, so this ships inside weflayr to
|
|
6
|
+
* cover it. The Python SDK gets embeddings for free from OpenLLMetry's
|
|
7
|
+
* instrumentor, so there is no Python counterpart to this file — but the span it
|
|
8
|
+
* emits is shaped to normalize identically. It is registered alongside the
|
|
9
|
+
* traceloop instrumentor whenever `autoInstrument({ aiSdks: 'openai' })` runs.
|
|
10
|
+
*
|
|
11
|
+
* Embeddings are billed per input token, so the span records the prompt tokens
|
|
12
|
+
* read off the response under `gen_ai.usage.input_tokens`; there is no completion.
|
|
13
|
+
*/
|
|
14
|
+
export declare class OpenAIEmbeddingsInstrumentor {
|
|
15
|
+
private _patched;
|
|
16
|
+
instrument(): void;
|
|
17
|
+
uninstrument(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hand-written OTel instrumentor for the OpenAI embeddings endpoint.
|
|
4
|
+
*
|
|
5
|
+
* `@traceloop/instrumentation-openai` (the package weflayr uses for OpenAI on
|
|
6
|
+
* Node) does not wrap the embeddings endpoint, so this ships inside weflayr to
|
|
7
|
+
* cover it. The Python SDK gets embeddings for free from OpenLLMetry's
|
|
8
|
+
* instrumentor, so there is no Python counterpart to this file — but the span it
|
|
9
|
+
* emits is shaped to normalize identically. It is registered alongside the
|
|
10
|
+
* traceloop instrumentor whenever `autoInstrument({ aiSdks: 'openai' })` runs.
|
|
11
|
+
*
|
|
12
|
+
* Embeddings are billed per input token, so the span records the prompt tokens
|
|
13
|
+
* read off the response under `gen_ai.usage.input_tokens`; there is no completion.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.OpenAIEmbeddingsInstrumentor = void 0;
|
|
17
|
+
const api_1 = require("@opentelemetry/api");
|
|
18
|
+
const TRACER_NAME = 'weflayr.instrumentation.openai_embeddings';
|
|
19
|
+
const EMBEDDINGS_OPERATION = 'embeddings';
|
|
20
|
+
// Return the gen_ai.system for this call, matching how the OpenAI chat
|
|
21
|
+
// instrumentor attributes the vendor: Azure clients (reached via the same openai
|
|
22
|
+
// SDK) report `azure.ai.openai`, everything else `openai`.
|
|
23
|
+
function _detectSystem(instance) {
|
|
24
|
+
const baseURL = (instance && instance._client && instance._client.baseURL) || '';
|
|
25
|
+
return baseURL.toLowerCase().includes('openai.azure.com') ? 'azure.ai.openai' : 'openai';
|
|
26
|
+
}
|
|
27
|
+
function _recordError(span, error) {
|
|
28
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error?.message ?? String(error) });
|
|
29
|
+
span.recordException(error);
|
|
30
|
+
}
|
|
31
|
+
class OpenAIEmbeddingsInstrumentor {
|
|
32
|
+
// [ownerPrototype, methodName, originalFunction] for uninstrument().
|
|
33
|
+
_patched = [];
|
|
34
|
+
instrument() {
|
|
35
|
+
let Embeddings;
|
|
36
|
+
try {
|
|
37
|
+
({ Embeddings } = require('openai/resources/embeddings'));
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
throw new Error('weflayr.autoInstrument(OPENAI): the openai client is not installed. ' +
|
|
41
|
+
'Install it with: npm install openai\n' +
|
|
42
|
+
`Underlying error: ${err.message}`);
|
|
43
|
+
}
|
|
44
|
+
const owner = Embeddings.prototype;
|
|
45
|
+
const original = owner.create;
|
|
46
|
+
function wrapper(body, ...rest) {
|
|
47
|
+
const span = api_1.trace.getTracer(TRACER_NAME).startSpan(EMBEDDINGS_OPERATION);
|
|
48
|
+
span.setAttribute('gen_ai.system', _detectSystem(this));
|
|
49
|
+
span.setAttribute('gen_ai.operation.name', EMBEDDINGS_OPERATION);
|
|
50
|
+
if (body && body.model) {
|
|
51
|
+
span.setAttribute('gen_ai.request.model', body.model);
|
|
52
|
+
}
|
|
53
|
+
// One user message per input element, matching the shape the chat
|
|
54
|
+
// instrumentors emit. The span processor strips it when message-content
|
|
55
|
+
// capture is disabled.
|
|
56
|
+
if (body && body.input != null) {
|
|
57
|
+
const inputs = Array.isArray(body.input) ? body.input : [body.input];
|
|
58
|
+
span.setAttribute('gen_ai.input.messages', JSON.stringify(inputs.map((content) => ({ role: 'user', parts: [{ type: 'text', content }] }))));
|
|
59
|
+
}
|
|
60
|
+
return Promise.resolve(original.call(this, body, ...rest)).then((response) => {
|
|
61
|
+
if (response && response.model) {
|
|
62
|
+
span.setAttribute('gen_ai.response.model', response.model);
|
|
63
|
+
}
|
|
64
|
+
const promptTokens = response && response.usage && response.usage.prompt_tokens;
|
|
65
|
+
if (promptTokens != null) {
|
|
66
|
+
span.setAttribute('gen_ai.usage.input_tokens', promptTokens);
|
|
67
|
+
}
|
|
68
|
+
span.end();
|
|
69
|
+
return response;
|
|
70
|
+
}, (error) => {
|
|
71
|
+
_recordError(span, error);
|
|
72
|
+
span.end();
|
|
73
|
+
throw error;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
owner.create = wrapper;
|
|
77
|
+
this._patched.push([owner, 'create', original]);
|
|
78
|
+
}
|
|
79
|
+
uninstrument() {
|
|
80
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
81
|
+
owner[methodName] = original;
|
|
82
|
+
}
|
|
83
|
+
this._patched = [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.OpenAIEmbeddingsInstrumentor = OpenAIEmbeddingsInstrumentor;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamed token-usage enrichment complement for the OpenAI chat path.
|
|
3
|
+
*
|
|
4
|
+
* `@traceloop/instrumentation-openai` does not record token usage for streamed
|
|
5
|
+
* calls: its stream handler accumulates `choices[].delta` but never reads the
|
|
6
|
+
* final usage chunk OpenAI sends when the request sets
|
|
7
|
+
* `stream_options: { include_usage: true }`. Non-streaming calls work because
|
|
8
|
+
* OpenAI returns `usage` in the response body, which the instrumentor reads
|
|
9
|
+
* directly. The Python OpenLLMetry instrumentor reads the streamed usage, so
|
|
10
|
+
* this gap is Node-only.
|
|
11
|
+
*
|
|
12
|
+
* It wraps the instrumentor instance's `_streamingWrapPromise`: that handler
|
|
13
|
+
* hands us the span and ends it only after its chunk loop, so when the usage
|
|
14
|
+
* chunk arrives the span is still open and we set the same `gen_ai.usage.*`
|
|
15
|
+
* attributes the instrumentor sets for non-streaming calls.
|
|
16
|
+
*/
|
|
17
|
+
export declare class OpenAIStreamingUsageInstrumentor {
|
|
18
|
+
private _patched;
|
|
19
|
+
instrument(instrumentation: any): void;
|
|
20
|
+
uninstrument(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Streamed token-usage enrichment complement for the OpenAI chat path.
|
|
4
|
+
*
|
|
5
|
+
* `@traceloop/instrumentation-openai` does not record token usage for streamed
|
|
6
|
+
* calls: its stream handler accumulates `choices[].delta` but never reads the
|
|
7
|
+
* final usage chunk OpenAI sends when the request sets
|
|
8
|
+
* `stream_options: { include_usage: true }`. Non-streaming calls work because
|
|
9
|
+
* OpenAI returns `usage` in the response body, which the instrumentor reads
|
|
10
|
+
* directly. The Python OpenLLMetry instrumentor reads the streamed usage, so
|
|
11
|
+
* this gap is Node-only.
|
|
12
|
+
*
|
|
13
|
+
* It wraps the instrumentor instance's `_streamingWrapPromise`: that handler
|
|
14
|
+
* hands us the span and ends it only after its chunk loop, so when the usage
|
|
15
|
+
* chunk arrives the span is still open and we set the same `gen_ai.usage.*`
|
|
16
|
+
* attributes the instrumentor sets for non-streaming calls.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.OpenAIStreamingUsageInstrumentor = void 0;
|
|
20
|
+
const USAGE_INPUT_TOKENS_ATTR = 'gen_ai.usage.input_tokens';
|
|
21
|
+
const USAGE_OUTPUT_TOKENS_ATTR = 'gen_ai.usage.output_tokens';
|
|
22
|
+
const USAGE_TOTAL_TOKENS_ATTR = 'gen_ai.usage.total_tokens';
|
|
23
|
+
const CACHE_READ_INPUT_TOKENS_ATTR = 'gen_ai.usage.cache_read_input_tokens';
|
|
24
|
+
function _setUsageAttributes(span, usage) {
|
|
25
|
+
if (typeof usage.prompt_tokens === 'number') {
|
|
26
|
+
span.setAttribute(USAGE_INPUT_TOKENS_ATTR, usage.prompt_tokens);
|
|
27
|
+
}
|
|
28
|
+
if (typeof usage.completion_tokens === 'number') {
|
|
29
|
+
span.setAttribute(USAGE_OUTPUT_TOKENS_ATTR, usage.completion_tokens);
|
|
30
|
+
}
|
|
31
|
+
if (typeof usage.total_tokens === 'number') {
|
|
32
|
+
span.setAttribute(USAGE_TOTAL_TOKENS_ATTR, usage.total_tokens);
|
|
33
|
+
}
|
|
34
|
+
// Cached-prompt tokens ride in the same streamed usage chunk; mirror the
|
|
35
|
+
// non-streamed cache backfill in get_cache_and_reasoning_telemetry.js.
|
|
36
|
+
const cachedTokens = usage.prompt_tokens_details && usage.prompt_tokens_details.cached_tokens;
|
|
37
|
+
if (cachedTokens) {
|
|
38
|
+
span.setAttribute(CACHE_READ_INPUT_TOKENS_ATTR, cachedTokens);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
class OpenAIStreamingUsageInstrumentor {
|
|
42
|
+
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
43
|
+
_patched = [];
|
|
44
|
+
// No-op if the instrumentor's internals have changed shape — the worst case is
|
|
45
|
+
// streamed spans without token counts, never a crash.
|
|
46
|
+
instrument(instrumentation) {
|
|
47
|
+
const original = instrumentation && instrumentation._streamingWrapPromise;
|
|
48
|
+
if (typeof original !== 'function')
|
|
49
|
+
return;
|
|
50
|
+
instrumentation._streamingWrapPromise = function streamingWrapWithUsage(streamArgs) {
|
|
51
|
+
const span = streamArgs && streamArgs.span;
|
|
52
|
+
const wrappedStream = original.call(this, streamArgs);
|
|
53
|
+
if (!span)
|
|
54
|
+
return wrappedStream;
|
|
55
|
+
return (async function* backfillUsage() {
|
|
56
|
+
for await (const chunk of wrappedStream) {
|
|
57
|
+
const usage = chunk && chunk.usage;
|
|
58
|
+
if (usage && span.isRecording()) {
|
|
59
|
+
_setUsageAttributes(span, usage);
|
|
60
|
+
}
|
|
61
|
+
yield chunk;
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
};
|
|
65
|
+
this._patched.push([instrumentation, '_streamingWrapPromise', original]);
|
|
66
|
+
}
|
|
67
|
+
uninstrument() {
|
|
68
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
69
|
+
owner[methodName] = original;
|
|
70
|
+
}
|
|
71
|
+
this._patched = [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.OpenAIStreamingUsageInstrumentor = OpenAIStreamingUsageInstrumentor;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured-output schema location for OpenAI requests.
|
|
3
|
+
*
|
|
4
|
+
* OpenAI pins an answer to a JSON schema with
|
|
5
|
+
* `response_format: { type: 'json_schema', json_schema: { schema } }`. The
|
|
6
|
+
* traceloop instrumentor records the request's model and sampling params but
|
|
7
|
+
* never the schema - which OpenAI bills as input.
|
|
8
|
+
*
|
|
9
|
+
* (The Python OpenLLMetry instrumentor stamps this one natively, so the Python
|
|
10
|
+
* SDK has no OpenAI counterpart to this file - see known_divergences.md.)
|
|
11
|
+
*/
|
|
12
|
+
export declare function readStructuredOutputSchema(params: any): unknown;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Structured-output schema location for OpenAI requests.
|
|
4
|
+
*
|
|
5
|
+
* OpenAI pins an answer to a JSON schema with
|
|
6
|
+
* `response_format: { type: 'json_schema', json_schema: { schema } }`. The
|
|
7
|
+
* traceloop instrumentor records the request's model and sampling params but
|
|
8
|
+
* never the schema - which OpenAI bills as input.
|
|
9
|
+
*
|
|
10
|
+
* (The Python OpenLLMetry instrumentor stamps this one natively, so the Python
|
|
11
|
+
* SDK has no OpenAI counterpart to this file - see known_divergences.md.)
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.readStructuredOutputSchema = readStructuredOutputSchema;
|
|
15
|
+
function readStructuredOutputSchema(params) {
|
|
16
|
+
const responseFormat = params && params.response_format;
|
|
17
|
+
if (!responseFormat || responseFormat.type !== 'json_schema')
|
|
18
|
+
return undefined;
|
|
19
|
+
return responseFormat.json_schema && responseFormat.json_schema.schema;
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI telemetry complement — the registered orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Wires every weflayr-supplied patch the traceloop OpenAI instrumentor misses:
|
|
5
|
+
* - the audio (TTS/STT) and embeddings endpoints it doesn't cover, and
|
|
6
|
+
* - usage it drops on the chat endpoint it does cover: streamed token usage and
|
|
7
|
+
* non-streamed cached-prompt tokens (these enrich the traceloop instrumentor
|
|
8
|
+
* instance, hence the `instrumentation` argument), and
|
|
9
|
+
* - the request's structured-output schema, and
|
|
10
|
+
* - Bedrock Mantle's provider attribution and reasoning output.
|
|
11
|
+
*/
|
|
12
|
+
export declare class OpenAITelemetryInstrumentor {
|
|
13
|
+
private _subInstrumentors;
|
|
14
|
+
constructor();
|
|
15
|
+
instrument(instrumentation: any): void;
|
|
16
|
+
uninstrument(): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenAI telemetry complement — the registered orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* Wires every weflayr-supplied patch the traceloop OpenAI instrumentor misses:
|
|
6
|
+
* - the audio (TTS/STT) and embeddings endpoints it doesn't cover, and
|
|
7
|
+
* - usage it drops on the chat endpoint it does cover: streamed token usage and
|
|
8
|
+
* non-streamed cached-prompt tokens (these enrich the traceloop instrumentor
|
|
9
|
+
* instance, hence the `instrumentation` argument), and
|
|
10
|
+
* - the request's structured-output schema, and
|
|
11
|
+
* - Bedrock Mantle's provider attribution and reasoning output.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.OpenAITelemetryInstrumentor = void 0;
|
|
15
|
+
const get_structured_output_schema_telemetry_1 = require("../shared/get_structured_output_schema_telemetry");
|
|
16
|
+
const get_audio_telemetry_1 = require("./get_audio_telemetry");
|
|
17
|
+
const get_bedrock_mantle_telemetry_1 = require("./get_bedrock_mantle_telemetry");
|
|
18
|
+
const get_cache_config_telemetry_1 = require("./get_cache_config_telemetry");
|
|
19
|
+
const get_embeddings_telemetry_1 = require("./get_embeddings_telemetry");
|
|
20
|
+
const get_streaming_usage_1 = require("./get_streaming_usage");
|
|
21
|
+
const get_cache_and_reasoning_telemetry_1 = require("./get_cache_and_reasoning_telemetry");
|
|
22
|
+
const get_structured_output_schema_telemetry_2 = require("./get_structured_output_schema_telemetry");
|
|
23
|
+
class OpenAITelemetryInstrumentor {
|
|
24
|
+
_subInstrumentors;
|
|
25
|
+
constructor() {
|
|
26
|
+
this._subInstrumentors = [
|
|
27
|
+
new get_audio_telemetry_1.OpenAIAudioInstrumentor(),
|
|
28
|
+
new get_embeddings_telemetry_1.OpenAIEmbeddingsInstrumentor(),
|
|
29
|
+
new get_streaming_usage_1.OpenAIStreamingUsageInstrumentor(),
|
|
30
|
+
new get_cache_and_reasoning_telemetry_1.OpenAIPromptCachingInstrumentor(),
|
|
31
|
+
new get_cache_config_telemetry_1.OpenAICacheConfigInstrumentor(),
|
|
32
|
+
new get_bedrock_mantle_telemetry_1.OpenAIBedrockMantleInstrumentor(),
|
|
33
|
+
new get_structured_output_schema_telemetry_1.TraceloopStructuredOutputSchemaInstrumentor(get_structured_output_schema_telemetry_2.readStructuredOutputSchema),
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
// The endpoint complements ignore `instrumentation`; the streaming/cache ones
|
|
37
|
+
// patch its internal handlers.
|
|
38
|
+
instrument(instrumentation) {
|
|
39
|
+
for (const sub of this._subInstrumentors)
|
|
40
|
+
sub.instrument(instrumentation);
|
|
41
|
+
}
|
|
42
|
+
uninstrument() {
|
|
43
|
+
for (const sub of this._subInstrumentors)
|
|
44
|
+
sub.uninstrument();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.OpenAITelemetryInstrumentor = OpenAITelemetryInstrumentor;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured-output schema capture, shared by every provider complement.
|
|
3
|
+
*
|
|
4
|
+
* Each provider asks for structured output under its own request field (OpenAI
|
|
5
|
+
* `response_format`, Anthropic `output_config.format`, Cohere `response_format`,
|
|
6
|
+
* Gemini `responseJsonSchema`), but they all serialize the schema into the
|
|
7
|
+
* request the same way - and OpenAI, Anthropic and Vercel bill it as input - so
|
|
8
|
+
* the complements stamp one provider-agnostic attribute and the worker stores it
|
|
9
|
+
* in `input_structured_output_schema`.
|
|
10
|
+
*
|
|
11
|
+
* Every traceloop instrumentor builds its span in `startSpan({ params })`, so
|
|
12
|
+
* the three that go through one (OpenAI, Anthropic, Cohere) share the wrapper
|
|
13
|
+
* below and supply only a reader for their own request vocabulary. Google is
|
|
14
|
+
* wrapped at its own call site, and stamps through `setStructuredOutputSchemaTelemetry`
|
|
15
|
+
* directly.
|
|
16
|
+
*/
|
|
17
|
+
import type { Span } from '@opentelemetry/api';
|
|
18
|
+
export declare const STRUCTURED_OUTPUT_SCHEMA_ATTRIBUTE = "gen_ai.request.structured_output_schema";
|
|
19
|
+
/**
|
|
20
|
+
* Stamp the requested JSON schema. JS callers always reach the request with a
|
|
21
|
+
* plain JSON-schema object (Zod and friends are converted by the client before
|
|
22
|
+
* it), so unlike the Python SDK there is no output-model shape to convert here.
|
|
23
|
+
*/
|
|
24
|
+
export declare function setStructuredOutputSchemaTelemetry(span: Span, schema: unknown): void;
|
|
25
|
+
/**
|
|
26
|
+
* Stamp the schema of every span a traceloop instrumentor opens, reading it out
|
|
27
|
+
* of the request with the provider's own `readSchema`.
|
|
28
|
+
*/
|
|
29
|
+
export declare class TraceloopStructuredOutputSchemaInstrumentor {
|
|
30
|
+
private readonly _readSchema;
|
|
31
|
+
private _patched;
|
|
32
|
+
constructor(_readSchema: (params: any) => unknown);
|
|
33
|
+
instrument(instrumentation: any): void;
|
|
34
|
+
uninstrument(): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Structured-output schema capture, shared by every provider complement.
|
|
4
|
+
*
|
|
5
|
+
* Each provider asks for structured output under its own request field (OpenAI
|
|
6
|
+
* `response_format`, Anthropic `output_config.format`, Cohere `response_format`,
|
|
7
|
+
* Gemini `responseJsonSchema`), but they all serialize the schema into the
|
|
8
|
+
* request the same way - and OpenAI, Anthropic and Vercel bill it as input - so
|
|
9
|
+
* the complements stamp one provider-agnostic attribute and the worker stores it
|
|
10
|
+
* in `input_structured_output_schema`.
|
|
11
|
+
*
|
|
12
|
+
* Every traceloop instrumentor builds its span in `startSpan({ params })`, so
|
|
13
|
+
* the three that go through one (OpenAI, Anthropic, Cohere) share the wrapper
|
|
14
|
+
* below and supply only a reader for their own request vocabulary. Google is
|
|
15
|
+
* wrapped at its own call site, and stamps through `setStructuredOutputSchemaTelemetry`
|
|
16
|
+
* directly.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.TraceloopStructuredOutputSchemaInstrumentor = exports.STRUCTURED_OUTPUT_SCHEMA_ATTRIBUTE = void 0;
|
|
20
|
+
exports.setStructuredOutputSchemaTelemetry = setStructuredOutputSchemaTelemetry;
|
|
21
|
+
exports.STRUCTURED_OUTPUT_SCHEMA_ATTRIBUTE = 'gen_ai.request.structured_output_schema';
|
|
22
|
+
/**
|
|
23
|
+
* Stamp the requested JSON schema. JS callers always reach the request with a
|
|
24
|
+
* plain JSON-schema object (Zod and friends are converted by the client before
|
|
25
|
+
* it), so unlike the Python SDK there is no output-model shape to convert here.
|
|
26
|
+
*/
|
|
27
|
+
function setStructuredOutputSchemaTelemetry(span, schema) {
|
|
28
|
+
if (!span || !span.isRecording || !span.isRecording())
|
|
29
|
+
return;
|
|
30
|
+
if (!schema || typeof schema !== 'object')
|
|
31
|
+
return;
|
|
32
|
+
span.setAttribute(exports.STRUCTURED_OUTPUT_SCHEMA_ATTRIBUTE, JSON.stringify(schema));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Stamp the schema of every span a traceloop instrumentor opens, reading it out
|
|
36
|
+
* of the request with the provider's own `readSchema`.
|
|
37
|
+
*/
|
|
38
|
+
class TraceloopStructuredOutputSchemaInstrumentor {
|
|
39
|
+
_readSchema;
|
|
40
|
+
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
41
|
+
_patched = [];
|
|
42
|
+
constructor(_readSchema) {
|
|
43
|
+
this._readSchema = _readSchema;
|
|
44
|
+
}
|
|
45
|
+
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
46
|
+
// the worst case is a missing schema, never a crash.
|
|
47
|
+
instrument(instrumentation) {
|
|
48
|
+
if (!instrumentation)
|
|
49
|
+
return;
|
|
50
|
+
// OpenAI and Anthropic build their span in `startSpan`, Cohere in
|
|
51
|
+
// `_startSpan`; all receive the same `{ params }` request.
|
|
52
|
+
const methodName = typeof instrumentation.startSpan === 'function' ? 'startSpan' : '_startSpan';
|
|
53
|
+
const startSpanOriginal = instrumentation[methodName];
|
|
54
|
+
if (typeof startSpanOriginal !== 'function')
|
|
55
|
+
return;
|
|
56
|
+
const readSchema = this._readSchema;
|
|
57
|
+
instrumentation[methodName] = function startSpanWithStructuredOutputSchema(request) {
|
|
58
|
+
const span = startSpanOriginal.call(this, request);
|
|
59
|
+
try {
|
|
60
|
+
setStructuredOutputSchemaTelemetry(span, readSchema(request && request.params));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Never break span creation over an enrichment failure.
|
|
64
|
+
}
|
|
65
|
+
return span;
|
|
66
|
+
};
|
|
67
|
+
this._patched.push([instrumentation, methodName, startSpanOriginal]);
|
|
68
|
+
}
|
|
69
|
+
uninstrument() {
|
|
70
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
71
|
+
owner[methodName] = original;
|
|
72
|
+
}
|
|
73
|
+
this._patched = [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.TraceloopStructuredOutputSchemaInstrumentor = TraceloopStructuredOutputSchemaInstrumentor;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VercelAiSdkInstrumentor = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Instrumentor for the Vercel AI SDK (`ai` package, v7+).
|
|
6
|
+
*
|
|
7
|
+
* The AI SDK ships its own OTel integration in `@ai-sdk/otel`; instrumenting
|
|
8
|
+
* is registering it globally so every generateText/streamText/embed call emits
|
|
9
|
+
* GenAI semconv spans through the tracer provider weflayr's setup() installed.
|
|
10
|
+
* `usage: true` adds the `ai.usage.*` token details (reasoning tokens, uncached
|
|
11
|
+
* input) that the semconv attributes do not carry; `providerMetadata: true`
|
|
12
|
+
* opts into `ai.response.providerMetadata` on the spans that carry it.
|
|
13
|
+
*
|
|
14
|
+
* The aggregate spans the integration emits around each model call
|
|
15
|
+
* (invoke_agent, agent_step, duplicated embeddings spans) are filtered out
|
|
16
|
+
* before export by otel/vercel-ai-sdk-span-filter.js.
|
|
17
|
+
*/
|
|
18
|
+
const get_cache_creation_telemetry_1 = require("./get_cache_creation_telemetry");
|
|
19
|
+
const get_structured_output_schema_telemetry_1 = require("./get_structured_output_schema_telemetry");
|
|
20
|
+
class VercelAiSdkInstrumentor {
|
|
21
|
+
instrument() {
|
|
22
|
+
let registerTelemetry;
|
|
23
|
+
let OpenTelemetry;
|
|
24
|
+
try {
|
|
25
|
+
({ registerTelemetry } = require('ai'));
|
|
26
|
+
({ OpenTelemetry } = require('@ai-sdk/otel'));
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
throw new Error('weflayr.autoInstrument("vercel_ai_gateway"): failed to load "ai" / "@ai-sdk/otel". ' +
|
|
30
|
+
`Install them with: npm install ai @ai-sdk/otel\nUnderlying error: ${err.message}`);
|
|
31
|
+
}
|
|
32
|
+
// registerTelemetry only exists since v7.
|
|
33
|
+
if (typeof registerTelemetry !== 'function') {
|
|
34
|
+
throw new Error('weflayr.autoInstrument("vercel_ai_gateway") requires version 7+ of the Vercel AI SDK ' +
|
|
35
|
+
'(the "ai" package), which added global telemetry registration. ' +
|
|
36
|
+
'Upgrade with: npm install ai@latest @ai-sdk/otel.\n' +
|
|
37
|
+
'On ai <= 6 telemetry can only be enabled per call: remove "vercel_ai_gateway" from ' +
|
|
38
|
+
'aiSdks (pass an empty list if no other sdks need to be instrumented). Instead, make sure ' +
|
|
39
|
+
'experimental_telemetry: { isEnabled: true } is passed on each generateText/streamText call.');
|
|
40
|
+
}
|
|
41
|
+
const dispatcher = new OpenTelemetry({ usage: true, providerMetadata: true });
|
|
42
|
+
// The integration never emits the raw provider usage, the only place the
|
|
43
|
+
// written cache tokens per TTL (Anthropic's `cache_creation`) are
|
|
44
|
+
// reported; this complement stamps it on the model-call span (see
|
|
45
|
+
// get_cache_creation_telemetry).
|
|
46
|
+
new get_cache_creation_telemetry_1.VercelCacheCreationInstrumentor().instrument(dispatcher);
|
|
47
|
+
// generateObject's schema is only stamped on the root span the weflayr
|
|
48
|
+
// filter drops; this complement re-stamps it on the model-call span (see
|
|
49
|
+
// get_structured_output_schema_telemetry).
|
|
50
|
+
new get_structured_output_schema_telemetry_1.VercelStructuredOutputSchemaInstrumentor().instrument(dispatcher);
|
|
51
|
+
registerTelemetry(dispatcher);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.VercelAiSdkInstrumentor = VercelAiSdkInstrumentor;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache-write capture for Vercel AI SDK (ai >= 7) model-call spans.
|
|
3
|
+
*
|
|
4
|
+
* Anthropic reports the written cache tokens per TTL in its raw usage
|
|
5
|
+
* (`usage.cache_creation`, ephemeral_5m/1h buckets — billed at different
|
|
6
|
+
* rates). ai >= 7 carries that verbatim on the model-call telemetry event as
|
|
7
|
+
* `event.usage.raw` (a documented field of LanguageModelUsage), but the
|
|
8
|
+
* `@ai-sdk/otel` integration never emits it, so the span loses it.
|
|
9
|
+
*
|
|
10
|
+
* Wrap the telemetry dispatcher's `onLanguageModelCallEnd(event)` (a public
|
|
11
|
+
* callback of the telemetry interface) to read `usage.cache_creation` off the
|
|
12
|
+
* raw usage and stamp it verbatim as `weflayr.cache.creation` on the
|
|
13
|
+
* still-open model-call span (reached through the integration's call state)
|
|
14
|
+
* before the original handler ends it; the normalizer owns the mapping to TTL
|
|
15
|
+
* tiers.
|
|
16
|
+
*/
|
|
17
|
+
export declare function cacheCreationFromRawUsage(rawUsage: any): Record<string, unknown> | undefined;
|
|
18
|
+
export declare class VercelCacheCreationInstrumentor {
|
|
19
|
+
private _patched;
|
|
20
|
+
instrument(dispatcher: any): void;
|
|
21
|
+
uninstrument(): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cache-write capture for Vercel AI SDK (ai >= 7) model-call spans.
|
|
4
|
+
*
|
|
5
|
+
* Anthropic reports the written cache tokens per TTL in its raw usage
|
|
6
|
+
* (`usage.cache_creation`, ephemeral_5m/1h buckets — billed at different
|
|
7
|
+
* rates). ai >= 7 carries that verbatim on the model-call telemetry event as
|
|
8
|
+
* `event.usage.raw` (a documented field of LanguageModelUsage), but the
|
|
9
|
+
* `@ai-sdk/otel` integration never emits it, so the span loses it.
|
|
10
|
+
*
|
|
11
|
+
* Wrap the telemetry dispatcher's `onLanguageModelCallEnd(event)` (a public
|
|
12
|
+
* callback of the telemetry interface) to read `usage.cache_creation` off the
|
|
13
|
+
* raw usage and stamp it verbatim as `weflayr.cache.creation` on the
|
|
14
|
+
* still-open model-call span (reached through the integration's call state)
|
|
15
|
+
* before the original handler ends it; the normalizer owns the mapping to TTL
|
|
16
|
+
* tiers.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.VercelCacheCreationInstrumentor = void 0;
|
|
20
|
+
exports.cacheCreationFromRawUsage = cacheCreationFromRawUsage;
|
|
21
|
+
const CACHE_CREATION_ATTR = 'weflayr.cache.creation';
|
|
22
|
+
// The raw `usage.cache_creation` of a provider usage, or undefined when the
|
|
23
|
+
// provider reports none (or reports only zeros — non-caching calls carry no
|
|
24
|
+
// attribute).
|
|
25
|
+
function cacheCreationFromRawUsage(rawUsage) {
|
|
26
|
+
const cacheCreation = rawUsage && rawUsage.cache_creation;
|
|
27
|
+
if (!cacheCreation)
|
|
28
|
+
return undefined;
|
|
29
|
+
return Object.values(cacheCreation).some(Boolean) ? cacheCreation : undefined;
|
|
30
|
+
}
|
|
31
|
+
class VercelCacheCreationInstrumentor {
|
|
32
|
+
// [owner, methodName, originalFunction] for uninstrument().
|
|
33
|
+
_patched = [];
|
|
34
|
+
// No-op (and never throws) if the interface is missing — the worst case is
|
|
35
|
+
// the cache-write capture staying empty, exactly the pre-complement
|
|
36
|
+
// behaviour.
|
|
37
|
+
instrument(dispatcher) {
|
|
38
|
+
if (!dispatcher || typeof dispatcher.onLanguageModelCallEnd !== 'function')
|
|
39
|
+
return;
|
|
40
|
+
const original = dispatcher.onLanguageModelCallEnd;
|
|
41
|
+
dispatcher.onLanguageModelCallEnd = function onLanguageModelCallEndWithCacheCreation(event) {
|
|
42
|
+
try {
|
|
43
|
+
const cacheCreation = cacheCreationFromRawUsage(event && event.usage && event.usage.raw);
|
|
44
|
+
if (cacheCreation) {
|
|
45
|
+
const callState = this.getCallState && this.getCallState(event.callId);
|
|
46
|
+
const span = callState && callState.inferenceSpan;
|
|
47
|
+
if (span && typeof span.setAttribute === 'function') {
|
|
48
|
+
span.setAttribute(CACHE_CREATION_ATTR, JSON.stringify(cacheCreation));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Never break span finalization over an enrichment failure.
|
|
54
|
+
}
|
|
55
|
+
return original.call(this, event);
|
|
56
|
+
};
|
|
57
|
+
this._patched.push([dispatcher, 'onLanguageModelCallEnd', original]);
|
|
58
|
+
}
|
|
59
|
+
uninstrument() {
|
|
60
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
61
|
+
owner[methodName] = original;
|
|
62
|
+
}
|
|
63
|
+
this._patched = [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.VercelCacheCreationInstrumentor = VercelCacheCreationInstrumentor;
|