Enhance Ingest Settings UI and functionality
- Added a new 'tableStructure' setting to the IngestSettings interface and UI. - Updated the IngestSettings component to include a select input for the embedding model. - Improved layout and accessibility of input fields and switches. - Integrated default settings for chunk size, chunk overlap, OCR, picture descriptions, and embedding model from constants. - Implemented synchronization of settings with backend data using React Query.
This commit is contained in:
parent
52dd723bba
commit
43b88a3f7e
4 changed files with 211 additions and 75 deletions
|
|
@ -2,13 +2,22 @@
|
|||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { ChevronRight, Info } from "lucide-react";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { IngestSettings as IngestSettingsType } from "./types";
|
||||
import { LabelWrapper } from "@/components/label-wrapper";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
interface IngestSettingsProps {
|
||||
isOpen: boolean;
|
||||
|
|
@ -30,6 +39,7 @@ export const IngestSettings = ({
|
|||
ocr: false,
|
||||
pictureDescriptions: false,
|
||||
embeddingModel: "text-embedding-3-small",
|
||||
tableStructure: false,
|
||||
};
|
||||
|
||||
// Use provided settings or defaults
|
||||
|
|
@ -58,79 +68,144 @@ export const IngestSettings = ({
|
|||
</CollapsibleTrigger>
|
||||
|
||||
<CollapsibleContent className="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-up-2 data-[state=open]:slide-down-2">
|
||||
<div className="pt-5 space-y-5">
|
||||
<div className="flex items-center gap-4 w-full">
|
||||
<div className="w-full">
|
||||
<div className="text-sm mb-2 font-semibold">Chunk size</div>
|
||||
<Input
|
||||
type="number"
|
||||
value={currentSettings.chunkSize}
|
||||
onChange={e =>
|
||||
handleSettingsChange({
|
||||
chunkSize: parseInt(e.target.value) || 0,
|
||||
})
|
||||
<div className="pt-5 space-y-6">
|
||||
<div className="space-y-2">
|
||||
<LabelWrapper
|
||||
helperText="Model used for knowledge ingest and retrieval"
|
||||
id="embedding-model-select"
|
||||
label="Embedding model"
|
||||
>
|
||||
<Select
|
||||
disabled={true}
|
||||
value={currentSettings.embeddingModel}
|
||||
onValueChange={value =>
|
||||
handleSettingsChange({ embeddingModel: value })
|
||||
}
|
||||
>
|
||||
<SelectTrigger disabled id="embedding-model-select">
|
||||
<SelectValue placeholder="text-embedding-3-small" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={currentSettings.embeddingModel}>
|
||||
{currentSettings.embeddingModel}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</LabelWrapper>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="chunk-size" className="text-sm font-medium">
|
||||
Chunk size
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="chunk-size"
|
||||
type="number"
|
||||
min="1"
|
||||
value={currentSettings.chunkSize}
|
||||
onChange={e =>
|
||||
handleSettingsChange({
|
||||
chunkSize: Math.max(0, parseInt(e.target.value) || 0),
|
||||
})
|
||||
}
|
||||
className="w-full pr-24"
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
characters
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="chunk-overlap" className="text-sm font-medium">
|
||||
Chunk overlap
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="chunk-overlap"
|
||||
type="number"
|
||||
min="0"
|
||||
value={currentSettings.chunkOverlap}
|
||||
onChange={e =>
|
||||
handleSettingsChange({
|
||||
chunkOverlap: Math.max(0, parseInt(e.target.value) || 0),
|
||||
})
|
||||
}
|
||||
className="w-full pr-24"
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
characters
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex-1">
|
||||
<Label
|
||||
htmlFor="ocr-toggle"
|
||||
className="text-base font-medium cursor-pointer text-sm"
|
||||
>
|
||||
Table Structure
|
||||
</Label>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Capture table structure during ingest.
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
id="ocr-toggle"
|
||||
checked={currentSettings.tableStructure}
|
||||
onCheckedChange={checked =>
|
||||
handleSettingsChange({ tableStructure: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="text-sm mb-2 font-semibold">Chunk overlap</div>
|
||||
<Input
|
||||
type="number"
|
||||
value={currentSettings.chunkOverlap}
|
||||
onChange={e =>
|
||||
handleSettingsChange({
|
||||
chunkOverlap: parseInt(e.target.value) || 0,
|
||||
})
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex-1">
|
||||
<Label
|
||||
htmlFor="ocr-toggle"
|
||||
className="text-base font-medium cursor-pointer text-sm"
|
||||
>
|
||||
OCR
|
||||
</Label>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Extracts text from images and scanned pages.
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
id="ocr-toggle"
|
||||
checked={currentSettings.ocr}
|
||||
onCheckedChange={checked =>
|
||||
handleSettingsChange({ ocr: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 items-center justify-between">
|
||||
<div>
|
||||
<div className="text-sm font-semibold pb-2">OCR</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Extracts text from images/PDFs. Ingest is slower when enabled.
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex-1">
|
||||
<Label
|
||||
htmlFor="picture-descriptions-toggle"
|
||||
className="text-base font-medium cursor-pointer text-sm"
|
||||
>
|
||||
Picture descriptions
|
||||
</Label>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Generates short image captions. More expensive when enabled.
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
id="picture-descriptions-toggle"
|
||||
checked={currentSettings.pictureDescriptions}
|
||||
onCheckedChange={checked =>
|
||||
handleSettingsChange({ pictureDescriptions: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
checked={currentSettings.ocr}
|
||||
onCheckedChange={checked =>
|
||||
handleSettingsChange({ ocr: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 items-center justify-between">
|
||||
<div>
|
||||
<div className="text-sm pb-2 font-semibold">
|
||||
Picture descriptions
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Adds captions for images. Ingest is more expensive when enabled.
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={currentSettings.pictureDescriptions}
|
||||
onCheckedChange={checked =>
|
||||
handleSettingsChange({ pictureDescriptions: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-sm font-semibold pb-2 flex items-center">
|
||||
Embedding model
|
||||
<Info className="w-3.5 h-3.5 text-muted-foreground ml-2" />
|
||||
</div>
|
||||
<Input
|
||||
disabled
|
||||
value={currentSettings.embeddingModel}
|
||||
onChange={e =>
|
||||
handleSettingsChange({ embeddingModel: e.target.value })
|
||||
}
|
||||
placeholder="text-embedding-3-small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
|
|
|
|||
|
|
@ -103,4 +103,5 @@ export interface IngestSettings {
|
|||
ocr: boolean;
|
||||
pictureDescriptions: boolean;
|
||||
embeddingModel: string;
|
||||
tableStructure: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import { PickerHeader } from "./picker-header";
|
|||
import { FileList } from "./file-list";
|
||||
import { IngestSettings } from "./ingest-settings";
|
||||
import { createProviderHandler } from "./provider-handlers";
|
||||
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
||||
import { useAuth } from "@/contexts/auth-context";
|
||||
import { DEFAULT_KNOWLEDGE_SETTINGS } from "@/lib/constants";
|
||||
|
||||
export const UnifiedCloudPicker = ({
|
||||
provider,
|
||||
|
|
@ -22,21 +25,73 @@ export const UnifiedCloudPicker = ({
|
|||
baseUrl,
|
||||
onSettingsChange,
|
||||
}: UnifiedCloudPickerProps) => {
|
||||
const { isNoAuthMode } = useAuth();
|
||||
const [isPickerLoaded, setIsPickerLoaded] = useState(false);
|
||||
const [isPickerOpen, setIsPickerOpen] = useState(false);
|
||||
const [isIngestSettingsOpen, setIsIngestSettingsOpen] = useState(false);
|
||||
const [isIngestSettingsOpen, setIsIngestSettingsOpen] = useState(true);
|
||||
const [isLoadingBaseUrl, setIsLoadingBaseUrl] = useState(false);
|
||||
const [autoBaseUrl, setAutoBaseUrl] = useState<string | undefined>(undefined);
|
||||
|
||||
// Fetch settings using React Query
|
||||
const { data: settings = {} } = useGetSettingsQuery({
|
||||
enabled: isAuthenticated || isNoAuthMode,
|
||||
});
|
||||
|
||||
// Settings state with defaults
|
||||
const [ingestSettings, setIngestSettings] = useState<IngestSettingsType>({
|
||||
chunkSize: 1000,
|
||||
chunkOverlap: 200,
|
||||
ocr: false,
|
||||
pictureDescriptions: false,
|
||||
embeddingModel: "text-embedding-3-small",
|
||||
chunkSize: DEFAULT_KNOWLEDGE_SETTINGS.chunk_size,
|
||||
chunkOverlap: DEFAULT_KNOWLEDGE_SETTINGS.chunk_overlap,
|
||||
ocr: DEFAULT_KNOWLEDGE_SETTINGS.ocr,
|
||||
pictureDescriptions: DEFAULT_KNOWLEDGE_SETTINGS.picture_descriptions,
|
||||
embeddingModel: DEFAULT_KNOWLEDGE_SETTINGS.embedding_model,
|
||||
tableStructure: DEFAULT_KNOWLEDGE_SETTINGS.table_structure,
|
||||
});
|
||||
|
||||
// Sync chunk size with backend settings
|
||||
useEffect(() => {
|
||||
const chunkSize = settings.knowledge?.chunk_size;
|
||||
if (chunkSize !== undefined) {
|
||||
setIngestSettings(prev => ({
|
||||
...prev,
|
||||
chunkSize: chunkSize,
|
||||
}));
|
||||
}
|
||||
}, [settings.knowledge]);
|
||||
|
||||
// Sync chunk overlap with backend settings
|
||||
useEffect(() => {
|
||||
const chunkOverlap = settings.knowledge?.chunk_overlap;
|
||||
if (chunkOverlap !== undefined) {
|
||||
setIngestSettings(prev => ({
|
||||
...prev,
|
||||
chunkOverlap: chunkOverlap,
|
||||
}));
|
||||
}
|
||||
}, [settings.knowledge]);
|
||||
|
||||
// Sync processing mode (doclingPresets) with OCR and picture descriptions
|
||||
useEffect(() => {
|
||||
const mode = settings.knowledge?.doclingPresets;
|
||||
if (mode) {
|
||||
setIngestSettings(prev => ({
|
||||
...prev,
|
||||
ocr: mode === "ocr" || mode === "picture_description" || mode === "VLM",
|
||||
pictureDescriptions: mode === "picture_description",
|
||||
}));
|
||||
}
|
||||
}, [settings.knowledge]);
|
||||
|
||||
// Sync embedding model with backend settings
|
||||
useEffect(() => {
|
||||
const embeddingModel = settings.knowledge?.embedding_model;
|
||||
if (embeddingModel) {
|
||||
setIngestSettings(prev => ({
|
||||
...prev,
|
||||
embeddingModel: embeddingModel,
|
||||
}));
|
||||
}
|
||||
}, [settings.knowledge]);
|
||||
|
||||
// Handle settings changes and notify parent
|
||||
const handleSettingsChange = (newSettings: IngestSettingsType) => {
|
||||
setIngestSettings(newSettings);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
*/
|
||||
export const DEFAULT_AGENT_SETTINGS = {
|
||||
llm_model: "gpt-4o-mini",
|
||||
system_prompt: "You are a helpful assistant that can use tools to answer questions and perform tasks."
|
||||
system_prompt:
|
||||
"You are a helpful assistant that can use tools to answer questions and perform tasks.",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
|
|
@ -12,7 +13,11 @@ export const DEFAULT_AGENT_SETTINGS = {
|
|||
export const DEFAULT_KNOWLEDGE_SETTINGS = {
|
||||
chunk_size: 1000,
|
||||
chunk_overlap: 200,
|
||||
processing_mode: "standard"
|
||||
processing_mode: "standard",
|
||||
table_structure: false,
|
||||
ocr: false,
|
||||
picture_descriptions: false,
|
||||
embedding_model: "text-embedding-3-small",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
|
|
@ -20,4 +25,4 @@ export const DEFAULT_KNOWLEDGE_SETTINGS = {
|
|||
*/
|
||||
export const UI_CONSTANTS = {
|
||||
MAX_SYSTEM_PROMPT_CHARS: 2000,
|
||||
} as const;
|
||||
} as const;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue