You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(ai-chat): document custom agents and complete the createSession reference
Adds a dedicated Custom agents page covering chat.customAgent (agent
registration + session binding) and both loop styles: the managed
createSession iterator and the hand-rolled primitives loop. Includes
the patterns the managed lifecycle otherwise covers for you:
continuation history seeding, persisting the user message before
streaming, racing totalUsage after a stop, and the slim wire shape.
The Backend page leads with a decision table across the three
abstraction levels and now focuses on chat.agent, routing to the new
page. Completes the ChatSessionOptions and ChatTurn reference tables
(compaction, pendingMessages, usage fields, setMessages, prepareStep)
and fixes stale examples that read a plural messages field off the
wire payload.
@@ -8,6 +8,22 @@ import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
8
8
9
9
<RcBanner />
10
10
11
+
There are three abstraction levels for a chat backend. All three speak the same wire protocol, so the [frontend transport](/ai-chat/frontend) works unchanged whichever you pick.
12
+
13
+
| Capability |`chat.agent()`|`chat.createSession()`| Raw primitives |
The raw-primitives column assumes [`chat.customAgent()`](/ai-chat/custom-agents) as the wrapper, which is what makes the task visible to the agent dashboard.
24
+
25
+
Start with `chat.agent()`. Drop to `chat.createSession()` when you want to own the per-turn code (model routing, persistence, custom telemetry) without rebuilding the turn loop. Drop to raw primitives only when you need full control over stream conversion or a custom protocol.
26
+
11
27
## chat.agent()
12
28
13
29
The highest-level approach. Handles message accumulation, stop signals, turn lifecycle, and auto-piping automatically.
@@ -119,7 +135,7 @@ writer.write({
119
135
</Info>
120
136
121
137
<Note>
122
-
`chat.response` and the `writer` accumulation behavior work with `chat.agent` and `chat.createSession`. If you're using [`chat.customAgent`](#raw-task-with-primitives), you own the accumulator — see the raw-task example for the manual pattern.
138
+
`chat.response` and the `writer` accumulation behavior work with `chat.agent` and `chat.createSession`. If you're using [`chat.customAgent`](/ai-chat/custom-agents), you own the accumulator — see the raw-task example for the manual pattern.
123
139
</Note>
124
140
125
141
### Raw streaming with `chat.stream`
@@ -750,7 +766,7 @@ See [ChatUIMessageStreamOptions](/ai-chat/reference#chatuimessagestreamoptions)
750
766
<Note>
751
767
`onFinish` is managed internally for response capture and cannot be overridden here. Use
752
768
`streamText`'s `onFinish` callback for custom finish handling, or use [raw task
753
-
mode](#raw-task-with-primitives) for full control over `toUIMessageStream()`.
769
+
mode](/ai-chat/custom-agents) for full control over `toUIMessageStream()`.
A middle ground between `chat.agent()` and raw primitives. You get an async iterator that yields `ChatTurn` objects — each turn handles stop signals, message accumulation, and turn-complete signaling automatically. You control initialization, model/tool selection, persistence, and any custom per-turn logic.
793
-
794
-
Use `chat.createSession()` inside a standard `task()`:
|`signal`|`AbortSignal`| required | Run-level cancel signal (from task context) |
841
-
|`idleTimeoutInSeconds`|`number`|`30`| Seconds to stay idle between turns |
842
-
|`timeout`|`string`|`"1h"`| Duration string for suspend timeout |
843
-
|`maxTurns`|`number`|`100`| Max turns before ending |
808
+
Both lower levels — `chat.createSession()` (managed turn iterator, your turn body) and `chat.customAgent()` with raw primitives (hand-rolled loop, full stream-conversion control) — are covered together on the Custom agents page, including the `ChatTurn` surface, the continuation-seeding pattern, and the hand-rolled-loop checklist:
|`turn.complete(source)`| Pipe stream, capture response, accumulate, and signal turn-complete |
864
-
|`turn.done()`| Just signal turn-complete (when you've piped manually) |
865
-
|`turn.addResponse(response)`| Add a response to the accumulator manually |
866
-
867
-
### turn.complete() vs manual control
868
-
869
-
`turn.complete(result)` is the easy path — it handles piping, capturing the response, accumulating messages, cleaning up aborted parts, and writing the turn-complete chunk.
For full control, use a standard `task()` with the composable primitives from the `chat` namespace. You manage everything: the turn loop, stop signals, message accumulation, and turn-complete signaling.
903
-
904
-
Raw task mode also lets you call `.toUIMessageStream()` yourself with any options — including `onFinish` and `originalMessages`. This is the right choice when you need complete control over the stream conversion beyond what `chat.setUIMessageStreamOptions()` provides.
0 commit comments