@tanstack/ai 0.32.0 → 0.34.0
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/esm/activities/chat/index.js +47 -20
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +7 -0
- package/dist/esm/activities/chat/tools/lazy-tool-manager.d.ts +25 -1
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +26 -2
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +13 -0
- package/dist/esm/activities/chat/tools/schema-converter.js +61 -33
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.d.ts +7 -0
- package/dist/esm/activities/generateAudio/index.js +26 -1
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +7 -0
- package/dist/esm/activities/generateImage/index.js +26 -1
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +7 -0
- package/dist/esm/activities/generateSpeech/index.js +26 -1
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +7 -0
- package/dist/esm/activities/generateTranscription/index.js +26 -1
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +9 -0
- package/dist/esm/activities/generateVideo/index.js +52 -2
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/middleware/index.d.ts +2 -0
- package/dist/esm/activities/middleware/run.d.ts +20 -0
- package/dist/esm/activities/middleware/run.js +42 -0
- package/dist/esm/activities/middleware/run.js.map +1 -0
- package/dist/esm/activities/middleware/types.d.ts +118 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/middlewares/otel.d.ts +8 -2
- package/dist/esm/middlewares/otel.js +145 -95
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/middlewares/usage-attributes.d.ts +24 -0
- package/dist/esm/middlewares/usage-attributes.js +43 -0
- package/dist/esm/middlewares/usage-attributes.js.map +1 -0
- package/dist/esm/types.d.ts +7 -7
- package/dist/esm/utilities/errors.d.ts +13 -0
- package/dist/esm/utilities/errors.js +22 -0
- package/dist/esm/utilities/errors.js.map +1 -0
- package/dist/esm/utilities/numbers.d.ts +8 -0
- package/dist/esm/utilities/numbers.js +12 -0
- package/dist/esm/utilities/numbers.js.map +1 -0
- package/package.json +3 -2
- package/src/activities/chat/index.ts +125 -35
- package/src/activities/chat/middleware/types.ts +7 -0
- package/src/activities/chat/tools/lazy-tool-manager.ts +46 -4
- package/src/activities/chat/tools/schema-converter.ts +146 -93
- package/src/activities/generateAudio/index.ts +42 -1
- package/src/activities/generateImage/index.ts +42 -1
- package/src/activities/generateSpeech/index.ts +42 -1
- package/src/activities/generateTranscription/index.ts +42 -1
- package/src/activities/generateVideo/index.ts +88 -2
- package/src/activities/middleware/index.ts +20 -0
- package/src/activities/middleware/run.ts +88 -0
- package/src/activities/middleware/types.ts +173 -0
- package/src/index.ts +19 -0
- package/src/middlewares/otel.ts +195 -120
- package/src/middlewares/usage-attributes.ts +65 -0
- package/src/types.ts +7 -7
- package/src/utilities/errors.ts +29 -0
- package/src/utilities/numbers.ts +15 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SpanKind, SpanStatusCode, trace, context } from "@opentelemetry/api";
|
|
2
2
|
import { MAX_TOKENS_KEYS, NESTED_MAX_TOKENS_KEY } from "../utilities/sampling-keys.js";
|
|
3
|
+
import { firstNumber } from "../utilities/numbers.js";
|
|
4
|
+
import { errorMessage, errorTypeName } from "../utilities/errors.js";
|
|
5
|
+
import { usageAttributes } from "./usage-attributes.js";
|
|
6
|
+
const OPERATION_NAME = {
|
|
7
|
+
chat: "chat",
|
|
8
|
+
image: "image_generation",
|
|
9
|
+
video: "video_generation",
|
|
10
|
+
audio: "audio_generation",
|
|
11
|
+
tts: "text_to_speech",
|
|
12
|
+
transcription: "transcription"
|
|
13
|
+
};
|
|
3
14
|
const stateByCtx = /* @__PURE__ */ new WeakMap();
|
|
4
15
|
const DEFAULT_MAX_CONTENT_LENGTH = 1e5;
|
|
5
16
|
const REDACTION_FAILED_SENTINEL = "[redaction_failed]";
|
|
@@ -51,68 +62,6 @@ function messageEventName(role) {
|
|
|
51
62
|
return `gen_ai.${role}.message`;
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
|
-
function firstNumber(...candidates) {
|
|
55
|
-
for (const candidate of candidates) {
|
|
56
|
-
if (typeof candidate === "number" && Number.isFinite(candidate)) {
|
|
57
|
-
return candidate;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return void 0;
|
|
61
|
-
}
|
|
62
|
-
function usageAttributes(usage) {
|
|
63
|
-
const attrs = {
|
|
64
|
-
"gen_ai.usage.input_tokens": usage.promptTokens,
|
|
65
|
-
"gen_ai.usage.output_tokens": usage.completionTokens
|
|
66
|
-
};
|
|
67
|
-
const optional = [
|
|
68
|
-
["gen_ai.usage.total_tokens", usage.totalTokens],
|
|
69
|
-
["gen_ai.usage.cost", usage.cost],
|
|
70
|
-
[
|
|
71
|
-
"gen_ai.usage.cache_read.input_tokens",
|
|
72
|
-
usage.promptTokensDetails?.cachedTokens
|
|
73
|
-
],
|
|
74
|
-
[
|
|
75
|
-
"gen_ai.usage.cache_creation.input_tokens",
|
|
76
|
-
usage.promptTokensDetails?.cacheWriteTokens
|
|
77
|
-
],
|
|
78
|
-
[
|
|
79
|
-
"gen_ai.usage.reasoning.output_tokens",
|
|
80
|
-
usage.completionTokensDetails?.reasoningTokens
|
|
81
|
-
],
|
|
82
|
-
["tanstack.ai.usage.duration_seconds", usage.durationSeconds],
|
|
83
|
-
["tanstack.ai.usage.upstream_cost", usage.costDetails?.upstreamCost],
|
|
84
|
-
[
|
|
85
|
-
"tanstack.ai.usage.upstream_input_cost",
|
|
86
|
-
usage.costDetails?.upstreamInputCost
|
|
87
|
-
],
|
|
88
|
-
[
|
|
89
|
-
"tanstack.ai.usage.upstream_output_cost",
|
|
90
|
-
usage.costDetails?.upstreamOutputCost
|
|
91
|
-
]
|
|
92
|
-
];
|
|
93
|
-
for (const [key, value] of optional) {
|
|
94
|
-
const num = firstNumber(value);
|
|
95
|
-
if (num !== void 0) attrs[key] = num;
|
|
96
|
-
}
|
|
97
|
-
return attrs;
|
|
98
|
-
}
|
|
99
|
-
function errorMessage(err) {
|
|
100
|
-
if (err instanceof Error) return err.message;
|
|
101
|
-
if (typeof err === "string") return err;
|
|
102
|
-
if (err && typeof err === "object" && "message" in err) {
|
|
103
|
-
const m = err.message;
|
|
104
|
-
if (typeof m === "string") return m;
|
|
105
|
-
}
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
|
-
function errorTypeName(err) {
|
|
109
|
-
if (err instanceof Error) return err.name || "Error";
|
|
110
|
-
if (err && typeof err === "object" && "name" in err) {
|
|
111
|
-
const n = err.name;
|
|
112
|
-
if (typeof n === "string") return n;
|
|
113
|
-
}
|
|
114
|
-
return "Error";
|
|
115
|
-
}
|
|
116
65
|
function safeCall(label, fn) {
|
|
117
66
|
try {
|
|
118
67
|
return fn();
|
|
@@ -180,17 +129,69 @@ function otelMiddleware(options) {
|
|
|
180
129
|
span.end();
|
|
181
130
|
state.currentIterationSpan = null;
|
|
182
131
|
};
|
|
132
|
+
const mediaSpans = /* @__PURE__ */ new WeakMap();
|
|
133
|
+
const recordMediaDuration = (ctx, durationMs, errorType) => {
|
|
134
|
+
if (!durationHistogram) return;
|
|
135
|
+
durationHistogram.record(durationMs / 1e3, {
|
|
136
|
+
"gen_ai.system": ctx.provider,
|
|
137
|
+
"gen_ai.operation.name": OPERATION_NAME[ctx.activity],
|
|
138
|
+
"gen_ai.request.model": ctx.model,
|
|
139
|
+
...errorType ? { "error.type": errorType } : {}
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const startMediaSpan = (ctx) => {
|
|
143
|
+
safeCall("otel.onStart", () => {
|
|
144
|
+
const operationName = OPERATION_NAME[ctx.activity];
|
|
145
|
+
const info = { kind: "generation", ctx };
|
|
146
|
+
const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `${operationName} ${ctx.model}`;
|
|
147
|
+
const baseOptions = {
|
|
148
|
+
kind: SpanKind.CLIENT,
|
|
149
|
+
attributes: {
|
|
150
|
+
"gen_ai.system": ctx.provider,
|
|
151
|
+
"gen_ai.operation.name": operationName,
|
|
152
|
+
"gen_ai.request.model": ctx.model
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const spanOptions = safeCall(
|
|
156
|
+
"otel.onBeforeSpanStart",
|
|
157
|
+
() => onBeforeSpanStart?.(info, baseOptions)
|
|
158
|
+
) ?? baseOptions;
|
|
159
|
+
const span = tracer.startSpan(name, spanOptions);
|
|
160
|
+
const enriched = safeCall(
|
|
161
|
+
"otel.attributeEnricher",
|
|
162
|
+
() => attributeEnricher?.(info)
|
|
163
|
+
);
|
|
164
|
+
if (enriched) span.setAttributes(enriched);
|
|
165
|
+
mediaSpans.set(ctx, span);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const endMediaSpan = (ctx, finalize) => {
|
|
169
|
+
const span = mediaSpans.get(ctx);
|
|
170
|
+
mediaSpans.delete(ctx);
|
|
171
|
+
if (!span) return;
|
|
172
|
+
finalize(span);
|
|
173
|
+
safeCall(
|
|
174
|
+
"otel.onSpanEnd",
|
|
175
|
+
() => onSpanEnd?.({ kind: "generation", ctx }, span)
|
|
176
|
+
);
|
|
177
|
+
span.end();
|
|
178
|
+
};
|
|
183
179
|
return {
|
|
184
180
|
name: "otel",
|
|
185
181
|
onStart(ctx) {
|
|
182
|
+
if (ctx.activity !== "chat") {
|
|
183
|
+
startMediaSpan(ctx);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const chatCtx = ctx;
|
|
186
187
|
safeCall("otel.onStart", () => {
|
|
187
|
-
const info = { kind: "chat", ctx };
|
|
188
|
-
const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `chat ${
|
|
188
|
+
const info = { kind: "chat", ctx: chatCtx };
|
|
189
|
+
const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `chat ${chatCtx.model}`;
|
|
189
190
|
const baseOptions = {
|
|
190
191
|
kind: SpanKind.INTERNAL,
|
|
191
192
|
attributes: {
|
|
192
|
-
"gen_ai.system":
|
|
193
|
-
"gen_ai.request.model":
|
|
193
|
+
"gen_ai.system": chatCtx.provider,
|
|
194
|
+
"gen_ai.request.model": chatCtx.model
|
|
194
195
|
// NOTE: `gen_ai.operation.name` is deliberately NOT set on the
|
|
195
196
|
// root span. The root represents a `chat()` invocation that may
|
|
196
197
|
// span multiple model calls; only iteration spans correspond to
|
|
@@ -209,14 +210,15 @@ function otelMiddleware(options) {
|
|
|
209
210
|
() => attributeEnricher?.(info)
|
|
210
211
|
);
|
|
211
212
|
if (enriched) rootSpan.setAttributes(enriched);
|
|
212
|
-
stateByCtx.set(
|
|
213
|
+
stateByCtx.set(chatCtx, {
|
|
213
214
|
rootSpan,
|
|
214
215
|
currentIterationSpan: null,
|
|
215
216
|
toolSpans: /* @__PURE__ */ new Map(),
|
|
216
217
|
iterationCount: 0,
|
|
217
218
|
assistantTextBuffer: "",
|
|
218
219
|
assistantTextBufferTruncated: false,
|
|
219
|
-
startTime: Date.now()
|
|
220
|
+
startTime: Date.now(),
|
|
221
|
+
lastFinishReason: null
|
|
220
222
|
});
|
|
221
223
|
});
|
|
222
224
|
},
|
|
@@ -348,6 +350,7 @@ function otelMiddleware(options) {
|
|
|
348
350
|
appendAssistantText(state, chunk.delta);
|
|
349
351
|
}
|
|
350
352
|
if (chunk.type !== "RUN_FINISHED") return;
|
|
353
|
+
if (chunk.finishReason) state.lastFinishReason = chunk.finishReason;
|
|
351
354
|
const span = state.currentIterationSpan;
|
|
352
355
|
if (!span) return;
|
|
353
356
|
if (chunk.finishReason) {
|
|
@@ -376,14 +379,22 @@ function otelMiddleware(options) {
|
|
|
376
379
|
return void 0;
|
|
377
380
|
},
|
|
378
381
|
onUsage(ctx, usage) {
|
|
382
|
+
if (ctx.activity !== "chat") {
|
|
383
|
+
safeCall("otel.onUsage", () => {
|
|
384
|
+
const span = mediaSpans.get(ctx);
|
|
385
|
+
if (span) span.setAttributes(usageAttributes(usage));
|
|
386
|
+
});
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const chatCtx = ctx;
|
|
379
390
|
safeCall("otel.onUsage", () => {
|
|
380
|
-
const state = stateByCtx.get(
|
|
391
|
+
const state = stateByCtx.get(chatCtx);
|
|
381
392
|
if (!state) return;
|
|
382
393
|
if (tokenHistogram) {
|
|
383
394
|
const metricAttrs = {
|
|
384
|
-
"gen_ai.system":
|
|
395
|
+
"gen_ai.system": chatCtx.provider,
|
|
385
396
|
"gen_ai.operation.name": "chat",
|
|
386
|
-
"gen_ai.request.model":
|
|
397
|
+
"gen_ai.request.model": chatCtx.model
|
|
387
398
|
};
|
|
388
399
|
tokenHistogram.record(usage.promptTokens, {
|
|
389
400
|
...metricAttrs,
|
|
@@ -506,8 +517,23 @@ function otelMiddleware(options) {
|
|
|
506
517
|
});
|
|
507
518
|
},
|
|
508
519
|
onError(ctx, info) {
|
|
520
|
+
if (ctx.activity !== "chat") {
|
|
521
|
+
safeCall("otel.onError", () => {
|
|
522
|
+
const message = errorMessage(info.error);
|
|
523
|
+
endMediaSpan(ctx, (span) => {
|
|
524
|
+
span.recordException(info.error);
|
|
525
|
+
span.setStatus({
|
|
526
|
+
code: SpanStatusCode.ERROR,
|
|
527
|
+
...message !== void 0 ? { message } : {}
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
recordMediaDuration(ctx, info.duration, errorTypeName(info.error));
|
|
531
|
+
});
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const chatCtx = ctx;
|
|
509
535
|
safeCall("otel.onError", () => {
|
|
510
|
-
const state = stateByCtx.get(
|
|
536
|
+
const state = stateByCtx.get(chatCtx);
|
|
511
537
|
if (!state) return;
|
|
512
538
|
const errType = errorTypeName(info.error);
|
|
513
539
|
const message = errorMessage(info.error);
|
|
@@ -525,7 +551,7 @@ function otelMiddleware(options) {
|
|
|
525
551
|
() => onSpanEnd?.(
|
|
526
552
|
{
|
|
527
553
|
kind: "iteration",
|
|
528
|
-
ctx,
|
|
554
|
+
ctx: chatCtx,
|
|
529
555
|
iteration: state.iterationCount - 1
|
|
530
556
|
},
|
|
531
557
|
iterationSpan
|
|
@@ -543,7 +569,7 @@ function otelMiddleware(options) {
|
|
|
543
569
|
() => onSpanEnd?.(
|
|
544
570
|
{
|
|
545
571
|
kind: "tool",
|
|
546
|
-
ctx,
|
|
572
|
+
ctx: chatCtx,
|
|
547
573
|
toolCallId: id,
|
|
548
574
|
toolName,
|
|
549
575
|
iteration: state.iterationCount - 1
|
|
@@ -561,23 +587,37 @@ function otelMiddleware(options) {
|
|
|
561
587
|
});
|
|
562
588
|
if (durationHistogram) {
|
|
563
589
|
durationHistogram.record(info.duration / 1e3, {
|
|
564
|
-
"gen_ai.system":
|
|
590
|
+
"gen_ai.system": chatCtx.provider,
|
|
565
591
|
"gen_ai.operation.name": "chat",
|
|
566
|
-
"gen_ai.request.model":
|
|
592
|
+
"gen_ai.request.model": chatCtx.model,
|
|
567
593
|
"error.type": errType
|
|
568
594
|
});
|
|
569
595
|
}
|
|
570
596
|
safeCall(
|
|
571
597
|
"otel.onSpanEnd",
|
|
572
|
-
() => onSpanEnd?.({ kind: "chat", ctx }, state.rootSpan)
|
|
598
|
+
() => onSpanEnd?.({ kind: "chat", ctx: chatCtx }, state.rootSpan)
|
|
573
599
|
);
|
|
574
600
|
state.rootSpan.end();
|
|
575
|
-
stateByCtx.delete(
|
|
601
|
+
stateByCtx.delete(chatCtx);
|
|
576
602
|
});
|
|
577
603
|
},
|
|
578
604
|
onAbort(ctx, info) {
|
|
605
|
+
if (ctx.activity !== "chat") {
|
|
606
|
+
safeCall("otel.onAbort", () => {
|
|
607
|
+
endMediaSpan(ctx, (span) => {
|
|
608
|
+
span.setAttribute("tanstack.ai.completion.reason", "cancelled");
|
|
609
|
+
span.setStatus({
|
|
610
|
+
code: SpanStatusCode.ERROR,
|
|
611
|
+
message: info.reason ?? "cancelled"
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
recordMediaDuration(ctx, info.duration, "cancelled");
|
|
615
|
+
});
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
const chatCtx = ctx;
|
|
579
619
|
safeCall("otel.onAbort", () => {
|
|
580
|
-
const state = stateByCtx.get(
|
|
620
|
+
const state = stateByCtx.get(chatCtx);
|
|
581
621
|
if (!state) return;
|
|
582
622
|
const closeCancelled = (span) => {
|
|
583
623
|
span.setAttribute("tanstack.ai.completion.reason", "cancelled");
|
|
@@ -591,7 +631,7 @@ function otelMiddleware(options) {
|
|
|
591
631
|
() => onSpanEnd?.(
|
|
592
632
|
{
|
|
593
633
|
kind: "iteration",
|
|
594
|
-
ctx,
|
|
634
|
+
ctx: chatCtx,
|
|
595
635
|
iteration: state.iterationCount - 1
|
|
596
636
|
},
|
|
597
637
|
iterationSpan
|
|
@@ -608,7 +648,7 @@ function otelMiddleware(options) {
|
|
|
608
648
|
() => onSpanEnd?.(
|
|
609
649
|
{
|
|
610
650
|
kind: "tool",
|
|
611
|
-
ctx,
|
|
651
|
+
ctx: chatCtx,
|
|
612
652
|
toolCallId: id,
|
|
613
653
|
toolName,
|
|
614
654
|
iteration: state.iterationCount - 1
|
|
@@ -622,23 +662,33 @@ function otelMiddleware(options) {
|
|
|
622
662
|
closeCancelled(state.rootSpan);
|
|
623
663
|
if (durationHistogram) {
|
|
624
664
|
durationHistogram.record(info.duration / 1e3, {
|
|
625
|
-
"gen_ai.system":
|
|
665
|
+
"gen_ai.system": chatCtx.provider,
|
|
626
666
|
"gen_ai.operation.name": "chat",
|
|
627
|
-
"gen_ai.request.model":
|
|
667
|
+
"gen_ai.request.model": chatCtx.model,
|
|
628
668
|
"error.type": "cancelled"
|
|
629
669
|
});
|
|
630
670
|
}
|
|
631
671
|
safeCall(
|
|
632
672
|
"otel.onSpanEnd",
|
|
633
|
-
() => onSpanEnd?.({ kind: "chat", ctx }, state.rootSpan)
|
|
673
|
+
() => onSpanEnd?.({ kind: "chat", ctx: chatCtx }, state.rootSpan)
|
|
634
674
|
);
|
|
635
675
|
state.rootSpan.end();
|
|
636
|
-
stateByCtx.delete(
|
|
676
|
+
stateByCtx.delete(chatCtx);
|
|
637
677
|
});
|
|
638
678
|
},
|
|
639
679
|
onFinish(ctx, info) {
|
|
680
|
+
if (ctx.activity !== "chat") {
|
|
681
|
+
safeCall("otel.onFinish", () => {
|
|
682
|
+
endMediaSpan(ctx, (span) => {
|
|
683
|
+
if (info.usage) span.setAttributes(usageAttributes(info.usage));
|
|
684
|
+
});
|
|
685
|
+
recordMediaDuration(ctx, info.duration);
|
|
686
|
+
});
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const chatCtx = ctx;
|
|
640
690
|
safeCall("otel.onFinish", () => {
|
|
641
|
-
const state = stateByCtx.get(
|
|
691
|
+
const state = stateByCtx.get(chatCtx);
|
|
642
692
|
if (!state) return;
|
|
643
693
|
for (const [id, entry] of state.toolSpans) {
|
|
644
694
|
const { span, toolName } = entry;
|
|
@@ -648,7 +698,7 @@ function otelMiddleware(options) {
|
|
|
648
698
|
() => onSpanEnd?.(
|
|
649
699
|
{
|
|
650
700
|
kind: "tool",
|
|
651
|
-
ctx,
|
|
701
|
+
ctx: chatCtx,
|
|
652
702
|
toolCallId: id,
|
|
653
703
|
toolName,
|
|
654
704
|
iteration: state.iterationCount - 1
|
|
@@ -659,20 +709,20 @@ function otelMiddleware(options) {
|
|
|
659
709
|
span.end();
|
|
660
710
|
state.toolSpans.delete(id);
|
|
661
711
|
}
|
|
662
|
-
closeIterationSpan(state,
|
|
712
|
+
closeIterationSpan(state, chatCtx);
|
|
663
713
|
if (durationHistogram) {
|
|
664
714
|
durationHistogram.record(info.duration / 1e3, {
|
|
665
|
-
"gen_ai.system":
|
|
715
|
+
"gen_ai.system": chatCtx.provider,
|
|
666
716
|
"gen_ai.operation.name": "chat",
|
|
667
|
-
"gen_ai.request.model":
|
|
717
|
+
"gen_ai.request.model": chatCtx.model
|
|
668
718
|
});
|
|
669
719
|
}
|
|
670
720
|
if (info.usage) {
|
|
671
721
|
state.rootSpan.setAttributes(usageAttributes(info.usage));
|
|
672
722
|
}
|
|
673
|
-
if (
|
|
723
|
+
if (state.lastFinishReason) {
|
|
674
724
|
state.rootSpan.setAttribute("gen_ai.response.finish_reasons", [
|
|
675
|
-
|
|
725
|
+
state.lastFinishReason
|
|
676
726
|
]);
|
|
677
727
|
}
|
|
678
728
|
state.rootSpan.setAttribute(
|
|
@@ -681,10 +731,10 @@ function otelMiddleware(options) {
|
|
|
681
731
|
);
|
|
682
732
|
safeCall(
|
|
683
733
|
"otel.onSpanEnd",
|
|
684
|
-
() => onSpanEnd?.({ kind: "chat", ctx }, state.rootSpan)
|
|
734
|
+
() => onSpanEnd?.({ kind: "chat", ctx: chatCtx }, state.rootSpan)
|
|
685
735
|
);
|
|
686
736
|
state.rootSpan.end();
|
|
687
|
-
stateByCtx.delete(
|
|
737
|
+
stateByCtx.delete(chatCtx);
|
|
688
738
|
});
|
|
689
739
|
}
|
|
690
740
|
};
|