Skip to content

feat: add the video job contract and provider error taxonomy - #349

Merged
kiyeonjeon21 merged 1 commit into
mainfrom
feat/video-provider-contract
Oct 4, 2026
Merged

kiyeonjeon21 merged 1 commit into
mainfrom
feat/video-provider-contract

Conversation

@kiyeonjeon21

Copy link
Copy Markdown
Contributor

First PR of the provider interface work (step 3.3): the shared contract, with Runway moved onto it.

Why

Each video provider spoke its own dialect.
Seedance blocks inside one fal.subscribe call with no job ID, so a crash mid-wait loses a paid job.
Kling needs a task type just to poll, Gemini's cancel is a no-op, and wait methods have five different names.
Callers told a likeness rejection from a quota error by matching provider wording, which breaks silently when a provider rewords a message.
The CLI branches on provider name in 16 places in generate video alone.

What this adds

  • VideoGenerator (ai-providers/src/video/contract.ts): submitVideo, getVideoJob, cancelVideoJob?, downloadVideo.
    • A VideoJob is plain, serializable data, so a job submitted in one process can be polled, resumed, or cancelled from another.
    • VideoRequest.from is where Omni multi-turn edits and Kling/Veo extension will plug in.
  • ProviderError with 12 kinds and classifyProviderError. Content wording beats status codes, so a 422 likeness filter reads as likeness and a 429 "insufficient credits" reads as quota. ModelRetiredError is now one of these.
  • providerRequest (ai-providers/src/shared/http.ts): one HTTP path. It retries 429/502/503/504 and network errors and honours Retry-After. Every failure becomes a classified error, and messages carry the path without the query string, so keys never leak.
  • resolveVideoModel: catalog lookup and the shutdown check before any request.
  • waitForVideoJob: generic polling that rides out retryable read errors, supports an abort signal, and on timeout keeps the job ID in the error.
  • describeVideoContract: a reusable suite every provider must pass. It covers handles, poll and download, classified job failures, classified submit errors, model validation before any request, declared continuations, and cancel.
  • Shared fake network: it moves to @vibeframe/ai-providers/testing with no test-runner dependency, and the CLI re-exports it with its build routes.
  • Runway on the contract: RunwayProvider implements the contract, and its older methods wrap it, so CLI callers are unchanged until the CLI moves (PR 3).

Also

  • Provider count fixed: sync-counts.sh and the website counted every folder under ai-providers/src, so catalog and shared were counted as integrations. They now count folders that contain a *Provider.ts: 14, not 16.
  • Old HTTP helper kept for now: fetchJson in shared/http.ts stays until Gemini moves onto providerRequest.

Verification

  • New tests:
    • Error classification: 4
    • HTTP retries and parsing: 5
    • Wait helper: 4
    • Runway contract: 9
    • Existing Runway tests unchanged: 4
  • pnpm build, pnpm lint, pnpm typecheck, tests (providers 164, CLI 1215, MCP 73), pre-push gate.
  • Live Runway: vibe generate video -p runway --duration 2 downloaded a 2.04 s clip (about $0.24).
  • Live auth error: a bad key came back as {kind: "auth", status: 401, retryable: false} with Runway's message.

Next

PR 2 moves Kling, Grok, Seedance (onto the fal queue, so jobs survive a crash), and Omni onto the contract.
PR 3 removes the provider-name branches from the CLI.

https://claude.ai/code/session_011S1BLTaFctZNQzGERpkCuA

Video providers each spoke their own dialect: Seedance blocked inside one
call with no job ID, Kling needed a task type to poll, Gemini's cancel was a
no-op, and callers told a likeness rejection from a quota error by matching
provider wording. This adds the shared contract they will all move onto,
and moves Runway first.

- `VideoGenerator` (ai-providers/src/video): submitVideo, getVideoJob,
  cancelVideoJob, downloadVideo, over a serializable `VideoJob` handle.
  `VideoRequest.from` carries extend/edit continuations (Omni sessions,
  Kling extend). `waitForVideoJob` polls any generator.
- `ProviderError` with a `kind` (auth, quota, rate-limit, likeness,
  moderation, invalid-request, unsupported, model-retired, not-found,
  timeout, provider, network) and `classifyProviderError`.
  `ModelRetiredError` is now a ProviderError.
- `providerRequest` (ai-providers/src/shared/http.ts): one HTTP path with
  retries on 429/5xx and network errors, Retry-After, a timeout, and
  classified errors whose messages never include the query string.
- `resolveVideoModel`: catalog lookup plus the shutdown check, before any
  request.
- `describeVideoContract`: the reusable suite each provider must pass.
- The fake provider network moves to `@vibeframe/ai-providers/testing`
  with no test-runner dependency; the CLI re-exports it with its routes.
- RunwayProvider implements the contract; its older methods wrap it.
- Provider counts now count directories with a `*Provider.ts`, so shared
  code is not counted as an integration (14, not 16).

Claude-Session: https://claude.ai/code/session_011S1BLTaFctZNQzGERpkCuA
@vercel

vercel Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
vibeframe Ready Ready Preview Oct 4, 2026 1:50pm UTC

Request Review

This branch was successfully deployed

1 active deployment
Preview — 24d60f0d Deployed Oct 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant