diff --git a/aria-brain/agent.py b/aria-brain/agent.py index a2d2a09..07f4607 100644 --- a/aria-brain/agent.py +++ b/aria-brain/agent.py @@ -132,6 +132,34 @@ WEB_SEARCH_TOOL = { # Meta-Tool: ARIA kann selbst neue Skills bauen META_TOOLS = [ + { + "type": "function", + "function": { + "name": "ear_control", + "description": ( + "Schaltet dein Ohr (den Wake-Word-Listener) an oder aus. Nutze das, " + "wenn Stefan will dass du aufhoerst zuzuhoeren — EGAL wie er es " + "formuliert: 'leg dich schlafen', 'geh schlafen', 'Ohr aus', 'gute " + "Nacht', 'mach mal Pause vom Zuhoeren', 'ich geh ins Bett, du kannst " + "aus' → action='off'. Wenn er dich wieder aktivieren will ('Ohr an', " + "'wach auf', 'hoer wieder zu') → action='on'. Reiner Steuerbefehl: " + "die App stoppt/startet den Listener, du bestaetigst nur kurz (wird " + "nicht vorgelesen). Nach 'off' geht Wieder-An per 'Ohr an' (Text/" + "Aufnahme-Knopf) oder App-Button." + ), + "parameters": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": ["off", "on"], + "description": "off = Ohr aus (nicht mehr zuhoeren), on = Ohr wieder an", + }, + }, + "required": ["action"], + }, + }, + }, { "type": "function", "function": { @@ -2135,6 +2163,7 @@ class Agent: # Konversation bleiben gesprochen. self._claude_turn_speak = True self._claude_turn_converse = True # Default Gespraech; run_*-Skill setzt es + self._ear_control = None # ear_control-Tool: 'off'|'on' oder None try: for iteration in range(self.MAX_TOOL_ITERATIONS): result = self.proxy.chat_full(messages, tools=tools, @@ -2248,8 +2277,16 @@ class Agent: converse = False elif _wants_continue: converse = True + # ear_control-Tool aufgerufen → Ohr schalten. answered_by=wake-off/wake-on + # nutzt die bestehende Plumbing (main.py → wake_off/wake_on → Bridge → App). + # Reiner Steuerbefehl: still + kein Weiterlauschen. + answered_by = "claude" + if self._ear_control == "off": + answered_by, speak, converse = "wake-off", False, False + elif self._ear_control == "on": + answered_by, speak, converse = "wake-on", False, False # awaiting_reply = ARIA stellt eine blockierende Rueckfrage (Queue pausiert). - return (final_reply, "claude", speak, converse, awaiting_reply) + return (final_reply, answered_by, speak, converse, awaiting_reply) # ── Tool-Dispatcher ─────────────────────────────────────── @@ -2844,6 +2881,15 @@ class Agent: f"WICHTIG: Schreibe in deiner Antwort an Stefan den Pfad EXAKT als " f"Marker: [FILE: {result['path']}] — dann zeigt die App das Bild inline." ) + if name == "ear_control": + action = (arguments.get("action") or "").strip().lower() + if action not in ("off", "on"): + return "FEHLER: action muss 'off' oder 'on' sein." + self._ear_control = action + logger.info("[ear_control] action=%s — App schaltet den Listener", action) + return ("Ohr wird ausgeschaltet — Wieder-An per 'Ohr an' (Text/" + "Aufnahme-Knopf) oder App-Button." if action == "off" + else "Ohr wird wieder eingeschaltet.") if name == "memory_search": query = (arguments.get("query") or "").strip() if not query: diff --git a/aria-brain/prompts.py b/aria-brain/prompts.py index 7bdbf09..0c73cc7 100644 --- a/aria-brain/prompts.py +++ b/aria-brain/prompts.py @@ -199,6 +199,13 @@ def build_voice_flow_section() -> str: "- Ohne Marker = normales Gespraech: du wirst vorgelesen und ich lausche " "danach kurz weiter (Stefan kann einfach antworten, ohne 'Computer').", "- Nie widerspruechlich: `[[ENDE]]` schlaegt `[[WEITER]]`.", + "", + "**Ohr aus/an:** Wenn Stefan will dass du aufhoerst zuzuhoeren — egal wie " + "formuliert ('leg dich schlafen', 'geh schlafen', 'Ohr aus', 'gute Nacht', " + "'mach Pause vom Zuhoeren') — ruf das Tool `ear_control(action='off')`. " + "Wieder aktivieren ('Ohr an', 'wach auf', 'hoer wieder zu') → " + "`ear_control(action='on')`. Die App stoppt/startet dann den Listener; du " + "bestaetigst nur kurz.", ])