fix: add 120s timeout per tool and increase route maxDuration to 300s
- Add AbortController timeout (120s) to Railway fetch requests - Gracefully handle timeout errors and report to health check system - Increase /api/chat maxDuration from 60s to 300s (5 minutes) - Prevents entire chat from timing out when one tool has large dependencies - Tools that timeout are logged and skipped, allowing others to load Fixes issue where tools like ctx-zip with many dependencies would cause the entire chat request to timeout after 60 seconds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e3b6ff6d25
commit
1bd484797f
2 changed files with 66 additions and 35 deletions
|
|
@ -12,7 +12,7 @@ import { loadAllTools, sanitizeToolName } from '~/lib/tool-loader';
|
|||
|
||||
export const runtime = 'nodejs';
|
||||
export const dynamic = 'force-dynamic';
|
||||
export const maxDuration = 60;
|
||||
export const maxDuration = 300; // 5 minutes for complex tool loading
|
||||
|
||||
// Add conversation state tracking (in-memory for MVP)
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Tool types from AI SDK are complex
|
||||
|
|
|
|||
|
|
@ -111,47 +111,78 @@ export async function loadToolDynamically(
|
|||
console.log(`🔗 Railway URL: ${RAILWAY_SERVICE_URL}`);
|
||||
|
||||
// Call Railway service to load and describe tool
|
||||
const response = await fetch(`${RAILWAY_SERVICE_URL}/load-and-describe`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
packageName,
|
||||
exportName,
|
||||
version,
|
||||
importUrl: importUrl || `https://esm.sh/${packageName}@${version}`,
|
||||
env: env || {},
|
||||
}),
|
||||
});
|
||||
// 120 second timeout per tool to handle large dependency downloads
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 120000);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error(`❌ Railway service error (${response.status}): ${errorText}`);
|
||||
let response;
|
||||
let data;
|
||||
|
||||
// Trigger health check update in background (non-blocking)
|
||||
reportToolFailure(
|
||||
packageName,
|
||||
exportName,
|
||||
`Railway service error (${response.status}): ${errorText}`,
|
||||
'import'
|
||||
).catch((err) =>
|
||||
console.error(`Failed to report tool failure for ${packageName}/${exportName}:`, err)
|
||||
);
|
||||
try {
|
||||
response = await fetch(`${RAILWAY_SERVICE_URL}/load-and-describe`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
packageName,
|
||||
exportName,
|
||||
version,
|
||||
importUrl: importUrl || `https://esm.sh/${packageName}@${version}`,
|
||||
env: env || {},
|
||||
}),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error(`❌ Railway service error (${response.status}): ${errorText}`);
|
||||
|
||||
if (!data.success) {
|
||||
console.error(`❌ Failed to load tool: ${data.error}`);
|
||||
|
||||
// Trigger health check update in background (non-blocking)
|
||||
reportToolFailure(packageName, exportName, data.error || 'Unknown error', 'import').catch(
|
||||
(err) =>
|
||||
// Trigger health check update in background (non-blocking)
|
||||
reportToolFailure(
|
||||
packageName,
|
||||
exportName,
|
||||
`Railway service error (${response.status}): ${errorText}`,
|
||||
'import'
|
||||
).catch((err) =>
|
||||
console.error(`Failed to report tool failure for ${packageName}/${exportName}:`, err)
|
||||
);
|
||||
);
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
data = await response.json();
|
||||
|
||||
if (!data.success) {
|
||||
console.error(`❌ Failed to load tool: ${data.error}`);
|
||||
|
||||
// Trigger health check update in background (non-blocking)
|
||||
reportToolFailure(packageName, exportName, data.error || 'Unknown error', 'import').catch(
|
||||
(err) =>
|
||||
console.error(`Failed to report tool failure for ${packageName}/${exportName}:`, err)
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
} catch (fetchError) {
|
||||
clearTimeout(timeout);
|
||||
|
||||
if (fetchError instanceof Error && fetchError.name === 'AbortError') {
|
||||
console.error(`❌ Railway request timeout after 120s for ${packageName}/${exportName}`);
|
||||
|
||||
reportToolFailure(
|
||||
packageName,
|
||||
exportName,
|
||||
'Railway service timeout (120s) - tool dependencies may be too large',
|
||||
'import'
|
||||
).catch((err) =>
|
||||
console.error(`Failed to report tool failure for ${packageName}/${exportName}:`, err)
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
throw fetchError;
|
||||
}
|
||||
|
||||
console.log(`✅ Tool loaded from Railway: ${cacheKey}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue