@tanstack/ai 0.35.0 → 0.36.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.
Files changed (208) hide show
  1. package/package.json +2 -2
  2. package/skills/ai-core/tool-calling/SKILL.md +26 -1
  3. package/src/activities/chat/index.ts +8 -0
  4. package/src/activities/chat/tools/lazy-tool-manager.ts +12 -4
  5. package/src/activities/chat/tools/lazy-tools.ts +33 -0
  6. package/src/index.ts +5 -0
  7. package/src/types.ts +23 -1
  8. package/dist/esm/activities/chat/adapter.d.ts +0 -163
  9. package/dist/esm/activities/chat/adapter.js +0 -17
  10. package/dist/esm/activities/chat/adapter.js.map +0 -1
  11. package/dist/esm/activities/chat/agent-loop-strategies.d.ts +0 -59
  12. package/dist/esm/activities/chat/agent-loop-strategies.js +0 -23
  13. package/dist/esm/activities/chat/agent-loop-strategies.js.map +0 -1
  14. package/dist/esm/activities/chat/index.d.ts +0 -264
  15. package/dist/esm/activities/chat/index.js +0 -1723
  16. package/dist/esm/activities/chat/index.js.map +0 -1
  17. package/dist/esm/activities/chat/mcp/manager.d.ts +0 -25
  18. package/dist/esm/activities/chat/mcp/manager.js +0 -71
  19. package/dist/esm/activities/chat/mcp/manager.js.map +0 -1
  20. package/dist/esm/activities/chat/mcp/types.d.ts +0 -56
  21. package/dist/esm/activities/chat/messages.d.ts +0 -78
  22. package/dist/esm/activities/chat/messages.js +0 -374
  23. package/dist/esm/activities/chat/messages.js.map +0 -1
  24. package/dist/esm/activities/chat/middleware/builder.d.ts +0 -46
  25. package/dist/esm/activities/chat/middleware/builder.js +0 -17
  26. package/dist/esm/activities/chat/middleware/builder.js.map +0 -1
  27. package/dist/esm/activities/chat/middleware/capabilities.d.ts +0 -93
  28. package/dist/esm/activities/chat/middleware/capabilities.js +0 -45
  29. package/dist/esm/activities/chat/middleware/capabilities.js.map +0 -1
  30. package/dist/esm/activities/chat/middleware/compose.d.ts +0 -87
  31. package/dist/esm/activities/chat/middleware/compose.js +0 -510
  32. package/dist/esm/activities/chat/middleware/compose.js.map +0 -1
  33. package/dist/esm/activities/chat/middleware/define.d.ts +0 -20
  34. package/dist/esm/activities/chat/middleware/define.js +0 -7
  35. package/dist/esm/activities/chat/middleware/define.js.map +0 -1
  36. package/dist/esm/activities/chat/middleware/index.d.ts +0 -10
  37. package/dist/esm/activities/chat/middleware/tool-cache-middleware.d.ts +0 -89
  38. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +0 -76
  39. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +0 -1
  40. package/dist/esm/activities/chat/middleware/types.d.ts +0 -405
  41. package/dist/esm/activities/chat/middleware/validate.d.ts +0 -19
  42. package/dist/esm/activities/chat/middleware/validate.js +0 -30
  43. package/dist/esm/activities/chat/middleware/validate.js.map +0 -1
  44. package/dist/esm/activities/chat/runtime-context-types.d.ts +0 -43
  45. package/dist/esm/activities/chat/stream/index.d.ts +0 -11
  46. package/dist/esm/activities/chat/stream/json-parser.d.ts +0 -38
  47. package/dist/esm/activities/chat/stream/json-parser.js +0 -28
  48. package/dist/esm/activities/chat/stream/json-parser.js.map +0 -1
  49. package/dist/esm/activities/chat/stream/message-updaters.d.ts +0 -81
  50. package/dist/esm/activities/chat/stream/message-updaters.js +0 -253
  51. package/dist/esm/activities/chat/stream/message-updaters.js.map +0 -1
  52. package/dist/esm/activities/chat/stream/processor.d.ts +0 -439
  53. package/dist/esm/activities/chat/stream/processor.js +0 -1426
  54. package/dist/esm/activities/chat/stream/processor.js.map +0 -1
  55. package/dist/esm/activities/chat/stream/strategies.d.ts +0 -43
  56. package/dist/esm/activities/chat/stream/strategies.js +0 -54
  57. package/dist/esm/activities/chat/stream/strategies.js.map +0 -1
  58. package/dist/esm/activities/chat/stream/types.d.ts +0 -90
  59. package/dist/esm/activities/chat/tools/lazy-tool-manager.d.ts +0 -81
  60. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +0 -188
  61. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +0 -1
  62. package/dist/esm/activities/chat/tools/schema-converter.d.ts +0 -140
  63. package/dist/esm/activities/chat/tools/schema-converter.js +0 -167
  64. package/dist/esm/activities/chat/tools/schema-converter.js.map +0 -1
  65. package/dist/esm/activities/chat/tools/tool-calls.d.ts +0 -140
  66. package/dist/esm/activities/chat/tools/tool-calls.js +0 -512
  67. package/dist/esm/activities/chat/tools/tool-calls.js.map +0 -1
  68. package/dist/esm/activities/chat/tools/tool-definition.d.ts +0 -135
  69. package/dist/esm/activities/chat/tools/tool-definition.js +0 -25
  70. package/dist/esm/activities/chat/tools/tool-definition.js.map +0 -1
  71. package/dist/esm/activities/error-payload.d.ts +0 -30
  72. package/dist/esm/activities/error-payload.js +0 -54
  73. package/dist/esm/activities/error-payload.js.map +0 -1
  74. package/dist/esm/activities/generateAudio/adapter.d.ts +0 -62
  75. package/dist/esm/activities/generateAudio/adapter.js +0 -16
  76. package/dist/esm/activities/generateAudio/adapter.js.map +0 -1
  77. package/dist/esm/activities/generateAudio/index.d.ts +0 -85
  78. package/dist/esm/activities/generateAudio/index.js +0 -114
  79. package/dist/esm/activities/generateAudio/index.js.map +0 -1
  80. package/dist/esm/activities/generateImage/adapter.d.ts +0 -78
  81. package/dist/esm/activities/generateImage/adapter.js +0 -16
  82. package/dist/esm/activities/generateImage/adapter.js.map +0 -1
  83. package/dist/esm/activities/generateImage/index.d.ts +0 -134
  84. package/dist/esm/activities/generateImage/index.js +0 -121
  85. package/dist/esm/activities/generateImage/index.js.map +0 -1
  86. package/dist/esm/activities/generateSpeech/adapter.d.ts +0 -62
  87. package/dist/esm/activities/generateSpeech/adapter.js +0 -16
  88. package/dist/esm/activities/generateSpeech/adapter.js.map +0 -1
  89. package/dist/esm/activities/generateSpeech/index.d.ts +0 -96
  90. package/dist/esm/activities/generateSpeech/index.js +0 -119
  91. package/dist/esm/activities/generateSpeech/index.js.map +0 -1
  92. package/dist/esm/activities/generateTranscription/adapter.d.ts +0 -62
  93. package/dist/esm/activities/generateTranscription/adapter.js +0 -16
  94. package/dist/esm/activities/generateTranscription/adapter.js.map +0 -1
  95. package/dist/esm/activities/generateTranscription/index.d.ts +0 -109
  96. package/dist/esm/activities/generateTranscription/index.js +0 -109
  97. package/dist/esm/activities/generateTranscription/index.js.map +0 -1
  98. package/dist/esm/activities/generateVideo/adapter.d.ts +0 -145
  99. package/dist/esm/activities/generateVideo/adapter.js +0 -30
  100. package/dist/esm/activities/generateVideo/adapter.js.map +0 -1
  101. package/dist/esm/activities/generateVideo/index.d.ts +0 -223
  102. package/dist/esm/activities/generateVideo/index.js +0 -295
  103. package/dist/esm/activities/generateVideo/index.js.map +0 -1
  104. package/dist/esm/activities/generateVideo/snap.d.ts +0 -14
  105. package/dist/esm/activities/generateVideo/snap.js +0 -54
  106. package/dist/esm/activities/generateVideo/snap.js.map +0 -1
  107. package/dist/esm/activities/index.d.ts +0 -27
  108. package/dist/esm/activities/index.js +0 -43
  109. package/dist/esm/activities/index.js.map +0 -1
  110. package/dist/esm/activities/middleware/index.d.ts +0 -2
  111. package/dist/esm/activities/middleware/run.d.ts +0 -20
  112. package/dist/esm/activities/middleware/run.js +0 -42
  113. package/dist/esm/activities/middleware/run.js.map +0 -1
  114. package/dist/esm/activities/middleware/types.d.ts +0 -118
  115. package/dist/esm/activities/stream-generation-result.d.ts +0 -15
  116. package/dist/esm/activities/stream-generation-result.js +0 -49
  117. package/dist/esm/activities/stream-generation-result.js.map +0 -1
  118. package/dist/esm/activities/summarize/adapter.d.ts +0 -74
  119. package/dist/esm/activities/summarize/adapter.js +0 -16
  120. package/dist/esm/activities/summarize/adapter.js.map +0 -1
  121. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +0 -45
  122. package/dist/esm/activities/summarize/chat-stream-summarize.js +0 -212
  123. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +0 -1
  124. package/dist/esm/activities/summarize/index.d.ts +0 -108
  125. package/dist/esm/activities/summarize/index.js +0 -113
  126. package/dist/esm/activities/summarize/index.js.map +0 -1
  127. package/dist/esm/adapter-internals.d.ts +0 -5
  128. package/dist/esm/adapter-internals.js +0 -10
  129. package/dist/esm/adapter-internals.js.map +0 -1
  130. package/dist/esm/client.d.ts +0 -44
  131. package/dist/esm/client.js +0 -67
  132. package/dist/esm/client.js.map +0 -1
  133. package/dist/esm/extend-adapter.d.ts +0 -152
  134. package/dist/esm/extend-adapter.js +0 -21
  135. package/dist/esm/extend-adapter.js.map +0 -1
  136. package/dist/esm/index.d.ts +0 -44
  137. package/dist/esm/index.js +0 -106
  138. package/dist/esm/index.js.map +0 -1
  139. package/dist/esm/logger/console-logger.d.ts +0 -29
  140. package/dist/esm/logger/console-logger.js +0 -83
  141. package/dist/esm/logger/console-logger.js.map +0 -1
  142. package/dist/esm/logger/internal-logger.d.ts +0 -41
  143. package/dist/esm/logger/internal-logger.js +0 -86
  144. package/dist/esm/logger/internal-logger.js.map +0 -1
  145. package/dist/esm/logger/resolve.d.ts +0 -14
  146. package/dist/esm/logger/resolve.js +0 -54
  147. package/dist/esm/logger/resolve.js.map +0 -1
  148. package/dist/esm/logger/types.d.ts +0 -75
  149. package/dist/esm/middlewares/content-guard.d.ts +0 -77
  150. package/dist/esm/middlewares/content-guard.js +0 -156
  151. package/dist/esm/middlewares/content-guard.js.map +0 -1
  152. package/dist/esm/middlewares/index.d.ts +0 -2
  153. package/dist/esm/middlewares/index.js +0 -7
  154. package/dist/esm/middlewares/index.js.map +0 -1
  155. package/dist/esm/middlewares/otel.d.ts +0 -81
  156. package/dist/esm/middlewares/otel.js +0 -745
  157. package/dist/esm/middlewares/otel.js.map +0 -1
  158. package/dist/esm/middlewares/tool-cache.d.ts +0 -1
  159. package/dist/esm/middlewares/usage-attributes.d.ts +0 -24
  160. package/dist/esm/middlewares/usage-attributes.js +0 -43
  161. package/dist/esm/middlewares/usage-attributes.js.map +0 -1
  162. package/dist/esm/realtime/index.d.ts +0 -28
  163. package/dist/esm/realtime/index.js +0 -8
  164. package/dist/esm/realtime/index.js.map +0 -1
  165. package/dist/esm/realtime/types.d.ts +0 -282
  166. package/dist/esm/stream-to-response.d.ts +0 -102
  167. package/dist/esm/stream-to-response.js +0 -121
  168. package/dist/esm/stream-to-response.js.map +0 -1
  169. package/dist/esm/strip-to-spec-middleware.d.ts +0 -18
  170. package/dist/esm/strip-to-spec-middleware.js +0 -20
  171. package/dist/esm/strip-to-spec-middleware.js.map +0 -1
  172. package/dist/esm/system-prompts.d.ts +0 -66
  173. package/dist/esm/system-prompts.js +0 -23
  174. package/dist/esm/system-prompts.js.map +0 -1
  175. package/dist/esm/tool-registry.d.ts +0 -81
  176. package/dist/esm/tool-registry.js +0 -49
  177. package/dist/esm/tool-registry.js.map +0 -1
  178. package/dist/esm/tools/provider-tool.d.ts +0 -30
  179. package/dist/esm/tools/provider-tool.js +0 -7
  180. package/dist/esm/tools/provider-tool.js.map +0 -1
  181. package/dist/esm/types.d.ts +0 -1573
  182. package/dist/esm/utilities/ag-ui-wire.d.ts +0 -44
  183. package/dist/esm/utilities/ag-ui-wire.js +0 -107
  184. package/dist/esm/utilities/ag-ui-wire.js.map +0 -1
  185. package/dist/esm/utilities/chat-params.d.ts +0 -85
  186. package/dist/esm/utilities/chat-params.js +0 -100
  187. package/dist/esm/utilities/chat-params.js.map +0 -1
  188. package/dist/esm/utilities/errors.d.ts +0 -13
  189. package/dist/esm/utilities/errors.js +0 -22
  190. package/dist/esm/utilities/errors.js.map +0 -1
  191. package/dist/esm/utilities/media-prompt.d.ts +0 -35
  192. package/dist/esm/utilities/media-prompt.js +0 -43
  193. package/dist/esm/utilities/media-prompt.js.map +0 -1
  194. package/dist/esm/utilities/numbers.d.ts +0 -8
  195. package/dist/esm/utilities/numbers.js +0 -12
  196. package/dist/esm/utilities/numbers.js.map +0 -1
  197. package/dist/esm/utilities/sampling-keys.d.ts +0 -20
  198. package/dist/esm/utilities/sampling-keys.js +0 -20
  199. package/dist/esm/utilities/sampling-keys.js.map +0 -1
  200. package/dist/esm/utilities/tool-result.d.ts +0 -21
  201. package/dist/esm/utilities/tool-result.js +0 -37
  202. package/dist/esm/utilities/tool-result.js.map +0 -1
  203. package/dist/esm/utilities/usage.d.ts +0 -31
  204. package/dist/esm/utilities/usage.js +0 -11
  205. package/dist/esm/utilities/usage.js.map +0 -1
  206. package/dist/esm/utils.d.ts +0 -17
  207. package/dist/esm/utils.js +0 -20
  208. package/dist/esm/utils.js.map +0 -1
@@ -1,745 +0,0 @@
1
- import { SpanKind, SpanStatusCode, trace, context } from "@opentelemetry/api";
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
- };
14
- const stateByCtx = /* @__PURE__ */ new WeakMap();
15
- const DEFAULT_MAX_CONTENT_LENGTH = 1e5;
16
- const REDACTION_FAILED_SENTINEL = "[redaction_failed]";
17
- function serializeContent(content) {
18
- if (typeof content === "string") return content;
19
- if (!Array.isArray(content)) return "";
20
- const parts = [];
21
- for (const part of content) {
22
- if (!part || typeof part !== "object") continue;
23
- const type = part.type;
24
- switch (type) {
25
- case "text":
26
- parts.push(
27
- (part.text ?? part.content ?? "").toString()
28
- );
29
- break;
30
- case "image":
31
- parts.push("[image]");
32
- break;
33
- case "audio":
34
- parts.push("[audio]");
35
- break;
36
- case "video":
37
- parts.push("[video]");
38
- break;
39
- case "document":
40
- parts.push("[document]");
41
- break;
42
- case void 0:
43
- parts.push("[unknown]");
44
- break;
45
- default:
46
- parts.push(`[${type}]`);
47
- }
48
- }
49
- return parts.join(" ");
50
- }
51
- function messageEventName(role) {
52
- switch (role) {
53
- case "user":
54
- return "gen_ai.user.message";
55
- case "assistant":
56
- return "gen_ai.assistant.message";
57
- case "tool":
58
- return "gen_ai.tool.message";
59
- case "system":
60
- return "gen_ai.system.message";
61
- default:
62
- return `gen_ai.${role}.message`;
63
- }
64
- }
65
- function safeCall(label, fn) {
66
- try {
67
- return fn();
68
- } catch (err) {
69
- console.warn(`[otelMiddleware] ${label} failed`, err);
70
- return void 0;
71
- }
72
- }
73
- function otelMiddleware(options) {
74
- const {
75
- tracer,
76
- meter,
77
- captureContent = false,
78
- redact = (s) => s,
79
- maxContentLength = DEFAULT_MAX_CONTENT_LENGTH,
80
- spanNameFormatter,
81
- attributeEnricher,
82
- onBeforeSpanStart,
83
- onSpanEnd
84
- } = options;
85
- const durationHistogram = meter?.createHistogram(
86
- "gen_ai.client.operation.duration",
87
- {
88
- description: "GenAI client operation duration",
89
- unit: "s"
90
- }
91
- );
92
- const tokenHistogram = meter?.createHistogram("gen_ai.client.token.usage", {
93
- description: "GenAI client token usage",
94
- unit: "{token}"
95
- });
96
- const redactContent = (text) => {
97
- try {
98
- return redact(text);
99
- } catch (err) {
100
- console.warn("[otelMiddleware] otel.redact failed", err);
101
- return REDACTION_FAILED_SENTINEL;
102
- }
103
- };
104
- const appendAssistantText = (state, delta) => {
105
- if (maxContentLength > 0) {
106
- if (state.assistantTextBufferTruncated) return;
107
- const remaining = maxContentLength - state.assistantTextBuffer.length;
108
- if (remaining <= 0) {
109
- state.assistantTextBufferTruncated = true;
110
- state.assistantTextBuffer += "…";
111
- return;
112
- }
113
- if (delta.length > remaining) {
114
- state.assistantTextBuffer += delta.slice(0, remaining) + "…";
115
- state.assistantTextBufferTruncated = true;
116
- return;
117
- }
118
- }
119
- state.assistantTextBuffer += delta;
120
- };
121
- const closeIterationSpan = (state, ctx) => {
122
- if (!state.currentIterationSpan) return;
123
- const span = state.currentIterationSpan;
124
- const iteration = state.iterationCount - 1;
125
- safeCall(
126
- "otel.onSpanEnd",
127
- () => onSpanEnd?.({ kind: "iteration", ctx, iteration }, span)
128
- );
129
- span.end();
130
- state.currentIterationSpan = null;
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
- };
179
- return {
180
- name: "otel",
181
- onStart(ctx) {
182
- if (ctx.activity !== "chat") {
183
- startMediaSpan(ctx);
184
- return;
185
- }
186
- const chatCtx = ctx;
187
- safeCall("otel.onStart", () => {
188
- const info = { kind: "chat", ctx: chatCtx };
189
- const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `chat ${chatCtx.model}`;
190
- const baseOptions = {
191
- kind: SpanKind.INTERNAL,
192
- attributes: {
193
- "gen_ai.system": chatCtx.provider,
194
- "gen_ai.request.model": chatCtx.model
195
- // NOTE: `gen_ai.operation.name` is deliberately NOT set on the
196
- // root span. The root represents a `chat()` invocation that may
197
- // span multiple model calls; only iteration spans correspond to
198
- // a single chat operation. Backends that map `operation.name=chat`
199
- // to a "generation" event (e.g. PostHog LLM Analytics) would
200
- // otherwise emit a duplicate generation for the wrapper span.
201
- }
202
- };
203
- const spanOptions = safeCall(
204
- "otel.onBeforeSpanStart",
205
- () => onBeforeSpanStart?.(info, baseOptions)
206
- ) ?? baseOptions;
207
- const rootSpan = tracer.startSpan(name, spanOptions);
208
- const enriched = safeCall(
209
- "otel.attributeEnricher",
210
- () => attributeEnricher?.(info)
211
- );
212
- if (enriched) rootSpan.setAttributes(enriched);
213
- stateByCtx.set(chatCtx, {
214
- rootSpan,
215
- currentIterationSpan: null,
216
- toolSpans: /* @__PURE__ */ new Map(),
217
- iterationCount: 0,
218
- assistantTextBuffer: "",
219
- assistantTextBufferTruncated: false,
220
- startTime: Date.now(),
221
- lastFinishReason: null
222
- });
223
- });
224
- },
225
- onConfig(ctx, config) {
226
- if (ctx.phase !== "beforeModel") return;
227
- safeCall("otel.onConfig", () => {
228
- const state = stateByCtx.get(ctx);
229
- if (!state) return;
230
- closeIterationSpan(state, ctx);
231
- const info = {
232
- kind: "iteration",
233
- ctx,
234
- iteration: ctx.iteration
235
- };
236
- const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `chat ${ctx.model} #${ctx.iteration}`;
237
- const baseAttrs = {
238
- "gen_ai.system": ctx.provider,
239
- "gen_ai.operation.name": "chat",
240
- "gen_ai.request.model": ctx.model,
241
- "tanstack.ai.iteration": ctx.iteration
242
- };
243
- const sampling = config.modelOptions ?? {};
244
- const nestedOptions = sampling["options"] && typeof sampling["options"] === "object" ? sampling["options"] : void 0;
245
- const samplingTemperature = firstNumber(
246
- sampling["temperature"],
247
- nestedOptions?.["temperature"]
248
- );
249
- const samplingTopP = firstNumber(
250
- sampling["top_p"],
251
- sampling["topP"],
252
- nestedOptions?.["top_p"]
253
- );
254
- const samplingMaxTokens = firstNumber(
255
- ...MAX_TOKENS_KEYS.map((k) => sampling[k]),
256
- nestedOptions?.[NESTED_MAX_TOKENS_KEY]
257
- );
258
- if (samplingTemperature !== void 0)
259
- baseAttrs["gen_ai.request.temperature"] = samplingTemperature;
260
- if (samplingTopP !== void 0)
261
- baseAttrs["gen_ai.request.top_p"] = samplingTopP;
262
- if (samplingMaxTokens !== void 0)
263
- baseAttrs["gen_ai.request.max_tokens"] = samplingMaxTokens;
264
- const baseOptions = {
265
- kind: SpanKind.CLIENT,
266
- attributes: baseAttrs
267
- };
268
- const spanOptions = safeCall(
269
- "otel.onBeforeSpanStart",
270
- () => onBeforeSpanStart?.(info, baseOptions)
271
- ) ?? baseOptions;
272
- const parentCtx = trace.setSpan(
273
- context.active(),
274
- state.rootSpan
275
- );
276
- let iterSpan;
277
- context.with(parentCtx, () => {
278
- iterSpan = tracer.startSpan(name, spanOptions, parentCtx);
279
- });
280
- const enriched = safeCall(
281
- "otel.attributeEnricher",
282
- () => attributeEnricher?.(info)
283
- );
284
- if (enriched) iterSpan.setAttributes(enriched);
285
- state.currentIterationSpan = iterSpan;
286
- state.assistantTextBuffer = "";
287
- state.assistantTextBufferTruncated = false;
288
- if (captureContent) {
289
- const systemPromptContents = config.systemPrompts.map(
290
- (p) => typeof p === "string" ? p : p.content
291
- );
292
- const systemPromptMetadata = config.systemPrompts.map(
293
- (p) => typeof p === "string" || p.metadata === void 0 ? null : p.metadata
294
- );
295
- if (systemPromptMetadata.some((m) => m !== null)) {
296
- iterSpan.setAttribute(
297
- "tanstack.ai.system_prompt.metadata",
298
- JSON.stringify(systemPromptMetadata)
299
- );
300
- }
301
- for (const sys of systemPromptContents) {
302
- iterSpan.addEvent("gen_ai.system.message", {
303
- content: redactContent(sys)
304
- });
305
- }
306
- for (const m of config.messages) {
307
- const body = serializeContent(m.content);
308
- if (body.length === 0) continue;
309
- iterSpan.addEvent(messageEventName(m.role), {
310
- content: redactContent(body)
311
- });
312
- }
313
- const inputMessages = [];
314
- for (const sys of systemPromptContents) {
315
- inputMessages.push({
316
- role: "system",
317
- content: redactContent(sys)
318
- });
319
- }
320
- for (const m of config.messages) {
321
- const body = serializeContent(m.content);
322
- if (body.length === 0) continue;
323
- inputMessages.push({
324
- role: m.role,
325
- content: redactContent(body)
326
- });
327
- }
328
- if (inputMessages.length > 0) {
329
- const inputJson = JSON.stringify(inputMessages);
330
- iterSpan.setAttribute("gen_ai.input.messages", inputJson);
331
- iterSpan.setAttribute("langfuse.observation.input", inputJson);
332
- if (state.iterationCount === 0) {
333
- state.rootSpan.setAttribute(
334
- "langfuse.observation.input",
335
- inputJson
336
- );
337
- state.rootSpan.setAttribute("langfuse.trace.input", inputJson);
338
- }
339
- }
340
- }
341
- state.iterationCount += 1;
342
- });
343
- return void 0;
344
- },
345
- onChunk(ctx, chunk) {
346
- safeCall("otel.onChunk", () => {
347
- const state = stateByCtx.get(ctx);
348
- if (!state) return;
349
- if (captureContent && chunk.type === "TEXT_MESSAGE_CONTENT") {
350
- appendAssistantText(state, chunk.delta);
351
- }
352
- if (chunk.type !== "RUN_FINISHED") return;
353
- if (chunk.finishReason) state.lastFinishReason = chunk.finishReason;
354
- const span = state.currentIterationSpan;
355
- if (!span) return;
356
- if (chunk.finishReason) {
357
- span.setAttribute("gen_ai.response.finish_reasons", [
358
- chunk.finishReason
359
- ]);
360
- }
361
- if (chunk.model) span.setAttribute("gen_ai.response.model", chunk.model);
362
- if (chunk.usage) {
363
- span.setAttributes(usageAttributes(chunk.usage));
364
- }
365
- if (captureContent && state.assistantTextBuffer.length > 0) {
366
- const completion = redactContent(state.assistantTextBuffer);
367
- const outputJson = JSON.stringify([
368
- { role: "assistant", content: completion }
369
- ]);
370
- span.addEvent("gen_ai.choice", { content: completion });
371
- span.setAttribute("gen_ai.output.messages", outputJson);
372
- span.setAttribute("langfuse.observation.output", outputJson);
373
- state.rootSpan.setAttribute("langfuse.observation.output", outputJson);
374
- state.rootSpan.setAttribute("langfuse.trace.output", outputJson);
375
- state.assistantTextBuffer = "";
376
- state.assistantTextBufferTruncated = false;
377
- }
378
- });
379
- return void 0;
380
- },
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;
390
- safeCall("otel.onUsage", () => {
391
- const state = stateByCtx.get(chatCtx);
392
- if (!state) return;
393
- if (tokenHistogram) {
394
- const metricAttrs = {
395
- "gen_ai.system": chatCtx.provider,
396
- "gen_ai.operation.name": "chat",
397
- "gen_ai.request.model": chatCtx.model
398
- };
399
- tokenHistogram.record(usage.promptTokens, {
400
- ...metricAttrs,
401
- "gen_ai.token.type": "input"
402
- });
403
- tokenHistogram.record(usage.completionTokens, {
404
- ...metricAttrs,
405
- "gen_ai.token.type": "output"
406
- });
407
- }
408
- const span = state.currentIterationSpan ?? state.rootSpan;
409
- span.setAttributes(usageAttributes(usage));
410
- });
411
- },
412
- onBeforeToolCall(ctx, hookCtx) {
413
- safeCall("otel.onBeforeToolCall", () => {
414
- const state = stateByCtx.get(ctx);
415
- if (!state) return;
416
- const parent = state.currentIterationSpan ?? state.rootSpan;
417
- const info = {
418
- kind: "tool",
419
- ctx,
420
- toolName: hookCtx.toolName,
421
- toolCallId: hookCtx.toolCallId,
422
- iteration: state.iterationCount - 1
423
- };
424
- const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `execute_tool ${hookCtx.toolName}`;
425
- const baseAttrs = {
426
- "gen_ai.tool.name": hookCtx.toolName,
427
- "gen_ai.tool.call.id": hookCtx.toolCallId,
428
- "gen_ai.tool.type": "function"
429
- };
430
- const baseOptions = {
431
- kind: SpanKind.INTERNAL,
432
- attributes: baseAttrs
433
- };
434
- const spanOptions = safeCall(
435
- "otel.onBeforeSpanStart",
436
- () => onBeforeSpanStart?.(info, baseOptions)
437
- ) ?? baseOptions;
438
- const parentCtx = trace.setSpan(context.active(), parent);
439
- let toolSpan;
440
- context.with(parentCtx, () => {
441
- toolSpan = tracer.startSpan(name, spanOptions, parentCtx);
442
- });
443
- const enriched = safeCall(
444
- "otel.attributeEnricher",
445
- () => attributeEnricher?.(info)
446
- );
447
- if (enriched) toolSpan.setAttributes(enriched);
448
- if (captureContent) {
449
- const argsBody = typeof hookCtx.args === "string" ? hookCtx.args : safeCall(
450
- "otel.serializeToolArgs",
451
- () => JSON.stringify(hookCtx.args ?? null)
452
- ) ?? "[unserializable_tool_args]";
453
- const redactedArgs = redactContent(argsBody);
454
- const toolInputJson = JSON.stringify([
455
- { role: "tool", content: redactedArgs }
456
- ]);
457
- toolSpan.setAttribute("gen_ai.input.messages", toolInputJson);
458
- toolSpan.setAttribute("langfuse.observation.input", toolInputJson);
459
- }
460
- state.toolSpans.set(hookCtx.toolCallId, {
461
- span: toolSpan,
462
- toolName: hookCtx.toolName
463
- });
464
- });
465
- return void 0;
466
- },
467
- onAfterToolCall(ctx, info) {
468
- safeCall("otel.onAfterToolCall", () => {
469
- const state = stateByCtx.get(ctx);
470
- if (!state) return;
471
- const entry = state.toolSpans.get(info.toolCallId);
472
- if (!entry) return;
473
- const { span: toolSpan } = entry;
474
- const outcome = info.ok ? "success" : "error";
475
- toolSpan.setAttribute("tanstack.ai.tool.outcome", outcome);
476
- if (!info.ok && info.error !== void 0) {
477
- toolSpan.recordException(info.error);
478
- const msg = errorMessage(info.error);
479
- toolSpan.setStatus({
480
- code: SpanStatusCode.ERROR,
481
- ...msg !== void 0 && { message: msg }
482
- });
483
- }
484
- if (captureContent) {
485
- const body = typeof info.result === "string" ? info.result : safeCall(
486
- "otel.serializeToolResult",
487
- () => JSON.stringify(info.result ?? null)
488
- ) ?? "[unserializable_tool_result]";
489
- const redactedBody = redactContent(body);
490
- if (state.currentIterationSpan) {
491
- state.currentIterationSpan.addEvent("gen_ai.tool.message", {
492
- content: redactedBody,
493
- tool_call_id: info.toolCallId
494
- });
495
- }
496
- const toolOutputJson = JSON.stringify([
497
- { role: "tool", content: redactedBody }
498
- ]);
499
- toolSpan.setAttribute("gen_ai.output.messages", toolOutputJson);
500
- toolSpan.setAttribute("langfuse.observation.output", toolOutputJson);
501
- }
502
- safeCall(
503
- "otel.onSpanEnd",
504
- () => onSpanEnd?.(
505
- {
506
- kind: "tool",
507
- ctx,
508
- toolName: info.toolName,
509
- toolCallId: info.toolCallId,
510
- iteration: state.iterationCount - 1
511
- },
512
- toolSpan
513
- )
514
- );
515
- toolSpan.end();
516
- state.toolSpans.delete(info.toolCallId);
517
- });
518
- },
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;
535
- safeCall("otel.onError", () => {
536
- const state = stateByCtx.get(chatCtx);
537
- if (!state) return;
538
- const errType = errorTypeName(info.error);
539
- const message = errorMessage(info.error);
540
- const statusMessage = message !== void 0 ? { message } : {};
541
- const exception = info.error;
542
- const iterationSpan = state.currentIterationSpan;
543
- if (iterationSpan) {
544
- iterationSpan.recordException(exception);
545
- iterationSpan.setStatus({
546
- code: SpanStatusCode.ERROR,
547
- ...statusMessage
548
- });
549
- safeCall(
550
- "otel.onSpanEnd",
551
- () => onSpanEnd?.(
552
- {
553
- kind: "iteration",
554
- ctx: chatCtx,
555
- iteration: state.iterationCount - 1
556
- },
557
- iterationSpan
558
- )
559
- );
560
- iterationSpan.end();
561
- state.currentIterationSpan = null;
562
- }
563
- for (const [id, entry] of state.toolSpans) {
564
- const { span, toolName } = entry;
565
- span.recordException(exception);
566
- span.setStatus({ code: SpanStatusCode.ERROR, ...statusMessage });
567
- safeCall(
568
- "otel.onSpanEnd",
569
- () => onSpanEnd?.(
570
- {
571
- kind: "tool",
572
- ctx: chatCtx,
573
- toolCallId: id,
574
- toolName,
575
- iteration: state.iterationCount - 1
576
- },
577
- span
578
- )
579
- );
580
- span.end();
581
- state.toolSpans.delete(id);
582
- }
583
- state.rootSpan.recordException(exception);
584
- state.rootSpan.setStatus({
585
- code: SpanStatusCode.ERROR,
586
- ...statusMessage
587
- });
588
- if (durationHistogram) {
589
- durationHistogram.record(info.duration / 1e3, {
590
- "gen_ai.system": chatCtx.provider,
591
- "gen_ai.operation.name": "chat",
592
- "gen_ai.request.model": chatCtx.model,
593
- "error.type": errType
594
- });
595
- }
596
- safeCall(
597
- "otel.onSpanEnd",
598
- () => onSpanEnd?.({ kind: "chat", ctx: chatCtx }, state.rootSpan)
599
- );
600
- state.rootSpan.end();
601
- stateByCtx.delete(chatCtx);
602
- });
603
- },
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;
619
- safeCall("otel.onAbort", () => {
620
- const state = stateByCtx.get(chatCtx);
621
- if (!state) return;
622
- const closeCancelled = (span) => {
623
- span.setAttribute("tanstack.ai.completion.reason", "cancelled");
624
- span.setStatus({ code: SpanStatusCode.ERROR, message: "cancelled" });
625
- };
626
- const iterationSpan = state.currentIterationSpan;
627
- if (iterationSpan) {
628
- closeCancelled(iterationSpan);
629
- safeCall(
630
- "otel.onSpanEnd",
631
- () => onSpanEnd?.(
632
- {
633
- kind: "iteration",
634
- ctx: chatCtx,
635
- iteration: state.iterationCount - 1
636
- },
637
- iterationSpan
638
- )
639
- );
640
- iterationSpan.end();
641
- state.currentIterationSpan = null;
642
- }
643
- for (const [id, entry] of state.toolSpans) {
644
- const { span, toolName } = entry;
645
- closeCancelled(span);
646
- safeCall(
647
- "otel.onSpanEnd",
648
- () => onSpanEnd?.(
649
- {
650
- kind: "tool",
651
- ctx: chatCtx,
652
- toolCallId: id,
653
- toolName,
654
- iteration: state.iterationCount - 1
655
- },
656
- span
657
- )
658
- );
659
- span.end();
660
- state.toolSpans.delete(id);
661
- }
662
- closeCancelled(state.rootSpan);
663
- if (durationHistogram) {
664
- durationHistogram.record(info.duration / 1e3, {
665
- "gen_ai.system": chatCtx.provider,
666
- "gen_ai.operation.name": "chat",
667
- "gen_ai.request.model": chatCtx.model,
668
- "error.type": "cancelled"
669
- });
670
- }
671
- safeCall(
672
- "otel.onSpanEnd",
673
- () => onSpanEnd?.({ kind: "chat", ctx: chatCtx }, state.rootSpan)
674
- );
675
- state.rootSpan.end();
676
- stateByCtx.delete(chatCtx);
677
- });
678
- },
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;
690
- safeCall("otel.onFinish", () => {
691
- const state = stateByCtx.get(chatCtx);
692
- if (!state) return;
693
- for (const [id, entry] of state.toolSpans) {
694
- const { span, toolName } = entry;
695
- span.setAttribute("tanstack.ai.tool.outcome", "unknown");
696
- safeCall(
697
- "otel.onSpanEnd",
698
- () => onSpanEnd?.(
699
- {
700
- kind: "tool",
701
- ctx: chatCtx,
702
- toolCallId: id,
703
- toolName,
704
- iteration: state.iterationCount - 1
705
- },
706
- span
707
- )
708
- );
709
- span.end();
710
- state.toolSpans.delete(id);
711
- }
712
- closeIterationSpan(state, chatCtx);
713
- if (durationHistogram) {
714
- durationHistogram.record(info.duration / 1e3, {
715
- "gen_ai.system": chatCtx.provider,
716
- "gen_ai.operation.name": "chat",
717
- "gen_ai.request.model": chatCtx.model
718
- });
719
- }
720
- if (info.usage) {
721
- state.rootSpan.setAttributes(usageAttributes(info.usage));
722
- }
723
- if (state.lastFinishReason) {
724
- state.rootSpan.setAttribute("gen_ai.response.finish_reasons", [
725
- state.lastFinishReason
726
- ]);
727
- }
728
- state.rootSpan.setAttribute(
729
- "tanstack.ai.iterations",
730
- state.iterationCount
731
- );
732
- safeCall(
733
- "otel.onSpanEnd",
734
- () => onSpanEnd?.({ kind: "chat", ctx: chatCtx }, state.rootSpan)
735
- );
736
- state.rootSpan.end();
737
- stateByCtx.delete(chatCtx);
738
- });
739
- }
740
- };
741
- }
742
- export {
743
- otelMiddleware
744
- };
745
- //# sourceMappingURL=otel.js.map