Skip to content

Commit

Permalink
Merge pull request #1094 from khoj-ai/features/add-chat-controls
Browse files Browse the repository at this point in the history
Make it easier to determine which model you're chatting with, and to effortlessly update said model from within a given chat. 

In this change, we introduce a side bar that allows users to quickly change their chat model, tools, custom instructions, and file filters, directly within the chat view. This removes the need for setting up custom agents for simple instructions and mitigates the requirement to go to the settings page to verify the chat model in action.

The settings page will still configure a per-user *default*, but the sidebar will allow for greater customization based on the needs of a conversation.

We also extend the chat model to include more attributes that help users make decisions about model selection, including `strengths` and `description`. This can help people quickly understand which model might work best for their use case.
  • Loading branch information
sabaimran authored Feb 1, 2025
2 parents f2eba66 + c558bbf commit 60e6913
Show file tree
Hide file tree
Showing 39 changed files with 1,743 additions and 340 deletions.
21 changes: 4 additions & 17 deletions src/interface/web/app/agents/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
AgentCard,
EditAgentSchema,
AgentModificationForm,
AgentData,
} from "@/app/components/agentCard/agentCard";

import { useForm } from "react-hook-form";
Expand All @@ -35,21 +36,6 @@ import { Separator } from "@/components/ui/separator";
import { KhojLogoType } from "../components/logo/khojLogo";
import { DialogTitle } from "@radix-ui/react-dialog";

export interface AgentData {
slug: string;
name: string;
persona: string;
color: string;
icon: string;
privacy_level: string;
files?: string[];
creator?: string;
managed_by_admin: boolean;
chat_model: string;
input_tools: string[];
output_modes: string[];
}

const agentsFetcher = () =>
window
.fetch("/api/agents")
Expand Down Expand Up @@ -171,7 +157,7 @@ function CreateAgentCard(props: CreateAgentCardProps) {
);
}

interface AgentConfigurationOptions {
export interface AgentConfigurationOptions {
input_tools: { [key: string]: string };
output_modes: { [key: string]: string };
}
Expand All @@ -185,7 +171,7 @@ export default function Agents() {
error: authenticationError,
isLoading: authenticationLoading,
} = useAuthenticatedData();
const { userConfig } = useUserConfig(true);
const { data: userConfig } = useUserConfig(true);
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
const isMobileWidth = useIsMobileWidth();

Expand Down Expand Up @@ -321,6 +307,7 @@ export default function Agents() {
chat_model: "",
input_tools: [],
output_modes: [],
is_hidden: false,
}}
userProfile={
authenticationLoading
Expand Down
4 changes: 2 additions & 2 deletions src/interface/web/app/chat/chat.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ div.inputBox:focus {
div.chatBodyFull {
display: grid;
grid-template-columns: 1fr;
height: 100%;
height: auto;
}

button.inputBox {
Expand Down Expand Up @@ -83,7 +83,7 @@ div.titleBar {
div.chatBoxBody {
display: grid;
height: 100%;
width: 95%;
width: 100%;
margin: auto;
}

Expand Down
92 changes: 60 additions & 32 deletions src/interface/web/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ import {
ChatOptions,
} from "../components/chatInputArea/chatInputArea";
import { useAuthenticatedData } from "../common/auth";
import { AgentData } from "../agents/page";
import {
AgentData,
} from "@/app/components/agentCard/agentCard";
import { ChatSessionActionMenu } from "../components/allConversations/allConversations";
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { AppSidebar } from "../components/appSidebar/appSidebar";
import { Separator } from "@/components/ui/separator";
import { KhojLogoType } from "../components/logo/khojLogo";
import { Button } from "@/components/ui/button";
import { Joystick } from "@phosphor-icons/react";
import { ChatSidebar } from "../components/chatSidebar/chatSidebar";

interface ChatBodyDataProps {
chatOptionsData: ChatOptions | null;
Expand All @@ -43,6 +48,8 @@ interface ChatBodyDataProps {
isLoggedIn: boolean;
setImages: (images: string[]) => void;
setTriggeredAbort: (triggeredAbort: boolean) => void;
isChatSideBarOpen: boolean;
setIsChatSideBarOpen: (open: boolean) => void;
}

function ChatBodyData(props: ChatBodyDataProps) {
Expand Down Expand Up @@ -138,37 +145,45 @@ function ChatBodyData(props: ChatBodyDataProps) {
}

return (
<>
<div className={false ? styles.chatBody : styles.chatBodyFull}>
<ChatHistory
conversationId={conversationId}
setTitle={props.setTitle}
setAgent={setAgentMetadata}
pendingMessage={processingMessage ? message : ""}
incomingMessages={props.streamedMessages}
setIncomingMessages={props.setStreamedMessages}
customClassName={chatHistoryCustomClassName}
/>
</div>
<div
className={`${styles.inputBox} p-1 md:px-2 shadow-md bg-background align-middle items-center justify-center dark:bg-neutral-700 dark:border-0 dark:shadow-sm rounded-2xl md:rounded-xl h-fit ${chatHistoryCustomClassName} mr-auto ml-auto`}
>
<ChatInputArea
agentColor={agentMetadata?.color}
isLoggedIn={props.isLoggedIn}
sendMessage={(message) => setMessage(message)}
sendImage={(image) => setImages((prevImages) => [...prevImages, image])}
sendDisabled={processingMessage}
chatOptionsData={props.chatOptionsData}
conversationId={conversationId}
isMobileWidth={props.isMobileWidth}
setUploadedFiles={props.setUploadedFiles}
ref={chatInputRef}
isResearchModeEnabled={isInResearchMode}
setTriggeredAbort={props.setTriggeredAbort}
/>
<div className="flex flex-row h-full w-full">
<div className="flex flex-col h-full w-full">
<div className={false ? styles.chatBody : styles.chatBodyFull}>
<ChatHistory
conversationId={conversationId}
setTitle={props.setTitle}
setAgent={setAgentMetadata}
pendingMessage={processingMessage ? message : ""}
incomingMessages={props.streamedMessages}
setIncomingMessages={props.setStreamedMessages}
customClassName={chatHistoryCustomClassName}
setIsChatSideBarOpen={props.setIsChatSideBarOpen}
/>
</div>
<div
className={`${styles.inputBox} p-1 md:px-2 shadow-md bg-background align-middle items-center justify-center dark:bg-neutral-700 dark:border-0 dark:shadow-sm rounded-2xl md:rounded-xl h-fit ${chatHistoryCustomClassName} mr-auto ml-auto mt-auto`}
>
<ChatInputArea
agentColor={agentMetadata?.color}
isLoggedIn={props.isLoggedIn}
sendMessage={(message) => setMessage(message)}
sendImage={(image) => setImages((prevImages) => [...prevImages, image])}
sendDisabled={processingMessage}
chatOptionsData={props.chatOptionsData}
conversationId={conversationId}
isMobileWidth={props.isMobileWidth}
setUploadedFiles={props.setUploadedFiles}
ref={chatInputRef}
isResearchModeEnabled={isInResearchMode}
setTriggeredAbort={props.setTriggeredAbort}
/>
</div>
</div>
</>
<ChatSidebar
conversationId={conversationId}
isOpen={props.isChatSideBarOpen}
onOpenChange={props.setIsChatSideBarOpen}
isMobileWidth={props.isMobileWidth} />
</div>
);
}

Expand Down Expand Up @@ -199,6 +214,7 @@ export default function Chat() {
isLoading: authenticationLoading,
} = useAuthenticatedData();
const isMobileWidth = useIsMobileWidth();
const [isChatSideBarOpen, setIsChatSideBarOpen] = useState(false);

useEffect(() => {
fetch("/api/chat/options")
Expand Down Expand Up @@ -432,6 +448,16 @@ export default function Chat() {
)}
</div>
)}
<div className="flex justify-end items-start gap-2 text-sm ml-auto">
<Button
variant="ghost"
size="icon"
className="h-12 w-12 data-[state=open]:bg-accent"
onClick={() => setIsChatSideBarOpen(!isChatSideBarOpen)}
>
<Joystick className="w-6 h-6" />
</Button>
</div>
</header>
<div className={`${styles.main} ${styles.chatLayout}`}>
<title>
Expand All @@ -452,12 +478,14 @@ export default function Chat() {
onConversationIdChange={handleConversationIdChange}
setImages={setImages}
setTriggeredAbort={setTriggeredAbort}
isChatSideBarOpen={isChatSideBarOpen}
setIsChatSideBarOpen={setIsChatSideBarOpen}
/>
</Suspense>
</div>
</div>
</div>
</SidebarInset>
</SidebarProvider>
</SidebarProvider >
);
}
21 changes: 16 additions & 5 deletions src/interface/web/app/common/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export function useAuthenticatedData() {
export interface ModelOptions {
id: number;
name: string;
description: string;
strengths: string;
}
export interface SyncedContent {
computer: boolean;
Expand Down Expand Up @@ -88,15 +90,24 @@ export interface UserConfig {
export function useUserConfig(detailed: boolean = false) {
const url = `/api/settings?detailed=${detailed}`;
const {
data: userConfig,
data,
error,
isLoading: isLoadingUserConfig,
isLoading,
} = useSWR<UserConfig>(url, fetcher, { revalidateOnFocus: false });

if (error || !userConfig || userConfig?.detail === "Forbidden")
return { userConfig: null, isLoadingUserConfig };
if (error || !data || data?.detail === "Forbidden") {
return { data: null, error, isLoading };
}

return { data, error, isLoading };
}

export function useChatModelOptions() {
const { data, error, isLoading } = useSWR<ModelOptions[]>(`/api/model/chat/options`, fetcher, {
revalidateOnFocus: false,
});

return { userConfig, isLoadingUserConfig };
return { models: data, error, isLoading };
}

export function isUserSubscribed(userConfig: UserConfig | null): boolean {
Expand Down
Loading

0 comments on commit 60e6913

Please sign in to comment.