| 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"}} |
|
|