@dbos-inc/vercel-ai 0.2.5 → 0.4.4
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.
- package/README.md +111 -87
- package/dist/agent-tool.d.ts +42 -0
- package/dist/agent-tool.d.ts.map +1 -0
- package/dist/agent-tool.js +92 -0
- package/dist/agent-tool.js.map +1 -0
- package/dist/durable-stream.d.ts +112 -0
- package/dist/durable-stream.d.ts.map +1 -0
- package/dist/durable-stream.js +351 -0
- package/dist/durable-stream.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +15 -0
- package/dist/internal.js.map +1 -1
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +27 -26
- package/dist/mcp.js.map +1 -1
- package/dist/middleware.d.ts +11 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +110 -67
- package/dist/middleware.js.map +1 -1
- package/dist/tools.d.ts +15 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +78 -0
- package/dist/tools.js.map +1 -0
- package/package.json +1 -1
- package/src/agent-tool.ts +122 -0
- package/src/durable-stream.ts +395 -0
- package/src/index.ts +12 -1
- package/src/internal.ts +14 -0
- package/src/mcp.ts +28 -25
- package/src/middleware.ts +123 -75
- package/src/tools.ts +86 -0
package/README.md
CHANGED
|
@@ -2,13 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[DBOS](https://docs.dbos.dev/) durable execution for the [Vercel AI SDK](https://ai-sdk.dev/).
|
|
4
4
|
|
|
5
|
-
This package makes AI SDK
|
|
6
|
-
All you have to do is wrap your model with `durableCalls` and run your
|
|
5
|
+
This package makes AI SDK agents durable, backed by your Postgres database.
|
|
6
|
+
All you have to do is wrap your model with `durableCalls` and your tools with `durableTools` and run your agents inside a DBOS workflow.
|
|
7
7
|
Then, this integration automatically checkpoints every action your agents take in Postgres.
|
|
8
|
-
If your process
|
|
9
|
-
|
|
10
|
-
This package is implemented as standard AI SDK [middleware](https://ai-sdk.dev/docs/ai-sdk-core/middleware), so you keep your provider, your model configuration, and the familiar APIs like `generateText`, `streamText`, and `ToolLoopAgent`.
|
|
11
|
-
Durability is transparent to your agent code.
|
|
8
|
+
If your process is interrupted, DBOS replays your agent from its checkpoints so it resumes from where it left off.
|
|
12
9
|
|
|
13
10
|
```ts
|
|
14
11
|
import { DBOS } from '@dbos-inc/dbos-sdk';
|
|
@@ -47,95 +44,120 @@ npm install @dbos-inc/vercel-ai @dbos-inc/dbos-sdk ai
|
|
|
47
44
|
|
|
48
45
|
Requires DBOS v4.21+ or v5, AI SDK v7+, and a Postgres database for DBOS.
|
|
49
46
|
|
|
50
|
-
##
|
|
47
|
+
## Durable Model Calls
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
To durably checkpoint each call you make to a model, wrap your model in `durableCalls`.
|
|
50
|
+
Then, call your model or agent from a workflow:
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
```ts
|
|
53
|
+
import { DBOS } from '@dbos-inc/dbos-sdk';
|
|
54
|
+
import { ToolLoopAgent, wrapLanguageModel } from 'ai';
|
|
55
|
+
import { openai } from '@ai-sdk/openai';
|
|
56
|
+
import { durableCalls } from '@dbos-inc/vercel-ai';
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
const model = wrapLanguageModel({ model: openai('gpt-5'), middleware: durableCalls() });
|
|
59
|
+
const agent = new ToolLoopAgent({ model, instructions: 'You are a helpful research assistant.', tools });
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
const researchAgent = DBOS.registerWorkflow(
|
|
62
|
+
async (question: string) => {
|
|
63
|
+
const result = await agent.stream({ prompt: question });
|
|
64
|
+
for await (const delta of result.textStream) process.stdout.write(delta);
|
|
65
|
+
return await result.text;
|
|
66
|
+
},
|
|
67
|
+
{ name: 'researchAgent' },
|
|
68
|
+
);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
You can parameterize `durableCalls` to configure model call retries and timeouts:
|
|
61
72
|
|
|
62
73
|
```ts
|
|
63
74
|
durableCalls({
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
name?: string; // step name (default: "<provider>.<modelId>.<operation>")
|
|
76
|
+
retriesAllowed?: boolean; // retry failed model calls (default: true)
|
|
77
|
+
maxAttempts?: number; // total attempts when retries are allowed (default: 3)
|
|
78
|
+
intervalSeconds?: number; // delay before first retry (default: 1)
|
|
79
|
+
backoffRate?: number; // exponential backoff multiplier (default: 2)
|
|
80
|
+
shouldRetry?: (error: unknown) => boolean; // default: skip provider-declared non-retryable errors and aborts
|
|
81
|
+
timeoutMS?: number; // per-attempt timeout
|
|
82
|
+
durableStream?: string; // stream each call's output to this durable stream
|
|
83
|
+
include?: { requestBody?: boolean; responseBody?: boolean }; // checkpoint raw provider bodies; match generateText's `include` (default: false)
|
|
71
84
|
});
|
|
72
85
|
```
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
The default `shouldRetry` treats errors the provider marks non-retryable (an AI SDK `APICallError`/`GatewayError` with `isRetryable === false`, e.g. a 401 or an invalid-request 400) and aborts/timeouts as terminal, so they fail fast instead of retrying `maxAttempts` times.
|
|
76
|
-
Pass your own `shouldRetry` to override it, or `retriesAllowed: false` to disable step retries.
|
|
77
|
-
|
|
78
|
-
Because DBOS owns retries by default, pass `maxRetries: 0` to the AI SDK call so retry behavior is governed in one place; otherwise the two compose multiplicatively and each AI SDK retry is a fresh step.
|
|
79
|
-
|
|
80
|
-
## Streaming
|
|
87
|
+
## Durable Streams
|
|
81
88
|
|
|
82
|
-
You can stream
|
|
83
|
-
|
|
84
|
-
As a consequence:
|
|
85
|
-
|
|
86
|
-
- You can safely forward streamed deltas to a UI or terminal, but you should not perform durable steps on them because model responses are not resumable. Instead, run your own durable steps on the complete result (`result.text`) after the stream ends. Tool calls performed by the AI SDK during streaming are already durable because they execute after the model call has been checkpointed.
|
|
87
|
-
- Do not exit a stream before it completes. To stop reading early, either drain the stream (`await result.consumeStream()`) or abort it.
|
|
88
|
-
- To abort early, pass an `abortSignal` to `streamText` and fire it. The abort detaches your consumer, but inside a workflow it does not cut the model call short; the step keeps draining and checkpoints the complete response.
|
|
89
|
-
- The AI SDK's own `timeout` option does not bound a durable model call, because its signal is an abort signal. Bound the call with the step's `timeoutMS` instead (`durableCalls({ timeoutMS })`), which DBOS tears down deterministically.
|
|
89
|
+
You can **durably stream** agent or model output so it can be read by an external client or UI.
|
|
90
|
+
To do this, configure `durableCalls` or `durableTools`/`durableMCPTools` with a durable stream name:
|
|
90
91
|
|
|
91
92
|
```ts
|
|
92
|
-
import { streamText } from 'ai';
|
|
93
|
-
import { durableCalls } from '@dbos-inc/vercel-ai';
|
|
93
|
+
import { createUIMessageStreamResponse, streamText } from 'ai';
|
|
94
|
+
import { durableCalls, durableTools, readDurableStream } from '@dbos-inc/vercel-ai';
|
|
94
95
|
|
|
95
|
-
const model = wrapLanguageModel({
|
|
96
|
-
|
|
97
|
-
middleware: durableCalls({ retriesAllowed: true, maxAttempts: 5 }),
|
|
98
|
-
});
|
|
96
|
+
const model = wrapLanguageModel({ model: openai('gpt-5'), middleware: durableCalls({ durableStream: 'ui' }) });
|
|
97
|
+
const tools = durableTools(myTools, { durableStream: 'ui' });
|
|
99
98
|
|
|
100
|
-
const
|
|
101
|
-
const result = streamText({ model,
|
|
102
|
-
for await (const delta of result.textStream) {
|
|
103
|
-
process.stdout.write(delta);
|
|
104
|
-
}
|
|
99
|
+
const chatTurn = DBOS.registerWorkflow(async (messages: ModelMessage[]) => {
|
|
100
|
+
const result = streamText({ model, messages, tools, stopWhen: stepCountIs(10) });
|
|
105
101
|
return await result.text;
|
|
106
|
-
}, { name: '
|
|
102
|
+
}, { name: 'chatTurn' });
|
|
103
|
+
|
|
104
|
+
const handle = await DBOS.startWorkflow(chatTurn)(messages);
|
|
105
|
+
return createUIMessageStreamResponse({
|
|
106
|
+
stream: readDurableStream({ workflowID: handle.workflowID, key: 'ui', messageId }),
|
|
107
|
+
});
|
|
107
108
|
```
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
You can read from a durable stream using `readDurableStream`, for example to stream it to a UI.
|
|
111
|
+
It emits a stream of AI SDK `UIMessageChunk`.
|
|
112
|
+
You can also pass a `DBOSClient` into `readDurableStream` to read it from a different process.
|
|
113
|
+
|
|
114
|
+
You can write your own data to a stream with `writeDurableStream(key, chunks)`.
|
|
115
|
+
Your streams are closed when your workflow finishes; you can also close a stream early using `closeDurableStream`.
|
|
116
|
+
|
|
117
|
+
If a workflow is interrupted during a model call, when the workflow recovers, it restarts the model call and streams its output again.
|
|
118
|
+
Readers that connect afterwards see the model's output once; live readers receive a transient `data-dbos-superseded` chunk indicating the model call has been restarted.
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
## Durable Tools
|
|
121
|
+
|
|
122
|
+
To durably checkpoint your agents' tool calls, wrap them in `durableTools`:
|
|
113
123
|
|
|
114
124
|
```ts
|
|
115
125
|
import { tool, stepCountIs } from 'ai';
|
|
126
|
+
import { durableTools } from '@dbos-inc/vercel-ai';
|
|
116
127
|
import { z } from 'zod';
|
|
117
128
|
|
|
129
|
+
const tools = durableTools({
|
|
130
|
+
getWeather: tool({
|
|
131
|
+
description: 'Get the weather for a city',
|
|
132
|
+
inputSchema: z.object({ city: z.string() }),
|
|
133
|
+
execute: ({ city }) => fetchWeather(city),
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
136
|
+
|
|
118
137
|
const agent = DBOS.registerWorkflow(async (question: string) => {
|
|
119
|
-
const result = await generateText({
|
|
120
|
-
model,
|
|
121
|
-
prompt: question,
|
|
122
|
-
tools: {
|
|
123
|
-
getWeather: tool({
|
|
124
|
-
description: 'Get the weather for a city',
|
|
125
|
-
inputSchema: z.object({ city: z.string() }),
|
|
126
|
-
execute: ({ city }) => DBOS.runStep(() => fetchWeather(city), { name: 'getWeather' }),
|
|
127
|
-
}),
|
|
128
|
-
},
|
|
129
|
-
stopWhen: stepCountIs(10),
|
|
130
|
-
});
|
|
138
|
+
const result = await generateText({ model, prompt: question, tools, stopWhen: stepCountIs(10) });
|
|
131
139
|
return result.text;
|
|
132
140
|
}, { name: 'weatherAgent' });
|
|
133
141
|
```
|
|
134
142
|
|
|
135
|
-
|
|
143
|
+
You can pass step configuration (such as timeouts or retries) to `durableTools`.
|
|
144
|
+
You can set defaults for all tools or configure tools individually.
|
|
145
|
+
Retries are off by default.
|
|
136
146
|
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
```ts
|
|
148
|
+
const tools = durableTools(myTools, {
|
|
149
|
+
timeoutMS: 30_000,
|
|
150
|
+
tools: {
|
|
151
|
+
getWeather: { retriesAllowed: true, maxAttempts: 3 },
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
When using durable tools, to ensure the ordering of parallel tool calls is consistent during recovery, do not await I/O in callbacks that run before a tool executes, such as `onToolExecutionStart`.
|
|
157
|
+
|
|
158
|
+
### Durable MCP Tools
|
|
159
|
+
|
|
160
|
+
`durableMCPTools` wraps an [MCP](https://modelcontextprotocol.io/) client (for example, from [`@ai-sdk/mcp`](https://www.npmjs.com/package/@ai-sdk/mcp)) so both the tool listing and every tool call run as durable steps:
|
|
139
161
|
|
|
140
162
|
```ts
|
|
141
163
|
import { createMCPClient } from '@ai-sdk/mcp';
|
|
@@ -157,30 +179,34 @@ const tools = await durableMCPTools(mcpClient, {
|
|
|
157
179
|
});
|
|
158
180
|
```
|
|
159
181
|
|
|
160
|
-
##
|
|
182
|
+
## Durable Subagents
|
|
161
183
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
To guard against nondeterminism, this integration throws an error if it detects concurrent durable model calls in the same workflow.
|
|
165
|
-
Sequential calls (including a normal tool-calling loop, where each model call completes before the next begins) are unaffected.
|
|
166
|
-
|
|
167
|
-
To fan out model calls in parallel, give each its own **child workflow**:
|
|
184
|
+
You can delegate complex tasks to **subagents**, which act as tools for their "parent" agent.
|
|
185
|
+
To create a durable subagent, wrap your agent in `agentTool`, then pass it into `durableTools` just like any other tool:
|
|
168
186
|
|
|
169
187
|
```ts
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
{ name: 'summarizeOne' },
|
|
173
|
-
);
|
|
188
|
+
import { ToolLoopAgent } from 'ai';
|
|
189
|
+
import { agentTool, durableTools } from '@dbos-inc/vercel-ai';
|
|
174
190
|
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
191
|
+
const researcher = new ToolLoopAgent({ model, instructions: 'Research thoroughly.', tools: researchTools });
|
|
192
|
+
|
|
193
|
+
const research = agentTool({
|
|
194
|
+
name: 'research', // subagent name
|
|
195
|
+
description: 'Research a question in depth',
|
|
196
|
+
inputSchema: z.object({ question: z.string() }),
|
|
197
|
+
agent: researcher,
|
|
198
|
+
prompt: ({ question }) => question, // tool input → prompt (or ModelMessage[])
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const tools = durableTools({ research, getWeather }, { durableStream: 'ui' });
|
|
202
|
+
const orchestrator = new ToolLoopAgent({ model, tools });
|
|
181
203
|
```
|
|
182
204
|
|
|
183
|
-
|
|
205
|
+
Internally, subagents are implemented as child workflows of the parent agent workflow, so each call has its own checkpoints and parallel calls are safe.
|
|
206
|
+
Call `agentTool` before `DBOS.launch()`, since it registers that workflow.
|
|
207
|
+
By default, the tool returns the subagent's final text; you can configure this with the `output` parameter.
|
|
208
|
+
|
|
209
|
+
## Durable Embedding Models
|
|
184
210
|
|
|
185
211
|
`durableEmbeddingCalls` enables durable calls to embedding models:
|
|
186
212
|
|
|
@@ -193,12 +219,10 @@ const embeddingModel = wrapEmbeddingModel({
|
|
|
193
219
|
middleware: durableEmbeddingCalls({ retriesAllowed: true }),
|
|
194
220
|
});
|
|
195
221
|
|
|
196
|
-
const { embeddings } = await embedMany({ model: embeddingModel, values: chunks
|
|
222
|
+
const { embeddings } = await embedMany({ model: embeddingModel, values: chunks });
|
|
197
223
|
```
|
|
198
224
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
## Images
|
|
225
|
+
## Durable Image Models
|
|
202
226
|
|
|
203
227
|
`durableImageCalls` makes image generation durable:
|
|
204
228
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FlexibleSchema, ModelMessage, Tool } from 'ai' with { 'resolution-mode': 'import' };
|
|
2
|
+
/** Marks a tool built by agentTool: durableTools leaves it unwrapped (it is a child workflow, not a step) and binds its durable stream. */
|
|
3
|
+
export declare const AGENT_TOOL: unique symbol;
|
|
4
|
+
type StreamingAgent = {
|
|
5
|
+
stream(options: {
|
|
6
|
+
prompt: string;
|
|
7
|
+
} | {
|
|
8
|
+
messages: ModelMessage[];
|
|
9
|
+
}): PromiseLike<{
|
|
10
|
+
consumeStream(): PromiseLike<void>;
|
|
11
|
+
readonly text: PromiseLike<string>;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
export interface AgentToolOptions<INPUT, AGENT extends StreamingAgent, OUTPUT> {
|
|
15
|
+
/** Name of the child workflow; must be unique. */
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
inputSchema: FlexibleSchema<INPUT>;
|
|
19
|
+
agent: AGENT;
|
|
20
|
+
/** Turns the tool's input into the sub-agent's prompt. */
|
|
21
|
+
prompt: (input: INPUT) => string | ModelMessage[];
|
|
22
|
+
/** Turns the sub-agent's result into the tool's output (default: its final text); must be serializable. */
|
|
23
|
+
output?: (result: Awaited<ReturnType<AGENT['stream']>>) => OUTPUT | Promise<OUTPUT>;
|
|
24
|
+
/** Record the call in this durable stream: a `data-dbos-subagent` part naming the child workflow, then its output. */
|
|
25
|
+
durableStream?: string;
|
|
26
|
+
/** Name of a DBOS queue to run each child on, e.g. to bound how many sub-agents run at once. */
|
|
27
|
+
queue?: string;
|
|
28
|
+
/** Workflow timeout for each child. */
|
|
29
|
+
timeoutMS?: number;
|
|
30
|
+
}
|
|
31
|
+
export type AgentTool<INPUT, OUTPUT> = Tool<INPUT, OUTPUT> & {
|
|
32
|
+
/** The registered child workflow; call it directly to run the sub-agent without a model in the loop. */
|
|
33
|
+
workflow: (input: INPUT) => Promise<OUTPUT>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Wraps an agent as a tool whose every call runs as a child workflow: durable at model-call granularity, with its own
|
|
37
|
+
* concurrency guard, safe to call in parallel, and visible as a child in the parent's step list. Call it at module load,
|
|
38
|
+
* before `DBOS.launch()`, since it registers the child workflow.
|
|
39
|
+
*/
|
|
40
|
+
export declare function agentTool<INPUT, AGENT extends StreamingAgent, OUTPUT = string>(options: AgentToolOptions<INPUT, AGENT, OUTPUT>): AgentTool<INPUT, OUTPUT>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=agent-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tool.d.ts","sourceRoot":"","sources":["../src/agent-tool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,OAAO,iBAAiB,EAAE,QAAQ,EAAE,CAAC;AAIlG,2IAA2I;AAC3I,eAAO,MAAM,UAAU,EAAE,OAAO,MAAoD,CAAC;AAkBrF,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,GAAG,WAAW,CAAC;QAAE,aAAa,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;CAC7J,CAAC;AAEF,MAAM,WAAW,gBAAgB,CAAC,KAAK,EAAE,KAAK,SAAS,cAAc,EAAE,MAAM;IAC3E,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACnC,KAAK,EAAE,KAAK,CAAC;IACb,0DAA0D;IAC1D,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,YAAY,EAAE,CAAC;IAClD,2GAA2G;IAC3G,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpF,sHAAsH;IACtH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,SAAS,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG;IAC3D,wGAAwG;IACxG,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,SAAS,cAAc,EAAE,MAAM,GAAG,MAAM,EAC5E,OAAO,EAAE,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAC9C,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAa1B"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AGENT_TOOL = void 0;
|
|
4
|
+
exports.agentTool = agentTool;
|
|
5
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
6
|
+
const dbos_sdk_1 = require("@dbos-inc/dbos-sdk");
|
|
7
|
+
const durable_stream_1 = require("./durable-stream");
|
|
8
|
+
const internal_1 = require("./internal");
|
|
9
|
+
/** Marks a tool built by agentTool: durableTools leaves it unwrapped (it is a child workflow, not a step) and binds its durable stream. */
|
|
10
|
+
exports.AGENT_TOOL = Symbol.for('@dbos-inc/vercel-ai/agentTool');
|
|
11
|
+
// Captured at module load, outside any workflow: a cancellation triggered by an abort must not claim a function id in the parent's log.
|
|
12
|
+
const outsideWorkflow = node_async_hooks_1.AsyncLocalStorage.snapshot();
|
|
13
|
+
// The child's row appears shortly after the call starts and a cancel of a missing row is a no-op, so wait for it, giving up once the call has settled.
|
|
14
|
+
async function cancelChild(childID, settled) {
|
|
15
|
+
for (let i = 0; i < 100 && !settled(); i++) {
|
|
16
|
+
if (await dbos_sdk_1.DBOS.getWorkflowStatus(childID)) {
|
|
17
|
+
// The child's own sub-agents are its children; without the cascade they would run on with nobody awaiting them.
|
|
18
|
+
await dbos_sdk_1.DBOS.cancelWorkflow(childID, { cancelChildren: true });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Wraps an agent as a tool whose every call runs as a child workflow: durable at model-call granularity, with its own
|
|
26
|
+
* concurrency guard, safe to call in parallel, and visible as a child in the parent's step list. Call it at module load,
|
|
27
|
+
* before `DBOS.launch()`, since it registers the child workflow.
|
|
28
|
+
*/
|
|
29
|
+
function agentTool(options) {
|
|
30
|
+
const { name, agent, prompt, output } = options;
|
|
31
|
+
const run = async (input) => {
|
|
32
|
+
const request = prompt(input);
|
|
33
|
+
// stream, not generate: only streamed calls write to a durable stream.
|
|
34
|
+
const result = await agent.stream(typeof request === 'string' ? { prompt: request } : { messages: request });
|
|
35
|
+
await result.consumeStream();
|
|
36
|
+
return output ? await output(result) : (await result.text);
|
|
37
|
+
};
|
|
38
|
+
const registered = dbos_sdk_1.DBOS.registerWorkflow(run, { name });
|
|
39
|
+
// Unbound on purpose: DBOS's registered invoker reads `this`, and as a method `this` would be the tool object.
|
|
40
|
+
const workflow = (input) => registered(input);
|
|
41
|
+
return build(options, registered, workflow, options.durableStream);
|
|
42
|
+
}
|
|
43
|
+
function build(options, registered, workflow, durableStream) {
|
|
44
|
+
const { name, description, inputSchema, queue, timeoutMS } = options;
|
|
45
|
+
const execute = async (input, execOptions) => {
|
|
46
|
+
if (!(0, internal_1.isInWorkflowFunction)())
|
|
47
|
+
return workflow(input);
|
|
48
|
+
const { toolCallId } = execOptions;
|
|
49
|
+
// The tool call id comes from the checkpointed model output, so the child id is the same on replay and known for cancellation.
|
|
50
|
+
const childID = `${dbos_sdk_1.DBOS.workflowID}-${toolCallId}`;
|
|
51
|
+
// Start and getResult each reserve their function id synchronously here, so parallel calls replay in order.
|
|
52
|
+
const started = dbos_sdk_1.DBOS.startWorkflow(registered, { workflowID: childID, queueName: queue, timeoutMS })(input);
|
|
53
|
+
const pending = dbos_sdk_1.DBOS.getResult(childID);
|
|
54
|
+
started.catch(() => { });
|
|
55
|
+
pending.catch(() => { });
|
|
56
|
+
let settled = false;
|
|
57
|
+
const cancel = () => void outsideWorkflow(() => cancelChild(childID, () => settled)).catch(() => { });
|
|
58
|
+
execOptions.abortSignal?.addEventListener('abort', cancel, { once: true });
|
|
59
|
+
if (execOptions.abortSignal?.aborted)
|
|
60
|
+
cancel();
|
|
61
|
+
try {
|
|
62
|
+
if (durableStream) {
|
|
63
|
+
await (0, durable_stream_1.writeDurableStream)(durableStream, [
|
|
64
|
+
{ type: 'data-dbos-subagent', id: toolCallId, data: { toolCallId, workflowID: childID, name } },
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
await started;
|
|
68
|
+
const result = (await pending);
|
|
69
|
+
// A tool record, not a raw chunk, so the reader masks a sub-agent's error text like any other tool's.
|
|
70
|
+
if (durableStream)
|
|
71
|
+
await (0, durable_stream_1.writeToolRecord)(durableStream, toolCallId, { output: result });
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (durableStream)
|
|
76
|
+
await (0, durable_stream_1.writeToolRecord)(durableStream, toolCallId, { errorText: error instanceof Error ? error.message : String(error) });
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
settled = true;
|
|
81
|
+
execOptions.abortSignal?.removeEventListener('abort', cancel);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
description,
|
|
86
|
+
inputSchema,
|
|
87
|
+
execute,
|
|
88
|
+
workflow,
|
|
89
|
+
[exports.AGENT_TOOL]: (key) => build(options, registered, workflow, key),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=agent-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tool.js","sourceRoot":"","sources":["../src/agent-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { UIMessageChunk } from 'ai' with { 'resolution-mode': 'import' };
|
|
2
|
+
import type { LanguageModelV4FinishReason, LanguageModelV4StreamPart } from '@ai-sdk/provider' with { 'resolution-mode': 'import' };
|
|
3
|
+
/** Durable stream config: the DBOS stream key, or the key plus batching limits for the model step's writes. */
|
|
4
|
+
export type DurableStreamOptions = string | {
|
|
5
|
+
key: string;
|
|
6
|
+
maxBatchParts?: number;
|
|
7
|
+
maxBatchDelayMs?: number;
|
|
8
|
+
};
|
|
9
|
+
/** One DBOS stream value; the reader turns these into AI SDK UI message chunks. */
|
|
10
|
+
export type DurableStreamRecord = {
|
|
11
|
+
kind: 'model';
|
|
12
|
+
step: number;
|
|
13
|
+
attempt: string;
|
|
14
|
+
parts: LanguageModelV4StreamPart[];
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'model-end';
|
|
17
|
+
step: number;
|
|
18
|
+
attempt: string;
|
|
19
|
+
finishReason?: LanguageModelV4FinishReason;
|
|
20
|
+
aborted?: true;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'tool';
|
|
23
|
+
step: number;
|
|
24
|
+
attempt: number;
|
|
25
|
+
toolCallId: string;
|
|
26
|
+
output?: unknown;
|
|
27
|
+
errorText?: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'ui';
|
|
30
|
+
step?: number;
|
|
31
|
+
attempt?: number;
|
|
32
|
+
chunks: UIMessageChunk[];
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'end';
|
|
35
|
+
finishReason: string;
|
|
36
|
+
};
|
|
37
|
+
interface ResolvedDurableStream {
|
|
38
|
+
key: string;
|
|
39
|
+
maxBatchParts: number;
|
|
40
|
+
maxBatchDelayMs: number;
|
|
41
|
+
}
|
|
42
|
+
export declare function resolveDurableStream(options: DurableStreamOptions | undefined): ResolvedDurableStream | undefined;
|
|
43
|
+
/** Batches a live model step's parts into step-scope stream writes; nothing is written on replay because the step body does not run. */
|
|
44
|
+
export declare class ModelStreamWriter {
|
|
45
|
+
private readonly config;
|
|
46
|
+
private pending;
|
|
47
|
+
private chain;
|
|
48
|
+
private failure;
|
|
49
|
+
private timer;
|
|
50
|
+
private readonly step;
|
|
51
|
+
private readonly attempt;
|
|
52
|
+
constructor(config: ResolvedDurableStream);
|
|
53
|
+
push(part: LanguageModelV4StreamPart): void;
|
|
54
|
+
/** Flushes, records how the call ended, and resolves once every write is durable; a write that failed after retries fails the call here. */
|
|
55
|
+
end(outcome: {
|
|
56
|
+
finishReason: LanguageModelV4FinishReason;
|
|
57
|
+
} | {
|
|
58
|
+
aborted: true;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
/** After a failure: flush what streamed so the record matches what the consumer saw; the stream's end then comes from the workflow's status. */
|
|
61
|
+
abandon(): Promise<void>;
|
|
62
|
+
private flush;
|
|
63
|
+
private write;
|
|
64
|
+
}
|
|
65
|
+
/** Records a tool call's outcome from inside its step. */
|
|
66
|
+
export declare function writeToolRecord(key: string, toolCallId: string, outcome: {
|
|
67
|
+
output: unknown;
|
|
68
|
+
} | {
|
|
69
|
+
errorText: string;
|
|
70
|
+
}): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Appends UI message chunks to a durable stream. From a step the write is cheap and at-least-once, so give data parts
|
|
73
|
+
* stable ids; from workflow code it is a checkpointed step, so the number of calls must be deterministic.
|
|
74
|
+
*/
|
|
75
|
+
export declare function writeDurableStream(key: string, chunks: UIMessageChunk[]): Promise<void>;
|
|
76
|
+
/** Marks the end of the turn explicitly and closes the stream; without it the reader infers the end from the last model call or the workflow's status. */
|
|
77
|
+
export declare function closeDurableStream(key: string, finishReason?: string): Promise<void>;
|
|
78
|
+
/** What the reader needs from DBOS: the `DBOS` class in a launched process, or a `DBOSClient` anywhere else. */
|
|
79
|
+
export interface DurableStreamSource {
|
|
80
|
+
readStream<T>(workflowID: string, key: string, options?: {
|
|
81
|
+
offset?: number;
|
|
82
|
+
}): AsyncGenerator<T, void, unknown>;
|
|
83
|
+
readStreamOffset<T>(workflowID: string, key: string, offset: number, options?: {
|
|
84
|
+
timeoutSeconds?: number;
|
|
85
|
+
}): Promise<T>;
|
|
86
|
+
retrieveWorkflow(workflowID: string): {
|
|
87
|
+
getStatus(): Promise<{
|
|
88
|
+
status: string;
|
|
89
|
+
error?: unknown;
|
|
90
|
+
} | null>;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export interface ReadDurableStreamOptions {
|
|
94
|
+
workflowID: string;
|
|
95
|
+
key: string;
|
|
96
|
+
/** Id for the `start` chunk; omitted on a resume (`offset` > 0). */
|
|
97
|
+
messageId?: string;
|
|
98
|
+
/** Number of records already consumed, from the last `data-dbos-offset` chunk. */
|
|
99
|
+
offset?: number;
|
|
100
|
+
/** Defaults to `DBOS`; pass a `DBOSClient` to read from a process that has not launched DBOS. */
|
|
101
|
+
client?: DurableStreamSource;
|
|
102
|
+
/** Emit reasoning parts (default true, as in the AI SDK). */
|
|
103
|
+
sendReasoning?: boolean;
|
|
104
|
+
/** Emit source parts (default false, as in the AI SDK). */
|
|
105
|
+
sendSources?: boolean;
|
|
106
|
+
/** Text sent to clients for a workflow or tool error; defaults to a generic message, as in the AI SDK, so server details stay private. */
|
|
107
|
+
onError?: (error: unknown) => string;
|
|
108
|
+
}
|
|
109
|
+
/** Reads a durable stream as AI SDK UI message chunks, live or after the fact, resuming from `offset`. */
|
|
110
|
+
export declare function readDurableStream(options: ReadDurableStreamOptions): ReadableStream<UIMessageChunk>;
|
|
111
|
+
export {};
|
|
112
|
+
//# sourceMappingURL=durable-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-stream.d.ts","sourceRoot":"","sources":["../src/durable-stream.ts"],"names":[],"mappings":"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"}
|