Integrate per-user browser runtimes into subagent API

This commit is contained in:
andreysk0304 2026-04-27 22:06:57 +03:00
parent 952b2e7d17
commit 280247e1e5
11 changed files with 777 additions and 21 deletions

View file

@ -5,6 +5,7 @@ from typing import Any
from api.clients.browser_rpc_contracts import BrowserRpcError, BrowserRpcRunner
from api.domain.task_status import TaskStatus
from api.repositories.task_store import TaskRecord, TaskStore
from api.services.browser_runtime_manager import cleanup_browser_runtime, ensure_browser_runtime
class TaskService:
@ -108,20 +109,28 @@ class TaskService:
await self._store.publish(task_id, self._event(task_id, "started", {"status": TaskStatus.running.value}))
async with self._semaphore:
runtime: dict[str, str] | None = None
try:
if rec.cancel_requested:
await self._store.set_cancelled(task_id)
await self._store.publish(task_id, self._event(task_id, "cancelled", {"status": TaskStatus.cancelled.value}))
return
runtime = await asyncio.to_thread(
ensure_browser_runtime,
task_id=task_id,
metadata=rec.metadata,
thread_id=rec.thread_id,
)
rpc_timeout = float(rec.timeout)
if self._rpc_timeout_cap is not None:
rpc_timeout = min(rpc_timeout, self._rpc_timeout_cap)
raw = await asyncio.wait_for(
self._rpc_client.run(task=rec.task, timeout_sec=rpc_timeout),
self._rpc_client.run(task=rec.task, timeout_sec=rpc_timeout, rpc_url=runtime.get("rpc_url")),
timeout=float(rec.timeout) + 5,
)
raw = self._with_runtime_metadata(raw, runtime)
success = bool(raw.get("success"))
await self._store.set_done(
task_id=task_id,
@ -188,6 +197,16 @@ class TaskService:
"status": failed.status.value,
"error": failed.error,
}))
finally:
try:
await asyncio.to_thread(
cleanup_browser_runtime,
task_id=task_id,
metadata=rec.metadata,
thread_id=rec.thread_id,
)
except Exception:
pass
async def _publish_history_events(self, rec: TaskRecord) -> None:
for index, item in enumerate(rec.history, start=1):
@ -225,3 +244,17 @@ class TaskService:
normalized.append(event)
return normalized
@staticmethod
def _with_runtime_metadata(raw: dict[str, Any], runtime: dict[str, str] | None) -> dict[str, Any]:
if not isinstance(raw, dict) or not runtime:
return raw
enriched = dict(raw)
browser_view = runtime.get("browser_view")
if browser_view and not enriched.get("browser_view"):
enriched["browser_view"] = browser_view
enriched["isolation_mode"] = runtime.get("isolation_mode", "shared")
owner_hash = runtime.get("owner_hash")
if owner_hash:
enriched["owner_hash"] = owner_hash
return enriched