Skip to main content
Video-gen provider plugins register a backend that services every video_generate tool call. Built-in providers (xAI, FAL, OpenRouter, DeepInfra) ship as plugins. Add a new one, or override a bundled one, by dropping a directory into plugins/video_gen/<name>/.
Video-gen mirrors Image Generation Provider Plugins almost line-for-line — if you’ve built an image-gen backend, you already know the shape. The main differences: a capabilities() method advertising modalities/aspect-ratios/durations, and a routing convention (pass image_url to use image-to-video, omit it to use text-to-video — the provider picks the right endpoint internally).

The unified surface (one tool, two modalities)

The video_generate tool exposes two modalities through one parameter:
  • Text-to-video — call with prompt only. The provider routes to its text-to-video endpoint.
  • Image-to-video — call with prompt + image_url. The provider routes to its image-to-video endpoint.
Edit and extend are intentionally out of scope. Most backends don’t support them and the inconsistency would force per-backend prose into the agent’s tool description.

How discovery works

Mibyan scans for video-gen backends in three places:
  1. Bundled — <repo>/plugins/video_gen/<name>/ (auto-loaded with kind: backend)
  2. User — ~/.mibyan/plugins/video_gen/<name>/ (opt-in via plugins.enabled)
  3. Pip — packages declaring a mibyan_agent.plugins entry point
Each plugin’s register(ctx) function calls ctx.register_video_gen_provider(...). The active provider is picked by video_gen.provider in config.yaml; mibyan tools → Video Generation walks users through selection. Unlike image_generate, there is no in-tree legacy backend — every provider is a plugin.

Directory structure

The VideoGenProvider ABC

Subclass agent.video_gen_provider.VideoGenProvider. Required: name property and generate() method.

The plugin manifest

The video_generate schema

The tool exposes one schema across every backend. Providers ignore parameters they don’t support. There is deliberately no model parameter: the backend and model are user configuration (video_gen.provider / video_gen.model), never an agent choice. Your generate() still receives model= — it is the configured model, resolved by the tool layer. The provider’s capabilities() advertises which of these are honored. The agent sees the active backend’s capabilities in the tool description, dynamically rebuilt when the user changes backend via mibyan tools.

Model families and endpoint routing (the FAL pattern)

When your backend has multiple endpoints per “model” — like FAL, where every family (Veo 3.1, Pixverse v6, Kling O3) has both a /text-to-video and an /image-to-video URL — represent each family as one catalog entry. Your generate() picks the right endpoint based on whether image_url was passed:
The user picks veo3.1 once in mibyan tools. The agent never thinks about endpoints — it just passes (or doesn’t pass) image_url.

Selection precedence

For per-instance model knobs (see plugins/video_gen/fal/__init__.py):
  1. <PROVIDER>_VIDEO_MODEL env var
  2. video_gen.<provider>.model in config.yaml
  3. video_gen.model in config.yaml (when it’s one of your IDs)
  4. Provider’s default_model()
The model= keyword your generate() receives is the outcome of this resolution — a model in the agent’s tool call is ignored, so the LLM cannot switch backends or billing tiers on its own.

Response shape

success_response() and error_response() produce the dict shape every backend returns. Use them — don’t hand-roll the dict. Success keys: success, video (URL or absolute path), model, prompt, modality ("text" or "image"), aspect_ratio, duration, provider, plus extra. Error keys: success, video (None), error, error_type, model, prompt, aspect_ratio, provider.

Where to save artifacts

If your backend returns base64, use save_b64_video() to write under $mibyan_HOME/cache/videos/. For raw bytes from a follow-up HTTP fetch, use save_bytes_video(). Otherwise return the upstream URL directly — the gateway resolves remote URLs on delivery.

Testing

Drop a smoke test under tests/plugins/video_gen/test_<name>_plugin.py. The xAI and FAL tests show the pattern — register, verify catalog, exercise routing both with and without image_url, assert clean error responses on missing auth.