Этап 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:
co-authored by
Claude Opus 5
parent
fec52dd1c2
commit
513d010c19
@@ -5,7 +5,8 @@
|
|||||||
- LLM: локальные модели (Ollama, LM Studio, llama.cpp) или OpenRouter — через единый OpenAI-совместимый API
|
- LLM: локальные модели (Ollama, LM Studio, llama.cpp) или OpenRouter — через единый OpenAI-совместимый API
|
||||||
- Текстовый чат со стримингом ответа
|
- Текстовый чат со стримингом ответа
|
||||||
- Озвучка ответов голосом Silero: фразы проговариваются по мере генерации, блоки кода пропускаются
|
- Озвучка ответов голосом Silero: фразы проговариваются по мере генерации, блоки кода пропускаются
|
||||||
- В планах: голосовой ввод (faster-whisper), слово активации
|
- Голосовой ввод по глобальной горячей клавише: faster-whisper на видеокарте, конец фразы по паузе (Silero VAD)
|
||||||
|
- В планах: слово активации
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
@@ -34,6 +35,17 @@ llm:
|
|||||||
Голос и модель задаются в секции `tts` конфига; озвучку можно выключить в меню значка.
|
Голос и модель задаются в секции `tts` конфига; озвучку можно выключить в меню значка.
|
||||||
Silero читает только кириллицу: числа переводятся в слова, латиница пропускается.
|
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`.
|
||||||
|
|
||||||
Закрытие окна сворачивает приложение в трей. Клик по значку открывает чат, в меню значка —
|
Закрытие окна сворачивает приложение в трей. Клик по значку открывает чат, в меню значка —
|
||||||
переключение провайдера, новый диалог и выход.
|
переключение провайдера, новый диалог и выход.
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,15 @@ description = "Минимальный голосовой AI-ассистент
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"faster-whisper>=1.2.1",
|
||||||
"num2words>=0.5.14",
|
"num2words>=0.5.14",
|
||||||
"numpy>=2.4.6",
|
"numpy>=2.4.6",
|
||||||
|
"nvidia-cublas-cu12>=12.9.2.10",
|
||||||
|
"nvidia-cudnn-cu12>=9.26.0.51",
|
||||||
"openai>=3.14.1",
|
"openai>=3.14.1",
|
||||||
"pyside6>=6.11.2",
|
"pyside6>=6.11.2",
|
||||||
"pyyaml>=6.0.3",
|
"pyyaml>=6.0.3",
|
||||||
|
"silero-vad>=6.2.1",
|
||||||
"sounddevice>=0.5.6",
|
"sounddevice>=0.5.6",
|
||||||
"torch>=2.14.0",
|
"torch>=2.14.0",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ from PySide6.QtWidgets import QApplication, QMessageBox, QSystemTrayIcon
|
|||||||
|
|
||||||
from agr_assistent import APP_NAME
|
from agr_assistent import APP_NAME
|
||||||
from agr_assistent.audio.player import AudioPlayer
|
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.config import ConfigError, data_dir, load_config
|
||||||
from agr_assistent.core.assistant import Assistant
|
from agr_assistent.core.assistant import Assistant
|
||||||
from agr_assistent.core.speech import Speaker
|
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.tts.silero import SileroTTS
|
||||||
from agr_assistent.ui.chat_window import ChatWindow
|
from agr_assistent.ui.chat_window import ChatWindow
|
||||||
from agr_assistent.ui.icons import state_icon
|
from agr_assistent.ui.icons import state_icon
|
||||||
@@ -44,15 +48,40 @@ def main() -> int:
|
|||||||
QMessageBox.critical(None, APP_NAME, "Системный трей недоступен")
|
QMessageBox.critical(None, APP_NAME, "Системный трей недоступен")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
models_dir = data_dir() / "models"
|
||||||
speaker = Speaker(
|
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:
|
if config.tts.enabled:
|
||||||
speaker.warm_up()
|
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))
|
app.setWindowIcon(state_icon(assistant.state))
|
||||||
window = ChatWindow(assistant)
|
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()
|
tray.show()
|
||||||
|
|
||||||
if config.ui.start_minimized:
|
if config.ui.start_minimized:
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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)
|
||||||
@@ -58,6 +58,26 @@ class TTSConfig:
|
|||||||
device: str
|
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
|
@dataclass
|
||||||
class UIConfig:
|
class UIConfig:
|
||||||
start_minimized: bool
|
start_minimized: bool
|
||||||
@@ -67,6 +87,8 @@ class UIConfig:
|
|||||||
class AppConfig:
|
class AppConfig:
|
||||||
llm: LLMConfig
|
llm: LLMConfig
|
||||||
tts: TTSConfig
|
tts: TTSConfig
|
||||||
|
stt: STTConfig
|
||||||
|
voice: VoiceConfig
|
||||||
ui: UIConfig
|
ui: UIConfig
|
||||||
path: Path
|
path: Path
|
||||||
|
|
||||||
@@ -139,6 +161,24 @@ def parse_config(data: dict[str, Any], path: Path) -> AppConfig:
|
|||||||
sample_rate=int(tts_data["sample_rate"]),
|
sample_rate=int(tts_data["sample_rate"]),
|
||||||
device=str(tts_data["device"]),
|
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"]))
|
ui = UIConfig(start_minimized=bool(data["ui"]["start_minimized"]))
|
||||||
except KeyError as exc:
|
except KeyError as exc:
|
||||||
raise ConfigError(f"{path}: отсутствует ключ {exc}") from exc
|
raise ConfigError(f"{path}: отсутствует ключ {exc}") from exc
|
||||||
@@ -150,7 +190,7 @@ def parse_config(data: dict[str, Any], path: Path) -> AppConfig:
|
|||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
f"{path}: провайдер '{llm.provider}' не описан в llm.providers (есть: {known})"
|
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:
|
def _expand_env(value: str) -> str:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from PySide6.QtCore import QObject, Signal, Slot
|
|||||||
|
|
||||||
from agr_assistent.config import LLMConfig
|
from agr_assistent.config import LLMConfig
|
||||||
from agr_assistent.core.speech import Speaker
|
from agr_assistent.core.speech import Speaker
|
||||||
|
from agr_assistent.core.voice import VoiceInput
|
||||||
from agr_assistent.llm.client import LLMClient, LLMError
|
from agr_assistent.llm.client import LLMClient, LLMError
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@@ -19,6 +20,8 @@ Message = dict[str, str]
|
|||||||
|
|
||||||
class AssistantState(Enum):
|
class AssistantState(Enum):
|
||||||
IDLE = "idle"
|
IDLE = "idle"
|
||||||
|
LISTENING = "listening"
|
||||||
|
RECOGNIZING = "recognizing"
|
||||||
THINKING = "thinking"
|
THINKING = "thinking"
|
||||||
SPEAKING = "speaking"
|
SPEAKING = "speaking"
|
||||||
|
|
||||||
@@ -55,11 +58,16 @@ class Assistant(QObject):
|
|||||||
_worker_done = Signal(int)
|
_worker_done = Signal(int)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, config: LLMConfig, speaker: Speaker, parent: QObject | None = None
|
self,
|
||||||
|
config: LLMConfig,
|
||||||
|
speaker: Speaker,
|
||||||
|
voice: VoiceInput | None,
|
||||||
|
parent: QObject | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._config = config
|
self._config = config
|
||||||
self._speaker = speaker
|
self._speaker = speaker
|
||||||
|
self._voice = voice
|
||||||
self._client: LLMClient | None = None
|
self._client: LLMClient | None = None
|
||||||
self._history: list[Message] = []
|
self._history: list[Message] = []
|
||||||
self._state = AssistantState.IDLE
|
self._state = AssistantState.IDLE
|
||||||
@@ -74,6 +82,12 @@ class Assistant(QObject):
|
|||||||
speaker.playback_started.connect(self._update_state)
|
speaker.playback_started.connect(self._update_state)
|
||||||
speaker.finished.connect(self._update_state)
|
speaker.finished.connect(self._update_state)
|
||||||
speaker.error_occurred.connect(self.error_occurred)
|
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
|
@property
|
||||||
def state(self) -> AssistantState:
|
def state(self) -> AssistantState:
|
||||||
@@ -104,6 +118,23 @@ class Assistant(QObject):
|
|||||||
def speech_enabled(self) -> bool:
|
def speech_enabled(self) -> bool:
|
||||||
return self._speaker.enabled
|
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:
|
def set_provider(self, name: str) -> None:
|
||||||
if name == self._config.provider:
|
if name == self._config.provider:
|
||||||
return
|
return
|
||||||
@@ -153,7 +184,9 @@ class Assistant(QObject):
|
|||||||
).start()
|
).start()
|
||||||
|
|
||||||
def cancel(self) -> None:
|
def cancel(self) -> None:
|
||||||
"""Прерывает и генерацию, и озвучку."""
|
"""Прерывает запись голоса, генерацию и озвучку."""
|
||||||
|
if self._voice is not None:
|
||||||
|
self._voice.cancel()
|
||||||
if self._generating:
|
if self._generating:
|
||||||
self._generation += 1
|
self._generation += 1
|
||||||
self._finish_reply()
|
self._finish_reply()
|
||||||
@@ -188,6 +221,13 @@ class Assistant(QObject):
|
|||||||
else:
|
else:
|
||||||
self._worker_done.emit(generation)
|
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)
|
@Slot(int, str)
|
||||||
def _on_worker_chunk(self, generation: int, piece: str) -> None:
|
def _on_worker_chunk(self, generation: int, piece: str) -> None:
|
||||||
if generation == self._generation:
|
if generation == self._generation:
|
||||||
@@ -220,7 +260,11 @@ class Assistant(QObject):
|
|||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def _update_state(self) -> None:
|
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
|
state = AssistantState.SPEAKING
|
||||||
elif self._generating:
|
elif self._generating:
|
||||||
state = AssistantState.THINKING
|
state = AssistantState.THINKING
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -42,6 +42,34 @@ tts:
|
|||||||
sample_rate: 48000
|
sample_rate: 48000
|
||||||
device: cpu
|
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 + клавиша (буква, цифра, f1–f24, 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:
|
ui:
|
||||||
# Запускаться сразу в трее, не показывая окно чата
|
# Запускаться сразу в трее, не показывая окно чата
|
||||||
start_minimized: false
|
start_minimized: false
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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']}"
|
||||||
@@ -25,7 +25,7 @@ from PySide6.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from agr_assistent import APP_NAME
|
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
|
from agr_assistent.ui.icons import state_icon
|
||||||
|
|
||||||
_ROLE_STYLES = {
|
_ROLE_STYLES = {
|
||||||
@@ -34,6 +34,11 @@ _ROLE_STYLES = {
|
|||||||
"error": ("Ошибка", "#e53935"),
|
"error": ("Ошибка", "#e53935"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_VOICE_BUTTON_TEXTS = {
|
||||||
|
AssistantState.LISTENING: "Готово",
|
||||||
|
AssistantState.RECOGNIZING: "Распознаю…",
|
||||||
|
}
|
||||||
|
|
||||||
# Во время стриминга перерисовываем историю не чаще, чем раз в N мс
|
# Во время стриминга перерисовываем историю не чаще, чем раз в N мс
|
||||||
_RENDER_INTERVAL_MS = 50
|
_RENDER_INTERVAL_MS = 50
|
||||||
|
|
||||||
@@ -85,13 +90,23 @@ class ChatWindow(QWidget):
|
|||||||
self._send_button.setMinimumWidth(100)
|
self._send_button.setMinimumWidth(100)
|
||||||
self._send_button.clicked.connect(self._on_send_clicked)
|
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 = QHBoxLayout()
|
||||||
header.addWidget(self._provider_label, 1)
|
header.addWidget(self._provider_label, 1)
|
||||||
header.addWidget(new_chat_button)
|
header.addWidget(new_chat_button)
|
||||||
|
|
||||||
|
buttons = QVBoxLayout()
|
||||||
|
buttons.addWidget(self._send_button)
|
||||||
|
buttons.addWidget(self._voice_button)
|
||||||
|
buttons.addStretch()
|
||||||
|
|
||||||
input_row = QHBoxLayout()
|
input_row = QHBoxLayout()
|
||||||
input_row.addWidget(self._input, 1)
|
input_row.addWidget(self._input, 1)
|
||||||
input_row.addWidget(self._send_button)
|
input_row.addLayout(buttons)
|
||||||
|
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.addLayout(header)
|
layout.addLayout(header)
|
||||||
@@ -146,8 +161,11 @@ class ChatWindow(QWidget):
|
|||||||
self._submit()
|
self._submit()
|
||||||
|
|
||||||
def _on_state_changed(self, state: object) -> None:
|
def _on_state_changed(self, state: object) -> None:
|
||||||
|
current = self._assistant.state
|
||||||
self._send_button.setText("Стоп" if self._assistant.is_busy else "Отправить")
|
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:
|
def _update_provider_label(self) -> None:
|
||||||
self._provider_label.setText(
|
self._provider_label.setText(
|
||||||
|
|||||||
@@ -11,12 +11,16 @@ from agr_assistent.core.assistant import AssistantState
|
|||||||
|
|
||||||
STATE_TITLES = {
|
STATE_TITLES = {
|
||||||
AssistantState.IDLE: "ожидание",
|
AssistantState.IDLE: "ожидание",
|
||||||
|
AssistantState.LISTENING: "слушаю…",
|
||||||
|
AssistantState.RECOGNIZING: "распознаю…",
|
||||||
AssistantState.THINKING: "думаю…",
|
AssistantState.THINKING: "думаю…",
|
||||||
AssistantState.SPEAKING: "говорю…",
|
AssistantState.SPEAKING: "говорю…",
|
||||||
}
|
}
|
||||||
|
|
||||||
_STATE_COLORS = {
|
_STATE_COLORS = {
|
||||||
AssistantState.IDLE: "#5c6bc0",
|
AssistantState.IDLE: "#5c6bc0",
|
||||||
|
AssistantState.LISTENING: "#e53935",
|
||||||
|
AssistantState.RECOGNIZING: "#8e24aa",
|
||||||
AssistantState.THINKING: "#fb8c00",
|
AssistantState.THINKING: "#fb8c00",
|
||||||
AssistantState.SPEAKING: "#43a047",
|
AssistantState.SPEAKING: "#43a047",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,18 @@ _NOTIFICATION_TIMEOUT_MS = 6000
|
|||||||
|
|
||||||
|
|
||||||
class TrayIcon(QSystemTrayIcon):
|
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))
|
super().__init__(state_icon(assistant.state))
|
||||||
self._assistant = assistant
|
self._assistant = assistant
|
||||||
self._window = window
|
self._window = window
|
||||||
|
|
||||||
menu = QMenu()
|
menu = QMenu()
|
||||||
menu.addAction("Открыть чат", window.show_and_raise)
|
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_menu = menu.addMenu("Провайдер LLM")
|
||||||
provider_group = QActionGroup(self)
|
provider_group = QActionGroup(self)
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -12,12 +12,16 @@ name = "agr-assistent"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
{ name = "faster-whisper" },
|
||||||
{ name = "num2words" },
|
{ name = "num2words" },
|
||||||
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
{ 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 = "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 = "openai" },
|
||||||
{ name = "pyside6" },
|
{ name = "pyside6" },
|
||||||
{ name = "pyyaml" },
|
{ name = "pyyaml" },
|
||||||
|
{ name = "silero-vad" },
|
||||||
{ name = "sounddevice" },
|
{ name = "sounddevice" },
|
||||||
{ name = "torch" },
|
{ name = "torch" },
|
||||||
]
|
]
|
||||||
@@ -29,11 +33,15 @@ dev = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
|
{ name = "faster-whisper", specifier = ">=1.2.1" },
|
||||||
{ name = "num2words", specifier = ">=0.5.14" },
|
{ name = "num2words", specifier = ">=0.5.14" },
|
||||||
{ name = "numpy", specifier = ">=2.4.6" },
|
{ 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 = "openai", specifier = ">=3.14.1" },
|
||||||
{ name = "pyside6", specifier = ">=6.11.2" },
|
{ name = "pyside6", specifier = ">=6.11.2" },
|
||||||
{ name = "pyyaml", specifier = ">=6.0.3" },
|
{ name = "pyyaml", specifier = ">=6.0.3" },
|
||||||
|
{ name = "silero-vad", specifier = ">=6.2.1" },
|
||||||
{ name = "sounddevice", specifier = ">=0.5.6" },
|
{ name = "sounddevice", specifier = ">=0.5.6" },
|
||||||
{ name = "torch", specifier = ">=2.14.0" },
|
{ 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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "cffi"
|
name = "cffi"
|
||||||
version = "2.1.1"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "cuda-bindings"
|
name = "cuda-bindings"
|
||||||
version = "13.4.1"
|
version = "13.4.1"
|
||||||
@@ -253,6 +342,22 @@ version = "0.6.2"
|
|||||||
source = { registry = "https://pypi.org/simple" }
|
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" }
|
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]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
version = "3.32.7"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "fsspec"
|
name = "fsspec"
|
||||||
version = "2026.7.0"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "httpcore2"
|
name = "httpcore2"
|
||||||
version = "2.13.0"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "httpx2"
|
name = "httpx2"
|
||||||
version = "2.13.0"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.19"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "nvidia-cuda-cupti"
|
name = "nvidia-cuda-cupti"
|
||||||
version = "13.0.85"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "nvidia-cuda-runtime"
|
name = "nvidia-cuda-runtime"
|
||||||
version = "13.0.96"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "nvidia-cudnn-cu13"
|
name = "nvidia-cudnn-cu13"
|
||||||
version = "9.24.0.43"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "openai"
|
name = "openai"
|
||||||
version = "3.14.1"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "pycparser"
|
name = "pycparser"
|
||||||
version = "3.0"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
version = "1.3.1"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "torch"
|
name = "torch"
|
||||||
version = "2.14.0"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "triton"
|
name = "triton"
|
||||||
version = "3.8.0"
|
version = "3.8.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user