Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/itchy-memes-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/ai": patch
---

Make `writable` property be required in `DurableAgent#stream()`
18 changes: 16 additions & 2 deletions docs/content/docs/api-reference/workflow-ai/durable-agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ Tool calls can be implemented as workflow steps for automatic retries, or as reg

```typescript lineNumbers
import { DurableAgent } from '@workflow/ai/agent';
import { getWritable } from 'workflow';
import { z } from 'zod';
import type { UIMessageChunk } from 'ai';

async function getWeather({ city }: { city: string }) {
"use step";
Expand All @@ -38,8 +40,13 @@ async function myAgent() {
},
});

// The agent will stream its output to the workflow
// run's default output stream
const writable = getWritable<UIMessageChunk>();

await agent.stream({
messages: [{ role: 'user', content: 'How is the weather in San Francisco?' }],
writable,
});
}
```
Expand Down Expand Up @@ -88,15 +95,16 @@ export default DurableAgentStreamOptions;`
- Tools can be implemented as workflow steps (using `"use step"` for automatic retries), or as regular workflow-level logic
- Tools can use core library features like `sleep()` and Hooks within their `execute` functions
- The agent processes tool calls iteratively until completion
- When no custom `writable` stream is provided, the agent uses the workflow's default writable stream

## Examples

### Basic Agent with Tools

```typescript
import { DurableAgent } from '@workflow/ai/agent';
import { getWritable } from 'workflow';
import { z } from 'zod';
import type { UIMessageChunk } from 'ai';

async function getWeather({ location }: { location: string }) {
"use step";
Expand Down Expand Up @@ -127,6 +135,7 @@ async function weatherAgentWorkflow(userQuery: string) {
content: userQuery,
},
],
writable: getWritable<UIMessageChunk>(),
});
}
```
Expand All @@ -135,7 +144,9 @@ async function weatherAgentWorkflow(userQuery: string) {

```typescript
import { DurableAgent } from '@workflow/ai/agent';
import { getWritable } from 'workflow';
import { z } from 'zod';
import type { UIMessageChunk } from 'ai';

async function getWeather({ location }: { location: string }) {
"use step";
Expand Down Expand Up @@ -173,6 +184,7 @@ async function multiToolAgentWorkflow(userQuery: string) {
content: userQuery,
},
],
writable: getWritable<UIMessageChunk>(),
});
}
```
Expand All @@ -181,8 +193,9 @@ async function multiToolAgentWorkflow(userQuery: string) {

```typescript
import { DurableAgent } from '@workflow/ai/agent';
import { sleep, defineHook } from 'workflow';
import { sleep, defineHook, getWritable } from 'workflow';
import { z } from 'zod';
import type { UIMessageChunk } from 'ai';

// Define a reusable hook type
const approvalHook = defineHook<{ approved: boolean; reason: string }>();
Expand Down Expand Up @@ -238,6 +251,7 @@ async function agentWithLibraryFeaturesWorkflow(userRequest: string) {

await agent.stream({
messages: [{ role: 'user', content: userRequest }],
writable: getWritable<UIMessageChunk>(),
});
}
```
Expand Down
12 changes: 5 additions & 7 deletions packages/ai/src/agent/durable-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
type UIMessageChunk,
} from 'ai';
import { convertToLanguageModelPrompt, standardizePrompt } from 'ai/internal';
import { getWritable } from 'workflow';
import { streamTextIterator } from './stream-text-iterator.js';

/**
Expand Down Expand Up @@ -54,9 +53,9 @@ export interface DurableAgentStreamOptions {
system?: string;

/**
* Optional custom writable stream for handling message chunks. If not provided, a default writable stream will be created using getWritable().
* The writable stream for handling message chunks. Use `getWritable<UIMessageChunk>()` to get the workflow's default output stream.
*/
writable?: WritableStream<UIMessageChunk>;
writable: WritableStream<UIMessageChunk>;

/**
* If true, prevents the writable stream from being closed after streaming completes.
Expand Down Expand Up @@ -97,6 +96,7 @@ export interface DurableAgentStreamOptions {
*
* await agent.stream({
* messages: [{ role: 'user', content: 'What is the weather?' }],
* writable: getWritable<UIMessageChunk>(),
* });
* ```
*/
Expand Down Expand Up @@ -127,12 +127,10 @@ export class DurableAgent {
download: undefined,
});

const writable = options.writable || getWritable();

const iterator = streamTextIterator({
model: this.model,
tools: this.tools,
writable,
writable: options.writable,
prompt: modelPrompt,
stopConditions: options.stopWhen,
});
Expand All @@ -150,7 +148,7 @@ export class DurableAgent {
}

if (!options.preventClose) {
await closeStream(writable);
await closeStream(options.writable);
}
}
}
Expand Down
Loading