構建圖像生成提供者插件
圖像生成(image-gen)提供者插件註冊一個後端,用於服務每一次 image_generate 工具調用——無論是 DALL·E、gpt-image、Grok、Flux、Imagen、Stable Diffusion、fal、Replicate、本地 ComfyUI 環境,還是其他任何後端。內置提供者(OpenAI、OpenAI-Codex、xAI)均作為插件提供。你可以通過在 plugins/image_gen/<name>/ 中放置一個目錄來添加新的提供者,或覆蓋已有的捆綁提供者。
圖像生成是 Hermes 支持的幾種後端插件之一。其他插件(具有更專用的抽象基類 ABC)包括 記憶提供者插件、上下文引擎插件 和 模型提供者插件。通用的工具/鉤子/CLI 插件請參閱 構建 Hermes 插件。
發現機制工作原理
Hermes 會在以下三個位置掃描圖像生成後端:
- 捆綁(Bundled) —
<repo>/plugins/image_gen/<name>/(隨kind: backend自動加載,始終可用) - 用戶(User) —
~/.hermes/plugins/image_gen/<name>/(通過plugins.enabled選擇啟用) - Pip — 聲明瞭
hermes_agent.plugins入口點的包
每個插件的 register(ctx) 函數都會調用 ctx.register_image_gen_provider(...)——這將其放入 agent/image_gen_registry.py 中的註冊表。活躍提供者由 config.yaml 中的 image_gen.provider 選定;hermes tools 會引導用戶進行選擇。
image_generate 工具包裝器向註冊表請求活躍提供者並分派到該提供者。如果沒有註冊任何提供者,該工具會顯示一條有用的錯誤信息,指向 hermes tools。
目錄結構
plugins/image_gen/my-backend/
├── __init__.py # ImageGenProvider subclass + register()
└── plugin.yaml # Manifest with kind: backend
此時,捆綁插件已完整。位於 ~/.hermes/plugins/image_gen/<name>/ 的用戶插件需要添加到 config.yaml 中的 plugins.enabled 列表中(或者運行 hermes plugins enable <name>)。
ImageGenProvider 抽象基類 (ABC)
繼承 agent.image_gen_provider.ImageGenProvider。唯一必需的成員是 name 屬性和 generate() 方法——其他所有成員都有合理的默認值:
# plugins/image_gen/my-backend/__init__.py
from typing import Any, Dict, List, Optional
import os
from agent.image_gen_provider import (
DEFAULT_ASPECT_RATIO,
ImageGenProvider,
error_response,
resolve_aspect_ratio,
save_b64_image,
success_response,
)
class MyBackendImageGenProvider(ImageGenProvider):
@property
def name(self) -> str:
# Stable id used in image_gen.provider config. Lowercase, no spaces.
return "my-backend"
@property
def display_name(self) -> str:
# Human label shown in `hermes tools`. Defaults to name.title() if omitted.
return "My Backend"
def is_available(self) -> bool:
# Return False if credentials or deps are missing.
# The tool's availability gate calls this before dispatch.
if not os.environ.get("MY_BACKEND_API_KEY"):
return False
try:
import my_backend_sdk # noqa: F401
except ImportError:
return False
return True
def list_models(self) -> List[Dict[str, Any]]:
# Catalog shown in `hermes tools` model picker.
return [
{
"id": "my-model-fast",
"display": "My Model (Fast)",
"speed": "~5s",
"strengths": "Quick iteration",
"price": "$0.01/image",
},
{
"id": "my-model-hq",
"display": "My Model (HQ)",
"speed": "~30s",
"strengths": "Highest fidelity",
"price": "$0.04/image",
},
]
def default_model(self) -> Optional[str]:
return "my-model-fast"
def get_setup_schema(self) -> Dict[str, Any]:
# Metadata for the `hermes tools` picker — keys to prompt for at setup.
return {
"name": "My Backend",
"badge": "paid", # optional; shown as a short tag in the picker
"tag": "One-line description shown under the name",
"env_vars": [
{
"key": "MY_BACKEND_API_KEY",
"prompt": "My Backend API key",
"url": "https://my-backend.example.com/api-keys",
},
],
}
def generate(
self,
prompt: str,
aspect_ratio: str = DEFAULT_ASPECT_RATIO,
**kwargs: Any,
) -> Dict[str, Any]:
prompt = (prompt or "").strip()
aspect_ratio = resolve_aspect_ratio(aspect_ratio)
if not prompt:
return error_response(
error="Prompt is required",
error_type="invalid_input",
provider=self.name,
prompt="",
aspect_ratio=aspect_ratio,
)
# Model selection precedence: env var → config → default. The helper
# _resolve_model() in the built-in openai plugin is a good reference.
model_id = kwargs.get("model") or self.default_model() or "my-model-fast"
try:
import my_backend_sdk
client = my_backend_sdk.Client(api_key=os.environ["MY_BACKEND_API_KEY"])
result = client.generate(
prompt=prompt,
model=model_id,
aspect_ratio=aspect_ratio,
)
# Two shapes supported:
# - URL string: return it as `image`
# - base64 data: save under $HERMES_HOME/cache/images/ via save_b64_image()
if result.get("image_b64"):
path = save_b64_image(
result["image_b64"],
prefix=self.name,
extension="png",
)
image = str(path)
else:
image = result["image_url"]
return success_response(
image=image,
model=model_id,
prompt=prompt,
aspect_ratio=aspect_ratio,
provider=self.name,
)
except Exception as exc:
return error_response(
error=str(exc),
error_type=type(exc).__name__,
provider=self.name,
model=model_id,
prompt=prompt,
aspect_ratio=aspect_ratio,
)
def register(ctx) -> None:
"""Plugin entry point — called once at load time."""
ctx.register_image_gen_provider(MyBackendImageGenProvider())
plugin.yaml
name: my-backend
version: 1.0.0
description: My image backend — text-to-image via My Backend SDK
author: Your Name
kind: backend
requires_env:
- MY_BACKEND_API_KEY
kind: backend 是將插件路由到圖像生成註冊路徑的關鍵。requires_env 會在執行 hermes plugins install 時提示用戶配置。
ABC 參考
完整契約位於 agent/image_gen_provider.py。你通常會被重寫的方法如下:
| 成員 | 必需 | 默認值 | 用途 |
|---|---|---|---|
name | ✅ | — | 在 image_gen.provider 配置中使用的穩定 ID |
display_name | — | name.title() | 在 hermes tools 中顯示的標籤 |
is_available() | — | True | 用於檢查缺失憑據/依賴項的門控 |
list_models() | — | [] | 供 hermes tools 模型選擇器使用的目錄 |
default_model() | — | list_models() 中的第一個 | 未配置模型時的回退選項 |
get_setup_schema() | — | 最小化 | 選擇器元數據 + 環境變量提示 |
generate(prompt, aspect_ratio, **kwargs) | ✅ | — | 實際調用 |
響應格式
generate() 必須返回通過 success_response() 或 error_response() 構建的字典。兩者均位於 agent/image_gen_provider.py 中。
成功:
success_response(
image=<url-or-absolute-path>,
model=<model-id>,
prompt=<echoed-prompt>,
aspect_ratio="landscape" | "square" | "portrait",
provider=<your-provider-name>,
extra={...}, # optional backend-specific fields
)
錯誤:
error_response(
error="human-readable message",
error_type="provider_error" | "invalid_input" | "<exception class name>",
provider=<your-provider-name>,
model=<model-id>,
prompt=<prompt>,
aspect_ratio=<resolved aspect>,
)
工具包裝器將該字典序列化為 JSON 並交給 LLM。錯誤會作為工具結果呈現;LLM 決定如何向用戶解釋這些錯誤。
處理 base64 與 URL 輸出
某些後端返回圖像 URL(如 fal、Replicate);其他後端返回 base64 負載(如 OpenAI gpt-image-2)。對於 base64 情況,請使用 save_b64_image()——它將文件寫入 $HERMES_HOME/cache/images/<prefix>_<timestamp>_<uuid>.<ext> 並返回絕對 Path。在 success_response() 中將該路徑(作為 str)作為 image= 參數傳遞。網關交付(Telegram 照片氣泡、Discord 附件)既能識別 URL 也能識別絕對路徑。
用戶覆蓋
在 ~/.hermes/plugins/image_gen/<name>/ 中放置一個用戶插件,其 name 屬性與某個捆綁插件相同,並通過 hermes plugins enable <name> 啟用它——註冊表採用“最後寫入者勝出”原則,因此你的版本將替換內置版本。這對於將 openai 插件指向私有代理,或換入自定義模型目錄非常有用。
測試
export HERMES_HOME=/tmp/hermes-imggen-test
mkdir -p $HERMES_HOME/plugins/image_gen/my-backend
# …copy __init__.py + plugin.yaml into that dir…
export MY_BACKEND_API_KEY=your-test-key
hermes plugins enable my-backend
# Pick it as the active provider
echo "image_gen:" >> $HERMES_HOME/config.yaml
echo " provider: my-backend" >> $HERMES_HOME/config.yaml
# Exercise it
hermes -z "Generate an image of a corgi in a spacesuit"
或者交互式測試:hermes tools → “Image Generation” → 選擇 my-backend → 如果提示則輸入 API 密鑰。
參考實現
plugins/image_gen/openai/__init__.py— 將 gpt-image-2 分為低/中/高三個層級,作為共享同一 API 模型但具有不同quality參數的三個虛擬模型 ID。這是在單一後端下分層模型以及 config.yaml 優先級鏈的良好示例。plugins/image_gen/xai/__init__.py— 通過 xAI 使用 Grok Imagine。形態不同(URL 輸出,更簡單的目錄)。plugins/image_gen/openai-codex/__init__.py— Codex 風格的 Responses API 變體,複用 OpenAI SDK 但使用不同的路由基礎 URL。
通過 pip 分發
# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-backend-imggen = "my_backend_imggen_package"
my_backend_imggen_package 必須暴露一個頂層的 register 函數。完整設置請參閱通用插件指南中的 通過 pip 分發。
相關頁面
- 圖像生成 — 面向用戶的功能文檔
- 插件概覽 — 一覽所有插件類型
- 構建 Hermes 插件 — 通用工具/鉤子/斜槓命令指南