@telemetry-dev/bedrock 0.1.3 → 0.1.5
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 +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{internal-handlers-CJPJo9q3.mjs → internal-handlers-B47qvHGh.mjs} +59 -10
- package/package.json +4 -4
- package/src/agent-handlers.ts +46 -0
- package/src/agents.ts +7 -0
- package/src/converse.ts +53 -0
- package/src/index.ts +7 -0
- package/src/internal-handlers.ts +25 -2
- package/src/internal.ts +123 -3
- package/src/invoke-model.ts +68 -4
package/src/internal.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type JsonValue =
|
|
|
20
20
|
| JsonValue[]
|
|
21
21
|
| JsonRecord
|
|
22
22
|
| ((...args: JsonValue[]) => JsonValue);
|
|
23
|
+
|
|
23
24
|
export interface JsonRecord {
|
|
24
25
|
[key: string]: JsonValue;
|
|
25
26
|
}
|
|
@@ -53,6 +54,7 @@ export function createInstrumentedSend(
|
|
|
53
54
|
const rawRest = rest.map(asJsonValue);
|
|
54
55
|
const name = isRecord(rawCommand) ? rawCommand.constructor?.name : undefined;
|
|
55
56
|
const handler = String(name) === name ? handlers[name] : undefined;
|
|
57
|
+
|
|
56
58
|
if (!handler) {
|
|
57
59
|
return originalSend.apply(this, [rawCommand, ...rawRest]);
|
|
58
60
|
}
|
|
@@ -64,25 +66,32 @@ export function createInstrumentedSend(
|
|
|
64
66
|
const fields = safe(() => handler.requestFields(input, options)) ?? {};
|
|
65
67
|
const span = startSpan(spanName, { type: spanType, ...fields });
|
|
66
68
|
const callbackIndex = rawRest.findIndex((arg) => arg instanceof Function);
|
|
69
|
+
|
|
67
70
|
if (callbackIndex !== -1) {
|
|
68
71
|
const callback = rawRest[callbackIndex] as (cause: JsonValue, data?: JsonValue) => void;
|
|
69
72
|
const wrappedRest = [...rawRest];
|
|
70
73
|
wrappedRest[callbackIndex] = <TError, TData>(error: TError, data?: TData) => {
|
|
71
74
|
const rawError: unknown = error;
|
|
72
75
|
const rawData: unknown = data;
|
|
76
|
+
|
|
73
77
|
if (rawError != null) {
|
|
74
78
|
endSpan(span, { ...awsMetadataFields(metadataOf(rawError)), error: asError(rawError) });
|
|
75
79
|
callback(asJsonValue(rawError), asJsonValue(rawData));
|
|
80
|
+
|
|
76
81
|
return;
|
|
77
82
|
}
|
|
83
|
+
|
|
78
84
|
let nextData: JsonValue | object = asJsonValue(rawData);
|
|
85
|
+
|
|
79
86
|
try {
|
|
80
87
|
nextData = handler.onResult(asJsonValue(rawData), span, t0, input, options);
|
|
81
88
|
} catch {
|
|
82
89
|
endSpan(span, awsMetadataFields(metadataOf(rawData)));
|
|
83
90
|
}
|
|
91
|
+
|
|
84
92
|
callback(asJsonValue(rawError), asJsonValue(nextData));
|
|
85
93
|
};
|
|
94
|
+
|
|
86
95
|
try {
|
|
87
96
|
return originalSend.apply(this, [rawCommand, ...wrappedRest]);
|
|
88
97
|
} catch (error) {
|
|
@@ -92,6 +101,7 @@ export function createInstrumentedSend(
|
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
let result: JsonValue | object | Promise<JsonValue | object>;
|
|
104
|
+
|
|
95
105
|
try {
|
|
96
106
|
result = originalSend.apply(this, [rawCommand, ...rawRest]);
|
|
97
107
|
} catch (error) {
|
|
@@ -105,6 +115,7 @@ export function createInstrumentedSend(
|
|
|
105
115
|
return handler.onResult(asJsonValue(resolved), span, t0, input, options);
|
|
106
116
|
} catch {
|
|
107
117
|
endSpan(span, awsMetadataFields(metadataOf(resolved)));
|
|
118
|
+
|
|
108
119
|
return resolved;
|
|
109
120
|
}
|
|
110
121
|
},
|
|
@@ -118,6 +129,7 @@ export function createInstrumentedSend(
|
|
|
118
129
|
|
|
119
130
|
export function commandInput<T>(command: T): JsonRecord {
|
|
120
131
|
if (!isRecord(command)) return {};
|
|
132
|
+
|
|
121
133
|
return isRecord(command.input) ? command.input : {};
|
|
122
134
|
}
|
|
123
135
|
|
|
@@ -151,6 +163,7 @@ export function updateSpan(span: SpanHandle, fields: SpanFields): void {
|
|
|
151
163
|
|
|
152
164
|
export function metadataOf<T>(value: T): JsonValue {
|
|
153
165
|
if (!isRecord(value)) return undefined;
|
|
166
|
+
|
|
154
167
|
return value.$metadata;
|
|
155
168
|
}
|
|
156
169
|
|
|
@@ -172,31 +185,42 @@ export function modeledStreamError(
|
|
|
172
185
|
throttlingException: "ThrottlingException",
|
|
173
186
|
validationException: "ValidationException",
|
|
174
187
|
} as const;
|
|
188
|
+
|
|
175
189
|
for (const [key, name] of Object.entries(keys)) {
|
|
176
190
|
const exception = isRecord(event[key]) ? event[key] : undefined;
|
|
191
|
+
|
|
177
192
|
if (!exception) continue;
|
|
193
|
+
|
|
178
194
|
const error = new Error(
|
|
179
195
|
stringValue(exception.message) ?? stringValue(exception.originalMessage) ?? name,
|
|
180
196
|
);
|
|
197
|
+
|
|
181
198
|
error.name = stringValue(exception.name) ?? name;
|
|
199
|
+
|
|
182
200
|
return { error, fields: awsMetadataFields(exception.$metadata) };
|
|
183
201
|
}
|
|
202
|
+
|
|
184
203
|
return undefined;
|
|
185
204
|
}
|
|
186
205
|
|
|
187
206
|
export function awsMetadataFields<T>(metadata: T): SpanFields {
|
|
188
207
|
const raw: unknown = metadata;
|
|
208
|
+
|
|
189
209
|
if (!isRecord(raw)) return {};
|
|
190
210
|
const requestId = stringValue(raw.requestId);
|
|
191
211
|
const attempts = numberValue(raw.attempts);
|
|
192
212
|
const httpStatusCode = numberValue(raw.httpStatusCode);
|
|
193
213
|
const totalRetryDelay = numberValue(raw.totalRetryDelay);
|
|
194
214
|
const attributes: SpanFields["attributes"] = {};
|
|
215
|
+
|
|
195
216
|
if (httpStatusCode !== undefined) attributes["aws.http.status_code"] = httpStatusCode;
|
|
217
|
+
|
|
196
218
|
if (attempts !== undefined && attempts > 1) attributes["aws.request.attempts"] = attempts;
|
|
219
|
+
|
|
197
220
|
if (totalRetryDelay !== undefined) {
|
|
198
221
|
attributes["aws.request.total_retry_delay_ms"] = totalRetryDelay;
|
|
199
222
|
}
|
|
223
|
+
|
|
200
224
|
return omitUndefined({
|
|
201
225
|
responseId: requestId,
|
|
202
226
|
attributes: Object.keys(attributes).length > 0 ? attributes : undefined,
|
|
@@ -205,14 +229,19 @@ export function awsMetadataFields<T>(metadata: T): SpanFields {
|
|
|
205
229
|
|
|
206
230
|
export function mergeFields(...fields: Array<SpanFields | undefined>): SpanFields {
|
|
207
231
|
const merged: SpanFields = {};
|
|
232
|
+
|
|
208
233
|
for (const field of fields) {
|
|
209
234
|
if (!field) continue;
|
|
210
235
|
const { metadata, attributes, usage, ...rest } = field;
|
|
211
236
|
Object.assign(merged, omitUndefined(rest));
|
|
237
|
+
|
|
212
238
|
if (usage) merged.usage = { ...merged.usage, ...usage };
|
|
239
|
+
|
|
213
240
|
if (metadata) merged.metadata = { ...merged.metadata, ...metadata };
|
|
241
|
+
|
|
214
242
|
if (attributes) merged.attributes = { ...merged.attributes, ...attributes };
|
|
215
243
|
}
|
|
244
|
+
|
|
216
245
|
return merged;
|
|
217
246
|
}
|
|
218
247
|
|
|
@@ -222,11 +251,13 @@ export function omitUndefined<T extends object>(value: T): T {
|
|
|
222
251
|
|
|
223
252
|
export function stringValue<T>(value: T): string | undefined {
|
|
224
253
|
const raw: unknown = value;
|
|
254
|
+
|
|
225
255
|
return String(raw) === raw && raw.length > 0 ? raw : undefined;
|
|
226
256
|
}
|
|
227
257
|
|
|
228
258
|
export function numberValue<T>(value: T): number | undefined {
|
|
229
259
|
const raw: unknown = value;
|
|
260
|
+
|
|
230
261
|
return Number(raw) === raw && Number.isFinite(raw) ? raw : undefined;
|
|
231
262
|
}
|
|
232
263
|
|
|
@@ -236,8 +267,11 @@ export function arrayValue<T, TValue = unknown>(value: TValue): T[] | undefined
|
|
|
236
267
|
|
|
237
268
|
export function bytesToString<T>(value: T): string | undefined {
|
|
238
269
|
const raw: unknown = value;
|
|
270
|
+
|
|
239
271
|
if (String(raw) === raw) return raw;
|
|
272
|
+
|
|
240
273
|
if (raw instanceof Uint8Array) return new TextDecoder().decode(raw);
|
|
274
|
+
|
|
241
275
|
if (ArrayBuffer.isView(raw)) {
|
|
242
276
|
return new TextDecoder().decode(new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength));
|
|
243
277
|
}
|
|
@@ -245,7 +279,9 @@ export function bytesToString<T>(value: T): string | undefined {
|
|
|
245
279
|
|
|
246
280
|
export function parseJson<T>(value: T): JsonValue {
|
|
247
281
|
const text = bytesToString(value);
|
|
282
|
+
|
|
248
283
|
if (text === undefined) return undefined;
|
|
284
|
+
|
|
249
285
|
try {
|
|
250
286
|
return asJsonValue(JSON.parse(text));
|
|
251
287
|
} catch {
|
|
@@ -254,7 +290,7 @@ export function parseJson<T>(value: T): JsonValue {
|
|
|
254
290
|
}
|
|
255
291
|
|
|
256
292
|
export interface StreamState {
|
|
257
|
-
feed<T>(event: T): void;
|
|
293
|
+
feed<T>(event: T): boolean | void;
|
|
258
294
|
finish(partial: boolean): SpanFields;
|
|
259
295
|
}
|
|
260
296
|
|
|
@@ -264,15 +300,18 @@ export function wrapAsyncIterable<T>(
|
|
|
264
300
|
span: SpanHandle,
|
|
265
301
|
t0: number,
|
|
266
302
|
baseFields: SpanFields,
|
|
303
|
+
trackOutputChunks = true,
|
|
267
304
|
): T {
|
|
268
305
|
if (!iterable || !isAsyncIterable(iterable)) {
|
|
269
306
|
endSpan(span, baseFields);
|
|
307
|
+
|
|
270
308
|
return iterable;
|
|
271
309
|
}
|
|
272
310
|
|
|
273
311
|
let ended = false;
|
|
274
312
|
let first = false;
|
|
275
313
|
const unregisterToken = {};
|
|
314
|
+
|
|
276
315
|
const endOnce = (fields?: SpanFields) => {
|
|
277
316
|
if (ended) return;
|
|
278
317
|
ended = true;
|
|
@@ -282,15 +321,25 @@ export function wrapAsyncIterable<T>(
|
|
|
282
321
|
|
|
283
322
|
const generator = async function* () {
|
|
284
323
|
let completed = false;
|
|
324
|
+
|
|
285
325
|
try {
|
|
286
326
|
for await (const event of iterable) {
|
|
327
|
+
const receivedAt = performance.now();
|
|
328
|
+
|
|
287
329
|
if (!first) {
|
|
288
330
|
first = true;
|
|
289
|
-
updateSpan(span, { timeToFirstChunkMs:
|
|
331
|
+
updateSpan(span, { timeToFirstChunkMs: receivedAt - t0 });
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const stateHasOutput = safe(() => state.feed(event));
|
|
335
|
+
|
|
336
|
+
if (trackOutputChunks && (stateHasOutput ?? bedrockEventHasOutput(event))) {
|
|
337
|
+
safe(() => span.recordOutputChunk?.(receivedAt));
|
|
290
338
|
}
|
|
291
|
-
|
|
339
|
+
|
|
292
340
|
yield event;
|
|
293
341
|
}
|
|
342
|
+
|
|
294
343
|
completed = true;
|
|
295
344
|
} catch (error) {
|
|
296
345
|
endOnce(
|
|
@@ -305,21 +354,90 @@ export function wrapAsyncIterable<T>(
|
|
|
305
354
|
};
|
|
306
355
|
|
|
307
356
|
const wrapped: { [key: PropertyKey]: JsonValue | (() => AsyncGenerator<unknown>) } = {};
|
|
357
|
+
|
|
308
358
|
if (isRecord(iterable)) {
|
|
309
359
|
for (const key of Reflect.ownKeys(iterable)) {
|
|
310
360
|
wrapped[key] = (iterable as { [key: PropertyKey]: JsonValue })[key];
|
|
311
361
|
}
|
|
312
362
|
}
|
|
363
|
+
|
|
313
364
|
const finishAbandoned = () => endOnce(safe(() => state.finish(true)));
|
|
314
365
|
wrapped[Symbol.asyncIterator] = () => {
|
|
315
366
|
const iterator = generator();
|
|
316
367
|
STREAM_FINALIZER?.register(iterator, finishAbandoned, unregisterToken);
|
|
368
|
+
|
|
317
369
|
return iterator;
|
|
318
370
|
};
|
|
371
|
+
|
|
319
372
|
STREAM_FINALIZER?.register(wrapped, finishAbandoned, unregisterToken);
|
|
373
|
+
|
|
320
374
|
return wrapped as T;
|
|
321
375
|
}
|
|
322
376
|
|
|
377
|
+
function bedrockEventHasOutput(event: unknown): boolean {
|
|
378
|
+
const record = isRecord(event) ? event : undefined;
|
|
379
|
+
|
|
380
|
+
if (!record) return false;
|
|
381
|
+
const output = isRecord(record.output) ? record.output : undefined;
|
|
382
|
+
|
|
383
|
+
if (typeof output?.text === "string" && output.text.length > 0) return true;
|
|
384
|
+
const block = isRecord(record.contentBlockDelta) ? record.contentBlockDelta : undefined;
|
|
385
|
+
const delta = isRecord(block?.delta) ? block.delta : undefined;
|
|
386
|
+
const reasoning = isRecord(delta?.reasoningContent) ? delta.reasoningContent : undefined;
|
|
387
|
+
const toolUse = isRecord(delta?.toolUse) ? delta.toolUse : undefined;
|
|
388
|
+
|
|
389
|
+
if (
|
|
390
|
+
(typeof delta?.text === "string" && delta.text.length > 0) ||
|
|
391
|
+
(typeof reasoning?.text === "string" && reasoning.text.length > 0) ||
|
|
392
|
+
(typeof toolUse?.input === "string" && toolUse.input.length > 0)
|
|
393
|
+
)
|
|
394
|
+
return true;
|
|
395
|
+
const chunk = isRecord(record.chunk) ? record.chunk : undefined;
|
|
396
|
+
const raw = chunk?.bytes;
|
|
397
|
+
|
|
398
|
+
if (raw === undefined) return false;
|
|
399
|
+
|
|
400
|
+
const text =
|
|
401
|
+
typeof raw === "string"
|
|
402
|
+
? raw
|
|
403
|
+
: raw instanceof Uint8Array
|
|
404
|
+
? new TextDecoder().decode(raw)
|
|
405
|
+
: undefined;
|
|
406
|
+
|
|
407
|
+
if (!text) return false;
|
|
408
|
+
const parsed = parseJson(text);
|
|
409
|
+
|
|
410
|
+
if (parsed === undefined) return text.length > 0;
|
|
411
|
+
|
|
412
|
+
if (!isRecord(parsed)) return false;
|
|
413
|
+
const parsedDelta = isRecord(parsed.delta) ? parsed.delta : undefined;
|
|
414
|
+
|
|
415
|
+
const contentDelta = isRecord(parsed.contentBlockDelta ?? parsed.content_block_delta)
|
|
416
|
+
? (parsed.contentBlockDelta ?? parsed.content_block_delta)
|
|
417
|
+
: undefined;
|
|
418
|
+
|
|
419
|
+
const nested =
|
|
420
|
+
isRecord(contentDelta) && isRecord(contentDelta.delta) ? contentDelta.delta : undefined;
|
|
421
|
+
|
|
422
|
+
const generations = Array.isArray(parsed.generations) ? parsed.generations : [];
|
|
423
|
+
const generation = isRecord(generations[0]) ? generations[0] : undefined;
|
|
424
|
+
const outputs = Array.isArray(parsed.outputs) ? parsed.outputs : [];
|
|
425
|
+
const firstOutput = isRecord(outputs[0]) ? outputs[0] : undefined;
|
|
426
|
+
|
|
427
|
+
return [
|
|
428
|
+
parsed.outputText,
|
|
429
|
+
parsed.generation,
|
|
430
|
+
parsed.completion,
|
|
431
|
+
parsed.text,
|
|
432
|
+
parsedDelta?.text,
|
|
433
|
+
parsedDelta?.thinking,
|
|
434
|
+
parsedDelta?.partial_json,
|
|
435
|
+
nested?.text,
|
|
436
|
+
firstOutput?.text,
|
|
437
|
+
generation?.text,
|
|
438
|
+
].some((value) => typeof value === "string" && value.length > 0);
|
|
439
|
+
}
|
|
440
|
+
|
|
323
441
|
function isAsyncIterable<T>(value: T): value is T & AsyncIterable<unknown> {
|
|
324
442
|
return Object(value) === value && Symbol.asyncIterator in Object(value);
|
|
325
443
|
}
|
|
@@ -334,6 +452,8 @@ function asError<T>(value: T): Error {
|
|
|
334
452
|
|
|
335
453
|
export function basenameArn<T>(value: T): string | undefined {
|
|
336
454
|
const text = stringValue(value);
|
|
455
|
+
|
|
337
456
|
if (!text) return undefined;
|
|
457
|
+
|
|
338
458
|
return text.split(/[/:]/).filter(Boolean).at(-1) ?? text;
|
|
339
459
|
}
|
package/src/invoke-model.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
|
|
19
19
|
export function isEmbeddingModel<T>(modelId: T): boolean {
|
|
20
20
|
const model = stringValue(modelId)?.toLowerCase() ?? "";
|
|
21
|
+
|
|
21
22
|
return (
|
|
22
23
|
model.includes("titan-embed") || model.includes("cohere.embed") || model.includes("-embedding")
|
|
23
24
|
);
|
|
@@ -25,12 +26,14 @@ export function isEmbeddingModel<T>(modelId: T): boolean {
|
|
|
25
26
|
|
|
26
27
|
export function invokeModelSpanName(input: JsonRecord): string {
|
|
27
28
|
const model = stringValue(input.modelId) ?? "unknown";
|
|
29
|
+
|
|
28
30
|
return `${isEmbeddingModel(model) ? "embeddings" : "chat"} ${model}`;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export function invokeModelRequestFields(input: JsonRecord): SpanFields {
|
|
32
34
|
const model = stringValue(input.modelId);
|
|
33
35
|
const body = parseBody(input.body, input.contentType);
|
|
36
|
+
|
|
34
37
|
return omitUndefined({
|
|
35
38
|
provider: PROVIDER,
|
|
36
39
|
model,
|
|
@@ -44,20 +47,25 @@ export function invokeModelResponseFields(input: JsonRecord, output: JsonValue):
|
|
|
44
47
|
const model = stringValue(input.modelId);
|
|
45
48
|
const body = isRecord(output) ? parseBody(output.body, output.contentType) : undefined;
|
|
46
49
|
const metadata = isRecord(output) ? output.$metadata : undefined;
|
|
50
|
+
|
|
47
51
|
return mergeFields(awsMetadataFields(metadata), nativeResponseFields(model, body, metadata));
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
export function parseBody<TBody, TContentType>(body: TBody, contentType: TContentType): JsonValue {
|
|
51
55
|
const type = stringValue(contentType) ?? "application/json";
|
|
56
|
+
|
|
52
57
|
if (!type.includes("json")) return undefined;
|
|
58
|
+
|
|
53
59
|
return parseJson(body);
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
function nativeSamplingFields<T>(model: string | undefined, body: T): SpanFields {
|
|
57
63
|
if (!isRecord(body)) return {};
|
|
58
64
|
const lower = model?.toLowerCase() ?? "";
|
|
65
|
+
|
|
59
66
|
if (lower.includes("amazon.titan")) {
|
|
60
67
|
const cfg = isRecord(body.textGenerationConfig) ? body.textGenerationConfig : {};
|
|
68
|
+
|
|
61
69
|
return omitUndefined({
|
|
62
70
|
temperature: numberValue(cfg.temperature),
|
|
63
71
|
topP: numberValue(cfg.topP),
|
|
@@ -65,8 +73,10 @@ function nativeSamplingFields<T>(model: string | undefined, body: T): SpanFields
|
|
|
65
73
|
stopSequences: arrayValue<string>(cfg.stopSequences),
|
|
66
74
|
});
|
|
67
75
|
}
|
|
76
|
+
|
|
68
77
|
if (lower.includes("amazon.nova")) {
|
|
69
78
|
const cfg = isRecord(body.inferenceConfig) ? body.inferenceConfig : {};
|
|
79
|
+
|
|
70
80
|
return omitUndefined({
|
|
71
81
|
temperature: numberValue(cfg.temperature),
|
|
72
82
|
topP: numberValue(cfg.topP) ?? numberValue(cfg.top_p),
|
|
@@ -75,6 +85,7 @@ function nativeSamplingFields<T>(model: string | undefined, body: T): SpanFields
|
|
|
75
85
|
stopSequences: arrayValue<string>(cfg.stopSequences),
|
|
76
86
|
});
|
|
77
87
|
}
|
|
88
|
+
|
|
78
89
|
if (lower.includes("anthropic.claude")) {
|
|
79
90
|
return omitUndefined({
|
|
80
91
|
temperature: numberValue(body.temperature),
|
|
@@ -84,6 +95,7 @@ function nativeSamplingFields<T>(model: string | undefined, body: T): SpanFields
|
|
|
84
95
|
stopSequences: arrayValue<string>(body.stop_sequences),
|
|
85
96
|
});
|
|
86
97
|
}
|
|
98
|
+
|
|
87
99
|
if (lower.includes("meta.llama")) {
|
|
88
100
|
return omitUndefined({
|
|
89
101
|
temperature: numberValue(body.temperature),
|
|
@@ -91,6 +103,7 @@ function nativeSamplingFields<T>(model: string | undefined, body: T): SpanFields
|
|
|
91
103
|
maxTokens: numberValue(body.max_gen_len),
|
|
92
104
|
});
|
|
93
105
|
}
|
|
106
|
+
|
|
94
107
|
return omitUndefined({
|
|
95
108
|
temperature: numberValue(body.temperature),
|
|
96
109
|
topP: numberValue(body.top_p) ?? numberValue(body.topP),
|
|
@@ -105,6 +118,7 @@ function nativeResponseFields(
|
|
|
105
118
|
): SpanFields {
|
|
106
119
|
if (!isRecord(body)) return headerUsageFields(metadata);
|
|
107
120
|
const lower = model?.toLowerCase() ?? "";
|
|
121
|
+
|
|
108
122
|
if (isEmbeddingModel(model)) {
|
|
109
123
|
return withHeaderUsage(
|
|
110
124
|
{
|
|
@@ -115,8 +129,10 @@ function nativeResponseFields(
|
|
|
115
129
|
metadata,
|
|
116
130
|
);
|
|
117
131
|
}
|
|
132
|
+
|
|
118
133
|
if (lower.includes("amazon.titan")) {
|
|
119
134
|
const first = Array.isArray(body.results) && isRecord(body.results[0]) ? body.results[0] : {};
|
|
135
|
+
|
|
120
136
|
return omitUndefined({
|
|
121
137
|
output: body,
|
|
122
138
|
finishReason: stringValue(first.completionReason),
|
|
@@ -126,8 +142,10 @@ function nativeResponseFields(
|
|
|
126
142
|
}),
|
|
127
143
|
});
|
|
128
144
|
}
|
|
145
|
+
|
|
129
146
|
if (lower.includes("amazon.nova")) {
|
|
130
147
|
const usage = isRecord(body.usage) ? body.usage : {};
|
|
148
|
+
|
|
131
149
|
return omitUndefined({
|
|
132
150
|
output: body,
|
|
133
151
|
finishReason: stringValue(body.stopReason),
|
|
@@ -138,8 +156,10 @@ function nativeResponseFields(
|
|
|
138
156
|
}),
|
|
139
157
|
});
|
|
140
158
|
}
|
|
159
|
+
|
|
141
160
|
if (lower.includes("anthropic.claude")) {
|
|
142
161
|
const usage = isRecord(body.usage) ? body.usage : {};
|
|
162
|
+
|
|
143
163
|
return withHeaderUsage(
|
|
144
164
|
{
|
|
145
165
|
output: body,
|
|
@@ -154,6 +174,7 @@ function nativeResponseFields(
|
|
|
154
174
|
metadata,
|
|
155
175
|
);
|
|
156
176
|
}
|
|
177
|
+
|
|
157
178
|
if (lower.includes("meta.llama")) {
|
|
158
179
|
return omitUndefined({
|
|
159
180
|
output: body,
|
|
@@ -164,32 +185,39 @@ function nativeResponseFields(
|
|
|
164
185
|
}),
|
|
165
186
|
});
|
|
166
187
|
}
|
|
188
|
+
|
|
167
189
|
if (lower.includes("mistral")) {
|
|
168
190
|
const first = Array.isArray(body.outputs) && isRecord(body.outputs[0]) ? body.outputs[0] : {};
|
|
191
|
+
|
|
169
192
|
return withHeaderUsage(
|
|
170
193
|
{ output: body, finishReason: stringValue(first.stop_reason) },
|
|
171
194
|
metadata,
|
|
172
195
|
);
|
|
173
196
|
}
|
|
197
|
+
|
|
174
198
|
if (lower.includes("cohere.command-r")) {
|
|
175
199
|
return withHeaderUsage(
|
|
176
200
|
{ output: body, finishReason: stringValue(body.finish_reason) },
|
|
177
201
|
metadata,
|
|
178
202
|
);
|
|
179
203
|
}
|
|
204
|
+
|
|
180
205
|
if (lower.includes("cohere.command")) {
|
|
181
206
|
const first =
|
|
182
207
|
Array.isArray(body.generations) && isRecord(body.generations[0]) ? body.generations[0] : {};
|
|
208
|
+
|
|
183
209
|
return withHeaderUsage(
|
|
184
210
|
{ output: body, finishReason: stringValue(first.finish_reason) },
|
|
185
211
|
metadata,
|
|
186
212
|
);
|
|
187
213
|
}
|
|
214
|
+
|
|
188
215
|
return withHeaderUsage({ output: body }, metadata);
|
|
189
216
|
}
|
|
190
217
|
|
|
191
218
|
function withHeaderUsage<T>(fields: SpanFields, metadata: T): SpanFields {
|
|
192
219
|
if (fields.usage && Object.keys(fields.usage).length > 0) return fields;
|
|
220
|
+
|
|
193
221
|
return mergeFields(fields, headerUsageFields(metadata));
|
|
194
222
|
}
|
|
195
223
|
|
|
@@ -201,19 +229,24 @@ function headerUsageFields<T>(metadata: T): SpanFields {
|
|
|
201
229
|
? metadata.HTTPHeaders
|
|
202
230
|
: undefined
|
|
203
231
|
: undefined;
|
|
232
|
+
|
|
204
233
|
const usage = omitUndefined({
|
|
205
234
|
inputTokens: headerNumber(headers?.["x-amzn-bedrock-input-token-count"]),
|
|
206
235
|
outputTokens: headerNumber(headers?.["x-amzn-bedrock-output-token-count"]),
|
|
207
236
|
});
|
|
237
|
+
|
|
208
238
|
return { usage: Object.keys(usage).length > 0 ? usage : undefined };
|
|
209
239
|
}
|
|
210
240
|
|
|
211
241
|
function headerNumber<T>(value: T): number | undefined {
|
|
212
242
|
const text = stringValue(value);
|
|
243
|
+
|
|
213
244
|
if (text !== undefined) {
|
|
214
245
|
const parsed = Number.parseInt(text, 10);
|
|
246
|
+
|
|
215
247
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
216
248
|
}
|
|
249
|
+
|
|
217
250
|
return numberValue(value);
|
|
218
251
|
}
|
|
219
252
|
|
|
@@ -224,35 +257,49 @@ export class InvokeModelStreamState implements StreamState {
|
|
|
224
257
|
private finishReason: string | undefined;
|
|
225
258
|
private error: Error | undefined;
|
|
226
259
|
private errorFields: SpanFields | undefined;
|
|
227
|
-
feed<T>(event: T):
|
|
228
|
-
if (!isRecord(event)) return;
|
|
260
|
+
feed<T>(event: T): boolean {
|
|
261
|
+
if (!isRecord(event)) return false;
|
|
229
262
|
const streamError = modeledStreamError(event);
|
|
263
|
+
|
|
230
264
|
if (streamError) {
|
|
231
265
|
this.error = streamError.error;
|
|
232
266
|
this.errorFields = streamError.fields;
|
|
233
|
-
|
|
267
|
+
|
|
268
|
+
return false;
|
|
234
269
|
}
|
|
270
|
+
|
|
235
271
|
const chunk = isRecord(event.chunk) ? event.chunk : undefined;
|
|
236
272
|
const parsed = parseJson(chunk?.bytes);
|
|
237
|
-
|
|
273
|
+
|
|
274
|
+
if (parsed === undefined) {
|
|
275
|
+
const raw = chunk?.bytes;
|
|
276
|
+
|
|
277
|
+
return (typeof raw === "string" || raw instanceof Uint8Array) && raw.length > 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
238
280
|
this.chunks.push(parsed);
|
|
239
281
|
this.text += textFromProviderChunk(parsed);
|
|
282
|
+
|
|
240
283
|
if (isRecord(parsed)) {
|
|
241
284
|
const delta = isRecord(parsed.delta) ? parsed.delta : undefined;
|
|
242
285
|
const message = isRecord(parsed.message) ? parsed.message : undefined;
|
|
243
286
|
const messageUsage = isRecord(message?.usage) ? message?.usage : undefined;
|
|
244
287
|
const usage = isRecord(parsed.usage) ? parsed.usage : undefined;
|
|
288
|
+
|
|
245
289
|
const metrics = isRecord(parsed["amazon-bedrock-invocationMetrics"])
|
|
246
290
|
? parsed["amazon-bedrock-invocationMetrics"]
|
|
247
291
|
: undefined;
|
|
292
|
+
|
|
248
293
|
const outputs =
|
|
249
294
|
Array.isArray(parsed.outputs) && isRecord(parsed.outputs[0])
|
|
250
295
|
? parsed.outputs[0]
|
|
251
296
|
: undefined;
|
|
297
|
+
|
|
252
298
|
const generation =
|
|
253
299
|
Array.isArray(parsed.generations) && isRecord(parsed.generations[0])
|
|
254
300
|
? parsed.generations[0]
|
|
255
301
|
: undefined;
|
|
302
|
+
|
|
256
303
|
this.finishReason =
|
|
257
304
|
stringValue(delta?.stop_reason) ??
|
|
258
305
|
stringValue(parsed.stop_reason) ??
|
|
@@ -279,6 +326,8 @@ export class InvokeModelStreamState implements StreamState {
|
|
|
279
326
|
this.usage?.cacheCreationInputTokens,
|
|
280
327
|
});
|
|
281
328
|
}
|
|
329
|
+
|
|
330
|
+
return providerChunkHasOutput(parsed);
|
|
282
331
|
}
|
|
283
332
|
|
|
284
333
|
finish(): SpanFields {
|
|
@@ -305,16 +354,20 @@ export class InvokeModelStreamState implements StreamState {
|
|
|
305
354
|
function textFromProviderChunk<T>(value: T): string {
|
|
306
355
|
if (!isRecord(value)) return "";
|
|
307
356
|
const delta = isRecord(value.delta) ? value.delta : undefined;
|
|
357
|
+
|
|
308
358
|
const contentBlockDelta = isRecord(value.contentBlockDelta)
|
|
309
359
|
? value.contentBlockDelta
|
|
310
360
|
: isRecord(value.content_block_delta)
|
|
311
361
|
? value.content_block_delta
|
|
312
362
|
: undefined;
|
|
363
|
+
|
|
313
364
|
const nestedDelta = isRecord(contentBlockDelta?.delta) ? contentBlockDelta?.delta : undefined;
|
|
365
|
+
|
|
314
366
|
const generation =
|
|
315
367
|
Array.isArray(value.generations) && isRecord(value.generations[0])
|
|
316
368
|
? value.generations[0]
|
|
317
369
|
: undefined;
|
|
370
|
+
|
|
318
371
|
return (
|
|
319
372
|
stringValue(value.outputText) ??
|
|
320
373
|
stringValue(value.generation) ??
|
|
@@ -329,3 +382,14 @@ function textFromProviderChunk<T>(value: T): string {
|
|
|
329
382
|
""
|
|
330
383
|
);
|
|
331
384
|
}
|
|
385
|
+
|
|
386
|
+
function providerChunkHasOutput<T>(value: T): boolean {
|
|
387
|
+
if (!isRecord(value)) return false;
|
|
388
|
+
const delta = isRecord(value.delta) ? value.delta : undefined;
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
textFromProviderChunk(value).length > 0 ||
|
|
392
|
+
(typeof delta?.thinking === "string" && delta.thinking.length > 0) ||
|
|
393
|
+
(typeof delta?.partial_json === "string" && delta.partial_json.length > 0)
|
|
394
|
+
);
|
|
395
|
+
}
|