@tanstack/ai 0.36.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/dist/esm/activities/chat/adapter.d.ts +163 -0
  2. package/dist/esm/activities/chat/adapter.js +17 -0
  3. package/dist/esm/activities/chat/adapter.js.map +1 -0
  4. package/dist/esm/activities/chat/agent-loop-strategies.d.ts +59 -0
  5. package/dist/esm/activities/chat/agent-loop-strategies.js +23 -0
  6. package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -0
  7. package/dist/esm/activities/chat/index.d.ts +270 -0
  8. package/dist/esm/activities/chat/index.js +1724 -0
  9. package/dist/esm/activities/chat/index.js.map +1 -0
  10. package/dist/esm/activities/chat/mcp/manager.d.ts +25 -0
  11. package/dist/esm/activities/chat/mcp/manager.js +80 -0
  12. package/dist/esm/activities/chat/mcp/manager.js.map +1 -0
  13. package/dist/esm/activities/chat/mcp/types.d.ts +78 -0
  14. package/dist/esm/activities/chat/messages.d.ts +78 -0
  15. package/dist/esm/activities/chat/messages.js +374 -0
  16. package/dist/esm/activities/chat/messages.js.map +1 -0
  17. package/dist/esm/activities/chat/middleware/builder.d.ts +46 -0
  18. package/dist/esm/activities/chat/middleware/builder.js +17 -0
  19. package/dist/esm/activities/chat/middleware/builder.js.map +1 -0
  20. package/dist/esm/activities/chat/middleware/capabilities.d.ts +93 -0
  21. package/dist/esm/activities/chat/middleware/capabilities.js +45 -0
  22. package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -0
  23. package/dist/esm/activities/chat/middleware/compose.d.ts +87 -0
  24. package/dist/esm/activities/chat/middleware/compose.js +510 -0
  25. package/dist/esm/activities/chat/middleware/compose.js.map +1 -0
  26. package/dist/esm/activities/chat/middleware/define.d.ts +20 -0
  27. package/dist/esm/activities/chat/middleware/define.js +7 -0
  28. package/dist/esm/activities/chat/middleware/define.js.map +1 -0
  29. package/dist/esm/activities/chat/middleware/index.d.ts +10 -0
  30. package/dist/esm/activities/chat/middleware/tool-cache-middleware.d.ts +89 -0
  31. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +76 -0
  32. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -0
  33. package/dist/esm/activities/chat/middleware/types.d.ts +405 -0
  34. package/dist/esm/activities/chat/middleware/validate.d.ts +19 -0
  35. package/dist/esm/activities/chat/middleware/validate.js +30 -0
  36. package/dist/esm/activities/chat/middleware/validate.js.map +1 -0
  37. package/dist/esm/activities/chat/runtime-context-types.d.ts +43 -0
  38. package/dist/esm/activities/chat/stream/index.d.ts +11 -0
  39. package/dist/esm/activities/chat/stream/json-parser.d.ts +38 -0
  40. package/dist/esm/activities/chat/stream/json-parser.js +28 -0
  41. package/dist/esm/activities/chat/stream/json-parser.js.map +1 -0
  42. package/dist/esm/activities/chat/stream/message-updaters.d.ts +81 -0
  43. package/dist/esm/activities/chat/stream/message-updaters.js +253 -0
  44. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -0
  45. package/dist/esm/activities/chat/stream/processor.d.ts +439 -0
  46. package/dist/esm/activities/chat/stream/processor.js +1449 -0
  47. package/dist/esm/activities/chat/stream/processor.js.map +1 -0
  48. package/dist/esm/activities/chat/stream/strategies.d.ts +43 -0
  49. package/dist/esm/activities/chat/stream/strategies.js +54 -0
  50. package/dist/esm/activities/chat/stream/strategies.js.map +1 -0
  51. package/dist/esm/activities/chat/stream/types.d.ts +90 -0
  52. package/dist/esm/activities/chat/tools/lazy-tool-manager.d.ts +82 -0
  53. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +194 -0
  54. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -0
  55. package/dist/esm/activities/chat/tools/lazy-tools.d.ts +15 -0
  56. package/dist/esm/activities/chat/tools/lazy-tools.js +16 -0
  57. package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -0
  58. package/dist/esm/activities/chat/tools/schema-converter.d.ts +140 -0
  59. package/dist/esm/activities/chat/tools/schema-converter.js +167 -0
  60. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -0
  61. package/dist/esm/activities/chat/tools/tool-calls.d.ts +145 -0
  62. package/dist/esm/activities/chat/tools/tool-calls.js +548 -0
  63. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -0
  64. package/dist/esm/activities/chat/tools/tool-definition.d.ts +135 -0
  65. package/dist/esm/activities/chat/tools/tool-definition.js +25 -0
  66. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -0
  67. package/dist/esm/activities/error-payload.d.ts +30 -0
  68. package/dist/esm/activities/error-payload.js +54 -0
  69. package/dist/esm/activities/error-payload.js.map +1 -0
  70. package/dist/esm/activities/generateAudio/adapter.d.ts +62 -0
  71. package/dist/esm/activities/generateAudio/adapter.js +16 -0
  72. package/dist/esm/activities/generateAudio/adapter.js.map +1 -0
  73. package/dist/esm/activities/generateAudio/index.d.ts +85 -0
  74. package/dist/esm/activities/generateAudio/index.js +114 -0
  75. package/dist/esm/activities/generateAudio/index.js.map +1 -0
  76. package/dist/esm/activities/generateImage/adapter.d.ts +78 -0
  77. package/dist/esm/activities/generateImage/adapter.js +16 -0
  78. package/dist/esm/activities/generateImage/adapter.js.map +1 -0
  79. package/dist/esm/activities/generateImage/index.d.ts +134 -0
  80. package/dist/esm/activities/generateImage/index.js +121 -0
  81. package/dist/esm/activities/generateImage/index.js.map +1 -0
  82. package/dist/esm/activities/generateSpeech/adapter.d.ts +62 -0
  83. package/dist/esm/activities/generateSpeech/adapter.js +16 -0
  84. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -0
  85. package/dist/esm/activities/generateSpeech/index.d.ts +96 -0
  86. package/dist/esm/activities/generateSpeech/index.js +119 -0
  87. package/dist/esm/activities/generateSpeech/index.js.map +1 -0
  88. package/dist/esm/activities/generateTranscription/adapter.d.ts +62 -0
  89. package/dist/esm/activities/generateTranscription/adapter.js +16 -0
  90. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -0
  91. package/dist/esm/activities/generateTranscription/index.d.ts +109 -0
  92. package/dist/esm/activities/generateTranscription/index.js +109 -0
  93. package/dist/esm/activities/generateTranscription/index.js.map +1 -0
  94. package/dist/esm/activities/generateVideo/adapter.d.ts +145 -0
  95. package/dist/esm/activities/generateVideo/adapter.js +30 -0
  96. package/dist/esm/activities/generateVideo/adapter.js.map +1 -0
  97. package/dist/esm/activities/generateVideo/index.d.ts +223 -0
  98. package/dist/esm/activities/generateVideo/index.js +295 -0
  99. package/dist/esm/activities/generateVideo/index.js.map +1 -0
  100. package/dist/esm/activities/generateVideo/snap.d.ts +14 -0
  101. package/dist/esm/activities/generateVideo/snap.js +54 -0
  102. package/dist/esm/activities/generateVideo/snap.js.map +1 -0
  103. package/dist/esm/activities/index.d.ts +27 -0
  104. package/dist/esm/activities/index.js +43 -0
  105. package/dist/esm/activities/index.js.map +1 -0
  106. package/dist/esm/activities/middleware/index.d.ts +2 -0
  107. package/dist/esm/activities/middleware/run.d.ts +20 -0
  108. package/dist/esm/activities/middleware/run.js +42 -0
  109. package/dist/esm/activities/middleware/run.js.map +1 -0
  110. package/dist/esm/activities/middleware/types.d.ts +118 -0
  111. package/dist/esm/activities/stream-generation-result.d.ts +15 -0
  112. package/dist/esm/activities/stream-generation-result.js +49 -0
  113. package/dist/esm/activities/stream-generation-result.js.map +1 -0
  114. package/dist/esm/activities/summarize/adapter.d.ts +74 -0
  115. package/dist/esm/activities/summarize/adapter.js +16 -0
  116. package/dist/esm/activities/summarize/adapter.js.map +1 -0
  117. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +45 -0
  118. package/dist/esm/activities/summarize/chat-stream-summarize.js +212 -0
  119. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -0
  120. package/dist/esm/activities/summarize/index.d.ts +108 -0
  121. package/dist/esm/activities/summarize/index.js +113 -0
  122. package/dist/esm/activities/summarize/index.js.map +1 -0
  123. package/dist/esm/adapter-internals.d.ts +5 -0
  124. package/dist/esm/adapter-internals.js +10 -0
  125. package/dist/esm/adapter-internals.js.map +1 -0
  126. package/dist/esm/client.d.ts +44 -0
  127. package/dist/esm/client.js +67 -0
  128. package/dist/esm/client.js.map +1 -0
  129. package/dist/esm/extend-adapter.d.ts +152 -0
  130. package/dist/esm/extend-adapter.js +21 -0
  131. package/dist/esm/extend-adapter.js.map +1 -0
  132. package/dist/esm/index.d.ts +45 -0
  133. package/dist/esm/index.js +109 -0
  134. package/dist/esm/index.js.map +1 -0
  135. package/dist/esm/logger/console-logger.d.ts +29 -0
  136. package/dist/esm/logger/console-logger.js +83 -0
  137. package/dist/esm/logger/console-logger.js.map +1 -0
  138. package/dist/esm/logger/internal-logger.d.ts +41 -0
  139. package/dist/esm/logger/internal-logger.js +86 -0
  140. package/dist/esm/logger/internal-logger.js.map +1 -0
  141. package/dist/esm/logger/resolve.d.ts +14 -0
  142. package/dist/esm/logger/resolve.js +54 -0
  143. package/dist/esm/logger/resolve.js.map +1 -0
  144. package/dist/esm/logger/types.d.ts +75 -0
  145. package/dist/esm/middlewares/content-guard.d.ts +77 -0
  146. package/dist/esm/middlewares/content-guard.js +156 -0
  147. package/dist/esm/middlewares/content-guard.js.map +1 -0
  148. package/dist/esm/middlewares/index.d.ts +2 -0
  149. package/dist/esm/middlewares/index.js +7 -0
  150. package/dist/esm/middlewares/index.js.map +1 -0
  151. package/dist/esm/middlewares/otel.d.ts +81 -0
  152. package/dist/esm/middlewares/otel.js +745 -0
  153. package/dist/esm/middlewares/otel.js.map +1 -0
  154. package/dist/esm/middlewares/tool-cache.d.ts +1 -0
  155. package/dist/esm/middlewares/usage-attributes.d.ts +24 -0
  156. package/dist/esm/middlewares/usage-attributes.js +43 -0
  157. package/dist/esm/middlewares/usage-attributes.js.map +1 -0
  158. package/dist/esm/realtime/index.d.ts +28 -0
  159. package/dist/esm/realtime/index.js +8 -0
  160. package/dist/esm/realtime/index.js.map +1 -0
  161. package/dist/esm/realtime/types.d.ts +282 -0
  162. package/dist/esm/stream-to-response.d.ts +102 -0
  163. package/dist/esm/stream-to-response.js +121 -0
  164. package/dist/esm/stream-to-response.js.map +1 -0
  165. package/dist/esm/strip-to-spec-middleware.d.ts +18 -0
  166. package/dist/esm/strip-to-spec-middleware.js +20 -0
  167. package/dist/esm/strip-to-spec-middleware.js.map +1 -0
  168. package/dist/esm/system-prompts.d.ts +66 -0
  169. package/dist/esm/system-prompts.js +23 -0
  170. package/dist/esm/system-prompts.js.map +1 -0
  171. package/dist/esm/tool-registry.d.ts +81 -0
  172. package/dist/esm/tool-registry.js +49 -0
  173. package/dist/esm/tool-registry.js.map +1 -0
  174. package/dist/esm/tools/provider-tool.d.ts +30 -0
  175. package/dist/esm/tools/provider-tool.js +7 -0
  176. package/dist/esm/tools/provider-tool.js.map +1 -0
  177. package/dist/esm/types.d.ts +1627 -0
  178. package/dist/esm/utilities/ag-ui-wire.d.ts +44 -0
  179. package/dist/esm/utilities/ag-ui-wire.js +107 -0
  180. package/dist/esm/utilities/ag-ui-wire.js.map +1 -0
  181. package/dist/esm/utilities/chat-params.d.ts +85 -0
  182. package/dist/esm/utilities/chat-params.js +100 -0
  183. package/dist/esm/utilities/chat-params.js.map +1 -0
  184. package/dist/esm/utilities/errors.d.ts +13 -0
  185. package/dist/esm/utilities/errors.js +22 -0
  186. package/dist/esm/utilities/errors.js.map +1 -0
  187. package/dist/esm/utilities/media-prompt.d.ts +35 -0
  188. package/dist/esm/utilities/media-prompt.js +43 -0
  189. package/dist/esm/utilities/media-prompt.js.map +1 -0
  190. package/dist/esm/utilities/numbers.d.ts +8 -0
  191. package/dist/esm/utilities/numbers.js +12 -0
  192. package/dist/esm/utilities/numbers.js.map +1 -0
  193. package/dist/esm/utilities/sampling-keys.d.ts +20 -0
  194. package/dist/esm/utilities/sampling-keys.js +20 -0
  195. package/dist/esm/utilities/sampling-keys.js.map +1 -0
  196. package/dist/esm/utilities/tool-result.d.ts +21 -0
  197. package/dist/esm/utilities/tool-result.js +37 -0
  198. package/dist/esm/utilities/tool-result.js.map +1 -0
  199. package/dist/esm/utilities/usage.d.ts +31 -0
  200. package/dist/esm/utilities/usage.js +11 -0
  201. package/dist/esm/utilities/usage.js.map +1 -0
  202. package/dist/esm/utils.d.ts +17 -0
  203. package/dist/esm/utils.js +20 -0
  204. package/dist/esm/utils.js.map +1 -0
  205. package/package.json +3 -3
  206. package/skills/ai-core/chat-experience/SKILL.md +30 -2
  207. package/skills/ai-core/media-generation/SKILL.md +13 -0
  208. package/skills/ai-core/tool-calling/SKILL.md +10 -3
  209. package/src/activities/chat/mcp/manager.ts +31 -1
  210. package/src/activities/chat/mcp/types.ts +23 -0
  211. package/src/activities/chat/messages.ts +5 -0
  212. package/src/activities/chat/stream/processor.ts +42 -0
  213. package/src/activities/chat/tools/tool-calls.ts +96 -2
  214. package/src/client.ts +1 -0
  215. package/src/types.ts +31 -0
@@ -0,0 +1,118 @@
1
+ import { TokenUsage } from '../../types.js';
2
+ /**
3
+ * The activity an observability event describes.
4
+ *
5
+ * Mirrors the public surface a caller reaches for: `'chat'` for `chat()`, and
6
+ * the media kinds for the `generate*` activities. `'tts'` matches the speech
7
+ * adapter's kind (the public discriminator avoids inventing a parallel
8
+ * `'speech'`/`'text'` vocabulary). `otelMiddleware` maps each to its
9
+ * `gen_ai.operation.name`.
10
+ */
11
+ export type GenerationActivity = 'chat' | 'image' | 'video' | 'audio' | 'tts' | 'transcription';
12
+ /**
13
+ * Stable context passed to every {@link GenerationMiddleware} hook. Created
14
+ * once per activity call and shared across the hooks of that call.
15
+ *
16
+ * Carries only fields every activity can honor. `ChatMiddlewareContext`
17
+ * structurally includes all of these plus chat-only state (messages,
18
+ * iteration, capabilities, …), which is why a chat middleware that reads those
19
+ * extra fields is not assignable to `GenerationMiddleware`.
20
+ */
21
+ export interface GenerationMiddlewareContext<TContext = unknown> {
22
+ /**
23
+ * Stable id correlating the `onStart` / `onFinish` / `onError` / `onAbort`
24
+ * hooks of a single activity call.
25
+ */
26
+ requestId: string;
27
+ /** Which activity this call is. Discriminates media from chat. */
28
+ activity: GenerationActivity;
29
+ /** Provider/adapter name (e.g. `"openai"`). Emitted as `gen_ai.system`. */
30
+ provider: string;
31
+ /** Model id. Emitted as `gen_ai.request.model`. */
32
+ model: string;
33
+ /**
34
+ * Provider-specific options passed to the activity, if any. Typed `unknown`
35
+ * because each activity's options are strongly typed per model; a supertype
36
+ * of `ChatMiddlewareContext`'s `modelOptions`.
37
+ */
38
+ modelOptions?: unknown;
39
+ /** Where the call originates. Always `'server'` for media activities. */
40
+ source: 'client' | 'server';
41
+ /** Generate a unique id with the given prefix. */
42
+ createId: (prefix: string) => string;
43
+ /** Runtime context provided by the activity options, if any. */
44
+ context: TContext;
45
+ }
46
+ /**
47
+ * Token usage passed to {@link GenerationMiddleware.onUsage}. Kept as an
48
+ * interface extending `TokenUsage` to preserve declaration merging for this
49
+ * publicly exported type.
50
+ */
51
+ export interface GenerationUsageInfo extends TokenUsage {
52
+ }
53
+ /** Information passed to {@link GenerationMiddleware.onFinish}. */
54
+ export interface GenerationFinishInfo {
55
+ /** Wall-clock duration of the activity call, in milliseconds. */
56
+ duration: number;
57
+ /** Unified usage, when the provider reported it. */
58
+ usage?: TokenUsage | undefined;
59
+ }
60
+ /** Information passed to {@link GenerationMiddleware.onAbort}. */
61
+ export interface GenerationAbortInfo {
62
+ /** The reason for the abort, if provided. */
63
+ reason?: string;
64
+ /** Wall-clock duration until the abort, in milliseconds. */
65
+ duration: number;
66
+ }
67
+ /** Information passed to {@link GenerationMiddleware.onError}. */
68
+ export interface GenerationErrorInfo {
69
+ /** The thrown value (typically an `Error`). */
70
+ error: unknown;
71
+ /** Wall-clock duration until the failure, in milliseconds. */
72
+ duration: number;
73
+ }
74
+ /**
75
+ * Activity-agnostic, observe-only middleware.
76
+ *
77
+ * A thin lifecycle observer registerable on any activity via its `middleware`
78
+ * option. Unlike `ChatMiddleware` (which can also rewrite config, chunks, and
79
+ * tool calls), these hooks only observe — the right fit for the single
80
+ * request → response shape of media activities. Pass `otelMiddleware()` for
81
+ * OpenTelemetry, or implement the hooks directly for a custom backend.
82
+ *
83
+ * Hooks are awaited in registration order. A hook that throws PROPAGATES and
84
+ * fails the activity — matching `chat()` middleware semantics. Keep them cheap;
85
+ * they run inline with the request.
86
+ *
87
+ * Exactly one of `onFinish` / `onAbort` / `onError` fires per call.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * import { generateImage } from '@tanstack/ai'
92
+ * import { otelMiddleware } from '@tanstack/ai/middlewares/otel'
93
+ * import { openaiImage } from '@tanstack/ai-openai'
94
+ * import { trace } from '@opentelemetry/api'
95
+ *
96
+ * await generateImage({
97
+ * adapter: openaiImage('gpt-image-1'),
98
+ * prompt: 'A serene mountain landscape at sunset',
99
+ * middleware: [otelMiddleware({ tracer: trace.getTracer('my-app') })],
100
+ * })
101
+ * ```
102
+ */
103
+ export interface GenerationMiddleware<TContext = unknown> {
104
+ /** Optional name, surfaced in diagnostics. */
105
+ name?: string;
106
+ /** Called before the adapter request begins. */
107
+ onStart?: (ctx: GenerationMiddlewareContext<TContext>) => void | Promise<void>;
108
+ /** Called when the provider reports usage, before `onFinish`. */
109
+ onUsage?: (ctx: GenerationMiddlewareContext<TContext>, usage: GenerationUsageInfo) => void | Promise<void>;
110
+ /** Called after the activity completes successfully. */
111
+ onFinish?: (ctx: GenerationMiddlewareContext<TContext>, info: GenerationFinishInfo) => void | Promise<void>;
112
+ /** Called when the activity is aborted (e.g. an abandoned stream). */
113
+ onAbort?: (ctx: GenerationMiddlewareContext<TContext>, info: GenerationAbortInfo) => void | Promise<void>;
114
+ /** Called when the activity throws before completing. */
115
+ onError?: (ctx: GenerationMiddlewareContext<TContext>, info: GenerationErrorInfo) => void | Promise<void>;
116
+ }
117
+ /** A `GenerationMiddleware` with a permissive context — for use as a constraint. */
118
+ export type AnyGenerationMiddleware = GenerationMiddleware<any>;
@@ -0,0 +1,15 @@
1
+ import { StreamChunk } from '../types.js';
2
+ /**
3
+ * Wrap a one-shot generation result as a StreamChunk async iterable.
4
+ *
5
+ * This allows non-streaming activities (image, speech, transcription, summarize)
6
+ * to be sent over the same streaming transport as chat.
7
+ *
8
+ * @param generator - An async function that performs the generation and returns the result
9
+ * @param options - Optional configuration (runId, threadId)
10
+ * @returns An AsyncIterable of StreamChunks with RUN_STARTED, CUSTOM(generation:result), and RUN_FINISHED events on success, or RUN_STARTED and RUN_ERROR on failure
11
+ */
12
+ export declare function streamGenerationResult<TResult>(generator: () => Promise<TResult>, options?: {
13
+ runId?: string;
14
+ threadId?: string;
15
+ }): AsyncIterable<StreamChunk>;
@@ -0,0 +1,49 @@
1
+ import { EventType } from "@ag-ui/core";
2
+ import { toRunErrorPayload } from "./error-payload.js";
3
+ function createId(prefix) {
4
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
5
+ }
6
+ async function* streamGenerationResult(generator, options) {
7
+ const runId = createId("run");
8
+ const threadId = createId("thread");
9
+ yield {
10
+ type: EventType.RUN_STARTED,
11
+ runId,
12
+ threadId,
13
+ timestamp: Date.now()
14
+ };
15
+ try {
16
+ const result = await generator();
17
+ yield {
18
+ type: EventType.CUSTOM,
19
+ name: "generation:result",
20
+ value: result,
21
+ timestamp: Date.now()
22
+ };
23
+ yield {
24
+ type: EventType.RUN_FINISHED,
25
+ runId,
26
+ threadId,
27
+ finishReason: "stop",
28
+ timestamp: Date.now()
29
+ };
30
+ } catch (error) {
31
+ const payload = toRunErrorPayload(error, "Generation failed");
32
+ const codeFields = payload.code !== void 0 ? { code: payload.code } : void 0;
33
+ yield {
34
+ type: EventType.RUN_ERROR,
35
+ message: payload.message,
36
+ ...codeFields,
37
+ // Deprecated nested form for backward compatibility
38
+ error: {
39
+ message: payload.message,
40
+ ...codeFields
41
+ },
42
+ timestamp: Date.now()
43
+ };
44
+ }
45
+ }
46
+ export {
47
+ streamGenerationResult
48
+ };
49
+ //# sourceMappingURL=stream-generation-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-generation-result.js","sources":["../../../src/activities/stream-generation-result.ts"],"sourcesContent":["/**\n * Internal helper for wrapping one-shot generation results as StreamChunk\n * async iterables. NOT exported from the package — used only by activity\n * implementations to support `stream: true`.\n */\n\nimport { EventType } from '@ag-ui/core'\nimport { toRunErrorPayload } from './error-payload'\nimport type { StreamChunk } from '../types'\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n/**\n * Wrap a one-shot generation result as a StreamChunk async iterable.\n *\n * This allows non-streaming activities (image, speech, transcription, summarize)\n * to be sent over the same streaming transport as chat.\n *\n * @param generator - An async function that performs the generation and returns the result\n * @param options - Optional configuration (runId, threadId)\n * @returns An AsyncIterable of StreamChunks with RUN_STARTED, CUSTOM(generation:result), and RUN_FINISHED events on success, or RUN_STARTED and RUN_ERROR on failure\n */\nexport async function* streamGenerationResult<TResult>(\n generator: () => Promise<TResult>,\n options?: { runId?: string; threadId?: string },\n): AsyncIterable<StreamChunk> {\n const runId = options?.runId ?? createId('run')\n const threadId = options?.threadId ?? createId('thread')\n\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n timestamp: Date.now(),\n }\n\n try {\n const result = await generator()\n\n yield {\n type: EventType.CUSTOM,\n name: 'generation:result',\n value: result as unknown,\n timestamp: Date.now(),\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n finishReason: 'stop',\n timestamp: Date.now(),\n }\n } catch (error: unknown) {\n const payload = toRunErrorPayload(error, 'Generation failed')\n // `code` is omitted entirely when undefined so the event matches the\n // AG-UI `code?: string` shape under `exactOptionalPropertyTypes`. The\n // deprecated nested `error` form preserves the same conditional\n // structure for backward compatibility.\n const codeFields =\n payload.code !== undefined ? { code: payload.code } : undefined\n yield {\n type: EventType.RUN_ERROR,\n message: payload.message,\n ...codeFields,\n // Deprecated nested form for backward compatibility\n error: {\n message: payload.message,\n ...codeFields,\n },\n timestamp: Date.now(),\n }\n }\n}\n"],"names":[],"mappings":";;AAUA,SAAS,SAAS,QAAwB;AACxC,SAAO,GAAG,MAAM,IAAI,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAYA,gBAAuB,uBACrB,WACA,SAC4B;AAC5B,QAAM,QAA0B,SAAS,KAAK;AAC9C,QAAM,WAAgC,SAAS,QAAQ;AAEvD,QAAM;AAAA,IACJ,MAAM,UAAU;AAAA,IAChB;AAAA,IACA;AAAA,IACA,WAAW,KAAK,IAAA;AAAA,EAAI;AAGtB,MAAI;AACF,UAAM,SAAS,MAAM,UAAA;AAErB,UAAM;AAAA,MACJ,MAAM,UAAU;AAAA,MAChB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,WAAW,KAAK,IAAA;AAAA,IAAI;AAGtB,UAAM;AAAA,MACJ,MAAM,UAAU;AAAA,MAChB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,WAAW,KAAK,IAAA;AAAA,IAAI;AAAA,EAExB,SAAS,OAAgB;AACvB,UAAM,UAAU,kBAAkB,OAAO,mBAAmB;AAK5D,UAAM,aACJ,QAAQ,SAAS,SAAY,EAAE,MAAM,QAAQ,SAAS;AACxD,UAAM;AAAA,MACJ,MAAM,UAAU;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,GAAG;AAAA;AAAA,MAEH,OAAO;AAAA,QACL,SAAS,QAAQ;AAAA,QACjB,GAAG;AAAA,MAAA;AAAA,MAEL,WAAW,KAAK,IAAA;AAAA,IAAI;AAAA,EAExB;AACF;"}
@@ -0,0 +1,74 @@
1
+ import { StreamChunk, SummarizationOptions, SummarizationResult } from '../../types.js';
2
+ /**
3
+ * Configuration for summarize adapter instances
4
+ */
5
+ export interface SummarizeAdapterConfig {
6
+ apiKey?: string;
7
+ baseUrl?: string;
8
+ timeout?: number;
9
+ maxRetries?: number;
10
+ headers?: Record<string, string>;
11
+ }
12
+ /**
13
+ * Summarize adapter interface with pre-resolved generics.
14
+ *
15
+ * An adapter is created by a provider function: `provider('model')` → `adapter`
16
+ * All type resolution happens at the provider call site, not in this interface.
17
+ *
18
+ * Generic parameters:
19
+ * - TModel: The specific model name (e.g., 'gpt-4o')
20
+ * - TProviderOptions: Provider-specific options (already resolved)
21
+ */
22
+ export interface SummarizeAdapter<TModel extends string = string, TProviderOptions extends object = Record<string, unknown>> {
23
+ /** Discriminator for adapter kind - used by generate() to determine API shape */
24
+ readonly kind: 'summarize';
25
+ /** Adapter name identifier */
26
+ readonly name: string;
27
+ /** The model this adapter is configured for */
28
+ readonly model: TModel;
29
+ /**
30
+ * @internal Type-only properties for inference. Not assigned at runtime.
31
+ */
32
+ '~types': {
33
+ providerOptions: TProviderOptions;
34
+ };
35
+ /**
36
+ * Summarize the given text
37
+ */
38
+ summarize: (options: SummarizationOptions<TProviderOptions>) => Promise<SummarizationResult>;
39
+ /**
40
+ * Stream summarization of the given text.
41
+ * Optional - if not implemented, the activity layer will fall back to
42
+ * non-streaming summarize and yield the result as a single chunk.
43
+ */
44
+ summarizeStream?: (options: SummarizationOptions<TProviderOptions>) => AsyncIterable<StreamChunk>;
45
+ }
46
+ /**
47
+ * A SummarizeAdapter with any/unknown type parameters.
48
+ * Useful as a constraint in generic functions and interfaces.
49
+ */
50
+ export type AnySummarizeAdapter = SummarizeAdapter<any, any>;
51
+ /**
52
+ * Abstract base class for summarize adapters.
53
+ * Extend this class to implement a summarize adapter for a specific provider.
54
+ *
55
+ * Generic parameters match SummarizeAdapter - all pre-resolved by the provider function.
56
+ */
57
+ export declare abstract class BaseSummarizeAdapter<TModel extends string = string, TProviderOptions extends object = Record<string, unknown>> implements SummarizeAdapter<TModel, TProviderOptions> {
58
+ readonly kind: "summarize";
59
+ abstract readonly name: string;
60
+ readonly model: TModel;
61
+ '~types': {
62
+ providerOptions: TProviderOptions;
63
+ };
64
+ protected config: SummarizeAdapterConfig;
65
+ constructor(config: SummarizeAdapterConfig | undefined, model: TModel);
66
+ abstract summarize(options: SummarizationOptions<TProviderOptions>): Promise<SummarizationResult>;
67
+ /**
68
+ * Stream summarization of the given text.
69
+ * Override this method in concrete implementations to enable streaming.
70
+ * If not overridden, the activity layer will fall back to non-streaming.
71
+ */
72
+ summarizeStream?(options: SummarizationOptions<TProviderOptions>): AsyncIterable<StreamChunk>;
73
+ protected generateId(): string;
74
+ }
@@ -0,0 +1,16 @@
1
+ class BaseSummarizeAdapter {
2
+ kind = "summarize";
3
+ model;
4
+ config;
5
+ constructor(config = {}, model) {
6
+ this.config = config;
7
+ this.model = model;
8
+ }
9
+ generateId() {
10
+ return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`;
11
+ }
12
+ }
13
+ export {
14
+ BaseSummarizeAdapter
15
+ };
16
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sources":["../../../../src/activities/summarize/adapter.ts"],"sourcesContent":["import type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '../../types'\n\n/**\n * Configuration for summarize adapter instances\n */\nexport interface SummarizeAdapterConfig {\n apiKey?: string\n baseUrl?: string\n timeout?: number\n maxRetries?: number\n headers?: Record<string, string>\n}\n\n/**\n * Summarize adapter interface with pre-resolved generics.\n *\n * An adapter is created by a provider function: `provider('model')` → `adapter`\n * All type resolution happens at the provider call site, not in this interface.\n *\n * Generic parameters:\n * - TModel: The specific model name (e.g., 'gpt-4o')\n * - TProviderOptions: Provider-specific options (already resolved)\n */\nexport interface SummarizeAdapter<\n TModel extends string = string,\n TProviderOptions extends object = Record<string, unknown>,\n> {\n /** Discriminator for adapter kind - used by generate() to determine API shape */\n readonly kind: 'summarize'\n /** Adapter name identifier */\n readonly name: string\n /** The model this adapter is configured for */\n readonly model: TModel\n\n /**\n * @internal Type-only properties for inference. Not assigned at runtime.\n */\n '~types': {\n providerOptions: TProviderOptions\n }\n\n /**\n * Summarize the given text\n */\n summarize: (\n options: SummarizationOptions<TProviderOptions>,\n ) => Promise<SummarizationResult>\n\n /**\n * Stream summarization of the given text.\n * Optional - if not implemented, the activity layer will fall back to\n * non-streaming summarize and yield the result as a single chunk.\n */\n summarizeStream?: (\n options: SummarizationOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n}\n\n/**\n * A SummarizeAdapter with any/unknown type parameters.\n * Useful as a constraint in generic functions and interfaces.\n */\nexport type AnySummarizeAdapter = SummarizeAdapter<any, any>\n\n/**\n * Abstract base class for summarize adapters.\n * Extend this class to implement a summarize adapter for a specific provider.\n *\n * Generic parameters match SummarizeAdapter - all pre-resolved by the provider function.\n */\nexport abstract class BaseSummarizeAdapter<\n TModel extends string = string,\n TProviderOptions extends object = Record<string, unknown>,\n> implements SummarizeAdapter<TModel, TProviderOptions> {\n readonly kind = 'summarize' as const\n abstract readonly name: string\n readonly model: TModel\n\n // Type-only property - never assigned at runtime\n declare '~types': {\n providerOptions: TProviderOptions\n }\n\n protected config: SummarizeAdapterConfig\n\n constructor(config: SummarizeAdapterConfig = {}, model: TModel) {\n this.config = config\n this.model = model\n }\n\n abstract summarize(\n options: SummarizationOptions<TProviderOptions>,\n ): Promise<SummarizationResult>\n\n /**\n * Stream summarization of the given text.\n * Override this method in concrete implementations to enable streaming.\n * If not overridden, the activity layer will fall back to non-streaming.\n */\n summarizeStream?(\n options: SummarizationOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk>\n\n protected generateId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n"],"names":[],"mappings":"AA0EO,MAAe,qBAGkC;AAAA,EAC7C,OAAO;AAAA,EAEP;AAAA,EAOC;AAAA,EAEV,YAAY,SAAiC,CAAA,GAAI,OAAe;AAC9D,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAAA,EAeU,aAAqB;AAC7B,WAAO,GAAG,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,EAC9E;AACF;"}
@@ -0,0 +1,45 @@
1
+ import { BaseSummarizeAdapter } from './adapter.js';
2
+ import { StreamChunk, SummarizationOptions, SummarizationResult, TextOptions } from '../../types.js';
3
+ /**
4
+ * Minimal contract for a text adapter that supports `chatStream`. Lets
5
+ * `ChatStreamSummarizeAdapter` work with any text adapter without coupling
6
+ * to a specific implementation.
7
+ *
8
+ * The provider-options shape is intentionally `any` here — the wrapper only
9
+ * forwards `modelOptions` straight through, so a text adapter with a richer
10
+ * per-model options type (e.g. `ResolveProviderOptions<TModel>`) is still
11
+ * acceptable. Summarize-level type safety is enforced via
12
+ * `SummarizationOptions<TProviderOptions>` on the wrapper itself.
13
+ */
14
+ export interface ChatStreamCapable {
15
+ chatStream: (options: TextOptions<any>) => AsyncIterable<StreamChunk>;
16
+ }
17
+ /**
18
+ * Extract the per-model `modelOptions` type a text adapter accepts. Used by
19
+ * provider summarize factories so their `modelOptions` IntelliSense matches
20
+ * what the underlying text adapter actually understands.
21
+ */
22
+ export type InferTextProviderOptions<TAdapter> = TAdapter extends {
23
+ '~types': {
24
+ providerOptions: infer P;
25
+ };
26
+ } ? P extends object ? P : object : object;
27
+ /**
28
+ * Summarize adapter that wraps any `ChatStreamCapable` text adapter and
29
+ * prompts it for summarization. Not tied to any wire format.
30
+ */
31
+ export declare class ChatStreamSummarizeAdapter<TModel extends string, TProviderOptions extends object = Record<string, unknown>> extends BaseSummarizeAdapter<TModel, TProviderOptions> {
32
+ readonly name: string;
33
+ private readonly textAdapter;
34
+ constructor(textAdapter: ChatStreamCapable, model: TModel, name?: string);
35
+ summarize(options: SummarizationOptions<TProviderOptions>): Promise<SummarizationResult>;
36
+ summarizeStream(options: SummarizationOptions<TProviderOptions>): AsyncIterable<StreamChunk>;
37
+ /**
38
+ * Build the TextOptions passed to the underlying chatStream. Provider
39
+ * `modelOptions` from the summarize call are forwarded as-is so knobs like
40
+ * Anthropic cache headers, Gemini safety settings, or Ollama tuning params
41
+ * still reach the wire layer.
42
+ */
43
+ protected buildTextOptions(options: SummarizationOptions<TProviderOptions>, systemPrompt: string): TextOptions<TProviderOptions>;
44
+ protected buildSummarizationPrompt(options: SummarizationOptions<TProviderOptions>): string;
45
+ }
@@ -0,0 +1,212 @@
1
+ import { EventType } from "@ag-ui/core";
2
+ import { toRunErrorPayload } from "../error-payload.js";
3
+ import { MAX_TOKENS_KEYS } from "../../utilities/sampling-keys.js";
4
+ 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"
12
+ };
13
+ const KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS;
14
+ function isKnownMaxTokensAdapter(adapterName) {
15
+ return adapterName === "ollama" || MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== void 0;
16
+ }
17
+ 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;
28
+ }
29
+ 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
+ }
208
+ }
209
+ export {
210
+ ChatStreamSummarizeAdapter
211
+ };
212
+ //# sourceMappingURL=chat-stream-summarize.js.map
@@ -0,0 +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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"}