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