diff --git a/src/interface/web/app/agents/page.tsx b/src/interface/web/app/agents/page.tsx index 35d23660..8b578de4 100644 --- a/src/interface/web/app/agents/page.tsx +++ b/src/interface/web/app/agents/page.tsx @@ -344,14 +344,16 @@ export default function Agents() { /> How it works Use any of these specialized personas to tune your conversation to your needs. - { - !isSubscribed && ( - - {" "} - Upgrade your plan to leverage custom models. You will fallback to the default model when chatting. - - ) - } + {!isSubscribed && ( + + {" "} + + Upgrade your plan + {" "} + to leverage custom models. You will fallback to the + default model when chatting. + + )}
diff --git a/src/interface/web/app/common/auth.ts b/src/interface/web/app/common/auth.ts index 4716fa13..defbf536 100644 --- a/src/interface/web/app/common/auth.ts +++ b/src/interface/web/app/common/auth.ts @@ -90,11 +90,9 @@ export interface UserConfig { export function useUserConfig(detailed: boolean = false) { const url = `/api/settings?detailed=${detailed}`; - const { - data, - error, - isLoading, - } = useSWR(url, fetcher, { revalidateOnFocus: false }); + const { data, error, isLoading } = useSWR(url, fetcher, { + revalidateOnFocus: false, + }); if (error || !data || data?.detail === "Forbidden") { return { data: null, error, isLoading }; diff --git a/src/interface/web/app/common/modelSelector.tsx b/src/interface/web/app/common/modelSelector.tsx index 62485dd8..4075d487 100644 --- a/src/interface/web/app/common/modelSelector.tsx +++ b/src/interface/web/app/common/modelSelector.tsx @@ -1,12 +1,12 @@ -"use client" +"use client"; -import * as React from "react" +import * as React from "react"; import { useState, useEffect } from "react"; -import { PopoverProps } from "@radix-ui/react-popover" +import { PopoverProps } from "@radix-ui/react-popover"; import { Check, CaretUpDown } from "@phosphor-icons/react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; import { useIsMobileWidth, useMutationObserver } from "@/app/common/utils"; import { Button } from "@/components/ui/button"; import { @@ -17,11 +17,7 @@ import { CommandItem, CommandList, } from "@/components/ui/command"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { ModelOptions, useUserConfig } from "./auth"; import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"; @@ -35,7 +31,7 @@ interface ModelSelectorProps extends PopoverProps { } export function ModelSelector({ ...props }: ModelSelectorProps) { - const [open, setOpen] = React.useState(false) + const [open, setOpen] = React.useState(false); const [peekedModel, setPeekedModel] = useState(undefined); const [selectedModel, setSelectedModel] = useState(undefined); const { data: userConfig, error, isLoading: isLoadingUserConfig } = useUserConfig(true); @@ -48,14 +44,18 @@ export function ModelSelector({ ...props }: ModelSelectorProps) { if (userConfig) { setModels(userConfig.chat_model_options); if (!props.initialModel) { - const selectedChatModelOption = userConfig.chat_model_options.find(model => model.id === userConfig.selected_chat_model_config); + const selectedChatModelOption = userConfig.chat_model_options.find( + (model) => model.id === userConfig.selected_chat_model_config, + ); if (!selectedChatModelOption && userConfig.chat_model_options.length > 0) { setSelectedModel(userConfig.chat_model_options[0]); } else { setSelectedModel(selectedChatModelOption); } } else { - const model = userConfig.chat_model_options.find(model => model.name === props.initialModel); + const model = userConfig.chat_model_options.find( + (model) => model.name === props.initialModel, + ); setSelectedModel(model); } } @@ -68,15 +68,11 @@ export function ModelSelector({ ...props }: ModelSelectorProps) { }, [selectedModel, userConfig, props.onSelect]); if (isLoadingUserConfig) { - return ( - - ); + return ; } if (error) { - return ( -
{error.message}
- ); + return
{error.message}
; } return ( @@ -92,30 +88,85 @@ export function ModelSelector({ ...props }: ModelSelectorProps) { disabled={props.disabled ?? false} >

- {selectedModel ? selectedModel.name?.substring(0, 20) : "Select a model..."} + {selectedModel + ? selectedModel.name?.substring(0, 20) + : "Select a model..."}

- { - isMobileWidth ? + {isMobileWidth ? ( +
+ + + + No Models found. + + {models && + models.length > 0 && + models.map((model) => ( + setPeekedModel(model)} + onSelect={() => { + setSelectedModel(model); + setOpen(false); + }} + isActive={props.isActive} + /> + ))} + + + +
+ ) : ( + + +
+

+ {peekedModel?.name} +

+
+ {peekedModel?.description} +
+ {peekedModel?.strengths ? ( +
+
+ Strengths +
+

+ {peekedModel.strengths} +

+
+ ) : null} +
+
+ No Models found. - {models && models.length > 0 && models - .map((model) => ( + {models && + models.length > 0 && + models.map((model) => ( setPeekedModel(model)} onSelect={() => { - setSelectedModel(model) - setOpen(false) + setSelectedModel(model); + setOpen(false); }} isActive={props.isActive} /> @@ -124,74 +175,24 @@ export function ModelSelector({ ...props }: ModelSelectorProps) {
- : - - -
-

{peekedModel?.name}

-
- {peekedModel?.description} -
- {peekedModel?.strengths ? ( -
-
- Strengths -
-

- {peekedModel.strengths} -

-
- ) : null} -
-
-
- - - - - No Models found. - - {models && models.length > 0 && models - .map((model) => ( - setPeekedModel(model)} - onSelect={() => { - setSelectedModel(model) - setOpen(false) - }} - isActive={props.isActive} - /> - ))} - - - -
-
- } +
+ )}
- ) + ); } interface ModelItemProps { - model: ModelOptions, - isSelected: boolean, - onSelect: () => void, - onPeek: (model: ModelOptions) => void - isActive?: boolean + model: ModelOptions; + isSelected: boolean; + onSelect: () => void; + onPeek: (model: ModelOptions) => void; + isActive?: boolean; } function ModelItem({ model, isSelected, onSelect, onPeek, isActive }: ModelItemProps) { - const ref = React.useRef(null) + const ref = React.useRef(null); useMutationObserver(ref, (mutations) => { mutations.forEach((mutation) => { @@ -200,10 +201,10 @@ function ModelItem({ model, isSelected, onSelect, onPeek, isActive }: ModelItemP mutation.attributeName === "aria-selected" && ref.current?.getAttribute("aria-selected") === "true" ) { - onPeek(model) + onPeek(model); } - }) - }) + }); + }); return ( - {model.name} {model.tier === "standard" && (Futurist)} - + {model.name}{" "} + {model.tier === "standard" && (Futurist)} + - ) + ); } diff --git a/src/interface/web/app/common/utils.ts b/src/interface/web/app/common/utils.ts index b3520c00..d5cba0cf 100644 --- a/src/interface/web/app/common/utils.ts +++ b/src/interface/web/app/common/utils.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import useSWR from "swr"; -import * as React from "react" +import * as React from "react"; export interface LocationData { city?: string; @@ -78,16 +78,16 @@ export const useMutationObserver = ( characterData: true, childList: true, subtree: true, - } + }, ) => { React.useEffect(() => { if (ref.current) { - const observer = new MutationObserver(callback) - observer.observe(ref.current, options) - return () => observer.disconnect() + const observer = new MutationObserver(callback); + observer.observe(ref.current, options); + return () => observer.disconnect(); } - }, [ref, callback, options]) -} + }, [ref, callback, options]); +}; export function useIsDarkMode() { const [darkMode, setDarkMode] = useState(false); diff --git a/src/interface/web/app/components/agentCard/agentCard.tsx b/src/interface/web/app/components/agentCard/agentCard.tsx index da7e8889..8e739d57 100644 --- a/src/interface/web/app/components/agentCard/agentCard.tsx +++ b/src/interface/web/app/components/agentCard/agentCard.tsx @@ -1061,12 +1061,27 @@ export function AgentModificationForm(props: AgentModificationFormProps) { className="h-6 px-2 text-xs" onClick={(e) => { e.stopPropagation(); - const filteredFiles = allFileOptions.filter(file => - file.toLowerCase().includes(fileSearchValue.toLowerCase()) + const filteredFiles = + allFileOptions.filter((file) => + file + .toLowerCase() + .includes( + fileSearchValue.toLowerCase(), + ), + ); + const currentFiles = + props.form.getValues("files") || + []; + const newFiles = [ + ...new Set([ + ...currentFiles, + ...filteredFiles, + ]), + ]; + props.form.setValue( + "files", + newFiles, ); - const currentFiles = props.form.getValues("files") || []; - const newFiles = [...new Set([...currentFiles, ...filteredFiles])]; - props.form.setValue("files", newFiles); }} > Select All @@ -1078,12 +1093,28 @@ export function AgentModificationForm(props: AgentModificationFormProps) { className="h-6 px-2 text-xs" onClick={(e) => { e.stopPropagation(); - const filteredFiles = allFileOptions.filter(file => - file.toLowerCase().includes(fileSearchValue.toLowerCase()) + const filteredFiles = + allFileOptions.filter((file) => + file + .toLowerCase() + .includes( + fileSearchValue.toLowerCase(), + ), + ); + const currentFiles = + props.form.getValues("files") || + []; + const newFiles = + currentFiles.filter( + (file) => + !filteredFiles.includes( + file, + ), + ); + props.form.setValue( + "files", + newFiles, ); - const currentFiles = props.form.getValues("files") || []; - const newFiles = currentFiles.filter(file => !filteredFiles.includes(file)); - props.form.setValue("files", newFiles); }} > Deselect All diff --git a/src/interface/web/app/components/allConversations/allConversations.tsx b/src/interface/web/app/components/allConversations/allConversations.tsx index 0d1c1b88..a8ff3702 100644 --- a/src/interface/web/app/components/allConversations/allConversations.tsx +++ b/src/interface/web/app/components/allConversations/allConversations.tsx @@ -127,7 +127,7 @@ function renameConversation(conversationId: string, newTitle: string) { }, }) .then((response) => response.json()) - .then((data) => { }) + .then((data) => {}) .catch((err) => { console.error(err); return; @@ -171,7 +171,7 @@ function deleteConversation(conversationId: string) { mutate("/api/chat/sessions"); } }) - .then((data) => { }) + .then((data) => {}) .catch((err) => { console.error(err); return; @@ -245,9 +245,7 @@ export function FilesMenu(props: FilesMenuProps) { Context

- { - error ? "Failed to load files" : "Failed to load selected files" - } + {error ? "Failed to load files" : "Failed to load selected files"}

@@ -257,7 +255,7 @@ export function FilesMenu(props: FilesMenuProps) { - ) + ); } if (!files) return ; @@ -443,10 +441,7 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
{props.sideBarOpen && ( - +
{props.subsetOrganizedData != null && Object.keys(props.subsetOrganizedData) @@ -471,7 +466,9 @@ function SessionsAndFiles(props: SessionsAndFilesProps) { agent_name={chatHistory.agent_name} agent_color={chatHistory.agent_color} agent_icon={chatHistory.agent_icon} - agent_is_hidden={chatHistory.agent_is_hidden} + agent_is_hidden={ + chatHistory.agent_is_hidden + } /> ), )} @@ -709,7 +706,7 @@ function ChatSession(props: ChatHistory) { className="flex items-center gap-2 no-underline" >

{title}

diff --git a/src/interface/web/app/components/appSidebar/appSidebar.tsx b/src/interface/web/app/components/appSidebar/appSidebar.tsx index 85901371..94b3b3ea 100644 --- a/src/interface/web/app/components/appSidebar/appSidebar.tsx +++ b/src/interface/web/app/components/appSidebar/appSidebar.tsx @@ -48,13 +48,12 @@ async function openChat(userData: UserProfile | null | undefined) { } } - // Menu items. const items = [ { title: "Home", url: "/", - icon: HouseSimple + icon: HouseSimple, }, { title: "Agents", diff --git a/src/interface/web/app/components/chatHistory/chatHistory.tsx b/src/interface/web/app/components/chatHistory/chatHistory.tsx index 18810a93..bb61c9f7 100644 --- a/src/interface/web/app/components/chatHistory/chatHistory.tsx +++ b/src/interface/web/app/components/chatHistory/chatHistory.tsx @@ -52,7 +52,7 @@ interface TrainOfThoughtFrame { } interface TrainOfThoughtGroup { - type: 'video' | 'text'; + type: "video" | "text"; frames?: TrainOfThoughtFrame[]; textEntries?: TrainOfThoughtObject[]; } @@ -65,7 +65,9 @@ interface TrainOfThoughtComponentProps { completed?: boolean; } -function extractTrainOfThoughtGroups(trainOfThought?: TrainOfThoughtObject[]): TrainOfThoughtGroup[] { +function extractTrainOfThoughtGroups( + trainOfThought?: TrainOfThoughtObject[], +): TrainOfThoughtGroup[] { if (!trainOfThought) return []; const groups: TrainOfThoughtGroup[] = []; @@ -94,8 +96,8 @@ function extractTrainOfThoughtGroups(trainOfThought?: TrainOfThoughtObject[]): T // If we have accumulated text entries, add them as a text group if (currentTextEntries.length > 0) { groups.push({ - type: 'text', - textEntries: [...currentTextEntries] + type: "text", + textEntries: [...currentTextEntries], }); currentTextEntries = []; } @@ -116,8 +118,8 @@ function extractTrainOfThoughtGroups(trainOfThought?: TrainOfThoughtObject[]): T // If we have accumulated video frames, add them as a video group if (currentVideoFrames.length > 0) { groups.push({ - type: 'video', - frames: [...currentVideoFrames] + type: "video", + frames: [...currentVideoFrames], }); currentVideoFrames = []; } @@ -130,14 +132,14 @@ function extractTrainOfThoughtGroups(trainOfThought?: TrainOfThoughtObject[]): T // Add any remaining frames/entries if (currentVideoFrames.length > 0) { groups.push({ - type: 'video', - frames: currentVideoFrames + type: "video", + frames: currentVideoFrames, }); } if (currentTextEntries.length > 0) { groups.push({ - type: 'text', - textEntries: currentTextEntries + type: "text", + textEntries: currentTextEntries, }); } @@ -177,10 +179,10 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) { // Convert string array to TrainOfThoughtObject array if needed let trainOfThoughtObjects: TrainOfThoughtObject[]; - if (typeof props.trainOfThought[0] === 'string') { + if (typeof props.trainOfThought[0] === "string") { trainOfThoughtObjects = (props.trainOfThought as string[]).map((data, index) => ({ - type: 'text', - data: data + type: "text", + data: data, })); } else { trainOfThoughtObjects = props.trainOfThought as TrainOfThoughtObject[]; @@ -221,28 +223,37 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) { {trainOfThoughtGroups.map((group, groupIndex) => (
- {group.type === 'video' && group.frames && group.frames.length > 0 && ( - - )} - {group.type === 'text' && group.textEntries && group.textEntries.map((entry, entryIndex) => { - const lastIndex = trainOfThoughtGroups.length - 1; - const isLastGroup = groupIndex === lastIndex; - const isLastEntry = entryIndex === group.textEntries!.length - 1; - const isPrimaryEntry = isLastGroup && isLastEntry && props.lastMessage && !props.completed; - - return ( - 0 && ( + - ); - })} + )} + {group.type === "text" && + group.textEntries && + group.textEntries.map((entry, entryIndex) => { + const lastIndex = trainOfThoughtGroups.length - 1; + const isLastGroup = groupIndex === lastIndex; + const isLastEntry = + entryIndex === group.textEntries!.length - 1; + const isPrimaryEntry = + isLastGroup && + isLastEntry && + props.lastMessage && + !props.completed; + + return ( + + ); + })}
))}
@@ -300,7 +311,8 @@ export default function ChatHistory(props: ChatHistoryProps) { // ResizeObserver to handle content height changes (e.g., images loading) useEffect(() => { const contentWrapper = scrollableContentWrapperRef.current; - const scrollViewport = scrollAreaRef.current?.querySelector(scrollAreaSelector); + const scrollViewport = + scrollAreaRef.current?.querySelector(scrollAreaSelector); if (!contentWrapper || !scrollViewport) return; @@ -308,14 +320,18 @@ export default function ChatHistory(props: ChatHistoryProps) { // Check current scroll position to decide if auto-scroll is warranted const { scrollTop, scrollHeight, clientHeight } = scrollViewport; const bottomThreshold = 50; - const currentlyNearBottom = (scrollHeight - (scrollTop + clientHeight)) <= bottomThreshold; + const currentlyNearBottom = + scrollHeight - (scrollTop + clientHeight) <= bottomThreshold; if (currentlyNearBottom) { // Only auto-scroll if there are incoming messages being processed if (props.incomingMessages && props.incomingMessages.length > 0) { const lastMessage = props.incomingMessages[props.incomingMessages.length - 1]; // If the last message is not completed, or it just completed (indicated by incompleteIncomingMessageIndex still being set) - if (!lastMessage.completed || (lastMessage.completed && incompleteIncomingMessageIndex !== null)) { + if ( + !lastMessage.completed || + (lastMessage.completed && incompleteIncomingMessageIndex !== null) + ) { scrollToBottom(true); // Use instant scroll } } @@ -463,7 +479,12 @@ export default function ChatHistory(props: ChatHistoryProps) { }); }); // Optimistically set, the scroll listener will verify - if (instant || scrollAreaEl && (scrollAreaEl.scrollHeight - (scrollAreaEl.scrollTop + scrollAreaEl.clientHeight)) < 5) { + if ( + instant || + (scrollAreaEl && + scrollAreaEl.scrollHeight - (scrollAreaEl.scrollTop + scrollAreaEl.clientHeight) < + 5) + ) { setIsNearBottom(true); } }; @@ -626,16 +647,19 @@ export default function ChatHistory(props: ChatHistoryProps) { conversationId={props.conversationId} turnId={messageTurnId} /> - {message.trainOfThought && message.trainOfThought.length > 0 && ( - t.length).join('-')}`} - keyId={`${index}trainOfThought`} - completed={message.completed} - /> - )} + {message.trainOfThought && + message.trainOfThought.length > 0 && ( + t.length).join("-")}`} + keyId={`${index}trainOfThought`} + completed={message.completed} + /> + )} ((props, ref) => /> )} - {props.chatMessage.by === "khoj" && props.onRetryMessage && props.isLastMessage && ( - - )} + }} + > + + + )} - { - doneCreating && createdSlug ? ( - - Created {customAgentName} - - ) : ( - - Create a New Agent - - ) - } + {doneCreating && createdSlug ? ( + Created {customAgentName} + ) : ( + Create a New Agent + )} - If these settings have been helpful, create a dedicated agent you can re-use across conversations. + If these settings have been helpful, create a dedicated agent you can re-use + across conversations.
- { - doneCreating && createdSlug ? ( -
- - - - - Created successfully! - - - - - - + {doneCreating && createdSlug ? ( +
+ + + + + Created successfully! + + + + + + +
+ ) : ( +
+
+ + setCustomAgentName(e.target.value)} + />
- ) : -
-
- - setCustomAgentName(e.target.value)} - /> +
+
+
-
-
- -
-
- -
+
+
- } +
+ )}
- { - error && ( -
- {error} -
- ) - } - { - !doneCreating && ( - - ) - } + {error &&
{error}
} + {!doneCreating && ( + + )}
- - - ) + + ); } function ChatSidebarInternal({ ...props }: ChatSideBarProps) { @@ -305,7 +326,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { const { data: agentConfigurationOptions, error: agentConfigurationOptionsError } = useSWR("/api/agents/options", fetcher); - const { data: agentData, isLoading: agentDataLoading, error: agentDataError } = useSWR(`/api/agents/conversation?conversation_id=${props.conversationId}`, fetcher); + const { + data: agentData, + isLoading: agentDataLoading, + error: agentDataError, + } = useSWR( + `/api/agents/conversation?conversation_id=${props.conversationId}`, + fetcher, + ); const { data: authenticatedData, error: authenticationError, @@ -317,7 +345,9 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { const [inputTools, setInputTools] = useState(); const [outputModes, setOutputModes] = useState(); const [hasModified, setHasModified] = useState(false); - const [isDefaultAgent, setIsDefaultAgent] = useState(!agentData || agentData?.slug?.toLowerCase() === "khoj"); + const [isDefaultAgent, setIsDefaultAgent] = useState( + !agentData || agentData?.slug?.toLowerCase() === "khoj", + ); const [displayInputTools, setDisplayInputTools] = useState(); const [displayOutputModes, setDisplayOutputModes] = useState(); @@ -330,12 +360,20 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { setInputTools(agentData.input_tools); setDisplayInputTools(agentData.input_tools); if (agentData.input_tools === undefined || agentData.input_tools.length === 0) { - setDisplayInputTools(agentConfigurationOptions?.input_tools ? Object.keys(agentConfigurationOptions.input_tools) : []); + setDisplayInputTools( + agentConfigurationOptions?.input_tools + ? Object.keys(agentConfigurationOptions.input_tools) + : [], + ); } setOutputModes(agentData.output_modes); setDisplayOutputModes(agentData.output_modes); if (agentData.output_modes === undefined || agentData.output_modes.length === 0) { - setDisplayOutputModes(agentConfigurationOptions?.output_modes ? Object.keys(agentConfigurationOptions.output_modes) : []); + setDisplayOutputModes( + agentConfigurationOptions?.output_modes + ? Object.keys(agentConfigurationOptions.output_modes) + : [], + ); } if (agentData.name?.toLowerCase() === "khoj" || agentData.is_hidden === true) { @@ -367,8 +405,12 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { 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()); + 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); @@ -394,7 +436,9 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { function handleSave() { if (hasModified) { if (!isDefaultAgent && agentData?.is_hidden === false) { - alert("This agent is not a hidden agent. It cannot be modified from this interface."); + alert( + "This agent is not a hidden agent. It cannot be modified from this interface.", + ); return; } @@ -409,12 +453,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { chat_model: selectedModel, input_tools: inputTools, output_modes: outputModes, - ...(isDefaultAgent ? {} : { slug: agentData?.slug }) + ...(isDefaultAgent ? {} : { slug: agentData?.slug }), }; setIsSaving(true); - const url = !isDefaultAgent ? `/api/agents/hidden` : `/api/agents/hidden?conversation_id=${props.conversationId}`; + const url = !isDefaultAgent + ? `/api/agents/hidden` + : `/api/agents/hidden?conversation_id=${props.conversationId}`; // There are four scenarios here. // 1. If the agent is a default agent, then we need to create a new agent just to associate with this conversation. @@ -424,13 +470,13 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { fetch(url, { method: mode, headers: { - "Content-Type": "application/json" + "Content-Type": "application/json", }, - body: JSON.stringify(data) + body: JSON.stringify(data), }) .then((res) => { setIsSaving(false); - res.json() + res.json(); }) .then((data) => { mutate(`/api/agents/conversation?conversation_id=${props.conversationId}`); @@ -456,43 +502,47 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { + variant="floating" + > - { - agentData && !isEditable ? ( - - ) : ( -
-

- Chat Options -

-
- ) - } + {agentData && !isEditable ? ( + + ) : ( +
+

Chat Options

+
+ )}
- { - agentData && agentData.has_files ? ( - -
-
-
Using custom knowledge base
+ {agentData && agentData.has_files ? ( + +
+
+
- - ) : null - } +
+ Using custom knowledge base +
+
+
+ ) : null} @@ -506,39 +556,41 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { value={customPrompt || ""} onChange={(e) => handleCustomPromptChange(e.target.value)} readOnly={!isEditable} - disabled={!isEditable} /> + disabled={!isEditable} + /> - { - !agentDataLoading && agentData && ( - - - - Model - { - !isSubscribed && ( - - Upgrade - - ) - } - - - - handleModelSelect(model.name)} - initialModel={isDefaultAgent ? undefined : agentData?.chat_model} - isActive={props.isActive} - /> - - - - - ) - } + {!agentDataLoading && agentData && ( + + + + Model + {!isSubscribed && ( + + Upgrade + + )} + + + + handleModelSelect(model.name)} + initialModel={ + isDefaultAgent ? undefined : agentData?.chat_model + } + isActive={props.isActive} + /> + + + + + )} @@ -550,82 +602,118 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { - { - Object.entries(agentConfigurationOptions?.input_tools ?? {}).map(([key, value]) => { - return ( - - - -
- - { - let updatedInputTools = handleCheckToggle(key, displayInputTools ?? []) - setInputTools(updatedInputTools); - setDisplayInputTools(updatedInputTools); - }} - disabled={!isEditable} - > + {Object.entries( + agentConfigurationOptions?.input_tools ?? {}, + ).map(([key, value]) => { + return ( + + + +
+
-
- - {value} - -
-
- ); - } - ) - } - { - Object.entries(agentConfigurationOptions?.output_modes ?? {}).map(([key, value]) => { - return ( - - - -
- - { - let updatedOutputModes = handleCheckToggle(key, displayOutputModes ?? []) - setOutputModes(updatedOutputModes); - setDisplayOutputModes(updatedOutputModes); - }} - disabled={!isEditable} - > +

+ + { + let updatedInputTools = + handleCheckToggle( + key, + displayInputTools ?? [], + ); + setInputTools( + updatedInputTools, + ); + setDisplayInputTools( + updatedInputTools, + ); + }} + disabled={!isEditable} + > + {key} + +
+
+ + {value} + +
+
+ ); + })} + {Object.entries( + agentConfigurationOptions?.output_modes ?? {}, + ).map(([key, value]) => { + return ( + + + +
+
-
- - {value} - -
-
- ); - } - ) - } +

+ + { + let updatedOutputModes = + handleCheckToggle( + key, + displayOutputModes ?? + [], + ); + setOutputModes( + updatedOutputModes, + ); + setDisplayOutputModes( + updatedOutputModes, + ); + }} + disabled={!isEditable} + > + {key} + +
+
+ + {value} + +
+
+ ); + })}
-
@@ -645,79 +733,75 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) { - { - props.isOpen && ( - - - - { - (agentData && !isEditable && agentData.is_creator) ? ( - - - - - - ) : - <> - { - !hasModified && isEditable && customPrompt && !isDefaultAgent && selectedModel && ( - - - - - - ) + {props.isOpen && ( + + + {agentData && !isEditable && agentData.is_creator ? ( + + + + + + ) : ( + <> + {!hasModified && + isEditable && + customPrompt && + !isDefaultAgent && + selectedModel && ( - + - - - - - - - - } - - - ) - } + )} + + + + + + + + + + + + )} + + + )} - ) + ); } diff --git a/src/interface/web/app/components/mermaid/mermaid.tsx b/src/interface/web/app/components/mermaid/mermaid.tsx index bdbbe6a3..1d35aea9 100644 --- a/src/interface/web/app/components/mermaid/mermaid.tsx +++ b/src/interface/web/app/components/mermaid/mermaid.tsx @@ -147,9 +147,7 @@ const Mermaid: React.FC = ({ chart }) => { {mermaidError}
- { - chart - } + {chart} ) : ( diff --git a/src/interface/web/app/components/navMenu/navMenu.tsx b/src/interface/web/app/components/navMenu/navMenu.tsx index f86cf6ee..c64e3e08 100644 --- a/src/interface/web/app/components/navMenu/navMenu.tsx +++ b/src/interface/web/app/components/navMenu/navMenu.tsx @@ -12,7 +12,15 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { Moon, Sun, UserCircle, Question, ArrowRight, Code, BuildingOffice } from "@phosphor-icons/react"; +import { + Moon, + Sun, + UserCircle, + Question, + ArrowRight, + Code, + BuildingOffice, +} from "@phosphor-icons/react"; import { useIsDarkMode, useIsMobileWidth } from "@/app/common/utils"; import LoginPrompt from "../loginPrompt/loginPrompt"; import { Button } from "@/components/ui/button"; @@ -69,7 +77,7 @@ export default function FooterMenu({ sideBarIsOpen }: NavMenuProps) { icon: , link: "https://khoj.dev/teams", }, - ] + ]; return ( @@ -131,18 +139,16 @@ export default function FooterMenu({ sideBarIsOpen }: NavMenuProps) {

- { - menuItems.map((menuItem, index) => ( - - -
- {menuItem.icon} -

{menuItem.title}

-
- -
- )) - } + {menuItems.map((menuItem, index) => ( + + +
+ {menuItem.icon} +

{menuItem.title}

+
+ +
+ ))} {!userData ? ( - setInputText(e.currentTarget.value)} /> + setInputText(e.currentTarget.value)} + /> No files found. @@ -614,7 +618,6 @@ export default function Search() { setSelectedFileFilter("INITIALIZE"); } } - }, [searchQuery]); function handleSearchInputChange(value: string) { diff --git a/src/interface/web/app/settings/page.tsx b/src/interface/web/app/settings/page.tsx index 5983c591..92d13ecf 100644 --- a/src/interface/web/app/settings/page.tsx +++ b/src/interface/web/app/settings/page.tsx @@ -23,8 +23,15 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { - AlertDialog, AlertDialogAction, AlertDialogCancel, - AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table"; @@ -72,7 +79,7 @@ import { KhojLogoType } from "../components/logo/khojLogo"; import { Progress } from "@/components/ui/progress"; import JSZip from "jszip"; -import { saveAs } from 'file-saver'; +import { saveAs } from "file-saver"; interface DropdownComponentProps { items: ModelOptions[]; @@ -81,7 +88,12 @@ interface DropdownComponentProps { callbackFunc: (value: string) => Promise; } -const DropdownComponent: React.FC = ({ items, selected, isActive, callbackFunc }) => { +const DropdownComponent: React.FC = ({ + items, + selected, + isActive, + callbackFunc, +}) => { const [position, setPosition] = useState(selected?.toString() ?? "0"); return ( @@ -114,7 +126,10 @@ const DropdownComponent: React.FC = ({ items, selected, value={item.id.toString()} disabled={!isActive && item.tier !== "free"} > - {item.name} {item.tier === "standard" && (Futurist)} + {item.name}{" "} + {item.tier === "standard" && ( + (Futurist) + )} ))} @@ -327,8 +342,8 @@ export default function SettingsView() { initialUserConfig?.is_phone_number_verified ? PhoneNumberValidationState.Verified : initialUserConfig?.phone_number - ? PhoneNumberValidationState.SendOTP - : PhoneNumberValidationState.Setup, + ? PhoneNumberValidationState.SendOTP + : PhoneNumberValidationState.Setup, ); setName(initialUserConfig?.given_name); setNotionToken(initialUserConfig?.notion_token ?? null); @@ -524,13 +539,14 @@ export default function SettingsView() { const updateModel = (modelType: string) => async (id: string) => { // Get the selected model from the options - const modelOptions = modelType === "chat" - ? userConfig?.chat_model_options - : modelType === "paint" - ? userConfig?.paint_model_options - : userConfig?.voice_model_options; + const modelOptions = + modelType === "chat" + ? userConfig?.chat_model_options + : modelType === "paint" + ? userConfig?.paint_model_options + : userConfig?.voice_model_options; - const selectedModel = modelOptions?.find(model => model.id.toString() === id); + const selectedModel = modelOptions?.find((model) => model.id.toString() === id); const modelName = selectedModel?.name; // Check if the model is free tier or if the user is active @@ -551,7 +567,8 @@ export default function SettingsView() { }, }); - if (!response.ok) throw new Error(`Failed to switch ${modelType} model to ${modelName}`); + if (!response.ok) + throw new Error(`Failed to switch ${modelType} model to ${modelName}`); toast({ title: `✅ Switched ${modelType} model to ${modelName}`, @@ -570,7 +587,7 @@ export default function SettingsView() { setIsExporting(true); // Get total conversation count - const statsResponse = await fetch('/api/chat/stats'); + const statsResponse = await fetch("/api/chat/stats"); const stats = await statsResponse.json(); const total = stats.num_conversations; setTotalConversations(total); @@ -586,7 +603,7 @@ export default function SettingsView() { conversations.push(...data); setExportedConversations((page + 1) * 10); - setExportProgress(((page + 1) * 10 / total) * 100); + setExportProgress((((page + 1) * 10) / total) * 100); } // Add conversations to zip @@ -605,7 +622,7 @@ export default function SettingsView() { toast({ title: "Export Failed", description: "Failed to export chats. Please try again.", - variant: "destructive" + variant: "destructive", }); } finally { setIsExporting(false); @@ -808,93 +825,93 @@ export default function SettingsView() { )) || (userConfig.subscription_state === "subscribed" && ( - <> -

- Futurist -

-

- Subscription renews on{" "} - - { - userConfig.subscription_renewal_date - } - -

- - )) || + <> +

+ Futurist +

+

+ Subscription renews on{" "} + + { + userConfig.subscription_renewal_date + } + +

+ + )) || (userConfig.subscription_state === "unsubscribed" && ( - <> -

Futurist

+ <> +

Futurist

+

+ Subscription ends on{" "} + + { + userConfig.subscription_renewal_date + } + +

+ + )) || + (userConfig.subscription_state === + "expired" && ( + <> +

Humanist

+ {(userConfig.subscription_renewal_date && (

- Subscription ends on{" "} + Subscription expired{" "} + on{" "} { userConfig.subscription_renewal_date }

- - )) || - (userConfig.subscription_state === - "expired" && ( - <> -

Humanist

- {(userConfig.subscription_renewal_date && ( -

- Subscription expired{" "} - on{" "} - - { - userConfig.subscription_renewal_date - } - -

- )) || ( -

- Check{" "} - - pricing page - {" "} - to compare plans. -

- )} - - ))} + )) || ( +

+ Check{" "} + + pricing page + {" "} + to compare plans. +

+ )} + + ))} {(userConfig.subscription_state == "subscribed" && ( - - )) || + + )) || (userConfig.subscription_state == "unsubscribed" && ( - - )) || + + )) || (userConfig.subscription_enabled_trial_at && ( - ) : /* Show set API key button notion oauth url not set setup */ - !userConfig.notion_oauth_url ? ( - - ) : ( - <> - ) + userConfig.enabled_content_source.notion && + notionToken === + userConfig.notion_token ? ( + + ) : /* Show set API key button notion oauth url not set setup */ + !userConfig.notion_oauth_url ? ( + + ) : ( + <> + ) } @@ -1245,7 +1277,11 @@ export default function SettingsView() {

- This will delete all your account data, including conversations, agents, and any assets you{"'"}ve generated. Be sure to export before you do this if you want to keep your information. + This will delete all your account data, + including conversations, agents, and any + assets you{"'"}ve generated. Be sure to + export before you do this if you want to + keep your information.

@@ -1261,36 +1297,56 @@ export default function SettingsView() { - Are you absolutely sure? + + Are you absolutely sure? + - This action is irreversible. This will permanently delete your account - and remove all your data from our servers. + This action is irreversible. + This will permanently delete + your account and remove all your + data from our servers. - Cancel + + Cancel + { try { - const response = await fetch('/api/self', { - method: 'DELETE' - }); - if (!response.ok) throw new Error('Failed to delete account'); + const response = + await fetch( + "/api/self", + { + method: "DELETE", + }, + ); + if (!response.ok) + throw new Error( + "Failed to delete account", + ); toast({ title: "Account Deleted", - description: "Your account has been successfully deleted.", + description: + "Your account has been successfully deleted.", }); // Redirect to home page after successful deletion - window.location.href = "/"; + window.location.href = + "/"; } catch (error) { - console.error('Error deleting account:', error); + console.error( + "Error deleting account:", + error, + ); toast({ title: "Error", - description: "Failed to delete account. Please try again or contact support.", - variant: "destructive" + description: + "Failed to delete account. Please try again or contact support.", + variant: + "destructive", }); } }} diff --git a/src/interface/web/components/ui/checkbox.tsx b/src/interface/web/components/ui/checkbox.tsx index ea65ec4d..b5fdc1fe 100644 --- a/src/interface/web/components/ui/checkbox.tsx +++ b/src/interface/web/components/ui/checkbox.tsx @@ -1,30 +1,30 @@ -"use client" +"use client"; -import * as React from "react" -import * as CheckboxPrimitive from "@radix-ui/react-checkbox" -import { Check } from "lucide-react" +import * as React from "react"; +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import { Check } from "lucide-react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Checkbox = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - - - -)) -Checkbox.displayName = CheckboxPrimitive.Root.displayName + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; -export { Checkbox } +export { Checkbox }; diff --git a/src/interface/web/components/ui/hover-card.tsx b/src/interface/web/components/ui/hover-card.tsx index e54d91cf..8cc87bbe 100644 --- a/src/interface/web/components/ui/hover-card.tsx +++ b/src/interface/web/components/ui/hover-card.tsx @@ -1,29 +1,29 @@ -"use client" +"use client"; -import * as React from "react" -import * as HoverCardPrimitive from "@radix-ui/react-hover-card" +import * as React from "react"; +import * as HoverCardPrimitive from "@radix-ui/react-hover-card"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; -const HoverCard = HoverCardPrimitive.Root +const HoverCard = HoverCardPrimitive.Root; -const HoverCardTrigger = HoverCardPrimitive.Trigger +const HoverCardTrigger = HoverCardPrimitive.Trigger; const HoverCardContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( - -)) -HoverCardContent.displayName = HoverCardPrimitive.Content.displayName + +)); +HoverCardContent.displayName = HoverCardPrimitive.Content.displayName; -export { HoverCard, HoverCardTrigger, HoverCardContent } +export { HoverCard, HoverCardTrigger, HoverCardContent }; diff --git a/src/interface/web/components/ui/pagination.tsx b/src/interface/web/components/ui/pagination.tsx index c5c99bea..75871b68 100644 --- a/src/interface/web/components/ui/pagination.tsx +++ b/src/interface/web/components/ui/pagination.tsx @@ -1,118 +1,99 @@ -import * as React from "react" -import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" +import * as React from "react"; +import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"; -import { cn } from "@/lib/utils" -import { ButtonProps, buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils"; +import { ButtonProps, buttonVariants } from "@/components/ui/button"; const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( -