Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ OPENAI_API_KEY=
GEMINI_API_KEY=
# Optional: override the default model (defaults to claude-sonnet-5).
ANTHROPIC_MODEL=
# Optional: seconds one AI chat request may run before the platform kills it.
# Must match the AI chat route's maxDuration in vercel.json (300 by default —
# the Vercel hobby-plan maximum). Raise both together on paid Vercel plans.
AI_CHAT_MAX_DURATION=

# Debug only: set to `true` to skip auth on /ycode/preview routes. Never enable in production.
DISABLE_PREVIEW_AUTH=
12 changes: 7 additions & 5 deletions app/(builder)/ycode/api/ai/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import type { AgentContentBlock, AgentMessage } from '@/lib/agent/providers/type

export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';
// Vercel hard-kills the function at this limit without running catch/finally,
// so the stream just stops and the turn looks silently truncated. The agent
// loop stops itself earlier (MAX_RUN_MS in lib/agent/config.ts) to end runs
// gracefully — keep that budget below this value.
export const maxDuration = 800;
// This route's maxDuration is set in vercel.json (300s — the Vercel hobby-plan
// ceiling), NOT exported here: a route-level export always overrides
// vercel.json, which would prevent paid deployments (e.g. Ycode Cloud) from
// raising it in their own vercel.json. Deployments that raise it must also set
// AI_CHAT_MAX_DURATION to match, so the agent loop (MAX_RUN_MS in
// lib/agent/config.ts) stops itself before Vercel hard-kills the function
// without running catch/finally.

/**
* Lightweight in-process rate limiter with a per-tenant sliding window, bounding
Expand Down
29 changes: 20 additions & 9 deletions lib/agent/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,27 @@ export const DEFAULT_MAX_TOKENS = 16384;
export const MAX_TOOL_TURNS = 24;

/**
* Wall-clock budget for one agent run. The chat route's exported `maxDuration`
* (app/(builder)/ycode/api/ai/chat/route.ts) hard-kills the function without
* streaming anything, leaving the turn silently truncated. The agent loop stops
* starting new turns once this budget is spent, so long runs end gracefully:
* page/component snapshots and usage are emitted, and the user gets a
* resumable "ran out of time" error instead of a silent cut. The buffer below
* `maxDuration` must cover one full provider turn plus its tool calls and the
* end-of-run snapshot/CSS work.
* Wall-clock budget for one agent run. Vercel's `maxDuration` for the chat
* route (set in vercel.json, not in the route file) hard-kills the function
* without streaming anything, leaving the turn silently truncated. The agent
* loop stops starting new turns once this budget is spent, so long runs end
* gracefully: page/component snapshots and usage are emitted, and the user
* gets a resumable "ran out of time" error instead of a silent cut.
*
* The default matches vercel.json's 300s (the Vercel hobby-plan ceiling).
* Deployments that raise maxDuration in their own vercel.json (e.g. Ycode
* Cloud at 800s) must set AI_CHAT_MAX_DURATION (seconds) to the same value.
* The 60s buffer below `maxDuration` must cover one full provider turn plus
* its tool calls and the end-of-run snapshot/CSS work.
*/
export const MAX_RUN_MS = 740_000; // maxDuration (800s) minus a 60s buffer
const DEFAULT_AI_CHAT_MAX_DURATION_SECONDS = 300;
const RUN_BUFFER_MS = 60_000;
const configuredMaxDurationSeconds = Number(process.env.AI_CHAT_MAX_DURATION);

export const MAX_RUN_MS =
(Number.isFinite(configuredMaxDurationSeconds) && configuredMaxDurationSeconds > 60
? configuredMaxDurationSeconds
: DEFAULT_AI_CHAT_MAX_DURATION_SECONDS) * 1000 - RUN_BUFFER_MS;

/**
* Cross-turn conversation history budget, applied before the agent runs so a long
Expand Down
3 changes: 3 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"app/(builder)/ycode/api/**/*.ts": {
"maxDuration": 60
},
"app/(builder)/ycode/api/ai/chat/route.ts": {
"maxDuration": 300
},
"app/(builder)/ycode/mcp/*/route.ts": {
"maxDuration": 60
}
Expand Down