@lelemondev/sdk 0.6.3 → 0.7.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/index.d.mts +169 -1
- package/dist/index.d.ts +169 -1
- package/dist/index.js +481 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +478 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
|
|
1
3
|
/* @lelemondev/sdk - LLM Observability */
|
|
2
4
|
var __defProp = Object.defineProperty;
|
|
3
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -102,9 +104,9 @@ var Transport = class {
|
|
|
102
104
|
* Enqueue a trace for sending
|
|
103
105
|
* Fire-and-forget - never blocks
|
|
104
106
|
*/
|
|
105
|
-
enqueue(
|
|
107
|
+
enqueue(trace2) {
|
|
106
108
|
if (this.config.disabled) return;
|
|
107
|
-
this.queue.push(
|
|
109
|
+
this.queue.push(trace2);
|
|
108
110
|
if (this.queue.length >= this.config.batchSize) {
|
|
109
111
|
this.flush();
|
|
110
112
|
} else {
|
|
@@ -286,6 +288,64 @@ function getNestedValue(obj, path) {
|
|
|
286
288
|
function isValidNumber(value) {
|
|
287
289
|
return typeof value === "number" && !isNaN(value) && isFinite(value);
|
|
288
290
|
}
|
|
291
|
+
var traceStorage = new AsyncLocalStorage();
|
|
292
|
+
function generateId() {
|
|
293
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
294
|
+
return crypto.randomUUID();
|
|
295
|
+
}
|
|
296
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
|
|
297
|
+
}
|
|
298
|
+
function getTraceContext() {
|
|
299
|
+
return traceStorage.getStore();
|
|
300
|
+
}
|
|
301
|
+
async function trace(nameOrOptions, fn) {
|
|
302
|
+
const options = typeof nameOrOptions === "string" ? { name: nameOrOptions } : nameOrOptions;
|
|
303
|
+
const parentContext = getTraceContext();
|
|
304
|
+
const traceId = parentContext?.traceId ?? generateId();
|
|
305
|
+
const rootSpanId = generateId();
|
|
306
|
+
const context = {
|
|
307
|
+
traceId,
|
|
308
|
+
rootSpanId,
|
|
309
|
+
currentSpanId: rootSpanId,
|
|
310
|
+
parentSpanId: parentContext?.currentSpanId,
|
|
311
|
+
name: options.name,
|
|
312
|
+
startTime: Date.now(),
|
|
313
|
+
input: options.input,
|
|
314
|
+
metadata: options.metadata,
|
|
315
|
+
tags: options.tags
|
|
316
|
+
};
|
|
317
|
+
return traceStorage.run(context, async () => {
|
|
318
|
+
try {
|
|
319
|
+
const result = await fn();
|
|
320
|
+
return result;
|
|
321
|
+
} finally {
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
function span(options) {
|
|
326
|
+
const context = getTraceContext();
|
|
327
|
+
if (!context) {
|
|
328
|
+
if (process.env.NODE_ENV !== "production") {
|
|
329
|
+
console.warn("[Lelemon] span() called outside of trace() - span will not be captured");
|
|
330
|
+
}
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
captureSpan({
|
|
334
|
+
type: options.type,
|
|
335
|
+
name: options.name,
|
|
336
|
+
input: options.input,
|
|
337
|
+
output: options.output,
|
|
338
|
+
durationMs: options.durationMs ?? 0,
|
|
339
|
+
status: options.status ?? "success",
|
|
340
|
+
errorMessage: options.errorMessage,
|
|
341
|
+
metadata: {
|
|
342
|
+
...options.metadata,
|
|
343
|
+
// Include trace context
|
|
344
|
+
_traceId: context.traceId,
|
|
345
|
+
_parentSpanId: context.currentSpanId
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
289
349
|
|
|
290
350
|
// src/core/capture.ts
|
|
291
351
|
var globalContext = {};
|
|
@@ -303,7 +363,8 @@ function captureTrace(params) {
|
|
|
303
363
|
debug("Transport disabled, skipping trace capture");
|
|
304
364
|
return;
|
|
305
365
|
}
|
|
306
|
-
const
|
|
366
|
+
const globalContext2 = getGlobalContext();
|
|
367
|
+
const traceContext = getTraceContext();
|
|
307
368
|
const request = {
|
|
308
369
|
provider: params.provider,
|
|
309
370
|
model: params.model,
|
|
@@ -314,10 +375,26 @@ function captureTrace(params) {
|
|
|
314
375
|
durationMs: params.durationMs,
|
|
315
376
|
status: params.status,
|
|
316
377
|
streaming: params.streaming,
|
|
317
|
-
sessionId:
|
|
318
|
-
userId:
|
|
319
|
-
|
|
320
|
-
|
|
378
|
+
sessionId: globalContext2.sessionId,
|
|
379
|
+
userId: globalContext2.userId,
|
|
380
|
+
// Hierarchy fields (Phase 7.2) - use trace context if available
|
|
381
|
+
traceId: traceContext?.traceId,
|
|
382
|
+
spanId: generateId(),
|
|
383
|
+
parentSpanId: traceContext?.currentSpanId,
|
|
384
|
+
metadata: {
|
|
385
|
+
...globalContext2.metadata,
|
|
386
|
+
...params.metadata,
|
|
387
|
+
// Include trace name for debugging
|
|
388
|
+
...traceContext ? { _traceName: traceContext.name } : {}
|
|
389
|
+
},
|
|
390
|
+
tags: globalContext2.tags,
|
|
391
|
+
// Extended fields (Phase 7.1)
|
|
392
|
+
stopReason: params.stopReason,
|
|
393
|
+
cacheReadTokens: params.cacheReadTokens,
|
|
394
|
+
cacheWriteTokens: params.cacheWriteTokens,
|
|
395
|
+
reasoningTokens: params.reasoningTokens,
|
|
396
|
+
firstTokenMs: params.firstTokenMs,
|
|
397
|
+
thinking: params.thinking
|
|
321
398
|
};
|
|
322
399
|
traceCapture(params.provider, params.model, params.durationMs, params.status);
|
|
323
400
|
transport.enqueue(request);
|
|
@@ -332,7 +409,8 @@ function captureError(params) {
|
|
|
332
409
|
debug("Transport disabled, skipping error capture");
|
|
333
410
|
return;
|
|
334
411
|
}
|
|
335
|
-
const
|
|
412
|
+
const globalContext2 = getGlobalContext();
|
|
413
|
+
const traceContext = getTraceContext();
|
|
336
414
|
const request = {
|
|
337
415
|
provider: params.provider,
|
|
338
416
|
model: params.model,
|
|
@@ -345,10 +423,18 @@ function captureError(params) {
|
|
|
345
423
|
errorMessage: params.error.message,
|
|
346
424
|
errorStack: params.error.stack,
|
|
347
425
|
streaming: params.streaming,
|
|
348
|
-
sessionId:
|
|
349
|
-
userId:
|
|
350
|
-
|
|
351
|
-
|
|
426
|
+
sessionId: globalContext2.sessionId,
|
|
427
|
+
userId: globalContext2.userId,
|
|
428
|
+
// Hierarchy fields (Phase 7.2)
|
|
429
|
+
traceId: traceContext?.traceId,
|
|
430
|
+
spanId: generateId(),
|
|
431
|
+
parentSpanId: traceContext?.currentSpanId,
|
|
432
|
+
metadata: {
|
|
433
|
+
...globalContext2.metadata,
|
|
434
|
+
...params.metadata,
|
|
435
|
+
...traceContext ? { _traceName: traceContext.name } : {}
|
|
436
|
+
},
|
|
437
|
+
tags: globalContext2.tags
|
|
352
438
|
};
|
|
353
439
|
traceCapture(params.provider, params.model, params.durationMs, "error");
|
|
354
440
|
debug("Error details", { message: params.error.message, stack: params.error.stack });
|
|
@@ -357,6 +443,93 @@ function captureError(params) {
|
|
|
357
443
|
traceCaptureError(params.provider, err instanceof Error ? err : new Error(String(err)));
|
|
358
444
|
}
|
|
359
445
|
}
|
|
446
|
+
function captureSpan(options) {
|
|
447
|
+
try {
|
|
448
|
+
const transport = getTransport();
|
|
449
|
+
if (!transport.isEnabled()) {
|
|
450
|
+
debug("Transport disabled, skipping span capture");
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const globalContext2 = getGlobalContext();
|
|
454
|
+
const traceContext = getTraceContext();
|
|
455
|
+
const metadataTraceId = options.metadata?._traceId;
|
|
456
|
+
const metadataParentSpanId = options.metadata?._parentSpanId;
|
|
457
|
+
const cleanMetadata = { ...globalContext2.metadata, ...options.metadata };
|
|
458
|
+
delete cleanMetadata._traceId;
|
|
459
|
+
delete cleanMetadata._parentSpanId;
|
|
460
|
+
const request = {
|
|
461
|
+
spanType: options.type,
|
|
462
|
+
name: options.name,
|
|
463
|
+
provider: "unknown",
|
|
464
|
+
// Manual spans don't have a provider
|
|
465
|
+
model: options.name,
|
|
466
|
+
// Use name as model for compatibility
|
|
467
|
+
input: sanitizeInput(options.input),
|
|
468
|
+
output: sanitizeOutput(options.output),
|
|
469
|
+
inputTokens: 0,
|
|
470
|
+
outputTokens: 0,
|
|
471
|
+
durationMs: options.durationMs,
|
|
472
|
+
status: options.status || "success",
|
|
473
|
+
errorMessage: options.errorMessage,
|
|
474
|
+
streaming: false,
|
|
475
|
+
sessionId: globalContext2.sessionId,
|
|
476
|
+
userId: globalContext2.userId,
|
|
477
|
+
// Hierarchy fields (Phase 7.2)
|
|
478
|
+
traceId: metadataTraceId ?? traceContext?.traceId,
|
|
479
|
+
spanId: generateId(),
|
|
480
|
+
parentSpanId: metadataParentSpanId ?? traceContext?.currentSpanId,
|
|
481
|
+
toolCallId: options.toolCallId,
|
|
482
|
+
metadata: cleanMetadata,
|
|
483
|
+
tags: globalContext2.tags
|
|
484
|
+
};
|
|
485
|
+
debug(`Span captured: ${options.type}/${options.name}`, { durationMs: options.durationMs });
|
|
486
|
+
transport.enqueue(request);
|
|
487
|
+
} catch (err) {
|
|
488
|
+
traceCaptureError("unknown", err instanceof Error ? err : new Error(String(err)));
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function captureToolSpans(toolCalls, provider) {
|
|
492
|
+
for (const tool of toolCalls) {
|
|
493
|
+
try {
|
|
494
|
+
const transport = getTransport();
|
|
495
|
+
if (!transport.isEnabled()) continue;
|
|
496
|
+
const globalContext2 = getGlobalContext();
|
|
497
|
+
const traceContext = getTraceContext();
|
|
498
|
+
const request = {
|
|
499
|
+
spanType: "tool",
|
|
500
|
+
name: tool.name,
|
|
501
|
+
provider,
|
|
502
|
+
model: tool.name,
|
|
503
|
+
input: sanitizeInput(tool.input),
|
|
504
|
+
output: null,
|
|
505
|
+
// Tool result will come later
|
|
506
|
+
inputTokens: 0,
|
|
507
|
+
outputTokens: 0,
|
|
508
|
+
durationMs: 0,
|
|
509
|
+
// Duration unknown at this point
|
|
510
|
+
status: "success",
|
|
511
|
+
streaming: false,
|
|
512
|
+
sessionId: globalContext2.sessionId,
|
|
513
|
+
userId: globalContext2.userId,
|
|
514
|
+
// Hierarchy fields (Phase 7.2)
|
|
515
|
+
traceId: traceContext?.traceId,
|
|
516
|
+
spanId: generateId(),
|
|
517
|
+
parentSpanId: traceContext?.currentSpanId,
|
|
518
|
+
toolCallId: tool.id,
|
|
519
|
+
metadata: {
|
|
520
|
+
...globalContext2.metadata,
|
|
521
|
+
toolUseDetected: true,
|
|
522
|
+
...traceContext ? { _traceName: traceContext.name } : {}
|
|
523
|
+
},
|
|
524
|
+
tags: globalContext2.tags
|
|
525
|
+
};
|
|
526
|
+
debug(`Tool span captured: ${tool.name}`, { toolCallId: tool.id });
|
|
527
|
+
transport.enqueue(request);
|
|
528
|
+
} catch (err) {
|
|
529
|
+
traceCaptureError(provider, err instanceof Error ? err : new Error(String(err)));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
360
533
|
var MAX_STRING_LENGTH = 1e5;
|
|
361
534
|
var SENSITIVE_KEYS = ["api_key", "apikey", "password", "secret", "token", "authorization"];
|
|
362
535
|
function sanitizeInput(input) {
|
|
@@ -421,7 +594,10 @@ function wrapChatCreate(originalFn) {
|
|
|
421
594
|
outputTokens: extracted.tokens?.outputTokens || 0,
|
|
422
595
|
durationMs,
|
|
423
596
|
status: "success",
|
|
424
|
-
streaming: false
|
|
597
|
+
streaming: false,
|
|
598
|
+
// Extended fields
|
|
599
|
+
stopReason: extracted.finishReason || void 0,
|
|
600
|
+
reasoningTokens: extracted.tokens?.reasoningTokens
|
|
425
601
|
});
|
|
426
602
|
return response;
|
|
427
603
|
} catch (error) {
|
|
@@ -551,14 +727,26 @@ function isAsyncIterable(value) {
|
|
|
551
727
|
async function* wrapStream(stream, request, startTime) {
|
|
552
728
|
const chunks = [];
|
|
553
729
|
let tokens = null;
|
|
730
|
+
let finishReason;
|
|
554
731
|
let error = null;
|
|
732
|
+
let firstTokenMs;
|
|
733
|
+
let firstTokenReceived = false;
|
|
555
734
|
try {
|
|
556
735
|
for await (const chunk of stream) {
|
|
557
|
-
const
|
|
736
|
+
const streamChunk = chunk;
|
|
737
|
+
const content = extractStreamChunkContent(streamChunk);
|
|
558
738
|
if (content) {
|
|
739
|
+
if (!firstTokenReceived) {
|
|
740
|
+
firstTokenReceived = true;
|
|
741
|
+
firstTokenMs = Date.now() - startTime;
|
|
742
|
+
}
|
|
559
743
|
chunks.push(content);
|
|
560
744
|
}
|
|
561
|
-
const
|
|
745
|
+
const chunkFinishReason = streamChunk?.choices?.[0]?.finish_reason;
|
|
746
|
+
if (chunkFinishReason) {
|
|
747
|
+
finishReason = chunkFinishReason;
|
|
748
|
+
}
|
|
749
|
+
const chunkTokens = extractStreamChunkTokens(streamChunk);
|
|
562
750
|
if (chunkTokens) {
|
|
563
751
|
tokens = chunkTokens;
|
|
564
752
|
}
|
|
@@ -589,7 +777,11 @@ async function* wrapStream(stream, request, startTime) {
|
|
|
589
777
|
outputTokens: tokens?.outputTokens || 0,
|
|
590
778
|
durationMs,
|
|
591
779
|
status: "success",
|
|
592
|
-
streaming: true
|
|
780
|
+
streaming: true,
|
|
781
|
+
// Extended fields
|
|
782
|
+
stopReason: finishReason,
|
|
783
|
+
reasoningTokens: tokens?.reasoningTokens,
|
|
784
|
+
firstTokenMs
|
|
593
785
|
});
|
|
594
786
|
}
|
|
595
787
|
}
|
|
@@ -600,8 +792,12 @@ function extractChatCompletion(response) {
|
|
|
600
792
|
() => getNestedValue(response, "choices.0.message.content"),
|
|
601
793
|
null
|
|
602
794
|
);
|
|
795
|
+
const finishReason = safeExtract(
|
|
796
|
+
() => getNestedValue(response, "choices.0.finish_reason"),
|
|
797
|
+
null
|
|
798
|
+
);
|
|
603
799
|
const tokens = extractTokens(response);
|
|
604
|
-
return { model, output, tokens };
|
|
800
|
+
return { model, output, tokens, finishReason };
|
|
605
801
|
}
|
|
606
802
|
function extractLegacyCompletion(response) {
|
|
607
803
|
const model = safeExtract(() => getNestedValue(response, "model"), null);
|
|
@@ -623,10 +819,16 @@ function extractTokens(response) {
|
|
|
623
819
|
if (!isValidNumber(promptTokens) && !isValidNumber(completionTokens)) {
|
|
624
820
|
return null;
|
|
625
821
|
}
|
|
822
|
+
let reasoningTokens;
|
|
823
|
+
const completionDetails = u.completion_tokens_details;
|
|
824
|
+
if (completionDetails && isValidNumber(completionDetails.reasoning_tokens)) {
|
|
825
|
+
reasoningTokens = completionDetails.reasoning_tokens;
|
|
826
|
+
}
|
|
626
827
|
return {
|
|
627
828
|
inputTokens: isValidNumber(promptTokens) ? promptTokens : 0,
|
|
628
829
|
outputTokens: isValidNumber(completionTokens) ? completionTokens : 0,
|
|
629
|
-
totalTokens: isValidNumber(totalTokens) ? totalTokens : 0
|
|
830
|
+
totalTokens: isValidNumber(totalTokens) ? totalTokens : 0,
|
|
831
|
+
reasoningTokens
|
|
630
832
|
};
|
|
631
833
|
} catch {
|
|
632
834
|
return null;
|
|
@@ -768,7 +970,8 @@ function wrapMessagesCreate(originalFn) {
|
|
|
768
970
|
return wrapStream2(response, request, startTime);
|
|
769
971
|
}
|
|
770
972
|
const durationMs = Date.now() - startTime;
|
|
771
|
-
const
|
|
973
|
+
const messageResponse = response;
|
|
974
|
+
const extracted = extractMessageResponse(messageResponse);
|
|
772
975
|
captureTrace({
|
|
773
976
|
provider: PROVIDER_NAME2,
|
|
774
977
|
model: request.model || extracted.model || "unknown",
|
|
@@ -778,8 +981,17 @@ function wrapMessagesCreate(originalFn) {
|
|
|
778
981
|
outputTokens: extracted.tokens?.outputTokens || 0,
|
|
779
982
|
durationMs,
|
|
780
983
|
status: "success",
|
|
781
|
-
streaming: false
|
|
984
|
+
streaming: false,
|
|
985
|
+
// Extended fields
|
|
986
|
+
stopReason: extracted.stopReason || void 0,
|
|
987
|
+
cacheReadTokens: extracted.tokens?.cacheReadTokens,
|
|
988
|
+
cacheWriteTokens: extracted.tokens?.cacheWriteTokens,
|
|
989
|
+
thinking: extracted.thinking || void 0
|
|
782
990
|
});
|
|
991
|
+
const toolCalls = extractToolCalls(messageResponse);
|
|
992
|
+
if (toolCalls.length > 0) {
|
|
993
|
+
captureToolSpans(toolCalls, PROVIDER_NAME2);
|
|
994
|
+
}
|
|
783
995
|
return response;
|
|
784
996
|
} catch (error) {
|
|
785
997
|
const durationMs = Date.now() - startTime;
|
|
@@ -828,10 +1040,19 @@ function wrapAnthropicStream(stream, request, startTime) {
|
|
|
828
1040
|
return stream;
|
|
829
1041
|
}
|
|
830
1042
|
const chunks = [];
|
|
1043
|
+
const thinkingChunks = [];
|
|
831
1044
|
let inputTokens = 0;
|
|
832
1045
|
let outputTokens = 0;
|
|
1046
|
+
let cacheReadTokens;
|
|
1047
|
+
let cacheWriteTokens;
|
|
1048
|
+
let stopReason;
|
|
833
1049
|
let model = request.model || "unknown";
|
|
834
1050
|
let captured = false;
|
|
1051
|
+
let firstTokenMs;
|
|
1052
|
+
let firstTokenReceived = false;
|
|
1053
|
+
const toolCalls = [];
|
|
1054
|
+
let currentToolIndex = null;
|
|
1055
|
+
let currentBlockType = null;
|
|
835
1056
|
const wrappedIterator = async function* () {
|
|
836
1057
|
try {
|
|
837
1058
|
for await (const event of originalStream) {
|
|
@@ -839,13 +1060,63 @@ function wrapAnthropicStream(stream, request, startTime) {
|
|
|
839
1060
|
model = event.message.model || model;
|
|
840
1061
|
if (event.message.usage) {
|
|
841
1062
|
inputTokens = event.message.usage.input_tokens || 0;
|
|
1063
|
+
cacheReadTokens = event.message.usage.cache_read_input_tokens;
|
|
1064
|
+
cacheWriteTokens = event.message.usage.cache_creation_input_tokens;
|
|
842
1065
|
}
|
|
843
1066
|
}
|
|
1067
|
+
if (event.type === "content_block_start" && event.content_block) {
|
|
1068
|
+
currentBlockType = event.content_block.type;
|
|
1069
|
+
}
|
|
844
1070
|
if (event.type === "content_block_delta" && event.delta?.text) {
|
|
845
|
-
|
|
1071
|
+
if (!firstTokenReceived) {
|
|
1072
|
+
firstTokenReceived = true;
|
|
1073
|
+
firstTokenMs = Date.now() - startTime;
|
|
1074
|
+
}
|
|
1075
|
+
if (currentBlockType === "thinking") {
|
|
1076
|
+
thinkingChunks.push(event.delta.text);
|
|
1077
|
+
} else {
|
|
1078
|
+
chunks.push(event.delta.text);
|
|
1079
|
+
}
|
|
846
1080
|
}
|
|
847
|
-
if (event.type === "
|
|
848
|
-
|
|
1081
|
+
if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
|
|
1082
|
+
const block = event.content_block;
|
|
1083
|
+
if (block.id && block.name) {
|
|
1084
|
+
currentToolIndex = event.index ?? toolCalls.length;
|
|
1085
|
+
toolCalls.push({
|
|
1086
|
+
id: block.id,
|
|
1087
|
+
name: block.name,
|
|
1088
|
+
input: block.input ?? {},
|
|
1089
|
+
inputJson: ""
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (event.type === "content_block_delta" && event.delta?.partial_json && currentToolIndex !== null) {
|
|
1094
|
+
const tool = toolCalls.find((_, i) => i === currentToolIndex);
|
|
1095
|
+
if (tool) {
|
|
1096
|
+
tool.inputJson += event.delta.partial_json;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
if (event.type === "content_block_stop") {
|
|
1100
|
+
if (currentToolIndex !== null) {
|
|
1101
|
+
const tool = toolCalls[currentToolIndex];
|
|
1102
|
+
if (tool && tool.inputJson) {
|
|
1103
|
+
try {
|
|
1104
|
+
tool.input = JSON.parse(tool.inputJson);
|
|
1105
|
+
} catch {
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
currentToolIndex = null;
|
|
1109
|
+
}
|
|
1110
|
+
currentBlockType = null;
|
|
1111
|
+
}
|
|
1112
|
+
if (event.type === "message_delta") {
|
|
1113
|
+
if (event.usage) {
|
|
1114
|
+
outputTokens = event.usage.output_tokens || 0;
|
|
1115
|
+
}
|
|
1116
|
+
const delta = event;
|
|
1117
|
+
if (delta.delta?.stop_reason) {
|
|
1118
|
+
stopReason = delta.delta.stop_reason;
|
|
1119
|
+
}
|
|
849
1120
|
}
|
|
850
1121
|
yield event;
|
|
851
1122
|
}
|
|
@@ -876,8 +1147,20 @@ function wrapAnthropicStream(stream, request, startTime) {
|
|
|
876
1147
|
outputTokens,
|
|
877
1148
|
durationMs,
|
|
878
1149
|
status: "success",
|
|
879
|
-
streaming: true
|
|
1150
|
+
streaming: true,
|
|
1151
|
+
// Extended fields
|
|
1152
|
+
stopReason,
|
|
1153
|
+
cacheReadTokens,
|
|
1154
|
+
cacheWriteTokens,
|
|
1155
|
+
firstTokenMs,
|
|
1156
|
+
thinking: thinkingChunks.length > 0 ? thinkingChunks.join("") : void 0
|
|
880
1157
|
});
|
|
1158
|
+
if (toolCalls.length > 0) {
|
|
1159
|
+
captureToolSpans(
|
|
1160
|
+
toolCalls.map((t) => ({ id: t.id, name: t.name, input: t.input })),
|
|
1161
|
+
PROVIDER_NAME2
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
881
1164
|
}
|
|
882
1165
|
}
|
|
883
1166
|
};
|
|
@@ -892,23 +1175,82 @@ function wrapAnthropicStream(stream, request, startTime) {
|
|
|
892
1175
|
}
|
|
893
1176
|
async function* wrapStream2(stream, request, startTime) {
|
|
894
1177
|
const chunks = [];
|
|
1178
|
+
const thinkingChunks = [];
|
|
895
1179
|
let inputTokens = 0;
|
|
896
1180
|
let outputTokens = 0;
|
|
1181
|
+
let cacheReadTokens;
|
|
1182
|
+
let cacheWriteTokens;
|
|
1183
|
+
let stopReason;
|
|
897
1184
|
let model = request.model || "unknown";
|
|
898
1185
|
let error = null;
|
|
1186
|
+
let firstTokenMs;
|
|
1187
|
+
let firstTokenReceived = false;
|
|
1188
|
+
const toolCalls = [];
|
|
1189
|
+
let currentToolIndex = null;
|
|
1190
|
+
let currentBlockType = null;
|
|
899
1191
|
try {
|
|
900
1192
|
for await (const event of stream) {
|
|
901
1193
|
if (event.type === "message_start" && event.message) {
|
|
902
1194
|
model = event.message.model || model;
|
|
903
1195
|
if (event.message.usage) {
|
|
904
1196
|
inputTokens = event.message.usage.input_tokens || 0;
|
|
1197
|
+
cacheReadTokens = event.message.usage.cache_read_input_tokens;
|
|
1198
|
+
cacheWriteTokens = event.message.usage.cache_creation_input_tokens;
|
|
905
1199
|
}
|
|
906
1200
|
}
|
|
1201
|
+
if (event.type === "content_block_start" && event.content_block) {
|
|
1202
|
+
currentBlockType = event.content_block.type;
|
|
1203
|
+
}
|
|
907
1204
|
if (event.type === "content_block_delta" && event.delta?.text) {
|
|
908
|
-
|
|
1205
|
+
if (!firstTokenReceived) {
|
|
1206
|
+
firstTokenReceived = true;
|
|
1207
|
+
firstTokenMs = Date.now() - startTime;
|
|
1208
|
+
}
|
|
1209
|
+
if (currentBlockType === "thinking") {
|
|
1210
|
+
thinkingChunks.push(event.delta.text);
|
|
1211
|
+
} else {
|
|
1212
|
+
chunks.push(event.delta.text);
|
|
1213
|
+
}
|
|
909
1214
|
}
|
|
910
|
-
if (event.type === "
|
|
911
|
-
|
|
1215
|
+
if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
|
|
1216
|
+
const block = event.content_block;
|
|
1217
|
+
if (block.id && block.name) {
|
|
1218
|
+
currentToolIndex = event.index ?? toolCalls.length;
|
|
1219
|
+
toolCalls.push({
|
|
1220
|
+
id: block.id,
|
|
1221
|
+
name: block.name,
|
|
1222
|
+
input: block.input ?? {},
|
|
1223
|
+
inputJson: ""
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (event.type === "content_block_delta" && event.delta?.partial_json && currentToolIndex !== null) {
|
|
1228
|
+
const tool = toolCalls.find((_, i) => i === currentToolIndex);
|
|
1229
|
+
if (tool) {
|
|
1230
|
+
tool.inputJson += event.delta.partial_json;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (event.type === "content_block_stop") {
|
|
1234
|
+
if (currentToolIndex !== null) {
|
|
1235
|
+
const tool = toolCalls[currentToolIndex];
|
|
1236
|
+
if (tool && tool.inputJson) {
|
|
1237
|
+
try {
|
|
1238
|
+
tool.input = JSON.parse(tool.inputJson);
|
|
1239
|
+
} catch {
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
currentToolIndex = null;
|
|
1243
|
+
}
|
|
1244
|
+
currentBlockType = null;
|
|
1245
|
+
}
|
|
1246
|
+
if (event.type === "message_delta") {
|
|
1247
|
+
if (event.usage) {
|
|
1248
|
+
outputTokens = event.usage.output_tokens || 0;
|
|
1249
|
+
}
|
|
1250
|
+
const delta = event;
|
|
1251
|
+
if (delta.delta?.stop_reason) {
|
|
1252
|
+
stopReason = delta.delta.stop_reason;
|
|
1253
|
+
}
|
|
912
1254
|
}
|
|
913
1255
|
yield event;
|
|
914
1256
|
}
|
|
@@ -936,20 +1278,38 @@ async function* wrapStream2(stream, request, startTime) {
|
|
|
936
1278
|
outputTokens,
|
|
937
1279
|
durationMs,
|
|
938
1280
|
status: "success",
|
|
939
|
-
streaming: true
|
|
1281
|
+
streaming: true,
|
|
1282
|
+
// Extended fields
|
|
1283
|
+
stopReason,
|
|
1284
|
+
cacheReadTokens,
|
|
1285
|
+
cacheWriteTokens,
|
|
1286
|
+
firstTokenMs,
|
|
1287
|
+
thinking: thinkingChunks.length > 0 ? thinkingChunks.join("") : void 0
|
|
940
1288
|
});
|
|
1289
|
+
if (toolCalls.length > 0) {
|
|
1290
|
+
captureToolSpans(
|
|
1291
|
+
toolCalls.map((t) => ({ id: t.id, name: t.name, input: t.input })),
|
|
1292
|
+
PROVIDER_NAME2
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
941
1295
|
}
|
|
942
1296
|
}
|
|
943
1297
|
}
|
|
944
1298
|
function extractMessageResponse(response) {
|
|
945
1299
|
const model = safeExtract(() => response.model ?? null, null);
|
|
1300
|
+
const stopReason = safeExtract(() => response.stop_reason ?? null, null);
|
|
946
1301
|
const output = safeExtract(() => {
|
|
947
1302
|
if (!response.content || !Array.isArray(response.content)) return null;
|
|
948
1303
|
const textBlocks = response.content.filter((block) => block.type === "text" && block.text).map((block) => block.text);
|
|
949
1304
|
return textBlocks.join("") || null;
|
|
950
1305
|
}, null);
|
|
1306
|
+
const thinking = safeExtract(() => {
|
|
1307
|
+
if (!response.content || !Array.isArray(response.content)) return null;
|
|
1308
|
+
const thinkingBlocks = response.content.filter((block) => block.type === "thinking" && block.thinking).map((block) => block.thinking);
|
|
1309
|
+
return thinkingBlocks.join("\n\n") || null;
|
|
1310
|
+
}, null);
|
|
951
1311
|
const tokens = extractTokens2(response);
|
|
952
|
-
return { model, output, tokens };
|
|
1312
|
+
return { model, output, tokens, stopReason, thinking };
|
|
953
1313
|
}
|
|
954
1314
|
function extractTokens2(response) {
|
|
955
1315
|
try {
|
|
@@ -963,12 +1323,31 @@ function extractTokens2(response) {
|
|
|
963
1323
|
return {
|
|
964
1324
|
inputTokens: isValidNumber(inputTokens) ? inputTokens : 0,
|
|
965
1325
|
outputTokens: isValidNumber(outputTokens) ? outputTokens : 0,
|
|
966
|
-
totalTokens: (isValidNumber(inputTokens) ? inputTokens : 0) + (isValidNumber(outputTokens) ? outputTokens : 0)
|
|
1326
|
+
totalTokens: (isValidNumber(inputTokens) ? inputTokens : 0) + (isValidNumber(outputTokens) ? outputTokens : 0),
|
|
1327
|
+
// Cache tokens (Anthropic prompt caching)
|
|
1328
|
+
cacheReadTokens: isValidNumber(usage.cache_read_input_tokens) ? usage.cache_read_input_tokens : void 0,
|
|
1329
|
+
cacheWriteTokens: isValidNumber(usage.cache_creation_input_tokens) ? usage.cache_creation_input_tokens : void 0
|
|
967
1330
|
};
|
|
968
1331
|
} catch {
|
|
969
1332
|
return null;
|
|
970
1333
|
}
|
|
971
1334
|
}
|
|
1335
|
+
function extractToolCalls(response) {
|
|
1336
|
+
try {
|
|
1337
|
+
if (!response.content || !Array.isArray(response.content)) {
|
|
1338
|
+
return [];
|
|
1339
|
+
}
|
|
1340
|
+
return response.content.filter(
|
|
1341
|
+
(block) => block.type === "tool_use" && !!block.id && !!block.name
|
|
1342
|
+
).map((block) => ({
|
|
1343
|
+
id: block.id,
|
|
1344
|
+
name: block.name,
|
|
1345
|
+
input: block.input ?? {}
|
|
1346
|
+
}));
|
|
1347
|
+
} catch {
|
|
1348
|
+
return [];
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
972
1351
|
|
|
973
1352
|
// src/providers/bedrock.ts
|
|
974
1353
|
var PROVIDER_NAME3 = "bedrock";
|
|
@@ -1027,14 +1406,19 @@ async function handleConverse(send, command) {
|
|
|
1027
1406
|
durationMs,
|
|
1028
1407
|
status: "success",
|
|
1029
1408
|
streaming: false,
|
|
1409
|
+
// Extended fields (Phase 7.1)
|
|
1410
|
+
stopReason: response.stopReason,
|
|
1411
|
+
cacheReadTokens: extracted.cacheReadTokens,
|
|
1412
|
+
cacheWriteTokens: extracted.cacheWriteTokens,
|
|
1030
1413
|
metadata: {
|
|
1031
|
-
stopReason: response.stopReason,
|
|
1032
1414
|
hasToolUse: extracted.hasToolUse,
|
|
1033
|
-
cacheReadTokens: extracted.cacheReadTokens,
|
|
1034
|
-
cacheWriteTokens: extracted.cacheWriteTokens,
|
|
1035
1415
|
latencyMs: response.metrics?.latencyMs
|
|
1036
1416
|
}
|
|
1037
1417
|
});
|
|
1418
|
+
const toolCalls = extractToolCalls2(response);
|
|
1419
|
+
if (toolCalls.length > 0) {
|
|
1420
|
+
captureToolSpans(toolCalls, PROVIDER_NAME3);
|
|
1421
|
+
}
|
|
1038
1422
|
return response;
|
|
1039
1423
|
} catch (error) {
|
|
1040
1424
|
captureError({
|
|
@@ -1077,11 +1461,36 @@ async function* wrapConverseStream(stream, input, startTime) {
|
|
|
1077
1461
|
let inputTokens = 0;
|
|
1078
1462
|
let outputTokens = 0;
|
|
1079
1463
|
let error = null;
|
|
1464
|
+
let stopReason;
|
|
1465
|
+
let firstTokenMs;
|
|
1466
|
+
let firstContentReceived = false;
|
|
1467
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1080
1468
|
try {
|
|
1081
1469
|
for await (const event of stream) {
|
|
1082
|
-
if (event.contentBlockDelta?.delta?.text) {
|
|
1470
|
+
if (event.contentBlockDelta?.delta?.text && !firstContentReceived) {
|
|
1471
|
+
firstContentReceived = true;
|
|
1472
|
+
firstTokenMs = Date.now() - startTime;
|
|
1473
|
+
chunks.push(event.contentBlockDelta.delta.text);
|
|
1474
|
+
} else if (event.contentBlockDelta?.delta?.text) {
|
|
1083
1475
|
chunks.push(event.contentBlockDelta.delta.text);
|
|
1084
1476
|
}
|
|
1477
|
+
if (event.contentBlockStart?.start?.toolUse) {
|
|
1478
|
+
const tool = event.contentBlockStart.start.toolUse;
|
|
1479
|
+
toolCalls.set(event.contentBlockStart.contentBlockIndex, {
|
|
1480
|
+
id: tool.toolUseId,
|
|
1481
|
+
name: tool.name,
|
|
1482
|
+
inputJson: ""
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
if (event.contentBlockDelta?.delta?.toolUse?.input) {
|
|
1486
|
+
const tool = toolCalls.get(event.contentBlockDelta.contentBlockIndex);
|
|
1487
|
+
if (tool) {
|
|
1488
|
+
tool.inputJson += event.contentBlockDelta.delta.toolUse.input;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
if (event.messageStop?.stopReason) {
|
|
1492
|
+
stopReason = event.messageStop.stopReason;
|
|
1493
|
+
}
|
|
1085
1494
|
if (event.metadata?.usage) {
|
|
1086
1495
|
inputTokens = event.metadata.usage.inputTokens || 0;
|
|
1087
1496
|
outputTokens = event.metadata.usage.outputTokens || 0;
|
|
@@ -1112,8 +1521,24 @@ async function* wrapConverseStream(stream, input, startTime) {
|
|
|
1112
1521
|
outputTokens,
|
|
1113
1522
|
durationMs,
|
|
1114
1523
|
status: "success",
|
|
1115
|
-
streaming: true
|
|
1524
|
+
streaming: true,
|
|
1525
|
+
// Extended fields (Phase 7.1)
|
|
1526
|
+
stopReason,
|
|
1527
|
+
firstTokenMs
|
|
1116
1528
|
});
|
|
1529
|
+
if (toolCalls.size > 0) {
|
|
1530
|
+
const tools = Array.from(toolCalls.values()).map((t) => {
|
|
1531
|
+
let parsedInput = {};
|
|
1532
|
+
try {
|
|
1533
|
+
if (t.inputJson) {
|
|
1534
|
+
parsedInput = JSON.parse(t.inputJson);
|
|
1535
|
+
}
|
|
1536
|
+
} catch {
|
|
1537
|
+
}
|
|
1538
|
+
return { id: t.id, name: t.name, input: parsedInput };
|
|
1539
|
+
});
|
|
1540
|
+
captureToolSpans(tools, PROVIDER_NAME3);
|
|
1541
|
+
}
|
|
1117
1542
|
}
|
|
1118
1543
|
}
|
|
1119
1544
|
}
|
|
@@ -1238,6 +1663,23 @@ function extractConverseOutput(response) {
|
|
|
1238
1663
|
hasToolUse
|
|
1239
1664
|
};
|
|
1240
1665
|
}
|
|
1666
|
+
function extractToolCalls2(response) {
|
|
1667
|
+
try {
|
|
1668
|
+
const content = response.output?.message?.content;
|
|
1669
|
+
if (!content || !Array.isArray(content)) {
|
|
1670
|
+
return [];
|
|
1671
|
+
}
|
|
1672
|
+
return content.filter(
|
|
1673
|
+
(block) => !!block.toolUse && !!block.toolUse.toolUseId && !!block.toolUse.name
|
|
1674
|
+
).map((block) => ({
|
|
1675
|
+
id: block.toolUse.toolUseId,
|
|
1676
|
+
name: block.toolUse.name,
|
|
1677
|
+
input: block.toolUse.input ?? {}
|
|
1678
|
+
}));
|
|
1679
|
+
} catch {
|
|
1680
|
+
return [];
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1241
1683
|
function parseInvokeModelBody(body) {
|
|
1242
1684
|
try {
|
|
1243
1685
|
const text = new TextDecoder().decode(body);
|
|
@@ -1907,6 +2349,6 @@ function createObserve(defaultOptions) {
|
|
|
1907
2349
|
};
|
|
1908
2350
|
}
|
|
1909
2351
|
|
|
1910
|
-
export { createObserve, flush, init, isEnabled, observe };
|
|
2352
|
+
export { captureSpan, createObserve, flush, getTraceContext, init, isEnabled, observe, span, trace };
|
|
1911
2353
|
//# sourceMappingURL=index.mjs.map
|
|
1912
2354
|
//# sourceMappingURL=index.mjs.map
|