A fully local, Windows-only voice assistant ("Jarvis") built from a Qt desktop shell, a fleet of small FastAPI services, and GPU-accelerated local models LLaMA for reasoning, Whisper for speech-to-text, Piper for speech, YOLO for camera presence, and a FAISS index over your own files.
Nothing leaves the machine except optional web search, which goes to a SearXNG instance you run yourself.
This is the single most confusing thing about the project, so it's up front.
There are two separate codebases that together make up "Nexon":
| Thing | Location | What it is |
|---|---|---|
| NexonInstaller | D:\Sam\NexonInstaller |
The installer suite. Nexon.py (ships as Nexon.exe) creates the venv, installs deps, checks for updates, then starts the payload. Nexon_Torrent_Helper.py downloads the ~3.9 GB model bundle. See its README. |
| NexonTorrent (this repo) | D:\Sam\NexonTorrent |
The actual assistant — the "payload" the launcher runs. |
And there is a third location that matters at runtime:
%USERPROFILE%\NexonTorrent\ <- where the payload must physically live to run
%USERPROFILE%\.Nexon\ <- the virtualenv the launcher creates
Guard/Config_Helper.py hardcodes the config path to
%USERPROFILE%\NexonTorrent\config\Config.json. The config/Config.json in this
source tree is not the file the app reads unless this tree is
%USERPROFILE%\NexonTorrent. If you edit config here and nothing changes, that's why.
Nexon.exe (NexonInstaller\Nexon.py, the launcher)
├─ reads %USERPROFILE%\NexonTorrent\config\Config.json
├─ verifies install_path lock (refuses to run if the exe moved)
├─ checks GitHub releases for a newer version
├─ creates %USERPROFILE%\.Nexon venv if missing
├─ pip install -r requirements.txt
├─ pip install torch/torchvision/torchaudio (cu121 index)
├─ pip install llama-cpp-python==0.3.4 (cu121 index)
└─ launches: .Nexon\Scripts\python.exe NexonTorrent\Orchestrator.py
│
└─ Orchestrator.py (this repo's entry point)
└─ Guard/Ai_Launch.py :: UltimatePrecisionToolbar
└─ [user flips the toggle]
└─ run_guard_stack() → 5 FastAPI procs + Flask + Qt window
So: Orchestrator.py is the entry point of this repo. Everything else hangs
off Guard/.
graph TD
O[Orchestrator.py] --> TB["Ai_Launch.py<br/>floating toolbar<br/>(the on/off switch)"]
TB -->|toggle on| STACK[run_guard_stack]
STACK --> BE["Backend_Main<br/>:7000 FastAPI"]
STACK --> WS["ai_search_service<br/>:7100 FastAPI"]
STACK --> APP["ai_application_service<br/>:7101 FastAPI"]
STACK --> FS["ai_file_agent<br/>:7102 FastAPI"]
STACK --> SCH["index_scheduler<br/>:7103 FastAPI"]
STACK --> FE["Frontend_Main<br/>:5000 Flask/waitress"]
STACK --> QT["Agent_AI.py<br/>Qt WebView2 window"]
QT -->|loads| FE
FE -->|fetch /chat /presence<br/>/notifications /is_speaking| BE
BE --> WS
BE --> APP
BE --> FS
SCH -->|nightly POST| FS
SCH -->|nightly POST| APP
WS -->|HTTP| SX[(SearXNG :8888<br/>you run this)]
Every port comes from config/Config.json → ports. All bind 127.0.0.1.
| Port | Config key | Service | Module |
|---|---|---|---|
| 5000 | port_6 |
Flask UI (served by waitress) | Guard/Guard_AI_Suite/Frontend/Frontend_Main.py |
| 7000 | port_1 |
Assistant core — chat, vision, STT, notifications | Guard/Guard_AI_Suite/Backend/Backend_Main.py |
| 7100 | port_2 |
Web search + page fetch | Guard/Guard_AI_Suite/Tools/Search/WebSearch/ai_search_service.py |
| 7101 | port_3 |
App librarian (Start Menu index + launch) | Guard/Guard_AI_Suite/Tools/App/ai_application_service.py |
| 7102 | port_4 |
File agent (FAISS semantic file search) | Guard/Guard_AI_Suite/Tools/Search/FileSearch/ai_file_agent.py |
| 7103 | port_5 |
Nightly re-index scheduler | Guard/Guard_AI_Suite/Scheduler/index_scheduler.py |
| 8888 | (env SEARXNG_BASE) |
SearXNG — external, not started by this app | — |
run_guard_stack() starts them in this order with a 1s gap between each:
file agent → web search → app service → backend → scheduler → Flask → Qt window.
This is the core loop, in Backend_Main.py:
POST /chatenqueues aChatJobonto_chat_queue. A single background worker thread (_chat_worker_loop) processes them one at a time, so the LLM is never re-entered concurrently.- Fact extraction —
FactBrainasks the LLM to pull long-term facts (name, location, occupation, tools, people) out of the message and writes them tomemory.sqlite3viamemory_store.add_fact. - Intent routing — three signals are combined:
IntentRouter— a second, smaller LLaMA instance (intent-k2.gguf) that classifies intochat | vision | system | dev | open | web_search.WebNeedDetector/OpenIntentDetector/FileSearchIntentDetector— all three wrap the same HuggingFaceFalconsai/intent_classificationmodel and return a confidence score. Scores>= 0.6override the router.
- Dispatch:
vision→ grabs a webcam frame, runs YOLO, returns a scene descriptionopen→POST :7101/open, fuzzy-matches the Start Menu index, launches it- file-search intent →
POST :7102/search/interactive system/dev→ stubs, they return a polite "not wired yet" string- anything else →
jarvis_with_search_tools()
- Tool loop —
jarvis_with_search_toolsgives the LLM a one-shot tool prompt. The model either replies in plain text, or emits a single line of JSON ({"tool": "web_search", "args": {...}}). If it's a tool call, the result is fed back in as acontext_hintand the model is asked again. One hop only — there is no multi-turn tool loop. - Speech — the answer goes to
VoiceAgent.speak(), which shells out topiper.exe, writesagent_response.wav, and plays it through PyAudio.
Meanwhile, independently:
CameraPresenceWatcher(a daemon thread) polls the webcam with YOLO face detection. 3 consecutive frames with a face → awake; 20 without → asleep, and it cuts off any in-progress speech. The UI polls/presenceto reflect this.index_schedulersleeps until midnight, then re-indexes files and apps every 24h.
| File | Role |
|---|---|
Orchestrator.py |
Start here. Creates the QApplication, shows the toolbar, docks it bottom-center. |
Guard/Ai_Launch.py |
The frameless floating toolbar. Custom-painted (animated water-fill "Get Started" button, sliding toggle). The toggle spawns/kills the whole service stack as a child multiprocessing.Process. |
Guard/Agent_AI.py |
The main assistant window — a qtwebview2 WebView2 host pointed at http://127.0.0.1:5000. Handles minimal mode, Esc-to-collapse, tray, and opens the config editor. |
| File | Notes |
|---|---|
Guard_AI_Suite/Backend/Backend_Main.py |
~1350 lines. The brain. LLM wrappers, intent routing, system monitor, chat queue, notifications, /chat /presence /vision_snapshot /notifications /is_speaking /stt. |
Guard_AI_Suite/Frontend/Frontend_Main.py |
~1600 lines, and all of it is one Python string: a single HTML_TEMPLATE with a Shadcn-Zinc themed UI, a Three.js scene, and the JS polling loops. Served by one Flask route. |
Guard_AI_Suite/Tools/Search/WebSearch/ai_search_service.py |
Queries SearXNG's JSON API; /fetch_url renders a page with headless Playwright Chromium and strips it to text with BeautifulSoup. |
Guard_AI_Suite/Tools/App/ai_application_service.py |
Walks the Start Menu, resolves .lnk shortcuts, builds a fuzzy-searchable app index, launches by name. |
Guard_AI_Suite/Tools/Search/FileSearch/ai_file_agent.py |
Indexes ~/Downloads, ~/Documents, ~/Pictures, ~/Videos, ~/Desktop. Chunks text/code/PDF at 1200 chars with 200 overlap, embeds with a local all-MiniLM-L12-v2, stores vectors in FAISS + metadata in SQLite. Supports open / delete (via Recycle Bin) / RAG-over-file. |
Guard_AI_Suite/Scheduler/index_scheduler.py |
80 lines. Midnight cron that POSTs to the file and app agents. |
Guard_AI_Suite/Backend/presence_watcher.py |
YOLO face-presence thread (see above). |
Guard_AI_Suite/Backend/voice_agent.py |
Piper subprocess + PyAudio playback, with an interruptible stop flag. |
Guard_AI_Suite/Memory/memory_store.py |
Thin SQLite layer over a facts table. Schema lives in Config.json, not in code. |
| File | Role |
|---|---|
Guard/Config_Helper.py |
Loads %USERPROFILE%\NexonTorrent\config\Config.json. Every service calls this at import time. |
Guard/find_items.py |
Resolves a config-relative path against six candidate roots so the same code works in source and inside a PyInstaller onefile bundle. |
Guard/config_editor.py |
A Qt config editor with a Pygments-highlighted JSON/Python viewer. Reachable from the assistant window. |
Guard/StatusBar.py |
RAM bar + network status dot, embedded into the toolbar. |
Guard/Style_Qss/qss.py |
The Qt stylesheet, as one big string. |
Guard/get_local_ip.py |
Single helper — UDP-connect trick to find the LAN IP. |
These are vendored, not downloaded at runtime:
Model/brain_Model/Llama-3-Groq-8B-Tool-Use-Q4_K_M.gguf— main reasoning modelModel/intent_Model/intent-k2.gguf— the router modelModel/all-MiniLM-L12-v2/— sentence-transformers embeddings for file searchModel/whisper_Model/ggml-base.bin+STT-Engine/whisper-cli.exe— speech-to-textModel/en_onnx-{ryan-high,amy-medium}_Model/+TTS-Engine/piper-cpu/piper.exe— speechModel/yolo_Model/—yolov8n-face.pt(presence),yolo11n/11s,yolov8n(scene)Model/mediapipe_face_Model/face_landmarker.task,Model/face_Model/nate_face_embedding.npy
Everything tunable lives in config/Config.json (read from
%USERPROFILE%\NexonTorrent\config\Config.json). Notable sections:
| Key | What it controls |
|---|---|
ports |
All six service ports |
models |
Paths to every .gguf / .onnx / .pt, resolved through find_items() |
LlamaCppModel_settings |
Main model: n_ctx 6096, 512 max tokens, 20 GPU layers |
IntentRouter_LlamaCppModel_settings |
Router: n_ctx 1024, 128 tokens, 2 GPU layers |
file_agent |
Which directories to index, extensions, exclusions, chunk size, worker count |
memory_db |
DB path and the full CREATE TABLE schema as a string |
install_path |
Written on first launch and then enforced — the launcher refuses to start if Nexon.exe has moved |
update_url / github_api_endpoint |
Points at github.com/headlessripper/Nexon/releases |
Only one setting is an environment variable rather than config: SEARXNG_BASE
(default http://127.0.0.1:8888).
- Windows only. WebView2,
.lnkresolution,%USERPROFILE%,CAP_DSHOWwebcam capture, and bundled.exe/.dllbinaries all assume it. - Python 3.11 (the checked-in
.pycfiles arecpython-311). - An NVIDIA GPU with CUDA 12.1. The launcher installs torch and
llama-cpp-python from cu121 indexes. It runs on CPU, but slowly —
DEVICEfalls back automatically. - A webcam and a microphone.
- A running SearXNG instance if you want web search.
Build/run Nexon.exe from NexonInstaller, with this
tree deployed to %USERPROFILE%\NexonTorrent. The launcher does the rest.
xcopy /E /I D:\Sam\NexonTorrent %USERPROFILE%\NexonTorrentThen:
python -m venv %USERPROFILE%\.Nexon%USERPROFILE%\.Nexon\Scripts\pip install -r %USERPROFILE%\NexonTorrent\requirements.txt%USERPROFILE%\.Nexon\Scripts\pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121%USERPROFILE%\.Nexon\Scripts\pip install llama-cpp-python==0.3.4 --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu121flask is missing from requirements.txt even though Frontend_Main.py needs it — install it too:
%USERPROFILE%\.Nexon\Scripts\pip install flaskPlaywright needs its browser downloaded once:
%USERPROFILE%\.Nexon\Scripts\playwright install chromiumThen run from the NexonTorrent root — cwd matters, because there are no
__init__.py files and Guard.* imports resolve as implicit namespace packages
relative to the working directory:
cd %USERPROFILE%\NexonTorrent && %USERPROFILE%\.Nexon\Scripts\python Orchestrator.pyA floating black toolbar appears at the bottom of the screen. Click the right side to expand it, then flip the toggle to start the service stack. Expect a slow first start — two GGUF models, YOLO, and a transformers classifier all load up front.
Guard/BuildConfig/Nexon_Build.json— auto-py-to-exe config for the payload (onefile, console on,--collect-all llama_cpp, every service listed as a hidden import).
The launcher's build configs (Nexon_Orca.json, Nexon.iss) now live with the
installer, in NexonInstaller/BuildConfig/. Copies remain here under
Guard/BuildConfig/ and can be deleted once you've confirmed the installer builds.
Note: all of these build configs still carry absolute paths to an older project
directory (C:\Users\nateg\Documents\Virus_Defense_Engine\). They need rewriting
before they'll build from the current layout.
Things that will surprise you, roughly in order of how much time they'll cost:
- The config the app reads is not the config in this tree. See the top section.
requirements.txtis UTF-16LE with a BOM. pip decodes it fine, but most editors and anygrep/catwill show it as spaced-out garbage. Converting it to UTF-8 is safe — the launcher's own parser handles both.flaskis absent fromrequirements.txtbut is a hard dependency of the Flask frontend./sttdoesn't transcribe. The endpoint saves the uploaded blob toincoming_audio.webmand returns an empty string. It also tries to constructSTTResponse(text="", error="TTS_ACTIVE_IGNORE"), butSTTResponsehas noerrorfield, so that branch raises. The working Whisper wrapper lives inMain-Engine/Source/Main/stt_engine.pyand is not wired to anything.systemanddevintents are stubs. The router will classify into them, and the handler returns a "not fully wired yet" sentence.Guard/Style_Qss/Head.pyis broken and orphaned. It importsGuard.SentinelStorageandGuard.SentinelWeather, but those files live atGuard/Style_Qss/. Nothing importsHead.py, so it never surfaces.stt_engine.pyimportsfrom find_items import find_items(notGuard.find_items) and needssounddevice+scipy, neither of which is inrequirements.txt. It only works as a standalone script.- The assistant is hardcoded to one user. Prompts address "Nate" by name,
reference "his Lenovo Legion", and memory rows are all written under the literal
user key
"nate". Search for"nate"to find them all. Main-Engine/Source/Tests/is not a test suite.test.py,test1.py,test2.py,test3.pyare ~1000-line snapshots of earlier monolithic versions of the backend. There are no automated tests in this project.- A stale duplicate source tree still exists at
D:\Sam\ZashironSentinel\Nexon_Project\Guard\(~3.9 GB). It's an older copy ofNexonTorrent/Guard/that has diverged: it shipsBrain_M2.ggufwhere config expectsLlama-3-Groq-8B-Tool-Use-Q4_K_M.gguf, and it's missingyolov8n-face.pt(whichpresence_watcherrequires). The installer has been extracted out toD:\Sam\NexonInstaller, so nothing references this tree any more — it's safe to delete once you've confirmed the installer builds.NexonTorrentis canonical. Config_Helper.pyexists in two versions. This repo's copy has onlyload_config(); the installer's copy addssave_config()andupdate_config_field().Nexon.pyneeds the superset. If you consolidate, keep the installer's version.- The install-path lock is strict. Move
Nexon.exeafter first run and the launcher refuses to start until you clearinstall_pathfrom the config. - Startup is
time.sleep(1.0)between services, not a health check. On a cold cache the file agent may not be listening when the backend first calls it.
NexonTorrent/
├── Orchestrator.py # entry point
├── requirements.txt # UTF-16LE
├── test.py # fastembed model pre-download probe
├── config/Config.json # NOT the file read at runtime
├── background/ # app icon
└── Guard/
├── Ai_Launch.py # floating toolbar + stack launcher
├── Agent_AI.py # WebView2 assistant window
├── Config_Helper.py # config loader
├── find_items.py # dev/frozen path resolver
├── config_editor.py # Qt JSON config editor
├── StatusBar.py # RAM + network widget
├── get_local_ip.py
├── BuildConfig/ # PyInstaller + Inno Setup configs
├── Cache/ # fastembed cache warm-up scripts
├── Style_Qss/ # stylesheet + storage/weather widgets
├── background/ # icons, images, tray art
├── Guard_AI_Suite/
│ ├── Backend/ # Backend_Main, presence_watcher, voice_agent
│ ├── Frontend/ # Flask UI (one giant HTML string)
│ ├── Memory/ # SQLite fact store
│ ├── Scheduler/ # nightly re-index
│ └── Tools/
│ ├── App/ # Start Menu index + launcher
│ └── Search/
│ ├── FileSearch/ # FAISS file agent
│ └── WebSearch/ # SearXNG + Playwright
└── Main-Engine/
├── Model/ # all .gguf / .onnx / .pt weights
├── STT-Engine/ # whisper.cpp binaries
├── TTS-Engine/ # piper binaries + espeak-ng data
├── Voice_Profiles/ # enrollment WAVs
└── Source/
├── Main/ # stt_engine.py, tts_engine.py (standalone)
├── CodeScripts/ # TTS/STT experiments
└── Tests/ # old monolith snapshots, NOT tests
If you want to make this less confusing, in rough priority:
- Add
__init__.pyfiles (or apyproject.toml) soGuard.*imports stop depending on the working directory. - Make
Config_Helperfall back to a config next to the source tree when%USERPROFILE%\NexonTorrent\config\Config.jsonis absent. - Re-encode
requirements.txtas UTF-8 and addflask,torch, andllama-cpp-python(with a comment about the cu121 index). - Delete
Main-Engine/Source/Tests/andGuard/Style_Qss/Head.py, or move them to anarchive/folder. - Replace the
time.sleep(1.0)startup gaps with/healthpolling — every service already exposes one. - Pull the "Nate" hardcoding into config so the assistant has a configurable user.
The installer and bootstrapper suite for Nexon. This folder does not contain the assistant — it contains the two GUI programs that get the assistant onto a machine and start it.
The assistant itself lives in %USERPROFILE%\NexonTorrent.
| File | Ships as | Toolkit | Role |
|---|---|---|---|
Nexon.py |
Nexon.exe |
PySide6 | The launcher. Creates the venv, installs dependencies, checks GitHub for updates, then starts the payload. |
Nexon_Torrent_Helper.py |
separate exe | PyQt6 | The payload downloader. Pulls the ~3.9 GB model bundle from HuggingFace with resumable download, extracts it to %USERPROFILE%. |
Guard/Config_Helper.py |
— | — | Reads/writes %USERPROFILE%\NexonTorrent\config\Config.json. Shared with the payload, but this copy is the superset (see below). |
Model-Icon/ |
bundled asset | — | forward.png / close.png — the start and stop buttons on the downloader. |
BuildConfig/Nexon_Orca.json |
— | — | auto-py-to-exe config that builds Nexon.exe (onefile, windowed). |
BuildConfig/Nexon.iss |
— | — | Inno Setup script that wraps the built exe into NexonSetup.exe. |
The two scripts use different Qt bindings. Nexon.py is PySide6, Nexon_Torrent_Helper.py
is PyQt6. That's why requirements.txt lists both. Don't consolidate them without
porting the downloader first.
Nexon spreads itself across three places at runtime. Knowing which is which explains almost every "why isn't my change taking effect" question:
%USERPROFILE%\NexonTorrent\ <- where the payload must physically live to run
%USERPROFILE%\.Nexon\ <- the venv the launcher builds
Guard/Config_Helper.py hardcodes the config path to
%USERPROFILE%\NexonTorrent\config\Config.json. Both the installer and the payload
read that one file. Editing Config.json in either source tree changes nothing
unless that tree is %USERPROFILE%\NexonTorrent.
1. NexonSetup.exe (Inno Setup, from BuildConfig/Nexon.iss)
installs Nexon.exe
2. Nexon_Torrent_Helper (if the payload isn't present yet)
downloads Engine-Models.zip from HuggingFace
extracts to %USERPROFILE% -> creates %USERPROFILE%\NexonTorrent\
3. Nexon.exe (Nexon.py)
reads %USERPROFILE%\NexonTorrent\config\Config.json
verifies install_path lock (refuses to run if the exe moved)
checks GitHub releases for a newer version
creates %USERPROFILE%\.Nexon venv if missing
pip install -r %USERPROFILE%\NexonTorrent\requirements.txt
pip install torch torchvision torchaudio (cu121 index)
pip install llama-cpp-python==0.3.4 (cu121 index)
launches: .Nexon\Scripts\python.exe NexonTorrent\Orchestrator.py
-> hands off to the payload
Step 3's last line is the handoff point. Everything after it is documented in the payload README.
python -m venv .venv.venv\Scripts\pip install -r requirements.txtRun from this folder — Nexon.py does from Guard.Config_Helper import ...,
and with no __init__.py that resolves as an implicit namespace package relative to
the working directory:
cd D:\Sam\NexonInstaller && .venv\Scripts\python Nexon.pyNexon.py will fail immediately with FileNotFoundError if
%USERPROFILE%\NexonTorrent\config\Config.json doesn't exist. Either run the payload
downloader first, or copy NexonTorrent\config\ there by hand.
To run just the downloader:
cd D:\Sam\NexonInstaller && .venv\Scripts\python Nexon_Torrent_Helper.pyBoth build configs are auto-py-to-exe format — load them from its GUI, or feed the
equivalent flags to PyInstaller directly.
BuildConfig/Nexon_Orca.json still points at an old project directory
(C:\Users\nateg\Documents\Virus_Defense_Engine\Nexon.py) and an icon at
C:\Users\nateg\Downloads\icons8-ai-96.ico. Update filenames and icon_file to
this folder before building.
BuildConfig/Nexon.iss has the same problem: OutputDir, Source, and
SetupIconFile all reference Virus_Defense_Engine. It also sets a hardcoded
installer password (Password=Nate@360, Encryption=yes) — worth removing or
rotating if this ever ships beyond your own machine.
Config_Helper.pyis duplicated and the two copies differ. This folder's copy hassave_config()andupdate_config_field(); the payload's copy (NexonTorrent/Guard/Config_Helper.py) has onlyload_config().Nexon.pyimportssave_config, so it needs this version. If you ever consolidate them, the installer's superset is the one to keep.- The install-path lock is strict. On first run
Nexon.pywrites its own directory intoconfig["install_path"]. On later runs a mismatch blocks startup. MovingNexon.exemeans clearing that key by hand. - The downloader always extracts to
%USERPROFILE%, hardcoded. The zip's internal structure is what creates theNexonTorrentfolder name — the script doesn't verify the result landed anywhere sensible. - A stale duplicate of the payload still exists at
D:\Sam\ZashironSentinel\Nexon_Project\Guard\(~3.9 GB). It's an older copy ofNexonTorrent/Guard— differentBackend_Main.py, shipsBrain_M2.ggufinstead of theLlama-3-Groqmodel thatConfig.jsonexpects, and is missingyolov8n-face.ptthatpresence_watcher.pyrequires. Nothing references it. It was left in place deliberately; delete it once you've confirmed this folder builds.