Skip to content

Studio workflow status polls are metered on the AI budget and drain the shared ai:<ip> bucket #1517

Description

@groupthinking

Summary

#1507 added /api/workflows to AI_ROUTE_PREFIXES in apps/web/src/proxy.ts. isAiRoute classifies by path prefix only, so the frequently-polled GET status endpoint is metered against the AI budget (UVAI_AI_RATE_LIMIT_PER_MINUTE, default 12/min) alongside the POST that starts a run.

pollVideoToActions in apps/web/src/lib/studio-workflow.ts polls at attempts = 20, delayMs = 150040 requests/min. The start POST shares the same bucket, so one run issues 21 AI-class requests inside a ~30s window.

The 12th request 429s, roughly 17 seconds into a 30-second poll window — before a run that does a transcript fetch plus an action-agent call can plausibly finish. Studio reaches its Still running after N polls branch on essentially every run, so "Act on findings" reads as broken rather than slow.

Why it is worse than one endpoint being throttled

The rate-limit bucket is keyed by class, not by path:

const routeClass = isAiRoute(pathname) ? 'ai' : 'api';
const key = `${routeClass}:${clientIp}`;

Every entry in AI_ROUTE_PREFIXES shares one ai:<ip> counter. A single Studio run therefore drains the allowance that /api/chat, /api/transcribe, /api/pipeline, /api/extract-events and /api/agents/dispatch draw from, for the remainder of the 60s window. A user who clicks the new button then finds chat and transcription 429ing as collateral.

Current state

Present on main as of 5135f4e (#1507, merged 2026-08-07 21:22 UTC). The finding was raised on #1507 by the Vercel Agent review and in a follow-up comment, but the PR merged without it addressed.

Acceptance criteria

  • GET/HEAD on /api/workflows is not metered against the AI budget.
  • POST to /api/workflows/video-to-actions stays AI-class — it does real model work.
  • The exemption is scoped per-prefix, not a blanket GET carve-out across all AI routes.
  • Poll cadence leaves meaningful headroom under whichever budget applies, rather than sitting just beneath the ceiling.
  • The classification is covered by tests that run in CI (test-frontend, added in ci: run apps/web vitest as required test-frontend job (#1449) #1480), including a control proving the fix does not over-exempt.

Notes

isAiRoute currently lives in proxy.ts, which imports next/server and so is awkward to unit test. apps/web/src/lib/auth-paths.ts exists precisely as the home for path policy "free of Next.js request types so vitest can import it offline" and already hosts the sibling shouldSkipRateLimit. Moving the classifier there makes it testable without a Next.js request harness.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions