mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[Haik]: satisfactory Agent class abstraction for now (altho im sure it doesnt fully work in the current setup yet), onto the AgentManager abstraction
This commit is contained in:
@@ -1,38 +1,19 @@
|
||||
"""Thin coordinator for agent sessions.
|
||||
# TODO: NON HAIK DEPS: ws_manager
|
||||
|
||||
Heavy logic lives in sibling modules:
|
||||
- agent_manager_ops – edit, close, resume, duplicate, invoke, LLM metadata
|
||||
- agent_loop – the SDK query loop, streaming, mock agent
|
||||
- agent_mock – session-completed analytics
|
||||
- prompt_builder – system-prompt composition & context injection
|
||||
- mcp_builder – MCP server construction & tool-policy helpers
|
||||
- session_store – on-disk persistence, history, message copying
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from claude_agent_sdk import ClaudeAgentOptions
|
||||
from pydantic import BaseModel, InstanceOf
|
||||
from pydantic import BaseModel, Field, InstanceOf
|
||||
from typing import List, Literal, Optional
|
||||
from typeguard import typechecked
|
||||
|
||||
from backend.apps.agents.models import AgentSession, AgentConfig, ApprovalRequest
|
||||
from backend.apps.agents.manager.ws_manager import ws_manager
|
||||
from backend.apps.agents.execution.prompt_builder import resolve_mode
|
||||
from backend.apps.agents.execution.mcp_builder import get_all_tool_names
|
||||
from backend.apps.settings.settings import load_settings
|
||||
from backend.apps.agents.manager.HaikFix.agent_loop import run_agent_loop
|
||||
from backend.apps.agents.manager.HaikFix.helpers.Message import Message
|
||||
from backend.apps.agents.manager.HaikFix.PromptChunks import ImageChunk
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
from backend.apps.agents.manager.HaikFix.run_agent_loop.run_agent_loop import run_agent_loop
|
||||
from backend.apps.agents.manager.HaikFix.shared_structs.Message import Message
|
||||
from backend.apps.agents.manager.HaikFix.shared_structs.PromptChunks import ImageChunk
|
||||
from backend.apps.agents.manager.HaikFix.shared_structs.ApprovalRequest import ApprovalRequest
|
||||
|
||||
os.environ.setdefault("CLAUDE_CODE_STREAM_CLOSE_TIMEOUT", "3600000")
|
||||
|
||||
@@ -48,66 +29,25 @@ class Skill(BaseModel):
|
||||
class Agent(BaseModel):
|
||||
model: str
|
||||
mode: str
|
||||
session_id: str
|
||||
status: Literal["running", "waiting_approval", "completed", "error", "stopped"]
|
||||
lock: InstanceOf[asyncio.Lock]
|
||||
pending_approvals: List[ApprovalRequest] = Field(default_factory=list)
|
||||
|
||||
session_id: str = Field(default_factory=lambda: uuid4().hex)
|
||||
config: ClaudeAgentOptions
|
||||
session: Optional[AgentSession] = None
|
||||
|
||||
branch_id: str = "main"
|
||||
children: List["Agent"] = Field(default_factory=list)
|
||||
parent_id: Optional[str] = None
|
||||
|
||||
task: Optional[asyncio.Task] = None
|
||||
lock: InstanceOf[asyncio.Lock] = Field(default_factory=asyncio.Lock)
|
||||
|
||||
@typechecked
|
||||
def __init__(
|
||||
self,
|
||||
model: str,
|
||||
mode: str,
|
||||
tools: List[str],
|
||||
effective_cwd: str,
|
||||
config: ClaudeAgentOptions,
|
||||
) -> None:
|
||||
id: str = uuid4().hex
|
||||
lock: asyncio.Lock = asyncio.Lock()
|
||||
super().__init__(
|
||||
model=model,
|
||||
mode=mode,
|
||||
tools=tools,
|
||||
effective_cwd=effective_cwd,
|
||||
status="running",
|
||||
id=id,
|
||||
task=None,
|
||||
lock=lock,
|
||||
config=config,
|
||||
)
|
||||
|
||||
async def launch_agent(self, config: AgentConfig) -> AgentSession:
|
||||
session_id = uuid4().hex
|
||||
mode_tools, _, mode_folder = resolve_mode(config.mode, get_all_tool_names)
|
||||
global_settings = load_settings()
|
||||
effective_cwd = (
|
||||
config.target_directory or mode_folder
|
||||
or global_settings.default_folder or os.path.expanduser("~")
|
||||
)
|
||||
if config.mode in ("view-builder", "skill-builder") and not config.target_directory:
|
||||
effective_cwd = os.path.join(effective_cwd, session_id)
|
||||
os.makedirs(effective_cwd, exist_ok=True)
|
||||
self.session = AgentSession(
|
||||
id=session_id, name=config.name,
|
||||
provider=getattr(config, "provider", "anthropic"),
|
||||
model=config.model, mode=config.mode,
|
||||
system_prompt=config.system_prompt, allowed_tools=mode_tools,
|
||||
max_turns=config.max_turns, cwd=effective_cwd,
|
||||
dashboard_id=config.dashboard_id,
|
||||
)
|
||||
self.session_id = session_id
|
||||
await ws_manager.emit_status(session_id, "running", self.session)
|
||||
return self.session
|
||||
|
||||
async def send_message(
|
||||
self,
|
||||
prompt: str,
|
||||
images: Optional[List[ImageChunk]] = None,
|
||||
):
|
||||
) -> None:
|
||||
async with self.lock:
|
||||
if self.task is not None and not self.task.done():
|
||||
print("[Agent.send_message] Agent is already running")
|
||||
@@ -133,21 +73,22 @@ class Agent(BaseModel):
|
||||
parent_id=self.parent_id,
|
||||
))
|
||||
|
||||
@typechecked
|
||||
async def stop_agent(self):
|
||||
for child in self.children:
|
||||
await child.stop_agent()
|
||||
|
||||
if self.task and not self.task.done():
|
||||
self.task.cancel()
|
||||
try:
|
||||
await self.task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
if self.session:
|
||||
for req in list[ApprovalRequest](self.session.pending_approvals):
|
||||
ws_manager.resolve_approval(req.id, {"behavior": "deny", "message": "Agent stopped"})
|
||||
self.session.pending_approvals = []
|
||||
if hasattr(self.session, '_cancel_event'):
|
||||
self.session._cancel_event.set()
|
||||
self.session.status = "stopped"
|
||||
if not self.session.closed_at:
|
||||
self.session.closed_at = datetime.now()
|
||||
await ws_manager.emit_status(self.session.id, "stopped", self.session)
|
||||
|
||||
# if self.session:
|
||||
for req in list[ApprovalRequest](self.pending_approvals):
|
||||
ws_manager.resolve_approval(req.id, {"behavior": "deny", "message": "Agent stopped"})
|
||||
self.pending_approvals = []
|
||||
|
||||
self.status = "stopped"
|
||||
await ws_manager.emit_status(self.session.id, "stopped", self.session)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
from backend.apps.agents.manager.HaikFix.helpers.Message import Message, ToolCallContent
|
||||
from backend.apps.agents.manager.HaikFix.shared_structs.Message import Message, ToolCallContent
|
||||
from claude_agent_sdk.types import TextBlock, ToolUseBlock, AssistantMessage
|
||||
from backend.apps.agents.manager.ws_manager import ws_manager
|
||||
from typeguard import typechecked
|
||||
+5
-15
@@ -1,12 +1,3 @@
|
||||
"""Main agent loop — orchestrates the Claude Agent SDK query loop.
|
||||
|
||||
Heavy logic is delegated to sibling modules:
|
||||
- agent_mock – mock-agent fallback, streaming helpers, session analytics
|
||||
- agent_hooks – SDK hook factories (approval, permissions, post-tool)
|
||||
- agent_options – MCP server construction & ClaudeAgentOptions building
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typeguard import typechecked
|
||||
|
||||
@@ -14,15 +5,14 @@ from claude_agent_sdk import (
|
||||
query, ClaudeAgentOptions, AssistantMessage,
|
||||
)
|
||||
from claude_agent_sdk.types import StreamEvent
|
||||
from backend.apps.agents.manager.HaikFix.helpers.handle_stream_event import handle_stream_event
|
||||
from backend.apps.agents.manager.HaikFix.helpers.handle_assistant_message import handle_assistant_message
|
||||
from backend.apps.agents.manager.HaikFix.run_agent_loop.helpers.handle_stream_event import handle_stream_event
|
||||
from backend.apps.agents.manager.HaikFix.run_agent_loop.helpers.handle_assistant_message import handle_assistant_message
|
||||
|
||||
from backend.apps.agents.manager.HaikFix.PromptChunks import ImageChunk, ImageChunkDict, TextChunk, TextChunkDict
|
||||
from backend.apps.agents.manager.HaikFix.shared_structs.PromptChunks import (
|
||||
ImageChunk, ImageChunkDict, TextChunk, TextChunkDict
|
||||
)
|
||||
from typing import List, Dict, Literal, Union, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@typechecked
|
||||
def build_image_prompt_content(prompt: str, images: List[ImageChunk]) -> List[TextChunk | ImageChunk]:
|
||||
content: List[Union[ImageChunkDict, TextChunkDict]] = [TextChunk(text=prompt).to_dict()]
|
||||
@@ -0,0 +1,11 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
class ApprovalRequest(BaseModel):
|
||||
id: str = Field(default_factory=lambda: uuid4().hex)
|
||||
session_id: str
|
||||
tool_name: str
|
||||
tool_input: dict[str, Any]
|
||||
created_at: datetime = Field(default_factory=datetime.now)
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
from typing import Optional, Literal, Union, List
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, Literal, Union, List, Dict
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
Reference in New Issue
Block a user