@telemetry-dev/bedrock 0.1.0 → 0.1.1
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/internal.ts
CHANGED
|
@@ -11,97 +11,118 @@ export interface BedrockInstrumentationOptions {
|
|
|
11
11
|
captureAgentTrace?: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export type
|
|
14
|
+
export type JsonValue =
|
|
15
|
+
| string
|
|
16
|
+
| number
|
|
17
|
+
| boolean
|
|
18
|
+
| null
|
|
19
|
+
| undefined
|
|
20
|
+
| JsonValue[]
|
|
21
|
+
| JsonRecord
|
|
22
|
+
| ((...args: JsonValue[]) => JsonValue);
|
|
23
|
+
export interface JsonRecord {
|
|
24
|
+
[key: string]: JsonValue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type SendFn = (...args: unknown[]) => JsonValue | object | Promise<JsonValue | object>;
|
|
15
28
|
|
|
16
29
|
export interface OpHandler {
|
|
17
|
-
spanName(input:
|
|
18
|
-
spanType(input:
|
|
19
|
-
requestFields(input:
|
|
30
|
+
spanName(input: JsonRecord): string;
|
|
31
|
+
spanType(input: JsonRecord): SpanType;
|
|
32
|
+
requestFields(input: JsonRecord, options: BedrockInstrumentationOptions): SpanFields;
|
|
20
33
|
onResult(
|
|
21
|
-
result:
|
|
34
|
+
result: JsonValue,
|
|
22
35
|
span: SpanHandle,
|
|
23
36
|
t0: number,
|
|
24
|
-
input:
|
|
37
|
+
input: JsonRecord,
|
|
25
38
|
options: BedrockInstrumentationOptions,
|
|
26
|
-
):
|
|
39
|
+
): JsonValue | object;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
export function createInstrumentedSend(
|
|
30
43
|
originalSend: SendFn,
|
|
31
44
|
options: BedrockInstrumentationOptions,
|
|
32
|
-
handlers:
|
|
45
|
+
handlers: { [key: string]: OpHandler },
|
|
33
46
|
): SendFn {
|
|
34
|
-
return function instrumentedSend
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
return function instrumentedSend<TThis, TCommand, TRest extends unknown[]>(
|
|
48
|
+
this: TThis,
|
|
49
|
+
command: TCommand,
|
|
50
|
+
...rest: TRest
|
|
51
|
+
) {
|
|
52
|
+
const rawCommand = asJsonValue(command);
|
|
53
|
+
const rawRest = rest.map(asJsonValue);
|
|
54
|
+
const name = isRecord(rawCommand) ? rawCommand.constructor?.name : undefined;
|
|
55
|
+
const handler = String(name) === name ? handlers[name] : undefined;
|
|
37
56
|
if (!handler) {
|
|
38
|
-
return originalSend.apply(this, [
|
|
57
|
+
return originalSend.apply(this, [rawCommand, ...rawRest]);
|
|
39
58
|
}
|
|
40
59
|
|
|
41
|
-
const input = commandInput(
|
|
60
|
+
const input = commandInput(rawCommand);
|
|
42
61
|
const t0 = performance.now();
|
|
43
62
|
const spanType = safe(() => handler.spanType(input)) ?? "span";
|
|
44
63
|
const spanName = safe(() => handler.spanName(input)) ?? name ?? "bedrock";
|
|
45
64
|
const fields = safe(() => handler.requestFields(input, options)) ?? {};
|
|
46
65
|
const span = startSpan(spanName, { type: spanType, ...fields });
|
|
47
|
-
const callbackIndex =
|
|
66
|
+
const callbackIndex = rawRest.findIndex((arg) => arg instanceof Function);
|
|
48
67
|
if (callbackIndex !== -1) {
|
|
49
|
-
const callback =
|
|
50
|
-
const wrappedRest = [...
|
|
51
|
-
wrappedRest[callbackIndex] = (error:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
const callback = rawRest[callbackIndex] as (cause: JsonValue, data?: JsonValue) => void;
|
|
69
|
+
const wrappedRest = [...rawRest];
|
|
70
|
+
wrappedRest[callbackIndex] = <TError, TData>(error: TError, data?: TData) => {
|
|
71
|
+
const rawError: unknown = error;
|
|
72
|
+
const rawData: unknown = data;
|
|
73
|
+
if (rawError != null) {
|
|
74
|
+
endSpan(span, { ...awsMetadataFields(metadataOf(rawError)), error: asError(rawError) });
|
|
75
|
+
callback(asJsonValue(rawError), asJsonValue(rawData));
|
|
55
76
|
return;
|
|
56
77
|
}
|
|
57
|
-
let nextData =
|
|
78
|
+
let nextData: JsonValue | object = asJsonValue(rawData);
|
|
58
79
|
try {
|
|
59
|
-
nextData = handler.onResult(
|
|
80
|
+
nextData = handler.onResult(asJsonValue(rawData), span, t0, input, options);
|
|
60
81
|
} catch {
|
|
61
|
-
endSpan(span, awsMetadataFields(metadataOf(
|
|
82
|
+
endSpan(span, awsMetadataFields(metadataOf(rawData)));
|
|
62
83
|
}
|
|
63
|
-
callback(
|
|
84
|
+
callback(asJsonValue(rawError), asJsonValue(nextData));
|
|
64
85
|
};
|
|
65
86
|
try {
|
|
66
|
-
return originalSend.apply(this, [
|
|
87
|
+
return originalSend.apply(this, [rawCommand, ...wrappedRest]);
|
|
67
88
|
} catch (error) {
|
|
68
|
-
endSpan(span, { ...awsMetadataFields(metadataOf(error)), error });
|
|
89
|
+
endSpan(span, { ...awsMetadataFields(metadataOf(error)), error: asError(error) });
|
|
69
90
|
throw error;
|
|
70
91
|
}
|
|
71
92
|
}
|
|
72
93
|
|
|
73
|
-
let result:
|
|
94
|
+
let result: JsonValue | object | Promise<JsonValue | object>;
|
|
74
95
|
try {
|
|
75
|
-
result = originalSend.apply(this, [
|
|
96
|
+
result = originalSend.apply(this, [rawCommand, ...rawRest]);
|
|
76
97
|
} catch (error) {
|
|
77
|
-
endSpan(span, { ...awsMetadataFields(metadataOf(error)), error });
|
|
98
|
+
endSpan(span, { ...awsMetadataFields(metadataOf(error)), error: asError(error) });
|
|
78
99
|
throw error;
|
|
79
100
|
}
|
|
80
101
|
|
|
81
102
|
return Promise.resolve(result).then(
|
|
82
103
|
(resolved) => {
|
|
83
104
|
try {
|
|
84
|
-
return handler.onResult(resolved, span, t0, input, options);
|
|
105
|
+
return handler.onResult(asJsonValue(resolved), span, t0, input, options);
|
|
85
106
|
} catch {
|
|
86
107
|
endSpan(span, awsMetadataFields(metadataOf(resolved)));
|
|
87
108
|
return resolved;
|
|
88
109
|
}
|
|
89
110
|
},
|
|
90
111
|
(error) => {
|
|
91
|
-
endSpan(span, { ...awsMetadataFields(metadataOf(error)), error });
|
|
112
|
+
endSpan(span, { ...awsMetadataFields(metadataOf(error)), error: asError(error) });
|
|
92
113
|
throw error;
|
|
93
114
|
},
|
|
94
115
|
);
|
|
95
116
|
};
|
|
96
117
|
}
|
|
97
118
|
|
|
98
|
-
export function commandInput(command:
|
|
99
|
-
|
|
100
|
-
return isRecord(
|
|
119
|
+
export function commandInput<T>(command: T): JsonRecord {
|
|
120
|
+
if (!isRecord(command)) return {};
|
|
121
|
+
return isRecord(command.input) ? command.input : {};
|
|
101
122
|
}
|
|
102
123
|
|
|
103
|
-
export function isRecord(value:
|
|
104
|
-
return value !== null &&
|
|
124
|
+
export function isRecord<T>(value: T): value is T & JsonRecord {
|
|
125
|
+
return value !== null && Object(value) === value && !Array.isArray(value);
|
|
105
126
|
}
|
|
106
127
|
|
|
107
128
|
export function safe<T>(fn: () => T): T | undefined {
|
|
@@ -128,15 +149,15 @@ export function updateSpan(span: SpanHandle, fields: SpanFields): void {
|
|
|
128
149
|
}
|
|
129
150
|
}
|
|
130
151
|
|
|
131
|
-
export function metadataOf(value:
|
|
132
|
-
if (isRecord(value)
|
|
133
|
-
return
|
|
152
|
+
export function metadataOf<T>(value: T): JsonValue {
|
|
153
|
+
if (!isRecord(value)) return undefined;
|
|
154
|
+
return value.$metadata;
|
|
134
155
|
}
|
|
135
156
|
|
|
136
157
|
export function modeledStreamError(
|
|
137
|
-
event:
|
|
158
|
+
event: JsonRecord,
|
|
138
159
|
): { error: Error; fields: SpanFields } | undefined {
|
|
139
|
-
const keys
|
|
160
|
+
const keys = {
|
|
140
161
|
accessDeniedException: "AccessDeniedException",
|
|
141
162
|
badGatewayException: "BadGatewayException",
|
|
142
163
|
conflictException: "ConflictException",
|
|
@@ -150,7 +171,7 @@ export function modeledStreamError(
|
|
|
150
171
|
serviceUnavailableException: "ServiceUnavailableException",
|
|
151
172
|
throttlingException: "ThrottlingException",
|
|
152
173
|
validationException: "ValidationException",
|
|
153
|
-
};
|
|
174
|
+
} as const;
|
|
154
175
|
for (const [key, name] of Object.entries(keys)) {
|
|
155
176
|
const exception = isRecord(event[key]) ? event[key] : undefined;
|
|
156
177
|
if (!exception) continue;
|
|
@@ -163,12 +184,13 @@ export function modeledStreamError(
|
|
|
163
184
|
return undefined;
|
|
164
185
|
}
|
|
165
186
|
|
|
166
|
-
export function awsMetadataFields(metadata:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
const
|
|
187
|
+
export function awsMetadataFields<T>(metadata: T): SpanFields {
|
|
188
|
+
const raw: unknown = metadata;
|
|
189
|
+
if (!isRecord(raw)) return {};
|
|
190
|
+
const requestId = stringValue(raw.requestId);
|
|
191
|
+
const attempts = numberValue(raw.attempts);
|
|
192
|
+
const httpStatusCode = numberValue(raw.httpStatusCode);
|
|
193
|
+
const totalRetryDelay = numberValue(raw.totalRetryDelay);
|
|
172
194
|
const attributes: SpanFields["attributes"] = {};
|
|
173
195
|
if (httpStatusCode !== undefined) attributes["aws.http.status_code"] = httpStatusCode;
|
|
174
196
|
if (attempts !== undefined && attempts > 1) attributes["aws.request.attempts"] = attempts;
|
|
@@ -194,41 +216,45 @@ export function mergeFields(...fields: Array<SpanFields | undefined>): SpanField
|
|
|
194
216
|
return merged;
|
|
195
217
|
}
|
|
196
218
|
|
|
197
|
-
export function omitUndefined<T extends
|
|
219
|
+
export function omitUndefined<T extends object>(value: T): T {
|
|
198
220
|
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined)) as T;
|
|
199
221
|
}
|
|
200
222
|
|
|
201
|
-
export function stringValue(value:
|
|
202
|
-
|
|
223
|
+
export function stringValue<T>(value: T): string | undefined {
|
|
224
|
+
const raw: unknown = value;
|
|
225
|
+
return String(raw) === raw && raw.length > 0 ? raw : undefined;
|
|
203
226
|
}
|
|
204
227
|
|
|
205
|
-
export function numberValue(value:
|
|
206
|
-
|
|
228
|
+
export function numberValue<T>(value: T): number | undefined {
|
|
229
|
+
const raw: unknown = value;
|
|
230
|
+
return Number(raw) === raw && Number.isFinite(raw) ? raw : undefined;
|
|
207
231
|
}
|
|
208
232
|
|
|
209
|
-
export function arrayValue<T>(value:
|
|
233
|
+
export function arrayValue<T, TValue = unknown>(value: TValue): T[] | undefined {
|
|
210
234
|
return Array.isArray(value) ? (value as T[]) : undefined;
|
|
211
235
|
}
|
|
212
236
|
|
|
213
|
-
export function bytesToString(value:
|
|
214
|
-
|
|
215
|
-
if (
|
|
216
|
-
if (
|
|
217
|
-
|
|
237
|
+
export function bytesToString<T>(value: T): string | undefined {
|
|
238
|
+
const raw: unknown = value;
|
|
239
|
+
if (String(raw) === raw) return raw;
|
|
240
|
+
if (raw instanceof Uint8Array) return new TextDecoder().decode(raw);
|
|
241
|
+
if (ArrayBuffer.isView(raw)) {
|
|
242
|
+
return new TextDecoder().decode(new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength));
|
|
243
|
+
}
|
|
218
244
|
}
|
|
219
245
|
|
|
220
|
-
export function parseJson(value:
|
|
246
|
+
export function parseJson<T>(value: T): JsonValue {
|
|
221
247
|
const text = bytesToString(value);
|
|
222
248
|
if (text === undefined) return undefined;
|
|
223
249
|
try {
|
|
224
|
-
return JSON.parse(text)
|
|
250
|
+
return asJsonValue(JSON.parse(text));
|
|
225
251
|
} catch {
|
|
226
252
|
return undefined;
|
|
227
253
|
}
|
|
228
254
|
}
|
|
229
255
|
|
|
230
256
|
export interface StreamState {
|
|
231
|
-
feed(event:
|
|
257
|
+
feed<T>(event: T): void;
|
|
232
258
|
finish(partial: boolean): SpanFields;
|
|
233
259
|
}
|
|
234
260
|
|
|
@@ -239,10 +265,7 @@ export function wrapAsyncIterable<T>(
|
|
|
239
265
|
t0: number,
|
|
240
266
|
baseFields: SpanFields,
|
|
241
267
|
): T {
|
|
242
|
-
if (
|
|
243
|
-
!iterable ||
|
|
244
|
-
typeof (iterable as unknown as AsyncIterable<unknown>)[Symbol.asyncIterator] !== "function"
|
|
245
|
-
) {
|
|
268
|
+
if (!iterable || !isAsyncIterable(iterable)) {
|
|
246
269
|
endSpan(span, baseFields);
|
|
247
270
|
return iterable;
|
|
248
271
|
}
|
|
@@ -257,11 +280,10 @@ export function wrapAsyncIterable<T>(
|
|
|
257
280
|
endSpan(span, mergeFields(baseFields, fields));
|
|
258
281
|
};
|
|
259
282
|
|
|
260
|
-
const source = iterable as unknown as AsyncIterable<unknown>;
|
|
261
283
|
const generator = async function* () {
|
|
262
284
|
let completed = false;
|
|
263
285
|
try {
|
|
264
|
-
for await (const event of
|
|
286
|
+
for await (const event of iterable) {
|
|
265
287
|
if (!first) {
|
|
266
288
|
first = true;
|
|
267
289
|
updateSpan(span, { timeToFirstChunkMs: performance.now() - t0 });
|
|
@@ -271,17 +293,21 @@ export function wrapAsyncIterable<T>(
|
|
|
271
293
|
}
|
|
272
294
|
completed = true;
|
|
273
295
|
} catch (error) {
|
|
274
|
-
endOnce(
|
|
296
|
+
endOnce(
|
|
297
|
+
mergeFields(state.finish(true), awsMetadataFields(metadataOf(error)), {
|
|
298
|
+
error: asError(error),
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
275
301
|
throw error;
|
|
276
302
|
} finally {
|
|
277
303
|
if (!ended) endOnce(state.finish(!completed));
|
|
278
304
|
}
|
|
279
305
|
};
|
|
280
306
|
|
|
281
|
-
const wrapped:
|
|
307
|
+
const wrapped: { [key: PropertyKey]: JsonValue | (() => AsyncGenerator<unknown>) } = {};
|
|
282
308
|
if (isRecord(iterable)) {
|
|
283
309
|
for (const key of Reflect.ownKeys(iterable)) {
|
|
284
|
-
wrapped[key] = (iterable as
|
|
310
|
+
wrapped[key] = (iterable as { [key: PropertyKey]: JsonValue })[key];
|
|
285
311
|
}
|
|
286
312
|
}
|
|
287
313
|
const finishAbandoned = () => endOnce(safe(() => state.finish(true)));
|
|
@@ -294,7 +320,19 @@ export function wrapAsyncIterable<T>(
|
|
|
294
320
|
return wrapped as T;
|
|
295
321
|
}
|
|
296
322
|
|
|
297
|
-
|
|
323
|
+
function isAsyncIterable<T>(value: T): value is T & AsyncIterable<unknown> {
|
|
324
|
+
return Object(value) === value && Symbol.asyncIterator in Object(value);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function asJsonValue<T>(value: T): JsonValue {
|
|
328
|
+
return value as JsonValue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function asError<T>(value: T): Error {
|
|
332
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function basenameArn<T>(value: T): string | undefined {
|
|
298
336
|
const text = stringValue(value);
|
|
299
337
|
if (!text) return undefined;
|
|
300
338
|
return text.split(/[/:]/).filter(Boolean).at(-1) ?? text;
|
package/src/invoke-model.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SpanFields } from "@telemetry-dev/sdk";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
arrayValue,
|
|
4
5
|
awsMetadataFields,
|
|
5
6
|
isRecord,
|
|
6
7
|
mergeFields,
|
|
@@ -10,22 +11,24 @@ import {
|
|
|
10
11
|
parseJson,
|
|
11
12
|
PROVIDER,
|
|
12
13
|
stringValue,
|
|
14
|
+
type JsonRecord,
|
|
15
|
+
type JsonValue,
|
|
13
16
|
type StreamState,
|
|
14
17
|
} from "./internal.ts";
|
|
15
18
|
|
|
16
|
-
export function isEmbeddingModel(modelId:
|
|
19
|
+
export function isEmbeddingModel<T>(modelId: T): boolean {
|
|
17
20
|
const model = stringValue(modelId)?.toLowerCase() ?? "";
|
|
18
21
|
return (
|
|
19
22
|
model.includes("titan-embed") || model.includes("cohere.embed") || model.includes("-embedding")
|
|
20
23
|
);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
export function invokeModelSpanName(input:
|
|
26
|
+
export function invokeModelSpanName(input: JsonRecord): string {
|
|
24
27
|
const model = stringValue(input.modelId) ?? "unknown";
|
|
25
28
|
return `${isEmbeddingModel(model) ? "embeddings" : "chat"} ${model}`;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export function invokeModelRequestFields(input:
|
|
31
|
+
export function invokeModelRequestFields(input: JsonRecord): SpanFields {
|
|
29
32
|
const model = stringValue(input.modelId);
|
|
30
33
|
const body = parseBody(input.body, input.contentType);
|
|
31
34
|
return omitUndefined({
|
|
@@ -37,23 +40,20 @@ export function invokeModelRequestFields(input: Record<string, unknown>): SpanFi
|
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
export function invokeModelResponseFields(
|
|
41
|
-
input: Record<string, unknown>,
|
|
42
|
-
output: unknown,
|
|
43
|
-
): SpanFields {
|
|
43
|
+
export function invokeModelResponseFields(input: JsonRecord, output: JsonValue): SpanFields {
|
|
44
44
|
const model = stringValue(input.modelId);
|
|
45
45
|
const body = isRecord(output) ? parseBody(output.body, output.contentType) : undefined;
|
|
46
46
|
const metadata = isRecord(output) ? output.$metadata : undefined;
|
|
47
47
|
return mergeFields(awsMetadataFields(metadata), nativeResponseFields(model, body, metadata));
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export function parseBody(body:
|
|
50
|
+
export function parseBody<TBody, TContentType>(body: TBody, contentType: TContentType): JsonValue {
|
|
51
51
|
const type = stringValue(contentType) ?? "application/json";
|
|
52
52
|
if (!type.includes("json")) return undefined;
|
|
53
53
|
return parseJson(body);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function nativeSamplingFields(model: string | undefined, body:
|
|
56
|
+
function nativeSamplingFields<T>(model: string | undefined, body: T): SpanFields {
|
|
57
57
|
if (!isRecord(body)) return {};
|
|
58
58
|
const lower = model?.toLowerCase() ?? "";
|
|
59
59
|
if (lower.includes("amazon.titan")) {
|
|
@@ -62,7 +62,7 @@ function nativeSamplingFields(model: string | undefined, body: unknown): SpanFie
|
|
|
62
62
|
temperature: numberValue(cfg.temperature),
|
|
63
63
|
topP: numberValue(cfg.topP),
|
|
64
64
|
maxTokens: numberValue(cfg.maxTokenCount),
|
|
65
|
-
stopSequences:
|
|
65
|
+
stopSequences: arrayValue<string>(cfg.stopSequences),
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
if (lower.includes("amazon.nova")) {
|
|
@@ -72,7 +72,7 @@ function nativeSamplingFields(model: string | undefined, body: unknown): SpanFie
|
|
|
72
72
|
topP: numberValue(cfg.topP) ?? numberValue(cfg.top_p),
|
|
73
73
|
topK: numberValue(cfg.topK),
|
|
74
74
|
maxTokens: numberValue(cfg.maxTokens) ?? numberValue(cfg.max_new_tokens),
|
|
75
|
-
stopSequences:
|
|
75
|
+
stopSequences: arrayValue<string>(cfg.stopSequences),
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
if (lower.includes("anthropic.claude")) {
|
|
@@ -81,9 +81,7 @@ function nativeSamplingFields(model: string | undefined, body: unknown): SpanFie
|
|
|
81
81
|
topP: numberValue(body.top_p),
|
|
82
82
|
topK: numberValue(body.top_k),
|
|
83
83
|
maxTokens: numberValue(body.max_tokens),
|
|
84
|
-
stopSequences:
|
|
85
|
-
? (body.stop_sequences as string[])
|
|
86
|
-
: undefined,
|
|
84
|
+
stopSequences: arrayValue<string>(body.stop_sequences),
|
|
87
85
|
});
|
|
88
86
|
}
|
|
89
87
|
if (lower.includes("meta.llama")) {
|
|
@@ -102,8 +100,8 @@ function nativeSamplingFields(model: string | undefined, body: unknown): SpanFie
|
|
|
102
100
|
|
|
103
101
|
function nativeResponseFields(
|
|
104
102
|
model: string | undefined,
|
|
105
|
-
body:
|
|
106
|
-
metadata:
|
|
103
|
+
body: JsonValue,
|
|
104
|
+
metadata: JsonValue,
|
|
107
105
|
): SpanFields {
|
|
108
106
|
if (!isRecord(body)) return headerUsageFields(metadata);
|
|
109
107
|
const lower = model?.toLowerCase() ?? "";
|
|
@@ -190,12 +188,12 @@ function nativeResponseFields(
|
|
|
190
188
|
return withHeaderUsage({ output: body }, metadata);
|
|
191
189
|
}
|
|
192
190
|
|
|
193
|
-
function withHeaderUsage(fields: SpanFields, metadata:
|
|
191
|
+
function withHeaderUsage<T>(fields: SpanFields, metadata: T): SpanFields {
|
|
194
192
|
if (fields.usage && Object.keys(fields.usage).length > 0) return fields;
|
|
195
193
|
return mergeFields(fields, headerUsageFields(metadata));
|
|
196
194
|
}
|
|
197
195
|
|
|
198
|
-
function headerUsageFields(metadata:
|
|
196
|
+
function headerUsageFields<T>(metadata: T): SpanFields {
|
|
199
197
|
const headers = isRecord(metadata)
|
|
200
198
|
? isRecord(metadata.httpHeaders)
|
|
201
199
|
? metadata.httpHeaders
|
|
@@ -210,9 +208,10 @@ function headerUsageFields(metadata: unknown): SpanFields {
|
|
|
210
208
|
return { usage: Object.keys(usage).length > 0 ? usage : undefined };
|
|
211
209
|
}
|
|
212
210
|
|
|
213
|
-
function headerNumber(value:
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
function headerNumber<T>(value: T): number | undefined {
|
|
212
|
+
const text = stringValue(value);
|
|
213
|
+
if (text !== undefined) {
|
|
214
|
+
const parsed = Number.parseInt(text, 10);
|
|
216
215
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
217
216
|
}
|
|
218
217
|
return numberValue(value);
|
|
@@ -225,7 +224,7 @@ export class InvokeModelStreamState implements StreamState {
|
|
|
225
224
|
private finishReason: string | undefined;
|
|
226
225
|
private error: Error | undefined;
|
|
227
226
|
private errorFields: SpanFields | undefined;
|
|
228
|
-
feed(event:
|
|
227
|
+
feed<T>(event: T): void {
|
|
229
228
|
if (!isRecord(event)) return;
|
|
230
229
|
const streamError = modeledStreamError(event);
|
|
231
230
|
if (streamError) {
|
|
@@ -303,7 +302,7 @@ export class InvokeModelStreamState implements StreamState {
|
|
|
303
302
|
}
|
|
304
303
|
}
|
|
305
304
|
|
|
306
|
-
function textFromProviderChunk(value:
|
|
305
|
+
function textFromProviderChunk<T>(value: T): string {
|
|
307
306
|
if (!isRecord(value)) return "";
|
|
308
307
|
const delta = isRecord(value.delta) ? value.delta : undefined;
|
|
309
308
|
const contentBlockDelta = isRecord(value.contentBlockDelta)
|