refactor(codex): extract template to config with i18n support
**Changes:** - Create src/config/codexTemplates.ts with getCodexCustomTemplate factory - Support both Chinese and English templates based on i18n.language - Remove 70 lines of duplicated template strings from ProviderForm.tsx - Update both useEffect and handlePresetChange to use template factory - Clean up unused "Custom (Blank Template)" preset entry **Benefits:** - ✅ Eliminates code duplication (35-line template repeated twice) - ✅ Adds internationalization support for English users - ✅ Follows project architecture (templates in config/ directory) - ✅ Improves maintainability (single source of truth) - ✅ Net reduction: 34 lines (81 additions, 115 deletions) **Technical Details:** - Template selection logic: (i18n.language || "zh").startsWith("zh") ? "zh" : "en" - Templates are identical except for comments language - Both auth and config are returned as a single CodexTemplate object Addresses DRY principle violation and architectural concerns identified in code review.
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
|||||||
} from "@/config/geminiProviderPresets";
|
} from "@/config/geminiProviderPresets";
|
||||||
import { applyTemplateValues } from "@/utils/providerConfigUtils";
|
import { applyTemplateValues } from "@/utils/providerConfigUtils";
|
||||||
import { mergeProviderMeta } from "@/utils/providerMetaUtils";
|
import { mergeProviderMeta } from "@/utils/providerMetaUtils";
|
||||||
|
import { getCodexCustomTemplate } from "@/config/codexTemplates";
|
||||||
import CodexConfigEditor from "./CodexConfigEditor";
|
import CodexConfigEditor from "./CodexConfigEditor";
|
||||||
import { CommonConfigEditor } from "./CommonConfigEditor";
|
import { CommonConfigEditor } from "./CommonConfigEditor";
|
||||||
import GeminiConfigEditor from "./GeminiConfigEditor";
|
import GeminiConfigEditor from "./GeminiConfigEditor";
|
||||||
@@ -89,7 +90,7 @@ export function ProviderForm({
|
|||||||
initialData,
|
initialData,
|
||||||
showButtons = true,
|
showButtons = true,
|
||||||
}: ProviderFormProps) {
|
}: ProviderFormProps) {
|
||||||
const { t } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const isEditMode = Boolean(initialData);
|
const isEditMode = Boolean(initialData);
|
||||||
|
|
||||||
const [selectedPresetId, setSelectedPresetId] = useState<string | null>(
|
const [selectedPresetId, setSelectedPresetId] = useState<string | null>(
|
||||||
@@ -220,6 +221,15 @@ export function ProviderForm({
|
|||||||
[originalHandleCodexConfigChange, debouncedValidate],
|
[originalHandleCodexConfigChange, debouncedValidate],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Codex 新建模式:初始化时自动填充模板(支持国际化)
|
||||||
|
useEffect(() => {
|
||||||
|
if (appId === "codex" && !initialData && selectedPresetId === "custom") {
|
||||||
|
const locale = (i18n.language || "zh").startsWith("zh") ? "zh" : "en";
|
||||||
|
const template = getCodexCustomTemplate(locale);
|
||||||
|
resetCodexConfig(template.auth, template.config);
|
||||||
|
}
|
||||||
|
}, [appId, initialData, selectedPresetId, resetCodexConfig, i18n.language]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.reset(defaultValues);
|
form.reset(defaultValues);
|
||||||
}, [defaultValues, form]);
|
}, [defaultValues, form]);
|
||||||
@@ -525,9 +535,11 @@ export function ProviderForm({
|
|||||||
setActivePreset(null);
|
setActivePreset(null);
|
||||||
form.reset(defaultValues);
|
form.reset(defaultValues);
|
||||||
|
|
||||||
// Codex 自定义模式:重置为空配置
|
// Codex 自定义模式:加载模板(支持国际化)
|
||||||
if (appId === "codex") {
|
if (appId === "codex") {
|
||||||
resetCodexConfig({}, "");
|
const locale = (i18n.language || "zh").startsWith("zh") ? "zh" : "en";
|
||||||
|
const template = getCodexCustomTemplate(locale);
|
||||||
|
resetCodexConfig(template.auth, template.config);
|
||||||
}
|
}
|
||||||
// Gemini 自定义模式:重置为空配置
|
// Gemini 自定义模式:重置为空配置
|
||||||
if (appId === "gemini") {
|
if (appId === "gemini") {
|
||||||
|
|||||||
@@ -98,52 +98,6 @@ requires_openai_auth = true`,
|
|||||||
textColor: "#FFFFFF",
|
textColor: "#FFFFFF",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Custom (Blank Template)",
|
|
||||||
websiteUrl: "https://docs.anthropic.com",
|
|
||||||
category: "third_party",
|
|
||||||
isCustomTemplate: true,
|
|
||||||
auth: generateThirdPartyAuth(""),
|
|
||||||
config: `# ========================================
|
|
||||||
# Codex 自定义供应商配置模板
|
|
||||||
# ========================================
|
|
||||||
# 快速上手:
|
|
||||||
# 1. 在上方 auth.json 中设置 API Key
|
|
||||||
# 2. 将下方 'custom' 替换为供应商名称(小写、无空格)
|
|
||||||
# 3. 替换 base_url 为实际的 API 端点
|
|
||||||
# 4. 根据需要调整模型名称
|
|
||||||
#
|
|
||||||
# 文档: https://docs.anthropic.com
|
|
||||||
# ========================================
|
|
||||||
|
|
||||||
# ========== 模型配置 ==========
|
|
||||||
model_provider = "custom" # 供应商唯一标识
|
|
||||||
model = "gpt-5-codex" # 模型名称
|
|
||||||
model_reasoning_effort = "high" # 推理强度:low, medium, high
|
|
||||||
disable_response_storage = true # 隐私:不本地存储响应
|
|
||||||
|
|
||||||
# ========== 供应商设置 ==========
|
|
||||||
[model_providers.custom]
|
|
||||||
name = "custom" # 与上方 model_provider 保持一致
|
|
||||||
base_url = "https://api.example.com/v1" # 👈 替换为实际端点
|
|
||||||
wire_api = "responses" # API 响应格式
|
|
||||||
requires_openai_auth = true # 使用 auth.json 中的 OPENAI_API_KEY
|
|
||||||
|
|
||||||
# ========== 可选:自定义请求头 ==========
|
|
||||||
# 如果供应商需要自定义请求头,取消注释:
|
|
||||||
# [model_providers.custom.headers]
|
|
||||||
# X-Custom-Header = "value"
|
|
||||||
|
|
||||||
# ========== 可选:模型覆盖 ==========
|
|
||||||
# 如果需要覆盖特定模型,取消注释:
|
|
||||||
# [model_overrides]
|
|
||||||
# "gpt-5-codex" = { model_provider = "custom", model = "your-model-name" }`,
|
|
||||||
theme: {
|
|
||||||
icon: "generic",
|
|
||||||
backgroundColor: "#6B7280", // gray-500
|
|
||||||
textColor: "#FFFFFF",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "AiHubMix",
|
name: "AiHubMix",
|
||||||
websiteUrl: "https://aihubmix.com",
|
websiteUrl: "https://aihubmix.com",
|
||||||
|
|||||||
95
src/config/codexTemplates.ts
Normal file
95
src/config/codexTemplates.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* Codex 配置模板
|
||||||
|
* 用于新建自定义供应商时的默认配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface CodexTemplate {
|
||||||
|
auth: Record<string, any>;
|
||||||
|
config: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 Codex 自定义模板
|
||||||
|
* @param locale 语言环境 ('zh' | 'en')
|
||||||
|
* @returns Codex 模板配置
|
||||||
|
*/
|
||||||
|
export function getCodexCustomTemplate(
|
||||||
|
locale: "zh" | "en" = "zh",
|
||||||
|
): CodexTemplate {
|
||||||
|
const templates = {
|
||||||
|
zh: `# ========================================
|
||||||
|
# Codex 自定义供应商配置模板
|
||||||
|
# ========================================
|
||||||
|
# 快速上手:
|
||||||
|
# 1. 在上方 auth.json 中设置 API Key
|
||||||
|
# 2. 将下方 'custom' 替换为供应商名称(小写、无空格)
|
||||||
|
# 3. 替换 base_url 为实际的 API 端点
|
||||||
|
# 4. 根据需要调整模型名称
|
||||||
|
#
|
||||||
|
# 文档: https://docs.anthropic.com
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
# ========== 模型配置 ==========
|
||||||
|
model_provider = "custom" # 供应商唯一标识
|
||||||
|
model = "gpt-5-codex" # 模型名称
|
||||||
|
model_reasoning_effort = "high" # 推理强度:low, medium, high
|
||||||
|
disable_response_storage = true # 隐私:不本地存储响应
|
||||||
|
|
||||||
|
# ========== 供应商设置 ==========
|
||||||
|
[model_providers.custom]
|
||||||
|
name = "custom" # 与上方 model_provider 保持一致
|
||||||
|
base_url = "https://api.example.com/v1" # 👈 替换为实际端点
|
||||||
|
wire_api = "responses" # API 响应格式
|
||||||
|
requires_openai_auth = true # 使用 auth.json 中的 OPENAI_API_KEY
|
||||||
|
|
||||||
|
# ========== 可选:自定义请求头 ==========
|
||||||
|
# 如果供应商需要自定义请求头,取消注释:
|
||||||
|
# [model_providers.custom.headers]
|
||||||
|
# X-Custom-Header = "value"
|
||||||
|
|
||||||
|
# ========== 可选:模型覆盖 ==========
|
||||||
|
# 如果需要覆盖特定模型,取消注释:
|
||||||
|
# [model_overrides]
|
||||||
|
# "gpt-5-codex" = { model_provider = "custom", model = "your-model-name" }`,
|
||||||
|
|
||||||
|
en: `# ========================================
|
||||||
|
# Codex Custom Provider Configuration Template
|
||||||
|
# ========================================
|
||||||
|
# Quick Start:
|
||||||
|
# 1. Set API Key in auth.json above
|
||||||
|
# 2. Replace 'custom' below with provider name (lowercase, no spaces)
|
||||||
|
# 3. Replace base_url with actual API endpoint
|
||||||
|
# 4. Adjust model name as needed
|
||||||
|
#
|
||||||
|
# Docs: https://docs.anthropic.com
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
# ========== Model Configuration ==========
|
||||||
|
model_provider = "custom" # Unique provider identifier
|
||||||
|
model = "gpt-5-codex" # Model name
|
||||||
|
model_reasoning_effort = "high" # Reasoning effort: low, medium, high
|
||||||
|
disable_response_storage = true # Privacy: do not store responses locally
|
||||||
|
|
||||||
|
# ========== Provider Settings ==========
|
||||||
|
[model_providers.custom]
|
||||||
|
name = "custom" # Must match model_provider above
|
||||||
|
base_url = "https://api.example.com/v1" # 👈 Replace with actual endpoint
|
||||||
|
wire_api = "responses" # API response format
|
||||||
|
requires_openai_auth = true # Use OPENAI_API_KEY from auth.json
|
||||||
|
|
||||||
|
# ========== Optional: Custom Headers ==========
|
||||||
|
# If provider requires custom headers, uncomment:
|
||||||
|
# [model_providers.custom.headers]
|
||||||
|
# X-Custom-Header = "value"
|
||||||
|
|
||||||
|
# ========== Optional: Model Overrides ==========
|
||||||
|
# If you need to override specific models, uncomment:
|
||||||
|
# [model_overrides]
|
||||||
|
# "gpt-5-codex" = { model_provider = "custom", model = "your-model-name" }`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
auth: { OPENAI_API_KEY: "" },
|
||||||
|
config: templates[locale] || templates.zh,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user