[loop-cycle-2386] perf: optimize sovereignty loop performance (#1431) (#1467)
Some checks failed
Tests / lint (push) Successful in 30s
Tests / test (push) Has been cancelled

This commit was merged in pull request #1467.
This commit is contained in:
2026-03-24 20:53:39 +00:00
parent 7c5975f161
commit e5373119cc
2 changed files with 60 additions and 32 deletions

3
data/narration.json Normal file
View File

@@ -0,0 +1,3 @@
{
"discovery": "You discovered a hidden cave in the {location}."
}

View File

@@ -22,16 +22,59 @@ Refs: #953 (The Sovereignty Loop), #955, #956, #961
from __future__ import annotations from __future__ import annotations
import functools import functools
import json
import logging import logging
from collections.abc import Callable from collections.abc import Callable
from pathlib import Path
from typing import Any, TypeVar from typing import Any, TypeVar
from timmy.sovereignty.auto_crystallizer import (
crystallize_reasoning,
get_rule_store,
)
from timmy.sovereignty.metrics import emit_sovereignty_event, get_metrics_store from timmy.sovereignty.metrics import emit_sovereignty_event, get_metrics_store
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
T = TypeVar("T") T = TypeVar("T")
# ── Module-level narration cache ─────────────────────────────────────────────
_narration_cache: dict[str, str] | None = None
_narration_cache_mtime: float = 0.0
def _load_narration_store() -> dict[str, str]:
"""Load narration templates from disk, with mtime-based caching."""
global _narration_cache, _narration_cache_mtime
from config import settings
narration_path = Path(settings.repo_root) / "data" / "narration.json"
if not narration_path.exists():
_narration_cache = {}
return _narration_cache
try:
mtime = narration_path.stat().st_mtime
except OSError:
if _narration_cache is not None:
return _narration_cache
return {}
if _narration_cache is not None and mtime == _narration_cache_mtime:
return _narration_cache
try:
with narration_path.open() as f:
_narration_cache = json.load(f)
_narration_cache_mtime = mtime
except Exception:
if _narration_cache is None:
_narration_cache = {}
return _narration_cache
# ── Perception Layer ────────────────────────────────────────────────────────── # ── Perception Layer ──────────────────────────────────────────────────────────
@@ -81,10 +124,7 @@ async def sovereign_perceive(
raw = await vlm.analyze(screenshot) raw = await vlm.analyze(screenshot)
# Step 3: parse # Step 3: parse
if parse_fn is not None: state = parse_fn(raw) if parse_fn is not None else raw
state = parse_fn(raw)
else:
state = raw
# Step 4: crystallize # Step 4: crystallize
if crystallize_fn is not None: if crystallize_fn is not None:
@@ -140,11 +180,6 @@ async def sovereign_decide(
dict[str, Any] dict[str, Any]
The decision result, with at least an ``"action"`` key. The decision result, with at least an ``"action"`` key.
""" """
from timmy.sovereignty.auto_crystallizer import (
crystallize_reasoning,
get_rule_store,
)
store = rule_store if rule_store is not None else get_rule_store() store = rule_store if rule_store is not None else get_rule_store()
# Step 1: check rules # Step 1: check rules
@@ -207,29 +242,16 @@ async def sovereign_narrate(
template_store: template_store:
Optional narration template store (dict-like mapping event types Optional narration template store (dict-like mapping event types
to template strings with ``{variable}`` slots). If ``None``, to template strings with ``{variable}`` slots). If ``None``,
tries to load from ``data/narration.json``. uses mtime-cached templates from ``data/narration.json``.
Returns Returns
------- -------
str str
The narration text. The narration text.
""" """
import json # Load templates from cache instead of disk every time
from pathlib import Path
from config import settings
# Load template store
if template_store is None: if template_store is None:
narration_path = Path(settings.repo_root) / "data" / "narration.json" template_store = _load_narration_store()
if narration_path.exists():
try:
with narration_path.open() as f:
template_store = json.load(f)
except Exception:
template_store = {}
else:
template_store = {}
event_type = event.get("type", "unknown") event_type = event.get("type", "unknown")
@@ -270,8 +292,7 @@ def _crystallize_narration_template(
Replaces concrete values in the narration with format placeholders Replaces concrete values in the narration with format placeholders
based on event keys, then saves to ``data/narration.json``. based on event keys, then saves to ``data/narration.json``.
""" """
import json global _narration_cache, _narration_cache_mtime
from pathlib import Path
from config import settings from config import settings
@@ -289,6 +310,9 @@ def _crystallize_narration_template(
narration_path.parent.mkdir(parents=True, exist_ok=True) narration_path.parent.mkdir(parents=True, exist_ok=True)
with narration_path.open("w") as f: with narration_path.open("w") as f:
json.dump(template_store, f, indent=2) json.dump(template_store, f, indent=2)
# Update cache so next read skips disk
_narration_cache = template_store
_narration_cache_mtime = narration_path.stat().st_mtime
logger.info("Crystallized narration template for event type '%s'", event_type) logger.info("Crystallized narration template for event type '%s'", event_type)
except Exception as exc: except Exception as exc:
logger.warning("Failed to persist narration template: %s", exc) logger.warning("Failed to persist narration template: %s", exc)
@@ -347,17 +371,18 @@ def sovereignty_enforced(
def decorator(fn: Callable) -> Callable: def decorator(fn: Callable) -> Callable:
@functools.wraps(fn) @functools.wraps(fn)
async def wrapper(*args: Any, **kwargs: Any) -> Any: async def wrapper(*args: Any, **kwargs: Any) -> Any:
session_id = kwargs.get("session_id", "")
store = get_metrics_store()
# Check cache # Check cache
if cache_check is not None: if cache_check is not None:
cached = cache_check(args, kwargs) cached = cache_check(args, kwargs)
if cached is not None: if cached is not None:
store = get_metrics_store() store.record(sovereign_event, session_id=session_id)
store.record(sovereign_event, session_id=kwargs.get("session_id", ""))
return cached return cached
# Cache miss — run the model # Cache miss — run the model
store = get_metrics_store() store.record(miss_event, session_id=session_id)
store.record(miss_event, session_id=kwargs.get("session_id", ""))
result = await fn(*args, **kwargs) result = await fn(*args, **kwargs)
# Crystallize # Crystallize
@@ -367,7 +392,7 @@ def sovereignty_enforced(
store.record( store.record(
"skill_crystallized", "skill_crystallized",
metadata={"layer": layer}, metadata={"layer": layer},
session_id=kwargs.get("session_id", ""), session_id=session_id,
) )
except Exception as exc: except Exception as exc:
logger.warning("Crystallization failed for %s: %s", layer, exc) logger.warning("Crystallization failed for %s: %s", layer, exc)