33 convertToModelMessages ,
44 createUIMessageStream ,
55 JsonToSseTransformStream ,
6- smoothStream ,
76 stepCountIs ,
87 streamText ,
98} from "ai" ;
@@ -26,16 +25,17 @@ import { createDocument } from "@/lib/ai/tools/create-document";
2625import { getWeather } from "@/lib/ai/tools/get-weather" ;
2726import { requestSuggestions } from "@/lib/ai/tools/request-suggestions" ;
2827import { updateDocument } from "@/lib/ai/tools/update-document" ;
28+ import { valyuSearch } from "@/lib/ai/tools/valyu-search" ;
2929import { isProductionEnvironment } from "@/lib/constants" ;
3030import {
31- createStreamId ,
3231 deleteChatById ,
3332 getChatById ,
3433 getMessageCountByUserId ,
3534 getMessagesByChatId ,
3635 saveChat ,
3736 saveMessages ,
3837 updateChatLastContextById ,
38+ updateChatTitleById ,
3939} from "@/lib/db/queries" ;
4040import type { DBMessage } from "@/lib/db/schema" ;
4141import { ChatSDKError } from "@/lib/errors" ;
@@ -101,13 +101,22 @@ export async function POST(request: Request) {
101101 message,
102102 selectedChatModel,
103103 selectedVisibilityType,
104+ enableValyuSearch,
104105 } : {
105106 id : string ;
106107 message : ChatMessage ;
107108 selectedChatModel : ChatModel [ "id" ] ;
108109 selectedVisibilityType : VisibilityType ;
110+ enableValyuSearch ?: boolean ;
109111 } = requestBody ;
110112
113+ console . log ( "🎯 [CHAT API] Request received:" , {
114+ chatId : id ,
115+ model : selectedChatModel ,
116+ enableValyuSearch,
117+ messagePreview : message . parts [ 0 ] ?. type === "text" ? message . parts [ 0 ] . text . substring ( 0 , 100 ) : "non-text" ,
118+ } ) ;
119+
111120 const session = await auth ( ) ;
112121
113122 if ( ! session ?. user ) {
@@ -135,19 +144,56 @@ export async function POST(request: Request) {
135144 // Only fetch messages if chat already exists
136145 messagesFromDb = await getMessagesByChatId ( { id } ) ;
137146 } else {
138- const title = await generateTitleFromUserMessage ( {
139- message,
140- } ) ;
141-
147+ // For new chats: First save the chat, THEN save the message (foreign key dependency)
142148 await saveChat ( {
143149 id,
144150 userId : session . user . id ,
145- title,
151+ title : "New Chat" , // Temporary title - will be updated in background
146152 visibility : selectedVisibilityType ,
147153 } ) ;
154+
155+ // Now save the user message (must happen after chat is created)
156+ await saveMessages ( {
157+ messages : [
158+ {
159+ chatId : id ,
160+ id : message . id ,
161+ role : "user" ,
162+ parts : message . parts ,
163+ attachments : [ ] ,
164+ createdAt : new Date ( ) ,
165+ } ,
166+ ] ,
167+ } ) ;
168+
169+ // Generate proper title in the background (non-blocking)
170+ after ( async ( ) => {
171+ try {
172+ const title = await generateTitleFromUserMessage ( { message } ) ;
173+ await updateChatTitleById ( { chatId : id , title } ) ;
174+ } catch ( err ) {
175+ console . warn ( "Failed to generate chat title for chat" , id , err ) ;
176+ }
177+ } ) ;
148178 // New chat - no need to fetch messages, it's empty
149179 }
150180
181+ // For existing chats, save the user message only
182+ if ( chat ) {
183+ await saveMessages ( {
184+ messages : [
185+ {
186+ chatId : id ,
187+ id : message . id ,
188+ role : "user" ,
189+ parts : message . parts ,
190+ attachments : [ ] ,
191+ createdAt : new Date ( ) ,
192+ } ,
193+ ] ,
194+ } ) ;
195+ }
196+
151197 const uiMessages = [ ...convertToUIMessages ( messagesFromDb ) , message ] ;
152198
153199 const { longitude, latitude, city, country } = geolocation ( request ) ;
@@ -159,41 +205,32 @@ export async function POST(request: Request) {
159205 country,
160206 } ;
161207
162- await saveMessages ( {
163- messages : [
164- {
165- chatId : id ,
166- id : message . id ,
167- role : "user" ,
168- parts : message . parts ,
169- attachments : [ ] ,
170- createdAt : new Date ( ) ,
171- } ,
172- ] ,
173- } ) ;
174-
175- const streamId = generateUUID ( ) ;
176- await createStreamId ( { streamId, chatId : id } ) ;
208+ // Note: Resumable streams are disabled (see commented code at line 289-297)
209+ // so we don't need to create a stream ID in the database
177210
178211 let finalMergedUsage : AppUsage | undefined ;
179212
213+ const activeTools =
214+ selectedChatModel === "chat-model-grok"
215+ ? [ ]
216+ : [
217+ "getWeather" as const ,
218+ "createDocument" as const ,
219+ "updateDocument" as const ,
220+ "requestSuggestions" as const ,
221+ ...( enableValyuSearch ? [ "valyuSearch" as const ] : [ ] ) ,
222+ ] ;
223+
224+ console . log ( "🔧 [CHAT API] Active tools:" , activeTools ) ;
225+
180226 const stream = createUIMessageStream ( {
181227 execute : ( { writer : dataStream } ) => {
182228 const result = streamText ( {
183229 model : myProvider . languageModel ( selectedChatModel ) ,
184- system : systemPrompt ( { selectedChatModel, requestHints } ) ,
230+ system : systemPrompt ( { selectedChatModel, requestHints, enableValyuSearch } ) ,
185231 messages : convertToModelMessages ( uiMessages ) ,
186232 stopWhen : stepCountIs ( 5 ) ,
187- experimental_activeTools :
188- selectedChatModel === "chat-model-reasoning"
189- ? [ ]
190- : [
191- "getWeather" ,
192- "createDocument" ,
193- "updateDocument" ,
194- "requestSuggestions" ,
195- ] ,
196- experimental_transform : smoothStream ( { chunking : "word" } ) ,
233+ experimental_activeTools : activeTools ,
197234 tools : {
198235 getWeather,
199236 createDocument : createDocument ( { session, dataStream } ) ,
@@ -202,41 +239,35 @@ export async function POST(request: Request) {
202239 session,
203240 dataStream,
204241 } ) ,
242+ ...( enableValyuSearch ? { valyuSearch } : { } ) ,
205243 } ,
206244 experimental_telemetry : {
207245 isEnabled : isProductionEnvironment ,
208246 functionId : "stream-text" ,
209247 } ,
210248 onFinish : async ( { usage } ) => {
249+ // Helper to write usage data
250+ const writeUsage = ( usageData : AppUsage ) => {
251+ finalMergedUsage = usageData ;
252+ dataStream . write ( { type : "data-usage" , data : usageData } ) ;
253+ } ;
254+
211255 try {
256+ const modelId = myProvider . languageModel ( selectedChatModel ) . modelId ;
212257 const providers = await getTokenlensCatalog ( ) ;
213- const modelId =
214- myProvider . languageModel ( selectedChatModel ) . modelId ;
215- if ( ! modelId ) {
216- finalMergedUsage = usage ;
217- dataStream . write ( {
218- type : "data-usage" ,
219- data : finalMergedUsage ,
220- } ) ;
221- return ;
222- }
223258
224- if ( ! providers ) {
225- finalMergedUsage = usage ;
226- dataStream . write ( {
227- type : "data-usage" ,
228- data : finalMergedUsage ,
229- } ) ;
259+ // If we can't enrich the usage data, just send the basic usage
260+ if ( ! modelId || ! providers ) {
261+ writeUsage ( usage ) ;
230262 return ;
231263 }
232264
265+ // Enrich usage data with cost information from TokenLens
233266 const summary = getUsage ( { modelId, usage, providers } ) ;
234- finalMergedUsage = { ...usage , ...summary , modelId } as AppUsage ;
235- dataStream . write ( { type : "data-usage" , data : finalMergedUsage } ) ;
267+ writeUsage ( { ...usage , ...summary , modelId } as AppUsage ) ;
236268 } catch ( err ) {
237269 console . warn ( "TokenLens enrichment failed" , err ) ;
238- finalMergedUsage = usage ;
239- dataStream . write ( { type : "data-usage" , data : finalMergedUsage } ) ;
270+ writeUsage ( usage ) ;
240271 }
241272 } ,
242273 } ) ;
@@ -245,7 +276,8 @@ export async function POST(request: Request) {
245276
246277 dataStream . merge (
247278 result . toUIMessageStream ( {
248- sendReasoning : true ,
279+ // Only send reasoning steps for the reasoning model to reduce overhead
280+ sendReasoning : selectedChatModel === "chat-model-grok" ,
249281 } )
250282 ) ;
251283 } ,
@@ -278,16 +310,7 @@ export async function POST(request: Request) {
278310 } ,
279311 } ) ;
280312
281- // const streamContext = getStreamContext();
282-
283- // if (streamContext) {
284- // return new Response(
285- // await streamContext.resumableStream(streamId, () =>
286- // stream.pipeThrough(new JsonToSseTransformStream())
287- // )
288- // );
289- // }
290-
313+
291314 return new Response ( stream . pipeThrough ( new JsonToSseTransformStream ( ) ) ) ;
292315 } catch ( error ) {
293316 const vercelId = request . headers . get ( "x-vercel-id" ) ;
0 commit comments