@redtuma/core 0.1.0 → 0.2.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.
@@ -1,4 +1,5 @@
1
1
  import { DynamicArgument, Logger, ModelConfig, RuntimeContext } from "../types.js";
2
+ import { ModelRouter } from "../llm/index.js";
2
3
  import { AnyToolAction } from "../tools/index.js";
3
4
  import * as ai0 from "ai";
4
5
  import { CoreMessage } from "ai";
@@ -31,7 +32,8 @@ interface AgentConfig {
31
32
  id: string;
32
33
  name?: string;
33
34
  instructions: DynamicArgument<string>;
34
- model: ModelConfig;
35
+ /** A single model, or a `tieredModel(...)` policy for adaptive cost routing. */
36
+ model: ModelConfig | ModelRouter;
35
37
  tools?: Record<string, AnyToolAction>;
36
38
  memory?: AgentMemory;
37
39
  defaultGenerateOptions?: Partial<GenerateOptions>;
@@ -63,6 +65,11 @@ interface GenerateResult<T = unknown> {
63
65
  };
64
66
  finishReason: string;
65
67
  response: unknown;
68
+ /** Present when a `tieredModel` policy chose this result. */
69
+ routing?: {
70
+ tier: number;
71
+ attempts: number;
72
+ };
66
73
  }
67
74
  declare class Agent {
68
75
  readonly id: string;
@@ -79,6 +86,10 @@ declare class Agent {
79
86
  private resolveInstructions;
80
87
  private prepare;
81
88
  private persist;
89
+ /** Run one model (no routing, no persistence) and shape the result. */
90
+ private generateOnce;
91
+ /** Try tiers cheapest-first, escalating until a tier's `accept` passes. */
92
+ private generateRouted;
82
93
  generate<T = unknown>(input: string | CoreMessage[], options?: GenerateOptions): Promise<GenerateResult<T>>;
83
94
  stream(input: string | CoreMessage[], options?: GenerateOptions): Promise<ai0.StreamTextResult<Record<string, ai0.Tool>, never>>;
84
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/agent/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAkBA;AAI0B,UAJT,WAAA,CAIS;EAApB,gBAAA,CAAA,IAAA,EAAA;IAI+C,QAAA,EAAA,MAAA;IAC/C,UAAA,EAAA,MAAA;EAAO,CAAA,CAAA,EALP,OAKO,CAAA;IAGI,QAAA,EARS,WAQE,EAAA;IAGZ,aAAA,CAAA,EAAA,MAAA;EACP,CAAA,CAAA;EACgB,YAAA,CAAA,IAAA,EAAA;IAAf,QAAA,EAAA,MAAA;IACC,UAAA,EAAA,MAAA;IACwB,QAAA,EAAA;MAAR,IAAA,EAAA,MAAA,GAAA,WAAA;MAAO,OAAA,EAXmB,WAWnB,CAAA,SAAA,CAAA;IAGjB,CAAA,EAAA;EAKA,CAAA,CAAA,EAlBX,OAkBW,CAAA,IAAA,CAAA;;AAMN,UArBM,WAAA,CAqBN;EACQ,EAAA,EAAA,MAAA;EACH,IAAA,CAAA,EAAA,MAAA;EAAW,YAAA,EApBX,eAoBW,CAAA,MAAA,CAAA;EAGV,KAAA,EAtBR,WAsBsB;EAWlB,KAAA,CAAA,EAhCH,MAgCQ,CAAA,MAAA,EAhCO,aAgCP,CAAA;EAOI,MAAA,CAAA,EAtCX,WAsCW;EAQQ,sBAAA,CAAA,EA7CH,OA6CG,CA7CK,eA6CL,CAAA;;AAwEV,UAlHH,WAAA,CAkHG;EACP,MAAA,EAAA,MAAA;EACe,QAAA,EAAA,MAAA;;AAAvB,UA/GY,eAAA,CA+GZ;EAiD0B,QAAA,CAAA,EAAA,MAAA;EAAwB,WAAA,CAAA,EAAA,MAAA;EAAoB,UAAA,CAAA,EAAA,MAAA,GAAA,MAAA,GAAA,UAAA;EAAA;EAAA,MAAA,CAAA,EA3JhE,CAAA,CAAE,UA2J8D;EAAA,MAAA,CAAA,EA1JhE,WA0JgE;EAAA,cAAA,CAAA,EAzJxD,cAyJwD;gBAxJ3D;;UAGC;;WAEN;;;;;;;;;;;;cASE,KAAA;;;;;;sBAOS;;;aAQQ;aAAiB;;;;;wCAwE3B,yBACP,kBACR,QAAQ,eAAe;yBAiDG,yBAAwB,kBAAoB,QAAA,GAAA,CAAA,iBAAA,eAAA,GAAA,CAAA,IAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/agent/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;AAiBA;AAI0B,UAJT,WAAA,CAIS;EAApB,gBAAA,CAAA,IAAA,EAAA;IAI+C,QAAA,EAAA,MAAA;IAC/C,UAAA,EAAA,MAAA;EAAO,CAAA,CAAA,EALP,OAKO,CAAA;IAGI,QAAA,EARS,WAQE,EAAA;IAGZ,aAAA,CAAA,EAAA,MAAA;EAEP,CAAA,CAAA;EAAc,YAAA,CAAA,IAAA,EAAA;IACE,QAAA,EAAA,MAAA;IAAf,UAAA,EAAA,MAAA;IACC,QAAA,EAAA;MACwB,IAAA,EAAA,MAAA,GAAA,WAAA;MAAR,OAAA,EAZ0B,WAY1B,CAAA,SAAA,CAAA;IAAO,CAAA,EAAA;EAGjB,CAAA,CAAA,EAdX,OAcW,CAAA,IAAW,CAAA;AAK5B;AAKa,UArBI,WAAA,CAqBJ;EACF,EAAA,EAAA,MAAA;EACQ,IAAA,CAAA,EAAA,MAAA;EACH,YAAA,EArBA,eAqBA,CAAA,MAAA,CAAA;EAAW;EAGV,KAAA,EAtBR,WAsBsB,GAtBR,WAwBX;EAWC,KAAA,CAAA,EAlCH,MAkCQ,CAAA,MAAA,EAlCO,aAkCP,CAAA;EAOI,MAAA,CAAA,EAxCX,WAwCW;EAQQ,sBAAA,CAAA,EA/CH,OA+CG,CA/CK,eA+CL,CAAA;;AAgJV,UA5LH,WAAA,CA4LG;EACP,MAAA,EAAA,MAAA;EACe,QAAA,EAAA,MAAA;;AAAvB,UAzLY,eAAA,CAyLZ;EAa0B,QAAA,CAAA,EAAA,MAAA;EAAwB,WAAA,CAAA,EAAA,MAAA;EAAoB,UAAA,CAAA,EAAA,MAAA,GAAA,MAAA,GAAA,UAAA;EAAA;EAAA,MAAA,CAAA,EAjMhE,CAAA,CAAE,UAiM8D;EAAA,MAAA,CAAA,EAhMhE,WAgMgE;EAAA,cAAA,CAAA,EA/LxD,cA+LwD;gBA9L3D;;UAGC;;WAEN;;;;;;;;;;;;;;;;;cAWE,KAAA;;;;;;sBAOS;;;aAQQ;aAAiB;;;;;;;;;wCAgJ3B,yBACP,kBACR,QAAQ,eAAe;yBAaG,yBAAwB,kBAAoB,QAAA,GAAA,CAAA,iBAAA,eAAA,GAAA,CAAA,IAAA"}
@@ -1,4 +1,4 @@
1
- import { resolveModel } from "../llm/index.js";
1
+ import { isModelRouter, resolveModel } from "../llm/index.js";
2
2
  import { RuntimeContext } from "../types.js";
3
3
  import { buildToolset } from "../tools/index.js";
4
4
  import { MessageList } from "../message-list.js";
@@ -33,7 +33,6 @@ var Agent = class {
33
33
  ...options
34
34
  };
35
35
  const runtimeContext = opts.runtimeContext ?? new RuntimeContext();
36
- const model = await resolveModel(this.config.model);
37
36
  let system = await this.resolveInstructions(runtimeContext);
38
37
  const list = new MessageList();
39
38
  const scope = opts.memory;
@@ -47,7 +46,6 @@ var Agent = class {
47
46
  }
48
47
  list.add(input);
49
48
  return {
50
- model,
51
49
  system,
52
50
  messages: list.toCore(),
53
51
  tools: buildToolset(this.config.tools, runtimeContext),
@@ -70,12 +68,10 @@ var Agent = class {
70
68
  }]
71
69
  });
72
70
  }
73
- async generate(input, options = {}) {
74
- const { model, system, messages, tools, scope } = await this.prepare(input, options);
75
- const opts = {
76
- ...this.config.defaultGenerateOptions,
77
- ...options
78
- };
71
+ /** Run one model (no routing, no persistence) and shape the result. */
72
+ async generateOnce(modelConfig, prepared, opts) {
73
+ const model = await resolveModel(modelConfig);
74
+ const { system, messages, tools } = prepared;
79
75
  if (opts.output) {
80
76
  const res$1 = await generateObject({
81
77
  model,
@@ -85,7 +81,6 @@ var Agent = class {
85
81
  temperature: opts.temperature,
86
82
  abortSignal: opts.abortSignal
87
83
  });
88
- await this.persist(scope, input, JSON.stringify(res$1.object));
89
84
  return {
90
85
  text: JSON.stringify(res$1.object),
91
86
  object: res$1.object,
@@ -107,7 +102,6 @@ var Agent = class {
107
102
  toolChoice: opts.toolChoice,
108
103
  abortSignal: opts.abortSignal
109
104
  });
110
- await this.persist(scope, input, res.text);
111
105
  return {
112
106
  text: res.text,
113
107
  toolCalls: res.toolCalls,
@@ -118,14 +112,49 @@ var Agent = class {
118
112
  response: res.response
119
113
  };
120
114
  }
115
+ /** Try tiers cheapest-first, escalating until a tier's `accept` passes. */
116
+ async generateRouted(router, prepared, opts) {
117
+ const last = router.tiers.length - 1;
118
+ for (let i = 0; i <= last; i++) {
119
+ const tier = router.tiers[i];
120
+ const result = await this.generateOnce(tier.model, prepared, opts);
121
+ if (i === last || !tier.accept || tier.accept({
122
+ text: result.text,
123
+ finishReason: result.finishReason,
124
+ usage: result.usage
125
+ })) {
126
+ result.routing = {
127
+ tier: i,
128
+ attempts: i + 1
129
+ };
130
+ return result;
131
+ }
132
+ router.onEscalate?.({
133
+ from: i,
134
+ to: i + 1
135
+ });
136
+ }
137
+ throw new Error("Model router exhausted all tiers without a result.");
138
+ }
139
+ async generate(input, options = {}) {
140
+ const prepared = await this.prepare(input, options);
141
+ const opts = {
142
+ ...this.config.defaultGenerateOptions,
143
+ ...options
144
+ };
145
+ const model = this.config.model;
146
+ const result = isModelRouter(model) ? await this.generateRouted(model, prepared, opts) : await this.generateOnce(model, prepared, opts);
147
+ await this.persist(prepared.scope, input, result.text);
148
+ return result;
149
+ }
121
150
  async stream(input, options = {}) {
122
- const { model, system, messages, tools, scope } = await this.prepare(input, options);
151
+ const { system, messages, tools, scope } = await this.prepare(input, options);
123
152
  const opts = {
124
153
  ...this.config.defaultGenerateOptions,
125
154
  ...options
126
155
  };
127
156
  return streamText({
128
- model,
157
+ model: await resolveModel(isModelRouter(this.config.model) ? this.config.model.tiers[0].model : this.config.model),
129
158
  system,
130
159
  messages,
131
160
  tools,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["res"],"sources":["../../src/agent/index.ts"],"sourcesContent":["import {\n generateText,\n streamText,\n generateObject,\n type CoreMessage,\n type LanguageModel,\n} from 'ai'\nimport type { z } from 'zod'\nimport { resolveModel } from '../llm'\nimport { buildToolset, type AnyToolAction } from '../tools'\nimport { MessageList } from '../message-list'\nimport { RuntimeContext, type DynamicArgument, type Logger, type ModelConfig } from '../types'\nimport { noopLogger } from '../logger'\n\n/**\n * Minimal memory contract the Agent depends on. Implemented by `@redtuma/memory`.\n * Kept in core so the Agent can integrate memory without a hard dependency.\n */\nexport interface AgentMemory {\n rememberMessages(args: {\n threadId: string\n resourceId: string\n }): Promise<{ messages: CoreMessage[]; systemContext?: string }>\n saveMessages(args: {\n threadId: string\n resourceId: string\n messages: { role: 'user' | 'assistant'; content: CoreMessage['content'] }[]\n }): Promise<void>\n}\n\nexport interface AgentConfig {\n id: string\n name?: string\n instructions: DynamicArgument<string>\n model: ModelConfig\n tools?: Record<string, AnyToolAction>\n memory?: AgentMemory\n defaultGenerateOptions?: Partial<GenerateOptions>\n}\n\nexport interface MemoryScope {\n thread: string\n resource: string\n}\n\nexport interface GenerateOptions {\n maxSteps?: number\n temperature?: number\n toolChoice?: 'auto' | 'none' | 'required'\n /** Zod schema; when set, returns a validated `object` instead of free text. */\n output?: z.ZodTypeAny\n memory?: MemoryScope\n runtimeContext?: RuntimeContext\n abortSignal?: AbortSignal\n}\n\nexport interface GenerateResult<T = unknown> {\n text: string\n object?: T\n toolCalls: unknown[]\n toolResults: unknown[]\n steps: unknown[]\n usage: { promptTokens: number; completionTokens: number; totalTokens: number }\n finishReason: string\n response: unknown\n}\n\nexport class Agent {\n readonly id: string\n readonly name: string\n private readonly config: AgentConfig\n private logger: Logger = noopLogger\n private memory?: AgentMemory\n\n constructor(config: AgentConfig) {\n this.config = config\n this.id = config.id\n this.name = config.name ?? config.id\n this.memory = config.memory\n }\n\n /** Called by the Redtuma registry to inject shared deps. */\n __register(deps: { logger?: Logger; memory?: AgentMemory }): void {\n if (deps.logger) this.logger = deps.logger\n if (!this.memory && deps.memory) this.memory = deps.memory\n }\n\n private async resolveInstructions(runtimeContext: RuntimeContext): Promise<string> {\n const ins = this.config.instructions\n return typeof ins === 'function' ? await ins({ runtimeContext }) : ins\n }\n\n private async prepare(\n input: string | CoreMessage[],\n options: GenerateOptions,\n ): Promise<{\n model: LanguageModel\n system: string\n messages: CoreMessage[]\n tools: ReturnType<typeof buildToolset>\n scope?: MemoryScope\n runtimeContext: RuntimeContext\n }> {\n const opts = { ...this.config.defaultGenerateOptions, ...options }\n const runtimeContext = opts.runtimeContext ?? new RuntimeContext()\n const model = await resolveModel(this.config.model)\n\n let system = await this.resolveInstructions(runtimeContext)\n const list = new MessageList()\n\n // Memory recall\n const scope = opts.memory\n if (this.memory && scope) {\n const recalled = await this.memory.rememberMessages({\n threadId: scope.thread,\n resourceId: scope.resource,\n })\n if (recalled.systemContext) system += `\\n\\n${recalled.systemContext}`\n for (const m of recalled.messages) list.add(m, m.role as 'user' | 'assistant')\n }\n\n list.add(input)\n\n return {\n model,\n system,\n messages: list.toCore(),\n tools: buildToolset(this.config.tools, runtimeContext),\n scope,\n runtimeContext,\n }\n }\n\n private async persist(\n scope: MemoryScope | undefined,\n userInput: string | CoreMessage[],\n assistantText: string,\n ): Promise<void> {\n if (!this.memory || !scope) return\n const userContent =\n typeof userInput === 'string'\n ? userInput\n : (userInput.at(-1)?.content ?? '')\n await this.memory.saveMessages({\n threadId: scope.thread,\n resourceId: scope.resource,\n messages: [\n { role: 'user', content: userContent },\n { role: 'assistant', content: assistantText },\n ],\n })\n }\n\n async generate<T = unknown>(\n input: string | CoreMessage[],\n options: GenerateOptions = {},\n ): Promise<GenerateResult<T>> {\n const { model, system, messages, tools, scope } = await this.prepare(input, options)\n const opts = { ...this.config.defaultGenerateOptions, ...options }\n\n if (opts.output) {\n const res = await generateObject({\n model,\n system,\n messages,\n schema: opts.output,\n temperature: opts.temperature,\n abortSignal: opts.abortSignal,\n })\n await this.persist(scope, input, JSON.stringify(res.object))\n return {\n text: JSON.stringify(res.object),\n object: res.object as T,\n toolCalls: [],\n toolResults: [],\n steps: [],\n usage: res.usage,\n finishReason: res.finishReason,\n response: res.response,\n }\n }\n\n const res = await generateText({\n model,\n system,\n messages,\n tools,\n maxSteps: opts.maxSteps ?? 5,\n temperature: opts.temperature,\n toolChoice: opts.toolChoice,\n abortSignal: opts.abortSignal,\n })\n await this.persist(scope, input, res.text)\n\n return {\n text: res.text,\n toolCalls: res.toolCalls,\n toolResults: res.toolResults,\n steps: res.steps,\n usage: res.usage,\n finishReason: res.finishReason,\n response: res.response,\n }\n }\n\n async stream(input: string | CoreMessage[], options: GenerateOptions = {}) {\n const { model, system, messages, tools, scope } = await this.prepare(input, options)\n const opts = { ...this.config.defaultGenerateOptions, ...options }\n\n const result = streamText({\n model,\n system,\n messages,\n tools,\n maxSteps: opts.maxSteps ?? 5,\n temperature: opts.temperature,\n toolChoice: opts.toolChoice,\n abortSignal: opts.abortSignal,\n onFinish: ({ text }) => {\n void this.persist(scope, input, text)\n },\n })\n\n return result\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["res"],"sources":["../../src/agent/index.ts"],"sourcesContent":["import {\n generateText,\n streamText,\n generateObject,\n type CoreMessage,\n} from 'ai'\nimport type { z } from 'zod'\nimport { resolveModel, isModelRouter, type ModelRouter } from '../llm'\nimport { buildToolset, type AnyToolAction } from '../tools'\nimport { MessageList } from '../message-list'\nimport { RuntimeContext, type DynamicArgument, type Logger, type ModelConfig } from '../types'\nimport { noopLogger } from '../logger'\n\n/**\n * Minimal memory contract the Agent depends on. Implemented by `@redtuma/memory`.\n * Kept in core so the Agent can integrate memory without a hard dependency.\n */\nexport interface AgentMemory {\n rememberMessages(args: {\n threadId: string\n resourceId: string\n }): Promise<{ messages: CoreMessage[]; systemContext?: string }>\n saveMessages(args: {\n threadId: string\n resourceId: string\n messages: { role: 'user' | 'assistant'; content: CoreMessage['content'] }[]\n }): Promise<void>\n}\n\nexport interface AgentConfig {\n id: string\n name?: string\n instructions: DynamicArgument<string>\n /** A single model, or a `tieredModel(...)` policy for adaptive cost routing. */\n model: ModelConfig | ModelRouter\n tools?: Record<string, AnyToolAction>\n memory?: AgentMemory\n defaultGenerateOptions?: Partial<GenerateOptions>\n}\n\nexport interface MemoryScope {\n thread: string\n resource: string\n}\n\nexport interface GenerateOptions {\n maxSteps?: number\n temperature?: number\n toolChoice?: 'auto' | 'none' | 'required'\n /** Zod schema; when set, returns a validated `object` instead of free text. */\n output?: z.ZodTypeAny\n memory?: MemoryScope\n runtimeContext?: RuntimeContext\n abortSignal?: AbortSignal\n}\n\nexport interface GenerateResult<T = unknown> {\n text: string\n object?: T\n toolCalls: unknown[]\n toolResults: unknown[]\n steps: unknown[]\n usage: { promptTokens: number; completionTokens: number; totalTokens: number }\n finishReason: string\n response: unknown\n /** Present when a `tieredModel` policy chose this result. */\n routing?: { tier: number; attempts: number }\n}\n\nexport class Agent {\n readonly id: string\n readonly name: string\n private readonly config: AgentConfig\n private logger: Logger = noopLogger\n private memory?: AgentMemory\n\n constructor(config: AgentConfig) {\n this.config = config\n this.id = config.id\n this.name = config.name ?? config.id\n this.memory = config.memory\n }\n\n /** Called by the Redtuma registry to inject shared deps. */\n __register(deps: { logger?: Logger; memory?: AgentMemory }): void {\n if (deps.logger) this.logger = deps.logger\n if (!this.memory && deps.memory) this.memory = deps.memory\n }\n\n private async resolveInstructions(runtimeContext: RuntimeContext): Promise<string> {\n const ins = this.config.instructions\n return typeof ins === 'function' ? await ins({ runtimeContext }) : ins\n }\n\n private async prepare(\n input: string | CoreMessage[],\n options: GenerateOptions,\n ): Promise<{\n system: string\n messages: CoreMessage[]\n tools: ReturnType<typeof buildToolset>\n scope?: MemoryScope\n runtimeContext: RuntimeContext\n }> {\n const opts = { ...this.config.defaultGenerateOptions, ...options }\n const runtimeContext = opts.runtimeContext ?? new RuntimeContext()\n\n let system = await this.resolveInstructions(runtimeContext)\n const list = new MessageList()\n\n // Memory recall\n const scope = opts.memory\n if (this.memory && scope) {\n const recalled = await this.memory.rememberMessages({\n threadId: scope.thread,\n resourceId: scope.resource,\n })\n if (recalled.systemContext) system += `\\n\\n${recalled.systemContext}`\n for (const m of recalled.messages) list.add(m, m.role as 'user' | 'assistant')\n }\n\n list.add(input)\n\n return {\n system,\n messages: list.toCore(),\n tools: buildToolset(this.config.tools, runtimeContext),\n scope,\n runtimeContext,\n }\n }\n\n private async persist(\n scope: MemoryScope | undefined,\n userInput: string | CoreMessage[],\n assistantText: string,\n ): Promise<void> {\n if (!this.memory || !scope) return\n const userContent =\n typeof userInput === 'string'\n ? userInput\n : (userInput.at(-1)?.content ?? '')\n await this.memory.saveMessages({\n threadId: scope.thread,\n resourceId: scope.resource,\n messages: [\n { role: 'user', content: userContent },\n { role: 'assistant', content: assistantText },\n ],\n })\n }\n\n /** Run one model (no routing, no persistence) and shape the result. */\n private async generateOnce<T>(\n modelConfig: ModelConfig,\n prepared: Awaited<ReturnType<Agent['prepare']>>,\n opts: GenerateOptions,\n ): Promise<GenerateResult<T>> {\n const model = await resolveModel(modelConfig)\n const { system, messages, tools } = prepared\n\n if (opts.output) {\n const res = await generateObject({\n model,\n system,\n messages,\n schema: opts.output,\n temperature: opts.temperature,\n abortSignal: opts.abortSignal,\n })\n return {\n text: JSON.stringify(res.object),\n object: res.object as T,\n toolCalls: [],\n toolResults: [],\n steps: [],\n usage: res.usage,\n finishReason: res.finishReason,\n response: res.response,\n }\n }\n\n const res = await generateText({\n model,\n system,\n messages,\n tools,\n maxSteps: opts.maxSteps ?? 5,\n temperature: opts.temperature,\n toolChoice: opts.toolChoice,\n abortSignal: opts.abortSignal,\n })\n return {\n text: res.text,\n toolCalls: res.toolCalls,\n toolResults: res.toolResults,\n steps: res.steps,\n usage: res.usage,\n finishReason: res.finishReason,\n response: res.response,\n }\n }\n\n /** Try tiers cheapest-first, escalating until a tier's `accept` passes. */\n private async generateRouted<T>(\n router: ModelRouter,\n prepared: Awaited<ReturnType<Agent['prepare']>>,\n opts: GenerateOptions,\n ): Promise<GenerateResult<T>> {\n const last = router.tiers.length - 1\n for (let i = 0; i <= last; i++) {\n const tier = router.tiers[i]!\n const result = await this.generateOnce<T>(tier.model, prepared, opts)\n const accepted =\n i === last ||\n !tier.accept ||\n tier.accept({ text: result.text, finishReason: result.finishReason, usage: result.usage })\n if (accepted) {\n result.routing = { tier: i, attempts: i + 1 }\n return result\n }\n router.onEscalate?.({ from: i, to: i + 1 })\n }\n // Unreachable: the last tier is always accepted.\n throw new Error('Model router exhausted all tiers without a result.')\n }\n\n async generate<T = unknown>(\n input: string | CoreMessage[],\n options: GenerateOptions = {},\n ): Promise<GenerateResult<T>> {\n const prepared = await this.prepare(input, options)\n const opts = { ...this.config.defaultGenerateOptions, ...options }\n const model = this.config.model\n\n const result = isModelRouter(model)\n ? await this.generateRouted<T>(model, prepared, opts)\n : await this.generateOnce<T>(model, prepared, opts)\n\n await this.persist(prepared.scope, input, result.text)\n return result\n }\n\n async stream(input: string | CoreMessage[], options: GenerateOptions = {}) {\n const { system, messages, tools, scope } = await this.prepare(input, options)\n const opts = { ...this.config.defaultGenerateOptions, ...options }\n\n // Streaming can't inspect a result before committing, so a routed model\n // streams from its cheapest tier; adaptive escalation applies to generate().\n const modelConfig = isModelRouter(this.config.model)\n ? this.config.model.tiers[0]!.model\n : this.config.model\n const model = await resolveModel(modelConfig)\n\n const result = streamText({\n model,\n system,\n messages,\n tools,\n maxSteps: opts.maxSteps ?? 5,\n temperature: opts.temperature,\n toolChoice: opts.toolChoice,\n abortSignal: opts.abortSignal,\n onFinish: ({ text }) => {\n void this.persist(scope, input, text)\n },\n })\n\n return result\n }\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { CoreMessage, DynamicArgument, Logger, ModelConfig, RuntimeContext } from "./types.js";
2
+ import { ModelRouter, ModelTier, RedtumaModelError, RoutingResult, SUPPORTED_PROVIDERS, isLanguageModel, isModelRouter, parseModelString, resolveModel, tieredModel } from "./llm/index.js";
2
3
  import { AnyToolAction, ToolAction, ToolExecuteContext, buildToolset, createTool, isToolAction, toAISDKTool } from "./tools/index.js";
3
4
  import { Agent, AgentConfig, AgentMemory, GenerateOptions, GenerateResult, MemoryScope } from "./agent/index.js";
4
- import { Run, RunResult, RunStatus, Step, StepExecuteContext, Workflow, createStep, createWorkflow } from "./workflows/index.js";
5
+ import { Run, RunResult, RunSnapshot, RunStatus, Step, StepExecuteContext, Workflow, createStep, createWorkflow } from "./workflows/index.js";
5
6
  import { MessageInput, MessageList, MessageRole, RedtumaMessage } from "./message-list.js";
6
7
  import { GetMessagesArgs, InMemoryStore, Resource, Store, Thread } from "./store/index.js";
7
8
  import { Redtuma, RedtumaConfig } from "./redtuma.js";
8
- import { RedtumaModelError, SUPPORTED_PROVIDERS, isLanguageModel, parseModelString, resolveModel } from "./llm/index.js";
9
9
  import { ConsoleLogger, LogLevel, noopLogger } from "./logger.js";
10
- export { Agent, type AgentConfig, type AgentMemory, type AnyToolAction, ConsoleLogger, type CoreMessage, type DynamicArgument, type GenerateOptions, type GenerateResult, type GetMessagesArgs, InMemoryStore, type LogLevel, type Logger, type MemoryScope, type MessageInput, MessageList, type MessageRole, type ModelConfig, Redtuma, type RedtumaConfig, type RedtumaMessage, RedtumaModelError, type Resource, Run, type RunResult, type RunStatus, RuntimeContext, SUPPORTED_PROVIDERS, type Step, type StepExecuteContext, type Store, type Thread, type ToolAction, type ToolExecuteContext, Workflow, buildToolset, createStep, createTool, createWorkflow, isLanguageModel, isToolAction, noopLogger, parseModelString, resolveModel, toAISDKTool };
10
+ export { Agent, type AgentConfig, type AgentMemory, type AnyToolAction, ConsoleLogger, type CoreMessage, type DynamicArgument, type GenerateOptions, type GenerateResult, type GetMessagesArgs, InMemoryStore, type LogLevel, type Logger, type MemoryScope, type MessageInput, MessageList, type MessageRole, type ModelConfig, type ModelRouter, type ModelTier, Redtuma, type RedtumaConfig, type RedtumaMessage, RedtumaModelError, type Resource, type RoutingResult, Run, type RunResult, type RunSnapshot, type RunStatus, RuntimeContext, SUPPORTED_PROVIDERS, type Step, type StepExecuteContext, type Store, type Thread, type ToolAction, type ToolExecuteContext, Workflow, buildToolset, createStep, createTool, createWorkflow, isLanguageModel, isModelRouter, isToolAction, noopLogger, parseModelString, resolveModel, tieredModel, toAISDKTool };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { RedtumaModelError, SUPPORTED_PROVIDERS, isLanguageModel, parseModelString, resolveModel } from "./llm/index.js";
1
+ import { RedtumaModelError, SUPPORTED_PROVIDERS, isLanguageModel, isModelRouter, parseModelString, resolveModel, tieredModel } from "./llm/index.js";
2
2
  import { RuntimeContext } from "./types.js";
3
3
  import { buildToolset, createTool, isToolAction, toAISDKTool } from "./tools/index.js";
4
4
  import { MessageList } from "./message-list.js";
@@ -8,4 +8,4 @@ import { Redtuma } from "./redtuma.js";
8
8
  import { Run, Workflow, createStep, createWorkflow } from "./workflows/index.js";
9
9
  import { InMemoryStore } from "./store/index.js";
10
10
 
11
- export { Agent, ConsoleLogger, InMemoryStore, MessageList, Redtuma, RedtumaModelError, Run, RuntimeContext, SUPPORTED_PROVIDERS, Workflow, buildToolset, createStep, createTool, createWorkflow, isLanguageModel, isToolAction, noopLogger, parseModelString, resolveModel, toAISDKTool };
11
+ export { Agent, ConsoleLogger, InMemoryStore, MessageList, Redtuma, RedtumaModelError, Run, RuntimeContext, SUPPORTED_PROVIDERS, Workflow, buildToolset, createStep, createTool, createWorkflow, isLanguageModel, isModelRouter, isToolAction, noopLogger, parseModelString, resolveModel, tieredModel, toAISDKTool };
@@ -17,6 +17,57 @@ declare function parseModelString(model: string): {
17
17
  */
18
18
  declare function resolveModel(model: ModelConfig): Promise<LanguageModel>;
19
19
  declare const SUPPORTED_PROVIDERS: string[];
20
+ /** The slice of a generation result a routing predicate may inspect. */
21
+ interface RoutingResult {
22
+ text: string;
23
+ finishReason: string;
24
+ usage: {
25
+ promptTokens: number;
26
+ completionTokens: number;
27
+ totalTokens: number;
28
+ };
29
+ }
30
+ interface ModelTier {
31
+ model: ModelConfig;
32
+ /**
33
+ * Accept this tier's result? Return `false` to escalate to the next tier.
34
+ * Omitted (or on the last tier) means always accept. Use it to encode a
35
+ * confidence/quality gate — e.g. reject empty output, refusals, or failed
36
+ * validation so a stronger model is only paid for when needed.
37
+ */
38
+ accept?: (result: RoutingResult) => boolean;
39
+ }
40
+ /** A tiered model policy understood by `Agent.generate`. */
41
+ interface ModelRouter {
42
+ readonly __redtumaRouter: true;
43
+ tiers: ModelTier[];
44
+ onEscalate?: (info: {
45
+ from: number;
46
+ to: number;
47
+ }) => void;
48
+ }
49
+ /**
50
+ * Build an adaptive, cost-aware model policy: try the cheapest tier first and
51
+ * escalate to a stronger model only when a tier's `accept` predicate rejects
52
+ * the result. The last tier is always accepted.
53
+ *
54
+ * ```ts
55
+ * model: tieredModel({
56
+ * tiers: [
57
+ * { model: 'anthropic/claude-haiku-4-5', accept: (r) => r.text.length > 0 },
58
+ * { model: 'anthropic/claude-opus-4-8' },
59
+ * ],
60
+ * })
61
+ * ```
62
+ */
63
+ declare function tieredModel(config: {
64
+ tiers: ModelTier[];
65
+ onEscalate?: (info: {
66
+ from: number;
67
+ to: number;
68
+ }) => void;
69
+ }): ModelRouter;
70
+ declare function isModelRouter(value: ModelConfig | ModelRouter): value is ModelRouter;
20
71
  //#endregion
21
- export { RedtumaModelError, SUPPORTED_PROVIDERS, isLanguageModel, parseModelString, resolveModel };
72
+ export { ModelRouter, ModelTier, RedtumaModelError, RoutingResult, SUPPORTED_PROVIDERS, isLanguageModel, isModelRouter, parseModelString, resolveModel, tieredModel };
22
73
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/llm/index.ts"],"sourcesContent":[],"mappings":";;;;cA6Ba,iBAAA,SAA0B,KAAA;;AAAvC;AAIgB,iBAAA,eAAA,CAAuB,KAAA,EAAA,WAAuB,CAAA,EAAa,KAAA,IAAb,aAAa;AAM3E;AAcsB,iBAdN,gBAAA,CAckB,KAAA,EAAA,MAAA,CAAA,EAAA;EAAQ,QAAA,EAAA,MAAA;EAAsB,OAAA,EAAA,MAAA;CAAR;;AAexD;;;iBAfsB,YAAA,QAAoB,cAAc,QAAQ;cAenD"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/llm/index.ts"],"sourcesContent":[],"mappings":";;;;cA6Ba,iBAAA,SAA0B,KAAA;;AAAvC;AAIgB,iBAAA,eAAA,CAAuB,KAAA,EAAA,WAAuB,CAAA,EAAa,KAAA,IAAb,aAAa;AAM3E;AAcsB,iBAdN,gBAAA,CAckB,KAAA,EAAA,MAAA,CAAA,EAAA;EAAQ,QAAA,EAAA,MAAA;EAAsB,OAAA,EAAA,MAAA;CAAR;;AAexD;AAOA;AAMA;AAYiB,iBAxCK,YAAA,CA0Cb,KAAS,EA1CwB,WA0CxB,CAAA,EA1CsC,OA0CtC,CA1C8C,aA0C9C,CAAA;AAkBF,cA7CH,mBA8CJ,EAEL,MAAA,EAAA;AAOJ;AAAqC,UAhDpB,aAAA,CAgDoB;EAAc,IAAA,EAAA,MAAA;EAAuB,YAAA,EAAA,MAAA;EAAW,KAAA,EAAA;;;;;;UA1CpE,SAAA;SACR;;;;;;;oBAOW;;;UAIH,WAAA;;SAER;;;;;;;;;;;;;;;;;;;;iBAkBO,WAAA;SACP;;;;;IAEL;iBAOY,aAAA,QAAqB,cAAc,uBAAuB"}
package/dist/llm/index.js CHANGED
@@ -40,7 +40,32 @@ async function resolveModel(model) {
40
40
  return (await loader())(modelId);
41
41
  }
42
42
  const SUPPORTED_PROVIDERS = Object.keys(PROVIDER_LOADERS);
43
+ /**
44
+ * Build an adaptive, cost-aware model policy: try the cheapest tier first and
45
+ * escalate to a stronger model only when a tier's `accept` predicate rejects
46
+ * the result. The last tier is always accepted.
47
+ *
48
+ * ```ts
49
+ * model: tieredModel({
50
+ * tiers: [
51
+ * { model: 'anthropic/claude-haiku-4-5', accept: (r) => r.text.length > 0 },
52
+ * { model: 'anthropic/claude-opus-4-8' },
53
+ * ],
54
+ * })
55
+ * ```
56
+ */
57
+ function tieredModel(config) {
58
+ if (config.tiers.length === 0) throw new RedtumaModelError("tieredModel requires at least one tier.");
59
+ return {
60
+ __redtumaRouter: true,
61
+ tiers: config.tiers,
62
+ onEscalate: config.onEscalate
63
+ };
64
+ }
65
+ function isModelRouter(value) {
66
+ return typeof value === "object" && value !== null && "__redtumaRouter" in value;
67
+ }
43
68
 
44
69
  //#endregion
45
- export { RedtumaModelError, SUPPORTED_PROVIDERS, isLanguageModel, parseModelString, resolveModel };
70
+ export { RedtumaModelError, SUPPORTED_PROVIDERS, isLanguageModel, isModelRouter, parseModelString, resolveModel, tieredModel };
46
71
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["PROVIDER_LOADERS: Record<string, () => Promise<ProviderFactory>>"],"sources":["../../src/llm/index.ts"],"sourcesContent":["import type { LanguageModel } from 'ai'\nimport type { ModelConfig } from '../types'\n\n/**\n * Map of `provider` prefix -> lazy loader for the AI SDK provider factory.\n * Providers are optional peer deps imported on demand, so installing only one\n * (e.g. `@ai-sdk/anthropic`) is enough.\n */\ntype ProviderFactory = (modelId: string) => LanguageModel\n\nconst PROVIDER_LOADERS: Record<string, () => Promise<ProviderFactory>> = {\n anthropic: async () => {\n const mod = await import('@ai-sdk/anthropic').catch(() => {\n throw new RedtumaModelError(\n `Model provider \"anthropic\" requires the \"@ai-sdk/anthropic\" package. Install it with: pnpm add @ai-sdk/anthropic`,\n )\n })\n return (id) => mod.anthropic(id)\n },\n openai: async () => {\n const mod = await import('@ai-sdk/openai').catch(() => {\n throw new RedtumaModelError(\n `Model provider \"openai\" requires the \"@ai-sdk/openai\" package. Install it with: pnpm add @ai-sdk/openai`,\n )\n })\n return (id) => mod.openai(id)\n },\n}\n\nexport class RedtumaModelError extends Error {\n override name = 'RedtumaModelError'\n}\n\nexport function isLanguageModel(value: ModelConfig): value is LanguageModel {\n // AI SDK v1 model objects are non-string with a specificationVersion field.\n return typeof value !== 'string'\n}\n\n/** Parse a `'provider/model'` string into its parts. */\nexport function parseModelString(model: string): { provider: string; modelId: string } {\n const idx = model.indexOf('/')\n if (idx <= 0 || idx === model.length - 1) {\n throw new RedtumaModelError(\n `Invalid model string \"${model}\". Expected \"provider/model\", e.g. \"anthropic/claude-opus-4-8\".`,\n )\n }\n return { provider: model.slice(0, idx), modelId: model.slice(idx + 1) }\n}\n\n/**\n * Resolve a {@link ModelConfig} to a concrete AI SDK {@link LanguageModel}.\n * Strings of the form `provider/model` are routed to the matching provider.\n */\nexport async function resolveModel(model: ModelConfig): Promise<LanguageModel> {\n if (isLanguageModel(model)) return model\n\n const { provider, modelId } = parseModelString(model)\n const loader = PROVIDER_LOADERS[provider]\n if (!loader) {\n throw new RedtumaModelError(\n `Unknown model provider \"${provider}\". Supported: ${Object.keys(PROVIDER_LOADERS).join(', ')}. ` +\n `Pass an AI SDK LanguageModel directly to use another provider.`,\n )\n }\n const factory = await loader()\n return factory(modelId)\n}\n\nexport const SUPPORTED_PROVIDERS = Object.keys(PROVIDER_LOADERS)\n"],"mappings":";AAUA,MAAMA,mBAAmE;CACvE,WAAW,YAAY;EACrB,MAAM,MAAM,MAAM,OAAO,qBAAqB,YAAY;AACxD,SAAM,IAAI,kBACR,mHACD;IACD;AACF,UAAQ,OAAO,IAAI,UAAU,GAAG;;CAElC,QAAQ,YAAY;EAClB,MAAM,MAAM,MAAM,OAAO,kBAAkB,YAAY;AACrD,SAAM,IAAI,kBACR,0GACD;IACD;AACF,UAAQ,OAAO,IAAI,OAAO,GAAG;;CAEhC;AAED,IAAa,oBAAb,cAAuC,MAAM;CAC3C,AAAS,OAAO;;AAGlB,SAAgB,gBAAgB,OAA4C;AAE1E,QAAO,OAAO,UAAU;;;AAI1B,SAAgB,iBAAiB,OAAsD;CACrF,MAAM,MAAM,MAAM,QAAQ,IAAI;AAC9B,KAAI,OAAO,KAAK,QAAQ,MAAM,SAAS,EACrC,OAAM,IAAI,kBACR,yBAAyB,MAAM,iEAChC;AAEH,QAAO;EAAE,UAAU,MAAM,MAAM,GAAG,IAAI;EAAE,SAAS,MAAM,MAAM,MAAM,EAAE;EAAE;;;;;;AAOzE,eAAsB,aAAa,OAA4C;AAC7E,KAAI,gBAAgB,MAAM,CAAE,QAAO;CAEnC,MAAM,EAAE,UAAU,YAAY,iBAAiB,MAAM;CACrD,MAAM,SAAS,iBAAiB;AAChC,KAAI,CAAC,OACH,OAAM,IAAI,kBACR,2BAA2B,SAAS,gBAAgB,OAAO,KAAK,iBAAiB,CAAC,KAAK,KAAK,CAAC,kEAE9F;AAGH,SADgB,MAAM,QAAQ,EACf,QAAQ;;AAGzB,MAAa,sBAAsB,OAAO,KAAK,iBAAiB"}
1
+ {"version":3,"file":"index.js","names":["PROVIDER_LOADERS: Record<string, () => Promise<ProviderFactory>>"],"sources":["../../src/llm/index.ts"],"sourcesContent":["import type { LanguageModel } from 'ai'\nimport type { ModelConfig } from '../types'\n\n/**\n * Map of `provider` prefix -> lazy loader for the AI SDK provider factory.\n * Providers are optional peer deps imported on demand, so installing only one\n * (e.g. `@ai-sdk/anthropic`) is enough.\n */\ntype ProviderFactory = (modelId: string) => LanguageModel\n\nconst PROVIDER_LOADERS: Record<string, () => Promise<ProviderFactory>> = {\n anthropic: async () => {\n const mod = await import('@ai-sdk/anthropic').catch(() => {\n throw new RedtumaModelError(\n `Model provider \"anthropic\" requires the \"@ai-sdk/anthropic\" package. Install it with: pnpm add @ai-sdk/anthropic`,\n )\n })\n return (id) => mod.anthropic(id)\n },\n openai: async () => {\n const mod = await import('@ai-sdk/openai').catch(() => {\n throw new RedtumaModelError(\n `Model provider \"openai\" requires the \"@ai-sdk/openai\" package. Install it with: pnpm add @ai-sdk/openai`,\n )\n })\n return (id) => mod.openai(id)\n },\n}\n\nexport class RedtumaModelError extends Error {\n override name = 'RedtumaModelError'\n}\n\nexport function isLanguageModel(value: ModelConfig): value is LanguageModel {\n // AI SDK v1 model objects are non-string with a specificationVersion field.\n return typeof value !== 'string'\n}\n\n/** Parse a `'provider/model'` string into its parts. */\nexport function parseModelString(model: string): { provider: string; modelId: string } {\n const idx = model.indexOf('/')\n if (idx <= 0 || idx === model.length - 1) {\n throw new RedtumaModelError(\n `Invalid model string \"${model}\". Expected \"provider/model\", e.g. \"anthropic/claude-opus-4-8\".`,\n )\n }\n return { provider: model.slice(0, idx), modelId: model.slice(idx + 1) }\n}\n\n/**\n * Resolve a {@link ModelConfig} to a concrete AI SDK {@link LanguageModel}.\n * Strings of the form `provider/model` are routed to the matching provider.\n */\nexport async function resolveModel(model: ModelConfig): Promise<LanguageModel> {\n if (isLanguageModel(model)) return model\n\n const { provider, modelId } = parseModelString(model)\n const loader = PROVIDER_LOADERS[provider]\n if (!loader) {\n throw new RedtumaModelError(\n `Unknown model provider \"${provider}\". Supported: ${Object.keys(PROVIDER_LOADERS).join(', ')}. ` +\n `Pass an AI SDK LanguageModel directly to use another provider.`,\n )\n }\n const factory = await loader()\n return factory(modelId)\n}\n\nexport const SUPPORTED_PROVIDERS = Object.keys(PROVIDER_LOADERS)\n\n// ---------------------------------------------------------------------------\n// Adaptive model routing\n// ---------------------------------------------------------------------------\n\n/** The slice of a generation result a routing predicate may inspect. */\nexport interface RoutingResult {\n text: string\n finishReason: string\n usage: { promptTokens: number; completionTokens: number; totalTokens: number }\n}\n\nexport interface ModelTier {\n model: ModelConfig\n /**\n * Accept this tier's result? Return `false` to escalate to the next tier.\n * Omitted (or on the last tier) means always accept. Use it to encode a\n * confidence/quality gate — e.g. reject empty output, refusals, or failed\n * validation so a stronger model is only paid for when needed.\n */\n accept?: (result: RoutingResult) => boolean\n}\n\n/** A tiered model policy understood by `Agent.generate`. */\nexport interface ModelRouter {\n readonly __redtumaRouter: true\n tiers: ModelTier[]\n onEscalate?: (info: { from: number; to: number }) => void\n}\n\n/**\n * Build an adaptive, cost-aware model policy: try the cheapest tier first and\n * escalate to a stronger model only when a tier's `accept` predicate rejects\n * the result. The last tier is always accepted.\n *\n * ```ts\n * model: tieredModel({\n * tiers: [\n * { model: 'anthropic/claude-haiku-4-5', accept: (r) => r.text.length > 0 },\n * { model: 'anthropic/claude-opus-4-8' },\n * ],\n * })\n * ```\n */\nexport function tieredModel(config: {\n tiers: ModelTier[]\n onEscalate?: (info: { from: number; to: number }) => void\n}): ModelRouter {\n if (config.tiers.length === 0) {\n throw new RedtumaModelError('tieredModel requires at least one tier.')\n }\n return { __redtumaRouter: true, tiers: config.tiers, onEscalate: config.onEscalate }\n}\n\nexport function isModelRouter(value: ModelConfig | ModelRouter): value is ModelRouter {\n return typeof value === 'object' && value !== null && '__redtumaRouter' in value\n}\n"],"mappings":";AAUA,MAAMA,mBAAmE;CACvE,WAAW,YAAY;EACrB,MAAM,MAAM,MAAM,OAAO,qBAAqB,YAAY;AACxD,SAAM,IAAI,kBACR,mHACD;IACD;AACF,UAAQ,OAAO,IAAI,UAAU,GAAG;;CAElC,QAAQ,YAAY;EAClB,MAAM,MAAM,MAAM,OAAO,kBAAkB,YAAY;AACrD,SAAM,IAAI,kBACR,0GACD;IACD;AACF,UAAQ,OAAO,IAAI,OAAO,GAAG;;CAEhC;AAED,IAAa,oBAAb,cAAuC,MAAM;CAC3C,AAAS,OAAO;;AAGlB,SAAgB,gBAAgB,OAA4C;AAE1E,QAAO,OAAO,UAAU;;;AAI1B,SAAgB,iBAAiB,OAAsD;CACrF,MAAM,MAAM,MAAM,QAAQ,IAAI;AAC9B,KAAI,OAAO,KAAK,QAAQ,MAAM,SAAS,EACrC,OAAM,IAAI,kBACR,yBAAyB,MAAM,iEAChC;AAEH,QAAO;EAAE,UAAU,MAAM,MAAM,GAAG,IAAI;EAAE,SAAS,MAAM,MAAM,MAAM,EAAE;EAAE;;;;;;AAOzE,eAAsB,aAAa,OAA4C;AAC7E,KAAI,gBAAgB,MAAM,CAAE,QAAO;CAEnC,MAAM,EAAE,UAAU,YAAY,iBAAiB,MAAM;CACrD,MAAM,SAAS,iBAAiB;AAChC,KAAI,CAAC,OACH,OAAM,IAAI,kBACR,2BAA2B,SAAS,gBAAgB,OAAO,KAAK,iBAAiB,CAAC,KAAK,KAAK,CAAC,kEAE9F;AAGH,SADgB,MAAM,QAAQ,EACf,QAAQ;;AAGzB,MAAa,sBAAsB,OAAO,KAAK,iBAAiB;;;;;;;;;;;;;;;AA6ChE,SAAgB,YAAY,QAGZ;AACd,KAAI,OAAO,MAAM,WAAW,EAC1B,OAAM,IAAI,kBAAkB,0CAA0C;AAExE,QAAO;EAAE,iBAAiB;EAAM,OAAO,OAAO;EAAO,YAAY,OAAO;EAAY;;AAGtF,SAAgB,cAAc,OAAwD;AACpF,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,qBAAqB"}
@@ -28,7 +28,7 @@ declare function createTool<TInputSchema extends z.ZodTypeAny, TOutputSchema ext
28
28
  type AnyToolAction = ToolAction<any, any>;
29
29
  declare function isToolAction(value: unknown): value is ToolAction;
30
30
  /** Adapt a {@link ToolAction} into the AI SDK `tool()` shape. */
31
- declare function toAISDKTool(toolAction: ToolAction, runtimeContext: RuntimeContext): Tool;
31
+ declare function toAISDKTool(toolAction: AnyToolAction, runtimeContext: RuntimeContext): Tool;
32
32
  /**
33
33
  * Build the AI SDK tool map from a record of Redtuma tools and/or raw AI SDK
34
34
  * tools. Redtuma `ToolAction`s are adapted; anything else is passed through.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/tools/index.ts"],"sourcesContent":[],"mappings":";;;;;UAIiB;;EAAA,OAAA,EAEN,MAFM;EAEN,cAAA,EACO,cADP;EACO,WAAA,CAAA,EACF,WADE;;AACS,UAGV,UAHU,CAAA,qBAIJ,CAAA,CAAE,UAJE,GAIW,CAAA,CAAE,UAJb,EAAA,sBAKH,CAAA,CAAE,UALC,GAKY,CAAA,CAAE,UALd,CAAA,CAAA;EAGV,EAAA,EAAA,MAAA;EACQ,WAAA,EAAA,MAAA;EAAe,WAAA,EAKzB,YALyB;EACd,YAAA,CAAA,EAKT,aALS;EAAe,OAAA,EAAA,CAAA,GAAA,EAMxB,kBANwB,CAML,CAAA,CAAE,KANG,CAMG,YANH,CAAA,CAAA,EAAA,GAMsB,OANtB,CAAA,OAAA,CAAA,GAAA,OAAA;;;AAMG,iBAI5B,UAJ4B,CAAA,qBAKrB,CAAA,CAAE,UALmB,EAAA,sBAMpB,CAAA,CAAE,UANkB,GAML,CAAA,CAAE,UANG,CAAA,CAAA,MAAA,EAAA;EAAN,EAAA,EAAA,MAAA;EAArB,WAAA,EAAA,MAAA;EAA8C,WAAA,EAUhD,YAVgD;EAAO,YAAA,CAAA,EAWrD,aAXqD;EAItD,OAAA,EAAA,CAAA,GAAU,EAQT,kBARS,CAQU,CAAA,CAAE,KARZ,CAQkB,YARlB,CAAA,CAAA,EAAA,GAQqC,OARrC,CAAA,OAAA,CAAA,GAAA,OAAA;CACH,CAAA,EAQnB,UARqB,CAQV,YARU,EAQI,aARJ,CAAA;;AACgB,KAa7B,aAAA,GAAgB,UAba,CAAA,GAAA,EAAA,GAAA,CAAA;AAI1B,iBAWC,YAAA,CAXD,KAAA,EAAA,OAAA,CAAA,EAAA,KAAA,IAWwC,UAXxC;;AAE6B,iBAoB5B,WAAA,CApB4B,UAAA,EAqB9B,UArB8B,EAAA,cAAA,EAsB1B,cAtB0B,CAAA,EAuBzC,IAvByC;;;;;AACf,iBAmCb,YAAA,CAnCa,KAAA,EAoCpB,MApCoB,CAAA,MAAA,EAoCL,aApCK,GAoCW,IApCX,CAAA,GAAA,SAAA,EAAA,cAAA,EAqCX,cArCW,CAAA,EAsC1B,MAtC0B,CAAA,MAAA,EAsCX,IAtCW,CAAA,GAAA,SAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/tools/index.ts"],"sourcesContent":[],"mappings":";;;;;UAIiB;;EAAA,OAAA,EAEN,MAFM;EAEN,cAAA,EACO,cADP;EACO,WAAA,CAAA,EACF,WADE;;AACS,UAGV,UAHU,CAAA,qBAIJ,CAAA,CAAE,UAJE,GAIW,CAAA,CAAE,UAJb,EAAA,sBAKH,CAAA,CAAE,UALC,GAKY,CAAA,CAAE,UALd,CAAA,CAAA;EAGV,EAAA,EAAA,MAAA;EACQ,WAAA,EAAA,MAAA;EAAe,WAAA,EAKzB,YALyB;EACd,YAAA,CAAA,EAKT,aALS;EAAe,OAAA,EAAA,CAAA,GAAA,EAMxB,kBANwB,CAML,CAAA,CAAE,KANG,CAMG,YANH,CAAA,CAAA,EAAA,GAMsB,OANtB,CAAA,OAAA,CAAA,GAAA,OAAA;;;AAMG,iBAI5B,UAJ4B,CAAA,qBAKrB,CAAA,CAAE,UALmB,EAAA,sBAMpB,CAAA,CAAE,UANkB,GAML,CAAA,CAAE,UANG,CAAA,CAAA,MAAA,EAAA;EAAN,EAAA,EAAA,MAAA;EAArB,WAAA,EAAA,MAAA;EAA8C,WAAA,EAUhD,YAVgD;EAAO,YAAA,CAAA,EAWrD,aAXqD;EAItD,OAAA,EAAA,CAAA,GAAU,EAQT,kBARS,CAQU,CAAA,CAAE,KARZ,CAQkB,YARlB,CAAA,CAAA,EAAA,GAQqC,OARrC,CAAA,OAAA,CAAA,GAAA,OAAA;CACH,CAAA,EAQnB,UARqB,CAQV,YARU,EAQI,aARJ,CAAA;;AACgB,KAa7B,aAAA,GAAgB,UAba,CAAA,GAAA,EAAA,GAAA,CAAA;AAI1B,iBAWC,YAAA,CAXD,KAAA,EAAA,OAAA,CAAA,EAAA,KAAA,IAWwC,UAXxC;;AAE6B,iBAoB5B,WAAA,CApB4B,UAAA,EAqB9B,aArB8B,EAAA,cAAA,EAsB1B,cAtB0B,CAAA,EAuBzC,IAvByC;;;;;AACf,iBAmCb,YAAA,CAnCa,KAAA,EAoCpB,MApCoB,CAAA,MAAA,EAoCL,aApCK,GAoCW,IApCX,CAAA,GAAA,SAAA,EAAA,cAAA,EAqCX,cArCW,CAAA,EAsC1B,MAtC0B,CAAA,MAAA,EAsCX,IAtCW,CAAA,GAAA,SAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["aiTool","out: Record<string, AiTool>"],"sources":["../../src/tools/index.ts"],"sourcesContent":["import { tool as aiTool, type Tool as AiTool } from 'ai'\nimport type { z } from 'zod'\nimport { RuntimeContext } from '../types'\n\nexport interface ToolExecuteContext<TInput> {\n /** Validated tool input. */\n context: TInput\n runtimeContext: RuntimeContext\n abortSignal?: AbortSignal\n}\n\nexport interface ToolAction<\n TInputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n id: string\n description: string\n inputSchema: TInputSchema\n outputSchema?: TOutputSchema\n execute: (ctx: ToolExecuteContext<z.infer<TInputSchema>>) => Promise<unknown> | unknown\n}\n\n/** Define a Redtuma tool. The id is used as the tool name presented to the model. */\nexport function createTool<\n TInputSchema extends z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n>(config: {\n id: string\n description: string\n inputSchema: TInputSchema\n outputSchema?: TOutputSchema\n execute: (ctx: ToolExecuteContext<z.infer<TInputSchema>>) => Promise<unknown> | unknown\n}): ToolAction<TInputSchema, TOutputSchema> {\n return { ...config }\n}\n\n/** A tool with its schema generics erased; use for heterogeneous collections. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyToolAction = ToolAction<any, any>\n\nexport function isToolAction(value: unknown): value is ToolAction {\n return (\n !!value &&\n typeof value === 'object' &&\n typeof (value as ToolAction).execute === 'function' &&\n 'inputSchema' in value &&\n 'description' in value\n )\n}\n\n/** Adapt a {@link ToolAction} into the AI SDK `tool()` shape. */\nexport function toAISDKTool(\n toolAction: ToolAction,\n runtimeContext: RuntimeContext,\n): AiTool {\n return aiTool({\n description: toolAction.description,\n parameters: toolAction.inputSchema,\n execute: async (args, { abortSignal }) =>\n toolAction.execute({ context: args, runtimeContext, abortSignal }),\n })\n}\n\n/**\n * Build the AI SDK tool map from a record of Redtuma tools and/or raw AI SDK\n * tools. Redtuma `ToolAction`s are adapted; anything else is passed through.\n */\nexport function buildToolset(\n tools: Record<string, AnyToolAction | AiTool> | undefined,\n runtimeContext: RuntimeContext,\n): Record<string, AiTool> | undefined {\n if (!tools) return undefined\n const out: Record<string, AiTool> = {}\n for (const [name, t] of Object.entries(tools)) {\n out[name] = isToolAction(t) ? toAISDKTool(t, runtimeContext) : (t as AiTool)\n }\n return out\n}\n"],"mappings":";;;;AAuBA,SAAgB,WAGd,QAM0C;AAC1C,QAAO,EAAE,GAAG,QAAQ;;AAOtB,SAAgB,aAAa,OAAqC;AAChE,QACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,OAAQ,MAAqB,YAAY,cACzC,iBAAiB,SACjB,iBAAiB;;;AAKrB,SAAgB,YACd,YACA,gBACQ;AACR,QAAOA,KAAO;EACZ,aAAa,WAAW;EACxB,YAAY,WAAW;EACvB,SAAS,OAAO,MAAM,EAAE,kBACtB,WAAW,QAAQ;GAAE,SAAS;GAAM;GAAgB;GAAa,CAAC;EACrE,CAAC;;;;;;AAOJ,SAAgB,aACd,OACA,gBACoC;AACpC,KAAI,CAAC,MAAO,QAAO;CACnB,MAAMC,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,MAAM,CAC3C,KAAI,QAAQ,aAAa,EAAE,GAAG,YAAY,GAAG,eAAe,GAAI;AAElE,QAAO"}
1
+ {"version":3,"file":"index.js","names":["aiTool","out: Record<string, AiTool>"],"sources":["../../src/tools/index.ts"],"sourcesContent":["import { tool as aiTool, type Tool as AiTool } from 'ai'\nimport type { z } from 'zod'\nimport { RuntimeContext } from '../types'\n\nexport interface ToolExecuteContext<TInput> {\n /** Validated tool input. */\n context: TInput\n runtimeContext: RuntimeContext\n abortSignal?: AbortSignal\n}\n\nexport interface ToolAction<\n TInputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n id: string\n description: string\n inputSchema: TInputSchema\n outputSchema?: TOutputSchema\n execute: (ctx: ToolExecuteContext<z.infer<TInputSchema>>) => Promise<unknown> | unknown\n}\n\n/** Define a Redtuma tool. The id is used as the tool name presented to the model. */\nexport function createTool<\n TInputSchema extends z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n>(config: {\n id: string\n description: string\n inputSchema: TInputSchema\n outputSchema?: TOutputSchema\n execute: (ctx: ToolExecuteContext<z.infer<TInputSchema>>) => Promise<unknown> | unknown\n}): ToolAction<TInputSchema, TOutputSchema> {\n return { ...config }\n}\n\n/** A tool with its schema generics erased; use for heterogeneous collections. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyToolAction = ToolAction<any, any>\n\nexport function isToolAction(value: unknown): value is ToolAction {\n return (\n !!value &&\n typeof value === 'object' &&\n typeof (value as ToolAction).execute === 'function' &&\n 'inputSchema' in value &&\n 'description' in value\n )\n}\n\n/** Adapt a {@link ToolAction} into the AI SDK `tool()` shape. */\nexport function toAISDKTool(\n toolAction: AnyToolAction,\n runtimeContext: RuntimeContext,\n): AiTool {\n return aiTool({\n description: toolAction.description,\n parameters: toolAction.inputSchema,\n execute: async (args, { abortSignal }) =>\n toolAction.execute({ context: args, runtimeContext, abortSignal }),\n })\n}\n\n/**\n * Build the AI SDK tool map from a record of Redtuma tools and/or raw AI SDK\n * tools. Redtuma `ToolAction`s are adapted; anything else is passed through.\n */\nexport function buildToolset(\n tools: Record<string, AnyToolAction | AiTool> | undefined,\n runtimeContext: RuntimeContext,\n): Record<string, AiTool> | undefined {\n if (!tools) return undefined\n const out: Record<string, AiTool> = {}\n for (const [name, t] of Object.entries(tools)) {\n out[name] = isToolAction(t) ? toAISDKTool(t, runtimeContext) : (t as AiTool)\n }\n return out\n}\n"],"mappings":";;;;AAuBA,SAAgB,WAGd,QAM0C;AAC1C,QAAO,EAAE,GAAG,QAAQ;;AAOtB,SAAgB,aAAa,OAAqC;AAChE,QACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,OAAQ,MAAqB,YAAY,cACzC,iBAAiB,SACjB,iBAAiB;;;AAKrB,SAAgB,YACd,YACA,gBACQ;AACR,QAAOA,KAAO;EACZ,aAAa,WAAW;EACxB,YAAY,WAAW;EACvB,SAAS,OAAO,MAAM,EAAE,kBACtB,WAAW,QAAQ;GAAE,SAAS;GAAM;GAAgB;GAAa,CAAC;EACrE,CAAC;;;;;;AAOJ,SAAgB,aACd,OACA,gBACoC;AACpC,KAAI,CAAC,MAAO,QAAO;CACnB,MAAMC,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,MAAM,CAC3C,KAAI,QAAQ,aAAa,EAAE,GAAG,YAAY,GAAG,eAAe,GAAI;AAElE,QAAO"}
@@ -63,6 +63,17 @@ interface RunResult {
63
63
  };
64
64
  steps: Record<string, unknown>;
65
65
  }
66
+ /**
67
+ * Serializable state of a suspended run. Persist it (via a `Store`) to resume a
68
+ * workflow on a different server instance / after a restart — required for
69
+ * stateless edge deployments where the live `Run` object does not survive.
70
+ */
71
+ interface RunSnapshot {
72
+ nodeIndex: number;
73
+ input: unknown;
74
+ results: Record<string, unknown>;
75
+ suspendedStepId: string;
76
+ }
66
77
  declare class Workflow {
67
78
  config: {
68
79
  id: string;
@@ -92,6 +103,10 @@ declare class Run {
92
103
  private readonly nodes;
93
104
  private snapshot;
94
105
  constructor(workflowId: string, nodes: Node[]);
106
+ /** The serializable snapshot of a suspended run, or null if not suspended. */
107
+ getSnapshot(): RunSnapshot | null;
108
+ /** Rehydrate a previously suspended run from a persisted {@link RunSnapshot}. */
109
+ restore(snapshot: RunSnapshot): this;
95
110
  start(args: {
96
111
  inputData: unknown;
97
112
  runtimeContext?: RuntimeContext;
@@ -110,5 +125,5 @@ declare function createWorkflow(config: {
110
125
  outputSchema?: z.ZodTypeAny;
111
126
  }): Workflow;
112
127
  //#endregion
113
- export { Run, RunResult, RunStatus, Step, StepExecuteContext, Workflow, createStep, createWorkflow };
128
+ export { Run, RunResult, RunSnapshot, RunStatus, Step, StepExecuteContext, Workflow, createStep, createWorkflow };
114
129
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/workflows/index.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/workflows/index.ts"],"sourcesContent":[],"mappings":"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"}
@@ -78,6 +78,15 @@ var Run = class {
78
78
  this.workflowId = workflowId;
79
79
  this.nodes = nodes;
80
80
  }
81
+ /** The serializable snapshot of a suspended run, or null if not suspended. */
82
+ getSnapshot() {
83
+ return this.snapshot;
84
+ }
85
+ /** Rehydrate a previously suspended run from a persisted {@link RunSnapshot}. */
86
+ restore(snapshot) {
87
+ this.snapshot = snapshot;
88
+ return this;
89
+ }
81
90
  async start(args) {
82
91
  const runtimeContext = args.runtimeContext ?? new RuntimeContext();
83
92
  return this.execute(0, args.inputData, {}, runtimeContext, void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["stepId: string","payload: unknown","config: { id: string; inputSchema?: z.ZodTypeAny; outputSchema?: z.ZodTypeAny }","workflowId: string","nodes: Node[]","merged: Record<string, unknown>","out: unknown[]"],"sources":["../../src/workflows/index.ts"],"sourcesContent":["import type { z } from 'zod'\nimport { RuntimeContext, type Logger } from '../types'\nimport { noopLogger } from '../logger'\n\nexport interface StepExecuteContext<TInput = unknown, TResume = unknown> {\n inputData: TInput\n runtimeContext: RuntimeContext\n /** Result of a previously completed step by id. */\n getStepResult: (stepId: string) => unknown\n /** Data passed to `run.resume()` when resuming a suspended step. */\n resumeData?: TResume\n /** Suspend the workflow for human-in-the-loop; throws to halt execution. */\n suspend: (payload?: unknown) => never\n}\n\nexport interface Step<\n TInputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n id: string\n inputSchema?: TInputSchema\n outputSchema?: TOutputSchema\n resumeSchema?: z.ZodTypeAny\n suspendSchema?: z.ZodTypeAny\n execute: (ctx: StepExecuteContext) => Promise<unknown> | unknown\n}\n\nexport function createStep<\n TInputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n>(config: {\n id: string\n inputSchema?: TInputSchema\n outputSchema?: TOutputSchema\n resumeSchema?: z.ZodTypeAny\n suspendSchema?: z.ZodTypeAny\n execute: (ctx: StepExecuteContext) => Promise<unknown> | unknown\n}): Step<TInputSchema, TOutputSchema> {\n return { ...config }\n}\n\nclass SuspendSignal {\n constructor(\n public readonly stepId: string,\n public readonly payload: unknown,\n ) {}\n}\n\ntype Condition = (args: { inputData: unknown; runtimeContext: RuntimeContext }) => boolean | Promise<boolean>\n\ntype Node =\n | { kind: 'step'; step: Step }\n | { kind: 'parallel'; steps: Step[] }\n | { kind: 'branch'; branches: [Condition, Step][] }\n | { kind: 'dountil'; step: Step; condition: Condition }\n | { kind: 'foreach'; step: Step }\n | { kind: 'map'; fn: (input: unknown) => unknown }\n\nexport type RunStatus = 'success' | 'suspended' | 'failed'\n\nexport interface RunResult {\n status: RunStatus\n result?: unknown\n error?: Error\n suspended?: { stepId: string; payload: unknown }\n steps: Record<string, unknown>\n}\n\ninterface Snapshot {\n nodeIndex: number\n input: unknown\n results: Record<string, unknown>\n suspendedStepId: string\n}\n\nexport class Workflow {\n readonly id: string\n private nodes: Node[] = []\n private committed = false\n logger: Logger = noopLogger\n\n constructor(public config: { id: string; inputSchema?: z.ZodTypeAny; outputSchema?: z.ZodTypeAny }) {\n this.id = config.id\n }\n\n then(step: Step): this {\n this.nodes.push({ kind: 'step', step })\n return this\n }\n parallel(steps: Step[]): this {\n this.nodes.push({ kind: 'parallel', steps })\n return this\n }\n branch(branches: [Condition, Step][]): this {\n this.nodes.push({ kind: 'branch', branches })\n return this\n }\n dountil(step: Step, condition: Condition): this {\n this.nodes.push({ kind: 'dountil', step, condition })\n return this\n }\n foreach(step: Step): this {\n this.nodes.push({ kind: 'foreach', step })\n return this\n }\n map(fn: (input: unknown) => unknown): this {\n this.nodes.push({ kind: 'map', fn })\n return this\n }\n commit(): this {\n this.committed = true\n return this\n }\n\n createRun(): Run {\n if (!this.committed) {\n throw new Error(`Workflow \"${this.id}\" must be committed with .commit() before running.`)\n }\n return new Run(this.id, this.nodes)\n }\n}\n\nexport class Run {\n private snapshot: Snapshot | null = null\n\n constructor(\n public readonly workflowId: string,\n private readonly nodes: Node[],\n ) {}\n\n async start(args: { inputData: unknown; runtimeContext?: RuntimeContext }): Promise<RunResult> {\n const runtimeContext = args.runtimeContext ?? new RuntimeContext()\n return this.execute(0, args.inputData, {}, runtimeContext, undefined)\n }\n\n async resume(args: {\n step: string\n resumeData?: unknown\n runtimeContext?: RuntimeContext\n }): Promise<RunResult> {\n if (!this.snapshot) throw new Error('Cannot resume: workflow is not suspended.')\n if (this.snapshot.suspendedStepId !== args.step) {\n throw new Error(\n `Cannot resume step \"${args.step}\": suspended step is \"${this.snapshot.suspendedStepId}\".`,\n )\n }\n const { nodeIndex, input, results } = this.snapshot\n const runtimeContext = args.runtimeContext ?? new RuntimeContext()\n this.snapshot = null\n return this.execute(nodeIndex, input, results, runtimeContext, args.resumeData)\n }\n\n private async execute(\n startIndex: number,\n initialInput: unknown,\n results: Record<string, unknown>,\n runtimeContext: RuntimeContext,\n resumeData: unknown,\n ): Promise<RunResult> {\n let input = initialInput\n const getStepResult = (id: string) => results[id]\n\n for (let i = startIndex; i < this.nodes.length; i++) {\n const node = this.nodes[i]!\n const isResumeTarget = resumeData !== undefined && i === startIndex\n const ctxBase = { runtimeContext, getStepResult }\n\n try {\n switch (node.kind) {\n case 'step': {\n input = await this.runStep(node.step, input, ctxBase, isResumeTarget ? resumeData : undefined)\n results[node.step.id] = input\n break\n }\n case 'parallel': {\n const out = await Promise.all(\n node.steps.map((s) => this.runStep(s, input, ctxBase, undefined)),\n )\n const merged: Record<string, unknown> = {}\n node.steps.forEach((s, idx) => {\n results[s.id] = out[idx]\n merged[s.id] = out[idx]\n })\n input = merged\n break\n }\n case 'branch': {\n for (const [cond, step] of node.branches) {\n if (await cond({ inputData: input, runtimeContext })) {\n input = await this.runStep(step, input, ctxBase, isResumeTarget ? resumeData : undefined)\n results[step.id] = input\n break\n }\n }\n break\n }\n case 'dountil': {\n do {\n input = await this.runStep(node.step, input, ctxBase, undefined)\n results[node.step.id] = input\n } while (!(await node.condition({ inputData: input, runtimeContext })))\n break\n }\n case 'foreach': {\n const items = Array.isArray(input) ? input : [input]\n const out: unknown[] = []\n for (const item of items) {\n out.push(await this.runStep(node.step, item, ctxBase, undefined))\n }\n input = out\n results[node.step.id] = out\n break\n }\n case 'map': {\n input = node.fn(input)\n break\n }\n }\n resumeData = undefined\n } catch (err) {\n if (err instanceof SuspendSignal) {\n this.snapshot = {\n nodeIndex: i,\n input,\n results,\n suspendedStepId: err.stepId,\n }\n return { status: 'suspended', suspended: { stepId: err.stepId, payload: err.payload }, steps: results }\n }\n return { status: 'failed', error: err as Error, steps: results }\n }\n }\n\n return { status: 'success', result: input, steps: results }\n }\n\n private async runStep(\n step: Step,\n inputData: unknown,\n base: { runtimeContext: RuntimeContext; getStepResult: (id: string) => unknown },\n resumeData: unknown,\n ): Promise<unknown> {\n const suspend = (payload?: unknown): never => {\n throw new SuspendSignal(step.id, payload)\n }\n return step.execute({ inputData, resumeData, suspend, ...base })\n }\n}\n\nexport function createWorkflow(config: {\n id: string\n inputSchema?: z.ZodTypeAny\n outputSchema?: z.ZodTypeAny\n}): Workflow {\n return new Workflow(config)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["stepId: string","payload: unknown","config: { id: string; inputSchema?: z.ZodTypeAny; outputSchema?: z.ZodTypeAny }","workflowId: string","nodes: Node[]","merged: Record<string, unknown>","out: unknown[]"],"sources":["../../src/workflows/index.ts"],"sourcesContent":["import type { z } from 'zod'\nimport { RuntimeContext, type Logger } from '../types'\nimport { noopLogger } from '../logger'\n\nexport interface StepExecuteContext<TInput = unknown, TResume = unknown> {\n inputData: TInput\n runtimeContext: RuntimeContext\n /** Result of a previously completed step by id. */\n getStepResult: (stepId: string) => unknown\n /** Data passed to `run.resume()` when resuming a suspended step. */\n resumeData?: TResume\n /** Suspend the workflow for human-in-the-loop; throws to halt execution. */\n suspend: (payload?: unknown) => never\n}\n\nexport interface Step<\n TInputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n id: string\n inputSchema?: TInputSchema\n outputSchema?: TOutputSchema\n resumeSchema?: z.ZodTypeAny\n suspendSchema?: z.ZodTypeAny\n execute: (ctx: StepExecuteContext) => Promise<unknown> | unknown\n}\n\nexport function createStep<\n TInputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny,\n>(config: {\n id: string\n inputSchema?: TInputSchema\n outputSchema?: TOutputSchema\n resumeSchema?: z.ZodTypeAny\n suspendSchema?: z.ZodTypeAny\n execute: (ctx: StepExecuteContext) => Promise<unknown> | unknown\n}): Step<TInputSchema, TOutputSchema> {\n return { ...config }\n}\n\nclass SuspendSignal {\n constructor(\n public readonly stepId: string,\n public readonly payload: unknown,\n ) {}\n}\n\ntype Condition = (args: { inputData: unknown; runtimeContext: RuntimeContext }) => boolean | Promise<boolean>\n\ntype Node =\n | { kind: 'step'; step: Step }\n | { kind: 'parallel'; steps: Step[] }\n | { kind: 'branch'; branches: [Condition, Step][] }\n | { kind: 'dountil'; step: Step; condition: Condition }\n | { kind: 'foreach'; step: Step }\n | { kind: 'map'; fn: (input: unknown) => unknown }\n\nexport type RunStatus = 'success' | 'suspended' | 'failed'\n\nexport interface RunResult {\n status: RunStatus\n result?: unknown\n error?: Error\n suspended?: { stepId: string; payload: unknown }\n steps: Record<string, unknown>\n}\n\n/**\n * Serializable state of a suspended run. Persist it (via a `Store`) to resume a\n * workflow on a different server instance / after a restart — required for\n * stateless edge deployments where the live `Run` object does not survive.\n */\nexport interface RunSnapshot {\n nodeIndex: number\n input: unknown\n results: Record<string, unknown>\n suspendedStepId: string\n}\n\nexport class Workflow {\n readonly id: string\n private nodes: Node[] = []\n private committed = false\n logger: Logger = noopLogger\n\n constructor(public config: { id: string; inputSchema?: z.ZodTypeAny; outputSchema?: z.ZodTypeAny }) {\n this.id = config.id\n }\n\n then(step: Step): this {\n this.nodes.push({ kind: 'step', step })\n return this\n }\n parallel(steps: Step[]): this {\n this.nodes.push({ kind: 'parallel', steps })\n return this\n }\n branch(branches: [Condition, Step][]): this {\n this.nodes.push({ kind: 'branch', branches })\n return this\n }\n dountil(step: Step, condition: Condition): this {\n this.nodes.push({ kind: 'dountil', step, condition })\n return this\n }\n foreach(step: Step): this {\n this.nodes.push({ kind: 'foreach', step })\n return this\n }\n map(fn: (input: unknown) => unknown): this {\n this.nodes.push({ kind: 'map', fn })\n return this\n }\n commit(): this {\n this.committed = true\n return this\n }\n\n createRun(): Run {\n if (!this.committed) {\n throw new Error(`Workflow \"${this.id}\" must be committed with .commit() before running.`)\n }\n return new Run(this.id, this.nodes)\n }\n}\n\nexport class Run {\n private snapshot: RunSnapshot | null = null\n\n constructor(\n public readonly workflowId: string,\n private readonly nodes: Node[],\n ) {}\n\n /** The serializable snapshot of a suspended run, or null if not suspended. */\n getSnapshot(): RunSnapshot | null {\n return this.snapshot\n }\n\n /** Rehydrate a previously suspended run from a persisted {@link RunSnapshot}. */\n restore(snapshot: RunSnapshot): this {\n this.snapshot = snapshot\n return this\n }\n\n async start(args: { inputData: unknown; runtimeContext?: RuntimeContext }): Promise<RunResult> {\n const runtimeContext = args.runtimeContext ?? new RuntimeContext()\n return this.execute(0, args.inputData, {}, runtimeContext, undefined)\n }\n\n async resume(args: {\n step: string\n resumeData?: unknown\n runtimeContext?: RuntimeContext\n }): Promise<RunResult> {\n if (!this.snapshot) throw new Error('Cannot resume: workflow is not suspended.')\n if (this.snapshot.suspendedStepId !== args.step) {\n throw new Error(\n `Cannot resume step \"${args.step}\": suspended step is \"${this.snapshot.suspendedStepId}\".`,\n )\n }\n const { nodeIndex, input, results } = this.snapshot\n const runtimeContext = args.runtimeContext ?? new RuntimeContext()\n this.snapshot = null\n return this.execute(nodeIndex, input, results, runtimeContext, args.resumeData)\n }\n\n private async execute(\n startIndex: number,\n initialInput: unknown,\n results: Record<string, unknown>,\n runtimeContext: RuntimeContext,\n resumeData: unknown,\n ): Promise<RunResult> {\n let input = initialInput\n const getStepResult = (id: string) => results[id]\n\n for (let i = startIndex; i < this.nodes.length; i++) {\n const node = this.nodes[i]!\n const isResumeTarget = resumeData !== undefined && i === startIndex\n const ctxBase = { runtimeContext, getStepResult }\n\n try {\n switch (node.kind) {\n case 'step': {\n input = await this.runStep(node.step, input, ctxBase, isResumeTarget ? resumeData : undefined)\n results[node.step.id] = input\n break\n }\n case 'parallel': {\n const out = await Promise.all(\n node.steps.map((s) => this.runStep(s, input, ctxBase, undefined)),\n )\n const merged: Record<string, unknown> = {}\n node.steps.forEach((s, idx) => {\n results[s.id] = out[idx]\n merged[s.id] = out[idx]\n })\n input = merged\n break\n }\n case 'branch': {\n for (const [cond, step] of node.branches) {\n if (await cond({ inputData: input, runtimeContext })) {\n input = await this.runStep(step, input, ctxBase, isResumeTarget ? resumeData : undefined)\n results[step.id] = input\n break\n }\n }\n break\n }\n case 'dountil': {\n do {\n input = await this.runStep(node.step, input, ctxBase, undefined)\n results[node.step.id] = input\n } while (!(await node.condition({ inputData: input, runtimeContext })))\n break\n }\n case 'foreach': {\n const items = Array.isArray(input) ? input : [input]\n const out: unknown[] = []\n for (const item of items) {\n out.push(await this.runStep(node.step, item, ctxBase, undefined))\n }\n input = out\n results[node.step.id] = out\n break\n }\n case 'map': {\n input = node.fn(input)\n break\n }\n }\n resumeData = undefined\n } catch (err) {\n if (err instanceof SuspendSignal) {\n this.snapshot = {\n nodeIndex: i,\n input,\n results,\n suspendedStepId: err.stepId,\n }\n return { status: 'suspended', suspended: { stepId: err.stepId, payload: err.payload }, steps: results }\n }\n return { status: 'failed', error: err as Error, steps: results }\n }\n }\n\n return { status: 'success', result: input, steps: results }\n }\n\n private async runStep(\n step: Step,\n inputData: unknown,\n base: { runtimeContext: RuntimeContext; getStepResult: (id: string) => unknown },\n resumeData: unknown,\n ): Promise<unknown> {\n const suspend = (payload?: unknown): never => {\n throw new SuspendSignal(step.id, payload)\n }\n return step.execute({ inputData, resumeData, suspend, ...base })\n }\n}\n\nexport function createWorkflow(config: {\n id: string\n inputSchema?: z.ZodTypeAny\n outputSchema?: z.ZodTypeAny\n}): Workflow {\n return new Workflow(config)\n}\n"],"mappings":";;;;AA2BA,SAAgB,WAGd,QAOoC;AACpC,QAAO,EAAE,GAAG,QAAQ;;AAGtB,IAAM,gBAAN,MAAoB;CAClB,YACE,AAAgBA,QAChB,AAAgBC,SAChB;EAFgB;EACA;;;AAoCpB,IAAa,WAAb,MAAsB;CACpB,AAAS;CACT,AAAQ,QAAgB,EAAE;CAC1B,AAAQ,YAAY;CACpB,SAAiB;CAEjB,YAAY,AAAOC,QAAiF;EAAjF;AACjB,OAAK,KAAK,OAAO;;CAGnB,KAAK,MAAkB;AACrB,OAAK,MAAM,KAAK;GAAE,MAAM;GAAQ;GAAM,CAAC;AACvC,SAAO;;CAET,SAAS,OAAqB;AAC5B,OAAK,MAAM,KAAK;GAAE,MAAM;GAAY;GAAO,CAAC;AAC5C,SAAO;;CAET,OAAO,UAAqC;AAC1C,OAAK,MAAM,KAAK;GAAE,MAAM;GAAU;GAAU,CAAC;AAC7C,SAAO;;CAET,QAAQ,MAAY,WAA4B;AAC9C,OAAK,MAAM,KAAK;GAAE,MAAM;GAAW;GAAM;GAAW,CAAC;AACrD,SAAO;;CAET,QAAQ,MAAkB;AACxB,OAAK,MAAM,KAAK;GAAE,MAAM;GAAW;GAAM,CAAC;AAC1C,SAAO;;CAET,IAAI,IAAuC;AACzC,OAAK,MAAM,KAAK;GAAE,MAAM;GAAO;GAAI,CAAC;AACpC,SAAO;;CAET,SAAe;AACb,OAAK,YAAY;AACjB,SAAO;;CAGT,YAAiB;AACf,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,aAAa,KAAK,GAAG,oDAAoD;AAE3F,SAAO,IAAI,IAAI,KAAK,IAAI,KAAK,MAAM;;;AAIvC,IAAa,MAAb,MAAiB;CACf,AAAQ,WAA+B;CAEvC,YACE,AAAgBC,YAChB,AAAiBC,OACjB;EAFgB;EACC;;;CAInB,cAAkC;AAChC,SAAO,KAAK;;;CAId,QAAQ,UAA6B;AACnC,OAAK,WAAW;AAChB,SAAO;;CAGT,MAAM,MAAM,MAAmF;EAC7F,MAAM,iBAAiB,KAAK,kBAAkB,IAAI,gBAAgB;AAClE,SAAO,KAAK,QAAQ,GAAG,KAAK,WAAW,EAAE,EAAE,gBAAgB,OAAU;;CAGvE,MAAM,OAAO,MAIU;AACrB,MAAI,CAAC,KAAK,SAAU,OAAM,IAAI,MAAM,4CAA4C;AAChF,MAAI,KAAK,SAAS,oBAAoB,KAAK,KACzC,OAAM,IAAI,MACR,uBAAuB,KAAK,KAAK,wBAAwB,KAAK,SAAS,gBAAgB,IACxF;EAEH,MAAM,EAAE,WAAW,OAAO,YAAY,KAAK;EAC3C,MAAM,iBAAiB,KAAK,kBAAkB,IAAI,gBAAgB;AAClE,OAAK,WAAW;AAChB,SAAO,KAAK,QAAQ,WAAW,OAAO,SAAS,gBAAgB,KAAK,WAAW;;CAGjF,MAAc,QACZ,YACA,cACA,SACA,gBACA,YACoB;EACpB,IAAI,QAAQ;EACZ,MAAM,iBAAiB,OAAe,QAAQ;AAE9C,OAAK,IAAI,IAAI,YAAY,IAAI,KAAK,MAAM,QAAQ,KAAK;GACnD,MAAM,OAAO,KAAK,MAAM;GACxB,MAAM,iBAAiB,eAAe,UAAa,MAAM;GACzD,MAAM,UAAU;IAAE;IAAgB;IAAe;AAEjD,OAAI;AACF,YAAQ,KAAK,MAAb;KACE,KAAK;AACH,cAAQ,MAAM,KAAK,QAAQ,KAAK,MAAM,OAAO,SAAS,iBAAiB,aAAa,OAAU;AAC9F,cAAQ,KAAK,KAAK,MAAM;AACxB;KAEF,KAAK,YAAY;MACf,MAAM,MAAM,MAAM,QAAQ,IACxB,KAAK,MAAM,KAAK,MAAM,KAAK,QAAQ,GAAG,OAAO,SAAS,OAAU,CAAC,CAClE;MACD,MAAMC,SAAkC,EAAE;AAC1C,WAAK,MAAM,SAAS,GAAG,QAAQ;AAC7B,eAAQ,EAAE,MAAM,IAAI;AACpB,cAAO,EAAE,MAAM,IAAI;QACnB;AACF,cAAQ;AACR;;KAEF,KAAK;AACH,WAAK,MAAM,CAAC,MAAM,SAAS,KAAK,SAC9B,KAAI,MAAM,KAAK;OAAE,WAAW;OAAO;OAAgB,CAAC,EAAE;AACpD,eAAQ,MAAM,KAAK,QAAQ,MAAM,OAAO,SAAS,iBAAiB,aAAa,OAAU;AACzF,eAAQ,KAAK,MAAM;AACnB;;AAGJ;KAEF,KAAK;AACH,SAAG;AACD,eAAQ,MAAM,KAAK,QAAQ,KAAK,MAAM,OAAO,SAAS,OAAU;AAChE,eAAQ,KAAK,KAAK,MAAM;eACjB,CAAE,MAAM,KAAK,UAAU;OAAE,WAAW;OAAO;OAAgB,CAAC;AACrE;KAEF,KAAK,WAAW;MACd,MAAM,QAAQ,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM;MACpD,MAAMC,MAAiB,EAAE;AACzB,WAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,MAAM,KAAK,QAAQ,KAAK,MAAM,MAAM,SAAS,OAAU,CAAC;AAEnE,cAAQ;AACR,cAAQ,KAAK,KAAK,MAAM;AACxB;;KAEF,KAAK;AACH,cAAQ,KAAK,GAAG,MAAM;AACtB;;AAGJ,iBAAa;YACN,KAAK;AACZ,QAAI,eAAe,eAAe;AAChC,UAAK,WAAW;MACd,WAAW;MACX;MACA;MACA,iBAAiB,IAAI;MACtB;AACD,YAAO;MAAE,QAAQ;MAAa,WAAW;OAAE,QAAQ,IAAI;OAAQ,SAAS,IAAI;OAAS;MAAE,OAAO;MAAS;;AAEzG,WAAO;KAAE,QAAQ;KAAU,OAAO;KAAc,OAAO;KAAS;;;AAIpE,SAAO;GAAE,QAAQ;GAAW,QAAQ;GAAO,OAAO;GAAS;;CAG7D,MAAc,QACZ,MACA,WACA,MACA,YACkB;EAClB,MAAM,WAAW,YAA6B;AAC5C,SAAM,IAAI,cAAc,KAAK,IAAI,QAAQ;;AAE3C,SAAO,KAAK,QAAQ;GAAE;GAAW;GAAY;GAAS,GAAG;GAAM,CAAC;;;AAIpE,SAAgB,eAAe,QAIlB;AACX,QAAO,IAAI,SAAS,OAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redtuma/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Redtuma core: the Redtuma registry, Agent, tools, model routing and workflow engine.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -57,11 +57,11 @@
57
57
  },
58
58
  "repository": {
59
59
  "type": "git",
60
- "url": "git+https://github.com/redtuma-ai/redtuma.git",
60
+ "url": "git+https://github.com/redtuma/redtuma.git",
61
61
  "directory": "packages/core"
62
62
  },
63
63
  "homepage": "https://redtuma.ai",
64
- "bugs": "https://github.com/redtuma-ai/redtuma/issues",
64
+ "bugs": "https://github.com/redtuma/redtuma/issues",
65
65
  "keywords": [
66
66
  "ai",
67
67
  "agents",