weflayr 0.24.0 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/package.json +51 -0
- package/dist/{api → src/api}/generated/facade.gen.d.ts +86 -12
- package/dist/{api → src/api}/generated/facade.gen.js +4 -2
- package/dist/src/api/generated/index.d.ts +2 -0
- package/dist/{api → src/api}/generated/index.js +3 -1
- package/dist/{api → src/api}/generated/sdk.gen.d.ts +11 -3
- package/dist/{api → src/api}/generated/sdk.gen.js +34 -4
- package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +436 -153
- package/dist/{api → src/api}/makeEndpoint.js +1 -3
- package/dist/src/auto-instrument.d.ts +42 -0
- package/dist/src/auto-instrument.js +156 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +49 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.d.ts +20 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +114 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.js +92 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +141 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.js +17 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +50 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +39 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +97 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +102 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.js +91 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +21 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +54 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.d.ts +19 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.js +81 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +126 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.d.ts +17 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.js +85 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +68 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.js +77 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +88 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.js +18 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.d.ts +31 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.js +106 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.js +75 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +44 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.d.ts +28 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.js +209 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.js +28 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.js +154 -0
- package/dist/src/instrumentation/google/get_cache_telemetry.d.ts +16 -0
- package/{src → dist/src}/instrumentation/google/get_cache_telemetry.js +18 -19
- package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_embeddings_telemetry.js +85 -0
- package/dist/src/instrumentation/google/get_provider_name.d.ts +11 -0
- package/{src → dist/src}/instrumentation/google/get_provider_name.js +11 -12
- package/{src/instrumentation/google/get_reasoning_telemetry.js → dist/src/instrumentation/google/get_reasoning_telemetry.d.ts} +1 -11
- package/dist/src/instrumentation/google/get_reasoning_telemetry.js +21 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.js +19 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.js +98 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.js +123 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +148 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +72 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +91 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +86 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.d.ts +21 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.js +74 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +20 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +76 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +3 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.js +54 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +66 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +24 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +84 -0
- package/dist/src/logger.d.ts +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/otel/exporter.d.ts +19 -0
- package/dist/src/otel/exporter.js +94 -0
- package/dist/src/otel/propagation.d.ts +46 -0
- package/dist/src/otel/propagation.js +110 -0
- package/dist/src/otel/span-filter.d.ts +3 -0
- package/dist/src/otel/span-filter.js +61 -0
- package/dist/src/otel/span-processor.d.ts +55 -0
- package/dist/src/otel/span-processor.js +169 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +12 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.js +88 -0
- package/dist/src/setup.d.ts +50 -0
- package/dist/src/setup.js +155 -0
- package/package.json +12 -5
- package/.prettierignore +0 -2
- package/.prettierrc.json +0 -7
- package/LICENSE-MIT +0 -21
- package/NOTICE +0 -12
- package/dist/api/generated/index.d.ts +0 -2
- package/dist/api/generated/types.gen.d.ts +0 -1005
- package/eslint.config.mjs +0 -36
- package/index.d.ts +0 -170
- package/openapi-ts.config.mjs +0 -9
- package/scripts/generate_facade.mjs +0 -86
- package/src/api/caseConversion.ts +0 -58
- package/src/api/generated/client/client.gen.ts +0 -277
- package/src/api/generated/client/index.ts +0 -27
- package/src/api/generated/client/types.gen.ts +0 -218
- package/src/api/generated/client/utils.gen.ts +0 -316
- package/src/api/generated/client.gen.ts +0 -16
- package/src/api/generated/core/auth.gen.ts +0 -48
- package/src/api/generated/core/bodySerializer.gen.ts +0 -82
- package/src/api/generated/core/params.gen.ts +0 -178
- package/src/api/generated/core/pathSerializer.gen.ts +0 -171
- package/src/api/generated/core/queryKeySerializer.gen.ts +0 -117
- package/src/api/generated/core/serverSentEvents.gen.ts +0 -242
- package/src/api/generated/core/types.gen.ts +0 -110
- package/src/api/generated/core/utils.gen.ts +0 -140
- package/src/api/generated/facade.gen.ts +0 -104
- package/src/api/generated/index.ts +0 -4
- package/src/api/generated/sdk.gen.ts +0 -175
- package/src/api/index.ts +0 -45
- package/src/api/makeEndpoint.ts +0 -54
- package/src/auto-instrument.js +0 -172
- package/src/index.js +0 -19
- package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -118
- package/src/instrumentation/anthropic/get_cache_config_telemetry.js +0 -90
- package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
- package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -55
- package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -39
- package/src/instrumentation/bedrock/get_cache_config_telemetry.js +0 -106
- package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -109
- package/src/instrumentation/bedrock/get_input_message_content_telemetry.js +0 -94
- package/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +0 -87
- package/src/instrumentation/bedrock/set_telemetry_attributes.js +0 -40
- package/src/instrumentation/cohere/get_chat_telemetry.js +0 -88
- package/src/instrumentation/cohere/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/cohere/set_telemetry_attributes.js +0 -32
- package/src/instrumentation/elevenlabs/full_instrumentation.js +0 -236
- package/src/instrumentation/google/get_audio_telemetry.js +0 -30
- package/src/instrumentation/google/get_cache_operations_telemetry.js +0 -171
- package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
- package/src/instrumentation/google/set_telemetry_attributes.js +0 -104
- package/src/instrumentation/openai/get_audio_telemetry.js +0 -149
- package/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +0 -71
- package/src/instrumentation/openai/get_cache_config_telemetry.js +0 -63
- package/src/instrumentation/openai/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/openai/get_streaming_usage.js +0 -81
- package/src/instrumentation/openai/set_telemetry_attributes.js +0 -41
- package/src/instrumentation/vercel/full_instrumentation.js +0 -52
- package/src/instrumentation/vercel/get_cache_creation_telemetry.js +0 -70
- package/src/logger.js +0 -8
- package/src/otel/exporter.js +0 -108
- package/src/otel/propagation.js +0 -146
- package/src/otel/span-filter.js +0 -69
- package/src/otel/span-processor.js +0 -170
- package/src/otel/vercel-ai-sdk-span-filter.js +0 -79
- package/src/setup.js +0 -167
- package/tests/api/index.test.js +0 -437
- package/tests/index.test.js +0 -884
- package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -181
- package/tests/instrumentation/anthropic/get_cache_config_telemetry.test.js +0 -92
- package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
- package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -97
- package/tests/instrumentation/bedrock/get_cache_config_telemetry.test.js +0 -107
- package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -118
- package/tests/instrumentation/bedrock/get_input_message_content_telemetry.test.js +0 -89
- package/tests/instrumentation/bedrock/get_streaming_output_message_telemetry.test.js +0 -87
- package/tests/instrumentation/cohere/get_chat_telemetry.test.js +0 -148
- package/tests/instrumentation/cohere/get_embeddings_telemetry.test.js +0 -132
- package/tests/instrumentation/elevenlabs/full_instrumentation.test.js +0 -258
- package/tests/instrumentation/google/get_cache_operations_telemetry.test.js +0 -130
- package/tests/instrumentation/google/get_embeddings_telemetry.test.js +0 -128
- package/tests/instrumentation/google/get_reasoning_telemetry.test.js +0 -41
- package/tests/instrumentation/google/set_telemetry_attributes.test.js +0 -219
- package/tests/instrumentation/openai/get_audio_telemetry.test.js +0 -170
- package/tests/instrumentation/openai/get_cache_and_reasoning_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_cache_config_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_embeddings_telemetry.test.js +0 -145
- package/tests/instrumentation/openai/get_streaming_usage.test.js +0 -105
- package/tests/instrumentation/vercel/full_instrumentation.test.js +0 -16
- package/tests/instrumentation/vercel/get_cache_creation_telemetry.test.js +0 -96
- package/tests/otel/vercel-ai-sdk-span-filter.test.js +0 -159
- package/tsconfig.json +0 -18
- /package/dist/{api → src/api}/caseConversion.d.ts +0 -0
- /package/dist/{api → src/api}/caseConversion.js +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/index.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/index.js +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/types.gen.js +0 -0
- /package/dist/{api → src/api}/index.d.ts +0 -0
- /package/dist/{api → src/api}/index.js +0 -0
- /package/dist/{api → src/api}/makeEndpoint.d.ts +0 -0
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Request-side prompt-caching config capture for Bedrock invoke_model spans.
|
|
5
|
-
*
|
|
6
|
-
* Bedrock callers opt into caching in the request body, in the model vendor's
|
|
7
|
-
* own schema: Anthropic-on-Bedrock bodies carry `cache_control` blocks (the
|
|
8
|
-
* native Anthropic shape), Amazon Nova bodies carry `cachePoint` blocks (the
|
|
9
|
-
* Converse shape) — on system entries, message content blocks or tool
|
|
10
|
-
* definitions, each with an optional raw `ttl` string ("5m"/"1h"). The
|
|
11
|
-
* traceloop instrumentor never records them.
|
|
12
|
-
*
|
|
13
|
-
* Wrap the instrumentor's `_startSpan({ params })` (params.body is the raw
|
|
14
|
-
* request JSON) to stamp every breakpoint found as `weflayr.cache.breakpoints`.
|
|
15
|
-
* Bedrock responses report no TTL split of the written tokens; the worker
|
|
16
|
-
* derives it from these breakpoints when they all share one TTL.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const { extractCacheBreakpoints } = require('../anthropic/get_cache_config_telemetry');
|
|
20
|
-
|
|
21
|
-
const BREAKPOINTS_ATTR = 'weflayr.cache.breakpoints';
|
|
22
|
-
|
|
23
|
-
// A breakpoint as location + the raw `cachePoint` `ttl` string, omitted when
|
|
24
|
-
// the request sets none.
|
|
25
|
-
function _cachePointEntry(location, cachePoint) {
|
|
26
|
-
const ttl = cachePoint && cachePoint.ttl;
|
|
27
|
-
return ttl ? { location, ttl: String(ttl) } : { location };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// The first `cachePoint` among a list of blocks (attached to a content block,
|
|
31
|
-
// or standing alone as its own block), or undefined.
|
|
32
|
-
function _cachePointOfBlocks(blocks) {
|
|
33
|
-
if (!Array.isArray(blocks)) return undefined;
|
|
34
|
-
const block = blocks.find((entry) => entry && entry.cachePoint != null);
|
|
35
|
-
return block && block.cachePoint;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Every cache breakpoint in an InvokeModel request body, as location + the
|
|
39
|
-
// raw `ttl` string (omitted when the breakpoint sets none): the
|
|
40
|
-
// Anthropic-on-Bedrock `cache_control` shape and the Nova `cachePoint` shape
|
|
41
|
-
// (which never overlap in one body).
|
|
42
|
-
function extractBedrockCacheBreakpoints(body) {
|
|
43
|
-
const breakpoints = extractCacheBreakpoints(body);
|
|
44
|
-
const systemCachePoint = _cachePointOfBlocks(body.system);
|
|
45
|
-
if (systemCachePoint != null) {
|
|
46
|
-
breakpoints.push(_cachePointEntry('system', systemCachePoint));
|
|
47
|
-
}
|
|
48
|
-
(body.messages || []).forEach((message, index) => {
|
|
49
|
-
const cachePoint = _cachePointOfBlocks(message && message.content);
|
|
50
|
-
if (cachePoint != null) {
|
|
51
|
-
breakpoints.push(_cachePointEntry(`messages[${index}]`, cachePoint));
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
const tools = (body.toolConfig && body.toolConfig.tools) || [];
|
|
55
|
-
tools.forEach((tool, index) => {
|
|
56
|
-
if (tool && tool.cachePoint != null) {
|
|
57
|
-
breakpoints.push(_cachePointEntry(`tools[${index}]`, tool.cachePoint));
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
return breakpoints;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
class BedrockCacheConfigInstrumentor {
|
|
64
|
-
constructor() {
|
|
65
|
-
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
66
|
-
this._patched = [];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
70
|
-
// the worst case is missing cache-config capture, never a crash.
|
|
71
|
-
instrument(instrumentation) {
|
|
72
|
-
if (!instrumentation) return;
|
|
73
|
-
const startSpanOriginal = instrumentation._startSpan;
|
|
74
|
-
if (typeof startSpanOriginal !== 'function') return;
|
|
75
|
-
|
|
76
|
-
instrumentation._startSpan = function startSpanWithCacheConfig(request) {
|
|
77
|
-
const span = startSpanOriginal.call(this, request);
|
|
78
|
-
try {
|
|
79
|
-
const rawBody =
|
|
80
|
-
request && request.params && request.params.input && request.params.input.body;
|
|
81
|
-
if (typeof rawBody === 'string' || rawBody instanceof Uint8Array) {
|
|
82
|
-
const body = JSON.parse(
|
|
83
|
-
typeof rawBody === 'string' ? rawBody : new TextDecoder().decode(rawBody)
|
|
84
|
-
);
|
|
85
|
-
const breakpoints = extractBedrockCacheBreakpoints(body || {});
|
|
86
|
-
if (breakpoints.length) {
|
|
87
|
-
span.setAttribute(BREAKPOINTS_ATTR, JSON.stringify(breakpoints));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch {
|
|
91
|
-
// Never break span creation over an enrichment failure.
|
|
92
|
-
}
|
|
93
|
-
return span;
|
|
94
|
-
};
|
|
95
|
-
this._patched.push([instrumentation, '_startSpan', startSpanOriginal]);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
uninstrument() {
|
|
99
|
-
for (const [owner, methodName, original] of this._patched) {
|
|
100
|
-
owner[methodName] = original;
|
|
101
|
-
}
|
|
102
|
-
this._patched = [];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
module.exports = { BedrockCacheConfigInstrumentor, extractBedrockCacheBreakpoints };
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Cached-prompt token enrichment complement for the Bedrock invoke_model path.
|
|
5
|
-
*
|
|
6
|
-
* `@traceloop/instrumentation-bedrock` records `gen_ai.usage.{input,output}_tokens`
|
|
7
|
-
* off the InvokeModel response body but never reads its cache-token counts, so
|
|
8
|
-
* spans for prompt-cache hits carry no cached-token figures. Amazon Nova's native
|
|
9
|
-
* InvokeModel response includes `usage.cacheReadInputTokenCount` /
|
|
10
|
-
* `usage.cacheWriteInputTokenCount` (Anthropic-on-Bedrock responses carry the
|
|
11
|
-
* native `usage.cache_read_input_tokens` / `cache_creation_input_tokens`);
|
|
12
|
-
* this maps them to the
|
|
13
|
-
* `gen_ai.usage.cache_read_input_tokens` / `cache_creation_input_tokens`
|
|
14
|
-
* attributes the worker reads, matching the Python SDK.
|
|
15
|
-
*
|
|
16
|
-
* It wraps the instrumentor instance's `_wrapPromise(span, promise, modelId)`,
|
|
17
|
-
* which resolves the send promise and enriches the span before ending it,
|
|
18
|
-
* letting us read the decoded body off the same response while the span is open.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const CACHE_READ_INPUT_TOKENS_ATTR = 'gen_ai.usage.cache_read_input_tokens';
|
|
22
|
-
const CACHE_CREATION_INPUT_TOKENS_ATTR = 'gen_ai.usage.cache_creation_input_tokens';
|
|
23
|
-
|
|
24
|
-
// Stamp cache counts from a `usage` object onto the span: Nova's camelCase
|
|
25
|
-
// count fields, or Anthropic-on-Bedrock's native snake_case ones.
|
|
26
|
-
function _setCacheFromUsage(span, usage) {
|
|
27
|
-
if (!usage || !span || !span.isRecording || !span.isRecording()) return;
|
|
28
|
-
const read = usage.cacheReadInputTokenCount ?? usage.cache_read_input_tokens;
|
|
29
|
-
const write = usage.cacheWriteInputTokenCount ?? usage.cache_creation_input_tokens;
|
|
30
|
-
if (typeof read === 'number' && read) {
|
|
31
|
-
span.setAttribute(CACHE_READ_INPUT_TOKENS_ATTR, read);
|
|
32
|
-
}
|
|
33
|
-
if (typeof write === 'number' && write) {
|
|
34
|
-
span.setAttribute(CACHE_CREATION_INPUT_TOKENS_ATTR, write);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function _setCacheTokens(span, response) {
|
|
39
|
-
if (!span || !span.isRecording || !span.isRecording()) return;
|
|
40
|
-
const body = response && response.body;
|
|
41
|
-
// Only the non-streaming InvokeModel response buffers its body as bytes; a
|
|
42
|
-
// response stream is an async iterable whose usage arrives in a trailing chunk
|
|
43
|
-
// handled by the streaming wrap below, so skip it here (and never consume the
|
|
44
|
-
// caller's stream).
|
|
45
|
-
if (!body || typeof body[Symbol.asyncIterator] === 'function') return;
|
|
46
|
-
try {
|
|
47
|
-
_setCacheFromUsage(span, JSON.parse(new TextDecoder().decode(body)).usage);
|
|
48
|
-
} catch {
|
|
49
|
-
// Never break span enrichment over a parse failure.
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
class BedrockPromptCachingInstrumentor {
|
|
54
|
-
constructor() {
|
|
55
|
-
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
56
|
-
this._patched = [];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
60
|
-
// the worst case is missing cached-token counts, never a crash.
|
|
61
|
-
instrument(instrumentation) {
|
|
62
|
-
if (!instrumentation) return;
|
|
63
|
-
|
|
64
|
-
// Non-streamed: enrich the buffered InvokeModel response body.
|
|
65
|
-
const wrapPromiseOriginal = instrumentation._wrapPromise;
|
|
66
|
-
if (typeof wrapPromiseOriginal === 'function') {
|
|
67
|
-
instrumentation._wrapPromise = function wrapPromiseWithCache(span, promise, modelId) {
|
|
68
|
-
// Stamp cache tokens off the response, then hand the same response to the
|
|
69
|
-
// original handler (which decodes the still-buffered body for input/output
|
|
70
|
-
// tokens). Resolving to the original response keeps behaviour identical.
|
|
71
|
-
const enriched = Promise.resolve(promise).then((response) => {
|
|
72
|
-
_setCacheTokens(span, response);
|
|
73
|
-
return response;
|
|
74
|
-
});
|
|
75
|
-
return wrapPromiseOriginal.call(this, span, enriched, modelId);
|
|
76
|
-
};
|
|
77
|
-
this._patched.push([instrumentation, '_wrapPromise', wrapPromiseOriginal]);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Streamed: the instrumentor processes the trailing metadata chunk via
|
|
81
|
-
// `_handleNovaStreamingMetadata(span, parsedResponse)` (reading
|
|
82
|
-
// `parsedResponse.metadata.usage` for input/output) but never its cache counts.
|
|
83
|
-
const metadataOriginal = instrumentation._handleNovaStreamingMetadata;
|
|
84
|
-
if (typeof metadataOriginal === 'function') {
|
|
85
|
-
instrumentation._handleNovaStreamingMetadata = function handleMetadataWithCache(
|
|
86
|
-
span,
|
|
87
|
-
parsedResponse
|
|
88
|
-
) {
|
|
89
|
-
try {
|
|
90
|
-
const usage = parsedResponse && parsedResponse.metadata && parsedResponse.metadata.usage;
|
|
91
|
-
_setCacheFromUsage(span, usage);
|
|
92
|
-
} catch {
|
|
93
|
-
// Never break the caller's stream over an enrichment failure.
|
|
94
|
-
}
|
|
95
|
-
return metadataOriginal.call(this, span, parsedResponse);
|
|
96
|
-
};
|
|
97
|
-
this._patched.push([instrumentation, '_handleNovaStreamingMetadata', metadataOriginal]);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
uninstrument() {
|
|
102
|
-
for (const [owner, methodName, original] of this._patched) {
|
|
103
|
-
owner[methodName] = original;
|
|
104
|
-
}
|
|
105
|
-
this._patched = [];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
module.exports = { BedrockPromptCachingInstrumentor };
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Input message content for Bedrock invoke_model spans the instrumentor misses.
|
|
5
|
-
*
|
|
6
|
-
* `@traceloop/instrumentation-bedrock` parses the request body per model vendor,
|
|
7
|
-
* so a call whose model id has no recognizable vendor (e.g. an invalid model —
|
|
8
|
-
* exactly the error spans) gets no `gen_ai.input.messages`. Wrap the
|
|
9
|
-
* instrumentor's `_startSpan({ params })` to stamp the input messages from the
|
|
10
|
-
* request body whenever the instrumentor did not. The span processor strips the
|
|
11
|
-
* attribute when message-content capture is disabled.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
function _contentToParts(content) {
|
|
15
|
-
if (typeof content === 'string') {
|
|
16
|
-
return [{ type: 'text', content }];
|
|
17
|
-
}
|
|
18
|
-
if (Array.isArray(content)) {
|
|
19
|
-
return content
|
|
20
|
-
.filter((block) => block && typeof block.text === 'string')
|
|
21
|
-
.map((block) => ({ type: 'text', content: block.text }));
|
|
22
|
-
}
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Messages in the role+parts shape from an InvokeModel JSON body — the
|
|
27
|
-
// `messages` schema (Nova, Anthropic-on-Bedrock) or the flat `inputText` /
|
|
28
|
-
// `prompt` schemas (Titan, legacy).
|
|
29
|
-
function _inputMessagesFromRequestBody(rawBody) {
|
|
30
|
-
if (typeof rawBody !== 'string') return [];
|
|
31
|
-
let body;
|
|
32
|
-
try {
|
|
33
|
-
body = JSON.parse(rawBody);
|
|
34
|
-
} catch {
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
if (!body || typeof body !== 'object') return [];
|
|
38
|
-
if (Array.isArray(body.messages)) {
|
|
39
|
-
return body.messages
|
|
40
|
-
.filter((message) => message && typeof message === 'object')
|
|
41
|
-
.map((message) => ({
|
|
42
|
-
role: String(message.role || ''),
|
|
43
|
-
parts: _contentToParts(message.content),
|
|
44
|
-
}));
|
|
45
|
-
}
|
|
46
|
-
const prompt = body.inputText || body.prompt;
|
|
47
|
-
if (typeof prompt === 'string') {
|
|
48
|
-
return [{ role: 'user', parts: [{ type: 'text', content: prompt }] }];
|
|
49
|
-
}
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
class BedrockInputMessageContentInstrumentor {
|
|
54
|
-
constructor() {
|
|
55
|
-
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
56
|
-
this._patched = [];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
60
|
-
// the worst case is missing input messages, never a crash.
|
|
61
|
-
instrument(instrumentation) {
|
|
62
|
-
if (!instrumentation) return;
|
|
63
|
-
const original = instrumentation._startSpan;
|
|
64
|
-
if (typeof original !== 'function') return;
|
|
65
|
-
|
|
66
|
-
instrumentation._startSpan = function startSpanWithInputMessages(request) {
|
|
67
|
-
const span = original.call(this, request);
|
|
68
|
-
try {
|
|
69
|
-
const attributes = span && span.attributes;
|
|
70
|
-
if (attributes && attributes['gen_ai.input.messages'] == null) {
|
|
71
|
-
const body =
|
|
72
|
-
request && request.params && request.params.input && request.params.input.body;
|
|
73
|
-
const messages = _inputMessagesFromRequestBody(body);
|
|
74
|
-
if (messages.length) {
|
|
75
|
-
span.setAttribute('gen_ai.input.messages', JSON.stringify(messages));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
} catch {
|
|
79
|
-
// Never break span creation over an enrichment failure.
|
|
80
|
-
}
|
|
81
|
-
return span;
|
|
82
|
-
};
|
|
83
|
-
this._patched.push([instrumentation, '_startSpan', original]);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
uninstrument() {
|
|
87
|
-
for (const [owner, methodName, original] of this._patched) {
|
|
88
|
-
owner[methodName] = original;
|
|
89
|
-
}
|
|
90
|
-
this._patched = [];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
module.exports = { BedrockInputMessageContentInstrumentor };
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Streamed-output message content for the Bedrock invoke_model stream span.
|
|
5
|
-
*
|
|
6
|
-
* `@traceloop/instrumentation-bedrock` accumulates the streamed text but formats
|
|
7
|
-
* the final `gen_ai.output.messages` by passing that string through its chat
|
|
8
|
-
* block mapper, which only accepts block arrays — so streamed Nova spans end up
|
|
9
|
-
* with an assistant message whose `parts` is empty. This complement wraps the
|
|
10
|
-
* instrumentor's per-chunk `_handleNovaStreamingMetadata(span, parsedResponse)`
|
|
11
|
-
* hook: it accumulates the `contentBlockDelta` text and the `messageStop` stop
|
|
12
|
-
* reason itself, and re-stamps a correct output message when the trailing
|
|
13
|
-
* `metadata` chunk (always last in a Nova stream) arrives — after the
|
|
14
|
-
* instrumentor has written its empty-parts message. The span processor strips
|
|
15
|
-
* the attribute when message-content capture is disabled.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
// Nova stop reasons mapped to the OTel finish_reason values the instrumentor
|
|
19
|
-
// uses on non-streamed spans, so streamed and non-streamed rows normalize alike.
|
|
20
|
-
const FINISH_REASON_BY_STOP_REASON = {
|
|
21
|
-
end_turn: 'stop',
|
|
22
|
-
stop_sequence: 'stop',
|
|
23
|
-
max_tokens: 'length',
|
|
24
|
-
tool_use: 'tool_call',
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
class BedrockStreamingOutputMessageInstrumentor {
|
|
28
|
-
constructor() {
|
|
29
|
-
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
30
|
-
this._patched = [];
|
|
31
|
-
// span -> {text, stopReason} accumulated across the stream's chunks.
|
|
32
|
-
this._streamStateBySpan = new WeakMap();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
36
|
-
// the worst case is an empty streamed output message, never a crash.
|
|
37
|
-
instrument(instrumentation) {
|
|
38
|
-
if (!instrumentation) return;
|
|
39
|
-
const original = instrumentation._handleNovaStreamingMetadata;
|
|
40
|
-
if (typeof original !== 'function') return;
|
|
41
|
-
|
|
42
|
-
const streamStateBySpan = this._streamStateBySpan;
|
|
43
|
-
instrumentation._handleNovaStreamingMetadata = function handleWithOutputMessage(
|
|
44
|
-
span,
|
|
45
|
-
parsedResponse
|
|
46
|
-
) {
|
|
47
|
-
try {
|
|
48
|
-
const state = streamStateBySpan.get(span) || { text: '', stopReason: null };
|
|
49
|
-
const delta =
|
|
50
|
-
parsedResponse &&
|
|
51
|
-
parsedResponse.contentBlockDelta &&
|
|
52
|
-
parsedResponse.contentBlockDelta.delta &&
|
|
53
|
-
parsedResponse.contentBlockDelta.delta.text;
|
|
54
|
-
if (typeof delta === 'string') state.text += delta;
|
|
55
|
-
const stopReason =
|
|
56
|
-
parsedResponse && parsedResponse.messageStop && parsedResponse.messageStop.stopReason;
|
|
57
|
-
if (stopReason != null) state.stopReason = stopReason;
|
|
58
|
-
streamStateBySpan.set(span, state);
|
|
59
|
-
|
|
60
|
-
if (parsedResponse && parsedResponse.metadata && state.text) {
|
|
61
|
-
const message = {
|
|
62
|
-
role: 'assistant',
|
|
63
|
-
parts: [{ type: 'text', content: state.text }],
|
|
64
|
-
};
|
|
65
|
-
if (state.stopReason != null) {
|
|
66
|
-
message.finish_reason =
|
|
67
|
-
FINISH_REASON_BY_STOP_REASON[state.stopReason] || state.stopReason;
|
|
68
|
-
}
|
|
69
|
-
span.setAttribute('gen_ai.output.messages', JSON.stringify([message]));
|
|
70
|
-
}
|
|
71
|
-
} catch {
|
|
72
|
-
// Never break the caller's stream over an enrichment failure.
|
|
73
|
-
}
|
|
74
|
-
return original.call(this, span, parsedResponse);
|
|
75
|
-
};
|
|
76
|
-
this._patched.push([instrumentation, '_handleNovaStreamingMetadata', original]);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
uninstrument() {
|
|
80
|
-
for (const [owner, methodName, original] of this._patched) {
|
|
81
|
-
owner[methodName] = original;
|
|
82
|
-
}
|
|
83
|
-
this._patched = [];
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
module.exports = { BedrockStreamingOutputMessageInstrumentor };
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Bedrock telemetry complement — the registered orchestrator.
|
|
5
|
-
*
|
|
6
|
-
* Wires the weflayr-supplied patches the traceloop Bedrock instrumentor misses:
|
|
7
|
-
* cached-prompt token counts on the invoke_model span (Nova returns them in the
|
|
8
|
-
* response body but traceloop never reads them), the request's cache
|
|
9
|
-
* breakpoints (`cache_control` / `cachePoint` blocks it never records) and the
|
|
10
|
-
* streamed output message content (its stream path formats an empty-parts
|
|
11
|
-
* message).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const { BedrockCacheConfigInstrumentor } = require('./get_cache_config_telemetry');
|
|
15
|
-
const { BedrockPromptCachingInstrumentor } = require('./get_cache_telemetry');
|
|
16
|
-
const { BedrockInputMessageContentInstrumentor } = require('./get_input_message_content_telemetry');
|
|
17
|
-
const {
|
|
18
|
-
BedrockStreamingOutputMessageInstrumentor,
|
|
19
|
-
} = require('./get_streaming_output_message_telemetry');
|
|
20
|
-
|
|
21
|
-
class BedrockTelemetryInstrumentor {
|
|
22
|
-
constructor() {
|
|
23
|
-
this._subInstrumentors = [
|
|
24
|
-
new BedrockPromptCachingInstrumentor(),
|
|
25
|
-
new BedrockCacheConfigInstrumentor(),
|
|
26
|
-
new BedrockInputMessageContentInstrumentor(),
|
|
27
|
-
new BedrockStreamingOutputMessageInstrumentor(),
|
|
28
|
-
];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
instrument(instrumentation) {
|
|
32
|
-
for (const sub of this._subInstrumentors) sub.instrument(instrumentation);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
uninstrument() {
|
|
36
|
-
for (const sub of this._subInstrumentors) sub.uninstrument();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = { BedrockTelemetryInstrumentor };
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Chat token-usage enrichment complement for the Cohere v1 chat path.
|
|
5
|
-
*
|
|
6
|
-
* `@traceloop/instrumentation-cohere` creates the `cohere.chat` span but reads
|
|
7
|
-
* token usage from the obsolete `result.token_count.{prompt,response}_tokens`
|
|
8
|
-
* shape; modern cohere-ai returns usage under `meta.billedUnits` instead, so the
|
|
9
|
-
* traceloop span ends up with zero chat tokens. This thin complement backfills
|
|
10
|
-
* `gen_ai.usage.{input,output}_tokens` from `meta.billedUnits`, the same source
|
|
11
|
-
* traceloop already uses for the `generate` endpoint — bringing Node chat tokens
|
|
12
|
-
* to parity with the Python SDK (OpenLLMetry captures them natively, so there is
|
|
13
|
-
* no Python counterpart).
|
|
14
|
-
*
|
|
15
|
-
* It hooks the internal `CohereClient.prototype.__chat` (which the public `chat`
|
|
16
|
-
* delegates to synchronously) rather than the public `chat`: traceloop wraps
|
|
17
|
-
* `chat` and opens the span around it, so `__chat` runs while that span is the
|
|
18
|
-
* active span — letting this complement enrich it regardless of patch order.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const { trace } = require('@opentelemetry/api');
|
|
22
|
-
|
|
23
|
-
function _billedUnits(result) {
|
|
24
|
-
// Public `chat` resolves to the response body, but `__chat` resolves to the
|
|
25
|
-
// Fern wrapper `{ data, rawResponse }`; read billedUnits off either shape.
|
|
26
|
-
const response = result && result.data ? result.data : result;
|
|
27
|
-
return response && response.meta ? response.meta.billedUnits : undefined;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function _setChatTokenUsage(span, result) {
|
|
31
|
-
const billedUnits = _billedUnits(result);
|
|
32
|
-
if (!billedUnits) return;
|
|
33
|
-
if (typeof billedUnits.inputTokens === 'number') {
|
|
34
|
-
span.setAttribute('gen_ai.usage.input_tokens', billedUnits.inputTokens);
|
|
35
|
-
}
|
|
36
|
-
if (typeof billedUnits.outputTokens === 'number') {
|
|
37
|
-
span.setAttribute('gen_ai.usage.output_tokens', billedUnits.outputTokens);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
class CohereChatTokenUsageInstrumentor {
|
|
42
|
-
constructor() {
|
|
43
|
-
// [ownerPrototype, methodName, originalFunction] for uninstrument().
|
|
44
|
-
this._patched = [];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
instrument() {
|
|
48
|
-
let CohereClient;
|
|
49
|
-
try {
|
|
50
|
-
({ CohereClient } = require('cohere-ai/Client'));
|
|
51
|
-
} catch (err) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
'weflayr.autoInstrument(COHERE): the cohere-ai client is not installed. ' +
|
|
54
|
-
'Install it with: npm install cohere-ai\n' +
|
|
55
|
-
`Underlying error: ${err.message}`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
this._wrap(CohereClient.prototype, '__chat');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
uninstrument() {
|
|
63
|
-
for (const [owner, methodName, original] of this._patched) {
|
|
64
|
-
owner[methodName] = original;
|
|
65
|
-
}
|
|
66
|
-
this._patched = [];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
_wrap(owner, methodName) {
|
|
70
|
-
const original = owner[methodName];
|
|
71
|
-
|
|
72
|
-
function wrapper(...args) {
|
|
73
|
-
// Captured synchronously, while traceloop's chat span is still active.
|
|
74
|
-
const span = trace.getActiveSpan();
|
|
75
|
-
const result = original.apply(this, args);
|
|
76
|
-
if (!span || !span.isRecording || !span.isRecording()) return result;
|
|
77
|
-
return Promise.resolve(result).then((response) => {
|
|
78
|
-
_setChatTokenUsage(span, response);
|
|
79
|
-
return response;
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
owner[methodName] = wrapper;
|
|
84
|
-
this._patched.push([owner, methodName, original]);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
module.exports = { CohereChatTokenUsageInstrumentor };
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Hand-written OTel instrumentor for the Cohere embeddings endpoint.
|
|
5
|
-
*
|
|
6
|
-
* `@traceloop/instrumentation-cohere` (the package weflayr uses for Cohere on
|
|
7
|
-
* Node) wraps chat/generate/rerank but not `embed`, so this ships inside weflayr
|
|
8
|
-
* to cover it. OpenLLMetry's cohere instrumentor doesn't cover `embed` either, so
|
|
9
|
-
* the Python SDK has a mirror complement (`cohere_embeddings.py`) shaped to
|
|
10
|
-
* normalize identically. It is registered alongside the traceloop instrumentor
|
|
11
|
-
* whenever `autoInstrument({ aiSdks: 'cohere' })` runs.
|
|
12
|
-
*
|
|
13
|
-
* Embeddings are billed per input token, so the span records the input tokens
|
|
14
|
-
* Cohere returns under `gen_ai.usage.input_tokens`; there is no completion.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const { trace, SpanStatusCode } = require('@opentelemetry/api');
|
|
18
|
-
|
|
19
|
-
const TRACER_NAME = 'weflayr.instrumentation.cohere_embeddings';
|
|
20
|
-
// Matches the gen_ai.system the cohere chat instrumentors set (Python + Node).
|
|
21
|
-
const SYSTEM = 'Cohere';
|
|
22
|
-
const EMBEDDINGS_OPERATION = 'embeddings';
|
|
23
|
-
|
|
24
|
-
function _recordError(span, error) {
|
|
25
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: error?.message ?? String(error) });
|
|
26
|
-
span.recordException(error);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function _wrapEmbed(owner, patched) {
|
|
30
|
-
const original = owner.embed;
|
|
31
|
-
|
|
32
|
-
function wrapper(request, ...rest) {
|
|
33
|
-
const span = trace.getTracer(TRACER_NAME).startSpan(EMBEDDINGS_OPERATION);
|
|
34
|
-
span.setAttribute('gen_ai.system', SYSTEM);
|
|
35
|
-
span.setAttribute('gen_ai.operation.name', EMBEDDINGS_OPERATION);
|
|
36
|
-
if (request && request.model) {
|
|
37
|
-
span.setAttribute('gen_ai.request.model', request.model);
|
|
38
|
-
}
|
|
39
|
-
// The embedded texts are the prompt; stamp one user message per text, like
|
|
40
|
-
// the chat path. The span processor strips it when content capture is off.
|
|
41
|
-
if (request && Array.isArray(request.texts) && request.texts.length) {
|
|
42
|
-
span.setAttribute(
|
|
43
|
-
'gen_ai.input.messages',
|
|
44
|
-
JSON.stringify(
|
|
45
|
-
request.texts.map((text) => ({ role: 'user', parts: [{ type: 'text', content: text }] }))
|
|
46
|
-
)
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
return Promise.resolve(original.call(this, request, ...rest)).then(
|
|
50
|
-
(response) => {
|
|
51
|
-
// Node SDK reports the billed input tokens under camelCase keys.
|
|
52
|
-
const inputTokens =
|
|
53
|
-
response?.meta?.billedUnits?.inputTokens ?? response?.meta?.tokens?.inputTokens;
|
|
54
|
-
if (inputTokens != null) {
|
|
55
|
-
span.setAttribute('gen_ai.usage.input_tokens', inputTokens);
|
|
56
|
-
}
|
|
57
|
-
span.end();
|
|
58
|
-
return response;
|
|
59
|
-
},
|
|
60
|
-
(error) => {
|
|
61
|
-
_recordError(span, error);
|
|
62
|
-
span.end();
|
|
63
|
-
throw error;
|
|
64
|
-
}
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
owner.embed = wrapper;
|
|
69
|
-
patched.push([owner, 'embed', original]);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
class CohereEmbeddingsInstrumentor {
|
|
73
|
-
constructor() {
|
|
74
|
-
// [ownerPrototype, methodName, originalFunction] for uninstrument().
|
|
75
|
-
this._patched = [];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
instrument() {
|
|
79
|
-
let CohereClient;
|
|
80
|
-
let V2Client;
|
|
81
|
-
try {
|
|
82
|
-
// v1 `embed` is a prototype method on CohereClient; v2 `embed` lives on
|
|
83
|
-
// V2Client.prototype (CohereClientV2 binds it as an instance property, so
|
|
84
|
-
// it must be patched on the underlying V2Client class instead).
|
|
85
|
-
({ CohereClient } = require('cohere-ai/Client'));
|
|
86
|
-
({ V2Client } = require('cohere-ai/api/resources/v2/client/Client'));
|
|
87
|
-
} catch (err) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
'weflayr.autoInstrument(COHERE): the cohere-ai client is not installed. ' +
|
|
90
|
-
'Install it with: npm install cohere-ai\n' +
|
|
91
|
-
`Underlying error: ${err.message}`
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
_wrapEmbed(CohereClient.prototype, this._patched);
|
|
96
|
-
_wrapEmbed(V2Client.prototype, this._patched);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
uninstrument() {
|
|
100
|
-
for (const [owner, methodName, original] of this._patched) {
|
|
101
|
-
owner[methodName] = original;
|
|
102
|
-
}
|
|
103
|
-
this._patched = [];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
module.exports = { CohereEmbeddingsInstrumentor };
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Cohere telemetry complement — the registered orchestrator.
|
|
5
|
-
*
|
|
6
|
-
* Wires the weflayr-supplied patches the traceloop Cohere instrumentor misses:
|
|
7
|
-
* - chat token usage (traceloop reads the obsolete usage shape and records
|
|
8
|
-
* zero chat tokens), and
|
|
9
|
-
* - the embeddings endpoint it doesn't cover.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const { CohereChatTokenUsageInstrumentor } = require('./get_chat_telemetry');
|
|
13
|
-
const { CohereEmbeddingsInstrumentor } = require('./get_embeddings_telemetry');
|
|
14
|
-
|
|
15
|
-
class CohereTelemetryInstrumentor {
|
|
16
|
-
constructor() {
|
|
17
|
-
this._subInstrumentors = [
|
|
18
|
-
new CohereChatTokenUsageInstrumentor(),
|
|
19
|
-
new CohereEmbeddingsInstrumentor(),
|
|
20
|
-
];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
instrument(instrumentation) {
|
|
24
|
-
for (const sub of this._subInstrumentors) sub.instrument(instrumentation);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
uninstrument() {
|
|
28
|
-
for (const sub of this._subInstrumentors) sub.uninstrument();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
module.exports = { CohereTelemetryInstrumentor };
|