diff --git a/src/interface/web/app/agents/layout.tsx b/src/interface/web/app/agents/layout.tsx
index 26935325..9500b371 100644
--- a/src/interface/web/app/agents/layout.tsx
+++ b/src/interface/web/app/agents/layout.tsx
@@ -1,8 +1,5 @@
import type { Metadata } from "next";
-import { noto_sans, noto_sans_arabic } from "@/app/fonts";
import "../globals.css";
-import { ContentSecurityPolicy } from "../common/layoutHelper";
-import { ThemeProvider } from "../components/providers/themeProvider";
export const metadata: Metadata = {
title: "Khoj AI - Agents",
@@ -34,33 +31,10 @@ export const metadata: Metadata = {
},
};
-export default function RootLayout({
+export default function ChildLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
- return (
-
-
-
-
-
-
-
- {children}
-
-
-
- );
+ return <>{children}>;
}
diff --git a/src/interface/web/app/automations/layout.tsx b/src/interface/web/app/automations/layout.tsx
index 2d97af5c..5c53091f 100644
--- a/src/interface/web/app/automations/layout.tsx
+++ b/src/interface/web/app/automations/layout.tsx
@@ -2,7 +2,6 @@ import type { Metadata } from "next";
import { Toaster } from "@/components/ui/toaster";
import "../globals.css";
-import { ContentSecurityPolicy } from "../common/layoutHelper";
export const metadata: Metadata = {
title: "Khoj AI - Automations",
@@ -34,18 +33,15 @@ export const metadata: Metadata = {
},
};
-export default function RootLayout({
+export default function ChildLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
-
-
-
- {children}
-
-
-
+ <>
+ {children}
+
+ >
);
}
diff --git a/src/interface/web/app/chat/layout.tsx b/src/interface/web/app/chat/layout.tsx
index 8a97bb9b..07c55338 100644
--- a/src/interface/web/app/chat/layout.tsx
+++ b/src/interface/web/app/chat/layout.tsx
@@ -1,8 +1,5 @@
import type { Metadata } from "next";
-import { noto_sans, noto_sans_arabic } from "@/app/fonts";
import "../globals.css";
-import { ContentSecurityPolicy } from "../common/layoutHelper";
-import { ThemeProvider } from "../components/providers/themeProvider";
export const metadata: Metadata = {
title: "Khoj AI - Chat",
@@ -34,38 +31,19 @@ export const metadata: Metadata = {
},
};
-export default function RootLayout({
+export default function ChildLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
-
-
-
-
-
-
-
- {children}
-
-
-
-
+ <>
+ {children}
+
+ >
);
}
diff --git a/src/interface/web/app/common/modelSelector.tsx b/src/interface/web/app/common/modelSelector.tsx
index beacdbdc..70c171b0 100644
--- a/src/interface/web/app/common/modelSelector.tsx
+++ b/src/interface/web/app/common/modelSelector.tsx
@@ -28,8 +28,7 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/h
import { Skeleton } from "@/components/ui/skeleton";
interface ModelSelectorProps extends PopoverProps {
- onSelect: (model: ModelOptions, userModification: boolean) => void;
- selectedModel?: string;
+ onSelect: (model: ModelOptions) => void;
disabled?: boolean;
initialModel?: string;
}
@@ -49,9 +48,8 @@ export function ModelSelector({ ...props }: ModelSelectorProps) {
setModels(userConfig.chat_model_options);
if (!props.initialModel) {
const selectedChatModelOption = userConfig.chat_model_options.find(model => model.id === userConfig.selected_chat_model_config);
- if (!selectedChatModelOption) {
+ if (!selectedChatModelOption && userConfig.chat_model_options.length > 0) {
setSelectedModel(userConfig.chat_model_options[0]);
- return;
} else {
setSelectedModel(selectedChatModelOption);
}
@@ -63,29 +61,10 @@ export function ModelSelector({ ...props }: ModelSelectorProps) {
}, [userConfig, props.initialModel, isLoadingUserConfig]);
useEffect(() => {
- if (props.selectedModel && selectedModel && props.selectedModel !== selectedModel.name) {
- const model = models.find(model => model.name === props.selectedModel);
- setSelectedModel(model);
+ if (selectedModel && userConfig) {
+ props.onSelect(selectedModel);
}
- else if (props.selectedModel === null && userConfig) {
- const selectedChatModelOption = userConfig.chat_model_options.find(model => model.id === userConfig.selected_chat_model_config);
- if (!selectedChatModelOption) {
- props.onSelect(userConfig.chat_model_options[0], false);
- return;
- } else {
- props.onSelect(selectedChatModelOption, false);
- }
- }
- }, [props.selectedModel, models]);
-
- useEffect(() => {
- if (selectedModel) {
- const userModification = selectedModel.id !== userConfig?.selected_chat_model_config;
- if (props.selectedModel !== selectedModel.name) {
- props.onSelect(selectedModel, userModification);
- }
- }
- }, [selectedModel]);
+ }, [selectedModel, userConfig, props.onSelect]);
if (isLoadingUserConfig) {
return (
diff --git a/src/interface/web/app/components/chatHistory/chatHistory.tsx b/src/interface/web/app/components/chatHistory/chatHistory.tsx
index c1cd923e..41989eb9 100644
--- a/src/interface/web/app/components/chatHistory/chatHistory.tsx
+++ b/src/interface/web/app/components/chatHistory/chatHistory.tsx
@@ -28,10 +28,6 @@ interface ChatResponse {
response: ChatHistoryData;
}
-interface ChatHistory {
- [key: string]: string;
-}
-
interface ChatHistoryProps {
conversationId: string;
setTitle: (title: string) => void;
@@ -368,7 +364,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
{data &&
data.chat &&
data.chat.map((chatMessage, index) => (
- <>
+
{chatMessage.trainOfThought && chatMessage.by === "khoj" && (
- >
+
))}
{props.incomingMessages &&
props.incomingMessages.map((message, index) => {
diff --git a/src/interface/web/app/components/chatSidebar/chatSidebar.tsx b/src/interface/web/app/components/chatSidebar/chatSidebar.tsx
index 52604ab0..1003204e 100644
--- a/src/interface/web/app/components/chatSidebar/chatSidebar.tsx
+++ b/src/interface/web/app/components/chatSidebar/chatSidebar.tsx
@@ -311,12 +311,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
isLoading: authenticationLoading,
} = useAuthenticatedData();
- const [customPrompt, setCustomPrompt] = useState("");
+ const [customPrompt, setCustomPrompt] = useState();
const [selectedModel, setSelectedModel] = useState();
const [inputTools, setInputTools] = useState();
const [outputModes, setOutputModes] = useState();
const [hasModified, setHasModified] = useState(false);
- const [isDefaultAgent, setIsDefaultAgent] = useState(agentData?.slug.toLowerCase() === "khoj" ? true : false);
+ const [isDefaultAgent, setIsDefaultAgent] = useState(!agentData || agentData?.slug.toLowerCase() === "khoj");
+ const [displayInputTools, setDisplayInputTools] = useState();
+ const [displayOutputModes, setDisplayOutputModes] = useState();
const [isSaving, setIsSaving] = useState(false);
@@ -325,12 +327,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
function setupAgentData() {
if (agentData) {
setInputTools(agentData.input_tools);
- if (agentData.input_tools === undefined || agentData.input_tools.length === 0) {
- setInputTools(agentConfigurationOptions?.input_tools ? Object.keys(agentConfigurationOptions.input_tools) : []);
+ setDisplayInputTools(agentData.input_tools);
+ if (agentData.input_tools === undefined) {
+ setDisplayInputTools(agentConfigurationOptions?.input_tools ? Object.keys(agentConfigurationOptions.input_tools) : []);
}
setOutputModes(agentData.output_modes);
- if (agentData.output_modes === undefined || agentData.output_modes.length === 0) {
- setOutputModes(agentConfigurationOptions?.output_modes ? Object.keys(agentConfigurationOptions.output_modes) : []);
+ setDisplayOutputModes(agentData.output_modes);
+ if (agentData.output_modes === undefined) {
+ setDisplayOutputModes(agentConfigurationOptions?.output_modes ? Object.keys(agentConfigurationOptions.output_modes) : []);
}
if (agentData.name.toLowerCase() === "khoj" || agentData.is_hidden === true) {
@@ -351,16 +355,30 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
useEffect(() => {
setupAgentData();
+ setHasModified(false);
}, [agentData]);
+ // Track changes to the model, prompt, input tools and output modes fields
+ useEffect(() => {
+ if (!agentData || agentDataLoading) return; // Don't compare until data is loaded
+
+ const modelChanged = !!selectedModel && selectedModel !== agentData.chat_model;
+ const promptChanged = !!customPrompt && customPrompt !== agentData.persona;
+
+ // Order independent check to ensure input tools or output modes haven't been changed.
+ const toolsChanged = JSON.stringify(inputTools?.sort() || []) !== JSON.stringify(agentData.input_tools?.sort());
+ const modesChanged = JSON.stringify(outputModes?.sort() || []) !== JSON.stringify(agentData.output_modes?.sort());
+
+ setHasModified(modelChanged || promptChanged || toolsChanged || modesChanged);
+
+ // Add agentDataLoading to dependencies to ensure it runs after loading finishes
+ }, [selectedModel, customPrompt, inputTools, outputModes, agentData, agentDataLoading]);
function isValueChecked(value: string, existingSelections: string[]): boolean {
return existingSelections.includes(value);
}
function handleCheckToggle(value: string, existingSelections: string[]): string[] {
- setHasModified(true);
-
if (existingSelections.includes(value)) {
return existingSelections.filter((v) => v !== value);
}
@@ -370,7 +388,6 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
function handleCustomPromptChange(value: string) {
setCustomPrompt(value);
- setHasModified(true);
}
function handleSave() {
@@ -430,11 +447,8 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
setHasModified(false);
}
- function handleModelSelect(model: string, userModification: boolean = true) {
+ function handleModelSelect(model: string) {
setSelectedModel(model);
- if (userModification) {
- setHasModified(true);
- }
}
return (
@@ -488,7 +502,7 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
diff --git a/src/interface/web/app/layout.tsx b/src/interface/web/app/layout.tsx
index b825f4ac..ce7056f4 100644
--- a/src/interface/web/app/layout.tsx
+++ b/src/interface/web/app/layout.tsx
@@ -48,7 +48,11 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+