@telemetry-dev/bedrock 0.1.0 → 0.1.2
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.d.mts +1 -1
- package/dist/agents.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{internal-handlers-BloMjBP8.mjs → internal-handlers-CJPJo9q3.mjs} +122 -87
- package/package.json +4 -4
- package/src/agent-handlers.ts +61 -50
- package/src/agents.ts +2 -2
- package/src/converse.ts +60 -44
- package/src/index.ts +2 -2
- package/src/internal-handlers.ts +12 -12
- package/src/internal.ts +110 -72
- package/src/invoke-model.ts +22 -23
package/src/agent-handlers.ts
CHANGED
|
@@ -13,15 +13,17 @@ import {
|
|
|
13
13
|
PROVIDER,
|
|
14
14
|
stringValue,
|
|
15
15
|
type BedrockInstrumentationOptions,
|
|
16
|
+
type JsonRecord,
|
|
17
|
+
type JsonValue,
|
|
16
18
|
type StreamState,
|
|
17
19
|
} from "./internal.ts";
|
|
18
20
|
|
|
19
|
-
export function agentInputMessages(inputText:
|
|
21
|
+
export function agentInputMessages<T>(inputText: T): JsonValue {
|
|
20
22
|
const text = stringValue(inputText);
|
|
21
23
|
return text ? [{ role: "user", parts: [{ type: "text", content: text }] }] : undefined;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
export function agentRequestFields(input:
|
|
26
|
+
export function agentRequestFields(input: JsonRecord): SpanFields {
|
|
25
27
|
return omitUndefined({
|
|
26
28
|
provider: PROVIDER,
|
|
27
29
|
input: agentInputMessages(input.inputText),
|
|
@@ -34,7 +36,7 @@ export function agentRequestFields(input: Record<string, unknown>): SpanFields {
|
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
export function inlineAgentRequestFields(input:
|
|
39
|
+
export function inlineAgentRequestFields(input: JsonRecord): SpanFields {
|
|
38
40
|
return omitUndefined({
|
|
39
41
|
input: agentInputMessages(input.inputText),
|
|
40
42
|
model: stringValue(input.foundationModel),
|
|
@@ -59,28 +61,29 @@ export class AgentStreamState implements StreamState {
|
|
|
59
61
|
|
|
60
62
|
constructor(private readonly options: BedrockInstrumentationOptions) {}
|
|
61
63
|
|
|
62
|
-
feed(event:
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
feed<T>(event: T): void {
|
|
65
|
+
const raw: unknown = event;
|
|
66
|
+
if (!isRecord(raw)) return;
|
|
67
|
+
const streamError = modeledStreamError(raw);
|
|
65
68
|
if (streamError) {
|
|
66
69
|
this.error = streamError.error;
|
|
67
70
|
this.errorFields = streamError.fields;
|
|
68
71
|
return;
|
|
69
72
|
}
|
|
70
|
-
const chunk = isRecord(
|
|
73
|
+
const chunk = isRecord(raw.chunk) ? raw.chunk : undefined;
|
|
71
74
|
const bytes = chunk?.bytes;
|
|
72
75
|
if (bytes !== undefined) this.text += bytesToString(bytes) ?? "";
|
|
73
76
|
const attribution = isRecord(chunk?.attribution) ? chunk.attribution : undefined;
|
|
74
77
|
const citations = arrayValue<unknown>(attribution?.citations);
|
|
75
78
|
if (citations) this.citationCount += citations.length;
|
|
76
79
|
|
|
77
|
-
const files = isRecord(
|
|
80
|
+
const files = isRecord(raw.files) ? arrayValue<JsonValue>(raw.files.files) : undefined;
|
|
78
81
|
if (files) this.outputFiles.push(...files.map(sanitizeOutputFile));
|
|
79
82
|
|
|
80
|
-
const returnControl = isRecord(
|
|
83
|
+
const returnControl = isRecord(raw.returnControl) ? raw.returnControl : undefined;
|
|
81
84
|
if (returnControl) this.returnControl = returnControl;
|
|
82
85
|
|
|
83
|
-
const traceEvent = isRecord(
|
|
86
|
+
const traceEvent = isRecord(raw.trace) ? raw.trace : undefined;
|
|
84
87
|
if (traceEvent) {
|
|
85
88
|
this.traceEventCount += 1;
|
|
86
89
|
if (this.options.captureAgentTrace) this.traces.push(traceEvent);
|
|
@@ -119,11 +122,12 @@ export class AgentStreamState implements StreamState {
|
|
|
119
122
|
this.errorFields,
|
|
120
123
|
);
|
|
121
124
|
}
|
|
122
|
-
private addUsage(value:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
const
|
|
125
|
+
private addUsage<T>(value: T): void {
|
|
126
|
+
const raw: unknown = value;
|
|
127
|
+
if (!isRecord(raw)) return;
|
|
128
|
+
const inputTokens = numberValue(raw.inputTokens);
|
|
129
|
+
const outputTokens = numberValue(raw.outputTokens);
|
|
130
|
+
const totalTokens = numberValue(raw.totalTokens);
|
|
127
131
|
if (inputTokens !== undefined)
|
|
128
132
|
this.usage.inputTokens = (this.usage.inputTokens ?? 0) + inputTokens;
|
|
129
133
|
if (outputTokens !== undefined)
|
|
@@ -136,15 +140,16 @@ export class AgentStreamState implements StreamState {
|
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
function sanitizeOutputFile(value:
|
|
140
|
-
|
|
143
|
+
function sanitizeOutputFile<T>(value: T): JsonValue {
|
|
144
|
+
const raw: unknown = value;
|
|
145
|
+
if (!isRecord(raw)) return undefined;
|
|
141
146
|
return omitUndefined({
|
|
142
|
-
name: stringValue(
|
|
143
|
-
type: stringValue(
|
|
147
|
+
name: stringValue(raw.name),
|
|
148
|
+
type: stringValue(raw.type),
|
|
144
149
|
});
|
|
145
150
|
}
|
|
146
151
|
|
|
147
|
-
export function retrieveRequestFields(input:
|
|
152
|
+
export function retrieveRequestFields(input: JsonRecord): SpanFields {
|
|
148
153
|
return omitUndefined({
|
|
149
154
|
provider: PROVIDER,
|
|
150
155
|
input: isRecord(input.retrievalQuery) ? input.retrievalQuery : undefined,
|
|
@@ -152,8 +157,9 @@ export function retrieveRequestFields(input: Record<string, unknown>): SpanField
|
|
|
152
157
|
});
|
|
153
158
|
}
|
|
154
159
|
|
|
155
|
-
export function retrieveResponseFields(output:
|
|
156
|
-
const
|
|
160
|
+
export function retrieveResponseFields<T>(output: T): SpanFields {
|
|
161
|
+
const raw: unknown = output;
|
|
162
|
+
const result = isRecord(raw) ? raw : {};
|
|
157
163
|
const retrievalResults = arrayValue<unknown>(result.retrievalResults);
|
|
158
164
|
return mergeFields(awsMetadataFields(result.$metadata), {
|
|
159
165
|
output: retrievalResults,
|
|
@@ -164,7 +170,7 @@ export function retrieveResponseFields(output: unknown): SpanFields {
|
|
|
164
170
|
});
|
|
165
171
|
}
|
|
166
172
|
|
|
167
|
-
export function ragModel(input:
|
|
173
|
+
export function ragModel(input: JsonRecord): string | undefined {
|
|
168
174
|
const config = isRecord(input.retrieveAndGenerateConfiguration)
|
|
169
175
|
? input.retrieveAndGenerateConfiguration
|
|
170
176
|
: undefined;
|
|
@@ -177,7 +183,7 @@ export function ragModel(input: Record<string, unknown>): string | undefined {
|
|
|
177
183
|
return stringValue(kb?.modelArn) ?? stringValue(external?.modelArn);
|
|
178
184
|
}
|
|
179
185
|
|
|
180
|
-
export function ragRequestFields(input:
|
|
186
|
+
export function ragRequestFields(input: JsonRecord): SpanFields {
|
|
181
187
|
const text = isRecord(input.input) ? stringValue(input.input.text) : undefined;
|
|
182
188
|
return omitUndefined({
|
|
183
189
|
provider: PROVIDER,
|
|
@@ -187,8 +193,9 @@ export function ragRequestFields(input: Record<string, unknown>): SpanFields {
|
|
|
187
193
|
});
|
|
188
194
|
}
|
|
189
195
|
|
|
190
|
-
export function ragResponseFields(output:
|
|
191
|
-
const
|
|
196
|
+
export function ragResponseFields<T>(output: T): SpanFields {
|
|
197
|
+
const raw: unknown = output;
|
|
198
|
+
const result = isRecord(raw) ? raw : {};
|
|
192
199
|
const text = isRecord(result.output) ? stringValue(result.output.text) : undefined;
|
|
193
200
|
return mergeFields(awsMetadataFields(result.$metadata), {
|
|
194
201
|
output: text ? [{ role: "assistant", parts: [{ type: "text", content: text }] }] : undefined,
|
|
@@ -207,18 +214,19 @@ export class RagStreamState implements StreamState {
|
|
|
207
214
|
private error: Error | undefined;
|
|
208
215
|
private errorFields: SpanFields | undefined;
|
|
209
216
|
|
|
210
|
-
feed(event:
|
|
211
|
-
|
|
212
|
-
|
|
217
|
+
feed<T>(event: T): void {
|
|
218
|
+
const raw: unknown = event;
|
|
219
|
+
if (!isRecord(raw)) return;
|
|
220
|
+
const streamError = modeledStreamError(raw);
|
|
213
221
|
if (streamError) {
|
|
214
222
|
this.error = streamError.error;
|
|
215
223
|
this.errorFields = streamError.fields;
|
|
216
224
|
return;
|
|
217
225
|
}
|
|
218
|
-
const output = isRecord(
|
|
226
|
+
const output = isRecord(raw.output) ? raw.output : undefined;
|
|
219
227
|
this.text += stringValue(output?.text) ?? "";
|
|
220
|
-
if (
|
|
221
|
-
const guardrail = isRecord(
|
|
228
|
+
if (raw.citation) this.citationCount += 1;
|
|
229
|
+
const guardrail = isRecord(raw.guardrail) ? raw.guardrail : undefined;
|
|
222
230
|
this.guardrailAction = stringValue(guardrail?.action) ?? this.guardrailAction;
|
|
223
231
|
}
|
|
224
232
|
|
|
@@ -239,7 +247,7 @@ export class RagStreamState implements StreamState {
|
|
|
239
247
|
}
|
|
240
248
|
}
|
|
241
249
|
|
|
242
|
-
export function flowRequestFields(input:
|
|
250
|
+
export function flowRequestFields(input: JsonRecord): SpanFields {
|
|
243
251
|
return omitUndefined({
|
|
244
252
|
provider: PROVIDER,
|
|
245
253
|
input: input.inputs,
|
|
@@ -253,21 +261,22 @@ export class FlowStreamState implements StreamState {
|
|
|
253
261
|
private error: Error | undefined;
|
|
254
262
|
private errorFields: SpanFields | undefined;
|
|
255
263
|
|
|
256
|
-
feed(event:
|
|
257
|
-
|
|
258
|
-
|
|
264
|
+
feed<T>(event: T): void {
|
|
265
|
+
const raw: unknown = event;
|
|
266
|
+
if (!isRecord(raw)) return;
|
|
267
|
+
const streamError = modeledStreamError(raw);
|
|
259
268
|
if (streamError) {
|
|
260
269
|
this.error = streamError.error;
|
|
261
270
|
this.errorFields = streamError.fields;
|
|
262
271
|
return;
|
|
263
272
|
}
|
|
264
|
-
const output = isRecord(
|
|
273
|
+
const output = isRecord(raw.flowOutputEvent) ? raw.flowOutputEvent : undefined;
|
|
265
274
|
if (output) this.outputs.push(output.content);
|
|
266
|
-
const inputRequest = isRecord(
|
|
267
|
-
?
|
|
275
|
+
const inputRequest = isRecord(raw.flowMultiTurnInputRequestEvent)
|
|
276
|
+
? raw.flowMultiTurnInputRequestEvent
|
|
268
277
|
: undefined;
|
|
269
278
|
if (inputRequest) this.outputs.push(inputRequest.content);
|
|
270
|
-
const completion = isRecord(
|
|
279
|
+
const completion = isRecord(raw.flowCompletionEvent) ? raw.flowCompletionEvent : undefined;
|
|
271
280
|
this.completionReason = stringValue(completion?.completionReason) ?? this.completionReason;
|
|
272
281
|
}
|
|
273
282
|
|
|
@@ -283,31 +292,33 @@ export class FlowStreamState implements StreamState {
|
|
|
283
292
|
}
|
|
284
293
|
}
|
|
285
294
|
|
|
286
|
-
export function flowName(input:
|
|
295
|
+
export function flowName(input: JsonRecord): string {
|
|
287
296
|
return `invoke_flow ${stringValue(input.flowIdentifier) ?? "unknown"}`;
|
|
288
297
|
}
|
|
289
298
|
|
|
290
|
-
export function ragName(input:
|
|
299
|
+
export function ragName(input: JsonRecord): string {
|
|
291
300
|
return `retrieve_and_generate ${basenameArn(ragModel(input)) ?? "knowledge-base"}`;
|
|
292
301
|
}
|
|
293
302
|
|
|
294
|
-
function collectTrace(value:
|
|
295
|
-
|
|
296
|
-
|
|
303
|
+
function collectTrace<T>(value: T, onUsage: <TUsage>(usage: TUsage) => void): void {
|
|
304
|
+
const raw: unknown = value;
|
|
305
|
+
if (!isRecord(raw)) return;
|
|
306
|
+
const output = isRecord(raw.modelInvocationOutput) ? raw.modelInvocationOutput : undefined;
|
|
297
307
|
if (output) {
|
|
298
308
|
const metadata = isRecord(output.metadata) ? output.metadata : undefined;
|
|
299
309
|
onUsage(metadata?.usage);
|
|
300
310
|
}
|
|
301
|
-
for (const child of Object.values(
|
|
311
|
+
for (const child of Object.values(raw)) {
|
|
302
312
|
if (isRecord(child)) collectTrace(child, onUsage);
|
|
303
313
|
else if (Array.isArray(child)) child.forEach((item) => collectTrace(item, onUsage));
|
|
304
314
|
}
|
|
305
315
|
}
|
|
306
316
|
|
|
307
|
-
function findKey(value:
|
|
308
|
-
|
|
309
|
-
if (
|
|
310
|
-
|
|
317
|
+
function findKey<T>(value: T, key: string): JsonValue {
|
|
318
|
+
const raw: unknown = value;
|
|
319
|
+
if (!isRecord(raw)) return undefined;
|
|
320
|
+
if (raw[key] !== undefined) return raw[key];
|
|
321
|
+
for (const child of Object.values(raw)) {
|
|
311
322
|
const found = findKey(child, key);
|
|
312
323
|
if (found !== undefined) return found;
|
|
313
324
|
}
|
package/src/agents.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
export type { BedrockInstrumentationOptions } from "./internal.ts";
|
|
11
11
|
|
|
12
12
|
export interface WrappableBedrockAgentClient {
|
|
13
|
-
send: (...args: never[]) =>
|
|
13
|
+
send: (...args: never[]) => any;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const WRAPPED = new WeakSet<object>();
|
|
@@ -33,7 +33,7 @@ export function wrapBedrockAgents<T extends WrappableBedrockAgentClient>(
|
|
|
33
33
|
|
|
34
34
|
export function instrumentBedrockAgents(options: BedrockInstrumentationOptions = {}): void {
|
|
35
35
|
if (ORIGINAL) return;
|
|
36
|
-
const proto = BedrockAgentRuntimeClient.prototype as
|
|
36
|
+
const proto = BedrockAgentRuntimeClient.prototype as { send: SendFn };
|
|
37
37
|
const hadOwn = Object.prototype.hasOwnProperty.call(proto, "send");
|
|
38
38
|
const original = proto.send;
|
|
39
39
|
const instrumentedSend = createInstrumentedSend(original, options, AGENT_HANDLERS);
|
package/src/converse.ts
CHANGED
|
@@ -11,17 +11,19 @@ import {
|
|
|
11
11
|
parseJson,
|
|
12
12
|
PROVIDER,
|
|
13
13
|
stringValue,
|
|
14
|
+
type JsonRecord,
|
|
15
|
+
type JsonValue,
|
|
14
16
|
type StreamState,
|
|
15
17
|
} from "./internal.ts";
|
|
16
18
|
|
|
17
|
-
export type MessagePart =
|
|
19
|
+
export type MessagePart = object;
|
|
18
20
|
export interface NormalizedMessage {
|
|
19
21
|
role: string | undefined;
|
|
20
22
|
parts: MessagePart[];
|
|
21
23
|
finish_reason?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
export function converseRequestFields(input:
|
|
26
|
+
export function converseRequestFields(input: JsonRecord): SpanFields {
|
|
25
27
|
const inference = isRecord(input.inferenceConfig) ? input.inferenceConfig : {};
|
|
26
28
|
const guardrail = isRecord(input.guardrailConfig) ? input.guardrailConfig : undefined;
|
|
27
29
|
return omitUndefined({
|
|
@@ -42,8 +44,8 @@ export function converseRequestFields(input: Record<string, unknown>): SpanField
|
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
export function converseResponseFields(output:
|
|
46
|
-
const result = isRecord(output) ? output : {};
|
|
47
|
+
export function converseResponseFields<T>(output: T): SpanFields {
|
|
48
|
+
const result: JsonRecord = isRecord(output) ? output : {};
|
|
47
49
|
const usage = usageFromConverse(result.usage);
|
|
48
50
|
const metrics = isRecord(result.metrics) ? result.metrics : undefined;
|
|
49
51
|
const trace = isRecord(result.trace) ? result.trace : undefined;
|
|
@@ -64,7 +66,7 @@ export function converseResponseFields(output: unknown): SpanFields {
|
|
|
64
66
|
});
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
export function usageFromConverse(value:
|
|
69
|
+
export function usageFromConverse<T>(value: T): SpanFields["usage"] | undefined {
|
|
68
70
|
if (!isRecord(value)) return undefined;
|
|
69
71
|
return omitUndefined({
|
|
70
72
|
inputTokens: numberValue(value.inputTokens),
|
|
@@ -75,8 +77,8 @@ export function usageFromConverse(value: unknown): SpanFields["usage"] | undefin
|
|
|
75
77
|
});
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
export function normalizeMessages(value:
|
|
79
|
-
const messages = arrayValue<
|
|
80
|
+
export function normalizeMessages<T>(value: T): NormalizedMessage[] | undefined {
|
|
81
|
+
const messages = arrayValue<JsonRecord>(value);
|
|
80
82
|
if (!messages) return undefined;
|
|
81
83
|
return messages.map((message) => ({
|
|
82
84
|
role: stringValue(message.role),
|
|
@@ -84,34 +86,33 @@ export function normalizeMessages(value: unknown): NormalizedMessage[] | undefin
|
|
|
84
86
|
}));
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
export function normalizeOutputMessage(
|
|
88
|
-
value:
|
|
89
|
+
export function normalizeOutputMessage<T>(
|
|
90
|
+
value: T,
|
|
89
91
|
finishReason?: string,
|
|
90
92
|
): NormalizedMessage[] | undefined {
|
|
91
93
|
if (!isRecord(value)) return undefined;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
];
|
|
94
|
+
const message = omitUndefined({
|
|
95
|
+
role: stringValue(value.role),
|
|
96
|
+
parts: normalizeContentList(value.content),
|
|
97
|
+
finish_reason: finishReason,
|
|
98
|
+
});
|
|
99
|
+
return [message];
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
export function normalizeSystem(value:
|
|
102
|
-
const blocks = arrayValue<
|
|
102
|
+
export function normalizeSystem<T>(value: T): MessagePart[] | undefined {
|
|
103
|
+
const blocks = arrayValue<JsonRecord>(value);
|
|
103
104
|
if (!blocks) return undefined;
|
|
104
105
|
const parts = blocks.flatMap((block) => normalizeContentBlock(block));
|
|
105
106
|
return parts.length > 0 ? parts : undefined;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
function normalizeContentList(value:
|
|
109
|
-
const blocks = arrayValue<
|
|
109
|
+
function normalizeContentList<T>(value: T): MessagePart[] {
|
|
110
|
+
const blocks = arrayValue<JsonRecord>(value);
|
|
110
111
|
if (!blocks) return [];
|
|
111
112
|
return blocks.flatMap((block) => normalizeContentBlock(block));
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
function normalizeContentBlock(block:
|
|
115
|
+
function normalizeContentBlock(block: JsonRecord): MessagePart[] {
|
|
115
116
|
const text = stringValue(block.text);
|
|
116
117
|
if (text !== undefined)
|
|
117
118
|
return [
|
|
@@ -178,11 +179,12 @@ function normalizeContentBlock(block: Record<string, unknown>): MessagePart[] {
|
|
|
178
179
|
|
|
179
180
|
const citationsContent = isRecord(block.citationsContent) ? block.citationsContent : undefined;
|
|
180
181
|
if (citationsContent) {
|
|
181
|
-
const contentBlocks = arrayValue<
|
|
182
|
-
const citations = arrayValue<
|
|
183
|
-
return contentBlocks.flatMap((contentBlock) =>
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
const contentBlocks = arrayValue<JsonRecord>(citationsContent.content) ?? [];
|
|
183
|
+
const citations = arrayValue<JsonValue>(citationsContent.citations);
|
|
184
|
+
return contentBlocks.flatMap((contentBlock) => {
|
|
185
|
+
if (!citations) return normalizeContentBlock(contentBlock);
|
|
186
|
+
return normalizeContentBlock({ ...contentBlock, citations });
|
|
187
|
+
});
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
const guardContent = isRecord(block.guardContent) ? block.guardContent : undefined;
|
|
@@ -199,14 +201,15 @@ function normalizeContentBlock(block: Record<string, unknown>): MessagePart[] {
|
|
|
199
201
|
}
|
|
200
202
|
}
|
|
201
203
|
|
|
202
|
-
function simplifyToolResult(content:
|
|
203
|
-
const blocks = arrayValue<
|
|
204
|
-
if (!blocks) return content;
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
function simplifyToolResult<T>(content: T): JsonValue {
|
|
205
|
+
const blocks = arrayValue<JsonRecord>(content);
|
|
206
|
+
if (!blocks) return isJsonValue(content) ? content : undefined;
|
|
207
|
+
const text = stringValue(blocks[0]?.text);
|
|
208
|
+
if (blocks.length === 1 && text !== undefined) return text;
|
|
209
|
+
return blocks.flatMap((block) => normalizeContentBlock(block)) as JsonValue[];
|
|
207
210
|
}
|
|
208
211
|
|
|
209
|
-
function s3Uri(value:
|
|
212
|
+
function s3Uri(value: JsonRecord | undefined): string | undefined {
|
|
210
213
|
const bucket = stringValue(value?.bucket);
|
|
211
214
|
const key = stringValue(value?.key);
|
|
212
215
|
return bucket && key ? `s3://${bucket}/${key}` : undefined;
|
|
@@ -218,7 +221,7 @@ interface StreamBlock {
|
|
|
218
221
|
toolUseId?: string;
|
|
219
222
|
name?: string;
|
|
220
223
|
input?: string;
|
|
221
|
-
content?:
|
|
224
|
+
content?: JsonRecord;
|
|
222
225
|
citations?: unknown[];
|
|
223
226
|
}
|
|
224
227
|
|
|
@@ -227,12 +230,12 @@ export class ConverseStreamState implements StreamState {
|
|
|
227
230
|
private readonly blocks = new Map<number, StreamBlock>();
|
|
228
231
|
private stopReason: string | undefined;
|
|
229
232
|
private usage: SpanFields["usage"];
|
|
230
|
-
private metadata:
|
|
233
|
+
private metadata: JsonRecord = {};
|
|
231
234
|
private responseModel: string | undefined;
|
|
232
235
|
private error: Error | undefined;
|
|
233
236
|
private errorFields: SpanFields | undefined;
|
|
234
237
|
|
|
235
|
-
feed(event:
|
|
238
|
+
feed<T>(event: T): void {
|
|
236
239
|
if (!isRecord(event)) return;
|
|
237
240
|
const streamError = modeledStreamError(event);
|
|
238
241
|
if (streamError) {
|
|
@@ -281,14 +284,16 @@ export class ConverseStreamState implements StreamState {
|
|
|
281
284
|
const index = numberValue(deltaEvent.contentBlockIndex) ?? 0;
|
|
282
285
|
const delta = isRecord(deltaEvent.delta) ? deltaEvent.delta : {};
|
|
283
286
|
const block = this.blocks.get(index) ?? { kind: "text", text: "" };
|
|
284
|
-
|
|
287
|
+
const text = stringValue(delta.text);
|
|
288
|
+
if (text !== undefined) {
|
|
285
289
|
block.kind = "text";
|
|
286
|
-
block.text = `${block.text ?? ""}${
|
|
290
|
+
block.text = `${block.text ?? ""}${text}`;
|
|
287
291
|
}
|
|
288
292
|
const toolUse = isRecord(delta.toolUse) ? delta.toolUse : undefined;
|
|
289
|
-
|
|
293
|
+
const input = stringValue(toolUse?.input);
|
|
294
|
+
if (toolUse && input !== undefined) {
|
|
290
295
|
block.kind = "tool";
|
|
291
|
-
block.input = `${block.input ?? ""}${
|
|
296
|
+
block.input = `${block.input ?? ""}${input}`;
|
|
292
297
|
}
|
|
293
298
|
const reasoning = isRecord(delta.reasoningContent) ? delta.reasoningContent : undefined;
|
|
294
299
|
if (reasoning) {
|
|
@@ -298,15 +303,15 @@ export class ConverseStreamState implements StreamState {
|
|
|
298
303
|
const image = isRecord(delta.image) ? delta.image : undefined;
|
|
299
304
|
if (image) {
|
|
300
305
|
const content = isRecord(block.content) ? block.content : {};
|
|
301
|
-
const current = isRecord(content.image) ? content.image : {};
|
|
306
|
+
const current: JsonRecord = isRecord(content.image) ? content.image : {};
|
|
302
307
|
block.kind = "content";
|
|
303
308
|
block.content = { image: { ...current, ...image } };
|
|
304
309
|
}
|
|
305
|
-
const toolResult = arrayValue<
|
|
310
|
+
const toolResult = arrayValue<JsonRecord>(delta.toolResult);
|
|
306
311
|
if (toolResult) {
|
|
307
312
|
const content = isRecord(block.content) ? block.content : {};
|
|
308
|
-
const current = isRecord(content.toolResult) ? content.toolResult : {};
|
|
309
|
-
const currentContent = arrayValue<
|
|
313
|
+
const current: JsonRecord = isRecord(content.toolResult) ? content.toolResult : {};
|
|
314
|
+
const currentContent = arrayValue<JsonValue>(current.content) ?? [];
|
|
310
315
|
block.kind = "content";
|
|
311
316
|
block.content = {
|
|
312
317
|
toolResult: {
|
|
@@ -370,7 +375,18 @@ export class ConverseStreamState implements StreamState {
|
|
|
370
375
|
}
|
|
371
376
|
}
|
|
372
377
|
|
|
373
|
-
function
|
|
378
|
+
function isJsonValue<T>(value: T): value is T & JsonValue {
|
|
379
|
+
return (
|
|
380
|
+
value == null ||
|
|
381
|
+
String(value) === value ||
|
|
382
|
+
Number(value) === value ||
|
|
383
|
+
Boolean(value) === value ||
|
|
384
|
+
Array.isArray(value) ||
|
|
385
|
+
isRecord(value)
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function blockToPart(block: StreamBlock) {
|
|
374
390
|
if (block.kind === "tool") {
|
|
375
391
|
return omitUndefined({
|
|
376
392
|
type: "tool_call",
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
export type { BedrockInstrumentationOptions } from "./internal.ts";
|
|
11
11
|
|
|
12
12
|
export interface WrappableBedrockClient {
|
|
13
|
-
send: (...args: never[]) =>
|
|
13
|
+
send: (...args: never[]) => any;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const WRAPPED = new WeakSet<object>();
|
|
@@ -33,7 +33,7 @@ export function wrapBedrock<T extends WrappableBedrockClient>(
|
|
|
33
33
|
|
|
34
34
|
export function instrumentBedrock(options: BedrockInstrumentationOptions = {}): void {
|
|
35
35
|
if (ORIGINAL) return;
|
|
36
|
-
const proto = BedrockRuntimeClient.prototype as
|
|
36
|
+
const proto = BedrockRuntimeClient.prototype as { send: SendFn };
|
|
37
37
|
const hadOwn = Object.prototype.hasOwnProperty.call(proto, "send");
|
|
38
38
|
const original = proto.send;
|
|
39
39
|
const instrumentedSend = createInstrumentedSend(original, options, RUNTIME_HANDLERS);
|
package/src/internal-handlers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpHandler } from "./internal.ts";
|
|
1
|
+
import type { JsonRecord, JsonValue, OpHandler } from "./internal.ts";
|
|
2
2
|
import {
|
|
3
3
|
AgentStreamState,
|
|
4
4
|
FlowStreamState,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
type BedrockInstrumentationOptions,
|
|
33
33
|
} from "./internal.ts";
|
|
34
34
|
|
|
35
|
-
export const RUNTIME_HANDLERS
|
|
35
|
+
export const RUNTIME_HANDLERS = {
|
|
36
36
|
ConverseCommand: {
|
|
37
37
|
spanName: (input) => `chat ${stringValue(input.modelId) ?? "unknown"}`,
|
|
38
38
|
spanType: () => "generation",
|
|
@@ -108,7 +108,7 @@ export const RUNTIME_HANDLERS: Record<string, OpHandler> = {
|
|
|
108
108
|
}),
|
|
109
109
|
}),
|
|
110
110
|
onResult(result, span) {
|
|
111
|
-
const response = result as
|
|
111
|
+
const response = result as JsonRecord;
|
|
112
112
|
endSpan(
|
|
113
113
|
span,
|
|
114
114
|
mergeFields(awsMetadataFields(response.$metadata), {
|
|
@@ -122,9 +122,9 @@ export const RUNTIME_HANDLERS: Record<string, OpHandler> = {
|
|
|
122
122
|
return result;
|
|
123
123
|
},
|
|
124
124
|
},
|
|
125
|
-
}
|
|
125
|
+
} satisfies Record<string, OpHandler>;
|
|
126
126
|
|
|
127
|
-
export const AGENT_HANDLERS
|
|
127
|
+
export const AGENT_HANDLERS = {
|
|
128
128
|
InvokeAgentCommand: {
|
|
129
129
|
spanName: (input) => `invoke_agent ${stringValue(input.agentId) ?? "unknown"}`,
|
|
130
130
|
spanType: () => "agent",
|
|
@@ -196,19 +196,19 @@ export const AGENT_HANDLERS: Record<string, OpHandler> = {
|
|
|
196
196
|
};
|
|
197
197
|
},
|
|
198
198
|
},
|
|
199
|
-
}
|
|
199
|
+
} satisfies Record<string, OpHandler>;
|
|
200
200
|
|
|
201
201
|
function streamAgentResult(key: "completion") {
|
|
202
202
|
return (
|
|
203
|
-
result:
|
|
203
|
+
result: JsonValue,
|
|
204
204
|
span: Parameters<OpHandler["onResult"]>[1],
|
|
205
205
|
t0: number,
|
|
206
|
-
_input:
|
|
206
|
+
_input: JsonRecord,
|
|
207
207
|
options: BedrockInstrumentationOptions,
|
|
208
|
-
)
|
|
209
|
-
const response = result as {
|
|
210
|
-
completion?:
|
|
211
|
-
$metadata?:
|
|
208
|
+
) => {
|
|
209
|
+
const response = result as JsonRecord & {
|
|
210
|
+
completion?: JsonValue;
|
|
211
|
+
$metadata?: JsonValue;
|
|
212
212
|
sessionId?: string;
|
|
213
213
|
memoryId?: string;
|
|
214
214
|
};
|