-
Notifications
You must be signed in to change notification settings - Fork 328
Expand file tree
/
Copy pathmain.py
More file actions
161 lines (128 loc) · 6.61 KB
/
Copy pathmain.py
File metadata and controls
161 lines (128 loc) · 6.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import json
import re
from src.agent.capability import MatchingCapability
from src.main import AgentWorker
from src.agent.capability_worker import CapabilityWorker
AVAILABLE_STATS = {
"get_cpu": "CPU usage",
"get_memory": "Memory usage",
"get_temperature": "Device temperature",
"get_uptime": "Device uptime",
"get_wifi": "Wi-Fi connection",
"get_disk": "Disk usage",
"get_health": "Overall device health",
"get_all_stats": "Summary of all key metrics",
}
FUNCTIONS_DESCRIPTION = "\n".join(
f"- {name}: {description}" for name, description in AVAILABLE_STATS.items()
)
SYSTEM_PROMPT = f"""You are a request router for a DevKit telemetry Ability. Your sole responsibility is to map user input to exactly one function name. You do not answer questions, explain concepts, or generate conversational responses.
## Device Context
The OpenHome DevKit is the user's locally connected device. Telemetry refers to its live runtime metrics: CPU, memory, temperature, uptime, Wi-Fi, disk, and health. This Ability is limited strictly to the functions listed below.
## Response Format
Always return a single JSON object. No prose, no markdown, no extra keys.
{{"function_name": "<function_name | none | exit>"}}
## Available Functions
{FUNCTIONS_DESCRIPTION}
## Routing Rules
- General status, "all stats", "everything", "snapshot", "system info" -> get_all_stats
- CPU, processor, load, compute, busy, usage -> get_cpu
- Memory, RAM, available memory, used memory -> get_memory
- Temperature, temp, heat, thermal, hot, warm -> get_temperature
- Uptime, boot time, running time, how long running -> get_uptime
- Wi-Fi, wifi, network, SSID, connection -> get_wifi
- Disk, storage, free space, used space -> get_disk
- Health, diagnostics, issues, problems, anything wrong -> get_health
## Exit Routing
Trigger `exit` when the user says: stop, quit, cancel, end, done, all done, that's all, thank you, thanks, goodbye, bye — or any close variation, even with filler words.
## Unsupported Requests
If the request is unrelated to DevKit telemetry, or asks for telemetry not covered by any available function, return:
{{"function_name": "none"}}
## Hard Rules
- Return exactly one function_name per response.
- Never explain, define, or discuss any concept — even if directly asked.
- Route by intent: if the user asks "what is my CPU usage?" that is a CPU telemetry request -> get_cpu.
- Do not include any text outside the JSON object.
"""
class DevkitStatsCapability(MatchingCapability):
worker: AgentWorker = None
capability_worker: CapabilityWorker = None
#{{register capability}}
async def first_function(self):
try:
is_first_turn = True
conversation_history = []
while True:
if is_first_turn:
user_message = await self.capability_worker.wait_for_complete_transcription()
else:
user_message = await self.capability_worker.user_response()
if not user_message or not user_message.strip():
continue
route = self._route_to_devkit_function(user_message, conversation_history)
function_name = route.get("function_name", "")
if is_first_turn and function_name in ("", "none"):
function_name = "get_all_stats"
if function_name == "exit":
await self.capability_worker.speak("Exiting DevKit stats.")
break
if function_name not in AVAILABLE_STATS:
await self.capability_worker.speak(
"I can't fetch that DevKit information. Try asking for CPU, memory, temperature, disk, uptime, Wi-Fi, or health."
)
is_first_turn = False
continue
result = await self.capability_worker.send_devkit_capability_action(
function_name=function_name,
args=[],
timeout=8,
)
spoken_message = self._spoken_response_from_result(result)
await self.capability_worker.speak(spoken_message)
conversation_history.append({"role": "user", "content": user_message})
conversation_history.append({"role": "assistant", "content": spoken_message})
conversation_history = conversation_history[-12:]
await self.capability_worker.speak("Want me to check anything else, or say stop to exit.")
is_first_turn = False
except Exception as error:
self.worker.editor_logging_handler.error(f"DevKit stats failed: {error}")
await self.capability_worker.speak("Something went wrong while checking DevKit stats.")
finally:
self.capability_worker.resume_normal_flow()
def _route_to_devkit_function(self, user_message, conversation_history):
response = self.capability_worker.text_to_text_response(
f'User request: "{user_message}"',
conversation_history,
system_prompt=SYSTEM_PROMPT,
)
cleaned = re.sub(r"^```[a-zA-Z]*\n|\n```$", "", response.strip())
try:
return json.loads(cleaned)
except (json.JSONDecodeError, TypeError, ValueError):
return {"function_name": ""}
def _spoken_response_from_result(self, result):
if not isinstance(result, dict):
return "I couldn't reach the DevKit."
if not result.get("success"):
self.worker.editor_logging_handler.error(
f"DevKit call failed: {result.get('error')}"
)
return "I couldn't fetch that DevKit information. Try asking for another stat."
output = (result.get("output") or "").strip()
if not output:
return "I couldn't fetch that DevKit information. Try asking for another stat."
try:
payload = json.loads(output)
except json.JSONDecodeError:
self.worker.editor_logging_handler.error(f"Invalid DevKit output: {output}")
return "I couldn't read the DevKit response."
if not payload.get("success"):
error = payload.get("error") or {}
self.worker.editor_logging_handler.warning(
f"DevKit stat unavailable: {error.get('code')} {error.get('message')}"
)
return payload.get("spoken_response") or "I couldn't read that DevKit stat."
def call(self, worker: AgentWorker):
self.worker = worker
self.capability_worker = CapabilityWorker(self)
self.worker.session_tasks.create(self.first_function())