v2.5.0 (#495)
Co-authored-by: Quentin Torroba <quentin.torroba@mistral.ai> Co-authored-by: Clement Sirieix <clem.sirieix@gmail.com> Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai> Co-authored-by: Simon Van de Kerckhove <simon.vandekerckhove@mistral.ai> Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai> Co-authored-by: Michel Thomazo <michel.thomazo@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
9421fbc08e
commit
5103019b01
104 changed files with 7277 additions and 691 deletions
65
tests/stubs/fake_audio_recorder.py
Normal file
65
tests/stubs/fake_audio_recorder.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncGenerator, Callable
|
||||
|
||||
from vibe.core.audio_recorder import (
|
||||
AlreadyRecordingError,
|
||||
AudioRecording,
|
||||
RecordingMode,
|
||||
)
|
||||
|
||||
FAKE_WAV_DATA = b"RIFF\x00\x00\x00\x00WAVEfmt "
|
||||
|
||||
|
||||
class FakeAudioRecorder:
|
||||
def __init__(self, *, fake_wav_data: bytes = FAKE_WAV_DATA) -> None:
|
||||
self._recording = False
|
||||
self._peak = 0.0
|
||||
self._mode = RecordingMode.BUFFER
|
||||
self._fake_wav_data = fake_wav_data
|
||||
self._stream_chunks: list[bytes] = []
|
||||
|
||||
@property
|
||||
def is_recording(self) -> bool:
|
||||
return self._recording
|
||||
|
||||
@property
|
||||
def peak(self) -> float:
|
||||
return self._peak
|
||||
|
||||
@property
|
||||
def mode(self) -> RecordingMode:
|
||||
return self._mode
|
||||
|
||||
def start(
|
||||
self,
|
||||
mode: RecordingMode,
|
||||
*,
|
||||
sample_rate: int = 48_000,
|
||||
channels: int = 1,
|
||||
max_duration: float = 300.0,
|
||||
on_expire: Callable[[AudioRecording], object] | None = None,
|
||||
) -> None:
|
||||
if self._recording:
|
||||
raise AlreadyRecordingError("Already recording")
|
||||
self._recording = True
|
||||
self._mode = mode
|
||||
|
||||
def stop(self, *, wait_for_queue_drained: bool = True) -> AudioRecording:
|
||||
if not self._recording:
|
||||
return AudioRecording(data=b"", duration=0.0)
|
||||
self._recording = False
|
||||
return AudioRecording(data=self._fake_wav_data, duration=1.0)
|
||||
|
||||
def cancel(self) -> None:
|
||||
self._recording = False
|
||||
|
||||
async def audio_stream(self) -> AsyncGenerator[bytes, None]:
|
||||
for chunk in self._stream_chunks:
|
||||
yield chunk
|
||||
|
||||
def set_peak(self, value: float) -> None:
|
||||
self._peak = value
|
||||
|
||||
def set_stream_chunks(self, chunks: list[bytes]) -> None:
|
||||
self._stream_chunks = chunks
|
||||
22
tests/stubs/fake_transcribe_client.py
Normal file
22
tests/stubs/fake_transcribe_client.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import Any
|
||||
|
||||
from vibe.core.transcribe.transcribe_client_port import TranscribeEvent
|
||||
|
||||
|
||||
class FakeTranscribeClient:
|
||||
def __init__(
|
||||
self, *_args: Any, events: list[TranscribeEvent] | None = None, **_kwargs: Any
|
||||
) -> None:
|
||||
self._events: list[TranscribeEvent] = events or []
|
||||
|
||||
def set_events(self, events: list[TranscribeEvent]) -> None:
|
||||
self._events = events
|
||||
|
||||
async def transcribe(
|
||||
self, audio_stream: AsyncIterator[bytes]
|
||||
) -> AsyncIterator[TranscribeEvent]:
|
||||
for event in self._events:
|
||||
yield event
|
||||
74
tests/stubs/fake_voice_manager.py
Normal file
74
tests/stubs/fake_voice_manager.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from tests.stubs.fake_audio_recorder import FakeAudioRecorder
|
||||
from vibe.cli.voice_manager import VoiceToggleResult
|
||||
from vibe.cli.voice_manager.voice_manager_port import (
|
||||
TranscribeState,
|
||||
VoiceManagerListener,
|
||||
)
|
||||
from vibe.core.audio_recorder import AudioRecorderPort
|
||||
from vibe.core.audio_recorder.audio_recorder_port import RecordingMode
|
||||
|
||||
|
||||
class FakeVoiceManager:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
is_voice_ready: bool = False,
|
||||
audio_recorder: AudioRecorderPort | None = None,
|
||||
) -> None:
|
||||
self._enabled = is_voice_ready
|
||||
self._audio_recorder: AudioRecorderPort = audio_recorder or FakeAudioRecorder()
|
||||
self._transcribe_state = TranscribeState.IDLE
|
||||
self._listeners: list[VoiceManagerListener] = []
|
||||
|
||||
@property
|
||||
def is_enabled(self) -> bool:
|
||||
return self._enabled
|
||||
|
||||
@property
|
||||
def transcribe_state(self) -> TranscribeState:
|
||||
return self._transcribe_state
|
||||
|
||||
@property
|
||||
def peak(self) -> float:
|
||||
return self._audio_recorder.peak
|
||||
|
||||
def toggle_voice_mode(self) -> VoiceToggleResult:
|
||||
self._enabled = not self._enabled
|
||||
if not self._enabled:
|
||||
self.cancel_recording()
|
||||
for listener in self._listeners:
|
||||
listener.on_voice_mode_change(self._enabled)
|
||||
return VoiceToggleResult(enabled=self._enabled)
|
||||
|
||||
def start_recording(self, mode: RecordingMode = RecordingMode.STREAM) -> None:
|
||||
self._set_state(TranscribeState.RECORDING)
|
||||
|
||||
async def stop_recording(self) -> None:
|
||||
self._set_state(TranscribeState.FLUSHING)
|
||||
self._set_state(TranscribeState.IDLE)
|
||||
|
||||
def cancel_recording(self) -> None:
|
||||
if self._transcribe_state == TranscribeState.IDLE:
|
||||
return
|
||||
if self._transcribe_state == TranscribeState.RECORDING:
|
||||
pass # fake: no actual audio to cancel
|
||||
self._set_state(TranscribeState.IDLE)
|
||||
|
||||
def add_listener(self, listener: VoiceManagerListener) -> None:
|
||||
if listener not in self._listeners:
|
||||
self._listeners.append(listener)
|
||||
|
||||
def remove_listener(self, listener: VoiceManagerListener) -> None:
|
||||
try:
|
||||
self._listeners.remove(listener)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def _set_state(self, state: TranscribeState) -> None:
|
||||
if self._transcribe_state == state:
|
||||
return
|
||||
self._transcribe_state = state
|
||||
for listener in self._listeners:
|
||||
listener.on_transcribe_state_change(state)
|
||||
Loading…
Add table
Add a link
Reference in a new issue