Skip to content

Commit 82dad78

Browse files
committed
docs(ai-chat): swap examples to Anthropic, add stopWhen, fix transport guidance
Sweep across the AI chat docs against the v4.5 release candidate: - Every `streamText` example uses Anthropic (`anthropic("claude-sonnet-4-5")`) instead of OpenAI, with imports rewritten. AI SDK v5+ defaults to a single step, so every example also now sets `stopWhen: stepCountIs(15)` to avoid the "tool calls fire but no follow-up text" gotcha. - `startSession` destructure drops the unused `taskId` — the customer's wrapped action is task-bound at creation time, and the SDK-side `chat.createStartSessionAction` now accepts a typed `clientData` field via a `<typeof yourChat>` generic. - Quick start gains a "before you start" paragraph linking to manual setup, and a note that the `@/*` path alias is Next.js-specific. - Frontend page opens with a new "How the transport works" section explaining the no-API-route architecture — the browser uses a session- scoped PAT to call `.in/append` and subscribe to `.out` directly, bypassing any route handler `useChat` would normally need. - Backend page now has an explicit "Which form to call" table for `chat.toStreamTextOptions()` — when to call with no args, when to pass `{ registry }`, when to pass `{ tools }`. - `auth.createPublicToken` is called out as resolving to `Promise<string>` (the JWT), not an object — clears up a small ambiguity in the docs.
1 parent 76f391c commit 82dad78

26 files changed

Lines changed: 257 additions & 183 deletions

docs/ai-chat/actions.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const myChat = chat.agent({
4949
},
5050

5151
run: async ({ messages, signal }) => {
52-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
52+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
5353
},
5454
});
5555
```
@@ -65,8 +65,9 @@ onAction: async ({ action, messages }) => {
6565
if (action.type === "regenerate") {
6666
chat.history.slice(0, -1); // drop the last assistant
6767
return streamText({
68-
model: openai("gpt-4o"),
68+
model: anthropic("claude-sonnet-4-5"),
6969
messages,
70+
stopWhen: stepCountIs(15),
7071
});
7172
}
7273
// other actions return void → side-effect only
@@ -84,7 +85,7 @@ onAction: async ({ action, messages, signal }) => {
8485
if (action.type === "regenerate") {
8586
if (chat.history.getPendingToolCalls().length > 0) return; // gated
8687
chat.history.slice(0, -1);
87-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
88+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
8889
}
8990
},
9091
```

docs/ai-chat/backend.mdx

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ The highest-level approach. Handles message accumulation, stop signals, turn lif
2727

2828
```ts
2929
streamText({
30-
...chat.toStreamTextOptions({ registry, tools }),
30+
...chat.toStreamTextOptions(), // or: chat.toStreamTextOptions({ registry, tools }) — see below
3131
messages,
3232
abortSignal: signal,
3333
// any explicit overrides go here
34+
stopWhen: stepCountIs(15),
3435
});
3536
```
3637

@@ -43,18 +44,19 @@ Return the `streamText` result from `run` and it's automatically piped to the fr
4344

4445
```ts
4546
import { chat } from "@trigger.dev/sdk/ai";
46-
import { streamText } from "ai";
47-
import { openai } from "@ai-sdk/openai";
47+
import { streamText, stepCountIs } from "ai";
48+
import { anthropic } from "@ai-sdk/anthropic";
4849

4950
export const simpleChat = chat.agent({
5051
id: "simple-chat",
5152
run: async ({ messages, signal }) => {
5253
return streamText({
5354
...chat.toStreamTextOptions(), // prepareStep, system, telemetry — see callout above
54-
model: openai("gpt-4o"),
55+
model: anthropic("claude-sonnet-4-5"),
5556
system: "You are a helpful assistant.",
5657
messages,
5758
abortSignal: signal,
59+
stopWhen: stepCountIs(15),
5860
});
5961
},
6062
});
@@ -67,7 +69,7 @@ For complex agent flows where `streamText` is called deep inside your code, use
6769
```ts trigger/agent-chat.ts
6870
import { chat } from "@trigger.dev/sdk/ai";
6971
import { streamText } from "ai";
70-
import { openai } from "@ai-sdk/openai";
72+
import { anthropic } from "@ai-sdk/anthropic";
7173
import type { ModelMessage } from "ai";
7274

7375
export const agentChat = chat.agent({
@@ -82,8 +84,9 @@ async function runAgentLoop(messages: ModelMessage[]) {
8284
// ... agent logic, tool calls, etc.
8385

8486
const result = streamText({
85-
model: openai("gpt-4o"),
87+
model: anthropic("claude-sonnet-4-5"),
8688
messages,
89+
stopWhen: stepCountIs(15),
8790
});
8891

8992
// Pipe from anywhere — no need to return it
@@ -116,7 +119,7 @@ export const myChat = chat.agent({
116119
data: { searchResults: results },
117120
});
118121

119-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
122+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
120123
},
121124
});
122125
```
@@ -186,14 +189,14 @@ Use [AI Prompts](/ai/prompts) to manage your system prompt as versioned, overrid
186189
import { chat } from "@trigger.dev/sdk/ai";
187190
import { prompts } from "@trigger.dev/sdk";
188191
import { streamText, createProviderRegistry } from "ai";
189-
import { openai } from "@ai-sdk/openai";
192+
import { anthropic } from "@ai-sdk/anthropic";
190193
import { z } from "zod";
191194

192-
const registry = createProviderRegistry({ openai });
195+
const registry = createProviderRegistry({ anthropic });
193196

194197
const systemPrompt = prompts.define({
195198
id: "my-chat-system",
196-
model: "openai:gpt-4o",
199+
model: "anthropic:claude-sonnet-4-5",
197200
config: { temperature: 0.7 },
198201
variables: z.object({ name: z.string() }),
199202
content: `You are a helpful assistant for {{name}}.`,
@@ -212,13 +215,23 @@ export const myChat = chat.agent({
212215
...chat.toStreamTextOptions({ registry }), // system, model, config, telemetry
213216
messages,
214217
abortSignal: signal,
218+
stopWhen: stepCountIs(15),
215219
});
216220
},
217221
});
218222
```
219223

220224
`chat.toStreamTextOptions()` returns an object with `system`, `model` (resolved via the registry), `temperature`, and `experimental_telemetry` — all from the stored prompt. Properties you set after the spread (like a client-selected model) take precedence.
221225

226+
**Which form to call:**
227+
228+
| Form | Use when |
229+
|---|---|
230+
| `chat.toStreamTextOptions()` | Default. Wires up `prepareStep` (compaction, steering, background injection), the stored prompt's `system` / `model` / `config`, and telemetry metadata. |
231+
| `chat.toStreamTextOptions({ registry })` | You're using [Prompts](/ai/prompts) with a provider-prefixed model string (e.g. `"anthropic:claude-sonnet-4-5"`). The registry resolves the prefix to a real model instance via `createProviderRegistry({ anthropic, openai, ... })`. |
232+
| `chat.toStreamTextOptions({ tools })` | You want HITL tool approvals — pass the same `tools` object you give to `streamText`. The SDK then knows which tool calls need to pause on `needsApproval: true`. |
233+
| `chat.toStreamTextOptions({ registry, tools })` | Both of the above. |
234+
222235
<Tip>
223236
See [Prompts](/ai/prompts) for the full guide — defining templates, variable schemas, dashboard
224237
overrides, and the management SDK.
@@ -245,9 +258,10 @@ export const myChat = chat.agent({
245258
id: "my-chat",
246259
run: async ({ messages, signal, stopSignal, cancelSignal }) => {
247260
return streamText({
248-
model: openai("gpt-4o"),
261+
model: anthropic("claude-sonnet-4-5"),
249262
messages,
250263
abortSignal: signal, // Handles both stop and cancel
264+
stopWhen: stepCountIs(15),
251265
});
252266
},
253267
});
@@ -272,7 +286,7 @@ export const myChat = chat.agent({
272286
});
273287
},
274288
run: async ({ messages, signal }) => {
275-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
289+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
276290
},
277291
});
278292
```
@@ -287,7 +301,7 @@ export const myChat = chat.agent({
287301
id: "my-chat",
288302
run: async ({ messages, signal }) => {
289303
return streamText({
290-
model: openai("gpt-4o"),
304+
model: anthropic("claude-sonnet-4-5"),
291305
messages,
292306
abortSignal: signal,
293307
onFinish: ({ isAborted }) => {
@@ -297,6 +311,7 @@ export const myChat = chat.agent({
297311
// handle stop — e.g. log analytics
298312
}
299313
},
314+
stopWhen: stepCountIs(15),
300315
});
301316
},
302317
});
@@ -339,10 +354,11 @@ export const myChat = chat.agent({
339354
id: "my-chat",
340355
run: async ({ messages, signal }) => {
341356
return streamText({
342-
model: openai("gpt-4o"),
357+
model: anthropic("claude-sonnet-4-5"),
343358
messages,
344359
tools: { sendEmail },
345360
abortSignal: signal,
361+
stopWhen: stepCountIs(15),
346362
});
347363
},
348364
});
@@ -383,6 +399,7 @@ export const myChat = chat.agent({
383399
/* ... */
384400
},
385401
abortSignal: signal,
402+
stopWhen: stepCountIs(15),
386403
});
387404
},
388405
});
@@ -532,7 +549,7 @@ export const myChat = chat.agent({
532549
];
533550
},
534551
run: async ({ messages, signal }) => {
535-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
552+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
536553
},
537554
});
538555
```
@@ -559,7 +576,7 @@ chat.agent({
559576
run: async ({ messages, signal }) => {
560577
// Single-response agent — exit after this turn.
561578
chat.endRun();
562-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
579+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
563580
},
564581
});
565582
```
@@ -577,7 +594,7 @@ Override how long the run stays suspended waiting for the next message. Call fro
577594
```ts
578595
run: async ({ messages, signal }) => {
579596
chat.setTurnTimeout("2h"); // Wait longer for this conversation
580-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
597+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
581598
},
582599
```
583600

@@ -588,7 +605,7 @@ Override how long the run stays idle (active, using compute) after each turn:
588605
```ts
589606
run: async ({ messages, signal }) => {
590607
chat.setIdleTimeoutInSeconds(60); // Stay idle for 1 minute
591-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
608+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
592609
},
593610
```
594611

@@ -622,7 +639,7 @@ export const myChat = chat.agent({
622639
},
623640
},
624641
run: async ({ messages, signal }) => {
625-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
642+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
626643
},
627644
});
628645
```
@@ -653,7 +670,7 @@ export const myChat = chat.agent({
653670
sendSources: true, // Forward source citations (default: false)
654671
},
655672
run: async ({ messages, signal }) => {
656-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
673+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
657674
},
658675
});
659676
```
@@ -671,7 +688,7 @@ export const myChat = chat.agent({
671688
generateMessageId: () => uuidv7(),
672689
},
673690
run: async ({ messages, signal }) => {
674-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
691+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
675692
},
676693
});
677694
```
@@ -691,7 +708,7 @@ export const myChat = chat
691708
.agent({
692709
id: "my-chat",
693710
run: async ({ messages, signal }) => {
694-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
711+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
695712
},
696713
});
697714
```
@@ -735,16 +752,17 @@ If you need full control over task options, use the standard `task()` with `Chat
735752
import { task } from "@trigger.dev/sdk";
736753
import { chat, type ChatTaskPayload } from "@trigger.dev/sdk/ai";
737754
import { streamText } from "ai";
738-
import { openai } from "@ai-sdk/openai";
755+
import { anthropic } from "@ai-sdk/anthropic";
739756

740757
export const manualChat = task({
741758
id: "manual-chat",
742759
retry: { maxAttempts: 3 },
743760
queue: { concurrencyLimit: 10 },
744761
run: async (payload: ChatTaskPayload) => {
745762
const result = streamText({
746-
model: openai("gpt-4o"),
763+
model: anthropic("claude-sonnet-4-5"),
747764
messages: payload.messages,
765+
stopWhen: stepCountIs(15),
748766
});
749767

750768
await chat.pipe(result);
@@ -770,7 +788,7 @@ Use `chat.createSession()` inside a standard `task()`:
770788
import { task } from "@trigger.dev/sdk";
771789
import { chat, type ChatTaskWirePayload } from "@trigger.dev/sdk/ai";
772790
import { streamText } from "ai";
773-
import { openai } from "@ai-sdk/openai";
791+
import { anthropic } from "@ai-sdk/anthropic";
774792

775793
export const myChat = task({
776794
id: "my-chat",
@@ -787,9 +805,10 @@ export const myChat = task({
787805

788806
for await (const turn of session) {
789807
const result = streamText({
790-
model: openai("gpt-4o"),
808+
model: anthropic("claude-sonnet-4-5"),
791809
messages: turn.messages,
792810
abortSignal: turn.signal,
811+
stopWhen: stepCountIs(15),
793812
});
794813

795814
// Pipe, capture, accumulate, and signal turn-complete — all in one call
@@ -845,9 +864,10 @@ For more control, you can do each step manually:
845864
```ts
846865
for await (const turn of session) {
847866
const result = streamText({
848-
model: openai("gpt-4o"),
867+
model: anthropic("claude-sonnet-4-5"),
849868
messages: turn.messages,
850869
abortSignal: turn.signal,
870+
stopWhen: stepCountIs(15),
851871
});
852872

853873
// Manual: pipe and capture separately
@@ -892,7 +912,7 @@ Raw task mode also lets you call `.toUIMessageStream()` yourself with any option
892912
import { task } from "@trigger.dev/sdk";
893913
import { chat, type ChatTaskWirePayload } from "@trigger.dev/sdk/ai";
894914
import { streamText } from "ai";
895-
import { openai } from "@ai-sdk/openai";
915+
import { anthropic } from "@ai-sdk/anthropic";
896916

897917
export const myChat = task({
898918
id: "my-chat-raw",
@@ -925,9 +945,10 @@ export const myChat = task({
925945
const combinedSignal = AbortSignal.any([runSignal, stop.signal]);
926946

927947
const result = streamText({
928-
model: openai("gpt-4o"),
948+
model: anthropic("claude-sonnet-4-5"),
929949
messages,
930950
abortSignal: combinedSignal,
951+
stopWhen: stepCountIs(15),
931952
});
932953

933954
let response;

docs/ai-chat/background-injection.mdx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const myChat = chat.agent({
5656
...chat.toStreamTextOptions({ registry }),
5757
messages,
5858
abortSignal: signal,
59+
stopWhen: stepCountIs(15),
5960
});
6061
},
6162
});
@@ -80,15 +81,15 @@ A cheap model reviews the agent's response after each turn and injects coaching
8081
```ts
8182
import { chat } from "@trigger.dev/sdk/ai";
8283
import { prompts } from "@trigger.dev/sdk";
83-
import { streamText, generateObject, createProviderRegistry } from "ai";
84-
import { openai } from "@ai-sdk/openai";
84+
import { streamText, generateObject, createProviderRegistry, stepCountIs } from "ai";
85+
import { anthropic } from "@ai-sdk/anthropic";
8586
import { z } from "zod";
8687

87-
const registry = createProviderRegistry({ openai });
88+
const registry = createProviderRegistry({ anthropic });
8889

8990
const selfReviewPrompt = prompts.define({
9091
id: "self-review",
91-
model: "openai:gpt-4o-mini",
92+
model: "anthropic:claude-haiku-4-5",
9293
content: `You are a conversation quality reviewer. Analyze the assistant's most recent response.
9394
9495
Focus on:
@@ -107,7 +108,7 @@ export const myChat = chat.agent({
107108
const resolved = await selfReviewPrompt.resolve({});
108109

109110
const review = await generateObject({
110-
model: registry.languageModel(resolved.model ?? "openai:gpt-4o-mini"),
111+
model: registry.languageModel(resolved.model ?? "anthropic:claude-haiku-4-5"),
111112
...resolved.toAISDKTelemetry(),
112113
system: resolved.text,
113114
prompt: messages
@@ -147,6 +148,7 @@ export const myChat = chat.agent({
147148
...chat.toStreamTextOptions({ registry }),
148149
messages,
149150
abortSignal: signal,
151+
stopWhen: stepCountIs(15),
150152
});
151153
},
152154
});
@@ -173,7 +175,7 @@ export const myChat = chat.agent({
173175
chat.defer(analytics.track("turn_started", { chatId, runId }));
174176
},
175177
run: async ({ messages, signal }) => {
176-
return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
178+
return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
177179
},
178180
});
179181
```

0 commit comments

Comments
 (0)