Xây dựng Plugin nhà cung cấp mô hình
Các plugin của nhà cung cấp mô hình khai báo một chương trình phụ trợ suy luận — điểm cuối tương thích với OpenAI, máy chủ Anthropic Messages, API Phản hồi kiểu Codex hoặc bề mặt gốc Bedrock — mà Hermes có thể định tuyến các cuộc gọi AIAgent qua. Mọi nhà cung cấp tích hợp sẵn (OpenRouter, Anthropic, GMI, DeepSeek, Nvidia, ...) đều cung cấp dưới dạng một trong những plugin này. Các bên thứ ba có thể thêm thư mục của riêng họ bằng cách thả một thư mục vào $HERMES_HOME/plugins/model-providers/ mà không có thay đổi nào đối với kho lưu trữ.
:::mẹo Plugin nhà cung cấp mô hình là loại plugin nhà cung cấp thứ ba. Các phần còn lại là Plugin nhà cung cấp bộ nhớ (kiến thức liên phiên) và Plugin công cụ bối cảnh (chiến lược nén ngữ cảnh). Cả ba đều tuân theo cùng một mẫu "thả thư mục, khai báo hồ sơ, không chỉnh sửa repo". :::
Cách hoạt động của tính năng khám phá
providers/__init__.py._discover_providers() chạy chậm trong lần đầu tiên bất kỳ mã nào gọi get_provider_profile() hoặc list_providers(). Thứ tự khám phá:
- Các plugin đi kèm —
<repo>/plugins/model-providers/<name>/— giao hàng với Hermes - Plugin người dùng —
$HERMES_HOME/plugins/model-providers/<name>/— thả vào bất kỳ thư mục nào; không cần khởi động lại cho các phiên tiếp theo - Tệp đơn kế thừa —
<repo>/providers/<name>.py— tương thích ngược cho các bản cài đặt có thể chỉnh sửa ngoài cây
Các plugin của người dùng ghi đè các plugin đi kèm cùng tên vì register_provider() là người viết cuối cùng sẽ thắng. Thả thư mục $HERMES_HOME/plugins/model-providers/gmi/ để thay thế cấu hình GMI tích hợp mà không cần chạm vào repo.
Cấu trúc thư mục
plugins/model-providers/my-provider/
├── __init__.py # Calls register_provider(profile) at module-level
├── plugin.yaml # kind: model-provider + metadata (optional but recommended)
└── README.md # Setup instructions (optional)
Tệp được yêu cầu duy nhất là __init__.py. plugin.yaml được plugin hermes sử dụng để xem xét nội tâm và bởi Trình quản lý plugin chung để định tuyến plugin đến trình tải phù hợp; không có nó, trình tải chung sẽ quay trở lại phương pháp phỏng đoán văn bản nguồn.
Ví dụ tối thiểu — một nhà cung cấp khóa API đơn giản
# plugins/model-providers/acme-inference/__init__.py
from providers import register_provider
from providers.base import ProviderProfile
acme = ProviderProfile(
name="acme-inference",
aliases=("acme",),
display_name="Acme Inference",
description="Acme — OpenAI-compatible direct API",
signup_url="https://acme.example.com/keys",
env_vars=("ACME_API_KEY", "ACME_BASE_URL"),
base_url="https://api.acme.example.com/v1",
auth_type="api_key",
default_aux_model="acme-small-fast",
fallback_models=(
"acme-large-v3",
"acme-medium-v3",
"acme-small-fast",
),
)
register_provider(acme)
# plugins/model-providers/acme-inference/plugin.yaml
name: acme-inference
kind: model-provider
version: 1.0.0
description: Acme Inference — OpenAI-compatible direct API
author: Your Name
Thế thôi. Sau khi bỏ hai tệp này, tự động nối dây sau đây không có chỉnh sửa nào khác:
| Integration | Where | What it gets |
|---|---|---|
| Credential resolution | hermes_cli/auth.py | PROVIDER_REGISTRY["acme-inference"] populated from profile |
--provider CLI flag | hermes_cli/main.py | Accepts acme-inference |
hermes model picker | hermes_cli/models.py | Appears in CANONICAL_PROVIDERS, model list fetched from {base_url}/models |
hermes doctor | hermes_cli/doctor.py | Health check for ACME_API_KEY + {base_url}/models probe |
hermes setup | hermes_cli/config.py | ACME_API_KEY appears in OPTIONAL_ENV_VARS and the setup wizard |
| URL reverse-mapping | agent/model_metadata.py | Hostname → provider name for auto-detection |
| Auxiliary model | agent/auxiliary_client.py | Uses default_aux_model for compression / summarization |
| Runtime resolution | hermes_cli/runtime_provider.py | Returns correct base_url, api_key, api_mode |
| Transport | agent/transports/chat_completions.py | Profile path generates kwargs via prepare_messages / build_extra_body / build_api_kwargs_extras |
trường Hồ sơ nhà cung cấp
Định nghĩa đầy đủ trong providers/base.py. Những cái hữu ích nhất:
| Field | Type | Purpose |
|---|---|---|
name | str | Canonical id — matches --provider choices and HERMES_INFERENCE_PROVIDER |
aliases | tuple[str, ...] | Alternative names resolved by get_provider_profile() (e.g. grok → xai) |
api_mode | str | chat_completions | codex_responses | anthropic_messages | bedrock_converse |
display_name | str | Human label shown in hermes model picker |
description | str | Picker subtitle |
signup_url | str | Shown during first-run setup ("get an API key here") |
env_vars | tuple[str, ...] | API-key env vars in priority order; a final *_BASE_URL entry is used as the user base-URL override |
base_url | str | Default inference endpoint |
models_url | str | Explicit catalog URL (falls back to {base_url}/models) |
auth_type | str | api_key | oauth_device_code | oauth_external | copilot | aws_sdk | external_process |
fallback_models | tuple[str, ...] | Curated list shown when live catalog fetch fails |
default_headers | dict[str, str] | Sent on every request (e.g. Copilot's Editor-Version) |
fixed_temperature | Any | None = use caller's value; OMIT_TEMPERATURE sentinel = don't send temperature at all (Kimi) |
default_max_tokens | int | None | Provider-level max_tokens cap (Nvidia: 16384) |
default_aux_model | str | Cheap model for auxiliary tasks (compression, vision, summarization) |
Móc có thể ghi đè
Lớp con ProviderProfile dành cho những điều kỳ quặc không tầm thường:
from typing import Any
from providers.base import ProviderProfile
class AcmeProfile(ProviderProfile):
def prepare_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Provider-specific message preprocessing. Runs after codex
sanitization, before developer-role swap. Default: pass-through."""
# Example: Qwen normalizes plain-text content to a list-of-parts
# array and injects cache_control; Kimi rewrites tool-call JSON
return messages
def build_extra_body(self, *, session_id=None, **context) -> dict:
"""Provider-specific extra_body fields merged into the API call.
Context includes: session_id, provider_preferences, model, base_url,
reasoning_config. Default: empty dict."""
# Example: OpenRouter's provider-preferences block,
# Gemini's thinking_config translation.
return {}
def build_api_kwargs_extras(self, *, reasoning_config=None, **context):
"""Returns (extra_body_additions, top_level_kwargs). Needed when some
fields go top-level (Kimi's reasoning_effort) and some go in extra_body
(OpenRouter's reasoning dict). Default: ({}, {})."""
return {}, {}
def fetch_models(self, *, api_key=None, timeout=8.0) -> list[str] | None:
"""Live catalog fetch. Default hits {models_url or base_url}/models with
Bearer auth. Override for: custom auth (Anthropic), no REST endpoint
(Bedrock → None), or public/unauthenticated catalogs (OpenRouter)."""
return super().fetch_models(api_key=api_key, timeout=timeout)
Ví dụ tham khảo hook
Hãy xem các plugin đi kèm này để biết thành ngữ:
| Plugin | Why look |
|---|---|
plugins/model-providers/openrouter/ | Aggregator with provider preferences, public model catalog |
plugins/model-providers/gemini/ | thinking_config translation (native + OpenAI-compat nested forms) |
plugins/model-providers/kimi-coding/ | OMIT_TEMPERATURE, extra_body.thinking, top-level reasoning_effort |
plugins/model-providers/qwen-oauth/ | Message normalization, cache_control injection, VL high-res |
plugins/model-providers/nous/ | Attribution tags, "omit reasoning when disabled" |
plugins/model-providers/custom/ | Ollama num_ctx + think: false quirks |
plugins/model-providers/bedrock/ | api_mode="bedrock_converse", fetch_models returns None (no REST endpoint) |
Ghi đè người dùng - thay thế phần tích hợp sẵn mà không cần chỉnh sửa repo
Giả sử bạn muốn trỏ gmi vào điểm cuối dàn dựng riêng tư của mình để thử nghiệm. Tạo ~/.hermes/plugins/model-providers/gmi/__init__.py:
from providers import register_provider
from providers.base import ProviderProfile
register_provider(ProviderProfile(
tên="gmi",
aliases=("gmi-cloud", "gmicloud"),
env_vars=("GMI_API_KEY",),
base_url="https://gmi-staging.internal.example.com/v1",
auth_type="api_key",
default_aux_model="google/gemini-3.1-flash-lite-preview",
))
Phiên tiếp theo, get_provider_profile("gmi").base_url trả về URL chạy thử. Không có bản vá repo, không xây dựng lại. Vì các plugin của người dùng được phát hiện sau các plugin được đóng gói nên lệnh gọi register_provider() của người dùng sẽ thắng.
lựa chọn api_mode
Bốn giá trị được công nhận. Hermes chọn một chiếc dựa trên:
- Ghi đè rõ ràng của người dùng (
config.yamlmodel.api_modekhi được đặt) - Công văn theo từng mô hình của OpenCode (
opencode_model_api_modecho Zen và Go) - Tự động phát hiện URL — hậu tố
/anthropic→anthropic_messages,api.openai.com→codex_responses,api.x.ai→codex_responses,/codingtrên các miền Kimi →chat_completions - Hồ sơ
api_modelàm dự phòng khi phát hiện URL không tìm thấy gì chat_completionsmặc định
Đặt profile.api_mode để khớp với mặc định mà nhà cung cấp của bạn cung cấp — nó hoạt động như một gợi ý. Ghi đè URL người dùng vẫn thắng.
Các loại xác thực
auth_type | Meaning | Who uses it |
|---|---|---|
api_key | Single env var carries a static API key | Most providers |
oauth_device_code | Device-code OAuth flow | — |
oauth_external | User signs in elsewhere, tokens land in auth.json | Anthropic OAuth, MiniMax OAuth, Gemini Cloud Code, Qwen Portal, Nous Portal |
copilot | GitHub Copilot token refresh cycle | copilot plugin only |
aws_sdk | AWS SDK credential chain (IAM role, profile, env) | bedrock plugin only |
external_process | Auth handled by a subprocess the agent spawns | copilot-acp plugin only |
Các cổng auth_type mà các đường dẫn mã coi nhà cung cấp của bạn là "nhà cung cấp khóa api đơn giản" — nếu đó không phải là api_key thì Trình quản lý plugin vẫn ghi lại bảng kê khai nhưng tính năng tự động hóa cấp CLI của Hermes (kiểm tra của bác sĩ, cờ --provider, ủy quyền của trình hướng dẫn thiết lập) có thể bỏ qua nó.
Thời điểm khám phá
Việc khám phá nhà cung cấp lười biếng — được kích hoạt bởi lệnh gọi get_provider_profile() hoặc list_providers() đầu tiên trong quy trình. Trong thực tế, điều này xảy ra sớm khi khởi động (tải mô-đun auth.py mở rộng PROVIDER_REGISTRY một cách háo hức). Nếu bạn cần xác minh plugin của mình đã được tải, hãy chạy:
hermes doctor
— hồ sơ auth_type="api_key" thành công xuất hiện trong phần Kết nối nhà cung cấp với đầu dò /models.
Đối với kiểm tra theo chương trình:
from providers import list_providers
for p in list_providers():
print(p.name, p.base_url, p.api_mode)
Kiểm tra plugin của bạn
Trỏ HERMES_HOME vào thư mục tạm thời để bạn không làm hỏng cấu hình thực của mình:
export HERMES_HOME=/tmp/hermes-plugin-test
mkdir -p $HERMES_HOME/plugins/model-providers/my-provider
cat > $HERMES_HOME/plugins/model-providers/my-provider/__init__.py <<'EOF'
from providers import register_provider
from providers.base import ProviderProfile
register_provider(ProviderProfile(
name="my-provider",
env_vars=("MY_API_KEY",),
base_url="https://api.my-provider.example.com/v1",
auth_type="api_key",
))
EOF
export MY_API_KEY=your-test-key
hermes -z "hello" --provider my-provider -m some-model
Tích hợp Trình quản lý plugin chung
PluginManager chung (thứ mà hermes plugin hoạt động) thấy các plugin của nhà cung cấp mô hình nhưng không nhập chúng — providers/__init__.py sở hữu vòng đời của chúng. Người quản lý ghi lại bảng kê khai để xem xét nội tâm và phân loại theo loại: nhà cung cấp mô hình. Khi bạn thả một plugin người dùng không được gắn nhãn vào $HERMES_HOME/plugins/ để gọi register_provider bằng ProviderProfile, trình quản lý sẽ tự động ép buộc nó thành kind: model-provider thông qua phương pháp phỏng đoán văn bản nguồn — vì vậy, plugin vẫn định tuyến chính xác ngay cả khi không có plugin.yaml.
Phân phối qua pip
Giống như bất kỳ plugin Hermes nào, nhà cung cấp mô hình có thể gửi dưới dạng gói pip. Thêm một điểm vào vào pyproject.toml của bạn:
[project.entry-points."hermes.plugins"]
acme-inference = "acme_hermes_plugin:register"
…trong đó acme_hermes_plugin:register là một hàm gọi register_provider(profile). Trình quản lý plugin chung sẽ chọn các plugin điểm đầu vào trong discover_and_load(). Đối với plugin pip kind: model-provider, bạn vẫn cần khai báo loại trong bảng kê khai của mình (hoặc dựa vào phương pháp phỏng đoán văn bản nguồn).
Xem Xây dựng plugin Hermes để biết thông tin thiết lập điểm vào đầy đủ.
Các trang liên quan
- Provider Runtime — ưu tiên độ phân giải + nơi mỗi lớp đọc hồ sơ
- Thêm nhà cung cấp — danh sách kiểm tra toàn diện cho các chương trình phụ trợ suy luận mới (bao gồm cả đường dẫn plugin nhanh và tích hợp CLI/auth đầy đủ)
- Plugin nhà cung cấp bộ nhớ
- Plugin Công cụ bối cảnh
- Xây dựng plugin Hermes — biên soạn plugin chung