diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index e8f3144..ca6d20d 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -260,15 +260,22 @@ export async function POST(req: NextRequest) { ); } - const { messages, context } = await req.json(); + const { messages: rawMessages, context } = await req.json(); - if (!messages || !Array.isArray(messages) || messages.length === 0) { + if (!rawMessages || !Array.isArray(rawMessages) || rawMessages.length === 0) { return NextResponse.json( { error: "messages array is required" }, { status: 400 } ); } + // Limit conversation history to prevent exceeding Claude's context window. + // Keep the last 20 messages (10 user + 10 assistant turns). + const MAX_HISTORY = 20; + const messages = rawMessages.length > MAX_HISTORY + ? rawMessages.slice(-MAX_HISTORY) + : rawMessages; + const userId = session.user.id; const organizationId = session.user.organizationId; const userRole = session.user.role; @@ -389,11 +396,17 @@ export async function POST(req: NextRequest) { error: result.error, }); + // Truncate large tool results to avoid exceeding context limits + let resultJson = JSON.stringify( + result.success ? result.data : { error: result.error } + ); + if (resultJson.length > 8000) { + resultJson = resultJson.slice(0, 8000) + '... [truncated]"}'; + } + toolResults.push({ tool_use_id: toolCall.id, - content: JSON.stringify( - result.success ? result.data : { error: result.error } - ), + content: resultJson, }); } diff --git a/src/hooks/use-chat.ts b/src/hooks/use-chat.ts index be12c15..c447de7 100644 --- a/src/hooks/use-chat.ts +++ b/src/hooks/use-chat.ts @@ -145,9 +145,12 @@ export function useChat(context?: ChatContext) { // Build message history for the API — send plain text only. // Tool interactions are resolved server-side within a single request, // so we never send tool_use/tool_result blocks back in history. + // Trim long assistant responses to keep token count manageable. const apiMessages = [...messages, userMsg].map((m) => ({ role: m.role, - content: m.content, + content: typeof m.content === "string" && m.content.length > 2000 + ? m.content.slice(0, 2000) + "..." + : m.content, })); abortRef.current = new AbortController();