Files
Thorsten/input_helper.py
ersterDetektiv 6f198c3d51 first commit
2026-09-25 06:47:14 +02:00

157 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Input-Helper: Entwicklungstool für den Sprachassistenten.
Nimmt Text-Eingabe im Terminal entgegen und leitet sie als JSON an llm_module.py weiter.
Aufruf:
python input_helper.py # interaktiver Modus (Endlosschleife)
python input_helper.py "Deine Frage" # einmaliger Aufruf mit Argument
python input_helper.py --emotion drunk # mit Emotion
Optionen:
--emotion NAME Emotion für TTS (amused|angry|disgusted|drunk|neutral|sleepy|surprised|whisper)
--no-play Nur JSON erzeugen, kein Audio abspielen
--json-only Gibt nur das JSON aus, ruft LLM nicht auf
--history Aktiviert Gesprächsverlauf (Kontext merken)
--help Diese Hilfe
"""
import sys
import json
import os
import subprocess
import argparse
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
LLM_INPUT_FILE = SCRIPT_DIR / "data" / "llm_input.json"
VALID_EMOTIONS = ["amused", "angry", "disgusted", "drunk", "neutral", "sleepy", "surprised", "whisper"]
def parse_args():
parser = argparse.ArgumentParser(description="Voice Assistant Input Helper", add_help=True)
parser.add_argument("text", nargs="?", help="Eingabetext (optional, sonst interaktiv)")
parser.add_argument("--emotion", default="neutral", choices=VALID_EMOTIONS,
help="Emotion für die TTS-Ausgabe (Standard: neutral)")
parser.add_argument("--no-play", action="store_true",
help="Kein Audio abspielen, nur Datei erzeugen")
parser.add_argument("--json-only", action="store_true",
help="Nur JSON ausgeben, LLM nicht aufrufen")
parser.add_argument("--history", action="store_true",
help="Gesprächsverlauf aktivieren")
return parser.parse_args()
def build_payload(text: str, emotion: str, autoplay: bool, history: list) -> dict:
"""Erstellt das JSON-Payload für das LLM-Modul."""
return {
"request": text,
"type": "query",
"emotion": emotion,
"autoplay": autoplay,
"history": history,
}
def save_and_run(payload: dict, json_only: bool):
"""Speichert JSON und ruft LLM-Modul auf."""
LLM_INPUT_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(LLM_INPUT_FILE, "w", encoding="utf-8") as f:
json.dump(payload, f, ensure_ascii=False, indent=2)
if json_only:
print(json.dumps(payload, ensure_ascii=False, indent=2))
return None
llm_script = SCRIPT_DIR / "llm_module.py"
venv_python = Path.home() / ".venv" / "bin" / "python"
python_bin = str(venv_python) if venv_python.exists() else sys.executable
result = subprocess.run(
[python_bin, str(llm_script), str(LLM_INPUT_FILE)],
cwd=SCRIPT_DIR,
)
return result.returncode
def get_last_answer() -> str | None:
"""Liest die letzte LLM-Antwort aus der TTS-Input-Datei (für History)."""
tts_file = SCRIPT_DIR / "data" / "tts_input.json"
if tts_file.exists():
try:
with open(tts_file) as f:
data = json.load(f)
return data.get("text")
except Exception:
pass
return None
def interactive_loop(args):
"""Interaktiver Modus: Eingabe in Schleife."""
history = []
print("=" * 60)
print(" Thorsten Voice Assistant – Entwicklungs-Helper")
print("=" * 60)
print(f" Emotion: {args.emotion} | History: {'an' if args.history else 'aus'}")
print(" Tippe deine Anfrage oder 'exit' zum Beenden.")
print(" Befehle: /emotion <name> /history /clear /exit")
print("=" * 60)
emotion = args.emotion
while True:
try:
user_input = input("\n>> ").strip()
except (EOFError, KeyboardInterrupt):
print("\n[Helper] Beendet.")
break
if not user_input:
continue
# Meta-Befehle
if user_input.lower() in ("exit", "quit", "/exit"):
print("[Helper] Beendet.")
break
elif user_input.startswith("/emotion "):
new_emotion = user_input.split(" ", 1)[1].strip().lower()
if new_emotion in VALID_EMOTIONS:
emotion = new_emotion
print(f"[Helper] Emotion geändert zu: {emotion}")
else:
print(f"[Helper] Ungültige Emotion. Verfügbar: {', '.join(VALID_EMOTIONS)}")
continue
elif user_input == "/history":
print(f"[Helper] Verlauf ({len(history)} Einträge):")
for i, h in enumerate(history):
print(f" [{i}] User: {h.get('user', '')} → Assistant: {h.get('assistant', '')[:50]}...")
continue
elif user_input == "/clear":
history.clear()
print("[Helper] Verlauf gelöscht.")
continue
last_request = user_input
payload = build_payload(user_input, emotion, not args.no_play, history if args.history else [])
save_and_run(payload, args.json_only)
# History aktualisieren
if args.history:
answer = get_last_answer()
if answer:
history.append({"user": last_request, "assistant": answer})
# Max. 10 Einträge behalten
if len(history) > 10:
history = history[-10:]
def main():
args = parse_args()
if args.text:
# Einmaliger Aufruf
payload = build_payload(args.text, args.emotion, not args.no_play, [])
save_and_run(payload, args.json_only)
else:
# Interaktiver Modus
interactive_loop(args)
if __name__ == "__main__":
main()