mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-15 21:11:08 +02:00
sort chat model
This commit is contained in:
parent
75b384eac1
commit
cd2f25f928
2 changed files with 79 additions and 20 deletions
|
|
@ -152,9 +152,19 @@ function ChatInputInner({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const defaultKey = parsed?.provider?.flavor && parsed?.model
|
||||||
|
? `${parsed.provider.flavor}/${parsed.model}`
|
||||||
|
: ''
|
||||||
|
models.sort((a, b) => {
|
||||||
|
const aKey = `${a.flavor}/${a.model}`
|
||||||
|
const bKey = `${b.flavor}/${b.model}`
|
||||||
|
if (aKey === defaultKey) return -1
|
||||||
|
if (bKey === defaultKey) return 1
|
||||||
|
return 0
|
||||||
|
})
|
||||||
setConfiguredModels(models)
|
setConfiguredModels(models)
|
||||||
if (parsed?.provider?.flavor && parsed?.model) {
|
if (defaultKey) {
|
||||||
setActiveModelKey(`${parsed.provider.flavor}/${parsed.model}`)
|
setActiveModelKey(defaultKey)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// No config yet
|
// No config yet
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ const defaultBaseURLs: Partial<Record<LlmProviderFlavor, string>> = {
|
||||||
|
|
||||||
function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
const [provider, setProvider] = useState<LlmProviderFlavor>("openai")
|
const [provider, setProvider] = useState<LlmProviderFlavor>("openai")
|
||||||
|
const [defaultProvider, setDefaultProvider] = useState<LlmProviderFlavor | null>(null)
|
||||||
const [providerConfigs, setProviderConfigs] = useState<Record<LlmProviderFlavor, { apiKey: string; baseURL: string; models: string[]; knowledgeGraphModel: string }>>({
|
const [providerConfigs, setProviderConfigs] = useState<Record<LlmProviderFlavor, { apiKey: string; baseURL: string; models: string[]; knowledgeGraphModel: string }>>({
|
||||||
openai: { apiKey: "", baseURL: "", models: [""], knowledgeGraphModel: "" },
|
openai: { apiKey: "", baseURL: "", models: [""], knowledgeGraphModel: "" },
|
||||||
anthropic: { apiKey: "", baseURL: "", models: [""], knowledgeGraphModel: "" },
|
anthropic: { apiKey: "", baseURL: "", models: [""], knowledgeGraphModel: "" },
|
||||||
|
|
@ -257,6 +258,7 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
if (parsed?.provider?.flavor && parsed?.model) {
|
if (parsed?.provider?.flavor && parsed?.model) {
|
||||||
const flavor = parsed.provider.flavor as LlmProviderFlavor
|
const flavor = parsed.provider.flavor as LlmProviderFlavor
|
||||||
setProvider(flavor)
|
setProvider(flavor)
|
||||||
|
setDefaultProvider(flavor)
|
||||||
setProviderConfigs(prev => {
|
setProviderConfigs(prev => {
|
||||||
const next = { ...prev };
|
const next = { ...prev };
|
||||||
// Hydrate all saved providers from the providers map
|
// Hydrate all saved providers from the providers map
|
||||||
|
|
@ -362,6 +364,7 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
const result = await window.ipc.invoke("models:test", providerConfig)
|
const result = await window.ipc.invoke("models:test", providerConfig)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
await window.ipc.invoke("models:saveConfig", providerConfig)
|
await window.ipc.invoke("models:saveConfig", providerConfig)
|
||||||
|
setDefaultProvider(provider)
|
||||||
setTestState({ status: "success" })
|
setTestState({ status: "success" })
|
||||||
toast.success("Model configuration saved")
|
toast.success("Model configuration saved")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -374,24 +377,70 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
|
||||||
}
|
}
|
||||||
}, [canTest, provider, activeConfig])
|
}, [canTest, provider, activeConfig])
|
||||||
|
|
||||||
const renderProviderCard = (p: { id: LlmProviderFlavor; name: string; description: string }) => (
|
const handleSetDefault = useCallback(async (prov: LlmProviderFlavor) => {
|
||||||
<button
|
const config = providerConfigs[prov]
|
||||||
key={p.id}
|
const allModels = config.models.map(m => m.trim()).filter(Boolean)
|
||||||
onClick={() => {
|
if (!allModels[0]) return
|
||||||
setProvider(p.id)
|
try {
|
||||||
setTestState({ status: "idle" })
|
await window.ipc.invoke("models:saveConfig", {
|
||||||
}}
|
provider: {
|
||||||
className={cn(
|
flavor: prov,
|
||||||
"rounded-md border px-3 py-2.5 text-left transition-colors",
|
apiKey: config.apiKey.trim() || undefined,
|
||||||
provider === p.id
|
baseURL: config.baseURL.trim() || undefined,
|
||||||
? "border-primary bg-primary/5"
|
},
|
||||||
: "border-border hover:bg-accent"
|
model: allModels[0],
|
||||||
)}
|
models: allModels,
|
||||||
>
|
knowledgeGraphModel: config.knowledgeGraphModel.trim() || undefined,
|
||||||
<div className="text-sm font-medium">{p.name}</div>
|
})
|
||||||
<div className="text-xs text-muted-foreground mt-0.5">{p.description}</div>
|
setDefaultProvider(prov)
|
||||||
</button>
|
toast.success("Default provider updated")
|
||||||
)
|
} catch {
|
||||||
|
toast.error("Failed to set default provider")
|
||||||
|
}
|
||||||
|
}, [providerConfigs])
|
||||||
|
|
||||||
|
const renderProviderCard = (p: { id: LlmProviderFlavor; name: string; description: string }) => {
|
||||||
|
const isDefault = defaultProvider === p.id
|
||||||
|
const isSelected = provider === p.id
|
||||||
|
const hasModel = providerConfigs[p.id].models[0]?.trim().length > 0
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={p.id}
|
||||||
|
onClick={() => {
|
||||||
|
setProvider(p.id)
|
||||||
|
setTestState({ status: "idle" })
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"rounded-md border px-3 py-2.5 text-left transition-colors relative",
|
||||||
|
isSelected
|
||||||
|
? "border-primary bg-primary/5"
|
||||||
|
: "border-border hover:bg-accent"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span className="text-sm font-medium">{p.name}</span>
|
||||||
|
{isDefault && (
|
||||||
|
<span className="rounded-full bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium leading-none text-primary">
|
||||||
|
Default
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground mt-0.5">{p.description}</div>
|
||||||
|
{!isDefault && hasModel && isSelected && (
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleSetDefault(p.id)
|
||||||
|
}}
|
||||||
|
className="mt-1.5 inline-flex text-[11px] text-muted-foreground hover:text-primary transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
Set as default
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (configLoading) {
|
if (configLoading) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue