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:
sabaimran 2024-10-28 15:29:24 -07:00
parent ebaed53069
commit 1f1b182461
8 changed files with 62 additions and 55 deletions

View file

@ -42,6 +42,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
const [images, setImages] = useState<string[]>([]);
const [processingMessage, setProcessingMessage] = useState(false);
const [agentMetadata, setAgentMetadata] = useState<AgentData | null>(null);
const [isInResearchMode, setIsInResearchMode] = useState(false);
const chatInputRef = useRef<HTMLTextAreaElement>(null);
const setQueryToProcess = props.setQueryToProcess;
@ -70,6 +71,10 @@ function ChatBodyData(props: ChatBodyDataProps) {
if (storedMessage) {
setProcessingMessage(true);
setQueryToProcess(storedMessage);
if (storedMessage.trim().startsWith("/research")) {
setIsInResearchMode(true);
}
}
}, [setQueryToProcess, props.setImages]);
@ -130,6 +135,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
isMobileWidth={props.isMobileWidth}
setUploadedFiles={props.setUploadedFiles}
ref={chatInputRef}
isResearchModeEnabled={isInResearchMode}
/>
</div>
</>

View file

@ -71,7 +71,7 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) {
</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)}
variant="ghost"
size="sm"
@ -283,13 +283,11 @@ export default function ChatHistory(props: ChatHistoryProps) {
}
return (
<ScrollArea className={`h-[80vh] relative`} ref={scrollAreaRef}>
<ScrollArea className={`h-[73vh] relative`} ref={scrollAreaRef}>
<div>
<div className={`${styles.chatHistory} ${props.customClassName}`}>
<div ref={sentinelRef} style={{ height: "1px" }}>
{fetchingData && (
<InlineLoading message="Loading Conversation" className="opacity-50" />
)}
{fetchingData && <InlineLoading className="opacity-50" />}
</div>
{data &&
data.chat &&

View file

@ -37,12 +37,7 @@ import { Popover, PopoverContent } from "@/components/ui/popover";
import { PopoverTrigger } from "@radix-ui/react-popover";
import { Textarea } from "@/components/ui/textarea";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import {
convertColorToTextClass,
convertToBGClass,
convertColorToBorderClass,
convertColorToRingClass,
} from "@/app/common/colorUtils";
import { convertColorToTextClass, convertToBGClass } from "@/app/common/colorUtils";
import LoginPrompt from "../loginPrompt/loginPrompt";
import { uploadDataForIndexing } from "../../common/chatFunctions";
@ -63,6 +58,7 @@ interface ChatInputProps {
isMobileWidth?: boolean;
isLoggedIn: boolean;
agentColor?: string;
isResearchModeEnabled?: boolean;
}
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 [showCommandList, setShowCommandList] = useState(false);
const [useResearchMode, setUseResearchMode] = useState(false);
const [useResearchMode, setUseResearchMode] = useState<boolean>(
props.isResearchModeEnabled || false,
);
const chatInputRef = ref as React.MutableRefObject<HTMLTextAreaElement>;
useEffect(() => {
@ -128,6 +126,12 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
fetchImageData();
}, [imagePaths]);
useEffect(() => {
if (props.isResearchModeEnabled) {
setUseResearchMode(props.isResearchModeEnabled);
}
}, [props.isResearchModeEnabled]);
function onSendMessage() {
if (imageUploaded) {
setImageUploaded(false);
@ -454,7 +458,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
onChange={handleFileChange}
style={{ display: "none" }}
/>
<div className="flex items-end pb-4">
<div className="flex items-center">
<Button
variant={"ghost"}
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" />
</Button>
</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">
{imageUploaded &&
imagePaths.map((path, index) => (
@ -490,8 +494,8 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
</div>
<Textarea
ref={chatInputRef}
className={`border ${props.agentColor ? convertColorToBorderClass(props.agentColor) : "border-orange-300"} focus:border-none
focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${convertColorToRingClass(props.agentColor)}
className={`border-none focus:border-none
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
${props.isMobileWidth ? "text-md" : "text-lg"}`}
placeholder="Type / to see a list of commands"
@ -510,7 +514,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
disabled={props.sendDisabled || recording}
/>
</div>
<div className="flex items-end pb-4">
<div className="flex items-center">
{recording ? (
<TooltipProvider>
<Tooltip>
@ -569,7 +573,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
<TooltipTrigger asChild>
<Button
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={() => {
setUseResearchMode(!useResearchMode);
chatInputRef?.current?.focus();
@ -585,7 +589,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
)}
</Button>
</TooltipTrigger>
<TooltipContent>
<TooltipContent className="text-xs">
Research Mode allows you to get more deeply researched, detailed
responses. Response times may be longer.
</TooltipContent>

View file

@ -14,25 +14,21 @@ interface SuggestionCardProps {
export default function SuggestionCard(data: SuggestionCardProps) {
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 titleClassName = `${styles.title} pt-2 dark:text-white dark:font-bold`;
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 descriptionClassName = `${styles.text} dark:text-white`;
const cardContent = (
<Card className={cardClassName}>
<CardHeader className="m-0 p-2 pb-1 relative">
<div className="flex flex-row md:flex-col">
<div className="flex">
<CardContent className="m-0 p-2">
{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
className={`${descriptionClassName} sm:line-clamp-2 md:line-clamp-4`}
>
{data.body}
</CardDescription>
</CardContent>
<CardDescription
className={`${descriptionClassName} sm:line-clamp-2 md:line-clamp-4 pt-1`}
>
{data.body}
</CardDescription>
</CardContent>
</div>
</Card>
);

View file

@ -1,12 +1,9 @@
.card {
padding: 0.5rem;
margin: 0.05rem;
border-radius: 0.5rem;
}
.title {
font-size: 1.0rem;
font-size: 1rem;
}
.text {

View file

@ -47,24 +47,24 @@ const DEFAULT_COLOR = "orange";
export function convertSuggestionTitleToIconClass(title: string, color: string) {
if (title === SuggestionType.Automation)
return getIconFromIconName("Robot", color, "w-8", "h-8");
if (title === SuggestionType.Paint) return getIconFromIconName("Palette", color, "w-8", "h-8");
return getIconFromIconName("Robot", color, "w-6", "h-6");
if (title === SuggestionType.Paint) return getIconFromIconName("Palette", color, "w-6", "h-6");
if (title === SuggestionType.PopCulture)
return getIconFromIconName("Confetti", color, "w-8", "h-8");
if (title === SuggestionType.Travel) return getIconFromIconName("Jeep", color, "w-8", "h-8");
if (title === SuggestionType.Learning) return getIconFromIconName("Book", color, "w-8", "h-8");
return getIconFromIconName("Confetti", color, "w-6", "h-6");
if (title === SuggestionType.Travel) return getIconFromIconName("Jeep", color, "w-6", "h-6");
if (title === SuggestionType.Learning) return getIconFromIconName("Book", color, "w-6", "h-6");
if (title === SuggestionType.Health)
return getIconFromIconName("Asclepius", color, "w-8", "h-8");
if (title === SuggestionType.Fun) return getIconFromIconName("Island", color, "w-8", "h-8");
if (title === SuggestionType.Home) return getIconFromIconName("House", color, "w-8", "h-8");
return getIconFromIconName("Asclepius", color, "w-6", "h-6");
if (title === SuggestionType.Fun) return getIconFromIconName("Island", color, "w-6", "h-6");
if (title === SuggestionType.Home) return getIconFromIconName("House", color, "w-6", "h-6");
if (title === SuggestionType.Language)
return getIconFromIconName("Translate", color, "w-8", "h-8");
if (title === SuggestionType.Code) return getIconFromIconName("Code", color, "w-8", "h-8");
if (title === SuggestionType.Food) return getIconFromIconName("BowlFood", color, "w-8", "h-8");
return getIconFromIconName("Translate", color, "w-6", "h-6");
if (title === SuggestionType.Code) return getIconFromIconName("Code", color, "w-6", "h-6");
if (title === SuggestionType.Food) return getIconFromIconName("BowlFood", color, "w-6", "h-6");
if (title === SuggestionType.Interviewing)
return getIconFromIconName("Lectern", color, "w-8", "h-8");
if (title === SuggestionType.Finance) return getIconFromIconName("Wallet", color, "w-8", "h-8");
else return getIconFromIconName("Lightbulb", color, "w-8", "h-8");
return getIconFromIconName("Lectern", color, "w-6", "h-6");
if (title === SuggestionType.Finance) return getIconFromIconName("Wallet", color, "w-6", "h-6");
else return getIconFromIconName("Lightbulb", color, "w-6", "h-6");
}
export const suggestionsData: Suggestion[] = [

View file

@ -116,8 +116,8 @@ function ChatBodyData(props: ChatBodyDataProps) {
`What would you like to get done${nameSuffix}?`,
`Hey${nameSuffix}! How can I help?`,
`Good ${timeOfDay}${nameSuffix}! What's on your mind?`,
`Ready to breeze through your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]}?`,
`Want help navigating your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]} workload?`,
`Ready to breeze through ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]}?`,
`Let's navigate your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]} workload`,
];
const greeting = greetings[Math.floor(Math.random() * greetings.length)];
setGreeting(greeting);

View file

@ -657,6 +657,8 @@ class AgentAdapters:
@staticmethod
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:
return True
if agent.creator == user:
@ -866,9 +868,13 @@ class ConversationAdapters:
agent = await AgentAdapters.aget_readonly_agent_by_slug(agent_slug, user)
if agent is None:
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()
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
def create_conversation_session(