weflayr 0.27.0 → 0.28.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/dist/package.json +3 -3
- package/dist/src/auto-instrument.d.ts +4 -3
- package/dist/src/auto-instrument.js +18 -17
- package/dist/src/get-data-uri-mime-type.d.ts +17 -0
- package/dist/src/get-data-uri-mime-type.js +25 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +5 -1
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +3 -1
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +6 -3
- package/dist/src/instrumentation/anthropic/get_vertex_billing_telemetry.d.ts +32 -0
- package/dist/src/instrumentation/anthropic/get_vertex_billing_telemetry.js +100 -0
- package/dist/src/instrumentation/anthropic/get_web_search_telemetry.d.ts +43 -0
- package/dist/src/instrumentation/anthropic/get_web_search_telemetry.js +147 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +5 -2
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +17 -4
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +13 -1
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +56 -1
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +8 -5
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +1 -1
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +1 -1
- package/dist/src/instrumentation/bedrock/get_openai_chat_format_telemetry.d.ts +45 -0
- package/dist/src/instrumentation/bedrock/get_openai_chat_format_telemetry.js +251 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +6 -5
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +15 -12
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +2 -2
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +12 -4
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +4 -2
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +10 -3
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +4 -6
- package/dist/src/instrumentation/cohere/get_input_message_content_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/cohere/get_input_message_content_telemetry.js +105 -0
- package/dist/src/instrumentation/cohere/get_v2_chat_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/cohere/get_v2_chat_telemetry.js +186 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +3 -1
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +5 -1
- package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +8 -4
- package/dist/src/instrumentation/google/get_embeddings_telemetry.js +68 -35
- package/dist/src/instrumentation/google/get_provider_name.d.ts +2 -1
- package/dist/src/instrumentation/google/get_provider_name.js +2 -1
- package/dist/src/instrumentation/google/get_web_search_telemetry.d.ts +38 -0
- package/dist/src/instrumentation/google/get_web_search_telemetry.js +125 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +2 -2
- package/dist/src/instrumentation/google/set_telemetry_attributes.js +6 -2
- package/dist/src/instrumentation/mistral/full_instrumentation.d.ts +60 -0
- package/dist/src/instrumentation/mistral/full_instrumentation.js +364 -0
- package/dist/src/instrumentation/mistral/get_input_message_content_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/mistral/get_input_message_content_telemetry.js +113 -0
- package/dist/src/instrumentation/mistral/get_structured_output_schema_telemetry.d.ts +13 -0
- package/dist/src/instrumentation/mistral/get_structured_output_schema_telemetry.js +24 -0
- package/dist/src/instrumentation/mistral/get_vertex_telemetry.d.ts +54 -0
- package/dist/src/instrumentation/mistral/get_vertex_telemetry.js +153 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +10 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +33 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +13 -6
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +17 -7
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +21 -4
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +50 -11
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +14 -6
- package/dist/src/instrumentation/openai/get_file_part_mime_type_telemetry.d.ts +26 -0
- package/dist/src/instrumentation/openai/get_file_part_mime_type_telemetry.js +100 -0
- package/dist/src/instrumentation/openai/get_openai_compatible_provider_telemetry.d.ts +48 -0
- package/dist/src/instrumentation/openai/get_openai_compatible_provider_telemetry.js +123 -0
- package/dist/src/instrumentation/openai/get_reasoning_content_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/openai/get_reasoning_content_telemetry.js +130 -0
- package/dist/src/instrumentation/openai/get_responses_telemetry.d.ts +89 -0
- package/dist/src/instrumentation/openai/get_responses_telemetry.js +587 -0
- package/dist/src/instrumentation/openai/get_service_tier_telemetry.d.ts +20 -0
- package/dist/src/instrumentation/openai/get_service_tier_telemetry.js +84 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +7 -5
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +15 -8
- package/dist/src/instrumentation/openai/get_vertex_openai_compatible_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/openai/get_vertex_openai_compatible_telemetry.js +69 -0
- package/dist/src/instrumentation/openai/get_web_search_telemetry.d.ts +74 -0
- package/dist/src/instrumentation/openai/get_web_search_telemetry.js +259 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +13 -1
- package/dist/src/instrumentation/openai/set_telemetry_attributes.js +33 -2
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +1 -1
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +1 -1
- package/dist/src/instrumentation/shared/is_vertex_base_url.d.ts +17 -0
- package/dist/src/instrumentation/shared/is_vertex_base_url.js +40 -0
- package/dist/src/otel/propagation.d.ts +37 -10
- package/dist/src/otel/propagation.js +62 -22
- package/dist/src/otel/span-filter.d.ts +2 -0
- package/dist/src/otel/span-filter.js +10 -1
- package/dist/src/otel/span-processor.d.ts +1 -2
- package/dist/src/otel/span-processor.js +17 -20
- package/dist/src/otel/strip-media-payloads.d.ts +26 -0
- package/dist/src/otel/strip-media-payloads.js +135 -0
- package/dist/src/propagate-metadata-to-gateway.d.ts +37 -0
- package/dist/src/propagate-metadata-to-gateway.js +213 -0
- package/package.json +3 -3
- package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +0 -3
- package/dist/src/instrumentation/vercel/full_instrumentation.js +0 -54
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +0 -22
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +0 -66
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +0 -24
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +0 -84
- package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +0 -12
- package/dist/src/otel/vercel-ai-sdk-span-filter.js +0 -88
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Request-side prompt-caching config
|
|
2
|
+
* Request-side capture for Bedrock spans: the prompt-caching config, and the
|
|
3
|
+
* AWS region the call was sent to.
|
|
3
4
|
*
|
|
4
5
|
* Bedrock callers opt into caching in the request body, in the model vendor's
|
|
5
6
|
* own schema: Anthropic-on-Bedrock bodies carry `cache_control` blocks (the
|
|
@@ -12,11 +13,22 @@
|
|
|
12
13
|
* request JSON) to stamp every breakpoint found as `weflayr.cache.breakpoints`.
|
|
13
14
|
* Bedrock responses report no TTL split of the written tokens; the worker
|
|
14
15
|
* derives it from these breakpoints when they all share one TTL.
|
|
16
|
+
*
|
|
17
|
+
* The region rides along: AWS bills a Bedrock call at the price of the region
|
|
18
|
+
* it is sent to, whatever geography a cross-region inference profile then
|
|
19
|
+
* routes it to, and a call carrying no region is priced by no row at all. Only
|
|
20
|
+
* the client names it, and `_startSpan` is handed the command but never the
|
|
21
|
+
* client, so `send` is wrapped to resolve the region and record it against the
|
|
22
|
+
* command the span is then started from. The client resolves its region
|
|
23
|
+
* through an async provider, hence the await before the call rather than a
|
|
24
|
+
* lookup inside `_startSpan`.
|
|
15
25
|
*/
|
|
16
26
|
import { type CacheBreakpoint } from '../anthropic/get_cache_config_telemetry';
|
|
27
|
+
export declare function recordCommandRegion(command: object, region: string): void;
|
|
17
28
|
export declare function extractBedrockCacheBreakpoints(body: any): CacheBreakpoint[];
|
|
18
29
|
export declare class BedrockCacheConfigInstrumentor {
|
|
19
30
|
private _patched;
|
|
20
31
|
instrument(instrumentation: any): void;
|
|
32
|
+
private _recordRegionPerCommand;
|
|
21
33
|
uninstrument(): void;
|
|
22
34
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Request-side prompt-caching config
|
|
3
|
+
* Request-side capture for Bedrock spans: the prompt-caching config, and the
|
|
4
|
+
* AWS region the call was sent to.
|
|
4
5
|
*
|
|
5
6
|
* Bedrock callers opt into caching in the request body, in the model vendor's
|
|
6
7
|
* own schema: Anthropic-on-Bedrock bodies carry `cache_control` blocks (the
|
|
@@ -13,12 +14,32 @@
|
|
|
13
14
|
* request JSON) to stamp every breakpoint found as `weflayr.cache.breakpoints`.
|
|
14
15
|
* Bedrock responses report no TTL split of the written tokens; the worker
|
|
15
16
|
* derives it from these breakpoints when they all share one TTL.
|
|
17
|
+
*
|
|
18
|
+
* The region rides along: AWS bills a Bedrock call at the price of the region
|
|
19
|
+
* it is sent to, whatever geography a cross-region inference profile then
|
|
20
|
+
* routes it to, and a call carrying no region is priced by no row at all. Only
|
|
21
|
+
* the client names it, and `_startSpan` is handed the command but never the
|
|
22
|
+
* client, so `send` is wrapped to resolve the region and record it against the
|
|
23
|
+
* command the span is then started from. The client resolves its region
|
|
24
|
+
* through an async provider, hence the await before the call rather than a
|
|
25
|
+
* lookup inside `_startSpan`.
|
|
16
26
|
*/
|
|
17
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
28
|
exports.BedrockCacheConfigInstrumentor = void 0;
|
|
29
|
+
exports.recordCommandRegion = recordCommandRegion;
|
|
19
30
|
exports.extractBedrockCacheBreakpoints = extractBedrockCacheBreakpoints;
|
|
20
31
|
const get_cache_config_telemetry_1 = require("../anthropic/get_cache_config_telemetry");
|
|
21
32
|
const BREAKPOINTS_ATTR = 'weflayr.cache.breakpoints';
|
|
33
|
+
const PROVIDER_REGION_ATTR = 'weflayr.provider_region';
|
|
34
|
+
// The region of the client each pending command was sent with. Keyed by the
|
|
35
|
+
// command object, which `_startSpan` is called with synchronously, so an entry
|
|
36
|
+
// is read by the span of its own call and by no other.
|
|
37
|
+
const regionByCommand = new WeakMap();
|
|
38
|
+
// Record the region a command was sent with. The `send` wrapper below is the
|
|
39
|
+
// only production caller; the tests use it to stand in for a real client.
|
|
40
|
+
function recordCommandRegion(command, region) {
|
|
41
|
+
regionByCommand.set(command, region);
|
|
42
|
+
}
|
|
22
43
|
// A breakpoint as location + the raw `cachePoint` `ttl` string, omitted when
|
|
23
44
|
// the request sets none.
|
|
24
45
|
function _cachePointEntry(location, cachePoint) {
|
|
@@ -79,6 +100,12 @@ class BedrockCacheConfigInstrumentor {
|
|
|
79
100
|
span.setAttribute(BREAKPOINTS_ATTR, JSON.stringify(breakpoints));
|
|
80
101
|
}
|
|
81
102
|
}
|
|
103
|
+
const command = request && request.params;
|
|
104
|
+
const region = command && regionByCommand.get(command);
|
|
105
|
+
if (region) {
|
|
106
|
+
span.setAttribute(PROVIDER_REGION_ATTR, region);
|
|
107
|
+
regionByCommand.delete(command);
|
|
108
|
+
}
|
|
82
109
|
}
|
|
83
110
|
catch {
|
|
84
111
|
// Never break span creation over an enrichment failure.
|
|
@@ -86,6 +113,34 @@ class BedrockCacheConfigInstrumentor {
|
|
|
86
113
|
return span;
|
|
87
114
|
};
|
|
88
115
|
this._patched.push([instrumentation, '_startSpan', startSpanOriginal]);
|
|
116
|
+
this._recordRegionPerCommand();
|
|
117
|
+
}
|
|
118
|
+
// Wrap `send` outside the instrumentor's own wrapper, so the region is
|
|
119
|
+
// resolved and recorded before the span it belongs to is started.
|
|
120
|
+
_recordRegionPerCommand() {
|
|
121
|
+
let BedrockRuntimeClient;
|
|
122
|
+
try {
|
|
123
|
+
({ BedrockRuntimeClient } = require('@aws-sdk/client-bedrock-runtime'));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// The caller reaches Bedrock another way: nothing to wrap.
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const sendOriginal = BedrockRuntimeClient.prototype.send;
|
|
130
|
+
if (typeof sendOriginal !== 'function')
|
|
131
|
+
return;
|
|
132
|
+
BedrockRuntimeClient.prototype.send = async function sendRecordingRegion(command, ...rest) {
|
|
133
|
+
try {
|
|
134
|
+
const region = await this.config.region();
|
|
135
|
+
if (region && command)
|
|
136
|
+
recordCommandRegion(command, String(region));
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Never fail the call over an enrichment failure.
|
|
140
|
+
}
|
|
141
|
+
return sendOriginal.apply(this, [command, ...rest]);
|
|
142
|
+
};
|
|
143
|
+
this._patched.push([BedrockRuntimeClient.prototype, 'send', sendOriginal]);
|
|
89
144
|
}
|
|
90
145
|
uninstrument() {
|
|
91
146
|
for (const [owner, methodName, original] of this._patched) {
|
|
@@ -65,7 +65,7 @@ class BedrockPromptCachingInstrumentor {
|
|
|
65
65
|
instrumentation._wrapPromise = function wrapPromiseWithCache(span, promise, modelId) {
|
|
66
66
|
// Stamp cache tokens off the response, then hand the same response to the
|
|
67
67
|
// original handler (which decodes the still-buffered body for input/output
|
|
68
|
-
// tokens). Resolving to the original response keeps
|
|
68
|
+
// tokens). Resolving to the original response keeps behavior identical.
|
|
69
69
|
const enriched = Promise.resolve(promise).then((response) => {
|
|
70
70
|
_setCacheTokens(span, response);
|
|
71
71
|
return response;
|
|
@@ -74,14 +74,17 @@ class BedrockPromptCachingInstrumentor {
|
|
|
74
74
|
};
|
|
75
75
|
this._patched.push([instrumentation, '_wrapPromise', wrapPromiseOriginal]);
|
|
76
76
|
}
|
|
77
|
-
// Streamed: the instrumentor
|
|
78
|
-
// `_handleNovaStreamingMetadata(span, parsedResponse)`
|
|
79
|
-
//
|
|
77
|
+
// Streamed: the instrumentor hands every chunk to
|
|
78
|
+
// `_handleNovaStreamingMetadata(span, parsedResponse)` but never reads its
|
|
79
|
+
// cache counts: Nova's arrive in the trailing `metadata` chunk, Anthropic's
|
|
80
|
+
// in the opening `message_start` one.
|
|
80
81
|
const metadataOriginal = instrumentation._handleNovaStreamingMetadata;
|
|
81
82
|
if (typeof metadataOriginal === 'function') {
|
|
82
83
|
instrumentation._handleNovaStreamingMetadata = function handleMetadataWithCache(span, parsedResponse) {
|
|
83
84
|
try {
|
|
84
|
-
const usage = parsedResponse
|
|
85
|
+
const usage = parsedResponse?.type === 'message_start'
|
|
86
|
+
? parsedResponse.message?.usage
|
|
87
|
+
: parsedResponse?.metadata?.usage;
|
|
85
88
|
_setCacheFromUsage(span, usage);
|
|
86
89
|
}
|
|
87
90
|
catch {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* "amazon.nova-micro-v1:0" becomes "nova-micro-v1:0" and
|
|
7
7
|
* "us.anthropic.claude-haiku-4-5-20251001-v1:0" becomes
|
|
8
8
|
* "claude-haiku-4-5-20251001-v1:0". Neither stripped form matches anything in the
|
|
9
|
-
* pricing
|
|
9
|
+
* pricing catalog, whose Bedrock keys are the full ids, so those calls price at
|
|
10
10
|
* nothing.
|
|
11
11
|
*
|
|
12
12
|
* Wrap `_startSpan({ params })` to re-stamp the id exactly as requested (so it
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* "amazon.nova-micro-v1:0" becomes "nova-micro-v1:0" and
|
|
8
8
|
* "us.anthropic.claude-haiku-4-5-20251001-v1:0" becomes
|
|
9
9
|
* "claude-haiku-4-5-20251001-v1:0". Neither stripped form matches anything in the
|
|
10
|
-
* pricing
|
|
10
|
+
* pricing catalog, whose Bedrock keys are the full ids, so those calls price at
|
|
11
11
|
* nothing.
|
|
12
12
|
*
|
|
13
13
|
* Wrap `_startSpan({ params })` to re-stamp the id exactly as requested (so it
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry for the Bedrock models whose InvokeModel body is OpenAI's chat
|
|
3
|
+
* completions format (Qwen, OpenAI's gpt-oss, DeepSeek, Kimi, MiniMax...).
|
|
4
|
+
*
|
|
5
|
+
* `@traceloop/instrumentation-bedrock` reads a request and its answer only for
|
|
6
|
+
* the vendors it knows (Amazon, Anthropic, Cohere, AI21, Meta), so a call to one
|
|
7
|
+
* of these models carries no request parameter and no answer, and is named a
|
|
8
|
+
* text completion. This complement:
|
|
9
|
+
*
|
|
10
|
+
* - names the call a chat (`_getOperationType`), and stamps its request
|
|
11
|
+
* parameters once it succeeded (`_startSpan` keeps the request, which
|
|
12
|
+
* `_endSpan`, only called on success, does not see), as the Python SDK
|
|
13
|
+
* records them only off an answer;
|
|
14
|
+
* - stamps a non-streamed answer, its finish reason, token counts, response
|
|
15
|
+
* model and id (`_setResponseAttributes`);
|
|
16
|
+
* - accumulates a stream into the body a non-streamed call answers, chunk by
|
|
17
|
+
* chunk (`_handleNovaStreamingMetadata`, which the instrumentor calls for
|
|
18
|
+
* every chunk whatever the vendor), and stamps it from the closing chunk.
|
|
19
|
+
* The instrumentor stamps a stream's token counts itself, off the closing
|
|
20
|
+
* chunk's `amazon-bedrock-invocationMetrics`.
|
|
21
|
+
*
|
|
22
|
+
* The input messages and the tool definitions are stamped by their own
|
|
23
|
+
* complements, which read a `messages` body whatever its vendor.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isOpenaiChatFormat(modelVendor: string, requestBody: any): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Fold one streamed chunk into the body a non-streamed call answers: the text
|
|
28
|
+
* deltas and each tool call's argument fragments are joined, and the token
|
|
29
|
+
* counts come from the closing chunk's invocation metrics.
|
|
30
|
+
*
|
|
31
|
+
* Example: chunks with deltas {content: "Hel"} then {content: "lo"}, then
|
|
32
|
+
* finish_reason "stop" and invocation metrics {inputTokenCount: 18,
|
|
33
|
+
* outputTokenCount: 4} give {choices: [{message: {content: "Hello",
|
|
34
|
+
* tool_calls: []}, finish_reason: "stop"}], usage: {prompt_tokens: 18,
|
|
35
|
+
* completion_tokens: 4}, ...}.
|
|
36
|
+
*/
|
|
37
|
+
export declare function accumulateStreamChunk(accumulatingBody: any, chunk: any): void;
|
|
38
|
+
export declare function setRequestAndUsageAttributes(span: any, requestBody: any, responseBody: any): void;
|
|
39
|
+
/** The finish reasons, and the answer when its content is captured. */
|
|
40
|
+
export declare function setOutputAttributes(span: any, responseBody: any, shouldSendPrompts: boolean): void;
|
|
41
|
+
export declare class BedrockOpenaiChatFormatInstrumentor {
|
|
42
|
+
private _patched;
|
|
43
|
+
instrument(instrumentation: any): void;
|
|
44
|
+
uninstrument(): void;
|
|
45
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Telemetry for the Bedrock models whose InvokeModel body is OpenAI's chat
|
|
4
|
+
* completions format (Qwen, OpenAI's gpt-oss, DeepSeek, Kimi, MiniMax...).
|
|
5
|
+
*
|
|
6
|
+
* `@traceloop/instrumentation-bedrock` reads a request and its answer only for
|
|
7
|
+
* the vendors it knows (Amazon, Anthropic, Cohere, AI21, Meta), so a call to one
|
|
8
|
+
* of these models carries no request parameter and no answer, and is named a
|
|
9
|
+
* text completion. This complement:
|
|
10
|
+
*
|
|
11
|
+
* - names the call a chat (`_getOperationType`), and stamps its request
|
|
12
|
+
* parameters once it succeeded (`_startSpan` keeps the request, which
|
|
13
|
+
* `_endSpan`, only called on success, does not see), as the Python SDK
|
|
14
|
+
* records them only off an answer;
|
|
15
|
+
* - stamps a non-streamed answer, its finish reason, token counts, response
|
|
16
|
+
* model and id (`_setResponseAttributes`);
|
|
17
|
+
* - accumulates a stream into the body a non-streamed call answers, chunk by
|
|
18
|
+
* chunk (`_handleNovaStreamingMetadata`, which the instrumentor calls for
|
|
19
|
+
* every chunk whatever the vendor), and stamps it from the closing chunk.
|
|
20
|
+
* The instrumentor stamps a stream's token counts itself, off the closing
|
|
21
|
+
* chunk's `amazon-bedrock-invocationMetrics`.
|
|
22
|
+
*
|
|
23
|
+
* The input messages and the tool definitions are stamped by their own
|
|
24
|
+
* complements, which read a `messages` body whatever its vendor.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.BedrockOpenaiChatFormatInstrumentor = void 0;
|
|
28
|
+
exports.isOpenaiChatFormat = isOpenaiChatFormat;
|
|
29
|
+
exports.accumulateStreamChunk = accumulateStreamChunk;
|
|
30
|
+
exports.setRequestAndUsageAttributes = setRequestAndUsageAttributes;
|
|
31
|
+
exports.setOutputAttributes = setOutputAttributes;
|
|
32
|
+
// The vendors the instrumentor reads itself; every other vendor's body is
|
|
33
|
+
// OpenAI's chat completions format.
|
|
34
|
+
const VENDORS_READ_BY_THE_INSTRUMENTOR = new Set(['cohere', 'anthropic', 'ai21', 'meta', 'amazon']);
|
|
35
|
+
const INVOCATION_METRICS_KEY = 'amazon-bedrock-invocationMetrics';
|
|
36
|
+
// OpenAI's finish reasons, in the OpenTelemetry GenAI vocabulary the other
|
|
37
|
+
// vendors' answers are recorded in.
|
|
38
|
+
const FINISH_REASON_BY_OPENAI_FINISH_REASON = {
|
|
39
|
+
stop: 'stop',
|
|
40
|
+
length: 'length',
|
|
41
|
+
tool_calls: 'tool_call',
|
|
42
|
+
content_filter: 'content_filter',
|
|
43
|
+
};
|
|
44
|
+
function isOpenaiChatFormat(modelVendor, requestBody) {
|
|
45
|
+
return (!VENDORS_READ_BY_THE_INSTRUMENTOR.has(modelVendor) &&
|
|
46
|
+
requestBody !== null &&
|
|
47
|
+
typeof requestBody === 'object' &&
|
|
48
|
+
Array.isArray(requestBody.messages));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Fold one streamed chunk into the body a non-streamed call answers: the text
|
|
52
|
+
* deltas and each tool call's argument fragments are joined, and the token
|
|
53
|
+
* counts come from the closing chunk's invocation metrics.
|
|
54
|
+
*
|
|
55
|
+
* Example: chunks with deltas {content: "Hel"} then {content: "lo"}, then
|
|
56
|
+
* finish_reason "stop" and invocation metrics {inputTokenCount: 18,
|
|
57
|
+
* outputTokenCount: 4} give {choices: [{message: {content: "Hello",
|
|
58
|
+
* tool_calls: []}, finish_reason: "stop"}], usage: {prompt_tokens: 18,
|
|
59
|
+
* completion_tokens: 4}, ...}.
|
|
60
|
+
*/
|
|
61
|
+
function accumulateStreamChunk(accumulatingBody, chunk) {
|
|
62
|
+
accumulatingBody.id ??= chunk.id;
|
|
63
|
+
accumulatingBody.model ??= chunk.model;
|
|
64
|
+
accumulatingBody.choices ??= [{ message: { content: '', tool_calls: [] } }];
|
|
65
|
+
const choices = accumulatingBody.choices;
|
|
66
|
+
const message = choices[0].message;
|
|
67
|
+
for (const choice of chunk.choices || []) {
|
|
68
|
+
const delta = choice.delta || {};
|
|
69
|
+
message.content += delta.content || '';
|
|
70
|
+
for (const toolCallDelta of delta.tool_calls || []) {
|
|
71
|
+
const index = toolCallDelta.index ?? 0;
|
|
72
|
+
while (message.tool_calls.length <= index) {
|
|
73
|
+
message.tool_calls.push({ id: null, function: { name: '', arguments: '' } });
|
|
74
|
+
}
|
|
75
|
+
const toolCall = message.tool_calls[index];
|
|
76
|
+
toolCall.id = toolCallDelta.id || toolCall.id;
|
|
77
|
+
const functionDelta = toolCallDelta.function || {};
|
|
78
|
+
toolCall.function.name += functionDelta.name || '';
|
|
79
|
+
toolCall.function.arguments += functionDelta.arguments || '';
|
|
80
|
+
}
|
|
81
|
+
if (choice.finish_reason)
|
|
82
|
+
choices[0].finish_reason = choice.finish_reason;
|
|
83
|
+
}
|
|
84
|
+
const invocationMetrics = chunk[INVOCATION_METRICS_KEY];
|
|
85
|
+
if (invocationMetrics && typeof invocationMetrics === 'object') {
|
|
86
|
+
accumulatingBody.usage = {
|
|
87
|
+
prompt_tokens: invocationMetrics.inputTokenCount,
|
|
88
|
+
completion_tokens: invocationMetrics.outputTokenCount,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function setRequestAndUsageAttributes(span, requestBody, responseBody) {
|
|
93
|
+
const attributes = {
|
|
94
|
+
'gen_ai.request.max_tokens': requestBody.max_tokens ?? requestBody.max_completion_tokens,
|
|
95
|
+
'gen_ai.request.temperature': requestBody.temperature,
|
|
96
|
+
'gen_ai.request.top_p': requestBody.top_p,
|
|
97
|
+
'gen_ai.response.model': responseBody.model,
|
|
98
|
+
'gen_ai.response.id': responseBody.id,
|
|
99
|
+
};
|
|
100
|
+
const usage = responseBody.usage || {};
|
|
101
|
+
const inputTokens = usage.prompt_tokens;
|
|
102
|
+
const outputTokens = usage.completion_tokens;
|
|
103
|
+
if (Number.isInteger(inputTokens) && Number.isInteger(outputTokens)) {
|
|
104
|
+
attributes['gen_ai.usage.input_tokens'] = inputTokens;
|
|
105
|
+
attributes['gen_ai.usage.output_tokens'] = outputTokens;
|
|
106
|
+
attributes['gen_ai.usage.total_tokens'] = inputTokens + outputTokens;
|
|
107
|
+
}
|
|
108
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
109
|
+
if (value !== undefined && value !== null)
|
|
110
|
+
span.setAttribute(name, value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function _parseArguments(argumentsJson) {
|
|
114
|
+
try {
|
|
115
|
+
return JSON.parse(argumentsJson);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return argumentsJson;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function _getFinishReason(choice) {
|
|
122
|
+
const openaiFinishReason = choice.finish_reason || '';
|
|
123
|
+
return FINISH_REASON_BY_OPENAI_FINISH_REASON[openaiFinishReason] ?? openaiFinishReason;
|
|
124
|
+
}
|
|
125
|
+
/** The finish reasons, and the answer when its content is captured. */
|
|
126
|
+
function setOutputAttributes(span, responseBody, shouldSendPrompts) {
|
|
127
|
+
const choices = responseBody.choices || [];
|
|
128
|
+
const finishReasons = choices.filter((choice) => choice.finish_reason).map(_getFinishReason);
|
|
129
|
+
if (finishReasons.length)
|
|
130
|
+
span.setAttribute('gen_ai.response.finish_reasons', finishReasons);
|
|
131
|
+
if (!shouldSendPrompts)
|
|
132
|
+
return;
|
|
133
|
+
const outputMessages = choices.map((choice) => {
|
|
134
|
+
const message = choice.message || {};
|
|
135
|
+
const parts = [];
|
|
136
|
+
if (message.content)
|
|
137
|
+
parts.push({ type: 'text', content: message.content });
|
|
138
|
+
for (const toolCall of message.tool_calls || []) {
|
|
139
|
+
parts.push({
|
|
140
|
+
type: 'tool_call',
|
|
141
|
+
id: toolCall.id,
|
|
142
|
+
name: toolCall.function?.name,
|
|
143
|
+
arguments: _parseArguments(toolCall.function?.arguments || ''),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return { role: 'assistant', parts, finish_reason: _getFinishReason(choice) };
|
|
147
|
+
});
|
|
148
|
+
span.setAttribute('gen_ai.output.messages', JSON.stringify(outputMessages));
|
|
149
|
+
}
|
|
150
|
+
class BedrockOpenaiChatFormatInstrumentor {
|
|
151
|
+
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
152
|
+
_patched = [];
|
|
153
|
+
// No-op (and never throws) if the instrumentor's internals have changed shape:
|
|
154
|
+
// the worst case is a call recorded without its answer, never a crash.
|
|
155
|
+
instrument(instrumentation) {
|
|
156
|
+
if (!instrumentation)
|
|
157
|
+
return;
|
|
158
|
+
const originalGetOperationType = instrumentation._getOperationType;
|
|
159
|
+
const originalStartSpan = instrumentation._startSpan;
|
|
160
|
+
const originalSetResponseAttributes = instrumentation._setResponseAttributes;
|
|
161
|
+
const originalHandleStreamChunk = instrumentation._handleNovaStreamingMetadata;
|
|
162
|
+
const originalEndSpan = instrumentation._endSpan;
|
|
163
|
+
if (typeof originalGetOperationType !== 'function' ||
|
|
164
|
+
typeof originalStartSpan !== 'function' ||
|
|
165
|
+
typeof originalSetResponseAttributes !== 'function' ||
|
|
166
|
+
typeof originalHandleStreamChunk !== 'function' ||
|
|
167
|
+
typeof originalEndSpan !== 'function') {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// span -> the request and the answer accumulated so far, for an
|
|
171
|
+
// OpenAI-format call.
|
|
172
|
+
const callBySpan = new WeakMap();
|
|
173
|
+
instrumentation._getOperationType = function getOperationType(vendor, requestBody) {
|
|
174
|
+
if (isOpenaiChatFormat(vendor, requestBody))
|
|
175
|
+
return 'chat';
|
|
176
|
+
return originalGetOperationType.call(this, vendor, requestBody);
|
|
177
|
+
};
|
|
178
|
+
instrumentation._startSpan = function startSpanRecordingOpenaiChatFormatCalls(request) {
|
|
179
|
+
const span = originalStartSpan.call(this, request);
|
|
180
|
+
try {
|
|
181
|
+
const input = request?.params?.input;
|
|
182
|
+
const requestBody = typeof input?.body === 'string' ? JSON.parse(input.body) : null;
|
|
183
|
+
const { modelVendor } = this._extractVendorAndModel(input?.modelId || '');
|
|
184
|
+
if (isOpenaiChatFormat(modelVendor, requestBody)) {
|
|
185
|
+
callBySpan.set(span, { requestBody, accumulatingBody: {} });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Never break span creation over an enrichment failure.
|
|
190
|
+
}
|
|
191
|
+
return span;
|
|
192
|
+
};
|
|
193
|
+
instrumentation._setResponseAttributes = function setResponseAttributesOfOpenaiChatFormat(vendor, response, isStream = false, ...args) {
|
|
194
|
+
const attributes = originalSetResponseAttributes.call(this, vendor, response, isStream, ...args);
|
|
195
|
+
try {
|
|
196
|
+
if (!isStream &&
|
|
197
|
+
!VENDORS_READ_BY_THE_INSTRUMENTOR.has(vendor) &&
|
|
198
|
+
Array.isArray(response?.choices)) {
|
|
199
|
+
const recordedAttributes = {};
|
|
200
|
+
const attributeCollector = {
|
|
201
|
+
setAttribute(name, value) {
|
|
202
|
+
recordedAttributes[name] = value;
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
setRequestAndUsageAttributes(attributeCollector, {}, response);
|
|
206
|
+
setOutputAttributes(attributeCollector, response, this._shouldSendPrompts());
|
|
207
|
+
return { ...attributes, ...recordedAttributes };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Never break the response handling over an enrichment failure.
|
|
212
|
+
}
|
|
213
|
+
return attributes;
|
|
214
|
+
};
|
|
215
|
+
instrumentation._handleNovaStreamingMetadata = function handleOpenaiChatFormatStreamChunk(span, parsedResponse) {
|
|
216
|
+
try {
|
|
217
|
+
const call = callBySpan.get(span);
|
|
218
|
+
if (call) {
|
|
219
|
+
accumulateStreamChunk(call.accumulatingBody, parsedResponse);
|
|
220
|
+
if (parsedResponse && INVOCATION_METRICS_KEY in parsedResponse) {
|
|
221
|
+
setRequestAndUsageAttributes(span, {}, call.accumulatingBody);
|
|
222
|
+
setOutputAttributes(span, call.accumulatingBody, this._shouldSendPrompts());
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// Never break the caller's stream over an enrichment failure.
|
|
228
|
+
}
|
|
229
|
+
return originalHandleStreamChunk.call(this, span, parsedResponse);
|
|
230
|
+
};
|
|
231
|
+
instrumentation._endSpan = function endSpanWithOpenaiChatFormatRequest(endSpanArguments) {
|
|
232
|
+
try {
|
|
233
|
+
const call = callBySpan.get(endSpanArguments?.span);
|
|
234
|
+
if (call)
|
|
235
|
+
setRequestAndUsageAttributes(endSpanArguments.span, call.requestBody, {});
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Never break the response handling over an enrichment failure.
|
|
239
|
+
}
|
|
240
|
+
return originalEndSpan.call(this, endSpanArguments);
|
|
241
|
+
};
|
|
242
|
+
this._patched.push([instrumentation, '_getOperationType', originalGetOperationType], [instrumentation, '_startSpan', originalStartSpan], [instrumentation, '_setResponseAttributes', originalSetResponseAttributes], [instrumentation, '_handleNovaStreamingMetadata', originalHandleStreamChunk], [instrumentation, '_endSpan', originalEndSpan]);
|
|
243
|
+
}
|
|
244
|
+
uninstrument() {
|
|
245
|
+
for (const [owner, methodName, original] of [...this._patched].reverse()) {
|
|
246
|
+
owner[methodName] = original;
|
|
247
|
+
}
|
|
248
|
+
this._patched = [];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
exports.BedrockOpenaiChatFormatInstrumentor = BedrockOpenaiChatFormatInstrumentor;
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* block mapper, which only accepts block arrays — so streamed Nova spans end up
|
|
7
7
|
* with an assistant message whose `parts` is empty. This complement wraps the
|
|
8
8
|
* instrumentor's per-chunk `_handleNovaStreamingMetadata(span, parsedResponse)`
|
|
9
|
-
* hook: it accumulates the
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
13
|
-
* the
|
|
9
|
+
* hook: it accumulates the text and the stop reason itself, Nova's
|
|
10
|
+
* (`contentBlockDelta`, `messageStop`) and Anthropic's (`content_block_delta`,
|
|
11
|
+
* `message_delta`), and re-stamps a correct output message when the stream's
|
|
12
|
+
* last chunk arrives (Nova's trailing `metadata`, Anthropic's `message_stop`),
|
|
13
|
+
* after the instrumentor has written its empty-parts message. The span
|
|
14
|
+
* processor strips the attribute when message-content capture is disabled.
|
|
14
15
|
*
|
|
15
16
|
* A streamed tool call is accumulated the same way and for the same reason:
|
|
16
17
|
* `contentBlockStart` opens it with an id and name, `contentBlockDelta` appends
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* block mapper, which only accepts block arrays — so streamed Nova spans end up
|
|
8
8
|
* with an assistant message whose `parts` is empty. This complement wraps the
|
|
9
9
|
* instrumentor's per-chunk `_handleNovaStreamingMetadata(span, parsedResponse)`
|
|
10
|
-
* hook: it accumulates the
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
14
|
-
* the
|
|
10
|
+
* hook: it accumulates the text and the stop reason itself, Nova's
|
|
11
|
+
* (`contentBlockDelta`, `messageStop`) and Anthropic's (`content_block_delta`,
|
|
12
|
+
* `message_delta`), and re-stamps a correct output message when the stream's
|
|
13
|
+
* last chunk arrives (Nova's trailing `metadata`, Anthropic's `message_stop`),
|
|
14
|
+
* after the instrumentor has written its empty-parts message. The span
|
|
15
|
+
* processor strips the attribute when message-content capture is disabled.
|
|
15
16
|
*
|
|
16
17
|
* A streamed tool call is accumulated the same way and for the same reason:
|
|
17
18
|
* `contentBlockStart` opens it with an id and name, `contentBlockDelta` appends
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
*/
|
|
23
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
25
|
exports.BedrockStreamingOutputMessageInstrumentor = void 0;
|
|
25
|
-
// Nova stop reasons mapped to the OTel finish_reason values the instrumentor
|
|
26
|
+
// Nova's and Anthropic's stop reasons mapped to the OTel finish_reason values the instrumentor
|
|
26
27
|
// uses on non-streamed spans, so streamed and non-streamed rows normalize alike.
|
|
27
28
|
const FINISH_REASON_BY_STOP_REASON = {
|
|
28
29
|
end_turn: 'stop',
|
|
@@ -75,18 +76,20 @@ class BedrockStreamingOutputMessageInstrumentor {
|
|
|
75
76
|
stopReason: null,
|
|
76
77
|
toolCalls: [],
|
|
77
78
|
};
|
|
78
|
-
const delta = parsedResponse
|
|
79
|
-
parsedResponse.
|
|
80
|
-
parsedResponse.contentBlockDelta.delta &&
|
|
81
|
-
parsedResponse.contentBlockDelta.delta.text;
|
|
79
|
+
const delta = parsedResponse?.contentBlockDelta?.delta?.text ??
|
|
80
|
+
(parsedResponse?.type === 'content_block_delta' ? parsedResponse.delta?.text : undefined);
|
|
82
81
|
if (typeof delta === 'string')
|
|
83
82
|
state.text += delta;
|
|
84
|
-
const stopReason = parsedResponse
|
|
83
|
+
const stopReason = parsedResponse?.messageStop?.stopReason ??
|
|
84
|
+
(parsedResponse?.type === 'message_delta'
|
|
85
|
+
? parsedResponse.delta?.stop_reason
|
|
86
|
+
: undefined);
|
|
85
87
|
if (stopReason != null)
|
|
86
88
|
state.stopReason = stopReason;
|
|
87
89
|
_accumulateToolCall(state, parsedResponse);
|
|
88
90
|
streamStateBySpan.set(span, state);
|
|
89
|
-
|
|
91
|
+
const isLastChunk = parsedResponse?.metadata || parsedResponse?.type === 'message_stop';
|
|
92
|
+
if (isLastChunk && (state.text || state.toolCalls.length)) {
|
|
90
93
|
const parts = [];
|
|
91
94
|
if (state.text)
|
|
92
95
|
parts.push({ type: 'text', content: state.text });
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* the inference config but never for its `toolConfig`, so a tool-using call
|
|
6
6
|
* carries no `gen_ai.tool.definitions`. Wrap the instrumentor's
|
|
7
7
|
* `_startSpan({ params })` to stamp the definitions from the request body,
|
|
8
|
-
* unwrapping each entry's `toolSpec`
|
|
9
|
-
*
|
|
8
|
+
* unwrapping each entry's `toolSpec` (or OpenAI's `function`) so the attribute
|
|
9
|
+
* holds the bare definitions rather than their wrapper.
|
|
10
10
|
*/
|
|
11
11
|
export declare class BedrockToolDefinitionsInstrumentor {
|
|
12
12
|
private _patched;
|
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
* the inference config but never for its `toolConfig`, so a tool-using call
|
|
7
7
|
* carries no `gen_ai.tool.definitions`. Wrap the instrumentor's
|
|
8
8
|
* `_startSpan({ params })` to stamp the definitions from the request body,
|
|
9
|
-
* unwrapping each entry's `toolSpec`
|
|
10
|
-
*
|
|
9
|
+
* unwrapping each entry's `toolSpec` (or OpenAI's `function`) so the attribute
|
|
10
|
+
* holds the bare definitions rather than their wrapper.
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.BedrockToolDefinitionsInstrumentor = void 0;
|
|
14
|
-
// The bare tool definitions from an InvokeModel JSON body, e.g.
|
|
14
|
+
// The bare tool definitions from an InvokeModel JSON body: Nova's, e.g.
|
|
15
15
|
// `{"toolConfig": {"tools": [{"toolSpec": {"name": "get_weather", ...}}]}}`
|
|
16
|
-
// -> `[{"name": "get_weather", ...}]
|
|
16
|
+
// -> `[{"name": "get_weather", ...}]`, or OpenAI's chat completions format, e.g.
|
|
17
|
+
// `{"tools": [{"type": "function", "function": {"name": "get_weather", ...}}]}`
|
|
18
|
+
// -> `[{"name": "get_weather", ...}]`. An Anthropic body's tools, which the
|
|
19
|
+
// instrumentor records itself, give none.
|
|
17
20
|
function _toolDefinitionsFromRequestBody(rawBody) {
|
|
18
21
|
if (typeof rawBody !== 'string')
|
|
19
22
|
return [];
|
|
@@ -24,6 +27,11 @@ function _toolDefinitionsFromRequestBody(rawBody) {
|
|
|
24
27
|
catch {
|
|
25
28
|
return [];
|
|
26
29
|
}
|
|
30
|
+
if (body && Array.isArray(body.tools)) {
|
|
31
|
+
return body.tools
|
|
32
|
+
.filter((tool) => tool && typeof tool.function === 'object' && tool.function !== null)
|
|
33
|
+
.map((tool) => tool.function);
|
|
34
|
+
}
|
|
27
35
|
const tools = body && body.toolConfig && body.toolConfig.tools;
|
|
28
36
|
if (!Array.isArray(tools))
|
|
29
37
|
return [];
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* breakpoints (`cache_control` / `cachePoint` blocks it never records), the
|
|
8
8
|
* streamed output message content (its stream path formats an empty-parts
|
|
9
9
|
* message), the request's tool definitions (it never reads the body's
|
|
10
|
-
* `toolConfig`)
|
|
11
|
-
* Anthropic's block shape, so Nova's `toolUse` blocks come through raw)
|
|
10
|
+
* `toolConfig`), the tool-call output parts (its block mapper only knows
|
|
11
|
+
* Anthropic's block shape, so Nova's `toolUse` blocks come through raw), and
|
|
12
|
+
* everything about a model whose body is OpenAI's chat completions format (it
|
|
13
|
+
* reads only the vendors it knows).
|
|
12
14
|
*/
|
|
13
15
|
export declare class BedrockTelemetryInstrumentor {
|
|
14
16
|
private _subInstrumentors;
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
* breakpoints (`cache_control` / `cachePoint` blocks it never records), the
|
|
9
9
|
* streamed output message content (its stream path formats an empty-parts
|
|
10
10
|
* message), the request's tool definitions (it never reads the body's
|
|
11
|
-
* `toolConfig`)
|
|
12
|
-
* Anthropic's block shape, so Nova's `toolUse` blocks come through raw)
|
|
11
|
+
* `toolConfig`), the tool-call output parts (its block mapper only knows
|
|
12
|
+
* Anthropic's block shape, so Nova's `toolUse` blocks come through raw), and
|
|
13
|
+
* everything about a model whose body is OpenAI's chat completions format (it
|
|
14
|
+
* reads only the vendors it knows).
|
|
13
15
|
*/
|
|
14
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
17
|
exports.BedrockTelemetryInstrumentor = void 0;
|
|
@@ -18,6 +20,7 @@ const get_cache_telemetry_1 = require("./get_cache_telemetry");
|
|
|
18
20
|
const get_input_message_content_telemetry_1 = require("./get_input_message_content_telemetry");
|
|
19
21
|
const get_streaming_output_message_telemetry_1 = require("./get_streaming_output_message_telemetry");
|
|
20
22
|
const get_model_id_telemetry_1 = require("./get_model_id_telemetry");
|
|
23
|
+
const get_openai_chat_format_telemetry_1 = require("./get_openai_chat_format_telemetry");
|
|
21
24
|
const get_reasoning_output_telemetry_1 = require("./get_reasoning_output_telemetry");
|
|
22
25
|
const get_tool_call_output_telemetry_1 = require("./get_tool_call_output_telemetry");
|
|
23
26
|
const get_tool_definitions_telemetry_1 = require("./get_tool_definitions_telemetry");
|
|
@@ -33,14 +36,18 @@ class BedrockTelemetryInstrumentor {
|
|
|
33
36
|
new get_tool_call_output_telemetry_1.BedrockToolCallOutputInstrumentor(),
|
|
34
37
|
new get_model_id_telemetry_1.BedrockModelIdInstrumentor(),
|
|
35
38
|
new get_reasoning_output_telemetry_1.BedrockReasoningOutputInstrumentor(),
|
|
39
|
+
new get_openai_chat_format_telemetry_1.BedrockOpenaiChatFormatInstrumentor(),
|
|
36
40
|
];
|
|
37
41
|
}
|
|
38
42
|
instrument(instrumentation) {
|
|
39
43
|
for (const sub of this._subInstrumentors)
|
|
40
44
|
sub.instrument(instrumentation);
|
|
41
45
|
}
|
|
46
|
+
// Reversed: several complements wrap the same instrumentor method
|
|
47
|
+
// (`_startSpan`), so the patches have to be unwound last-patched-first or an
|
|
48
|
+
// earlier wrapper is written back over a later one and stays installed.
|
|
42
49
|
uninstrument() {
|
|
43
|
-
for (const sub of this._subInstrumentors)
|
|
50
|
+
for (const sub of [...this._subInstrumentors].reverse())
|
|
44
51
|
sub.uninstrument();
|
|
45
52
|
}
|
|
46
53
|
}
|