@telemetry-dev/bedrock 0.1.9 → 0.1.12
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 +30 -5
- package/dist/agents.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{internal-handlers-Byxp14OZ.mjs → internal-handlers-CLP2wXDh.mjs} +304 -52
- package/package.json +5 -5
- package/src/agent-handlers.ts +214 -36
- package/src/converse.ts +6 -3
- package/src/internal-handlers.ts +2 -1
- package/src/internal.ts +109 -5
- package/src/invoke-model.ts +83 -13
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
First-class AWS Bedrock instrumentation for the telemetry.dev TypeScript SDK.
|
|
4
4
|
|
|
5
|
+
Docs: https://telemetry.dev/docs/integrations/bedrock
|
|
6
|
+
|
|
5
7
|
This package instruments AWS SDK for JavaScript v3 clients for:
|
|
6
8
|
|
|
7
9
|
- `@aws-sdk/client-bedrock-runtime`
|
|
@@ -29,15 +31,18 @@ pnpm add @telemetry-dev/sdk @telemetry-dev/bedrock @aws-sdk/client-bedrock-runti
|
|
|
29
31
|
pnpm add @aws-sdk/client-bedrock-agent-runtime
|
|
30
32
|
```
|
|
31
33
|
|
|
34
|
+
Requires `@aws-sdk/client-bedrock-runtime` 3.600.0 or a later 3.x release (and the same range for the optional `@aws-sdk/client-bedrock-agent-runtime`), and `@telemetry-dev/sdk`, which needs Node.js 20.19.0 or later.
|
|
35
|
+
|
|
32
36
|
## Quickstart
|
|
33
37
|
|
|
38
|
+
`init()` reads your project API key from the `TELEMETRY_DEV_API_KEY` environment variable, so set it before you start the app.
|
|
39
|
+
|
|
34
40
|
```ts
|
|
35
41
|
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
|
|
36
42
|
import { init, shutdown } from "@telemetry-dev/sdk";
|
|
37
43
|
import { wrapBedrock } from "@telemetry-dev/bedrock";
|
|
38
44
|
|
|
39
45
|
init({
|
|
40
|
-
apiKey: process.env.TELEMETRY_DEV_API_KEY,
|
|
41
46
|
serviceName: "bedrock-app",
|
|
42
47
|
environment: "production",
|
|
43
48
|
});
|
|
@@ -98,9 +103,22 @@ instrumentBedrockAgents({ captureAgentTrace: false });
|
|
|
98
103
|
|
|
99
104
|
## Options
|
|
100
105
|
|
|
101
|
-
| Option | Default | Applies to | Notes
|
|
102
|
-
| ------------------- | ------- | --------------------- |
|
|
103
|
-
| `captureAgentTrace` | `false` | Agent Runtime streams | Aggregates trace usage and counts by default. When enabled, also attaches
|
|
106
|
+
| Option | Default | Applies to | Notes |
|
|
107
|
+
| ------------------- | ------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
108
|
+
| `captureAgentTrace` | `false` | Agent Runtime streams | Aggregates trace usage and counts by default. When enabled, also attaches trace events as `td.metadata.agent_trace`, filtered as described below. |
|
|
109
|
+
|
|
110
|
+
`td.metadata.agent_trace` is truncated but never passed to your `mask`, so each trace event is rebuilt from an explicit allowlist instead:
|
|
111
|
+
|
|
112
|
+
- Envelope fields: `agentId`, `agentAliasId`, `agentVersion`, `sessionId`, `collaboratorName`, `eventTime`, and the alias ARNs in `callerChain`.
|
|
113
|
+
- Content the agent sends to a model, tool, or guardrail (`modelInvocationInput`, `invocationInput`, `inputAssessments`) is kept as received when `captureInput` is on.
|
|
114
|
+
- Content that comes back (`modelInvocationOutput`, `rationale`, `observation`, `outputAssessments`, custom orchestration `event`) is kept as received when `captureOutput` is on.
|
|
115
|
+
- A failure reason (`failureTrace.failureReason`, and `td.metadata.failure_reason`) is free-form text, so it is kept only when both are on. `td.metadata.failure` always records that the invocation failed, with `td.metadata.failure_code` when AWS sends one.
|
|
116
|
+
- When a `mask` is set, all of this content is withheld.
|
|
117
|
+
- Withheld content and the trace structure around it keep only trace ids, step types, the foundation model, guardrail action, failure code, and the usage, timings, and `clientRequestId` from AWS's trace `metadata`.
|
|
118
|
+
|
|
119
|
+
Any other field, including unknown trace types and the AWS SDK's `$unknown` union member, is omitted even when both capture options are on; a content part that is kept is exported whole, including any fields inside it that are not listed here. Errors are not covered by this filtering: AWS exceptions, including errors raised inside the stream, still record the provider's error message on the span.
|
|
120
|
+
|
|
121
|
+
Output file names in `td.metadata.output_files` follow `captureOutput` the same way, and are withheld when a `mask` is set; file types are always kept.
|
|
104
122
|
|
|
105
123
|
## Signal coverage
|
|
106
124
|
|
|
@@ -138,8 +156,11 @@ InvokeModel captures provider-native JSON bodies verbatim. Non-JSON bodies are n
|
|
|
138
156
|
- Instrumentation is fail-open. Normalization, span updates, and span endings are guarded so telemetry failures do not break caller code.
|
|
139
157
|
- Requests are never modified. This is especially important for AWS SigV4 signing and for application-level request object reuse.
|
|
140
158
|
- Streams are pull-through. The wrapper does not pre-buffer chunks and therefore preserves backpressure.
|
|
141
|
-
- Streams end spans exactly once. Exhaustion records full output and finish reason; early `break`, `return()`, `close()`, abort, or stream errors record partial output.
|
|
159
|
+
- Streams end spans exactly once. Exhaustion records full output and finish reason; early `break`, `return()`, `close()`, abort, or stream errors record partial output and set `telemetry.dev.capture.truncated`.
|
|
160
|
+
- Stream reconstruction (output, Agent trace events, and Agent output-file metadata) retains up to 48 KiB and 1,000 items; events past the bound still pass through to the caller. The configured `maxAttributeLength` does not shrink this bound; the core applies it to the exported attribute after the mask runs.
|
|
161
|
+
- A stream cut at the bound, stopped early, or failed sets `telemetry.dev.capture.truncated`. With a mask configured, its incomplete reconstructed output is omitted because the mask cannot inspect the complete value. Agent trace events and output-file metadata share the bound, but dropping them only sets the attribute; Agent text is omitted only when a text chunk was dropped or the stream stopped early or failed. A normally exhausted Agent stream that returned control keeps its complete `returnControl` output even if earlier text or metadata passed the bound; an early stop or failure still omits the output when a mask is configured.
|
|
142
162
|
- `AbortSignal` passed to `send(command, { abortSignal })` is forwarded untouched. Pre-response aborts end the span with the thrown abort error. Mid-stream aborts end the span with partial output and the stream error.
|
|
163
|
+
- `Rerank` input and results are bounded to 48 KiB and 1,000 items. A value over the bound sets `telemetry.dev.capture.truncated`. Without a mask, the bounded part is kept. With a mask configured, that value is omitted because the mask cannot inspect the complete value. Input is omitted before the span starts, so a custom `sampler` sees the bounded input when no mask is configured; with an older `@telemetry-dev/sdk` that cannot report whether a mask is configured, cut input is omitted as if one were.
|
|
143
164
|
- Callback-style `send(command, callback)` and `send(command, options, callback)` calls are instrumented; callbacks receive the original data/error while the span is ended before callback invocation.
|
|
144
165
|
|
|
145
166
|
## Coverage gaps
|
|
@@ -156,3 +177,7 @@ The following calls pass through unless a supported operation above is used:
|
|
|
156
177
|
- `AgenticRetrieveStream`
|
|
157
178
|
|
|
158
179
|
Provider-native `InvokeModel` usage is best-effort. Anthropic Claude, Amazon Titan/Nova, Meta Llama, and Titan embeddings expose known token fields. Cohere and Mistral text models do not consistently include usage in the JSON body; spans still capture request/response bodies and finish reasons where present.
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT. See [LICENSE](https://github.com/telemetry-dev/telemetry/blob/main/packages/bedrock/LICENSE).
|
package/dist/agents.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as createInstrumentedSend, t as AGENT_HANDLERS } from "./internal-handlers-
|
|
1
|
+
import { r as createInstrumentedSend, t as AGENT_HANDLERS } from "./internal-handlers-CLP2wXDh.mjs";
|
|
2
2
|
import { BedrockAgentRuntimeClient } from "@aws-sdk/client-bedrock-agent-runtime";
|
|
3
3
|
//#region src/agents.ts
|
|
4
4
|
const WRAPPED = /* @__PURE__ */ new WeakSet();
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as RUNTIME_HANDLERS, r as createInstrumentedSend } from "./internal-handlers-
|
|
1
|
+
import { n as RUNTIME_HANDLERS, r as createInstrumentedSend } from "./internal-handlers-CLP2wXDh.mjs";
|
|
2
2
|
import { BedrockRuntimeClient } from "@aws-sdk/client-bedrock-runtime";
|
|
3
3
|
//#region src/index.ts
|
|
4
4
|
const WRAPPED = /* @__PURE__ */ new WeakSet();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as telemetrySdk from "@telemetry-dev/sdk";
|
|
2
|
+
import { boundedCapture, boundedCaptureDetails, captureEnabled, startSpan } from "@telemetry-dev/sdk";
|
|
2
3
|
//#region src/internal.ts
|
|
3
4
|
const PROVIDER = "amazon-bedrock";
|
|
4
5
|
const STREAM_FINALIZER = typeof FinalizationRegistry === "function" ? new FinalizationRegistry((finish) => finish()) : void 0;
|
|
@@ -12,7 +13,7 @@ function createInstrumentedSend(originalSend, options, handlers) {
|
|
|
12
13
|
const input = commandInput(rawCommand);
|
|
13
14
|
const t0 = performance.now();
|
|
14
15
|
const spanType = safe(() => handler.spanType(input)) ?? "span";
|
|
15
|
-
const span =
|
|
16
|
+
const span = startRequestSpan(safe(() => handler.spanName(input)) ?? name ?? "bedrock", {
|
|
16
17
|
type: spanType,
|
|
17
18
|
...safe(() => handler.requestFields(input, options)) ?? {}
|
|
18
19
|
});
|
|
@@ -103,6 +104,33 @@ function withCacheInclusiveInput(fields) {
|
|
|
103
104
|
}
|
|
104
105
|
};
|
|
105
106
|
}
|
|
107
|
+
function capturesWithMask(span) {
|
|
108
|
+
return span.capturePolicy?.mask ?? true;
|
|
109
|
+
}
|
|
110
|
+
function isCut(fields) {
|
|
111
|
+
return fields?.attributes?.["telemetry.dev.capture.truncated"] === true;
|
|
112
|
+
}
|
|
113
|
+
function captureMaskedBeforeStart() {
|
|
114
|
+
const read = telemetrySdk.captureMasked;
|
|
115
|
+
try {
|
|
116
|
+
return typeof read === "function" ? read() : true;
|
|
117
|
+
} catch {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function startRequestSpan(name, options) {
|
|
122
|
+
return startSpan(name, options.input !== void 0 && isCut(options) && captureMaskedBeforeStart() ? {
|
|
123
|
+
...options,
|
|
124
|
+
input: void 0
|
|
125
|
+
} : options);
|
|
126
|
+
}
|
|
127
|
+
/** Ends a non-streamed span, withholding output cut to the retention bound from a mask. */
|
|
128
|
+
function endSpanWithBoundedOutput(span, fields) {
|
|
129
|
+
endSpan(span, fields.output !== void 0 && isCut(fields) && capturesWithMask(span) ? {
|
|
130
|
+
...fields,
|
|
131
|
+
output: void 0
|
|
132
|
+
} : fields);
|
|
133
|
+
}
|
|
106
134
|
function endSpan(span, fields) {
|
|
107
135
|
try {
|
|
108
136
|
span.end(withCacheInclusiveInput(fields));
|
|
@@ -209,6 +237,46 @@ function parseJson(value) {
|
|
|
209
237
|
return;
|
|
210
238
|
}
|
|
211
239
|
}
|
|
240
|
+
const STREAM_CAPTURE_MAX_BYTES = 48 * 1024;
|
|
241
|
+
const STREAM_CAPTURE_MAX_ITEMS = 1e3;
|
|
242
|
+
const CAPTURE_TRUNCATED_ATTRIBUTE = "telemetry.dev.capture.truncated";
|
|
243
|
+
const LEGACY_CAPTURE_POLICY = {
|
|
244
|
+
output: true,
|
|
245
|
+
mask: true,
|
|
246
|
+
maxAttributeLength: 65536
|
|
247
|
+
};
|
|
248
|
+
/** Tracks the fixed byte and item bound before a stream state retains a value. */
|
|
249
|
+
var StreamRetention = class {
|
|
250
|
+
bytes = 0;
|
|
251
|
+
items = 0;
|
|
252
|
+
truncated = false;
|
|
253
|
+
accept(value) {
|
|
254
|
+
if (this.truncated) return false;
|
|
255
|
+
const measured = boundedCaptureDetails(value, {
|
|
256
|
+
maxBytes: STREAM_CAPTURE_MAX_BYTES - this.bytes,
|
|
257
|
+
maxItems: STREAM_CAPTURE_MAX_ITEMS - this.items
|
|
258
|
+
});
|
|
259
|
+
if (measured.truncated) {
|
|
260
|
+
this.truncated = true;
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
this.bytes += measured.bytes;
|
|
264
|
+
this.items += measured.items;
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
function finishStream(state, span, partial) {
|
|
269
|
+
const fields = state.finish(partial);
|
|
270
|
+
const interrupted = partial || fields.error !== void 0;
|
|
271
|
+
if (!interrupted && !state.retention.truncated) return fields;
|
|
272
|
+
fields.attributes = {
|
|
273
|
+
...fields.attributes,
|
|
274
|
+
[CAPTURE_TRUNCATED_ATTRIBUTE]: true
|
|
275
|
+
};
|
|
276
|
+
const outputTruncated = state.outputTruncated?.() ?? state.retention.truncated;
|
|
277
|
+
if ((span.capturePolicy ?? LEGACY_CAPTURE_POLICY).mask && (interrupted || outputTruncated)) delete fields.output;
|
|
278
|
+
return fields;
|
|
279
|
+
}
|
|
212
280
|
function wrapAsyncIterable(iterable, state, span, t0, baseFields, trackOutputChunks = true) {
|
|
213
281
|
if (!iterable || !isAsyncIterable(iterable)) {
|
|
214
282
|
endSpan(span, baseFields);
|
|
@@ -238,15 +306,15 @@ function wrapAsyncIterable(iterable, state, span, t0, baseFields, trackOutputChu
|
|
|
238
306
|
}
|
|
239
307
|
completed = true;
|
|
240
308
|
} catch (error) {
|
|
241
|
-
endOnce(mergeFields(state
|
|
309
|
+
endOnce(mergeFields(finishStream(state, span, true), awsMetadataFields(metadataOf(error)), { error: asError(error) }));
|
|
242
310
|
throw error;
|
|
243
311
|
} finally {
|
|
244
|
-
if (!ended) endOnce(state
|
|
312
|
+
if (!ended) endOnce(finishStream(state, span, !completed));
|
|
245
313
|
}
|
|
246
314
|
};
|
|
247
315
|
const wrapped = {};
|
|
248
316
|
if (isRecord(iterable)) for (const key of Reflect.ownKeys(iterable)) wrapped[key] = iterable[key];
|
|
249
|
-
const finishAbandoned = () => endOnce(safe(() => state
|
|
317
|
+
const finishAbandoned = () => endOnce(safe(() => finishStream(state, span, true)));
|
|
250
318
|
wrapped[Symbol.asyncIterator] = () => {
|
|
251
319
|
const iterator = generator();
|
|
252
320
|
STREAM_FINALIZER?.register(iterator, finishAbandoned, unregisterToken);
|
|
@@ -340,17 +408,27 @@ function inlineAgentRequestFields(input) {
|
|
|
340
408
|
});
|
|
341
409
|
}
|
|
342
410
|
var AgentStreamState = class {
|
|
411
|
+
retention = new StreamRetention();
|
|
343
412
|
text = "";
|
|
413
|
+
textRejected = false;
|
|
344
414
|
usage = {};
|
|
345
415
|
traceEventCount = 0;
|
|
416
|
+
failed = false;
|
|
417
|
+
failureCode;
|
|
346
418
|
failureReason;
|
|
347
419
|
guardrailAction;
|
|
348
420
|
returnControl;
|
|
421
|
+
sawReturnControl = false;
|
|
422
|
+
returnControlRejected = false;
|
|
349
423
|
traces = [];
|
|
350
424
|
error;
|
|
351
425
|
errorFields;
|
|
352
426
|
citationCount = 0;
|
|
353
427
|
outputFiles = [];
|
|
428
|
+
contentCapture = {
|
|
429
|
+
input: captureEnabled("input") && !captureMaskedBeforeStart(),
|
|
430
|
+
output: captureEnabled("output") && !captureMaskedBeforeStart()
|
|
431
|
+
};
|
|
354
432
|
constructor(options) {
|
|
355
433
|
this.options = options;
|
|
356
434
|
}
|
|
@@ -365,30 +443,58 @@ var AgentStreamState = class {
|
|
|
365
443
|
}
|
|
366
444
|
const chunk = isRecord(raw.chunk) ? raw.chunk : void 0;
|
|
367
445
|
const bytes = chunk?.bytes;
|
|
368
|
-
|
|
446
|
+
const piece = bytes === void 0 ? void 0 : bytesToString(bytes);
|
|
447
|
+
if (piece) if (this.retention.accept(piece)) this.text += piece;
|
|
448
|
+
else this.textRejected = true;
|
|
369
449
|
const citations = arrayValue((isRecord(chunk?.attribution) ? chunk.attribution : void 0)?.citations);
|
|
370
450
|
if (citations) this.citationCount += citations.length;
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
451
|
+
const sanitizedFiles = (isRecord(raw.files) ? arrayValue(raw.files.files) : void 0)?.map((file) => sanitizeOutputFile(file, this.contentCapture.output));
|
|
452
|
+
if (sanitizedFiles && this.retention.accept(sanitizedFiles)) this.outputFiles.push(...sanitizedFiles);
|
|
373
453
|
const returnControl = isRecord(raw.returnControl) ? raw.returnControl : void 0;
|
|
374
|
-
if (returnControl)
|
|
454
|
+
if (returnControl) {
|
|
455
|
+
this.sawReturnControl = true;
|
|
456
|
+
if (new StreamRetention().accept(returnControl)) this.returnControl = returnControl;
|
|
457
|
+
else {
|
|
458
|
+
this.returnControl = void 0;
|
|
459
|
+
this.returnControlRejected = true;
|
|
460
|
+
this.retention.truncated = true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
375
463
|
const traceEvent = isRecord(raw.trace) ? raw.trace : void 0;
|
|
376
464
|
if (traceEvent) {
|
|
377
465
|
this.traceEventCount += 1;
|
|
378
|
-
if (this.options.captureAgentTrace)
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const
|
|
466
|
+
if (this.options.captureAgentTrace) {
|
|
467
|
+
const retained = projectTrace(traceEvent, TRACE_PART, this.contentCapture);
|
|
468
|
+
if (this.retention.accept(retained)) this.traces.push(retained);
|
|
469
|
+
}
|
|
470
|
+
const trace = isRecord(traceEvent.trace) ? traceEvent.trace : {};
|
|
471
|
+
for (const step of MODEL_INVOCATION_STEPS) {
|
|
472
|
+
const member = isRecord(trace[step]) ? trace[step] : void 0;
|
|
473
|
+
const output = isRecord(member?.modelInvocationOutput) ? member.modelInvocationOutput : {};
|
|
474
|
+
this.addUsage(isRecord(output.metadata) ? output.metadata.usage : void 0);
|
|
475
|
+
}
|
|
476
|
+
const failure = trace.failureTrace;
|
|
477
|
+
if (isRecord(failure)) {
|
|
478
|
+
this.failed = true;
|
|
479
|
+
this.failureCode = numberValue(failure.failureCode) ?? this.failureCode;
|
|
480
|
+
if (this.contentCapture.input && this.contentCapture.output) this.failureReason = stringValue(failure.failureReason) ?? this.failureReason;
|
|
481
|
+
}
|
|
482
|
+
const guardrail = trace.guardrailTrace;
|
|
383
483
|
if (isRecord(guardrail)) this.guardrailAction = stringValue(guardrail.action) ?? this.guardrailAction;
|
|
384
484
|
}
|
|
385
485
|
}
|
|
486
|
+
outputTruncated() {
|
|
487
|
+
if (this.returnControl !== void 0) return false;
|
|
488
|
+
return this.textRejected || this.returnControlRejected;
|
|
489
|
+
}
|
|
386
490
|
finish() {
|
|
387
491
|
const metadata = omitUndefined({
|
|
388
492
|
trace_event_count: this.traceEventCount || void 0,
|
|
493
|
+
failure: this.failed ? true : void 0,
|
|
494
|
+
failure_code: this.failureCode,
|
|
389
495
|
failure_reason: this.failureReason,
|
|
390
496
|
guardrail_action: this.guardrailAction,
|
|
391
|
-
return_control: this.
|
|
497
|
+
return_control: this.sawReturnControl ? true : void 0,
|
|
392
498
|
citation_count: this.citationCount || void 0,
|
|
393
499
|
output_files: this.outputFiles.length > 0 ? this.outputFiles : void 0,
|
|
394
500
|
agent_trace: this.options.captureAgentTrace && this.traces.length > 0 ? this.traces : void 0
|
|
@@ -418,11 +524,11 @@ var AgentStreamState = class {
|
|
|
418
524
|
else if (inputTokens !== void 0 || outputTokens !== void 0) this.usage.totalTokens = (this.usage.inputTokens ?? 0) + (this.usage.outputTokens ?? 0);
|
|
419
525
|
}
|
|
420
526
|
};
|
|
421
|
-
function sanitizeOutputFile(value) {
|
|
527
|
+
function sanitizeOutputFile(value, keepName) {
|
|
422
528
|
const raw = value;
|
|
423
529
|
if (!isRecord(raw)) return void 0;
|
|
424
530
|
return omitUndefined({
|
|
425
|
-
name: stringValue(raw.name),
|
|
531
|
+
name: keepName ? stringValue(raw.name) : void 0,
|
|
426
532
|
type: stringValue(raw.type)
|
|
427
533
|
});
|
|
428
534
|
}
|
|
@@ -533,6 +639,7 @@ function ragResponseFields(output) {
|
|
|
533
639
|
});
|
|
534
640
|
}
|
|
535
641
|
var RagStreamState = class {
|
|
642
|
+
retention = new StreamRetention();
|
|
536
643
|
text = "";
|
|
537
644
|
citationCount = 0;
|
|
538
645
|
guardrailAction;
|
|
@@ -547,8 +654,8 @@ var RagStreamState = class {
|
|
|
547
654
|
this.errorFields = streamError.fields;
|
|
548
655
|
return;
|
|
549
656
|
}
|
|
550
|
-
const
|
|
551
|
-
|
|
657
|
+
const text = stringValue((isRecord(raw.output) ? raw.output : void 0)?.text);
|
|
658
|
+
if (text && this.retention.accept(text)) this.text += text;
|
|
552
659
|
if (raw.citation) this.citationCount += 1;
|
|
553
660
|
const guardrail = isRecord(raw.guardrail) ? raw.guardrail : void 0;
|
|
554
661
|
this.guardrailAction = stringValue(guardrail?.action) ?? this.guardrailAction;
|
|
@@ -578,6 +685,7 @@ function flowRequestFields(input) {
|
|
|
578
685
|
});
|
|
579
686
|
}
|
|
580
687
|
var FlowStreamState = class {
|
|
688
|
+
retention = new StreamRetention();
|
|
581
689
|
outputs = [];
|
|
582
690
|
completionReason;
|
|
583
691
|
error;
|
|
@@ -592,9 +700,9 @@ var FlowStreamState = class {
|
|
|
592
700
|
return;
|
|
593
701
|
}
|
|
594
702
|
const output = isRecord(raw.flowOutputEvent) ? raw.flowOutputEvent : void 0;
|
|
595
|
-
if (output) this.outputs.push(output.content);
|
|
703
|
+
if (output && this.retention.accept(output.content)) this.outputs.push(output.content);
|
|
596
704
|
const inputRequest = isRecord(raw.flowMultiTurnInputRequestEvent) ? raw.flowMultiTurnInputRequestEvent : void 0;
|
|
597
|
-
if (inputRequest) this.outputs.push(inputRequest.content);
|
|
705
|
+
if (inputRequest && this.retention.accept(inputRequest.content)) this.outputs.push(inputRequest.content);
|
|
598
706
|
const completion = isRecord(raw.flowCompletionEvent) ? raw.flowCompletionEvent : void 0;
|
|
599
707
|
this.completionReason = stringValue(completion?.completionReason) ?? this.completionReason;
|
|
600
708
|
}
|
|
@@ -612,22 +720,111 @@ function flowName(input) {
|
|
|
612
720
|
function ragName(input) {
|
|
613
721
|
return `retrieve_and_generate ${basenameArn(ragModel(input)) ?? "knowledge-base"}`;
|
|
614
722
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
723
|
+
/** Content kept as received when every listed capture is on; otherwise reduced to `withheld`. */
|
|
724
|
+
var TraceContent = class {
|
|
725
|
+
constructor(needs, withheld) {
|
|
726
|
+
this.needs = needs;
|
|
727
|
+
this.withheld = withheld;
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
const SCALAR = "scalar";
|
|
731
|
+
const TRACE_METADATA = {
|
|
732
|
+
usage: {
|
|
733
|
+
inputTokens: SCALAR,
|
|
734
|
+
outputTokens: SCALAR
|
|
735
|
+
},
|
|
736
|
+
startTime: SCALAR,
|
|
737
|
+
endTime: SCALAR,
|
|
738
|
+
totalTimeMs: SCALAR,
|
|
739
|
+
operationTotalTimeMs: SCALAR,
|
|
740
|
+
clientRequestId: SCALAR
|
|
741
|
+
};
|
|
742
|
+
const MODEL_INVOCATION_INPUT = new TraceContent(["input"], {
|
|
743
|
+
traceId: SCALAR,
|
|
744
|
+
type: SCALAR,
|
|
745
|
+
foundationModel: SCALAR
|
|
746
|
+
});
|
|
747
|
+
const INVOCATION_INPUT = new TraceContent(["input"], {
|
|
748
|
+
traceId: SCALAR,
|
|
749
|
+
invocationType: SCALAR
|
|
750
|
+
});
|
|
751
|
+
const MODEL_INVOCATION_OUTPUT = new TraceContent(["output"], {
|
|
752
|
+
traceId: SCALAR,
|
|
753
|
+
metadata: TRACE_METADATA
|
|
754
|
+
});
|
|
755
|
+
const OBSERVATION = new TraceContent(["output"], {
|
|
756
|
+
traceId: SCALAR,
|
|
757
|
+
type: SCALAR
|
|
758
|
+
});
|
|
759
|
+
const MODEL_INVOCATION_STEPS = [
|
|
760
|
+
"preProcessingTrace",
|
|
761
|
+
"orchestrationTrace",
|
|
762
|
+
"postProcessingTrace",
|
|
763
|
+
"routingClassifierTrace"
|
|
764
|
+
];
|
|
765
|
+
const PROCESSING_TRACE = {
|
|
766
|
+
modelInvocationInput: MODEL_INVOCATION_INPUT,
|
|
767
|
+
modelInvocationOutput: MODEL_INVOCATION_OUTPUT
|
|
768
|
+
};
|
|
769
|
+
const TRACE_PART = {
|
|
770
|
+
agentId: SCALAR,
|
|
771
|
+
agentAliasId: SCALAR,
|
|
772
|
+
agentVersion: SCALAR,
|
|
773
|
+
sessionId: SCALAR,
|
|
774
|
+
collaboratorName: SCALAR,
|
|
775
|
+
eventTime: SCALAR,
|
|
776
|
+
callerChain: [{ agentAliasArn: SCALAR }],
|
|
777
|
+
trace: {
|
|
778
|
+
preProcessingTrace: PROCESSING_TRACE,
|
|
779
|
+
postProcessingTrace: PROCESSING_TRACE,
|
|
780
|
+
orchestrationTrace: {
|
|
781
|
+
modelInvocationInput: MODEL_INVOCATION_INPUT,
|
|
782
|
+
invocationInput: INVOCATION_INPUT,
|
|
783
|
+
modelInvocationOutput: MODEL_INVOCATION_OUTPUT,
|
|
784
|
+
rationale: new TraceContent(["output"], { traceId: SCALAR }),
|
|
785
|
+
observation: OBSERVATION
|
|
786
|
+
},
|
|
787
|
+
routingClassifierTrace: {
|
|
788
|
+
modelInvocationInput: MODEL_INVOCATION_INPUT,
|
|
789
|
+
invocationInput: INVOCATION_INPUT,
|
|
790
|
+
modelInvocationOutput: MODEL_INVOCATION_OUTPUT,
|
|
791
|
+
observation: OBSERVATION
|
|
792
|
+
},
|
|
793
|
+
customOrchestrationTrace: {
|
|
794
|
+
traceId: SCALAR,
|
|
795
|
+
event: new TraceContent(["output"])
|
|
796
|
+
},
|
|
797
|
+
guardrailTrace: {
|
|
798
|
+
traceId: SCALAR,
|
|
799
|
+
action: SCALAR,
|
|
800
|
+
metadata: TRACE_METADATA,
|
|
801
|
+
inputAssessments: new TraceContent(["input"]),
|
|
802
|
+
outputAssessments: new TraceContent(["output"])
|
|
803
|
+
},
|
|
804
|
+
failureTrace: {
|
|
805
|
+
traceId: SCALAR,
|
|
806
|
+
failureCode: SCALAR,
|
|
807
|
+
metadata: TRACE_METADATA,
|
|
808
|
+
failureReason: new TraceContent(["input", "output"])
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
function projectTrace(value, shape, capture) {
|
|
813
|
+
if (shape instanceof TraceContent) {
|
|
814
|
+
if (shape.needs.every((kind) => capture[kind])) return value ?? void 0;
|
|
815
|
+
return shape.withheld === void 0 ? void 0 : projectTrace(value, shape.withheld, capture);
|
|
816
|
+
}
|
|
817
|
+
if (shape === SCALAR) return [
|
|
818
|
+
"string",
|
|
819
|
+
"number",
|
|
820
|
+
"boolean"
|
|
821
|
+
].includes(typeof value) || value instanceof Date ? value : void 0;
|
|
822
|
+
if (Array.isArray(shape)) {
|
|
823
|
+
if (!Array.isArray(value)) return void 0;
|
|
824
|
+
return value.map((item) => projectTrace(item, shape[0], capture)).filter((item) => item !== void 0);
|
|
630
825
|
}
|
|
826
|
+
if (!isRecord(value)) return void 0;
|
|
827
|
+
return omitUndefined(Object.fromEntries(Object.entries(shape).map(([field, child]) => [field, projectTrace(value[field], child, capture)])));
|
|
631
828
|
}
|
|
632
829
|
//#endregion
|
|
633
830
|
//#region src/converse.ts
|
|
@@ -801,6 +998,7 @@ function s3Uri(value) {
|
|
|
801
998
|
return bucket && key ? `s3://${bucket}/${key}` : void 0;
|
|
802
999
|
}
|
|
803
1000
|
var ConverseStreamState = class {
|
|
1001
|
+
retention = new StreamRetention();
|
|
804
1002
|
role = "assistant";
|
|
805
1003
|
blocks = /* @__PURE__ */ new Map();
|
|
806
1004
|
stopReason;
|
|
@@ -820,7 +1018,7 @@ var ConverseStreamState = class {
|
|
|
820
1018
|
const messageStart = isRecord(event.messageStart) ? event.messageStart : void 0;
|
|
821
1019
|
if (messageStart) this.role = stringValue(messageStart.role) ?? this.role;
|
|
822
1020
|
const blockStart = isRecord(event.contentBlockStart) ? event.contentBlockStart : void 0;
|
|
823
|
-
if (blockStart) {
|
|
1021
|
+
if (blockStart && this.retention.accept(blockStart.start ?? {})) {
|
|
824
1022
|
const index = numberValue(blockStart.contentBlockIndex) ?? this.blocks.size;
|
|
825
1023
|
const start = isRecord(blockStart.start) ? blockStart.start : void 0;
|
|
826
1024
|
const toolUse = isRecord(start?.toolUse) ? start?.toolUse : void 0;
|
|
@@ -847,9 +1045,9 @@ var ConverseStreamState = class {
|
|
|
847
1045
|
});
|
|
848
1046
|
}
|
|
849
1047
|
const deltaEvent = isRecord(event.contentBlockDelta) ? event.contentBlockDelta : void 0;
|
|
850
|
-
|
|
1048
|
+
const delta = isRecord(deltaEvent?.delta) ? deltaEvent.delta : {};
|
|
1049
|
+
if (deltaEvent && this.retention.accept(delta)) {
|
|
851
1050
|
const index = numberValue(deltaEvent.contentBlockIndex) ?? 0;
|
|
852
|
-
const delta = isRecord(deltaEvent.delta) ? deltaEvent.delta : {};
|
|
853
1051
|
const block = this.blocks.get(index) ?? {
|
|
854
1052
|
kind: "text",
|
|
855
1053
|
text: ""
|
|
@@ -1058,12 +1256,7 @@ function nativeResponseFields(model, body, metadata) {
|
|
|
1058
1256
|
return withHeaderUsage({
|
|
1059
1257
|
output: body,
|
|
1060
1258
|
finishReason: stringValue(body.stop_reason),
|
|
1061
|
-
usage
|
|
1062
|
-
inputTokens: numberValue(usage.input_tokens),
|
|
1063
|
-
outputTokens: numberValue(usage.output_tokens),
|
|
1064
|
-
cacheReadInputTokens: numberValue(usage.cache_read_input_tokens),
|
|
1065
|
-
cacheCreationInputTokens: numberValue(usage.cache_creation_input_tokens)
|
|
1066
|
-
})
|
|
1259
|
+
...claudeUsageFields(usage)
|
|
1067
1260
|
}, metadata);
|
|
1068
1261
|
}
|
|
1069
1262
|
if (lower.includes("meta.llama")) return omitUndefined({
|
|
@@ -1088,6 +1281,35 @@ function nativeResponseFields(model, body, metadata) {
|
|
|
1088
1281
|
}, metadata);
|
|
1089
1282
|
return withHeaderUsage({ output: body }, metadata);
|
|
1090
1283
|
}
|
|
1284
|
+
const CLAUDE_USAGE_KEYS = {
|
|
1285
|
+
inputTokens: "input_tokens",
|
|
1286
|
+
outputTokens: "output_tokens",
|
|
1287
|
+
cacheReadInputTokens: "cache_read_input_tokens",
|
|
1288
|
+
cacheCreationInputTokens: "cache_creation_input_tokens"
|
|
1289
|
+
};
|
|
1290
|
+
function claudeUsageFields(record) {
|
|
1291
|
+
const usage = {};
|
|
1292
|
+
for (const [key, source] of Object.entries(CLAUDE_USAGE_KEYS)) {
|
|
1293
|
+
const value = numberValue(record[source]);
|
|
1294
|
+
if (value !== void 0) usage[key] = value;
|
|
1295
|
+
}
|
|
1296
|
+
const iterations = record.iterations;
|
|
1297
|
+
if (!Array.isArray(iterations)) return { usage };
|
|
1298
|
+
const first = Math.max(0, iterations.length - STREAM_CAPTURE_MAX_ITEMS);
|
|
1299
|
+
for (let index = first; index < iterations.length; index += 1) {
|
|
1300
|
+
const entry = iterations[index];
|
|
1301
|
+
if (!isRecord(entry) || entry.type !== "compaction") continue;
|
|
1302
|
+
for (const [key, source] of Object.entries(CLAUDE_USAGE_KEYS)) {
|
|
1303
|
+
const value = numberValue(entry[source]);
|
|
1304
|
+
const target = key;
|
|
1305
|
+
if (value !== void 0) usage[target] = (usage[target] ?? 0) + value;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
return {
|
|
1309
|
+
usage,
|
|
1310
|
+
...first > 0 ? { attributes: { "telemetry.dev.capture.truncated": true } } : {}
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1091
1313
|
function withHeaderUsage(fields, metadata) {
|
|
1092
1314
|
if (fields.usage && Object.keys(fields.usage).length > 0) return fields;
|
|
1093
1315
|
return mergeFields(fields, headerUsageFields(metadata));
|
|
@@ -1109,9 +1331,13 @@ function headerNumber(value) {
|
|
|
1109
1331
|
return numberValue(value);
|
|
1110
1332
|
}
|
|
1111
1333
|
var InvokeModelStreamState = class {
|
|
1334
|
+
retention = new StreamRetention();
|
|
1112
1335
|
chunks = [];
|
|
1113
1336
|
text = "";
|
|
1114
1337
|
usage;
|
|
1338
|
+
finalUsage = {};
|
|
1339
|
+
startUsage = {};
|
|
1340
|
+
compaction = {};
|
|
1115
1341
|
finishReason;
|
|
1116
1342
|
error;
|
|
1117
1343
|
errorFields;
|
|
@@ -1129,28 +1355,53 @@ var InvokeModelStreamState = class {
|
|
|
1129
1355
|
const raw = chunk?.bytes;
|
|
1130
1356
|
return (typeof raw === "string" || raw instanceof Uint8Array) && raw.length > 0;
|
|
1131
1357
|
}
|
|
1132
|
-
this.
|
|
1133
|
-
|
|
1358
|
+
if (this.retention.accept(parsed)) {
|
|
1359
|
+
this.chunks.push(parsed);
|
|
1360
|
+
this.text += textFromProviderChunk(parsed);
|
|
1361
|
+
}
|
|
1134
1362
|
if (isRecord(parsed)) {
|
|
1135
1363
|
const delta = isRecord(parsed.delta) ? parsed.delta : void 0;
|
|
1136
1364
|
const message = isRecord(parsed.message) ? parsed.message : void 0;
|
|
1137
1365
|
const messageUsage = isRecord(message?.usage) ? message?.usage : void 0;
|
|
1138
1366
|
const usage = isRecord(parsed.usage) ? parsed.usage : void 0;
|
|
1367
|
+
for (const [key, source] of Object.entries(CLAUDE_USAGE_KEYS)) {
|
|
1368
|
+
const value = numberValue(usage?.[source]);
|
|
1369
|
+
if (value !== void 0) this.finalUsage[key] = value;
|
|
1370
|
+
}
|
|
1371
|
+
this.startUsage = omitUndefined({
|
|
1372
|
+
...this.startUsage,
|
|
1373
|
+
inputTokens: numberValue(messageUsage?.input_tokens) ?? this.startUsage.inputTokens,
|
|
1374
|
+
outputTokens: numberValue(messageUsage?.output_tokens) ?? this.startUsage.outputTokens,
|
|
1375
|
+
cacheReadInputTokens: numberValue(messageUsage?.cache_read_input_tokens) ?? this.startUsage.cacheReadInputTokens,
|
|
1376
|
+
cacheCreationInputTokens: numberValue(messageUsage?.cache_creation_input_tokens) ?? this.startUsage.cacheCreationInputTokens
|
|
1377
|
+
});
|
|
1378
|
+
const iterations = Array.isArray(usage?.iterations) ? usage.iterations : messageUsage?.iterations;
|
|
1379
|
+
if (Array.isArray(iterations)) this.compaction = claudeUsageFields({ iterations });
|
|
1139
1380
|
const metrics = isRecord(parsed["amazon-bedrock-invocationMetrics"]) ? parsed["amazon-bedrock-invocationMetrics"] : void 0;
|
|
1140
1381
|
const outputs = Array.isArray(parsed.outputs) && isRecord(parsed.outputs[0]) ? parsed.outputs[0] : void 0;
|
|
1141
1382
|
const generation = Array.isArray(parsed.generations) && isRecord(parsed.generations[0]) ? parsed.generations[0] : void 0;
|
|
1142
1383
|
this.finishReason = stringValue(delta?.stop_reason) ?? stringValue(parsed.stop_reason) ?? stringValue(outputs?.stop_reason) ?? stringValue(generation?.finish_reason) ?? stringValue(parsed.completionReason) ?? this.finishReason;
|
|
1143
1384
|
this.usage = omitUndefined({
|
|
1144
1385
|
...this.usage,
|
|
1145
|
-
inputTokens: numberValue(
|
|
1386
|
+
inputTokens: numberValue(usage?.input_tokens) ?? numberValue(metrics?.inputTokenCount) ?? numberValue(parsed.inputTextTokenCount) ?? this.usage?.inputTokens,
|
|
1146
1387
|
outputTokens: numberValue(usage?.output_tokens) ?? numberValue(metrics?.outputTokenCount) ?? numberValue(parsed.totalOutputTextTokenCount) ?? this.usage?.outputTokens,
|
|
1147
|
-
cacheReadInputTokens: numberValue(
|
|
1148
|
-
cacheCreationInputTokens: numberValue(
|
|
1388
|
+
cacheReadInputTokens: numberValue(usage?.cache_read_input_tokens) ?? this.usage?.cacheReadInputTokens,
|
|
1389
|
+
cacheCreationInputTokens: numberValue(usage?.cache_creation_input_tokens) ?? this.usage?.cacheCreationInputTokens
|
|
1149
1390
|
});
|
|
1150
1391
|
}
|
|
1151
1392
|
return providerChunkHasOutput(parsed);
|
|
1152
1393
|
}
|
|
1153
1394
|
finish() {
|
|
1395
|
+
const usage = {
|
|
1396
|
+
...this.startUsage,
|
|
1397
|
+
...this.usage,
|
|
1398
|
+
...this.finalUsage
|
|
1399
|
+
};
|
|
1400
|
+
for (const key of Object.keys(CLAUDE_USAGE_KEYS)) {
|
|
1401
|
+
const value = this.compaction.usage?.[key];
|
|
1402
|
+
const baseKey = key === "outputTokens" ? "outputTokens" : "inputTokens";
|
|
1403
|
+
if (value !== void 0 && (this.finalUsage[baseKey] !== void 0 || this.usage?.[baseKey] === void 0)) usage[key] = (usage[key] ?? 0) + value;
|
|
1404
|
+
}
|
|
1154
1405
|
return mergeFields(omitUndefined({
|
|
1155
1406
|
output: this.text ? [{
|
|
1156
1407
|
role: "assistant",
|
|
@@ -1161,7 +1412,8 @@ var InvokeModelStreamState = class {
|
|
|
1161
1412
|
finish_reason: this.finishReason
|
|
1162
1413
|
}] : this.chunks,
|
|
1163
1414
|
finishReason: this.finishReason,
|
|
1164
|
-
usage
|
|
1415
|
+
usage,
|
|
1416
|
+
attributes: this.compaction.attributes,
|
|
1165
1417
|
error: this.error
|
|
1166
1418
|
}), this.errorFields);
|
|
1167
1419
|
}
|
|
@@ -1278,7 +1530,7 @@ const AGENT_HANDLERS = {
|
|
|
1278
1530
|
spanType: () => "span",
|
|
1279
1531
|
requestFields: rerankRequestFields,
|
|
1280
1532
|
onResult(result, span) {
|
|
1281
|
-
|
|
1533
|
+
endSpanWithBoundedOutput(span, rerankResponseFields(result));
|
|
1282
1534
|
return result;
|
|
1283
1535
|
}
|
|
1284
1536
|
},
|