mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 21:19:12 +00:00
Initialize the concept in the backend of hidden agents
A hidden agent basically allows each individual conversation to maintain custom settings, via an agent that's not exposed to the traditional functionalities allotted for manually created agents (e.g., browsing, maintenance in agents page). This will be hooked up to the front-end such that any conversation that's initiated with the default agent can then be given custom settings, which in the background creates a hidden agent. This allows us to repurpose all of our existing agents infrastructure for chat-level customization.
This commit is contained in:
@@ -69,6 +69,7 @@ from khoj.search_filter.word_filter import WordFilter
|
||||
from khoj.utils import state
|
||||
from khoj.utils.config import OfflineChatProcessorModel
|
||||
from khoj.utils.helpers import (
|
||||
generate_random_internal_agent_name,
|
||||
generate_random_name,
|
||||
in_debug_mode,
|
||||
is_none_or_empty,
|
||||
@@ -806,13 +807,15 @@ class AgentAdapters:
|
||||
privacy_level: str,
|
||||
icon: str,
|
||||
color: str,
|
||||
chat_model: str,
|
||||
chat_model: Optional[str],
|
||||
files: List[str],
|
||||
input_tools: List[str],
|
||||
output_modes: List[str],
|
||||
slug: Optional[str] = None,
|
||||
is_hidden: Optional[bool] = False,
|
||||
):
|
||||
if not chat_model:
|
||||
chat_model = ConversationAdapters.get_default_chat_model(user)
|
||||
chat_model_option = await ChatModel.objects.filter(name=chat_model).afirst()
|
||||
|
||||
# Slug will be None for new agents, which will trigger a new agent creation with a generated, immutable slug
|
||||
@@ -864,6 +867,35 @@ class AgentAdapters:
|
||||
|
||||
return agent
|
||||
|
||||
@staticmethod
|
||||
@arequire_valid_user
|
||||
async def aupdate_hidden_agent(
|
||||
user: KhojUser,
|
||||
slug: Optional[str] = None,
|
||||
persona: Optional[str] = None,
|
||||
chat_model: Optional[str] = None,
|
||||
input_tools: Optional[List[str]] = None,
|
||||
output_modes: Optional[List[str]] = None,
|
||||
):
|
||||
random_name = generate_random_internal_agent_name()
|
||||
|
||||
agent = await AgentAdapters.aupdate_agent(
|
||||
user=user,
|
||||
name=random_name,
|
||||
personality=persona,
|
||||
privacy_level=Agent.PrivacyLevel.PRIVATE,
|
||||
icon=Agent.StyleIconTypes.LIGHTBULB,
|
||||
color=Agent.StyleColorTypes.BLUE,
|
||||
chat_model=chat_model,
|
||||
files=[],
|
||||
input_tools=input_tools,
|
||||
output_modes=output_modes,
|
||||
slug=slug,
|
||||
is_hidden=True,
|
||||
)
|
||||
|
||||
return agent
|
||||
|
||||
|
||||
class PublicConversationAdapters:
|
||||
@staticmethod
|
||||
|
||||
@@ -285,7 +285,6 @@ class Agent(DbBaseModel):
|
||||
|
||||
class OutputModeOptions(models.TextChoices):
|
||||
# These map to various ConversationCommand types
|
||||
TEXT = "text"
|
||||
IMAGE = "image"
|
||||
AUTOMATION = "automation"
|
||||
DIAGRAM = "diagram"
|
||||
|
||||
@@ -9,7 +9,7 @@ from fastapi import APIRouter, Request
|
||||
from fastapi.requests import Request
|
||||
from fastapi.responses import Response
|
||||
from pydantic import BaseModel
|
||||
from starlette.authentication import requires
|
||||
from starlette.authentication import has_required_scope, requires
|
||||
|
||||
from khoj.database.adapters import AgentAdapters, ConversationAdapters
|
||||
from khoj.database.models import Agent, Conversation, KhojUser
|
||||
@@ -41,6 +41,14 @@ class ModifyAgentBody(BaseModel):
|
||||
is_hidden: Optional[bool] = False
|
||||
|
||||
|
||||
class ModifyHiddenAgentBody(BaseModel):
|
||||
slug: str
|
||||
persona: Optional[str] = None
|
||||
chat_model: Optional[str] = None
|
||||
input_tools: Optional[List[str]] = []
|
||||
output_modes: Optional[List[str]] = []
|
||||
|
||||
|
||||
@api_agents.get("", response_class=Response)
|
||||
async def all_agents(
|
||||
request: Request,
|
||||
@@ -183,6 +191,93 @@ async def delete_agent(
|
||||
return Response(content=json.dumps({"message": "Agent deleted."}), media_type="application/json", status_code=200)
|
||||
|
||||
|
||||
@api_agents.patch("/hidden", response_class=Response)
|
||||
@requires(["authenticated"])
|
||||
async def update_hidden_agent(
|
||||
request: Request,
|
||||
common: CommonQueryParams,
|
||||
body: ModifyHiddenAgentBody,
|
||||
) -> Response:
|
||||
user: KhojUser = request.user.object
|
||||
|
||||
subscribed = has_required_scope(request, ["premium"])
|
||||
chat_model = body.chat_model if subscribed else None
|
||||
|
||||
selected_agent = await AgentAdapters.aget_agent_by_slug(body.slug, user)
|
||||
|
||||
if not selected_agent:
|
||||
return Response(
|
||||
content=json.dumps({"error": f"Agent with name {body.slug} not found."}),
|
||||
media_type="application/json",
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
agent = await AgentAdapters.aupdate_hidden_agent(
|
||||
user,
|
||||
body.slug,
|
||||
body.persona,
|
||||
chat_model,
|
||||
body.input_tools,
|
||||
body.output_modes,
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
return Response(content=json.dumps(agents_packet), media_type="application/json", status_code=200)
|
||||
|
||||
|
||||
@api_agents.post("/hidden", response_class=Response)
|
||||
@requires(["authenticated"])
|
||||
async def create_hidden_agent(
|
||||
request: Request,
|
||||
common: CommonQueryParams,
|
||||
body: ModifyHiddenAgentBody,
|
||||
) -> Response:
|
||||
user: KhojUser = request.user.object
|
||||
|
||||
subscribed = has_required_scope(request, ["premium"])
|
||||
chat_model = body.chat_model if subscribed else None
|
||||
|
||||
agent = await AgentAdapters.aupdate_hidden_agent(
|
||||
user,
|
||||
body.slug,
|
||||
body.persona,
|
||||
chat_model,
|
||||
body.input_tools,
|
||||
body.output_modes,
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
return Response(content=json.dumps(agents_packet), media_type="application/json", status_code=200)
|
||||
|
||||
|
||||
@api_agents.post("", response_class=Response)
|
||||
@requires(["authenticated"])
|
||||
async def create_agent(
|
||||
@@ -203,6 +298,9 @@ async def create_agent(
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
subscribed = has_required_scope(request, ["premium"])
|
||||
chat_model = body.chat_model if subscribed else None
|
||||
|
||||
agent = await AgentAdapters.aupdate_agent(
|
||||
user,
|
||||
body.name,
|
||||
@@ -210,7 +308,7 @@ async def create_agent(
|
||||
body.privacy_level,
|
||||
body.icon,
|
||||
body.color,
|
||||
body.chat_model,
|
||||
chat_model,
|
||||
body.files,
|
||||
body.input_tools,
|
||||
body.output_modes,
|
||||
@@ -266,6 +364,9 @@ async def update_agent(
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
subscribed = has_required_scope(request, ["premium"])
|
||||
chat_model = body.chat_model if subscribed else None
|
||||
|
||||
agent = await AgentAdapters.aupdate_agent(
|
||||
user,
|
||||
body.name,
|
||||
@@ -273,7 +374,7 @@ async def update_agent(
|
||||
body.privacy_level,
|
||||
body.icon,
|
||||
body.color,
|
||||
body.chat_model,
|
||||
chat_model,
|
||||
body.files,
|
||||
body.input_tools,
|
||||
body.output_modes,
|
||||
|
||||
@@ -223,13 +223,17 @@ 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,
|
||||
"isCreator": conversation.agent.creator == user,
|
||||
"is_creator": conversation.agent.creator == user,
|
||||
"color": conversation.agent.style_color,
|
||||
"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
|
||||
@@ -282,13 +286,17 @@ 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,
|
||||
"isCreator": conversation.agent.creator == user,
|
||||
"is_creator": conversation.agent.creator == user,
|
||||
"color": conversation.agent.style_color,
|
||||
"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
|
||||
|
||||
Reference in New Issue
Block a user