이미지 생성 제공자 플러그인 구축
이미지 생성 제공자 플러그인은 모든 image_generate 도구 호출(DALL·E, gpt-image, Grok, Flux, Imagen, Stable Diffusion, fal, Replicate, 로컬 ComfyUI 장비 등)을 서비스하는 백엔드를 등록합니다. 내장 제공자(OpenAI, OpenAI-Codex, xAI)는 모두 플러그인으로 제공됩니다. plugins/image_gen/<name>/에 디렉터리를 놓아 새 항목을 추가하거나 번들된 항목을 재정의할 수 있습니다.
Image-gen은 Hermes가 지원하는 여러 백엔드 플러그인 중 하나입니다. 다른 것(더 전문화된 ABC 포함)은 메모리 제공자 플러그인, 컨텍스트 엔진 플러그인 및 모델 제공자 플러그인입니다. 일반 도구/후크/CLI 플러그인은 Hermes 플러그인 빌드에 있습니다.
검색 작동 방식
Hermes는 세 곳에서 image-gen 백엔드를 검색합니다.
- 번들 —
<repo>/plugins/image_gen/<name>/(kind: backend로 자동 로드됨, 항상 사용 가능) - 사용자 —
~/.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())
플러그인.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`은 플러그인을 image-gen 등록 경로로 라우팅하는 것입니다. `requires_env`은 `hermes plugins install` 중에 메시지가 표시됩니다.
## ABC 참조 \{#abc-reference}
`agent/image_gen_provider.py`의 전체 계약입니다. 일반적으로 재정의하는 메서드는 다음과 같습니다.
| 회원 | 필수 | 기본값 | 목적 |
|---|---|---|---|
| `name` | ✅ | — | `image_gen.provider` 구성에 사용되는 안정적인 ID |
| `display_name` | — | `name.title()` | `hermes tools`에 표시된 라벨 |
| `is_available()` | — | `True` | 누락된 cred/dep에 대한 게이트 |
| `list_models()` | — | `` | `hermes tools` 모델 선택기 카탈로그 |
| `default_model()` | — | 첫 번째 `list_models()`에서 | 모델이 구성되지 않은 경우 대체 |
| `get_setup_schema()` | — | 최소한의 | 선택기 메타데이터 + env-var 프롬프트 |
| `generate(prompt, aspect_ratio, **kwargs)` | ✅ | — | 전화 |
## 응답 형식 \{#response-format}
`generate()`은 `success_response()` 또는 `error_response()`을 통해 작성된 사전을 반환해야 합니다. 둘 다 `agent/image_gen_provider.py`에 거주합니다.
**성공:**
```python
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을 반환합니다. 해당 경로(str)를 success_response()의 image=로 전달합니다. 게이트웨이 전달(텔레그램 사진 풍선, Discord 첨부 파일)은 URL과 절대 경로를 모두 인식합니다.
사용자 재정의
번들 플러그인과 동일한 name 속성을 사용하여 ~/.hermes/plugins/image_gen/<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 → "이미지 생성" → my-backend 선택 → 메시지가 표시되면 API 키를 입력합니다.
참조 구현
plugins/image_gen/openai/__init__.py— 서로 다른quality매개변수를 사용하여 하나의 API 모델을 공유하는 3개의 가상 모델 ID로 낮은/중간/높은 계층의 gpt-image-2. 단일 백엔드 + config.yaml 우선 순위 체인 아래의 계층형 모델의 좋은 예입니다.plugins/image_gen/xai/__init__.py— Grok xAI를 통해 상상해보세요. 다른 모양(URL 출력, 더 간단한 카탈로그).plugins/image_gen/openai-codex/__init__.py— 다른 라우팅 기본 URL로 OpenAI SDK를 재사용하는 Codex 스타일 응답 API 변형입니다.
pip를 통해 배포
# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-backend-imggen = "my_backend_imggen_package"
``my_backend_imggen_package`은 최상위 `register` 함수를 노출해야 합니다. 전체 설정은 일반 플러그인 가이드의 [pip를 통해 배포](/docs/guides/build-a-hermes-plugin#distribute-via-pip)를 참조하세요.
## 관련 페이지 \{#abc-reference}
- [이미지 생성](/docs/user-guide/features/image-generation) — 사용자 대상 기능 문서
- [플러그인 개요](/docs/user-guide/features/plugins) — 모든 플러그인 유형 개요
- [Hermes 플러그인 빌드](/docs/guides/build-a-hermes-plugin) — 일반 도구/후크/슬래시 명령 가이드