Files
wehub-resource-sync 0ef5fcb1c5
Security / Dependency audit (pip-audit) (push) Has been cancelled
Security / CodeQL (javascript-typescript) (push) Has been cancelled
Security / CodeQL (python) (push) Has been cancelled
Security / Secret scan (gitleaks) (push) Has been cancelled
rust / test (ubuntu) (push) Has been cancelled
rust / simulator e2e (macos-latest) (push) Has been cancelled
rust / simulator e2e (ubuntu-latest) (push) Has been cancelled
rust / simulator e2e (windows-latest) (push) Has been cancelled
rust / wheels (aarch64-apple-darwin) (push) Has been cancelled
rust / wheels (x86_64-unknown-linux-gnu) (push) Has been cancelled
rust / wheels (x86_64-apple-darwin) (push) Has been cancelled
rust / audit (push) Has been cancelled
rust / parity (nightly, allowed to fail during Phase 0) (push) Has been cancelled
CI / commitlint (push) Has been skipped
Dev Containers / validate (.devcontainer/devcontainer.json, default) (push) Failing after 0s
Dev Containers / validate (.devcontainer/memory-stack/devcontainer.json, memory-stack) (push) Failing after 0s
Dev Containers / validate-worktree (push) Failing after 0s
CI / changes (push) Failing after 4s
Deploy Documentation / validate (push) Has been skipped
Deploy Documentation / deploy (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, claude) (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, codex) (push) Failing after 1s
Install Native E2E / install-native (ubuntu-latest) (push) Failing after 1s
OpenCode Plugin / typecheck + build + test (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, copilot) (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
Wrap E2E / docker-wrap-e2e (push) Failing after 1s
Wrap Native E2E / wrap-native (ubuntu-latest) (push) Failing after 1s
Init E2E / docker-init-e2e (push) Failing after 4s
Merge Conflicts / merge-conflicts (push) Failing after 4s
CI / lint (push) Has been cancelled
CI / build-wheel (push) Has been cancelled
CI / build-wheel-windows (push) Has been cancelled
CI / prefetch-model (push) Has been cancelled
CI / test-dashboard-ui (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test-extras (push) Has been cancelled
CI / test-agno (push) Has been cancelled
CI / build (push) Has been cancelled
CI / workflow-validation (push) Has been cancelled
CI / docker-native-e2e (push) Has been cancelled
CI / windows-native-wrapper (push) Has been cancelled
CI / macos-native-wrapper (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / promote-latest (push) Has been cancelled
Init Native E2E / init-native (macos-latest, claude) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, codex) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, copilot) (push) Has been cancelled
Install Native E2E / install-native (macos-latest) (push) Has been cancelled
Wrap Native E2E / wrap-native (macos-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:03:20 +08:00

109 lines
4.1 KiB
Python

"""AIMD controller for live verbosity adjustment.
The offline ``learn --verbosity`` pass sets the *starting* level. This controller
tracks drift during a session and nudges the level from runtime signals, using
the congestion-control intuition:
- **Additive increase** toward terser output: only after *sustained* "the user
isn't reading this" pressure (a streak of TOO_MUCH signals) do we step the
level up by one. Probing up is cheap to get wrong only slowly.
- **Multiplicative-style decrease** on a TOO_LITTLE signal (the user asked for
more): back off immediately by a level and enter a cooldown that suppresses
re-escalation. Annoying the user is the expensive event — like congestion —
so we react fast and then hold off.
The controller is a pure state machine over an abstract signal. Detecting the
signals at the proxy (fast-skip from reply timing, interrupt from a cancelled
stream) is the caller's job; keeping detection out of here makes the control
logic deterministic and testable, and lets the live path enable only the
signals it can measure reliably.
"""
from __future__ import annotations
import json
from dataclasses import asdict, dataclass
from enum import Enum
from pathlib import Path
class Signal(Enum):
"""An abstract feedback signal about the last response's verbosity."""
TOO_MUCH = "too_much" # interrupted / fast-skipped → output went unread
TOO_LITTLE = "too_little" # user asked to explain / expand
NEUTRAL = "neutral" # engaged normally
@dataclass
class ControllerState:
"""Per-conversation (or per-project) controller state."""
level: int
up_streak: int = 0
cooldown: int = 0
def to_dict(self) -> dict[str, int]:
return asdict(self)
@classmethod
def from_dict(cls, d: dict[str, int]) -> ControllerState:
return cls(
level=int(d.get("level", 2)),
up_streak=int(d.get("up_streak", 0)),
cooldown=int(d.get("cooldown", 0)),
)
@dataclass(frozen=True)
class VerbosityController:
"""Pure AIMD controller. ``observe`` maps (state, signal) → new state."""
floor: int = 1
ceil: int = 4
probe_threshold: int = 3 # consecutive TOO_MUCH before stepping up
cooldown_turns: int = 5 # turns after a back-off during which we don't re-probe
def observe(self, state: ControllerState, signal: Signal) -> ControllerState:
level = state.level
up_streak = state.up_streak
cooldown = max(0, state.cooldown - 1) # one turn elapses per observation
if signal is Signal.TOO_LITTLE:
# Fast back-off: drop a level immediately and suppress re-escalation.
return ControllerState(
level=max(self.floor, level - 1),
up_streak=0,
cooldown=self.cooldown_turns,
)
if signal is Signal.TOO_MUCH:
if cooldown > 0:
# Recently backed off — don't re-terse yet; keep cooling down.
return ControllerState(level=level, up_streak=0, cooldown=cooldown)
up_streak += 1
if up_streak >= self.probe_threshold and level < self.ceil:
return ControllerState(level=level + 1, up_streak=0, cooldown=0)
return ControllerState(level=level, up_streak=up_streak, cooldown=cooldown)
# NEUTRAL: engagement resets the upward streak (we require *consecutive*
# pressure) and lets any cooldown tick down.
return ControllerState(level=level, up_streak=0, cooldown=cooldown)
def load_state(path: Path, default_level: int, floor: int, ceil: int) -> ControllerState:
"""Load controller state, clamped to [floor, ceil]; seed from default."""
try:
d = json.loads(Path(path).read_text())
state = ControllerState.from_dict(d)
except (OSError, json.JSONDecodeError, ValueError):
state = ControllerState(level=default_level)
state.level = max(floor, min(ceil, state.level))
return state
def save_state(path: Path, state: ControllerState) -> None:
p = Path(path)
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(json.dumps(state.to_dict(), separators=(",", ":")))