Files
multiAgentTry/backend/app/services/file_lock.py
Claude Code dc398d7c7b 完整实现 Swarm 多智能体协作系统
- 新增 CLIPluginAdapter 统一接口 (backend/app/core/agent_adapter.py)
- 新增 LLM 服务层,支持 Anthropic/OpenAI/DeepSeek/Ollama (backend/app/services/llm_service.py)
- 新增 Agent 执行引擎,支持文件锁自动管理 (backend/app/services/agent_executor.py)
- 新增 NativeLLMAgent 原生 LLM 适配器 (backend/app/adapters/native_llm_agent.py)
- 新增进程管理器 (backend/app/services/process_manager.py)
- 新增 Agent 控制 API (backend/app/routers/agents_control.py)
- 新增 WebSocket 实时通信 (backend/app/routers/websocket.py)
- 更新前端 AgentsPage,支持启动/停止 Agent
- 测试通过:Agent 启动、批量操作、栅栏同步

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:32:11 +08:00

116 lines
3.7 KiB
Python

"""
文件锁服务 - 管理 Agent 对文件的访问锁
"""
import asyncio
from datetime import datetime
from typing import Dict, List, Optional
from dataclasses import dataclass, asdict
from .storage import get_storage
@dataclass
class LockInfo:
"""文件锁信息"""
file_path: str
agent_id: str
acquired_at: str
agent_name: str = ""
@property
def elapsed_seconds(self) -> int:
acquired_time = datetime.fromisoformat(self.acquired_at)
return int((datetime.now() - acquired_time).total_seconds())
@property
def elapsed_display(self) -> str:
seconds = self.elapsed_seconds
if seconds < 60:
return f"{seconds}s ago"
minutes = seconds // 60
secs = seconds % 60
return f"{minutes}m {secs:02d}s ago"
class FileLockService:
"""文件锁服务"""
LOCKS_FILE = "cache/file_locks.json"
LOCK_TIMEOUT = 300
def __init__(self):
self._storage = get_storage()
self._lock = asyncio.Lock()
async def _load_locks(self) -> Dict[str, Dict]:
return await self._storage.read_json(self.LOCKS_FILE)
async def _save_locks(self, locks: Dict[str, Dict]) -> None:
await self._storage.write_json(self.LOCKS_FILE, locks)
def _is_expired(self, lock_data: Dict) -> bool:
acquired_at = datetime.fromisoformat(lock_data["acquired_at"])
return (datetime.now() - acquired_at).total_seconds() >= self.LOCK_TIMEOUT
async def _cleanup_expired(self, locks: Dict[str, Dict]) -> Dict[str, Dict]:
return {k: v for k, v in locks.items() if not self._is_expired(v)}
async def acquire_lock(self, file_path: str, agent_id: str, agent_name: str = "") -> bool:
async with self._lock:
locks = await self._cleanup_expired(await self._load_locks())
if file_path in locks and locks[file_path]["agent_id"] != agent_id:
return False
locks[file_path] = asdict(LockInfo(
file_path=file_path,
agent_id=agent_id,
acquired_at=datetime.now().isoformat(),
agent_name=agent_name
))
await self._save_locks(locks)
return True
async def release_lock(self, file_path: str, agent_id: str) -> bool:
async with self._lock:
locks = await self._load_locks()
if file_path not in locks or locks[file_path]["agent_id"] != agent_id:
return False
del locks[file_path]
await self._save_locks(locks)
return True
async def get_locks(self) -> List[LockInfo]:
locks = await self._cleanup_expired(await self._load_locks())
return [LockInfo(**data) for data in locks.values()]
async def check_locked(self, file_path: str) -> Optional[str]:
locks = await self._cleanup_expired(await self._load_locks())
return locks.get(file_path, {}).get("agent_id")
async def get_agent_locks(self, agent_id: str) -> List[LockInfo]:
return [lock for lock in await self.get_locks() if lock.agent_id == agent_id]
async def release_all_agent_locks(self, agent_id: str) -> int:
async with self._lock:
locks = await self._load_locks()
to_remove = [k for k, v in locks.items() if v["agent_id"] == agent_id]
for k in to_remove:
del locks[k]
await self._save_locks(locks)
return len(to_remove)
# 简化单例实现
_file_lock_service: Optional[FileLockService] = None
def get_file_lock_service() -> FileLockService:
global _file_lock_service
if _file_lock_service is None:
_file_lock_service = FileLockService()
return _file_lock_service