@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,212 +1,262 @@
1
- import { EventType } from "@ag-ui/core";
2
1
  import { toRunErrorPayload } from "../error-payload.js";
3
- import { MAX_TOKENS_KEYS } from "../../utilities/sampling-keys.js";
4
2
  import { BaseSummarizeAdapter } from "./adapter.js";
5
- const MAX_TOKENS_KEY_BY_ADAPTER = {
6
- openai: "max_output_tokens",
7
- anthropic: "max_tokens",
8
- grok: "max_tokens",
9
- groq: "max_completion_tokens",
10
- gemini: "maxOutputTokens",
11
- openrouter: "maxCompletionTokens"
3
+ import { MAX_TOKENS_KEYS } from "../../utilities/sampling-keys.js";
4
+ import { EventType } from "@ag-ui/core";
5
+ //#region src/activities/summarize/chat-stream-summarize.ts
6
+ /**
7
+ * Provider-native max-output-tokens key per summarize-adapter `name`. summarize
8
+ * is provider-agnostic and forwards `modelOptions` opaquely to the wrapped text
9
+ * adapter, so `maxLength` must be written under the exact key the underlying
10
+ * provider reads — no adapter reads a generic `maxTokens`. Ollama is the one
11
+ * exception: it nests sampling under `options`, so it has no entry here and is
12
+ * handled as a special nested case in `applyMaxLength`/`applyDefaultTemperature`.
13
+ *
14
+ * Keep in sync with each adapter's wire mapping:
15
+ * - OpenAI (Responses): `max_output_tokens`
16
+ * - Anthropic / Grok: `max_tokens`
17
+ * - Groq: `max_completion_tokens`
18
+ * - Gemini: `maxOutputTokens`
19
+ * - OpenRouter: `maxCompletionTokens`
20
+ * - Ollama: nested `options.num_predict` (no entry — see `applyMaxLength`)
21
+ */
22
+ var MAX_TOKENS_KEY_BY_ADAPTER = {
23
+ openai: "max_output_tokens",
24
+ anthropic: "max_tokens",
25
+ grok: "max_tokens",
26
+ groq: "max_completion_tokens",
27
+ gemini: "maxOutputTokens",
28
+ openrouter: "maxCompletionTokens"
12
29
  };
13
- const KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS;
30
+ /**
31
+ * Every flat key any supported provider uses to cap output tokens (plus the
32
+ * generic `maxTokens` spelling no adapter reads). Used to detect a
33
+ * caller-supplied token limit so the summarize default never overrides an
34
+ * explicit caller value. Shared with the OTel middleware via
35
+ * `MAX_TOKENS_KEYS` so the two spelling sets cannot drift.
36
+ */
37
+ var KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS;
38
+ /**
39
+ * Whether `applyMaxLength` knows how to place a token limit for this adapter
40
+ * `name` (either the nested Ollama shape or a flat provider-native key).
41
+ * Used to surface a warning when `maxLength` would otherwise be silently
42
+ * dropped for an unrecognised adapter name.
43
+ */
14
44
  function isKnownMaxTokensAdapter(adapterName) {
15
- return adapterName === "ollama" || MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== void 0;
45
+ return adapterName === "ollama" || MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== void 0;
16
46
  }
47
+ /**
48
+ * Apply the low-temperature summarize default to a working copy of the
49
+ * caller's `modelOptions`, placed where the wrapped provider actually reads
50
+ * it (nested under `options` for Ollama, flat otherwise). The caller always
51
+ * wins: if they already set `temperature` in that location, it is untouched.
52
+ */
17
53
  function applyDefaultTemperature(adapterName, temperature, modelOptions) {
18
- const merged = { ...modelOptions };
19
- if (adapterName === "ollama") {
20
- const existing = merged.options && typeof merged.options === "object" ? merged.options : void 0;
21
- if (existing && "temperature" in existing) return merged;
22
- merged.options = { temperature, ...existing };
23
- return merged;
24
- }
25
- if ("temperature" in merged) return merged;
26
- merged.temperature = temperature;
27
- return merged;
54
+ const merged = { ...modelOptions };
55
+ if (adapterName === "ollama") {
56
+ const existing = merged.options && typeof merged.options === "object" ? merged.options : void 0;
57
+ if (existing && "temperature" in existing) return merged;
58
+ merged.options = {
59
+ temperature,
60
+ ...existing
61
+ };
62
+ return merged;
63
+ }
64
+ if ("temperature" in merged) return merged;
65
+ merged.temperature = temperature;
66
+ return merged;
28
67
  }
68
+ /**
69
+ * Resolve `maxLength` to the provider-native max-output-tokens key for the
70
+ * given summarize-adapter `name` (this wrapper's OWN `name`, not the wrapped
71
+ * text adapter's) and merge it into a working copy of the caller's
72
+ * `modelOptions`. The caller always wins: if they already set any recognised
73
+ * token-limit key (flat or, for Ollama, nested `options.num_predict`), the
74
+ * default is left untouched. Unknown/unrecognised adapter names fall back to
75
+ * NOT setting a token key (the prompt hint still asks the model to stay under
76
+ * `maxLength`) rather than writing a dead key no provider reads.
77
+ *
78
+ * Caveat (intentional): "caller wins" keys off ANY recognised spelling in
79
+ * `KNOWN_MAX_TOKENS_KEYS`, but only the adapter's native key is read on the
80
+ * wire. So a caller who sets a NON-native spelling for this provider — e.g.
81
+ * `maxTokens`, or Anthropic's `max_tokens` against an OpenAI adapter — suppresses
82
+ * the summarize default WITHOUT getting their own value applied either: neither
83
+ * cap reaches the wire. This favours never clobbering a migration leftover over
84
+ * guaranteeing a cap; the prompt-level hint still asks the model to stay under
85
+ * `maxLength`. Rename the key to the provider-native spelling to forward it.
86
+ */
29
87
  function applyMaxLength(adapterName, maxLength, modelOptions) {
30
- const merged = { ...modelOptions };
31
- if (adapterName === "ollama") {
32
- const callerSetFlatLimit = KNOWN_MAX_TOKENS_KEYS.some(
33
- (k) => typeof merged[k] === "number"
34
- );
35
- const existing = merged.options && typeof merged.options === "object" ? merged.options : void 0;
36
- if (callerSetFlatLimit || existing && typeof existing.num_predict === "number") {
37
- return merged;
38
- }
39
- merged.options = { num_predict: maxLength, ...existing };
40
- return merged;
41
- }
42
- const key = MAX_TOKENS_KEY_BY_ADAPTER[adapterName];
43
- if (key === void 0) return merged;
44
- const callerSetLimit = KNOWN_MAX_TOKENS_KEYS.some(
45
- (k) => typeof merged[k] === "number"
46
- );
47
- if (callerSetLimit) return merged;
48
- merged[key] = maxLength;
49
- return merged;
50
- }
51
- class ChatStreamSummarizeAdapter extends BaseSummarizeAdapter {
52
- name;
53
- textAdapter;
54
- constructor(textAdapter, model, name = "chat-stream-summarize") {
55
- super({}, model);
56
- this.name = name;
57
- this.textAdapter = textAdapter;
58
- }
59
- async summarize(options) {
60
- const systemPrompt = this.buildSummarizationPrompt(options);
61
- let summary = "";
62
- const id = this.generateId();
63
- let model = options.model;
64
- let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
65
- options.logger.request(
66
- `activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? "unset"}`,
67
- { provider: this.name, model: options.model }
68
- );
69
- try {
70
- for await (const chunk of this.textAdapter.chatStream(
71
- this.buildTextOptions(options, systemPrompt)
72
- )) {
73
- if (chunk.type === "TEXT_MESSAGE_CONTENT") {
74
- if (chunk.content) {
75
- summary = chunk.content;
76
- } else if (chunk.delta) {
77
- summary += chunk.delta;
78
- }
79
- model = chunk.model || model;
80
- }
81
- if (chunk.type === "RUN_FINISHED") {
82
- if (chunk.usage) {
83
- usage = chunk.usage;
84
- }
85
- }
86
- if (chunk.type === "RUN_ERROR") {
87
- const message = (chunk.error && typeof chunk.error.message === "string" ? chunk.error.message : null) ?? "Summarization failed";
88
- const code = chunk.error && typeof chunk.error.code === "string" ? chunk.error.code : void 0;
89
- const err = new Error(message);
90
- if (code) {
91
- ;
92
- err.code = code;
93
- }
94
- throw err;
95
- }
96
- }
97
- } catch (error) {
98
- options.logger.errors(`${this.name}.summarize fatal`, {
99
- error: toRunErrorPayload(error, `${this.name}.summarize failed`),
100
- source: `${this.name}.summarize`
101
- });
102
- throw error;
103
- }
104
- return { id, model, summary, usage };
105
- }
106
- async *summarizeStream(options) {
107
- const systemPrompt = this.buildSummarizationPrompt(options);
108
- options.logger.request(
109
- `activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? "unset"}`,
110
- { provider: this.name, model: options.model }
111
- );
112
- const id = this.generateId();
113
- let summary = "";
114
- let model = options.model;
115
- let usage = {
116
- promptTokens: 0,
117
- completionTokens: 0,
118
- totalTokens: 0
119
- };
120
- try {
121
- for await (const chunk of this.textAdapter.chatStream(
122
- this.buildTextOptions(options, systemPrompt)
123
- )) {
124
- if (chunk.type === "TEXT_MESSAGE_CONTENT") {
125
- if (chunk.content) {
126
- summary = chunk.content;
127
- } else if (chunk.delta) {
128
- summary += chunk.delta;
129
- }
130
- if (chunk.model) model = chunk.model;
131
- }
132
- if (chunk.type === "RUN_FINISHED") {
133
- if (chunk.usage) usage = chunk.usage;
134
- if (chunk.model) model = chunk.model;
135
- yield {
136
- type: EventType.CUSTOM,
137
- name: "generation:result",
138
- value: { id, model, summary, usage },
139
- model,
140
- timestamp: Date.now()
141
- };
142
- }
143
- yield chunk;
144
- }
145
- } catch (error) {
146
- options.logger.errors(`${this.name}.summarizeStream fatal`, {
147
- error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`),
148
- source: `${this.name}.summarizeStream`
149
- });
150
- throw error;
151
- }
152
- }
153
- /**
154
- * Build the TextOptions passed to the underlying chatStream. Provider
155
- * `modelOptions` from the summarize call are forwarded as-is so knobs like
156
- * Anthropic cache headers, Gemini safety settings, or Ollama tuning params
157
- * still reach the wire layer.
158
- */
159
- buildTextOptions(options, systemPrompt) {
160
- let working = {
161
- ...options.modelOptions
162
- };
163
- working = applyDefaultTemperature(this.name, 0.3, working);
164
- if (options.maxLength !== void 0) {
165
- if (!isKnownMaxTokensAdapter(this.name)) {
166
- options.logger.warn(
167
- `summarize: maxLength=${options.maxLength} could not be mapped to a provider token key for adapter name "${this.name}" — it was dropped from modelOptions (the prompt still asks the model to stay under it). Construct ChatStreamSummarizeAdapter with a recognised provider name to forward the cap.`,
168
- { provider: this.name }
169
- );
170
- }
171
- working = applyMaxLength(this.name, options.maxLength, working);
172
- }
173
- const modelOptions = working;
174
- return {
175
- model: options.model,
176
- messages: [{ role: "user", content: options.text }],
177
- systemPrompts: [systemPrompt],
178
- modelOptions,
179
- logger: options.logger
180
- };
181
- }
182
- buildSummarizationPrompt(options) {
183
- let prompt = "You are a professional summarizer. ";
184
- switch (options.style) {
185
- case "bullet-points":
186
- prompt += "Provide a summary in bullet point format. ";
187
- break;
188
- case "paragraph":
189
- prompt += "Provide a summary in paragraph format. ";
190
- break;
191
- case "concise":
192
- prompt += "Provide a very concise summary in 1-2 sentences. ";
193
- break;
194
- case void 0:
195
- prompt += "Provide a clear and concise summary. ";
196
- break;
197
- default:
198
- prompt += "Provide a clear and concise summary. ";
199
- }
200
- if (options.focus && options.focus.length > 0) {
201
- prompt += `Focus on the following aspects: ${options.focus.join(", ")}. `;
202
- }
203
- if (options.maxLength) {
204
- prompt += `Keep the summary under ${options.maxLength} tokens. `;
205
- }
206
- return prompt;
207
- }
88
+ const merged = { ...modelOptions };
89
+ if (adapterName === "ollama") {
90
+ const callerSetFlatLimit = KNOWN_MAX_TOKENS_KEYS.some((k) => typeof merged[k] === "number");
91
+ const existing = merged.options && typeof merged.options === "object" ? merged.options : void 0;
92
+ if (callerSetFlatLimit || existing && typeof existing.num_predict === "number") return merged;
93
+ merged.options = {
94
+ num_predict: maxLength,
95
+ ...existing
96
+ };
97
+ return merged;
98
+ }
99
+ const key = MAX_TOKENS_KEY_BY_ADAPTER[adapterName];
100
+ if (key === void 0) return merged;
101
+ if (KNOWN_MAX_TOKENS_KEYS.some((k) => typeof merged[k] === "number")) return merged;
102
+ merged[key] = maxLength;
103
+ return merged;
208
104
  }
209
- export {
210
- ChatStreamSummarizeAdapter
105
+ /**
106
+ * Summarize adapter that wraps any `ChatStreamCapable` text adapter and
107
+ * prompts it for summarization. Not tied to any wire format.
108
+ */
109
+ var ChatStreamSummarizeAdapter = class extends BaseSummarizeAdapter {
110
+ name;
111
+ textAdapter;
112
+ constructor(textAdapter, model, name = "chat-stream-summarize") {
113
+ super({}, model);
114
+ this.name = name;
115
+ this.textAdapter = textAdapter;
116
+ }
117
+ async summarize(options) {
118
+ const systemPrompt = this.buildSummarizationPrompt(options);
119
+ let summary = "";
120
+ const id = this.generateId();
121
+ let model = options.model;
122
+ let usage = {
123
+ promptTokens: 0,
124
+ completionTokens: 0,
125
+ totalTokens: 0
126
+ };
127
+ options.logger.request(`activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? "unset"}`, {
128
+ provider: this.name,
129
+ model: options.model
130
+ });
131
+ try {
132
+ for await (const chunk of this.textAdapter.chatStream(this.buildTextOptions(options, systemPrompt))) {
133
+ if (chunk.type === "TEXT_MESSAGE_CONTENT") {
134
+ if (chunk.content) summary = chunk.content;
135
+ else if (chunk.delta) summary += chunk.delta;
136
+ model = chunk.model || model;
137
+ }
138
+ if (chunk.type === "RUN_FINISHED") {
139
+ if (chunk.usage) usage = chunk.usage;
140
+ }
141
+ if (chunk.type === "RUN_ERROR") {
142
+ const message = (chunk.error && typeof chunk.error.message === "string" ? chunk.error.message : null) ?? "Summarization failed";
143
+ const code = chunk.error && typeof chunk.error.code === "string" ? chunk.error.code : void 0;
144
+ const err = new Error(message);
145
+ if (code) err.code = code;
146
+ throw err;
147
+ }
148
+ }
149
+ } catch (error) {
150
+ options.logger.errors(`${this.name}.summarize fatal`, {
151
+ error: toRunErrorPayload(error, `${this.name}.summarize failed`),
152
+ source: `${this.name}.summarize`
153
+ });
154
+ throw error;
155
+ }
156
+ return {
157
+ id,
158
+ model,
159
+ summary,
160
+ usage
161
+ };
162
+ }
163
+ async *summarizeStream(options) {
164
+ const systemPrompt = this.buildSummarizationPrompt(options);
165
+ options.logger.request(`activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? "unset"}`, {
166
+ provider: this.name,
167
+ model: options.model
168
+ });
169
+ const id = this.generateId();
170
+ let summary = "";
171
+ let model = options.model;
172
+ let usage = {
173
+ promptTokens: 0,
174
+ completionTokens: 0,
175
+ totalTokens: 0
176
+ };
177
+ try {
178
+ for await (const chunk of this.textAdapter.chatStream(this.buildTextOptions(options, systemPrompt))) {
179
+ if (chunk.type === "TEXT_MESSAGE_CONTENT") {
180
+ if (chunk.content) summary = chunk.content;
181
+ else if (chunk.delta) summary += chunk.delta;
182
+ if (chunk.model) model = chunk.model;
183
+ }
184
+ if (chunk.type === "RUN_FINISHED") {
185
+ if (chunk.usage) usage = chunk.usage;
186
+ if (chunk.model) model = chunk.model;
187
+ yield {
188
+ type: EventType.CUSTOM,
189
+ name: "generation:result",
190
+ value: {
191
+ id,
192
+ model,
193
+ summary,
194
+ usage
195
+ },
196
+ model,
197
+ timestamp: Date.now()
198
+ };
199
+ }
200
+ yield chunk;
201
+ }
202
+ } catch (error) {
203
+ options.logger.errors(`${this.name}.summarizeStream fatal`, {
204
+ error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`),
205
+ source: `${this.name}.summarizeStream`
206
+ });
207
+ throw error;
208
+ }
209
+ }
210
+ /**
211
+ * Build the TextOptions passed to the underlying chatStream. Provider
212
+ * `modelOptions` from the summarize call are forwarded as-is so knobs like
213
+ * Anthropic cache headers, Gemini safety settings, or Ollama tuning params
214
+ * still reach the wire layer.
215
+ */
216
+ buildTextOptions(options, systemPrompt) {
217
+ let working = { ...options.modelOptions };
218
+ working = applyDefaultTemperature(this.name, .3, working);
219
+ if (options.maxLength !== void 0) {
220
+ if (!isKnownMaxTokensAdapter(this.name)) options.logger.warn(`summarize: maxLength=${options.maxLength} could not be mapped to a provider token key for adapter name "${this.name}" — it was dropped from modelOptions (the prompt still asks the model to stay under it). Construct ChatStreamSummarizeAdapter with a recognised provider name to forward the cap.`, { provider: this.name });
221
+ working = applyMaxLength(this.name, options.maxLength, working);
222
+ }
223
+ const modelOptions = working;
224
+ return {
225
+ model: options.model,
226
+ messages: [{
227
+ role: "user",
228
+ content: options.text
229
+ }],
230
+ systemPrompts: [systemPrompt],
231
+ modelOptions,
232
+ logger: options.logger,
233
+ ...options.runId !== void 0 ? { runId: options.runId } : {},
234
+ ...options.threadId !== void 0 ? { threadId: options.threadId } : {}
235
+ };
236
+ }
237
+ buildSummarizationPrompt(options) {
238
+ let prompt = "You are a professional summarizer. ";
239
+ switch (options.style) {
240
+ case "bullet-points":
241
+ prompt += "Provide a summary in bullet point format. ";
242
+ break;
243
+ case "paragraph":
244
+ prompt += "Provide a summary in paragraph format. ";
245
+ break;
246
+ case "concise":
247
+ prompt += "Provide a very concise summary in 1-2 sentences. ";
248
+ break;
249
+ case void 0:
250
+ prompt += "Provide a clear and concise summary. ";
251
+ break;
252
+ default: prompt += "Provide a clear and concise summary. ";
253
+ }
254
+ if (options.focus && options.focus.length > 0) prompt += `Focus on the following aspects: ${options.focus.join(", ")}. `;
255
+ if (options.maxLength) prompt += `Keep the summary under ${options.maxLength} tokens. `;
256
+ return prompt;
257
+ }
211
258
  };
212
- //# sourceMappingURL=chat-stream-summarize.js.map
259
+ //#endregion
260
+ export { ChatStreamSummarizeAdapter };
261
+
262
+ //# sourceMappingURL=chat-stream-summarize.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-stream-summarize.js","sources":["../../../../src/activities/summarize/chat-stream-summarize.ts"],"sourcesContent":["import { EventType } from '@ag-ui/core'\nimport { toRunErrorPayload } from '../error-payload'\nimport { MAX_TOKENS_KEYS } from '../../utilities/sampling-keys'\nimport { BaseSummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n TextOptions,\n} from '../../types'\n\n/**\n * Minimal contract for a text adapter that supports `chatStream`. Lets\n * `ChatStreamSummarizeAdapter` work with any text adapter without coupling\n * to a specific implementation.\n *\n * The provider-options shape is intentionally `any` here — the wrapper only\n * forwards `modelOptions` straight through, so a text adapter with a richer\n * per-model options type (e.g. `ResolveProviderOptions<TModel>`) is still\n * acceptable. Summarize-level type safety is enforced via\n * `SummarizationOptions<TProviderOptions>` on the wrapper itself.\n */\nexport interface ChatStreamCapable {\n chatStream: (options: TextOptions<any>) => AsyncIterable<StreamChunk>\n}\n\n/**\n * Provider-native max-output-tokens key per summarize-adapter `name`. summarize\n * is provider-agnostic and forwards `modelOptions` opaquely to the wrapped text\n * adapter, so `maxLength` must be written under the exact key the underlying\n * provider reads — no adapter reads a generic `maxTokens`. Ollama is the one\n * exception: it nests sampling under `options`, so it has no entry here and is\n * handled as a special nested case in `applyMaxLength`/`applyDefaultTemperature`.\n *\n * Keep in sync with each adapter's wire mapping:\n * - OpenAI (Responses): `max_output_tokens`\n * - Anthropic / Grok: `max_tokens`\n * - Groq: `max_completion_tokens`\n * - Gemini: `maxOutputTokens`\n * - OpenRouter: `maxCompletionTokens`\n * - Ollama: nested `options.num_predict` (no entry — see `applyMaxLength`)\n */\nconst MAX_TOKENS_KEY_BY_ADAPTER: Record<string, string> = {\n openai: 'max_output_tokens',\n anthropic: 'max_tokens',\n grok: 'max_tokens',\n groq: 'max_completion_tokens',\n gemini: 'maxOutputTokens',\n openrouter: 'maxCompletionTokens',\n}\n\n/**\n * Every flat key any supported provider uses to cap output tokens (plus the\n * generic `maxTokens` spelling no adapter reads). Used to detect a\n * caller-supplied token limit so the summarize default never overrides an\n * explicit caller value. Shared with the OTel middleware via\n * `MAX_TOKENS_KEYS` so the two spelling sets cannot drift.\n */\nconst KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS\n\n/**\n * Whether `applyMaxLength` knows how to place a token limit for this adapter\n * `name` (either the nested Ollama shape or a flat provider-native key).\n * Used to surface a warning when `maxLength` would otherwise be silently\n * dropped for an unrecognised adapter name.\n */\nfunction isKnownMaxTokensAdapter(adapterName: string): boolean {\n return (\n adapterName === 'ollama' ||\n MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== undefined\n )\n}\n\n/**\n * Apply the low-temperature summarize default to a working copy of the\n * caller's `modelOptions`, placed where the wrapped provider actually reads\n * it (nested under `options` for Ollama, flat otherwise). The caller always\n * wins: if they already set `temperature` in that location, it is untouched.\n */\nfunction applyDefaultTemperature(\n adapterName: string,\n temperature: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (existing && 'temperature' in existing) return merged\n merged.options = { temperature, ...existing }\n return merged\n }\n\n if ('temperature' in merged) return merged\n merged.temperature = temperature\n return merged\n}\n\n/**\n * Resolve `maxLength` to the provider-native max-output-tokens key for the\n * given summarize-adapter `name` (this wrapper's OWN `name`, not the wrapped\n * text adapter's) and merge it into a working copy of the caller's\n * `modelOptions`. The caller always wins: if they already set any recognised\n * token-limit key (flat or, for Ollama, nested `options.num_predict`), the\n * default is left untouched. Unknown/unrecognised adapter names fall back to\n * NOT setting a token key (the prompt hint still asks the model to stay under\n * `maxLength`) rather than writing a dead key no provider reads.\n *\n * Caveat (intentional): \"caller wins\" keys off ANY recognised spelling in\n * `KNOWN_MAX_TOKENS_KEYS`, but only the adapter's native key is read on the\n * wire. So a caller who sets a NON-native spelling for this provider — e.g.\n * `maxTokens`, or Anthropic's `max_tokens` against an OpenAI adapter — suppresses\n * the summarize default WITHOUT getting their own value applied either: neither\n * cap reaches the wire. This favours never clobbering a migration leftover over\n * guaranteeing a cap; the prompt-level hint still asks the model to stay under\n * `maxLength`. Rename the key to the provider-native spelling to forward it.\n */\nfunction applyMaxLength(\n adapterName: string,\n maxLength: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n // Honor a caller-set limit in either shape: a recognised flat key (e.g.\n // left over from a migration) or the nested `options.num_predict`.\n const callerSetFlatLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (\n callerSetFlatLimit ||\n (existing && typeof existing.num_predict === 'number')\n ) {\n return merged\n }\n merged.options = { num_predict: maxLength, ...existing }\n return merged\n }\n\n const key = MAX_TOKENS_KEY_BY_ADAPTER[adapterName]\n if (key === undefined) return merged\n\n const callerSetLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n if (callerSetLimit) return merged\n\n merged[key] = maxLength\n return merged\n}\n\n/**\n * Extract the per-model `modelOptions` type a text adapter accepts. Used by\n * provider summarize factories so their `modelOptions` IntelliSense matches\n * what the underlying text adapter actually understands.\n */\nexport type InferTextProviderOptions<TAdapter> = TAdapter extends {\n '~types': { providerOptions: infer P }\n}\n ? P extends object\n ? P\n : object\n : object\n\n/**\n * Summarize adapter that wraps any `ChatStreamCapable` text adapter and\n * prompts it for summarization. Not tied to any wire format.\n */\nexport class ChatStreamSummarizeAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, unknown>,\n> extends BaseSummarizeAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n private readonly textAdapter: ChatStreamCapable\n\n constructor(\n textAdapter: ChatStreamCapable,\n model: TModel,\n name: string = 'chat-stream-summarize',\n ) {\n super({}, model)\n this.name = name\n this.textAdapter = textAdapter\n }\n\n async summarize(\n options: SummarizationOptions<TProviderOptions>,\n ): Promise<SummarizationResult> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n let summary = ''\n const id = this.generateId()\n let model = options.model\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }\n\n options.logger.request(\n `activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n try {\n for await (const chunk of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n if (chunk.type === 'TEXT_MESSAGE_CONTENT') {\n if (chunk.content) {\n summary = chunk.content\n } else if (chunk.delta) {\n // Append delta only when present — a content-less chunk with no\n // delta would otherwise concat literal `'undefined'`.\n summary += chunk.delta\n }\n model = chunk.model || model\n }\n if (chunk.type === 'RUN_FINISHED') {\n if (chunk.usage) {\n usage = chunk.usage\n }\n }\n // Surface failures: the underlying chatStream emits RUN_ERROR instead\n // of throwing, so without this branch summarize() would return an\n // empty summary and pretend a failed run succeeded.\n if (chunk.type === 'RUN_ERROR') {\n const message =\n (chunk.error && typeof chunk.error.message === 'string'\n ? chunk.error.message\n : null) ?? 'Summarization failed'\n const code =\n chunk.error && typeof chunk.error.code === 'string'\n ? chunk.error.code\n : undefined\n const err = new Error(message)\n if (code) {\n ;(err as Error & { code?: string }).code = code\n }\n throw err\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.summarize fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarize failed`),\n source: `${this.name}.summarize`,\n })\n throw error\n }\n\n return { id, model, summary, usage }\n }\n\n override async *summarizeStream(\n options: SummarizationOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n options.logger.request(\n `activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n const id = this.generateId()\n let summary = ''\n let model = options.model\n let usage: SummarizationResult['usage'] = {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n }\n\n try {\n for await (const chunk of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n // Accumulate the same way `summarize()` does so consumers see deltas\n // AND the terminal `generation:result` event below carries the same\n // final summary that non-streaming returns.\n if (chunk.type === 'TEXT_MESSAGE_CONTENT') {\n if (chunk.content) {\n summary = chunk.content\n } else if (chunk.delta) {\n summary += chunk.delta\n }\n if (chunk.model) model = chunk.model\n }\n\n // Emit the GenerationClient-shaped result event just before the\n // terminal RUN_FINISHED so subscribers (useSummarize) populate\n // `result` before flipping `status` to success.\n if (chunk.type === 'RUN_FINISHED') {\n if (chunk.usage) usage = chunk.usage\n if (chunk.model) model = chunk.model\n yield {\n type: EventType.CUSTOM,\n name: 'generation:result',\n value: { id, model, summary, usage } satisfies SummarizationResult,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield chunk\n }\n } catch (error: unknown) {\n options.logger.errors(`${this.name}.summarizeStream fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`),\n source: `${this.name}.summarizeStream`,\n })\n throw error\n }\n }\n\n /**\n * Build the TextOptions passed to the underlying chatStream. Provider\n * `modelOptions` from the summarize call are forwarded as-is so knobs like\n * Anthropic cache headers, Gemini safety settings, or Ollama tuning params\n * still reach the wire layer.\n */\n protected buildTextOptions(\n options: SummarizationOptions<TProviderOptions>,\n systemPrompt: string,\n ): TextOptions<TProviderOptions> {\n // Sampling knobs now live in provider-native `modelOptions`. Apply the\n // low-temperature default where the wrapped provider actually reads it\n // (nested under `options` for Ollama, flat otherwise) so callers can still\n // override it. Resolving the placement from this summarize adapter's OWN\n // `name` keeps the default off the wire correctly per provider — a flat\n // `temperature` would be silently dropped by Ollama while still showing up\n // in OTel.\n let working: Record<string, unknown> = {\n ...(options.modelOptions as Record<string, unknown> | undefined),\n }\n working = applyDefaultTemperature(this.name, 0.3, working)\n // `maxLength` must reach the wire under the provider-native token key (it\n // differs per provider, and no adapter reads a generic `maxTokens`).\n // Resolve it from this summarize adapter's `name` (the constructor arg,\n // not the wrapped text adapter's name), never overriding a caller-supplied\n // token limit.\n if (options.maxLength !== undefined) {\n if (!isKnownMaxTokensAdapter(this.name)) {\n options.logger.warn(\n `summarize: maxLength=${options.maxLength} could not be mapped to a provider token key for adapter name \"${this.name}\" — it was dropped from modelOptions (the prompt still asks the model to stay under it). Construct ChatStreamSummarizeAdapter with a recognised provider name to forward the cap.`,\n { provider: this.name },\n )\n }\n working = applyMaxLength(this.name, options.maxLength, working)\n }\n const modelOptions = working as TProviderOptions\n\n return {\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n modelOptions,\n logger: options.logger,\n }\n }\n\n protected buildSummarizationPrompt(\n options: SummarizationOptions<TProviderOptions>,\n ): string {\n let prompt = 'You are a professional summarizer. '\n\n switch (options.style) {\n case 'bullet-points':\n prompt += 'Provide a summary in bullet point format. '\n break\n case 'paragraph':\n prompt += 'Provide a summary in paragraph format. '\n break\n case 'concise':\n prompt += 'Provide a very concise summary in 1-2 sentences. '\n break\n case undefined:\n prompt += 'Provide a clear and concise summary. '\n break\n default:\n prompt += 'Provide a clear and concise summary. '\n }\n\n if (options.focus && options.focus.length > 0) {\n prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `\n }\n\n if (options.maxLength) {\n prompt += `Keep the summary under ${options.maxLength} tokens. `\n }\n\n return prompt\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-stream-summarize.js","names":[],"sources":["../../../../src/activities/summarize/chat-stream-summarize.ts"],"sourcesContent":["import { EventType } from '@ag-ui/core'\nimport { toRunErrorPayload } from '../error-payload'\nimport { MAX_TOKENS_KEYS } from '../../utilities/sampling-keys'\nimport { BaseSummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n TextOptions,\n} from '../../types'\n\n/**\n * Minimal contract for a text adapter that supports `chatStream`. Lets\n * `ChatStreamSummarizeAdapter` work with any text adapter without coupling\n * to a specific implementation.\n *\n * The provider-options shape is intentionally `any` here — the wrapper only\n * forwards `modelOptions` straight through, so a text adapter with a richer\n * per-model options type (e.g. `ResolveProviderOptions<TModel>`) is still\n * acceptable. Summarize-level type safety is enforced via\n * `SummarizationOptions<TProviderOptions>` on the wrapper itself.\n */\nexport interface ChatStreamCapable {\n chatStream: (options: TextOptions<any>) => AsyncIterable<StreamChunk>\n}\n\n/**\n * Provider-native max-output-tokens key per summarize-adapter `name`. summarize\n * is provider-agnostic and forwards `modelOptions` opaquely to the wrapped text\n * adapter, so `maxLength` must be written under the exact key the underlying\n * provider reads — no adapter reads a generic `maxTokens`. Ollama is the one\n * exception: it nests sampling under `options`, so it has no entry here and is\n * handled as a special nested case in `applyMaxLength`/`applyDefaultTemperature`.\n *\n * Keep in sync with each adapter's wire mapping:\n * - OpenAI (Responses): `max_output_tokens`\n * - Anthropic / Grok: `max_tokens`\n * - Groq: `max_completion_tokens`\n * - Gemini: `maxOutputTokens`\n * - OpenRouter: `maxCompletionTokens`\n * - Ollama: nested `options.num_predict` (no entry — see `applyMaxLength`)\n */\nconst MAX_TOKENS_KEY_BY_ADAPTER: Record<string, string> = {\n openai: 'max_output_tokens',\n anthropic: 'max_tokens',\n grok: 'max_tokens',\n groq: 'max_completion_tokens',\n gemini: 'maxOutputTokens',\n openrouter: 'maxCompletionTokens',\n}\n\n/**\n * Every flat key any supported provider uses to cap output tokens (plus the\n * generic `maxTokens` spelling no adapter reads). Used to detect a\n * caller-supplied token limit so the summarize default never overrides an\n * explicit caller value. Shared with the OTel middleware via\n * `MAX_TOKENS_KEYS` so the two spelling sets cannot drift.\n */\nconst KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS\n\n/**\n * Whether `applyMaxLength` knows how to place a token limit for this adapter\n * `name` (either the nested Ollama shape or a flat provider-native key).\n * Used to surface a warning when `maxLength` would otherwise be silently\n * dropped for an unrecognised adapter name.\n */\nfunction isKnownMaxTokensAdapter(adapterName: string): boolean {\n return (\n adapterName === 'ollama' ||\n MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== undefined\n )\n}\n\n/**\n * Apply the low-temperature summarize default to a working copy of the\n * caller's `modelOptions`, placed where the wrapped provider actually reads\n * it (nested under `options` for Ollama, flat otherwise). The caller always\n * wins: if they already set `temperature` in that location, it is untouched.\n */\nfunction applyDefaultTemperature(\n adapterName: string,\n temperature: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (existing && 'temperature' in existing) return merged\n merged.options = { temperature, ...existing }\n return merged\n }\n\n if ('temperature' in merged) return merged\n merged.temperature = temperature\n return merged\n}\n\n/**\n * Resolve `maxLength` to the provider-native max-output-tokens key for the\n * given summarize-adapter `name` (this wrapper's OWN `name`, not the wrapped\n * text adapter's) and merge it into a working copy of the caller's\n * `modelOptions`. The caller always wins: if they already set any recognised\n * token-limit key (flat or, for Ollama, nested `options.num_predict`), the\n * default is left untouched. Unknown/unrecognised adapter names fall back to\n * NOT setting a token key (the prompt hint still asks the model to stay under\n * `maxLength`) rather than writing a dead key no provider reads.\n *\n * Caveat (intentional): \"caller wins\" keys off ANY recognised spelling in\n * `KNOWN_MAX_TOKENS_KEYS`, but only the adapter's native key is read on the\n * wire. So a caller who sets a NON-native spelling for this provider — e.g.\n * `maxTokens`, or Anthropic's `max_tokens` against an OpenAI adapter — suppresses\n * the summarize default WITHOUT getting their own value applied either: neither\n * cap reaches the wire. This favours never clobbering a migration leftover over\n * guaranteeing a cap; the prompt-level hint still asks the model to stay under\n * `maxLength`. Rename the key to the provider-native spelling to forward it.\n */\nfunction applyMaxLength(\n adapterName: string,\n maxLength: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n // Honor a caller-set limit in either shape: a recognised flat key (e.g.\n // left over from a migration) or the nested `options.num_predict`.\n const callerSetFlatLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (\n callerSetFlatLimit ||\n (existing && typeof existing.num_predict === 'number')\n ) {\n return merged\n }\n merged.options = { num_predict: maxLength, ...existing }\n return merged\n }\n\n const key = MAX_TOKENS_KEY_BY_ADAPTER[adapterName]\n if (key === undefined) return merged\n\n const callerSetLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n if (callerSetLimit) return merged\n\n merged[key] = maxLength\n return merged\n}\n\n/**\n * Extract the per-model `modelOptions` type a text adapter accepts. Used by\n * provider summarize factories so their `modelOptions` IntelliSense matches\n * what the underlying text adapter actually understands.\n */\nexport type InferTextProviderOptions<TAdapter> = TAdapter extends {\n '~types': { providerOptions: infer P }\n}\n ? P extends object\n ? P\n : object\n : object\n\n/**\n * Summarize adapter that wraps any `ChatStreamCapable` text adapter and\n * prompts it for summarization. Not tied to any wire format.\n */\nexport class ChatStreamSummarizeAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, unknown>,\n> extends BaseSummarizeAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n private readonly textAdapter: ChatStreamCapable\n\n constructor(\n textAdapter: ChatStreamCapable,\n model: TModel,\n name: string = 'chat-stream-summarize',\n ) {\n super({}, model)\n this.name = name\n this.textAdapter = textAdapter\n }\n\n async summarize(\n options: SummarizationOptions<TProviderOptions>,\n ): Promise<SummarizationResult> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n let summary = ''\n const id = this.generateId()\n let model = options.model\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }\n\n options.logger.request(\n `activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n try {\n for await (const chunk of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n if (chunk.type === 'TEXT_MESSAGE_CONTENT') {\n if (chunk.content) {\n summary = chunk.content\n } else if (chunk.delta) {\n // Append delta only when present — a content-less chunk with no\n // delta would otherwise concat literal `'undefined'`.\n summary += chunk.delta\n }\n model = chunk.model || model\n }\n if (chunk.type === 'RUN_FINISHED') {\n if (chunk.usage) {\n usage = chunk.usage\n }\n }\n // Surface failures: the underlying chatStream emits RUN_ERROR instead\n // of throwing, so without this branch summarize() would return an\n // empty summary and pretend a failed run succeeded.\n if (chunk.type === 'RUN_ERROR') {\n const message =\n (chunk.error && typeof chunk.error.message === 'string'\n ? chunk.error.message\n : null) ?? 'Summarization failed'\n const code =\n chunk.error && typeof chunk.error.code === 'string'\n ? chunk.error.code\n : undefined\n const err = new Error(message)\n if (code) {\n ;(err as Error & { code?: string }).code = code\n }\n throw err\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.summarize fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarize failed`),\n source: `${this.name}.summarize`,\n })\n throw error\n }\n\n return { id, model, summary, usage }\n }\n\n override async *summarizeStream(\n options: SummarizationOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n options.logger.request(\n `activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n const id = this.generateId()\n let summary = ''\n let model = options.model\n let usage: SummarizationResult['usage'] = {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n }\n\n try {\n for await (const chunk of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n // Accumulate the same way `summarize()` does so consumers see deltas\n // AND the terminal `generation:result` event below carries the same\n // final summary that non-streaming returns.\n if (chunk.type === 'TEXT_MESSAGE_CONTENT') {\n if (chunk.content) {\n summary = chunk.content\n } else if (chunk.delta) {\n summary += chunk.delta\n }\n if (chunk.model) model = chunk.model\n }\n\n // Emit the GenerationClient-shaped result event just before the\n // terminal RUN_FINISHED so subscribers (useSummarize) populate\n // `result` before flipping `status` to success.\n if (chunk.type === 'RUN_FINISHED') {\n if (chunk.usage) usage = chunk.usage\n if (chunk.model) model = chunk.model\n yield {\n type: EventType.CUSTOM,\n name: 'generation:result',\n value: { id, model, summary, usage } satisfies SummarizationResult,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield chunk\n }\n } catch (error: unknown) {\n options.logger.errors(`${this.name}.summarizeStream fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`),\n source: `${this.name}.summarizeStream`,\n })\n throw error\n }\n }\n\n /**\n * Build the TextOptions passed to the underlying chatStream. Provider\n * `modelOptions` from the summarize call are forwarded as-is so knobs like\n * Anthropic cache headers, Gemini safety settings, or Ollama tuning params\n * still reach the wire layer.\n */\n protected buildTextOptions(\n options: SummarizationOptions<TProviderOptions>,\n systemPrompt: string,\n ): TextOptions<TProviderOptions> {\n // Sampling knobs now live in provider-native `modelOptions`. Apply the\n // low-temperature default where the wrapped provider actually reads it\n // (nested under `options` for Ollama, flat otherwise) so callers can still\n // override it. Resolving the placement from this summarize adapter's OWN\n // `name` keeps the default off the wire correctly per provider — a flat\n // `temperature` would be silently dropped by Ollama while still showing up\n // in OTel.\n let working: Record<string, unknown> = {\n ...(options.modelOptions as Record<string, unknown> | undefined),\n }\n working = applyDefaultTemperature(this.name, 0.3, working)\n // `maxLength` must reach the wire under the provider-native token key (it\n // differs per provider, and no adapter reads a generic `maxTokens`).\n // Resolve it from this summarize adapter's `name` (the constructor arg,\n // not the wrapped text adapter's name), never overriding a caller-supplied\n // token limit.\n if (options.maxLength !== undefined) {\n if (!isKnownMaxTokensAdapter(this.name)) {\n options.logger.warn(\n `summarize: maxLength=${options.maxLength} could not be mapped to a provider token key for adapter name \"${this.name}\" — it was dropped from modelOptions (the prompt still asks the model to stay under it). Construct ChatStreamSummarizeAdapter with a recognised provider name to forward the cap.`,\n { provider: this.name },\n )\n }\n working = applyMaxLength(this.name, options.maxLength, working)\n }\n const modelOptions = working as TProviderOptions\n\n return {\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n modelOptions,\n logger: options.logger,\n // Forward the run identity so the wrapped chat stamps it onto RUN_STARTED\n // (chat uses `runId` as its `runIdOverride`). Conditional spreads keep the\n // fields absent when unset, under `exactOptionalPropertyTypes`.\n ...(options.runId !== undefined ? { runId: options.runId } : {}),\n ...(options.threadId !== undefined ? { threadId: options.threadId } : {}),\n }\n }\n\n protected buildSummarizationPrompt(\n options: SummarizationOptions<TProviderOptions>,\n ): string {\n let prompt = 'You are a professional summarizer. '\n\n switch (options.style) {\n case 'bullet-points':\n prompt += 'Provide a summary in bullet point format. '\n break\n case 'paragraph':\n prompt += 'Provide a summary in paragraph format. '\n break\n case 'concise':\n prompt += 'Provide a very concise summary in 1-2 sentences. '\n break\n case undefined:\n prompt += 'Provide a clear and concise summary. '\n break\n default:\n prompt += 'Provide a clear and concise summary. '\n }\n\n if (options.focus && options.focus.length > 0) {\n prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `\n }\n\n if (options.maxLength) {\n prompt += `Keep the summary under ${options.maxLength} tokens. `\n }\n\n return prompt\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { DebugOption } from '../../logger/types.js';
2
+ import { GenerationMiddleware } from '../middleware/types.js';
2
3
  import { SummarizeAdapter } from './adapter.js';
3
4
  import { StreamChunk, SummarizationResult } from '../../types.js';
4
5
  /** The adapter kind this activity handles */
@@ -27,6 +28,32 @@ export interface SummarizeActivityOptions<TAdapter extends SummarizeAdapter<stri
27
28
  focus?: Array<string>;
28
29
  /** Provider-specific options */
29
30
  modelOptions?: SummarizeProviderOptions<TAdapter>;
31
+ /**
32
+ * Optional run identity. When set on a streaming summarize, it is stamped
33
+ * onto the emitted `RUN_STARTED` so a delivery-durable route keys the run's
34
+ * log by the same id the client rejoins with — making a mid-run reload
35
+ * resumable. Filed under `threadId` when persistence is wired.
36
+ */
37
+ runId?: string;
38
+ /**
39
+ * Stable conversation/thread id for correlating this run when persisted — the
40
+ * slot a reloading client hydrates the last summary by. Pass it whenever
41
+ * persistence is on; `withGenerationPersistence` refuses a run without one.
42
+ */
43
+ threadId?: string;
44
+ /**
45
+ * Observe-only middleware notified on start, usage, success, and error. Pass
46
+ * `otelMiddleware()` for OpenTelemetry, `withGenerationPersistence()` to
47
+ * record the run (summaries are text, so the run record holds the result and
48
+ * there are no artifacts to store), or implement the `GenerationMiddleware`
49
+ * contract for a custom backend.
50
+ *
51
+ * Streaming and non-streaming behave the same way: one `onStart`, then a
52
+ * terminal `onFinish` / `onError`, with the result transforms applied to the
53
+ * `SummarizationResult` in between. A streaming consumer that disconnects
54
+ * mid-summary fires `onAbort`.
55
+ */
56
+ middleware?: Array<GenerationMiddleware>;
30
57
  /**
31
58
  * Whether to stream the summarization result.
32
59
  * When true, returns an AsyncIterable<StreamChunk> for streaming output.