mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] backend: final leading-_ cleanup, file-local shorts + move session cancel-event to manager-side table (off the pydantic model, kills the last underscore); naming linter now 0/0
This commit is contained in:
@@ -82,6 +82,10 @@ class AgentManager(SessionLifecycleMixin, SessionPersistenceMixin, MessagingMixi
|
||||
# stop can persist the partial reply instantly instead of waiting out the
|
||||
# multi-second SDK teardown the cancel handler sits behind.
|
||||
self.live_partial: Dict[str, LivePartial] = {}
|
||||
# Per-session cancel signal: the loop stashes its asyncio.Event here so a
|
||||
# stop/close can set it. Lives on the manager, not the AgentSession model,
|
||||
# so it stays out of serialization (an Event can't be model_dump'd).
|
||||
self.cancel_events: Dict[str, asyncio.Event] = {}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ async def run_browser_agent(
|
||||
system_prompt=SYSTEM_PROMPT,
|
||||
parent_session_id=parent_session_id,
|
||||
)
|
||||
session._cancel_event = cancel_event
|
||||
agent_manager.cancel_events[session_id] = cancel_event
|
||||
agent_manager.sessions[session_id] = session
|
||||
|
||||
# If parent was already stopped before we registered, bail immediately
|
||||
|
||||
@@ -31,8 +31,9 @@ class SessionControlMixin:
|
||||
if session:
|
||||
# Set cancel event BEFORE cancelling the task so in-flight
|
||||
# browser agent loops see it immediately
|
||||
if hasattr(session, '_cancel_event'):
|
||||
session._cancel_event.set()
|
||||
ev = self.cancel_events.get(session_id)
|
||||
if ev:
|
||||
ev.set()
|
||||
|
||||
for req in list(session.pending_approvals):
|
||||
ws_manager.resolve_approval(req.id, {"behavior": "deny", "message": "Agent stopped"})
|
||||
|
||||
@@ -69,8 +69,9 @@ class SessionLifecycleMixin:
|
||||
ws_manager.resolve_approval(req.id, {"behavior": "deny", "message": "Session closed"})
|
||||
session.pending_approvals = []
|
||||
|
||||
if hasattr(session, '_cancel_event'):
|
||||
session._cancel_event.set()
|
||||
ev = self.cancel_events.get(session_id)
|
||||
if ev:
|
||||
ev.set()
|
||||
|
||||
self.sync_session_close(session)
|
||||
|
||||
@@ -103,6 +104,7 @@ class SessionLifecycleMixin:
|
||||
self.sessions.pop(session_id, None)
|
||||
self.tasks.pop(session_id, None)
|
||||
self.live_partial.pop(session_id, None)
|
||||
self.cancel_events.pop(session_id, None)
|
||||
view_builder_render_retry_counts.pop(session_id, None)
|
||||
view_builder_dirty_sessions.discard(session_id)
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ WEBAPP_TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "webapp_template")
|
||||
# Bundled default; used as the read-once fallback if the user-editable
|
||||
# copy at ~/.claude/skills/app_builder_skill.md has been removed despite
|
||||
# the built-in flag (defensive; shouldn't happen in normal use).
|
||||
with open(APP_BUILDER_SKILL_SOURCE_PATH, encoding="utf-8") as _f:
|
||||
APP_BUILDER_SKILL_DEFAULT = _f.read()
|
||||
with open(APP_BUILDER_SKILL_SOURCE_PATH, encoding="utf-8") as p_f:
|
||||
APP_BUILDER_SKILL_DEFAULT = p_f.read()
|
||||
|
||||
|
||||
def load_app_builder_skill() -> str:
|
||||
|
||||
@@ -38,14 +38,14 @@ class MainApp:
|
||||
p_boot_t0 = time.perf_counter()
|
||||
for sub_app in sub_apps:
|
||||
debug(sub_app.name)
|
||||
_t0 = time.perf_counter()
|
||||
p_t0 = time.perf_counter()
|
||||
await stack.enter_async_context(sub_app.lifespan())
|
||||
_dt = (time.perf_counter() - _t0) * 1000
|
||||
if _dt > 50: # only flag a slow lifespan; keeps boot logs quiet
|
||||
print(f"[perf] lifespan {sub_app.name} t={_dt:.0f}ms", flush=True)
|
||||
p_dt = (time.perf_counter() - p_t0) * 1000
|
||||
if p_dt > 50: # only flag a slow lifespan; keeps boot logs quiet
|
||||
print(f"[perf] lifespan {sub_app.name} t={p_dt:.0f}ms", flush=True)
|
||||
print(f"[perf] lifespans-total t={(time.perf_counter() - p_boot_t0) * 1000:.0f}ms", flush=True)
|
||||
_port = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
print(f"\nCheck out the API docs at: http://127.0.0.1:{_port}/docs\n")
|
||||
p_port = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
print(f"\nCheck out the API docs at: http://127.0.0.1:{p_port}/docs\n")
|
||||
yield
|
||||
|
||||
self.app = FastAPI(lifespan=lifespan)
|
||||
|
||||
@@ -8,21 +8,21 @@ import uuid
|
||||
from backend.config.paths import DATA_ROOT
|
||||
|
||||
P_INSTALL_ID_FILE = os.path.join(DATA_ROOT, "install_id")
|
||||
_cached: str | None = None
|
||||
p_cached: str | None = None
|
||||
|
||||
|
||||
def get_install_id() -> str:
|
||||
"""Return the persistent install_id, generating and persisting on first call."""
|
||||
global _cached
|
||||
if _cached:
|
||||
return _cached
|
||||
global p_cached
|
||||
if p_cached:
|
||||
return p_cached
|
||||
|
||||
try:
|
||||
with open(P_INSTALL_ID_FILE, "r", encoding="utf-8") as f:
|
||||
existing = f.read().strip()
|
||||
if p_looks_like_uuid(existing):
|
||||
_cached = existing
|
||||
return _cached
|
||||
p_cached = existing
|
||||
return p_cached
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception:
|
||||
@@ -35,8 +35,8 @@ def get_install_id() -> str:
|
||||
os.write(fd, fresh.encode("utf-8"))
|
||||
finally:
|
||||
os.close(fd)
|
||||
_cached = fresh
|
||||
return _cached
|
||||
p_cached = fresh
|
||||
return p_cached
|
||||
|
||||
|
||||
def p_looks_like_uuid(s: str) -> bool:
|
||||
|
||||
+3
-3
@@ -10,9 +10,9 @@ from uuid import uuid4
|
||||
# handlers; uvicorn's own access logs are untouched.
|
||||
p_backend_logger = logging.getLogger("backend")
|
||||
if not p_backend_logger.handlers:
|
||||
_h = logging.StreamHandler()
|
||||
_h.setFormatter(logging.Formatter("%(asctime)s %(levelname).1s %(name)s: %(message)s", "%H:%M:%S"))
|
||||
p_backend_logger.addHandler(_h)
|
||||
p_h = logging.StreamHandler()
|
||||
p_h.setFormatter(logging.Formatter("%(asctime)s %(levelname).1s %(name)s: %(message)s", "%H:%M:%S"))
|
||||
p_backend_logger.addHandler(p_h)
|
||||
p_backend_logger.setLevel(logging.INFO)
|
||||
p_backend_logger.propagate = False
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ async def test_arm_with_no_sub_is_bounded_and_falls_through_to_arm(monkeypatch):
|
||||
monkeypatch.setattr(ft, "p_has_connected_subscription", never_sub)
|
||||
# Short-circuit before the cloud mint so the test stays offline + deterministic;
|
||||
# reaching this branch proves arm did NOT falsely conclude has_model.
|
||||
monkeypatch.setattr(ft, "p_fingerprint", lambda _s: None)
|
||||
monkeypatch.setattr(ft, "p_fingerprint", lambda p_s: None)
|
||||
|
||||
s = AppSettings()
|
||||
t = time.monotonic()
|
||||
|
||||
Reference in New Issue
Block a user