mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-07 02:07:45 +02:00
[eric] clean up remaining non-opaque references
This commit is contained in:
@@ -761,6 +761,23 @@ async def execute_browser_tool(
|
||||
return result
|
||||
|
||||
|
||||
def _extract_domain(url: str) -> str | None:
|
||||
"""Extract the apex domain from a URL (acme-corp.notion.so → notion.so).
|
||||
Returns None for non-http URLs."""
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
parsed = urlparse(url)
|
||||
host = parsed.hostname or ""
|
||||
if not host or host in ("localhost", "127.0.0.1", ""):
|
||||
return None
|
||||
parts = host.split(".")
|
||||
if len(parts) >= 2:
|
||||
return ".".join(parts[-2:])
|
||||
return host
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _format_tool_result(result: dict, tool_name: str) -> list[dict]:
|
||||
"""Convert a browser command result dict into Anthropic API content blocks."""
|
||||
if "error" in result:
|
||||
@@ -1238,6 +1255,14 @@ async def run_browser_agent(
|
||||
recent_tool_calls = recent_tool_calls[-_LOOP_WINDOW_SIZE * 2:]
|
||||
|
||||
content_blocks = _format_tool_result(result, tu.name)
|
||||
try:
|
||||
url = result.get("url") or (tu.input or {}).get("url")
|
||||
if url:
|
||||
domain = _extract_domain(str(url))
|
||||
if domain and domain not in session.browser_domains:
|
||||
session.browser_domains.append(domain)
|
||||
except Exception:
|
||||
pass
|
||||
if is_loop:
|
||||
loop_trigger_count += 1
|
||||
repeat_count = sum(1 for c in recent_tool_calls if c == call_key)
|
||||
|
||||
@@ -98,6 +98,7 @@ class AgentSession(BaseModel):
|
||||
# active model changes (model switch) or on close. Lets dashboards
|
||||
# answer "how long did each model run?" without inferring from turns.
|
||||
time_per_model: dict[str, int] = Field(default_factory=dict)
|
||||
browser_domains: list[str] = Field(default_factory=list)
|
||||
messages: list[Message] = Field(default_factory=list)
|
||||
pending_approvals: list[ApprovalRequest] = Field(default_factory=list)
|
||||
branches: dict[str, "MessageBranch"] = Field(default_factory=lambda: {"main": MessageBranch(id="main")})
|
||||
|
||||
@@ -214,6 +214,15 @@ async def drain_spool(batch_size: int = 50) -> int:
|
||||
# Public API
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def _log(kind: str, payload: dict) -> None:
|
||||
"""Append to the rolling operational log for diagnostics."""
|
||||
try:
|
||||
from backend.apps.service.ring_buffer import record
|
||||
record(kind)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def submit(kind: str, payload: dict) -> None:
|
||||
"""Hand off an opaque payload to the cloud.
|
||||
|
||||
@@ -237,6 +246,7 @@ def submit(kind: str, payload: dict) -> None:
|
||||
"kind": kind,
|
||||
"ts": time.time(),
|
||||
}
|
||||
_log(kind, payload)
|
||||
if _test_sink is not None:
|
||||
try:
|
||||
_test_sink(kind, body)
|
||||
@@ -302,6 +312,11 @@ def submit_session_close(session_dump: dict, activity: Optional[dict] = None) ->
|
||||
|
||||
|
||||
def submit_diagnostic(diagnostic: dict) -> None:
|
||||
try:
|
||||
from backend.apps.service.ring_buffer import snapshot
|
||||
diagnostic["recent_log"] = snapshot()
|
||||
except Exception:
|
||||
pass
|
||||
submit("diagnostic", {"diagnostic": diagnostic})
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Fixed-size event log for operational diagnostics.
|
||||
|
||||
Maintains a rolling window of the last N app events so support
|
||||
diagnostics can include context about recent activity. Used by
|
||||
the error report builder to attach "what just happened" when
|
||||
something goes wrong.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
import time
|
||||
from collections import deque
|
||||
|
||||
_MAX_SIZE = 50
|
||||
_lock = threading.Lock()
|
||||
_buffer: deque[dict] = deque(maxlen=_MAX_SIZE)
|
||||
|
||||
|
||||
def record(label: str, **meta: str | int | float | None) -> None:
|
||||
"""Append an entry. Oldest drops when full."""
|
||||
with _lock:
|
||||
_buffer.append({
|
||||
"l": label,
|
||||
"t": time.time(),
|
||||
**{k: v for k, v in meta.items() if v is not None},
|
||||
})
|
||||
|
||||
|
||||
def snapshot() -> list[dict]:
|
||||
"""Return a copy of the current buffer, oldest first."""
|
||||
with _lock:
|
||||
return list(_buffer)
|
||||
|
||||
|
||||
def clear() -> None:
|
||||
with _lock:
|
||||
_buffer.clear()
|
||||
@@ -92,13 +92,13 @@ async def _heartbeat_loop():
|
||||
props[f"cost_model_{safe_name}"] = model_data.get("cost", 0)
|
||||
except Exception:
|
||||
pass
|
||||
svc.record("app.heartbeat", props)
|
||||
svc.submit("state", props)
|
||||
if "nine_router_total_cost" in props:
|
||||
svc.record("cost.delta", {
|
||||
"cost_delta_usd": cost_delta,
|
||||
"prompt_tokens_delta": int(prompt_delta),
|
||||
"completion_tokens_delta": int(completion_delta),
|
||||
"requests_delta": int(requests_delta),
|
||||
svc.submit("state", {
|
||||
"d_cost": cost_delta,
|
||||
"d_prompt": int(prompt_delta),
|
||||
"d_completion": int(completion_delta),
|
||||
"d_requests": int(requests_delta),
|
||||
})
|
||||
except Exception:
|
||||
pass
|
||||
@@ -146,7 +146,7 @@ async def service_lifespan():
|
||||
for cp in getattr(settings, "custom_providers", []):
|
||||
providers.append(cp.name)
|
||||
|
||||
svc.record("app.opened", {
|
||||
svc.submit("state", {
|
||||
"os": platform.system(),
|
||||
"platform": platform.platform(),
|
||||
"provider_count": len(providers),
|
||||
@@ -181,7 +181,7 @@ async def service_lifespan():
|
||||
if is_paying and getattr(settings, "openswarm_subscription_expires", None):
|
||||
id_props["subscription_expires"] = settings.openswarm_subscription_expires
|
||||
|
||||
svc.identify(id_props)
|
||||
svc.submit("state", {"identity": id_props})
|
||||
except Exception as e:
|
||||
logger.debug(f"Service startup event failed (non-critical): {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user