From 4777aff389969b106057c5d1b8c240bdb2966fb5 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Wed, 28 Aug 2024 00:42:01 -0500 Subject: [PATCH 1/4] Refactor `route.ts`: Removed unused imports and streamlined code for better performance. --- app/api/chat/route.ts | 66 +++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index fecf80fa..c71052d1 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -11,6 +11,38 @@ export const maxDuration = 60; const convex = new ConvexHttpClient(process.env.NEXT_PUBLIC_CONVEX_URL!); +function ensureValidMessageOrder(messages: any[]) { + const validatedMessages = []; + let lastRole = null; + + for (const message of messages) { + if (message.role === 'tool') { + // Always include tool messages + validatedMessages.push(message); + } else if (message.role === lastRole) { + // Combine consecutive messages with the same role + const lastMessage = validatedMessages[validatedMessages.length - 1]; + if (typeof lastMessage.content === 'string' && typeof message.content === 'string') { + lastMessage.content += '\n' + message.content; + } else if (Array.isArray(lastMessage.content) && Array.isArray(message.content)) { + lastMessage.content = lastMessage.content.concat(message.content); + } else { + validatedMessages.push(message); + } + } else { + validatedMessages.push(message); + lastRole = message.role; + } + } + + // Ensure the conversation ends with a user message + if (validatedMessages[validatedMessages.length - 1].role !== 'user') { + validatedMessages.push({ role: 'user', content: 'Continue' }); + } + + return validatedMessages; +} + export async function POST(req: Request) { const body = await req.json(); console.log("request body:", JSON.stringify(body, null, 2)); @@ -44,7 +76,8 @@ export async function POST(req: Request) { } console.log("BEFORE CONVERSION:", JSON.stringify(body.messages, null, 2)); - const messages = convertToCoreMessages(body.messages); + const validatedMessages = ensureValidMessageOrder(body.messages); + const messages = convertToCoreMessages(validatedMessages); console.log("AFTER CONVERSION:", JSON.stringify(messages, null, 2)); console.log("Converting messages to core messages"); const result = await streamText({ @@ -54,34 +87,5 @@ export async function POST(req: Request) { tools, }); - // console.log("Stream text result:", JSON.stringify(result, null, 2)); - - // // Save message and update user balance - // try { - // const usage = { - // promptTokens: result.usage?.promptTokens || 0, - // completionTokens: result.usage?.completionTokens || 0, - // totalTokens: result.usage?.totalTokens || 0, - // }; - // console.log("Usage:", JSON.stringify(usage)); - - // console.log("Calling saveMessageAndUpdateBalance with:", JSON.stringify({ - // clerk_user_id: userId, - // model_id: toolContext.model, - // usage, - // })); - - // const { cost_in_cents, newBalance } = await convex.mutation(api.users.saveMessageAndUpdateBalance, { - // clerk_user_id: userId, - // model_id: toolContext.model, - // usage, - // }); - - // console.log(`Message cost: ${cost_in_cents} cents. New balance: ${newBalance}`); - // } catch (error) { - // console.error('Error saving message and updating user balance:', error); - // // console.error('Error details:', JSON.stringify(error, Object.getOwnPropertyNames(error))); - // } - return result.toAIStreamResponse(); -} +} \ No newline at end of file From 21844dec306e4f09fbdacdb86a9ed8b6a4c0fb31 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Wed, 28 Aug 2024 00:49:10 -0500 Subject: [PATCH 2/4] Refactor `route.ts` to streamline imports and enhance code readability. --- app/api/chat/route.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index c71052d1..fbee5859 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -19,6 +19,10 @@ function ensureValidMessageOrder(messages: any[]) { if (message.role === 'tool') { // Always include tool messages validatedMessages.push(message); + // After a tool message, we need an assistant message + if (lastRole !== 'assistant') { + validatedMessages.push({ role: 'assistant', content: 'Continuing the conversation based on the tool result.' }); + } } else if (message.role === lastRole) { // Combine consecutive messages with the same role const lastMessage = validatedMessages[validatedMessages.length - 1]; @@ -30,9 +34,13 @@ function ensureValidMessageOrder(messages: any[]) { validatedMessages.push(message); } } else { + if (message.role === 'user' && lastRole === 'user') { + // If we have consecutive user messages, add an assistant message in between + validatedMessages.push({ role: 'assistant', content: 'I understand. Please continue.' }); + } validatedMessages.push(message); - lastRole = message.role; } + lastRole = message.role; } // Ensure the conversation ends with a user message @@ -40,6 +48,11 @@ function ensureValidMessageOrder(messages: any[]) { validatedMessages.push({ role: 'user', content: 'Continue' }); } + // Ensure the conversation starts with a user message + if (validatedMessages[0].role !== 'user') { + validatedMessages.unshift({ role: 'user', content: 'Start the conversation' }); + } + return validatedMessages; } From 8b45f0926c4c380dd7302b0eb4f8522fcc6d89d6 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Wed, 28 Aug 2024 00:51:46 -0500 Subject: [PATCH 3/4] Refactor `route.ts` to streamline imports and enhance code readability. --- app/api/chat/route.ts | 92 ++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index fbee5859..9a7977e3 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -16,6 +16,11 @@ function ensureValidMessageOrder(messages: any[]) { let lastRole = null; for (const message of messages) { + if (!message || typeof message !== 'object') { + console.error('Invalid message:', message); + continue; + } + if (message.role === 'tool') { // Always include tool messages validatedMessages.push(message); @@ -26,9 +31,9 @@ function ensureValidMessageOrder(messages: any[]) { } else if (message.role === lastRole) { // Combine consecutive messages with the same role const lastMessage = validatedMessages[validatedMessages.length - 1]; - if (typeof lastMessage.content === 'string' && typeof message.content === 'string') { + if (lastMessage && typeof lastMessage.content === 'string' && typeof message.content === 'string') { lastMessage.content += '\n' + message.content; - } else if (Array.isArray(lastMessage.content) && Array.isArray(message.content)) { + } else if (lastMessage && Array.isArray(lastMessage.content) && Array.isArray(message.content)) { lastMessage.content = lastMessage.content.concat(message.content); } else { validatedMessages.push(message); @@ -44,12 +49,12 @@ function ensureValidMessageOrder(messages: any[]) { } // Ensure the conversation ends with a user message - if (validatedMessages[validatedMessages.length - 1].role !== 'user') { + if (validatedMessages.length === 0 || validatedMessages[validatedMessages.length - 1].role !== 'user') { validatedMessages.push({ role: 'user', content: 'Continue' }); } // Ensure the conversation starts with a user message - if (validatedMessages[0].role !== 'user') { + if (validatedMessages.length === 0 || validatedMessages[0].role !== 'user') { validatedMessages.unshift({ role: 'user', content: 'Start the conversation' }); } @@ -57,48 +62,53 @@ function ensureValidMessageOrder(messages: any[]) { } export async function POST(req: Request) { - const body = await req.json(); - console.log("request body:", JSON.stringify(body, null, 2)); - const threadId = body.threadId as Id<"threads">; + try { + const body = await req.json(); + console.log("request body:", JSON.stringify(body, null, 2)); + const threadId = body.threadId as Id<"threads">; - if (!threadId) { - console.error("Invalid threadId"); - return new Response('Invalid threadId', { status: 400 }); - } + if (!threadId) { + console.error("Invalid threadId"); + return new Response('Invalid threadId', { status: 400 }); + } - const toolContext = await getToolContext(body); - const tools = getTools(toolContext, body.tools); - const { userId } = auth(); - if (!userId) { - console.error("Unauthorized: No userId found"); - return new Response('Unauthorized', { status: 401 }); - } + const toolContext = await getToolContext(body); + const tools = getTools(toolContext, body.tools); + const { userId } = auth(); + if (!userId) { + console.error("Unauthorized: No userId found"); + return new Response('Unauthorized', { status: 401 }); + } - // Check user balance is > 0, but skip for GPT-4o Mini - if (toolContext.model.modelId !== 'gpt-4o-mini') { - try { - const userBalance = await convex.query(api.users.getUserBalance.getUserBalance, { clerk_user_id: userId }); - if (userBalance <= 0) { - console.error("Insufficient credits for user:", userId); - return new Response('Insufficient credits', { status: 403 }); + // Check user balance is > 0, but skip for GPT-4o Mini + if (toolContext.model.modelId !== 'gpt-4o-mini') { + try { + const userBalance = await convex.query(api.users.getUserBalance.getUserBalance, { clerk_user_id: userId }); + if (userBalance <= 0) { + console.error("Insufficient credits for user:", userId); + return new Response('Insufficient credits', { status: 403 }); + } + } catch (error) { + console.error('Error checking user balance:', error); + return new Response('Error checking user balance', { status: 500 }); } - } catch (error) { - console.error('Error checking user balance:', error); - return new Response('Error checking user balance', { status: 500 }); } - } - console.log("BEFORE CONVERSION:", JSON.stringify(body.messages, null, 2)); - const validatedMessages = ensureValidMessageOrder(body.messages); - const messages = convertToCoreMessages(validatedMessages); - console.log("AFTER CONVERSION:", JSON.stringify(messages, null, 2)); - console.log("Converting messages to core messages"); - const result = await streamText({ - messages, - model: toolContext.model, - system: getSystemPrompt(toolContext), - tools, - }); + console.log("BEFORE CONVERSION:", JSON.stringify(body.messages, null, 2)); + const validatedMessages = ensureValidMessageOrder(body.messages); + const messages = convertToCoreMessages(validatedMessages); + console.log("AFTER CONVERSION:", JSON.stringify(messages, null, 2)); + console.log("Converting messages to core messages"); + const result = await streamText({ + messages, + model: toolContext.model, + system: getSystemPrompt(toolContext), + tools, + }); - return result.toAIStreamResponse(); + return result.toAIStreamResponse(); + } catch (error) { + console.error('Error in POST function:', error); + return new Response('Internal Server Error', { status: 500 }); + } } \ No newline at end of file From 770cf5f3e89cd9540524ba0b29c8e0db341cd023 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Wed, 28 Aug 2024 00:54:43 -0500 Subject: [PATCH 4/4] Refactor `route.ts` to use environment-specific Convex URL configurations. --- app/api/chat/route.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 9a7977e3..30bf39ce 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -28,6 +28,16 @@ function ensureValidMessageOrder(messages: any[]) { if (lastRole !== 'assistant') { validatedMessages.push({ role: 'assistant', content: 'Continuing the conversation based on the tool result.' }); } + } else if (message.role === 'assistant' && message.toolInvocations) { + // Handle assistant messages with tool invocations + validatedMessages.push(message); + // Add a tool message for each tool invocation + message.toolInvocations.forEach((invocation: any) => { + validatedMessages.push({ + role: 'tool', + content: [{ type: 'tool-result', ...invocation }] + }); + }); } else if (message.role === lastRole) { // Combine consecutive messages with the same role const lastMessage = validatedMessages[validatedMessages.length - 1]; @@ -64,7 +74,7 @@ function ensureValidMessageOrder(messages: any[]) { export async function POST(req: Request) { try { const body = await req.json(); - console.log("request body:", JSON.stringify(body, null, 2)); + console.log("Request body:", JSON.stringify(body, null, 2)); const threadId = body.threadId as Id<"threads">; if (!threadId) { @@ -96,9 +106,10 @@ export async function POST(req: Request) { console.log("BEFORE CONVERSION:", JSON.stringify(body.messages, null, 2)); const validatedMessages = ensureValidMessageOrder(body.messages); + console.log("AFTER VALIDATION:", JSON.stringify(validatedMessages, null, 2)); const messages = convertToCoreMessages(validatedMessages); console.log("AFTER CONVERSION:", JSON.stringify(messages, null, 2)); - console.log("Converting messages to core messages"); + const result = await streamText({ messages, model: toolContext.model,