Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f18af23

Browse files
feat: only send user message as part of request (#957)
1 parent 9279135 commit f18af23

File tree

8 files changed

+90
-54
lines changed

8 files changed

+90
-54
lines changed

app/(chat)/actions.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
'use server';
22

3-
import { generateText, Message } from 'ai';
3+
import { generateText, type UIMessage } from 'ai';
44
import { cookies } from 'next/headers';
5-
65
import {
76
deleteMessagesByChatIdAfterTimestamp,
87
getMessageById,
98
updateChatVisiblityById,
109
} from '@/lib/db/queries';
11-
import { VisibilityType } from '@/components/visibility-selector';
10+
import type { VisibilityType } from '@/components/visibility-selector';
1211
import { myProvider } from '@/lib/ai/providers';
1312

1413
export async function saveChatModelAsCookie(model: string) {
@@ -19,7 +18,7 @@ export async function saveChatModelAsCookie(model: string) {
1918
export async function generateTitleFromUserMessage({
2019
message,
2120
}: {
22-
message: Message;
21+
message: UIMessage;
2322
}) {
2423
const { text: title } = await generateText({
2524
model: myProvider.languageModel('title-model'),

app/(chat)/api/chat/route.ts

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
type UIMessage,
2+
appendClientMessage,
33
appendResponseMessages,
44
createDataStreamResponse,
55
smoothStream,
@@ -11,14 +11,11 @@ import {
1111
deleteChatById,
1212
getChatById,
1313
getMessageCountByUserId,
14+
getMessagesByChatId,
1415
saveChat,
1516
saveMessages,
1617
} from '@/lib/db/queries';
17-
import {
18-
generateUUID,
19-
getMostRecentUserMessage,
20-
getTrailingMessageId,
21-
} from '@/lib/utils';
18+
import { generateUUID, getTrailingMessageId } from '@/lib/utils';
2219
import { generateTitleFromUserMessage } from '../../actions';
2320
import { createDocument } from '@/lib/ai/tools/create-document';
2421
import { updateDocument } from '@/lib/ai/tools/update-document';
@@ -27,24 +24,26 @@ import { getWeather } from '@/lib/ai/tools/get-weather';
2724
import { isProductionEnvironment } from '@/lib/constants';
2825
import { myProvider } from '@/lib/ai/providers';
2926
import { entitlementsByUserType } from '@/lib/ai/entitlements';
27+
import { postRequestBodySchema, type PostRequestBody } from './schema';
3028

3129
export const maxDuration = 60;
3230

3331
export async function POST(request: Request) {
32+
let requestBody: PostRequestBody;
33+
34+
try {
35+
const json = await request.json();
36+
requestBody = postRequestBodySchema.parse(json);
37+
} catch (_) {
38+
return new Response('Invalid request body', { status: 400 });
39+
}
40+
3441
try {
35-
const {
36-
id,
37-
messages,
38-
selectedChatModel,
39-
}: {
40-
id: string;
41-
messages: Array<UIMessage>;
42-
selectedChatModel: string;
43-
} = await request.json();
42+
const { id, message, selectedChatModel } = requestBody;
4443

4544
const session = await auth();
4645

47-
if (!session?.user?.id) {
46+
if (!session?.user) {
4847
return new Response('Unauthorized', { status: 401 });
4948
}
5049

@@ -64,17 +63,11 @@ export async function POST(request: Request) {
6463
);
6564
}
6665

67-
const userMessage = getMostRecentUserMessage(messages);
68-
69-
if (!userMessage) {
70-
return new Response('No user message found', { status: 400 });
71-
}
72-
7366
const chat = await getChatById({ id });
7467

7568
if (!chat) {
7669
const title = await generateTitleFromUserMessage({
77-
message: userMessage,
70+
message,
7871
});
7972

8073
await saveChat({ id, userId: session.user.id, title });
@@ -84,14 +77,22 @@ export async function POST(request: Request) {
8477
}
8578
}
8679

80+
const previousMessages = await getMessagesByChatId({ id });
81+
82+
const messages = appendClientMessage({
83+
// @ts-expect-error: todo add type conversion from DBMessage[] to UIMessage[]
84+
messages: previousMessages,
85+
message,
86+
});
87+
8788
await saveMessages({
8889
messages: [
8990
{
9091
chatId: id,
91-
id: userMessage.id,
92+
id: message.id,
9293
role: 'user',
93-
parts: userMessage.parts,
94-
attachments: userMessage.experimental_attachments ?? [],
94+
parts: message.parts,
95+
attachments: message.experimental_attachments ?? [],
9596
createdAt: new Date(),
9697
},
9798
],
@@ -138,7 +139,7 @@ export async function POST(request: Request) {
138139
}
139140

140141
const [, assistantMessage] = appendResponseMessages({
141-
messages: [userMessage],
142+
messages: [message],
142143
responseMessages: response.messages,
143144
});
144145

@@ -176,7 +177,7 @@ export async function POST(request: Request) {
176177
return 'Oops, an error occurred!';
177178
},
178179
});
179-
} catch (error) {
180+
} catch (_) {
180181
return new Response('An error occurred while processing your request!', {
181182
status: 500,
182183
});

app/(chat)/api/chat/schema.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { z } from 'zod';
2+
3+
const textPartSchema = z.object({
4+
text: z.string().min(1).max(2000),
5+
type: z.enum(['text']),
6+
});
7+
8+
export const postRequestBodySchema = z.object({
9+
id: z.string().uuid(),
10+
message: z.object({
11+
id: z.string().uuid(),
12+
createdAt: z.coerce.date(),
13+
role: z.enum(['user']),
14+
content: z.string().min(1).max(2000),
15+
parts: z.array(textPartSchema),
16+
experimental_attachments: z
17+
.array(
18+
z.object({
19+
url: z.string().url(),
20+
name: z.string().min(1).max(2000),
21+
contentType: z.enum(['image/png', 'image/jpg', 'image/jpeg']),
22+
}),
23+
)
24+
.optional(),
25+
}),
26+
selectedChatModel: z.enum(['chat-model', 'chat-model-reasoning']),
27+
});
28+
29+
export type PostRequestBody = z.infer<typeof postRequestBodySchema>;

components/chat.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,15 @@ export function Chat({
4646
reload,
4747
} = useChat({
4848
id,
49-
body: { id, selectedChatModel: selectedChatModel },
5049
initialMessages,
5150
experimental_throttle: 100,
5251
sendExtraMessageFields: true,
5352
generateId: generateUUID,
53+
experimental_prepareRequestBody: (body) => ({
54+
id,
55+
message: body.messages.at(-1),
56+
selectedChatModel,
57+
}),
5458
onFinish: () => {
5559
mutate(unstable_serialize(getChatHistoryPaginationKey));
5660
},

lib/ai/providers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const myProvider = isTestEnvironment
2323
})
2424
: customProvider({
2525
languageModels: {
26-
'chat-model': xai('grok-2-1212'),
26+
'chat-model': xai('grok-2-vision-1212'),
2727
'chat-model-reasoning': wrapLanguageModel({
2828
model: xai('grok-3-mini-beta'),
2929
middleware: extractReasoningMiddleware({ tagName: 'think' }),

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ai-chatbot",
3-
"version": "3.0.7",
3+
"version": "3.0.8",
44
"private": true,
55
"scripts": {
66
"dev": "next dev --turbo",

tests/prompts/routes.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import { generateUUID } from '@/lib/utils';
2+
13
export const TEST_PROMPTS = {
24
SKY: {
3-
MESSAGES: [
4-
{
5-
role: 'user',
6-
content: 'Why is the sky blue?',
7-
parts: [{ type: 'text', text: 'Why is the sky blue?' }],
8-
},
9-
],
5+
MESSAGE: {
6+
id: generateUUID(),
7+
createdAt: new Date().toISOString(),
8+
role: 'user',
9+
content: 'Why is the sky blue?',
10+
parts: [{ type: 'text', text: 'Why is the sky blue?' }],
11+
},
1012
OUTPUT_STREAM: [
1113
'0:"It\'s "',
1214
'0:"just "',
@@ -17,13 +19,14 @@ export const TEST_PROMPTS = {
1719
],
1820
},
1921
GRASS: {
20-
MESSAGES: [
21-
{
22-
role: 'user',
23-
content: 'Why is grass green?',
24-
parts: [{ type: 'text', text: 'Why is grass green?' }],
25-
},
26-
],
22+
MESSAGE: {
23+
id: generateUUID(),
24+
createdAt: new Date().toISOString(),
25+
role: 'user',
26+
content: 'Why is grass green?',
27+
parts: [{ type: 'text', text: 'Why is grass green?' }],
28+
},
29+
2730
OUTPUT_STREAM: [
2831
'0:"It\'s "',
2932
'0:"just "',

tests/routes/chat.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ test.describe
1010
adaContext,
1111
}) => {
1212
const response = await adaContext.request.post('/api/chat', {
13-
data: {},
13+
data: JSON.stringify({}),
1414
});
15-
expect(response.status()).toBe(500);
15+
expect(response.status()).toBe(400);
1616

1717
const text = await response.text();
18-
expect(text).toEqual('An error occurred while processing your request!');
18+
expect(text).toEqual('Invalid request body');
1919
});
2020

2121
test('Ada can invoke chat generation', async ({ adaContext }) => {
@@ -24,7 +24,7 @@ test.describe
2424
const response = await adaContext.request.post('/api/chat', {
2525
data: {
2626
id: chatId,
27-
messages: TEST_PROMPTS.SKY.MESSAGES,
27+
message: TEST_PROMPTS.SKY.MESSAGE,
2828
selectedChatModel: 'chat-model',
2929
},
3030
});
@@ -47,7 +47,7 @@ test.describe
4747
const response = await babbageContext.request.post('/api/chat', {
4848
data: {
4949
id: chatId,
50-
messages: TEST_PROMPTS.GRASS.MESSAGES,
50+
message: TEST_PROMPTS.GRASS.MESSAGE,
5151
selectedChatModel: 'chat-model',
5252
},
5353
});

0 commit comments

Comments
 (0)