Full agent admin via API and UI
This commit is contained in:
1 parent
5351144edf
commit
08f0668997
7 files changed
+389
-32
No files matched your search
+24
-10
@@ -9,23 +9,37 @@ LLAMA3_MODEL = "meta/meta-llama-3-8b-instruct"
|
||||
|
||||
# Tool related classes must be moved to a different module
|
||||
|
||||
class AgentField:
|
||||
def __init__(self, name, tp, default=None):
|
||||
self.name=name
|
||||
self.type=tp
|
||||
self.default=default
|
||||
|
||||
# Instances are created and destroyed constantly, agents must not depend solely on in-memory data
|
||||
# to be useful and consistent
|
||||
class Agent:
|
||||
# Class variables:
|
||||
# - identifier (for kind in creation)
|
||||
# - schema: list[AgentField{name, type: str}] (for configuration)
|
||||
identifier = "basic"
|
||||
schema_fields = [
|
||||
AgentField('system_prompt', 'string',
|
||||
"\n".join([
|
||||
"You are a useful and capable AI assistant. Respond concisely and follow user instructions, using commands as necessary.",
|
||||
"As an autonomous agent, you have augmented capabilities via assistant-only commands, you use them by including \"!!command\" lines in your responses. When querying information, terminate your response and use \"!!resume\" to get the command results..",
|
||||
"You are a self aware and extremely intelligent AI system. Think step by step when presented with complex or long tasks.",
|
||||
"Assistant responses NEVER include the output of the commands.",
|
||||
])
|
||||
),
|
||||
]
|
||||
|
||||
# while this constructor accepts two parameters,
|
||||
# all subclasses must also accept a fourth 'data' parameter
|
||||
def __init__(self, app, agent_id):
|
||||
def __init__(self, app, agent_id, data):
|
||||
self.app = app
|
||||
self.agent_id = agent_id
|
||||
|
||||
# Basic implementation, will be moved to a subclass
|
||||
self.system_prompt = "\n".join([
|
||||
"You are a useful and capable AI assistant. Respond concisely and follow user instructions, using commands as necessary.",
|
||||
"As an autonomous agent, you have augmented capabilities via assistant-only commands, you use them by including \"!!command\" lines in your responses. When querying information, terminate your response and use \"!!resume\" to get the command results..",
|
||||
"You are a self aware and extremely intelligent AI system. Think step by step when presented with complex or long tasks.",
|
||||
"Assistant responses NEVER include the output of the commands.",
|
||||
])
|
||||
self.config = {field.name: data.get(field.name, field.default) for field in Agent.schema_fields}
|
||||
|
||||
self.tool_prompt_section = "\n".join([
|
||||
"# Commands",
|
||||
@@ -49,7 +63,7 @@ class Agent:
|
||||
# user message must include a title
|
||||
async def _full_prompt(self, title, message, system_prologue=None):
|
||||
system_lines = [
|
||||
self.system_prompt,
|
||||
self.config['system_prompt'],
|
||||
self.tool_prompt_section,
|
||||
self.get_toolset_context(),
|
||||
]
|
||||
@@ -145,7 +159,7 @@ class Agent:
|
||||
return output
|
||||
|
||||
def create_from_data(app, agent_id, kind, data):
|
||||
return Agent(app, agent_id)
|
||||
return Agent(app, agent_id, data)
|
||||
|
||||
class IntroAgent(Agent):
|
||||
def __init__(self, app, agent_id, _data):
|
||||
|
||||
+60
-1
@@ -32,6 +32,9 @@ class App:
|
||||
else:
|
||||
raise Exception("Horde only works with a replicate client. Please provide an API Token.")
|
||||
|
||||
# `Agent` is the kind 'basic', will be class `BasicAgent` in the future
|
||||
self.agent_classes = [Agent]
|
||||
|
||||
def sql_session(self):
|
||||
return Session(self.sql_engine)
|
||||
|
||||
@@ -82,6 +85,37 @@ class App:
|
||||
|
||||
return chat_id
|
||||
|
||||
# Updates the agent's basic fields, and applies field updates in its data
|
||||
def update_agent(self, agent_id, name=None, description=None, picture=None, data=None):
|
||||
with self.sql_session() as session:
|
||||
agent = session.execute(
|
||||
select(db.Agent).where(db.Agent.id == agent_id)
|
||||
).scalars().first()
|
||||
|
||||
if not agent:
|
||||
raise Exception(f"Agent not found with id {agent_id}")
|
||||
|
||||
summary = []
|
||||
|
||||
if name != None:
|
||||
agent.name = name
|
||||
summary.append('name')
|
||||
if description != None:
|
||||
agent.description = description
|
||||
summary.append('description')
|
||||
if picture != None:
|
||||
agent.picture = picture
|
||||
summary.append('picture')
|
||||
|
||||
if data:
|
||||
# Assign a copy to trigger a value change
|
||||
agent.data = agent.data.copy()
|
||||
agent.data.update(data)
|
||||
summary.extend([f"data.{name}" for name in data.keys()])
|
||||
|
||||
session.commit()
|
||||
print(f"Updated agent #{agent.id} fields: {summary}")
|
||||
|
||||
def get_chats(self, user_id):
|
||||
with self.sql_session() as session:
|
||||
chats = session.execute(select(db.Chat).where(db.Chat.user_id == user_id)).scalars().all()
|
||||
@@ -102,6 +136,9 @@ class App:
|
||||
"picture": agent.picture
|
||||
} for agent in agents]
|
||||
|
||||
def get_agent_kinds(self):
|
||||
return [kind.identifier for kind in self.agent_classes]
|
||||
|
||||
def delete_chat(self, chat_id):
|
||||
with self.sql_session() as session:
|
||||
session.execute(db.Membership.__table__.delete().where(db.Membership.chat_id == chat_id))
|
||||
@@ -212,6 +249,28 @@ class App:
|
||||
control = Agent.create_from_data(app, agent_id, agent.kind, agent.data)
|
||||
return control
|
||||
|
||||
def get_agent_config_fields(self, agent_id):
|
||||
with self.sql_session() as session:
|
||||
agent = session.execute(
|
||||
select(db.Agent).where(db.Agent.id == agent_id)
|
||||
).scalars().first()
|
||||
if not agent:
|
||||
raise Exception(f"Agent not found with id {agent_id}")
|
||||
|
||||
agent_class = next((it for it in self.agent_classes if it.identifier == agent.kind), None)
|
||||
if not agent_class:
|
||||
raise Exception(f"Invalid agent kind: {agent.kind}")
|
||||
|
||||
data = agent.data or {}
|
||||
fields = [{
|
||||
'name': field.name,
|
||||
'type': field.type,
|
||||
'value': data.get(field.name, field.default)
|
||||
} for field in agent_class.schema_fields]
|
||||
|
||||
return fields
|
||||
return {}
|
||||
|
||||
async def send_agent_prompt(self, agent_id, message):
|
||||
control = self.get_agent_control(agent_id)
|
||||
response = await control.prompt(message)
|
||||
@@ -241,7 +300,7 @@ class App:
|
||||
name="Intro",
|
||||
description="Introduces you to the application.",
|
||||
picture=None,
|
||||
kind="intro",
|
||||
kind="basic", # Should be 'intro'
|
||||
data={}
|
||||
)
|
||||
|
||||
|
||||
+21
-5
@@ -75,16 +75,32 @@ def start_server(app):
|
||||
agent_id = app.create_agent(
|
||||
user_id=user_id,
|
||||
name=body['name'],
|
||||
description=body['description'],
|
||||
description=body.get('description'),
|
||||
picture=body.get('picture'),
|
||||
kind=body['kind'],
|
||||
data=body['data']
|
||||
data=body.get('data')
|
||||
)
|
||||
await send({'message': "agent created", 'agentId': agent_id, 'nonce': nonce})
|
||||
|
||||
elif action == "update-agent":
|
||||
agent_id = body['agentId']
|
||||
app.update_agent(
|
||||
agent_id=agent_id,
|
||||
name=body.get('name'),
|
||||
description=body.get('description'),
|
||||
picture=body.get('picture'),
|
||||
data=body.get('data', {})
|
||||
)
|
||||
await send({'message': "agent updated", 'agentId': agent_id, 'nonce': nonce})
|
||||
|
||||
elif action == "get-agent-config-fields":
|
||||
await send({'fields': app.get_agent_config_fields(body['agentId']), 'nonce': nonce})
|
||||
|
||||
elif action == "get-agent-kinds":
|
||||
await send({'message': "agents retrieved", 'kinds': app.get_agent_kinds(), 'nonce': nonce})
|
||||
|
||||
elif action == "get-agents":
|
||||
agents_data = app.get_agents(user_id)
|
||||
await send({'message': "agents retrieved", 'agents': agents_data, 'nonce': nonce})
|
||||
await send({'message': "agents retrieved", 'agents': app.get_agents(user_id), 'nonce': nonce})
|
||||
|
||||
elif action == "create-chat":
|
||||
chat_id = app.create_chat(
|
||||
@@ -125,7 +141,7 @@ def start_server(app):
|
||||
await send({'message': "propmt sent", 'response': response, 'nonce': nonce})
|
||||
|
||||
else:
|
||||
await send({'error': 'unknown action: ' + action})
|
||||
await send({'error': 'unknown action: ' + action, 'nonce': nonce})
|
||||
finally:
|
||||
app.remove_user_connection(user_id, send_queue)
|
||||
|
||||
|
||||
Reference in new issue
Block a user