weflayr 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{api → src/api}/generated/facade.gen.d.ts +86 -12
- package/dist/{api → src/api}/generated/facade.gen.js +4 -2
- package/dist/src/api/generated/index.d.ts +2 -0
- package/dist/{api → src/api}/generated/index.js +3 -1
- package/dist/{api → src/api}/generated/sdk.gen.d.ts +11 -3
- package/dist/{api → src/api}/generated/sdk.gen.js +34 -4
- package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +436 -153
- package/dist/{api → src/api}/makeEndpoint.js +1 -3
- package/dist/src/auto-instrument.d.ts +42 -0
- package/dist/src/auto-instrument.js +156 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +49 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.d.ts +20 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +114 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.js +92 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +141 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.js +17 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +50 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +39 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +97 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +102 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.js +91 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +21 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +54 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.d.ts +19 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.js +81 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +126 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.d.ts +17 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.js +85 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +68 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.js +77 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +88 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.js +18 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.d.ts +31 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.js +106 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.js +75 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +44 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.d.ts +28 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.js +209 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.js +28 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.js +154 -0
- package/dist/src/instrumentation/google/get_cache_telemetry.d.ts +16 -0
- package/{src → dist/src}/instrumentation/google/get_cache_telemetry.js +18 -19
- package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_embeddings_telemetry.js +85 -0
- package/dist/src/instrumentation/google/get_provider_name.d.ts +11 -0
- package/{src → dist/src}/instrumentation/google/get_provider_name.js +11 -12
- package/{src/instrumentation/google/get_reasoning_telemetry.js → dist/src/instrumentation/google/get_reasoning_telemetry.d.ts} +1 -11
- package/dist/src/instrumentation/google/get_reasoning_telemetry.js +21 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.js +19 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.js +98 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.js +123 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +148 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +72 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +91 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +86 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.d.ts +21 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.js +74 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +20 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +76 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +3 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.js +54 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +66 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +24 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +84 -0
- package/dist/src/logger.d.ts +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/otel/exporter.d.ts +19 -0
- package/dist/src/otel/exporter.js +94 -0
- package/dist/src/otel/propagation.d.ts +46 -0
- package/dist/src/otel/propagation.js +110 -0
- package/dist/src/otel/span-filter.d.ts +3 -0
- package/dist/src/otel/span-filter.js +61 -0
- package/dist/src/otel/span-processor.d.ts +55 -0
- package/dist/src/otel/span-processor.js +169 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +12 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.js +88 -0
- package/dist/src/setup.d.ts +50 -0
- package/dist/src/setup.js +155 -0
- package/package.json +11 -5
- package/.prettierignore +0 -2
- package/.prettierrc.json +0 -7
- package/LICENSE-MIT +0 -21
- package/NOTICE +0 -12
- package/dist/api/generated/index.d.ts +0 -2
- package/dist/api/generated/types.gen.d.ts +0 -1005
- package/eslint.config.mjs +0 -36
- package/index.d.ts +0 -170
- package/openapi-ts.config.mjs +0 -9
- package/scripts/generate_facade.mjs +0 -86
- package/src/api/caseConversion.ts +0 -58
- package/src/api/generated/client/client.gen.ts +0 -277
- package/src/api/generated/client/index.ts +0 -27
- package/src/api/generated/client/types.gen.ts +0 -218
- package/src/api/generated/client/utils.gen.ts +0 -316
- package/src/api/generated/client.gen.ts +0 -16
- package/src/api/generated/core/auth.gen.ts +0 -48
- package/src/api/generated/core/bodySerializer.gen.ts +0 -82
- package/src/api/generated/core/params.gen.ts +0 -178
- package/src/api/generated/core/pathSerializer.gen.ts +0 -171
- package/src/api/generated/core/queryKeySerializer.gen.ts +0 -117
- package/src/api/generated/core/serverSentEvents.gen.ts +0 -242
- package/src/api/generated/core/types.gen.ts +0 -110
- package/src/api/generated/core/utils.gen.ts +0 -140
- package/src/api/generated/facade.gen.ts +0 -104
- package/src/api/generated/index.ts +0 -4
- package/src/api/generated/sdk.gen.ts +0 -175
- package/src/api/index.ts +0 -45
- package/src/api/makeEndpoint.ts +0 -54
- package/src/auto-instrument.js +0 -172
- package/src/index.js +0 -19
- package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -118
- package/src/instrumentation/anthropic/get_cache_config_telemetry.js +0 -90
- package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
- package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -55
- package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -39
- package/src/instrumentation/bedrock/get_cache_config_telemetry.js +0 -106
- package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -109
- package/src/instrumentation/bedrock/get_input_message_content_telemetry.js +0 -94
- package/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +0 -87
- package/src/instrumentation/bedrock/set_telemetry_attributes.js +0 -40
- package/src/instrumentation/cohere/get_chat_telemetry.js +0 -88
- package/src/instrumentation/cohere/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/cohere/set_telemetry_attributes.js +0 -32
- package/src/instrumentation/elevenlabs/full_instrumentation.js +0 -236
- package/src/instrumentation/google/get_audio_telemetry.js +0 -30
- package/src/instrumentation/google/get_cache_operations_telemetry.js +0 -171
- package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
- package/src/instrumentation/google/set_telemetry_attributes.js +0 -104
- package/src/instrumentation/openai/get_audio_telemetry.js +0 -149
- package/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +0 -71
- package/src/instrumentation/openai/get_cache_config_telemetry.js +0 -63
- package/src/instrumentation/openai/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/openai/get_streaming_usage.js +0 -81
- package/src/instrumentation/openai/set_telemetry_attributes.js +0 -41
- package/src/instrumentation/vercel/full_instrumentation.js +0 -52
- package/src/instrumentation/vercel/get_cache_creation_telemetry.js +0 -70
- package/src/logger.js +0 -8
- package/src/otel/exporter.js +0 -108
- package/src/otel/propagation.js +0 -146
- package/src/otel/span-filter.js +0 -69
- package/src/otel/span-processor.js +0 -170
- package/src/otel/vercel-ai-sdk-span-filter.js +0 -79
- package/src/setup.js +0 -167
- package/tests/api/index.test.js +0 -437
- package/tests/index.test.js +0 -884
- package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -181
- package/tests/instrumentation/anthropic/get_cache_config_telemetry.test.js +0 -92
- package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
- package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -97
- package/tests/instrumentation/bedrock/get_cache_config_telemetry.test.js +0 -107
- package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -118
- package/tests/instrumentation/bedrock/get_input_message_content_telemetry.test.js +0 -89
- package/tests/instrumentation/bedrock/get_streaming_output_message_telemetry.test.js +0 -87
- package/tests/instrumentation/cohere/get_chat_telemetry.test.js +0 -148
- package/tests/instrumentation/cohere/get_embeddings_telemetry.test.js +0 -132
- package/tests/instrumentation/elevenlabs/full_instrumentation.test.js +0 -258
- package/tests/instrumentation/google/get_cache_operations_telemetry.test.js +0 -130
- package/tests/instrumentation/google/get_embeddings_telemetry.test.js +0 -128
- package/tests/instrumentation/google/get_reasoning_telemetry.test.js +0 -41
- package/tests/instrumentation/google/set_telemetry_attributes.test.js +0 -219
- package/tests/instrumentation/openai/get_audio_telemetry.test.js +0 -170
- package/tests/instrumentation/openai/get_cache_and_reasoning_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_cache_config_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_embeddings_telemetry.test.js +0 -145
- package/tests/instrumentation/openai/get_streaming_usage.test.js +0 -105
- package/tests/instrumentation/vercel/full_instrumentation.test.js +0 -16
- package/tests/instrumentation/vercel/get_cache_creation_telemetry.test.js +0 -96
- package/tests/otel/vercel-ai-sdk-span-filter.test.js +0 -159
- package/tsconfig.json +0 -18
- /package/dist/{api → src/api}/caseConversion.d.ts +0 -0
- /package/dist/{api → src/api}/caseConversion.js +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/index.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/index.js +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/types.gen.js +0 -0
- /package/dist/{api → src/api}/index.d.ts +0 -0
- /package/dist/{api → src/api}/index.js +0 -0
- /package/dist/{api → src/api}/makeEndpoint.d.ts +0 -0
package/src/otel/exporter.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const retry = require('async-retry');
|
|
4
|
-
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
|
|
5
|
-
|
|
6
|
-
const { debug } = require('../logger');
|
|
7
|
-
|
|
8
|
-
// OTLP-spec retryable statuses: rate-limit + transient server errors.
|
|
9
|
-
const _RETRYABLE_STATUS = new Set([429, 502, 503, 504]);
|
|
10
|
-
|
|
11
|
-
function _isNonRetryable(err) {
|
|
12
|
-
// Tells async-retry's `bail` whether to give up: bail on 4xx (other than
|
|
13
|
-
// 429); retry on retryable statuses and on transport errors (no status).
|
|
14
|
-
const status = err?.exportResult?.error?.code;
|
|
15
|
-
return status !== undefined && !_RETRYABLE_STATUS.has(status);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function _formatFailure(result, url) {
|
|
19
|
-
const err = result?.error;
|
|
20
|
-
const status = err?.code;
|
|
21
|
-
const body = typeof err?.data === 'string' ? err.data.slice(0, 200) : undefined;
|
|
22
|
-
const message = err?.message || String(err) || 'unknown error';
|
|
23
|
-
let hint = '';
|
|
24
|
-
if (status === 401 || status === 403) {
|
|
25
|
-
hint = ' Check your apiKey / WEFLAYR_API_KEY.';
|
|
26
|
-
} else if (status === 404) {
|
|
27
|
-
hint = ' Check the baseUrl is correct.';
|
|
28
|
-
} else if (!status && /ECONN|ENOTFOUND|timeout/i.test(message)) {
|
|
29
|
-
hint = ' The endpoint is unreachable — check baseUrl and network.';
|
|
30
|
-
}
|
|
31
|
-
return (
|
|
32
|
-
`Weflayr: failed to export telemetry to ${url}` +
|
|
33
|
-
(status ? ` (HTTP ${status})` : '') +
|
|
34
|
-
`: ${message}.${hint}` +
|
|
35
|
-
(body ? ` Response: ${body}` : '')
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Wrap a callback-style OTel exporter `export(spans, cb)` with retry. Retries
|
|
40
|
-
// transport errors + retryable HTTP statuses; bails on other 4xx.
|
|
41
|
-
function _exportWithRetry(originalExport, { maxRetries, maxRetryMs }) {
|
|
42
|
-
return (spans, resultCallback) => {
|
|
43
|
-
retry(
|
|
44
|
-
async (bail) => {
|
|
45
|
-
const result = await new Promise((resolve) => originalExport(spans, resolve));
|
|
46
|
-
if (result?.code === 0) return result;
|
|
47
|
-
const err = new Error(result?.error?.message || 'export failed');
|
|
48
|
-
err.exportResult = result;
|
|
49
|
-
if (_isNonRetryable(err)) {
|
|
50
|
-
bail(err);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
throw err;
|
|
54
|
-
},
|
|
55
|
-
{ retries: maxRetries - 1, minTimeout: 50, maxRetryTime: maxRetryMs, factor: 2 }
|
|
56
|
-
)
|
|
57
|
-
.then((result) => resultCallback(result))
|
|
58
|
-
.catch((err) => resultCallback(err.exportResult || { code: 1, error: err }));
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function buildExporter({
|
|
63
|
-
baseUrl,
|
|
64
|
-
apiKey,
|
|
65
|
-
timeoutMs = 5000,
|
|
66
|
-
warnOnExportError = true,
|
|
67
|
-
maxRetries = 2,
|
|
68
|
-
maxRetryMs = 1000,
|
|
69
|
-
}) {
|
|
70
|
-
const url = `${baseUrl.replace(/\/$/, '')}/`;
|
|
71
|
-
const exporter = new OTLPTraceExporter({
|
|
72
|
-
url,
|
|
73
|
-
headers: {
|
|
74
|
-
Authorization: `Bearer ${apiKey}`,
|
|
75
|
-
},
|
|
76
|
-
timeoutMillis: timeoutMs,
|
|
77
|
-
});
|
|
78
|
-
const originalExport = exporter.export.bind(exporter);
|
|
79
|
-
const wrappedExport = _exportWithRetry(originalExport, { maxRetries, maxRetryMs });
|
|
80
|
-
exporter.export = (spans, resultCallback) => {
|
|
81
|
-
const start = Date.now();
|
|
82
|
-
debug('exporter.export: posting count=%d', spans.length);
|
|
83
|
-
wrappedExport(spans, (result) => {
|
|
84
|
-
const ms = Date.now() - start;
|
|
85
|
-
if (result && result.code === 0) {
|
|
86
|
-
debug('exporter.export: ok count=%d elapsed_ms=%d', spans.length, ms);
|
|
87
|
-
} else {
|
|
88
|
-
debug(
|
|
89
|
-
'exporter.export: FAIL count=%d elapsed_ms=%d code=%s error=%s',
|
|
90
|
-
spans.length,
|
|
91
|
-
ms,
|
|
92
|
-
result?.code,
|
|
93
|
-
result?.error?.message || result?.error || '(none)'
|
|
94
|
-
);
|
|
95
|
-
if (warnOnExportError) {
|
|
96
|
-
// console.warn writes to stderr synchronously. process.emitWarning
|
|
97
|
-
// is queued for the next tick, so a caller doing process.exit() from
|
|
98
|
-
// their error handler races past it and the warning is lost.
|
|
99
|
-
console.warn(`[weflayr] ${_formatFailure(result, url)}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
resultCallback(result);
|
|
103
|
-
});
|
|
104
|
-
};
|
|
105
|
-
return exporter;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
module.exports = { buildExporter, _exportWithRetry, _RETRYABLE_STATUS };
|
package/src/otel/propagation.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// Copyright 2024 Langfuse GmbH
|
|
2
|
-
// Licensed under the MIT License.
|
|
3
|
-
// Original source: https://github.com/langfuse/langfuse-js/blob/main/packages/core/src/propagation.ts
|
|
4
|
-
//
|
|
5
|
-
// Modifications copyright 2026 Weflayr
|
|
6
|
-
// This file has been modified from its original form:
|
|
7
|
-
// - Translated from TypeScript to JavaScript
|
|
8
|
-
// - Replaced Langfuse's fixed schema with Weflayr's structured metadata
|
|
9
|
-
// (featureName, customerName, providerNameOverride, extraTags) plus an
|
|
10
|
-
// auto-generated per-call uuid
|
|
11
|
-
// - Removed asBaggage cross-process propagation (not needed yet)
|
|
12
|
-
// - Removed experiment framework keys
|
|
13
|
-
// - Structured fields are written under stable `weflayr.*` span attribute
|
|
14
|
-
// keys; extraTags entries are written verbatim
|
|
15
|
-
//
|
|
16
|
-
// See LICENSE-MIT in the package root for the full license text.
|
|
17
|
-
|
|
18
|
-
'use strict';
|
|
19
|
-
|
|
20
|
-
const { randomUUID } = require('node:crypto');
|
|
21
|
-
const { context: otelContext, trace, createContextKey } = require('@opentelemetry/api');
|
|
22
|
-
|
|
23
|
-
const WEFLAYR_METADATA_CONTEXT_KEY = createContextKey('weflayr_metadata');
|
|
24
|
-
|
|
25
|
-
const FEATURE_NAME_ATTR = 'weflayr.feature_name';
|
|
26
|
-
const CUSTOMER_NAME_ATTR = 'weflayr.customer_name';
|
|
27
|
-
const PROVIDER_NAME_ATTR = 'weflayr.provider_name';
|
|
28
|
-
const UUID_ATTR = 'weflayr.uuid';
|
|
29
|
-
// Prefix for user-supplied `extraTags` so downstream normalizers can pick
|
|
30
|
-
// them out without colliding with instrumentor-set `gen_ai.*` attributes.
|
|
31
|
-
const EXTRA_TAG_PREFIX = 'weflayr.tag.';
|
|
32
|
-
|
|
33
|
-
function getMergedMetadataFromContext(parentContext) {
|
|
34
|
-
const ctx = parentContext ?? otelContext.active();
|
|
35
|
-
const existing = ctx.getValue(WEFLAYR_METADATA_CONTEXT_KEY);
|
|
36
|
-
return existing && typeof existing === 'object' ? { ...existing } : {};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function _buildAttributes({
|
|
40
|
-
featureName,
|
|
41
|
-
customerName,
|
|
42
|
-
providerNameOverride,
|
|
43
|
-
extraTags,
|
|
44
|
-
spanUuid,
|
|
45
|
-
}) {
|
|
46
|
-
const attributes = {
|
|
47
|
-
[FEATURE_NAME_ATTR]: featureName,
|
|
48
|
-
[CUSTOMER_NAME_ATTR]: customerName,
|
|
49
|
-
[UUID_ATTR]: spanUuid,
|
|
50
|
-
};
|
|
51
|
-
for (const [key, value] of Object.entries(extraTags)) {
|
|
52
|
-
attributes[`${EXTRA_TAG_PREFIX}${key}`] = value;
|
|
53
|
-
}
|
|
54
|
-
if (providerNameOverride !== undefined && providerNameOverride !== null) {
|
|
55
|
-
attributes[PROVIDER_NAME_ATTR] = providerNameOverride;
|
|
56
|
-
}
|
|
57
|
-
return attributes;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Attach Weflayr metadata to every LLM span created inside this scope.
|
|
62
|
-
*
|
|
63
|
-
* Callable form: `propagateMetadata(options, fn)` runs `fn` immediately.
|
|
64
|
-
* Decorator form: `propagateMetadata(options)` returns a wrapper usable as a
|
|
65
|
-
* function-decorator (`@propagateMetadata(...)` on class methods) or as a
|
|
66
|
-
* plain `wrap(fn)` -> wrappedFn helper. Each invocation of the wrapped
|
|
67
|
-
* function mints a fresh `uuid`, matching the callable form's "one uuid per
|
|
68
|
-
* propagateMetadata call" contract.
|
|
69
|
-
*/
|
|
70
|
-
function propagateMetadata(options, fn) {
|
|
71
|
-
const { featureName, customerId, providerName } = options ?? {};
|
|
72
|
-
let { customerName, providerNameOverride } = options ?? {};
|
|
73
|
-
if (customerId != null) {
|
|
74
|
-
process.emitWarning(
|
|
75
|
-
'propagateMetadata: `customerId` is deprecated, use `customerName`',
|
|
76
|
-
'DeprecationWarning'
|
|
77
|
-
);
|
|
78
|
-
customerName = customerName ?? customerId;
|
|
79
|
-
}
|
|
80
|
-
if (providerName != null) {
|
|
81
|
-
process.emitWarning(
|
|
82
|
-
'propagateMetadata: `providerName` is deprecated, use `providerNameOverride`',
|
|
83
|
-
'DeprecationWarning'
|
|
84
|
-
);
|
|
85
|
-
providerNameOverride = providerNameOverride ?? providerName;
|
|
86
|
-
}
|
|
87
|
-
if (featureName == null || customerName == null) {
|
|
88
|
-
throw new Error('propagateMetadata: `featureName` and `customerName` are required');
|
|
89
|
-
}
|
|
90
|
-
const normalizedOptions = { ...options, customerName, providerNameOverride };
|
|
91
|
-
|
|
92
|
-
if (typeof fn === 'function') {
|
|
93
|
-
return _runWithMetadata(normalizedOptions, fn);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return function decorator(target, key, descriptor) {
|
|
97
|
-
if (descriptor && typeof descriptor.value === 'function') {
|
|
98
|
-
const original = descriptor.value;
|
|
99
|
-
descriptor.value = function wrapped(...args) {
|
|
100
|
-
return _runWithMetadata(normalizedOptions, () => original.apply(this, args));
|
|
101
|
-
};
|
|
102
|
-
return descriptor;
|
|
103
|
-
}
|
|
104
|
-
if (typeof target === 'function') {
|
|
105
|
-
return function wrapped(...args) {
|
|
106
|
-
return _runWithMetadata(normalizedOptions, () => target.apply(this, args));
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return target;
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function _runWithMetadata(options, fn) {
|
|
114
|
-
const { featureName, customerName, providerNameOverride, extraTags } = options;
|
|
115
|
-
const attributes = _buildAttributes({
|
|
116
|
-
featureName,
|
|
117
|
-
customerName,
|
|
118
|
-
providerNameOverride,
|
|
119
|
-
extraTags: extraTags ?? {},
|
|
120
|
-
spanUuid: randomUUID().replace(/-/g, ''),
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const parent = otelContext.active();
|
|
124
|
-
const merged = { ...getMergedMetadataFromContext(parent), ...attributes };
|
|
125
|
-
const newContext = parent.setValue(WEFLAYR_METADATA_CONTEXT_KEY, merged);
|
|
126
|
-
|
|
127
|
-
const activeSpan = trace.getActiveSpan();
|
|
128
|
-
if (activeSpan && activeSpan.isRecording && activeSpan.isRecording()) {
|
|
129
|
-
for (const [k, v] of Object.entries(attributes)) {
|
|
130
|
-
activeSpan.setAttribute(k, v);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return otelContext.with(newContext, fn);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
module.exports = {
|
|
138
|
-
propagateMetadata,
|
|
139
|
-
getMergedMetadataFromContext,
|
|
140
|
-
WEFLAYR_METADATA_CONTEXT_KEY,
|
|
141
|
-
FEATURE_NAME_ATTR,
|
|
142
|
-
CUSTOMER_NAME_ATTR,
|
|
143
|
-
PROVIDER_NAME_ATTR,
|
|
144
|
-
UUID_ATTR,
|
|
145
|
-
EXTRA_TAG_PREFIX,
|
|
146
|
-
};
|
package/src/otel/span-filter.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// Copyright 2024 Langfuse GmbH
|
|
2
|
-
// Licensed under the MIT License.
|
|
3
|
-
// Original source: https://github.com/langfuse/langfuse-js/blob/main/packages/otel/src/span-filter.ts
|
|
4
|
-
//
|
|
5
|
-
// Modifications copyright 2026 Weflayr
|
|
6
|
-
// This file has been modified from its original form:
|
|
7
|
-
// - Translated from TypeScript to JavaScript
|
|
8
|
-
// - Dropped the LANGFUSE_TRACER_NAME special case (we have no SDK-internal tracer)
|
|
9
|
-
// - Dropped isLangfuseSpan
|
|
10
|
-
// - Export only spans from known LLM instrumentation scopes: general-purpose
|
|
11
|
-
// APM SDKs (Sentry, Datadog, ...) also instrument LLM calls with gen_ai.*
|
|
12
|
-
// attributes, so the original "any span with gen_ai.* attributes" check
|
|
13
|
-
// exported the same call twice
|
|
14
|
-
//
|
|
15
|
-
// See LICENSE-MIT in the package root for the full license text.
|
|
16
|
-
|
|
17
|
-
'use strict';
|
|
18
|
-
|
|
19
|
-
const KNOWN_LLM_INSTRUMENTATION_SCOPE_PREFIXES = [
|
|
20
|
-
'agent_framework',
|
|
21
|
-
'ai',
|
|
22
|
-
'gen_ai',
|
|
23
|
-
'haystack',
|
|
24
|
-
'langsmith',
|
|
25
|
-
'litellm',
|
|
26
|
-
'mistralai_sdk_tracer',
|
|
27
|
-
'openinference',
|
|
28
|
-
'opentelemetry.instrumentation.anthropic',
|
|
29
|
-
'opentelemetry.instrumentation.aws_bedrock',
|
|
30
|
-
'opentelemetry.instrumentation.bedrock',
|
|
31
|
-
'opentelemetry.instrumentation.cohere',
|
|
32
|
-
'opentelemetry.instrumentation.gemini',
|
|
33
|
-
'opentelemetry.instrumentation.google_genai',
|
|
34
|
-
'opentelemetry.instrumentation.google_generativeai',
|
|
35
|
-
'opentelemetry.instrumentation.openai',
|
|
36
|
-
'opentelemetry.instrumentation.openai_v2',
|
|
37
|
-
'opentelemetry.instrumentation.vertex_ai',
|
|
38
|
-
'opentelemetry.instrumentation.vertexai',
|
|
39
|
-
'weflayr.instrumentation',
|
|
40
|
-
'strands-agents',
|
|
41
|
-
'vllm',
|
|
42
|
-
'@traceloop/instrumentation-openai',
|
|
43
|
-
'@traceloop/instrumentation-anthropic',
|
|
44
|
-
'@traceloop/instrumentation-bedrock',
|
|
45
|
-
'@traceloop/instrumentation-cohere',
|
|
46
|
-
'@traceloop/instrumentation-google-generativeai',
|
|
47
|
-
'@traceloop/instrumentation-langchain',
|
|
48
|
-
'@traceloop/instrumentation-llamaindex',
|
|
49
|
-
'@traceloop/instrumentation-pinecone',
|
|
50
|
-
'@traceloop/instrumentation-chromadb',
|
|
51
|
-
'@traceloop/instrumentation-qdrant',
|
|
52
|
-
'@traceloop/instrumentation-mcp',
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
function isKnownLLMInstrumentor(span) {
|
|
56
|
-
const scope = span.instrumentationScope.name;
|
|
57
|
-
return KNOWN_LLM_INSTRUMENTATION_SCOPE_PREFIXES.some(
|
|
58
|
-
(prefix) => scope === prefix || scope.startsWith(`${prefix}.`)
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function isDefaultExportSpan(span) {
|
|
63
|
-
return isKnownLLMInstrumentor(span);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
module.exports = {
|
|
67
|
-
isKnownLLMInstrumentor,
|
|
68
|
-
isDefaultExportSpan,
|
|
69
|
-
};
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
// Copyright 2024 Langfuse GmbH
|
|
2
|
-
// Licensed under the MIT License.
|
|
3
|
-
// Original source: https://github.com/langfuse/langfuse-js/blob/main/packages/otel/src/span-processor.ts
|
|
4
|
-
//
|
|
5
|
-
// Modifications copyright 2026 Weflayr
|
|
6
|
-
// This file has been modified from its original form:
|
|
7
|
-
// - Translated from TypeScript to JavaScript
|
|
8
|
-
// - Removed MediaService integration (no media upload support yet)
|
|
9
|
-
// - Removed LangfuseAPIClient / app-root marker logic (not used)
|
|
10
|
-
// - Removed Langfuse-specific span attributes (TRACE_INPUT/OUTPUT/METADATA, IS_APP_ROOT, ENVIRONMENT, RELEASE)
|
|
11
|
-
// - Mask hook now receives the full flat attribute bag, not a {data} envelope, and runs synchronously
|
|
12
|
-
// - default_tags are applied as span attributes in onStart
|
|
13
|
-
// - propagated tags come from Weflayr's tag bag (see propagation.js), not Langfuse's typed schema
|
|
14
|
-
//
|
|
15
|
-
// See LICENSE-MIT in the package root for the full license text.
|
|
16
|
-
|
|
17
|
-
'use strict';
|
|
18
|
-
|
|
19
|
-
const { SpanStatusCode } = require('@opentelemetry/api');
|
|
20
|
-
const { BatchSpanProcessor, SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base');
|
|
21
|
-
const { EXTRA_TAG_PREFIX, getMergedMetadataFromContext } = require('./propagation');
|
|
22
|
-
const { isDefaultExportSpan, isKnownLLMInstrumentor } = require('./span-filter');
|
|
23
|
-
const {
|
|
24
|
-
VercelAiSdkSpanFilter,
|
|
25
|
-
stampVercelGatewayProviderName,
|
|
26
|
-
} = require('./vercel-ai-sdk-span-filter');
|
|
27
|
-
const { debug } = require('../logger');
|
|
28
|
-
|
|
29
|
-
// setup({captureMessageContent: ...}) records the choice in this env var (the
|
|
30
|
-
// official OTel GenAI one). Instrumentors gate content on their own knobs
|
|
31
|
-
// (openllmetry reads its own config and captures by default), so the processor
|
|
32
|
-
// enforces the flag itself: when the env var is "false", message content is
|
|
33
|
-
// stripped from every span before export.
|
|
34
|
-
const CONTENT_CAPTURE_ENV = 'OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT';
|
|
35
|
-
|
|
36
|
-
// The new-semconv JSON message attributes and the legacy indexed
|
|
37
|
-
// `gen_ai.prompt.{i}.*` / `gen_ai.completion.{i}.*` attributes.
|
|
38
|
-
const CONTENT_ATTRIBUTES = [
|
|
39
|
-
'gen_ai.input.messages',
|
|
40
|
-
'gen_ai.output.messages',
|
|
41
|
-
'gen_ai.system_instructions',
|
|
42
|
-
];
|
|
43
|
-
const CONTENT_ATTRIBUTE_PREFIXES = ['gen_ai.prompt.', 'gen_ai.completion.'];
|
|
44
|
-
|
|
45
|
-
function _isContentAttribute(key) {
|
|
46
|
-
return (
|
|
47
|
-
CONTENT_ATTRIBUTES.includes(key) ||
|
|
48
|
-
CONTENT_ATTRIBUTE_PREFIXES.some((prefix) => key.startsWith(prefix))
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Stamped on every exported span when a `mask` is configured: the mask
|
|
53
|
-
// function's source code, so the stored raw event shows what redaction ran
|
|
54
|
-
// client-side when debugging missing attributes.
|
|
55
|
-
const MASK_FUNCTION_ATTRIBUTE = 'weflayr.mask_function';
|
|
56
|
-
|
|
57
|
-
class WeflayrSpanProcessor {
|
|
58
|
-
constructor({ exporter, exportMode = 'batch', defaultTags = {}, mask, shouldExportSpan } = {}) {
|
|
59
|
-
if (!exporter) {
|
|
60
|
-
throw new Error('WeflayrSpanProcessor: exporter is required');
|
|
61
|
-
}
|
|
62
|
-
this._delegate =
|
|
63
|
-
exportMode === 'immediate'
|
|
64
|
-
? new SimpleSpanProcessor(exporter)
|
|
65
|
-
: new BatchSpanProcessor(exporter);
|
|
66
|
-
this._defaultTags = defaultTags;
|
|
67
|
-
this._mask = mask;
|
|
68
|
-
this._maskFunctionSource = mask ? String(mask) : null;
|
|
69
|
-
this._shouldExportSpan = shouldExportSpan ?? isDefaultExportSpan;
|
|
70
|
-
this._vercelAiSdkSpanFilter = new VercelAiSdkSpanFilter();
|
|
71
|
-
// LLM spans we've started but not yet seen end. On forceFlush we end any
|
|
72
|
-
// leftovers with ERROR status — covers cases like openllmetry's anthropic
|
|
73
|
-
// streaming wrapper, which leaks the span when the underlying call
|
|
74
|
-
// rejects. Generic across providers; no per-provider patching needed.
|
|
75
|
-
this._openLlmSpans = new Set();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
onStart(span, parentContext) {
|
|
79
|
-
for (const [k, v] of Object.entries(this._defaultTags)) {
|
|
80
|
-
span.setAttribute(`${EXTRA_TAG_PREFIX}${k}`, v);
|
|
81
|
-
}
|
|
82
|
-
const propagated = getMergedMetadataFromContext(parentContext);
|
|
83
|
-
for (const [k, v] of Object.entries(propagated)) {
|
|
84
|
-
span.setAttribute(k, v);
|
|
85
|
-
}
|
|
86
|
-
if (isKnownLLMInstrumentor(span)) {
|
|
87
|
-
this._openLlmSpans.add(span);
|
|
88
|
-
}
|
|
89
|
-
this._vercelAiSdkSpanFilter.onStart(span);
|
|
90
|
-
debug(
|
|
91
|
-
'span start: name=%s defaultTags=%d propagatedMetadata=%d',
|
|
92
|
-
span.name,
|
|
93
|
-
Object.keys(this._defaultTags).length,
|
|
94
|
-
Object.keys(propagated).length
|
|
95
|
-
);
|
|
96
|
-
return this._delegate.onStart(span, parentContext);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
onEnd(span) {
|
|
100
|
-
this._openLlmSpans.delete(span);
|
|
101
|
-
try {
|
|
102
|
-
const vercelVerdict = this._vercelAiSdkSpanFilter.shouldExport(span);
|
|
103
|
-
if (vercelVerdict === true) {
|
|
104
|
-
stampVercelGatewayProviderName(span);
|
|
105
|
-
}
|
|
106
|
-
if (vercelVerdict === false || (vercelVerdict === null && !this._shouldExportSpan(span))) {
|
|
107
|
-
debug(
|
|
108
|
-
'span end: filtered out name=%s scope=%s',
|
|
109
|
-
span.name,
|
|
110
|
-
span.instrumentationScope?.name
|
|
111
|
-
);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
} catch {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
debug('span end: exporting name=%s scope=%s', span.name, span.instrumentationScope?.name);
|
|
118
|
-
|
|
119
|
-
if ((process.env[CONTENT_CAPTURE_ENV] || '').toLowerCase() === 'false') {
|
|
120
|
-
for (const key of Object.keys(span.attributes)) {
|
|
121
|
-
if (_isContentAttribute(key)) delete span.attributes[key];
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (this._mask) {
|
|
126
|
-
try {
|
|
127
|
-
const masked = this._mask(span.attributes);
|
|
128
|
-
if (masked && masked !== span.attributes) {
|
|
129
|
-
for (const k of Object.keys(span.attributes)) delete span.attributes[k];
|
|
130
|
-
Object.assign(span.attributes, masked);
|
|
131
|
-
}
|
|
132
|
-
} catch {
|
|
133
|
-
for (const k of Object.keys(span.attributes)) delete span.attributes[k];
|
|
134
|
-
}
|
|
135
|
-
// Stamped after masking so the mask can't strip it.
|
|
136
|
-
span.attributes[MASK_FUNCTION_ATTRIBUTE] = this._maskFunctionSource;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return this._delegate.onEnd(span);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async forceFlush() {
|
|
143
|
-
this._endOrphanedLlmSpans();
|
|
144
|
-
return this._delegate.forceFlush();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
_endOrphanedLlmSpans() {
|
|
148
|
-
const orphans = [...this._openLlmSpans];
|
|
149
|
-
this._openLlmSpans.clear();
|
|
150
|
-
for (const span of orphans) {
|
|
151
|
-
try {
|
|
152
|
-
if (typeof span.isRecording === 'function' && !span.isRecording()) continue;
|
|
153
|
-
span.setStatus({
|
|
154
|
-
code: SpanStatusCode.ERROR,
|
|
155
|
-
message: 'weflayr: span did not complete before flush',
|
|
156
|
-
});
|
|
157
|
-
span.end();
|
|
158
|
-
debug('forceFlush: ended orphaned LLM span name=%s', span.name);
|
|
159
|
-
} catch (err) {
|
|
160
|
-
debug('forceFlush: failed to end orphan span: %s', err?.message || err);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
shutdown() {
|
|
166
|
-
return this._delegate.shutdown();
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
module.exports = { CONTENT_CAPTURE_ENV, WeflayrSpanProcessor };
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// The Vercel AI SDK's OTel integrations emit aggregate spans around each model
|
|
4
|
-
// call, all repeating the same usage counts:
|
|
5
|
-
// - `@ai-sdk/otel` (ai >= 7, scope "gen_ai"): per-call "chat" spans are
|
|
6
|
-
// wrapped in "agent_step" + "invoke_agent" spans, and embed/embedMany
|
|
7
|
-
// emits an outer "embeddings" operation span around the model-call
|
|
8
|
-
// "embeddings" span(s).
|
|
9
|
-
// - the legacy integration (ai <= 6, scope "ai"): `ai.<fn>.do<Call>` spans
|
|
10
|
-
// are wrapped in an outer `ai.<fn>` span.
|
|
11
|
-
// Exactly one span per model call may reach the exporter — exporting an
|
|
12
|
-
// aggregate alongside its children would bill every call twice.
|
|
13
|
-
|
|
14
|
-
const VERCEL_SEMCONV_SCOPE = 'gen_ai';
|
|
15
|
-
const VERCEL_LEGACY_SCOPE = 'ai';
|
|
16
|
-
|
|
17
|
-
const LEGACY_MODEL_CALL_SPAN_NAME = /\.do(Generate|Stream|Embed)$/;
|
|
18
|
-
|
|
19
|
-
function _parentSpanId(span) {
|
|
20
|
-
return span.parentSpanContext?.spanId ?? span.parentSpanId;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
class VercelAiSdkSpanFilter {
|
|
24
|
-
constructor() {
|
|
25
|
-
// spanId -> gen_ai.operation.name for open "gen_ai"-scope spans, so a
|
|
26
|
-
// span can check what its parent is (children end before their parent).
|
|
27
|
-
this._openSpanOperations = new Map();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
onStart(span) {
|
|
31
|
-
if (span.instrumentationScope?.name !== VERCEL_SEMCONV_SCOPE) return;
|
|
32
|
-
this._openSpanOperations.set(
|
|
33
|
-
span.spanContext().spanId,
|
|
34
|
-
span.attributes['gen_ai.operation.name']
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Export verdict for a Vercel AI SDK span, or null when the span is not
|
|
40
|
-
* one (the caller falls back to the default filter).
|
|
41
|
-
*/
|
|
42
|
-
shouldExport(span) {
|
|
43
|
-
const scope = span.instrumentationScope?.name;
|
|
44
|
-
if (scope === VERCEL_LEGACY_SCOPE) {
|
|
45
|
-
return LEGACY_MODEL_CALL_SPAN_NAME.test(span.name);
|
|
46
|
-
}
|
|
47
|
-
if (scope !== VERCEL_SEMCONV_SCOPE) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const operation = span.attributes['gen_ai.operation.name'];
|
|
51
|
-
this._openSpanOperations.delete(span.spanContext().spanId);
|
|
52
|
-
if (operation === 'chat') {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
if (operation === 'embeddings') {
|
|
56
|
-
// Keep the outer aggregate (one event per embed/embedMany call, with the
|
|
57
|
-
// total usage) and drop the nested model-call span(s) repeating it.
|
|
58
|
-
return this._openSpanOperations.get(_parentSpanId(span)) !== 'embeddings';
|
|
59
|
-
}
|
|
60
|
-
// invoke_agent / agent_step (usage aggregates) and execute_tool (no usage).
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// `@ai-sdk/otel` reports the Vercel AI Gateway as the bare provider id
|
|
66
|
-
// "gateway" — too generic a name to special-case downstream. Stamp the weflayr
|
|
67
|
-
// provider override (which the normalizer prefers) with the canonical name.
|
|
68
|
-
// A providerNameOverride set through propagateMetadata was stamped at span start and
|
|
69
|
-
// wins.
|
|
70
|
-
function stampVercelGatewayProviderName(span) {
|
|
71
|
-
if (
|
|
72
|
-
span.attributes['gen_ai.provider.name'] === 'gateway' &&
|
|
73
|
-
span.attributes['weflayr.provider_name'] === undefined
|
|
74
|
-
) {
|
|
75
|
-
span.attributes['weflayr.provider_name'] = 'vercel_ai_gateway';
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
module.exports = { VercelAiSdkSpanFilter, stampVercelGatewayProviderName };
|