Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 138 additions & 63 deletions frontend/src/components/cloud-picker/ingest-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +39,7 @@ export const IngestSettings = ({
ocr: false,
pictureDescriptions: false,
embeddingModel: "text-embedding-3-small",
tableStructure: false,
};

// Use provided settings or defaults
Expand Down Expand Up @@ -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 })
}
/>
</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>
>
<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="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="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>
<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 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>
<div className="text-sm text-muted-foreground">
Adds captions for images. Ingest is more expensive when enabled.
<Switch
id="ocr-toggle"
checked={currentSettings.tableStructure}
onCheckedChange={checked =>
handleSettingsChange({ tableStructure: checked })
}
/>
</div>
<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>
<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 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>
<Input
disabled
value={currentSettings.embeddingModel}
onChange={e =>
handleSettingsChange({ embeddingModel: e.target.value })
}
placeholder="text-embedding-3-small"
/>
</div>
</div>
</CollapsibleContent>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/cloud-picker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,5 @@ export interface IngestSettings {
ocr: boolean;
pictureDescriptions: boolean;
embeddingModel: string;
tableStructure: boolean;
}
67 changes: 61 additions & 6 deletions frontend/src/components/cloud-picker/unified-cloud-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -12,12 +13,16 @@ 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;

/**
* UI Constants
*/
export const UI_CONSTANTS = {
MAX_SYSTEM_PROMPT_CHARS: 2000,
} as const;
} as const;