Fork of Priler/jarvis (CC BY-NC-SA 4.0) https://github.com/Priler/jarvis
Find a file
Bossiara13 01ea46c091 fix(app): show toast + open Sound settings when mic enumeration returns nothing
Before: clicking "Запустить" in jarvis-gui spawned jarvis-app, which
silently exited with code 1 (or in release builds, looked like the
console window flashed and closed). The pv_recorder library returns
INVALID_ARGUMENT from pv_recorder_init when Windows Core Audio reports
zero capture endpoints (some other app holding the mic exclusively,
or all input devices disabled in mmsys.cpl). User saw no actionable
feedback.

Now: on recorder::init failure jarvis-app calls notify_mic_problem()
which (Windows-only):
- Fires a long-duration Windows toast titled "J.A.R.V.I.S.: микрофон
  не найден" with a hint pointing at mmsys.cpl / Recording.
- Spawns "start ms-settings:sound" so the Sound settings page opens
  automatically — user can re-enable the mic in two clicks.

Then the original app::close(1, ...) path runs to keep the same exit
behaviour the GUI's get_jarvis_app_stats poller expects.

Cargo.toml: jarvis-app now pulls winrt-notification (already in
workspace.dependencies via jarvis-core) for the toast.

Also incidentally fixed: the release-build C0000139 (entrypoint not
found) loader crash that was showing up before this change. It went
away after the workspace dep was added and the release relink ran.
Most likely the previous release exe had a stale import table from an
earlier partial rebuild; the clean relink resolves it.

Non-Windows builds get a no-op eprintln so the binary still compiles
for Linux/macOS.
2026-05-15 11:58:47 +03:00
crates fix(app): show toast + open Sound settings when mic enumeration returns nothing 2026-05-15 11:58:47 +03:00
frontend chore(gui): rebrand — strip TG/Boosty/Patreon/feedback-bot, keep CC BY-NC-SA attribution 2026-05-15 01:09:14 +03:00
lib library files required for the build added 2026-01-17 06:08:09 +05:00
resources feat(commands): tier-1 PC tools — window mgmt, clipboard, notes, process killer, web search 2026-05-15 11:27:02 +03:00
.gitignore chore: gitignore tauri-generated schema files 2026-04-22 17:55:56 +03:00
Cargo.lock chore: add webrtc-vad workspace dep 2026-04-23 11:07:04 +03:00
Cargo.toml chore(gui): rebrand — strip TG/Boosty/Patreon/feedback-bot, keep CC BY-NC-SA attribution 2026-05-15 01:09:14 +03:00
LICENSE.txt Readme and License 2023-04-28 19:39:48 +05:00
post_build.py some fixes + gliner first implementation 2026-02-11 07:21:50 +05:00
poster.jpg Some funky stuff, bruh 2023-05-01 01:52:47 +05:00
README.md docs: README — VAD smart command end 2026-04-23 11:08:09 +03:00

J.A.R.V.I.S (Rust) — форк Bossiara13

Форк Rust-переписки голосового ассистента Priler/jarvis. Текущий репозиторий: https://github.com/DmitryBykov-ISPO/J.A.R.V.I.S-rust.

Что это

Голосовой ассистент, написанный на Rust, работающий локально (без облака). Текущий стек:

  • Vosk — Speech-to-Text (через vosk-rs).
  • fastembed + ort — локальные эмбеддинги для intent-классификации (MiniLM L6/L12 ONNX).
  • Picovoice Porcupine / Rustpotter / Vosk — три опциональных движка wake-word.
  • mlua (Lua 5.5, vendored) — скрипты пользовательских команд.
  • Tauri + Vite/Svelte — GUI-оболочка (фронтенд в отдельной папке frontend/).
  • nnnoiseless — подавление шума.
  • fluent / unic-langid — i18n (ru, ua, en).

LLM-клиент (Groq / OpenAI-совместимый) добавлен в jarvis-core::llm и подключён к голосовому циклу. Если фраза начинается с триггера («скажи», «ответь», «произнеси»), она уходит в Groq и ответ возвращается через IPC-событие LlmReply. Без триггера всё работает как раньше — wake-word + intent + Lua. Это следующий шаг после CLI-only LLM из v0.2.0.

Это форк

Оригинальный автор — Abraham Tugalov (Priler). Апстрим: https://github.com/Priler/jarvis. Лицензия сохранена: CC BY-NC-SA 4.0 (см. LICENSE.txt). Атрибуция в Cargo.toml и voice.toml пакетов озвучки не изменена.

Что отличается от апстрима

  • Обновлён список авторов в Cargo.toml (добавлен Bossiara13 (fork), оригинал сохранён).
  • README переписан и отражает фактическую архитектуру (апстримный README называет проект "Tauri+Svelte", что давно не соответствует действительности — это workspace из 4-х крейтов).
  • Отсутствующие в апстриме ONNX-модели (all-MiniLM-L6-v2, paraphrase-multilingual-MiniLM-L12-v2-onnx-Q) подтянуты через Git LFS из HuggingFace (Qdrant) и запушены в форк.

Структура репозитория

Cargo workspace из четырёх крейтов:

Крейт Назначение
jarvis-core Библиотека: конфиг, intent, STT, wake-word, аудио, Lua-бэкенд, i18n.
jarvis-app Бинарь-«демон»: собирает всё вместе, tray, IPC.
jarvis-gui Tauri-приложение (использует frontend/dist/client).
jarvis-cli CLI для отладки: классификация intent, список команд, dump конфига.

Прочее:

  • frontend/ — Vite + Svelte UI для jarvis-gui. Собирается отдельно.
  • lib/windows/amd64/ — нативные DLL/LIB для Vosk, Porcupine, PvRecorder.
  • resources/ — голоса, модели, конфиги по умолчанию. ONNX-модели хранятся в Git LFS.
  • post_build.py — постпроцессинг артефактов сборки (Python 3).

Сборка

Требования:

  • Rust 1.93+ (собирается на stable MSVC).
  • Node 24+ и npm — для фронтенда.
  • Python 3 — для post_build.py.
  • MSVC build tools (Windows, x64).
  • Установленные libvosk.lib, libpv_porcupine.dll, libpv_recorder.dll в lib/windows/amd64/ (уже в репозитории).

Перед сборкой jarvis-gui нужно собрать фронтенд:

cd frontend
npm install
npm run build
cd ..

Затем workspace:

cargo build --workspace

Холодная сборка занимает около 10 минут (ONNX runtime, aws-lc-rs, tauri).

Статус сборки в этом форке

На моей машине (cargo build --workspace, stable MSVC) итог:

  • jarvis-core — собрался (1 warning, unused import).
  • jarvis-app — собрался, бинарник target/debug/jarvis-app.exe создан.
  • jarvis-cliпадает на линковке: LNK1181: cannot open input file "libvosk.lib". Причина: у jarvis-cli нет своего build.rs, а .cargo/config.toml с rustc-link-search лежит только внутри crates/jarvis-app/ и не подтягивается для jarvis-cli. Лечится либо добавлением такого же build.rs в crates/jarvis-cli/, либо вынесением config.toml в корень. Сознательно не трогал — фикс выходит за рамки рефакторинга (v0.0.1-import фиксирует поведение апстрима как есть).
  • jarvis-gui — падает в tauri::generate_context!(): frontendDist = "../../frontend/dist/client" не существует. Это ожидаемо, если не запустить npm run build в frontend/ заранее (см. секцию «Сборка»).

Запуск уже собранного:

./target/debug/jarvis-app.exe

Для CLI (jarvis-cli --help, команды classify, execute, list, phrases) нужно сначала починить линковку Vosk (см. выше).

LLM (Groq)

В jarvis-core есть модуль llm — блокирующий клиент для OpenAI-совместимого эндпоинта chat completions. По умолчанию настроен на Groq. Используется через фиче-флаг llm (включён в дефолтный набор jarvis_app, также подтянут в jarvis-cli).

Переменные окружения:

Переменная Обязательна Значение по умолчанию
GROQ_TOKEN да
GROQ_BASE_URL нет https://api.groq.com/openai/v1
GROQ_MODEL нет llama-3.3-70b-versatile

Быстрая проверка через CLI:

set GROQ_TOKEN=gsk_...
jarvis-cli ask "скажи привет одной фразой"

Ответ печатается в stdout. Без GROQ_TOKEN команда завершится с кодом 2 и сообщением об ошибке. При ошибке API — код 1 и тело ответа.

Программное использование из Rust:

use jarvis_core::llm::{LlmClient, ChatMessage};

let client = LlmClient::from_env()?;
let reply = client.complete(&[ChatMessage::user("привет")], 256)?;
println!("{}", reply);

Подключение к голосовому циклу

Помимо CLI, LLM подключён напрямую в jarvis-app. Логика в crates/jarvis-app/src/llm_fallback.rs:

  • При старте jarvis-app пытается прочитать GROQ_TOKEN. Если переменной нет — фоллбэк отключается, в лог пишется warning, голосовые команды продолжают работать как раньше.
  • Распознанная фраза (как из микрофона, так и из текстовой панели GUI) проверяется на префиксы-триггеры из config::get_llm_trigger_phrases (для ru/ua: скажи, ответь, произнеси; для en: say, tell, answer).
  • Если триггер найден — остаток фразы уходит в LlmClient::complete(), ответ публикуется в IPC как IpcEvent::LlmReply { text } (UI/GUI слушает этот ивент и проговаривает текст уже на своей стороне), а звук-«ок» проигрывается из текущего голосового пресета.
  • История разговора хранится в ConversationHistory с потолком LLM_DEFAULT_MAX_HISTORY = 8 ходов; system-prompt всегда сохраняется при вытеснении старых ходов.
  • При сетевой/API-ошибке последний user-turn убирается из истории, в IPC уходит LlmReply с короткой русской фразой («Не могу связаться с сервером, сэр.»), играется звук-«error». Голосовой цикл не падает.

Системный промпт (русский) описывает J.A.R.V.I.S. как британского дворецкого Тони Старка — короткие реплики (13 предложения), обращение «сэр», без излишней цензуры. Меняется в config::LLM_SYSTEM_PROMPT_RU.

VAD: умное завершение команды по тишине

После срабатывания wake-word jarvis-app слушает команду. Раньше окно закрывалось либо по таймауту Vosk, либо по жёсткому пределу из config::CMS_WAIT_DELAY — короткие команды («стоп», «громче») всё равно ждали несколько секунд тишины.

Теперь окно закрывается, как только пользователь замолчал. Реализовано через webrtc-vad (чистый Rust, тот же алгоритм, что в Python-версии). Это паритет с Python v0.2.0, где использовался webrtcvad с теми же параметрами.

Логика (state-машина в crates/jarvis-core/src/audio_processing/vad/listen_window.rs):

  • Кадр VAD: 30 мс при 16 кГц = 480 сэмплов = 960 байт (mono i16). Кадры с микрофона (по 512 сэмплов от pv_recorder) аккумулируются и режутся на VAD-кадры адаптером WebRtcVad::push_samples.
  • Каждый VAD-кадр учитывается в счётчиках speech_ms / silence_ms.
  • Окно закрывается, когда silence_ms >= VAD_COMMAND_END_SILENCE_MS И speech_ms >= VAD_COMMAND_MIN_SPEECH_MS.
  • До истечения VAD_COMMAND_MIN_LISTEN_MS окно не закрывается — пользователю даётся время начать говорить.
  • При достижении VAD_COMMAND_MAX_LISTEN_MS срабатывает hard-cap и управление возвращается в режим ожидания wake-word.
  • На событии «закрыть окно» вызывается stt::finalize_speech() — Vosk форсированно отдаёт финальный результат, не дожидаясь собственного таймаута.

Параметры (crates/jarvis-core/src/config.rs):

Константа Значение Назначение
VAD_AGGRESSIVENESS 2 Уровень WebRTC VAD: 0 — Quality, 3 — VeryAggressive.
VAD_COMMAND_END_SILENCE_MS 1200 Сколько тишины подряд считать «команда закончилась».
VAD_COMMAND_MIN_SPEECH_MS 500 Минимум речи в окне — иначе закрытие игнорируется.
VAD_COMMAND_MIN_LISTEN_MS 1000 Минимальная длительность окна (страховка от ранних закрытий)
VAD_COMMAND_MAX_LISTEN_MS 15000 Жёсткий потолок окна.

На короткие команды отзыв стал заметно быстрее (мы выходим на распознавание сразу после паузы пользователя, а не по 5-секундному порогу Vosk).

Лицензия

Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). Полный текст — в LICENSE.txt. Атрибуция оригинального автора (Abraham Tugalov) сохранена.

В Cargo.toml декларирован license = "GPL-3.0-only" — это несоответствие унаследовано от апстрима и не правилось, чтобы не расходиться с upstream-конфигом. Приоритет имеет LICENSE.txt.

Python-версия

Старая версия ассистента была на Python. Последний коммит с Python-кодом в апстриме — 943efbf.