refactor: extract Kimi model selector logic to dedicated hook

- Create useKimiModelSelector hook to manage Kimi-specific state
- Auto-detect Kimi providers by preset name or config content
- Support model initialization from existing config in edit mode
- Sync model selections with JSON configuration
- Maintain clean separation between UI and business logic
This commit is contained in:
Jason
2025-10-16 20:21:42 +08:00
parent 6541c14421
commit e4f85f4f65
3 changed files with 317 additions and 133 deletions

View File

@@ -26,6 +26,7 @@ import { applyTemplateValues } from "@/utils/providerConfigUtils";
import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput"; import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput";
import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest"; import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest";
import CodexConfigEditor from "@/components/ProviderForm/CodexConfigEditor"; import CodexConfigEditor from "@/components/ProviderForm/CodexConfigEditor";
import KimiModelSelector from "@/components/ProviderForm/KimiModelSelector";
import { Zap } from "lucide-react"; import { Zap } from "lucide-react";
import { import {
useProviderCategory, useProviderCategory,
@@ -35,6 +36,7 @@ import {
useCodexConfigState, useCodexConfigState,
useApiKeyLink, useApiKeyLink,
useCustomEndpoints, useCustomEndpoints,
useKimiModelSelector,
} from "./hooks"; } from "./hooks";
const CLAUDE_DEFAULT_CONFIG = JSON.stringify({ env: {}, config: {} }, null, 2); const CLAUDE_DEFAULT_CONFIG = JSON.stringify({ env: {}, config: {} }, null, 2);
@@ -69,7 +71,7 @@ export function ProviderForm({
const isEditMode = Boolean(initialData); const isEditMode = Boolean(initialData);
const [selectedPresetId, setSelectedPresetId] = useState<string | null>( const [selectedPresetId, setSelectedPresetId] = useState<string | null>(
initialData ? null : "custom", initialData ? null : "custom"
); );
const [activePreset, setActivePreset] = useState<{ const [activePreset, setActivePreset] = useState<{
id: string; id: string;
@@ -78,7 +80,9 @@ export function ProviderForm({
const [isEndpointModalOpen, setIsEndpointModalOpen] = useState(false); const [isEndpointModalOpen, setIsEndpointModalOpen] = useState(false);
// 新建供应商:收集端点测速弹窗中的"自定义端点",提交时一次性落盘到 meta.custom_endpoints // 新建供应商:收集端点测速弹窗中的"自定义端点",提交时一次性落盘到 meta.custom_endpoints
const [draftCustomEndpoints, setDraftCustomEndpoints] = useState<string[]>([]); const [draftCustomEndpoints, setDraftCustomEndpoints] = useState<string[]>(
[]
);
// 使用 category hook // 使用 category hook
const { category } = useProviderCategory({ const { category } = useProviderCategory({
@@ -102,7 +106,7 @@ export function ProviderForm({
? CODEX_DEFAULT_CONFIG ? CODEX_DEFAULT_CONFIG
: CLAUDE_DEFAULT_CONFIG, : CLAUDE_DEFAULT_CONFIG,
}), }),
[initialData, appType], [initialData, appType]
); );
const form = useForm<ProviderFormData>({ const form = useForm<ProviderFormData>({
@@ -112,7 +116,11 @@ export function ProviderForm({
}); });
// 使用 API Key hook // 使用 API Key hook
const { apiKey, handleApiKeyChange, showApiKey: shouldShowApiKey } = useApiKeyState({ const {
apiKey,
handleApiKeyChange,
showApiKey: shouldShowApiKey,
} = useApiKeyState({
initialConfig: form.watch("settingsConfig"), initialConfig: form.watch("settingsConfig"),
onConfigChange: (config) => form.setValue("settingsConfig", config), onConfigChange: (config) => form.setValue("settingsConfig", config),
selectedPresetId, selectedPresetId,
@@ -136,10 +144,11 @@ export function ProviderForm({
}); });
// 使用 Model hook // 使用 Model hook
const { claudeModel, claudeSmallFastModel, handleModelChange } = useModelState({ const { claudeModel, claudeSmallFastModel, handleModelChange } =
settingsConfig: form.watch("settingsConfig"), useModelState({
onConfigChange: (config) => form.setValue("settingsConfig", config), settingsConfig: form.watch("settingsConfig"),
}); onConfigChange: (config) => form.setValue("settingsConfig", config),
});
// 使用 Codex 配置 hook (仅 Codex 模式) // 使用 Codex 配置 hook (仅 Codex 模式)
const { const {
@@ -155,7 +164,8 @@ export function ProviderForm({
resetCodexConfig, resetCodexConfig,
} = useCodexConfigState({ initialData }); } = useCodexConfigState({ initialData });
const [isCodexEndpointModalOpen, setIsCodexEndpointModalOpen] = useState(false); const [isCodexEndpointModalOpen, setIsCodexEndpointModalOpen] =
useState(false);
useEffect(() => { useEffect(() => {
form.reset(defaultValues); form.reset(defaultValues);
@@ -169,6 +179,55 @@ export function ProviderForm({
: false; : false;
}, [theme]); }, [theme]);
const presetCategoryLabels: Record<string, string> = useMemo(
() => ({
official: t("providerPreset.categoryOfficial", {
defaultValue: "官方",
}),
cn_official: t("providerPreset.categoryCnOfficial", {
defaultValue: "国内官方",
}),
aggregator: t("providerPreset.categoryAggregator", {
defaultValue: "聚合服务",
}),
third_party: t("providerPreset.categoryThirdParty", {
defaultValue: "第三方",
}),
}),
[t]
);
const presetEntries = useMemo(() => {
if (appType === "codex") {
return codexProviderPresets.map<PresetEntry>((preset, index) => ({
id: `codex-${index}`,
preset,
}));
}
return providerPresets.map<PresetEntry>((preset, index) => ({
id: `claude-${index}`,
preset,
}));
}, [appType]);
// 使用 Kimi 模型选择器 hook
const {
shouldShow: shouldShowKimiSelector,
kimiAnthropicModel,
kimiAnthropicSmallFastModel,
handleKimiModelChange,
} = useKimiModelSelector({
initialData,
settingsConfig: form.watch("settingsConfig"),
onConfigChange: (config) => form.setValue("settingsConfig", config),
selectedPresetId,
presetName:
selectedPresetId && selectedPresetId !== "custom"
? presetEntries.find((item) => item.id === selectedPresetId)?.preset
.name || ""
: "",
});
const handleSubmit = (values: ProviderFormData) => { const handleSubmit = (values: ProviderFormData) => {
let settingsConfig: string; let settingsConfig: string;
@@ -212,37 +271,6 @@ export function ProviderForm({
onSubmit(payload); onSubmit(payload);
}; };
const presetCategoryLabels: Record<string, string> = useMemo(
() => ({
official: t("providerPreset.categoryOfficial", {
defaultValue: "官方推荐",
}),
cn_official: t("providerPreset.categoryCnOfficial", {
defaultValue: "国内官方",
}),
aggregator: t("providerPreset.categoryAggregator", {
defaultValue: "聚合服务",
}),
third_party: t("providerPreset.categoryThirdParty", {
defaultValue: "第三方",
}),
}),
[t],
);
const presetEntries = useMemo(() => {
if (appType === "codex") {
return codexProviderPresets.map<PresetEntry>((preset, index) => ({
id: `codex-${index}`,
preset,
}));
}
return providerPresets.map<PresetEntry>((preset, index) => ({
id: `claude-${index}`,
preset,
}));
}, [appType]);
const groupedPresets = useMemo(() => { const groupedPresets = useMemo(() => {
return presetEntries.reduce<Record<string, PresetEntry[]>>((acc, entry) => { return presetEntries.reduce<Record<string, PresetEntry[]>>((acc, entry) => {
const category = entry.preset.category ?? "others"; const category = entry.preset.category ?? "others";
@@ -256,7 +284,7 @@ export function ProviderForm({
const categoryKeys = useMemo(() => { const categoryKeys = useMemo(() => {
return Object.keys(groupedPresets).filter( return Object.keys(groupedPresets).filter(
(key) => key !== "custom" && groupedPresets[key]?.length, (key) => key !== "custom" && groupedPresets[key]?.length
); );
}, [groupedPresets]); }, [groupedPresets]);
@@ -341,7 +369,7 @@ export function ProviderForm({
const preset = entry.preset as ProviderPreset; const preset = entry.preset as ProviderPreset;
const config = applyTemplateValues( const config = applyTemplateValues(
preset.settingsConfig, preset.settingsConfig,
preset.templateValues, preset.templateValues
); );
form.reset({ form.reset({
@@ -446,34 +474,41 @@ export function ProviderForm({
/> />
{/* API Key 输入框(仅 Claude 且非编辑模式显示) */} {/* API Key 输入框(仅 Claude 且非编辑模式显示) */}
{appType === "claude" && shouldShowApiKey(form.watch("settingsConfig"), isEditMode) && ( {appType === "claude" &&
<div className="space-y-1"> shouldShowApiKey(form.watch("settingsConfig"), isEditMode) && (
<ApiKeyInput <div className="space-y-1">
value={apiKey} <ApiKeyInput
onChange={handleApiKeyChange} value={apiKey}
required={category !== "official"} onChange={handleApiKeyChange}
placeholder={ required={category !== "official"}
category === "official" placeholder={
? t("providerForm.officialNoApiKey", { defaultValue: "官方供应商无需 API Key" }) category === "official"
: t("providerForm.apiKeyAutoFill", { defaultValue: "输入 API Key将自动填充到配置" }) ? t("providerForm.officialNoApiKey", {
} defaultValue: "官方供应商无需 API Key",
disabled={category === "official"} })
/> : t("providerForm.apiKeyAutoFill", {
{/* API Key 获取链接 */} defaultValue: "输入 API Key将自动填充到配置",
{shouldShowClaudeApiKeyLink && claudeWebsiteUrl && ( })
<div className="-mt-1 pl-1"> }
<a disabled={category === "official"}
href={claudeWebsiteUrl} />
target="_blank" {/* API Key 获取链接 */}
rel="noopener noreferrer" {shouldShowClaudeApiKeyLink && claudeWebsiteUrl && (
className="text-xs text-blue-400 dark:text-blue-500 hover:text-blue-500 dark:hover:text-blue-400 transition-colors" <div className="-mt-1 pl-1">
> <a
{t("providerForm.getApiKey", { defaultValue: "获取 API Key" })} href={claudeWebsiteUrl}
</a> target="_blank"
</div> rel="noopener noreferrer"
)} className="text-xs text-blue-400 dark:text-blue-500 hover:text-blue-500 dark:hover:text-blue-400 transition-colors"
</div> >
)} {t("providerForm.getApiKey", {
defaultValue: "获取 API Key",
})}
</a>
</div>
)}
</div>
)}
{/* Base URL 输入框(仅 Claude 第三方/自定义显示) */} {/* Base URL 输入框(仅 Claude 第三方/自定义显示) */}
{appType === "claude" && shouldShowSpeedTest && ( {appType === "claude" && shouldShowSpeedTest && (
@@ -488,7 +523,9 @@ export function ProviderForm({
className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors" className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
> >
<Zap className="h-3.5 w-3.5" /> <Zap className="h-3.5 w-3.5" />
{t("providerForm.manageAndTest", { defaultValue: "管理和测速" })} {t("providerForm.manageAndTest", {
defaultValue: "管理和测速",
})}
</button> </button>
</div> </div>
<Input <Input
@@ -496,12 +533,16 @@ export function ProviderForm({
type="url" type="url"
value={baseUrl} value={baseUrl}
onChange={(e) => handleClaudeBaseUrlChange(e.target.value)} onChange={(e) => handleClaudeBaseUrlChange(e.target.value)}
placeholder={t("providerForm.apiEndpointPlaceholder", { defaultValue: "https://api.example.com" })} placeholder={t("providerForm.apiEndpointPlaceholder", {
defaultValue: "https://api.example.com",
})}
autoComplete="off" autoComplete="off"
/> />
<div className="p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700 rounded-lg"> <div className="p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700 rounded-lg">
<p className="text-xs text-amber-600 dark:text-amber-400"> <p className="text-xs text-amber-600 dark:text-amber-400">
{t("providerForm.apiHint", { defaultValue: "API 端点地址用于连接服务器" })} {t("providerForm.apiHint", {
defaultValue: "API 端点地址用于连接服务器",
})}
</p> </p>
</div> </div>
</div> </div>
@@ -520,52 +561,75 @@ export function ProviderForm({
/> />
)} )}
{/* 模型选择器(仅 Claude 非官方供应商显示) */} {/* 模型选择器(仅 Claude 非官方且非 Kimi 供应商显示) */}
{appType === "claude" && category !== "official" && ( {appType === "claude" &&
<div className="space-y-3"> category !== "official" &&
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> !shouldShowKimiSelector && (
{/* ANTHROPIC_MODEL */} <div className="space-y-3">
<div className="space-y-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormLabel htmlFor="claudeModel"> {/* ANTHROPIC_MODEL */}
{t("providerForm.anthropicModel", { defaultValue: "主模型" })} <div className="space-y-2">
</FormLabel> <FormLabel htmlFor="claudeModel">
<Input {t("providerForm.anthropicModel", {
id="claudeModel" defaultValue: "主模型",
type="text" })}
value={claudeModel} </FormLabel>
onChange={(e) => handleModelChange("ANTHROPIC_MODEL", e.target.value)} <Input
placeholder={t("providerForm.modelPlaceholder", { id="claudeModel"
defaultValue: "claude-3-7-sonnet-20250219" type="text"
})} value={claudeModel}
autoComplete="off" onChange={(e) =>
/> handleModelChange("ANTHROPIC_MODEL", e.target.value)
</div> }
placeholder={t("providerForm.modelPlaceholder", {
defaultValue: "claude-3-7-sonnet-20250219",
})}
autoComplete="off"
/>
</div>
{/* ANTHROPIC_SMALL_FAST_MODEL */} {/* ANTHROPIC_SMALL_FAST_MODEL */}
<div className="space-y-2"> <div className="space-y-2">
<FormLabel htmlFor="claudeSmallFastModel"> <FormLabel htmlFor="claudeSmallFastModel">
{t("providerForm.anthropicSmallFastModel", { {t("providerForm.anthropicSmallFastModel", {
defaultValue: "快速模型" defaultValue: "快速模型",
})} })}
</FormLabel> </FormLabel>
<Input <Input
id="claudeSmallFastModel" id="claudeSmallFastModel"
type="text" type="text"
value={claudeSmallFastModel} value={claudeSmallFastModel}
onChange={(e) => handleModelChange("ANTHROPIC_SMALL_FAST_MODEL", e.target.value)} onChange={(e) =>
placeholder={t("providerForm.smallModelPlaceholder", { handleModelChange(
defaultValue: "claude-3-5-haiku-20241022" "ANTHROPIC_SMALL_FAST_MODEL",
})} e.target.value
autoComplete="off" )
/> }
placeholder={t("providerForm.smallModelPlaceholder", {
defaultValue: "claude-3-5-haiku-20241022",
})}
autoComplete="off"
/>
</div>
</div> </div>
<p className="text-xs text-muted-foreground">
{t("providerForm.modelHelper", {
defaultValue:
"可选:指定默认使用的 Claude 模型,留空则使用系统默认。",
})}
</p>
</div> </div>
<p className="text-xs text-muted-foreground"> )}
{t("providerForm.modelHelper", {
defaultValue: "可选:指定默认使用的 Claude 模型,留空则使用系统默认。", {/* Kimi 模型选择器(仅 Claude 且是 Kimi 供应商时显示) */}
})} {appType === "claude" && shouldShowKimiSelector && (
</p> <KimiModelSelector
</div> apiKey={apiKey}
anthropicModel={kimiAnthropicModel}
anthropicSmallFastModel={kimiAnthropicSmallFastModel}
onModelChange={handleKimiModelChange}
disabled={category === "official"}
/>
)} )}
{/* Codex API Key 输入框 */} {/* Codex API Key 输入框 */}
@@ -579,8 +643,12 @@ export function ProviderForm({
required={category !== "official"} required={category !== "official"}
placeholder={ placeholder={
category === "official" category === "official"
? t("providerForm.codexOfficialNoApiKey", { defaultValue: "官方供应商无需 API Key" }) ? t("providerForm.codexOfficialNoApiKey", {
: t("providerForm.codexApiKeyAutoFill", { defaultValue: "输入 API Key,将自动填充到配置" }) defaultValue: "官方供应商无需 API Key",
})
: t("providerForm.codexApiKeyAutoFill", {
defaultValue: "输入 API Key将自动填充到配置",
})
} }
disabled={category === "official"} disabled={category === "official"}
/> />
@@ -593,7 +661,9 @@ export function ProviderForm({
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-xs text-blue-400 dark:text-blue-500 hover:text-blue-500 dark:hover:text-blue-400 transition-colors" className="text-xs text-blue-400 dark:text-blue-500 hover:text-blue-500 dark:hover:text-blue-400 transition-colors"
> >
{t("providerForm.getApiKey", { defaultValue: "获取 API Key" })} {t("providerForm.getApiKey", {
defaultValue: "获取 API Key",
})}
</a> </a>
</div> </div>
)} )}
@@ -613,7 +683,9 @@ export function ProviderForm({
className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors" className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
> >
<Zap className="h-3.5 w-3.5" /> <Zap className="h-3.5 w-3.5" />
{t("providerForm.manageAndTest", { defaultValue: "管理和测速" })} {t("providerForm.manageAndTest", {
defaultValue: "管理和测速",
})}
</button> </button>
</div> </div>
<Input <Input
@@ -621,29 +693,35 @@ export function ProviderForm({
type="url" type="url"
value={codexBaseUrl} value={codexBaseUrl}
onChange={(e) => handleCodexBaseUrlChange(e.target.value)} onChange={(e) => handleCodexBaseUrlChange(e.target.value)}
placeholder={t("providerForm.codexApiEndpointPlaceholder", { defaultValue: "https://api.example.com/v1" })} placeholder={t("providerForm.codexApiEndpointPlaceholder", {
defaultValue: "https://api.example.com/v1",
})}
autoComplete="off" autoComplete="off"
/> />
<div className="p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700 rounded-lg"> <div className="p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700 rounded-lg">
<p className="text-xs text-amber-600 dark:text-amber-400"> <p className="text-xs text-amber-600 dark:text-amber-400">
{t("providerForm.codexApiHint", { defaultValue: "Codex API 端点地址" })} {t("providerForm.codexApiHint", {
defaultValue: "Codex API 端点地址",
})}
</p> </p>
</div> </div>
</div> </div>
)} )}
{/* 端点测速弹窗 - Codex */} {/* 端点测速弹窗 - Codex */}
{appType === "codex" && shouldShowSpeedTest && isCodexEndpointModalOpen && ( {appType === "codex" &&
<EndpointSpeedTest shouldShowSpeedTest &&
appType={appType} isCodexEndpointModalOpen && (
value={codexBaseUrl} <EndpointSpeedTest
onChange={handleCodexBaseUrlChange} appType={appType}
initialEndpoints={[{ url: codexBaseUrl }]} value={codexBaseUrl}
visible={isCodexEndpointModalOpen} onChange={handleCodexBaseUrlChange}
onClose={() => setIsCodexEndpointModalOpen(false)} initialEndpoints={[{ url: codexBaseUrl }]}
onCustomEndpointsChange={setDraftCustomEndpoints} visible={isCodexEndpointModalOpen}
/> onClose={() => setIsCodexEndpointModalOpen(false)}
)} onCustomEndpointsChange={setDraftCustomEndpoints}
/>
)}
{/* 配置编辑器Claude 使用 JSON 编辑器Codex 使用专用编辑器 */} {/* 配置编辑器Claude 使用 JSON 编辑器Codex 使用专用编辑器 */}
{appType === "codex" ? ( {appType === "codex" ? (

View File

@@ -5,3 +5,4 @@ export { useModelState } from "./useModelState";
export { useCodexConfigState } from "./useCodexConfigState"; export { useCodexConfigState } from "./useCodexConfigState";
export { useApiKeyLink } from "./useApiKeyLink"; export { useApiKeyLink } from "./useApiKeyLink";
export { useCustomEndpoints } from "./useCustomEndpoints"; export { useCustomEndpoints } from "./useCustomEndpoints";
export { useKimiModelSelector } from "./useKimiModelSelector";

View File

@@ -0,0 +1,105 @@
import { useState, useEffect, useCallback } from "react";
interface UseKimiModelSelectorProps {
initialData?: {
settingsConfig?: Record<string, unknown>;
};
settingsConfig: string;
onConfigChange: (config: string) => void;
selectedPresetId: string | null;
presetName?: string;
}
/**
* 管理 Kimi 模型选择器的状态和逻辑
*/
export function useKimiModelSelector({
initialData,
settingsConfig,
onConfigChange,
selectedPresetId,
presetName = "",
}: UseKimiModelSelectorProps) {
const [kimiAnthropicModel, setKimiAnthropicModel] = useState("");
const [kimiAnthropicSmallFastModel, setKimiAnthropicSmallFastModel] = useState("");
// 判断是否显示 Kimi 模型选择器
const shouldShowKimiSelector =
selectedPresetId !== null &&
selectedPresetId !== "custom" &&
presetName.includes("Kimi");
// 判断是否正在编辑 Kimi 供应商
const isEditingKimi = Boolean(
initialData &&
(settingsConfig.includes("api.moonshot.cn") &&
settingsConfig.includes("ANTHROPIC_MODEL"))
);
const shouldShow = shouldShowKimiSelector || isEditingKimi;
// 初始化 Kimi 模型选择(编辑模式)
useEffect(() => {
if (initialData?.settingsConfig && typeof initialData.settingsConfig === "object") {
const config = initialData.settingsConfig as { env?: Record<string, unknown> };
if (config.env) {
const model = typeof config.env.ANTHROPIC_MODEL === "string"
? config.env.ANTHROPIC_MODEL
: "";
const smallFastModel = typeof config.env.ANTHROPIC_SMALL_FAST_MODEL === "string"
? config.env.ANTHROPIC_SMALL_FAST_MODEL
: "";
setKimiAnthropicModel(model);
setKimiAnthropicSmallFastModel(smallFastModel);
}
}
}, [initialData]);
// 处理 Kimi 模型变化
const handleKimiModelChange = useCallback(
(field: "ANTHROPIC_MODEL" | "ANTHROPIC_SMALL_FAST_MODEL", value: string) => {
if (field === "ANTHROPIC_MODEL") {
setKimiAnthropicModel(value);
} else {
setKimiAnthropicSmallFastModel(value);
}
// 更新配置 JSON
try {
const currentConfig = JSON.parse(settingsConfig || "{}");
if (!currentConfig.env) currentConfig.env = {};
currentConfig.env[field] = value;
const updatedConfigString = JSON.stringify(currentConfig, null, 2);
onConfigChange(updatedConfigString);
} catch (err) {
console.error("更新 Kimi 模型配置失败:", err);
}
},
[settingsConfig, onConfigChange],
);
// 当选择 Kimi 预设时,同步模型值
useEffect(() => {
if (shouldShowKimiSelector && settingsConfig) {
try {
const config = JSON.parse(settingsConfig);
if (config.env) {
const model = config.env.ANTHROPIC_MODEL || "";
const smallFastModel = config.env.ANTHROPIC_SMALL_FAST_MODEL || "";
setKimiAnthropicModel(model);
setKimiAnthropicSmallFastModel(smallFastModel);
}
} catch {
// ignore
}
}
}, [shouldShowKimiSelector, settingsConfig]);
return {
shouldShow,
kimiAnthropicModel,
kimiAnthropicSmallFastModel,
handleKimiModelChange,
};
}