Files
ARIAandClaude Sonnet 5 6a989d55e4 Fast-Path-Plugin raus, offizielle quick_commands rein (funktioniert auch in der TUI)
Root Cause fuers "geht trotzdem ans LLM": unser fast-paths-Plugin war auf
pre_gateway_dispatch registriert -- der Hook feuert laut Sourcecode nur fuer
die Gateway-Plattformen (Telegram/Discord/...), nicht fuer die TUI
(tui_gateway/server.py), die komplett am Gateway-Dispatcher vorbei direkt in
den Agent-Loop laeuft. Ein Patch an tui_gateway/server.py waere noetig
gewesen, aber Concurrency-kritischer Kern-Code -- zu riskant.

Hermes hat dafuer schon einen eigenen Mechanismus: quick_commands (type:
exec) bypassen den Agent-Loop nachweislich auf JEDER Plattform inkl. TUI,
kein LLM-Call, keine Tokens, offiziell dokumentiert. Trade-off: fester
Slash-Befehl statt Freitext (/next statt "naechstes Lied") -- gleiches
Prinzip wie Alexas Intent-Slots, ohne Slot-Fuellung.

scripts/spotify_quick.py buendelt next/previous/pause/play/volume_up/
volume_down/current in einem Skript (ein Argument pro Aktion), nutzt
denselben SpotifyClient wie der echte Tool-Dispatch. docker-compose.yml
mountet scripts/ jetzt read-only nach /opt/data/scripts statt fast-paths/
nach /opt/data/plugins/fast-paths.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 16:48:55 +00:00

132 lines
4.8 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
"""Zero-LLM Spotify-Transportsteuerung fuer Hermes' offizielle `quick_commands`.
Hintergrund: Wir hatten zuerst ein eigenes Fast-Path-Plugin gebaut (Regex vor
dem LLM abfangen), das ist wieder raus -- der Hook (`pre_gateway_dispatch`),
auf den es registriert war, feuert nur fuer die Gateway-Plattformen
(Telegram/Discord/...), NICHT fuer die TUI (siehe `gateway/run.py` vs.
`tui_gateway/server.py` im echten Hermes-Sourcecode). Ein eigener Patch an
`tui_gateway/server.py` waere noetig gewesen -- riskant, weil die Funktion
dort ~700 Zeilen Concurrency-kritischen Code enthaelt (History-Lock,
Busy-Queue, Session-State).
Hermes hat dafuer aber bereits einen EIGENEN, offiziellen Mechanismus:
`quick_commands` (type: exec) in config.yaml. Laut Sourcecode
(cli.py:process_command, tui_gateway/server.py, gateway/run.py) bypassen die
NACHWEISLICH den Agent-Loop komplett -- kein LLM-Call, keine Tokens -- und
funktionieren auf ALLEN Plattformen (CLI/TUI, Telegram, Discord, Slack,
WhatsApp, Signal, Email, Home Assistant), offiziell dokumentiert unter
website/docs/user-guide/configuration.md#quick-commands.
Der Haken (ehrlich, nicht schoengeredet): quick_commands reichen KEINE
Argumente durch -- jeder Befehl ist ein fester Slash-Befehl (z.B. "/next"),
kein Freitext wie ARIAs Regex-`fast_patterns`. Fuer feste Steuerbefehle
(naechstes Lied, Pause, lauter/leiser) reicht das aber exakt so gut wie
Alexas Intent-Slots -- nur ohne Slot-Fuellung. Freitext-Faelle ("spiel
Playlist X auf Geraet Y ab") bleiben bewusst beim LLM, weil das ohne Modell
so oder so nicht geht (siehe README).
Ein Skript, ein Argument pro Aktion -- vermeidet zehn Mini-Skripte. Nutzt
denselben `SpotifyClient` (plugins.spotify.client) wie der echte
Tool-Dispatch: gleiche OAuth-Session, kein doppelter Token-Code.
Aufruf (aus quick_commands heraus, IM Container):
python3 /opt/data/scripts/spotify_quick.py next
python3 /opt/data/scripts/spotify_quick.py previous
python3 /opt/data/scripts/spotify_quick.py pause
python3 /opt/data/scripts/spotify_quick.py play
python3 /opt/data/scripts/spotify_quick.py volume_up
python3 /opt/data/scripts/spotify_quick.py volume_down
python3 /opt/data/scripts/spotify_quick.py current
"""
from __future__ import annotations
import sys
try:
from plugins.spotify.client import (
SpotifyAPIError,
SpotifyAuthRequiredError,
SpotifyClient,
SpotifyError,
)
except ImportError as exc: # pragma: no cover
sys.exit(
"Konnte plugins.spotify.client nicht importieren -- Skript muss "
f"INNERHALB des hermes-agent-Containers laufen. Fehler: {exc}"
)
VOLUME_STEP = 10
def _current_volume(client: "SpotifyClient") -> int:
state = client.get_playback_state() or {}
device = state.get("device") or {}
vol = device.get("volume_percent")
return int(vol) if isinstance(vol, (int, float)) else 50
def run(action: str) -> str:
client = SpotifyClient()
if action == "next":
client.skip_next()
return "⏭ naechstes Lied"
if action == "previous":
client.skip_previous()
return "⏮ vorheriges Lied"
if action == "pause":
client.pause_playback()
return "⏸ pausiert"
if action == "play":
client.start_playback()
return "▶ weiter"
if action == "volume_up":
new_vol = min(100, _current_volume(client) + VOLUME_STEP)
client.set_volume(volume_percent=new_vol)
return f"🔊 Lautstaerke {new_vol}%"
if action == "volume_down":
new_vol = max(0, _current_volume(client) - VOLUME_STEP)
client.set_volume(volume_percent=new_vol)
return f"🔉 Lautstaerke {new_vol}%"
if action == "current":
state = client.get_currently_playing() or {}
item = state.get("item") or {}
name = item.get("name")
artists = ", ".join(a.get("name", "") for a in (item.get("artists") or []))
if not name:
return "Gerade laeuft nichts."
return f"Läuft: {name} {artists}" if artists else f"Läuft: {name}"
return (
f"Unbekannte Aktion '{action}'. Erlaubt: next, previous, pause, play, "
"volume_up, volume_down, current"
)
def main() -> None:
if len(sys.argv) < 2:
sys.exit("Usage: spotify_quick.py <next|previous|pause|play|volume_up|volume_down|current>")
action = sys.argv[1].strip().lower()
try:
print(run(action))
except SpotifyAuthRequiredError as exc:
sys.exit(f"Spotify nicht eingeloggt: {exc}")
except SpotifyAPIError as exc:
# Echten error.reason zitieren statt zu raten (z.B. NO_ACTIVE_DEVICE,
# ALREADY_PAUSED, PREMIUM_REQUIRED) -- gleiche Regel wie ueberall sonst.
sys.exit(f"Spotify-Fehler (status={exc.status_code}): {exc}")
except SpotifyError as exc:
sys.exit(f"Spotify-Fehler: {exc}")
if __name__ == "__main__":
main()