mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-02 03:42:38 +02:00
remove provider
This commit is contained in:
parent
cd2f25f928
commit
e6cdbb393e
2 changed files with 87 additions and 21 deletions
|
|
@ -175,10 +175,21 @@ function ChatInputInner({
|
||||||
loadModelConfig()
|
loadModelConfig()
|
||||||
}, [isActive, loadModelConfig])
|
}, [isActive, loadModelConfig])
|
||||||
|
|
||||||
|
// Reload when model config changes (e.g. from settings dialog)
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = () => { loadModelConfig() }
|
||||||
|
window.addEventListener('models-config-changed', handler)
|
||||||
|
return () => window.removeEventListener('models-config-changed', handler)
|
||||||
|
}, [loadModelConfig])
|
||||||
|
|
||||||
const handleModelChange = useCallback(async (key: string) => {
|
const handleModelChange = useCallback(async (key: string) => {
|
||||||
const entry = configuredModels.find((m) => `${m.flavor}/${m.model}` === key)
|
const entry = configuredModels.find((m) => `${m.flavor}/${m.model}` === key)
|
||||||
if (!entry) return
|
if (!entry) return
|
||||||
setActiveModelKey(key)
|
setActiveModelKey(key)
|
||||||
|
// Collect all models for this provider so the full list is preserved
|
||||||
|
const providerModels = configuredModels
|
||||||
|
.filter((m) => m.flavor === entry.flavor)
|
||||||
|
.map((m) => m.model)
|
||||||
try {
|
try {
|
||||||
await window.ipc.invoke('models:saveConfig', {
|
await window.ipc.invoke('models:saveConfig', {
|
||||||
provider: {
|
provider: {
|
||||||
|
|
@ -188,6 +199,7 @@ function ChatInputInner({
|
||||||
headers: entry.headers,
|
headers: entry.headers,
|
||||||
},
|
},
|
||||||
model: entry.model,
|
model: entry.model,
|
||||||
|
models: providerModels,
|
||||||
knowledgeGraphModel: entry.knowledgeGraphModel,
|
knowledgeGraphModel: entry.knowledgeGraphModel,
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,9 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Active provider takes precedence from top-level config
|
// Active provider takes precedence from top-level config,
|
||||||
|
// but only if it exists in the providers map (wasn't deleted)
|
||||||
|
if (parsed.providers?.[flavor]) {
|
||||||
const existingModels = next[flavor].models;
|
const existingModels = next[flavor].models;
|
||||||
const activeModels = existingModels[0] === parsed.model
|
const activeModels = existingModels[0] === parsed.model
|
||||||
? existingModels
|
? existingModels
|
||||||
|
|
@ -289,6 +291,7 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
models: activeModels.length > 0 ? activeModels : [""],
|
models: activeModels.length > 0 ? activeModels : [""],
|
||||||
knowledgeGraphModel: parsed.knowledgeGraphModel || "",
|
knowledgeGraphModel: parsed.knowledgeGraphModel || "",
|
||||||
};
|
};
|
||||||
|
}
|
||||||
return next;
|
return next;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -366,6 +369,7 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
await window.ipc.invoke("models:saveConfig", providerConfig)
|
await window.ipc.invoke("models:saveConfig", providerConfig)
|
||||||
setDefaultProvider(provider)
|
setDefaultProvider(provider)
|
||||||
setTestState({ status: "success" })
|
setTestState({ status: "success" })
|
||||||
|
window.dispatchEvent(new Event('models-config-changed'))
|
||||||
toast.success("Model configuration saved")
|
toast.success("Model configuration saved")
|
||||||
} else {
|
} else {
|
||||||
setTestState({ status: "error", error: result.error })
|
setTestState({ status: "error", error: result.error })
|
||||||
|
|
@ -393,12 +397,50 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
knowledgeGraphModel: config.knowledgeGraphModel.trim() || undefined,
|
knowledgeGraphModel: config.knowledgeGraphModel.trim() || undefined,
|
||||||
})
|
})
|
||||||
setDefaultProvider(prov)
|
setDefaultProvider(prov)
|
||||||
|
window.dispatchEvent(new Event('models-config-changed'))
|
||||||
toast.success("Default provider updated")
|
toast.success("Default provider updated")
|
||||||
} catch {
|
} catch {
|
||||||
toast.error("Failed to set default provider")
|
toast.error("Failed to set default provider")
|
||||||
}
|
}
|
||||||
}, [providerConfigs])
|
}, [providerConfigs])
|
||||||
|
|
||||||
|
const handleDeleteProvider = useCallback(async (prov: LlmProviderFlavor) => {
|
||||||
|
try {
|
||||||
|
const result = await window.ipc.invoke("workspace:readFile", { path: "config/models.json" })
|
||||||
|
const parsed = JSON.parse(result.data)
|
||||||
|
if (parsed?.providers?.[prov]) {
|
||||||
|
delete parsed.providers[prov]
|
||||||
|
}
|
||||||
|
// If the deleted provider is the current top-level active one,
|
||||||
|
// switch top-level config to the current default provider
|
||||||
|
if (parsed?.provider?.flavor === prov && defaultProvider && defaultProvider !== prov) {
|
||||||
|
const defConfig = providerConfigs[defaultProvider]
|
||||||
|
const defModels = defConfig.models.map(m => m.trim()).filter(Boolean)
|
||||||
|
parsed.provider = {
|
||||||
|
flavor: defaultProvider,
|
||||||
|
apiKey: defConfig.apiKey.trim() || undefined,
|
||||||
|
baseURL: defConfig.baseURL.trim() || undefined,
|
||||||
|
}
|
||||||
|
parsed.model = defModels[0] || ""
|
||||||
|
parsed.models = defModels
|
||||||
|
parsed.knowledgeGraphModel = defConfig.knowledgeGraphModel.trim() || undefined
|
||||||
|
}
|
||||||
|
await window.ipc.invoke("workspace:writeFile", {
|
||||||
|
path: "config/models.json",
|
||||||
|
data: JSON.stringify(parsed, null, 2),
|
||||||
|
})
|
||||||
|
setProviderConfigs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[prov]: { apiKey: "", baseURL: defaultBaseURLs[prov] || "", models: [""], knowledgeGraphModel: "" },
|
||||||
|
}))
|
||||||
|
setTestState({ status: "idle" })
|
||||||
|
window.dispatchEvent(new Event('models-config-changed'))
|
||||||
|
toast.success("Provider configuration removed")
|
||||||
|
} catch {
|
||||||
|
toast.error("Failed to remove provider")
|
||||||
|
}
|
||||||
|
}, [defaultProvider, providerConfigs])
|
||||||
|
|
||||||
const renderProviderCard = (p: { id: LlmProviderFlavor; name: string; description: string }) => {
|
const renderProviderCard = (p: { id: LlmProviderFlavor; name: string; description: string }) => {
|
||||||
const isDefault = defaultProvider === p.id
|
const isDefault = defaultProvider === p.id
|
||||||
const isSelected = provider === p.id
|
const isSelected = provider === p.id
|
||||||
|
|
@ -427,16 +469,28 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground mt-0.5">{p.description}</div>
|
<div className="text-xs text-muted-foreground mt-0.5">{p.description}</div>
|
||||||
{!isDefault && hasModel && isSelected && (
|
{!isDefault && hasModel && isSelected && (
|
||||||
|
<div className="mt-1.5 flex items-center gap-3">
|
||||||
<span
|
<span
|
||||||
role="button"
|
role="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
handleSetDefault(p.id)
|
handleSetDefault(p.id)
|
||||||
}}
|
}}
|
||||||
className="mt-1.5 inline-flex text-[11px] text-muted-foreground hover:text-primary transition-colors cursor-pointer"
|
className="inline-flex text-[11px] text-muted-foreground hover:text-primary transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
Set as default
|
Set as default
|
||||||
</span>
|
</span>
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleDeleteProvider(p.id)
|
||||||
|
}}
|
||||||
|
className="inline-flex text-[11px] text-muted-foreground hover:text-destructive transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue