diff --git a/frontend/components/ui/accordion.tsx b/frontend/components/ui/accordion.tsx
index 2be19b59..37ec5c3c 100644
--- a/frontend/components/ui/accordion.tsx
+++ b/frontend/components/ui/accordion.tsx
@@ -14,7 +14,7 @@ const AccordionItem = React.forwardRef<
>(({ className, ...props }, ref) => (
));
diff --git a/frontend/components/ui/tabs.tsx b/frontend/components/ui/tabs.tsx
index 5d75c455..874f054f 100644
--- a/frontend/components/ui/tabs.tsx
+++ b/frontend/components/ui/tabs.tsx
@@ -13,7 +13,7 @@ const TabsList = React.forwardRef<
)}
- {(hasLanguageModels || hasEmbeddingModels) && !updatedOnboarding && }
- {!updatedOnboarding && (
-
-
-
- )}
diff --git a/frontend/src/app/onboarding/components/ollama-onboarding.tsx b/frontend/src/app/onboarding/components/ollama-onboarding.tsx
index b40e6714..9c86b94c 100644
--- a/frontend/src/app/onboarding/components/ollama-onboarding.tsx
+++ b/frontend/src/app/onboarding/components/ollama-onboarding.tsx
@@ -7,154 +7,143 @@ import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutat
import { useGetOllamaModelsQuery } from "../../api/queries/useGetModelsQuery";
import { useModelSelection } from "../hooks/useModelSelection";
import { useUpdateSettings } from "../hooks/useUpdateSettings";
-import { AdvancedOnboarding } from "./advanced";
import { ModelSelector } from "./model-selector";
export function OllamaOnboarding({
- setSettings,
- sampleDataset,
- setSampleDataset,
+ setSettings,
+ sampleDataset,
+ setSampleDataset,
}: {
- setSettings: (settings: OnboardingVariables) => void;
- sampleDataset: boolean;
- setSampleDataset: (dataset: boolean) => void;
+ setSettings: (settings: OnboardingVariables) => void;
+ sampleDataset: boolean;
+ setSampleDataset: (dataset: boolean) => void;
}) {
- const [endpoint, setEndpoint] = useState(`http://localhost:11434`);
- const [showConnecting, setShowConnecting] = useState(false);
- const debouncedEndpoint = useDebouncedValue(endpoint, 500);
+ const [endpoint, setEndpoint] = useState(`http://localhost:11434`);
+ const [showConnecting, setShowConnecting] = useState(false);
+ const debouncedEndpoint = useDebouncedValue(endpoint, 500);
- // Fetch models from API when endpoint is provided (debounced)
- const {
- data: modelsData,
- isLoading: isLoadingModels,
- error: modelsError,
- } = useGetOllamaModelsQuery(
- debouncedEndpoint ? { endpoint: debouncedEndpoint } : undefined,
- );
+ // Fetch models from API when endpoint is provided (debounced)
+ const {
+ data: modelsData,
+ isLoading: isLoadingModels,
+ error: modelsError,
+ } = useGetOllamaModelsQuery(
+ debouncedEndpoint ? { endpoint: debouncedEndpoint } : undefined,
+ );
- // Use custom hook for model selection logic
- const {
- languageModel,
- embeddingModel,
- setLanguageModel,
- setEmbeddingModel,
- languageModels,
- embeddingModels,
- } = useModelSelection(modelsData);
+ // Use custom hook for model selection logic
+ const {
+ languageModel,
+ embeddingModel,
+ setLanguageModel,
+ setEmbeddingModel,
+ languageModels,
+ embeddingModels,
+ } = useModelSelection(modelsData);
- // Handle delayed display of connecting state
- useEffect(() => {
- let timeoutId: NodeJS.Timeout;
+ // Handle delayed display of connecting state
+ useEffect(() => {
+ let timeoutId: NodeJS.Timeout;
- if (debouncedEndpoint && isLoadingModels) {
- timeoutId = setTimeout(() => {
- setShowConnecting(true);
- }, 500);
- } else {
- setShowConnecting(false);
- }
+ if (debouncedEndpoint && isLoadingModels) {
+ timeoutId = setTimeout(() => {
+ setShowConnecting(true);
+ }, 500);
+ } else {
+ setShowConnecting(false);
+ }
- return () => {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- };
- }, [debouncedEndpoint, isLoadingModels]);
+ return () => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ };
+ }, [debouncedEndpoint, isLoadingModels]);
- const handleSampleDatasetChange = (dataset: boolean) => {
- setSampleDataset(dataset);
- };
+ // Update settings when values change
+ useUpdateSettings(
+ "ollama",
+ {
+ endpoint,
+ languageModel,
+ embeddingModel,
+ },
+ setSettings,
+ );
- // Update settings when values change
- useUpdateSettings(
- "ollama",
- {
- endpoint,
- languageModel,
- embeddingModel,
- },
- setSettings,
- );
+ // Check validation state based on models query
+ const hasConnectionError = debouncedEndpoint && modelsError;
+ const hasNoModels =
+ modelsData &&
+ !modelsData.language_models?.length &&
+ !modelsData.embedding_models?.length;
- // Check validation state based on models query
- const hasConnectionError = debouncedEndpoint && modelsError;
- const hasNoModels =
- modelsData &&
- !modelsData.language_models?.length &&
- !modelsData.embedding_models?.length;
-
- return (
- <>
-
-
-
setEndpoint(e.target.value)}
- />
- {showConnecting && (
-
- Connecting to Ollama server...
-
- )}
- {hasConnectionError && (
-
- Can’t reach Ollama at {debouncedEndpoint}. Update the base URL or
- start the server.
-
- )}
- {hasNoModels && (
-
- No models found. Install embedding and agent models on your Ollama
- server.
-
- )}
-
-
- }
- noOptionsPlaceholder={
- isLoadingModels
- ? "Loading models..."
- : "No embedding models detected. Install an embedding model to continue."
- }
- value={embeddingModel}
- onValueChange={setEmbeddingModel}
- />
-
-
- }
- noOptionsPlaceholder={
- isLoadingModels
- ? "Loading models..."
- : "No language models detected. Install a language model to continue."
- }
- value={languageModel}
- onValueChange={setLanguageModel}
- />
-
-
-
- >
- );
+ return (
+
+
+
setEndpoint(e.target.value)}
+ />
+ {showConnecting && (
+
+ Connecting to Ollama server...
+
+ )}
+ {hasConnectionError && (
+
+ Can’t reach Ollama at {debouncedEndpoint}. Update the base URL or
+ start the server.
+
+ )}
+ {hasNoModels && (
+
+ No models found. Install embedding and agent models on your Ollama
+ server.
+
+ )}
+
+
+ }
+ noOptionsPlaceholder={
+ isLoadingModels
+ ? "Loading models..."
+ : "No embedding models detected. Install an embedding model to continue."
+ }
+ value={embeddingModel}
+ onValueChange={setEmbeddingModel}
+ />
+
+
+ }
+ noOptionsPlaceholder={
+ isLoadingModels
+ ? "Loading models..."
+ : "No language models detected. Install a language model to continue."
+ }
+ value={languageModel}
+ onValueChange={setLanguageModel}
+ />
+
+
+ );
}
diff --git a/frontend/src/app/onboarding/components/onboarding-card.tsx b/frontend/src/app/onboarding/components/onboarding-card.tsx
index 10f08d09..745dd076 100644
--- a/frontend/src/app/onboarding/components/onboarding-card.tsx
+++ b/frontend/src/app/onboarding/components/onboarding-card.tsx
@@ -4,8 +4,8 @@ import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import {
- type OnboardingVariables,
- useOnboardingMutation,
+ type OnboardingVariables,
+ useOnboardingMutation,
} from "@/app/api/mutations/useOnboardingMutation";
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
import { useDoclingHealth } from "@/components/docling-health-banner";
@@ -14,24 +14,25 @@ import OllamaLogo from "@/components/logo/ollama-logo";
import OpenAILogo from "@/components/logo/openai-logo";
import { Button } from "@/components/ui/button";
import {
- Card,
- CardContent,
- CardFooter,
- CardHeader,
+ Card,
+ CardContent,
+ CardFooter,
+ CardHeader,
} from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
} from "@/components/ui/tooltip";
+import { cn } from "@/lib/utils";
import { AnimatedProviderSteps } from "./animated-provider-steps";
import { IBMOnboarding } from "./ibm-onboarding";
import { OllamaOnboarding } from "./ollama-onboarding";
import { OpenAIOnboarding } from "./openai-onboarding";
interface OnboardingCardProps {
- onComplete: () => void;
+ onComplete: () => void;
}
@@ -45,220 +46,226 @@ const STEP_LIST = [
const TOTAL_PROVIDER_STEPS = STEP_LIST.length;
const OnboardingCard = ({ onComplete }: OnboardingCardProps) => {
- const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
- const { isHealthy: isDoclingHealthy } = useDoclingHealth();
+ const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
+ const { isHealthy: isDoclingHealthy } = useDoclingHealth();
- const [modelProvider, setModelProvider] = useState("openai");
+ const [modelProvider, setModelProvider] = useState("openai");
- const [sampleDataset, setSampleDataset] = useState(true);
+ const [sampleDataset, setSampleDataset] = useState(true);
- const handleSetModelProvider = (provider: string) => {
- setModelProvider(provider);
- setSettings({
- model_provider: provider,
- embedding_model: "",
- llm_model: "",
- });
- };
+ const handleSetModelProvider = (provider: string) => {
+ setModelProvider(provider);
+ setSettings({
+ model_provider: provider,
+ embedding_model: "",
+ llm_model: "",
+ });
+ };
- const [settings, setSettings] = useState({
- model_provider: modelProvider,
- embedding_model: "",
- llm_model: "",
- });
+ const [settings, setSettings] = useState({
+ model_provider: modelProvider,
+ embedding_model: "",
+ llm_model: "",
+ });
- const [currentStep, setCurrentStep] = useState(null);
+ const [currentStep, setCurrentStep] = useState(null);
- // Query tasks to track completion
- const { data: tasks } = useGetTasksQuery({
- enabled: currentStep !== null, // Only poll when onboarding has started
- refetchInterval: currentStep !== null ? 1000 : false, // Poll every 1 second during onboarding
- });
+ // Query tasks to track completion
+ const { data: tasks } = useGetTasksQuery({
+ enabled: currentStep !== null, // Only poll when onboarding has started
+ refetchInterval: currentStep !== null ? 1000 : false, // Poll every 1 second during onboarding
+ });
- // Monitor tasks and call onComplete when all tasks are done
- useEffect(() => {
- if (currentStep === null || !tasks) {
- return;
- }
+ // Monitor tasks and call onComplete when all tasks are done
+ useEffect(() => {
+ if (currentStep === null || !tasks) {
+ return;
+ }
- // Check if there are any active tasks (pending, running, or processing)
- const activeTasks = tasks.find(
- (task) =>
- task.status === "pending" ||
- task.status === "running" ||
- task.status === "processing",
- );
+ // Check if there are any active tasks (pending, running, or processing)
+ const activeTasks = tasks.find(
+ (task) =>
+ task.status === "pending" ||
+ task.status === "running" ||
+ task.status === "processing",
+ );
- // If no active tasks and we've started onboarding, complete it
- if (
- (!activeTasks || (activeTasks.processed_files ?? 0) > 0) &&
- tasks.length > 0
- ) {
- // Set to final step to show "Done"
- setCurrentStep(TOTAL_PROVIDER_STEPS);
- // Wait a bit before completing
- setTimeout(() => {
- onComplete();
- }, 1000);
- }
- }, [tasks, currentStep, onComplete]);
+ // If no active tasks and we've started onboarding, complete it
+ if (
+ (!activeTasks || (activeTasks.processed_files ?? 0) > 0) &&
+ tasks.length > 0
+ ) {
+ // Set to final step to show "Done"
+ setCurrentStep(TOTAL_PROVIDER_STEPS);
+ // Wait a bit before completing
+ setTimeout(() => {
+ onComplete();
+ }, 1000);
+ }
+ }, [tasks, currentStep, onComplete]);
- // Mutations
- const onboardingMutation = useOnboardingMutation({
- onSuccess: (data) => {
- console.log("Onboarding completed successfully", data);
- setCurrentStep(0);
- },
- onError: (error) => {
- toast.error("Failed to complete onboarding", {
- description: error.message,
- });
- },
- });
+ // Mutations
+ const onboardingMutation = useOnboardingMutation({
+ onSuccess: (data) => {
+ console.log("Onboarding completed successfully", data);
+ setCurrentStep(0);
+ },
+ onError: (error) => {
+ toast.error("Failed to complete onboarding", {
+ description: error.message,
+ });
+ },
+ });
- const handleComplete = () => {
- if (
- !settings.model_provider ||
- !settings.llm_model ||
- !settings.embedding_model
- ) {
- toast.error("Please complete all required fields");
- return;
- }
+ const handleComplete = () => {
+ if (
+ !settings.model_provider ||
+ !settings.llm_model ||
+ !settings.embedding_model
+ ) {
+ toast.error("Please complete all required fields");
+ return;
+ }
- // Prepare onboarding data
- const onboardingData: OnboardingVariables = {
- model_provider: settings.model_provider,
- llm_model: settings.llm_model,
- embedding_model: settings.embedding_model,
- sample_data: sampleDataset,
- };
+ // Prepare onboarding data
+ const onboardingData: OnboardingVariables = {
+ model_provider: settings.model_provider,
+ llm_model: settings.llm_model,
+ embedding_model: settings.embedding_model,
+ sample_data: sampleDataset,
+ };
- // Add API key if available
- if (settings.api_key) {
- onboardingData.api_key = settings.api_key;
- }
+ // Add API key if available
+ if (settings.api_key) {
+ onboardingData.api_key = settings.api_key;
+ }
- // Add endpoint if available
- if (settings.endpoint) {
- onboardingData.endpoint = settings.endpoint;
- }
+ // Add endpoint if available
+ if (settings.endpoint) {
+ onboardingData.endpoint = settings.endpoint;
+ }
- // Add project_id if available
- if (settings.project_id) {
- onboardingData.project_id = settings.project_id;
- }
+ // Add project_id if available
+ if (settings.project_id) {
+ onboardingData.project_id = settings.project_id;
+ }
- onboardingMutation.mutate(onboardingData);
- setCurrentStep(0);
- };
+ onboardingMutation.mutate(onboardingData);
+ setCurrentStep(0);
+ };
- const isComplete =
- !!settings.llm_model && !!settings.embedding_model && isDoclingHealthy;
+ const isComplete =
+ !!settings.llm_model && !!settings.embedding_model && isDoclingHealthy;
- return (
-
- {currentStep === null ? (
-
-
-
-
-
-
-
- OpenAI
-
-
-
- IBM watsonx.ai
-
-
-
- Ollama
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {!isComplete && (
-
- {!!settings.llm_model &&
- !!settings.embedding_model &&
- !isDoclingHealthy
- ? "docling-serve must be running to continue"
- : "Please fill in all required fields"}
-
- )}
-
-
-
-
- ) : (
-
-
+ {currentStep === null ? (
+
+
+
+
+
+
+
+
+ OpenAI
+
+
+
+
+
+ IBM watsonx.ai
+
+
+
+ Ollama
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {!isComplete && (
+
+ {!!settings.llm_model &&
+ !!settings.embedding_model &&
+ !isDoclingHealthy
+ ? "docling-serve must be running to continue"
+ : "Please fill in all required fields"}
+
+ )}
+
+
+
+ ) : (
+
+
-
- )}
-
- );
+
+ )}
+
+ );
};
export default OnboardingCard;
diff --git a/frontend/src/components/chat-renderer.tsx b/frontend/src/components/chat-renderer.tsx
index 3087b586..925ddb76 100644
--- a/frontend/src/components/chat-renderer.tsx
+++ b/frontend/src/components/chat-renderer.tsx
@@ -28,7 +28,7 @@ export function ChatRenderer({
settings,
children,
}: {
- settings: Settings;
+ settings: Settings | undefined;
children: React.ReactNode;
}) {
const pathname = usePathname();
@@ -51,7 +51,8 @@ export function ChatRenderer({
if (typeof window === "undefined") return false;
const savedStep = localStorage.getItem(ONBOARDING_STEP_KEY);
// Show layout if settings.edited is true and if no onboarding step is saved
- return !!settings?.edited && savedStep === null;
+ const isEdited = settings?.edited ?? true;
+ return isEdited ? savedStep === null : false;
});
// Only fetch conversations on chat page
diff --git a/frontend/src/components/layout-wrapper.tsx b/frontend/src/components/layout-wrapper.tsx
index 17339f60..36713e74 100644
--- a/frontend/src/components/layout-wrapper.tsx
+++ b/frontend/src/components/layout-wrapper.tsx
@@ -35,9 +35,10 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
const isUnhealthy = health?.status === "unhealthy" || isError;
const isBannerVisible = !isHealthLoading && isUnhealthy;
+ const isSettingsLoadingOrError = isSettingsLoading || !settings;
// Show loading state when backend isn't ready
- if (isLoading || isSettingsLoading || !settings) {
+ if (isLoading || (isSettingsLoadingOrError && (isNoAuthMode || isAuthenticated))) {
return (