プロバイダーを追加する
目次
- 頭の中の見取り図
- ツール呼び出しの通信形式
- まず実装の道筋を決める
- 道筋 A — OpenAI 互換のプロバイダー
- 道筋 B — 独自形式のプロバイダー
- ファイルのチェックリスト
- すべての組み込みプロバイダーに必要なもの
- 独自形式(OpenAI 系でない)プロバイダーに追加で必要なもの
- 近道: API キーだけの単純なプロバイダー
- 本道: OAuth や込み入ったプロバイダー
- ステップ 1: 正式なプロバイダー ID を 1 つ決める
- ステップ 2: hermes_cli/auth.py に認証のメタデータを足す
- ステップ 3: hermes_cli/models.py にモデルの一覧と別名を足す
- ステップ 4: hermes_cli/runtime_provider.py で実行時のデータを解決する
- ステップ 5: hermes_cli/main.py で CLI につなぐ
- ステップ 6: 脇で走る呼び出しを保つ
- agent/auxiliary_client.py
- agent/model_metadata.py
- ステップ 7: 独自形式なら、アダプターと run_agent.py の対応を足す
- 新しいアダプターのファイル
- run_agent.py
- プロンプトキャッシュと、プロバイダー独自のリクエスト項目
- ステップ 8: テスト
- ステップ 9: 実際に動かして確かめる
- ステップ 10: 利用者向けドキュメントを更新する
- OpenAI 互換プロバイダーのチェックリスト
- 独自形式プロバイダーのチェックリスト
- つまずきやすいところ
- 1. 認証には足したのに、モデルの読み取りに足していない
- 2. config["model"] が文字列にも dict にもなり得ることを忘れる
- 3. 組み込みのプロバイダーが必要だと思い込む
- 4. 脇で走る経路を忘れる
- 5. 独自形式の枝が run_agent.py に隠れている
- 6. OpenRouter だけの設定を、ほかのプロバイダーに送ってしまう
- 7. hermes model は直したのに、hermes setup を直していない
- 作業中に検索するとよい語
- 関連するドキュメント
Hermes は、独自プロバイダーの経路を通せば、OpenAI 互換のエンドポイントならどれでもすでに話せます。次のような、そのサービス専用の使い心地を用意したいのでなければ、組み込みのプロバイダーを足す必要はありません。
- そのプロバイダー独自の認証やトークンの更新
- 選び抜いたモデルの一覧
- セットアップや
hermes modelのメニューへの項目追加 provider:modelの書き方で使えるプロバイダーの別名- アダプターが要る、OpenAI とは違う形の API
「OpenAI 互換のベース URL と API キーがもう 1 つ増えるだけ」なら、名前を付けた独自プロバイダーで足りることもあります。
頭の中の見取り図
組み込みのプロバイダーは、いくつかの層で辻褄を合わせる必要があります。
hermes_cli/auth.pyが、資格情報をどこから見つけるかを決めます。hermes_cli/runtime_provider.pyが、それを実行時のデータに変えます。
providerapi_modebase_urlapi_keysource
run_agent.pyがapi_modeを見て、リクエストの組み立て方と送り方を決めます。hermes_cli/models.pyとhermes_cli/main.pyが、そのプロバイダーを CLI に出します。(hermes_cli/setup.pyはmain.pyに自動で任せるので、そちらに手を入れる必要はありません。)agent/auxiliary_client.pyとagent/model_metadata.pyが、脇で走る処理とトークンの残り勘定を保ちます。
肝心な考え方が api_mode です。
- たいていのプロバイダーは
chat_completionsを使います。 - Codex と Meta Model API(
api.meta.ai— Muse Spark)はcodex_responsesを使います(プロンプトキャッシュのためにprompt_cache_retention: 24hが自動で送られます。api.meta.aiで 93〜99% のキャッシュ命中が出るのは/v1/responsesだけです)。 - Ramp Router(
api.router.com)もcodex_responsesを使います。Router にとっては Responses が本来の通信路で(/v1/chat/completionsは最小限の互換の受け皿にすぎません)、モデルごとにreasoning.effortを検査します。router のプロファイルは、生きた一覧からモデルごとの語彙を宣言することでこれに合わせています(ProviderProfile.supported_reasoning_efforts)。 - Anthropic は
anthropic_messagesを使います。 - OpenAI 系ではない新しい通信規約なら、たいていは新しいアダプターと新しい
api_modeの枝を足すことになります。
ツール呼び出しの通信形式
Hermes は会話の履歴を、内部では OpenAI の chat-completions の形で持っています。そのため chat_completions の通信路にある convert_messages と convert_tools(agent/transports/chat_completions.py)はほぼ素通しで、ほかの通信路はどれもその形*から*それぞれの規約へ変換します。この形の正本 — JSON スキーマの parameters を持つ tools の定義、function.arguments を文字列にした assistant の tool_calls の項目、tool_call_id で対応づけられる role: "tool" の結果メッセージ — は OpenAI chat completions API の仕様書にあります。独自のアダプターを書くときは、そのページが変換元の形を定め、追加するプロバイダーのドキュメントが変換先の形を定めます。
まず実装の道筋を決める
道筋 A — OpenAI 互換のプロバイダー
プロバイダーが標準的な chat-completions 形式のリクエストを受け付けるなら、こちらです。
やることは、だいたい次のとおりです。
- 認証のメタデータを足す
- モデルの一覧と別名を足す
- 実行時の解決を足す
- CLI のメニューにつなぐ
- 補助モデルの既定値を足す
- テストと利用者向けドキュメントを足す
新しいアダプターや新しい api_mode は、たいてい要りません。
道筋 B — 独自形式のプロバイダー
プロバイダーの振る舞いが OpenAI の chat completions と違うなら、こちらです。
現在リポジトリにある例は次のとおりです。
codex_responses(OpenAI Codex、xAI Grok、api.meta.ai経由の Meta Muse Spark — 最後のものはprompt_cache_retention: 24hを自動で送ります — そしてapi.router.com経由の Ramp Router)anthropic_messages
この道筋では、道筋 A の全部に加えて次が必要です。
agent/に置くプロバイダーのアダプター- リクエストの組み立て、振り分け、使用量の取り出し、中断の処理、応答の正規化にあたる
run_agent.pyの枝 - アダプターのテスト
ファイルのチェックリスト
すべての組み込みプロバイダーに必要なもの
hermes_cli/auth.pyhermes_cli/models.pyhermes_cli/runtime_provider.pyhermes_cli/main.pyagent/auxiliary_client.pyagent/model_metadata.py- テスト
website/docs/以下の利用者向けドキュメント
独自形式(OpenAI 系でない)プロバイダーに追加で必要なもの
agent/<provider>_adapter.pyrun_agent.py- プロバイダーの SDK が要るなら
pyproject.toml
近道: API キーだけの単純なプロバイダー
追加したいプロバイダーが、API キー 1 つで認証する OpenAI 互換のエンドポイントにすぎないなら、auth.py、runtime_provider.py、main.py をはじめ、以下の長いチェックリストにあるファイルには触らなくて済みます。
必要なのはこれだけです。
plugins/model-providers/<your-provider>/の下に置くプラグインのディレクトリ。中身は次の 2 つです。
__init__.py— モジュールの階層でregister_provider(profile)を呼ぶplugin.yaml— 目録(name、kind: model-provider、version、description)
- これで終わりです。プロバイダーのプラグインは、
get_provider_profile()かlist_providers()が最初に呼ばれたときに自動で読み込まれます。同梱のプラグイン(このリポジトリのもの)も、$HERMES_HOME/plugins/model-providers/に置いた自前のプラグインも、どちらも拾われます。
プラグインを追加して register_provider() が呼ばれると、次のものが自動でつながります。
auth.pyのPROVIDER_REGISTRYの項目(資格情報の解決、環境変数の参照)api_modeがchat_completionsに設定されるbase_urlが、設定または宣言した環境変数から取られる- API キーを探すとき、
env_varsが優先順に調べられる - そのプロバイダーの
fallback_modelsの一覧が登録される - CLI の
--providerフラグがそのプロバイダー ID を受け付ける hermes modelのメニューにそのプロバイダーが並ぶhermes setupのウィザードがmain.pyに自動で任せるprovider:modelの別名の書き方が使える- 実行時の解決役が、正しい
base_urlとapi_keyを返す - CLI の
--provider <name>フラグがそのプロバイダー ID を受け付ける - フォールバックのモデルに切り替わるとき、そのプロバイダーへきれいに移れる
$HERMES_HOME/plugins/model-providers/<name>/ に置いた自前のプラグインは、同じ名前の同梱プラグインより優先されます(register_provider() は後から書いたほうが勝ちます)。そのため、第三者はリポジトリに手を入れずに、どの組み込みプロファイルでも上書きしたり差し替えたりできます。
雛形としては plugins/model-providers/nvidia/ か plugins/model-providers/gmi/ を見てください。項目の説明、フックの書き方の型、端から端までの実例はモデルプロバイダープラグインの手引きにあります。
本道: OAuth や込み入ったプロバイダー
次のどれかが必要なプロバイダーでは、以下の長いチェックリストを使ってください。
- OAuth やトークンの更新(Nous Portal、Codex、Qwen Portal、Copilot)
- 新しいアダプターが要る、OpenAI とは違う形の API(Anthropic Messages、Codex Responses)
- 独自のエンドポイント判定や、複数の地域への接続試行(z.ai、Kimi)
- 選び抜いた固定のモデル一覧、または
/modelsからの取得 - 独自の認証の流れを持つ、
hermes modelメニューのプロバイダー専用項目
ステップ 1: 正式なプロバイダー ID を 1 つ決める
プロバイダー ID を 1 つ決めて、どこでもそれを使ってください。
リポジトリにある例です。
openai-codexkimi-codingminimax-cn
同じ ID を、次の場所すべてに出します。
hermes_cli/auth.pyのPROVIDER_REGISTRYhermes_cli/models_catalog_static.pyの_PROVIDER_LABELS(hermes_cli/models.pyから再公開されています)hermes_cli/auth.pyとhermes_cli/models_catalog_static.pyの両方にある_PROVIDER_ALIASEShermes_cli/main.pyの CLI--providerの選択肢- セットアップとモデル選択の枝
- 補助モデルの既定値
- テスト
これらのファイルで ID が食い違うと、プロバイダーは中途半端につながった状態になります。認証は通るのに、/model、セットアップ、実行時の解決だけが黙って取りこぼす、という形です。
ステップ 2: hermes_cli/auth.py に認証のメタデータを足す
API キーを使うプロバイダーなら、PROVIDER_REGISTRY に ProviderConfig の項目を足します。中身は次のとおりです。
idnameauth_type="api_key"inference_base_urlapi_key_env_vars- 任意で
base_url_env_var
_PROVIDER_ALIASES にも別名を足します。
雛形には、すでにあるプロバイダーを使ってください。
- API キーだけの単純な形: Z.AI、MiniMax
- エンドポイント判定つきの API キーの形: Kimi、Z.AI
- 独自のトークン解決: Anthropic
- OAuth と資格情報の保管を使う形: Nous、OpenAI Codex
ここで答えを決めておく問いです。
- Hermes はどの環境変数を、どの優先順で調べるべきか
- ベース URL の上書きが要るか
- エンドポイントの接続試行やトークンの更新が要るか
- 資格情報が見つからないとき、認証のエラーには何と出すべきか
「API キーを探す」以上のことが必要なら、関係のない枝にロジックを押し込まず、専用の資格情報の解決役を足してください。
ステップ 3: hermes_cli/models.py にモデルの一覧と別名を足す
メニューと provider:model の書き方で使えるように、プロバイダーの一覧を更新します。
よく手を入れるのはこのあたりです。
_PROVIDER_MODELS_PROVIDER_LABELS_PROVIDER_ALIASESlist_available_providers()の中でのプロバイダーの表示順- プロバイダーが
/modelsからの取得に対応しているならprovider_model_ids()
プロバイダーが最新のモデル一覧を公開しているなら、まずそちらを使い、_PROVIDER_MODELS は固定の予備として残します。
このファイルは、次のような入力を通すためのものでもあります。
anthropic:claude-sonnet-4-6
kimi:model-nameここに別名がないと、認証はうまくいくのに /model の読み取りで失敗する、ということが起こります。
ステップ 4: hermes_cli/runtime_provider.py で実行時のデータを解決する
resolve_runtime_provider() は、CLI、ゲートウェイ、cron、ACP、補助のクライアントが共通で通る道です。
少なくとも次の内容を持つ dict を返す枝を足します。
{
"provider": "your-provider",
"api_mode": "chat_completions", # or your native mode
"base_url": "https://...",
"api_key": "...",
"source": "env|portal|auth-store|explicit",
"requested_provider": requested_provider,
}プロバイダーが OpenAI 互換なら、api_mode はふつう chat_completions のままにします。
API キーの優先順位には気をつけてください。Hermes には、OpenRouter のキーが関係のないエンドポイントへ漏れないようにする仕組みがすでに入っています。新しいプロバイダーでも、どのキーをどのベース URL に渡すのかを同じくらいはっきりさせてください。
ステップ 5: hermes_cli/main.py で CLI につなぐ
対話的な hermes model の流れに出てくるまで、そのプロバイダーは見つけてもらえません。
hermes_cli/main.py の次の箇所を更新します。
provider_labelsの dictselect_provider_and_model()の中のprovidersの一覧- プロバイダーの振り分け(
if selected_provider == ...) --provider引数の選択肢- ログインとログアウトの流れに対応しているなら、その選択肢
_model_flow_<provider>()の関数。合うなら_model_flow_api_key_provider()を使い回してもかまいません
ステップ 6: 脇で走る呼び出しを保つ
ここで関わるのは 2 つのファイルです。
agent/auxiliary_client.py
API キーを直接使うプロバイダーなら、安くて速い補助モデルの既定値を _API_KEY_PROVIDER_AUX_MODELS に足します。
脇で走る処理には、次のようなものがあります。
- 画像の内容のまとめ
- Web から抜き出した本文のまとめ
- 文脈の圧縮のためのまとめ
- セッション検索のまとめ
- メモリの書き出し
そのプロバイダーに手頃な補助の既定値がないと、脇の処理がよくない形で切り替わったり、思いがけず高価な主モデルを使ってしまったりします。
agent/model_metadata.py
トークンの残り勘定、圧縮の始まる目安、上限が正しく働くように、そのプロバイダーのモデルのコンテキスト長を足します。
ステップ 7: 独自形式なら、アダプターと run_agent.py の対応を足す
素の chat completions ではないプロバイダーなら、そのプロバイダー独自の処理は agent/<provider>_adapter.py に閉じ込めます。
run_agent.py は取りまとめに専念させてください。アダプターの補助関数を呼ぶ形にして、ファイルのあちこちでプロバイダーの送信内容を手組みしないようにします。
独自形式のプロバイダーでは、たいてい次の場所に手が要ります。
新しいアダプターのファイル
受け持つのは、だいたい次のことです。
- SDK や HTTP クライアントを作る
- トークンを解決する
- OpenAI 形式の会話メッセージを、そのプロバイダーのリクエスト形式に変換する
- 必要ならツールのスキーマを変換する
- プロバイダーの応答を、
run_agent.pyが期待する形に戻す - 使用量と終了理由のデータを取り出す
run_agent.py
api_mode を検索して、分岐しているところをすべて点検します。少なくとも次を確かめてください。
__init__が新しいapi_modeを選ぶ- そのプロバイダー向けにクライアントを作れる
_build_api_kwargs()がリクエストの整え方を知っている_interruptible_api_call()が正しいクライアント呼び出しに振り分ける- 中断とクライアントの作り直しの経路が動く
- 応答の検査が、そのプロバイダーの形を受け付ける
- 終了理由の取り出しが正しい
- トークン使用量の取り出しが正しい
- フォールバックのモデルに切り替わるとき、新しいプロバイダーへきれいに移れる
- まとめの生成とメモリの書き出しの経路が、これまでどおり動く
run_agent.py の中を self.client. でも検索してください。標準の OpenAI クライアントがあることを前提にしているコードは、独自形式のプロバイダーが別のクライアントを使ったり self.client = None になったりすると壊れることがあります。
プロンプトキャッシュと、プロバイダー独自のリクエスト項目
プロンプトキャッシュや、プロバイダーごとの細かい設定は、うっかり壊しやすいところです。
すでにリポジトリにある例です。
- Anthropic には独自のプロンプトキャッシュの経路がある
- OpenRouter にはプロバイダーの振り分け用の項目が渡る
- どのプロバイダーにも、リクエスト側の設定を全部渡してよいわけではない
独自形式のプロバイダーを足すときは、そのプロバイダーが実際に理解できる項目だけを Hermes が送っているか、あらためて確かめてください。
ステップ 8: テスト
少なくとも、プロバイダーのつなぎ込みを守っているテストには手を入れます。
よく触るのは次のあたりです。
tests/hermes_cli/test_runtime_provider_resolution.pytests/hermes_cli/test_cli_provider_resolution.pytests/hermes_cli/test_model_switch_custom_providers.py(および隣にあるtests/hermes_cli/test_model_switch_*.py)tests/hermes_cli/test_setup_model_provider.pytests/agent/test_provider_parity.pytests/agent/test_run_agent.py- 独自形式のプロバイダーなら
tests/agent/test_<provider>_adapter.py
ドキュメント上の例なので、実際に触るファイルは違うかもしれません。大事なのは、次を押さえることです。
- 認証の解決
- CLI のメニューとプロバイダーの選択
- 実行時のプロバイダーの解決
- エージェントの実行経路
- provider:model の読み取り
- アダプター独自のメッセージ変換があれば、それ
独立したテスト環境を用意してから、 正規のテストランナーを使います。ファイルごとに分離して走らせ、認証情報も取り除いてくれます。
scripts/run_tests.sh tests/hermes_cli/test_runtime_provider_resolution.py tests/hermes_cli/test_cli_provider_resolution.py tests/hermes_cli/test_setup_model_provider.py tests/agent/test_provider_parity.py -q変更が深いところに及ぶなら、push の前に全体を走らせます。
scripts/run_tests.sh tests/ -qステップ 9: 実際に動かして確かめる
テストのあとは、チェックアウトから本物で軽く動かしてみます。 PM の開発者向けワークフローと、 それが用意する分離された開発用ホームを使ってください。PM を有効化する前に、テスト用の venv からは抜けておきます。
source ./activate
python hermes chat -q "Say hello" --provider your-provider --model your-modelメニューを変えたなら、対話的な流れも試します。
source ./activate
python hermes model
python hermes setup独自形式のプロバイダーでは、ただの文章の応答だけでなく、ツール呼び出しも最低 1 回は確かめてください。
ステップ 10: 利用者向けドキュメントを更新する
そのプロバイダーを正式な選択肢として配るつもりなら、利用者向けのドキュメントも更新します。
website/docs/getting-started/quickstart.mdwebsite/docs/user-guide/configuration.mdwebsite/docs/reference/environment-variables.md
開発者側で完璧につないでも、必要な環境変数やセットアップの流れを利用者が見つけられないままになることがあります。
OpenAI 互換プロバイダーのチェックリスト
標準の chat completions のプロバイダーなら、こちらを使ってください。
- [ ]
hermes_cli/auth.pyにProviderConfigを追加した - [ ]
hermes_cli/auth.pyとhermes_cli/models.pyに別名を追加した - [ ]
hermes_cli/models.pyにモデルの一覧を追加した - [ ]
hermes_cli/runtime_provider.pyに実行時の枝を追加した - [ ]
hermes_cli/main.pyに CLI のつなぎ込みを追加した(setup.py は自動で引き継ぎます) - [ ]
agent/auxiliary_client.pyに補助モデルを追加した - [ ]
agent/model_metadata.pyにコンテキスト長を追加した - [ ] 実行時と CLI のテストを更新した
- [ ] 利用者向けドキュメントを更新した
独自形式プロバイダーのチェックリスト
新しい通信規約の経路が必要なプロバイダーなら、こちらを使ってください。
- [ ] OpenAI 互換のチェックリストの全項目
- [ ]
agent/<provider>_adapter.pyにアダプターを追加した - [ ]
run_agent.pyが新しいapi_modeに対応した - [ ] 中断と作り直しの経路が動く
- [ ] 使用量と終了理由の取り出しが動く
- [ ] フォールバックの経路が動く
- [ ] アダプターのテストを追加した
- [ ] 実際に動かした確認が通る
つまずきやすいところ
1. 認証には足したのに、モデルの読み取りに足していない
資格情報はきちんと解決されるのに、/model や provider:model の入力が通らなくなります。
2. config["model"] が文字列にも dict にもなり得ることを忘れる
プロバイダー選択のコードの多くは、どちらの形も揃えて扱う必要があります。
3. 組み込みのプロバイダーが必要だと思い込む
そのサービスが OpenAI 互換なだけなら、独自プロバイダーのほうが手間なく困りごとを解けているかもしれません。
4. 脇で走る経路を忘れる
補助側の振り分けを更新し忘れると、ふだんの会話は動くのに、まとめ、メモリの書き出し、画像の処理だけが失敗する、ということが起こります。
5. 独自形式の枝が run_agent.py に隠れている
api_mode と self.client. で検索してください。目につくリクエストの経路だけが唯一だと思い込まないことです。
6. OpenRouter だけの設定を、ほかのプロバイダーに送ってしまう
プロバイダーの振り分けのような項目は、それに対応しているプロバイダーにだけ付けます。
7. hermes model は直したのに、hermes setup を直していない
どちらの流れも、そのプロバイダーを知っている必要があります。
作業中に検索するとよい語
プロバイダーが関わる箇所をすべて洗い出したいときは、次の名前で検索してください。
PROVIDER_REGISTRY_PROVIDER_ALIASES_PROVIDER_MODELSresolve_runtime_provider_model_flow_select_provider_and_modelapi_mode_API_KEY_PROVIDER_AUX_MODELSself.client.