ai 7.0.114 → 7.0.117
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/index.js +951 -1250
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +159 -212
- package/dist/internal/index.js.map +1 -1
- package/docs/03-agents/02-building-agents.mdx +2 -2
- package/docs/03-agents/08-terminal-ui.mdx +12 -8
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +6 -1
- package/docs/03-ai-sdk-core/17-mcp-apps.mdx +1 -1
- package/docs/03-ai-sdk-core/26-reasoning.mdx +2 -2
- package/docs/03-ai-sdk-core/32-evaluation.mdx +7 -4
- package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +7 -4
- package/docs/03-ai-sdk-harnesses/01-overview.mdx +3 -2
- package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +97 -107
- package/docs/03-ai-sdk-harnesses/03-tools.mdx +1 -22
- package/docs/03-ai-sdk-harnesses/04-skills.mdx +0 -4
- package/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx +3 -3
- package/docs/03-ai-sdk-harnesses/06-workflow-utilities.mdx +43 -9
- package/docs/03-ai-sdk-harnesses/07-ui.mdx +33 -30
- package/docs/03-ai-sdk-harnesses/08-terminal-ui.mdx +8 -6
- package/docs/04-ai-sdk-ui/01-overview.mdx +1 -1
- package/docs/04-ai-sdk-ui/02-chatbot.mdx +9 -7
- package/docs/04-ai-sdk-ui/08-object-generation.mdx +4 -1
- package/docs/06-advanced/04-caching.mdx +2 -1
- package/docs/06-advanced/11-secure-url-fetching.mdx +39 -0
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +1 -1
- package/docs/07-reference/02-ai-sdk-ui/03-use-object.mdx +4 -1
- package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +1 -0
- package/docs/07-reference/03-ai-sdk-rsc/02-create-ai.mdx +1 -1
- package/docs/07-reference/03-ai-sdk-rsc/03-create-streamable-ui.mdx +1 -1
- package/docs/07-reference/06-ai-sdk-tui/01-run-agent-tui.mdx +12 -8
- package/package.json +12 -12
- package/src/prompt/file-part-data.ts +9 -3
- package/src/util/download/download.ts +2 -2
|
@@ -38,10 +38,10 @@ The AI SDK includes the following harness adapters:
|
|
|
38
38
|
| [Claude Code](/providers/ai-sdk-harnesses/claude-code) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
39
39
|
| [Cline](/providers/ai-sdk-harnesses/cline) | Host process | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
40
40
|
| [Codex](/providers/ai-sdk-harnesses/codex) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
|
|
41
|
-
| [Cursor](/providers/ai-sdk-harnesses/cursor) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <
|
|
41
|
+
| [Cursor](/providers/ai-sdk-harnesses/cursor) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
|
|
42
42
|
| [Deep Agents](/providers/ai-sdk-harnesses/deepagents) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> via auto-rejection |
|
|
43
43
|
| [fx](/providers/ai-sdk-harnesses/fx) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> |
|
|
44
|
-
| [GitHub Copilot](/providers/ai-sdk-harnesses/github-copilot) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <
|
|
45
|
-
| [Grok Build](/providers/ai-sdk-harnesses/grok-build) | Sandbox via ACP | <Check /> | <Check /> | <Check /> | <
|
|
44
|
+
| [GitHub Copilot](/providers/ai-sdk-harnesses/github-copilot) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
|
|
45
|
+
| [Grok Build](/providers/ai-sdk-harnesses/grok-build) | Sandbox via ACP | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
|
|
46
46
|
| [OpenCode](/providers/ai-sdk-harnesses/opencode) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> via auto-rejection |
|
|
47
47
|
| [Pi](/providers/ai-sdk-harnesses/pi) | Host process | <Check /> | <Check /> | <Cross /> | <Check /> | <Check /> |
|
|
@@ -23,7 +23,7 @@ before following the examples.
|
|
|
23
23
|
<InstallPackages packages="@ai-sdk/workflow-harness workflow" />
|
|
24
24
|
|
|
25
25
|
In addition to the workflow specific packages, install the core harness package,
|
|
26
|
-
a harness adapter, and a sandbox
|
|
26
|
+
a harness adapter, and a sandbox adapter as shown in
|
|
27
27
|
[HarnessAgent](/docs/ai-sdk-harnesses/harness-agent).
|
|
28
28
|
|
|
29
29
|
## Configuring the Harness Agent
|
|
@@ -35,15 +35,10 @@ semantic agent steps, set `stopWhen` to `isStepCount(1)` so one call to
|
|
|
35
35
|
```ts filename='harness-workflow/agent.ts' highlight="13-17"
|
|
36
36
|
import { HarnessAgent } from '@ai-sdk/harness/agent';
|
|
37
37
|
import { claudeCode } from '@ai-sdk/harness-claude-code';
|
|
38
|
-
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
|
|
39
38
|
import { isStepCount } from 'ai';
|
|
40
39
|
|
|
41
40
|
export const agent = new HarnessAgent({
|
|
42
41
|
harness: claudeCode,
|
|
43
|
-
sandbox: createVercelSandbox({
|
|
44
|
-
runtime: 'node24',
|
|
45
|
-
ports: [4000],
|
|
46
|
-
}),
|
|
47
42
|
instructions: 'You are a helpful coding assistant.',
|
|
48
43
|
/*
|
|
49
44
|
* Only needed for semantic agent-step workflows.
|
|
@@ -62,7 +57,7 @@ the shared agent with the highlighted `stopWhen` option shown above, then call
|
|
|
62
57
|
### Defining the Agent Step
|
|
63
58
|
|
|
64
59
|
Keep the Workflow step in its own module and import the agent dynamically inside
|
|
65
|
-
the step body. This keeps the agent, sandbox
|
|
60
|
+
the step body. This keeps the agent, sandbox adapter, and other Node.js
|
|
66
61
|
dependencies out of the workflow bundle.
|
|
67
62
|
|
|
68
63
|
```ts filename='harness-workflow/agent-step.ts'
|
|
@@ -77,10 +72,25 @@ export async function agentStep(
|
|
|
77
72
|
'use step';
|
|
78
73
|
|
|
79
74
|
const { agent } = await import('./agent');
|
|
75
|
+
const {
|
|
76
|
+
createVercelNetworkSandboxSession,
|
|
77
|
+
resumeVercelNetworkSandboxSession,
|
|
78
|
+
} = await import('@ai-sdk/sandbox-vercel');
|
|
79
|
+
const sandboxId = `harness-${state.sessionId}`;
|
|
80
|
+
const isFirstStep = state.resumeFrom == null && state.continueFrom == null;
|
|
81
|
+
const sandboxSession = isFirstStep
|
|
82
|
+
? await createVercelNetworkSandboxSession({
|
|
83
|
+
sandboxId,
|
|
84
|
+
runtime: 'node24',
|
|
85
|
+
ports: [4000],
|
|
86
|
+
template: await agent.getSandboxTemplate(),
|
|
87
|
+
})
|
|
88
|
+
: await resumeVercelNetworkSandboxSession({ sandboxId });
|
|
80
89
|
|
|
81
90
|
return runHarnessAgentStep({
|
|
82
91
|
agent,
|
|
83
92
|
state,
|
|
93
|
+
sandboxSession,
|
|
84
94
|
});
|
|
85
95
|
}
|
|
86
96
|
```
|
|
@@ -165,7 +175,9 @@ export async function POST(request: Request) {
|
|
|
165
175
|
}
|
|
166
176
|
```
|
|
167
177
|
|
|
168
|
-
The `sessionId`
|
|
178
|
+
The workflow state `sessionId` identifies the harness session. The step derives
|
|
179
|
+
`sandboxId` from it before first creating the sandbox and uses that same ID to
|
|
180
|
+
reattach on later steps. The state does not serialize a live sandbox. Keep
|
|
169
181
|
`agent.ts`, `agent-step.ts`, `workflow.ts`, and the route in separate modules so
|
|
170
182
|
the workflow bundle does not include Node-heavy agent, sandbox, or framework
|
|
171
183
|
dependencies.
|
|
@@ -194,10 +206,25 @@ export async function timeSliceStep(
|
|
|
194
206
|
'use step';
|
|
195
207
|
|
|
196
208
|
const { agent } = await import('./agent');
|
|
209
|
+
const {
|
|
210
|
+
createVercelNetworkSandboxSession,
|
|
211
|
+
resumeVercelNetworkSandboxSession,
|
|
212
|
+
} = await import('@ai-sdk/sandbox-vercel');
|
|
213
|
+
const sandboxId = `harness-${state.sessionId}`;
|
|
214
|
+
const isFirstStep = state.resumeFrom == null && state.continueFrom == null;
|
|
215
|
+
const sandboxSession = isFirstStep
|
|
216
|
+
? await createVercelNetworkSandboxSession({
|
|
217
|
+
sandboxId,
|
|
218
|
+
runtime: 'node24',
|
|
219
|
+
ports: [4000],
|
|
220
|
+
template: await agent.getSandboxTemplate(),
|
|
221
|
+
})
|
|
222
|
+
: await resumeVercelNetworkSandboxSession({ sandboxId });
|
|
197
223
|
|
|
198
224
|
return runHarnessAgentTimeSlice({
|
|
199
225
|
agent,
|
|
200
226
|
state,
|
|
227
|
+
sandboxSession,
|
|
201
228
|
});
|
|
202
229
|
}
|
|
203
230
|
```
|
|
@@ -281,11 +308,18 @@ export async function POST(request: Request) {
|
|
|
281
308
|
}
|
|
282
309
|
```
|
|
283
310
|
|
|
284
|
-
The `sessionId`
|
|
311
|
+
The workflow state `sessionId` identifies the harness session. The step derives
|
|
312
|
+
`sandboxId` from it; otherwise, you must persist `sandboxSession.id` separately
|
|
313
|
+
to reattach across steps. Keep
|
|
285
314
|
`agent.ts`, `time-slice-step.ts`, `workflow.ts`, and the route in separate
|
|
286
315
|
modules so the workflow bundle does not include Node-heavy agent, sandbox, or
|
|
287
316
|
framework dependencies.
|
|
288
317
|
|
|
318
|
+
`destroyOnFinish` destroys the harness session, but never the supplied sandbox.
|
|
319
|
+
The caller can explicitly call `sandboxSession.destroy()` on the network
|
|
320
|
+
session when no later step needs to reattach. Never destroy it at a
|
|
321
|
+
time-slice boundary where a subsequent step must resume it.
|
|
322
|
+
|
|
289
323
|
## Resume Persistence
|
|
290
324
|
|
|
291
325
|
Workflow automatically persists the `HarnessWorkflowState` returned by each
|
|
@@ -82,53 +82,53 @@ Define the `HarnessAgent` on the server:
|
|
|
82
82
|
```ts filename='app/api/chat/agent.ts'
|
|
83
83
|
import { HarnessAgent } from '@ai-sdk/harness/agent';
|
|
84
84
|
import { claudeCode } from '@ai-sdk/harness-claude-code';
|
|
85
|
-
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
|
|
86
85
|
|
|
87
86
|
export const agent = new HarnessAgent({
|
|
88
87
|
harness: claudeCode,
|
|
89
|
-
sandbox: createVercelSandbox({
|
|
90
|
-
runtime: 'node24',
|
|
91
|
-
ports: [4000],
|
|
92
|
-
}),
|
|
93
88
|
instructions: 'You are a helpful coding assistant.',
|
|
94
89
|
});
|
|
95
90
|
```
|
|
96
91
|
|
|
97
92
|
## Session Store
|
|
98
93
|
|
|
99
|
-
Persist
|
|
94
|
+
Persist the opaque resume state returned by `session.detach()`. If the
|
|
100
95
|
turn paused for approval or was otherwise interrupted, that resume state carries
|
|
101
96
|
the continuation state internally. The chat id can also be the harness
|
|
102
|
-
`sessionId
|
|
103
|
-
|
|
97
|
+
`sessionId`. Here we derive a stable `sandboxId` from that known chat id. If
|
|
98
|
+
you do not derive one, persist the returned `sandboxSession.id` separately.
|
|
104
99
|
|
|
105
100
|
```ts filename='app/api/chat/session-store.ts'
|
|
106
|
-
import type {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
import type { HarnessAgentResumeSessionState } from '@ai-sdk/harness/agent';
|
|
102
|
+
import {
|
|
103
|
+
createVercelNetworkSandboxSession,
|
|
104
|
+
resumeVercelNetworkSandboxSession,
|
|
105
|
+
} from '@ai-sdk/sandbox-vercel';
|
|
110
106
|
|
|
111
107
|
const states: Record<string, HarnessAgentResumeSessionState | undefined> = {};
|
|
112
108
|
|
|
113
|
-
type SessionFactory = {
|
|
114
|
-
createSession(options?: {
|
|
115
|
-
sessionId?: string;
|
|
116
|
-
resumeFrom?: HarnessAgentResumeSessionState;
|
|
117
|
-
}): Promise<HarnessAgentSession>;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
109
|
export async function resumeOrCreateSession({
|
|
121
110
|
agent,
|
|
122
111
|
chatId,
|
|
123
112
|
}: {
|
|
124
|
-
agent:
|
|
113
|
+
agent: typeof import('./agent').agent;
|
|
125
114
|
chatId: string;
|
|
126
115
|
}) {
|
|
127
116
|
const resumeFrom = states[chatId];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
117
|
+
const sandboxId = `harness-${chatId}`;
|
|
118
|
+
const sandboxSession = resumeFrom
|
|
119
|
+
? await resumeVercelNetworkSandboxSession({ sandboxId })
|
|
120
|
+
: await createVercelNetworkSandboxSession({
|
|
121
|
+
sandboxId,
|
|
122
|
+
runtime: 'node24',
|
|
123
|
+
ports: [4000],
|
|
124
|
+
template: await agent.getSandboxTemplate(),
|
|
125
|
+
});
|
|
126
|
+
const session = await agent.createSession({
|
|
127
|
+
sessionId: chatId,
|
|
128
|
+
resumeFrom,
|
|
129
|
+
sandboxSession,
|
|
130
|
+
});
|
|
131
|
+
return { session, sandboxSession };
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
export async function detachAndPersist({
|
|
@@ -136,13 +136,15 @@ export async function detachAndPersist({
|
|
|
136
136
|
session,
|
|
137
137
|
}: {
|
|
138
138
|
chatId: string;
|
|
139
|
-
session:
|
|
139
|
+
session: Awaited<ReturnType<typeof resumeOrCreateSession>>['session'];
|
|
140
140
|
}) {
|
|
141
141
|
states[chatId] = await session.detach();
|
|
142
142
|
}
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
Use durable storage instead of an in-memory map in production.
|
|
145
|
+
Use durable storage instead of an in-memory map in production. The creator
|
|
146
|
+
always creates a new sandbox and fails on a conflicting `sandboxId`; only the
|
|
147
|
+
resume function reattaches an existing one.
|
|
146
148
|
|
|
147
149
|
## Route
|
|
148
150
|
|
|
@@ -177,7 +179,7 @@ export async function POST(request: Request) {
|
|
|
177
179
|
return createUIMessageStreamResponse({
|
|
178
180
|
stream: createUIMessageStream({
|
|
179
181
|
execute: async ({ writer }) => {
|
|
180
|
-
const session = await resumeOrCreateSession({ agent, chatId });
|
|
182
|
+
const { session } = await resumeOrCreateSession({ agent, chatId });
|
|
181
183
|
const result = await agent.stream({ session, messages });
|
|
182
184
|
|
|
183
185
|
writer.merge(
|
|
@@ -212,9 +214,10 @@ sandbox warm for the next request. Bridge-backed adapters can usually reattach
|
|
|
212
214
|
or replay efficiently. If the turn is unfinished, `detach()` includes the turn
|
|
213
215
|
continuation state in the returned resume state.
|
|
214
216
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
`session.stop()` saves resume state and stops the harness runtime, but does not
|
|
218
|
+
stop a supplied sandbox. The caller owns that network session and should call
|
|
219
|
+
its `destroy()` method when the chat ends, not while a later request needs to
|
|
220
|
+
reattach. Preserve the chosen `sandboxId` across processes.
|
|
218
221
|
|
|
219
222
|
## Rendering Harness Parts
|
|
220
223
|
|
|
@@ -19,15 +19,11 @@ session for the lifetime of the terminal UI.
|
|
|
19
19
|
```ts filename='tui.ts'
|
|
20
20
|
import { HarnessAgent, type HarnessAgentSession } from '@ai-sdk/harness/agent';
|
|
21
21
|
import { codex } from '@ai-sdk/harness-codex';
|
|
22
|
-
import {
|
|
22
|
+
import { createVercelNetworkSandboxSession } from '@ai-sdk/sandbox-vercel';
|
|
23
23
|
import { runAgentTUI, type AgentTUIAgent } from '@ai-sdk/tui';
|
|
24
24
|
|
|
25
25
|
const agent = new HarnessAgent({
|
|
26
26
|
harness: codex,
|
|
27
|
-
sandbox: createVercelSandbox({
|
|
28
|
-
runtime: 'node24',
|
|
29
|
-
ports: [4000],
|
|
30
|
-
}),
|
|
31
27
|
});
|
|
32
28
|
|
|
33
29
|
function createTUIAgent({
|
|
@@ -56,7 +52,12 @@ function createTUIAgent({
|
|
|
56
52
|
} as AgentTUIAgent;
|
|
57
53
|
}
|
|
58
54
|
|
|
59
|
-
const
|
|
55
|
+
const sandboxSession = await createVercelNetworkSandboxSession({
|
|
56
|
+
runtime: 'node24',
|
|
57
|
+
ports: [4000],
|
|
58
|
+
template: await agent.getSandboxTemplate(),
|
|
59
|
+
});
|
|
60
|
+
const session = await agent.createSession({ sandboxSession });
|
|
60
61
|
|
|
61
62
|
try {
|
|
62
63
|
await runAgentTUI({
|
|
@@ -67,6 +68,7 @@ try {
|
|
|
67
68
|
});
|
|
68
69
|
} finally {
|
|
69
70
|
await session.destroy();
|
|
71
|
+
await sandboxSession.destroy();
|
|
70
72
|
}
|
|
71
73
|
```
|
|
72
74
|
|
|
@@ -9,7 +9,7 @@ AI SDK UI is designed to help you build interactive chat, completion, and assist
|
|
|
9
9
|
|
|
10
10
|
AI SDK UI provides robust abstractions that simplify the complex tasks of managing chat streams and UI updates on the frontend, enabling you to develop dynamic AI-driven interfaces more efficiently. With three main hooks — **`useChat`**, **`useCompletion`**, and **`useObject`** — you can incorporate real-time chat capabilities, text completions, streamed JSON, and interactive assistant features into your app.
|
|
11
11
|
|
|
12
|
-
- **[`useChat`](/docs/ai-sdk-ui/chatbot)** offers real-time streaming of chat messages
|
|
12
|
+
- **[`useChat`](/docs/ai-sdk-ui/chatbot)** offers real-time streaming of chat messages and manages message, loading, and error state. Manage input state in your component for seamless integration into any UI design.
|
|
13
13
|
- **[`useCompletion`](/docs/ai-sdk-ui/completion)** enables you to handle text completions in your applications, managing the prompt input and automatically updating the UI as new completions are streamed.
|
|
14
14
|
- **[`useObject`](/docs/ai-sdk-ui/object-generation)** is a hook that allows you to consume streamed JSON objects, providing a simple way to handle and display structured data in your application.
|
|
15
15
|
|
|
@@ -1009,10 +1009,12 @@ These are available as `reasoning-file` parts (`ReasoningFileUIPart`) with
|
|
|
1009
1009
|
Some providers such as [Perplexity](/providers/ai-sdk-providers/perplexity#sources) and
|
|
1010
1010
|
[Google](/providers/ai-sdk-providers/google#sources) include sources in the response.
|
|
1011
1011
|
|
|
1012
|
-
|
|
1013
|
-
You can forward them to the client with the `sendSources` option:
|
|
1012
|
+
You can forward sources to the client with the `sendSources` option. This example uses the Perplexity Agent API's `fast` preset to search the web. Install `@ai-sdk/perplexity` and set `PERPLEXITY_API_KEY` in your environment:
|
|
1014
1013
|
|
|
1015
|
-
|
|
1014
|
+
<InstallPackages packages="@ai-sdk/perplexity" />
|
|
1015
|
+
|
|
1016
|
+
```ts filename="app/api/chat/route.ts" highlight="21"
|
|
1017
|
+
import { perplexity } from '@ai-sdk/perplexity';
|
|
1016
1018
|
import {
|
|
1017
1019
|
convertToModelMessages,
|
|
1018
1020
|
createUIMessageStreamResponse,
|
|
@@ -1025,7 +1027,7 @@ export async function POST(req: Request) {
|
|
|
1025
1027
|
const { messages }: { messages: UIMessage[] } = await req.json();
|
|
1026
1028
|
|
|
1027
1029
|
const result = streamText({
|
|
1028
|
-
model:
|
|
1030
|
+
model: perplexity('fast'),
|
|
1029
1031
|
messages: await convertToModelMessages(messages),
|
|
1030
1032
|
});
|
|
1031
1033
|
|
|
@@ -1051,7 +1053,7 @@ messages.map(message => (
|
|
|
1051
1053
|
{message.parts
|
|
1052
1054
|
.filter(part => part.type === 'source-url')
|
|
1053
1055
|
.map(part => (
|
|
1054
|
-
<span key={`source-${part.
|
|
1056
|
+
<span key={`source-${part.sourceId}`}>
|
|
1055
1057
|
[
|
|
1056
1058
|
<a href={part.url} target="_blank">
|
|
1057
1059
|
{part.title ?? new URL(part.url).hostname}
|
|
@@ -1064,8 +1066,8 @@ messages.map(message => (
|
|
|
1064
1066
|
{message.parts
|
|
1065
1067
|
.filter(part => part.type === 'source-document')
|
|
1066
1068
|
.map(part => (
|
|
1067
|
-
<span key={`source-${part.
|
|
1068
|
-
[<span>{part.title ?? `Document ${part.
|
|
1069
|
+
<span key={`source-${part.sourceId}`}>
|
|
1070
|
+
[<span>{part.title ?? `Document ${part.sourceId}`}</span>]
|
|
1069
1071
|
</span>
|
|
1070
1072
|
))}
|
|
1071
1073
|
</div>
|
|
@@ -5,7 +5,10 @@ description: Learn how to use the useObject hook.
|
|
|
5
5
|
|
|
6
6
|
# Object Generation
|
|
7
7
|
|
|
8
|
-
<Note
|
|
8
|
+
<Note>
|
|
9
|
+
`useObject` is available in React, Svelte, Vue, and the SolidJS community
|
|
10
|
+
integration. Angular provides the equivalent `StructuredObject` service.
|
|
11
|
+
</Note>
|
|
9
12
|
|
|
10
13
|
The [`useObject`](/docs/reference/ai-sdk-ui/use-object) hook allows you to create interfaces that represent a structured JSON object that is being streamed.
|
|
11
14
|
|
|
@@ -30,6 +30,7 @@ const redis = new Redis({
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
export const cacheMiddleware: LanguageModelV4Middleware = {
|
|
33
|
+
specificationVersion: 'v4',
|
|
33
34
|
wrapGenerate: async ({ doGenerate, params }) => {
|
|
34
35
|
const cacheKey = JSON.stringify(params);
|
|
35
36
|
|
|
@@ -117,7 +118,7 @@ export const cacheMiddleware: LanguageModelV4Middleware = {
|
|
|
117
118
|
replayed from the cache on later requests.
|
|
118
119
|
</Note>
|
|
119
120
|
|
|
120
|
-
`LanguageModelV4Middleware` has
|
|
121
|
+
`LanguageModelV4Middleware` has the `wrapGenerate` and `wrapStream` wrapping methods, plus the optional `transformParams` method. `wrapGenerate` is called when using [`generateText`](/docs/reference/ai-sdk-core/generate-text), while `wrapStream` is called when using [`streamText`](/docs/reference/ai-sdk-core/stream-text).
|
|
121
122
|
|
|
122
123
|
For `wrapGenerate`, you can cache the response directly. Instead, for `wrapStream`, you cache an array of the stream parts, which can then be used with [`simulateReadableStream`](/docs/ai-sdk-core/testing#simulate-ui-message-stream-responses) function to create a simulated `ReadableStream` that returns the cached response. In this way, the cached response is returned chunk-by-chunk as if it were being generated by the model. You can control the initial delay and delay between chunks by adjusting the `initialDelayInMs` and `chunkDelayInMs` parameters of `simulateReadableStream`.
|
|
123
124
|
|
|
@@ -45,6 +45,45 @@ When the SDK fetches a URL taken from a provider response, it:
|
|
|
45
45
|
|
|
46
46
|
A blocked URL surfaces as a `DownloadError`.
|
|
47
47
|
|
|
48
|
+
## Fetching untrusted URLs directly
|
|
49
|
+
|
|
50
|
+
For custom integrations using `@ai-sdk/provider-utils`, use `fetchUntrustedUrl`
|
|
51
|
+
when the URL comes from a response or other untrusted input. It validates URLs
|
|
52
|
+
and redirects and withholds credentials and unknown custom headers on the first
|
|
53
|
+
request unless the URL matches a configured origin:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { fetchUntrustedUrl } from '@ai-sdk/provider-utils';
|
|
57
|
+
|
|
58
|
+
const response = await fetchUntrustedUrl({
|
|
59
|
+
url: result.downloadUrl,
|
|
60
|
+
headers: {
|
|
61
|
+
authorization: `Bearer ${apiKey}`,
|
|
62
|
+
accept: 'image/*',
|
|
63
|
+
},
|
|
64
|
+
credentialedOrigin: providerBaseURL,
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Here, `Authorization` is sent only when the initial URL has the same origin as
|
|
69
|
+
`providerBaseURL`. Otherwise, only allowlisted metadata such as `Accept`,
|
|
70
|
+
`Range`, and `User-Agent` is sent. A cross-origin redirect drops all caller
|
|
71
|
+
headers except `User-Agent`, and credentials are never reattached on later hops.
|
|
72
|
+
|
|
73
|
+
`credentialedOrigin` must come from your configuration, never from the response
|
|
74
|
+
URL. It does not disable URL validation. For a configured private endpoint, pass
|
|
75
|
+
`trustedOrigin` as well; if `credentialedOrigin` is omitted, `fetchUntrustedUrl`
|
|
76
|
+
uses `trustedOrigin` as the origin allowed to receive credentials. Extra protocol
|
|
77
|
+
metadata can be allowed with `untrustedFirstHopHeaders: ['x-protocol-version']`.
|
|
78
|
+
Only list headers that are safe to disclose to any destination; use a configured
|
|
79
|
+
origin for credentials.
|
|
80
|
+
|
|
81
|
+
The existing `fetchWithValidatedRedirects` helper preserves its first-request
|
|
82
|
+
behavior: it sanitizes risky headers but still forwards authentication and
|
|
83
|
+
custom headers. Switch to `fetchUntrustedUrl` to opt into first-request
|
|
84
|
+
credential isolation. Existing calls to `getFromApi` also retain their behavior;
|
|
85
|
+
pass `credentialedOrigin` when fetching response-supplied URLs with credentials.
|
|
86
|
+
|
|
48
87
|
## Self-hosted and local endpoints
|
|
49
88
|
|
|
50
89
|
URLs that are same-origin with the provider endpoint **you configured** (e.g. a
|
|
@@ -1071,7 +1071,7 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
1071
1071
|
name: 'Output.json()',
|
|
1072
1072
|
type: 'Output',
|
|
1073
1073
|
description:
|
|
1074
|
-
'Output specification for unstructured JSON generation. When the model generates a text response, it will return
|
|
1074
|
+
'Output specification for unstructured JSON generation. When the model generates a text response, it will return any valid JSON value.',
|
|
1075
1075
|
properties: [
|
|
1076
1076
|
{
|
|
1077
1077
|
type: 'Options',
|
|
@@ -5,7 +5,10 @@ description: API reference for the useObject hook.
|
|
|
5
5
|
|
|
6
6
|
# `useObject()`
|
|
7
7
|
|
|
8
|
-
<Note
|
|
8
|
+
<Note>
|
|
9
|
+
`useObject` is available in React, Svelte, Vue, and the SolidJS community
|
|
10
|
+
integration. Angular provides the equivalent `StructuredObject` service.
|
|
11
|
+
</Note>
|
|
9
12
|
|
|
10
13
|
Allows you to consume text streams that represent a JSON object and parse them into a complete object based on a schema.
|
|
11
14
|
You can use it together with [`streamText`](/docs/reference/ai-sdk-core/stream-text) and [`Output.object()`](/docs/reference/ai-sdk-core/output#output-object) in the backend.
|
|
@@ -40,7 +40,7 @@ Creates a client-server context provider that can be used to wrap parts of your
|
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
name: 'onGetUIState',
|
|
43
|
-
type: '() => UIState',
|
|
43
|
+
type: '() => Promise<UIState | undefined>',
|
|
44
44
|
description: 'is called during SSR to compare and update UI state.',
|
|
45
45
|
},
|
|
46
46
|
{
|
|
@@ -72,7 +72,7 @@ Create a stream that sends UI from the server to the client. On the client side,
|
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
name: 'error',
|
|
75
|
-
type: '(
|
|
75
|
+
type: '(any) => StreamableUIWrapper',
|
|
76
76
|
description:
|
|
77
77
|
'Signals that there is an error in the UI stream. It will be thrown on the client side and caught by the nearest error boundary component.',
|
|
78
78
|
},
|
|
@@ -191,17 +191,21 @@ await runAgentTUI({
|
|
|
191
191
|
## Example with Sandbox
|
|
192
192
|
|
|
193
193
|
```ts
|
|
194
|
-
import {
|
|
194
|
+
import { createJustBashNetworkSandboxSession } from '@ai-sdk/sandbox-just-bash';
|
|
195
195
|
|
|
196
|
-
const sandboxSession = await
|
|
196
|
+
const sandboxSession = await createJustBashNetworkSandboxSession({
|
|
197
197
|
cwd: '/home/user',
|
|
198
|
-
}).createSession();
|
|
199
|
-
|
|
200
|
-
await runAgentTUI({
|
|
201
|
-
title: 'Sandbox Assistant',
|
|
202
|
-
agent,
|
|
203
|
-
sandbox: sandboxSession.restricted(),
|
|
204
198
|
});
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
await runAgentTUI({
|
|
202
|
+
title: 'Sandbox Assistant',
|
|
203
|
+
agent,
|
|
204
|
+
sandbox: sandboxSession.restricted(),
|
|
205
|
+
});
|
|
206
|
+
} finally {
|
|
207
|
+
await sandboxSession.destroy();
|
|
208
|
+
}
|
|
205
209
|
```
|
|
206
210
|
|
|
207
211
|
The sandbox is forwarded to every `agent.stream()` call as
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.117",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.95",
|
|
46
46
|
"@ai-sdk/provider": "4.0.18",
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.49"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
51
|
-
"@ai-sdk/deepseek": "3.0.
|
|
52
|
-
"@ai-sdk/google": "4.0.
|
|
53
|
-
"@ai-sdk/groq": "4.0.
|
|
54
|
-
"@ai-sdk/huggingface": "2.0.
|
|
55
|
-
"@ai-sdk/moonshotai": "3.0.
|
|
56
|
-
"@ai-sdk/openai": "4.0.
|
|
57
|
-
"@ai-sdk/test-server": "2.0.
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.97",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.54",
|
|
52
|
+
"@ai-sdk/google": "4.0.82",
|
|
53
|
+
"@ai-sdk/groq": "4.0.50",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.57",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.58",
|
|
56
|
+
"@ai-sdk/openai": "4.0.78",
|
|
57
|
+
"@ai-sdk/test-server": "2.0.2",
|
|
58
|
+
"@ai-sdk/xai": "5.0.10",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
|
@@ -24,7 +24,10 @@ type ConvertResult = {
|
|
|
24
24
|
mediaType: string | undefined;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
function convertUrlToFilePartData(
|
|
27
|
+
function convertUrlToFilePartData(
|
|
28
|
+
url: URL,
|
|
29
|
+
originalUrl?: string,
|
|
30
|
+
): ConvertResult {
|
|
28
31
|
if (url.protocol === 'data:') {
|
|
29
32
|
const { mediaType, base64Content } = splitDataUrl(url.toString());
|
|
30
33
|
|
|
@@ -38,7 +41,10 @@ function convertUrlToFilePartData(url: URL): ConvertResult {
|
|
|
38
41
|
return { data: { type: 'data', data: base64Content }, mediaType };
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
return {
|
|
44
|
+
return {
|
|
45
|
+
data: { type: 'url', url, ...(originalUrl != null ? { originalUrl } : {}) },
|
|
46
|
+
mediaType: undefined,
|
|
47
|
+
};
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
function convertUrlStringToFilePartData(content: string): ConvertResult {
|
|
@@ -98,7 +104,7 @@ export function convertToLanguageModelV4FilePart(
|
|
|
98
104
|
}
|
|
99
105
|
return convertInlineDataToFilePartData(content.data);
|
|
100
106
|
case 'url':
|
|
101
|
-
return convertUrlToFilePartData(content.url);
|
|
107
|
+
return convertUrlToFilePartData(content.url, content.originalUrl);
|
|
102
108
|
case 'reference':
|
|
103
109
|
return {
|
|
104
110
|
data: { type: 'reference', reference: content.reference },
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DownloadError,
|
|
4
4
|
readResponseWithSizeLimit,
|
|
5
5
|
DEFAULT_MAX_DOWNLOAD_SIZE,
|
|
6
|
-
|
|
6
|
+
fetchUntrustedUrl,
|
|
7
7
|
withUserAgentSuffix,
|
|
8
8
|
getRuntimeEnvironmentUserAgent,
|
|
9
9
|
} from '@ai-sdk/provider-utils';
|
|
@@ -36,7 +36,7 @@ export const download = async ({
|
|
|
36
36
|
getRuntimeEnvironmentUserAgent(),
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
const response = await
|
|
39
|
+
const response = await fetchUntrustedUrl({
|
|
40
40
|
url: urlText,
|
|
41
41
|
headers,
|
|
42
42
|
abortSignal,
|