Skip to content

Commit 977fa6b

Browse files
committed
Add detailed logging to chat API for debugging 503 errors
1 parent ec53be4 commit 977fa6b

2 files changed

Lines changed: 68 additions & 12 deletions

File tree

app/api/chat/route.ts

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,28 @@ Context from user's documents will be provided below.`;
6161
* Chat with documents using RAG
6262
*/
6363
export async function POST(request: NextRequest) {
64+
const startTime = Date.now();
65+
console.log('[Chat API] Starting request');
66+
6467
try {
68+
console.log('[Chat API] Verifying user...');
6569
const user = await verifyUser(request);
6670
if (!user) {
71+
console.log('[Chat API] User verification failed');
6772
return NextResponse.json(
6873
{ success: false, error: 'Unauthorized' },
6974
{ status: 401 }
7075
);
7176
}
77+
console.log('[Chat API] User verified:', user.id, 'Time:', Date.now() - startTime, 'ms');
7278

7379
const body = await request.json();
74-
const { message, documentId, contextSize = 5 } = body;
80+
const { message, documentId, contextSize = 3 } = body;
81+
console.log('[Chat API] Request body parsed, message length:', message?.length);
82+
83+
// Groq free tier limit is ~6000 tokens (~4 chars per token)
84+
// Limit context to ~2000 tokens (8000 chars) leaving room for system prompt + response
85+
const MAX_CONTEXT_CHARS = 8000;
7586

7687
if (!message || typeof message !== 'string') {
7788
return NextResponse.json(
@@ -105,31 +116,50 @@ export async function POST(request: NextRequest) {
105116
.eq('status', 'ready');
106117

107118
if (!docCount || docCount === 0) {
119+
console.log('[Chat API] No documents found for user');
108120
return NextResponse.json({
109121
success: true,
110122
response: "You don't have any processed documents yet. Please upload and process some documents first, then I can answer questions about them.",
111123
sources: [],
112124
provider
113125
});
114126
}
127+
console.log('[Chat API] Found', docCount, 'documents. Time:', Date.now() - startTime, 'ms');
115128

116129
// Generate embedding for the query
117-
const queryEmbedding = await generateEmbedding(message);
130+
console.log('[Chat API] Generating query embedding...');
131+
const embeddingStartTime = Date.now();
132+
let queryEmbedding: number[];
133+
try {
134+
queryEmbedding = await generateEmbedding(message);
135+
console.log('[Chat API] Embedding generated in', Date.now() - embeddingStartTime, 'ms');
136+
} catch (embeddingError) {
137+
console.error('[Chat API] Embedding generation failed:', embeddingError);
138+
return NextResponse.json(
139+
{ success: false, error: 'Failed to process query. Please try again.' },
140+
{ status: 503 }
141+
);
142+
}
118143

119144
// Search for relevant chunks
145+
console.log('[Chat API] Searching for relevant chunks...');
146+
const searchStartTime = Date.now();
120147
const { data: searchResults, error: searchError } = await supabase.rpc('match_documents', {
121148
query_embedding: `[${queryEmbedding.join(',')}]`,
122149
match_count: contextSize,
123150
filter_user_id: user.id
124151
});
125152

153+
console.log('[Chat API] Search completed in', Date.now() - searchStartTime, 'ms');
154+
126155
if (searchError) {
127-
console.error('Search error:', searchError);
156+
console.error('[Chat API] Search error:', searchError);
128157
return NextResponse.json(
129158
{ success: false, error: 'Failed to search documents' },
130159
{ status: 500 }
131160
);
132161
}
162+
console.log('[Chat API] Found', searchResults?.length || 0, 'relevant chunks');
133163

134164
// Filter by documentId if provided
135165
let relevantChunks = searchResults || [];
@@ -151,12 +181,28 @@ export async function POST(request: NextRequest) {
151181
(documents || []).map((d: { id: string; name: string }) => [d.id, d.name])
152182
);
153183

154-
// Build context from relevant chunks
155-
const contextParts = relevantChunks.map((chunk: {
156-
document_id: string;
157-
content: string;
158-
similarity: number;
159-
}, index: number) => {
184+
// Build context from relevant chunks, respecting token limits
185+
let totalChars = 0;
186+
const truncatedChunks: Array<{ document_id: string; content: string; similarity: number }> = [];
187+
188+
for (const chunk of relevantChunks as Array<{ document_id: string; content: string; similarity: number }>) {
189+
const chunkLength = chunk.content.length;
190+
if (totalChars + chunkLength > MAX_CONTEXT_CHARS) {
191+
// Truncate this chunk to fit
192+
const remainingSpace = MAX_CONTEXT_CHARS - totalChars;
193+
if (remainingSpace > 200) {
194+
truncatedChunks.push({
195+
...chunk,
196+
content: chunk.content.substring(0, remainingSpace) + '... [truncated]'
197+
});
198+
}
199+
break;
200+
}
201+
truncatedChunks.push(chunk);
202+
totalChars += chunkLength;
203+
}
204+
205+
const contextParts = truncatedChunks.map((chunk, index: number) => {
160206
const docName = documentMap.get(chunk.document_id) || 'Unknown Document';
161207
return `[Source ${index + 1}: ${docName}]\n${chunk.content}`;
162208
});
@@ -177,6 +223,8 @@ export async function POST(request: NextRequest) {
177223
}));
178224

179225
// Try to generate response using LLM
226+
console.log('[Chat API] Calling LLM, provider:', provider, 'context chars:', totalChars);
227+
const llmStartTime = Date.now();
180228
try {
181229
const llmResponse = await generateLLMResponse(
182230
[
@@ -195,6 +243,9 @@ export async function POST(request: NextRequest) {
195243
}
196244
);
197245

246+
console.log('[Chat API] LLM response received in', Date.now() - llmStartTime, 'ms');
247+
console.log('[Chat API] Total request time:', Date.now() - startTime, 'ms');
248+
198249
return NextResponse.json({
199250
success: true,
200251
response: llmResponse.content,
@@ -204,6 +255,7 @@ export async function POST(request: NextRequest) {
204255
});
205256

206257
} catch (llmError) {
258+
console.error('[Chat API] LLM call failed after', Date.now() - llmStartTime, 'ms');
207259
// Handle LLM-specific errors with helpful fallbacks
208260
if (llmError instanceof Error) {
209261
// For API key errors, return context-only response
@@ -233,7 +285,8 @@ export async function POST(request: NextRequest) {
233285
}
234286

235287
// User-friendly error message
236-
console.error('LLM error:', llmError.message);
288+
console.error('[Chat API] LLM error:', llmError.message);
289+
console.error('[Chat API] LLM error stack:', llmError.stack);
237290
return NextResponse.json(
238291
{ success: false, error: 'AI service temporarily unavailable. Please try again in a moment.' },
239292
{ status: 503 }
@@ -244,7 +297,8 @@ export async function POST(request: NextRequest) {
244297
}
245298

246299
} catch (error) {
247-
console.error('Chat error:', error);
300+
console.error('[Chat API] Unhandled error:', error);
301+
console.error('[Chat API] Stack:', error instanceof Error ? error.stack : 'no stack');
248302

249303
return NextResponse.json(
250304
{ success: false, error: 'Internal server error' },

lib/llm-client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ async function generateWithGroq(
7070
maxTokens: number
7171
): Promise<LLMResponse> {
7272
// Use provided key or fallback to server-side key
73-
const key = apiKey || process.env.GROQ_API_KEY;
73+
// Clean the key: trim whitespace and remove any literal \n or escaped newlines
74+
const rawKey = apiKey || process.env.GROQ_API_KEY;
75+
const key = rawKey?.trim().replace(/\\n/g, '').replace(/\n/g, '');
7476

7577
if (!key) {
7678
throw new Error('Groq API key not configured');

0 commit comments

Comments
 (0)