- harness/es.py: append-only event store + проекции (образец — кооп-портал) - harness/levels.py: уровни 1с/1м/1ч/1д, контекст ∝ масштабу - harness/scheduler.py: уровневые потоки, идемпотентные тики, absorbed-стабы - harness/isolated_runner.py + _llm_worker.py: LLM-тик в сабпроцессе (глобальный asyncio-рантайм Strands не переживает 2+ create_harness) - cli.py: init/task/tasks/status/events + requeue (написан самим минутным агентом) - 9router: отдельный ключ sk-simbiot, модель auto (glm-5.3 первым)
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
"""Process-lifetime event loop for detached background work."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import inspect
|
|
import threading
|
|
from collections.abc import Awaitable, Callable
|
|
from typing import TypeVar, cast
|
|
|
|
_T = TypeVar("_T")
|
|
|
|
|
|
class _BackgroundTaskRuntime:
|
|
"""Own a daemon event loop shared by in-process task managers."""
|
|
|
|
def __init__(self) -> None:
|
|
self._loop: asyncio.AbstractEventLoop | None = None
|
|
self._thread: threading.Thread | None = None
|
|
self._ready = threading.Event()
|
|
self._start_lock = threading.Lock()
|
|
|
|
async def run(self, operation: Callable[[], _T | Awaitable[_T]]) -> _T:
|
|
"""Run an operation on the persistent loop."""
|
|
loop = self._ensure_started()
|
|
if threading.current_thread() is self._thread:
|
|
return await self._invoke(operation)
|
|
future = asyncio.run_coroutine_threadsafe(self._invoke(operation), loop)
|
|
return await asyncio.wrap_future(future)
|
|
|
|
def _ensure_started(self) -> asyncio.AbstractEventLoop:
|
|
existing_loop = self._loop
|
|
if existing_loop is not None:
|
|
return existing_loop
|
|
with self._start_lock:
|
|
if self._loop is None:
|
|
self._thread = threading.Thread(
|
|
target=self._run_loop,
|
|
name="strands-background-tasks",
|
|
daemon=True,
|
|
)
|
|
self._thread.start()
|
|
self._ready.wait()
|
|
loop = self._loop
|
|
if loop is None:
|
|
raise RuntimeError("Background task runtime failed to start")
|
|
return loop
|
|
|
|
def _run_loop(self) -> None:
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
self._loop = loop
|
|
self._ready.set()
|
|
loop.run_forever()
|
|
|
|
async def _invoke(self, operation: Callable[[], _T | Awaitable[_T]]) -> _T:
|
|
result = operation()
|
|
if inspect.isawaitable(result):
|
|
return await cast(Awaitable[_T], result)
|
|
return result
|
|
|
|
|
|
_RUNTIME = _BackgroundTaskRuntime()
|
|
|
|
|
|
def get_background_task_runtime() -> _BackgroundTaskRuntime:
|
|
"""Return the shared process-lifetime runtime."""
|
|
return _RUNTIME
|