Skip to content

Commit 2340289

Browse files
committed
Make DurableAgent#stream() return a messages array
1 parent 1e636e1 commit 2340289

File tree

4 files changed

+81
-3
lines changed

4 files changed

+81
-3
lines changed

.changeset/eleven-roses-enter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@workflow/ai": patch
3+
---
4+
5+
Make `DurableAgent#stream()` return a `messages` array

docs/content/docs/api-reference/workflow-ai/durable-agent.mdx

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export default DurableAgentStreamOptions;`}
8686
- Tools can be implemented as workflow steps (using `"use step"` for automatic retries), or as regular workflow-level logic
8787
- Tools can use core library features like `sleep()` and Hooks within their `execute` functions
8888
- The agent processes tool calls iteratively until completion
89+
- The `stream()` method returns `{ messages }` containing the full conversation history, including initial messages, assistant responses, and tool results
8990

9091
## Examples
9192

@@ -119,7 +120,7 @@ async function weatherAgentWorkflow(userQuery: string) {
119120
system: 'You are a helpful weather assistant. Always provide accurate weather information.',
120121
});
121122

122-
await agent.stream({
123+
const { messages } = await agent.stream({
123124
messages: [
124125
{
125126
role: 'user',
@@ -128,6 +129,12 @@ async function weatherAgentWorkflow(userQuery: string) {
128129
],
129130
writable: getWritable<UIMessageChunk>(),
130131
});
132+
133+
// messages now contains the full conversation history including:
134+
// - initial user message
135+
// - assistant responses (text and tool calls)
136+
// - tool results
137+
console.log('Conversation history:', messages);
131138
}
132139
```
133140

@@ -180,6 +187,52 @@ async function multiToolAgentWorkflow(userQuery: string) {
180187
}
181188
```
182189

190+
### Multi-turn Conversation
191+
192+
```typescript
193+
import { DurableAgent } from '@workflow/ai/agent';
194+
import { z } from 'zod';
195+
196+
async function searchProducts({ query }: { query: string }) {
197+
"use step";
198+
// Search product database
199+
return `Found 3 products matching "${query}"`;
200+
}
201+
202+
async function multiTurnAgentWorkflow() {
203+
'use workflow';
204+
205+
const agent = new DurableAgent({
206+
model: 'anthropic/claude-haiku-4.5',
207+
tools: {
208+
searchProducts: {
209+
description: 'Search for products',
210+
inputSchema: z.object({ query: z.string() }),
211+
execute: searchProducts,
212+
},
213+
},
214+
});
215+
216+
// First user message
217+
let { messages } = await agent.stream({
218+
messages: [
219+
{ role: 'user', content: 'Find me some laptops' }
220+
],
221+
});
222+
223+
// Continue the conversation with the accumulated message history
224+
const result = await agent.stream({
225+
messages: [
226+
...messages,
227+
{ role: 'user', content: 'Which one has the best battery life?' }
228+
],
229+
});
230+
231+
// result.messages now contains the complete conversation history
232+
return result.messages;
233+
}
234+
```
235+
183236
### Tools with Workflow Library Features
184237

185238
```typescript

packages/ai/src/agent/durable-agent.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ export class DurableAgent {
150150
if (!options.preventClose) {
151151
await closeStream(options.writable);
152152
}
153+
154+
// The iterator returns the final conversation prompt (LanguageModelV2Prompt)
155+
// which is compatible with ModelMessage[]
156+
const messages = result.value as ModelMessage[];
157+
158+
return { messages };
153159
}
154160
}
155161

@@ -178,7 +184,7 @@ async function executeTool(
178184
}
179185
const toolResult = await tool.execute(input.value, {
180186
toolCallId: toolCall.toolCallId,
181-
// TODO: pass the proper messages to the tool
187+
// TODO: pass the proper messages to the tool (we'd need to pass them through the iterator)
182188
messages: [],
183189
});
184190

packages/ai/src/agent/stream-text-iterator.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export async function* streamTextIterator({
2323
stopConditions?: ModelStopCondition[] | ModelStopCondition;
2424
}): AsyncGenerator<
2525
LanguageModelV2ToolCall[],
26-
void,
26+
LanguageModelV2Prompt,
2727
LanguageModelV2ToolResultPart[]
2828
> {
2929
const conversationPrompt = [...prompt]; // Create a mutable copy
@@ -70,11 +70,25 @@ export async function* streamTextIterator({
7070
}
7171
}
7272
} else if (finish?.finishReason === 'stop') {
73+
// Add assistant message with text content to the conversation
74+
const textContent = step.content.filter(
75+
(item) => item.type === 'text'
76+
) as Array<{ type: 'text'; text: string }>;
77+
78+
if (textContent.length > 0) {
79+
conversationPrompt.push({
80+
role: 'assistant',
81+
content: textContent,
82+
});
83+
}
84+
7385
done = true;
7486
} else {
7587
throw new Error(`Unexpected finish reason: ${finish?.finishReason}`);
7688
}
7789
}
90+
91+
return conversationPrompt;
7892
}
7993

8094
async function writeToolOutputToUI(

0 commit comments

Comments
 (0)