Chuyển tới nội dung chính

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á:

  1. Các plugin đi kèm<repo>/plugins/model-providers/<name>/ — giao hàng với Hermes
  2. 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
  3. 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ênregister_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:

IntegrationWhereWhat it gets
Credential resolutionhermes_cli/auth.pyPROVIDER_REGISTRY["acme-inference"] populated from profile
--provider CLI flaghermes_cli/main.pyAccepts acme-inference
hermes model pickerhermes_cli/models.pyAppears in CANONICAL_PROVIDERS, model list fetched from {base_url}/models
hermes doctorhermes_cli/doctor.pyHealth check for ACME_API_KEY + {base_url}/models probe
hermes setuphermes_cli/config.pyACME_API_KEY appears in OPTIONAL_ENV_VARS and the setup wizard
URL reverse-mappingagent/model_metadata.pyHostname → provider name for auto-detection
Auxiliary modelagent/auxiliary_client.pyUses default_aux_model for compression / summarization
Runtime resolutionhermes_cli/runtime_provider.pyReturns correct base_url, api_key, api_mode
Transportagent/transports/chat_completions.pyProfile 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:

FieldTypePurpose
namestrCanonical id — matches --provider choices and HERMES_INFERENCE_PROVIDER
aliasestuple[str, ...]Alternative names resolved by get_provider_profile() (e.g. grokxai)
api_modestrchat_completions | codex_responses | anthropic_messages | bedrock_converse
display_namestrHuman label shown in hermes model picker
descriptionstrPicker subtitle
signup_urlstrShown during first-run setup ("get an API key here")
env_varstuple[str, ...]API-key env vars in priority order; a final *_BASE_URL entry is used as the user base-URL override
base_urlstrDefault inference endpoint
models_urlstrExplicit catalog URL (falls back to {base_url}/models)
auth_typestrapi_key | oauth_device_code | oauth_external | copilot | aws_sdk | external_process
fallback_modelstuple[str, ...]Curated list shown when live catalog fetch fails
default_headersdict[str, str]Sent on every request (e.g. Copilot's Editor-Version)
fixed_temperatureAnyNone = use caller's value; OMIT_TEMPERATURE sentinel = don't send temperature at all (Kimi)
default_max_tokensint | NoneProvider-level max_tokens cap (Nvidia: 16384)
default_aux_modelstrCheap 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ữ:

PluginWhy 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:

  1. Ghi đè rõ ràng của người dùng (config.yaml model.api_mode khi được đặt)
  2. Công văn theo từng mô hình của OpenCode (opencode_model_api_mode cho Zen và Go)
  3. Tự động phát hiện URL — hậu tố /anthropicanthropic_messages, api.openai.comcodex_responses, api.x.aicodex_responses, /coding trên các miền Kimi → chat_completions
  4. Hồ sơ api_mode làm dự phòng khi phát hiện URL không tìm thấy gì
  5. chat_completions mặ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_typeMeaningWho uses it
api_keySingle env var carries a static API keyMost providers
oauth_device_codeDevice-code OAuth flow
oauth_externalUser signs in elsewhere, tokens land in auth.jsonAnthropic OAuth, MiniMax OAuth, Gemini Cloud Code, Qwen Portal, Nous Portal
copilotGitHub Copilot token refresh cyclecopilot plugin only
aws_sdkAWS SDK credential chain (IAM role, profile, env)bedrock plugin only
external_processAuth handled by a subprocess the agent spawnscopilot-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