Changed settings page to use model selector
This commit is contained in:
parent
8360388101
commit
4225bbeb0d
1 changed files with 1096 additions and 1106 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ArrowUpRight, Loader2, PlugZap, Plus, RefreshCw } from "lucide-react";
|
import { ArrowUpRight, Loader2, PlugZap, Plus } from "lucide-react";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { Suspense, useCallback, useEffect, useState } from "react";
|
import { Suspense, useCallback, useEffect, useState } from "react";
|
||||||
import { useUpdateFlowSettingMutation } from "@/app/api/mutations/useUpdateFlowSettingMutation";
|
import { useUpdateFlowSettingMutation } from "@/app/api/mutations/useUpdateFlowSettingMutation";
|
||||||
|
|
@ -11,6 +11,8 @@ import {
|
||||||
} from "@/app/api/queries/useGetModelsQuery";
|
} from "@/app/api/queries/useGetModelsQuery";
|
||||||
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
||||||
import { ConfirmationDialog } from "@/components/confirmation-dialog";
|
import { ConfirmationDialog } from "@/components/confirmation-dialog";
|
||||||
|
import { LabelWrapper } from "@/components/label-wrapper";
|
||||||
|
import OpenAILogo from "@/components/logo/openai-logo";
|
||||||
import { ProtectedRoute } from "@/components/protected-route";
|
import { ProtectedRoute } from "@/components/protected-route";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
@ -21,8 +23,6 @@ import {
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import { Switch } from "@/components/ui/switch";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import {
|
import {
|
||||||
|
|
@ -31,18 +31,19 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { useAuth } from "@/contexts/auth-context";
|
import { useAuth } from "@/contexts/auth-context";
|
||||||
import { useTask } from "@/contexts/task-context";
|
import { useTask } from "@/contexts/task-context";
|
||||||
import { useDebounce } from "@/lib/debounce";
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_AGENT_SETTINGS,
|
DEFAULT_AGENT_SETTINGS,
|
||||||
DEFAULT_KNOWLEDGE_SETTINGS,
|
DEFAULT_KNOWLEDGE_SETTINGS,
|
||||||
UI_CONSTANTS,
|
UI_CONSTANTS,
|
||||||
} from "@/lib/constants";
|
} from "@/lib/constants";
|
||||||
|
import { useDebounce } from "@/lib/debounce";
|
||||||
|
import { ModelSelector } from "../onboarding/components/model-selector";
|
||||||
import { getFallbackModels, type ModelProvider } from "./helpers/model-helpers";
|
import { getFallbackModels, type ModelProvider } from "./helpers/model-helpers";
|
||||||
import { ModelSelectItems } from "./helpers/model-select-item";
|
import { ModelSelectItems } from "./helpers/model-select-item";
|
||||||
import { LabelWrapper } from "@/components/label-wrapper";
|
|
||||||
|
|
||||||
const { MAX_SYSTEM_PROMPT_CHARS } = UI_CONSTANTS;
|
const { MAX_SYSTEM_PROMPT_CHARS } = UI_CONSTANTS;
|
||||||
|
|
||||||
|
|
@ -114,7 +115,8 @@ function KnowledgeSourcesPage() {
|
||||||
const [chunkOverlap, setChunkOverlap] = useState<number>(50);
|
const [chunkOverlap, setChunkOverlap] = useState<number>(50);
|
||||||
const [tableStructure, setTableStructure] = useState<boolean>(false);
|
const [tableStructure, setTableStructure] = useState<boolean>(false);
|
||||||
const [ocr, setOcr] = useState<boolean>(false);
|
const [ocr, setOcr] = useState<boolean>(false);
|
||||||
const [pictureDescriptions, setPictureDescriptions] = useState<boolean>(false);
|
const [pictureDescriptions, setPictureDescriptions] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
|
||||||
// Fetch settings using React Query
|
// Fetch settings using React Query
|
||||||
const { data: settings = {} } = useGetSettingsQuery({
|
const { data: settings = {} } = useGetSettingsQuery({
|
||||||
|
|
@ -131,7 +133,7 @@ function KnowledgeSourcesPage() {
|
||||||
{
|
{
|
||||||
enabled:
|
enabled:
|
||||||
(isAuthenticated || isNoAuthMode) && currentProvider === "openai",
|
(isAuthenticated || isNoAuthMode) && currentProvider === "openai",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: ollamaModelsData } = useGetOllamaModelsQuery(
|
const { data: ollamaModelsData } = useGetOllamaModelsQuery(
|
||||||
|
|
@ -139,14 +141,14 @@ function KnowledgeSourcesPage() {
|
||||||
{
|
{
|
||||||
enabled:
|
enabled:
|
||||||
(isAuthenticated || isNoAuthMode) && currentProvider === "ollama",
|
(isAuthenticated || isNoAuthMode) && currentProvider === "ollama",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: ibmModelsData } = useGetIBMModelsQuery(
|
const { data: ibmModelsData } = useGetIBMModelsQuery(
|
||||||
undefined, // No params for now, could be extended later
|
undefined, // No params for now, could be extended later
|
||||||
{
|
{
|
||||||
enabled: (isAuthenticated || isNoAuthMode) && currentProvider === "ibm",
|
enabled: (isAuthenticated || isNoAuthMode) && currentProvider === "ibm",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Select the appropriate models data based on provider
|
// Select the appropriate models data based on provider
|
||||||
|
|
@ -164,7 +166,7 @@ function KnowledgeSourcesPage() {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
console.log("Setting updated successfully");
|
console.log("Setting updated successfully");
|
||||||
},
|
},
|
||||||
onError: error => {
|
onError: (error) => {
|
||||||
console.error("Failed to update setting:", error.message);
|
console.error("Failed to update setting:", error.message);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -174,7 +176,7 @@ function KnowledgeSourcesPage() {
|
||||||
(variables: Parameters<typeof updateFlowSettingMutation.mutate>[0]) => {
|
(variables: Parameters<typeof updateFlowSettingMutation.mutate>[0]) => {
|
||||||
updateFlowSettingMutation.mutate(variables);
|
updateFlowSettingMutation.mutate(variables);
|
||||||
},
|
},
|
||||||
500
|
500,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sync system prompt state with settings data
|
// Sync system prompt state with settings data
|
||||||
|
|
@ -303,8 +305,8 @@ function KnowledgeSourcesPage() {
|
||||||
|
|
||||||
// Initialize connectors list with metadata from backend
|
// Initialize connectors list with metadata from backend
|
||||||
const initialConnectors = connectorTypes
|
const initialConnectors = connectorTypes
|
||||||
.filter(type => connectorsResult.connectors[type].available) // Only show available connectors
|
.filter((type) => connectorsResult.connectors[type].available) // Only show available connectors
|
||||||
.map(type => ({
|
.map((type) => ({
|
||||||
id: type,
|
id: type,
|
||||||
name: connectorsResult.connectors[type].name,
|
name: connectorsResult.connectors[type].name,
|
||||||
description: connectorsResult.connectors[type].description,
|
description: connectorsResult.connectors[type].description,
|
||||||
|
|
@ -323,20 +325,20 @@ function KnowledgeSourcesPage() {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const connections = data.connections || [];
|
const connections = data.connections || [];
|
||||||
const activeConnection = connections.find(
|
const activeConnection = connections.find(
|
||||||
(conn: Connection) => conn.is_active
|
(conn: Connection) => conn.is_active,
|
||||||
);
|
);
|
||||||
const isConnected = activeConnection !== undefined;
|
const isConnected = activeConnection !== undefined;
|
||||||
|
|
||||||
setConnectors(prev =>
|
setConnectors((prev) =>
|
||||||
prev.map(c =>
|
prev.map((c) =>
|
||||||
c.type === connectorType
|
c.type === connectorType
|
||||||
? {
|
? {
|
||||||
...c,
|
...c,
|
||||||
status: isConnected ? "connected" : "not_connected",
|
status: isConnected ? "connected" : "not_connected",
|
||||||
connectionId: activeConnection?.connection_id,
|
connectionId: activeConnection?.connection_id,
|
||||||
}
|
}
|
||||||
: c
|
: c,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -347,7 +349,7 @@ function KnowledgeSourcesPage() {
|
||||||
|
|
||||||
const handleConnect = async (connector: Connector) => {
|
const handleConnect = async (connector: Connector) => {
|
||||||
setIsConnecting(connector.id);
|
setIsConnecting(connector.id);
|
||||||
setSyncResults(prev => ({ ...prev, [connector.id]: null }));
|
setSyncResults((prev) => ({ ...prev, [connector.id]: null }));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use the shared auth callback URL, same as connectors page
|
// Use the shared auth callback URL, same as connectors page
|
||||||
|
|
@ -379,7 +381,7 @@ function KnowledgeSourcesPage() {
|
||||||
`response_type=code&` +
|
`response_type=code&` +
|
||||||
`scope=${result.oauth_config.scopes.join(" ")}&` +
|
`scope=${result.oauth_config.scopes.join(" ")}&` +
|
||||||
`redirect_uri=${encodeURIComponent(
|
`redirect_uri=${encodeURIComponent(
|
||||||
result.oauth_config.redirect_uri
|
result.oauth_config.redirect_uri,
|
||||||
)}&` +
|
)}&` +
|
||||||
`access_type=offline&` +
|
`access_type=offline&` +
|
||||||
`prompt=consent&` +
|
`prompt=consent&` +
|
||||||
|
|
@ -508,9 +510,9 @@ function KnowledgeSourcesPage() {
|
||||||
// Watch for task completions and refresh stats
|
// Watch for task completions and refresh stats
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Find newly completed tasks by comparing with previous state
|
// Find newly completed tasks by comparing with previous state
|
||||||
const newlyCompletedTasks = tasks.filter(task => {
|
const newlyCompletedTasks = tasks.filter((task) => {
|
||||||
const wasCompleted =
|
const wasCompleted =
|
||||||
prevTasks.find(prev => prev.task_id === task.task_id)?.status ===
|
prevTasks.find((prev) => prev.task_id === task.task_id)?.status ===
|
||||||
"completed";
|
"completed";
|
||||||
return task.status === "completed" && !wasCompleted;
|
return task.status === "completed" && !wasCompleted;
|
||||||
});
|
});
|
||||||
|
|
@ -533,7 +535,7 @@ function KnowledgeSourcesPage() {
|
||||||
|
|
||||||
const handleEditInLangflow = (
|
const handleEditInLangflow = (
|
||||||
flowType: "chat" | "ingest",
|
flowType: "chat" | "ingest",
|
||||||
closeDialog: () => void
|
closeDialog: () => void,
|
||||||
) => {
|
) => {
|
||||||
// Select the appropriate flow ID and edit URL based on flow type
|
// Select the appropriate flow ID and edit URL based on flow type
|
||||||
const targetFlowId =
|
const targetFlowId =
|
||||||
|
|
@ -564,7 +566,7 @@ function KnowledgeSourcesPage() {
|
||||||
fetch(`/api/reset-flow/retrieval`, {
|
fetch(`/api/reset-flow/retrieval`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
@ -577,7 +579,7 @@ function KnowledgeSourcesPage() {
|
||||||
handleModelChange(DEFAULT_AGENT_SETTINGS.llm_model);
|
handleModelChange(DEFAULT_AGENT_SETTINGS.llm_model);
|
||||||
closeDialog(); // Close after successful completion
|
closeDialog(); // Close after successful completion
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
console.error("Error restoring retrieval flow:", error);
|
console.error("Error restoring retrieval flow:", error);
|
||||||
closeDialog(); // Close even on error (could show error toast instead)
|
closeDialog(); // Close even on error (could show error toast instead)
|
||||||
});
|
});
|
||||||
|
|
@ -587,7 +589,7 @@ function KnowledgeSourcesPage() {
|
||||||
fetch(`/api/reset-flow/ingest`, {
|
fetch(`/api/reset-flow/ingest`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
@ -602,7 +604,7 @@ function KnowledgeSourcesPage() {
|
||||||
setPictureDescriptions(false);
|
setPictureDescriptions(false);
|
||||||
closeDialog(); // Close after successful completion
|
closeDialog(); // Close after successful completion
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
console.error("Error restoring ingest flow:", error);
|
console.error("Error restoring ingest flow:", error);
|
||||||
closeDialog(); // Close even on error (could show error toast instead)
|
closeDialog(); // Close even on error (could show error toast instead)
|
||||||
});
|
});
|
||||||
|
|
@ -700,7 +702,7 @@ function KnowledgeSourcesPage() {
|
||||||
|
|
||||||
{/* Connectors Grid */}
|
{/* Connectors Grid */}
|
||||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{connectors.map(connector => (
|
{connectors.map((connector) => (
|
||||||
<Card key={connector.id} className="relative flex flex-col">
|
<Card key={connector.id} className="relative flex flex-col">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|
@ -834,7 +836,7 @@ function KnowledgeSourcesPage() {
|
||||||
}
|
}
|
||||||
confirmText="Proceed"
|
confirmText="Proceed"
|
||||||
confirmIcon={<ArrowUpRight />}
|
confirmIcon={<ArrowUpRight />}
|
||||||
onConfirm={closeDialog =>
|
onConfirm={(closeDialog) =>
|
||||||
handleEditInLangflow("chat", closeDialog)
|
handleEditInLangflow("chat", closeDialog)
|
||||||
}
|
}
|
||||||
variant="warning"
|
variant="warning"
|
||||||
|
|
@ -846,32 +848,18 @@ function KnowledgeSourcesPage() {
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<LabelWrapper
|
<LabelWrapper
|
||||||
helperText="Model used for chat"
|
|
||||||
id="model-select"
|
|
||||||
label="Language model"
|
label="Language model"
|
||||||
required
|
helperText="Model used for chat"
|
||||||
|
id="embedding-model"
|
||||||
|
required={true}
|
||||||
>
|
>
|
||||||
<Select
|
<ModelSelector
|
||||||
value={
|
options={modelsData?.language_models || []}
|
||||||
settings.agent?.llm_model ||
|
noOptionsPlaceholder={modelsData ? "No language models detected." : "Loading models..."}
|
||||||
modelsData?.language_models?.find(m => m.default)?.value ||
|
icon={<OpenAILogo className="w-4 h-4" />}
|
||||||
"gpt-4"
|
value={modelsData ? settings.agent?.llm_model || "" : ""}
|
||||||
}
|
|
||||||
onValueChange={handleModelChange}
|
onValueChange={handleModelChange}
|
||||||
>
|
|
||||||
<SelectTrigger id="model-select">
|
|
||||||
<SelectValue placeholder="Select a model" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<ModelSelectItems
|
|
||||||
models={modelsData?.language_models}
|
|
||||||
fallbackModels={
|
|
||||||
getFallbackModels(currentProvider).language
|
|
||||||
}
|
|
||||||
provider={currentProvider}
|
|
||||||
/>
|
/>
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</LabelWrapper>
|
</LabelWrapper>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|
@ -882,7 +870,7 @@ function KnowledgeSourcesPage() {
|
||||||
id="system-prompt"
|
id="system-prompt"
|
||||||
placeholder="Enter your agent instructions here..."
|
placeholder="Enter your agent instructions here..."
|
||||||
value={systemPrompt}
|
value={systemPrompt}
|
||||||
onChange={e => setSystemPrompt(e.target.value)}
|
onChange={(e) => setSystemPrompt(e.target.value)}
|
||||||
rows={6}
|
rows={6}
|
||||||
className={`resize-none ${
|
className={`resize-none ${
|
||||||
systemPrompt.length > MAX_SYSTEM_PROMPT_CHARS
|
systemPrompt.length > MAX_SYSTEM_PROMPT_CHARS
|
||||||
|
|
@ -993,7 +981,7 @@ function KnowledgeSourcesPage() {
|
||||||
confirmText="Proceed"
|
confirmText="Proceed"
|
||||||
confirmIcon={<ArrowUpRight />}
|
confirmIcon={<ArrowUpRight />}
|
||||||
variant="warning"
|
variant="warning"
|
||||||
onConfirm={closeDialog =>
|
onConfirm={(closeDialog) =>
|
||||||
handleEditInLangflow("ingest", closeDialog)
|
handleEditInLangflow("ingest", closeDialog)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1013,7 +1001,8 @@ function KnowledgeSourcesPage() {
|
||||||
disabled={true}
|
disabled={true}
|
||||||
value={
|
value={
|
||||||
settings.knowledge?.embedding_model ||
|
settings.knowledge?.embedding_model ||
|
||||||
modelsData?.embedding_models?.find(m => m.default)?.value ||
|
modelsData?.embedding_models?.find((m) => m.default)
|
||||||
|
?.value ||
|
||||||
"text-embedding-ada-002"
|
"text-embedding-ada-002"
|
||||||
}
|
}
|
||||||
onValueChange={handleEmbeddingModelChange}
|
onValueChange={handleEmbeddingModelChange}
|
||||||
|
|
@ -1051,7 +1040,7 @@ function KnowledgeSourcesPage() {
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
value={chunkSize}
|
value={chunkSize}
|
||||||
onChange={e => handleChunkSizeChange(e.target.value)}
|
onChange={(e) => handleChunkSizeChange(e.target.value)}
|
||||||
className="w-full pr-20"
|
className="w-full pr-20"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center pr-8 pointer-events-none">
|
<div className="absolute inset-y-0 right-0 flex items-center pr-8 pointer-events-none">
|
||||||
|
|
@ -1074,7 +1063,7 @@ function KnowledgeSourcesPage() {
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
value={chunkOverlap}
|
value={chunkOverlap}
|
||||||
onChange={e => handleChunkOverlapChange(e.target.value)}
|
onChange={(e) => handleChunkOverlapChange(e.target.value)}
|
||||||
className="w-full pr-20"
|
className="w-full pr-20"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center pr-8 pointer-events-none">
|
<div className="absolute inset-y-0 right-0 flex items-center pr-8 pointer-events-none">
|
||||||
|
|
@ -1113,7 +1102,8 @@ function KnowledgeSourcesPage() {
|
||||||
OCR
|
OCR
|
||||||
</Label>
|
</Label>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
Extracts text from images/PDFs. Ingest is slower when enabled.
|
Extracts text from images/PDFs. Ingest is slower when
|
||||||
|
enabled.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue