跳到主要內容

構建視頻生成提供商插件

視頻生成(video-gen)提供商插件註冊一個後端,用於服務每次 video_generate 工具調用。內置提供商(xAI、FAL)作為插件提供。通過將目錄放入 plugins/video_gen/<name>/ 中,可以添加新的提供商或覆蓋捆綁的提供商。

提示

視頻生成幾乎逐行鏡像了圖像生成提供商插件——如果你已經構建過圖像生成後端,你就已經瞭解了其結構。主要區別在於:一個用於宣傳模態/寬高比/時長的 capabilities() 方法,以及一種路由約定(傳遞 image_url 以使用圖生視頻,省略它以使用文生視頻——提供商在內部選擇正確的端點)。

統一接口(一個工具,兩種模態)

video_generate 工具通過一個參數暴露兩種模態:

  • 文生視頻 — 僅使用 prompt 調用。提供商將其路由到文生視頻端點。
  • 圖生視頻 — 使用 prompt + image_url 調用。提供商將其路由到圖生視頻端點。

編輯和擴展功能故意不在範圍內。大多數後端不支持這些功能,且這種不一致性會迫使代理的工具描述中包含針對每個後端的說明文字。

發現機制如何工作

Hermes 在三個位置掃描視頻生成後端:

  1. 捆綁<repo>/plugins/video_gen/<name>/(隨 kind: backend 自動加載)
  2. 用戶~/.hermes/plugins/video_gen/<name>/(通過 plugins.enabled 啟用)
  3. Pip — 聲明瞭 hermes_agent.plugins 入口點的包

每個插件的 register(ctx) 函數調用 ctx.register_video_gen_provider(...)。活動提供商由 config.yaml 中的 video_gen.provider 選定;hermes tools → Video Generation 會引導用戶進行選擇。與 image_generate 不同,代碼庫中沒有遺留的後端——每個提供商都是一個插件。

目錄結構

plugins/video_gen/my-backend/
├── __init__.py # VideoGenProvider subclass + register()
└── plugin.yaml # Manifest with kind: backend

VideoGenProvider 抽象基類

子類化 agent.video_gen_provider.VideoGenProvider。必需項:name 屬性和 generate() 方法。

# plugins/video_gen/my-backend/__init__.py
from typing import Any, Dict, List, Optional
import os

from agent.video_gen_provider import (
VideoGenProvider,
error_response,
success_response,
)


class MyVideoGenProvider(VideoGenProvider):
@property
def name(self) -> str:
return "my-backend"

@property
def display_name(self) -> str:
return "My Backend"

def is_available(self) -> bool:
return bool(os.environ.get("MY_API_KEY"))

def list_models(self) -> List[Dict[str, Any]]:
# Each entry is a model FAMILY — a name the user picks once.
# Your provider's generate() routes within the family based on
# whether image_url was passed.
return [
{
"id": "fast",
"display": "Fast",
"speed": "~30s",
"strengths": "Cheapest tier",
"price": "$0.05/s",
"modalities": ["text", "image"], # advisory
},
]

def default_model(self) -> Optional[str]:
return "fast"

def capabilities(self) -> Dict[str, Any]:
return {
"modalities": ["text", "image"],
"aspect_ratios": ["16:9", "9:16"],
"resolutions": ["720p", "1080p"],
"min_duration": 1,
"max_duration": 10,
"supports_audio": False,
"supports_negative_prompt": True,
"max_reference_images": 0,
}

def get_setup_schema(self) -> Dict[str, Any]:
return {
"name": "My Backend",
"badge": "paid",
"tag": "Short description shown in `hermes tools`",
"env_vars": [
{
"key": "MY_API_KEY",
"prompt": "My Backend API key",
"url": "https://mybackend.example.com/keys",
},
],
}

def generate(
self,
prompt: str,
*,
model: Optional[str] = None,
image_url: Optional[str] = None,
reference_image_urls: Optional[List[str]] = None,
duration: Optional[int] = None,
aspect_ratio: str = "16:9",
resolution: str = "720p",
negative_prompt: Optional[str] = None,
audio: Optional[bool] = None,
seed: Optional[int] = None,
**kwargs: Any, # always ignore unknown kwargs for forward-compat
) -> Dict[str, Any]:
# ROUTE: image_url presence picks the endpoint.
if image_url:
endpoint = "my-backend/image-to-video"
modality_used = "image"
else:
endpoint = "my-backend/text-to-video"
modality_used = "text"

# ... call your API ...

return success_response(
video="https://your-cdn/output.mp4",
model=model or "fast",
prompt=prompt,
modality=modality_used,
aspect_ratio=aspect_ratio,
duration=duration or 5,
provider=self.name,
)


def register(ctx) -> None:
ctx.register_video_gen_provider(MyVideoGenProvider())

插件清單

# plugins/video_gen/my-backend/plugin.yaml
name: my-backend
version: 1.0.0
description: "My video generation backend"
author: Your Name
kind: backend
requires_env:
- MY_API_KEY

video_generate 模式

該工具在所有後端之間暴露統一的模式。提供商忽略其不支持的參數。

參數作用
prompt文本指令(必需)
image_url設置時 → 圖生視頻;省略時 → 文生視頻
reference_image_urls風格/角色參考(取決於提供商)
duration秒數——由提供商限制範圍
aspect_ratio"16:9""9:16""1:1" 等——由提供商限制範圍
resolution"480p" / "540p" / "720p" / "1080p"——由提供商限制範圍
negative_prompt要避免的內容(僅限 Pixverse/Kling)
audio原生音頻(Veo3 / Pixverse 定價層級)
seed可復現性
model覆蓋活動模型/系列

提供商的 capabilities() 會宣傳哪些參數受支持。代理在工具描述中看到活動後端的功能,當用戶通過 hermes tools 更改後端時,這些功能會動態重建。

模型系列和端點路由(FAL 模式)

當你的後端每個“模型”有多個端點時——例如 FAL,其中每個系列(Veo 3.1、Pixverse v6、Kling O3)都有 /text-to-video/image-to-video URL——將每個系列表示為一個目錄條目。你的 generate() 根據是否傳遞了 image_url 來選擇正確的端點:

FAMILIES = {
"veo3.1": {
"text_endpoint": "fal-ai/veo3.1",
"image_endpoint": "fal-ai/veo3.1/image-to-video",
# ... family-specific capability flags ...
},
}

def generate(self, prompt, *, image_url=None, model=None, **kwargs):
family_id, family = _resolve_family(model)
endpoint = family["image_endpoint"] if image_url else family["text_endpoint"]
# ... build payload from family's declared capability flags, call endpoint ...

用戶在 hermes tools 中選擇一次 veo3.1。代理從不考慮端點——它只是傳遞(或不傳遞)image_url

選擇優先級

對於每個實例的模型調節參數(參見 plugins/video_gen/fal/__init__.py):

  1. 工具調用中的 model= 關鍵字
  2. <PROVIDER>_VIDEO_MODEL 環境變量
  3. config.yaml 中的 video_gen.<provider>.model
  4. config.yaml 中的 video_gen.model(當它是你的 ID 之一時)
  5. 提供商的 default_model()

響應結構

success_response()error_response() 生成每個後端返回的字典結構。請使用它們——不要手動構建字典。

成功鍵:successvideo(URL 或絕對路徑)、modelpromptmodality"text""image")、aspect_ratiodurationprovider,以及 extra

錯誤鍵:successvideo(None)、errorerror_typemodelpromptaspect_ratioprovider

保存工件的位置

如果你的後端返回 base64,請使用 save_b64_video() 寫入 $HERMES_HOME/cache/videos/ 下。對於來自後續 HTTP 獲取的原始字節,請使用 save_bytes_video()。否則直接返回上游 URL——網關會在交付時解析遠程 URL。

測試

將冒煙測試放入 tests/plugins/video_gen/test_<name>_plugin.py。xAI 和 FAL 測試展示了模式——註冊、驗證目錄、在有和沒有 image_url 的情況下練習路由、斷言在缺少身份驗證時的乾淨錯誤響應。