feat(proxy): session-based prompt_cache_key routing for Codex Chat bridge

Codex always sends prompt_cache_key in its Responses requests, but the
Responses -> Chat Completions conversion dropped it, breaking session
cache affinity on upstreams that route by key (e.g. Kimi Coding).

- Re-inject prompt_cache_key after conversion in the forwarder: an
  explicit client key wins, otherwise a client-provided session ID;
  generated per-request UUIDs are never sent upstream.
- Provider-aware gating: "auto" enables only known-compatible upstreams
  (api.openai.com, api.kimi.com/coding) because strict gateways reject
  unknown fields with HTTP 400 (e.g. Fireworks); an advanced
  Auto/Enabled/Disabled override is available on the Codex form in all
  four locales.
- Kimi For Coding preset opts in explicitly.
This commit is contained in:
Jason
2026-07-11 21:42:43 +08:00
parent 0e563b50c5
commit a078b4b207
14 changed files with 295 additions and 4 deletions
@@ -21,6 +21,7 @@ import type {
CodexApiFormat,
CodexCatalogModel,
CodexChatReasoning,
PromptCacheRoutingMode,
ClaudeApiKeyField,
} from "@/types";
import {
@@ -357,6 +358,7 @@ function ProviderFormFull({
),
});
setCodexChatReasoning(initialData?.meta?.codexChatReasoning ?? {});
setPromptCacheRouting(initialData?.meta?.promptCacheRouting ?? "auto");
setCustomUserAgent(initialData?.meta?.customUserAgent ?? "");
setLocalProxyHeadersOverride(
formatRequestOverrideObject(
@@ -529,6 +531,10 @@ function ProviderFormFull({
useState<CodexChatReasoning>(
() => initialData?.meta?.codexChatReasoning ?? {},
);
const [promptCacheRouting, setPromptCacheRouting] =
useState<PromptCacheRoutingMode>(
() => initialData?.meta?.promptCacheRouting ?? "auto",
);
const [customUserAgent, setCustomUserAgent] = useState<string>(
() => initialData?.meta?.customUserAgent ?? "",
);
@@ -632,6 +638,7 @@ function ProviderFormFull({
const template = getCodexCustomTemplate();
resetCodexConfig(template.auth, template.config);
setCodexChatReasoning({});
setPromptCacheRouting("auto");
}
}, [appId, initialData, selectedPresetId, resetCodexConfig]);
@@ -1485,6 +1492,13 @@ function ProviderFormFull({
localCodexApiFormat === "openai_chat"
? normalizeCodexChatReasoningForSave(codexChatReasoning)
: undefined,
promptCacheRouting:
appId === "codex" &&
category !== "official" &&
localCodexApiFormat === "openai_chat" &&
promptCacheRouting !== "auto"
? promptCacheRouting
: undefined,
customUserAgent:
(appId === "claude" || appId === "codex") && category !== "official"
? customUserAgent.trim() || undefined
@@ -1651,6 +1665,7 @@ function ProviderFormFull({
const template = getCodexCustomTemplate();
resetCodexConfig(template.auth, template.config);
setCodexChatReasoning({});
setPromptCacheRouting("auto");
setLocalCodexApiFormat(
codexApiFormatFromWireApi(extractCodexWireApi(template.config)) ??
"openai_responses",
@@ -1692,6 +1707,7 @@ function ProviderFormFull({
resetCodexConfig(auth, config, preset.modelCatalog ?? []);
setCodexChatReasoning(preset.codexChatReasoning ?? {});
setPromptCacheRouting(preset.promptCacheRouting ?? "auto");
setLocalCodexApiFormat(
preset.apiFormat ??
codexApiFormatFromWireApi(extractCodexWireApi(config)) ??
@@ -2182,6 +2198,8 @@ function ProviderFormFull({
onMaxOutputTokensChange={setLocalCodexMaxOutputTokens}
codexChatReasoning={codexChatReasoning}
onCodexChatReasoningChange={setCodexChatReasoning}
promptCacheRouting={promptCacheRouting}
onPromptCacheRoutingChange={setPromptCacheRouting}
catalogModels={codexCatalogModels}
onCatalogModelsChange={setCodexCatalogModels}
speedTestEndpoints={speedTestEndpoints}