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:
Shivering Ocelot committed 2026-05-25 18:05:17 -05:00
1 parent eb9a1ad9e8
commit 4e15b44d39
14 files changed
+1151 -522

No files matched your search

+26
View File
@@ -30,6 +30,7 @@ let credentialsDeferred = createDeferred();
const pendingActions = {}; // Maps nonce to { resolve, reject }
const chatListeners = {}; // Maps chatId to an array of callback functions
const orphanHandlers = []; // Queue of callbacks for orphan server messages
const runListeners = []; // Queue of callbacks for run events
function createDeferred() {
let resolve, reject;
@@ -191,6 +192,15 @@ function handleIncomingMessage(rawData) {
} else {
console.log(`No active listeners for chatId ${chatId}`);
}
} else if (packet.action == 'run-event') {
// Broadcast run events to all listeners
runListeners.forEach(callback => {
try {
callback(packet);
} catch (e) {
console.error(`Error in run event listener:`, e);
}
});
} else {
if (orphanHandlers.length) {
const f = orphanHandlers.shift();
@@ -247,6 +257,22 @@ export function onChatMessages(chatId, callback) {
};
}
export function onRunEvents(callback) {
if (typeof callback !== 'function') {
throw new TypeError("onRunEvents: callback must be a function.");
}
runListeners.push(callback);
// Return an unsubscribe function
return () => {
const index = runListeners.indexOf(callback);
if (index > -1) {
runListeners.splice(index, 1);
}
};
}
export async function getChats() {
if (cache.chats === null) {
const response = await sendAction("get-chats", {});