import asyncio, logging, time
from collections import defaultdict
from typing import Sequence
log = logging.getLogger("asi_build.self_optimiser")
class AsyncSelfOptimiser:
"""
Routes ImprovementAction items to the appropriate subsystem.
Rate-limits repeated same-kind actions on the same module.
"""
def __init__(
self,
orchestrator=None, # LiveModuleOrchestrator | None
cognitive_cycle=None, # CognitiveCycle | None (duck-typed)
alert_queue: asyncio.Queue | None = None,
config: OptimiserConfig | None = None,
) -> None:
self._orch = orchestrator
self._cycle = cognitive_cycle
self._alerts = alert_queue or asyncio.Queue()
self._cfg = config or OptimiserConfig()
self._last_enacted: dict[tuple[str, str], float] = {} # (module, kind) → monotonic
self._lock = asyncio.Lock()
self._stats: dict[str, int] = defaultdict(int)
# ------------------------------------------------------------------ #
# Public interface #
# ------------------------------------------------------------------ #
async def enact(
self,
actions: Sequence[ImprovementAction],
*,
config: OptimiserConfig | None = None,
) -> list[EnactmentRecord]:
cfg = config or self._cfg
records: list[EnactmentRecord] = []
hot_swap_count = 0
async with self._lock:
for action in actions:
if len(records) >= cfg.max_enactments_per_cycle:
break
# Rate-limit gate
key = (action.module_name, action.action_kind.name)
if cfg.enable_rate_limit:
last = self._last_enacted.get(key, 0.0)
if time.monotonic() - last < cfg.cool_down_s:
records.append(EnactmentRecord(
action=action,
status=EnactmentStatus.RATE_LIMITED,
detail=f"cool-down active ({cfg.cool_down_s}s)",
duration_s=0.0,
attempted_at=time.monotonic(),
))
self._stats["rate_limited"] += 1
continue
# HOT_SWAP cap
if action.action_kind.name == "HOT_SWAP_MODULE":
if hot_swap_count >= cfg.max_hot_swap_per_cycle:
records.append(EnactmentRecord(
action=action,
status=EnactmentStatus.SKIPPED,
detail="hot-swap cap reached for this cycle",
duration_s=0.0,
attempted_at=time.monotonic(),
))
self._stats["skipped"] += 1
continue
hot_swap_count += 1
record = await self._dispatch(action, cfg)
records.append(record)
if record.status == EnactmentStatus.SUCCESS:
self._last_enacted[key] = time.monotonic()
self._stats["enacted"] += 1
elif record.status == EnactmentStatus.FAILED:
self._stats["failed"] += 1
else:
self._stats["skipped"] += 1
self._stats["enact_calls"] += 1
return records
async def stats(self) -> dict[str, int]:
return dict(self._stats)
async def reset(self) -> None:
async with self._lock:
self._stats.clear()
self._last_enacted.clear()
# ------------------------------------------------------------------ #
# Dispatcher #
# ------------------------------------------------------------------ #
async def _dispatch(
self, action: ImprovementAction, cfg: OptimiserConfig
) -> EnactmentRecord:
t0 = time.monotonic()
params = dict(action.parameters)
if cfg.dry_run:
return self._record(action, EnactmentStatus.SKIPPED,
f"dry-run: would enact {action.action_kind.name}", t0)
match action.action_kind.name:
case "TUNE_THRESHOLD":
return await self._apply_tune(action, params, t0)
case "INCREASE_BUDGET":
return await self._apply_budget(action, params, t0)
case "REDUCE_LOAD":
return await self._apply_load_shed(action, params, t0)
case "HOT_SWAP_MODULE":
return await self._apply_hot_swap(action, params, t0)
case "FLAG_FOR_REVIEW":
await self._alerts.put(action)
return self._record(action, EnactmentStatus.SUCCESS, "queued for review", t0)
case "NO_OP" | _:
return self._record(action, EnactmentStatus.SKIPPED, "no-op", t0)
async def _apply_tune(self, action, params, t0) -> EnactmentRecord:
"""Adjust a WeaknessDetector threshold for the target module."""
try:
delta = float(params.get("suggested_delta", "0.1"))
if self._cycle and hasattr(self._cycle, "_detector_config"):
cfg = self._cycle._detector_config
# Clamp new threshold to reasonable range
new_p95 = max(50.0, cfg.latency_p95_threshold_ms * (1 + delta))
self._cycle._detector_config = DetectorConfig(
latency_p95_threshold_ms=new_p95,
latency_p99_threshold_ms=cfg.latency_p99_threshold_ms,
error_rate_threshold=cfg.error_rate_threshold,
)
return self._record(action, EnactmentStatus.SUCCESS,
f"p95_threshold adjusted to {new_p95:.1f}ms", t0)
return self._record(action, EnactmentStatus.SKIPPED,
"no detector_config available on cycle", t0)
except Exception as exc:
return self._record(action, EnactmentStatus.FAILED, str(exc), t0)
async def _apply_budget(self, action, params, t0) -> EnactmentRecord:
"""Increase the time budget allocated to the target module."""
try:
factor = float(params.get("budget_factor", "1.5"))
if self._cycle and hasattr(self._cycle, "_budget_map"):
old = self._cycle._budget_map.get(action.module_name, 1.0)
self._cycle._budget_map[action.module_name] = old * factor
return self._record(action, EnactmentStatus.SUCCESS,
f"budget {old:.2f}→{old*factor:.2f}", t0)
return self._record(action, EnactmentStatus.SKIPPED, "no budget_map on cycle", t0)
except Exception as exc:
return self._record(action, EnactmentStatus.FAILED, str(exc), t0)
async def _apply_load_shed(self, action, params, t0) -> EnactmentRecord:
"""Signal the load-shedder to reduce pressure on the target module."""
try:
if self._cycle and hasattr(self._cycle, "_load_shedder"):
await self._cycle._load_shedder.shed(action.module_name)
return self._record(action, EnactmentStatus.SUCCESS,
"load-shed signal sent", t0)
return self._record(action, EnactmentStatus.SKIPPED, "no load_shedder on cycle", t0)
except Exception as exc:
return self._record(action, EnactmentStatus.FAILED, str(exc), t0)
async def _apply_hot_swap(self, action, params, t0) -> EnactmentRecord:
"""Invoke Phase 15 LiveModuleOrchestrator to swap the module."""
if self._orch is None:
return self._record(action, EnactmentStatus.SKIPPED, "no orchestrator injected", t0)
try:
import hashlib, os
req = OrchestratorRequest(
module_id = action.module_name,
new_code = b"", # placeholder; real impl fetches from CodeSynthesiser
new_version = params.get("target_version", "auto"),
requester = "self_optimiser",
)
result = await self._orch.orchestrate_swap(req)
status = (EnactmentStatus.SUCCESS if result.outcome.name == "SUCCESS"
else EnactmentStatus.FAILED)
return self._record(action, status,
f"orchestrator: {result.outcome.name} — {result.detail}", t0)
except Exception as exc:
return self._record(action, EnactmentStatus.FAILED, str(exc), t0)
@staticmethod
def _record(action, status, detail, t0) -> EnactmentRecord:
return EnactmentRecord(
action=action, status=status, detail=detail,
duration_s=time.monotonic() - t0,
attempted_at=time.monotonic(),
)
class NullOptimiser:
"""No-op optimiser — for testing or disabled-enactment mode."""
async def enact(self, actions, *, config=None) -> list[EnactmentRecord]:
return []
async def stats(self) -> dict[str, int]:
return {}
async def reset(self) -> None:
pass
# Enactment failure rate
rate(asi_optimiser_actions_enacted_total{status="FAILED"}[5m])
/ rate(asi_optimiser_actions_enacted_total[5m]) > 0.3
# Alert: hot-swap success rate below 80%
rate(asi_optimiser_hot_swaps_total{outcome="SUCCESS"}[10m])
/ rate(asi_optimiser_hot_swaps_total[10m]) < 0.8
Phase 16.4 —
SelfOptimiser: action-enacting layer for the self-improvement pipelinePhase: 16 — Cognitive Reflection & Self-Improvement
Component:
SelfOptimiserDepends on: Phase 16.3
ImprovementPlanner(#423), Phase 15.5LiveModuleOrchestrator(#413), Phase 10.5ReplanningEngine(#333)Feeds into: Phase 16.5
ReflectionCycleMotivation
ImprovementPlannerproduces a priority-ranked list ofImprovementActionitems.SelfOptimiseris the executor layer — it inspects each action, routes it to the correct subsystem (orchestrator, replanner, budget manager, or safety escalation), tracks outcomes, and surfaces enactment telemetry toReflectionCyclefor retrospective analysis.Without
SelfOptimiser, the planning pipeline is read-only: it diagnoses and proposes but never acts.SelfOptimisercloses that loop, making the system genuinely self-improving.Data Model
Protocol
Dispatcher: ActionKind → Subsystem
ActionKindTUNE_THRESHOLDDetectorConfighot-update via_apply_tuneINCREASE_BUDGETCognitiveCycle._budget_mapREDUCE_LOADCognitiveCycle._load_shedderHOT_SWAP_MODULELiveModuleOrchestrator.orchestrate_swap()FLAG_FOR_REVIEWasyncio.Queue→ operator alert channelNO_OPAsyncSelfOptimiser— Reference ImplementationCognitiveCycle Integration
Prometheus Metrics
asi_optimiser_enact_calls_totalasi_optimiser_actions_enacted_totalaction_kind,statusasi_optimiser_actions_rate_limited_totalmodule,action_kindasi_optimiser_hot_swaps_totalmodule,outcomeasi_optimiser_enact_duration_secondsaction_kindTest Targets (12)
test_flag_for_review_always_success— FLAG_FOR_REVIEW always enqueued; status=SUCCESStest_no_op_returns_skipped— NO_OP action → SKIPPED recordtest_dry_run_skips_all— dry_run=True → all records SKIPPED, no subsystem callstest_rate_limit_gate— same module+kind twice within cool_down_s → second RATE_LIMITEDtest_rate_limit_expires— after cool_down_s elapses, second enactment proceedstest_hot_swap_cap— max_hot_swap_per_cycle=1, two HOT_SWAP actions → second SKIPPEDtest_max_enactments_cap— max_enactments_per_cycle=2, 5 actions → 2 records returnedtest_orchestrator_success— mock orchestrator → SUCCESS EnactmentRecordtest_orchestrator_failure— mock orchestrator raises → FAILED record, no exception propagationtest_tune_threshold_adjusts_config— CognitiveCycle mock with_detector_configattributetest_concurrent_enact_calls— asyncio.gather 3× enact → Lock prevents racetest_reset_clears_state— reset() → stats() empty, last_enacted clearedImplementation Order (14 steps)
EnactmentStatusenumEnactmentRecordfrozen dataclassOptimiserConfigfrozen dataclassSelfOptimiserProtocol (@runtime_checkable)NullOptimiserno-opAsyncSelfOptimiser.__init__(inject orchestrator + cycle + alert_queue)_apply_tune()dispatcher method_apply_budget()dispatcher method_apply_load_shed()dispatcher method_apply_hot_swap()dispatcher method (delegates to LiveModuleOrchestrator)_dispatch()match statement + FLAG_FOR_REVIEW + NO_OP branchesenact()— rate-limit gate + hot-swap cap + loop + statsenact()with histogram + counters)--strictcleanShow & Tell: #426 | Q&A: #427 | Wiki: Phase 16 — SelfOptimiser
Phase 16 Sub-Phase Tracker
PerformanceProfilerWeaknessDetectorImprovementPlannerSelfOptimiserReflectionCycle