feat: update provider/model config handling

This commit is contained in:
KimiSwitch Dev
2026-07-15 19:15:04 +08:00
parent a394e5f730
commit 211986a1e5
7 changed files with 182 additions and 67 deletions
+74 -8
View File
@@ -10,6 +10,30 @@ import type { Agent, Model, Provider } from "./types";
const AGENT_STORAGE_KEY = "pi-switch-agent";
function getProviderDefaultModel(provider: Provider): string | null {
const raw = provider.raw_other as Record<string, unknown> | undefined;
const value = raw?.default_model;
return typeof value === "string" ? value : null;
}
function setProviderDefaultModel(provider: Provider, alias: string | null): Provider {
const raw = (provider.raw_other as Record<string, unknown> | undefined) ?? {};
if (alias) {
return { ...provider, raw_other: { ...raw, default_model: alias } };
}
const { default_model: _, ...rest } = raw;
return { ...provider, raw_other: rest };
}
function findFirstModelForProvider(models: Record<string, Model>, providerName: string): string | null {
for (const [key, m] of Object.entries(models)) {
if (m.provider === providerName) {
return key;
}
}
return null;
}
const AGENTS: { key: Agent; label: string }[] = [
{ key: "kimi_code", label: "Kimi Code" },
{ key: "pi", label: "Pi" },
@@ -189,7 +213,17 @@ export default function App() {
};
const handleSetDefaultModel = (alias: string) => {
updateConfig((cfg) => ({ ...cfg, default_model: alias }));
updateConfig((cfg) => {
const model = cfg.models[alias];
if (!model) return cfg;
const provider = cfg.providers[model.provider];
if (!provider) return cfg;
const providers = {
...cfg.providers,
[provider.name]: setProviderDefaultModel(provider, alias),
};
return { ...cfg, providers, default_model: alias };
});
};
const handleSwitchProvider = async (name: string) => {
@@ -197,6 +231,16 @@ export default function App() {
const target = cfg.providers[name];
if (!target) return cfg;
// Find the currently active provider so we can remember its default
// model before switching away.
let activeProviderName: string | null = null;
for (const [key, p] of Object.entries(cfg.providers)) {
if (p.active) {
activeProviderName = key;
break;
}
}
// Only the selected provider is active. All other providers (including
// Kimi native/managed and custom) are deactivated so the target agent
// follows Pi Switch's choice exactly.
@@ -205,15 +249,25 @@ export default function App() {
providers[key] = { ...p, active: key === name };
}
// Set default model to the first model of the selected provider.
let default_model: string | null = null;
for (const [key, m] of Object.entries(cfg.models)) {
if (m.provider === name) {
default_model = key;
break;
// Remember the active provider's current default model (if it belongs to
// that provider) so switching back later restores the user's choice.
if (activeProviderName && cfg.default_model) {
const activeProvider = providers[activeProviderName];
if (activeProvider && cfg.models[cfg.default_model]?.provider === activeProviderName) {
providers[activeProviderName] = setProviderDefaultModel(
activeProvider,
cfg.default_model
);
}
}
// Prefer the target provider's remembered default model; fall back to
// its first model when there is no saved preference or it is invalid.
let default_model = getProviderDefaultModel(target);
if (!default_model || cfg.models[default_model]?.provider !== name) {
default_model = findFirstModelForProvider(cfg.models, name);
}
return { ...cfg, providers, default_model };
});
@@ -259,6 +313,13 @@ export default function App() {
default_model = null;
}
// Remember this provider's default model preference so it survives
// provider switches.
const updatedProvider = providers[provider.name];
if (updatedProvider && default_model && updatedModels[default_model]?.provider === provider.name) {
providers[provider.name] = setProviderDefaultModel(updatedProvider, default_model);
}
return { ...cfg, providers, models: updatedModels, default_model };
});
if (provider.name !== editingProvider) {
@@ -391,6 +452,11 @@ export default function App() {
});
}}
onModelAdd={() => {
const providerModels = Object.values(config.models).filter(
(m) => m.provider === currentProvider.name
);
const roles = ["Sonnet", "Opus", "Fable", "Haiku"];
const defaultRole = roles[providerModels.length % roles.length];
const alias = `新模型[${currentProvider.name}]`;
updateConfig((cfg) => ({
...cfg,
@@ -402,7 +468,7 @@ export default function App() {
model: "",
max_context_size: getDefaultMaxContextSize(alias),
display_name: null,
role: `新模型[${currentProvider.name}]`,
role: defaultRole,
supports_1m: false,
capabilities: [],
},
+12 -12
View File
@@ -482,23 +482,23 @@ function ModelMapping({
<tbody className="divide-y divide-[#2a2a2e]">
{models.map((m) => (
<tr key={m.alias} className="hover:bg-[#1c1c20]">
<td className="px-4 py-2">
<select
className="w-full bg-[#1f1f23] border border-[#2a2a2e] rounded px-2 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
value={m.role || "Sonnet"}
onChange={(e) => onModelChange({ ...m, role: e.target.value })}
>
<option value="Sonnet">Sonnet</option>
<option value="Opus">Opus</option>
<option value="Fable">Fable</option>
<option value="Haiku">Haiku</option>
</select>
</td>
<td className="px-4 py-2">
<span className="text-sm text-gray-400">
{m.model ? `${m.model}[${m.provider}]` : m.alias}
</span>
</td>
<td className="px-4 py-2">
<input
className="w-full bg-transparent border border-[#2a2a2e] rounded px-2 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
value={m.display_name || ""}
onChange={(e) =>
onModelChange({
...m,
display_name: e.target.value || null,
})
}
/>
</td>
<td className="px-4 py-2">
<input
className="w-full bg-transparent border border-[#2a2a2e] rounded px-2 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
+1 -1
View File
@@ -92,7 +92,7 @@ export function ProviderList({
(m) => m.provider === provider.name
);
const defaultModelName = defaultModel
? models[defaultModel]?.display_name || defaultModel
? models[defaultModel]?.model || models[defaultModel]?.display_name || defaultModel
: null;
const isActive = provider.active === true;