إضافات مزوّد النماذج
Model Provider Plugins
ما هذه الصفحة، وماذا تحتوي.
المزوّد والنموذج: المزوّد هو الشركة التي تشغّل نموذج الذكاء الاصطناعي، والنموذج هو «العقل» الذي يفكّر لـHermes. Hermes نفسه لا يفكّر؛ هو ينظّم العمل ويستدعي النموذج. لذلك اختيار النموذج يحدّد جودة النتيجة وتكلفتها. ستستعمل هنا hermes plugins وhermes doctor، والقراءة نحو 9 دقائق. انتبه: الأغلى ليس دائمًا الأفضل لمهمتك. جرّب مهمة واحدة على نموذجين وقارن، وضع سقفًا للإنفاق من البداية.
How to build a model provider (inference backend) plugin for Hermes Agent
نتائج مأخوذة من هذه الصفحة، لا من قالب.
- تعرف ما المزوّد والنموذج ولماذا قد تحتاجه.
- تنفّذ
hermes pluginsوhermes doctorوتفهم ما يحدث بعدها. - تقرأ الجدول وتأخذ منه السطر الذي يخصّك فقط.
- تضبط
ACME_API_KEYفي المكان الصحيح.
كما تظهر تمامًا داخل Hermes.
hermes pluginshermes doctor
ACME_API_KEYACME_BASE_URLGMI_API_KEYPROVIDER_REGISTRYHERMES_HOME
انتقل مباشرة إلى ما تحتاجه.
- 01How discovery works
- 02Directory structure
- 03Minimal example — a simple API-key provider
- 04ProviderProfile fields
- 05Overridable hooks
- 06Hook reference examples
- 07User overrides — replace a built-in without editing the repo
- 08apimode selection
- 09Auth types
- 10Discovery timing
- 11Testing your plugin
- 12General PluginManager integration
- 13Distribute via pip
- 14Related pages
بلا اختصار أو حذف.
النص أدناه منقول من المصدر الرسمي بالإنجليزية حتى تبقى الأوامر والأسماء دقيقة كما هي. قبل كل قسم شرح عربي يوضّح ما بداخله.
Model provider plugins declare an inference backend — an OpenAI-compatible endpoint, an Anthropic Messages server, a Codex-style Responses API, or a Bedrock-native surface — that Hermes can route AIAgent calls through. Every built-in provider (OpenRouter, Anthropic, GMI, DeepSeek, Nvidia, …) ships as one of these plugins. Third parties can add their own by dropping a directory under $HERMES_HOME/plugins/model-providers/ with zero changes to the repo.
How discovery works
شرح للفكرة نفسها. اقرأه ببطء، فبقية الأقسام تبني عليه. تذكير: المزوّد هو الشركة التي تشغّل نموذج الذكاء الاصطناعي، والنموذج هو «العقل» الذي يفكّر لـHermes.
providers/__init__.py._discover_providers() runs lazily the first time any code calls get_provider_profile() or list_providers(). Discovery order:
- Bundled plugins —
<repo>/plugins/model-providers/<name>/— ship with Hermes - User plugins —
$HERMES_HOME/plugins/model-providers/<name>/— drop in any directory; no restart required for subsequent sessions - Legacy single-file —
<repo>/providers/<name>.py— back-compat for out-of-tree editable installs
User plugins override bundled plugins of the same name because register_provider() is last-writer-wins. Drop a $HERMES_HOME/plugins/model-providers/gmi/ directory to replace the built-in GMI profile without touching the repo.
Directory structure
أوامر تكتبها في الطرفية. افهم ما يفعله الأمر قبل نسخه. الأوامر هنا: hermes plugins.
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)The only required file is __init__.py. plugin.yaml is used by hermes plugins for introspection and by the general PluginManager to route the plugin to the right loader; without it, the general loader falls back to a source-text heuristic.
Minimal example — a simple API-key provider
جدول مرجعي. لا تقرأه كله، ابحث عن السطر الذي يخصّك فقط.
# 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 NameThat's it. After dropping these two files, the following auto-wire with no other edits:
| 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 |
ProviderProfile fields
جدول مرجعي. لا تقرأه كله، ابحث عن السطر الذي يخصّك فقط.
Full definition in providers/base.py. The most useful ones:
| Field | Type | Purpose |
|---|---|---|
name | str | Canonical id — matches model.provider in config.yaml and the --provider flag |
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) |
Overridable hooks
شرح للفكرة نفسها. اقرأه ببطء، فبقية الأقسام تبني عليه.
Subclass ProviderProfile for non-trivial quirks:
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, OpenRouter's verbosity for
adaptive Anthropic models) and some go in extra_body (OpenRouter's
reasoning dict). Default: ({}, {})."""
return {}, {}
def fetch_models(self, *, api_key=None, base_url=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, base_url=base_url, timeout=timeout)Hook reference examples
جدول مرجعي. لا تقرأه كله، ابحث عن السطر الذي يخصّك فقط.
Look at these bundled plugins for idioms:
| 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) |
User overrides — replace a built-in without editing the repo
إعدادات تضبطها مرة وتنساها. غيّر واحدًا في كل مرة حتى تعرف أثر كل تغيير. تضبط GMI_API_KEY خارج المحادثة، في بيئة التشغيل.
Say you want to point gmi at your private staging endpoint for testing. Create ~/.hermes/plugins/model-providers/gmi/__init__.py:
from providers import register_provider
from providers.base import ProviderProfile
register_provider(ProviderProfile(
name="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",
))Next session, get_provider_profile("gmi").base_url returns the staging URL. No repo patch, no rebuild. Because user plugins are discovered after bundled ones, the user register_provider() call wins.
apimode selection
شرح للفكرة نفسها. اقرأه ببطء، فبقية الأقسام تبني عليه.
Four values are recognized. Hermes picks one based on:
- User explicit override (
config.yamlmodel.api_modewhen set) - OpenCode's per-model dispatch (
opencode_model_api_modefor Zen and Go) - URL auto-detection —
/anthropicsuffix →anthropic_messages,api.openai.com→codex_responses,api.x.ai→codex_responses,/codingon Kimi domains →chat_completions - Profile
api_modeas a fallback when URL detection finds nothing - Default
chat_completions
Set profile.api_mode to match the default your provider ships — it acts as a hint. User URL overrides still win.
Auth types
جدول مرجعي. لا تقرأه كله، ابحث عن السطر الذي يخصّك فقط.
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, 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 |
auth_type gates which codepaths treat your provider as a "simple api-key provider" — if it's not api_key, the PluginManager still records the manifest but Hermes' CLI-level automation (doctor checks, --provider flag, setup wizard delegation) may skip over it.
Discovery timing
إعدادات تضبطها مرة وتنساها. غيّر واحدًا في كل مرة حتى تعرف أثر كل تغيير. الأوامر هنا: hermes doctor. تضبط PROVIDER_REGISTRY خارج المحادثة، في بيئة التشغيل.
Provider discovery is lazy — triggered by the first get_provider_profile() or list_providers() call in the process. In practice this happens early at startup (auth.py module load extends PROVIDER_REGISTRY eagerly). If you need to verify your plugin loaded, run:
hermes doctor— a successful auth_type="api_key" profile appears under the Provider Connectivity section with a /models probe.
For programmatic inspection:
from providers import list_providers
for p in list_providers():
print(p.name, p.base_url, p.api_mode)Testing your plugin
إعدادات تضبطها مرة وتنساها. غيّر واحدًا في كل مرة حتى تعرف أثر كل تغيير. تضبط HERMES_HOME خارج المحادثة، في بيئة التشغيل.
Point HERMES_HOME at a temp directory so you don't pollute your real config:
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-modelGeneral PluginManager integration
أوامر تكتبها في الطرفية. افهم ما يفعله الأمر قبل نسخه. الأوامر هنا: hermes plugins.
The general PluginManager (the thing hermes plugins operates on) sees model-provider plugins but does not import them — providers/__init__.py owns their lifecycle. The manager records the manifest for introspection and categorizes by kind: model-provider. When you drop an unlabeled user plugin into $HERMES_HOME/plugins/ that happens to call register_provider with a ProviderProfile, the manager auto-coerces it to kind: model-provider via a source-text heuristic — so the plugin still routes correctly even without plugin.yaml.
Distribute via pip
شرح للفكرة نفسها. اقرأه ببطء، فبقية الأقسام تبني عليه.
Model providers can ship as a pip package. Expose an entry point in the
hermes_agent.plugins group in your pyproject.toml:
[project.entry-points."hermes_agent.plugins"]
acme-inference = "acme_hermes_plugin:register"The target may be either:
- a callable (
module:func) — invoked with no arguments; it should callregister_provider(profile), or - a bare module (
module) — imported for its module-levelregister_provider(...)side effect, mirroring the directory-plugin__init__.pycontract.
providers/__init__.py discovers these entry points itself — the general
PluginManager never invokes provider registration for pip packages (its
entry-point path targets register(ctx)-style general plugins, gated by
plugins.enabled), so the provider registry does its own scan. Two rules
apply:
- Opt-in required. The same
plugins.enabledallow-list (andplugins.disableddeny-list) fromconfig.yamlgoverns this scan. A pip package is never imported just because it is installed — users must add the entry-point name toplugins.enabled:
plugins:
enabled:
- acme-inference- Lowest precedence. Entry-point plugins are discovered before filesystem plugins: because
register_provider()is last-writer-wins, a bundled or$HERMES_HOMEprofile of the same name always overrides a pip-installed one. A pip package can add a genuinely new provider, but cannot silently hijack a first-party provider name.
Targets that require arguments (a general plugin's register(ctx)) are
skipped by the provider scan — they belong to the PluginManager. A broken
entry point is isolated — it is logged at warning level and skipped, and never
blocks discovery of the other providers.
See Building a Hermes Plugin for the full entry-points setup.
4 أسئلة إجاباتها كلها في هذه الصفحة.
كل خيار اسم حقيقي من توثيق Hermes. حتى الخيارات الخاطئة حقيقية، لكنها من صفحات أخرى.