Download tests/test_policy.py from pearlyjam21/reachy_mini_multimodal_emotion: direct link, hf CLI and curl.
- Browser
- Download file 2.73 kB
-
https://huggingface.co/spaces/pearlyjam21/reachy_mini_multimodal_emotion/resolve/d626f8b26251736adc17511bda87c11f012aae67/tests/test_policy.py
- Command line
-
hf download hf://spaces/pearlyjam21/reachy_mini_multimodal_emotion@d626f8b26251736adc17511bda87c11f012aae67/tests/test_policy.py
-
curl -L -o test_policy.py https://huggingface.co/spaces/pearlyjam21/reachy_mini_multimodal_emotion/resolve/d626f8b26251736adc17511bda87c11f012aae67/tests/test_policy.py
2.73 kB
| """Reaction policy and SER engine behaviour.""" | |
| import random | |
| import numpy as np | |
| from reachy_mini_multimodal_emotion.engine.labels import EMOTIONS | |
| from reachy_mini_multimodal_emotion.reactions import EMPATHY_MOVES, ReactionPolicy, pick_move | |
| def probs_for(emotion, p): | |
| x = np.full(7, (1 - p) / 6) | |
| x[EMOTIONS.index(emotion)] = p | |
| return x | |
| def test_policy_needs_sustained_confidence_then_cools_down(): | |
| pol = ReactionPolicy(hold_s=1.5, cooldown_s=6.0) | |
| happy = probs_for("happy", 0.8) | |
| assert pol.update(happy, False, 0.0) is None # first sighting starts the hold timer | |
| assert pol.update(happy, False, 1.0) is None # not held long enough | |
| assert pol.update(happy, False, 1.6) == "happy" # fires after 1.5 s | |
| assert pol.update(happy, False, 2.0) is None # restarts hold | |
| assert pol.update(happy, False, 4.0) is None # held, but inside the 6 s cooldown | |
| assert pol.update(happy, False, 7.7) == "happy" # cooldown over | |
| def test_policy_ignores_neutral_idle_low_confidence_and_resets_on_switch(): | |
| pol = ReactionPolicy(hold_s=1.0, cooldown_s=0.0) | |
| assert pol.update(probs_for("neutral", 0.9), False, 0.0) is None | |
| assert pol.update(probs_for("neutral", 0.9), False, 5.0) is None # neutral never reacts | |
| assert pol.update(probs_for("angry", 0.4), False, 6.0) is None # below threshold | |
| pol.update(probs_for("angry", 0.8), False, 7.0) | |
| assert pol.update(probs_for("angry", 0.8), True, 8.5) is None # idle interrupts | |
| pol.update(probs_for("angry", 0.8), False, 9.0) | |
| pol.update(probs_for("fear", 0.8), False, 9.5) # switch restarts hold | |
| assert pol.update(probs_for("fear", 0.8), False, 10.0) is None | |
| assert pol.update(probs_for("fear", 0.8), False, 10.6) == "fear" | |
| def test_every_reactable_emotion_has_moves(): | |
| rng = random.Random(0) | |
| for emo in EMOTIONS[1:]: | |
| assert pick_move(emo, rng) in EMPATHY_MOVES[emo] | |
| assert pick_move("neutral") is None | |
| def test_mic_source_returns_with_continuous_audio(): | |
| """Regression from SER_Reachy_merli_v2: a chunk is always ready, so read() must not drain 'until None'.""" | |
| import threading | |
| from types import SimpleNamespace | |
| from reachy_mini_multimodal_emotion.main import MicSource | |
| class EndlessMic: | |
| def start_recording(self): pass | |
| def stop_recording(self): pass | |
| def get_audio_sample(self): return np.full((256, 2), 0.1, np.float32) | |
| src = MicSource(SimpleNamespace(media=EndlessMic())) | |
| out = [] | |
| t = threading.Thread(target=lambda: out.append(src.read()), daemon=True) | |
| t.start(); t.join(timeout=1.0) | |
| assert not t.is_alive() and out[0].shape == (256,) | |