@tanstack/ai 0.42.0 → 0.43.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.
Files changed (273) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/activities/chat/adapter.js +23 -16
  3. package/dist/esm/activities/chat/adapter.js.map +1 -1
  4. package/dist/esm/activities/chat/agent-loop-strategies.d.ts +5 -36
  5. package/dist/esm/activities/chat/agent-loop-strategies.js +75 -21
  6. package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
  7. package/dist/esm/activities/chat/cancel.d.ts +40 -0
  8. package/dist/esm/activities/chat/cancel.js +54 -0
  9. package/dist/esm/activities/chat/cancel.js.map +1 -0
  10. package/dist/esm/activities/chat/index.d.ts +28 -21
  11. package/dist/esm/activities/chat/index.js +2100 -1813
  12. package/dist/esm/activities/chat/index.js.map +1 -1
  13. package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
  14. package/dist/esm/activities/chat/mcp/manager.js +90 -77
  15. package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
  16. package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
  17. package/dist/esm/activities/chat/messages.js +397 -346
  18. package/dist/esm/activities/chat/messages.js.map +1 -1
  19. package/dist/esm/activities/chat/middleware/builder.js +17 -15
  20. package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
  21. package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
  22. package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
  23. package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
  24. package/dist/esm/activities/chat/middleware/compose.js +623 -531
  25. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  26. package/dist/esm/activities/chat/middleware/define.js +12 -5
  27. package/dist/esm/activities/chat/middleware/define.js.map +1 -1
  28. package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
  29. package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
  30. package/dist/esm/activities/chat/middleware/locks.js +71 -0
  31. package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
  32. package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
  33. package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
  34. package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
  35. package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
  36. package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
  37. package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
  38. package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
  39. package/dist/esm/activities/chat/middleware/run-store.js +176 -0
  40. package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
  41. package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
  42. package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
  43. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
  44. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
  45. package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
  46. package/dist/esm/activities/chat/middleware/validate.js +23 -28
  47. package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
  48. package/dist/esm/activities/chat/stream/json-parser.js +39 -25
  49. package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
  50. package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
  51. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  52. package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
  53. package/dist/esm/activities/chat/stream/processor.js +1341 -1542
  54. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  55. package/dist/esm/activities/chat/stream/strategies.js +69 -53
  56. package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
  57. package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
  58. package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
  59. package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
  60. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
  61. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
  62. package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
  63. package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
  64. package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
  65. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
  66. package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
  67. package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
  68. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  69. package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
  70. package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
  71. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  72. package/dist/esm/activities/error-payload.js +85 -47
  73. package/dist/esm/activities/error-payload.js.map +1 -1
  74. package/dist/esm/activities/generateAudio/adapter.js +22 -15
  75. package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
  76. package/dist/esm/activities/generateAudio/index.d.ts +4 -0
  77. package/dist/esm/activities/generateAudio/index.js +141 -105
  78. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  79. package/dist/esm/activities/generateImage/adapter.js +22 -15
  80. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  81. package/dist/esm/activities/generateImage/index.d.ts +4 -0
  82. package/dist/esm/activities/generateImage/index.js +155 -111
  83. package/dist/esm/activities/generateImage/index.js.map +1 -1
  84. package/dist/esm/activities/generateSpeech/adapter.js +22 -15
  85. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
  86. package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
  87. package/dist/esm/activities/generateSpeech/index.js +159 -110
  88. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  89. package/dist/esm/activities/generateTranscription/adapter.js +22 -15
  90. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
  91. package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
  92. package/dist/esm/activities/generateTranscription/index.js +159 -100
  93. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  94. package/dist/esm/activities/generateVideo/adapter.js +36 -29
  95. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  96. package/dist/esm/activities/generateVideo/index.d.ts +143 -19
  97. package/dist/esm/activities/generateVideo/index.js +456 -279
  98. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  99. package/dist/esm/activities/generateVideo/snap.js +60 -48
  100. package/dist/esm/activities/generateVideo/snap.js.map +1 -1
  101. package/dist/esm/activities/index.js +8 -34
  102. package/dist/esm/activities/middleware/index.d.ts +1 -1
  103. package/dist/esm/activities/middleware/run.d.ts +10 -0
  104. package/dist/esm/activities/middleware/run.js +53 -29
  105. package/dist/esm/activities/middleware/run.js.map +1 -1
  106. package/dist/esm/activities/middleware/types.d.ts +44 -6
  107. package/dist/esm/activities/stream-generation-result.d.ts +4 -1
  108. package/dist/esm/activities/stream-generation-result.js +79 -44
  109. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  110. package/dist/esm/activities/summarize/adapter.js +22 -15
  111. package/dist/esm/activities/summarize/adapter.js.map +1 -1
  112. package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
  113. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  114. package/dist/esm/activities/summarize/index.d.ts +27 -0
  115. package/dist/esm/activities/summarize/index.js +268 -102
  116. package/dist/esm/activities/summarize/index.js.map +1 -1
  117. package/dist/esm/adapter-internals.d.ts +2 -1
  118. package/dist/esm/adapter-internals.js +4 -11
  119. package/dist/esm/client.d.ts +25 -3
  120. package/dist/esm/client.js +131 -64
  121. package/dist/esm/client.js.map +1 -1
  122. package/dist/esm/custom-events.d.ts +76 -0
  123. package/dist/esm/custom-events.js +37 -0
  124. package/dist/esm/custom-events.js.map +1 -0
  125. package/dist/esm/delivery-detach.d.ts +50 -0
  126. package/dist/esm/delivery-detach.js +71 -0
  127. package/dist/esm/delivery-detach.js.map +1 -0
  128. package/dist/esm/delivery-disconnect.d.ts +62 -0
  129. package/dist/esm/delivery-disconnect.js +81 -0
  130. package/dist/esm/delivery-disconnect.js.map +1 -0
  131. package/dist/esm/extend-adapter.js +19 -17
  132. package/dist/esm/extend-adapter.js.map +1 -1
  133. package/dist/esm/index.d.ts +24 -6
  134. package/dist/esm/index.js +30 -98
  135. package/dist/esm/interrupt-resume.d.ts +71 -0
  136. package/dist/esm/interrupt-resume.js +438 -0
  137. package/dist/esm/interrupt-resume.js.map +1 -0
  138. package/dist/esm/interrupt-serialization.d.ts +12 -0
  139. package/dist/esm/interrupt-serialization.js +178 -0
  140. package/dist/esm/interrupt-serialization.js.map +1 -0
  141. package/dist/esm/interrupts.d.ts +84 -0
  142. package/dist/esm/interrupts.js +31 -0
  143. package/dist/esm/interrupts.js.map +1 -0
  144. package/dist/esm/locks.d.ts +10 -0
  145. package/dist/esm/locks.js +2 -0
  146. package/dist/esm/logger/console-logger.js +101 -78
  147. package/dist/esm/logger/console-logger.js.map +1 -1
  148. package/dist/esm/logger/internal-logger.js +104 -89
  149. package/dist/esm/logger/internal-logger.js.map +1 -1
  150. package/dist/esm/logger/resolve.js +54 -49
  151. package/dist/esm/logger/resolve.js.map +1 -1
  152. package/dist/esm/logger/types.d.ts +1 -1
  153. package/dist/esm/middlewares/content-guard.js +142 -148
  154. package/dist/esm/middlewares/content-guard.js.map +1 -1
  155. package/dist/esm/middlewares/index.js +2 -6
  156. package/dist/esm/middlewares/otel.d.ts +3 -1
  157. package/dist/esm/middlewares/otel.js +599 -732
  158. package/dist/esm/middlewares/otel.js.map +1 -1
  159. package/dist/esm/middlewares/usage-attributes.js +47 -40
  160. package/dist/esm/middlewares/usage-attributes.js.map +1 -1
  161. package/dist/esm/realtime/event-emitter.js +24 -25
  162. package/dist/esm/realtime/event-emitter.js.map +1 -1
  163. package/dist/esm/realtime/index.d.ts +5 -9
  164. package/dist/esm/realtime/index.js +29 -6
  165. package/dist/esm/realtime/index.js.map +1 -1
  166. package/dist/esm/scope.d.ts +47 -0
  167. package/dist/esm/stream-durability.d.ts +171 -0
  168. package/dist/esm/stream-durability.js +295 -0
  169. package/dist/esm/stream-durability.js.map +1 -0
  170. package/dist/esm/stream-to-response.d.ts +178 -13
  171. package/dist/esm/stream-to-response.js +663 -115
  172. package/dist/esm/stream-to-response.js.map +1 -1
  173. package/dist/esm/strip-to-spec-middleware.js +30 -16
  174. package/dist/esm/strip-to-spec-middleware.js.map +1 -1
  175. package/dist/esm/system-prompts.js +27 -21
  176. package/dist/esm/system-prompts.js.map +1 -1
  177. package/dist/esm/tool-registry.js +72 -45
  178. package/dist/esm/tool-registry.js.map +1 -1
  179. package/dist/esm/tools/provider-tool.js +14 -5
  180. package/dist/esm/tools/provider-tool.js.map +1 -1
  181. package/dist/esm/types.d.ts +321 -42
  182. package/dist/esm/types.js +2 -0
  183. package/dist/esm/utilities/ag-ui-wire.js +79 -93
  184. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  185. package/dist/esm/utilities/chat-params.d.ts +26 -4
  186. package/dist/esm/utilities/chat-params.js +218 -92
  187. package/dist/esm/utilities/chat-params.js.map +1 -1
  188. package/dist/esm/utilities/errors.js +28 -18
  189. package/dist/esm/utilities/errors.js.map +1 -1
  190. package/dist/esm/utilities/media-prompt.js +46 -41
  191. package/dist/esm/utilities/media-prompt.js.map +1 -1
  192. package/dist/esm/utilities/numbers.js +13 -10
  193. package/dist/esm/utilities/numbers.js.map +1 -1
  194. package/dist/esm/utilities/provider-executed.js +20 -11
  195. package/dist/esm/utilities/provider-executed.js.map +1 -1
  196. package/dist/esm/utilities/sampling-keys.js +31 -19
  197. package/dist/esm/utilities/sampling-keys.js.map +1 -1
  198. package/dist/esm/utilities/tool-result.js +42 -30
  199. package/dist/esm/utilities/tool-result.js.map +1 -1
  200. package/dist/esm/utilities/usage.js +27 -9
  201. package/dist/esm/utilities/usage.js.map +1 -1
  202. package/dist/esm/utils.js +26 -18
  203. package/dist/esm/utils.js.map +1 -1
  204. package/package.json +10 -6
  205. package/skills/ai-core/SKILL.md +69 -18
  206. package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
  207. package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
  208. package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
  209. package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
  210. package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
  211. package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
  212. package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
  213. package/skills/ai-core/chat-experience/SKILL.md +98 -11
  214. package/skills/ai-core/client-persistence/SKILL.md +277 -0
  215. package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
  216. package/skills/ai-core/debug-logging/SKILL.md +1 -1
  217. package/skills/ai-core/locks/SKILL.md +143 -0
  218. package/skills/ai-core/media-generation/SKILL.md +144 -12
  219. package/skills/ai-core/middleware/SKILL.md +258 -33
  220. package/skills/ai-core/structured-outputs/SKILL.md +1 -1
  221. package/skills/ai-core/tool-calling/SKILL.md +54 -61
  222. package/src/activities/chat/agent-loop-strategies.ts +5 -39
  223. package/src/activities/chat/cancel.ts +81 -0
  224. package/src/activities/chat/index.ts +1091 -200
  225. package/src/activities/chat/mcp/manager.ts +4 -4
  226. package/src/activities/chat/mcp/types.ts +2 -2
  227. package/src/activities/chat/messages.ts +5 -3
  228. package/src/activities/chat/middleware/builder.ts +1 -1
  229. package/src/activities/chat/middleware/compose.ts +186 -9
  230. package/src/activities/chat/middleware/index.ts +26 -0
  231. package/src/activities/chat/middleware/locks.ts +102 -0
  232. package/src/activities/chat/middleware/pending-turn.ts +47 -0
  233. package/src/activities/chat/middleware/run-disconnect.ts +62 -0
  234. package/src/activities/chat/middleware/run-store.ts +412 -0
  235. package/src/activities/chat/middleware/types.ts +62 -1
  236. package/src/activities/chat/stream/processor.ts +189 -5
  237. package/src/activities/chat/tools/approval-schema.ts +205 -0
  238. package/src/activities/chat/tools/tool-calls.ts +106 -13
  239. package/src/activities/chat/tools/tool-definition.ts +210 -39
  240. package/src/activities/generateAudio/index.ts +20 -3
  241. package/src/activities/generateImage/index.ts +20 -3
  242. package/src/activities/generateSpeech/index.ts +25 -3
  243. package/src/activities/generateTranscription/index.ts +26 -3
  244. package/src/activities/generateVideo/index.ts +345 -82
  245. package/src/activities/middleware/index.ts +2 -0
  246. package/src/activities/middleware/run.ts +31 -0
  247. package/src/activities/middleware/types.ts +49 -5
  248. package/src/activities/stream-generation-result.ts +30 -2
  249. package/src/activities/summarize/chat-stream-summarize.ts +5 -0
  250. package/src/activities/summarize/index.ts +200 -10
  251. package/src/adapter-internals.ts +10 -1
  252. package/src/client.ts +244 -0
  253. package/src/custom-events.ts +107 -0
  254. package/src/delivery-detach.ts +72 -0
  255. package/src/delivery-disconnect.ts +84 -0
  256. package/src/index.ts +138 -1
  257. package/src/interrupt-resume.ts +824 -0
  258. package/src/interrupt-serialization.ts +183 -0
  259. package/src/interrupts.ts +146 -0
  260. package/src/locks.ts +17 -0
  261. package/src/logger/types.ts +1 -1
  262. package/src/middlewares/otel.ts +23 -5
  263. package/src/realtime/index.ts +5 -9
  264. package/src/scope.ts +47 -0
  265. package/src/stream-durability.ts +598 -0
  266. package/src/stream-to-response.ts +1051 -95
  267. package/src/strip-to-spec-middleware.ts +3 -3
  268. package/src/types.ts +405 -45
  269. package/src/utilities/chat-params.ts +245 -55
  270. package/dist/esm/activities/index.js.map +0 -1
  271. package/dist/esm/adapter-internals.js.map +0 -1
  272. package/dist/esm/index.js.map +0 -1
  273. package/dist/esm/middlewares/index.js.map +0 -1
@@ -1,745 +1,612 @@
1
- import { SpanKind, SpanStatusCode, trace, context } from "@opentelemetry/api";
2
1
  import { MAX_TOKENS_KEYS, NESTED_MAX_TOKENS_KEY } from "../utilities/sampling-keys.js";
3
2
  import { firstNumber } from "../utilities/numbers.js";
4
3
  import { errorMessage, errorTypeName } from "../utilities/errors.js";
5
4
  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"
5
+ import { SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
6
+ //#region src/middlewares/otel.ts
7
+ /**
8
+ * `gen_ai.operation.name` per activity. Chat uses the GenAI semconv value;
9
+ * media operations have no semconv entry yet, so these are the de-facto names
10
+ * consumed by GenAI backends (PostHog, Langfuse, …). Documented in
11
+ * `docs/advanced/otel.md`.
12
+ */
13
+ var OPERATION_NAME = {
14
+ chat: "chat",
15
+ image: "image_generation",
16
+ video: "video_generation",
17
+ audio: "audio_generation",
18
+ tts: "text_to_speech",
19
+ transcription: "transcription",
20
+ summarize: "summarize"
13
21
  };
14
- const stateByCtx = /* @__PURE__ */ new WeakMap();
15
- const DEFAULT_MAX_CONTENT_LENGTH = 1e5;
16
- const REDACTION_FAILED_SENTINEL = "[redaction_failed]";
22
+ var stateByCtx = /* @__PURE__ */ new WeakMap();
23
+ var DEFAULT_MAX_CONTENT_LENGTH = 1e5;
24
+ var REDACTION_FAILED_SENTINEL = "[redaction_failed]";
17
25
  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(" ");
26
+ if (typeof content === "string") return content;
27
+ if (!Array.isArray(content)) return "";
28
+ const parts = [];
29
+ for (const part of content) {
30
+ if (!part || typeof part !== "object") continue;
31
+ const type = part.type;
32
+ switch (type) {
33
+ case "text":
34
+ parts.push((part.text ?? part.content ?? "").toString());
35
+ break;
36
+ case "image":
37
+ parts.push("[image]");
38
+ break;
39
+ case "audio":
40
+ parts.push("[audio]");
41
+ break;
42
+ case "video":
43
+ parts.push("[video]");
44
+ break;
45
+ case "document":
46
+ parts.push("[document]");
47
+ break;
48
+ case void 0:
49
+ parts.push("[unknown]");
50
+ break;
51
+ default: parts.push(`[${type}]`);
52
+ }
53
+ }
54
+ return parts.join(" ");
50
55
  }
51
56
  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
- }
57
+ switch (role) {
58
+ case "user": return "gen_ai.user.message";
59
+ case "assistant": return "gen_ai.assistant.message";
60
+ case "tool": return "gen_ai.tool.message";
61
+ case "system": return "gen_ai.system.message";
62
+ default: return `gen_ai.${role}.message`;
63
+ }
64
64
  }
65
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
- }
66
+ try {
67
+ return fn();
68
+ } catch (err) {
69
+ console.warn(`[otelMiddleware] ${label} failed`, err);
70
+ return;
71
+ }
72
72
  }
73
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
- };
74
+ const { tracer, meter, captureContent = false, redact = (s) => s, maxContentLength = DEFAULT_MAX_CONTENT_LENGTH, spanNameFormatter, attributeEnricher, onBeforeSpanStart, onSpanEnd } = options;
75
+ const durationHistogram = meter?.createHistogram("gen_ai.client.operation.duration", {
76
+ description: "GenAI client operation duration",
77
+ unit: "s"
78
+ });
79
+ const tokenHistogram = meter?.createHistogram("gen_ai.client.token.usage", {
80
+ description: "GenAI client token usage",
81
+ unit: "{token}"
82
+ });
83
+ const redactContent = (text) => {
84
+ try {
85
+ return redact(text);
86
+ } catch (err) {
87
+ console.warn("[otelMiddleware] otel.redact failed", err);
88
+ return REDACTION_FAILED_SENTINEL;
89
+ }
90
+ };
91
+ const appendAssistantText = (state, delta) => {
92
+ if (maxContentLength > 0) {
93
+ if (state.assistantTextBufferTruncated) return;
94
+ const remaining = maxContentLength - state.assistantTextBuffer.length;
95
+ if (remaining <= 0) {
96
+ state.assistantTextBufferTruncated = true;
97
+ state.assistantTextBuffer += "…";
98
+ return;
99
+ }
100
+ if (delta.length > remaining) {
101
+ state.assistantTextBuffer += delta.slice(0, remaining) + "…";
102
+ state.assistantTextBufferTruncated = true;
103
+ return;
104
+ }
105
+ }
106
+ state.assistantTextBuffer += delta;
107
+ };
108
+ const closeIterationSpan = (state, ctx) => {
109
+ if (!state.currentIterationSpan) return;
110
+ const span = state.currentIterationSpan;
111
+ const iteration = state.iterationCount - 1;
112
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
113
+ kind: "iteration",
114
+ ctx,
115
+ iteration
116
+ }, span));
117
+ span.end();
118
+ state.currentIterationSpan = null;
119
+ };
120
+ const mediaSpans = /* @__PURE__ */ new WeakMap();
121
+ const recordMediaDuration = (ctx, durationMs, errorType) => {
122
+ if (!durationHistogram) return;
123
+ durationHistogram.record(durationMs / 1e3, {
124
+ "gen_ai.system": ctx.provider,
125
+ "gen_ai.operation.name": OPERATION_NAME[ctx.activity],
126
+ "gen_ai.request.model": ctx.model,
127
+ ...errorType ? { "error.type": errorType } : {}
128
+ });
129
+ };
130
+ const startMediaSpan = (ctx) => {
131
+ safeCall("otel.onStart", () => {
132
+ const operationName = OPERATION_NAME[ctx.activity];
133
+ const info = {
134
+ kind: "generation",
135
+ ctx
136
+ };
137
+ const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `${operationName} ${ctx.model}`;
138
+ const baseOptions = {
139
+ kind: SpanKind.CLIENT,
140
+ attributes: {
141
+ "gen_ai.system": ctx.provider,
142
+ "gen_ai.operation.name": operationName,
143
+ "gen_ai.request.model": ctx.model
144
+ }
145
+ };
146
+ const spanOptions = safeCall("otel.onBeforeSpanStart", () => onBeforeSpanStart?.(info, baseOptions)) ?? baseOptions;
147
+ const span = tracer.startSpan(name, spanOptions);
148
+ const enriched = safeCall("otel.attributeEnricher", () => attributeEnricher?.(info));
149
+ if (enriched) span.setAttributes(enriched);
150
+ mediaSpans.set(ctx, span);
151
+ });
152
+ };
153
+ const endMediaSpan = (ctx, finalize) => {
154
+ const span = mediaSpans.get(ctx);
155
+ mediaSpans.delete(ctx);
156
+ if (!span) return;
157
+ finalize(span);
158
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
159
+ kind: "generation",
160
+ ctx
161
+ }, span));
162
+ span.end();
163
+ };
164
+ return {
165
+ name: "otel",
166
+ onStart(ctx) {
167
+ if (ctx.activity !== "chat") {
168
+ startMediaSpan(ctx);
169
+ return;
170
+ }
171
+ const chatCtx = ctx;
172
+ safeCall("otel.onStart", () => {
173
+ const info = {
174
+ kind: "chat",
175
+ ctx: chatCtx
176
+ };
177
+ const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `chat ${chatCtx.model}`;
178
+ const baseOptions = {
179
+ kind: SpanKind.INTERNAL,
180
+ attributes: {
181
+ "gen_ai.system": chatCtx.provider,
182
+ "gen_ai.request.model": chatCtx.model
183
+ }
184
+ };
185
+ const spanOptions = safeCall("otel.onBeforeSpanStart", () => onBeforeSpanStart?.(info, baseOptions)) ?? baseOptions;
186
+ const rootSpan = tracer.startSpan(name, spanOptions);
187
+ const enriched = safeCall("otel.attributeEnricher", () => attributeEnricher?.(info));
188
+ if (enriched) rootSpan.setAttributes(enriched);
189
+ stateByCtx.set(chatCtx, {
190
+ rootSpan,
191
+ currentIterationSpan: null,
192
+ toolSpans: /* @__PURE__ */ new Map(),
193
+ iterationCount: 0,
194
+ assistantTextBuffer: "",
195
+ assistantTextBufferTruncated: false,
196
+ startTime: Date.now(),
197
+ lastFinishReason: null
198
+ });
199
+ });
200
+ },
201
+ onConfig(ctx, config) {
202
+ if (ctx.phase !== "beforeModel" && ctx.phase !== "structuredOutput") return;
203
+ safeCall("otel.onConfig", () => {
204
+ const state = stateByCtx.get(ctx);
205
+ if (!state) return;
206
+ closeIterationSpan(state, ctx);
207
+ const iteration = state.iterationCount;
208
+ const info = {
209
+ kind: "iteration",
210
+ ctx,
211
+ iteration
212
+ };
213
+ const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `chat ${ctx.model} #${iteration}`;
214
+ const baseAttrs = {
215
+ "gen_ai.system": ctx.provider,
216
+ "gen_ai.operation.name": "chat",
217
+ "gen_ai.request.model": ctx.model,
218
+ "tanstack.ai.iteration": iteration
219
+ };
220
+ const sampling = config.modelOptions ?? {};
221
+ const nestedOptions = sampling["options"] && typeof sampling["options"] === "object" ? sampling["options"] : void 0;
222
+ const samplingTemperature = firstNumber(sampling["temperature"], nestedOptions?.["temperature"]);
223
+ const samplingTopP = firstNumber(sampling["top_p"], sampling["topP"], nestedOptions?.["top_p"]);
224
+ const samplingMaxTokens = firstNumber(...MAX_TOKENS_KEYS.map((k) => sampling[k]), nestedOptions?.[NESTED_MAX_TOKENS_KEY]);
225
+ if (samplingTemperature !== void 0) baseAttrs["gen_ai.request.temperature"] = samplingTemperature;
226
+ if (samplingTopP !== void 0) baseAttrs["gen_ai.request.top_p"] = samplingTopP;
227
+ if (samplingMaxTokens !== void 0) baseAttrs["gen_ai.request.max_tokens"] = samplingMaxTokens;
228
+ const baseOptions = {
229
+ kind: SpanKind.CLIENT,
230
+ attributes: baseAttrs
231
+ };
232
+ const spanOptions = safeCall("otel.onBeforeSpanStart", () => onBeforeSpanStart?.(info, baseOptions)) ?? baseOptions;
233
+ const parentCtx = trace.setSpan(context.active(), state.rootSpan);
234
+ let iterSpan;
235
+ context.with(parentCtx, () => {
236
+ iterSpan = tracer.startSpan(name, spanOptions, parentCtx);
237
+ });
238
+ const enriched = safeCall("otel.attributeEnricher", () => attributeEnricher?.(info));
239
+ if (enriched) iterSpan.setAttributes(enriched);
240
+ state.currentIterationSpan = iterSpan;
241
+ state.assistantTextBuffer = "";
242
+ state.assistantTextBufferTruncated = false;
243
+ if (captureContent) {
244
+ const systemPromptContents = config.systemPrompts.map((p) => typeof p === "string" ? p : p.content);
245
+ const systemPromptMetadata = config.systemPrompts.map((p) => typeof p === "string" || p.metadata === void 0 ? null : p.metadata);
246
+ if (systemPromptMetadata.some((m) => m !== null)) iterSpan.setAttribute("tanstack.ai.system_prompt.metadata", JSON.stringify(systemPromptMetadata));
247
+ for (const sys of systemPromptContents) iterSpan.addEvent("gen_ai.system.message", { content: redactContent(sys) });
248
+ for (const m of config.messages) {
249
+ const body = serializeContent(m.content);
250
+ if (body.length === 0) continue;
251
+ iterSpan.addEvent(messageEventName(m.role), { content: redactContent(body) });
252
+ }
253
+ const inputMessages = [];
254
+ for (const sys of systemPromptContents) inputMessages.push({
255
+ role: "system",
256
+ content: redactContent(sys)
257
+ });
258
+ for (const m of config.messages) {
259
+ const body = serializeContent(m.content);
260
+ if (body.length === 0) continue;
261
+ inputMessages.push({
262
+ role: m.role,
263
+ content: redactContent(body)
264
+ });
265
+ }
266
+ if (inputMessages.length > 0) {
267
+ const inputJson = JSON.stringify(inputMessages);
268
+ iterSpan.setAttribute("gen_ai.input.messages", inputJson);
269
+ iterSpan.setAttribute("langfuse.observation.input", inputJson);
270
+ if (state.iterationCount === 0) {
271
+ state.rootSpan.setAttribute("langfuse.observation.input", inputJson);
272
+ state.rootSpan.setAttribute("langfuse.trace.input", inputJson);
273
+ }
274
+ }
275
+ }
276
+ state.iterationCount += 1;
277
+ });
278
+ },
279
+ onChunk(ctx, chunk) {
280
+ safeCall("otel.onChunk", () => {
281
+ const state = stateByCtx.get(ctx);
282
+ if (!state) return;
283
+ if (captureContent && chunk.type === "TEXT_MESSAGE_CONTENT") appendAssistantText(state, chunk.delta);
284
+ if (chunk.type !== "RUN_FINISHED") return;
285
+ if (chunk.finishReason) state.lastFinishReason = chunk.finishReason;
286
+ const span = state.currentIterationSpan;
287
+ if (!span) return;
288
+ if (chunk.finishReason) span.setAttribute("gen_ai.response.finish_reasons", [chunk.finishReason]);
289
+ if (chunk.model) span.setAttribute("gen_ai.response.model", chunk.model);
290
+ if (chunk.usage) span.setAttributes(usageAttributes(chunk.usage));
291
+ if (captureContent && state.assistantTextBuffer.length > 0) {
292
+ const completion = redactContent(state.assistantTextBuffer);
293
+ const outputJson = JSON.stringify([{
294
+ role: "assistant",
295
+ content: completion
296
+ }]);
297
+ span.addEvent("gen_ai.choice", { content: completion });
298
+ span.setAttribute("gen_ai.output.messages", outputJson);
299
+ span.setAttribute("langfuse.observation.output", outputJson);
300
+ state.rootSpan.setAttribute("langfuse.observation.output", outputJson);
301
+ state.rootSpan.setAttribute("langfuse.trace.output", outputJson);
302
+ state.assistantTextBuffer = "";
303
+ state.assistantTextBufferTruncated = false;
304
+ }
305
+ });
306
+ },
307
+ onUsage(ctx, usage) {
308
+ if (ctx.activity !== "chat") {
309
+ safeCall("otel.onUsage", () => {
310
+ const span = mediaSpans.get(ctx);
311
+ if (span) span.setAttributes(usageAttributes(usage));
312
+ });
313
+ return;
314
+ }
315
+ const chatCtx = ctx;
316
+ safeCall("otel.onUsage", () => {
317
+ const state = stateByCtx.get(chatCtx);
318
+ if (!state) return;
319
+ if (tokenHistogram) {
320
+ const metricAttrs = {
321
+ "gen_ai.system": chatCtx.provider,
322
+ "gen_ai.operation.name": "chat",
323
+ "gen_ai.request.model": chatCtx.model
324
+ };
325
+ tokenHistogram.record(usage.promptTokens, {
326
+ ...metricAttrs,
327
+ "gen_ai.token.type": "input"
328
+ });
329
+ tokenHistogram.record(usage.completionTokens, {
330
+ ...metricAttrs,
331
+ "gen_ai.token.type": "output"
332
+ });
333
+ }
334
+ (state.currentIterationSpan ?? state.rootSpan).setAttributes(usageAttributes(usage));
335
+ });
336
+ },
337
+ onBeforeToolCall(ctx, hookCtx) {
338
+ safeCall("otel.onBeforeToolCall", () => {
339
+ const state = stateByCtx.get(ctx);
340
+ if (!state) return;
341
+ const parent = state.currentIterationSpan ?? state.rootSpan;
342
+ const info = {
343
+ kind: "tool",
344
+ ctx,
345
+ toolName: hookCtx.toolName,
346
+ toolCallId: hookCtx.toolCallId,
347
+ iteration: state.iterationCount - 1
348
+ };
349
+ const name = safeCall("otel.spanNameFormatter", () => spanNameFormatter?.(info)) ?? `execute_tool ${hookCtx.toolName}`;
350
+ const baseAttrs = {
351
+ "gen_ai.tool.name": hookCtx.toolName,
352
+ "gen_ai.tool.call.id": hookCtx.toolCallId,
353
+ "gen_ai.tool.type": "function"
354
+ };
355
+ const baseOptions = {
356
+ kind: SpanKind.INTERNAL,
357
+ attributes: baseAttrs
358
+ };
359
+ const spanOptions = safeCall("otel.onBeforeSpanStart", () => onBeforeSpanStart?.(info, baseOptions)) ?? baseOptions;
360
+ const parentCtx = trace.setSpan(context.active(), parent);
361
+ let toolSpan;
362
+ context.with(parentCtx, () => {
363
+ toolSpan = tracer.startSpan(name, spanOptions, parentCtx);
364
+ });
365
+ const enriched = safeCall("otel.attributeEnricher", () => attributeEnricher?.(info));
366
+ if (enriched) toolSpan.setAttributes(enriched);
367
+ if (captureContent) {
368
+ const argsBody = typeof hookCtx.args === "string" ? hookCtx.args : safeCall("otel.serializeToolArgs", () => JSON.stringify(hookCtx.args ?? null)) ?? "[unserializable_tool_args]";
369
+ const redactedArgs = redactContent(argsBody);
370
+ const toolInputJson = JSON.stringify([{
371
+ role: "tool",
372
+ content: redactedArgs
373
+ }]);
374
+ toolSpan.setAttribute("gen_ai.input.messages", toolInputJson);
375
+ toolSpan.setAttribute("langfuse.observation.input", toolInputJson);
376
+ }
377
+ state.toolSpans.set(hookCtx.toolCallId, {
378
+ span: toolSpan,
379
+ toolName: hookCtx.toolName
380
+ });
381
+ });
382
+ },
383
+ onAfterToolCall(ctx, info) {
384
+ safeCall("otel.onAfterToolCall", () => {
385
+ const state = stateByCtx.get(ctx);
386
+ if (!state) return;
387
+ const entry = state.toolSpans.get(info.toolCallId);
388
+ if (!entry) return;
389
+ const { span: toolSpan } = entry;
390
+ const outcome = info.ok ? "success" : "error";
391
+ toolSpan.setAttribute("tanstack.ai.tool.outcome", outcome);
392
+ if (!info.ok && info.error !== void 0) {
393
+ toolSpan.recordException(info.error);
394
+ const msg = errorMessage(info.error);
395
+ toolSpan.setStatus({
396
+ code: SpanStatusCode.ERROR,
397
+ ...msg !== void 0 && { message: msg }
398
+ });
399
+ }
400
+ if (captureContent) {
401
+ const body = typeof info.result === "string" ? info.result : safeCall("otel.serializeToolResult", () => JSON.stringify(info.result ?? null)) ?? "[unserializable_tool_result]";
402
+ const redactedBody = redactContent(body);
403
+ if (state.currentIterationSpan) state.currentIterationSpan.addEvent("gen_ai.tool.message", {
404
+ content: redactedBody,
405
+ tool_call_id: info.toolCallId
406
+ });
407
+ const toolOutputJson = JSON.stringify([{
408
+ role: "tool",
409
+ content: redactedBody
410
+ }]);
411
+ toolSpan.setAttribute("gen_ai.output.messages", toolOutputJson);
412
+ toolSpan.setAttribute("langfuse.observation.output", toolOutputJson);
413
+ }
414
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
415
+ kind: "tool",
416
+ ctx,
417
+ toolName: info.toolName,
418
+ toolCallId: info.toolCallId,
419
+ iteration: state.iterationCount - 1
420
+ }, toolSpan));
421
+ toolSpan.end();
422
+ state.toolSpans.delete(info.toolCallId);
423
+ });
424
+ },
425
+ onError(ctx, info) {
426
+ if (ctx.activity !== "chat") {
427
+ safeCall("otel.onError", () => {
428
+ const message = errorMessage(info.error);
429
+ endMediaSpan(ctx, (span) => {
430
+ span.recordException(info.error);
431
+ span.setStatus({
432
+ code: SpanStatusCode.ERROR,
433
+ ...message !== void 0 ? { message } : {}
434
+ });
435
+ });
436
+ recordMediaDuration(ctx, info.duration, errorTypeName(info.error));
437
+ });
438
+ return;
439
+ }
440
+ const chatCtx = ctx;
441
+ safeCall("otel.onError", () => {
442
+ const state = stateByCtx.get(chatCtx);
443
+ if (!state) return;
444
+ const errType = errorTypeName(info.error);
445
+ const message = errorMessage(info.error);
446
+ const statusMessage = message !== void 0 ? { message } : {};
447
+ const exception = info.error;
448
+ const iterationSpan = state.currentIterationSpan;
449
+ if (iterationSpan) {
450
+ iterationSpan.recordException(exception);
451
+ iterationSpan.setStatus({
452
+ code: SpanStatusCode.ERROR,
453
+ ...statusMessage
454
+ });
455
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
456
+ kind: "iteration",
457
+ ctx: chatCtx,
458
+ iteration: state.iterationCount - 1
459
+ }, iterationSpan));
460
+ iterationSpan.end();
461
+ state.currentIterationSpan = null;
462
+ }
463
+ for (const [id, entry] of state.toolSpans) {
464
+ const { span, toolName } = entry;
465
+ span.recordException(exception);
466
+ span.setStatus({
467
+ code: SpanStatusCode.ERROR,
468
+ ...statusMessage
469
+ });
470
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
471
+ kind: "tool",
472
+ ctx: chatCtx,
473
+ toolCallId: id,
474
+ toolName,
475
+ iteration: state.iterationCount - 1
476
+ }, span));
477
+ span.end();
478
+ state.toolSpans.delete(id);
479
+ }
480
+ state.rootSpan.recordException(exception);
481
+ state.rootSpan.setStatus({
482
+ code: SpanStatusCode.ERROR,
483
+ ...statusMessage
484
+ });
485
+ if (durationHistogram) durationHistogram.record(info.duration / 1e3, {
486
+ "gen_ai.system": chatCtx.provider,
487
+ "gen_ai.operation.name": "chat",
488
+ "gen_ai.request.model": chatCtx.model,
489
+ "error.type": errType
490
+ });
491
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
492
+ kind: "chat",
493
+ ctx: chatCtx
494
+ }, state.rootSpan));
495
+ state.rootSpan.end();
496
+ stateByCtx.delete(chatCtx);
497
+ });
498
+ },
499
+ onAbort(ctx, info) {
500
+ if (ctx.activity !== "chat") {
501
+ safeCall("otel.onAbort", () => {
502
+ endMediaSpan(ctx, (span) => {
503
+ span.setAttribute("tanstack.ai.completion.reason", "cancelled");
504
+ span.setStatus({
505
+ code: SpanStatusCode.ERROR,
506
+ message: info.reason ?? "cancelled"
507
+ });
508
+ });
509
+ recordMediaDuration(ctx, info.duration, "cancelled");
510
+ });
511
+ return;
512
+ }
513
+ const chatCtx = ctx;
514
+ safeCall("otel.onAbort", () => {
515
+ const state = stateByCtx.get(chatCtx);
516
+ if (!state) return;
517
+ const closeCancelled = (span) => {
518
+ span.setAttribute("tanstack.ai.completion.reason", "cancelled");
519
+ span.setStatus({
520
+ code: SpanStatusCode.ERROR,
521
+ message: "cancelled"
522
+ });
523
+ };
524
+ const iterationSpan = state.currentIterationSpan;
525
+ if (iterationSpan) {
526
+ closeCancelled(iterationSpan);
527
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
528
+ kind: "iteration",
529
+ ctx: chatCtx,
530
+ iteration: state.iterationCount - 1
531
+ }, iterationSpan));
532
+ iterationSpan.end();
533
+ state.currentIterationSpan = null;
534
+ }
535
+ for (const [id, entry] of state.toolSpans) {
536
+ const { span, toolName } = entry;
537
+ closeCancelled(span);
538
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
539
+ kind: "tool",
540
+ ctx: chatCtx,
541
+ toolCallId: id,
542
+ toolName,
543
+ iteration: state.iterationCount - 1
544
+ }, span));
545
+ span.end();
546
+ state.toolSpans.delete(id);
547
+ }
548
+ closeCancelled(state.rootSpan);
549
+ if (durationHistogram) durationHistogram.record(info.duration / 1e3, {
550
+ "gen_ai.system": chatCtx.provider,
551
+ "gen_ai.operation.name": "chat",
552
+ "gen_ai.request.model": chatCtx.model,
553
+ "error.type": "cancelled"
554
+ });
555
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
556
+ kind: "chat",
557
+ ctx: chatCtx
558
+ }, state.rootSpan));
559
+ state.rootSpan.end();
560
+ stateByCtx.delete(chatCtx);
561
+ });
562
+ },
563
+ onFinish(ctx, info) {
564
+ if (ctx.activity !== "chat") {
565
+ safeCall("otel.onFinish", () => {
566
+ endMediaSpan(ctx, (span) => {
567
+ if (info.usage) span.setAttributes(usageAttributes(info.usage));
568
+ });
569
+ recordMediaDuration(ctx, info.duration);
570
+ });
571
+ return;
572
+ }
573
+ const chatCtx = ctx;
574
+ safeCall("otel.onFinish", () => {
575
+ const state = stateByCtx.get(chatCtx);
576
+ if (!state) return;
577
+ for (const [id, entry] of state.toolSpans) {
578
+ const { span, toolName } = entry;
579
+ span.setAttribute("tanstack.ai.tool.outcome", "unknown");
580
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
581
+ kind: "tool",
582
+ ctx: chatCtx,
583
+ toolCallId: id,
584
+ toolName,
585
+ iteration: state.iterationCount - 1
586
+ }, span));
587
+ span.end();
588
+ state.toolSpans.delete(id);
589
+ }
590
+ closeIterationSpan(state, chatCtx);
591
+ if (durationHistogram) durationHistogram.record(info.duration / 1e3, {
592
+ "gen_ai.system": chatCtx.provider,
593
+ "gen_ai.operation.name": "chat",
594
+ "gen_ai.request.model": chatCtx.model
595
+ });
596
+ if (info.usage) state.rootSpan.setAttributes(usageAttributes(info.usage));
597
+ if (state.lastFinishReason) state.rootSpan.setAttribute("gen_ai.response.finish_reasons", [state.lastFinishReason]);
598
+ state.rootSpan.setAttribute("tanstack.ai.iterations", state.iterationCount);
599
+ safeCall("otel.onSpanEnd", () => onSpanEnd?.({
600
+ kind: "chat",
601
+ ctx: chatCtx
602
+ }, state.rootSpan));
603
+ state.rootSpan.end();
604
+ stateByCtx.delete(chatCtx);
605
+ });
606
+ }
607
+ };
741
608
  }
742
- export {
743
- otelMiddleware
744
- };
745
- //# sourceMappingURL=otel.js.map
609
+ //#endregion
610
+ export { otelMiddleware };
611
+
612
+ //# sourceMappingURL=otel.js.map