Этап 3: голосовой ввод по горячей клавише

- Глобальная горячая клавиша через RegisterHotKey (по умолчанию Win+Alt+Space)
- Запись с микрофона, конец фразы по паузе через Silero VAD, звуковые сигналы
- Распознавание faster-whisper large-v3-turbo на GPU, CUDA-библиотеки из pip-пакетов nvidia-*
- Состояния «слушаю» и «распознаю», кнопка «Говорить» в чате и пункт в трее
- Голосовая команда прерывает текущий ответ и озвучку
- Тесты горячей клавиши, детектора конца фразы и сеанса голосового ввода

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nid
2026-09-17 03:34:09 +03:00
co-authored by Claude Opus 5
parent fec52dd1c2
commit 513d010c19
19 changed files with 1298 additions and 12 deletions
+13 -1
View File
@@ -5,7 +5,8 @@
- LLM: локальные модели (Ollama, LM Studio, llama.cpp) или OpenRouter — через единый OpenAI-совместимый API
- Текстовый чат со стримингом ответа
- Озвучка ответов голосом Silero: фразы проговариваются по мере генерации, блоки кода пропускаются
- В планах: голосовой ввод (faster-whisper), слово активации
- Голосовой ввод по глобальной горячей клавише: faster-whisper на видеокарте, конец фразы по паузе (Silero VAD)
- В планах: слово активации
## Запуск
@@ -34,6 +35,17 @@ llm:
Голос и модель задаются в секции `tts` конфига; озвучку можно выключить в меню значка.
Silero читает только кириллицу: числа переводятся в слова, латиница пропускается.
### Голосовой ввод
Нажмите `Win+Alt+Space` (настраивается в `voice.hotkey`), дождитесь короткого сигнала и говорите —
запись закончится сама после паузы, или нажмите клавишу ещё раз. Если ассистент в этот момент
отвечает, он замолкает и слушает.
Распознаёт faster-whisper (`large-v3-turbo`, ~1.6 ГБ, скачивается при первом запуске).
При наличии видеокарты NVIDIA используется она: библиотеки CUDA ставятся pip-пакетами
`nvidia-cublas-cu12` и `nvidia-cudnn-cu12`, отдельно устанавливать CUDA Toolkit не нужно.
Без видеокарты распознавание идёт на CPU — тогда лучше выбрать модель `small` или `medium`.
Закрытие окна сворачивает приложение в трей. Клик по значку открывает чат, в меню значка —
переключение провайдера, новый диалог и выход.
+4
View File
@@ -5,11 +5,15 @@ description = "Минимальный голосовой AI-ассистент
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"faster-whisper>=1.2.1",
"num2words>=0.5.14",
"numpy>=2.4.6",
"nvidia-cublas-cu12>=12.9.2.10",
"nvidia-cudnn-cu12>=9.26.0.51",
"openai>=3.14.1",
"pyside6>=6.11.2",
"pyyaml>=6.0.3",
"silero-vad>=6.2.1",
"sounddevice>=0.5.6",
"torch>=2.14.0",
]
+32 -3
View File
@@ -10,9 +10,13 @@ from PySide6.QtWidgets import QApplication, QMessageBox, QSystemTrayIcon
from agr_assistent import APP_NAME
from agr_assistent.audio.player import AudioPlayer
from agr_assistent.audio.recorder import SpeechRecorder
from agr_assistent.config import ConfigError, data_dir, load_config
from agr_assistent.core.assistant import Assistant
from agr_assistent.core.speech import Speaker
from agr_assistent.core.voice import VoiceInput
from agr_assistent.hotkey import GlobalHotkey, HotkeyError
from agr_assistent.stt.whisper import WhisperSTT
from agr_assistent.tts.silero import SileroTTS
from agr_assistent.ui.chat_window import ChatWindow
from agr_assistent.ui.icons import state_icon
@@ -44,15 +48,40 @@ def main() -> int:
QMessageBox.critical(None, APP_NAME, "Системный трей недоступен")
return 1
models_dir = data_dir() / "models"
speaker = Speaker(
SileroTTS(config.tts, data_dir() / "models"), AudioPlayer(), enabled=config.tts.enabled
SileroTTS(config.tts, models_dir), AudioPlayer(), enabled=config.tts.enabled
)
if config.tts.enabled:
speaker.warm_up()
assistant = Assistant(config.llm, speaker)
voice = None
if config.voice.enabled:
voice = VoiceInput(
SpeechRecorder(config.voice),
WhisperSTT(config.stt, models_dir),
sound_cues=config.voice.sound_cues,
)
voice.warm_up()
assistant = Assistant(config.llm, speaker, voice)
app.setWindowIcon(state_icon(assistant.state))
window = ChatWindow(assistant)
tray = TrayIcon(assistant, window)
hotkey_spec = config.voice.hotkey if voice is not None else ""
if hotkey_spec:
try:
hotkey = GlobalHotkey(hotkey_spec, app)
except HotkeyError as exc:
assistant.error_occurred.emit(str(exc))
hotkey_spec = ""
else:
hotkey.activated.connect(assistant.toggle_listening)
hotkey.failed.connect(assistant.error_occurred)
app.aboutToQuit.connect(hotkey.stop)
hotkey.start()
tray = TrayIcon(assistant, window, hotkey_spec)
tray.show()
if config.ui.start_minimized:
+29
View File
@@ -0,0 +1,29 @@
"""Короткие звуковые сигналы начала и конца записи."""
from __future__ import annotations
import numpy as np
import sounddevice as sd
_SAMPLE_RATE = 24000
_DURATION_SECONDS = 0.09
_FADE_SECONDS = 0.01
_VOLUME = 0.25
START_FREQUENCY = 880.0
STOP_FREQUENCY = 587.0
def play_tone(frequency: float) -> None:
"""Блокирует до конца сигнала, чтобы он не попал в запись."""
samples = int(_SAMPLE_RATE * _DURATION_SECONDS)
t = np.arange(samples) / _SAMPLE_RATE
tone = np.sin(2 * np.pi * frequency * t) * _VOLUME
fade = int(_SAMPLE_RATE * _FADE_SECONDS)
envelope = np.ones(samples)
envelope[:fade] = np.linspace(0, 1, fade)
envelope[-fade:] = np.linspace(1, 0, fade)
sd.play((tone * envelope).astype(np.float32), _SAMPLE_RATE)
sd.wait()
+113
View File
@@ -0,0 +1,113 @@
"""Запись фразы с микрофона: детектор речи сам определяет, когда человек замолчал."""
from __future__ import annotations
import threading
from collections.abc import Callable
from enum import Enum
from typing import Any
import numpy as np
import sounddevice as sd
from agr_assistent.config import VoiceConfig
SAMPLE_RATE = 16000
# Silero VAD принимает окна ровно по 512 отсчётов при 16 кГц
_VAD_WINDOW = 512
class UtteranceStatus(Enum):
CONTINUE = "continue"
COMPLETE = "complete"
NO_SPEECH = "no_speech"
class UtteranceDetector:
"""По вероятностям речи от VAD решает, закончилась ли фраза."""
def __init__(
self,
*,
threshold: float,
start_timeout_seconds: float,
silence_seconds: float,
max_seconds: float,
sample_rate: int = SAMPLE_RATE,
) -> None:
self._threshold = threshold
self._start_timeout = int(start_timeout_seconds * sample_rate)
self._silence_limit = int(silence_seconds * sample_rate)
self._max = int(max_seconds * sample_rate)
self._total = 0
self._silence = 0
self._speech_detected = False
@property
def speech_detected(self) -> bool:
return self._speech_detected
def update(self, speech_probability: float, samples: int) -> UtteranceStatus:
self._total += samples
if speech_probability >= self._threshold:
self._speech_detected = True
self._silence = 0
elif self._speech_detected:
self._silence += samples
if self._speech_detected and self._silence >= self._silence_limit:
return UtteranceStatus.COMPLETE
if self._total >= self._max:
return UtteranceStatus.COMPLETE if self._speech_detected else UtteranceStatus.NO_SPEECH
if not self._speech_detected and self._total >= self._start_timeout:
return UtteranceStatus.NO_SPEECH
return UtteranceStatus.CONTINUE
class SpeechRecorder:
def __init__(self, config: VoiceConfig) -> None:
self._config = config
self._vad: Any = None
self._load_lock = threading.Lock()
# Отменённая запись может ещё доигрывать последние миллисекунды, когда начинается новая
self._record_lock = threading.Lock()
def load(self) -> None:
with self._load_lock:
if self._vad is None:
from silero_vad import load_silero_vad
self._vad = load_silero_vad()
def record(self, should_stop: Callable[[], bool]) -> np.ndarray | None:
"""Записывает одну фразу; None — речи не было или запись прервали до её начала."""
self.load()
import torch
config = self._config
detector = UtteranceDetector(
threshold=config.vad_threshold,
start_timeout_seconds=config.start_timeout_seconds,
silence_seconds=config.silence_seconds,
max_seconds=config.max_seconds,
)
chunks: list[np.ndarray] = []
with self._record_lock, sd.InputStream(
samplerate=SAMPLE_RATE, channels=1, dtype="float32", blocksize=_VAD_WINDOW
) as stream:
self._vad.reset_states()
while not should_stop():
block, _overflowed = stream.read(_VAD_WINDOW)
window = np.ascontiguousarray(block[:, 0])
chunks.append(window)
with torch.inference_mode():
probability = self._vad(torch.from_numpy(window), SAMPLE_RATE).item()
status = detector.update(probability, len(window))
if status is UtteranceStatus.NO_SPEECH:
return None
if status is UtteranceStatus.COMPLETE:
break
if not detector.speech_detected:
return None
return np.concatenate(chunks)
+41 -1
View File
@@ -58,6 +58,26 @@ class TTSConfig:
device: str
@dataclass
class STTConfig:
model: str
device: str
compute_type: str
language: str | None
beam_size: int
@dataclass
class VoiceConfig:
enabled: bool
hotkey: str
sound_cues: bool
start_timeout_seconds: float
silence_seconds: float
max_seconds: float
vad_threshold: float
@dataclass
class UIConfig:
start_minimized: bool
@@ -67,6 +87,8 @@ class UIConfig:
class AppConfig:
llm: LLMConfig
tts: TTSConfig
stt: STTConfig
voice: VoiceConfig
ui: UIConfig
path: Path
@@ -139,6 +161,24 @@ def parse_config(data: dict[str, Any], path: Path) -> AppConfig:
sample_rate=int(tts_data["sample_rate"]),
device=str(tts_data["device"]),
)
stt_data = data["stt"]
stt = STTConfig(
model=str(stt_data["model"]),
device=str(stt_data["device"]),
compute_type=str(stt_data["compute_type"]),
language=str(stt_data.get("language") or "") or None,
beam_size=int(stt_data["beam_size"]),
)
voice_data = data["voice"]
voice = VoiceConfig(
enabled=bool(voice_data["enabled"]),
hotkey=str(voice_data.get("hotkey") or ""),
sound_cues=bool(voice_data["sound_cues"]),
start_timeout_seconds=float(voice_data["start_timeout_seconds"]),
silence_seconds=float(voice_data["silence_seconds"]),
max_seconds=float(voice_data["max_seconds"]),
vad_threshold=float(voice_data["vad_threshold"]),
)
ui = UIConfig(start_minimized=bool(data["ui"]["start_minimized"]))
except KeyError as exc:
raise ConfigError(f"{path}: отсутствует ключ {exc}") from exc
@@ -150,7 +190,7 @@ def parse_config(data: dict[str, Any], path: Path) -> AppConfig:
raise ConfigError(
f"{path}: провайдер '{llm.provider}' не описан в llm.providers (есть: {known})"
)
return AppConfig(llm=llm, tts=tts, ui=ui, path=path)
return AppConfig(llm=llm, tts=tts, stt=stt, voice=voice, ui=ui, path=path)
def _expand_env(value: str) -> str:
+47 -3
View File
@@ -10,6 +10,7 @@ from PySide6.QtCore import QObject, Signal, Slot
from agr_assistent.config import LLMConfig
from agr_assistent.core.speech import Speaker
from agr_assistent.core.voice import VoiceInput
from agr_assistent.llm.client import LLMClient, LLMError
log = logging.getLogger(__name__)
@@ -19,6 +20,8 @@ Message = dict[str, str]
class AssistantState(Enum):
IDLE = "idle"
LISTENING = "listening"
RECOGNIZING = "recognizing"
THINKING = "thinking"
SPEAKING = "speaking"
@@ -55,11 +58,16 @@ class Assistant(QObject):
_worker_done = Signal(int)
def __init__(
self, config: LLMConfig, speaker: Speaker, parent: QObject | None = None
self,
config: LLMConfig,
speaker: Speaker,
voice: VoiceInput | None,
parent: QObject | None = None,
) -> None:
super().__init__(parent)
self._config = config
self._speaker = speaker
self._voice = voice
self._client: LLMClient | None = None
self._history: list[Message] = []
self._state = AssistantState.IDLE
@@ -74,6 +82,12 @@ class Assistant(QObject):
speaker.playback_started.connect(self._update_state)
speaker.finished.connect(self._update_state)
speaker.error_occurred.connect(self.error_occurred)
if voice is not None:
voice.listening_started.connect(self._update_state)
voice.recognizing_started.connect(self._update_state)
voice.finished.connect(self._update_state)
voice.recognized.connect(self._on_voice_recognized)
voice.error_occurred.connect(self.error_occurred)
@property
def state(self) -> AssistantState:
@@ -104,6 +118,23 @@ class Assistant(QObject):
def speech_enabled(self) -> bool:
return self._speaker.enabled
@property
def voice_available(self) -> bool:
return self._voice is not None
def toggle_listening(self) -> None:
"""Горячая клавиша: начать запись, а во время записи — закончить её досрочно.
Если ассистент в этот момент думает или говорит, он замолкает и слушает.
"""
if self._voice is None or self._voice.is_recognizing:
return
if self._voice.is_listening:
self._voice.stop()
return
self.cancel()
self._voice.start()
def set_provider(self, name: str) -> None:
if name == self._config.provider:
return
@@ -153,7 +184,9 @@ class Assistant(QObject):
).start()
def cancel(self) -> None:
"""Прерывает и генерацию, и озвучку."""
"""Прерывает запись голоса, генерацию и озвучку."""
if self._voice is not None:
self._voice.cancel()
if self._generating:
self._generation += 1
self._finish_reply()
@@ -188,6 +221,13 @@ class Assistant(QObject):
else:
self._worker_done.emit(generation)
@Slot(str)
def _on_voice_recognized(self, text: str) -> None:
if text.strip():
self.send(text)
else:
self.error_occurred.emit("Не удалось разобрать речь")
@Slot(int, str)
def _on_worker_chunk(self, generation: int, piece: str) -> None:
if generation == self._generation:
@@ -220,7 +260,11 @@ class Assistant(QObject):
@Slot()
def _update_state(self) -> None:
if self._speaker.is_playing:
if self._voice is not None and self._voice.is_listening:
state = AssistantState.LISTENING
elif self._voice is not None and self._voice.is_recognizing:
state = AssistantState.RECOGNIZING
elif self._speaker.is_playing:
state = AssistantState.SPEAKING
elif self._generating:
state = AssistantState.THINKING
+171
View File
@@ -0,0 +1,171 @@
"""Голосовой ввод: запись фразы и её распознавание в фоновом потоке."""
from __future__ import annotations
import logging
import threading
from collections.abc import Callable
from enum import Enum
from typing import Protocol
import numpy as np
from PySide6.QtCore import QObject, Signal, Slot
from agr_assistent.audio import cues
log = logging.getLogger(__name__)
_NO_GENERATION = -1 # ошибка, не привязанная к сеансу записи
class Recorder(Protocol):
def load(self) -> None: ...
def record(self, should_stop: Callable[[], bool]) -> np.ndarray | None: ...
class Recognizer(Protocol):
def load(self) -> None: ...
def transcribe(self, audio: np.ndarray) -> str: ...
class _Phase(Enum):
IDLE = "idle"
LISTENING = "listening"
RECOGNIZING = "recognizing"
class VoiceInput(QObject):
listening_started = Signal()
recognizing_started = Signal()
recognized = Signal(str) # может быть пустой строкой, если слов не разобрать
finished = Signal() # сеанс завершён: распознан, прерван или речи не было
error_occurred = Signal(str)
# Мост из фонового потока в главный; int — номер сеанса
_worker_recognizing = Signal(int)
_worker_recognized = Signal(int, str)
_worker_done = Signal(int)
_worker_failed = Signal(int, str)
def __init__(
self,
recorder: Recorder,
recognizer: Recognizer,
*,
sound_cues: bool,
parent: QObject | None = None,
) -> None:
super().__init__(parent)
self._recorder = recorder
self._recognizer = recognizer
self._sound_cues = sound_cues
self._phase = _Phase.IDLE
# Увеличивается при каждом сеансе и отмене: результаты устаревших потоков отбрасываются
self._generation = 0
self._stop_requested = False
self._worker_recognizing.connect(self._on_worker_recognizing)
self._worker_recognized.connect(self._on_worker_recognized)
self._worker_done.connect(self._on_worker_done)
self._worker_failed.connect(self._on_worker_failed)
@property
def is_listening(self) -> bool:
return self._phase is _Phase.LISTENING
@property
def is_recognizing(self) -> bool:
return self._phase is _Phase.RECOGNIZING
@property
def is_active(self) -> bool:
return self._phase is not _Phase.IDLE
def warm_up(self) -> None:
"""Загружает детектор речи и модель распознавания заранее."""
threading.Thread(target=self._load_models, name="stt-warm-up", daemon=True).start()
def start(self) -> None:
if self.is_active:
return
self._generation += 1
self._stop_requested = False
self._phase = _Phase.LISTENING
self.listening_started.emit()
threading.Thread(
target=self._run,
args=(self._generation,),
name=f"voice-input-{self._generation}",
daemon=True,
).start()
def stop(self) -> None:
"""Заканчивает запись досрочно и распознаёт то, что успели сказать."""
if self.is_listening:
self._stop_requested = True
def cancel(self) -> None:
if self.is_active:
self._generation += 1
self._finish()
def _load_models(self) -> None:
try:
self._recorder.load()
self._recognizer.load()
except Exception as exc:
log.exception("Не удалось загрузить модели голосового ввода")
self._worker_failed.emit(_NO_GENERATION, f"Голосовой ввод недоступен: {exc}")
def _run(self, generation: int) -> None:
"""Фоновый поток: сигнал, запись, распознавание."""
try:
if self._sound_cues:
cues.play_tone(cues.START_FREQUENCY)
audio = self._recorder.record(
lambda: self._stop_requested or generation != self._generation
)
if generation != self._generation:
return
if self._sound_cues:
cues.play_tone(cues.STOP_FREQUENCY)
if audio is None:
log.info("Речь не обнаружена")
self._worker_done.emit(generation)
return
self._worker_recognizing.emit(generation)
text = self._recognizer.transcribe(audio)
log.info("Распознано: %s", text)
self._worker_recognized.emit(generation, text)
except Exception as exc:
log.exception("Сбой голосового ввода")
self._worker_failed.emit(generation, f"Ошибка голосового ввода: {exc}")
@Slot(int)
def _on_worker_recognizing(self, generation: int) -> None:
if generation == self._generation and self.is_listening:
self._phase = _Phase.RECOGNIZING
self.recognizing_started.emit()
@Slot(int, str)
def _on_worker_recognized(self, generation: int, text: str) -> None:
if generation == self._generation and self.is_active:
self._finish()
self.recognized.emit(text)
@Slot(int)
def _on_worker_done(self, generation: int) -> None:
if generation == self._generation and self.is_active:
self._finish()
@Slot(int, str)
def _on_worker_failed(self, generation: int, message: str) -> None:
if generation == self._generation:
self.cancel()
elif generation != _NO_GENERATION:
return
self.error_occurred.emit(message)
def _finish(self) -> None:
self._phase = _Phase.IDLE
self.finished.emit()
+28
View File
@@ -42,6 +42,34 @@ tts:
sample_rate: 48000
device: cpu
stt:
# Модель faster-whisper: tiny, base, small, medium, large-v3, large-v3-turbo.
# Скачивается при первом использовании (large-v3-turbo — ~1.6 ГБ)
model: large-v3-turbo
# auto (видеокарта NVIDIA, если доступна), cuda или cpu
device: auto
# auto, float16, int8_float16 (GPU), int8 (CPU)
compute_type: auto
# Язык речи; пустая строка — определять автоматически
language: ru
beam_size: 5
voice:
# Голосовой ввод: загрузка моделей при старте и горячая клавиша
enabled: true
# Глобальная горячая клавиша: ctrl, alt, shift, win + клавиша (буква, цифра, f1f24, space...).
# Нажатие начинает запись, повторное — заканчивает досрочно
hotkey: win+alt+space
# Короткие звуковые сигналы в начале и в конце записи
sound_cues: true
# Сколько ждать начала речи
start_timeout_seconds: 6
# Пауза, после которой фраза считается законченной
silence_seconds: 1.0
max_seconds: 30
# Порог детектора речи 0..1: выше — меньше ложных срабатываний на шум
vad_threshold: 0.5
ui:
# Запускаться сразу в трее, не показывая окно чата
start_minimized: false
+132
View File
@@ -0,0 +1,132 @@
"""Глобальная горячая клавиша (Windows, RegisterHotKey)."""
from __future__ import annotations
import ctypes
import logging
import re
import sys
import threading
from PySide6.QtCore import QObject, Signal
log = logging.getLogger(__name__)
_MOD_ALT = 0x0001
_MOD_CONTROL = 0x0002
_MOD_SHIFT = 0x0004
_MOD_WIN = 0x0008
_MOD_NOREPEAT = 0x4000
_WM_HOTKEY = 0x0312
_WM_QUIT = 0x0012
_HOTKEY_ID = 1
_MODIFIERS = {
"ctrl": _MOD_CONTROL,
"control": _MOD_CONTROL,
"alt": _MOD_ALT,
"shift": _MOD_SHIFT,
"win": _MOD_WIN,
}
_NAMED_KEYS = {
"space": 0x20,
"enter": 0x0D,
"tab": 0x09,
"esc": 0x1B,
"escape": 0x1B,
"backspace": 0x08,
"insert": 0x2D,
"delete": 0x2E,
"home": 0x24,
"end": 0x23,
"pageup": 0x21,
"pagedown": 0x22,
"pause": 0x13,
"left": 0x25,
"up": 0x26,
"right": 0x27,
"down": 0x28,
"`": 0xC0,
}
_FUNCTION_KEY = re.compile(r"f([1-9]|1\d|2[0-4])")
class HotkeyError(Exception):
pass
def parse_hotkey(spec: str) -> tuple[int, int]:
"""«ctrl+alt+space» -> (модификаторы, виртуальный код клавиши)."""
parts = [part.strip().lower() for part in spec.split("+")]
if not spec.strip() or not all(parts):
raise HotkeyError(f"Некорректная горячая клавиша: '{spec}'")
*modifier_names, key = parts
modifiers = 0
for name in modifier_names:
if name not in _MODIFIERS:
raise HotkeyError(f"Неизвестный модификатор '{name}' в горячей клавише '{spec}'")
modifiers |= _MODIFIERS[name]
if len(key) == 1 and key.isascii() and key.isalnum():
virtual_key = ord(key.upper())
elif key in _NAMED_KEYS:
virtual_key = _NAMED_KEYS[key]
elif match := _FUNCTION_KEY.fullmatch(key):
virtual_key = 0x70 + int(match.group(1)) - 1
else:
raise HotkeyError(f"Неизвестная клавиша '{key}' в горячей клавише '{spec}'")
return modifiers, virtual_key
class GlobalHotkey(QObject):
"""Слушает клавишу в отдельном потоке с собственной очередью сообщений Windows."""
activated = Signal()
failed = Signal(str)
def __init__(self, spec: str, parent: QObject | None = None) -> None:
super().__init__(parent)
self._spec = spec
self._modifiers, self._virtual_key = parse_hotkey(spec)
self._thread_id: int | None = None
self._ready = threading.Event()
def start(self) -> None:
if sys.platform != "win32":
self.failed.emit("Глобальные горячие клавиши пока поддерживаются только в Windows")
return
threading.Thread(target=self._run, name="global-hotkey", daemon=True).start()
def stop(self) -> None:
if self._ready.wait(timeout=1) and self._thread_id is not None:
ctypes.windll.user32.PostThreadMessageW(self._thread_id, _WM_QUIT, 0, 0)
def _run(self) -> None:
from ctypes import wintypes
user32 = ctypes.windll.user32
self._thread_id = ctypes.windll.kernel32.GetCurrentThreadId()
registered = user32.RegisterHotKey(
None, _HOTKEY_ID, self._modifiers | _MOD_NOREPEAT, self._virtual_key
)
self._ready.set()
if not registered:
message = (
f"Не удалось зарегистрировать горячую клавишу {self._spec}: "
"возможно, она уже занята другой программой"
)
log.warning(message)
self.failed.emit(message)
return
log.info("Горячая клавиша: %s", self._spec)
message = wintypes.MSG()
try:
while user32.GetMessageW(ctypes.byref(message), None, 0, 0) > 0:
if message.message == _WM_HOTKEY:
self.activated.emit()
finally:
user32.UnregisterHotKey(None, _HOTKEY_ID)
View File
+107
View File
@@ -0,0 +1,107 @@
"""Распознавание речи через faster-whisper."""
from __future__ import annotations
import logging
import os
import sys
import threading
from pathlib import Path
from typing import Any
import numpy as np
from agr_assistent.config import STTConfig
log = logging.getLogger(__name__)
SAMPLE_RATE = 16000
_WARM_UP_SECONDS = 1
class STTError(Exception):
"""Ошибка распознавания речи с понятным пользователю текстом."""
class WhisperSTT:
def __init__(self, config: STTConfig, models_dir: Path) -> None:
self._config = config
self._download_root = models_dir / "whisper"
self._model: Any = None
self._lock = threading.Lock()
def load(self) -> None:
with self._lock:
if self._model is not None:
return
log.info(
"Загружаю модель распознавания %s (при первом запуске она скачивается)",
self._config.model,
)
device = self._resolve_device()
try:
model = self._create_model(device)
except Exception as exc:
if self._config.device != "auto" or device == "cpu":
raise STTError(f"Не удалось загрузить модель Whisper: {exc}") from exc
log.warning("Видеокарта недоступна (%s), распознаю речь на CPU", exc)
device = "cpu"
model = self._create_model(device)
self._model = model
log.info("Модель Whisper %s загружена (%s)", self._config.model, device)
def transcribe(self, audio: np.ndarray) -> str:
"""audio — моно float32 с частотой 16 кГц."""
self.load()
segments, _info = self._model.transcribe(
audio,
language=self._config.language,
beam_size=self._config.beam_size,
vad_filter=True,
condition_on_previous_text=False,
)
return " ".join(segment.text.strip() for segment in segments).strip()
def _resolve_device(self) -> str:
if self._config.device == "cpu":
return "cpu"
_add_cuda_dll_directories()
if self._config.device == "cuda":
return "cuda"
import ctranslate2
return "cuda" if ctranslate2.get_cuda_device_count() > 0 else "cpu"
def _create_model(self, device: str) -> Any:
from faster_whisper import WhisperModel
compute_type = self._config.compute_type
if compute_type == "auto":
compute_type = "int8_float16" if device == "cuda" else "int8"
model = WhisperModel(
self._config.model,
device=device,
compute_type=compute_type,
download_root=str(self._download_root),
)
# Прогон на тишине: библиотеки CUDA подгружаются лениво, ошибки всплывут здесь,
# а первое настоящее распознавание не будет ждать инициализации
silence = np.zeros(SAMPLE_RATE * _WARM_UP_SECONDS, dtype=np.float32)
segments, _info = model.transcribe(silence, language=self._config.language, beam_size=1)
list(segments)
return model
def _add_cuda_dll_directories() -> None:
"""cuBLAS и cuDNN из pip-пакетов nvidia-* не лежат в PATH — добавляем их сами."""
if sys.platform != "win32":
return
try:
import nvidia
except ImportError:
return
for package_dir in nvidia.__path__:
for bin_dir in Path(package_dir).glob("*/bin"):
if str(bin_dir) not in os.environ["PATH"]:
os.add_dll_directory(str(bin_dir))
os.environ["PATH"] = f"{bin_dir}{os.pathsep}{os.environ['PATH']}"
+21 -3
View File
@@ -25,7 +25,7 @@ from PySide6.QtWidgets import (
)
from agr_assistent import APP_NAME
from agr_assistent.core.assistant import Assistant
from agr_assistent.core.assistant import Assistant, AssistantState
from agr_assistent.ui.icons import state_icon
_ROLE_STYLES = {
@@ -34,6 +34,11 @@ _ROLE_STYLES = {
"error": ("Ошибка", "#e53935"),
}
_VOICE_BUTTON_TEXTS = {
AssistantState.LISTENING: "Готово",
AssistantState.RECOGNIZING: "Распознаю…",
}
# Во время стриминга перерисовываем историю не чаще, чем раз в N мс
_RENDER_INTERVAL_MS = 50
@@ -85,13 +90,23 @@ class ChatWindow(QWidget):
self._send_button.setMinimumWidth(100)
self._send_button.clicked.connect(self._on_send_clicked)
self._voice_button = QPushButton()
self._voice_button.setMinimumWidth(100)
self._voice_button.clicked.connect(assistant.toggle_listening)
self._voice_button.setVisible(assistant.voice_available)
header = QHBoxLayout()
header.addWidget(self._provider_label, 1)
header.addWidget(new_chat_button)
buttons = QVBoxLayout()
buttons.addWidget(self._send_button)
buttons.addWidget(self._voice_button)
buttons.addStretch()
input_row = QHBoxLayout()
input_row.addWidget(self._input, 1)
input_row.addWidget(self._send_button)
input_row.addLayout(buttons)
layout = QVBoxLayout(self)
layout.addLayout(header)
@@ -146,8 +161,11 @@ class ChatWindow(QWidget):
self._submit()
def _on_state_changed(self, state: object) -> None:
current = self._assistant.state
self._send_button.setText("Стоп" if self._assistant.is_busy else "Отправить")
self.setWindowIcon(state_icon(self._assistant.state))
self._voice_button.setText(_VOICE_BUTTON_TEXTS.get(current, "Говорить"))
self._voice_button.setEnabled(current is not AssistantState.RECOGNIZING)
self.setWindowIcon(state_icon(current))
def _update_provider_label(self) -> None:
self._provider_label.setText(
+4
View File
@@ -11,12 +11,16 @@ from agr_assistent.core.assistant import AssistantState
STATE_TITLES = {
AssistantState.IDLE: "ожидание",
AssistantState.LISTENING: "слушаю…",
AssistantState.RECOGNIZING: "распознаю…",
AssistantState.THINKING: "думаю…",
AssistantState.SPEAKING: "говорю…",
}
_STATE_COLORS = {
AssistantState.IDLE: "#5c6bc0",
AssistantState.LISTENING: "#e53935",
AssistantState.RECOGNIZING: "#8e24aa",
AssistantState.THINKING: "#fb8c00",
AssistantState.SPEAKING: "#43a047",
}
+6 -1
View File
@@ -15,13 +15,18 @@ _NOTIFICATION_TIMEOUT_MS = 6000
class TrayIcon(QSystemTrayIcon):
def __init__(self, assistant: Assistant, window: ChatWindow) -> None:
def __init__(self, assistant: Assistant, window: ChatWindow, hotkey: str = "") -> None:
super().__init__(state_icon(assistant.state))
self._assistant = assistant
self._window = window
menu = QMenu()
menu.addAction("Открыть чат", window.show_and_raise)
if assistant.voice_available:
# Текст после табуляции QMenu выравнивает по правому краю, как подсказку клавиши
label = f"Говорить\t{hotkey.title()}" if hotkey else "Говорить"
menu.addAction(label, assistant.toggle_listening)
menu.addSeparator()
provider_menu = menu.addMenu("Провайдер LLM")
provider_group = QActionGroup(self)
+22
View File
@@ -0,0 +1,22 @@
import pytest
from agr_assistent.hotkey import HotkeyError, parse_hotkey
@pytest.mark.parametrize(
("spec", "expected"),
[
("ctrl+alt+space", (0x0002 | 0x0001, 0x20)),
("Win + Shift + A", (0x0008 | 0x0004, ord("A"))),
("f12", (0, 0x7B)),
("ctrl+7", (0x0002, ord("7"))),
],
)
def test_parse_hotkey(spec: str, expected: tuple[int, int]) -> None:
assert parse_hotkey(spec) == expected
@pytest.mark.parametrize("spec", ["", "ctrl+", "hyper+a", "ctrl+f25", "ctrl+ж"])
def test_parse_hotkey_rejects_invalid(spec: str) -> None:
with pytest.raises(HotkeyError):
parse_hotkey(spec)
+43
View File
@@ -0,0 +1,43 @@
from agr_assistent.audio.recorder import UtteranceDetector, UtteranceStatus
# Частота 100 отсчётов в секунду, окно — 10 отсчётов (0.1 с): так проще считать
_WINDOW = 10
def _detector(**overrides: float) -> UtteranceDetector:
settings = dict(threshold=0.5, start_timeout_seconds=1, silence_seconds=0.3, max_seconds=5)
settings.update(overrides)
return UtteranceDetector(sample_rate=100, **settings)
def _feed(detector: UtteranceDetector, probabilities: list[float]) -> list[UtteranceStatus]:
return [detector.update(p, _WINDOW) for p in probabilities]
def test_phrase_completes_after_silence() -> None:
statuses = _feed(_detector(), [0.1, 0.9, 0.8, 0.2, 0.1, 0.1])
assert statuses[:-1] == [UtteranceStatus.CONTINUE] * 5
assert statuses[-1] is UtteranceStatus.COMPLETE
def test_short_pause_does_not_end_phrase() -> None:
statuses = _feed(_detector(), [0.9, 0.1, 0.1, 0.9, 0.1, 0.1])
assert UtteranceStatus.COMPLETE not in statuses
def test_no_speech_before_timeout() -> None:
statuses = _feed(_detector(), [0.1] * 10)
assert statuses[-1] is UtteranceStatus.NO_SPEECH
assert statuses[:-1] == [UtteranceStatus.CONTINUE] * 9
def test_max_duration_completes_ongoing_speech() -> None:
detector = _detector(max_seconds=0.5)
statuses = _feed(detector, [0.9] * 5)
assert statuses[-1] is UtteranceStatus.COMPLETE
assert detector.speech_detected
+127
View File
@@ -0,0 +1,127 @@
import threading
import time
from collections.abc import Callable
import numpy as np
import pytest
from PySide6.QtCore import QCoreApplication
from agr_assistent.core.voice import VoiceInput
@pytest.fixture(scope="module")
def qapp() -> QCoreApplication:
return QCoreApplication.instance() or QCoreApplication([])
class FakeRecorder:
def __init__(self, *, wait_for_stop: bool = False, speech: bool = True) -> None:
self.wait_for_stop = wait_for_stop
self.speech = speech
self.recording = threading.Event()
def load(self) -> None:
pass
def record(self, should_stop: Callable[[], bool]) -> np.ndarray | None:
self.recording.set()
while self.wait_for_stop and not should_stop():
time.sleep(0.005)
return np.zeros(160, dtype=np.float32) if self.speech else None
class FakeRecognizer:
def __init__(self, text: str = "привет", error: Exception | None = None) -> None:
self.text = text
self.error = error
def load(self) -> None:
pass
def transcribe(self, audio: np.ndarray) -> str:
if self.error:
raise self.error
return self.text
def _wait_until(condition: Callable[[], bool], timeout: float = 3.0) -> None:
deadline = time.monotonic() + timeout
while not condition():
assert time.monotonic() < deadline, "условие не выполнилось вовремя"
QCoreApplication.processEvents()
time.sleep(0.005)
def _voice(recorder: FakeRecorder, recognizer: FakeRecognizer) -> VoiceInput:
return VoiceInput(recorder, recognizer, sound_cues=False)
def test_recognized_text_is_emitted(qapp: QCoreApplication) -> None:
voice = _voice(FakeRecorder(), FakeRecognizer("какая погода"))
events: list[str] = []
voice.recognizing_started.connect(lambda: events.append("recognizing"))
voice.recognized.connect(lambda text: events.append(text))
voice.start()
assert voice.is_listening
_wait_until(lambda: "какая погода" in events)
assert events == ["recognizing", "какая погода"]
assert not voice.is_active
def test_stop_finishes_recording_early(qapp: QCoreApplication) -> None:
recorder = FakeRecorder(wait_for_stop=True)
voice = _voice(recorder, FakeRecognizer())
recognized: list[str] = []
voice.recognized.connect(recognized.append)
voice.start()
assert recorder.recording.wait(1)
voice.stop()
_wait_until(lambda: recognized == ["привет"])
def test_cancel_discards_result(qapp: QCoreApplication) -> None:
recorder = FakeRecorder(wait_for_stop=True)
voice = _voice(recorder, FakeRecognizer())
recognized: list[str] = []
finished: list[bool] = []
voice.recognized.connect(recognized.append)
voice.finished.connect(lambda: finished.append(True))
voice.start()
assert recorder.recording.wait(1)
voice.cancel()
assert finished == [True]
time.sleep(0.1)
QCoreApplication.processEvents()
assert recognized == []
assert finished == [True]
def test_no_speech_finishes_silently(qapp: QCoreApplication) -> None:
voice = _voice(FakeRecorder(speech=False), FakeRecognizer())
events: list[str] = []
voice.recognized.connect(lambda text: events.append("recognized"))
voice.error_occurred.connect(lambda message: events.append("error"))
voice.finished.connect(lambda: events.append("finished"))
voice.start()
_wait_until(lambda: "finished" in events)
assert events == ["finished"]
def test_recognition_error_is_reported(qapp: QCoreApplication) -> None:
voice = _voice(FakeRecorder(), FakeRecognizer(error=RuntimeError("нет CUDA")))
errors: list[str] = []
voice.error_occurred.connect(errors.append)
voice.start()
_wait_until(lambda: bool(errors))
assert "нет CUDA" in errors[0]
assert not voice.is_active
Generated
+358
View File
@@ -12,12 +12,16 @@ name = "agr-assistent"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "faster-whisper" },
{ name = "num2words" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "nvidia-cublas-cu12" },
{ name = "nvidia-cudnn-cu12" },
{ name = "openai" },
{ name = "pyside6" },
{ name = "pyyaml" },
{ name = "silero-vad" },
{ name = "sounddevice" },
{ name = "torch" },
]
@@ -29,11 +33,15 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "faster-whisper", specifier = ">=1.2.1" },
{ name = "num2words", specifier = ">=0.5.14" },
{ name = "numpy", specifier = ">=2.4.6" },
{ name = "nvidia-cublas-cu12", specifier = ">=12.9.2.10" },
{ name = "nvidia-cudnn-cu12", specifier = ">=9.26.0.51" },
{ name = "openai", specifier = ">=3.14.1" },
{ name = "pyside6", specifier = ">=6.11.2" },
{ name = "pyyaml", specifier = ">=6.0.3" },
{ name = "silero-vad", specifier = ">=6.2.1" },
{ name = "sounddevice", specifier = ">=0.5.6" },
{ name = "torch", specifier = ">=2.14.0" },
]
@@ -63,6 +71,41 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" },
]
[[package]]
name = "av"
version = "18.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8d/f4/f22114d30d3435e38c6af2b4870f37b864403dca6ae7af747a289ce0a18e/av-18.1.0.tar.gz", hash = "sha256:47bfc286e1bc9de7ab4681fc2b575cd2460a66919d31ffe1bd5aa54fae531a28", size = 4451061, upload-time = "2026-08-12T22:28:18.761Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/d4/d7cdc8bff143c17a6d35924375ae28dd692cacde38700a7d419fde54f44a/av-18.1.0-cp311-abi3-macosx_11_0_x86_64.whl", hash = "sha256:ae75d8bb6467895ed1f8572ededf7ffa49eac07f6e483222f5d7d62a41d12f04", size = 22546147, upload-time = "2026-08-12T22:27:11.851Z" },
{ url = "https://files.pythonhosted.org/packages/3f/c9/37a619297492256b77d5ed906e7d8166c10a26ed251dccf1ae03ab19bff6/av-18.1.0-cp311-abi3-macosx_14_0_arm64.whl", hash = "sha256:b30a4e8d934558e19602b68998a4d9ac9f250fa0dacef216f7e8e40153b13316", size = 18217603, upload-time = "2026-08-12T22:27:14.713Z" },
{ url = "https://files.pythonhosted.org/packages/d9/84/2464ffb64c08c5ce8b522c8e74594714414e3b0575267652c5c51c0574b9/av-18.1.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6fc837cc51adf80331ac850779cd53b5d4c4460b0ebe9057a02a921c6736f19d", size = 33640142, upload-time = "2026-08-12T22:27:17.835Z" },
{ url = "https://files.pythonhosted.org/packages/27/3a/204dbfc3e08eb4cdc6e6ff57be02150bc44523ebdb50182d10025792ebd9/av-18.1.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:8a032e8d8ebc73dec079364b9b4a6837638a2d106e8472314e685ffbf163e700", size = 35786210, upload-time = "2026-08-12T22:27:20.984Z" },
{ url = "https://files.pythonhosted.org/packages/e1/99/b0d04ec553ff9a7e00455458dfa3a39c8a8f627b273056b4e5fe57d590de/av-18.1.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:3c8b1f8b46f99d52e2d8b0ed5d0cdadf172d24794d46e2077b16e44ed08e26ff", size = 39379798, upload-time = "2026-08-12T22:27:24.432Z" },
{ url = "https://files.pythonhosted.org/packages/56/b1/e00d4feae59160149df6126585e726fdc6300798fd40c5dd324879e81f68/av-18.1.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ab5ac081bc9eaf54109120d4e56284674fecfbe520d9aa1707c7fa911ec5f4d2", size = 34690321, upload-time = "2026-08-12T22:27:27.769Z" },
{ url = "https://files.pythonhosted.org/packages/dc/94/836fa987e3084d11a21489f11357fb24843ef3aa8faf74ddddfc603d5062/av-18.1.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:191224788d87af06c31784a395bb73f14b72f33d7f4871ace0157de2abdc6276", size = 36859932, upload-time = "2026-08-12T22:27:31.403Z" },
{ url = "https://files.pythonhosted.org/packages/33/b4/76ba21e46704f632004276b85289a1582e95f5eff760436d6149875a1881/av-18.1.0-cp311-abi3-win_amd64.whl", hash = "sha256:ea1480b7a8d5405cb5f382b344731bf125fd2c1c6fae3964f6c48595628387ff", size = 27595679, upload-time = "2026-08-12T22:27:35.177Z" },
{ url = "https://files.pythonhosted.org/packages/4f/ad/a3135884c5753b09773176b97201ae602f67ad14206c395ff838d66bf9b0/av-18.1.0-cp311-abi3-win_arm64.whl", hash = "sha256:5509ec12aaa19fd6601de13cfa6f4cdad450da07982118510592875d970454d6", size = 20257584, upload-time = "2026-08-12T22:27:38.472Z" },
{ url = "https://files.pythonhosted.org/packages/4f/5b/4a756265d7fb164336c8d377bca21c39cfa2c178be23cedee840a69b59c5/av-18.1.0-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:b36b0bae9e4c62f9487c99481ec15e4e3870fcc868522cd6d18fc2d6bfa04f01", size = 22795654, upload-time = "2026-08-12T22:27:42.016Z" },
{ url = "https://files.pythonhosted.org/packages/d5/cc/1bc841462114a1adf4f7d87456ab78a6972e23271e71865fcd2bbd0e7360/av-18.1.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:025f84494cb23278498f03b0d8117d3e47a1cbc9c44b97eb31875cf02251e46b", size = 18435735, upload-time = "2026-08-12T22:27:45.787Z" },
{ url = "https://files.pythonhosted.org/packages/b8/20/005500ed17a2e62a5e4bb94aa3786942560ec2f55ec1895ebf174c87abef/av-18.1.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:08a9ae288299cfcbf739dba4ad0c53b9b71f45184303dd45947920d022fed695", size = 37090807, upload-time = "2026-08-12T22:27:50.14Z" },
{ url = "https://files.pythonhosted.org/packages/5c/f7/11e7f6d848d3690c31ca4f8578167393e619177f1493ccc93b9400852d4e/av-18.1.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:cf8a17466bef07765dbdecc9e66ed9b25d20b4e14f654fbf35345a58ac45fa0c", size = 38976836, upload-time = "2026-08-12T22:27:54.565Z" },
{ url = "https://files.pythonhosted.org/packages/c3/63/b271473b24e806062d31191e40c6d65545e9cf59f80f044eba56dcbba0f4/av-18.1.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d49a5c542dfdc00f43c6cdb6cc41dac1781ee206fe180b56aa7433dfa816dfae", size = 40896630, upload-time = "2026-08-12T22:27:59.118Z" },
{ url = "https://files.pythonhosted.org/packages/6b/9f/2ab7fa292a947ad3466ed8e655eefa3b82f535d7ea598c297b4471a937c4/av-18.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5548b79e2bf1f59b3e9aedc918a72d9dc45b9adaac10ff9470d5dbdda0002e47", size = 37895673, upload-time = "2026-08-12T22:28:03.98Z" },
{ url = "https://files.pythonhosted.org/packages/e9/d8/04507c57249b399c3e4f23f01d221532f357338b5316fd2858fbd343127d/av-18.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7ea063f6690193ea335a1d592d6e0274350d45e2ed6af83ee107cb90cbfd84f", size = 39992431, upload-time = "2026-08-12T22:28:08.736Z" },
{ url = "https://files.pythonhosted.org/packages/d6/d6/bc4b95bea9c2353a7e4d62a3fcfad9adcf0f881741c6ce01ee179d539ce3/av-18.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e4d48b9f12cad009cc72fe4f4099107de5e819c95f82767f4fd01a01481c0661", size = 28497798, upload-time = "2026-08-12T22:28:13.003Z" },
{ url = "https://files.pythonhosted.org/packages/c1/d2/0c277a46f12647c1833f40496e132fb6001e0d19e6144b5ea30896461feb/av-18.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5cd9085028902c9880622bd37a12fd4b33060f06a52311f6f4867ca9f29a2c3b", size = 21421979, upload-time = "2026-08-12T22:28:16.48Z" },
]
[[package]]
name = "certifi"
version = "2026.7.22"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" },
]
[[package]]
name = "cffi"
version = "2.1.1"
@@ -161,6 +204,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" },
]
[[package]]
name = "click"
version = "8.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
@@ -170,6 +222,43 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "ctranslate2"
version = "4.8.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "pyyaml" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/17/f22fbbb0723891704af803e1e7af5541bb46013adb347f870cbd8c4834d3/ctranslate2-4.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb2a5b4c206b3bd1f7f4b44d07e86e42050af6753053cdc86444f8d0ead9d6d8", size = 1271641, upload-time = "2026-08-31T19:37:07.757Z" },
{ url = "https://files.pythonhosted.org/packages/b2/af/cfe767012a0809c15155ecae3125ca66a16aeee4669bae146220cc631a76/ctranslate2-4.8.2-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:8221875b09ef982e3579a5165f8c9fcfb3d5de0f710af228eca2f73d69635ace", size = 11929414, upload-time = "2026-08-31T19:37:09.435Z" },
{ url = "https://files.pythonhosted.org/packages/10/2e/2d08b1219303af7256aeb78b98ee847ffb229c5fe4815e6d3a1b6483846a/ctranslate2-4.8.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3823c9883c2c410a76b2f19feda9da628a3c112cedfd912db815e0055c8235e2", size = 16720002, upload-time = "2026-08-31T19:37:11.957Z" },
{ url = "https://files.pythonhosted.org/packages/f3/3d/75c029ffb484957f1d2be0f9c3b9b1474955bb16c22df85bcba50c48df11/ctranslate2-4.8.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a24e0a95151b970941867fec94c983df978630f1996242a587c072d455607d28", size = 39378274, upload-time = "2026-08-31T19:37:14.898Z" },
{ url = "https://files.pythonhosted.org/packages/7e/86/21926da682103a20d4f4833b389131b801c71e6b8d6047be5f0a28fe673a/ctranslate2-4.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:995938fcd24a1174a7abf9765e7fa216b5b91a1d8e8c4c8f383c7a186e8bab2e", size = 19220522, upload-time = "2026-08-31T19:37:17.845Z" },
{ url = "https://files.pythonhosted.org/packages/12/97/9c63a51a8c8e13e95ec2aec639460fb0f271b0421e1a365b8aad440385c4/ctranslate2-4.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fedda669421a57f8164568ee86ea265727016988d310fcc2e24ca30e9ec457cd", size = 1271613, upload-time = "2026-08-31T19:37:19.958Z" },
{ url = "https://files.pythonhosted.org/packages/ca/27/e419389fb6040a8170bb30c3dcd50c29b70638852a264acc5bf804bf8800/ctranslate2-4.8.2-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:5385f15493e7b6f41377da83d0d5afeb8e0f36188260722f3800c3c7121455af", size = 11931717, upload-time = "2026-08-31T19:37:21.897Z" },
{ url = "https://files.pythonhosted.org/packages/53/46/bfa42114fd583b0ef30b67113486ab72d3ff466e60a824739257bcc533bd/ctranslate2-4.8.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:781f835da7fdc4adcc38f9d674dbe9b37eaf3c9aee3b2bc17d684342ff59d549", size = 16894628, upload-time = "2026-08-31T19:37:24.328Z" },
{ url = "https://files.pythonhosted.org/packages/01/23/d72e70cac2b7c5c832a629c380235b53b20fac8c0921856bcf625b08ba44/ctranslate2-4.8.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:554a0b2abf098a11c66ef7930c0a05b3de683a455e8ea3f2e6ab6966aaabc136", size = 39555908, upload-time = "2026-08-31T19:37:27.948Z" },
{ url = "https://files.pythonhosted.org/packages/4e/23/e3b5322ff7368fcbed181ea4c209149416e7940b5b04971d5ee4084afe1a/ctranslate2-4.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:d94421d565d0de61c032998f737a18942b0f2bef40c0424b1846ec6f67300105", size = 19222069, upload-time = "2026-08-31T19:37:31.531Z" },
{ url = "https://files.pythonhosted.org/packages/2c/00/3f5d12d94daa22ddf51de0252313476c0f8fca8b1c7776592953764a8b85/ctranslate2-4.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a40b190248389e1adb38ff90407937131c6bb9d7f9771f29ca1dca01946ff252", size = 1271572, upload-time = "2026-08-31T19:37:33.476Z" },
{ url = "https://files.pythonhosted.org/packages/b4/b5/fde838502472462f2c8fd9e8fe17e734854bbe1047452c28d4fbe3eab7b2/ctranslate2-4.8.2-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:2778eaf89340062feb0d2d3dd35bedb14644673ccbce8934782dc8d093b2cab9", size = 11931591, upload-time = "2026-08-31T19:37:35.016Z" },
{ url = "https://files.pythonhosted.org/packages/a6/c7/cf22407330c90c1f3ef642ece11ff6bfe686509e995b95545951edf29117/ctranslate2-4.8.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:249b35f575adf8dd11e6ba43b212d5cbfc845d1140abc610c2872eef1ffb3481", size = 16895415, upload-time = "2026-08-31T19:37:37.342Z" },
{ url = "https://files.pythonhosted.org/packages/89/8b/051962470b8e9df1a43f0c9c7a03ce1c8a279db76be4e6a6fcb5171d1d29/ctranslate2-4.8.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:633f19d1ff8d053a8b179ca742b242002ac7882a2ab44e319c6efcde27d586a4", size = 39555517, upload-time = "2026-08-31T19:37:40.87Z" },
{ url = "https://files.pythonhosted.org/packages/c2/fc/a9e9e0ce1c0a29bc4c17bf56ccb4274293c0bbb2b8aae561727d82fcb0ca/ctranslate2-4.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:399c20a7336b6358f69ce3c615e090eabc08f29735a079fd1ac1e464119e0861", size = 19222061, upload-time = "2026-08-31T19:37:43.754Z" },
{ url = "https://files.pythonhosted.org/packages/72/b4/28111e86927b2490388d8e0b79ec83db38f2a81bf452318f24563b42e205/ctranslate2-4.8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e32ae5e625a8999749745de43c2de5c12ceebb6d0d9cc9a3a956e8912bd68edb", size = 1272406, upload-time = "2026-08-31T19:37:45.671Z" },
{ url = "https://files.pythonhosted.org/packages/03/07/444fccd37ba9e3d84dcb985fc174c82636e7a8c69b47bb0c1bf566045d37/ctranslate2-4.8.2-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:980f13e1d41987908af945861081158fbcb2983f4f9399514adc8c87378b5587", size = 11932080, upload-time = "2026-08-31T19:37:47.24Z" },
{ url = "https://files.pythonhosted.org/packages/f9/c5/c8d91fa1848f9bc09e943337b9c7ebd20d77f1344f5d595d10979f5e59d4/ctranslate2-4.8.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e866ccf5f5668f7302c39b535d19d7e5811f3f00d6246faf6ed6fa070fa1af3", size = 16884555, upload-time = "2026-08-31T19:37:50.337Z" },
{ url = "https://files.pythonhosted.org/packages/14/0c/de0305052c2bb35979c4a6c668c7aa6f139c10bc0939833450b462cef829/ctranslate2-4.8.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:573dcf5d96034dd9bcf7ee0db1657af490a52292ffc77dfb4b104da1e93322ad", size = 39521307, upload-time = "2026-08-31T19:37:53.623Z" },
{ url = "https://files.pythonhosted.org/packages/f2/86/03386a60b634a34c820e00f82bec808d38841cc2c007ec6b797892a9a5b5/ctranslate2-4.8.2-cp314-cp314-win_amd64.whl", hash = "sha256:00bc9f44172d05bd2becefe757d2397ab3292fad1695169f9ebb9c16eb497ce2", size = 19478465, upload-time = "2026-08-31T19:37:56.488Z" },
{ url = "https://files.pythonhosted.org/packages/33/46/f25a66e905d3ce13aba221e6e7d63e24acd2539e48549675e3e0527f9ffe/ctranslate2-4.8.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:58b1a78d050a7f281907b8acfe6f0cdbbf2feaf7464f38eb05b846f83fbaa297", size = 1295017, upload-time = "2026-08-31T19:37:58.379Z" },
{ url = "https://files.pythonhosted.org/packages/28/c2/0fc88102f448aee8fa704fec13e568f716df11aa701e901b2e401aa5d8ab/ctranslate2-4.8.2-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:70e2388d31dfb59968e6e011d01a6fc6bf802c0e1ad920a8d2a413f1c9da02ae", size = 11953132, upload-time = "2026-08-31T19:38:00.199Z" },
{ url = "https://files.pythonhosted.org/packages/62/23/f859ee8af1c6366795d59979a7e941a397823cc38585f5731c34a66f1048/ctranslate2-4.8.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32b9d0e984aa17a0da9b37bb7e4dcc638e898a2c32de08d5a4db53f15f640faa", size = 16872477, upload-time = "2026-08-31T19:38:02.641Z" },
{ url = "https://files.pythonhosted.org/packages/63/87/b964f427fcdfad983977859386007e1367eb340f747f63b6c0001cf78905/ctranslate2-4.8.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd9ab8b521230e8e962a58362b1ccde582806e781af8c1b9d7b244f3024468b1", size = 39491894, upload-time = "2026-08-31T19:38:06.365Z" },
{ url = "https://files.pythonhosted.org/packages/6a/39/9d316f00f184cea15e807a977df5bc76fc8f27f81246015f12083f42cd1c/ctranslate2-4.8.2-cp314-cp314t-win_amd64.whl", hash = "sha256:f6f0b576c247984d3fc299a372ccc9319b668d6d25b0539f3c861beba15d0504", size = 19500896, upload-time = "2026-08-31T19:38:09.111Z" },
]
[[package]]
name = "cuda-bindings"
version = "13.4.1"
@@ -253,6 +342,22 @@ version = "0.6.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" }
[[package]]
name = "faster-whisper"
version = "1.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "av" },
{ name = "ctranslate2" },
{ name = "huggingface-hub" },
{ name = "onnxruntime" },
{ name = "tokenizers" },
{ name = "tqdm" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/99/49ee85903dee060d9f08297b4a342e5e0bcfca2f027a07b4ee0a38ab13f9/faster_whisper-1.2.1-py3-none-any.whl", hash = "sha256:79a66ad50688c0b794dd501dc340a736992a6342f7f95e5811be60b5224a26a7", size = 1118909, upload-time = "2025-10-31T11:35:47.794Z" },
]
[[package]]
name = "filelock"
version = "3.32.7"
@@ -262,6 +367,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/15/df/31098c5aeb4d966b553641472bd55fcf5fdfac953549894b8a765ba44e91/filelock-3.32.7-py3-none-any.whl", hash = "sha256:65ff0d0190ea42038b32bda4b77834fb05be2cad4c5b9b01aa4dfb3614536e52", size = 100157, upload-time = "2026-09-16T00:24:19.543Z" },
]
[[package]]
name = "flatbuffers"
version = "25.12.19"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" },
]
[[package]]
name = "fsspec"
version = "2026.7.0"
@@ -280,6 +393,43 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]]
name = "hf-xet"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/ab/522a2ab67f27971a9d48ca666d4fca85ef7d5282d142e31fd087e27b1bbe/hf_xet-1.6.0.tar.gz", hash = "sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef", size = 920527, upload-time = "2026-08-03T22:33:13.243Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/41/62/3c062f593bd92ef4e77a0ef39541e3d82a0a1d3947c8a777a02a13a27828/hf_xet-1.6.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:70cbb9c896901600128cb9b6f06e132954fbede1db30f31f7c6c63f84cb7c31d", size = 4074584, upload-time = "2026-08-03T22:32:47.364Z" },
{ url = "https://files.pythonhosted.org/packages/bb/1e/c0ad437dd267a8e435bef594acf781bbc3874ff0b6435b4962d03ecf7cc4/hf_xet-1.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:23379c2f9ec8696d952b16414a2bae72cad86a52df869b050698ba60f538c675", size = 3867381, upload-time = "2026-08-03T22:32:49.049Z" },
{ url = "https://files.pythonhosted.org/packages/d5/ee/7c0d7b6ab336167531b1c30af2af003f054af4c749becbd7209ae33a77c3/hf_xet-1.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f7278c05c22fd60cb436cda1269649b3e81db65ecdc8496e5e164aa4143e7b", size = 4453982, upload-time = "2026-08-03T22:32:50.568Z" },
{ url = "https://files.pythonhosted.org/packages/63/06/ad8eab1c9525246650cbaa821caa3cdbaca734ab1a5b8c91bea09cbd8d69/hf_xet-1.6.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:948f15d3a9545cfe5932f6bd8b440f6ae630aee108f14b7bd6c561f7c2dcc522", size = 4249445, upload-time = "2026-08-03T22:32:52.391Z" },
{ url = "https://files.pythonhosted.org/packages/d8/26/1eee8aedb0dafc1ab9717dc9ac602cde33361b232dc06803f1f6ed18b58c/hf_xet-1.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5153e6bb103ad49d6ea9f1b2e230db5a2ea32551ad09a706d2f61d7c7c80d80e", size = 4451099, upload-time = "2026-08-03T22:32:54.114Z" },
{ url = "https://files.pythonhosted.org/packages/67/57/0b88af1f194ab6c9c650547d9cc06bfeaab836ae4dcdb331676bfb8be95a/hf_xet-1.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:35cec30d75c6f9eb9c16a77cef68e85a103b72e24d4b473714ec9ff06428bab9", size = 4664712, upload-time = "2026-08-03T22:32:55.547Z" },
{ url = "https://files.pythonhosted.org/packages/53/a0/26b717a9d1840e8abf48dcec64b5ed8fbe472671d38ad28d30e147132b33/hf_xet-1.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5789835d7c6bc9436962853192082374297fb72d7eff7e7762ec25ceb7e25338", size = 4025906, upload-time = "2026-08-03T22:32:57.391Z" },
{ url = "https://files.pythonhosted.org/packages/49/f6/4a9966633c6fef83af997e2cff68ec1963676d412bdfd096df2a93b8e185/hf_xet-1.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:75765820ce4700db3750c94acc8fe27c5fae4c9ec000a0dbac3ca082acf97765", size = 3849221, upload-time = "2026-08-03T22:32:59.123Z" },
{ url = "https://files.pythonhosted.org/packages/a2/50/7afa2c9c787405864fc47a0d1bbc02c62e9101947ed43c1f43899fc7d91d/hf_xet-1.6.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:633dc0cd71d32da58ab8c03ad38e2fac452c15c2b0a2866ebf6ededfe0a5061d", size = 4071729, upload-time = "2026-08-03T22:33:00.721Z" },
{ url = "https://files.pythonhosted.org/packages/4b/69/55b8dcf636142ae660fec1869fcac14c4da2e8412e14d6eee1523be77e9f/hf_xet-1.6.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:f0906082d9932ae0c0057fa194041c22b4e2cdb46b2592ef3b91f020d62a081a", size = 3876287, upload-time = "2026-08-03T22:33:02.251Z" },
{ url = "https://files.pythonhosted.org/packages/67/4e/a28359bf1c1ecf11eba22123168c138698f7cb576ac678f5a2e16cd5da08/hf_xet-1.6.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f", size = 4464663, upload-time = "2026-08-03T22:33:03.802Z" },
{ url = "https://files.pythonhosted.org/packages/9a/69/1f0cbc2fb22ae6082d094f743d1b8945a3f36f6089cb95f42b7ee348cda7/hf_xet-1.6.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0e6e21fa3cdfcdcd76748564bf593870a5e013f47d97cf10aed63aa222cff5b7", size = 4262538, upload-time = "2026-08-03T22:33:05.287Z" },
{ url = "https://files.pythonhosted.org/packages/d1/3a/4f4f2301ade26e404462d3336fa11f7958d914cabbabdd6e03c3c5d5658c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4fc74352a17015bd0ee90038bc9efe38db894cde45f268b6712b04fce8cd0acb", size = 4460520, upload-time = "2026-08-03T22:33:06.81Z" },
{ url = "https://files.pythonhosted.org/packages/ab/5f/311725e2a905534dfee2dcb5b08414f249147f1f12252bfc2bd24caa075c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c", size = 4675937, upload-time = "2026-08-03T22:33:08.616Z" },
{ url = "https://files.pythonhosted.org/packages/98/b7/8c59a66d15205024662f1d66968136f13893f96df1ddc5087e2e281fc95f/hf_xet-1.6.0-cp38-abi3-win_amd64.whl", hash = "sha256:fb4fadde1b2b70bf4c0c14a6dccbe7194b1c28947fefd5bbe3fed9d940676c3b", size = 4033128, upload-time = "2026-08-03T22:33:10.171Z" },
{ url = "https://files.pythonhosted.org/packages/73/63/ca511b6f802f28cf3489b280fe77475bcca8de85e81a6299d7916b5b5555/hf_xet-1.6.0-cp38-abi3-win_arm64.whl", hash = "sha256:3dc3e35441ba395006af5aaacc40ef2e603c51ef46c3530b9156185f00935ea3", size = 3859359, upload-time = "2026-08-03T22:33:11.725Z" },
]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]]
name = "httpcore2"
version = "2.13.0"
@@ -293,6 +443,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/0d/117a771a2bb91df334b66bf4da14cd02f21aefbcfe53180f336ce55e8f90/httpcore2-2.13.0-py3-none-any.whl", hash = "sha256:35ae5be347aa40467b4a5dc032ac67ebb6d27189fc97e8cebcf99616f6a1bb9e", size = 83162, upload-time = "2026-09-14T14:18:02.529Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]]
name = "httpx2"
version = "2.13.0"
@@ -319,6 +484,26 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" },
]
[[package]]
name = "huggingface-hub"
version = "1.31.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "filelock" },
{ name = "fsspec" },
{ name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
{ name = "httpx" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "tqdm" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9b/f0/61159db90b5cd275d55516fe27920828e7d3be4053fdbdb27c3f70e5f1ef/huggingface_hub-1.31.0.tar.gz", hash = "sha256:f8e9e710a210613fa5d0f26bba6da05ef4aef9fba5a0f23f508f5ac4d08b6f90", size = 968039, upload-time = "2026-09-10T10:27:22.724Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c3/7f/3f886a625043b77312b80da2f2bf00b5ecbf5a73061af1aa0259cd258c9d/huggingface_hub-1.31.0-py3-none-any.whl", hash = "sha256:9dbb6a503cbe2494ea666695207e7262d410659e09134059deb83e5480864667", size = 798313, upload-time = "2026-09-10T10:27:20.798Z" },
]
[[package]]
name = "idna"
version = "3.19"
@@ -737,6 +922,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" },
]
[[package]]
name = "nvidia-cublas-cu12"
version = "12.9.2.10"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cuda-nvrtc-cu12" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/a2/c96163a0fff1839c0c9548bbdeae7b853b867009e33b9b9264adc238b1cf/nvidia_cublas_cu12-12.9.2.10-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:5572131a59c3eebeeb1c4c8144f772d49372c20124916e072a0e3fc30df421d5", size = 575012079, upload-time = "2026-04-08T18:51:47.303Z" },
{ url = "https://files.pythonhosted.org/packages/cb/c0/0a517bfe63ccd3b92eb254d264e28fca3c7cab75d07daea315250fb1bf73/nvidia_cublas_cu12-12.9.2.10-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:e4f53a8ca8c5d6e8c492d0d0a3d565ecb59a751b19cfdaa4f6da0ab2104c1702", size = 581240110, upload-time = "2026-04-08T18:52:31.532Z" },
{ url = "https://files.pythonhosted.org/packages/20/e2/fc9a0e985249d873150276d5afb02e39a66817fedbf1a385724393e505ed/nvidia_cublas_cu12-12.9.2.10-py3-none-win_amd64.whl", hash = "sha256:623f43027d40d44ceadf0043f002bd25cf353e8f13ce90b9a87057019f560661", size = 553162896, upload-time = "2026-04-08T18:53:10.035Z" },
]
[[package]]
name = "nvidia-cuda-cupti"
version = "13.0.85"
@@ -755,6 +953,16 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" },
]
[[package]]
name = "nvidia-cuda-nvrtc-cu12"
version = "12.9.86"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/85/e4af82cc9202023862090bfca4ea827d533329e925c758f0cde964cb54b7/nvidia_cuda_nvrtc_cu12-12.9.86-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:210cf05005a447e29214e9ce50851e83fc5f4358df8b453155d5e1918094dcb4", size = 89568129, upload-time = "2025-06-05T20:02:41.973Z" },
{ url = "https://files.pythonhosted.org/packages/64/eb/c2295044b8f3b3b08860e2f6a912b702fc92568a167259df5dddb78f325e/nvidia_cuda_nvrtc_cu12-12.9.86-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:096d4de6bda726415dfaf3198d4f5c522b8e70139c97feef5cd2ca6d4cd9cead", size = 44528905, upload-time = "2025-06-05T20:02:29.754Z" },
{ url = "https://files.pythonhosted.org/packages/52/de/823919be3b9d0ccbf1f784035423c5f18f4267fb0123558d58b813c6ec86/nvidia_cuda_nvrtc_cu12-12.9.86-py3-none-win_amd64.whl", hash = "sha256:72972ebdcf504d69462d3bcd67e7b81edd25d0fb85a2c46d3ea3517666636349", size = 76408187, upload-time = "2025-06-05T20:12:27.819Z" },
]
[[package]]
name = "nvidia-cuda-runtime"
version = "13.0.96"
@@ -764,6 +972,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" },
]
[[package]]
name = "nvidia-cudnn-cu12"
version = "9.26.0.51"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cublas-cu12" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/ae/412ef5533642e63f360db552235337d28c00ddc1dfca807a6ddae95e939c/nvidia_cudnn_cu12-9.26.0.51-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:24458a34856d6b5e83aa9b7041e92a789f0ef390f4d881529fb986df6aecca60", size = 776301552, upload-time = "2026-09-10T16:44:35.71Z" },
{ url = "https://files.pythonhosted.org/packages/18/d2/ac670e921b185ba348a7ba7dd490ef2aad9c3e1918a87553567be86e37f3/nvidia_cudnn_cu12-9.26.0.51-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ce8603d4ea88d134be5a92c5e0833d513bea1ce0ee4bbc5d649fcb8718d51cbf", size = 770488318, upload-time = "2026-09-10T16:47:14.978Z" },
{ url = "https://files.pythonhosted.org/packages/c5/ee/baebebf270df5a57830e40879b4016de47ca43961095cf18b7749452150f/nvidia_cudnn_cu12-9.26.0.51-py3-none-win_amd64.whl", hash = "sha256:010abb90f513fc6e2b6e9278d56f594b87922fff80737434ab415da609db8311", size = 746465599, upload-time = "2026-09-10T16:50:17.867Z" },
]
[[package]]
name = "nvidia-cudnn-cu13"
version = "9.24.0.43"
@@ -877,6 +1098,44 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" },
]
[[package]]
name = "onnxruntime"
version = "1.30.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "flatbuffers" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "packaging" },
{ name = "protobuf" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/73/b6/e2fa6dfc8c3cd19639d452c3120fb0e02f5176b4e24ecabb24b5362be725/onnxruntime-1.30.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f36c87ec504f4f328e968ee322e4ba9682abe66280b3425625afcbed5333fed7", size = 21533037, upload-time = "2026-09-10T16:31:03.582Z" },
{ url = "https://files.pythonhosted.org/packages/4f/1a/4d197d2676a866f6239cd2d06a95b6db40d47dc29d5bec9fb1493a13dd94/onnxruntime-1.30.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bac3aee7b40acd805659fd81a2acd6096312d5b0f014c97ddfe75fb4c1644627", size = 21332697, upload-time = "2026-09-10T16:31:08.118Z" },
{ url = "https://files.pythonhosted.org/packages/4f/2e/4c96278a99140d0307ccda6fd51e6e9d1ca7c9f6fdd1fa96ff375acfc713/onnxruntime-1.30.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fd54b314ea385bcecac69ab431f020ba503e3878dad4ebb645fec5a24b041242", size = 23561046, upload-time = "2026-09-10T16:31:11.494Z" },
{ url = "https://files.pythonhosted.org/packages/66/68/b8aaade8c1883f569228f4b57201cce922bb7d4af6f977d619520a733430/onnxruntime-1.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:0edd0145a6e3fce8a1276491dc784d615e3c58bcb952c9b4e5c876d5c6a12ad7", size = 14309136, upload-time = "2026-09-10T16:31:14.181Z" },
{ url = "https://files.pythonhosted.org/packages/96/f1/fbda6fb08ac4db6c5c6f7c3e128e4d8fb398b52fe2fc3282c45cadedd18b/onnxruntime-1.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:16506cfa5d218850f2b46e0edce1b952121f2f84f8e0074dafe15f4787f0c8fb", size = 14167408, upload-time = "2026-09-10T16:31:16.505Z" },
{ url = "https://files.pythonhosted.org/packages/31/6f/48169f2e62b405bff5053cbd1d73fb5ce41ef7ecd13bb3bfcc191e689b8a/onnxruntime-1.30.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:001ed726c9bd5e2bc92faade7d37d889e9606a350b7d5529f0227df2e3bb57fd", size = 21544867, upload-time = "2026-09-10T16:31:19.876Z" },
{ url = "https://files.pythonhosted.org/packages/16/bd/cbc5b8f91963689fdd622f463508c01d0aa95d3f944747b1e0b1eb2160b8/onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:6c32a000d5139a38ba9349030b0032e3331acb559d596b22738d9d2b343a2b83", size = 21345202, upload-time = "2026-09-10T16:31:23.361Z" },
{ url = "https://files.pythonhosted.org/packages/34/35/e7f862dbacbc99fadd9b14a614e49c99bf0f35fd9927a82f096e3de33531/onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:fa688e7891a6aa206636fe7372e27ee75fd17713289f6b4fc7b190e0a7de9328", size = 23585654, upload-time = "2026-09-10T16:31:26.65Z" },
{ url = "https://files.pythonhosted.org/packages/a6/13/0f1699f6de549c9324bc9112a2a85b14c517904cd11b562a654643b755a1/onnxruntime-1.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3501472571f1b1eee50e017851e7929f5ea37312d2d8c2494a19e8fc58b4a38", size = 14311470, upload-time = "2026-09-10T16:31:31.273Z" },
{ url = "https://files.pythonhosted.org/packages/a2/17/02b13e5f51461f0453b18ab854e2d0bc1b6ec353241b05a2ee6b79e35d87/onnxruntime-1.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:dc4c706f1935ebb62356e6a095b047859badd854482c40560888e95c328ed262", size = 14175072, upload-time = "2026-09-10T16:31:34.541Z" },
{ url = "https://files.pythonhosted.org/packages/f0/75/508454c5d01f31641dabc597fe559594c931a520a2673031179319d0afd8/onnxruntime-1.30.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:05e4fc41711d1f4abd19a9124b5be7a65a506cb2670a7f14b16162ef13c58134", size = 21544990, upload-time = "2026-09-10T16:31:37.685Z" },
{ url = "https://files.pythonhosted.org/packages/89/06/e603c71f43f4fe3fd156a053af79cbed6e27a2c649f0988a67d97fedd39f/onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5327cf6aa15a02bad805fac8bd6882a62571e8b72f6f2938a8f37e6bd1966ce9", size = 21344996, upload-time = "2026-09-10T16:31:40.915Z" },
{ url = "https://files.pythonhosted.org/packages/f1/a1/ede48ab5dc54907a2999362777f541e132639fb06628ded1932058aa8a36/onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:86f940afc801ea9681a4da8af84fbe95e1d9ea7d80903952cc1bfad54faad38f", size = 23585560, upload-time = "2026-09-10T16:31:44.941Z" },
{ url = "https://files.pythonhosted.org/packages/3c/dd/c57c529dbc6dd55eca24b12cfbeab1b6a690de72083824eca689085f55b0/onnxruntime-1.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:4b63041bd623a9a9ac5e353948436c6fa7f43edd12d6b4a4ebc340bca959ba93", size = 14311378, upload-time = "2026-09-10T16:31:48.034Z" },
{ url = "https://files.pythonhosted.org/packages/11/2f/ef00b45b911e7f2115273a24ed1b43e73c8c6c9ec17f9bfa13f8492581f8/onnxruntime-1.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:c389b6887fc95e0fcb80e89b156bc2cb18e662c29df55e9326fe64140e7d7b4f", size = 14175134, upload-time = "2026-09-10T16:31:50.799Z" },
{ url = "https://files.pythonhosted.org/packages/92/0a/284fd6fe701c9a8aff39dbc119c37582ca40e9cff1ea05425a7cc8606a02/onnxruntime-1.30.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e5224ba2b00284cb1c48b3edcd303c109245d66df9ec1b861858fd6de672a38e", size = 21349453, upload-time = "2026-09-10T16:31:53.898Z" },
{ url = "https://files.pythonhosted.org/packages/d6/72/4f4466f8fa1ec267a9ef5e2f3bd175c203d3da0facbdf4049dc93abbe91c/onnxruntime-1.30.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3f9e002417f1e3bbb31ed43dafa4f22ca1b2b68832244fdee192fcaa1ae19bca", size = 23579837, upload-time = "2026-09-10T16:31:56.82Z" },
{ url = "https://files.pythonhosted.org/packages/6a/03/05c9a9234688757d2876ddecf80bba908561ee11debf125bc1a427ae6f48/onnxruntime-1.30.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:8b6169c16a48429890d2f4a0c774ebf54dfe9066a998514aad0518a16d398547", size = 21545313, upload-time = "2026-09-10T16:31:59.654Z" },
{ url = "https://files.pythonhosted.org/packages/c6/bc/1069e58b24779ba9d2fd479db5ecb3a15a6f49b585107c898819c0789558/onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:d2184fddb6798136e7c478244391ca82443f5c757f59f15bb9e5ad2da5e03175", size = 21346830, upload-time = "2026-09-10T16:32:02.275Z" },
{ url = "https://files.pythonhosted.org/packages/f1/38/8138eed225c5bc6ddfc05879ecac7dacc63c34b9b6f99be72839c1f6dc49/onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:8b611d24db2954545ce6bd9acd4670183cb368e4642450de7a9ab6474eb374ec", size = 23586333, upload-time = "2026-09-10T16:32:04.996Z" },
{ url = "https://files.pythonhosted.org/packages/4d/0a/748b86000fbf9518b5dfc5bdc1b924eeaca976203740fd9d30649e568ee6/onnxruntime-1.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:3bdd1752a8502ac1a7ccc6e878d16db6943df574d54ed7f01058a6806ae05be4", size = 14675319, upload-time = "2026-09-10T16:32:07.5Z" },
{ url = "https://files.pythonhosted.org/packages/2f/db/db8e4c0cf6f70f1311060560630fd21763648e01dfbba5195a2592c052ef/onnxruntime-1.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:83d543843cbd352cfa9996a6c7b92f8a480f114a18f95ff2a1acaf6921e85b6d", size = 14569360, upload-time = "2026-09-10T16:32:09.896Z" },
{ url = "https://files.pythonhosted.org/packages/24/0a/ec0a9d656e39b43887c378a5388b20c3b1e1ee43bded84580b0936466694/onnxruntime-1.30.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:265de607ba6f9814264e1d5d413fa7069d70f48e3049b5e460b3b04bdfcef294", size = 21348433, upload-time = "2026-09-10T16:32:12.772Z" },
{ url = "https://files.pythonhosted.org/packages/91/f0/40f74b7c00077e1e25627067ed98a70df1fef5c0e21b82849190d312554e/onnxruntime-1.30.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:67ad7f03433b6462c627d0f555dece80e6a26bc71e8542ced35cebd32142d1b7", size = 23579340, upload-time = "2026-09-10T16:32:15.532Z" },
]
[[package]]
name = "openai"
version = "3.14.1"
@@ -912,6 +1171,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "protobuf"
version = "7.36.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/86/73/f66c748df06e7fe24e658eddd600d19c4b40bad836c97ce2d0ad9851fb6b/protobuf-7.36.1.tar.gz", hash = "sha256:d0f6470f0ce2b84e3feaea2d4b816378b37ba4d4aa08a274305373de93e2d524", size = 512499, upload-time = "2026-08-31T22:40:04.667Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/6c/3a54a58f2948b0f485df9ecdd06590f15d0a7abf46a89d50c3de709ff4ff/protobuf-7.36.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:3cf2ee25d006cee57294a1196ea43b37feb78e0dcd1e8af5c1aeddb777655aca", size = 456046, upload-time = "2026-08-31T22:39:56.865Z" },
{ url = "https://files.pythonhosted.org/packages/6e/08/9f9548793c771095245c0eeaf0c84b76b16a5ef26158043d456f551344a0/protobuf-7.36.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:43d3d37b1eb24c113b9b7d02008cac44e423f00b611b7781ae998d7623972969", size = 344226, upload-time = "2026-08-31T22:39:58.263Z" },
{ url = "https://files.pythonhosted.org/packages/fe/51/1bdbd612fa3c51e42ea6f45d05e84dc748ddcd2663e1aa1e89a00a33facd/protobuf-7.36.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:39c518c05586c016d7874ff6079ee115bcec1ea5fbb1d177fbf7867ef4c67e44", size = 357229, upload-time = "2026-08-31T22:39:59.198Z" },
{ url = "https://files.pythonhosted.org/packages/22/df/c799fe7a05ef16ba853a59db01f3a2c5f7d0676469589ccc4874f76a2a88/protobuf-7.36.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:97198b77e369a0abd8e262b8f6c7266c55ddb796a3a12c76d7b8881188ed83aa", size = 343228, upload-time = "2026-08-31T22:40:00.179Z" },
{ url = "https://files.pythonhosted.org/packages/3d/33/d4724ec5d86d496fe4108e220618aa0837ad3423a7af7ccdd9684ccc77c8/protobuf-7.36.1-cp310-abi3-win32.whl", hash = "sha256:0b53ce95272aad50ad25d7ff03373743209822e8ba42ea7fad27d2bee1547d00", size = 443002, upload-time = "2026-08-31T22:40:01.32Z" },
{ url = "https://files.pythonhosted.org/packages/db/37/155788a0d8daded960375af604202805308169f9b859419ea0aa370946e2/protobuf-7.36.1-cp310-abi3-win_amd64.whl", hash = "sha256:51139351435d9b43d88a55eaa49fb6f737fbb478fb0cbf2cf694d1a04a9d3363", size = 456518, upload-time = "2026-08-31T22:40:02.494Z" },
{ url = "https://files.pythonhosted.org/packages/39/ca/c47f91d3cab175b01fd8c4f0d80fdf8613be876cc616e66ad281a59c5ddf/protobuf-7.36.1-py3-none-any.whl", hash = "sha256:7d951e46b3f963d6c264c367c437921de9d5aedd9c3f9612b9077736b4e3ad5c", size = 179813, upload-time = "2026-08-31T22:40:03.54Z" },
]
[[package]]
name = "pycparser"
version = "3.0"
@@ -1187,6 +1461,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/b5/80c4e5aafeb8ac777cf5a1becfe87ac7197c35051df00b5f7819787e09d5/shiboken6-6.11.2-cp310-abi3-win_arm64.whl", hash = "sha256:97c49432488df958f0308736f3d15b6915d8826fc380af17bb4b9ec5c4a5e81b", size = 1784669, upload-time = "2026-08-18T06:37:21.046Z" },
]
[[package]]
name = "silero-vad"
version = "6.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
{ name = "torch" },
{ name = "torchaudio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/32/d3/e31f526482782764aa4f70e20fd4545cf2e4a81a60b6fb0f089f6d107991/silero_vad-6.2.1.tar.gz", hash = "sha256:b23062b0e39fad17b1266fc23c1e7b4290219dbe82ce08510889e32f681f4b3b", size = 28913811, upload-time = "2026-02-24T08:41:59.329Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/2b/48566f29a8b53d856ceb1994f209122749b3fda0a733a07e82047257de7a/silero_vad-6.2.1-py3-none-any.whl", hash = "sha256:09de93c4d874bb19c53e62a47dd38be5f163cedad2b5599583231f2a84ef79cb", size = 9146242, upload-time = "2026-02-24T08:41:56.955Z" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
@@ -1224,6 +1512,33 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
]
[[package]]
name = "tokenizers"
version = "0.23.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "huggingface-hub" },
]
sdist = { url = "https://files.pythonhosted.org/packages/18/1e/bc6587c5ab643b2e17776cace9070a2ae73549c86bffac9934a600bf3c31/tokenizers-0.23.2.tar.gz", hash = "sha256:7f0f085686b9de0d0079e6f874ae053600db64c5d13049e0bbc0119926d25aac", size = 385745, upload-time = "2026-09-03T08:55:42.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/ed/8a443528baa6fac8dfe8c3b75b038c63ac92bb539bcabe311e227c718173/tokenizers-0.23.2-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:85a9a357a3764aecc904ee76bdaf8cf1ad8e5a67a1b929a487c4a39b49ed0e90", size = 3148852, upload-time = "2026-09-03T08:55:30.874Z" },
{ url = "https://files.pythonhosted.org/packages/67/49/22da045a91732384d3a3771816bf188dc5a1f702c32e635afa7c679c0bef/tokenizers-0.23.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:986670e43691469dcee610ea0f846f91a8f84e91fc6f7a48d4c064414c0ec2bf", size = 3101593, upload-time = "2026-09-03T08:55:28.587Z" },
{ url = "https://files.pythonhosted.org/packages/2e/4d/8f569ed49372a3ed8e57099bd515055fd48d7c95912c4307cda6973c2168/tokenizers-0.23.2-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a37039b5dfc4af84eb3ef0a92f4307e28936c8f9adccba2629d36f652e9bf7a2", size = 3516830, upload-time = "2026-09-03T08:55:14.741Z" },
{ url = "https://files.pythonhosted.org/packages/2a/de/e2f14c8919d5bf51874051d00d6c7b7e0e8bde6c6a2dbeddda7f642896ff/tokenizers-0.23.2-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b7e37ba198f24150f523e1242e83c4970de4a525480586be5dcc24d9add32c5", size = 3407975, upload-time = "2026-09-03T08:55:16.842Z" },
{ url = "https://files.pythonhosted.org/packages/c5/bd/93c69152d02ef06ce47aed8b2bf4952dcf733c935a62791873932b2934d9/tokenizers-0.23.2-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43e4f2071e3cc8d5d86421c874aebc82659bb51a68bcdef5a0da75ee89511ccb", size = 3748165, upload-time = "2026-09-03T08:55:24.769Z" },
{ url = "https://files.pythonhosted.org/packages/2d/b7/56b84b80bc96942bba8eb23751a9e8a1fce4faaf4390425e7083f721c98c/tokenizers-0.23.2-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:325fee2e0418a9dc6c9ecf736a5f5f0db7875183ace9549ae339da76f7a1fbb7", size = 4024165, upload-time = "2026-09-03T08:55:18.806Z" },
{ url = "https://files.pythonhosted.org/packages/9b/8a/0175e216f005c2fe08238292663aa41e4c802b216e71047a69a0e9fc6fa3/tokenizers-0.23.2-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:950d7c9426fa72406a0ffeacdbc0bb9985f5db20eb8b263f29c79aaf83105703", size = 3591899, upload-time = "2026-09-03T08:55:22.752Z" },
{ url = "https://files.pythonhosted.org/packages/2c/ca/ca6b93c7820df123b2662a9469e8facc826ccc94e98fdd0d615f6431e73a/tokenizers-0.23.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41c2f84d172449b4dadb9cdc508e3e364076613c35b16e76ecfe47a60d1e3305", size = 3386843, upload-time = "2026-09-03T08:55:26.584Z" },
{ url = "https://files.pythonhosted.org/packages/e9/a4/4f9106d317b14a80aefea9f0e3a8d07ef25f856a7607eb7f5ab894281fcb/tokenizers-0.23.2-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:12f0835dc2ee694746a76adf7b1567d4346a4a502ebe93fb1f5f80ea49799b78", size = 3577314, upload-time = "2026-09-03T08:55:20.825Z" },
{ url = "https://files.pythonhosted.org/packages/8d/6a/1552b70fb0d9ab074fd3fc961435d01364e79c9058481822c3af6e8d402c/tokenizers-0.23.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb2f9c8a24da020ea8c11a01a19c1c2547912d92121ae4a01cfbca46125dee40", size = 9967367, upload-time = "2026-09-03T08:55:33.188Z" },
{ url = "https://files.pythonhosted.org/packages/06/01/3ccb3a956c7528b2507b8a9714155c4baf86af593039db6ea375dd0c96c3/tokenizers-0.23.2-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f486f402f6f9abee5bb032553736813af0c710a86b2e0ca592634c55cea1f835", size = 9811886, upload-time = "2026-09-03T08:55:35.642Z" },
{ url = "https://files.pythonhosted.org/packages/fa/73/7038e612d48bda1599457f712f6bd3854eae1a9dc9c13aa47f835349db48/tokenizers-0.23.2-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:bef235815a067b2648caf6dcc7a71091b0b0fff9ee8057f6451eb9335fae52ef", size = 10146224, upload-time = "2026-09-03T08:55:38.391Z" },
{ url = "https://files.pythonhosted.org/packages/b5/d8/8e9e4e0b287a338d8f88976729628c9d22e8a54cfaf9777018a7f7cb58a0/tokenizers-0.23.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5c56bda1511921587789163e524d196ed8284174ac23abd7685d5ea8da6c4718", size = 10256304, upload-time = "2026-09-03T08:55:40.977Z" },
{ url = "https://files.pythonhosted.org/packages/f3/1f/c79a01f671a49728ebb0b61f7ff9ea45663b66cab40bc0858e9859b25c16/tokenizers-0.23.2-cp310-abi3-win32.whl", hash = "sha256:debf978920d93ba9c219bd67cc4bbfaf912c9039e41e7a28b91ec15e3728c95a", size = 2592809, upload-time = "2026-09-03T08:55:48.02Z" },
{ url = "https://files.pythonhosted.org/packages/db/f7/0a69ac6b82dbccf3f71add938a161c497952749294b8dd6dfe03a819dc40/tokenizers-0.23.2-cp310-abi3-win_amd64.whl", hash = "sha256:2e96f5699d5249c9c64aa8412e044f727aae3a4098cf830f9901ec1afc361cde", size = 2863236, upload-time = "2026-09-03T08:55:46.193Z" },
{ url = "https://files.pythonhosted.org/packages/d7/b0/dee84cb44175be1b4c35bd2f770727494e78f0bb38e571a623ade94dbebb/tokenizers-0.23.2-cp310-abi3-win_arm64.whl", hash = "sha256:e49c394456dd9985787fec76132438ba3fb8911f857b1bf3d40119f9292d41aa", size = 2729352, upload-time = "2026-09-03T08:55:44.345Z" },
]
[[package]]
name = "torch"
version = "2.14.0"
@@ -1267,6 +1582,49 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f8/c3/72ae1f02747b1f012e1975743e48cd608f83095d7f9ce58de78b79248b35/torch-2.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:731784e3914843c6bcc7aba3987ff7610ac57dbbc816a5d6b9b62e04c240a641", size = 124400194, upload-time = "2026-09-02T13:45:53.555Z" },
]
[[package]]
name = "torchaudio"
version = "2.11.0"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/94/77/0eec7f175d88f312296bd5b11c23bd58da37c1021f53da3db4df449ce3ee/torchaudio-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:492dd64645e9d0bb843e94f1d9a4d1e31426262ffc594fafecc1697df9df5eb9", size = 684142, upload-time = "2026-03-23T18:13:36.805Z" },
{ url = "https://files.pythonhosted.org/packages/b3/f9/6f7ebe071b44592c85269762b55b63ab0a091b5f479f73544738f7564a1e/torchaudio-2.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:73dab4841f94d888bc7c2aed7b5547c643edc974306919fe1adfb65d57cccf4b", size = 1626527, upload-time = "2026-03-23T18:13:39.011Z" },
{ url = "https://files.pythonhosted.org/packages/ac/70/17408e0d154d0c894537a88dcbadc48e8ad3b6e1ef4a1dabda5d40245ee0/torchaudio-2.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1a07ec72fd6f26a588c39b5f029e0130d16bb40bc4221635580bf8fb18fcbc80", size = 1771930, upload-time = "2026-03-23T18:13:37.963Z" },
{ url = "https://files.pythonhosted.org/packages/c9/75/b6d03fc75b409bdaec597274d1bdd4213db716ed16f6801386b31d59c551/torchaudio-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:bb59ba4452bbbe95d75ad3ef18df9824955625f36698ce9a5998a4a9f3c1ba1d", size = 328658, upload-time = "2026-03-23T18:13:44.545Z" },
{ url = "https://files.pythonhosted.org/packages/f1/b1/77658817acacd01a72b714440c62f419efc4d90170e704e8e7a2c0918988/torchaudio-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1cf1acc883bee9cb906a933572fed6a8a933f86ef34e9ea7d803f72317e8c1b", size = 684226, upload-time = "2026-03-23T18:13:40.023Z" },
{ url = "https://files.pythonhosted.org/packages/78/28/c7adc053039f286c2aca0038b766cbe3294e66fec6b29a820e95128f9ede/torchaudio-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:bc653defca1c16154398517a1adc98d0fb7f1dd08e58ced217558d213c2c6e29", size = 1626670, upload-time = "2026-03-23T18:13:42.162Z" },
{ url = "https://files.pythonhosted.org/packages/88/d8/d6d0f896e064aa67377484efef4911cdcc07bce2929474e1417cc0af18c2/torchaudio-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6503c0bdb29daf2e6281bb70ea2dfe2c3553b782b619eb5d73bdadd8a3f7cecf", size = 1771992, upload-time = "2026-03-23T18:13:33.188Z" },
{ url = "https://files.pythonhosted.org/packages/23/a8/941277ecc39f7a0a169d554302a1f1afd87c1d94a8aec828891916cea59a/torchaudio-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:478110f981e5d40a8d82221732c57a56c85a1d5895fb8fe646e86ee15eded3bd", size = 328663, upload-time = "2026-03-23T18:13:19.218Z" },
{ url = "https://files.pythonhosted.org/packages/fb/9e/f76fcd9877c8c78f258ee34e0fb8291fdb91e6218d582d9ca66b1e4bd4ae/torchaudio-2.11.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e3f9696a9ef1d49acc452159b052370c636406d072e9d8f10895fda87b591ea9", size = 679904, upload-time = "2026-03-23T18:13:28.329Z" },
{ url = "https://files.pythonhosted.org/packages/85/70/249c1498ebdad3e7752866635ec0855fc0dcf898beccda5a9d2b9df8e4d0/torchaudio-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:b034d7672f1c415434f48ef17807f2cce47f29e8795338c751d4e596c9fbe8b5", size = 1618523, upload-time = "2026-03-23T18:13:15.703Z" },
{ url = "https://files.pythonhosted.org/packages/4f/98/be13fe35d9aa5c26381c0e453c828a789d15c007f8f7d08c95341d19974d/torchaudio-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1c1101c1243ef0e4063ec63298977e2d3655c15cf88d9eb0a1bd4fe2db9f47ea", size = 1771992, upload-time = "2026-03-23T18:13:35.343Z" },
{ url = "https://files.pythonhosted.org/packages/e2/8b/2bbb3dca6ff28cba0de250874d5ef4fc2822c47a934b59b3974cff3219ef/torchaudio-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:986f4df5ed17b003dc52489468601720090e65f964f8bebccf90eb45bba75744", size = 328662, upload-time = "2026-03-23T18:13:18.308Z" },
{ url = "https://files.pythonhosted.org/packages/fe/ce/52c652d30af7d6e96c8f1735d26131e94708e3f38d852b8fa97958804dd8/torchaudio-2.11.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:bda09ea630ae7207384fb0f28c35e4f8c0d82dd6eba020b6b335ad0caa9fed49", size = 680814, upload-time = "2026-03-23T18:13:17.08Z" },
{ url = "https://files.pythonhosted.org/packages/06/95/1ad1507482e7263e556709a3f5f87fecd375a0742cdaf238806c8e72eaad/torchaudio-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:9fe3083c62e035646483a14e180d33561bdc2eed436c9ab1259c137fb7120b4a", size = 1618546, upload-time = "2026-03-23T18:13:29.686Z" },
{ url = "https://files.pythonhosted.org/packages/98/4c/480328ba07487eb9890406720304d0d460dd7a6a64098614f5aa53b662ca/torchaudio-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:13cff988697ccbad539987599f9dc672f40c417bed67570b365e4e5002bbd096", size = 1771991, upload-time = "2026-03-23T18:13:30.843Z" },
{ url = "https://files.pythonhosted.org/packages/3e/98/5d4790e2d6548768999acd34999d5aeefce8bcc23a07afaa5f03e723f557/torchaudio-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ed404c4399ad7f172c86a47c1b25293d322d1d58e26b10b0456a86cf67d37d84", size = 328661, upload-time = "2026-03-23T18:13:34.359Z" },
{ url = "https://files.pythonhosted.org/packages/39/fe/ffa618b4f0d9732d7df7a2fa2bd48657d896599bc224e5af3c70d46c546b/torchaudio-2.11.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:cc09cd1f6015b8549e7fe255fb1be5346b57e7fee06541d3f3dbb012d8c4715f", size = 679901, upload-time = "2026-03-23T18:13:25.472Z" },
{ url = "https://files.pythonhosted.org/packages/5c/54/f414d7b92dd0b3094a2409c95a97bd6c49aa0620da722a0e55462f9bd9cb/torchaudio-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:79fb3cb99169fd41bd9719647261402a164da0d105a4d81f42a3260844ec5e79", size = 1618527, upload-time = "2026-03-23T18:13:26.68Z" },
{ url = "https://files.pythonhosted.org/packages/a8/a8/bf2e1f6ce24c990192400ae49b4acc1a0d0295b6c6a06bceecdc46ce08de/torchaudio-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:00e9f71ab9c656f0abdb40c515bd65d4658ab0ad380dee27a2efd7d51dabd3d6", size = 1771995, upload-time = "2026-03-23T18:13:23.373Z" },
{ url = "https://files.pythonhosted.org/packages/83/6f/b0efb44e0bfe8dd4d78d76ae3be280354e1fb5c8631c782785d74cd8a7b1/torchaudio-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:1424638adb8bb40087bc7b6eb103e8e4fe398210f09076f33b7b5e61501b5d66", size = 328662, upload-time = "2026-03-23T18:13:32.243Z" },
{ url = "https://files.pythonhosted.org/packages/60/84/1c792b0b700eac9a96772cfd9f96c097b17bca3234a2fde3c64b8063660d/torchaudio-2.11.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:da2725e250866da42a12934c9a6552f65a18b7187fd7a6221387f0e605fb3b96", size = 679926, upload-time = "2026-03-23T18:13:24.452Z" },
{ url = "https://files.pythonhosted.org/packages/9a/a0/62a5842062f739239691f2e57523e0570dd06704ad987755f7644a3afa23/torchaudio-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:1be3767064364ae82705bdf2b15c1e8b41fea82c4cd04d47428a8684b634b6ed", size = 1618552, upload-time = "2026-03-23T18:13:21.09Z" },
{ url = "https://files.pythonhosted.org/packages/6d/89/c293d818f9f899db93bf291b42401c05ae29acfb2e53d5341c30ea703e62/torchaudio-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:67f6edac29ed004652c11db5c19d9debb5d835695930574f564efc8bdd061bba", size = 1771986, upload-time = "2026-03-23T18:13:22.153Z" },
{ url = "https://files.pythonhosted.org/packages/93/f7/ee5da8c03f1a3c7662c6c6a119f24a4b3e646da94be56dce3201e3a6ee9b/torchaudio-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:88fb5e29f670a33d9bac6aabb1d2734460cf6e461bde5cdc352826035851b16d", size = 328661, upload-time = "2026-03-23T18:13:20.1Z" },
]
[[package]]
name = "tqdm"
version = "4.70.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0d/ea/b2a5bd54b28a324dae8211928b2d730b6547500342c7e6c6dea08bd0a485/tqdm-4.70.1.tar.gz", hash = "sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4", size = 171846, upload-time = "2026-09-11T07:25:16.601Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl", hash = "sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73", size = 80199, upload-time = "2026-09-11T07:25:14.599Z" },
]
[[package]]
name = "triton"
version = "3.8.0"