mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-29 15:31:38 +08:00
Compare commits
1 Commits
fix/plugin
...
fix/anthro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
205891c9c8 |
@@ -340,13 +340,23 @@ def resolve_runtime_provider(
|
|||||||
if pconfig and pconfig.auth_type == "api_key":
|
if pconfig and pconfig.auth_type == "api_key":
|
||||||
creds = resolve_api_key_provider_credentials(provider)
|
creds = resolve_api_key_provider_credentials(provider)
|
||||||
model_cfg = _get_model_config()
|
model_cfg = _get_model_config()
|
||||||
|
base_url = creds.get("base_url", "").rstrip("/")
|
||||||
api_mode = "chat_completions"
|
api_mode = "chat_completions"
|
||||||
if provider == "copilot":
|
if provider == "copilot":
|
||||||
api_mode = _copilot_runtime_api_mode(model_cfg, creds.get("api_key", ""))
|
api_mode = _copilot_runtime_api_mode(model_cfg, creds.get("api_key", ""))
|
||||||
|
else:
|
||||||
|
# Check explicit api_mode from model config first
|
||||||
|
configured_mode = _parse_api_mode(model_cfg.get("api_mode"))
|
||||||
|
if configured_mode:
|
||||||
|
api_mode = configured_mode
|
||||||
|
# Auto-detect Anthropic-compatible endpoints by URL convention
|
||||||
|
# (e.g. https://api.minimax.io/anthropic, https://dashscope.../anthropic)
|
||||||
|
elif base_url.rstrip("/").endswith("/anthropic"):
|
||||||
|
api_mode = "anthropic_messages"
|
||||||
return {
|
return {
|
||||||
"provider": provider,
|
"provider": provider,
|
||||||
"api_mode": api_mode,
|
"api_mode": api_mode,
|
||||||
"base_url": creds.get("base_url", "").rstrip("/"),
|
"base_url": base_url,
|
||||||
"api_key": creds.get("api_key", ""),
|
"api_key": creds.get("api_key", ""),
|
||||||
"source": creds.get("source", "env"),
|
"source": creds.get("source", "env"),
|
||||||
"requested_provider": requested_provider,
|
"requested_provider": requested_provider,
|
||||||
|
|||||||
@@ -493,6 +493,11 @@ class AIAgent:
|
|||||||
elif self.provider == "anthropic" or (provider_name is None and "api.anthropic.com" in self._base_url_lower):
|
elif self.provider == "anthropic" or (provider_name is None and "api.anthropic.com" in self._base_url_lower):
|
||||||
self.api_mode = "anthropic_messages"
|
self.api_mode = "anthropic_messages"
|
||||||
self.provider = "anthropic"
|
self.provider = "anthropic"
|
||||||
|
elif self._base_url_lower.rstrip("/").endswith("/anthropic"):
|
||||||
|
# Third-party Anthropic-compatible endpoints (e.g. MiniMax, DashScope)
|
||||||
|
# use a URL convention ending in /anthropic. Auto-detect these so the
|
||||||
|
# Anthropic Messages API adapter is used instead of chat completions.
|
||||||
|
self.api_mode = "anthropic_messages"
|
||||||
else:
|
else:
|
||||||
self.api_mode = "chat_completions"
|
self.api_mode = "chat_completions"
|
||||||
|
|
||||||
@@ -3474,11 +3479,11 @@ class AIAgent:
|
|||||||
|
|
||||||
# Determine api_mode from provider
|
# Determine api_mode from provider
|
||||||
fb_api_mode = "chat_completions"
|
fb_api_mode = "chat_completions"
|
||||||
|
fb_base_url = str(fb_client.base_url)
|
||||||
if fb_provider == "openai-codex":
|
if fb_provider == "openai-codex":
|
||||||
fb_api_mode = "codex_responses"
|
fb_api_mode = "codex_responses"
|
||||||
elif fb_provider == "anthropic":
|
elif fb_provider == "anthropic" or fb_base_url.rstrip("/").lower().endswith("/anthropic"):
|
||||||
fb_api_mode = "anthropic_messages"
|
fb_api_mode = "anthropic_messages"
|
||||||
fb_base_url = str(fb_client.base_url)
|
|
||||||
|
|
||||||
old_model = self.model
|
old_model = self.model
|
||||||
self.model = fb_model
|
self.model = fb_model
|
||||||
|
|||||||
@@ -438,10 +438,75 @@ def test_named_custom_provider_without_api_mode_defaults(monkeypatch):
|
|||||||
lambda p: {
|
lambda p: {
|
||||||
"name": "my-server",
|
"name": "my-server",
|
||||||
"base_url": "http://localhost:8000/v1",
|
"base_url": "http://localhost:8000/v1",
|
||||||
"api_key": "sk-test",
|
"api_key": "***",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
resolved = rp.resolve_runtime_provider(requested="my-server")
|
resolved = rp.resolve_runtime_provider(requested="my-server")
|
||||||
|
|
||||||
assert resolved["api_mode"] == "chat_completions"
|
assert resolved["api_mode"] == "chat_completions"
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_messages_in_valid_api_modes():
|
||||||
|
"""anthropic_messages should be accepted by _parse_api_mode."""
|
||||||
|
assert rp._parse_api_mode("anthropic_messages") == "anthropic_messages"
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_key_provider_anthropic_url_auto_detection(monkeypatch):
|
||||||
|
"""API-key providers with /anthropic base URL should auto-detect anthropic_messages mode."""
|
||||||
|
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "minimax")
|
||||||
|
monkeypatch.setattr(rp, "_get_model_config", lambda: {})
|
||||||
|
monkeypatch.setenv("MINIMAX_API_KEY", "test-minimax-key")
|
||||||
|
monkeypatch.setenv("MINIMAX_BASE_URL", "https://api.minimax.io/anthropic")
|
||||||
|
|
||||||
|
resolved = rp.resolve_runtime_provider(requested="minimax")
|
||||||
|
|
||||||
|
assert resolved["provider"] == "minimax"
|
||||||
|
assert resolved["api_mode"] == "anthropic_messages"
|
||||||
|
assert resolved["base_url"] == "https://api.minimax.io/anthropic"
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_key_provider_explicit_api_mode_config(monkeypatch):
|
||||||
|
"""API-key providers should respect api_mode from model config."""
|
||||||
|
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "minimax")
|
||||||
|
monkeypatch.setattr(rp, "_get_model_config", lambda: {"api_mode": "anthropic_messages"})
|
||||||
|
monkeypatch.setenv("MINIMAX_API_KEY", "test-minimax-key")
|
||||||
|
monkeypatch.delenv("MINIMAX_BASE_URL", raising=False)
|
||||||
|
|
||||||
|
resolved = rp.resolve_runtime_provider(requested="minimax")
|
||||||
|
|
||||||
|
assert resolved["provider"] == "minimax"
|
||||||
|
assert resolved["api_mode"] == "anthropic_messages"
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_key_provider_default_url_stays_chat_completions(monkeypatch):
|
||||||
|
"""API-key providers with default /v1 URL should stay on chat_completions."""
|
||||||
|
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "minimax")
|
||||||
|
monkeypatch.setattr(rp, "_get_model_config", lambda: {})
|
||||||
|
monkeypatch.setenv("MINIMAX_API_KEY", "test-minimax-key")
|
||||||
|
monkeypatch.delenv("MINIMAX_BASE_URL", raising=False)
|
||||||
|
|
||||||
|
resolved = rp.resolve_runtime_provider(requested="minimax")
|
||||||
|
|
||||||
|
assert resolved["provider"] == "minimax"
|
||||||
|
assert resolved["api_mode"] == "chat_completions"
|
||||||
|
assert resolved["base_url"] == "https://api.minimax.io/v1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_named_custom_provider_anthropic_api_mode(monkeypatch):
|
||||||
|
"""Custom providers should accept api_mode: anthropic_messages."""
|
||||||
|
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "my-anthropic-proxy")
|
||||||
|
monkeypatch.setattr(
|
||||||
|
rp, "_get_named_custom_provider",
|
||||||
|
lambda p: {
|
||||||
|
"name": "my-anthropic-proxy",
|
||||||
|
"base_url": "https://proxy.example.com/anthropic",
|
||||||
|
"api_key": "test-key",
|
||||||
|
"api_mode": "anthropic_messages",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
resolved = rp.resolve_runtime_provider(requested="my-anthropic-proxy")
|
||||||
|
|
||||||
|
assert resolved["api_mode"] == "anthropic_messages"
|
||||||
|
assert resolved["base_url"] == "https://proxy.example.com/anthropic"
|
||||||
|
|||||||
Reference in New Issue
Block a user