الأكاديمية ← دليل المطوّرتوثيق رسمي · إرشاد عربي

إضافات مزوّد النماذج

Model Provider Plugins

متقدم9 دقائق قراءةالدرس 194 أسئلة✓ 2026-08-18
قبل أن تقرأ

ما هذه الصفحة، وماذا تحتوي.

المزوّد والنموذج: المزوّد هو الشركة التي تشغّل نموذج الذكاء الاصطناعي، والنموذج هو «العقل» الذي يفكّر لـHermes. Hermes نفسه لا يفكّر؛ هو ينظّم العمل ويستدعي النموذج. لذلك اختيار النموذج يحدّد جودة النتيجة وتكلفتها. ستستعمل هنا hermes plugins وhermes doctor، والقراءة نحو 9 دقائق. انتبه: الأغلى ليس دائمًا الأفضل لمهمتك. جرّب مهمة واحدة على نموذجين وقارن، وضع سقفًا للإنفاق من البداية.

14أقسام
10أمثلة برمجية
4جداول
2أوامر
1,546كلمة من المصدر
الوصف الرسمي في سطر

How to build a model provider (inference backend) plugin for Hermes Agent

ماذا ستستطيع بعدها

نتائج مأخوذة من هذه الصفحة، لا من قالب.

  • تعرف ما المزوّد والنموذج ولماذا قد تحتاجه.
  • تنفّذ hermes plugins وhermes doctor وتفهم ما يحدث بعدها.
  • تقرأ الجدول وتأخذ منه السطر الذي يخصّك فقط.
  • تضبط ACME_API_KEY في المكان الصحيح.
ما ستقابله من أسماء

كما تظهر تمامًا داخل Hermes.

الأوامر
  • hermes plugins
  • hermes doctor
متغيرات البيئة
  • ACME_API_KEY
  • ACME_BASE_URL
  • GMI_API_KEY
  • PROVIDER_REGISTRY
  • HERMES_HOME
خريطة الصفحة

انتقل مباشرة إلى ما تحتاجه.

  1. 01How discovery works
  2. 02Directory structure
  3. 03Minimal example — a simple API-key provider
  4. 04ProviderProfile fields
  5. 05Overridable hooks
  6. 06Hook reference examples
  7. 07User overrides — replace a built-in without editing the repo
  8. 08apimode selection
  9. 09Auth types
  10. 10Discovery timing
  11. 11Testing your plugin
  12. 12General PluginManager integration
  13. 13Distribute via pip
  14. 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:

  1. Bundled plugins — <repo>/plugins/model-providers/<name>/ — ship with Hermes
  2. User plugins — $HERMES_HOME/plugins/model-providers/<name>/ — drop in any directory; no restart required for subsequent sessions
  3. 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.

Text4 أسطر
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

جدول مرجعي. لا تقرأه كله، ابحث عن السطر الذي يخصّك فقط.

Python22 سطرًا
# 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)
YAML6 أسطر
# 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

That's it. After dropping these two files, the following auto-wire with no other edits:

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

ProviderProfile fields

جدول مرجعي. لا تقرأه كله، ابحث عن السطر الذي يخصّك فقط.

Full definition in providers/base.py. The most useful ones:

FieldTypePurpose
namestrCanonical id — matches model.provider in config.yaml and the --provider flag
aliasestuple[str, ...]Alternative names resolved by get_provider_profile() (e.g. grok → xai)
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)

Overridable hooks

شرح للفكرة نفسها. اقرأه ببطء، فبقية الأقسام تبني عليه.

Subclass ProviderProfile for non-trivial quirks:

Python31 سطرًا
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:

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)

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:

Python11 سطرًا
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:

  1. User explicit override (config.yaml model.api_mode when set)
  2. OpenCode's per-model dispatch (opencode_model_api_mode for Zen and Go)
  3. URL auto-detection — /anthropic suffix → anthropic_messages, api.openai.com → codex_responses, api.x.ai → codex_responses, /coding on Kimi domains → chat_completions
  4. Profile api_mode as a fallback when URL detection finds nothing
  5. 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_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, 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

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:

Shellسطر واحد
hermes doctor

— a successful auth_type="api_key" profile appears under the Provider Connectivity section with a /models probe.

For programmatic inspection:

Python3 أسطر
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:

Shell15 سطرًا
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

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

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 call register_provider(profile), or
  • a bare module (module) — imported for its module-level register_provider(...) side effect, mirroring the directory-plugin __init__.py contract.

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.enabled allow-list (and plugins.disabled deny-list) from config.yaml governs this scan. A pip package is never imported just because it is installed — users must add the entry-point name to plugins.enabled:
YAML3 أسطر
  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_HOME profile 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. حتى الخيارات الخاطئة حقيقية، لكنها من صفحات أخرى.

1. في جدول هذا الدرس، ما «Where» المقابل لـ«hermes setup»؟
2. أي متغير بيئة من التالي يظهر فعليًا في هذا الدرس؟
3. أي عنوان من التالي لا يظهر في هذا الدرس؟
4. أي مفتاح إعداد يظهر في أمثلة هذا الدرس؟