Files
mission-control-v2/client/hermes-voice/ai_client.py
T
Hitonabi 037c4b11df 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>
2026-06-26 22:23:36 +02:00

48 lines
1.6 KiB
Python

import httpx
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, settings: dict) -> str:
"""Schickt Text (+ optionalen Screenshot) an MC2 und gibt die Antwort zurück."""
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 = [
{"type": "text", "text": text},
{"type": "image_url", "image_url": {
"url": f"data:image/jpeg;base64,{screenshot_b64}"
}},
]
else:
user_content = text
payload = {
"model": model,
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_content},
],
"max_tokens": max_tokens,
"stream": False,
}
try:
with httpx.Client(timeout=REQUEST_TIMEOUT) as client:
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:
return "Entschuldigung, die Antwort hat zu lange gedauert."
except Exception as e:
return f"Verbindungsfehler: {e}"