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
@@ -40,7 +40,7 @@ import {
} from "@/lib/api/model-fetch";
import {
hermesApiModes,
type HermesApiModeChoice,
type HermesApiMode,
type HermesModel,
} from "@/config/hermesProviderPresets";
import type { ProviderCategory } from "@/types";
@@ -55,8 +55,8 @@ interface HermesFormFieldsProps {
websiteUrl: string;
isPartner?: boolean;
partnerPromotionKey?: string;
apiMode: HermesApiModeChoice;
onApiModeChange: (mode: HermesApiModeChoice) => void;
apiMode: HermesApiMode;
onApiModeChange: (mode: HermesApiMode) => void;
models: HermesModel[];
onModelsChange: (models: HermesModel[]) => void;
}
@@ -181,7 +181,7 @@ export function HermesFormFields({
</FormLabel>
<Select
value={apiMode}
onValueChange={(v) => onApiModeChange(v as HermesApiModeChoice)}
onValueChange={(v) => onApiModeChange(v as HermesApiMode)}
>
<SelectTrigger id="hermes-api-mode">
<SelectValue />
@@ -196,8 +196,7 @@ export function HermesFormFields({
</Select>
<p className="text-xs text-muted-foreground">
{t("hermes.form.apiModeHint", {
defaultValue:
"供应商 API 协议。Auto 表示让 Hermes 按端点自动判断。",
defaultValue: "供应商 API 协议。请根据端点选择正确的协议。",
})}
</p>
</div>
@@ -1,10 +1,11 @@
import { useState, useCallback, useMemo } from "react";
import type { AppId } from "@/lib/api";
import { useProvidersQuery } from "@/lib/query/queries";
import type {
HermesApiModeChoice,
HermesModel,
HermesProviderSettingsConfig,
import {
HERMES_DEFAULT_API_MODE,
type HermesApiMode,
type HermesModel,
type HermesProviderSettingsConfig,
} from "@/config/hermesProviderPresets";
interface UseHermesFormStateParams {
@@ -34,12 +35,12 @@ export interface HermesFormState {
setHermesProviderKey: (key: string) => void;
hermesBaseUrl: string;
hermesApiKey: string;
hermesApiMode: HermesApiModeChoice;
hermesApiMode: HermesApiMode;
hermesModels: HermesModel[];
existingHermesKeys: string[];
handleHermesBaseUrlChange: (baseUrl: string) => void;
handleHermesApiKeyChange: (apiKey: string) => void;
handleHermesApiModeChange: (mode: HermesApiModeChoice) => void;
handleHermesApiModeChange: (mode: HermesApiMode) => void;
handleHermesModelsChange: (models: HermesModel[]) => void;
resetHermesState: (config?: Partial<HermesProviderSettingsConfig>) => void;
}
@@ -92,17 +93,15 @@ export function useHermesFormState({
return parseHermesField(initialData, "api_key", "");
});
const [hermesApiMode, setHermesApiMode] = useState<HermesApiModeChoice>(
() => {
if (appId !== "hermes") return "auto";
const stored = parseHermesField<HermesApiModeChoice | "">(
initialData,
"api_mode",
"",
);
return stored || "auto";
},
);
const [hermesApiMode, setHermesApiMode] = useState<HermesApiMode>(() => {
if (appId !== "hermes") return HERMES_DEFAULT_API_MODE;
const stored = parseHermesField<HermesApiMode | "">(
initialData,
"api_mode",
"",
);
return stored || HERMES_DEFAULT_API_MODE;
});
const [hermesModels, setHermesModels] = useState<HermesModel[]>(() => {
if (appId !== "hermes") return [];
@@ -143,14 +142,10 @@ export function useHermesFormState({
);
const handleHermesApiModeChange = useCallback(
(mode: HermesApiModeChoice) => {
(mode: HermesApiMode) => {
setHermesApiMode(mode);
updateHermesConfig((config) => {
if (mode === "auto") {
delete config.api_mode;
} else {
config.api_mode = mode;
}
config.api_mode = mode;
});
},
[updateHermesConfig],
@@ -175,7 +170,7 @@ export function useHermesFormState({
setHermesProviderKey("");
setHermesBaseUrl(config?.base_url || "");
setHermesApiKey(config?.api_key || "");
setHermesApiMode(config?.api_mode ?? "auto");
setHermesApiMode(config?.api_mode ?? HERMES_DEFAULT_API_MODE);
setHermesModels(config?.models ?? []);
},
[],