Show agents sorted by mru, Select mru agent by default on web app

Have get agents API return agents ordered intelligently
- Put the default agent first
- Sort used agents by most recently chatted with agent for ease of access
- Randomly shuffle the remaining unused agents for discoverability
This commit is contained in:
Debanjum Singh Solanky 2024-10-20 10:52:14 -07:00
parent 42a5ba8bf4
commit 25df81993c
2 changed files with 44 additions and 35 deletions

View file

@ -109,22 +109,13 @@ function ChatBodyData(props: ChatBodyDataProps) {
}, [props.chatOptionsData]); }, [props.chatOptionsData]);
useEffect(() => { useEffect(() => {
const nSlice = props.isMobileWidth ? 2 : 4; const agents = (agentsData || []).filter((agent) => agent !== null && agent !== undefined);
const shuffledAgents = agentsData ? [...agentsData].sort(() => 0.5 - Math.random()) : [];
const agents = agentsData ? [agentsData[0]] : []; // Always add the first/default agent.
shuffledAgents.forEach((agent) => {
if (!agents.find((a) => a.slug === agent.slug)) {
agents.push(agent);
}
});
setAgents(agents); setAgents(agents);
// set the selected agent to the most recently used agent, first agent is always khoj
setSelectedAgent(agents.length > 1 ? agents[1].slug : "khoj");
//generate colored icons for the selected agents // generate colored icons for the available agents
const agentIcons = agents const agentIcons = agents.map((agent) => getIconFromIconName(agent.icon, agent.color)!);
.filter((agent) => agent !== null && agent !== undefined)
.map((agent) => getIconFromIconName(agent.icon, agent.color)!);
setAgentIcons(agentIcons); setAgentIcons(agentIcons);
}, [agentsData, props.isMobileWidth]); }, [agentsData, props.isMobileWidth]);

View file

@ -1,5 +1,7 @@
import json import json
import logging import logging
import random
from datetime import datetime, timezone
from typing import Dict, List, Optional from typing import Dict, List, Optional
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
@ -9,8 +11,8 @@ from fastapi.responses import Response
from pydantic import BaseModel from pydantic import BaseModel
from starlette.authentication import requires from starlette.authentication import requires
from khoj.database.adapters import AgentAdapters from khoj.database.adapters import AgentAdapters, ConversationAdapters
from khoj.database.models import Agent, KhojUser from khoj.database.models import Agent, Conversation, KhojUser
from khoj.routers.helpers import CommonQueryParams, acheck_if_safe_prompt from khoj.routers.helpers import CommonQueryParams, acheck_if_safe_prompt
from khoj.utils.helpers import ( from khoj.utils.helpers import (
ConversationCommand, ConversationCommand,
@ -45,30 +47,46 @@ async def all_agents(
) -> Response: ) -> Response:
user: KhojUser = request.user.object if request.user.is_authenticated else None user: KhojUser = request.user.object if request.user.is_authenticated else None
agents = await AgentAdapters.aget_all_accessible_agents(user) agents = await AgentAdapters.aget_all_accessible_agents(user)
default_agent = await AgentAdapters.aget_default_agent()
default_agent_packet = None
agents_packet = list() agents_packet = list()
for agent in agents: for agent in agents:
files = agent.fileobject_set.all() files = agent.fileobject_set.all()
file_names = [file.file_name for file in files] file_names = [file.file_name for file in files]
agents_packet.append( agent_packet = {
{ "slug": agent.slug,
"slug": agent.slug, "name": agent.name,
"name": agent.name, "persona": agent.personality,
"persona": agent.personality, "creator": agent.creator.username if agent.creator else None,
"creator": agent.creator.username if agent.creator else None, "managed_by_admin": agent.managed_by_admin,
"managed_by_admin": agent.managed_by_admin, "color": agent.style_color,
"color": agent.style_color, "icon": agent.style_icon,
"icon": agent.style_icon, "privacy_level": agent.privacy_level,
"privacy_level": agent.privacy_level, "chat_model": agent.chat_model.chat_model,
"chat_model": agent.chat_model.chat_model, "files": file_names,
"files": file_names, "input_tools": agent.input_tools,
"input_tools": agent.input_tools, "output_modes": agent.output_modes,
"output_modes": agent.output_modes, }
} if agent.slug == default_agent.slug:
) default_agent_packet = agent_packet
else:
agents_packet.append(agent_packet)
# Load Conversation Sessions
min_date = datetime.min.replace(tzinfo=timezone.utc)
conversations = []
if user:
conversations = await sync_to_async(list[Conversation])(
ConversationAdapters.get_conversation_sessions(user, request.user.client_app).reverse()
)
conversation_times = {conv.agent.slug: conv.updated_at for conv in conversations}
# Put default agent first, then sort by mru and finally shuffle unused randomly
random.shuffle(agents_packet)
agents_packet.sort(key=lambda x: conversation_times.get(x["slug"]) or min_date, reverse=True)
if default_agent_packet:
agents_packet.insert(0, default_agent_packet)
# Make sure that the agent named 'khoj' is first in the list. Everything else is sorted by name.
agents_packet.sort(key=lambda x: x["name"])
agents_packet.sort(key=lambda x: x["slug"] == "khoj", reverse=True)
return Response(content=json.dumps(agents_packet), media_type="application/json", status_code=200) return Response(content=json.dumps(agents_packet), media_type="application/json", status_code=200)