refactor(hermes): drop "Auto" api_mode and require explicit protocol

Hermes' built-in api_mode detection only matches a handful of official
endpoints (api.openai.com, api.anthropic.com, api.x.ai, AWS Bedrock);
third-party / proxy endpoints silently fall back to chat_completions,
which causes opaque 401/404s on Anthropic-protocol or Codex-Responses
providers. The "Auto" option was misleading for the common third-party
case.

- Drop the "Auto" option from the API Mode dropdown; remove the
  HermesApiModeChoice sentinel type so writes always emit api_mode.
- Default new providers and legacy entries lacking api_mode to
  chat_completions (only persisted on user save).
- Deeplink imports now write api_mode: chat_completions explicitly
  instead of relying on URL heuristics; test renamed accordingly.
- Rename the "Codex Responses (Copilot / OpenCode)" label to
  "OpenAI Responses" to match OpenAI's /v1/responses naming.
This commit is contained in:
Jason
2026-04-20 11:40:59 +08:00
parent a9461ad52f
commit 21a1518f9f
7 changed files with 48 additions and 60 deletions
+13 -6
View File
@@ -428,8 +428,11 @@ fn build_additive_app_settings(request: &DeepLinkImportRequest) -> serde_json::V
/// Emitting camelCase here — as the OpenClaw path does — would poison the
/// YAML with unknown root fields the Hermes runtime ignores.
///
/// `api_mode` is deliberately omitted so Hermes auto-detects the protocol
/// from the endpoint; callers can still override via the UI later.
/// `api_mode` is always written explicitly. Deeplinks have no field to carry
/// it, so we default to `chat_completions` (the most widely compatible
/// protocol) and let the user adjust via the UI after import. We never rely
/// on Hermes' built-in URL heuristics, which only recognize a handful of
/// official endpoints.
fn build_hermes_settings(request: &DeepLinkImportRequest) -> serde_json::Value {
let endpoint = get_primary_endpoint(request);
@@ -447,6 +450,8 @@ fn build_hermes_settings(request: &DeepLinkImportRequest) -> serde_json::Value {
config.insert("api_key".to_string(), json!(api_key));
}
config.insert("api_mode".to_string(), json!("chat_completions"));
if let Some(model) = &request.model {
config.insert(
"models".to_string(),
@@ -784,11 +789,12 @@ mod tests {
}
#[test]
fn build_hermes_settings_omits_api_mode_for_auto_detect() {
fn build_hermes_settings_writes_default_api_mode() {
let settings = build_hermes_settings(&hermes_request());
assert!(
settings.as_object().unwrap().get("api_mode").is_none(),
"api_mode must be omitted so Hermes auto-detects"
assert_eq!(
settings.as_object().unwrap().get("api_mode").unwrap(),
"chat_completions",
"api_mode must be written explicitly so Hermes never falls back to URL auto-detection"
);
}
@@ -810,6 +816,7 @@ mod tests {
assert!(obj.get("base_url").is_none());
assert!(obj.get("api_key").is_none());
assert!(obj.get("models").is_none());
assert_eq!(obj.get("api_mode").unwrap(), "chat_completions");
}
#[test]