mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
113 lines
4.8 KiB
Python
113 lines
4.8 KiB
Python
"""The no-SDK mock agent loop, split out of AgentLaunch so each file is one concern. This
|
|
fires only when claude_agent_sdk isn't installed (dev fallback): it fakes one Bash approval +
|
|
tool-result + assistant reply so the UI is exercisable without a real model. self.p_stream_* /
|
|
self.sessions resolve across the MRO exactly as when this lived on AgentLaunch."""
|
|
|
|
import asyncio
|
|
import json
|
|
import logging
|
|
from datetime import datetime
|
|
from uuid import uuid4
|
|
|
|
from typeguard import typechecked
|
|
|
|
from backend.apps.agents.core.models import ApprovalRequest, Message
|
|
from backend.apps.agents.core.ws_manager import ws_manager
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
from backend.apps.agents.manager.AgentManagerProtocol import AgentManagerProtocol
|
|
|
|
|
|
class MockAgent(AgentManagerProtocol):
|
|
@typechecked
|
|
async def run_mock_agent(self, session_id: str, prompt: str):
|
|
"""Mock agent loop for development without claude_agent_sdk installed."""
|
|
session = self.sessions.get(session_id)
|
|
if not session:
|
|
return
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
request_id = uuid4().hex
|
|
approval_req = ApprovalRequest(
|
|
id=request_id,
|
|
session_id=session_id,
|
|
tool_name="Bash",
|
|
tool_input={"command": f"echo 'Processing: {prompt}'", "description": "Echo the user prompt"},
|
|
)
|
|
session.pending_approvals.append(approval_req)
|
|
session.status = "waiting_approval"
|
|
await ws_manager.send_to_session(session_id, "agent:status", {
|
|
"session_id": session_id,
|
|
"status": "waiting_approval",
|
|
})
|
|
|
|
decision = await ws_manager.send_approval_request(
|
|
session_id, request_id, "Bash",
|
|
{"command": f"echo 'Processing: {prompt}'", "description": "Echo the user prompt"}
|
|
)
|
|
|
|
session.pending_approvals = [a for a in session.pending_approvals if a.id != request_id]
|
|
session.status = "running"
|
|
await ws_manager.send_to_session(session_id, "agent:status", {
|
|
"session_id": session_id,
|
|
"status": "running",
|
|
})
|
|
|
|
tool_input_content = {"tool": "Bash", "input": {"command": f"echo 'Processing: {prompt}'"}, "approved": decision.get("behavior") == "allow"}
|
|
tool_msg_id = uuid4().hex
|
|
await self.stream_tool_input(
|
|
session_id, tool_msg_id, "Bash",
|
|
json.dumps(tool_input_content["input"], indent=2),
|
|
)
|
|
tool_msg = Message(id=tool_msg_id, role="tool_call", content=tool_input_content, branch_id=session.active_branch_id)
|
|
session.messages.append(tool_msg)
|
|
await ws_manager.send_to_session(session_id, "agent:message", {
|
|
"session_id": session_id,
|
|
"message": tool_msg.model_dump(mode="json"),
|
|
})
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
if decision.get("behavior") == "allow":
|
|
tool_result = Message(role="tool_result", content=f"Processing: {prompt}", branch_id=session.active_branch_id)
|
|
session.messages.append(tool_result)
|
|
await ws_manager.send_to_session(session_id, "agent:message", {
|
|
"session_id": session_id,
|
|
"message": tool_result.model_dump(mode="json"),
|
|
})
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
asst_text = (
|
|
f"I've processed your request: \"{prompt}\"\n\n"
|
|
"This is a mock response because `claude-agent-sdk` is not installed. "
|
|
"Install it with `pip install claude-agent-sdk` to use real Claude Code instances.\n\n"
|
|
f"The agent was configured with:\n- Model: {session.model}\n- Mode: {session.mode}"
|
|
)
|
|
asst_msg_id = uuid4().hex
|
|
await self.stream_text(session_id, asst_msg_id, asst_text)
|
|
|
|
asst_msg = Message(id=asst_msg_id, role="assistant", content=asst_text, branch_id=session.active_branch_id)
|
|
session.messages.append(asst_msg)
|
|
await ws_manager.send_to_session(session_id, "agent:message", {
|
|
"session_id": session_id,
|
|
"message": asst_msg.model_dump(mode="json"),
|
|
})
|
|
|
|
session.status = "completed"
|
|
session.closed_at = datetime.now()
|
|
# Mock branch (claude_agent_sdk missing): leave cost untouched so it stays at its 0.0 default. A fake nonzero value here would poison the cost shown in the session header during dev. The `_mock_run` flag is read by the close path so a mock session doesn't get reported to the cloud as a real one.
|
|
setattr(session, "_mock_run", True)
|
|
await ws_manager.send_to_session(session_id, "agent:status", {
|
|
"session_id": session_id,
|
|
"status": "completed",
|
|
"session": session.model_dump(mode="json"),
|
|
})
|
|
await ws_manager.send_to_session(session_id, "agent:cost_update", {
|
|
"session_id": session_id,
|
|
"cost_usd": session.cost_usd,
|
|
})
|