- agent/task_registry.py: file-based JSONL event registry (cross-process) - agent/task_worker.py: autonomous LLM loop with weather/time/memory/web tools - agent/dispatch_mcp.py: MCP tool exposing dispatch_task to the main agent - agent/agent.py: registers dispatch toolset, polls task events → room data - web: slide-out task panel (FAB button + badge), live step streaming via data channel topic 'tasks', status dots (running/completed/failed) - Dockerfile: copies new task_*.py and dispatch_mcp.py files The dispatch MCP runs in its own process; events flow through /tmp/tasks/events.jsonl which the main agent tails every second and forwards to the browser. Tasks run up to 10 LLM iterations with tool calls.
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""Dispatch MCP server — lets Hope spawn background tasks.
|
|
|
|
The main agent calls dispatch_task(description) when the user asks for
|
|
something long-running. The tool returns immediately with a task ID; the
|
|
actual work runs in a background asyncio task via TaskWorker.
|
|
|
|
Communication with the main agent process happens via /tmp/tasks/events.jsonl
|
|
(the file-based registry).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
|
|
from mcp.server.fastmcp import FastMCP
|
|
|
|
logger = logging.getLogger("voice-agent.dispatch")
|
|
|
|
mcp = FastMCP("dispatch")
|
|
|
|
|
|
@mcp.tool()
|
|
async def dispatch_task(description: str) -> str:
|
|
"""Spawn a background task to research or look something up.
|
|
|
|
Use this when the user asks you to do something that will take more than
|
|
a few seconds (research, look up news, multi-step investigation). The task
|
|
runs in parallel while you continue the conversation. When it completes,
|
|
the result will be delivered back to you automatically — you can tell the
|
|
user "I'll let you know when I find out."
|
|
|
|
Returns a short confirmation with the task ID.
|
|
"""
|
|
try:
|
|
from agent.task_registry import registry
|
|
from agent.task_worker import TaskWorker
|
|
except ImportError:
|
|
from task_registry import registry
|
|
from task_worker import TaskWorker
|
|
|
|
task = registry.create(description)
|
|
worker = TaskWorker(task.id, description)
|
|
asyncio.create_task(_run_worker(worker, task.id))
|
|
logger.info("Dispatched task %s: %s", task.id, description)
|
|
return f"Task {task.id} started. I'll report back when it's done."
|
|
|
|
|
|
async def _run_worker(worker: "TaskWorker", task_id: str):
|
|
try:
|
|
from agent.task_registry import registry
|
|
except ImportError:
|
|
from task_registry import registry
|
|
|
|
try:
|
|
await worker.run()
|
|
except Exception as e:
|
|
logger.exception("Task %s crashed: %s", task_id, e)
|
|
registry.fail(task_id, str(e))
|