mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-06 22:12:12 +02:00
refactor(models): simplify provider model selection UI
This commit is contained in:
parent
3b507208bc
commit
13a2637df7
6 changed files with 16 additions and 128 deletions
|
|
@ -113,7 +113,7 @@ export function BedrockConnectForm({ onDraftChange }: ProviderConnectFormProps)
|
|||
</p>
|
||||
) : null}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Add Bedrock model IDs from the provider's settings after connecting.
|
||||
After entering credentials, refresh models to discover the Bedrock catalog for this region.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { useAtomValue } from "jotai";
|
|||
import { Eye, EyeOff, Settings } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import {
|
||||
addManualModelMutationAtom,
|
||||
bulkUpdateModelsMutationAtom,
|
||||
discoverConnectionModelsMutationAtom,
|
||||
testPreviewModelMutationAtom,
|
||||
|
|
@ -50,26 +49,17 @@ export function ConnectionSettingsDialog({
|
|||
const discoverModels = useAtomValue(discoverConnectionModelsMutationAtom);
|
||||
const testPreviewModel = useAtomValue(testPreviewModelMutationAtom);
|
||||
const updateConnection = useAtomValue(updateModelConnectionMutationAtom);
|
||||
const addManualModel = useAtomValue(addManualModelMutationAtom);
|
||||
const bulkUpdateModels = useAtomValue(bulkUpdateModelsMutationAtom);
|
||||
|
||||
const allowlist = Array.isArray(connection.extra?.model_ids)
|
||||
? (connection.extra.model_ids as string[])
|
||||
: [];
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [baseUrlDraft, setBaseUrlDraft] = useState(connection.base_url ?? "");
|
||||
const [apiKeyDraft, setApiKeyDraft] = useState("");
|
||||
const [showApiKey, setShowApiKey] = useState(false);
|
||||
const [allowlistText, setAllowlistText] = useState(allowlist.join(", "));
|
||||
const [isSavingConnectionSettings, setIsSavingConnectionSettings] = useState(false);
|
||||
const [draftEnabledModelIds, setDraftEnabledModelIds] = useState(() =>
|
||||
enabledModelIds(connection.models)
|
||||
);
|
||||
|
||||
const isLocal =
|
||||
connection.provider === "ollama_chat" ||
|
||||
connection.provider === "lm_studio" ||
|
||||
!connection.base_url?.startsWith("https");
|
||||
const hasConnectionChanges =
|
||||
baseUrlDraft.trim() !== (connection.base_url ?? "") ||
|
||||
apiKeyDraft.trim() !== (connection.api_key ?? "");
|
||||
|
|
@ -93,7 +83,6 @@ export function ConnectionSettingsDialog({
|
|||
setBaseUrlDraft(connection.base_url ?? "");
|
||||
setApiKeyDraft(connection.api_key ?? "");
|
||||
setShowApiKey(false);
|
||||
setAllowlistText(allowlist.join(", "));
|
||||
setIsSavingConnectionSettings(false);
|
||||
setDraftEnabledModelIds(enabledModelIds(connection.models));
|
||||
}
|
||||
|
|
@ -168,17 +157,6 @@ export function ConnectionSettingsDialog({
|
|||
}
|
||||
}
|
||||
|
||||
function saveAllowlist() {
|
||||
const ids = allowlistText
|
||||
.split(",")
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
updateConnection.mutate({
|
||||
id: connection.id,
|
||||
data: { extra: { ...(connection.extra ?? {}), model_ids: ids } },
|
||||
});
|
||||
}
|
||||
|
||||
function handleToggleModel(model: SelectableModel, enabled: boolean) {
|
||||
if (typeof model.id !== "number") return;
|
||||
const modelId = model.id;
|
||||
|
|
@ -276,41 +254,15 @@ export function ConnectionSettingsDialog({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{!isLocal ? (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs">Model IDs filter (optional)</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={allowlistText}
|
||||
onChange={(event) => setAllowlistText(event.target.value)}
|
||||
placeholder="Comma-separated, e.g. anthropic/claude-sonnet-4-5, google/gemini-2.5-pro"
|
||||
/>
|
||||
<Button size="sm" onClick={saveAllowlist} disabled={updateConnection.isPending}>
|
||||
Save filter
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Leave empty to discover all models. Recommended for providers with large catalogs.
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Separator className="bg-muted-foreground/20" />
|
||||
|
||||
<ModelsSelectionPanel
|
||||
models={draftModels}
|
||||
isRefreshing={discoverModels.isPending}
|
||||
isAddingManual={addManualModel.isPending}
|
||||
isUpdatingModel={isSavingConnectionSettings}
|
||||
isBulkUpdating={isSavingConnectionSettings || bulkUpdateModels.isPending}
|
||||
refreshLabel={`Refresh ${providerLabel} models`}
|
||||
onRefresh={() => discoverModels.mutate(connection.id)}
|
||||
onAddManual={(modelId) =>
|
||||
addManualModel.mutate({
|
||||
connectionId: connection.id,
|
||||
data: { model_id: modelId },
|
||||
})
|
||||
}
|
||||
onToggleModel={handleToggleModel}
|
||||
onBulkToggle={handleBulkToggle}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -201,22 +201,6 @@ export function ModelProviderConnectionsPanel({
|
|||
);
|
||||
}
|
||||
|
||||
function addConnectModel(modelId: string) {
|
||||
setConnectModels((current) => {
|
||||
if (current.some((model) => model.model_id === modelId)) return current;
|
||||
return [
|
||||
...current,
|
||||
{
|
||||
model_id: modelId,
|
||||
display_name: modelId,
|
||||
source: "MANUAL",
|
||||
enabled: true,
|
||||
metadata: {},
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
function toggleConnectModel(model: SelectableModel, enabled: boolean) {
|
||||
setConnectModels((current) =>
|
||||
current.map((item) => (item.model_id === model.model_id ? { ...item, enabled } : item))
|
||||
|
|
@ -277,7 +261,6 @@ export function ModelProviderConnectionsPanel({
|
|||
previewModels={connectModels}
|
||||
isPreviewingModels={previewModels.isPending}
|
||||
onPreviewModels={refreshConnectModels}
|
||||
onAddPreviewModel={addConnectModel}
|
||||
onTogglePreviewModel={toggleConnectModel}
|
||||
onBulkTogglePreviewModels={bulkToggleConnectModels}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { RefreshCw } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import {
|
||||
capability,
|
||||
capabilityLabels,
|
||||
|
|
@ -18,14 +15,11 @@ interface ModelsSelectionPanelProps {
|
|||
models: SelectableModel[];
|
||||
description?: string;
|
||||
emptyMessage?: string;
|
||||
manualInputPlaceholder?: string;
|
||||
refreshLabel?: string;
|
||||
isRefreshing?: boolean;
|
||||
isAddingManual?: boolean;
|
||||
isUpdatingModel?: boolean;
|
||||
isBulkUpdating?: boolean;
|
||||
onRefresh?: () => void;
|
||||
onAddManual?: (modelId: string) => void;
|
||||
onToggleModel?: (model: SelectableModel, enabled: boolean) => void;
|
||||
onBulkToggle?: (models: SelectableModel[], enabled: boolean) => void;
|
||||
}
|
||||
|
|
@ -34,18 +28,14 @@ export function ModelsSelectionPanel({
|
|||
models,
|
||||
description = "Select models to make available for this provider.",
|
||||
emptyMessage = "No models available.",
|
||||
manualInputPlaceholder = "Add a model ID manually",
|
||||
refreshLabel = "Refresh models",
|
||||
isRefreshing = false,
|
||||
isAddingManual = false,
|
||||
isUpdatingModel = false,
|
||||
isBulkUpdating = false,
|
||||
onRefresh,
|
||||
onAddManual,
|
||||
onToggleModel,
|
||||
onBulkToggle,
|
||||
}: ModelsSelectionPanelProps) {
|
||||
const [manualModelId, setManualModelId] = useState("");
|
||||
const [modelFilter, setModelFilter] = useState<ModelCapabilityFilter | null>(null);
|
||||
|
||||
const filteredModels = modelFilter
|
||||
|
|
@ -54,13 +44,6 @@ export function ModelsSelectionPanel({
|
|||
const allFilteredModelsEnabled =
|
||||
filteredModels.length > 0 && filteredModels.every((model) => model.enabled);
|
||||
|
||||
function addModel() {
|
||||
const modelId = manualModelId.trim();
|
||||
if (!modelId || !onAddManual) return;
|
||||
onAddManual(modelId);
|
||||
setManualModelId("");
|
||||
}
|
||||
|
||||
function toggleFilteredModels() {
|
||||
const nextEnabled = !allFilteredModelsEnabled;
|
||||
const changedModels = filteredModels.filter((model) => model.enabled !== nextEnabled);
|
||||
|
|
@ -100,32 +83,6 @@ export function ModelsSelectionPanel({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{onAddManual ? (
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={manualModelId}
|
||||
onChange={(event) => setManualModelId(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
addModel();
|
||||
}
|
||||
}}
|
||||
placeholder={manualInputPlaceholder}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={addModel}
|
||||
disabled={isAddingManual || !manualModelId.trim()}
|
||||
className="relative min-w-[88px]"
|
||||
>
|
||||
<span className={isAddingManual ? "opacity-0" : ""}>Add model</span>
|
||||
{isAddingManual ? <Spinner size="xs" className="absolute" /> : null}
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{models.length > 0 ? (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="text-xs font-medium text-muted-foreground">Filter models</span>
|
||||
|
|
@ -179,11 +136,6 @@ export function ModelsSelectionPanel({
|
|||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2 text-sm font-medium">
|
||||
<span className="truncate">{modelLabel(model)}</span>
|
||||
{model.source === "MANUAL" ? (
|
||||
<Badge variant="outline" className="text-[10px]">
|
||||
manual
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{capabilityLabels(model) || "No discovered capabilities"}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ interface ProviderConnectDialogProps {
|
|||
previewModels?: SelectableModel[];
|
||||
isPreviewingModels?: boolean;
|
||||
onPreviewModels?: (draft: ConnectionDraft) => void;
|
||||
onAddPreviewModel?: (modelId: string) => void;
|
||||
onTogglePreviewModel?: (model: SelectableModel, enabled: boolean) => void;
|
||||
onBulkTogglePreviewModels?: (models: SelectableModel[], enabled: boolean) => void;
|
||||
}
|
||||
|
|
@ -53,7 +52,6 @@ export function ProviderConnectDialog({
|
|||
previewModels = [],
|
||||
isPreviewingModels = false,
|
||||
onPreviewModels,
|
||||
onAddPreviewModel,
|
||||
onTogglePreviewModel,
|
||||
onBulkTogglePreviewModels,
|
||||
}: ProviderConnectDialogProps) {
|
||||
|
|
@ -130,18 +128,21 @@ export function ProviderConnectDialog({
|
|||
<DefaultConnectForm {...formProps} />
|
||||
)}
|
||||
|
||||
<Separator className="bg-muted-foreground/20" />
|
||||
{!isAzure ? (
|
||||
<>
|
||||
<Separator className="bg-muted-foreground/20" />
|
||||
|
||||
<ModelsSelectionPanel
|
||||
models={previewModels}
|
||||
description={modelDescription}
|
||||
isRefreshing={isPreviewingModels}
|
||||
refreshLabel={`Refresh ${meta.name} models`}
|
||||
onRefresh={canRefreshModels ? () => onPreviewModels?.(currentDraft) : undefined}
|
||||
onAddManual={onAddPreviewModel}
|
||||
onToggleModel={onTogglePreviewModel}
|
||||
onBulkToggle={onBulkTogglePreviewModels}
|
||||
/>
|
||||
<ModelsSelectionPanel
|
||||
models={previewModels}
|
||||
description={modelDescription}
|
||||
isRefreshing={isPreviewingModels}
|
||||
refreshLabel={`Refresh ${meta.name} models`}
|
||||
onRefresh={canRefreshModels ? () => onPreviewModels?.(currentDraft) : undefined}
|
||||
onToggleModel={onTogglePreviewModel}
|
||||
onBulkToggle={onBulkTogglePreviewModels}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<ConnectFormFooter
|
||||
onCancel={() => onOpenChange(false)}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export function VertexConnectForm({ onDraftChange }: ProviderConnectFormProps) {
|
|||
</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Add Vertex AI model IDs from the provider's settings after connecting.
|
||||
SurfSense will show supported Vertex AI models from the provider catalog.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue