@tanstack/ai 0.52.3 → 0.54.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 (74) hide show
  1. package/README.md +14 -13
  2. package/dist/esm/activities/chat/index.js +5 -3
  3. package/dist/esm/activities/chat/index.js.map +1 -1
  4. package/dist/esm/activities/generateLiveVideo/adapter.d.ts +69 -0
  5. package/dist/esm/activities/generateLiveVideo/adapter.js +23 -0
  6. package/dist/esm/activities/generateLiveVideo/adapter.js.map +1 -0
  7. package/dist/esm/activities/generateLiveVideo/index.d.ts +99 -0
  8. package/dist/esm/activities/generateLiveVideo/index.js +162 -0
  9. package/dist/esm/activities/generateLiveVideo/index.js.map +1 -0
  10. package/dist/esm/activities/generateVideo/index.js +3 -1
  11. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  12. package/dist/esm/activities/generateWorld/adapter.d.ts +69 -0
  13. package/dist/esm/activities/generateWorld/adapter.js +23 -0
  14. package/dist/esm/activities/generateWorld/adapter.js.map +1 -0
  15. package/dist/esm/activities/generateWorld/index.d.ts +99 -0
  16. package/dist/esm/activities/generateWorld/index.js +162 -0
  17. package/dist/esm/activities/generateWorld/index.js.map +1 -0
  18. package/dist/esm/activities/index.d.ts +8 -2
  19. package/dist/esm/activities/index.js +11 -7
  20. package/dist/esm/activities/middleware/types.d.ts +1 -1
  21. package/dist/esm/activities/summarize/chat-stream-summarize.js +2 -1
  22. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  23. package/dist/esm/byok/define-provider.d.ts +6 -0
  24. package/dist/esm/byok/define-provider.js +2 -1
  25. package/dist/esm/byok/define-provider.js.map +1 -1
  26. package/dist/esm/byok/get-key.d.ts +7 -0
  27. package/dist/esm/byok/get-key.js +8 -1
  28. package/dist/esm/byok/get-key.js.map +1 -1
  29. package/dist/esm/byok/server.d.ts +1 -1
  30. package/dist/esm/byok/server.js +2 -2
  31. package/dist/esm/client.d.ts +4 -2
  32. package/dist/esm/client.js +3 -1
  33. package/dist/esm/client.js.map +1 -1
  34. package/dist/esm/index.d.ts +4 -2
  35. package/dist/esm/index.js +3 -1
  36. package/dist/esm/middlewares/otel.js +3 -1
  37. package/dist/esm/middlewares/otel.js.map +1 -1
  38. package/dist/esm/types.d.ts +112 -0
  39. package/package.json +2 -2
  40. package/skills/ai-core/adapter-configuration/SKILL.md +103 -54
  41. package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +39 -21
  42. package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +5 -0
  43. package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +14 -6
  44. package/skills/ai-core/adapter-configuration/references/grok-adapter.md +33 -25
  45. package/skills/ai-core/adapter-configuration/references/groq-adapter.md +7 -2
  46. package/skills/ai-core/adapter-configuration/references/ollama-adapter.md +25 -12
  47. package/skills/ai-core/adapter-configuration/references/openai-adapter.md +19 -9
  48. package/skills/ai-core/adapter-configuration/references/openrouter-adapter.md +34 -21
  49. package/skills/ai-core/ag-ui-protocol/SKILL.md +16 -10
  50. package/skills/ai-core/chat-experience/SKILL.md +228 -108
  51. package/skills/ai-core/client-persistence/SKILL.md +21 -9
  52. package/skills/ai-core/custom-backend-integration/SKILL.md +86 -52
  53. package/skills/ai-core/debug-logging/SKILL.md +100 -18
  54. package/skills/ai-core/locks/SKILL.md +35 -7
  55. package/skills/ai-core/media-generation/SKILL.md +114 -49
  56. package/skills/ai-core/middleware/SKILL.md +174 -69
  57. package/skills/ai-core/structured-outputs/SKILL.md +99 -49
  58. package/skills/ai-core/tool-calling/SKILL.md +245 -158
  59. package/src/activities/chat/index.ts +6 -7
  60. package/src/activities/generateLiveVideo/adapter.ts +99 -0
  61. package/src/activities/generateLiveVideo/index.ts +339 -0
  62. package/src/activities/generateVideo/index.ts +3 -4
  63. package/src/activities/generateWorld/adapter.ts +96 -0
  64. package/src/activities/generateWorld/index.ts +339 -0
  65. package/src/activities/index.ts +44 -0
  66. package/src/activities/middleware/types.ts +2 -0
  67. package/src/activities/summarize/chat-stream-summarize.ts +2 -0
  68. package/src/byok/define-provider.ts +7 -0
  69. package/src/byok/get-key.ts +18 -0
  70. package/src/byok/server.ts +1 -1
  71. package/src/client.ts +8 -0
  72. package/src/index.ts +8 -0
  73. package/src/middlewares/otel.ts +2 -0
  74. package/src/types.ts +128 -0
@@ -0,0 +1,69 @@
1
+ import { WorldGenerationOptions, WorldGenerationResult } from '../../types.js';
2
+ /**
3
+ * Configuration for world generation adapter instances.
4
+ *
5
+ * @experimental World generation is an experimental feature and may change.
6
+ */
7
+ export interface WorldAdapterConfig {
8
+ apiKey?: string;
9
+ baseUrl?: string;
10
+ timeout?: number;
11
+ maxRetries?: number;
12
+ headers?: Record<string, string>;
13
+ }
14
+ /**
15
+ * World adapter interface with pre-resolved generics.
16
+ *
17
+ * An adapter is created by a provider function: `provider('model')` → `adapter`.
18
+ * All type resolution happens at the provider call site, not in this interface.
19
+ *
20
+ * Generic parameters:
21
+ * - TModel: The specific model name (e.g. 'visko-orbis-stable')
22
+ * - TProviderOptions: Provider-specific options (already resolved)
23
+ *
24
+ * @experimental World generation is an experimental feature and may change.
25
+ */
26
+ export interface WorldAdapter<TModel extends string = string, TProviderOptions extends object = Record<string, unknown>> {
27
+ /** Discriminator for adapter kind - used to determine API shape */
28
+ readonly kind: 'world';
29
+ /** Adapter name identifier */
30
+ readonly name: string;
31
+ /** The model this adapter is configured for */
32
+ readonly model: TModel;
33
+ /**
34
+ * @internal Type-only properties for inference. Not assigned at runtime.
35
+ */
36
+ '~types': {
37
+ providerOptions: TProviderOptions;
38
+ };
39
+ /**
40
+ * Open a world session from a prompt.
41
+ *
42
+ * Server adapters typically mint a short-lived token and return it with the
43
+ * prompt so a browser can connect, set the prompt, and start streaming.
44
+ */
45
+ createWorld: (options: WorldGenerationOptions<TProviderOptions>) => Promise<WorldGenerationResult>;
46
+ }
47
+ /**
48
+ * A WorldAdapter with any/unknown type parameters.
49
+ * Useful as a constraint in generic functions and interfaces.
50
+ */
51
+ export type AnyWorldAdapter = WorldAdapter<any, any>;
52
+ /**
53
+ * Abstract base class for world generation adapters.
54
+ * Extend this class to implement a world adapter for a specific provider.
55
+ *
56
+ * @experimental World generation is an experimental feature and may change.
57
+ */
58
+ export declare abstract class BaseWorldAdapter<TModel extends string = string, TProviderOptions extends object = Record<string, unknown>> implements WorldAdapter<TModel, TProviderOptions> {
59
+ readonly kind: "world";
60
+ abstract readonly name: string;
61
+ readonly model: TModel;
62
+ '~types': {
63
+ providerOptions: TProviderOptions;
64
+ };
65
+ protected config: WorldAdapterConfig;
66
+ constructor(model: TModel, config?: WorldAdapterConfig);
67
+ abstract createWorld(options: WorldGenerationOptions<TProviderOptions>): Promise<WorldGenerationResult>;
68
+ protected generateId(): string;
69
+ }
@@ -0,0 +1,23 @@
1
+ //#region src/activities/generateWorld/adapter.ts
2
+ /**
3
+ * Abstract base class for world generation adapters.
4
+ * Extend this class to implement a world adapter for a specific provider.
5
+ *
6
+ * @experimental World generation is an experimental feature and may change.
7
+ */
8
+ var BaseWorldAdapter = class {
9
+ kind = "world";
10
+ model;
11
+ config;
12
+ constructor(model, config = {}) {
13
+ this.config = config;
14
+ this.model = model;
15
+ }
16
+ generateId() {
17
+ return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`;
18
+ }
19
+ };
20
+ //#endregion
21
+ export { BaseWorldAdapter };
22
+
23
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","names":[],"sources":["../../../../src/activities/generateWorld/adapter.ts"],"sourcesContent":["import type { WorldGenerationOptions, WorldGenerationResult } from '../../types'\n\n/**\n * Configuration for world generation adapter instances.\n *\n * @experimental World generation is an experimental feature and may change.\n */\nexport interface WorldAdapterConfig {\n apiKey?: string\n baseUrl?: string\n timeout?: number\n maxRetries?: number\n headers?: Record<string, string>\n}\n\n/**\n * World 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. 'visko-orbis-stable')\n * - TProviderOptions: Provider-specific options (already resolved)\n *\n * @experimental World generation is an experimental feature and may change.\n */\nexport interface WorldAdapter<\n TModel extends string = string,\n TProviderOptions extends object = Record<string, unknown>,\n> {\n /** Discriminator for adapter kind - used to determine API shape */\n readonly kind: 'world'\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 * Open a world session from a prompt.\n *\n * Server adapters typically mint a short-lived token and return it with the\n * prompt so a browser can connect, set the prompt, and start streaming.\n */\n createWorld: (\n options: WorldGenerationOptions<TProviderOptions>,\n ) => Promise<WorldGenerationResult>\n}\n\n/**\n * A WorldAdapter with any/unknown type parameters.\n * Useful as a constraint in generic functions and interfaces.\n */\nexport type AnyWorldAdapter = WorldAdapter<any, any>\n\n/**\n * Abstract base class for world generation adapters.\n * Extend this class to implement a world adapter for a specific provider.\n *\n * @experimental World generation is an experimental feature and may change.\n */\nexport abstract class BaseWorldAdapter<\n TModel extends string = string,\n TProviderOptions extends object = Record<string, unknown>,\n> implements WorldAdapter<TModel, TProviderOptions> {\n readonly kind = 'world' 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: WorldAdapterConfig\n\n constructor(model: TModel, config: WorldAdapterConfig = {}) {\n this.config = config\n this.model = model\n }\n\n abstract createWorld(\n options: WorldGenerationOptions<TProviderOptions>,\n ): Promise<WorldGenerationResult>\n\n protected generateId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n"],"mappings":";;;;;;;AAoEA,IAAsB,mBAAtB,MAGoD;CAClD,OAAgB;CAEhB;CAOA;CAEA,YAAY,OAAe,SAA6B,CAAC,GAAG;EAC1D,KAAK,SAAS;EACd,KAAK,QAAQ;CACf;CAMA,aAA+B;EAC7B,OAAO,GAAG,KAAK,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,CAAC;CAC7E;AACF"}
@@ -0,0 +1,99 @@
1
+ import { DebugOption } from '../../logger/types.js';
2
+ import { GenerationMiddleware } from '../middleware/types.js';
3
+ import { WorldAdapter } from './adapter.js';
4
+ import { StreamChunk, WorldGenerationResult } from '../../types.js';
5
+ /** The adapter kind this activity handles */
6
+ export declare const kind: "world";
7
+ /**
8
+ * Extract provider options from a WorldAdapter via ~types.
9
+ */
10
+ export type WorldProviderOptions<TAdapter> = TAdapter extends {
11
+ '~types': {
12
+ providerOptions: infer P extends object;
13
+ };
14
+ } ? P : object;
15
+ /**
16
+ * Options for the world generation activity.
17
+ * The model is extracted from the adapter's model property.
18
+ *
19
+ * @template TAdapter - The world adapter type
20
+ * @template TStream - Whether to stream the output
21
+ *
22
+ * @experimental World generation is an experimental feature and may change.
23
+ */
24
+ export interface WorldActivityOptions<TAdapter extends WorldAdapter<string, WorldProviderOptions<TAdapter>>, TStream extends boolean = false> {
25
+ /** The world adapter to use (must be created with a model) */
26
+ adapter: TAdapter & {
27
+ kind: typeof kind;
28
+ };
29
+ /** Natural-language description of the world or scene */
30
+ prompt: string;
31
+ /** Provider-specific options for world generation */
32
+ modelOptions?: WorldProviderOptions<TAdapter>;
33
+ /**
34
+ * Whether to wrap the token result as StreamChunks for SSE transport.
35
+ * This is not the live video. When false or omitted, returns
36
+ * Promise<WorldGenerationResult>.
37
+ *
38
+ * @default false
39
+ */
40
+ stream?: TStream;
41
+ /**
42
+ * Enable debug logging. Pass `true` to enable all categories, `false` to
43
+ * silence everything including errors, or a `DebugConfig` object for granular
44
+ * control and/or a custom `Logger`.
45
+ */
46
+ debug?: DebugOption;
47
+ /**
48
+ * Observe-only middleware notified on start, usage, success, and error. Pass
49
+ * `otelMiddleware()` to emit OpenTelemetry spans, or implement the
50
+ * `GenerationMiddleware` contract for a custom backend.
51
+ */
52
+ middleware?: Array<GenerationMiddleware>;
53
+ /** Stable conversation/thread id for correlating this run when persisted. */
54
+ threadId?: string;
55
+ /** Stable run id for correlating this run when persisted. */
56
+ runId?: string;
57
+ /**
58
+ * Maximum duration of the token mint in milliseconds.
59
+ * No SDK-wide default. Composed with {@link abortSignal}; the first abort wins.
60
+ */
61
+ timeout?: number;
62
+ /**
63
+ * Caller cancellation signal (request disconnects, job/runtime cancellation).
64
+ * Composed with {@link timeout} into an effective signal forwarded to the
65
+ * adapter. Request-specific — not stored on global provider client config.
66
+ */
67
+ abortSignal?: AbortSignal;
68
+ }
69
+ /**
70
+ * Result type for the world generation activity.
71
+ * - If stream is true: AsyncIterable<StreamChunk>
72
+ * - Otherwise: Promise<WorldGenerationResult>
73
+ */
74
+ export type WorldActivityResult<TStream extends boolean = false> = TStream extends true ? AsyncIterable<StreamChunk> : Promise<WorldGenerationResult>;
75
+ /**
76
+ * World generation activity - opens a live, prompt-steerable world session.
77
+ *
78
+ * @example Mint a session token on the server
79
+ * ```ts
80
+ * import { generateWorld } from '@tanstack/ai'
81
+ * import { reactorWorld } from '@tanstack/ai-reactor'
82
+ *
83
+ * const world = await generateWorld({
84
+ * adapter: reactorWorld('visko-orbis-stable'),
85
+ * prompt: 'A neon cyberpunk city at night, slow aerial drift',
86
+ * })
87
+ *
88
+ * // Hand world.token, world.model, and world.prompt to the browser.
89
+ * ```
90
+ *
91
+ * @experimental World generation is an experimental feature and may change.
92
+ */
93
+ export declare function generateWorld<TAdapter extends WorldAdapter<string, WorldProviderOptions<TAdapter>>, TStream extends boolean = false>(options: WorldActivityOptions<TAdapter, TStream>): WorldActivityResult<TStream>;
94
+ /**
95
+ * Create typed options for the generateWorld() function without executing.
96
+ */
97
+ export declare function createWorldOptions<TAdapter extends WorldAdapter<string, WorldProviderOptions<TAdapter>>, TStream extends boolean = false>(options: WorldActivityOptions<TAdapter, TStream>): WorldActivityOptions<TAdapter, TStream>;
98
+ export type { WorldAdapter, WorldAdapterConfig, AnyWorldAdapter, } from './adapter.js';
99
+ export { BaseWorldAdapter } from './adapter.js';
@@ -0,0 +1,162 @@
1
+ import { resolveDebugOption } from "../../logger/resolve.js";
2
+ import { streamGenerationResult } from "../stream-generation-result.js";
3
+ import { applyGenerationResultTransforms, createGenerationContext, runGenerationAbort, runGenerationError, runGenerationFinish, runGenerationStart, runGenerationUsage } from "../middleware/run.js";
4
+ import { abortReasonMessage, createActivityAbortControls, isActivityAbortError, raceWithAbort } from "../../utilities/activity-abort.js";
5
+ import "./adapter.js";
6
+ import { aiEventClient } from "@tanstack/ai-event-client";
7
+ //#region src/activities/generateWorld/index.ts
8
+ /**
9
+ * World Activity (Experimental)
10
+ *
11
+ * Mints a session token for a live, prompt-steerable world. Unlike
12
+ * generateVideo (a job that finishes with a URL), the browser then connects
13
+ * with the token, sets the prompt, and streams until pause/reset/close.
14
+ *
15
+ * @experimental World generation is an experimental feature and may change.
16
+ */
17
+ /** The adapter kind this activity handles */
18
+ var kind = "world";
19
+ function createId(prefix) {
20
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
21
+ }
22
+ /**
23
+ * World generation activity - opens a live, prompt-steerable world session.
24
+ *
25
+ * @example Mint a session token on the server
26
+ * ```ts
27
+ * import { generateWorld } from '@tanstack/ai'
28
+ * import { reactorWorld } from '@tanstack/ai-reactor'
29
+ *
30
+ * const world = await generateWorld({
31
+ * adapter: reactorWorld('visko-orbis-stable'),
32
+ * prompt: 'A neon cyberpunk city at night, slow aerial drift',
33
+ * })
34
+ *
35
+ * // Hand world.token, world.model, and world.prompt to the browser.
36
+ * ```
37
+ *
38
+ * @experimental World generation is an experimental feature and may change.
39
+ */
40
+ function generateWorld(options) {
41
+ if (options.stream) return streamGenerationResult((resolved) => runGenerateWorld({
42
+ ...options,
43
+ runId: resolved.runId
44
+ }), options);
45
+ return runGenerateWorld(options);
46
+ }
47
+ /**
48
+ * Run the core world generation logic (non-streaming).
49
+ */
50
+ async function runGenerateWorld(options) {
51
+ const { adapter, stream: _stream, debug: _debug, middleware, threadId, runId, timeout, abortSignal: callerAbortSignal, ...rest } = options;
52
+ const model = adapter.model;
53
+ const requestId = createId("world");
54
+ const startTime = Date.now();
55
+ const logger = resolveDebugOption(options.debug);
56
+ const abortControls = createActivityAbortControls({
57
+ timeout,
58
+ abortSignal: callerAbortSignal
59
+ });
60
+ const providerName = adapter.provider ?? adapter.name ?? "unknown";
61
+ const mwCtx = createGenerationContext({
62
+ requestId,
63
+ activity: "world",
64
+ provider: adapter.name,
65
+ model,
66
+ modelOptions: rest.modelOptions,
67
+ threadId,
68
+ runId,
69
+ artifactInputs: { prompt: rest.prompt },
70
+ createId
71
+ });
72
+ await runGenerationStart(middleware, mwCtx);
73
+ aiEventClient.emit("world:request:started", {
74
+ requestId,
75
+ provider: adapter.name,
76
+ model,
77
+ prompt: rest.prompt,
78
+ timestamp: startTime,
79
+ ...rest.modelOptions !== void 0 && { modelOptions: rest.modelOptions }
80
+ });
81
+ logger.request(`activity=generateWorld provider=${providerName}`, {
82
+ provider: providerName,
83
+ model
84
+ });
85
+ try {
86
+ const rawResult = await raceWithAbort(adapter.createWorld({
87
+ ...rest,
88
+ model,
89
+ logger,
90
+ ...abortControls.signal ? { abortSignal: abortControls.signal } : {}
91
+ }), abortControls.signal);
92
+ abortControls.clear();
93
+ const result = await applyGenerationResultTransforms(mwCtx, rawResult);
94
+ const elapsedMs = Date.now() - startTime;
95
+ aiEventClient.emit("world:request:completed", {
96
+ requestId,
97
+ provider: adapter.name,
98
+ model: result.model,
99
+ prompt: result.prompt,
100
+ status: result.status,
101
+ duration: elapsedMs,
102
+ timestamp: Date.now(),
103
+ ...rest.modelOptions !== void 0 && { modelOptions: rest.modelOptions }
104
+ });
105
+ if (result.usage) aiEventClient.emit("world:usage", {
106
+ requestId,
107
+ model: result.model,
108
+ usage: result.usage,
109
+ timestamp: Date.now(),
110
+ ...rest.modelOptions !== void 0 && { modelOptions: rest.modelOptions }
111
+ });
112
+ logger.output(`activity=generateWorld provider=${providerName}`, {
113
+ model: result.model,
114
+ status: result.status
115
+ });
116
+ if (result.usage) await runGenerationUsage(middleware, mwCtx, result.usage);
117
+ await runGenerationFinish(middleware, mwCtx, {
118
+ duration: elapsedMs,
119
+ usage: result.usage
120
+ });
121
+ return result;
122
+ } catch (error) {
123
+ abortControls.clear();
124
+ const elapsedMs = Date.now() - startTime;
125
+ const err = error;
126
+ aiEventClient.emit("world:request:error", {
127
+ requestId,
128
+ provider: adapter.name,
129
+ model,
130
+ error: {
131
+ message: err.message,
132
+ name: err.name
133
+ },
134
+ duration: elapsedMs,
135
+ timestamp: Date.now(),
136
+ ...rest.modelOptions !== void 0 && { modelOptions: rest.modelOptions }
137
+ });
138
+ if (isActivityAbortError(error, abortControls.signal)) await runGenerationAbort(middleware, mwCtx, {
139
+ reason: abortReasonMessage(error, abortControls.signal),
140
+ duration: elapsedMs
141
+ });
142
+ else await runGenerationError(middleware, mwCtx, {
143
+ error,
144
+ duration: elapsedMs
145
+ });
146
+ logger.errors("generateWorld activity failed", {
147
+ error,
148
+ source: "generateWorld"
149
+ });
150
+ throw error;
151
+ }
152
+ }
153
+ /**
154
+ * Create typed options for the generateWorld() function without executing.
155
+ */
156
+ function createWorldOptions(options) {
157
+ return options;
158
+ }
159
+ //#endregion
160
+ export { createWorldOptions, generateWorld, kind };
161
+
162
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/activities/generateWorld/index.ts"],"sourcesContent":["/**\n * World Activity (Experimental)\n *\n * Mints a session token for a live, prompt-steerable world. Unlike\n * generateVideo (a job that finishes with a URL), the browser then connects\n * with the token, sets the prompt, and streams until pause/reset/close.\n *\n * @experimental World generation is an experimental feature and may change.\n */\n\nimport { aiEventClient } from '@tanstack/ai-event-client'\nimport { streamGenerationResult } from '../stream-generation-result.js'\nimport { resolveDebugOption } from '../../logger/resolve'\nimport {\n applyGenerationResultTransforms,\n createGenerationContext,\n runGenerationAbort,\n runGenerationError,\n runGenerationFinish,\n runGenerationStart,\n runGenerationUsage,\n} from '../middleware/run'\nimport {\n abortReasonMessage,\n createActivityAbortControls,\n isActivityAbortError,\n raceWithAbort,\n} from '../../utilities/activity-abort'\nimport type { InternalLogger } from '../../logger/internal-logger'\nimport type { DebugOption } from '../../logger/types'\nimport type { GenerationMiddleware } from '../middleware/types'\nimport type { WorldAdapter } from './adapter'\nimport type { StreamChunk, WorldGenerationResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'world' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/**\n * Extract provider options from a WorldAdapter via ~types.\n */\nexport type WorldProviderOptions<TAdapter> = TAdapter extends {\n '~types': { providerOptions: infer P extends object }\n}\n ? P\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the world generation activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The world adapter type\n * @template TStream - Whether to stream the output\n *\n * @experimental World generation is an experimental feature and may change.\n */\nexport interface WorldActivityOptions<\n TAdapter extends WorldAdapter<string, WorldProviderOptions<TAdapter>>,\n TStream extends boolean = false,\n> {\n /** The world adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** Natural-language description of the world or scene */\n prompt: string\n /** Provider-specific options for world generation */\n modelOptions?: WorldProviderOptions<TAdapter>\n /**\n * Whether to wrap the token result as StreamChunks for SSE transport.\n * This is not the live video. When false or omitted, returns\n * Promise<WorldGenerationResult>.\n *\n * @default false\n */\n stream?: TStream\n /**\n * Enable debug logging. Pass `true` to enable all categories, `false` to\n * silence everything including errors, or a `DebugConfig` object for granular\n * control and/or a custom `Logger`.\n */\n debug?: DebugOption\n /**\n * Observe-only middleware notified on start, usage, success, and error. Pass\n * `otelMiddleware()` to emit OpenTelemetry spans, or implement the\n * `GenerationMiddleware` contract for a custom backend.\n */\n middleware?: Array<GenerationMiddleware>\n /** Stable conversation/thread id for correlating this run when persisted. */\n threadId?: string\n /** Stable run id for correlating this run when persisted. */\n runId?: string\n /**\n * Maximum duration of the token mint in milliseconds.\n * No SDK-wide default. Composed with {@link abortSignal}; the first abort wins.\n */\n timeout?: number\n /**\n * Caller cancellation signal (request disconnects, job/runtime cancellation).\n * Composed with {@link timeout} into an effective signal forwarded to the\n * adapter. Request-specific — not stored on global provider client config.\n */\n abortSignal?: AbortSignal\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the world generation activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<WorldGenerationResult>\n */\nexport type WorldActivityResult<TStream extends boolean = false> =\n TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<WorldGenerationResult>\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * World generation activity - opens a live, prompt-steerable world session.\n *\n * @example Mint a session token on the server\n * ```ts\n * import { generateWorld } from '@tanstack/ai'\n * import { reactorWorld } from '@tanstack/ai-reactor'\n *\n * const world = await generateWorld({\n * adapter: reactorWorld('visko-orbis-stable'),\n * prompt: 'A neon cyberpunk city at night, slow aerial drift',\n * })\n *\n * // Hand world.token, world.model, and world.prompt to the browser.\n * ```\n *\n * @experimental World generation is an experimental feature and may change.\n */\nexport function generateWorld<\n TAdapter extends WorldAdapter<string, WorldProviderOptions<TAdapter>>,\n TStream extends boolean = false,\n>(\n options: WorldActivityOptions<TAdapter, TStream>,\n): WorldActivityResult<TStream> {\n if (options.stream) {\n return streamGenerationResult(\n (resolved) => runGenerateWorld({ ...options, runId: resolved.runId }),\n options,\n ) as WorldActivityResult<TStream>\n }\n return runGenerateWorld(options) as WorldActivityResult<TStream>\n}\n\n/**\n * Run the core world generation logic (non-streaming).\n */\nasync function runGenerateWorld<\n TAdapter extends WorldAdapter<string, WorldProviderOptions<TAdapter>>,\n>(\n options: WorldActivityOptions<TAdapter, boolean>,\n): Promise<WorldGenerationResult> {\n const {\n adapter,\n stream: _stream,\n debug: _debug,\n middleware,\n threadId,\n runId,\n timeout,\n abortSignal: callerAbortSignal,\n ...rest\n } = options\n const model = adapter.model\n const requestId = createId('world')\n const startTime = Date.now()\n const logger: InternalLogger = resolveDebugOption(options.debug)\n const abortControls = createActivityAbortControls({\n timeout,\n abortSignal: callerAbortSignal,\n })\n const providerName =\n (adapter as { name?: string; provider?: string }).provider ??\n (adapter as { name?: string }).name ??\n 'unknown'\n\n const mwCtx = createGenerationContext({\n requestId,\n activity: 'world',\n provider: adapter.name,\n model,\n modelOptions: rest.modelOptions,\n threadId,\n runId,\n artifactInputs: { prompt: rest.prompt },\n createId,\n })\n\n await runGenerationStart(middleware, mwCtx)\n\n aiEventClient.emit('world:request:started', {\n requestId,\n provider: adapter.name,\n model,\n prompt: rest.prompt,\n timestamp: startTime,\n ...(rest.modelOptions !== undefined && {\n modelOptions: rest.modelOptions as Record<string, unknown>,\n }),\n })\n\n logger.request(`activity=generateWorld provider=${providerName}`, {\n provider: providerName,\n model,\n })\n\n try {\n const rawResult = await raceWithAbort(\n adapter.createWorld({\n ...rest,\n model,\n logger,\n ...(abortControls.signal ? { abortSignal: abortControls.signal } : {}),\n }),\n abortControls.signal,\n )\n abortControls.clear()\n const result = await applyGenerationResultTransforms(mwCtx, rawResult)\n const elapsedMs = Date.now() - startTime\n\n aiEventClient.emit('world:request:completed', {\n requestId,\n provider: adapter.name,\n model: result.model,\n prompt: result.prompt,\n status: result.status,\n duration: elapsedMs,\n timestamp: Date.now(),\n ...(rest.modelOptions !== undefined && {\n modelOptions: rest.modelOptions as Record<string, unknown>,\n }),\n })\n\n if (result.usage) {\n aiEventClient.emit('world:usage', {\n requestId,\n model: result.model,\n usage: result.usage,\n timestamp: Date.now(),\n ...(rest.modelOptions !== undefined && {\n modelOptions: rest.modelOptions as Record<string, unknown>,\n }),\n })\n }\n\n logger.output(`activity=generateWorld provider=${providerName}`, {\n model: result.model,\n status: result.status,\n })\n\n if (result.usage) await runGenerationUsage(middleware, mwCtx, result.usage)\n await runGenerationFinish(middleware, mwCtx, {\n duration: elapsedMs,\n usage: result.usage,\n })\n\n return result\n } catch (error) {\n abortControls.clear()\n const elapsedMs = Date.now() - startTime\n const err = error as Error\n aiEventClient.emit('world:request:error', {\n requestId,\n provider: adapter.name,\n model,\n error: { message: err.message, name: err.name },\n duration: elapsedMs,\n timestamp: Date.now(),\n ...(rest.modelOptions !== undefined && {\n modelOptions: rest.modelOptions as Record<string, unknown>,\n }),\n })\n if (isActivityAbortError(error, abortControls.signal)) {\n await runGenerationAbort(middleware, mwCtx, {\n reason: abortReasonMessage(error, abortControls.signal),\n duration: elapsedMs,\n })\n } else {\n await runGenerationError(middleware, mwCtx, {\n error,\n duration: elapsedMs,\n })\n }\n logger.errors('generateWorld activity failed', {\n error,\n source: 'generateWorld',\n })\n throw error\n }\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the generateWorld() function without executing.\n */\nexport function createWorldOptions<\n TAdapter extends WorldAdapter<string, WorldProviderOptions<TAdapter>>,\n TStream extends boolean = false,\n>(\n options: WorldActivityOptions<TAdapter, TStream>,\n): WorldActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n WorldAdapter,\n WorldAdapterConfig,\n AnyWorldAdapter,\n} from './adapter'\nexport { BaseWorldAdapter } from './adapter'\n"],"mappings":"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"}
@@ -7,6 +7,8 @@ import { AnyTTSAdapter } from './generateSpeech/adapter.js';
7
7
  import { AnyTranscriptionAdapter } from './generateTranscription/adapter.js';
8
8
  import { AnyEmbeddingAdapter } from './embed/adapter.js';
9
9
  import { AnyRerankAdapter } from './rerank/adapter.js';
10
+ import { AnyWorldAdapter } from './generateWorld/adapter.js';
11
+ import { AnyLiveVideoAdapter } from './generateLiveVideo/adapter.js';
10
12
  export { kind as textKind, chat, type TextActivityOptions, type TextActivityResult, } from './chat/index.js';
11
13
  export { BaseTextAdapter, type AnyTextAdapter, type TextAdapter, type TextAdapterConfig, type StructuredOutputOptions, type StructuredOutputResult, } from './chat/adapter.js';
12
14
  export { kind as summarizeKind, summarize, type SummarizeActivityOptions, type SummarizeActivityResult, type SummarizeProviderOptions, } from './summarize/index.js';
@@ -27,7 +29,11 @@ export { kind as transcriptionKind, generateTranscription, type TranscriptionAct
27
29
  export { BaseTranscriptionAdapter, type TranscriptionAdapter, type TranscriptionAdapterConfig, type AnyTranscriptionAdapter, } from './generateTranscription/adapter.js';
28
30
  export { kind as embeddingKind, embed, type EmbedOptions, type EmbedProviderOptionsForModel, type EmbeddingInputForModel, } from './embed/index.js';
29
31
  export { BaseEmbeddingAdapter, type EmbeddingAdapter, type EmbeddingAdapterConfig, type AnyEmbeddingAdapter, } from './embed/adapter.js';
32
+ export { kind as worldKind, generateWorld, type WorldActivityOptions, type WorldActivityResult, type WorldProviderOptions, } from './generateWorld/index.js';
33
+ export { BaseWorldAdapter, type WorldAdapter, type WorldAdapterConfig, type AnyWorldAdapter, } from './generateWorld/adapter.js';
34
+ export { kind as liveVideoKind, generateLiveVideo, type LiveVideoActivityOptions, type LiveVideoActivityResult, type LiveVideoProviderOptions, } from './generateLiveVideo/index.js';
35
+ export { BaseLiveVideoAdapter, type LiveVideoAdapter, type LiveVideoAdapterConfig, type AnyLiveVideoAdapter, } from './generateLiveVideo/adapter.js';
30
36
  /** Union of all adapter types that can be passed to chat() */
31
- export type AIAdapter = AnyTextAdapter | AnySummarizeAdapter | AnyImageAdapter | AnyAudioAdapter | AnyVideoAdapter | AnyTTSAdapter | AnyTranscriptionAdapter | AnyEmbeddingAdapter | AnyRerankAdapter;
37
+ export type AIAdapter = AnyTextAdapter | AnySummarizeAdapter | AnyImageAdapter | AnyAudioAdapter | AnyVideoAdapter | AnyTTSAdapter | AnyTranscriptionAdapter | AnyEmbeddingAdapter | AnyRerankAdapter | AnyWorldAdapter | AnyLiveVideoAdapter;
32
38
  /** Union type of all adapter kinds */
33
- export type AdapterKind = 'text' | 'summarize' | 'image' | 'audio' | 'video' | 'tts' | 'transcription' | 'embedding' | 'rerank';
39
+ export type AdapterKind = 'text' | 'summarize' | 'image' | 'audio' | 'video' | 'tts' | 'transcription' | 'embedding' | 'rerank' | 'world' | 'liveVideo';
@@ -1,21 +1,25 @@
1
1
  import { BaseTextAdapter } from "./chat/adapter.js";
2
- import { chat, kind as kind$5 } from "./chat/index.js";
2
+ import { chat, kind as kind$6 } from "./chat/index.js";
3
3
  import { BaseSummarizeAdapter } from "./summarize/adapter.js";
4
4
  import { ChatStreamSummarizeAdapter } from "./summarize/chat-stream-summarize.js";
5
- import { kind as kind$4, summarize } from "./summarize/index.js";
5
+ import { kind as kind$5, summarize } from "./summarize/index.js";
6
6
  import { BaseRerankAdapter } from "./rerank/adapter.js";
7
- import { createRerankOptions, kind as kind$3, rerank } from "./rerank/index.js";
7
+ import { createRerankOptions, kind as kind$4, rerank } from "./rerank/index.js";
8
8
  import { BaseImageAdapter } from "./generateImage/adapter.js";
9
9
  import { generateImage, kind as kind$2 } from "./generateImage/index.js";
10
10
  import { BaseAudioAdapter } from "./generateAudio/adapter.js";
11
11
  import { generateAudio, kind } from "./generateAudio/index.js";
12
12
  import { BaseVideoAdapter } from "./generateVideo/adapter.js";
13
- import { generateVideo, getVideoJobStatus, kind as kind$8 } from "./generateVideo/index.js";
13
+ import { generateVideo, getVideoJobStatus, kind as kind$9 } from "./generateVideo/index.js";
14
14
  import { snapToDurationOption } from "./generateVideo/snap.js";
15
15
  import { BaseTTSAdapter } from "./generateSpeech/adapter.js";
16
- import { generateSpeech, kind as kind$7 } from "./generateSpeech/index.js";
16
+ import { generateSpeech, kind as kind$8 } from "./generateSpeech/index.js";
17
17
  import { BaseTranscriptionAdapter } from "./generateTranscription/adapter.js";
18
- import { generateTranscription, kind as kind$6 } from "./generateTranscription/index.js";
18
+ import { generateTranscription, kind as kind$7 } from "./generateTranscription/index.js";
19
19
  import { BaseEmbeddingAdapter } from "./embed/adapter.js";
20
20
  import { embed, kind as kind$1 } from "./embed/index.js";
21
- export { BaseAudioAdapter, BaseEmbeddingAdapter, BaseImageAdapter, BaseRerankAdapter, BaseSummarizeAdapter, BaseTTSAdapter, BaseTextAdapter, BaseTranscriptionAdapter, BaseVideoAdapter, ChatStreamSummarizeAdapter, kind as audioKind, chat, createRerankOptions, embed, kind$1 as embeddingKind, generateAudio, generateImage, generateSpeech, generateTranscription, generateVideo, getVideoJobStatus, kind$2 as imageKind, rerank, kind$3 as rerankKind, snapToDurationOption, summarize, kind$4 as summarizeKind, kind$5 as textKind, kind$6 as transcriptionKind, kind$7 as ttsKind, kind$8 as videoKind };
21
+ import { BaseWorldAdapter } from "./generateWorld/adapter.js";
22
+ import { generateWorld, kind as kind$10 } from "./generateWorld/index.js";
23
+ import { BaseLiveVideoAdapter } from "./generateLiveVideo/adapter.js";
24
+ import { generateLiveVideo, kind as kind$3 } from "./generateLiveVideo/index.js";
25
+ export { BaseAudioAdapter, BaseEmbeddingAdapter, BaseImageAdapter, BaseLiveVideoAdapter, BaseRerankAdapter, BaseSummarizeAdapter, BaseTTSAdapter, BaseTextAdapter, BaseTranscriptionAdapter, BaseVideoAdapter, BaseWorldAdapter, ChatStreamSummarizeAdapter, kind as audioKind, chat, createRerankOptions, embed, kind$1 as embeddingKind, generateAudio, generateImage, generateLiveVideo, generateSpeech, generateTranscription, generateVideo, generateWorld, getVideoJobStatus, kind$2 as imageKind, kind$3 as liveVideoKind, rerank, kind$4 as rerankKind, snapToDurationOption, summarize, kind$5 as summarizeKind, kind$6 as textKind, kind$7 as transcriptionKind, kind$8 as ttsKind, kind$9 as videoKind, kind$10 as worldKind };
@@ -11,7 +11,7 @@ import { TokenUsage } from '../../types.js';
11
11
  * `'summarize'` produces text, not media, so it has no artifacts — a
12
12
  * persistence middleware stores its run record and result and nothing else.
13
13
  */
14
- export type GenerationActivity = 'chat' | 'image' | 'video' | 'audio' | 'tts' | 'transcription' | 'embedding' | 'rerank' | 'summarize';
14
+ export type GenerationActivity = 'chat' | 'image' | 'video' | 'audio' | 'tts' | 'transcription' | 'embedding' | 'rerank' | 'summarize' | 'world' | 'liveVideo';
15
15
  /**
16
16
  * Stable context passed to every {@link GenerationMiddleware} hook. Created
17
17
  * once per activity call and shared across the hooks of that call.
@@ -48,7 +48,8 @@ var MAX_TOKENS_KEY_BY_ADAPTER = {
48
48
  groq: "max_completion_tokens",
49
49
  gemini: "maxOutputTokens",
50
50
  openrouter: "maxCompletionTokens",
51
- llmgateway: "max_tokens"
51
+ llmgateway: "max_tokens",
52
+ cloudflare: "max_tokens"
52
53
  };
53
54
  /**
54
55
  * Every flat key any supported provider uses to cap output tokens (plus the
@@ -1 +1 @@
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 { rebuildTokenUsage } from '../../utilities/ag-ui-usage'\nimport type { AdapterYieldChunk } from '../../utilities/adapter-yield-chunk'\nimport { tanstackMetadata } from '../../utilities/merge-metadata'\nimport { normalizeStreamChunk } from '../../utilities/normalize-stream-chunk'\nimport { BaseSummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n TextOptions,\n TokenUsage,\n} from '../../types'\n\nfunction consumeSpecSummarizeChunk(\n chunk: StreamChunk,\n state: { summary: string; model: string; usage: TokenUsage },\n): void {\n if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {\n if (chunk.delta) state.summary += chunk.delta\n return\n }\n\n const tanstack = tanstackMetadata(chunk)\n if (\n (chunk.type === EventType.RUN_STARTED ||\n chunk.type === EventType.RUN_FINISHED ||\n chunk.type === EventType.TEXT_MESSAGE_START) &&\n typeof tanstack?.model === 'string'\n ) {\n state.model = tanstack.model\n }\n\n if (chunk.type === EventType.RUN_FINISHED) {\n const rebuilt = rebuildTokenUsage(chunk.usage, tanstack?.usage)\n if (rebuilt) state.usage = rebuilt\n }\n}\n\nfunction throwRunError(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n): never {\n const message =\n typeof chunk.message === 'string' && chunk.message.length > 0\n ? chunk.message\n : 'Summarization failed'\n const err = new Error(message)\n if (typeof chunk.code === 'string') {\n ;(err as Error & { code?: string }).code = chunk.code\n }\n throw err\n}\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<AdapterYieldChunk>\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 * - LLM Gateway: `max_tokens`\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 // LLM Gateway exposes an OpenAI-compatible Chat Completions surface whose\n // only output cap is `max_tokens` — it does not read `max_completion_tokens`.\n llmgateway: 'max_tokens',\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 const id = this.generateId()\n const state = {\n summary: '',\n model: options.model,\n usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n }\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 raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\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 === EventType.RUN_ERROR) throwRunError(chunk)\n consumeSpecSummarizeChunk(chunk, state)\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 {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n }\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 const state = {\n summary: '',\n model: options.model,\n usage: {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n } satisfies SummarizationResult['usage'],\n }\n\n try {\n for await (const raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\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 consumeSpecSummarizeChunk(chunk, state)\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 === EventType.RUN_FINISHED) {\n yield {\n type: EventType.CUSTOM,\n name: 'generation:result',\n value: {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n } satisfies SummarizationResult,\n timestamp: Date.now(),\n }\n }\n\n yield chunk\n }\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
+ {"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 { rebuildTokenUsage } from '../../utilities/ag-ui-usage'\nimport type { AdapterYieldChunk } from '../../utilities/adapter-yield-chunk'\nimport { tanstackMetadata } from '../../utilities/merge-metadata'\nimport { normalizeStreamChunk } from '../../utilities/normalize-stream-chunk'\nimport { BaseSummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n TextOptions,\n TokenUsage,\n} from '../../types'\n\nfunction consumeSpecSummarizeChunk(\n chunk: StreamChunk,\n state: { summary: string; model: string; usage: TokenUsage },\n): void {\n if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {\n if (chunk.delta) state.summary += chunk.delta\n return\n }\n\n const tanstack = tanstackMetadata(chunk)\n if (\n (chunk.type === EventType.RUN_STARTED ||\n chunk.type === EventType.RUN_FINISHED ||\n chunk.type === EventType.TEXT_MESSAGE_START) &&\n typeof tanstack?.model === 'string'\n ) {\n state.model = tanstack.model\n }\n\n if (chunk.type === EventType.RUN_FINISHED) {\n const rebuilt = rebuildTokenUsage(chunk.usage, tanstack?.usage)\n if (rebuilt) state.usage = rebuilt\n }\n}\n\nfunction throwRunError(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n): never {\n const message =\n typeof chunk.message === 'string' && chunk.message.length > 0\n ? chunk.message\n : 'Summarization failed'\n const err = new Error(message)\n if (typeof chunk.code === 'string') {\n ;(err as Error & { code?: string }).code = chunk.code\n }\n throw err\n}\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<AdapterYieldChunk>\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 * - LLM Gateway: `max_tokens`\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 // LLM Gateway exposes an OpenAI-compatible Chat Completions surface whose\n // only output cap is `max_tokens` — it does not read `max_completion_tokens`.\n llmgateway: 'max_tokens',\n // Workers AI's OpenAI-compatible Chat Completions surface reads `max_tokens`.\n cloudflare: 'max_tokens',\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 const id = this.generateId()\n const state = {\n summary: '',\n model: options.model,\n usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n }\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 raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\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 === EventType.RUN_ERROR) throwRunError(chunk)\n consumeSpecSummarizeChunk(chunk, state)\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 {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n }\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 const state = {\n summary: '',\n model: options.model,\n usage: {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n } satisfies SummarizationResult['usage'],\n }\n\n try {\n for await (const raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\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 consumeSpecSummarizeChunk(chunk, state)\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 === EventType.RUN_FINISHED) {\n yield {\n type: EventType.CUSTOM,\n name: 'generation:result',\n value: {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n } satisfies SummarizationResult,\n timestamp: Date.now(),\n }\n }\n\n yield chunk\n }\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":";;;;;;;;AAgBA,SAAS,0BACP,OACA,OACM;CACN,IAAI,MAAM,SAAS,UAAU,sBAAsB;EACjD,IAAI,MAAM,OAAO,MAAM,WAAW,MAAM;EACxC;CACF;CAEA,MAAM,WAAW,iBAAiB,KAAK;CACvC,KACG,MAAM,SAAS,UAAU,eACxB,MAAM,SAAS,UAAU,gBACzB,MAAM,SAAS,UAAU,uBAC3B,OAAO,UAAU,UAAU,UAE3B,MAAM,QAAQ,SAAS;CAGzB,IAAI,MAAM,SAAS,UAAU,cAAc;EACzC,MAAM,UAAU,kBAAkB,MAAM,OAAO,UAAU,KAAK;EAC9D,IAAI,SAAS,MAAM,QAAQ;CAC7B;AACF;AAEA,SAAS,cACP,OACO;CACP,MAAM,UACJ,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,SAAS,IACxD,MAAM,UACN;CACN,MAAM,MAAM,IAAI,MAAM,OAAO;CAC7B,IAAI,OAAO,MAAM,SAAS,UACvB,IAAmC,OAAO,MAAM;CAEnD,MAAM;AACR;;;;;;;;;;;;;;;;;;AAkCA,IAAM,4BAAoD;CACxD,QAAQ;CACR,WAAW;CACX,MAAM;CACN,MAAM;CACN,QAAQ;CACR,YAAY;CAGZ,YAAY;CAEZ,YAAY;AACd;;;;;;;;AASA,IAAM,wBAAwB;;;;;;;AAQ9B,SAAS,wBAAwB,aAA8B;CAC7D,OACE,gBAAgB,YAChB,0BAA0B,iBAAiB,KAAA;AAE/C;;;;;;;AAQA,SAAS,wBACP,aACA,aACA,cACyB;CACzB,MAAM,SAAkC,EAAE,GAAG,aAAa;CAE1D,IAAI,gBAAgB,UAAU;EAC5B,MAAM,WACJ,OAAO,WAAW,OAAO,OAAO,YAAY,WACvC,OAAO,UACR,KAAA;EACN,IAAI,YAAY,iBAAiB,UAAU,OAAO;EAClD,OAAO,UAAU;GAAE;GAAa,GAAG;EAAS;EAC5C,OAAO;CACT;CAEA,IAAI,iBAAiB,QAAQ,OAAO;CACpC,OAAO,cAAc;CACrB,OAAO;AACT;;;;;;;;;;;;;;;;;;;;AAqBA,SAAS,eACP,aACA,WACA,cACyB;CACzB,MAAM,SAAkC,EAAE,GAAG,aAAa;CAE1D,IAAI,gBAAgB,UAAU;EAG5B,MAAM,qBAAqB,sBAAsB,MAC9C,MAAM,OAAO,OAAO,OAAO,QAC9B;EACA,MAAM,WACJ,OAAO,WAAW,OAAO,OAAO,YAAY,WACvC,OAAO,UACR,KAAA;EACN,IACE,sBACC,YAAY,OAAO,SAAS,gBAAgB,UAE7C,OAAO;EAET,OAAO,UAAU;GAAE,aAAa;GAAW,GAAG;EAAS;EACvD,OAAO;CACT;CAEA,MAAM,MAAM,0BAA0B;CACtC,IAAI,QAAQ,KAAA,GAAW,OAAO;CAK9B,IAHuB,sBAAsB,MAC1C,MAAM,OAAO,OAAO,OAAO,QAE1B,GAAgB,OAAO;CAE3B,OAAO,OAAO;CACd,OAAO;AACT;;;;;AAmBA,IAAa,6BAAb,cAGU,qBAA+C;CACvD;CAEA;CAEA,YACE,aACA,OACA,OAAe,yBACf;EACA,MAAM,CAAC,GAAG,KAAK;EACf,KAAK,OAAO;EACZ,KAAK,cAAc;CACrB;CAEA,MAAM,UACJ,SAC8B;EAC9B,MAAM,eAAe,KAAK,yBAAyB,OAAO;EAE1D,MAAM,KAAK,KAAK,WAAW;EAC3B,MAAM,QAAQ;GACZ,SAAS;GACT,OAAO,QAAQ;GACf,OAAO;IAAE,cAAc;IAAG,kBAAkB;IAAG,aAAa;GAAE;EAChE;EAEA,QAAQ,OAAO,QACb,+BAA+B,KAAK,KAAK,SAAS,QAAQ,MAAM,eAAe,QAAQ,KAAK,OAAO,aAAa,QAAQ,aAAa,WACrI;GAAE,UAAU,KAAK;GAAM,OAAO,QAAQ;EAAM,CAC9C;EAEA,IAAI;GACF,WAAW,MAAM,OAAO,KAAK,YAAY,WACvC,KAAK,iBAAiB,SAAS,YAAY,CAC7C,GACE,KAAK,MAAM,SAAS,qBAAqB,GAAwB,GAAG;IAIlE,IAAI,MAAM,SAAS,UAAU,WAAW,cAAc,KAAK;IAC3D,0BAA0B,OAAO,KAAK;GACxC;EAEJ,SAAS,OAAgB;GAGvB,QAAQ,OAAO,OAAO,GAAG,KAAK,KAAK,mBAAmB;IACpD,OAAO,kBAAkB,OAAO,GAAG,KAAK,KAAK,kBAAkB;IAC/D,QAAQ,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,MAAM;EACR;EAEA,OAAO;GACL;GACA,OAAO,MAAM;GACb,SAAS,MAAM;GACf,OAAO,MAAM;EACf;CACF;CAEA,OAAgB,gBACd,SAC4B;EAC5B,MAAM,eAAe,KAAK,yBAAyB,OAAO;EAE1D,QAAQ,OAAO,QACb,qCAAqC,KAAK,KAAK,SAAS,QAAQ,MAAM,eAAe,QAAQ,KAAK,OAAO,aAAa,QAAQ,aAAa,WAC3I;GAAE,UAAU,KAAK;GAAM,OAAO,QAAQ;EAAM,CAC9C;EAEA,MAAM,KAAK,KAAK,WAAW;EAC3B,MAAM,QAAQ;GACZ,SAAS;GACT,OAAO,QAAQ;GACf,OAAO;IACL,cAAc;IACd,kBAAkB;IAClB,aAAa;GACf;EACF;EAEA,IAAI;GACF,WAAW,MAAM,OAAO,KAAK,YAAY,WACvC,KAAK,iBAAiB,SAAS,YAAY,CAC7C,GACE,KAAK,MAAM,SAAS,qBAAqB,GAAwB,GAAG;IAIlE,0BAA0B,OAAO,KAAK;IAKtC,IAAI,MAAM,SAAS,UAAU,cAC3B,MAAM;KACJ,MAAM,UAAU;KAChB,MAAM;KACN,OAAO;MACL;MACA,OAAO,MAAM;MACb,SAAS,MAAM;MACf,OAAO,MAAM;KACf;KACA,WAAW,KAAK,IAAI;IACtB;IAGF,MAAM;GACR;EAEJ,SAAS,OAAgB;GACvB,QAAQ,OAAO,OAAO,GAAG,KAAK,KAAK,yBAAyB;IAC1D,OAAO,kBAAkB,OAAO,GAAG,KAAK,KAAK,wBAAwB;IACrE,QAAQ,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,MAAM;EACR;CACF;;;;;;;CAQA,iBACE,SACA,cAC+B;EAQ/B,IAAI,UAAmC,EACrC,GAAI,QAAQ,aACd;EACA,UAAU,wBAAwB,KAAK,MAAM,IAAK,OAAO;EAMzD,IAAI,QAAQ,cAAc,KAAA,GAAW;GACnC,IAAI,CAAC,wBAAwB,KAAK,IAAI,GACpC,QAAQ,OAAO,KACb,wBAAwB,QAAQ,UAAU,iEAAiE,KAAK,KAAK,oLACrH,EAAE,UAAU,KAAK,KAAK,CACxB;GAEF,UAAU,eAAe,KAAK,MAAM,QAAQ,WAAW,OAAO;EAChE;EACA,MAAM,eAAe;EAErB,OAAO;GACL,OAAO,QAAQ;GACf,UAAU,CAAC;IAAE,MAAM;IAAQ,SAAS,QAAQ;GAAK,CAAC;GAClD,eAAe,CAAC,YAAY;GAC5B;GACA,QAAQ,QAAQ;GAIhB,GAAI,QAAQ,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;GAC9D,GAAI,QAAQ,aAAa,KAAA,IAAY,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;EACzE;CACF;CAEA,yBACE,SACQ;EACR,IAAI,SAAS;EAEb,QAAQ,QAAQ,OAAhB;GACE,KAAK;IACH,UAAU;IACV;GACF,KAAK;IACH,UAAU;IACV;GACF,KAAK;IACH,UAAU;IACV;GACF,KAAK,KAAA;IACH,UAAU;IACV;GACF,SACE,UAAU;EACd;EAEA,IAAI,QAAQ,SAAS,QAAQ,MAAM,SAAS,GAC1C,UAAU,mCAAmC,QAAQ,MAAM,KAAK,IAAI,EAAE;EAGxE,IAAI,QAAQ,WACV,UAAU,0BAA0B,QAAQ,UAAU;EAGxD,OAAO;CACT;AACF"}
@@ -10,6 +10,11 @@ export interface ByokProvider<TId extends string = string> {
10
10
  * values here. This object is imported on the client.
11
11
  */
12
12
  readonly env?: ReadonlyArray<string>;
13
+ /**
14
+ * Other descriptors this credential needs. A send for this provider also
15
+ * carries their headers and prompts for each one that is missing.
16
+ */
17
+ readonly with?: ReadonlyArray<ByokProvider>;
13
18
  }
14
19
  /**
15
20
  * Input for {@link defineByokProvider}. `id` cannot be optional: if `TId`
@@ -19,5 +24,6 @@ export type ByokProviderInit<TId extends string> = {
19
24
  readonly id: undefined extends TId ? never : TId;
20
25
  readonly label: string;
21
26
  readonly env?: string | ReadonlyArray<string>;
27
+ readonly with?: ReadonlyArray<ByokProvider>;
22
28
  };
23
29
  export declare function defineByokProvider<const TId extends string>(provider: ByokProviderInit<TId>): ByokProvider<TId>;
@@ -10,7 +10,8 @@ function defineByokProvider(provider) {
10
10
  return {
11
11
  id: provider.id,
12
12
  label: provider.label,
13
- ...env ? { env } : {}
13
+ ...env ? { env } : {},
14
+ ...provider.with ? { with: provider.with } : {}
14
15
  };
15
16
  }
16
17
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"define-provider.js","names":[],"sources":["../../../src/byok/define-provider.ts"],"sourcesContent":["import { isProviderId } from './providers'\n\n/**\n * A BYOK provider declared by an adapter. `id` is the `x-byok-<id>` slug and\n * is required — `{ id?: string }` is not a {@link ByokProvider}.\n */\nexport interface ByokProvider<TId extends string = string> {\n readonly id: TId\n readonly label: string\n /**\n * Env var names the relay may read. Names only — never put `process.env`\n * values here. This object is imported on the client.\n */\n readonly env?: ReadonlyArray<string>\n}\n\n/**\n * Input for {@link defineByokProvider}. `id` cannot be optional: if `TId`\n * includes `undefined`, `id` becomes `never` and the object is unassignable.\n */\nexport type ByokProviderInit<TId extends string> = {\n readonly id: undefined extends TId ? never : TId\n readonly label: string\n readonly env?: string | ReadonlyArray<string>\n}\n\nfunction normalizeEnv(\n env: string | ReadonlyArray<string> | undefined,\n): ReadonlyArray<string> | undefined {\n if (env === undefined) return undefined\n return typeof env === 'string' ? [env] : env\n}\n\nexport function defineByokProvider<const TId extends string>(\n provider: ByokProviderInit<TId>,\n): ByokProvider<TId> {\n if (!isProviderId(provider.id)) {\n throw new Error(`Invalid BYOK provider id: ${String(provider.id)}`)\n }\n const env = normalizeEnv(provider.env)\n return {\n id: provider.id,\n label: provider.label,\n ...(env ? { env } : {}),\n }\n}\n"],"mappings":";;AA0BA,SAAS,aACP,KACmC;CACnC,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;CAC9B,OAAO,OAAO,QAAQ,WAAW,CAAC,GAAG,IAAI;AAC3C;AAEA,SAAgB,mBACd,UACmB;CACnB,IAAI,CAAC,aAAa,SAAS,EAAE,GAC3B,MAAM,IAAI,MAAM,6BAA6B,OAAO,SAAS,EAAE,GAAG;CAEpE,MAAM,MAAM,aAAa,SAAS,GAAG;CACrC,OAAO;EACL,IAAI,SAAS;EACb,OAAO,SAAS;EAChB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;CACvB;AACF"}
1
+ {"version":3,"file":"define-provider.js","names":[],"sources":["../../../src/byok/define-provider.ts"],"sourcesContent":["import { isProviderId } from './providers'\n\n/**\n * A BYOK provider declared by an adapter. `id` is the `x-byok-<id>` slug and\n * is required — `{ id?: string }` is not a {@link ByokProvider}.\n */\nexport interface ByokProvider<TId extends string = string> {\n readonly id: TId\n readonly label: string\n /**\n * Env var names the relay may read. Names only — never put `process.env`\n * values here. This object is imported on the client.\n */\n readonly env?: ReadonlyArray<string>\n /**\n * Other descriptors this credential needs. A send for this provider also\n * carries their headers and prompts for each one that is missing.\n */\n readonly with?: ReadonlyArray<ByokProvider>\n}\n\n/**\n * Input for {@link defineByokProvider}. `id` cannot be optional: if `TId`\n * includes `undefined`, `id` becomes `never` and the object is unassignable.\n */\nexport type ByokProviderInit<TId extends string> = {\n readonly id: undefined extends TId ? never : TId\n readonly label: string\n readonly env?: string | ReadonlyArray<string>\n readonly with?: ReadonlyArray<ByokProvider>\n}\n\nfunction normalizeEnv(\n env: string | ReadonlyArray<string> | undefined,\n): ReadonlyArray<string> | undefined {\n if (env === undefined) return undefined\n return typeof env === 'string' ? [env] : env\n}\n\nexport function defineByokProvider<const TId extends string>(\n provider: ByokProviderInit<TId>,\n): ByokProvider<TId> {\n if (!isProviderId(provider.id)) {\n throw new Error(`Invalid BYOK provider id: ${String(provider.id)}`)\n }\n const env = normalizeEnv(provider.env)\n return {\n id: provider.id,\n label: provider.label,\n ...(env ? { env } : {}),\n ...(provider.with ? { with: provider.with } : {}),\n }\n}\n"],"mappings":";;AAgCA,SAAS,aACP,KACmC;CACnC,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;CAC9B,OAAO,OAAO,QAAQ,WAAW,CAAC,GAAG,IAAI;AAC3C;AAEA,SAAgB,mBACd,UACmB;CACnB,IAAI,CAAC,aAAa,SAAS,EAAE,GAC3B,MAAM,IAAI,MAAM,6BAA6B,OAAO,SAAS,EAAE,GAAG;CAEpE,MAAM,MAAM,aAAa,SAAS,GAAG;CACrC,OAAO;EACL,IAAI,SAAS;EACb,OAAO,SAAS;EAChB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;EACrB,GAAI,SAAS,OAAO,EAAE,MAAM,SAAS,KAAK,IAAI,CAAC;CACjD;AACF"}