2026-03-10 18:27:44 +08:00
|
|
|
import uuid
|
2026-03-10 18:58:03 +08:00
|
|
|
from datetime import datetime
|
2026-03-10 18:27:44 +08:00
|
|
|
from app.models.session import SessionType, Task, TaskStatus, CreateSessionRequest
|
|
|
|
|
from app.services.opencode_client import opencode_client
|
2026-03-10 18:58:03 +08:00
|
|
|
from app.services.storage import storage
|
|
|
|
|
from app.services.history import history_service
|
2026-03-10 18:27:44 +08:00
|
|
|
from app.core.logging import logger
|
|
|
|
|
|
2026-03-10 18:58:03 +08:00
|
|
|
TASKS_KEY = "tasks"
|
|
|
|
|
|
2026-03-10 18:27:44 +08:00
|
|
|
|
|
|
|
|
class SessionManager:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.tasks: dict[str, Task] = {}
|
|
|
|
|
|
2026-03-10 18:58:03 +08:00
|
|
|
def load(self):
|
|
|
|
|
data = storage.load(TASKS_KEY, {})
|
|
|
|
|
if isinstance(data, dict):
|
|
|
|
|
for k, v in data.items():
|
|
|
|
|
self.tasks[k] = Task(**v)
|
|
|
|
|
logger.info(f"Loaded {len(self.tasks)} tasks")
|
|
|
|
|
|
|
|
|
|
def save(self):
|
|
|
|
|
data = {k: v.model_dump() for k, v in self.tasks.items()}
|
|
|
|
|
storage.save(TASKS_KEY, data)
|
|
|
|
|
|
2026-03-10 18:27:44 +08:00
|
|
|
async def create_task(self, request: CreateSessionRequest) -> Task:
|
|
|
|
|
task_id = str(uuid.uuid4())
|
|
|
|
|
task = Task(
|
|
|
|
|
id=task_id,
|
|
|
|
|
type=request.type,
|
|
|
|
|
prompt=request.prompt or "",
|
2026-03-10 18:58:03 +08:00
|
|
|
created_at=datetime.now(),
|
2026-03-10 18:27:44 +08:00
|
|
|
)
|
|
|
|
|
self.tasks[task_id] = task
|
2026-03-10 18:58:03 +08:00
|
|
|
self.save()
|
2026-03-10 18:27:44 +08:00
|
|
|
logger.info(f"Created task: {task_id}")
|
|
|
|
|
return task
|
|
|
|
|
|
|
|
|
|
async def execute_task(self, task_id: str) -> dict:
|
|
|
|
|
task = self.tasks.get(task_id)
|
|
|
|
|
if not task:
|
|
|
|
|
raise ValueError(f"Task {task_id} not found")
|
|
|
|
|
|
|
|
|
|
task.status = TaskStatus.RUNNING
|
2026-03-10 18:58:03 +08:00
|
|
|
task.started_at = datetime.now()
|
|
|
|
|
self.save()
|
|
|
|
|
|
2026-03-10 18:27:44 +08:00
|
|
|
session = await opencode_client.create_session(
|
|
|
|
|
title=task.prompt[:50] if task.prompt else None
|
|
|
|
|
)
|
|
|
|
|
session_id = session["id"]
|
|
|
|
|
task.session_id = session_id
|
2026-03-10 18:58:03 +08:00
|
|
|
self.save()
|
2026-03-10 18:27:44 +08:00
|
|
|
|
|
|
|
|
logger.info(f"Executing task {task_id} with session {session_id}")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
result = await opencode_client.send_message(session_id, task.prompt)
|
|
|
|
|
task.status = TaskStatus.COMPLETED
|
2026-03-10 18:58:03 +08:00
|
|
|
task.finished_at = datetime.now()
|
|
|
|
|
self.save()
|
|
|
|
|
history_service.add_task_history(task)
|
2026-03-10 18:27:44 +08:00
|
|
|
return result
|
|
|
|
|
except Exception as e:
|
|
|
|
|
task.status = TaskStatus.FAILED
|
2026-03-10 18:58:03 +08:00
|
|
|
task.finished_at = datetime.now()
|
|
|
|
|
task.error = str(e)
|
|
|
|
|
self.save()
|
|
|
|
|
history_service.add_task_history(task)
|
2026-03-10 18:27:44 +08:00
|
|
|
logger.error(f"Task {task_id} failed: {e}")
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
async def execute_task_async(self, task_id: str) -> None:
|
|
|
|
|
task = self.tasks.get(task_id)
|
|
|
|
|
if not task:
|
|
|
|
|
raise ValueError(f"Task {task_id} not found")
|
|
|
|
|
|
|
|
|
|
task.status = TaskStatus.RUNNING
|
2026-03-10 18:58:03 +08:00
|
|
|
task.started_at = datetime.now()
|
|
|
|
|
self.save()
|
|
|
|
|
|
2026-03-10 18:27:44 +08:00
|
|
|
session = await opencode_client.create_session(
|
|
|
|
|
title=task.prompt[:50] if task.prompt else None
|
|
|
|
|
)
|
|
|
|
|
session_id = session["id"]
|
|
|
|
|
task.session_id = session_id
|
2026-03-10 18:58:03 +08:00
|
|
|
self.save()
|
2026-03-10 18:27:44 +08:00
|
|
|
|
|
|
|
|
logger.info(f"Executing async task {task_id} with session {session_id}")
|
|
|
|
|
|
|
|
|
|
await opencode_client.send_message_async(session_id, task.prompt)
|
|
|
|
|
|
2026-03-10 18:58:03 +08:00
|
|
|
from app.services.websocket_manager import ws_manager
|
|
|
|
|
|
|
|
|
|
await ws_manager.start_poll_task(task_id, session_id)
|
|
|
|
|
|
|
|
|
|
async def update_task_status(
|
|
|
|
|
self, task_id: str, status: TaskStatus, error: str | None = None
|
|
|
|
|
):
|
|
|
|
|
task = self.tasks.get(task_id)
|
|
|
|
|
if task:
|
|
|
|
|
task.status = status
|
|
|
|
|
if status in (TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.ABORTED):
|
|
|
|
|
task.finished_at = datetime.now()
|
|
|
|
|
if error:
|
|
|
|
|
task.error = error
|
|
|
|
|
self.save()
|
|
|
|
|
history_service.add_task_history(task)
|
|
|
|
|
|
2026-03-10 18:27:44 +08:00
|
|
|
async def abort_task(self, task_id: str) -> bool:
|
|
|
|
|
task = self.tasks.get(task_id)
|
|
|
|
|
if not task or not task.session_id:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
result = await opencode_client.abort_session(task.session_id)
|
2026-03-10 18:58:03 +08:00
|
|
|
task.status = TaskStatus.ABORTED
|
|
|
|
|
task.finished_at = datetime.now()
|
|
|
|
|
self.save()
|
|
|
|
|
history_service.add_task_history(task)
|
2026-03-10 18:27:44 +08:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
async def get_task(self, task_id: str) -> Task | None:
|
|
|
|
|
return self.tasks.get(task_id)
|
|
|
|
|
|
|
|
|
|
async def list_tasks(self) -> list[Task]:
|
|
|
|
|
return list(self.tasks.values())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
session_manager = SessionManager()
|