建立影片生成 Provider 外掛
影片生成 provider 外掛註冊一個後端來處理每一個 video_generate 工具呼叫。內建的 provider(xAI、FAL)以外掛形式發佈。你可以透過在 plugins/video_gen/<name>/ 中放入一個目錄來新增一個新的,或覆寫一個已有的。
提示
影片生成幾乎逐行對應 Image Generation Provider Plugins — 如果你已經建構過圖片生成後端,你就已經知道格式了。主要差異:一個
capabilities()方法廣告模態/寬高比/時長,以及一個路由慣例(傳遞image_url以使用圖生影片,省略它以使用文生影片 — provider 在內部選擇正確的端點)。
統一介面(一個工具,兩種模態)
video_generate 工具透過一個參數暴露兩種模態:
- 文生影片 — 只使用
prompt呼叫。Provider 路由到其文生影片端點。 - 圖生影片 — 使用
prompt+image_url呼叫。Provider 路由到其圖生影片端點。
編輯和擴展有意不在範圍內。大多數後端不支援它們,而且不一致會迫使每個後端的描述文字進入代理程式的工具描述中。
發現機制如何運作
Hermes 在三個位置掃描影片生成後端:
- 內建 —
<repo>/plugins/video_gen/<name>/(自動載入,kind: backend) - 使用者 —
~/.hermes/plugins/video_gen/<name>/(透過plugins.enabled選擇性啟用) - Pip — 宣告
hermes_agent.plugins入口點的套件
每個外掛的 register(ctx) 函式呼叫 ctx.register_video_gen_provider(...)。啟用的 provider 由 config.yaml 中的 video_gen.provider 選擇;hermes tools → Video Generation 引導使用者進行選擇。與 image_generate 不同,沒有樹內的舊式後端 — 每個 provider 都是外掛。
目錄結構
plugins/video_gen/my-backend/
├── __init__.py # VideoGenProvider 子類別 + register()
└── plugin.yaml # 帶有 kind: backend 的 Manifest
VideoGenProvider ABC
繼承 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]]:
# 每個條目是一個模型系列 — 使用者選擇一次的名稱。
# 你的 provider 的 generate() 根據是否傳遞了 image_url
# 在系列內路由。
return [
{
"id": "fast",
"display": "Fast",
"speed": "~30s",
"strengths": "Cheapest tier",
"price": "$0.05/s",
"modalities": ["text", "image"], # 諮詢性
},
]
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": "在 `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, # 始終忽略未知 kwargs 以實現向前相容
) -> Dict[str, Any]:
# 路由:image_url 的存在決定端點。
if image_url:
endpoint = "my-backend/image-to-video"
modality_used = "image"
else:
endpoint = "my-backend/text-to-video"
modality_used = "text"
# ... 呼叫你的 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())
外掛 Manifest
# 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 Schema
工具在每個後端上暴露一個統一的 schema。Provider 會忽略它們不支援的參數。
| 參數 | 功能 |
|---|---|
prompt | 文字指令(必填) |
image_url | 設定時 → 圖生影片;省略時 → 文生影片 |
reference_image_urls | 風格/角色參考(取決於 provider) |
duration | 秒數 — provider 會限制 |
aspect_ratio | "16:9"、"9:16"、"1:1"…… — provider 會限制 |
resolution | "480p" / "540p" / "720p" / "1080p" — provider 會限制 |
negative_prompt | 要避免的內容(僅 Pixverse/Kling) |
audio | 原生音訊(Veo3 / Pixverse 定價層級) |
seed | 可重現性 |
model | 覆寫啟用的模型/系列 |
Provider 的 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",
# ... 系列特定的能力標誌 ...
},
}
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"]
# ... 從系列宣告的能力標誌建構載荷,呼叫端點 ...
使用者在 hermes tools 中選擇一次 veo3.1。代理程式永遠不需要考慮端點 — 它只傳遞(或不傳遞)image_url。
選擇優先順序
對於每個實例的模型旋鈕(參見 plugins/video_gen/fal/__init__.py):
- 工具呼叫中的
model=關鍵字 <PROVIDER>_VIDEO_MODEL環境變數config.yaml中的video_gen.<provider>.modelconfig.yaml中的video_gen.model(當它是你的 ID 之一時)- Provider 的
default_model()
回應格式
success_response() 和 error_response() 產生每個後端回傳的字典格式。使用它們 — 不要手動製作字典。
成功金鑰:success、video(URL 或絕對路徑)、model、prompt、modality("text" 或 "image")、aspect_ratio、duration、provider,加上 extra。
錯誤金鑰:success、video(None)、error、error_type、model、prompt、aspect_ratio、provider。
儲存工件的位置
如果你的後端回傳 base64,使用 save_b64_video() 寫入 $HERMES_HOME/cache/videos/ 下。對於後續 HTTP 取得的原始位元組,使用 save_bytes_video()。 otherwise 直接回傳上游 URL — 閘道器在遞送時解析遠端 URL。
測試
在 tests/plugins/video_gen/test_<name>_plugin.py 下放置一個冒煙測試。xAI 和 FAL 測試展示了模式 — 註冊、驗證目錄、在有和沒有 image_url 的情況下測試路由、斷言缺少認證時的乾淨錯誤回應。