jarvis / tests /test_runtime_state.py
Jonathan Haas
Extract runtime state and telemetry helpers from main runtime
3f3a0a5
Raw
History Blame Contribute Delete
10.8 kB
from __future__ import annotations
import json
from collections import deque
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock
from jarvis.runtime_constants import (
EPISODIC_TIMELINE_MAXLEN,
RUNTIME_INVARIANT_HISTORY_MAXLEN,
VALID_BACKCHANNEL_STYLES,
VALID_CONTROL_PRESETS,
VALID_PERSONA_STYLES,
VALID_VOICE_PROFILE_CONFIRMATIONS,
VALID_VOICE_PROFILE_PACE,
VALID_VOICE_PROFILE_TONE,
VALID_VOICE_PROFILE_VERBOSITY,
)
from jarvis.runtime_state import (
apply_runtime_profile,
check_runtime_invariants,
load_runtime_state,
runtime_profile_snapshot,
save_runtime_state,
)
from jarvis.voice_attention import (
VALID_TIMEOUT_PROFILES,
VALID_WAKE_MODES,
VoiceAttentionConfig,
VoiceAttentionController,
)
def _parse_control_bool(value: object) -> bool | None:
if isinstance(value, bool):
return value
if isinstance(value, int):
if value in {0, 1}:
return bool(value)
return None
if isinstance(value, str):
lowered = value.strip().lower()
if lowered in {"1", "true", "yes", "on", "enabled"}:
return True
if lowered in {"0", "false", "no", "off", "disabled"}:
return False
return None
def _parse_control_choice(value: object, allowed: set[str]) -> str | None:
choice = str(value or "").strip().lower()
if not choice:
return None
if choice in allowed:
return choice
return None
def _build_runtime(*, state_path: Path | None = None) -> SimpleNamespace:
runtime = SimpleNamespace()
runtime.config = SimpleNamespace(
motion_enabled=True,
home_enabled=True,
safe_mode_enabled=False,
persona_style="composed",
backchannel_style="balanced",
)
runtime.presence = SimpleNamespace(
start=MagicMock(),
stop=MagicMock(),
set_backchannel_style=MagicMock(),
)
runtime._voice_attention = VoiceAttentionController(VoiceAttentionConfig(wake_words=["jarvis"]))
runtime._voice_user_profiles = {}
runtime._active_control_preset = "custom"
runtime._tts_output_enabled = True
runtime._awaiting_confirmation = False
runtime._pending_text = None
runtime._awaiting_repair_confirmation = False
runtime._repair_candidate_text = None
runtime._episode_seq = 0
runtime._episodic_timeline = deque(maxlen=EPISODIC_TIMELINE_MAXLEN)
runtime._runtime_state_path = state_path
runtime._personality_preview_snapshot = None
runtime._runtime_invariant_checked_at = 0.0
runtime._runtime_invariant_checked_monotonic = 0.0
runtime._runtime_invariant_violations_total = 0
runtime._runtime_invariant_auto_heals_total = 0
runtime._runtime_invariant_recent = deque(maxlen=RUNTIME_INVARIANT_HISTORY_MAXLEN)
runtime._observability = None
runtime._publish_voice_status = MagicMock()
runtime._persist_runtime_state_safe = MagicMock()
runtime._parse_control_bool = _parse_control_bool
runtime._parse_control_choice = _parse_control_choice
def _set_persona_style(style: str) -> None:
runtime.config.persona_style = style
runtime._set_persona_style = _set_persona_style
runtime._voice_controller = lambda: runtime._voice_attention
return runtime
def _save(runtime: SimpleNamespace) -> None:
save_runtime_state(
runtime,
episodic_timeline_maxlen=EPISODIC_TIMELINE_MAXLEN,
valid_persona_styles=VALID_PERSONA_STYLES,
valid_backchannel_styles=VALID_BACKCHANNEL_STYLES,
)
def _load(runtime: SimpleNamespace, *, set_safe_mode_fn: MagicMock) -> None:
load_runtime_state(
runtime,
episodic_timeline_maxlen=EPISODIC_TIMELINE_MAXLEN,
valid_persona_styles=VALID_PERSONA_STYLES,
valid_backchannel_styles=VALID_BACKCHANNEL_STYLES,
valid_voice_profile_verbosity=VALID_VOICE_PROFILE_VERBOSITY,
valid_voice_profile_confirmations=VALID_VOICE_PROFILE_CONFIRMATIONS,
valid_voice_profile_pace=VALID_VOICE_PROFILE_PACE,
valid_voice_profile_tone=VALID_VOICE_PROFILE_TONE,
valid_control_presets=VALID_CONTROL_PRESETS,
set_safe_mode_fn=set_safe_mode_fn,
)
def test_runtime_state_round_trip_restore(tmp_path: Path) -> None:
state_path = tmp_path / "runtime-state.json"
source = _build_runtime(state_path=state_path)
source._voice_attention.set_mode("push_to_talk")
source._voice_attention.set_timeout_profile("long")
source._voice_attention.set_push_to_talk_active(True)
source._voice_attention.sleeping = True
source.config.motion_enabled = False
source.config.home_enabled = False
source.config.safe_mode_enabled = True
source.config.persona_style = "friendly"
source.config.backchannel_style = "expressive"
source._tts_output_enabled = False
source._voice_user_profiles = {
"operator": {
"verbosity": "brief",
"confirmations": "minimal",
"pace": "fast",
"tone": "formal",
}
}
source._active_control_preset = "quiet_hours"
source._awaiting_confirmation = True
source._pending_text = "lock doors"
source._awaiting_repair_confirmation = True
source._repair_candidate_text = "turn off office lamp"
source._episode_seq = 7
source._episodic_timeline = deque(
[
{
"episode_id": 7,
"timestamp": 1700000000.0,
"turn_id": 12,
"intent": "action",
"lifecycle": "completed",
"summary": "Lock doors and turn off office lamp",
"tool_count": 2,
"completion_success": True,
"response_success": True,
}
],
maxlen=EPISODIC_TIMELINE_MAXLEN,
)
_save(source)
payload = json.loads(state_path.read_text(encoding="utf-8"))
assert payload["runtime"]["active_control_preset"] == "quiet_hours"
assert payload["runtime"]["voice_user_profiles"]["operator"]["verbosity"] == "brief"
restored = _build_runtime(state_path=state_path)
restored.config.motion_enabled = True
restored.config.home_enabled = True
restored.config.safe_mode_enabled = False
restored.config.persona_style = "composed"
restored.config.backchannel_style = "balanced"
set_safe_mode = MagicMock()
_load(restored, set_safe_mode_fn=set_safe_mode)
assert restored._voice_attention.mode == "push_to_talk"
assert restored._voice_attention.timeout_profile == "long"
assert restored._voice_attention.push_to_talk_active is True
assert restored._voice_attention.sleeping is True
assert restored.config.motion_enabled is False
assert restored.config.home_enabled is False
assert restored.config.safe_mode_enabled is True
assert restored._tts_output_enabled is False
assert restored.config.persona_style == "friendly"
assert restored.config.backchannel_style == "expressive"
assert restored._active_control_preset == "quiet_hours"
assert restored._pending_text == "lock doors"
assert restored._repair_candidate_text == "turn off office lamp"
assert restored._episode_seq == 7
assert len(restored._episodic_timeline) == 1
set_safe_mode.assert_called_once_with(True)
def test_check_runtime_invariants_auto_heals_mismatch() -> None:
runtime = _build_runtime()
runtime._voice_attention.set_mode("push_to_talk")
runtime._voice_attention.set_push_to_talk_active(False)
runtime._active_control_preset = "invalid-preset"
snapshot = check_runtime_invariants(
runtime,
auto_heal=True,
runtime_invariant_history_maxlen=RUNTIME_INVARIANT_HISTORY_MAXLEN,
valid_control_presets=VALID_CONTROL_PRESETS,
)
assert runtime._voice_attention.push_to_talk_active is True
assert runtime._active_control_preset == "custom"
assert snapshot["total_violations"] == 2
assert snapshot["total_auto_heals"] == 2
runtime._persist_runtime_state_safe.assert_called_once()
def test_apply_runtime_profile_updates_runtime_and_persists() -> None:
runtime = _build_runtime()
profile = {
"wake_mode": "always_listening",
"sleeping": False,
"timeout_profile": "short",
"push_to_talk_active": False,
"motion_enabled": False,
"home_enabled": False,
"safe_mode_enabled": True,
"tts_enabled": False,
"persona_style": "friendly",
"backchannel_style": "quiet",
"voice_user_profiles": {
"Operator": {
"verbosity": "brief",
"confirmations": "minimal",
"pace": "fast",
"tone": "direct",
},
"": {"verbosity": "detailed"},
},
}
set_safe_mode = MagicMock()
snapshot = apply_runtime_profile(
runtime,
profile,
mark_custom=True,
valid_wake_modes=VALID_WAKE_MODES,
valid_timeout_profiles=VALID_TIMEOUT_PROFILES,
valid_persona_styles=VALID_PERSONA_STYLES,
valid_backchannel_styles=VALID_BACKCHANNEL_STYLES,
valid_voice_profile_verbosity=VALID_VOICE_PROFILE_VERBOSITY,
valid_voice_profile_confirmations=VALID_VOICE_PROFILE_CONFIRMATIONS,
valid_voice_profile_pace=VALID_VOICE_PROFILE_PACE,
valid_voice_profile_tone=VALID_VOICE_PROFILE_TONE,
set_safe_mode_fn=set_safe_mode,
)
assert runtime._voice_attention.mode == "always_listening"
assert runtime._voice_attention.timeout_profile == "short"
assert runtime.config.motion_enabled is False
assert runtime.config.home_enabled is False
assert runtime.config.safe_mode_enabled is True
assert runtime._tts_output_enabled is False
assert runtime.config.persona_style == "friendly"
assert runtime.config.backchannel_style == "quiet"
assert runtime._voice_user_profiles["operator"]["verbosity"] == "brief"
assert runtime._active_control_preset == "custom"
runtime.presence.stop.assert_called_once()
runtime.presence.set_backchannel_style.assert_called_once_with("quiet")
set_safe_mode.assert_called_once_with(True)
runtime._publish_voice_status.assert_called_once()
runtime._persist_runtime_state_safe.assert_called_once()
assert snapshot["active_control_preset"] == "custom"
def test_runtime_profile_snapshot_sanitizes_voice_profiles() -> None:
runtime = _build_runtime()
runtime._voice_user_profiles = {
"operator": {"verbosity": "brief"},
"bad": "not-a-dict",
}
snapshot = runtime_profile_snapshot(runtime)
assert snapshot["voice_user_profiles"] == {"operator": {"verbosity": "brief"}}