Add Chat Completions routing for Codex providers

- Add a Codex API format selector and routing badge for Chat Completions providers.
- Convert Codex Responses requests to upstream Chat Completions when routing is required.
- Convert Chat Completions JSON and SSE responses back to Responses format.
- Keep generated Codex wire_api values on Responses for Codex compatibility.
- Add i18n labels, provider metadata handling, and focused conversion tests.
This commit is contained in:
Jason
2026-05-19 10:23:48 +08:00
parent 61e68d754c
commit 1c82b8a3fa
19 changed files with 2230 additions and 35 deletions
@@ -1,6 +1,14 @@
import { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import { FormLabel } from "@/components/ui/form";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { toast } from "sonner";
import { Download, Loader2 } from "lucide-react";
import EndpointSpeedTest from "./EndpointSpeedTest";
@@ -10,7 +18,7 @@ import {
showFetchModelsError,
type FetchedModel,
} from "@/lib/api/model-fetch";
import type { ProviderCategory } from "@/types";
import type { CodexApiFormat, ProviderCategory } from "@/types";
interface EndpointCandidate {
url: string;
@@ -39,6 +47,10 @@ interface CodexFormFieldsProps {
autoSelect: boolean;
onAutoSelectChange: (checked: boolean) => void;
// API Format
apiFormat: CodexApiFormat;
onApiFormatChange: (format: CodexApiFormat) => void;
// Model Name
shouldShowModelField?: boolean;
modelName?: string;
@@ -67,6 +79,8 @@ export function CodexFormFields({
onCustomEndpointsChange,
autoSelect,
onAutoSelectChange,
apiFormat,
onApiFormatChange,
shouldShowModelField = true,
modelName = "",
onModelNameChange,
@@ -143,6 +157,43 @@ export function CodexFormFields({
/>
)}
{/* Codex API 格式选择 */}
{shouldShowSpeedTest && (
<div className="space-y-2">
<FormLabel htmlFor="codexApiFormat">
{t("providerForm.apiFormat", { defaultValue: "API 格式" })}
</FormLabel>
<Select
value={apiFormat}
onValueChange={(value) =>
onApiFormatChange(value as CodexApiFormat)
}
>
<SelectTrigger id="codexApiFormat" className="w-full">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="openai_responses">
{t("providerForm.codexApiFormatResponses", {
defaultValue: "OpenAI Responses API (原生)",
})}
</SelectItem>
<SelectItem value="openai_chat">
{t("providerForm.codexApiFormatOpenAIChat", {
defaultValue: "OpenAI Chat Completions (需开启路由)",
})}
</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
{t("providerForm.codexApiFormatHint", {
defaultValue:
"选择供应商真实支持的 Codex API 格式;Chat Completions 会通过本地路由自动转换为 Responses。",
})}
</p>
</div>
)}
{/* Codex Model Name 输入框 */}
{shouldShowModelField && onModelNameChange && (
<div className="space-y-2">