Refactor agent execution to use AgentRun with OpenAI-compatible API
Major refactoring to support isolated run objects with streaming, events, and OpenAI-compatible endpoints. ## Backend Changes - **Agent class**: Added `run()` method, `received_message()` wraps chat into AgentRun - **AgentRun class**: New class for run state with event system, serialization, and user tracking - **inference.py**: Complete refactor with `build_messages()`, `chat_completion()`, tool support for llama-cpp/ollama/replicate - **tools.py**: New centralized tool management (ToolSpec, ToolParameter, format_tools_for_engine) - **tools.py**: Removed legacy Toolset/Action/ToolDescription system - **server.py**: Added OpenAI-compatible `/api/openai/v1/chat/completions` endpoint with streaming support - **temporary.py**: Added `self.runs` dict to State class - **expert.py**: Refactored to use new Agent.run() signature - **scripted.py**: Updated to raise NotImplementedError ## Frontend Changes - **backend.js**: Added `onRunEvents()` for run event subscriptions - **Sidebar.vue**: Added runs navigation mode - **router/index.js**: Renamed User route to RunsView - **RunsView.vue**: New view for listing/monitoring agent runs ## Breaking Changes - Agent `prompt()` signature changed: `(instruction, input=None, **kwargs)` instead of `(content)` - Agent `received_message()` must use AgentRun pattern - Tools.py removed, functionality moved to tool.py - Deprecated tool prompt/action system removed
This commit is contained in:
1 parent
eb9a1ad9e8
commit
4e15b44d39
14 files changed
+1151
-522
No files matched your search
+46
-16
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
@@ -25,6 +26,7 @@ class App(ChatMixin):
|
||||
self.user_connections = {}
|
||||
self.remote_connections = {}
|
||||
self.streams = {}
|
||||
self.event_listeners = []
|
||||
|
||||
self.tmp = tmp.State()
|
||||
|
||||
@@ -37,14 +39,25 @@ class App(ChatMixin):
|
||||
raise Exception("Horde only works with a replicate connection. Please provide an API Token.")
|
||||
|
||||
self.agent_classes = [
|
||||
agent_mod.BasicAgent,
|
||||
agents_mod.CharacterAgent,
|
||||
agents_mod.ExpertAgent,
|
||||
agents_mod.ScriptedAgent,
|
||||
]
|
||||
|
||||
# --- Connection Management (Synchronous) ---
|
||||
|
||||
def on_event(self, callback):
|
||||
"""Register a callback for events."""
|
||||
if callback not in self.event_listeners:
|
||||
self.event_listeners.append(callback)
|
||||
return lambda: self.event_listeners.remove(callback)
|
||||
|
||||
def emit_event(self, name, data=None):
|
||||
"""Emit an event to all listeners."""
|
||||
for listener in self.event_listeners:
|
||||
try:
|
||||
listener(name, data)
|
||||
except Exception as e:
|
||||
print(f"Error in event listener: {name}, {data}")
|
||||
|
||||
def add_user_connection(self, user_id, channel):
|
||||
if user_id not in self.user_connections:
|
||||
self.user_connections[user_id] = []
|
||||
@@ -62,7 +75,7 @@ class App(ChatMixin):
|
||||
if user_id not in self.remote_connections:
|
||||
self.remote_connections[user_id] = {}
|
||||
user_connections = self.remote_connections[user_id]
|
||||
|
||||
|
||||
if remotename not in user_connections:
|
||||
user_connections[remotename] = []
|
||||
if channel not in user_connections[remotename]:
|
||||
@@ -133,7 +146,7 @@ class App(ChatMixin):
|
||||
|
||||
async def update_agent(self, agent_id, name=None, description=None, picture=None, agentname=None, data=None):
|
||||
agent = await self.get_agent(agent_id)
|
||||
|
||||
|
||||
summary = []
|
||||
if name is not None:
|
||||
agent.name = name
|
||||
@@ -206,7 +219,7 @@ class App(ChatMixin):
|
||||
)
|
||||
print(f"Created user #{user.id}: {username} ({email})")
|
||||
return user.id
|
||||
else:
|
||||
else:
|
||||
print(f"Signup error: '{email}' or '{username}' are already used")
|
||||
return None
|
||||
|
||||
@@ -214,11 +227,11 @@ class App(ChatMixin):
|
||||
|
||||
async def get_agent_control(self, agent_id):
|
||||
agent = await self.get_agent(agent_id)
|
||||
|
||||
|
||||
agent_class = next((kind for kind in self.agent_classes if kind.identifier == agent.kind), None)
|
||||
if agent_class is None:
|
||||
raise Exception(f"invalid agent kind: {agent.kind}")
|
||||
|
||||
|
||||
control = agent_class(self, agent.data)
|
||||
control._set_basic_info(agent_id, agent.name)
|
||||
return control
|
||||
@@ -265,7 +278,7 @@ class App(ChatMixin):
|
||||
kind="conversation",
|
||||
data={}
|
||||
)
|
||||
|
||||
|
||||
# --- Resource Management ---
|
||||
|
||||
async def get_agent_resource(self, agent_id, kind, key):
|
||||
@@ -274,8 +287,8 @@ class App(ChatMixin):
|
||||
|
||||
async def save_agent_resource(self, agent_id, kind, key, value):
|
||||
await db.AgentResource.update_or_create(
|
||||
agent_id=agent_id,
|
||||
kind=kind,
|
||||
agent_id=agent_id,
|
||||
kind=kind,
|
||||
key=key,
|
||||
value=value,
|
||||
)
|
||||
@@ -291,8 +304,8 @@ class App(ChatMixin):
|
||||
async def save_model(self, model_id, kind, name, value):
|
||||
if model_id is None:
|
||||
model = await db.Model.create(
|
||||
name=name,
|
||||
kind=kind,
|
||||
name=name,
|
||||
kind=kind,
|
||||
value=value,
|
||||
)
|
||||
else:
|
||||
@@ -302,14 +315,31 @@ class App(ChatMixin):
|
||||
return None
|
||||
else:
|
||||
model = await db.Model.update_or_create(
|
||||
id=model_id,
|
||||
name=name,
|
||||
kind=kind,
|
||||
id=model_id,
|
||||
name=name,
|
||||
kind=kind,
|
||||
value=value,
|
||||
)
|
||||
print(f"Saved model: {model.id} - {name}")
|
||||
return model
|
||||
|
||||
async def create_agent_run(self, agent_id):
|
||||
agent = await self.get_agent(agent_id)
|
||||
run = agent_mod.AgentRun.create(
|
||||
self,
|
||||
agent_id=agent_id,
|
||||
user_id=agent.user_id,
|
||||
)
|
||||
return run.id
|
||||
|
||||
async def get_agent_runs(self, agent_id=None, user_id=None):
|
||||
runs = list(self.tmp.runs.values())
|
||||
if agent_id:
|
||||
runs = [r for r in runs if r.agent_id == agent_id]
|
||||
elif user_id:
|
||||
runs = [r for r in runs if r.user_id == user_id]
|
||||
return [r.serialize() for r in runs]
|
||||
|
||||
# --- Main Entry Point ---
|
||||
|
||||
async def main():
|
||||
|
||||
Reference in new issue
Block a user