astra-local-voice/tests/test_inference.py
Jeuner a42b3e8d73 feat: initial commit of astra local voice agent
Lokaler deutscher Sprachagent für Apple Silicon: Pipecat-Pipeline mit
Nemotron-ASR (MLX), Qwen über natives Ollama /api/chat, und Pocket TTS.
Loopback-only WebRTC-Server mit Origin/Host-Härtung.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 13:39:51 +02:00

55 lines
1.5 KiB
Python

import asyncio
import threading
from concurrent.futures import ThreadPoolExecutor
import pytest
from astra.inference import drain_stream, on_executor
@pytest.mark.asyncio
async def test_cancel_waits_for_native_inference_before_releasing_model():
started = threading.Event()
release = threading.Event()
finished = threading.Event()
def inference():
started.set()
release.wait(timeout=5)
finished.set()
return "audio"
with ThreadPoolExecutor(max_workers=1) as executor:
task = asyncio.create_task(on_executor(executor, inference))
await asyncio.to_thread(started.wait, 3)
task.cancel()
await asyncio.sleep(0.01)
assert not task.done()
release.set()
with pytest.raises(asyncio.CancelledError):
await task
assert finished.is_set()
@pytest.mark.asyncio
async def test_native_exception_is_visible():
def fail():
raise RuntimeError("Model failed")
with ThreadPoolExecutor(max_workers=1) as executor:
with pytest.raises(RuntimeError, match="Model failed"):
await on_executor(executor, fail)
def test_abandoned_tts_stream_reaches_normal_join_before_reuse():
joined = []
def threaded_stream():
yield b"first audio"
yield b"remaining audio"
joined.append(True) # Represents Pocket TTS's join after its yield loop.
stream = threaded_stream()
next(stream)
drain_stream(stream)
assert joined == [True]