Skip to content

Commit 79a8e7f

Browse files
authored
feat(agent): support abortSignal in createAgentUIStream (#10518)
## Background `abortSignal` has been added to `AgentCallParameters` but was not exposed in the agent UI stream helpers. ## Summary Forward `abortSignal`.
1 parent 1b21131 commit 79a8e7f

File tree

7 files changed

+68
-8
lines changed

7 files changed

+68
-8
lines changed

.changeset/old-drinks-prove.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat(agent): support abortSignal in createAgentUIStream

content/docs/07-reference/01-ai-sdk-core/17-create-agent-ui-stream.mdx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ const agent = new ToolLoopAgent({
2222
tools: { weather: weatherTool, calculator: calculatorTool },
2323
});
2424

25-
export async function* streamAgent(messages: unknown[]) {
25+
export async function* streamAgent(
26+
messages: unknown[],
27+
abortSignal?: AbortSignal,
28+
) {
2629
const stream = await createAgentUIStream({
2730
agent,
2831
messages,
32+
abortSignal,
2933
// ...other options
3034
});
3135

@@ -53,6 +57,13 @@ export async function* streamAgent(messages: unknown[]) {
5357
description:
5458
'Array of input UI messages sent to the agent (e.g., from user/assistant).',
5559
},
60+
{
61+
name: 'abortSignal',
62+
type: 'AbortSignal',
63+
isRequired: false,
64+
description:
65+
'Optional abort signal to cancel the agent streaming, e.g., in response to client disconnection.',
66+
},
5667
{
5768
name: '...options',
5869
type: 'UIMessageStreamOptions',
@@ -72,30 +83,36 @@ A `Promise<AsyncIterableStream<UIMessageChunk>>`, where each yielded value is a
7283
```ts
7384
import { createAgentUIStream } from 'ai';
7485

86+
const controller = new AbortController();
87+
7588
const stream = await createAgentUIStream({
7689
agent,
7790
messages: [{ role: 'user', content: 'What is the weather in SF today?' }],
91+
abortSignal: controller.signal,
7892
sendStart: true,
7993
});
8094

8195
for await (const chunk of stream) {
8296
// Process each UI message chunk (e.g., send to client)
8397
console.log(chunk);
8498
}
99+
100+
// Call controller.abort() to stop streaming early if needed.
85101
```
86102

87103
## How It Works
88104

89105
1. **Message Validation:** The incoming array of `messages` is validated and normalized according to the agent's tools and requirements. Invalid messages will cause an error.
90106
2. **Model Message Conversion:** The validated UI messages are converted into the model message format the agent expects.
91-
3. **Agent Streaming:** The agent's `.stream({ prompt })` method is invoked to produce a low-level result stream.
107+
3. **Agent Streaming:** The agent's `.stream({ prompt, abortSignal })` method is invoked to produce a low-level result stream. If an `abortSignal` is provided and triggered, streaming will be canceled promptly.
92108
4. **UI Message Stream:** That result stream is exposed as a streaming async iterable of UI message chunks.
93109

94110
## Notes
95111

96112
- The agent **must** define its `tools` and a `.stream({ prompt })` method.
97113
- This utility returns an async iterator for full streaming flexibility. To produce an HTTP response, see [`createAgentUIStreamResponse`](/docs/reference/ai-sdk-core/create-agent-ui-stream-response) or [`pipeAgentUIStreamToResponse`](/docs/reference/ai-sdk-core/pipe-agent-ui-stream-to-response).
98114
- You can provide UI message stream options (see [`UIMessageStreamOptions`](/docs/reference/ai-sdk-core/ui-message-stream-options)) for fine-grained control over the output.
115+
- To cancel a streaming agent operation, supply an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) via the `abortSignal` option.
99116

100117
## See Also
101118

content/docs/07-reference/01-ai-sdk-core/18-create-agent-ui-stream-response.mdx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@ const agent = new ToolLoopAgent({
2525
tools: { weather: weatherTool, calculator: calculatorTool },
2626
});
2727

28+
// Typical usage with streaming options
2829
export async function POST(request: Request) {
2930
const { messages } = await request.json();
3031

31-
// Stream agent response as an HTTP Response with UI messages
32+
// Optional: Use abortSignal for cancellation support (client-side disconnects)
33+
const abortController = new AbortController();
34+
3235
return createAgentUIStreamResponse({
3336
agent,
3437
messages,
35-
// ...other optional streaming options
38+
abortSignal: abortController.signal, // optional
39+
// ...other UIMessageStreamOptions like sendSources, includeUsage, experimental_transform, etc.
3640
});
3741
}
3842
```
@@ -55,12 +59,19 @@ export async function POST(request: Request) {
5559
description:
5660
'Array of input UI messages sent to the agent (typically user and assistant message objects).',
5761
},
62+
{
63+
name: 'abortSignal',
64+
type: 'AbortSignal',
65+
isRequired: false,
66+
description:
67+
'Optional abort signal to cancel streaming, e.g., when client disconnects. Useful for long-running or cancelable requests.',
68+
},
5869
{
5970
name: '...options',
6071
type: 'UIMessageStreamOptions',
6172
isRequired: false,
6273
description:
63-
'Additional options for customizing UI message streaming. For example, controlling source inclusion or stream behavior.',
74+
'Additional UI message streaming options, such as `sendSources`, `includeUsage`, `experimental_transform`, etc. See [`UIMessageStreamOptions`](/docs/reference/ai-sdk-core/ui-message-stream-options) for details.',
6475
},
6576
]}
6677
/>
@@ -82,6 +93,8 @@ export async function POST(request: Request) {
8293
agent: MyCustomAgent,
8394
messages,
8495
sendSources: true, // optionally include sources in the UI message stream
96+
includeUsage: true, // include token usage details if enabled by the agent
97+
// Optionally, provide abortSignal for cancellation and other stream options
8598
});
8699
}
87100
```
@@ -99,7 +112,7 @@ Under the hood, this function uses the internal `createAgentUIStream` utility an
99112

100113
- The agent **must** define its `tools` and implement `.stream({ prompt })`.
101114
- **Do not use in the browser**; call this from backend/API/server code only.
102-
- You can provide additional UI message streaming options (see [`UIMessageStreamOptions`](/docs/reference/ai-sdk-core/ui-message-stream-options)) to customize the response.
115+
- You can provide additional UI message streaming options (see [`UIMessageStreamOptions`](/docs/reference/ai-sdk-core/ui-message-stream-options)) to customize the response, including experimental stream transforms.
103116
- The returned `Response` leverages [Readable Streams](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). Make sure your client or framework can consume streamed HTTP responses.
104117

105118
## See Also

content/docs/07-reference/01-ai-sdk-core/18-pipe-agent-ui-stream-to-response.mdx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@ import { MyCustomAgent } from './agent';
2323
export async function handler(req, res) {
2424
const { messages } = JSON.parse(req.body);
2525

26+
// Optional: Use abortSignal for request cancellation support
27+
const abortController = new AbortController();
28+
2629
await pipeAgentUIStreamToResponse({
2730
response: res, // Node.js ServerResponse
2831
agent: MyCustomAgent,
2932
messages,
33+
abortSignal: abortController.signal, // optional, see notes
3034
// ...other optional streaming options
3135
});
3236
}
@@ -57,6 +61,13 @@ export async function handler(req, res) {
5761
description:
5862
'Array of input UI messages sent to the agent (typically user and assistant message objects).',
5963
},
64+
{
65+
name: 'abortSignal',
66+
type: 'AbortSignal',
67+
isRequired: false,
68+
description:
69+
'Optional abort signal to cancel streaming (e.g., when the client disconnects). Useful for enabling cancellation of long-running or streaming agent responses. Provide an instance of [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) (for example, from an `AbortController`).',
70+
},
6071
{
6172
name: '...options',
6273
type: 'UIMessageStreamResponseInit & UIMessageStreamOptions',
@@ -77,14 +88,18 @@ A `Promise<void>`. This function returns a promise that resolves when piping the
7788
import { pipeAgentUIStreamToResponse } from 'ai';
7889
import { openaiWebSearchAgent } from './openai-web-search-agent';
7990

80-
// Hono/Express handler signature
8191
app.post('/chat', async (req, res) => {
8292
const { messages } = await getJsonBody(req);
8393

94+
// Optionally use abortSignal for cancellation on disconnect, etc.
95+
const abortController = new AbortController();
96+
// e.g., tie abortController to request lifecycle/disconnect detection as needed
97+
8498
await pipeAgentUIStreamToResponse({
8599
response: res,
86100
agent: openaiWebSearchAgent,
87101
messages,
102+
abortSignal: abortController.signal, // optional
88103
// status: 200,
89104
// headers: { 'X-Custom': 'foo' },
90105
// ...additional streaming options
@@ -95,10 +110,12 @@ app.post('/chat', async (req, res) => {
95110
## How It Works
96111

97112
1. **Streams Output:** The function creates a UI message stream from the agent and efficiently pipes it to the provided Node.js `ServerResponse`, setting appropriate HTTP headers (including content type and streaming-friendly headers) and status.
98-
2. **No Response Object:** Unlike serverless `Response`-returning APIs, this function does _not_ return a Response object. It writes streaming bytes directly to the Node.js response. This is more memory- and latency-efficient for Node.js server frameworks.
113+
2. **Abort Support:** If you provide an `abortSignal`, you can cancel the streaming response (for example, when a client disconnects or a timeout occurs), improving resource usage and responsiveness.
114+
3. **No Response Object:** Unlike serverless `Response`-returning APIs, this function does _not_ return a Response object. It writes streaming bytes directly to the Node.js response. This is more memory- and latency-efficient for Node.js server frameworks.
99115

100116
## Notes
101117

118+
- **abortSignal for Cancellation:** Use `abortSignal` to stop agent and stream processing early, improving robustness for long-running connections. In frameworks like Express or Hono, tie this to your server's disconnect or timeout events when possible.
102119
- **Only for Node.js:** This function is intended for use in Node.js environments with access to `ServerResponse` objects, not for Edge/serverless/server-side frameworks using web `Response` objects.
103120
- **Streaming Support:** Ensure your client and reverse proxy/server infrastructure support streaming HTTP responses.
104121
- Supports both Hono (`@hono/node-server`), Express, and similar Node.js frameworks.

packages/ai/src/agent/create-agent-ui-stream-response.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export async function createAgentUIStreamResponse<
2929
}: {
3030
agent: Agent<CALL_OPTIONS, TOOLS, OUTPUT>;
3131
messages: unknown[];
32+
abortSignal?: AbortSignal;
3233
options?: CALL_OPTIONS;
3334
experimental_transform?:
3435
| StreamTextTransform<TOOLS>

packages/ai/src/agent/create-agent-ui-stream.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import { Agent } from './agent';
1313
*
1414
* @param agent - The agent to run.
1515
* @param messages - The input UI messages.
16+
* @param abortSignal - The abort signal. Optional.
17+
* @param options - The options for the agent.
18+
* @param experimental_transform - The stream transformations. Optional.
1619
*
1720
* @returns The UI message stream.
1821
*/
@@ -25,11 +28,13 @@ export async function createAgentUIStream<
2528
agent,
2629
messages,
2730
options,
31+
abortSignal,
2832
experimental_transform,
2933
...uiMessageStreamOptions
3034
}: {
3135
agent: Agent<CALL_OPTIONS, TOOLS, OUTPUT>;
3236
messages: unknown[];
37+
abortSignal?: AbortSignal;
3338
options?: CALL_OPTIONS;
3439
experimental_transform?:
3540
| StreamTextTransform<TOOLS>
@@ -55,6 +60,7 @@ export async function createAgentUIStream<
5560
const result = await agent.stream({
5661
prompt: modelMessages,
5762
options: options as CALL_OPTIONS,
63+
abortSignal,
5864
experimental_transform,
5965
});
6066

packages/ai/src/agent/pipe-agent-ui-stream-to-response.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export async function pipeAgentUIStreamToResponse<
3030
response: ServerResponse;
3131
agent: Agent<CALL_OPTIONS, TOOLS, OUTPUT>;
3232
messages: unknown[];
33+
abortSignal?: AbortSignal;
3334
options?: CALL_OPTIONS;
3435
experimental_transform?:
3536
| StreamTextTransform<TOOLS>

0 commit comments

Comments
 (0)