feat: Jarvis v2 - CPU optimized always-listening for iMac Ubuntu
- PipeWire/pw-record backend (no PortAudio needed) - tested mic live - VAD: Energy (immediate) + Silero (torch CPU) switchable - Wakeword: openWakeWord TFLite hey_jarvis 0.4 - STT: faster-whisper base int8 CPU - model loads OK on i3 - SpeakerID: ECAPA embeddings + fallback stats, enrollment store - Vision: OpenCV Haar + face_recognition + webcam cam0 640x480 verified - LLM: ollama/qwen2.5:3b, gemini_cli, hermes_api, echo strategies - TTS: Piper + espeak + echo fallbacks - Core pipeline: IDLE -> WAKE -> RECORDING -> STT -> SpeakerID/FaceID fusion -> LLM -> TTS -> followup window - Config via jarvis.yaml, soul.md persona - CLI tools: run, devices, enroll-speaker, enroll-face, test-vad, test-mic - Systemd user service ready - Tested on iMac 2010 i3 550 15GB Ubuntu 26.04 CPU-only
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import yaml, os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
def load_config(path: Optional[Path] = None) -> Dict[str, Any]:
|
||||
default = Path(__file__).parent.parent / "jarvis.yaml"
|
||||
example = Path(__file__).parent.parent / "jarvis.yaml.example"
|
||||
if path is None:
|
||||
if default.exists():
|
||||
path = default
|
||||
elif example.exists():
|
||||
path = example
|
||||
else:
|
||||
return {}
|
||||
path = Path(path)
|
||||
if not path.exists():
|
||||
return {}
|
||||
with open(path, 'r') as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
|
||||
def get_nested(cfg: dict, dotted: str, default=None):
|
||||
cur = cfg
|
||||
for part in dotted.split('.'):
|
||||
if isinstance(cur, dict) and part in cur:
|
||||
cur = cur[part]
|
||||
else:
|
||||
return default
|
||||
return cur
|
||||
Reference in New Issue
Block a user