mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-06 05:39:12 +00:00
Hook up hidden agent creation and update APIs to the UI
- This allows users to initiate hidden agent creation from the side bar directly. Any updates can easily be applied to the conversation agent.
This commit is contained in:
@@ -181,7 +181,6 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
||||
conversationId={conversationId}
|
||||
isOpen={props.isChatSideBarOpen}
|
||||
onOpenChange={props.onChatSideBarOpenChange}
|
||||
preexistingAgent={agentMetadata}
|
||||
isMobileWidth={props.isMobileWidth} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Lightbulb, ArrowDown, XCircle } from "@phosphor-icons/react";
|
||||
|
||||
import AgentProfileCard from "../profileCard/profileCard";
|
||||
import { getIconFromIconName } from "@/app/common/iconUtils";
|
||||
import { AgentData } from "@/app/agents/page";
|
||||
import { AgentData } from "../agentCard/agentCard";
|
||||
import React from "react";
|
||||
import { useIsMobileWidth } from "@/app/common/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -281,12 +281,20 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
|
||||
function constructAgentName() {
|
||||
if (!data || !data.agent || !data.agent?.name) return `Agent`;
|
||||
if (data.agent.is_hidden) return 'Khoj';
|
||||
console.log(data.agent);
|
||||
return data.agent?.name;
|
||||
}
|
||||
|
||||
function constructAgentPersona() {
|
||||
if (!data || !data.agent || !data.agent?.persona)
|
||||
if (!data || !data.agent) {
|
||||
return `Your agent is no longer available. You will be reset to the default agent.`;
|
||||
}
|
||||
|
||||
if (!data.agent?.persona) {
|
||||
return `You can set a persona for your agent in the Chat Options side panel.`;
|
||||
}
|
||||
|
||||
return data.agent?.persona;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowsDownUp, Bell, CaretCircleDown, Sparkle } from "@phosphor-icons/react";
|
||||
import { ArrowsDownUp, CaretCircleDown, CircleNotch, Sparkle } from "@phosphor-icons/react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
||||
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ModelSelector } from "@/app/common/modelSelector";
|
||||
import { FilesMenu } from "../allConversations/allConversations";
|
||||
import { AgentConfigurationOptions } from "@/app/agents/page";
|
||||
import useSWR from "swr";
|
||||
import { mutate } from "swr";
|
||||
import { Sheet, SheetContent } from "@/components/ui/sheet";
|
||||
import { AgentData } from "../agentCard/agentCard";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getIconForSlashCommand, getIconFromIconName } from "@/app/common/iconUtils";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
@@ -22,7 +22,6 @@ import { TooltipContent } from "@radix-ui/react-tooltip";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
|
||||
interface ChatSideBarProps {
|
||||
preexistingAgent?: AgentData | null;
|
||||
conversationId: string;
|
||||
isOpen: boolean;
|
||||
isMobileWidth?: boolean;
|
||||
@@ -48,47 +47,162 @@ export function ChatSidebar({ ...props }: ChatSideBarProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ChatSidebarInternal {...props} />
|
||||
<div className="relative">
|
||||
<ChatSidebarInternal {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
const isEditable = props.preexistingAgent?.name.toLowerCase() === "khoj" || props.preexistingAgent?.is_hidden === true;
|
||||
const isDefaultAgent = props.preexistingAgent?.name.toLowerCase() === "khoj";
|
||||
const [isEditable, setIsEditable] = useState<boolean>(false);
|
||||
const [isDefaultAgent, setIsDefaultAgent] = useState<boolean>(false);
|
||||
const { data: agentConfigurationOptions, error: agentConfigurationOptionsError } =
|
||||
useSWR<AgentConfigurationOptions>("/api/agents/options", fetcher);
|
||||
|
||||
const [customPrompt, setCustomPrompt] = useState<string | undefined>(!isDefaultAgent && props.preexistingAgent ? props.preexistingAgent.persona : "always respond in spanish");
|
||||
const [selectedModel, setSelectedModel] = useState<string | undefined>(props.preexistingAgent?.chat_model);
|
||||
const [inputTools, setInputTools] = useState<string[] | undefined>(props.preexistingAgent?.input_tools);
|
||||
const { data: agentData, error: agentDataError } = useSWR<AgentData>(`/api/agents/conversation?conversation_id=${props.conversationId}`, fetcher);
|
||||
|
||||
const [customPrompt, setCustomPrompt] = useState<string | undefined>("");
|
||||
const [selectedModel, setSelectedModel] = useState<string | undefined>();
|
||||
const [inputTools, setInputTools] = useState<string[] | undefined>();
|
||||
const [outputModes, setOutputModes] = useState<string[] | undefined>();
|
||||
const [hasModified, setHasModified] = useState<boolean>(false);
|
||||
|
||||
function isValueChecked(value: string, existingSelections: string[] | undefined): boolean {
|
||||
if (existingSelections === undefined || existingSelections === null || existingSelections.length === 0) {
|
||||
return true;
|
||||
const [isSaving, setIsSaving] = useState<boolean>(false);
|
||||
|
||||
function setupAgentData() {
|
||||
if (agentData) {
|
||||
setSelectedModel(agentData.chat_model);
|
||||
setInputTools(agentData.input_tools);
|
||||
if (agentData.input_tools === undefined || agentData.input_tools.length === 0) {
|
||||
setInputTools(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) : []);
|
||||
}
|
||||
|
||||
if (agentData.name.toLowerCase() === "khoj" || agentData.is_hidden === true) {
|
||||
setIsEditable(true);
|
||||
}
|
||||
|
||||
if (agentData.slug.toLowerCase() === "khoj") {
|
||||
setIsDefaultAgent(true);
|
||||
} else {
|
||||
setCustomPrompt(agentData.persona);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setupAgentData();
|
||||
}, [agentData]);
|
||||
|
||||
|
||||
function isValueChecked(value: string, existingSelections: string[]): boolean {
|
||||
console.log("isValueChecked", value, existingSelections);
|
||||
|
||||
return existingSelections.includes(value);
|
||||
}
|
||||
|
||||
function handleCheckToggle(value: string, existingSelections: string[]): string[] {
|
||||
console.log("handleCheckToggle", value, existingSelections);
|
||||
|
||||
setHasModified(true);
|
||||
|
||||
if (existingSelections.includes(value)) {
|
||||
return existingSelections.filter((v) => v !== value);
|
||||
}
|
||||
|
||||
return [...existingSelections, value];
|
||||
}
|
||||
|
||||
function handleCustomPromptChange(value: string) {
|
||||
setCustomPrompt(value);
|
||||
setHasModified(true);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
if (hasModified) {
|
||||
|
||||
if (agentData?.is_hidden === false) {
|
||||
alert("This agent is not a hidden agent. It cannot be modified from this interface.");
|
||||
return;
|
||||
}
|
||||
|
||||
let mode = "PATCH";
|
||||
|
||||
if (isDefaultAgent) {
|
||||
mode = "POST";
|
||||
}
|
||||
|
||||
const data = {
|
||||
persona: customPrompt,
|
||||
chat_model: selectedModel,
|
||||
input_tools: inputTools,
|
||||
output_modes: outputModes,
|
||||
...(isDefaultAgent ? {} : { slug: agentData?.slug })
|
||||
};
|
||||
|
||||
console.log("outgoing data payload", data);
|
||||
setIsSaving(true);
|
||||
|
||||
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.
|
||||
// 2. If the agent is not a default agent, then we need to update the existing hidden agent. This will be associated using the `slug` field.
|
||||
// 3. If the agent is a "proper" agent and not a hidden agent, then it cannot be updated from this API.
|
||||
// 4. The API is being called before the new conversation has been provisioned. If this happens, then create the agent and associate it with the conversation. Reroute the user to the new conversation page.
|
||||
fetch(url, {
|
||||
method: mode,
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then((res) => {
|
||||
setIsSaving(false);
|
||||
res.json()
|
||||
})
|
||||
.then((data) => {
|
||||
mutate(`/api/agents/conversation?conversation_id=${props.conversationId}`);
|
||||
setHasModified(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
setIsSaving(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
setupAgentData();
|
||||
setHasModified(false);
|
||||
}
|
||||
|
||||
function handleModelSelect(model: string) {
|
||||
setSelectedModel(model);
|
||||
setHasModified(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<Sidebar
|
||||
collapsible="none"
|
||||
className={`ml-auto opacity-30 rounded-lg p-2 transition-all transform duration-300 ease-in-out
|
||||
${props.isOpen
|
||||
? "translate-x-0 opacity-100 w-[300px]"
|
||||
: "translate-x-full opacity-0 w-0"}
|
||||
`}
|
||||
${props.isOpen
|
||||
? "translate-x-0 opacity-100 w-[300px] relative"
|
||||
: "translate-x-full opacity-0 w-0 p-0 m-0 fixed"}
|
||||
`}
|
||||
variant="floating">
|
||||
<SidebarContent>
|
||||
<SidebarHeader>
|
||||
{
|
||||
props.preexistingAgent && !isEditable ? (
|
||||
agentData && !isEditable ? (
|
||||
<div className="flex items-center relative top-2">
|
||||
<a className="text-lg font-bold flex flex-row items-center" href={`/agents?agent=${props.preexistingAgent.slug}`}>
|
||||
{getIconFromIconName(props.preexistingAgent.icon, props.preexistingAgent.color)}
|
||||
{props.preexistingAgent.name}
|
||||
<a className="text-lg font-bold flex flex-row items-center" href={`/agents?agent=${agentData.slug}`}>
|
||||
{getIconFromIconName(agentData.icon, agentData.color)}
|
||||
{agentData.name}
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
@@ -103,7 +217,7 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<SidebarGroupContent className="gap-0">
|
||||
<SidebarMenu className="p-0 m-0">
|
||||
{
|
||||
props.preexistingAgent && props.preexistingAgent.has_files ? (
|
||||
agentData && agentData.has_files ? (
|
||||
<SidebarMenuItem key={"agent_knowledge"} className="list-none">
|
||||
<div className="flex items-center space-x-2 rounded-full">
|
||||
<div className="text-muted-foreground"><Sparkle /></div>
|
||||
@@ -123,7 +237,7 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<Textarea
|
||||
className="w-full h-32"
|
||||
value={customPrompt}
|
||||
onChange={(e) => setCustomPrompt(e.target.value)}
|
||||
onChange={(e) => handleCustomPromptChange(e.target.value)}
|
||||
readOnly={!isEditable}
|
||||
disabled={!isEditable} />
|
||||
</SidebarMenuItem>
|
||||
@@ -137,7 +251,7 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<SidebarMenuItem key={"model"} className="list-none">
|
||||
<ModelSelector
|
||||
disabled={!isEditable}
|
||||
onSelect={(model) => setSelectedModel(model.name)}
|
||||
onSelect={(model) => handleModelSelect(model.name)}
|
||||
selectedModel={selectedModel}
|
||||
/>
|
||||
</SidebarMenuItem>
|
||||
@@ -154,68 +268,75 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
</SidebarGroupLabel>
|
||||
<CollapsibleContent>
|
||||
<SidebarGroupContent>
|
||||
{
|
||||
Object.entries(agentConfigurationOptions?.input_tools ?? {}).map(([key, value]) => {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger key={key} asChild>
|
||||
<div className="flex items-center space-x-2 py-1 justify-between">
|
||||
<Label htmlFor={key} className="flex items-center gap-2 text-accent-foreground p-1 cursor-pointer">
|
||||
{getIconForSlashCommand(key)}
|
||||
<p className="text-sm my-auto flex items-center">
|
||||
{key}
|
||||
</p>
|
||||
</Label>
|
||||
<Checkbox
|
||||
id={key}
|
||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||
checked={isValueChecked(key, props.preexistingAgent?.input_tools)}
|
||||
onCheckedChange={() => { }}
|
||||
disabled={!isEditable}
|
||||
>
|
||||
{key}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={5} side="left" align="start" className="text-sm bg-background text-foreground shadow-sm border border-slate-500 border-opacity-20 p-2 rounded-lg">
|
||||
{value}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
<SidebarMenu className="p-1 m-0">
|
||||
{
|
||||
Object.entries(agentConfigurationOptions?.input_tools ?? {}).map(([key, value]) => {
|
||||
return (
|
||||
<SidebarMenuItem key={key} className="list-none">
|
||||
<Tooltip>
|
||||
<TooltipTrigger key={key} asChild>
|
||||
<div className="flex items-center space-x-2 py-1 justify-between">
|
||||
<Label htmlFor={key} className="flex items-center gap-2 text-accent-foreground p-1 cursor-pointer">
|
||||
{getIconForSlashCommand(key)}
|
||||
<p className="text-sm my-auto flex items-center">
|
||||
{key}
|
||||
</p>
|
||||
</Label>
|
||||
<Checkbox
|
||||
id={key}
|
||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||
checked={isValueChecked(key, inputTools ?? [])}
|
||||
onCheckedChange={() => setInputTools(handleCheckToggle(key, inputTools ?? []))}
|
||||
disabled={!isEditable}
|
||||
>
|
||||
{key}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={5} side="left" align="start" className="text-sm bg-background text-foreground shadow-sm border border-slate-500 border-opacity-20 p-2 rounded-lg">
|
||||
{value}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
{
|
||||
Object.entries(agentConfigurationOptions?.output_modes ?? {}).map(([key, value]) => {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger key={key} asChild>
|
||||
<div className="flex items-center space-x-2 py-1 justify-between">
|
||||
<Label htmlFor={key} className="flex items-center gap-2 p-1 rounded-lg cursor-pointer">
|
||||
{getIconForSlashCommand(key)}
|
||||
<p className="text-sm my-auto flex items-center">
|
||||
{key}
|
||||
</p>
|
||||
</Label>
|
||||
<Checkbox
|
||||
id={key}
|
||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||
checked={isValueChecked(key, props.preexistingAgent?.output_modes)}
|
||||
onCheckedChange={() => { }}
|
||||
disabled={!isEditable}
|
||||
>
|
||||
{key}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={5} side="left" align="start" className="text-sm bg-background text-foreground shadow-sm border border-slate-500 border-opacity-20 p-2 rounded-lg">
|
||||
{value}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
{
|
||||
Object.entries(agentConfigurationOptions?.output_modes ?? {}).map(([key, value]) => {
|
||||
return (
|
||||
<SidebarMenuItem key={key} className="list-none">
|
||||
<Tooltip>
|
||||
<TooltipTrigger key={key} asChild>
|
||||
<div className="flex items-center space-x-2 py-1 justify-between">
|
||||
<Label htmlFor={key} className="flex items-center gap-2 p-1 rounded-lg cursor-pointer">
|
||||
{getIconForSlashCommand(key)}
|
||||
<p className="text-sm my-auto flex items-center">
|
||||
{key}
|
||||
</p>
|
||||
</Label>
|
||||
<Checkbox
|
||||
id={key}
|
||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||
checked={isValueChecked(key, outputModes ?? [])}
|
||||
onCheckedChange={() => setOutputModes(handleCheckToggle(key, outputModes ?? []))}
|
||||
disabled={!isEditable}
|
||||
>
|
||||
{key}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={5} side="left" align="start" className="text-sm bg-background text-foreground shadow-sm border border-slate-500 border-opacity-20 p-2 rounded-lg">
|
||||
{value}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</SidebarMenu>
|
||||
|
||||
</SidebarGroupContent>
|
||||
</CollapsibleContent>
|
||||
</SidebarGroup>
|
||||
@@ -239,13 +360,13 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<SidebarMenu className="p-0 m-0">
|
||||
|
||||
{
|
||||
(props.preexistingAgent && props.preexistingAgent.is_creator) ? (
|
||||
(agentData && !isEditable && agentData.is_creator) ? (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton asChild>
|
||||
<Button
|
||||
className="w-full"
|
||||
variant={"ghost"}
|
||||
onClick={() => window.location.href = `/agents?agent=${props.preexistingAgent?.slug}`}
|
||||
onClick={() => window.location.href = `/agents?agent=${agentData?.slug}`}
|
||||
>
|
||||
Manage
|
||||
</Button>
|
||||
@@ -257,9 +378,9 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<SidebarMenuButton asChild>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => { }}
|
||||
onClick={() => handleReset()}
|
||||
variant={"ghost"}
|
||||
disabled={!isEditable}
|
||||
disabled={!isEditable || !hasModified}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
@@ -270,10 +391,18 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<Button
|
||||
className="w-full"
|
||||
variant={"secondary"}
|
||||
onClick={() => { }}
|
||||
disabled={!isEditable}
|
||||
onClick={() => handleSave()}
|
||||
disabled={!isEditable || !hasModified || isSaving}
|
||||
>
|
||||
Save
|
||||
{
|
||||
isSaving ?
|
||||
<CircleNotch className="animate-spin" />
|
||||
:
|
||||
<ArrowsDownUp />
|
||||
}
|
||||
{
|
||||
isSaving ? "Saving" : "Save"
|
||||
}
|
||||
</Button>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
@@ -13,7 +13,7 @@ const Checkbox = React.forwardRef<
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-muted ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-gray-500 data-[state=checked]:text-primary-foreground",
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-secondary-foreground ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-gray-500 data-[state=checked]:text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -876,12 +876,13 @@ class AgentAdapters:
|
||||
chat_model: Optional[str] = None,
|
||||
input_tools: Optional[List[str]] = None,
|
||||
output_modes: Optional[List[str]] = None,
|
||||
existing_agent: Optional[Agent] = None,
|
||||
):
|
||||
random_name = generate_random_internal_agent_name()
|
||||
name = generate_random_internal_agent_name() if not existing_agent else existing_agent.name
|
||||
|
||||
agent = await AgentAdapters.aupdate_agent(
|
||||
user=user,
|
||||
name=random_name,
|
||||
name=name,
|
||||
personality=persona,
|
||||
privacy_level=Agent.PrivacyLevel.PRIVATE,
|
||||
icon=Agent.StyleIconTypes.LIGHTBULB,
|
||||
|
||||
@@ -303,7 +303,7 @@ class Agent(DbBaseModel):
|
||||
managed_by_admin = models.BooleanField(default=False)
|
||||
chat_model = models.ForeignKey(ChatModel, on_delete=models.CASCADE)
|
||||
slug = models.CharField(max_length=200, unique=True)
|
||||
style_color = models.CharField(max_length=200, choices=StyleColorTypes.choices, default=StyleColorTypes.BLUE)
|
||||
style_color = models.CharField(max_length=200, choices=StyleColorTypes.choices, default=StyleColorTypes.ORANGE)
|
||||
style_icon = models.CharField(max_length=200, choices=StyleIconTypes.choices, default=StyleIconTypes.LIGHTBULB)
|
||||
privacy_level = models.CharField(max_length=30, choices=PrivacyLevel.choices, default=PrivacyLevel.PRIVATE)
|
||||
is_hidden = models.BooleanField(default=False)
|
||||
|
||||
@@ -11,7 +11,7 @@ from fastapi.responses import Response
|
||||
from pydantic import BaseModel
|
||||
from starlette.authentication import has_required_scope, requires
|
||||
|
||||
from khoj.database.adapters import AgentAdapters, ConversationAdapters
|
||||
from khoj.database.adapters import AgentAdapters, ConversationAdapters, EntryAdapters
|
||||
from khoj.database.models import Agent, Conversation, KhojUser
|
||||
from khoj.routers.helpers import CommonQueryParams, acheck_if_safe_prompt
|
||||
from khoj.utils.helpers import (
|
||||
@@ -42,7 +42,7 @@ class ModifyAgentBody(BaseModel):
|
||||
|
||||
|
||||
class ModifyHiddenAgentBody(BaseModel):
|
||||
slug: str
|
||||
slug: Optional[str] = None
|
||||
persona: Optional[str] = None
|
||||
chat_model: Optional[str] = None
|
||||
input_tools: Optional[List[str]] = []
|
||||
@@ -102,6 +102,47 @@ async def all_agents(
|
||||
return Response(content=json.dumps(agents_packet), media_type="application/json", status_code=200)
|
||||
|
||||
|
||||
@api_agents.get("/conversation", response_class=Response)
|
||||
@requires(["authenticated"])
|
||||
async def get_agent_by_conversation(
|
||||
request: Request,
|
||||
common: CommonQueryParams,
|
||||
conversation_id: str,
|
||||
) -> Response:
|
||||
user: KhojUser = request.user.object if request.user.is_authenticated else None
|
||||
conversation = await ConversationAdapters.aget_conversation_by_user(user=user, conversation_id=conversation_id)
|
||||
|
||||
if not conversation:
|
||||
return Response(
|
||||
content=json.dumps({"error": f"Conversation with id {conversation_id} not found for user {user}."}),
|
||||
media_type="application/json",
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
agent = await AgentAdapters.aget_agent_by_slug(conversation.agent.slug, user)
|
||||
|
||||
has_files = agent.fileobject_set.exists()
|
||||
|
||||
agents_packet = {
|
||||
"slug": agent.slug,
|
||||
"name": agent.name,
|
||||
"persona": agent.personality,
|
||||
"creator": agent.creator.username if agent.creator else None,
|
||||
"managed_by_admin": agent.managed_by_admin,
|
||||
"color": agent.style_color,
|
||||
"icon": agent.style_icon,
|
||||
"privacy_level": agent.privacy_level,
|
||||
"chat_model": agent.chat_model.name,
|
||||
"has_files": has_files,
|
||||
"input_tools": agent.input_tools,
|
||||
"output_modes": agent.output_modes,
|
||||
"is_creator": agent.creator == user,
|
||||
"is_hidden": agent.is_hidden,
|
||||
}
|
||||
|
||||
return Response(content=json.dumps(agents_packet), media_type="application/json", status_code=200)
|
||||
|
||||
|
||||
@api_agents.get("/options", response_class=Response)
|
||||
async def get_agent_configuration_options(
|
||||
request: Request,
|
||||
@@ -213,12 +254,13 @@ async def update_hidden_agent(
|
||||
)
|
||||
|
||||
agent = await AgentAdapters.aupdate_hidden_agent(
|
||||
user,
|
||||
body.slug,
|
||||
body.persona,
|
||||
chat_model,
|
||||
body.input_tools,
|
||||
body.output_modes,
|
||||
user=user,
|
||||
slug=body.slug,
|
||||
persona=body.persona,
|
||||
chat_model=chat_model,
|
||||
input_tools=body.input_tools,
|
||||
output_modes=body.output_modes,
|
||||
existing_agent=selected_agent,
|
||||
)
|
||||
|
||||
agents_packet = {
|
||||
@@ -226,12 +268,7 @@ async def update_hidden_agent(
|
||||
"name": agent.name,
|
||||
"persona": agent.personality,
|
||||
"creator": agent.creator.username if agent.creator else None,
|
||||
"managed_by_admin": agent.managed_by_admin,
|
||||
"color": agent.style_color,
|
||||
"icon": agent.style_icon,
|
||||
"privacy_level": agent.privacy_level,
|
||||
"chat_model": agent.chat_model.name,
|
||||
"files": body.files,
|
||||
"input_tools": agent.input_tools,
|
||||
"output_modes": agent.output_modes,
|
||||
}
|
||||
@@ -244,6 +281,7 @@ async def update_hidden_agent(
|
||||
async def create_hidden_agent(
|
||||
request: Request,
|
||||
common: CommonQueryParams,
|
||||
conversation_id: str,
|
||||
body: ModifyHiddenAgentBody,
|
||||
) -> Response:
|
||||
user: KhojUser = request.user.object
|
||||
@@ -251,26 +289,44 @@ async def create_hidden_agent(
|
||||
subscribed = has_required_scope(request, ["premium"])
|
||||
chat_model = body.chat_model if subscribed else None
|
||||
|
||||
conversation = await ConversationAdapters.aget_conversation_by_user(user=user, conversation_id=conversation_id)
|
||||
if not conversation:
|
||||
return Response(
|
||||
content=json.dumps({"error": f"Conversation with id {conversation_id} not found for user {user}."}),
|
||||
media_type="application/json",
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
if conversation.agent:
|
||||
# If the conversation is not already associated with an agent (i.e., it's using the default agent ), we can create a new one
|
||||
if conversation.agent.slug != AgentAdapters.DEFAULT_AGENT_SLUG:
|
||||
return Response(
|
||||
content=json.dumps(
|
||||
{"error": f"Conversation with id {conversation_id} already has an agent. Use the PATCH method."}
|
||||
),
|
||||
media_type="application/json",
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
agent = await AgentAdapters.aupdate_hidden_agent(
|
||||
user,
|
||||
body.slug,
|
||||
body.persona,
|
||||
chat_model,
|
||||
body.input_tools,
|
||||
body.output_modes,
|
||||
user=user,
|
||||
slug=body.slug,
|
||||
persona=body.persona,
|
||||
chat_model=chat_model,
|
||||
input_tools=body.input_tools,
|
||||
output_modes=body.output_modes,
|
||||
existing_agent=None,
|
||||
)
|
||||
|
||||
conversation.agent = agent
|
||||
await conversation.asave()
|
||||
|
||||
agents_packet = {
|
||||
"slug": agent.slug,
|
||||
"name": agent.name,
|
||||
"persona": agent.personality,
|
||||
"creator": agent.creator.username if agent.creator else None,
|
||||
"managed_by_admin": agent.managed_by_admin,
|
||||
"color": agent.style_color,
|
||||
"icon": agent.style_icon,
|
||||
"privacy_level": agent.privacy_level,
|
||||
"chat_model": agent.chat_model.name,
|
||||
"files": body.files,
|
||||
"input_tools": agent.input_tools,
|
||||
"output_modes": agent.output_modes,
|
||||
}
|
||||
|
||||
@@ -223,7 +223,6 @@ def chat_history(
|
||||
if conversation.agent.privacy_level == Agent.PrivacyLevel.PRIVATE and conversation.agent.creator != user:
|
||||
conversation.agent = None
|
||||
else:
|
||||
agent_has_files = EntryAdapters.agent_has_entries(conversation.agent)
|
||||
agent_metadata = {
|
||||
"slug": conversation.agent.slug,
|
||||
"name": conversation.agent.name,
|
||||
@@ -232,8 +231,6 @@ def chat_history(
|
||||
"icon": conversation.agent.style_icon,
|
||||
"persona": conversation.agent.personality,
|
||||
"is_hidden": conversation.agent.is_hidden,
|
||||
"chat_model": conversation.agent.chat_model.name,
|
||||
"has_files": agent_has_files,
|
||||
}
|
||||
|
||||
meta_log = conversation.conversation_log
|
||||
@@ -286,7 +283,6 @@ def get_shared_chat(
|
||||
if conversation.agent.privacy_level == Agent.PrivacyLevel.PRIVATE:
|
||||
conversation.agent = None
|
||||
else:
|
||||
agent_has_files = EntryAdapters.agent_has_entries(conversation.agent)
|
||||
agent_metadata = {
|
||||
"slug": conversation.agent.slug,
|
||||
"name": conversation.agent.name,
|
||||
@@ -295,8 +291,6 @@ def get_shared_chat(
|
||||
"icon": conversation.agent.style_icon,
|
||||
"persona": conversation.agent.personality,
|
||||
"is_hidden": conversation.agent.is_hidden,
|
||||
"chat_model": conversation.agent.chat_model.name,
|
||||
"has_files": agent_has_files,
|
||||
}
|
||||
|
||||
meta_log = conversation.conversation_log
|
||||
|
||||
@@ -435,7 +435,7 @@ def generate_random_name():
|
||||
def generate_random_internal_agent_name():
|
||||
random_name = generate_random_name()
|
||||
|
||||
random_name.replace(" ", "_")
|
||||
random_name = random_name.replace(" ", "_")
|
||||
|
||||
random_number = random.randint(1000, 9999)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user