Fork of Priler/jarvis (CC BY-NC-SA 4.0) https://github.com/Priler/jarvis
Find a file
Bossiara13 cdd331af35
Some checks failed
Python CI / pytest (ubuntu-latest) (push) Failing after 27s
Python CI / ruff (push) Failing after 5s
Python CI / pytest (windows-latest) (push) Has been cancelled
fix: Python startup no longer silent on first run
ROOT CAUSE

User reported the Python edition showed "только пустой терминал" — empty
terminal — when launched. Three compounding issues:

1. `tts.py` called `torch.hub.load(...)` at MODULE IMPORT time. On first
   use this downloads a 60MB Silero model with no progress output beyond
   one "Using cache found in..." line. With no prior banner the user
   couldn't tell if it crashed or was loading.
2. main.py's startup banner (`print("=" * 60)...J.A.R.V.I.S. v...`)
   appeared AFTER all the heavy imports. So even when nothing was wrong,
   visible feedback was delayed by 10-60 seconds.
3. If `pip install -r requirements.txt` failed for any package
   (`simpleaudio` notoriously needs MSVC to build), `import simpleaudio`
   crashed the whole process with a raw ModuleNotFoundError.

FIXES

- `tts.py`: Silero model load moved into `_ensure_model()` called lazily
  from `synthesize()`. First synth pays the load cost (with a one-line
  progress hint) — everything before that is visible immediately.
- `main.py`: print "J.A.R.V.I.S. loading..." banner BEFORE any heavy
  imports. User sees the terminal is alive within ~100ms.
- `main.py`: scan required deps before importing them; if any are
  missing, print "Run: pip install -r requirements.txt" with the
  specific list and exit cleanly (exit code 2) instead of stack-tracing.
- `main.py`: `simpleaudio` is now soft-optional. Falls back to stdlib
  `winsound` on Windows installs that couldn't build the C extension.
  Sound cues still work; only the precise wait_done semantics are
  slightly different.

VERIFIED LOCALLY

`.venv\Scripts\python.exe main.py` now prints:
```
============================================================
  J.A.R.V.I.S.  loading (Python edition)...
  Heavy modules (vosk / torch / pvrecorder) take a few seconds.
============================================================
[scheduler] thread started (1 tasks loaded)
============================================================
  J.A.R.V.I.S.  v0.4.1   [Python edition]
  Total commands: 200
============================================================
Using device: Микрофон (5- Fifine Microphone)
Jarvis (v0.4.1) начал свою работу ...
Yes, sir.
```

Total time to first banner: ~100ms (was 10-60s of nothing).
All 104 pytest tests still pass.
2026-05-24 22:12:24 +03:00
.github/workflows chore: GitHub Actions CI workflow 2026-05-16 13:38:22 +03:00
.idea some reformat 2023-04-16 16:59:18 +05:00
custom-commands Revert "Поменял архитектуру, сильно уменьшил кол-во зависимостей, убрал pyaduio, porcupine" 2023-04-16 15:56:20 +05:00
gui feat: Wave 3 — plugin loader + Flet GUI (parity with Rust fork) 2026-05-16 13:27:04 +03:00
model_small some reformat 2023-04-16 16:59:18 +05:00
models/all-MiniLM-L6-v2 chore: vendor MiniLM-L6-v2 onnx model from Qdrant 2026-04-23 10:51:53 +03:00
sound Revert "Поменял архитектуру, сильно уменьшил кол-во зависимостей, убрал pyaduio, porcupine" 2023-04-16 15:56:20 +05:00
tests feat: expense tracker Python parity (Wave 10) 2026-05-16 15:02:07 +03:00
tools/command_builder feat(url): optional browser field (yandex/chrome/firefox/edge) 2026-04-27 20:06:26 +03:00
utils chore: add utils/preview_effects.py for A/B comparison 2026-04-22 19:59:45 +03:00
.gitattributes chore: enable git lfs for .onnx files 2026-04-23 10:51:29 +03:00
.gitignore feat: expense tracker Python parity (Wave 10) 2026-05-16 15:02:07 +03:00
commands.yaml feat: expense tracker Python parity (Wave 10) 2026-05-16 15:02:07 +03:00
config.py feat: parity with rust fork — LLM auto-fallback + 6 new action types + 20+ commands 2026-05-15 01:48:34 +03:00
dev.env.example chore: drop pvporcupine dep and PICOVOICE_TOKEN from env template 2026-04-22 17:47:22 +03:00
dev_handlers.py feat: LLM hot-swap voice command + Ollama backend (148 → 151 commands) 2026-05-16 00:44:42 +03:00
effects.py feat: add tts effects module (bandpass + reverb + optional pitch) 2026-04-22 19:58:20 +03:00
expenses_handlers.py feat: expense tracker Python parity (Wave 10) 2026-05-16 15:02:07 +03:00
extensions.py feat: expense tracker Python parity (Wave 10) 2026-05-16 15:02:07 +03:00
gui.bat feat: Wave 3 — plugin loader + Flet GUI (parity with Rust fork) 2026-05-16 13:27:04 +03:00
intent.py feat: onnx intent classifier replacing fuzzy match 2026-04-23 10:53:18 +03:00
LICENSE.txt license added 2023-04-22 02:17:06 +05:00
llm_backend.py feat: LLM hot-swap voice command + Ollama backend (148 → 151 commands) 2026-05-16 00:44:42 +03:00
macros_store.py feat: vision + macros + scheduler in Python (129 → 142 commands) 2026-05-16 00:00:22 +03:00
main.py fix: Python startup no longer silent on first run 2026-05-24 22:12:24 +03:00
memory_store.py feat: Python parity for Wave 6-8 — memory admin / cooking / leftoff / trivia / routines 2026-05-16 14:55:47 +03:00
outlook_handlers.py feat: Wave 5 Python parity — outlook COM + time tracker 2026-05-16 14:34:44 +03:00
personality_handlers.py feat: personality pack — Python parity 2026-05-16 13:53:44 +03:00
plugins_store.py feat: Wave 3 — plugin loader + Flet GUI (parity with Rust fork) 2026-05-16 13:27:04 +03:00
profiles_store.py feat: memory_store + profiles_store + 10 new yaml entries (119 → 129) 2026-05-15 23:54:33 +03:00
README.md feat: Wave 3 — plugin loader + Flet GUI (parity with Rust fork) 2026-05-16 13:27:04 +03:00
requirements.txt feat: Wave 5 Python parity — outlook COM + time tracker 2026-05-16 14:34:44 +03:00
run.bat feat: "Python edition" startup banner + one-click run.bat launcher 2026-05-15 12:12:21 +03:00
run_builder.bat docs: command builder README + user-facing README section + run_builder.bat 2026-04-23 02:13:47 +03:00
scheduler_store.py feat: vision + macros + scheduler in Python (129 → 142 commands) 2026-05-16 00:00:22 +03:00
time_tracker_handlers.py feat: Wave 5 Python parity — outlook COM + time tracker 2026-05-16 14:34:44 +03:00
tts.py fix: Python startup no longer silent on first run 2026-05-24 22:12:24 +03:00
vision_handler.py feat: LLM hot-swap voice command + Ollama backend (148 → 151 commands) 2026-05-16 00:44:42 +03:00
wave1_handlers.py feat: Wave 1 python parity — 20 new commands (154 → 174) 2026-05-16 01:08:17 +03:00
wave68_handlers.py feat: Python parity for Wave 6-8 — memory admin / cooking / leftoff / trivia / routines 2026-05-16 14:55:47 +03:00

J.A.R.V.I.S (Python) — Bossiara13's fork

Голосовой ассистент для Windows с wake-word «jarvis». Распознаёт речь локально через Vosk (русская модель), отвечает голосом через Silero TTS, а свободные вопросы (фразы, начинающиеся со слова «скажи …») отправляет в LLM на Groq и зачитывает ответ.

Это форк

Форк репозитория Priler/jarvis (автор оригинала — Abraham Tugalov, 2022). Лицензия наследуется: CC BY-NC-SA 4.0 (см. LICENSE.txt).

Что отличается от оригинала

  • Из истории удалены случайно закоммиченные секреты.
  • Бэкенд LLM переключён с OpenAI на Groq (бесплатный тариф) через openai-совместимый API.
  • Код обновлён под новую версию SDK openai (>=1.0); старый pre-1.0 интерфейс был сломан.
  • Убрана зависимость от Picovoice Porcupine — wake-word распознаётся напрямую через Vosk с grammar-constraint (второй KaldiRecognizer со словарём из WAKE_WORDS). Ключ Picovoice больше не нужен.
  • Обновлена ветка/тэги (dev, v0.0.1-import), причёсан README и requirements.txt.

Установка

Требуется Python 3.11 (на 3.13 ряд зависимостей пока ставится с бубном).

git clone https://github.com/DmitryBykov-ISPO/J.A.R.V.I.S-py.git
cd J.A.R.V.I.S-py
py -3.11 -m venv .venv
.venv\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txt

Скопируйте dev.env.example в dev.env и заполните ключ:

Vosk-модель для русского языка лежит в model_small/ (уже в репозитории). Запуск:

python main.py

GUI (Flet)

Опциональная графическая панель — те же 7 страниц, что и в Rust-форке (Главная / Команды / Макросы / Расписание / Память / Плагины / Настройки).

pip install flet>=0.85   # одноразово, уже в requirements.txt
python -m gui            # или просто gui.bat

GUI не запускает ассистент сам — это панель управления. Кнопка «Запустить J.A.R.V.I.S.» на главной запускает main.py отдельным процессом.

Плагины

Голосовые команды, которые лежат вне репозитория. Добавляются без правки проекта: создайте папку в

%APPDATA%\com.priler.jarvis\plugins\<имя_плагина>\
    commands.yaml      (тот же формат, что и корневой commands.yaml)
    disabled           (опционально — пустой файл, отключает плагин)

Плагины подхватываются при следующем запуске main.py. ID встроенных команд выигрывают конфликты (плагин не может переопределить, например, open_browser). GUI-страница «Плагины» показывает список, флажки enable/disable и кнопку «Открыть папку».

Конфигурация

  • config.py:
    • MICROPHONE_INDEX = -1 — индекс микрофона (-1 означает устройство по умолчанию). Если микрофонов несколько и берётся не тот, поменяйте число.
    • WAKE_WORDS = ('jarvis', 'джарвис') — варианты транскрипции wake-word, которые принимает Vosk. Если ваш голос/акцент стабильно расшифровывается как, например, жарвис — добавьте этот вариант сюда (всё в нижнем регистре).
    • GROQ_MODEL — имя модели Groq (по умолчанию llama-3.3-70b-versatile).
    • INTENT_SIMILARITY_THRESHOLD = 0.45 — минимальная косинусная близость для срабатывания команды (см. ниже).
    • DENOISE_ENABLED = False — шумоподавление перед Vosk (см. ниже).
  • commands.yaml — набор голосовых команд и их вариантов; матчится семантически по эмбеддингам.
  • custom-commands/ — скомпилированные AHK-скрипты под конкретный сетап автора оригинала (переключение мониторов, управление Яндекс.Музыкой и т.п.). На вашей машине большая часть из них работать не будет, но ассистент запустится в любом случае.

Использование

  1. Скажите «Джарвис» (или «jarvis») — ассистент даст звуковой сигнал готовности.
  2. Произнесите команду — окно записи закрывается само, как только вы замолчите (см. ниже про VAD):
    • либо одну из фраз из commands.yaml (открыть браузер, выключить звук и т.д.);
    • либо начните со слова «скажи …» — остаток фразы уйдёт в Groq, ответ будет зачитан вслух.

VAD (определение конца команды)

Окно записи команды закрывается по голосовой активности (webrtcvad), а не по таймеру. Параметры в config.py:

  • VAD_AGGRESSIVENESS (0..3) — насколько агрессивно VAD режет шум; по умолчанию 2.
  • COMMAND_END_SILENCE_MS — длительность тишины, после которой окно закрывается (по умолчанию 1200 мс).
  • COMMAND_MIN_SPEECH_MS — минимальная длительность речи, иначе тишина не считается концом (500 мс).
  • COMMAND_MIN_LISTEN_MS — минимальное время записи, чтобы не закрыться мгновенно (1000 мс).
  • COMMAND_MAX_LISTEN_MS — жёсткий потолок (15000 мс).

Семантический матчинг команд

Раньше распознанная фраза сравнивалась с фразами из commands.yaml через fuzzywuzzy.fuzz.ratio — это срабатывало только на формальное совпадение букв. Теперь используется sentence-transformer all-MiniLM-L6-v2 (ONNX, CPU) — фраза кодируется в 384-мерный вектор, считается косинусная близость с фразами всех команд, побеждает самая близкая.

Это означает, что фраза «запусти браузер» сматчится с командой open_browser, даже если в YAML записано только «открой браузер»у них близкий смысл.

  • Модель лежит в models/all-MiniLM-L6-v2/ (87 МБ, хранится через Git LFS — git lfs pull после клонирования).
  • Порог INTENT_SIMILARITY_THRESHOLD = 0.45 (косинус, диапазон [-1; 1], для близких смыслов обычно 0.7+). Ниже порога — «команда не распознана», сработает not_found.wav (или ветка «скажи …» для LLM).
  • Все фразы из commands.yaml эмбеддятся один раз при старте; на каждой команде эмбеддится только распознанная пользователем фраза (~1-5 мс на CPU).

Шумоподавление перед STT

Опциональный пре-процессинг аудио командного буфера через noisereduce (спектральное гейтирование). Помогает в шумных помещениях (кулер, кондиционер, фоновая музыка). К wake-word фазе не применяется — там важна отзывчивость.

В config.py:

  • DENOISE_ENABLED = False — главный тумблер.
  • DENOISE_PROP = 0.85 — доля подавления (0..1).
  • DENOISE_STATIONARY = FalseFalse для меняющегося шума (рекомендуется), True для стабильного фона.

Эффекты под AI-ассистента

Silero звучит естественно, но иногда хочется лёгкой «AI-окраски» в духе J.A.R.V.I.S. — узкую полосу, еле заметную реверберацию, при желании небольшой сдвиг высоты. Это тонкий постпроцессинг, голос и sample rate не меняются.

По умолчанию эффекты выключены. Включаются в config.py:

  • TTS_EFFECTS_ENABLED — главный тумблер (False по умолчанию).
  • TTS_BANDPASS_LOW_HZ / TTS_BANDPASS_HIGH_HZ — полоса пропускания (по умолчанию 2007000 Гц). Сужение полосы даёт ощущение «голоса по радио».
  • TTS_REVERB_WET (0..1) и TTS_REVERB_DECAY_MS — доля мокрого сигнала и длительность хвоста короткой реверберации (по умолчанию 0.20 и 100 мс).
  • TTS_PITCH_SEMITONES — сдвиг высоты в полутонах (по умолчанию 0, разумный диапазон ±2).

Фильтры реализованы на scipy.signal (IIR, sosfiltfilt — нулевая фазовая задержка), реверберация — несколько затухающих задержанных копий, pitch — ресэмплинг по линейной интерполяции. Всё in-process, без временных файлов и лишних зависимостей.

Быстрое A/B сравнение перед тем как переключать тумблер:

.venv\Scripts\python.exe utils\preview_effects.py

Скрипт сгенерирует sound/_preview_silero_raw.wav и sound/_preview_silero_fx.wav — откройте оба в плеере и решите, нравится ли эффект. Параметры fx-версии берутся из текущего config.py (независимо от TTS_EFFECTS_ENABLED).

Схема commands.yaml

Каждая команда — это пара phrases + action:

open_browser:
  phrases:
    - открой браузер
    - запусти браузер
  action: {type: exe, file: "Run browser.exe"}

Типы действий:

  • exe — запустить .exe из custom-commands/. Поля: file (имя файла), необязательные wait: true (ждать завершения), delay_ms (пауза после запуска). По умолчанию после exe проигрывается звук подтверждения ok — отключается через confirm_sound: null.
  • play_sound — проиграть WAV из sound/. Поле name (без расширения). Имена greet и ok проигрываются с рандомным выбором из вариантов greet1/2/3.wav, ok1/2/3.wav.
  • system — встроенные операции. op: volume_mute, volume_unmute, exit.
  • multi — последовательность шагов: steps: [...].

Шаги внутри multi — тех же типов. Пример:

sound_off:
  phrases: [выключи звук, отключи звук]
  action:
    type: multi
    steps:
      - {type: play_sound, name: ok}
      - {type: system, op: volume_mute}

Конструктор команд

GUI для добавления команд в commands.yaml без ручной правки YAML.

Запуск:

python -m tools.command_builder

Или из корня репо run_builder.bat (под Windows). Открывается окно pywebview с пошаговым мастером: фразы → тип действия → параметры → предпросмотр YAML и сохранение. На шаге «Действие» доступна вкладка «Подсказать (LLM)» — модель Groq предложит готовый блок по описанию на русском.

После сохранения команды Jarvis нужно перезапустить, чтобы она подхватилась.

Требования:

  • Windows 10/11 с установленным WebView2 Runtime (обычно уже стоит вместе с Edge — иначе скачать у Microsoft).
  • Зависимости из requirements.txt (включая pywebview, ruamel.yaml, pyautogui).

Подробности для разработчиков — tools/command_builder/README.md. TODO: добавить скриншот окна.

Лицензия

CC BY-NC-SA 4.0 — см. LICENSE.txt. Авторство оригинала — Abraham Tugalov / Priler. Изменения в этом форке — Bossiara13 (Dmitry Bykov), 2026.