backend: separated agent classes. frontend: chat message context menu (does nothing)

This commit is contained in:
Shivering Ocelot committed 2025-06-01 23:59:38 -05:00
1 parent 3f37f727af
commit 22fe5619d8
5 files changed
+205 -82

No files matched your search

+94 -26
View File
@@ -17,11 +17,45 @@ class AgentField:
# Instances are created and destroyed constantly, agents must not depend solely on in-memory data
# to be useful and consistent
class Agent:
# Class variables:
# Class variables, must be defined by subclasses of Agent:
# - identifier (for kind in creation)
# - schema: list[AgentField{name, type: str}] (for configuration)
# - tools: list[ToolDescription] (actions the agent class support)
identifier = "basic"
# tools: list[ToolDescription] (custom actions supported by the class)
def __init__(self, app, data):
self.app = app
# These variables are set by '_set_basic_info'
self.id = None
self.name = None
# Auto populated based on the schema fields
agent_class = type(self)
self.config = {field.name: data.get(field.name, field.default) for field in getattr(agent_class, 'schema_fields', [])}
def _set_basic_info(self, id, name):
self.id = id
self.name = name
# Receives a single string prompt, and must return a string response
async def prompt(self, content):
raise Exception("'prompt' is not implemented for this agent class")
# Receives a chat message object, agent can send messages to the chat in response
async def received_message(self, message):
raise Exception("'received_message' is not implemented for this agent class")
# Executes a tool/action in this agent
async def execute_action(self, name: str, argument: dict):
# TODO: For security, check that the method is in the tool list
method = getattr(self, name)
result = method(**argument)
if inspect.isawaitable(result):
result = await result
return result
class ToolUsingAgent:
identifier = "tool-using"
schema_fields = [
AgentField('system_prompt', 'string',
"\n".join([
@@ -32,13 +66,12 @@ class Agent:
])
),
]
# tools: list[ToolDescription] (actions the agent class support)
tools = []
def __init__(self, app, agent_id, data):
self.app = app
self.agent_id = agent_id
self.config = {field.name: data.get(field.name, field.default) for field in Agent.schema_fields}
def __init__(self, app, data):
Agent.__init__(self, app, data)
self.tool_prompt_section = "\n".join([
"# Commands",
@@ -106,7 +139,6 @@ class Agent:
return agent_response or full_response
# Receives a single string prompt, and must respond with a string
async def prompt(self, content):
response = await self._full_prompt(f"User Message", content)
@@ -127,7 +159,7 @@ class Agent:
self.app.send_chat_message(
chat_id=message.chat_id,
sender=self.agent_id,
sender=self.id,
content=response,
)
@@ -154,17 +186,58 @@ class Agent:
action_result = json.dumps(action_result)
output += f"[{action.toolset_name}.{action.tool_name}]:{action_result}\n"
return output
def create_from_data(app, agent_id, kind, data):
return Agent(app, agent_id, data)
async def execute_action(self, name: str, argument: dict):
# TODO: For security, check that the method is in the tool list
method = getattr(self, name)
result = method(**argument)
if inspect.isawaitable(result):
result = await result
return result
class Llama8bAgent(Agent):
identifier = "llama-8b"
schema_fields = [
AgentField('system_prompt', 'string', default="You are a helpful AI assistant."),
]
tools = []
def __init__(self, app, data):
Agent.__init__(self, app, data)
async def prompt(self, content):
messages = [
LlamaMessage("system", self.config['system_prompt']),
LlamaMessage("user", content)
]
response = await replicate_llama_completion(self.app, messages)
return response
async def received_message(self, message):
def sender_name(message):
sender = message['sender']
is_digit = isinstance(sender, (int, str)) and str(sender).isdigit()
is_self = is_digit and str(sender) == str(self.id)
return self.name if is_self else sender
chat_context = self.app.get_chat_context(message.chat_id)
chat_summary = "\n".join(f"[{sender_name(message)}]: {message['content']}" for message in chat_context.messages)
messages = [
LlamaMessage("system", f"{self.config['system_prompt']}\nYou are given the context of the conversation, respond with your next message in the conversation."),
LlamaMessage("user", f"Recent Messages:\n{chat_summary}")
]
response = await replicate_llama_completion(self.app, messages)
self.app.send_chat_message(
chat_id=message.chat_id,
sender=self.id,
content=response,
)
class BasicAgent(Llama8bAgent):
identifier = "basic"
schema_fields = Llama8bAgent.schema_fields
tools = Llama8bAgent.tools
class CoderV1Agent(Llama8bAgent):
identifier = "coder-v1"
schema_fields = [
AgentField('system_prompt', 'string', default="You are an expert Software Engineer profficient in multiple programming languages."),
]
# TODO: add gral functions to the tool list
tools = []
async def gral_message(self, message_history: list, file_map: dict):
return await gral.gral_message(self.app, self.config['system_prompt'], message_history, file_map)
@@ -173,11 +246,6 @@ class Agent:
return await gral.gral_update_file(self.app, self.config['system_prompt'], message_history, filepath, file_content)
class IntroAgent(Agent):
def __init__(self, app, agent_id, _data):
Agent.__init__(self, app, agent_id)
self.system_prompt = "You are a useful and precise AI assistant. Respond concisely."
class ParsedResponse:
def __init__(self):
self.response = None