Skip to content

feat(ai-openrouter): video generation adapter (/api/v1/videos) + image activity follow-ups#740

Draft
tombeckenham wants to merge 7 commits into
mainfrom
707-featai-openrouter-video-generation-adapter-apiv1videos-+-image-activity-follow-ups
Draft

feat(ai-openrouter): video generation adapter (/api/v1/videos) + image activity follow-ups#740
tombeckenham wants to merge 7 commits into
mainfrom
707-featai-openrouter-video-generation-adapter-apiv1videos-+-image-activity-follow-ups

Conversation

@tombeckenham

@tombeckenham tombeckenham commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

Implements #707 (follow-up to #618 / #624, both merged). Rebased onto main.

openRouterVideo adapter

  • New tree-shakeable adapter for OpenRouter's dedicated async video API (POST /api/v1/videos → poll GET /api/v1/videos/{jobId} → download) — Seedance 2.0, Veo 3.1, Wan, Kling, Sora 2 Pro through one key, on the same jobs/polling architecture as the Sora adapter.
  • imageInputs role mapping: start_frame/end_frameframe_images[] (first_frame/last_frame), reference/characterinput_references[], mask/control → throw, unroled image → start frame. Frame roles validated against each model's supported_frame_images. videoInputs/audioInputs throw (unsupported by the API).
  • Per-model size/duration/resolution/aspectRatio types and runtime validation generated from GET /api/v1/videos/models (OPENROUTER_VIDEO_MODEL_META); seed/generateAudio dropped from the type for models whose metadata reports them unsupported. Sync scripts now fetch/convert the second endpoint.
  • Typed per-model durations on the shared contract (matching the Veo adapter): duration is narrowed to each model's union, and the adapter implements availableDurations() / snapDuration(seconds) to enumerate the valid set and coerce raw UI seconds to the nearest supported value.
  • Completed videos returned as data: URLs — unsigned_urls 401 without the API key header (verified live), so they can't go straight into a <video> tag. Downloads >10 MiB log an OOM warning. Gateway-reported cost surfaced as usage.cost.

Image activity follow-ups (#624 review)

  • Unmapped size now throws with the supported list. Root cause: OpenRouterImageModelSizeByName used the Unicode × (U+00D7) while the lookup used ASCII x, so every typed size except 1024x1024 silently dropped its aspect ratio. Union fixed to ASCII; × still normalized at runtime.
  • numberOfImages > 1 now throws (live-verified: the chat-completions pathway ignores every count key and returns one image). image_config casing confirmed live (snake_case applies, camelCase ignored).
  • image_config.strength (0.0–1.0 i2i influence) exposed via modelOptions.strength.

Core fix: generateVideo typed-duration constraint (@tanstack/ai)

Adding the OpenRouter example surfaced a pre-existing #624 bug: generateVideo()'s TAdapter extends VideoAdapter<string, any, any, any> bound let the duration generic default to Record<string, number>. Because createVideoJob is a contravariant function-valued property, no adapter with per-model literal durations satisfied the bound — even the documented generateVideo({ adapter: geminiVideo('veo-3.1-…') }) failed to type-check (CI missed it because the e2e passes a type-erased adapter). Widened the bound to leave size/duration unpinned at all 10 activity sites; per-model types are still recovered via inference. Fixes the Gemini path too.

Example, tests, docs

  • examples/ts-react-media: demos openRouterVideo alongside fal — Seedance 2.0 (text-to-video) and Veo 3.1 (image-to-video), showcasing adapter.snapDuration(); poll/status path now resolves the adapter by model.
  • Unit tests for request shapes (role mapping), polling lifecycle, download/error paths, and per-model duration introspection + a compile-only generateVideo constraint regression test. Full submit→poll→download lifecycle verified live against OpenRouter.
  • E2E: matrix exclusion documented in feature-support.ts — aimock 1.29 only mocks the OpenAI-shaped /v1/videos, not OpenRouter's job shape (same constraint as feat: multimodal prompt for generateImage/generateVideo (image-to-image, image-to-video) #624).
  • Docs: media/video-generation.md, media/image-generation.md, adapters/openrouter.md (+ config.json dates), media-generation SKILL.md. Changesets: @tanstack/ai-openrouter (minor), @tanstack/ai (patch).

Follow-up

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added OpenRouter support for image generation and experimental async video generation, including job polling and results delivered as data URLs.
    • Video generation now exposes model-typed size/duration options and surfaces gateway-reported usage cost.
    • Extended supported OpenRouter video models and options for image-to-video prompt mapping.
  • Bug Fixes

    • Improved OpenRouter image validation (strict size mapping and rejection of requests with more than one image).
    • Fixed video activity type constraints so per-model duration unions type-check correctly.
  • Documentation & Tests

    • Updated OpenRouter/media/video docs and added adapter coverage tests.

@tombeckenham tombeckenham requested a review from a team as a code owner June 10, 2026 08:35
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe3e92af-0dcb-4f37-a3ca-1b0575059780

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds OpenRouter video generation support with typed model metadata, async job polling, server-side media download, and example wiring. It also tightens OpenRouter image generation validation and updates related docs, tests, exports, and type constraints.

Changes

OpenRouter Video Adapter & Image Adapter Fixes

Layer / File(s) Summary
Video model metadata fetch and codegen scripts
scripts/fetch-openrouter-models.ts, scripts/openrouter.video-models.ts, scripts/openrouter.video-models.json, scripts/convert-openrouter-models.ts
fetch-openrouter-models.ts adds a separate video-model fetch/write pipeline; openrouter.video-models.ts parses the committed video-model JSON; openrouter.video-models.json stores the OpenRouter video model capability records; convert-openrouter-models.ts merges video model ids and emits OPENROUTER_VIDEO_MODEL_META.
Generated model metadata and video provider option types
packages/ai-openrouter/src/model-meta.ts, packages/ai-openrouter/src/video/video-provider-options.ts
model-meta.ts gains OPENROUTER_VIDEO_MODELS and OPENROUTER_VIDEO_MODEL_META; video-provider-options.ts defines OpenRouter video model types, per-model option narrowing, derived size and duration unions, and runtime helpers for metadata lookup and validation.
Image adapter size and strength handling
packages/ai-openrouter/src/image/image-provider-options.ts, packages/ai-openrouter/src/adapters/image.ts, packages/ai-openrouter/tests/image-adapter.test.ts
OpenRouter image provider options gain strength; image size literals switch to x-separated values; image.ts normalizes size strings, rejects unsupported sizes, forbids numberOfImages above 1, and forwards modelOptions.strength; tests update for the new behavior.
OpenRouterVideoAdapter core
packages/ai-openrouter/src/adapters/video.ts
Defines OpenRouterVideoConfig, image-part URL conversion, prompt role mapping into frameImages and inputReferences, status and usage conversion helpers, the OpenRouterVideoAdapter implementation, and createOpenRouterVideo/openRouterVideo factories.
VideoAdapter generic arity fix in generateVideo activity
packages/ai/src/activities/generateVideo/index.ts
generateVideo/index.ts widens all VideoAdapter generic constraints from four to six type arguments across exported option types and internal activity functions.
Package exports and SDK version bumps
packages/ai-openrouter/src/index.ts, packages/ai-openrouter/package.json, testing/e2e/package.json
index.ts re-exports the video adapter factory, instance, and video option types; both package manifests bump @openrouter/sdk to 0.12.79.
Example app OpenRouter video wiring
examples/ts-react-media/package.json, examples/ts-react-media/src/lib/models.ts, examples/ts-react-media/src/lib/server-functions.ts
The example adds the ai-openrouter dependency, extends VIDEO_MODELS with OpenRouter video entries, introduces videoAdapterForModel, and updates video job, status, and URL handlers to route OpenRouter models through the new adapter.
Adapter tests and type-safety checks
packages/ai-openrouter/tests/video-adapter.test.ts, packages/ai-openrouter/tests/video-per-model-type-safety.test.ts
video-adapter.test.ts covers video job creation, status polling, URL download, and duration helpers; video-per-model-type-safety.test.ts adds compile-time per-model duration checks.
Docs, changesets, skill docs, and e2e comments
docs/adapters/openrouter.md, docs/media/video-generation.md, docs/media/image-generation.md, docs/config.json, packages/ai/skills/ai-core/media-generation/SKILL.md, .changeset/*, testing/e2e/src/lib/feature-support.ts
Changesets document the release and duration-constraint fix; OpenRouter and media-generation docs add image/video sections; config metadata timestamps change; the media-generation skill and e2e feature-support comments add OpenRouter-specific guidance.

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant OpenRouterVideoAdapter
  participant OpenRouterAPI
  participant ContentURL

  App->>OpenRouterVideoAdapter: createVideoJob
  OpenRouterVideoAdapter->>OpenRouterVideoAdapter: validate size and duration
  OpenRouterVideoAdapter->>OpenRouterVideoAdapter: map image roles to video fields
  OpenRouterVideoAdapter->>OpenRouterAPI: submit generation request
  OpenRouterAPI-->>OpenRouterVideoAdapter: return job id

  App->>OpenRouterVideoAdapter: getVideoStatus
  OpenRouterVideoAdapter->>OpenRouterAPI: fetch generation status
  OpenRouterAPI-->>OpenRouterVideoAdapter: return mapped status

  App->>OpenRouterVideoAdapter: getVideoUrl
  OpenRouterVideoAdapter->>OpenRouterAPI: fetch completed job
  OpenRouterAPI-->>OpenRouterVideoAdapter: return content URL and usage
  OpenRouterVideoAdapter->>ContentURL: download video bytes with auth
  ContentURL-->>OpenRouterVideoAdapter: return media bytes
  OpenRouterVideoAdapter-->>App: return data URL and usage
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • TanStack/ai#624: Both PRs update packages/ai/src/activities/generateVideo/index.ts type constraints, making them directly related at the video activity type layer.

Suggested reviewers

  • AlemTuzlak

Poem

🐇 I hopped through jobs and waited by the stream,
OpenRouter spun a video from a dream.
One image, one path, with durations that fit,
A data URL shimmered—just right, bit by bit.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the main change: adding the OpenRouter video adapter plus related image follow-ups.
Description check ✅ Passed The description follows the required template and includes the changes, checklist, and release impact sections with concrete details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 707-featai-openrouter-video-generation-adapter-apiv1videos-+-image-activity-follow-ups

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​openrouter/​sdk@​0.12.799410010098100

View full report

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

3 package(s) bumped directly, 19 bumped as dependents.

🟨 Minor bumps

Package Version Reason
@tanstack/ai-openrouter 0.14.2 → 0.15.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai 0.34.0 → 0.34.1 Changeset
@tanstack/ai-mcp 0.1.6 → 0.1.7 Changeset
@tanstack/ai-angular 0.1.6 → 0.1.7 Dependent
@tanstack/ai-client 0.18.2 → 0.18.3 Dependent
@tanstack/ai-code-mode 0.2.11 → 0.2.12 Dependent
@tanstack/ai-code-mode-skills 0.3.0 → 0.3.1 Dependent
@tanstack/ai-devtools-core 0.4.14 → 0.4.15 Dependent
@tanstack/ai-event-client 0.6.5 → 0.6.6 Dependent
@tanstack/ai-fal 0.9.2 → 0.9.3 Dependent
@tanstack/ai-isolate-cloudflare 0.2.27 → 0.2.28 Dependent
@tanstack/ai-isolate-node 0.1.36 → 0.1.37 Dependent
@tanstack/ai-isolate-quickjs 0.1.36 → 0.1.37 Dependent
@tanstack/ai-preact 0.9.11 → 0.9.12 Dependent
@tanstack/ai-react 0.15.11 → 0.15.12 Dependent
@tanstack/ai-solid 0.13.11 → 0.13.12 Dependent
@tanstack/ai-svelte 0.13.11 → 0.13.12 Dependent
@tanstack/ai-vue 0.13.11 → 0.13.12 Dependent
@tanstack/ai-vue-ui 0.2.23 → 0.2.24 Dependent
@tanstack/preact-ai-devtools 0.1.57 → 0.1.58 Dependent
@tanstack/react-ai-devtools 0.2.57 → 0.2.58 Dependent
@tanstack/solid-ai-devtools 0.2.57 → 0.2.58 Dependent

@nx-cloud

nx-cloud Bot commented Jun 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit e813487

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-24 07:53:42 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jun 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@740

@tanstack/ai-angular

npm i https://pkg.pr.new/@tanstack/ai-angular@740

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@740

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@740

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@740

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@740

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@740

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@740

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@740

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@740

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@740

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@740

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@740

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@740

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@740

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@740

@tanstack/ai-mcp

npm i https://pkg.pr.new/@tanstack/ai-mcp@740

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@740

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@740

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@740

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@740

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@740

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@740

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@740

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@740

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@740

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@740

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@740

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@740

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@740

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@740

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@740

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@740

commit: e813487

@tombeckenham tombeckenham force-pushed the 618-image-to-image-and-image-to-video-support branch from 0c65cc7 to acd7319 Compare June 11, 2026 00:18
@tombeckenham tombeckenham force-pushed the 707-featai-openrouter-video-generation-adapter-apiv1videos-+-image-activity-follow-ups branch from 413f0a7 to 7bb9066 Compare June 11, 2026 00:41
@tombeckenham tombeckenham marked this pull request as draft June 11, 2026 00:43
Base automatically changed from 618-image-to-image-and-image-to-video-support to main June 17, 2026 13:18
@tombeckenham tombeckenham force-pushed the 707-featai-openrouter-video-generation-adapter-apiv1videos-+-image-activity-follow-ups branch from 7bb9066 to c27d0b0 Compare June 23, 2026 10:09
@socket-security

socket-security Bot commented Jun 23, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm js-yaml is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@tanstack/solid-router-ssr-query@1.141.1npm/js-yaml@4.2.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/js-yaml@4.2.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@tombeckenham tombeckenham marked this pull request as ready for review June 24, 2026 06:51

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
packages/ai-openrouter/tests/video-adapter.test.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move this unit test alongside the source module.

This test is under packages/ai-openrouter/tests/, but the repo guideline requires colocated *.test.ts files next to source.
As per coding guidelines, "**/*.test.ts: Place unit tests alongside source code in *.test.ts files".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-openrouter/tests/video-adapter.test.ts` at line 1, The video
adapter unit test is in the wrong location and should be moved to sit next to
the source module to match the repository’s colocated test convention. Relocate
the test for the video adapter so it lives alongside the module it exercises,
and keep the existing test contents and imports intact; use the video adapter
test file itself and the related source module name to find the correct
colocated `*.test.ts` location.

Source: Coding guidelines

packages/ai-openrouter/tests/video-per-model-type-safety.test.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Place this unit test next to the source it validates.

This file is in packages/ai-openrouter/tests/, but unit tests should be colocated as *.test.ts beside source modules.
As per coding guidelines, "**/*.test.ts: Place unit tests alongside source code in *.test.ts files".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-openrouter/tests/video-per-model-type-safety.test.ts` at line 1,
The unit test is in the wrong location and should be colocated with the source
module it validates. Move the video-per-model-type-safety test to the same
directory as the related source file and keep its name as a nearby *.test.ts
file so it follows the testing convention used across the codebase. Use the
existing test name to find the related module and place the test beside it.

Source: Coding guidelines

examples/ts-react-media/src/lib/server-functions.ts (1)

72-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Single source of truth for OpenRouter model ids.

OPENROUTER_VIDEO_MODEL_IDS duplicates the OpenRouter ids that also appear as createVideoJobFn switch cases and in VIDEO_MODELS (models.ts). For example code this is acceptable, but deriving the set from VIDEO_MODELS would prevent the two lists from drifting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/ts-react-media/src/lib/server-functions.ts` around lines 72 - 91,
The OpenRouter model id list is duplicated across `OPENROUTER_VIDEO_MODEL_IDS`,
`createVideoJobFn`, and `VIDEO_MODELS`, which risks drift. Update
`videoAdapterForModel` to derive its OpenRouter membership from the shared
`VIDEO_MODELS` source of truth instead of hardcoding ids locally, and make sure
the routing logic still correctly selects `openRouterVideo` versus `falVideo`
for each model.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/config.json`:
- Around line 259-260: The docs freshness metadata for the
media/image-generation page is stale because the config entry still uses an
older updatedAt value even though docs/media/image-generation.md was modified in
this PR. Update the corresponding entry in docs/config.json for
media/image-generation so its updatedAt matches the PR date, using the existing
docs metadata entry structure as the anchor.

In `@packages/ai-openrouter/src/adapters/video.ts`:
- Around line 45-52: The authenticated video download in getVideoUrl() can stall
indefinitely, so add a configurable timeout or abort signal around the direct
fetch used there and ensure generateVideo() uses that bounded path. Extend
OpenRouterVideoConfig and the getVideoUrl / generateVideo flow in video.ts so
callers can control the fetch deadline, and make the fetch cleanup handle
cancellation consistently when the gateway/CDN is slow.
- Around line 118-147: The video adapter in the OpenRouter path is validating
only start/end frame images, but it still forwards reference/character images
without checking whether the model supports reference conditioning. Update the
input classification logic in the same function that handles `starts`, `ends`,
and `references` to validate `reference` / `character` images against the model
metadata before building `inputReferences`, and throw a local error when the
model does not support them, consistent with the existing
`getVideoModelMeta(model)?.frameImages` checks.
- Around line 281-310: Add runtime validation for the model-specific
`resolution` and `aspectRatio` options before assigning them in the video
request builder. In the video adapter’s request construction flow, after
`validateVideoSize` and `validateVideoDuration`, validate
`modelOptions.resolution` and `modelOptions.aspectRatio` against the allowed
values for `this.model` (using the existing video metadata/helpers if available)
instead of only casting them in the `request` assembly. Keep the current
`request` shape in the video generation path, but ensure unsupported values are
rejected before `VideoGenerationRequest` is sent.

In `@packages/ai/skills/ai-core/media-generation/SKILL.md`:
- Around line 271-283: The role/matrix section has stale Veo availability text
that still says support is “planned” or “no Veo adapter yet,” which no longer
matches current behavior. Update the `'character'`, `'start_frame'`, and
Gemini/Veo rows in SKILL.md to describe the actual Veo handling instead of
deferred support. Keep the wording aligned with the existing mapping table so
the `generateImage`/`generateVideo` provider support matrix and the role mapping
stay consistent.

---

Nitpick comments:
In `@examples/ts-react-media/src/lib/server-functions.ts`:
- Around line 72-91: The OpenRouter model id list is duplicated across
`OPENROUTER_VIDEO_MODEL_IDS`, `createVideoJobFn`, and `VIDEO_MODELS`, which
risks drift. Update `videoAdapterForModel` to derive its OpenRouter membership
from the shared `VIDEO_MODELS` source of truth instead of hardcoding ids
locally, and make sure the routing logic still correctly selects
`openRouterVideo` versus `falVideo` for each model.

In `@packages/ai-openrouter/tests/video-adapter.test.ts`:
- Line 1: The video adapter unit test is in the wrong location and should be
moved to sit next to the source module to match the repository’s colocated test
convention. Relocate the test for the video adapter so it lives alongside the
module it exercises, and keep the existing test contents and imports intact; use
the video adapter test file itself and the related source module name to find
the correct colocated `*.test.ts` location.

In `@packages/ai-openrouter/tests/video-per-model-type-safety.test.ts`:
- Line 1: The unit test is in the wrong location and should be colocated with
the source module it validates. Move the video-per-model-type-safety test to the
same directory as the related source file and keep its name as a nearby
*.test.ts file so it follows the testing convention used across the codebase.
Use the existing test name to find the related module and place the test beside
it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ca3dcb1-4c47-4a38-b8f8-1b1bed471759

📥 Commits

Reviewing files that changed from the base of the PR and between eddfbbd and e55fac2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (27)
  • .changeset/openrouter-video-adapter.md
  • .changeset/video-adapter-duration-constraint.md
  • docs/adapters/openrouter.md
  • docs/config.json
  • docs/media/image-generation.md
  • docs/media/video-generation.md
  • examples/ts-react-media/package.json
  • examples/ts-react-media/src/lib/models.ts
  • examples/ts-react-media/src/lib/server-functions.ts
  • packages/ai-openrouter/package.json
  • packages/ai-openrouter/src/adapters/image.ts
  • packages/ai-openrouter/src/adapters/video.ts
  • packages/ai-openrouter/src/image/image-provider-options.ts
  • packages/ai-openrouter/src/index.ts
  • packages/ai-openrouter/src/model-meta.ts
  • packages/ai-openrouter/src/video/video-provider-options.ts
  • packages/ai-openrouter/tests/image-adapter.test.ts
  • packages/ai-openrouter/tests/video-adapter.test.ts
  • packages/ai-openrouter/tests/video-per-model-type-safety.test.ts
  • packages/ai/skills/ai-core/media-generation/SKILL.md
  • packages/ai/src/activities/generateVideo/index.ts
  • scripts/convert-openrouter-models.ts
  • scripts/fetch-openrouter-models.ts
  • scripts/openrouter.video-models.json
  • scripts/openrouter.video-models.ts
  • testing/e2e/package.json
  • testing/e2e/src/lib/feature-support.ts

Comment thread docs/config.json
Comment on lines +259 to 260
"updatedAt": "2026-06-10"
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Refresh media/image-generation updatedAt to this PR date.

docs/media/image-generation.md is changed in this PR, but its entry still shows "updatedAt": "2026-06-10" instead of today (2026-06-24), so docs freshness metadata is inconsistent.

As per coding guidelines, “Update updatedAt timestamp in docs/config.json when making content changes to a documentation page.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/config.json` around lines 259 - 260, The docs freshness metadata for the
media/image-generation page is stale because the config entry still uses an
older updatedAt value even though docs/media/image-generation.md was modified in
this PR. Update the corresponding entry in docs/config.json for
media/image-generation so its updatedAt matches the PR date, using the existing
docs metadata entry structure as the anchor.

Source: Coding guidelines

Comment on lines +45 to +52
export interface OpenRouterVideoConfig extends OpenRouterClientConfig {
/**
* Injectable fetch implementation used for the authenticated video
* content download (tests, custom runtimes). Defaults to the global
* fetch.
*/
fetch?: typeof globalThis.fetch
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound the authenticated video download.

getVideoUrl() can hang indefinitely if the gateway/CDN stalls, and downstream generateVideo() waits on this call. Add a configurable timeout/abort signal around the direct content fetch.

Suggested direction
 export interface OpenRouterVideoConfig extends OpenRouterClientConfig {
@@
   fetch?: typeof globalThis.fetch
+  downloadTimeoutMs?: number
 }
+
+const DEFAULT_VIDEO_DOWNLOAD_TIMEOUT_MS = 120_000
@@
     const doFetch = this.clientConfig.fetch ?? globalThis.fetch
-    const contentResponse = await doFetch(contentUrl, {
-      headers: { Authorization: `Bearer ${this.clientConfig.apiKey}` },
-    })
+    const controller = new AbortController()
+    const timeout = setTimeout(
+      () => controller.abort(),
+      this.clientConfig.downloadTimeoutMs ?? DEFAULT_VIDEO_DOWNLOAD_TIMEOUT_MS,
+    )
+    let contentResponse: Response
+    try {
+      contentResponse = await doFetch(contentUrl, {
+        headers: { Authorization: `Bearer ${this.clientConfig.apiKey}` },
+        signal: controller.signal,
+      })
+    } finally {
+      clearTimeout(timeout)
+    }

Also applies to: 373-382

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-openrouter/src/adapters/video.ts` around lines 45 - 52, The
authenticated video download in getVideoUrl() can stall indefinitely, so add a
configurable timeout or abort signal around the direct fetch used there and
ensure generateVideo() uses that bounded path. Extend OpenRouterVideoConfig and
the getVideoUrl / generateVideo flow in video.ts so callers can control the
fetch deadline, and make the fetch cleanup handle cancellation consistently when
the gateway/CDN is slow.

Comment on lines +118 to +147
if (role === 'end_frame') ends.push(url)
else if (role === 'reference' || role === 'character') references.push(url)
// Unroled parts default to the start frame (image-to-video).
else starts.push(url)
}

if (starts.length > 1) {
throw new Error(
`openrouter: at most one start-frame image is supported per request (received ${starts.length}). Mark additional images with metadata.role 'reference' or 'end_frame'.`,
)
}
if (ends.length > 1) {
throw new Error(
`openrouter: at most one input with metadata.role === 'end_frame' is supported per request (received ${ends.length}).`,
)
}

const supportedFrames = getVideoModelMeta(model)?.frameImages
if (supportedFrames) {
if (starts.length > 0 && !supportedFrames.includes('first_frame')) {
throw new Error(
`openrouter: model ${model} does not accept a start-frame image (supported frame images: ${supportedFrames.join(', ') || 'none'}).`,
)
}
if (ends.length > 0 && !supportedFrames.includes('last_frame')) {
throw new Error(
`openrouter: model ${model} does not accept an end-frame image (supported frame images: ${supportedFrames.join(', ') || 'none'}).`,
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate reference-image support before forwarding inputReferences.

Frame images are checked against model metadata, but reference / character inputs are always accepted. For models that do not support reference/image conditioning, this submits an invalid provider request instead of failing locally as the adapter promises.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-openrouter/src/adapters/video.ts` around lines 118 - 147, The
video adapter in the OpenRouter path is validating only start/end frame images,
but it still forwards reference/character images without checking whether the
model supports reference conditioning. Update the input classification logic in
the same function that handles `starts`, `ends`, and `references` to validate
`reference` / `character` images against the model metadata before building
`inputReferences`, and throw a local error when the model does not support them,
consistent with the existing `getVideoModelMeta(model)?.frameImages` checks.

Comment on lines +281 to +310
validateVideoSize(this.model, size)
validateVideoDuration(this.model, duration)

const imageFields = mapImagePartsToVideoFields(this.model, resolved.images)

const request: VideoGenerationRequest = {
model: this.model,
prompt: resolved.text,
...imageFields,
...(size ? { size } : {}),
...(duration !== undefined ? { duration } : {}),
...(modelOptions?.seed !== undefined ? { seed: modelOptions.seed } : {}),
...(modelOptions?.generateAudio !== undefined
? { generateAudio: modelOptions.generateAudio }
: {}),
...(modelOptions?.callbackUrl
? { callbackUrl: modelOptions.callbackUrl }
: {}),
...(modelOptions?.provider ? { provider: modelOptions.provider } : {}),
}
// The SDK types these as branded open enums; the per-model literal
// unions derived from OPENROUTER_VIDEO_MODEL_META can be broader than
// the SDK's enum members (e.g. grok-imagine-video's '3:2'), so narrow at
// the boundary — the wire format is a plain string either way.
if (modelOptions?.resolution) {
request.resolution = modelOptions.resolution as Resolution
}
if (modelOptions?.aspectRatio) {
request.aspectRatio = modelOptions.aspectRatio as AspectRatio
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add runtime validation for resolution and aspectRatio.

size and duration are metadata-validated, but modelOptions.resolution and modelOptions.aspectRatio are cast and forwarded without runtime checks. JavaScript callers or escaped TS values can still send unsupported per-model options.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-openrouter/src/adapters/video.ts` around lines 281 - 310, Add
runtime validation for the model-specific `resolution` and `aspectRatio` options
before assigning them in the video request builder. In the video adapter’s
request construction flow, after `validateVideoSize` and
`validateVideoDuration`, validate `modelOptions.resolution` and
`modelOptions.aspectRatio` against the allowed values for `this.model` (using
the existing video metadata/helpers if available) instead of only casting them
in the `request` assembly. Keep the current `request` shape in the video
generation path, but ensure unsupported values are rejected before
`VideoGenerationRequest` is sent.

Comment on lines +271 to +283
| `'character'` | Same as `'reference'`; Veo `referenceImages` slot (planned — no Veo adapter yet) |
| `'mask'` | OpenAI `mask` (gpt-image-2, gpt-image-1, dall-e-2); fal `mask_url` |
| `'control'` | fal `control_image_url` (ControlNet / depth / pose) |
| `'start_frame'` | fal `start_image_url` (or the endpoint's field, e.g. `image_url` on Kling i2v); OpenRouter `frame_images[]` `first_frame`; Veo `image` (planned) |
| `'end_frame'` | fal `end_image_url` (or e.g. `tail_image_url` / `last_frame_url`); OpenRouter `frame_images[]` `last_frame`; Veo `lastFrame` (planned) |

**Provider support matrix:**

| Provider | `generateImage` image parts | `generateVideo` image parts |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OpenAI | gpt-image-2 / gpt-image-1 / -mini → `images.edit()` (up to 16). dall-e-2 → edit (1). dall-e-3 throws. | Sora-2 / -pro → `input_reference` (single). Throws if >1. |
| Gemini | Native (gemini-\*-flash-image, "nano-banana") → multimodal `contents`. Imagen throws. | No native Veo adapter yet — deferred to a follow-up. |
| fal | Per-endpoint field names from a generated map (`pnpm generate:fal-image-fields`). Defaults: 1 input → `image_url`; >1 → `image_urls`; roles → `mask_url` / `control_image_url` / `reference_image_urls`. | Per-endpoint map (e.g. Kling i2v start frame → `image_url`). Defaults: 1 input → `image_url`; `start_frame`/`end_frame` → `start_image_url`/`end_image_url`; `reference` → `reference_image_urls`. |
| Grok | grok-imagine models → `/v1/images/edits` JSON endpoint (≤3 sources, addressed by xAI in request order; prompt sent verbatim; mask/control throw). grok-2-image-1212 throws. | n/a |
| OpenRouter | Prompt parts map 1:1 onto multimodal `text` / `image_url` content parts, preserving interleaved order. | n/a |
| Anthropic | n/a (no image generation API). | n/a |
| Provider | `generateImage` image parts | `generateVideo` image parts |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OpenAI | gpt-image-2 / gpt-image-1 / -mini → `images.edit()` (up to 16). dall-e-2 → edit (1). dall-e-3 throws. | Sora-2 / -pro → `input_reference` (single). Throws if >1. |
| Gemini | Native (gemini-\*-flash-image, "nano-banana") → multimodal `contents`. Imagen throws. | No native Veo adapter yet — deferred to a follow-up. |
| fal | Per-endpoint field names from a generated map (`pnpm generate:fal-image-fields`). Defaults: 1 input → `image_url`; >1 → `image_urls`; roles → `mask_url` / `control_image_url` / `reference_image_urls`. | Per-endpoint map (e.g. Kling i2v start frame → `image_url`). Defaults: 1 input → `image_url`; `start_frame`/`end_frame` → `start_image_url`/`end_image_url`; `reference` → `reference_image_urls`. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix stale Veo availability wording in the role/matrix section.

This section still says Veo support is “planned” / “no Veo adapter yet,” which conflicts with the current Gemini/Veo usage shown in this PR context. Please update these rows to reflect current behavior.

🧰 Tools
🪛 SkillSpector (2.2.3)

[warning] 521: [E1] External Transmission: Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Remediation: Verify the destination URL is trusted and necessary. Remove or replace with documented APIs. Ensure no secrets, tokens, or PII are transmitted.

(Data Exfiltration (E1))


[error] 544: [MP3] Memory Manipulation: Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Remediation: Protect agent memory and state from modification by untrusted content. Use read-only memory for critical instructions and validate all state changes.

(Memory Poisoning (MP3))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai/skills/ai-core/media-generation/SKILL.md` around lines 271 - 283,
The role/matrix section has stale Veo availability text that still says support
is “planned” or “no Veo adapter yet,” which no longer matches current behavior.
Update the `'character'`, `'start_frame'`, and Gemini/Veo rows in SKILL.md to
describe the actual Veo handling instead of deferred support. Keep the wording
aligned with the existing mapping table so the `generateImage`/`generateVideo`
provider support matrix and the role mapping stay consistent.

tombeckenham and others added 6 commits June 24, 2026 17:12
…e activity follow-ups

Closes #707.

- Add openRouterVideo: async jobs adapter for OpenRouter's dedicated video
  API (submit -> poll -> download). Per-model size/duration/option types are
  generated from GET /api/v1/videos/models; frame roles map onto
  frame_images[] / input_references[] per the MediaInputRole taxonomy.
- Teach the model-meta sync scripts the videos/models endpoint
  (openrouter.video-models.json + OPENROUTER_VIDEO_MODEL_META).
- Image adapter follow-ups from the #624 review: throw on unmapped sizes
  (the size union used a Unicode multiplication sign so every non-square
  size silently dropped its aspect ratio), throw on numberOfImages > 1
  (live-verified: the gateway ignores all count keys), expose
  image_config.strength.
- Completed videos are returned as data: URLs (unsigned_urls 401 without
  the API key header) with gateway-reported cost on usage.cost. The SDK's
  getVideoContent is bypassed: its matcher only accepts
  application/octet-stream while the endpoint serves video/mp4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The getVideoContent response-matcher bug is still present in 0.12.79 (the
stream matcher only accepts application/octet-stream while the endpoint
serves video/mp4), so the direct unsigned-URL download stays. Link the
aimock feature request (CopilotKit/aimock#261) from the e2e matrix
exclusion. Submit/poll/download lifecycle re-verified live on the new SDK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire openRouterVideo onto the shared typed-duration contract (the same one
geminiVideo uses): add the sixth BaseVideoAdapter generic
(OpenRouterVideoModelDurationByName), narrow `duration` per model from the
published `/api/v1/videos/models` metadata, and override availableDurations()
/ snapDuration() (backed by snapToDurationOption). `duration` is now a
compile-time per-model union; the runtime validateVideoDuration backstop stays
for JS callers and unknown-meta models.

- video-provider-options.ts: OpenRouterVideoModelDurationByName +
  getVideoDurationOptions (discrete from meta, none when unknown/empty)
- adapter: 6th generic, createVideoJob narrowed to per-model size/duration,
  availableDurations()/snapDuration() overrides
- export OpenRouterVideoModelDurationByName from index
- tests: 4 introspection cases; existing negative size/duration tests now use
  @ts-expect-error (proves the union rejects them) while still asserting the
  runtime throw
- docs/media/video-generation.md, docs/adapters/openrouter.md, media-generation
  SKILL.md: document snapDuration/availableDurations for OpenRouter; bump
  updatedAt; changeset

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o typed-duration constraint

ts-react-media now demos openRouterVideo alongside fal:
- add @tanstack/ai-openrouter dep
- Seedance 2.0 (text-to-video) and Veo 3.1 (image-to-video) model entries
- switch cases showcasing adapter.snapDuration() to coerce raw UI seconds to
  the model's nearest supported duration (Seedance 7→7, Veo 3.1 7→6)
- videoAdapterForModel() resolver routes the poll/status path to the right
  adapter (the helpers were hardcoded to falVideo)

Building the example surfaced a pre-existing bug in the #624 typed-duration
contract: generateVideo()'s `TAdapter extends VideoAdapter<string, any, any,
any>` bound let the sixth (duration) generic fall back to its
`Record<string, number>` default. Because VideoAdapter.createVideoJob is a
contravariant function-valued property, no adapter whose `duration` is a
per-model literal union (Veo `4|6|8`, Seedance `4..15`) satisfied the bound —
so even the documented `generateVideo({ adapter: geminiVideo('veo-3.1-...') })`
failed to type-check. Widen the bound to leave size/duration unpinned at all 10
activity sites; per-model types are still recovered via inference
(VideoSizeForAdapter / VideoDurationForAdapter). Adds a compile-only regression
test and an @tanstack/ai changeset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…doc type-check

The new kiira doc-typecheck (#805) type-checks fenced TS samples. The OpenRouter
snapDuration snippet referenced generateVideo/openRouterVideo/sliderSeconds
without imports or a declaration; add them so the snippet type-checks standalone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tombeckenham tombeckenham force-pushed the 707-featai-openrouter-video-generation-adapter-apiv1videos-+-image-activity-follow-ups branch from e55fac2 to fc9f720 Compare June 24, 2026 07:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/ai/skills/ai-core/media-generation/SKILL.md (1)

271-283: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale Veo wording in role/matrix to match current support.

Line 271 and Line 282 still say Veo is “planned/deferred,” but Line 439+ documents active Veo support via geminiVideo. Please align these rows with the current behavior to avoid contradictory guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai/skills/ai-core/media-generation/SKILL.md` around lines 271 - 283,
Update the stale Veo entries in the media-generation skill docs so they match
the actual `geminiVideo` support. In the role mapping and the provider support
matrix, remove the “planned/deferred/no adapter yet” wording for Veo and replace
it with the current supported behavior, keeping the descriptions consistent with
the existing `geminiVideo` section. Use the nearby symbols `'character'`,
`'start_frame'`, `'end_frame'`, `generateImage`, and `generateVideo` to locate
the affected rows.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/ai/skills/ai-core/media-generation/SKILL.md`:
- Around line 271-283: Update the stale Veo entries in the media-generation
skill docs so they match the actual `geminiVideo` support. In the role mapping
and the provider support matrix, remove the “planned/deferred/no adapter yet”
wording for Veo and replace it with the current supported behavior, keeping the
descriptions consistent with the existing `geminiVideo` section. Use the nearby
symbols `'character'`, `'start_frame'`, `'end_frame'`, `generateImage`, and
`generateVideo` to locate the affected rows.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 403b8554-7d30-449a-b560-614b7de96d5b

📥 Commits

Reviewing files that changed from the base of the PR and between e55fac2 and fc9f720.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (27)
  • .changeset/openrouter-video-adapter.md
  • .changeset/video-adapter-duration-constraint.md
  • docs/adapters/openrouter.md
  • docs/config.json
  • docs/media/image-generation.md
  • docs/media/video-generation.md
  • examples/ts-react-media/package.json
  • examples/ts-react-media/src/lib/models.ts
  • examples/ts-react-media/src/lib/server-functions.ts
  • packages/ai-openrouter/package.json
  • packages/ai-openrouter/src/adapters/image.ts
  • packages/ai-openrouter/src/adapters/video.ts
  • packages/ai-openrouter/src/image/image-provider-options.ts
  • packages/ai-openrouter/src/index.ts
  • packages/ai-openrouter/src/model-meta.ts
  • packages/ai-openrouter/src/video/video-provider-options.ts
  • packages/ai-openrouter/tests/image-adapter.test.ts
  • packages/ai-openrouter/tests/video-adapter.test.ts
  • packages/ai-openrouter/tests/video-per-model-type-safety.test.ts
  • packages/ai/skills/ai-core/media-generation/SKILL.md
  • packages/ai/src/activities/generateVideo/index.ts
  • scripts/convert-openrouter-models.ts
  • scripts/fetch-openrouter-models.ts
  • scripts/openrouter.video-models.json
  • scripts/openrouter.video-models.ts
  • testing/e2e/package.json
  • testing/e2e/src/lib/feature-support.ts
✅ Files skipped from review due to trivial changes (8)
  • examples/ts-react-media/src/lib/models.ts
  • docs/media/image-generation.md
  • packages/ai-openrouter/tests/video-per-model-type-safety.test.ts
  • .changeset/openrouter-video-adapter.md
  • testing/e2e/src/lib/feature-support.ts
  • docs/config.json
  • .changeset/video-adapter-duration-constraint.md
  • testing/e2e/package.json
🚧 Files skipped from review as they are similar to previous changes (18)
  • packages/ai-openrouter/package.json
  • scripts/openrouter.video-models.ts
  • packages/ai-openrouter/src/image/image-provider-options.ts
  • examples/ts-react-media/package.json
  • packages/ai-openrouter/tests/image-adapter.test.ts
  • docs/adapters/openrouter.md
  • packages/ai-openrouter/src/index.ts
  • scripts/convert-openrouter-models.ts
  • packages/ai-openrouter/src/adapters/image.ts
  • packages/ai-openrouter/src/model-meta.ts
  • docs/media/video-generation.md
  • scripts/openrouter.video-models.json
  • packages/ai-openrouter/src/adapters/video.ts
  • examples/ts-react-media/src/lib/server-functions.ts
  • scripts/fetch-openrouter-models.ts
  • packages/ai/src/activities/generateVideo/index.ts
  • packages/ai-openrouter/src/video/video-provider-options.ts
  • packages/ai-openrouter/tests/video-adapter.test.ts

@tombeckenham

Copy link
Copy Markdown
Contributor Author

Video generated using Seedance 2.0 through openrouter in the example app.

download.mp4

@tombeckenham tombeckenham marked this pull request as draft June 24, 2026 07:38
@tombeckenham

Copy link
Copy Markdown
Contributor Author

Hold off a minute. I just spotted something horible Claude did

…ass comment

Live-verified against @openrouter/sdk 0.12.79: getVideoContent still rejects the
real 'video/mp4' response ('Unexpected Status or Content-Type') even though the
body is a valid MP4, so the manual authenticated download (and its injectable
fetch seam) stays. Only the comment's stale 0.12.35 reference was wrong.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* content download (tests, custom runtimes). Defaults to the global
* fetch.
*/
fetch?: typeof globalThis.fetch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a horrible workaround that claude added. It's needed as the sdk isn't downloading video properly. don't merge like this

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.

feat(ai-openrouter): video generation adapter (/api/v1/videos) + image activity follow-ups

1 participant