Compare commits
1 commit
main
...
add-ingest
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43b88a3f7e |
4 changed files with 211 additions and 75 deletions
|
|
@ -2,13 +2,22 @@
|
||||||
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
import {
|
import {
|
||||||
Collapsible,
|
Collapsible,
|
||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible";
|
} from "@/components/ui/collapsible";
|
||||||
import { ChevronRight, Info } from "lucide-react";
|
import { ChevronRight } from "lucide-react";
|
||||||
import { IngestSettings as IngestSettingsType } from "./types";
|
import { IngestSettings as IngestSettingsType } from "./types";
|
||||||
|
import { LabelWrapper } from "@/components/label-wrapper";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
interface IngestSettingsProps {
|
interface IngestSettingsProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|
@ -30,6 +39,7 @@ export const IngestSettings = ({
|
||||||
ocr: false,
|
ocr: false,
|
||||||
pictureDescriptions: false,
|
pictureDescriptions: false,
|
||||||
embeddingModel: "text-embedding-3-small",
|
embeddingModel: "text-embedding-3-small",
|
||||||
|
tableStructure: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use provided settings or defaults
|
// Use provided settings or defaults
|
||||||
|
|
@ -58,79 +68,144 @@ export const IngestSettings = ({
|
||||||
</CollapsibleTrigger>
|
</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">
|
<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="pt-5 space-y-6">
|
||||||
<div className="flex items-center gap-4 w-full">
|
<div className="space-y-2">
|
||||||
<div className="w-full">
|
<LabelWrapper
|
||||||
<div className="text-sm mb-2 font-semibold">Chunk size</div>
|
helperText="Model used for knowledge ingest and retrieval"
|
||||||
<Input
|
id="embedding-model-select"
|
||||||
type="number"
|
label="Embedding model"
|
||||||
value={currentSettings.chunkSize}
|
>
|
||||||
onChange={e =>
|
<Select
|
||||||
handleSettingsChange({
|
disabled={true}
|
||||||
chunkSize: parseInt(e.target.value) || 0,
|
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>
|
||||||
<div className="w-full">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="text-sm mb-2 font-semibold">Chunk overlap</div>
|
<div className="flex-1">
|
||||||
<Input
|
<Label
|
||||||
type="number"
|
htmlFor="ocr-toggle"
|
||||||
value={currentSettings.chunkOverlap}
|
className="text-base font-medium cursor-pointer text-sm"
|
||||||
onChange={e =>
|
>
|
||||||
handleSettingsChange({
|
OCR
|
||||||
chunkOverlap: parseInt(e.target.value) || 0,
|
</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>
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="flex-1">
|
||||||
<div className="flex gap-2 items-center justify-between">
|
<Label
|
||||||
<div>
|
htmlFor="picture-descriptions-toggle"
|
||||||
<div className="text-sm font-semibold pb-2">OCR</div>
|
className="text-base font-medium cursor-pointer text-sm"
|
||||||
<div className="text-sm text-muted-foreground">
|
>
|
||||||
Extracts text from images/PDFs. Ingest is slower when enabled.
|
Picture descriptions
|
||||||
|
</Label>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
Generates short image captions. More expensive when enabled.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="picture-descriptions-toggle"
|
||||||
|
checked={currentSettings.pictureDescriptions}
|
||||||
|
onCheckedChange={checked =>
|
||||||
|
handleSettingsChange({ pictureDescriptions: checked })
|
||||||
|
}
|
||||||
|
/>
|
||||||
</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>
|
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
|
|
|
||||||
|
|
@ -103,4 +103,5 @@ export interface IngestSettings {
|
||||||
ocr: boolean;
|
ocr: boolean;
|
||||||
pictureDescriptions: boolean;
|
pictureDescriptions: boolean;
|
||||||
embeddingModel: string;
|
embeddingModel: string;
|
||||||
|
tableStructure: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ import { PickerHeader } from "./picker-header";
|
||||||
import { FileList } from "./file-list";
|
import { FileList } from "./file-list";
|
||||||
import { IngestSettings } from "./ingest-settings";
|
import { IngestSettings } from "./ingest-settings";
|
||||||
import { createProviderHandler } from "./provider-handlers";
|
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 = ({
|
export const UnifiedCloudPicker = ({
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -22,21 +25,73 @@ export const UnifiedCloudPicker = ({
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
}: UnifiedCloudPickerProps) => {
|
}: UnifiedCloudPickerProps) => {
|
||||||
|
const { isNoAuthMode } = useAuth();
|
||||||
const [isPickerLoaded, setIsPickerLoaded] = useState(false);
|
const [isPickerLoaded, setIsPickerLoaded] = useState(false);
|
||||||
const [isPickerOpen, setIsPickerOpen] = useState(false);
|
const [isPickerOpen, setIsPickerOpen] = useState(false);
|
||||||
const [isIngestSettingsOpen, setIsIngestSettingsOpen] = useState(false);
|
const [isIngestSettingsOpen, setIsIngestSettingsOpen] = useState(true);
|
||||||
const [isLoadingBaseUrl, setIsLoadingBaseUrl] = useState(false);
|
const [isLoadingBaseUrl, setIsLoadingBaseUrl] = useState(false);
|
||||||
const [autoBaseUrl, setAutoBaseUrl] = useState<string | undefined>(undefined);
|
const [autoBaseUrl, setAutoBaseUrl] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
|
// Fetch settings using React Query
|
||||||
|
const { data: settings = {} } = useGetSettingsQuery({
|
||||||
|
enabled: isAuthenticated || isNoAuthMode,
|
||||||
|
});
|
||||||
|
|
||||||
// Settings state with defaults
|
// Settings state with defaults
|
||||||
const [ingestSettings, setIngestSettings] = useState<IngestSettingsType>({
|
const [ingestSettings, setIngestSettings] = useState<IngestSettingsType>({
|
||||||
chunkSize: 1000,
|
chunkSize: DEFAULT_KNOWLEDGE_SETTINGS.chunk_size,
|
||||||
chunkOverlap: 200,
|
chunkOverlap: DEFAULT_KNOWLEDGE_SETTINGS.chunk_overlap,
|
||||||
ocr: false,
|
ocr: DEFAULT_KNOWLEDGE_SETTINGS.ocr,
|
||||||
pictureDescriptions: false,
|
pictureDescriptions: DEFAULT_KNOWLEDGE_SETTINGS.picture_descriptions,
|
||||||
embeddingModel: "text-embedding-3-small",
|
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
|
// Handle settings changes and notify parent
|
||||||
const handleSettingsChange = (newSettings: IngestSettingsType) => {
|
const handleSettingsChange = (newSettings: IngestSettingsType) => {
|
||||||
setIngestSettings(newSettings);
|
setIngestSettings(newSettings);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
*/
|
*/
|
||||||
export const DEFAULT_AGENT_SETTINGS = {
|
export const DEFAULT_AGENT_SETTINGS = {
|
||||||
llm_model: "gpt-4o-mini",
|
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;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -12,7 +13,11 @@ export const DEFAULT_AGENT_SETTINGS = {
|
||||||
export const DEFAULT_KNOWLEDGE_SETTINGS = {
|
export const DEFAULT_KNOWLEDGE_SETTINGS = {
|
||||||
chunk_size: 1000,
|
chunk_size: 1000,
|
||||||
chunk_overlap: 200,
|
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;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue