Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
629a3d82ac | ||
|
|
9bb90a777a | ||
|
|
c3bb7985a2 | ||
|
|
debd96b16f |
@@ -79,8 +79,8 @@ android {
|
|||||||
applicationId "com.ariacockpit"
|
applicationId "com.ariacockpit"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 20102
|
versionCode 20103
|
||||||
versionName "0.2.1.2"
|
versionName "0.2.1.3"
|
||||||
// Fallback fuer Libraries mit Product Flavors
|
// Fallback fuer Libraries mit Product Flavors
|
||||||
missingDimensionStrategy 'react-native-camera', 'general'
|
missingDimensionStrategy 'react-native-camera', 'general'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aria-cockpit",
|
"name": "aria-cockpit",
|
||||||
"version": "0.2.1.2",
|
"version": "0.2.1.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
|
|||||||
@@ -1537,9 +1537,15 @@ const ChatScreen: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
// Kein Speech im Window → Konversation beenden
|
// Kein Speech im Window → Konversation beenden
|
||||||
console.log('[Chat] STT-Endpoint ohne Text (reason=%s) — endConversation', ev.reason);
|
console.log('[Chat] STT-Endpoint ohne Text (reason=%s) — endConversation', ev.reason);
|
||||||
// Placeholder-Bubble wieder weg
|
// NUR den noch UNAUFGELOESTEN Platzhalter entfernen. Nach manuellem Stop
|
||||||
|
// klappt oft ein zweites, LEERES stream_end-Endpoint nach (das Audio war
|
||||||
|
// schon vom echten Endpoint transkribiert) — das darf die bereits mit
|
||||||
|
// dem STT-Text gefuellte Bubble NICHT loeschen, sonst verschwindet die
|
||||||
|
// Sprachnachricht obwohl ARIA schon an der Antwort arbeitet.
|
||||||
if (ev.audioRequestId) {
|
if (ev.audioRequestId) {
|
||||||
setMessages(prev => prev.filter(m => m.audioRequestId !== ev.audioRequestId));
|
setMessages(prev => prev.filter(m =>
|
||||||
|
!(m.audioRequestId === ev.audioRequestId
|
||||||
|
&& m.text.includes('Spracheingabe wird verarbeitet'))));
|
||||||
}
|
}
|
||||||
// Bei Passive-Listen + speaker_mismatch oder no-speech: erneut passiv
|
// Bei Passive-Listen + speaker_mismatch oder no-speech: erneut passiv
|
||||||
// lauschen (Timer im wakeword-service laeuft weiter, regelt das Ende).
|
// lauschen (Timer im wakeword-service laeuft weiter, regelt das Ende).
|
||||||
|
|||||||
+62
-3
@@ -284,6 +284,27 @@ META_TOOLS = [
|
|||||||
"parameters": {"type": "object", "properties": {}},
|
"parameters": {"type": "object", "properties": {}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "skill_get",
|
||||||
|
"description": (
|
||||||
|
"Liest einen EXISTIERENDEN Skill VOLLSTAENDIG: Manifest (inkl. "
|
||||||
|
"args, fast_patterns, speak/converse) + kompletter entry_code "
|
||||||
|
"(der echte Python-Code) + README. IMMER vor `skill_update` "
|
||||||
|
"aufrufen, damit du den bestehenden Code SIEHST und ihn gezielt "
|
||||||
|
"aenderst statt blind zu ueberschreiben (Blind-Rewrite killt "
|
||||||
|
"leicht funktionierende Teile!)."
|
||||||
|
),
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string", "description": "Skill-Name."},
|
||||||
|
},
|
||||||
|
"required": ["name"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
@@ -1225,8 +1246,12 @@ class Agent:
|
|||||||
continue
|
continue
|
||||||
args = pat.get("args") or {}
|
args = pat.get("args") or {}
|
||||||
reply = pat.get("reply") or f"{skill_name}: ok"
|
reply = pat.get("reply") or f"{skill_name}: ok"
|
||||||
# Vorlesen? Folgt dem Skill-Manifest (Default False=Steuerbefehl).
|
# Vorlesen/Weiterlauschen folgen dem Skill — GENAU wie bei local/
|
||||||
|
# Claude: Manifest-Default, vom Skill-Output pro Aufruf ueber-
|
||||||
|
# schreibbar. Also NICHT generell converse=False: ein Fast-Path-
|
||||||
|
# Skill darf ein Dialog-Skill sein.
|
||||||
self._fast_path_speak = bool(skill.get("speak", False))
|
self._fast_path_speak = bool(skill.get("speak", False))
|
||||||
|
self._fast_path_converse = bool(skill.get("converse", False))
|
||||||
logger.info("[fast-path] match skill=%s pattern=%r msg=%r",
|
logger.info("[fast-path] match skill=%s pattern=%r msg=%r",
|
||||||
skill_name, rx, user_message[:60])
|
skill_name, rx, user_message[:60])
|
||||||
try:
|
try:
|
||||||
@@ -1239,6 +1264,16 @@ class Agent:
|
|||||||
tail = (res.get("stderr") or res.get("stdout") or "").strip().splitlines()
|
tail = (res.get("stderr") or res.get("stdout") or "").strip().splitlines()
|
||||||
hint = (tail[-1] if tail else "")[:120]
|
hint = (tail[-1] if tail else "")[:120]
|
||||||
return f"{skill_name}: {reply} — Fehler: {hint or 'siehe Brain-Log'}"
|
return f"{skill_name}: {reply} — Fehler: {hint or 'siehe Brain-Log'}"
|
||||||
|
# Per-Aufruf-Override aus dem Skill-JSON-Output (falls vorhanden).
|
||||||
|
try:
|
||||||
|
_j = json.loads((res.get("stdout") or "").strip())
|
||||||
|
if isinstance(_j, dict):
|
||||||
|
if isinstance(_j.get("speak"), bool):
|
||||||
|
self._fast_path_speak = _j["speak"]
|
||||||
|
if isinstance(_j.get("converse"), bool):
|
||||||
|
self._fast_path_converse = _j["converse"]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return reply
|
return reply
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -1507,8 +1542,9 @@ class Agent:
|
|||||||
# Vorlesen folgt dem Skill-Manifest (speak): Steuerbefehl=stumm,
|
# Vorlesen folgt dem Skill-Manifest (speak): Steuerbefehl=stumm,
|
||||||
# Antwort-Skill=vorlesen. Default False (reiner Steuerbefehl).
|
# Antwort-Skill=vorlesen. Default False (reiner Steuerbefehl).
|
||||||
speak = bool(getattr(self, "_fast_path_speak", False))
|
speak = bool(getattr(self, "_fast_path_speak", False))
|
||||||
# Fast-Path = reiner Steuerbefehl → nie ins 30s-Gespraech (converse=False).
|
# converse folgt dem Skill (Manifest/Output) — nicht mehr generell False.
|
||||||
return fast_reply, "fast-path", speak, False
|
converse = bool(getattr(self, "_fast_path_converse", False))
|
||||||
|
return fast_reply, "fast-path", speak, converse
|
||||||
|
|
||||||
# 1. User-Turn an die Konversation
|
# 1. User-Turn an die Konversation
|
||||||
self.conversation.add("user", user_message, source=source,
|
self.conversation.add("user", user_message, source=source,
|
||||||
@@ -1817,6 +1853,29 @@ class Agent:
|
|||||||
f"- {s['name']} ({s['execution']}) {'aktiv' if s.get('active', True) else 'DEAKTIVIERT'}: {s.get('description', '')}"
|
f"- {s['name']} ({s['execution']}) {'aktiv' if s.get('active', True) else 'DEAKTIVIERT'}: {s.get('description', '')}"
|
||||||
for s in items
|
for s in items
|
||||||
)
|
)
|
||||||
|
if name == "skill_get":
|
||||||
|
sk_name = (arguments.get("name") or "").strip()
|
||||||
|
if not sk_name:
|
||||||
|
return "FEHLER: name ist Pflicht."
|
||||||
|
src = skills_mod.read_skill_source(sk_name)
|
||||||
|
if src is None:
|
||||||
|
return f"FEHLER: Skill '{sk_name}' nicht gefunden."
|
||||||
|
m = src["manifest"]
|
||||||
|
# Manifest kompakt + kompletter Code, damit ARIA gezielt aendern kann.
|
||||||
|
meta = {
|
||||||
|
"name": m.get("name"), "description": m.get("description"),
|
||||||
|
"execution": m.get("execution"), "entry": m.get("entry"),
|
||||||
|
"active": m.get("active"), "args": m.get("args"),
|
||||||
|
"requires": m.get("requires"),
|
||||||
|
"config_schema": m.get("config_schema"),
|
||||||
|
"fast_patterns": m.get("fast_patterns"),
|
||||||
|
"speak": m.get("speak"), "converse": m.get("converse"),
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
f"MANIFEST:\n{json.dumps(meta, ensure_ascii=False, indent=2)}\n\n"
|
||||||
|
f"ENTRY-CODE ({src['entry']}):\n{src['entry_code']}\n\n"
|
||||||
|
f"README:\n{src.get('readme') or '(leer)'}"
|
||||||
|
)
|
||||||
if name == "skill_update":
|
if name == "skill_update":
|
||||||
skill_name = (arguments.get("name") or "").strip()
|
skill_name = (arguments.get("name") or "").strip()
|
||||||
if not skill_name:
|
if not skill_name:
|
||||||
|
|||||||
@@ -139,6 +139,26 @@ def read_manifest(name: str) -> Optional[dict]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def read_skill_source(name: str) -> Optional[dict]:
|
||||||
|
"""Manifest + kompletter entry_code + README eines Skills. Damit ARIA einen
|
||||||
|
Skill LESEN kann bevor sie ihn per skill_update aendert (sonst Blind-Rewrite,
|
||||||
|
der bestehende Funktionen killt)."""
|
||||||
|
m = read_manifest(name)
|
||||||
|
if m is None:
|
||||||
|
return None
|
||||||
|
d = _skill_dir(name)
|
||||||
|
entry = m.get("entry", "run.sh")
|
||||||
|
try:
|
||||||
|
code = (d / entry).read_text(encoding="utf-8")
|
||||||
|
except Exception as exc:
|
||||||
|
code = f"(entry-Datei '{entry}' nicht lesbar: {exc})"
|
||||||
|
try:
|
||||||
|
readme = (d / "README.md").read_text(encoding="utf-8")
|
||||||
|
except Exception:
|
||||||
|
readme = ""
|
||||||
|
return {"manifest": m, "entry": entry, "entry_code": code, "readme": readme}
|
||||||
|
|
||||||
|
|
||||||
def write_manifest(name: str, manifest: dict) -> None:
|
def write_manifest(name: str, manifest: dict) -> None:
|
||||||
d = _skill_dir(name)
|
d = _skill_dir(name)
|
||||||
d.mkdir(parents=True, exist_ok=True)
|
d.mkdir(parents=True, exist_ok=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user