mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-05-25 23:06:20 +00:00
feat: Add collaboration tool and UI
Integrates real-time document collaboration with a new tool and UI. Co-authored-by: nicsins <nicsins@gmail.com>
This commit is contained in:
parent
f3c41bca08
commit
8df4fc3b19
8 changed files with 225 additions and 0 deletions
52
prompts/default/agent.system.tool.collaboration.md
Normal file
52
prompts/default/agent.system.tool.collaboration.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
### collaboration_tool
|
||||
|
||||
Interact with the real-time collaborative document space.
|
||||
Allows reading, writing (overwrite), and appending to shared documents.
|
||||
Use this to collaborate with human users or other agents.
|
||||
Default doc_id is "default".
|
||||
|
||||
usage:
|
||||
|
||||
1. Read document
|
||||
~~~json
|
||||
{
|
||||
"thoughts": [
|
||||
"Checking what is in the shared document..."
|
||||
],
|
||||
"tool_name": "collaboration_tool",
|
||||
"tool_args": {
|
||||
"action": "read",
|
||||
"doc_id": "default"
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
2. Append to document
|
||||
~~~json
|
||||
{
|
||||
"thoughts": [
|
||||
"Adding my analysis to the shared doc..."
|
||||
],
|
||||
"tool_name": "collaboration_tool",
|
||||
"tool_args": {
|
||||
"action": "append",
|
||||
"content": "Here is the summary of the data...",
|
||||
"doc_id": "default"
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
3. Overwrite document
|
||||
~~~json
|
||||
{
|
||||
"thoughts": [
|
||||
"Clearing the doc and starting fresh..."
|
||||
],
|
||||
"tool_name": "collaboration_tool",
|
||||
"tool_args": {
|
||||
"action": "write",
|
||||
"content": "Meeting Agenda:\n1. Intro...",
|
||||
"doc_id": "default"
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
|
@ -15,3 +15,5 @@
|
|||
{{ include './agent.system.tool.input.md' }}
|
||||
|
||||
{{ include './agent.system.tool.browser.md' }}
|
||||
|
||||
{{ include './agent.system.tool.collaboration.md' }}
|
||||
|
|
|
|||
32
python/collaboration.py
Normal file
32
python/collaboration.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
from flask_socketio import SocketIO, emit, join_room, leave_room
|
||||
from flask import request
|
||||
|
||||
socketio = SocketIO(cors_allowed_origins="*")
|
||||
|
||||
# In-memory store for documents: {doc_id: content}
|
||||
documents = {}
|
||||
|
||||
def init_collaboration(app):
|
||||
socketio.init_app(app)
|
||||
|
||||
@socketio.on('connect')
|
||||
def handle_connect():
|
||||
pass
|
||||
|
||||
@socketio.on('join_document')
|
||||
def on_join(data):
|
||||
room = data.get('doc_id', 'default')
|
||||
join_room(room)
|
||||
# Send current document state
|
||||
content = documents.get(room, "")
|
||||
emit('document_state', {'content': content}, room=request.sid)
|
||||
|
||||
@socketio.on('update_document')
|
||||
def on_update(data):
|
||||
room = data.get('doc_id', 'default')
|
||||
content = data.get('content', '')
|
||||
documents[room] = content
|
||||
# Broadcast to others in the room
|
||||
emit('document_updated', {'content': content}, room=room, include_self=False)
|
||||
|
||||
return socketio
|
||||
24
python/tools/collaboration_tool.py
Normal file
24
python/tools/collaboration_tool.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from python.helpers.tool import Tool, Response
|
||||
from python.collaboration import documents, socketio
|
||||
|
||||
class CollaborationTool(Tool):
|
||||
async def execute(self, action, content="", doc_id="default", **kwargs):
|
||||
if action == "read":
|
||||
text = documents.get(doc_id, "")
|
||||
return Response(message=f"Document content:\n{text}", break_loop=False)
|
||||
|
||||
elif action == "write":
|
||||
# Overwrite
|
||||
documents[doc_id] = content
|
||||
socketio.emit('document_updated', {'content': content}, room=doc_id)
|
||||
return Response(message="Document updated.", break_loop=False)
|
||||
|
||||
elif action == "append":
|
||||
current = documents.get(doc_id, "")
|
||||
new_content = current + "\n" + content
|
||||
documents[doc_id] = new_content
|
||||
socketio.emit('document_updated', {'content': new_content}, room=doc_id)
|
||||
return Response(message="Appended to document.", break_loop=False)
|
||||
|
||||
else:
|
||||
return Response(message="Unknown action. Use read, write, or append.", break_loop=False)
|
||||
|
|
@ -10,6 +10,7 @@ from python.helpers.cloudflare_tunnel import CloudflareTunnel
|
|||
from python.helpers.extract_tools import load_classes_from_folder
|
||||
from python.helpers.api import ApiHandler
|
||||
from python.helpers.print_style import PrintStyle
|
||||
from python.collaboration import init_collaboration
|
||||
|
||||
|
||||
# initialize the internal Flask server
|
||||
|
|
@ -126,6 +127,9 @@ def run():
|
|||
for handler in handlers:
|
||||
register_api_handler(app, handler)
|
||||
|
||||
# Initialize collaboration (SocketIO)
|
||||
socketio = init_collaboration(app)
|
||||
|
||||
try:
|
||||
server = make_server(
|
||||
host=host,
|
||||
|
|
|
|||
65
webui/collaboration.html
Normal file
65
webui/collaboration.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Collaboration Space</title>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background-color: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
#editor-container {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#editor {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background-color: #252526;
|
||||
color: #d4d4d4;
|
||||
border: 1px solid #3e3e42;
|
||||
padding: 10px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
resize: none;
|
||||
outline: none;
|
||||
}
|
||||
header {
|
||||
padding: 10px 20px;
|
||||
background-color: #333333;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
.status {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Collaboration Space</h1>
|
||||
<div id="status" class="status">Disconnected</div>
|
||||
</header>
|
||||
<div id="editor-container">
|
||||
<textarea id="editor" placeholder="Start typing..."></textarea>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
|
||||
<script src="js/collaboration.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -351,6 +351,13 @@
|
|||
<span x-text="paused ? 'Resume Agent' : 'Pause Agent'"></span>
|
||||
</button>
|
||||
|
||||
<button class="text-button" id="collaboration_window" @click="window.open('collaboration.html', '_blank')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
|
||||
<path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/>
|
||||
</svg>
|
||||
<p>Collaboration</p>
|
||||
</button>
|
||||
|
||||
<button class="text-button" @click="loadKnowledge()"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
|
|
|
|||
39
webui/js/collaboration.js
Normal file
39
webui/js/collaboration.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
const socket = io();
|
||||
const editor = document.getElementById('editor');
|
||||
const statusDiv = document.getElementById('status');
|
||||
const docId = 'default';
|
||||
|
||||
socket.on('connect', () => {
|
||||
statusDiv.textContent = 'Connected';
|
||||
console.log('Connected to server');
|
||||
socket.emit('join_document', { doc_id: docId });
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
statusDiv.textContent = 'Disconnected';
|
||||
console.log('Disconnected from server');
|
||||
});
|
||||
|
||||
socket.on('document_state', (data) => {
|
||||
console.log('Received document state');
|
||||
editor.value = data.content;
|
||||
});
|
||||
|
||||
socket.on('document_updated', (data) => {
|
||||
console.log('Received document update');
|
||||
// Save cursor position
|
||||
const start = editor.selectionStart;
|
||||
const end = editor.selectionEnd;
|
||||
|
||||
editor.value = data.content;
|
||||
|
||||
// Restore cursor
|
||||
// This is naive and will be incorrect if text was inserted before cursor
|
||||
// But sufficient for a prototype
|
||||
editor.setSelectionRange(start, end);
|
||||
});
|
||||
|
||||
editor.addEventListener('input', () => {
|
||||
const content = editor.value;
|
||||
socket.emit('update_document', { doc_id: docId, content: content });
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue