mirror of
https://github.com/khoj-ai/khoj.git
synced 2024-11-27 17:35:07 +01:00
Automatically carry over research mode from home page to chat
- Improve mobile friendliness with new research mode toggle, since chat input area is now taking up more space - Remove clunky title from the suggestion card - Fix fk lookup error for agent.creator
This commit is contained in:
parent
ebaed53069
commit
1f1b182461
8 changed files with 62 additions and 55 deletions
|
@ -42,6 +42,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
||||||
const [images, setImages] = useState<string[]>([]);
|
const [images, setImages] = useState<string[]>([]);
|
||||||
const [processingMessage, setProcessingMessage] = useState(false);
|
const [processingMessage, setProcessingMessage] = useState(false);
|
||||||
const [agentMetadata, setAgentMetadata] = useState<AgentData | null>(null);
|
const [agentMetadata, setAgentMetadata] = useState<AgentData | null>(null);
|
||||||
|
const [isInResearchMode, setIsInResearchMode] = useState(false);
|
||||||
const chatInputRef = useRef<HTMLTextAreaElement>(null);
|
const chatInputRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
const setQueryToProcess = props.setQueryToProcess;
|
const setQueryToProcess = props.setQueryToProcess;
|
||||||
|
@ -70,6 +71,10 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
||||||
if (storedMessage) {
|
if (storedMessage) {
|
||||||
setProcessingMessage(true);
|
setProcessingMessage(true);
|
||||||
setQueryToProcess(storedMessage);
|
setQueryToProcess(storedMessage);
|
||||||
|
|
||||||
|
if (storedMessage.trim().startsWith("/research")) {
|
||||||
|
setIsInResearchMode(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [setQueryToProcess, props.setImages]);
|
}, [setQueryToProcess, props.setImages]);
|
||||||
|
|
||||||
|
@ -130,6 +135,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
||||||
isMobileWidth={props.isMobileWidth}
|
isMobileWidth={props.isMobileWidth}
|
||||||
setUploadedFiles={props.setUploadedFiles}
|
setUploadedFiles={props.setUploadedFiles}
|
||||||
ref={chatInputRef}
|
ref={chatInputRef}
|
||||||
|
isResearchModeEnabled={isInResearchMode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -71,7 +71,7 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) {
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
className="w-fit text-left justify-start content-start text-xs p-0"
|
className="w-fit text-left justify-start content-start text-xs p-0 h-fit"
|
||||||
onClick={() => setCollapsed(true)}
|
onClick={() => setCollapsed(true)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@ -283,13 +283,11 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea className={`h-[80vh] relative`} ref={scrollAreaRef}>
|
<ScrollArea className={`h-[73vh] relative`} ref={scrollAreaRef}>
|
||||||
<div>
|
<div>
|
||||||
<div className={`${styles.chatHistory} ${props.customClassName}`}>
|
<div className={`${styles.chatHistory} ${props.customClassName}`}>
|
||||||
<div ref={sentinelRef} style={{ height: "1px" }}>
|
<div ref={sentinelRef} style={{ height: "1px" }}>
|
||||||
{fetchingData && (
|
{fetchingData && <InlineLoading className="opacity-50" />}
|
||||||
<InlineLoading message="Loading Conversation" className="opacity-50" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{data &&
|
{data &&
|
||||||
data.chat &&
|
data.chat &&
|
||||||
|
|
|
@ -37,12 +37,7 @@ import { Popover, PopoverContent } from "@/components/ui/popover";
|
||||||
import { PopoverTrigger } from "@radix-ui/react-popover";
|
import { PopoverTrigger } from "@radix-ui/react-popover";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import {
|
import { convertColorToTextClass, convertToBGClass } from "@/app/common/colorUtils";
|
||||||
convertColorToTextClass,
|
|
||||||
convertToBGClass,
|
|
||||||
convertColorToBorderClass,
|
|
||||||
convertColorToRingClass,
|
|
||||||
} from "@/app/common/colorUtils";
|
|
||||||
|
|
||||||
import LoginPrompt from "../loginPrompt/loginPrompt";
|
import LoginPrompt from "../loginPrompt/loginPrompt";
|
||||||
import { uploadDataForIndexing } from "../../common/chatFunctions";
|
import { uploadDataForIndexing } from "../../common/chatFunctions";
|
||||||
|
@ -63,6 +58,7 @@ interface ChatInputProps {
|
||||||
isMobileWidth?: boolean;
|
isMobileWidth?: boolean;
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
agentColor?: string;
|
agentColor?: string;
|
||||||
|
isResearchModeEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((props, ref) => {
|
export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((props, ref) => {
|
||||||
|
@ -86,7 +82,9 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
const [isDragAndDropping, setIsDragAndDropping] = useState(false);
|
const [isDragAndDropping, setIsDragAndDropping] = useState(false);
|
||||||
|
|
||||||
const [showCommandList, setShowCommandList] = useState(false);
|
const [showCommandList, setShowCommandList] = useState(false);
|
||||||
const [useResearchMode, setUseResearchMode] = useState(false);
|
const [useResearchMode, setUseResearchMode] = useState<boolean>(
|
||||||
|
props.isResearchModeEnabled || false,
|
||||||
|
);
|
||||||
|
|
||||||
const chatInputRef = ref as React.MutableRefObject<HTMLTextAreaElement>;
|
const chatInputRef = ref as React.MutableRefObject<HTMLTextAreaElement>;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -128,6 +126,12 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
fetchImageData();
|
fetchImageData();
|
||||||
}, [imagePaths]);
|
}, [imagePaths]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.isResearchModeEnabled) {
|
||||||
|
setUseResearchMode(props.isResearchModeEnabled);
|
||||||
|
}
|
||||||
|
}, [props.isResearchModeEnabled]);
|
||||||
|
|
||||||
function onSendMessage() {
|
function onSendMessage() {
|
||||||
if (imageUploaded) {
|
if (imageUploaded) {
|
||||||
setImageUploaded(false);
|
setImageUploaded(false);
|
||||||
|
@ -454,7 +458,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
style={{ display: "none" }}
|
style={{ display: "none" }}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-end pb-4">
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant={"ghost"}
|
variant={"ghost"}
|
||||||
className="!bg-none p-0 m-2 h-auto text-3xl rounded-full text-gray-300 hover:text-gray-500"
|
className="!bg-none p-0 m-2 h-auto text-3xl rounded-full text-gray-300 hover:text-gray-500"
|
||||||
|
@ -464,7 +468,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
<Paperclip className="w-8 h-8" />
|
<Paperclip className="w-8 h-8" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow flex flex-col w-full gap-1.5 relative pb-2">
|
<div className="flex-grow flex flex-col w-full gap-1.5 relative">
|
||||||
<div className="flex items-center gap-2 overflow-x-auto">
|
<div className="flex items-center gap-2 overflow-x-auto">
|
||||||
{imageUploaded &&
|
{imageUploaded &&
|
||||||
imagePaths.map((path, index) => (
|
imagePaths.map((path, index) => (
|
||||||
|
@ -490,8 +494,8 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
ref={chatInputRef}
|
ref={chatInputRef}
|
||||||
className={`border ${props.agentColor ? convertColorToBorderClass(props.agentColor) : "border-orange-300"} focus:border-none
|
className={`border-none focus:border-none
|
||||||
focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${convertColorToRingClass(props.agentColor)}
|
focus:outline-none focus-visible:ring-transparent
|
||||||
w-full h-16 min-h-16 max-h-[128px] md:py-4 rounded-lg resize-none dark:bg-neutral-700
|
w-full h-16 min-h-16 max-h-[128px] md:py-4 rounded-lg resize-none dark:bg-neutral-700
|
||||||
${props.isMobileWidth ? "text-md" : "text-lg"}`}
|
${props.isMobileWidth ? "text-md" : "text-lg"}`}
|
||||||
placeholder="Type / to see a list of commands"
|
placeholder="Type / to see a list of commands"
|
||||||
|
@ -510,7 +514,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
disabled={props.sendDisabled || recording}
|
disabled={props.sendDisabled || recording}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end pb-4">
|
<div className="flex items-center">
|
||||||
{recording ? (
|
{recording ? (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
@ -569,7 +573,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="float-right justify-center gap-1 flex items-center p-0 mr-2"
|
className="float-right justify-center gap-1 flex items-center p-1.5 mr-2 h-fit"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUseResearchMode(!useResearchMode);
|
setUseResearchMode(!useResearchMode);
|
||||||
chatInputRef?.current?.focus();
|
chatInputRef?.current?.focus();
|
||||||
|
@ -585,7 +589,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent className="text-xs">
|
||||||
Research Mode allows you to get more deeply researched, detailed
|
Research Mode allows you to get more deeply researched, detailed
|
||||||
responses. Response times may be longer.
|
responses. Response times may be longer.
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
|
|
|
@ -14,25 +14,21 @@ interface SuggestionCardProps {
|
||||||
|
|
||||||
export default function SuggestionCard(data: SuggestionCardProps) {
|
export default function SuggestionCard(data: SuggestionCardProps) {
|
||||||
const bgColors = converColorToBgGradient(data.color);
|
const bgColors = converColorToBgGradient(data.color);
|
||||||
const cardClassName = `${styles.card} ${bgColors} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] md:h-[200px] cursor-pointer`;
|
const cardClassName = `${styles.card} ${bgColors} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] md:h-[180px] cursor-pointer md:p-2`;
|
||||||
const titleClassName = `${styles.title} pt-2 dark:text-white dark:font-bold`;
|
|
||||||
const descriptionClassName = `${styles.text} dark:text-white`;
|
const descriptionClassName = `${styles.text} dark:text-white`;
|
||||||
|
|
||||||
const cardContent = (
|
const cardContent = (
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="m-0 p-2 pb-1 relative">
|
<div className="flex">
|
||||||
<div className="flex flex-row md:flex-col">
|
<CardContent className="m-0 p-2">
|
||||||
{convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())}
|
{convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())}
|
||||||
<CardTitle className={titleClassName}>{data.title}</CardTitle>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="m-0 p-2 pr-4 pt-1">
|
|
||||||
<CardDescription
|
<CardDescription
|
||||||
className={`${descriptionClassName} sm:line-clamp-2 md:line-clamp-4`}
|
className={`${descriptionClassName} sm:line-clamp-2 md:line-clamp-4 pt-1`}
|
||||||
>
|
>
|
||||||
{data.body}
|
{data.body}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
padding: 0.5rem;
|
|
||||||
margin: 0.05rem;
|
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 1.0rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
|
|
|
@ -47,24 +47,24 @@ const DEFAULT_COLOR = "orange";
|
||||||
|
|
||||||
export function convertSuggestionTitleToIconClass(title: string, color: string) {
|
export function convertSuggestionTitleToIconClass(title: string, color: string) {
|
||||||
if (title === SuggestionType.Automation)
|
if (title === SuggestionType.Automation)
|
||||||
return getIconFromIconName("Robot", color, "w-8", "h-8");
|
return getIconFromIconName("Robot", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Paint) return getIconFromIconName("Palette", color, "w-8", "h-8");
|
if (title === SuggestionType.Paint) return getIconFromIconName("Palette", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.PopCulture)
|
if (title === SuggestionType.PopCulture)
|
||||||
return getIconFromIconName("Confetti", color, "w-8", "h-8");
|
return getIconFromIconName("Confetti", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Travel) return getIconFromIconName("Jeep", color, "w-8", "h-8");
|
if (title === SuggestionType.Travel) return getIconFromIconName("Jeep", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Learning) return getIconFromIconName("Book", color, "w-8", "h-8");
|
if (title === SuggestionType.Learning) return getIconFromIconName("Book", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Health)
|
if (title === SuggestionType.Health)
|
||||||
return getIconFromIconName("Asclepius", color, "w-8", "h-8");
|
return getIconFromIconName("Asclepius", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Fun) return getIconFromIconName("Island", color, "w-8", "h-8");
|
if (title === SuggestionType.Fun) return getIconFromIconName("Island", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Home) return getIconFromIconName("House", color, "w-8", "h-8");
|
if (title === SuggestionType.Home) return getIconFromIconName("House", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Language)
|
if (title === SuggestionType.Language)
|
||||||
return getIconFromIconName("Translate", color, "w-8", "h-8");
|
return getIconFromIconName("Translate", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Code) return getIconFromIconName("Code", color, "w-8", "h-8");
|
if (title === SuggestionType.Code) return getIconFromIconName("Code", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Food) return getIconFromIconName("BowlFood", color, "w-8", "h-8");
|
if (title === SuggestionType.Food) return getIconFromIconName("BowlFood", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Interviewing)
|
if (title === SuggestionType.Interviewing)
|
||||||
return getIconFromIconName("Lectern", color, "w-8", "h-8");
|
return getIconFromIconName("Lectern", color, "w-6", "h-6");
|
||||||
if (title === SuggestionType.Finance) return getIconFromIconName("Wallet", color, "w-8", "h-8");
|
if (title === SuggestionType.Finance) return getIconFromIconName("Wallet", color, "w-6", "h-6");
|
||||||
else return getIconFromIconName("Lightbulb", color, "w-8", "h-8");
|
else return getIconFromIconName("Lightbulb", color, "w-6", "h-6");
|
||||||
}
|
}
|
||||||
|
|
||||||
export const suggestionsData: Suggestion[] = [
|
export const suggestionsData: Suggestion[] = [
|
||||||
|
|
|
@ -116,8 +116,8 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
||||||
`What would you like to get done${nameSuffix}?`,
|
`What would you like to get done${nameSuffix}?`,
|
||||||
`Hey${nameSuffix}! How can I help?`,
|
`Hey${nameSuffix}! How can I help?`,
|
||||||
`Good ${timeOfDay}${nameSuffix}! What's on your mind?`,
|
`Good ${timeOfDay}${nameSuffix}! What's on your mind?`,
|
||||||
`Ready to breeze through your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]}?`,
|
`Ready to breeze through ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]}?`,
|
||||||
`Want help navigating your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]} workload?`,
|
`Let's navigate your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]} workload`,
|
||||||
];
|
];
|
||||||
const greeting = greetings[Math.floor(Math.random() * greetings.length)];
|
const greeting = greetings[Math.floor(Math.random() * greetings.length)];
|
||||||
setGreeting(greeting);
|
setGreeting(greeting);
|
||||||
|
|
|
@ -657,6 +657,8 @@ class AgentAdapters:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def ais_agent_accessible(agent: Agent, user: KhojUser) -> bool:
|
async def ais_agent_accessible(agent: Agent, user: KhojUser) -> bool:
|
||||||
|
agent = await Agent.objects.select_related("creator").aget(pk=agent.pk)
|
||||||
|
|
||||||
if agent.privacy_level == Agent.PrivacyLevel.PUBLIC:
|
if agent.privacy_level == Agent.PrivacyLevel.PUBLIC:
|
||||||
return True
|
return True
|
||||||
if agent.creator == user:
|
if agent.creator == user:
|
||||||
|
@ -866,9 +868,13 @@ class ConversationAdapters:
|
||||||
agent = await AgentAdapters.aget_readonly_agent_by_slug(agent_slug, user)
|
agent = await AgentAdapters.aget_readonly_agent_by_slug(agent_slug, user)
|
||||||
if agent is None:
|
if agent is None:
|
||||||
raise HTTPException(status_code=400, detail="No such agent currently exists.")
|
raise HTTPException(status_code=400, detail="No such agent currently exists.")
|
||||||
return await Conversation.objects.acreate(user=user, client=client_application, agent=agent, title=title)
|
return await Conversation.objects.select_related("agent", "agent__creator", "agent__chat_model").acreate(
|
||||||
|
user=user, client=client_application, agent=agent, title=title
|
||||||
|
)
|
||||||
agent = await AgentAdapters.aget_default_agent()
|
agent = await AgentAdapters.aget_default_agent()
|
||||||
return await Conversation.objects.acreate(user=user, client=client_application, agent=agent, title=title)
|
return await Conversation.objects.select_related("agent", "agent__creator", "agent__chat_model").acreate(
|
||||||
|
user=user, client=client_application, agent=agent, title=title
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_conversation_session(
|
def create_conversation_session(
|
||||||
|
|
Loading…
Reference in a new issue