Pular para o conteúdo principal

Documentação oficial desta página · v0.20.6

Plugins de Geração de Vídeo

Construir um plugin de provedor de geração de vídeo

Plugins de provedor de geração de vídeo registram um backend que atende toda chamada da ferramenta video_generate. Os provedores embutidos (xAI, FAL, DeepInfra) são distribuídos como plugins. Para adicionar um novo, ou substituir um dos que vêm no pacote, basta colocar um diretório em plugins/video_gen/<nome>/.

dica

A geração de vídeo espelha os Plugins de Geração de Imagem quase linha por linha — se você já construiu um backend de geração de imagem, o formato já é conhecido. As diferenças principais: um método capabilities() que anuncia modalidades, proporções e durações, e uma convenção de roteamento (passe image_url para usar imagem-para-vídeo, omita para usar texto-para-vídeo — o provedor escolhe o endpoint certo internamente).

A superfície unificada (uma ferramenta, duas modalidades)​

A ferramenta video_generate expõe duas modalidades por meio de um único parâmetro:

  • Texto-para-vídeo — chame só com prompt. O provedor encaminha para o endpoint de texto-para-vídeo.
  • Imagem-para-vídeo — chame com prompt + image_url. O provedor encaminha para o endpoint de imagem-para-vídeo.

Editar e estender ficaram de fora de propósito. A maioria dos backends não dá suporte a isso, e a inconsistência obrigaria a enfiar prosa específica de cada backend na descrição da ferramenta que o agente lê.

Como funciona a descoberta​

O Hermes procura backends de geração de vídeo em três lugares:

  1. Empacotados — <repo>/plugins/video_gen/<nome>/ (carregados automaticamente com kind: backend)
  2. Do usuário — ~/.hermes/plugins/video_gen/<nome>/ (habilitação explícita via plugins.enabled)
  3. Pip — pacotes que declaram um entry point hermes_agent.plugins

A função register(ctx) de cada plugin chama ctx.register_video_gen_provider(...). O provedor ativo é escolhido por video_gen.provider no config.yaml; hermes tools → Video Generation conduz o usuário pela seleção. Diferente de image_generate, não existe backend legado dentro da árvore do projeto — todo provedor é um plugin.

Estrutura de diretórios​

plugins/video_gen/meu-backend/
├── __init__.py # subclasse de VideoGenProvider + register()
└── plugin.yaml # manifesto com kind: backend

A ABC VideoGenProvider​

Herde de agent.video_gen_provider.VideoGenProvider. Obrigatórios: a propriedade name e o método generate().

# plugins/video_gen/meu-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 "meu-backend"

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

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

def list_models(self) -> List[Dict[str, Any]]:
# Cada entrada é uma FAMÍLIA de modelos — um nome que o usuário
# escolhe uma vez. O generate() do seu provedor roteia dentro da
# família conforme image_url tenha sido passado ou não.
return [
{
"id": "fast",
"display": "Fast",
"speed": "~30s",
"strengths": "Faixa mais barata",
"price": "$0.05/s",
"modalities": ["text", "image"], # informativo
},
]

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": "Meu Backend",
"badge": "paid",
"tag": "Descrição curta exibida em `hermes tools`",
"env_vars": [
{
"key": "MY_API_KEY",
"prompt": "Chave de API do Meu Backend",
"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, # sempre ignore kwargs desconhecidos, por compatibilidade futura
) -> Dict[str, Any]:
# ROTEAMENTO: a presença de image_url escolhe o endpoint.
if image_url:
endpoint = "my-backend/image-to-video"
modality_used = "image"
else:
endpoint = "my-backend/text-to-video"
modality_used = "text"

# ... chame a sua API ...

return success_response(
video="https://sua-cdn/saida.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())

O manifesto do plugin​

# plugins/video_gen/meu-backend/plugin.yaml
name: my-backend
version: 1.0.0
description: "Meu backend de geração de vídeo"
author: Seu Nome
kind: backend
requires_env:
- MY_API_KEY

O schema de video_generate​

A ferramenta expõe um único schema para todos os backends. Cada provedor ignora os parâmetros que não suporta.

ParâmetroO que faz
promptInstrução em texto (obrigatório)
image_urlQuando definido → imagem-para-vídeo; quando omitido → texto-para-vídeo
reference_image_urlsReferências de estilo/personagem (depende do provedor)
durationSegundos — o provedor limita
aspect_ratio"16:9", "9:16", "1:1", ... — o provedor limita
resolution"480p" / "540p" / "720p" / "1080p" — o provedor limita
negative_promptConteúdo a evitar (só Pixverse/Kling)
audioÁudio nativo (faixa de preço do Veo3 / Pixverse)
seedReprodutibilidade
modelSobrescreve o modelo/família ativo

O capabilities() do provedor anuncia quais desses parâmetros são respeitados. O agente vê as capacidades do backend ativo na descrição da ferramenta, reconstruída dinamicamente quando o usuário troca de backend pelo hermes tools.

Famílias de modelos e roteamento de endpoint (o padrão FAL)​

Quando o seu backend tem vários endpoints por "modelo" — como o FAL, em que cada família (Veo 3.1, Pixverse v6, Kling O3) tem uma URL /text-to-video e outra /image-to-video — represente cada família como uma entrada de catálogo. O seu generate() escolhe o endpoint certo conforme image_url tenha sido passado ou não:

FAMILIES = {
"veo3.1": {
"text_endpoint": "fal-ai/veo3.1",
"image_endpoint": "fal-ai/veo3.1/image-to-video",
# ... flags de capacidade específicas da família ...
},
}

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"]
# ... monte o payload a partir das flags de capacidade da família e chame o endpoint ...

O usuário escolhe veo3.1 uma vez no hermes tools. O agente nunca pensa em endpoints — ele apenas passa (ou deixa de passar) image_url.

Precedência de seleção​

Para ajustes de modelo por instância (veja plugins/video_gen/fal/__init__.py):

  1. Palavra-chave model= vinda da chamada de ferramenta
  2. Variável de ambiente <PROVIDER>_VIDEO_MODEL
  3. video_gen.<provider>.model no config.yaml
  4. video_gen.model no config.yaml (quando for um dos seus IDs)
  5. O default_model() do provedor

Formato da resposta​

success_response() e error_response() produzem o formato de dicionário que todo backend devolve. Use essas funções — não monte o dicionário na mão.

Chaves de sucesso: success, video (URL ou caminho absoluto), model, prompt, modality ("text" ou "image"), aspect_ratio, duration, provider, além de extra.

Chaves de erro: success, video (None), error, error_type, model, prompt, aspect_ratio, provider.

Onde salvar os artefatos​

Se o seu backend devolve base64, use save_b64_video() para gravar em $HERMES_HOME/cache/videos/. Para bytes crus vindos de um download HTTP posterior, use save_bytes_video(). Caso contrário, devolva a URL de origem diretamente — o gateway resolve URLs remotas na hora da entrega.

Testes​

Coloque um teste de fumaça em tests/plugins/video_gen/test_<nome>_plugin.py. Os testes do xAI e do FAL mostram o padrão — registrar, conferir o catálogo, exercitar o roteamento com e sem image_url, e verificar respostas de erro limpas quando falta autenticação.