from __future__ import annotations import os import sys from pathlib import Path from time import perf_counter ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "src" ROOT_ENV = ROOT / ".env" LOCAL_ENV = ROOT / "data" / "local.env" if str(SRC) not in sys.path: sys.path.insert(0, str(SRC)) def main() -> int: _load_local_env(ROOT_ENV, override=True) _load_local_env(LOCAL_ENV, override=True) os.environ["TIME_MACHINE_ADAPTER_PROFILE"] = "modal" audio_path = Path(os.getenv("TIME_MACHINE_MODAL_SMOKE_AUDIO", ROOT / "data" / "audio" / "stt_probe.wav")) if not audio_path.exists(): print(f"Missing smoke audio: {audio_path}") return 2 from time_machine.application.container import create_container from time_machine.domain.events import AudioEvent, ConversationTextEvent, TranscriptEvent container = create_container(adapter_profile="modal") launch_started = perf_counter() launch_events = list( container.encounter_service.start_encounter( mode=os.getenv("TIME_MACHINE_MODAL_SMOKE_MODE", "strange"), coordinate_prompt=os.getenv( "TIME_MACHINE_MODAL_SMOKE_PROMPT", "a noisy workshop with a radio channel open", ), session_id="modal-smoke", ) ) launch_elapsed = perf_counter() - launch_started encounter_id = launch_events[0].metadata["encounter_id"] turn_started = perf_counter() turn_events = list( container.speech_orchestrator.handle_audio_turn( encounter_id=encounter_id, audio=str(audio_path), ) ) turn_elapsed = perf_counter() - turn_started transcript = _single_event(turn_events, TranscriptEvent).transcript response = _single_event(turn_events, ConversationTextEvent) audio = _single_event(turn_events, AudioEvent).audio print("Modal smoke passed.") print(f"Transcript: {transcript.text}") print(f"Reply: {response.text[:240]}") print(f"Audio: {audio.path} ({audio.duration_seconds}s)") print(f"Audio note: {audio.description}") print(f"Launch wall time: {launch_elapsed:.2f}s") print(f"Audio turn wall time: {turn_elapsed:.2f}s") print("Turn stage timings:") for timing in _event_timings(turn_events): print(f" - {timing['stage']}: {timing['elapsed_ms'] / 1000:.2f}s") return 0 def _load_local_env(path: Path, *, override: bool = False) -> None: if not path.exists(): return for raw_line in path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) key = key.strip() value = value.strip().strip('"').strip("'") if key and (override or key not in os.environ): os.environ[key] = value def _single_event(events: list[object], event_type: type) -> object: matches = [event for event in events if isinstance(event, event_type)] if not matches: raise RuntimeError(f"Expected event {event_type.__name__}, got {[type(e).__name__ for e in events]}") return matches[-1] def _event_timings(events: list[object]) -> list[dict[str, object]]: timings: list[dict[str, object]] = [] seen: set[tuple[str, float]] = set() for event in events: metadata = getattr(event, "metadata", {}) for timing in metadata.get("timings", []): stage = timing.get("stage") elapsed_ms = timing.get("elapsed_ms") key = (stage, elapsed_ms) if key in seen: continue seen.add(key) timings.append(timing) return timings if __name__ == "__main__": raise SystemExit(main())