Skip to content

Commit 56bbc2a

Browse files
authoredJul 11, 2024··
feat (ai/ui): set body and headers directly on options for handleSubmit and append (#2239)
1 parent 3236fa0 commit 56bbc2a

File tree

10 files changed

+192
-64
lines changed

10 files changed

+192
-64
lines changed
 

‎.changeset/metal-mirrors-stare.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@ai-sdk/ui-utils': patch
3+
'@ai-sdk/svelte': patch
4+
'@ai-sdk/react': patch
5+
'@ai-sdk/solid': patch
6+
'ai': patch
7+
'@ai-sdk/vue': patch
8+
---
9+
10+
feat (ai/ui): set body and headers directly on options for handleSubmit and append

‎content/docs/05-ai-sdk-ui/02-chatbot.mdx

+4-6
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,10 @@ In this example, the `useChat` hook sends a POST request to the `/api/custom-cha
224224

225225
## Setting custom body fields per request
226226

227-
You can configure custom `body` fields on a per-request basis using the `options` field of the `handleSubmit` function.
227+
You can configure custom `body` fields on a per-request basis using the `body` option of the `handleSubmit` function.
228228
This is useful if you want to pass in additional information to your backend that is not part of the message list.
229229

230-
```tsx filename="app/page.tsx" highlight="19-21"
230+
```tsx filename="app/page.tsx" highlight="18-20"
231231
'use client';
232232

233233
import { useChat } from 'ai/react';
@@ -245,10 +245,8 @@ export default function Chat() {
245245
<form
246246
onSubmit={event => {
247247
handleSubmit(event, {
248-
options: {
249-
body: {
250-
customKey: 'customValue',
251-
},
248+
body: {
249+
customKey: 'customValue',
252250
},
253251
});
254252
}}

‎content/docs/07-reference/ai-sdk-ui/01-use-chat.mdx

+37-25
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,33 @@ Allows you to easily create a conversational user interface for your chatbot app
160160
},
161161
{
162162
name: 'append',
163-
type: '(message: Message | CreateMessage, chatRequestOptions: { options: { headers, body } }) => Promise<string | undefined>',
163+
type: '(message: Message | CreateMessage, options?: ChatRequestOptions) => Promise<string | undefined>',
164164
description:
165165
'Function to append a message to the chat, triggering an API call for the AI response. It returns a promise that resolves to full response message content when the API call is successfully finished, or throws an error when the API call fails.',
166+
properties: [
167+
{
168+
type: 'ChatRequestOptions',
169+
parameters: [
170+
{
171+
name: 'headers',
172+
type: 'Record<string, string> | Headers',
173+
description:
174+
'Additional headers that should be to be passed to the API endpoint.',
175+
},
176+
{
177+
name: 'body',
178+
type: 'object',
179+
description:
180+
'Additional body JSON properties that should be sent to the API endpoint.',
181+
},
182+
{
183+
name: 'data',
184+
type: 'JSONValue',
185+
description: 'Additional data to be sent to the API endpoint.',
186+
},
187+
],
188+
},
189+
],
166190
},
167191
{
168192
name: 'reload',
@@ -199,41 +223,29 @@ Allows you to easily create a conversational user interface for your chatbot app
199223
},
200224
{
201225
name: 'handleSubmit',
202-
type: '(event?: { preventDefault?: () => void }, chatRequestOptions?: ChatRequestOptions) => void',
226+
type: '(event?: { preventDefault?: () => void }, options?: ChatRequestOptions) => void',
203227
description:
204228
'Form submission handler that automatically resets the input field and appends a user message. You can use the `options` parameter to send additional data, headers and more to the server.',
205229
properties: [
206230
{
207231
type: 'ChatRequestOptions',
208232
parameters: [
209233
{
210-
name: 'options',
211-
type: 'RequestOptions',
212-
description: 'The options to be passed to the fetch call.',
213-
properties: [
214-
{
215-
type: 'ChatRequestOptions',
216-
parameters: [
217-
{
218-
name: 'headers',
219-
type: 'Record<string, string> | Headers',
220-
description:
221-
'An optional object of headers to be passed to the API endpoint.',
222-
},
223-
{
224-
name: 'body',
225-
type: 'object',
226-
description:
227-
'An optional object to be passed to the API endpoint.',
228-
},
229-
],
230-
},
231-
],
234+
name: 'headers',
235+
type: 'Record<string, string> | Headers',
236+
description:
237+
'Additional headers that should be to be passed to the API endpoint.',
238+
},
239+
{
240+
name: 'body',
241+
type: 'object',
242+
description:
243+
'Additional body JSON properties that should be sent to the API endpoint.',
232244
},
233245
{
234246
name: 'data',
235247
type: 'JSONValue',
236-
description: 'Additional data to be sent to the server.',
248+
description: 'Additional data to be sent to the API endpoint.',
237249
},
238250
],
239251
},

‎packages/core/svelte/use-chat.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ const getStreamedResponse = async (
119119
messages: constructedMessagesPayload,
120120
data: chatRequest.data,
121121
...extraMetadata.body,
122-
...chatRequest.options?.body,
122+
...chatRequest.body,
123123
...(chatRequest.functions !== undefined && {
124124
functions: chatRequest.functions,
125125
}),
@@ -137,7 +137,7 @@ const getStreamedResponse = async (
137137
credentials: extraMetadata.credentials,
138138
headers: {
139139
...extraMetadata.headers,
140-
...chatRequest.options?.headers,
140+
...chatRequest.headers,
141141
},
142142
abortController: () => abortControllerRef,
143143
restoreMessagesOnFailure() {
@@ -284,15 +284,24 @@ export function useChat({
284284
tools,
285285
tool_choice,
286286
data,
287+
headers,
288+
body,
287289
}: ChatRequestOptions = {},
288290
) => {
289291
if (!message.id) {
290292
message.id = generateId();
291293
}
292294

295+
const requestOptions = {
296+
headers: headers ?? options?.headers,
297+
body: body ?? options?.body,
298+
};
299+
293300
const chatRequest: ChatRequest = {
294301
messages: get(messages).concat(message as Message),
295-
options,
302+
options: requestOptions,
303+
headers: requestOptions.headers,
304+
body: requestOptions.body,
296305
data,
297306
...(functions !== undefined && { functions }),
298307
...(function_call !== undefined && { function_call }),
@@ -358,6 +367,11 @@ export function useChat({
358367
event?.preventDefault?.();
359368
const inputValue = get(input);
360369

370+
const requestOptions = {
371+
headers: options.headers ?? options.options?.headers,
372+
body: options.body ?? options.options?.body,
373+
};
374+
361375
const chatRequest: ChatRequest = {
362376
messages: inputValue
363377
? get(messages).concat({
@@ -367,7 +381,9 @@ export function useChat({
367381
createdAt: new Date(),
368382
} as Message)
369383
: get(messages),
370-
options: options.options,
384+
options: requestOptions,
385+
body: requestOptions.body,
386+
headers: requestOptions.headers,
371387
data: options.data,
372388
};
373389

‎packages/react/src/use-chat.ts

+21-5
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,12 @@ const getStreamedResponse = async (
134134
body: experimental_prepareRequestBody?.({
135135
messages: chatRequest.messages,
136136
requestData: chatRequest.data,
137-
requestBody: chatRequest.options?.body,
137+
requestBody: chatRequest.body,
138138
}) ?? {
139139
messages: constructedMessagesPayload,
140140
data: chatRequest.data,
141141
...extraMetadataRef.current.body,
142-
...chatRequest.options?.body,
142+
...chatRequest.body,
143143
...(chatRequest.functions !== undefined && {
144144
functions: chatRequest.functions,
145145
}),
@@ -157,7 +157,7 @@ const getStreamedResponse = async (
157157
credentials: extraMetadataRef.current.credentials,
158158
headers: {
159159
...extraMetadataRef.current.headers,
160-
...chatRequest.options?.headers,
160+
...chatRequest.headers,
161161
},
162162
abortController: () => abortControllerRef.current,
163163
restoreMessagesOnFailure() {
@@ -420,15 +420,24 @@ By default, it's set to 0, which will disable the feature.
420420
tools,
421421
tool_choice,
422422
data,
423+
headers,
424+
body,
423425
}: ChatRequestOptions = {},
424426
) => {
425427
if (!message.id) {
426428
message.id = generateId();
427429
}
428430

431+
const requestOptions = {
432+
headers: headers ?? options?.headers,
433+
body: body ?? options?.body,
434+
};
435+
429436
const chatRequest: ChatRequest = {
430437
messages: messagesRef.current.concat(message as Message),
431-
options,
438+
options: requestOptions,
439+
headers: requestOptions.headers,
440+
body: requestOptions.body,
432441
data,
433442
...(functions !== undefined && { functions }),
434443
...(function_call !== undefined && { function_call }),
@@ -513,6 +522,11 @@ By default, it's set to 0, which will disable the feature.
513522

514523
event?.preventDefault?.();
515524

525+
const requestOptions = {
526+
headers: options.headers ?? options.options?.headers,
527+
body: options.body ?? options.options?.body,
528+
};
529+
516530
const chatRequest: ChatRequest = {
517531
messages: input
518532
? messagesRef.current.concat({
@@ -521,7 +535,9 @@ By default, it's set to 0, which will disable the feature.
521535
content: input,
522536
})
523537
: messagesRef.current,
524-
options: options.options,
538+
options: requestOptions,
539+
headers: requestOptions.headers,
540+
body: requestOptions.body,
525541
data: options.data,
526542
};
527543

‎packages/react/src/use-chat.ui.test.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,12 @@ describe('form actions', () => {
227227
</div>
228228
))}
229229

230-
<form onSubmit={handleSubmit} className="fixed bottom-0 p-2 w-full">
230+
<form onSubmit={handleSubmit} className="fixed bottom-0 w-full p-2">
231231
<input
232232
value={input}
233233
placeholder="Send message..."
234234
onChange={handleInputChange}
235-
className="bg-zinc-100 w-full p-2"
235+
className="w-full p-2 bg-zinc-100"
236236
disabled={isLoading}
237237
data-testid="do-input"
238238
/>
@@ -311,9 +311,7 @@ describe('prepareRequestBody', () => {
311311
{ role: 'user', content: 'hi' },
312312
{
313313
data: { 'test-data-key': 'test-data-value' },
314-
options: {
315-
body: { 'request-body-key': 'request-body-value' },
316-
},
314+
body: { 'request-body-key': 'request-body-value' },
317315
},
318316
);
319317
}}

‎packages/solid/src/use-chat.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,13 @@ const getStreamedResponse = async (
129129
messages: constructedMessagesPayload,
130130
data: chatRequest.data,
131131
...extraMetadata.body,
132-
...chatRequest.options?.body,
132+
...chatRequest.body,
133133
},
134134
streamMode,
135135
credentials: extraMetadata.credentials,
136136
headers: {
137137
...extraMetadata.headers,
138-
...chatRequest.options?.headers,
138+
...chatRequest.headers,
139139
},
140140
abortController: () => abortController,
141141
restoreMessagesOnFailure() {
@@ -307,15 +307,22 @@ export function useChat(
307307

308308
const append: UseChatHelpers['append'] = async (
309309
message,
310-
{ options, data } = {},
310+
{ options, data, headers, body } = {},
311311
) => {
312312
if (!message.id) {
313313
message.id = generateId()();
314314
}
315315

316+
const requestOptions = {
317+
headers: headers ?? options?.headers,
318+
body: body ?? options?.body,
319+
};
320+
316321
const chatRequest: ChatRequest = {
317322
messages: messagesRef.concat(message as Message),
318-
options,
323+
options: requestOptions,
324+
headers: requestOptions.headers,
325+
body: requestOptions.body,
319326
data,
320327
};
321328

@@ -375,6 +382,11 @@ export function useChat(
375382
event?.preventDefault?.();
376383
const inputValue = input();
377384

385+
const requestOptions = {
386+
headers: options.headers ?? options.options?.headers,
387+
body: options.body ?? options.options?.body,
388+
};
389+
378390
const chatRequest: ChatRequest = {
379391
messages: inputValue
380392
? messagesRef.concat({
@@ -384,7 +396,9 @@ export function useChat(
384396
createdAt: new Date(),
385397
})
386398
: messagesRef,
387-
options: options.options,
399+
options: requestOptions,
400+
body: requestOptions.body,
401+
headers: requestOptions.headers,
388402
data: options.data,
389403
};
390404

‎packages/svelte/src/use-chat.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ const getStreamedResponse = async (
119119
messages: constructedMessagesPayload,
120120
data: chatRequest.data,
121121
...extraMetadata.body,
122-
...chatRequest.options?.body,
122+
...chatRequest.body,
123123
...(chatRequest.functions !== undefined && {
124124
functions: chatRequest.functions,
125125
}),
@@ -137,7 +137,7 @@ const getStreamedResponse = async (
137137
credentials: extraMetadata.credentials,
138138
headers: {
139139
...extraMetadata.headers,
140-
...chatRequest.options?.headers,
140+
...chatRequest.headers,
141141
},
142142
abortController: () => abortControllerRef,
143143
restoreMessagesOnFailure() {
@@ -281,15 +281,24 @@ export function useChat({
281281
tools,
282282
tool_choice,
283283
data,
284+
headers,
285+
body,
284286
}: ChatRequestOptions = {},
285287
) => {
286288
if (!message.id) {
287289
message.id = generateId();
288290
}
289291

292+
const requestOptions = {
293+
headers: headers ?? options?.headers,
294+
body: body ?? options?.body,
295+
};
296+
290297
const chatRequest: ChatRequest = {
291298
messages: get(messages).concat(message as Message),
292-
options,
299+
options: requestOptions,
300+
headers: requestOptions.headers,
301+
body: requestOptions.body,
293302
data,
294303
...(functions !== undefined && { functions }),
295304
...(function_call !== undefined && { function_call }),
@@ -355,6 +364,11 @@ export function useChat({
355364
event?.preventDefault?.();
356365
const inputValue = get(input);
357366

367+
const requestOptions = {
368+
headers: options.headers ?? options.options?.headers,
369+
body: options.body ?? options.options?.body,
370+
};
371+
358372
const chatRequest: ChatRequest = {
359373
messages: inputValue
360374
? get(messages).concat({
@@ -364,7 +378,9 @@ export function useChat({
364378
createdAt: new Date(),
365379
} as Message)
366380
: get(messages),
367-
options: options.options,
381+
options: requestOptions,
382+
body: requestOptions.body,
383+
headers: requestOptions.headers,
368384
data: options.data,
369385
};
370386

‎packages/ui-utils/src/types.ts

+41-6
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,33 @@ export type CreateMessage = Omit<Message, 'id'> & {
190190
};
191191

192192
export type ChatRequest = {
193+
/**
194+
An optional object of headers to be passed to the API endpoint.
195+
*/
196+
headers?: Record<string, string> | Headers;
197+
198+
/**
199+
An optional object to be passed to the API endpoint.
200+
*/
201+
body?: object;
202+
203+
/**
204+
The messages of the chat.
205+
*/
193206
messages: Message[];
194-
options?: RequestOptions;
207+
208+
/**
209+
Additional data to be sent to the server.
210+
*/
195211
data?: JSONValue;
196212

213+
/**
214+
The options to be passed to the fetch call.
215+
216+
@deprecated use `headers` and `body` directly
217+
*/
218+
options?: RequestOptions;
219+
197220
/**
198221
* @deprecated
199222
*/
@@ -244,8 +267,25 @@ An optional object to be passed to the API endpoint.
244267
};
245268

246269
export type ChatRequestOptions = {
270+
/**
271+
Additional headers that should be to be passed to the API endpoint.
272+
*/
273+
headers?: Record<string, string> | Headers;
274+
275+
/**
276+
Additional body JSON properties that should be sent to the API endpoint.
277+
*/
278+
body?: object;
279+
280+
/**
281+
Additional data to be sent to the API endpoint.
282+
*/
283+
data?: JSONValue;
284+
247285
/**
248286
The options to be passed to the fetch call.
287+
288+
@deprecated use `headers` and `body` directly
249289
*/
250290
options?: RequestOptions;
251291

@@ -268,11 +308,6 @@ The options to be passed to the fetch call.
268308
@deprecated
269309
*/
270310
tool_choice?: ToolChoice;
271-
272-
/**
273-
Additional data to be sent to the server.
274-
*/
275-
data?: JSONValue;
276311
};
277312

278313
export type UseChatOptions = {

‎packages/vue/src/use-chat.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export function useChat({
119119
let abortController: AbortController | null = null;
120120
async function triggerRequest(
121121
messagesSnapshot: Message[],
122-
{ options, data }: ChatRequestOptions = {},
122+
{ options, data, headers, body }: ChatRequestOptions = {},
123123
) {
124124
try {
125125
error.value = undefined;
@@ -132,9 +132,16 @@ export function useChat({
132132
const previousMessages = messagesData.value;
133133
mutate(messagesSnapshot);
134134

135+
const requestOptions = {
136+
headers: headers ?? options?.headers,
137+
body: body ?? options?.body,
138+
};
139+
135140
let chatRequest: ChatRequest = {
136141
messages: messagesSnapshot,
137-
options,
142+
options: requestOptions,
143+
body: requestOptions.body,
144+
headers: requestOptions.headers,
138145
data,
139146
};
140147

@@ -169,12 +176,12 @@ export function useChat({
169176
messages: constructedMessagesPayload,
170177
data: chatRequest.data,
171178
...unref(body), // Use unref to unwrap the ref value
172-
...options?.body,
179+
...requestOptions.body,
173180
},
174181
streamMode,
175182
headers: {
176183
...headers,
177-
...options?.headers,
184+
...requestOptions.headers,
178185
},
179186
abortController: () => abortController,
180187
credentials,
@@ -227,6 +234,12 @@ export function useChat({
227234
if (!message.id) {
228235
message.id = generateId();
229236
}
237+
238+
const requestOptions = {
239+
headers: options?.headers ?? options?.options?.headers,
240+
body: options?.body ?? options?.options?.body,
241+
};
242+
230243
return triggerRequest(messages.value.concat(message as Message), options);
231244
};
232245

0 commit comments

Comments
 (0)
Please sign in to comment.