refactor: improve error handling and code formatting
- Enhanced error messages in Rust backend to include file paths - Improved provider switching error handling with detailed messages - Added MCP button placeholder in UI (functionality TODO) - Applied code formatting across frontend components - Extended error notification duration to 6s for better readability
This commit is contained in:
@@ -42,11 +42,11 @@ const collectTemplatePaths = (
|
||||
source: unknown,
|
||||
templateKeys: string[],
|
||||
currentPath: TemplatePath = [],
|
||||
acc: TemplatePath[] = []
|
||||
acc: TemplatePath[] = [],
|
||||
): TemplatePath[] => {
|
||||
if (typeof source === "string") {
|
||||
const hasPlaceholder = templateKeys.some((key) =>
|
||||
source.includes(`\${${key}}`)
|
||||
source.includes(`\${${key}}`),
|
||||
);
|
||||
if (hasPlaceholder) {
|
||||
acc.push([...currentPath]);
|
||||
@@ -56,14 +56,14 @@ const collectTemplatePaths = (
|
||||
|
||||
if (Array.isArray(source)) {
|
||||
source.forEach((item, index) =>
|
||||
collectTemplatePaths(item, templateKeys, [...currentPath, index], acc)
|
||||
collectTemplatePaths(item, templateKeys, [...currentPath, index], acc),
|
||||
);
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (source && typeof source === "object") {
|
||||
Object.entries(source).forEach(([key, value]) =>
|
||||
collectTemplatePaths(value, templateKeys, [...currentPath, key], acc)
|
||||
collectTemplatePaths(value, templateKeys, [...currentPath, key], acc),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ const getValueAtPath = (source: any, path: TemplatePath) => {
|
||||
const setValueAtPath = (
|
||||
target: any,
|
||||
path: TemplatePath,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): any => {
|
||||
if (path.length === 0) {
|
||||
return value;
|
||||
@@ -120,7 +120,7 @@ const setValueAtPath = (
|
||||
const applyTemplateValuesToConfigString = (
|
||||
presetConfig: any,
|
||||
currentConfigString: string,
|
||||
values: TemplateValueMap
|
||||
values: TemplateValueMap,
|
||||
) => {
|
||||
const replacedConfig = applyTemplateValues(presetConfig, values);
|
||||
const templateKeys = Object.keys(values);
|
||||
@@ -203,7 +203,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
: "",
|
||||
});
|
||||
const [category, setCategory] = useState<ProviderCategory | undefined>(
|
||||
initialData?.category
|
||||
initialData?.category,
|
||||
);
|
||||
|
||||
// Claude 模型配置状态
|
||||
@@ -224,7 +224,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
useState(false);
|
||||
// 新建供应商:收集端点测速弹窗中的“自定义端点”,提交时一次性落盘到 meta.custom_endpoints
|
||||
const [draftCustomEndpoints, setDraftCustomEndpoints] = useState<string[]>(
|
||||
[]
|
||||
[],
|
||||
);
|
||||
// 端点测速弹窗状态
|
||||
const [isEndpointModalOpen, setIsEndpointModalOpen] = useState(false);
|
||||
@@ -232,7 +232,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
useState(false);
|
||||
// -1 表示自定义,null 表示未选择,>= 0 表示预设索引
|
||||
const [selectedCodexPreset, setSelectedCodexPreset] = useState<number | null>(
|
||||
showPresets && isCodex ? -1 : null
|
||||
showPresets && isCodex ? -1 : null,
|
||||
);
|
||||
|
||||
const setCodexAuth = (value: string) => {
|
||||
@@ -244,7 +244,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
setCodexConfigState((prev) =>
|
||||
typeof value === "function"
|
||||
? (value as (input: string) => string)(prev)
|
||||
: value
|
||||
: value,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -305,7 +305,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
}
|
||||
try {
|
||||
const stored = window.localStorage.getItem(
|
||||
CODEX_COMMON_CONFIG_STORAGE_KEY
|
||||
CODEX_COMMON_CONFIG_STORAGE_KEY,
|
||||
);
|
||||
if (stored && stored.trim()) {
|
||||
return stored.trim();
|
||||
@@ -322,7 +322,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
|
||||
// -1 表示自定义,null 表示未选择,>= 0 表示预设索引
|
||||
const [selectedPreset, setSelectedPreset] = useState<number | null>(
|
||||
showPresets ? -1 : null
|
||||
showPresets ? -1 : null,
|
||||
);
|
||||
const [apiKey, setApiKey] = useState("");
|
||||
const [codexAuthError, setCodexAuthError] = useState("");
|
||||
@@ -390,11 +390,11 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const configString = JSON.stringify(
|
||||
initialData.settingsConfig,
|
||||
null,
|
||||
2
|
||||
2,
|
||||
);
|
||||
const hasCommon = hasCommonConfigSnippet(
|
||||
configString,
|
||||
commonConfigSnippet
|
||||
commonConfigSnippet,
|
||||
);
|
||||
setUseCommonConfig(hasCommon);
|
||||
setSettingsConfigError(validateSettingsConfig(configString));
|
||||
@@ -410,14 +410,14 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
if (config.env) {
|
||||
setClaudeModel(config.env.ANTHROPIC_MODEL || "");
|
||||
setClaudeSmallFastModel(
|
||||
config.env.ANTHROPIC_SMALL_FAST_MODEL || ""
|
||||
config.env.ANTHROPIC_SMALL_FAST_MODEL || "",
|
||||
);
|
||||
setBaseUrl(config.env.ANTHROPIC_BASE_URL || ""); // 初始化基础 URL
|
||||
|
||||
// 初始化 Kimi 模型选择
|
||||
setKimiAnthropicModel(config.env.ANTHROPIC_MODEL || "");
|
||||
setKimiAnthropicSmallFastModel(
|
||||
config.env.ANTHROPIC_SMALL_FAST_MODEL || ""
|
||||
config.env.ANTHROPIC_SMALL_FAST_MODEL || "",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -425,7 +425,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
// Codex 初始化时检查 TOML 通用配置
|
||||
const hasCommon = hasTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
codexCommonConfigSnippet
|
||||
codexCommonConfigSnippet,
|
||||
);
|
||||
setUseCodexCommonConfig(hasCommon);
|
||||
}
|
||||
@@ -445,7 +445,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
if (selectedPreset !== null && selectedPreset >= 0) {
|
||||
const preset = providerPresets[selectedPreset];
|
||||
setCategory(
|
||||
preset?.category || (preset?.isOfficial ? "official" : undefined)
|
||||
preset?.category || (preset?.isOfficial ? "official" : undefined),
|
||||
);
|
||||
} else if (selectedPreset === -1) {
|
||||
setCategory("custom");
|
||||
@@ -454,7 +454,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
if (selectedCodexPreset !== null && selectedCodexPreset >= 0) {
|
||||
const preset = codexProviderPresets[selectedCodexPreset];
|
||||
setCategory(
|
||||
preset?.category || (preset?.isOfficial ? "official" : undefined)
|
||||
preset?.category || (preset?.isOfficial ? "official" : undefined),
|
||||
);
|
||||
} else if (selectedCodexPreset === -1) {
|
||||
setCategory("custom");
|
||||
@@ -506,7 +506,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
if (commonConfigSnippet.trim()) {
|
||||
window.localStorage.setItem(
|
||||
COMMON_CONFIG_STORAGE_KEY,
|
||||
commonConfigSnippet
|
||||
commonConfigSnippet,
|
||||
);
|
||||
} else {
|
||||
window.localStorage.removeItem(COMMON_CONFIG_STORAGE_KEY);
|
||||
@@ -569,7 +569,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
}
|
||||
} else {
|
||||
const currentSettingsError = validateSettingsConfig(
|
||||
formData.settingsConfig
|
||||
formData.settingsConfig,
|
||||
);
|
||||
setSettingsConfigError(currentSettingsError);
|
||||
if (currentSettingsError) {
|
||||
@@ -634,7 +634,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
};
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const { name, value } = e.target;
|
||||
|
||||
@@ -664,7 +664,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const { updatedConfig, error: snippetError } = updateCommonConfigSnippet(
|
||||
formData.settingsConfig,
|
||||
commonConfigSnippet,
|
||||
checked
|
||||
checked,
|
||||
);
|
||||
|
||||
if (snippetError) {
|
||||
@@ -697,7 +697,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const { updatedConfig } = updateCommonConfigSnippet(
|
||||
formData.settingsConfig,
|
||||
previousSnippet,
|
||||
false
|
||||
false,
|
||||
);
|
||||
// 直接更新 formData,不通过 handleChange
|
||||
updateSettingsConfigValue(updatedConfig);
|
||||
@@ -719,7 +719,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const removeResult = updateCommonConfigSnippet(
|
||||
formData.settingsConfig,
|
||||
previousSnippet,
|
||||
false
|
||||
false,
|
||||
);
|
||||
if (removeResult.error) {
|
||||
setCommonConfigError(removeResult.error);
|
||||
@@ -731,7 +731,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const addResult = updateCommonConfigSnippet(
|
||||
removeResult.updatedConfig,
|
||||
value,
|
||||
true
|
||||
true,
|
||||
);
|
||||
|
||||
if (addResult.error) {
|
||||
@@ -775,11 +775,11 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
? config.editorValue
|
||||
: (config.defaultValue ?? ""),
|
||||
},
|
||||
])
|
||||
]),
|
||||
);
|
||||
appliedSettingsConfig = applyTemplateValues(
|
||||
preset.settingsConfig,
|
||||
initialTemplateValues
|
||||
initialTemplateValues,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -794,7 +794,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
});
|
||||
setSettingsConfigError(validateSettingsConfig(configString));
|
||||
setCategory(
|
||||
preset.category || (preset.isOfficial ? "official" : undefined)
|
||||
preset.category || (preset.isOfficial ? "official" : undefined),
|
||||
);
|
||||
|
||||
// 设置选中的预设
|
||||
@@ -824,7 +824,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
if (preset.name?.includes("Kimi")) {
|
||||
setKimiAnthropicModel(config.env.ANTHROPIC_MODEL || "");
|
||||
setKimiAnthropicSmallFastModel(
|
||||
config.env.ANTHROPIC_SMALL_FAST_MODEL || ""
|
||||
config.env.ANTHROPIC_SMALL_FAST_MODEL || "",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -872,7 +872,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
// Codex: 应用预设
|
||||
const applyCodexPreset = (
|
||||
preset: (typeof codexProviderPresets)[0],
|
||||
index: number
|
||||
index: number,
|
||||
) => {
|
||||
const authString = JSON.stringify(preset.auth || {}, null, 2);
|
||||
setCodexAuth(authString);
|
||||
@@ -890,7 +890,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
|
||||
setSelectedCodexPreset(index);
|
||||
setCategory(
|
||||
preset.category || (preset.isOfficial ? "official" : undefined)
|
||||
preset.category || (preset.isOfficial ? "official" : undefined),
|
||||
);
|
||||
|
||||
// 清空 API Key,让用户重新输入
|
||||
@@ -906,7 +906,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const customConfig = generateThirdPartyConfig(
|
||||
"custom",
|
||||
"https://your-api-endpoint.com/v1",
|
||||
"gpt-5-codex"
|
||||
"gpt-5-codex",
|
||||
);
|
||||
|
||||
setFormData({
|
||||
@@ -929,7 +929,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const configString = setApiKeyInConfig(
|
||||
formData.settingsConfig,
|
||||
key.trim(),
|
||||
{ createIfMissing: selectedPreset !== null && selectedPreset !== -1 }
|
||||
{ createIfMissing: selectedPreset !== null && selectedPreset !== -1 },
|
||||
);
|
||||
|
||||
// 更新表单配置
|
||||
@@ -1025,7 +1025,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const { updatedConfig } = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
previousSnippet,
|
||||
false
|
||||
false,
|
||||
);
|
||||
setCodexConfig(updatedConfig);
|
||||
setUseCodexCommonConfig(false);
|
||||
@@ -1038,12 +1038,12 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const removeResult = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
previousSnippet,
|
||||
false
|
||||
false,
|
||||
);
|
||||
const addResult = updateTomlCommonConfigSnippet(
|
||||
removeResult.updatedConfig,
|
||||
sanitizedValue,
|
||||
true
|
||||
true,
|
||||
);
|
||||
|
||||
if (addResult.error) {
|
||||
@@ -1065,7 +1065,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
try {
|
||||
window.localStorage.setItem(
|
||||
CODEX_COMMON_CONFIG_STORAGE_KEY,
|
||||
sanitizedValue
|
||||
sanitizedValue,
|
||||
);
|
||||
} catch {
|
||||
// ignore localStorage 写入失败
|
||||
@@ -1078,7 +1078,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
if (!isUpdatingFromCodexCommonConfig.current) {
|
||||
const hasCommon = hasTomlCommonConfigSnippet(
|
||||
value,
|
||||
codexCommonConfigSnippet
|
||||
codexCommonConfigSnippet,
|
||||
);
|
||||
setUseCodexCommonConfig(hasCommon);
|
||||
}
|
||||
@@ -1306,7 +1306,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
// 处理模型输入变化,自动更新 JSON 配置
|
||||
const handleModelChange = (
|
||||
field: "ANTHROPIC_MODEL" | "ANTHROPIC_SMALL_FAST_MODEL",
|
||||
value: string
|
||||
value: string,
|
||||
) => {
|
||||
if (field === "ANTHROPIC_MODEL") {
|
||||
setClaudeModel(value);
|
||||
@@ -1336,7 +1336,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
// Kimi 模型选择处理函数
|
||||
const handleKimiModelChange = (
|
||||
field: "ANTHROPIC_MODEL" | "ANTHROPIC_SMALL_FAST_MODEL",
|
||||
value: string
|
||||
value: string,
|
||||
) => {
|
||||
if (field === "ANTHROPIC_MODEL") {
|
||||
setKimiAnthropicModel(value);
|
||||
@@ -1361,7 +1361,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
useEffect(() => {
|
||||
if (!initialData) return;
|
||||
const parsedKey = getApiKeyFromConfig(
|
||||
JSON.stringify(initialData.settingsConfig)
|
||||
JSON.stringify(initialData.settingsConfig),
|
||||
);
|
||||
if (parsedKey) setApiKey(parsedKey);
|
||||
}, [initialData]);
|
||||
@@ -1544,7 +1544,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
templateValueEntries.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||
{t("providerForm.parameterConfig", { name: selectedTemplatePreset.name.trim() })}
|
||||
{t("providerForm.parameterConfig", {
|
||||
name: selectedTemplatePreset.name.trim(),
|
||||
})}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{templateValueEntries.map(([key, config]) => (
|
||||
@@ -1583,14 +1585,14 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
applyTemplateValuesToConfigString(
|
||||
selectedTemplatePreset.settingsConfig,
|
||||
formData.settingsConfig,
|
||||
nextValues
|
||||
nextValues,
|
||||
);
|
||||
setFormData((prevForm) => ({
|
||||
...prevForm,
|
||||
settingsConfig: configString,
|
||||
}));
|
||||
setSettingsConfigError(
|
||||
validateSettingsConfig(configString)
|
||||
validateSettingsConfig(configString),
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("更新模板值失败:", err);
|
||||
@@ -1830,7 +1832,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
onChange={(e) =>
|
||||
handleModelChange(
|
||||
"ANTHROPIC_SMALL_FAST_MODEL",
|
||||
e.target.value
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
placeholder={t("providerForm.fastModelPlaceholder")}
|
||||
|
||||
Reference in New Issue
Block a user