본문으로 건너뛰기

비디오 생성 제공자 플러그인 구축

비디오 생성 제공자 플러그인은 모든 video_generate 도구 호출을 서비스하는 백엔드를 등록합니다. 내장 제공자(xAI, FAL)는 플러그인으로 제공됩니다. plugins/video_gen/<name>/에 디렉터리를 놓아 새 항목을 추가하거나 번들된 항목을 재정의합니다.

Video-gen은 이미지 생성 제공자 플러그인을 거의 한 줄씩 미러링합니다. image-gen 백엔드를 구축했다면 이미 그 모양을 알고 있을 것입니다. 주요 차이점: capabilities() 메소드 광고 형식/종횡비/기간 및 라우팅 규칙(이미지-비디오를 사용하려면 image_url을 전달하고, 텍스트-비디오를 사용하려면 이를 생략합니다. 제공자는 내부적으로 올바른 엔드포인트를 선택합니다).

통합된 표면(하나의 도구, 두 가지 양식)

video_generate 도구는 하나의 매개변수를 통해 두 가지 양식을 노출합니다.

  • 텍스트-비디오prompt로만 통화하세요. 제공자는 텍스트-비디오 엔드포인트로 라우팅합니다.
  • 이미지-비디오prompt + image_url로 호출합니다. 제공자는 이미지-비디오 엔드포인트로 라우팅합니다.

편집 및 확장은 의도적으로 범위를 벗어났습니다. 대부분의 백엔드는 이를 지원하지 않으며 불일치로 인해 백엔드별 구문이 에이전트의 도구 설명에 포함됩니다.

검색 작동 방식

Hermes는 세 곳에서 video-gen 백엔드를 검색합니다.

  1. 번들<repo>/plugins/video_gen/<name>/(kind: backend로 자동 로드됨)
  2. 사용자~/.hermes/plugins/video_gen/<name>/(plugins.enabled을 통해 선택)
  3. Piphermes_agent.plugins 진입점을 선언하는 패키지

각 플러그인의 register(ctx) 함수는 ctx.register_video_gen_provider(...)을 호출합니다. 활성 제공자는 config.yamlvideo_gen.provider에 의해 선택됩니다. hermes tools → 비디오 생성은 사용자에게 선택 과정을 안내합니다. image_generate과 달리 트리 내 레거시 백엔드가 없습니다. 모든 제공자는 플러그인입니다.

디렉토리 구조

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

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]]:
# 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. video_gen.<provider>.model(config.yaml)
  4. video_gen.model in config.yaml (ID 중 하나인 경우)
  5. 제공업체의 default_model()

응답 형태

success_response()error_response()은 모든 백엔드가 반환하는 사전 형태를 생성합니다. 이를 사용하세요. 딕셔너리를 직접 작성하지 마세요.

성공 키: success, video(URL 또는 절대 경로), model, prompt, modality("text" 또는 "image"), aspect_ratio, duration, providerextra.

오류 키: success, video (없음), error, error_type, model, prompt, aspect_ratio, provider.

유물을 저장할 위치

백엔드가 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 유무에 관계없이 라우팅 실행, 인증 누락에 대한 명확한 오류 응답 주장.