@telemetry-dev/bedrock 0.1.3 → 0.1.6

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/agents.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { r as createInstrumentedSend, t as AGENT_HANDLERS } from "./internal-handlers-CJPJo9q3.mjs";
1
+ import { r as createInstrumentedSend, t as AGENT_HANDLERS } from "./internal-handlers-B47qvHGh.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-CJPJo9q3.mjs";
1
+ import { n as RUNTIME_HANDLERS, r as createInstrumentedSend } from "./internal-handlers-B47qvHGh.mjs";
2
2
  import { BedrockRuntimeClient } from "@aws-sdk/client-bedrock-runtime";
3
3
  //#region src/index.ts
4
4
  const WRAPPED = /* @__PURE__ */ new WeakSet();
@@ -195,7 +195,7 @@ function parseJson(value) {
195
195
  return;
196
196
  }
197
197
  }
198
- function wrapAsyncIterable(iterable, state, span, t0, baseFields) {
198
+ function wrapAsyncIterable(iterable, state, span, t0, baseFields, trackOutputChunks = true) {
199
199
  if (!iterable || !isAsyncIterable(iterable)) {
200
200
  endSpan(span, baseFields);
201
201
  return iterable;
@@ -213,11 +213,13 @@ function wrapAsyncIterable(iterable, state, span, t0, baseFields) {
213
213
  let completed = false;
214
214
  try {
215
215
  for await (const event of iterable) {
216
+ const receivedAt = performance.now();
216
217
  if (!first) {
217
218
  first = true;
218
- updateSpan(span, { timeToFirstChunkMs: performance.now() - t0 });
219
+ updateSpan(span, { timeToFirstChunkMs: receivedAt - t0 });
219
220
  }
220
- safe(() => state.feed(event));
221
+ const stateHasOutput = safe(() => state.feed(event));
222
+ if (trackOutputChunks && (stateHasOutput ?? bedrockEventHasOutput(event))) safe(() => span.recordOutputChunk?.(receivedAt));
221
223
  yield event;
222
224
  }
223
225
  completed = true;
@@ -239,6 +241,43 @@ function wrapAsyncIterable(iterable, state, span, t0, baseFields) {
239
241
  STREAM_FINALIZER?.register(wrapped, finishAbandoned, unregisterToken);
240
242
  return wrapped;
241
243
  }
244
+ function bedrockEventHasOutput(event) {
245
+ const record = isRecord(event) ? event : void 0;
246
+ if (!record) return false;
247
+ const output = isRecord(record.output) ? record.output : void 0;
248
+ if (typeof output?.text === "string" && output.text.length > 0) return true;
249
+ const block = isRecord(record.contentBlockDelta) ? record.contentBlockDelta : void 0;
250
+ const delta = isRecord(block?.delta) ? block.delta : void 0;
251
+ const reasoning = isRecord(delta?.reasoningContent) ? delta.reasoningContent : void 0;
252
+ const toolUse = isRecord(delta?.toolUse) ? delta.toolUse : void 0;
253
+ if (typeof delta?.text === "string" && delta.text.length > 0 || typeof reasoning?.text === "string" && reasoning.text.length > 0 || typeof toolUse?.input === "string" && toolUse.input.length > 0) return true;
254
+ const raw = (isRecord(record.chunk) ? record.chunk : void 0)?.bytes;
255
+ if (raw === void 0) return false;
256
+ const text = typeof raw === "string" ? raw : raw instanceof Uint8Array ? new TextDecoder().decode(raw) : void 0;
257
+ if (!text) return false;
258
+ const parsed = parseJson(text);
259
+ if (parsed === void 0) return text.length > 0;
260
+ if (!isRecord(parsed)) return false;
261
+ const parsedDelta = isRecord(parsed.delta) ? parsed.delta : void 0;
262
+ const contentDelta = isRecord(parsed.contentBlockDelta ?? parsed.content_block_delta) ? parsed.contentBlockDelta ?? parsed.content_block_delta : void 0;
263
+ const nested = isRecord(contentDelta) && isRecord(contentDelta.delta) ? contentDelta.delta : void 0;
264
+ const generations = Array.isArray(parsed.generations) ? parsed.generations : [];
265
+ const generation = isRecord(generations[0]) ? generations[0] : void 0;
266
+ const outputs = Array.isArray(parsed.outputs) ? parsed.outputs : [];
267
+ const firstOutput = isRecord(outputs[0]) ? outputs[0] : void 0;
268
+ return [
269
+ parsed.outputText,
270
+ parsed.generation,
271
+ parsed.completion,
272
+ parsed.text,
273
+ parsedDelta?.text,
274
+ parsedDelta?.thinking,
275
+ parsedDelta?.partial_json,
276
+ nested?.text,
277
+ firstOutput?.text,
278
+ generation?.text
279
+ ].some((value) => typeof value === "string" && value.length > 0);
280
+ }
242
281
  function isAsyncIterable(value) {
243
282
  return Object(value) === value && Symbol.asyncIterator in Object(value);
244
283
  }
@@ -1016,15 +1055,19 @@ var InvokeModelStreamState = class {
1016
1055
  error;
1017
1056
  errorFields;
1018
1057
  feed(event) {
1019
- if (!isRecord(event)) return;
1058
+ if (!isRecord(event)) return false;
1020
1059
  const streamError = modeledStreamError(event);
1021
1060
  if (streamError) {
1022
1061
  this.error = streamError.error;
1023
1062
  this.errorFields = streamError.fields;
1024
- return;
1063
+ return false;
1064
+ }
1065
+ const chunk = isRecord(event.chunk) ? event.chunk : void 0;
1066
+ const parsed = parseJson(chunk?.bytes);
1067
+ if (parsed === void 0) {
1068
+ const raw = chunk?.bytes;
1069
+ return (typeof raw === "string" || raw instanceof Uint8Array) && raw.length > 0;
1025
1070
  }
1026
- const parsed = parseJson((isRecord(event.chunk) ? event.chunk : void 0)?.bytes);
1027
- if (parsed === void 0) return;
1028
1071
  this.chunks.push(parsed);
1029
1072
  this.text += textFromProviderChunk(parsed);
1030
1073
  if (isRecord(parsed)) {
@@ -1044,6 +1087,7 @@ var InvokeModelStreamState = class {
1044
1087
  cacheCreationInputTokens: numberValue(messageUsage?.cache_creation_input_tokens) ?? this.usage?.cacheCreationInputTokens
1045
1088
  });
1046
1089
  }
1090
+ return providerChunkHasOutput(parsed);
1047
1091
  }
1048
1092
  finish() {
1049
1093
  return mergeFields(omitUndefined({
@@ -1069,6 +1113,11 @@ function textFromProviderChunk(value) {
1069
1113
  const generation = Array.isArray(value.generations) && isRecord(value.generations[0]) ? value.generations[0] : void 0;
1070
1114
  return stringValue(value.outputText) ?? stringValue(value.generation) ?? stringValue(value.completion) ?? stringValue(value.text) ?? stringValue(delta?.text) ?? stringValue(nestedDelta?.text) ?? (Array.isArray(value.outputs) && isRecord(value.outputs[0]) ? stringValue(value.outputs[0].text) : void 0) ?? stringValue(generation?.text) ?? "";
1071
1115
  }
1116
+ function providerChunkHasOutput(value) {
1117
+ if (!isRecord(value)) return false;
1118
+ const delta = isRecord(value.delta) ? value.delta : void 0;
1119
+ return textFromProviderChunk(value).length > 0 || typeof delta?.thinking === "string" && delta.thinking.length > 0 || typeof delta?.partial_json === "string" && delta.partial_json.length > 0;
1120
+ }
1072
1121
  //#endregion
1073
1122
  //#region src/internal-handlers.ts
1074
1123
  const RUNTIME_HANDLERS = {
@@ -1194,7 +1243,7 @@ const AGENT_HANDLERS = {
1194
1243
  }
1195
1244
  return {
1196
1245
  ...response,
1197
- stream: wrapAsyncIterable(response.stream, new RagStreamState(), span, t0, base)
1246
+ stream: wrapAsyncIterable(response.stream, new RagStreamState(), span, t0, base, false)
1198
1247
  };
1199
1248
  }
1200
1249
  },
@@ -1210,7 +1259,7 @@ const AGENT_HANDLERS = {
1210
1259
  }
1211
1260
  return {
1212
1261
  ...response,
1213
- responseStream: wrapAsyncIterable(response.responseStream, new FlowStreamState(), span, t0, awsMetadataFields(response.$metadata))
1262
+ responseStream: wrapAsyncIterable(response.responseStream, new FlowStreamState(), span, t0, awsMetadataFields(response.$metadata), false)
1214
1263
  };
1215
1264
  }
1216
1265
  }
@@ -1228,7 +1277,7 @@ function streamAgentResult(key) {
1228
1277
  }
1229
1278
  return {
1230
1279
  ...response,
1231
- [key]: wrapAsyncIterable(response[key], new AgentStreamState(options), span, t0, base)
1280
+ [key]: wrapAsyncIterable(response[key], new AgentStreamState(options), span, t0, base, false)
1232
1281
  };
1233
1282
  };
1234
1283
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telemetry-dev/bedrock",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "AWS Bedrock instrumentation for telemetry.dev: Converse, InvokeModel, and Bedrock Agents spans for AWS SDK v3 clients.",
5
5
  "keywords": [
6
6
  "aws",
@@ -16,7 +16,7 @@
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+https://github.com/telemetry-dev/sdks.git",
19
+ "url": "git+https://github.com/telemetry-dev/telemetry.git",
20
20
  "directory": "packages/bedrock"
21
21
  },
22
22
  "files": [
@@ -41,15 +41,15 @@
41
41
  "access": "public"
42
42
  },
43
43
  "devDependencies": {
44
- "@aws-sdk/client-bedrock-agent-runtime": "^3.1000.0",
45
- "@aws-sdk/client-bedrock-runtime": "^3.1000.0",
44
+ "@aws-sdk/client-bedrock-agent-runtime": "3.1135.0",
45
+ "@aws-sdk/client-bedrock-runtime": "3.1135.0",
46
46
  "@opentelemetry/api": "^1.9.1",
47
47
  "@opentelemetry/sdk-logs": "^0.218.0",
48
48
  "@opentelemetry/sdk-trace-base": "^2.7.1",
49
49
  "@smithy/eventstream-codec": "^4.2.5",
50
50
  "@smithy/protocol-http": "^5.3.10",
51
51
  "@smithy/types": "^4.12.0",
52
- "@telemetry-dev/sdk": "^0.1.3",
52
+ "@telemetry-dev/sdk": "^0.1.5",
53
53
  "@types/node": "^25.5.0",
54
54
  "@typescript/native-preview": "7.0.0-dev.20260328.1",
55
55
  "typescript": "^6.0.2",
@@ -59,7 +59,7 @@
59
59
  "peerDependencies": {
60
60
  "@aws-sdk/client-bedrock-agent-runtime": ">=3.600.0 <4",
61
61
  "@aws-sdk/client-bedrock-runtime": ">=3.600.0 <4",
62
- "@telemetry-dev/sdk": "^0.1.3"
62
+ "@telemetry-dev/sdk": "^0.1.5"
63
63
  },
64
64
  "peerDependenciesMeta": {
65
65
  "@aws-sdk/client-bedrock-agent-runtime": {
@@ -20,6 +20,7 @@ import {
20
20
 
21
21
  export function agentInputMessages<T>(inputText: T): JsonValue {
22
22
  const text = stringValue(inputText);
23
+
23
24
  return text ? [{ role: "user", parts: [{ type: "text", content: text }] }] : undefined;
24
25
  }
25
26
 
@@ -63,35 +64,47 @@ export class AgentStreamState implements StreamState {
63
64
 
64
65
  feed<T>(event: T): void {
65
66
  const raw: unknown = event;
67
+
66
68
  if (!isRecord(raw)) return;
67
69
  const streamError = modeledStreamError(raw);
70
+
68
71
  if (streamError) {
69
72
  this.error = streamError.error;
70
73
  this.errorFields = streamError.fields;
74
+
71
75
  return;
72
76
  }
77
+
73
78
  const chunk = isRecord(raw.chunk) ? raw.chunk : undefined;
74
79
  const bytes = chunk?.bytes;
80
+
75
81
  if (bytes !== undefined) this.text += bytesToString(bytes) ?? "";
76
82
  const attribution = isRecord(chunk?.attribution) ? chunk.attribution : undefined;
77
83
  const citations = arrayValue<unknown>(attribution?.citations);
84
+
78
85
  if (citations) this.citationCount += citations.length;
79
86
 
80
87
  const files = isRecord(raw.files) ? arrayValue<JsonValue>(raw.files.files) : undefined;
88
+
81
89
  if (files) this.outputFiles.push(...files.map(sanitizeOutputFile));
82
90
 
83
91
  const returnControl = isRecord(raw.returnControl) ? raw.returnControl : undefined;
92
+
84
93
  if (returnControl) this.returnControl = returnControl;
85
94
 
86
95
  const traceEvent = isRecord(raw.trace) ? raw.trace : undefined;
96
+
87
97
  if (traceEvent) {
88
98
  this.traceEventCount += 1;
99
+
89
100
  if (this.options.captureAgentTrace) this.traces.push(traceEvent);
90
101
  collectTrace(traceEvent, (usage) => this.addUsage(usage));
91
102
  const failure = findKey(traceEvent, "failureTrace");
103
+
92
104
  if (isRecord(failure))
93
105
  this.failureReason = stringValue(failure.failureReason) ?? this.failureReason;
94
106
  const guardrail = findKey(traceEvent, "guardrailTrace");
107
+
95
108
  if (isRecord(guardrail))
96
109
  this.guardrailAction = stringValue(guardrail.action) ?? this.guardrailAction;
97
110
  }
@@ -108,6 +121,7 @@ export class AgentStreamState implements StreamState {
108
121
  agent_trace:
109
122
  this.options.captureAgentTrace && this.traces.length > 0 ? this.traces : undefined,
110
123
  });
124
+
111
125
  return mergeFields(
112
126
  omitUndefined({
113
127
  output: this.returnControl
@@ -124,14 +138,18 @@ export class AgentStreamState implements StreamState {
124
138
  }
125
139
  private addUsage<T>(value: T): void {
126
140
  const raw: unknown = value;
141
+
127
142
  if (!isRecord(raw)) return;
128
143
  const inputTokens = numberValue(raw.inputTokens);
129
144
  const outputTokens = numberValue(raw.outputTokens);
130
145
  const totalTokens = numberValue(raw.totalTokens);
146
+
131
147
  if (inputTokens !== undefined)
132
148
  this.usage.inputTokens = (this.usage.inputTokens ?? 0) + inputTokens;
149
+
133
150
  if (outputTokens !== undefined)
134
151
  this.usage.outputTokens = (this.usage.outputTokens ?? 0) + outputTokens;
152
+
135
153
  if (totalTokens !== undefined)
136
154
  this.usage.totalTokens = (this.usage.totalTokens ?? 0) + totalTokens;
137
155
  else if (inputTokens !== undefined || outputTokens !== undefined) {
@@ -142,7 +160,9 @@ export class AgentStreamState implements StreamState {
142
160
 
143
161
  function sanitizeOutputFile<T>(value: T): JsonValue {
144
162
  const raw: unknown = value;
163
+
145
164
  if (!isRecord(raw)) return undefined;
165
+
146
166
  return omitUndefined({
147
167
  name: stringValue(raw.name),
148
168
  type: stringValue(raw.type),
@@ -161,6 +181,7 @@ export function retrieveResponseFields<T>(output: T): SpanFields {
161
181
  const raw: unknown = output;
162
182
  const result = isRecord(raw) ? raw : {};
163
183
  const retrievalResults = arrayValue<unknown>(result.retrievalResults);
184
+
164
185
  return mergeFields(awsMetadataFields(result.$metadata), {
165
186
  output: retrievalResults,
166
187
  metadata: omitUndefined({
@@ -174,17 +195,21 @@ export function ragModel(input: JsonRecord): string | undefined {
174
195
  const config = isRecord(input.retrieveAndGenerateConfiguration)
175
196
  ? input.retrieveAndGenerateConfiguration
176
197
  : undefined;
198
+
177
199
  const kb = isRecord(config?.knowledgeBaseConfiguration)
178
200
  ? config?.knowledgeBaseConfiguration
179
201
  : undefined;
202
+
180
203
  const external = isRecord(config?.externalSourcesConfiguration)
181
204
  ? config?.externalSourcesConfiguration
182
205
  : undefined;
206
+
183
207
  return stringValue(kb?.modelArn) ?? stringValue(external?.modelArn);
184
208
  }
185
209
 
186
210
  export function ragRequestFields(input: JsonRecord): SpanFields {
187
211
  const text = isRecord(input.input) ? stringValue(input.input.text) : undefined;
212
+
188
213
  return omitUndefined({
189
214
  provider: PROVIDER,
190
215
  model: ragModel(input),
@@ -197,6 +222,7 @@ export function ragResponseFields<T>(output: T): SpanFields {
197
222
  const raw: unknown = output;
198
223
  const result = isRecord(raw) ? raw : {};
199
224
  const text = isRecord(result.output) ? stringValue(result.output.text) : undefined;
225
+
200
226
  return mergeFields(awsMetadataFields(result.$metadata), {
201
227
  output: text ? [{ role: "assistant", parts: [{ type: "text", content: text }] }] : undefined,
202
228
  metadata: omitUndefined({
@@ -216,15 +242,20 @@ export class RagStreamState implements StreamState {
216
242
 
217
243
  feed<T>(event: T): void {
218
244
  const raw: unknown = event;
245
+
219
246
  if (!isRecord(raw)) return;
220
247
  const streamError = modeledStreamError(raw);
248
+
221
249
  if (streamError) {
222
250
  this.error = streamError.error;
223
251
  this.errorFields = streamError.fields;
252
+
224
253
  return;
225
254
  }
255
+
226
256
  const output = isRecord(raw.output) ? raw.output : undefined;
227
257
  this.text += stringValue(output?.text) ?? "";
258
+
228
259
  if (raw.citation) this.citationCount += 1;
229
260
  const guardrail = isRecord(raw.guardrail) ? raw.guardrail : undefined;
230
261
  this.guardrailAction = stringValue(guardrail?.action) ?? this.guardrailAction;
@@ -263,18 +294,25 @@ export class FlowStreamState implements StreamState {
263
294
 
264
295
  feed<T>(event: T): void {
265
296
  const raw: unknown = event;
297
+
266
298
  if (!isRecord(raw)) return;
267
299
  const streamError = modeledStreamError(raw);
300
+
268
301
  if (streamError) {
269
302
  this.error = streamError.error;
270
303
  this.errorFields = streamError.fields;
304
+
271
305
  return;
272
306
  }
307
+
273
308
  const output = isRecord(raw.flowOutputEvent) ? raw.flowOutputEvent : undefined;
309
+
274
310
  if (output) this.outputs.push(output.content);
311
+
275
312
  const inputRequest = isRecord(raw.flowMultiTurnInputRequestEvent)
276
313
  ? raw.flowMultiTurnInputRequestEvent
277
314
  : undefined;
315
+
278
316
  if (inputRequest) this.outputs.push(inputRequest.content);
279
317
  const completion = isRecord(raw.flowCompletionEvent) ? raw.flowCompletionEvent : undefined;
280
318
  this.completionReason = stringValue(completion?.completionReason) ?? this.completionReason;
@@ -302,12 +340,15 @@ export function ragName(input: JsonRecord): string {
302
340
 
303
341
  function collectTrace<T>(value: T, onUsage: <TUsage>(usage: TUsage) => void): void {
304
342
  const raw: unknown = value;
343
+
305
344
  if (!isRecord(raw)) return;
306
345
  const output = isRecord(raw.modelInvocationOutput) ? raw.modelInvocationOutput : undefined;
346
+
307
347
  if (output) {
308
348
  const metadata = isRecord(output.metadata) ? output.metadata : undefined;
309
349
  onUsage(metadata?.usage);
310
350
  }
351
+
311
352
  for (const child of Object.values(raw)) {
312
353
  if (isRecord(child)) collectTrace(child, onUsage);
313
354
  else if (Array.isArray(child)) child.forEach((item) => collectTrace(item, onUsage));
@@ -316,11 +357,16 @@ function collectTrace<T>(value: T, onUsage: <TUsage>(usage: TUsage) => void): vo
316
357
 
317
358
  function findKey<T>(value: T, key: string): JsonValue {
318
359
  const raw: unknown = value;
360
+
319
361
  if (!isRecord(raw)) return undefined;
362
+
320
363
  if (raw[key] !== undefined) return raw[key];
364
+
321
365
  for (const child of Object.values(raw)) {
322
366
  const found = findKey(child, key);
367
+
323
368
  if (found !== undefined) return found;
324
369
  }
370
+
325
371
  return undefined;
326
372
  }
package/src/agents.ts CHANGED
@@ -28,6 +28,7 @@ export function wrapBedrockAgents<T extends WrappableBedrockAgentClient>(
28
28
  writable: true,
29
29
  });
30
30
  WRAPPED.add(client as object);
31
+
31
32
  return client;
32
33
  }
33
34
 
@@ -37,13 +38,16 @@ export function instrumentBedrockAgents(options: BedrockInstrumentationOptions =
37
38
  const hadOwn = Object.prototype.hasOwnProperty.call(proto, "send");
38
39
  const original = proto.send;
39
40
  const instrumentedSend = createInstrumentedSend(original, options, AGENT_HANDLERS);
41
+
40
42
  const wrapper = function telemetryDevBedrockAgentSend(
41
43
  this: WrappableBedrockAgentClient,
42
44
  ...args: unknown[]
43
45
  ) {
44
46
  if (WRAPPED.has(this as object)) return original.apply(this, args);
47
+
45
48
  return instrumentedSend.apply(this, args);
46
49
  };
50
+
47
51
  ORIGINAL = { proto, hadOwn, value: original, wrapper };
48
52
  Object.defineProperty(proto, "send", {
49
53
  configurable: true,
@@ -54,9 +58,12 @@ export function instrumentBedrockAgents(options: BedrockInstrumentationOptions =
54
58
 
55
59
  export function uninstrumentBedrockAgents(): void {
56
60
  const original = ORIGINAL;
61
+
57
62
  if (!original) return;
63
+
58
64
  if (Object.getOwnPropertyDescriptor(original.proto, "send")?.value !== original.wrapper) return;
59
65
  ORIGINAL = undefined;
66
+
60
67
  if (original.hadOwn) {
61
68
  Object.defineProperty(original.proto, "send", {
62
69
  configurable: true,