Feat: Hermes Voice Client — CustomTkinter GUI + Push-to-Talk + .exe Build
- main.py: CustomTkinter GUI (400x520) mit Status-Indikator, Conversation-Log, Screenshot-Toggle und Settings-Dialog. Thread-sicherer UI-Queue für Hotkey-Callbacks. - hotkey_listener.py: pynput globaler Hotkey (press/release) + KeyCapturer für interaktive Hotkey-Konfiguration im Settings-Dialog. - config_manager.py: JSON-Settings in ~/.hermes-voice/settings.json mit DEFAULTS, load() merged gespeicherte mit Default-Werten. - audio_input.py: Vereinfacht auf start_recording/stop_recording/transcribe (kein Wake-Word-Loop mehr, direkt hotkey-gesteuert). - ai_client.py: Settings-dict statt config.py-Imports, MC2-URL/Modelle konfigurierbar. - requirements.txt: customtkinter>=5.2.0 + pynput>=1.7.6 hinzugefügt, pystray entfernt. - build.bat: PyInstaller --onefile --windowed → dist/HermesVoice.exe. - setup.bat: Veraltete Wake-Word-Hinweise entfernt, GUI-Check angepasst. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
import httpx
|
||||
from config import (
|
||||
MC2_BASE_URL, CHAT_MODEL, VISION_MODEL,
|
||||
MAX_RESPONSE_TOKENS, REQUEST_TIMEOUT, SYSTEM_PROMPT,
|
||||
SCREENSHOT_ON_QUERY,
|
||||
|
||||
SYSTEM_PROMPT = (
|
||||
"Du bist Hermes, ein hilfreicher KI-Assistent. "
|
||||
"Antworte kurz und präzise, da deine Antwort vorgelesen wird. "
|
||||
"Maximal 3-4 Sätze, kein Markdown."
|
||||
)
|
||||
REQUEST_TIMEOUT = 30
|
||||
|
||||
|
||||
def ask(text: str, screenshot_b64: str | None = None) -> str:
|
||||
def ask(text: str, screenshot_b64: str | None, settings: dict) -> str:
|
||||
"""Schickt Text (+ optionalen Screenshot) an MC2 und gibt die Antwort zurück."""
|
||||
use_vision = screenshot_b64 is not None and SCREENSHOT_ON_QUERY
|
||||
model = VISION_MODEL if use_vision else CHAT_MODEL
|
||||
use_vision = screenshot_b64 is not None
|
||||
model = settings.get("vision_model", "Qwen3-VL-2B-Instruct") if use_vision \
|
||||
else settings.get("chat_model", "Hermes-4-14B")
|
||||
base_url = settings.get("mc2_url", "http://192.168.178.151:9001/v1")
|
||||
max_tokens = int(settings.get("max_response_tokens", 200))
|
||||
|
||||
if use_vision:
|
||||
user_content = [
|
||||
@@ -27,16 +32,13 @@ def ask(text: str, screenshot_b64: str | None = None) -> str:
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": user_content},
|
||||
],
|
||||
"max_tokens": MAX_RESPONSE_TOKENS,
|
||||
"max_tokens": max_tokens,
|
||||
"stream": False,
|
||||
}
|
||||
|
||||
try:
|
||||
with httpx.Client(timeout=REQUEST_TIMEOUT) as client:
|
||||
response = client.post(
|
||||
f"{MC2_BASE_URL}/chat/completions",
|
||||
json=payload,
|
||||
)
|
||||
response = client.post(f"{base_url}/chat/completions", json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()["choices"][0]["message"]["content"].strip()
|
||||
except httpx.TimeoutException:
|
||||
|
||||
Reference in New Issue
Block a user