@agentunion/kite 1.0.7 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +208 -0
- package/README.md +48 -0
- package/cli.js +1 -1
- package/extensions/agents/__init__.py +1 -0
- package/extensions/agents/assistant/__init__.py +1 -0
- package/extensions/agents/assistant/entry.py +329 -0
- package/extensions/agents/assistant/module.md +22 -0
- package/extensions/agents/assistant/server.py +197 -0
- package/extensions/channels/__init__.py +1 -0
- package/extensions/channels/acp_channel/__init__.py +1 -0
- package/extensions/channels/acp_channel/entry.py +329 -0
- package/extensions/channels/acp_channel/module.md +22 -0
- package/extensions/channels/acp_channel/server.py +197 -0
- package/extensions/event_hub_bench/entry.py +624 -379
- package/extensions/event_hub_bench/module.md +2 -1
- package/extensions/services/backup/__init__.py +1 -0
- package/extensions/services/backup/entry.py +508 -0
- package/extensions/services/backup/module.md +22 -0
- package/extensions/services/model_service/__init__.py +1 -0
- package/extensions/services/model_service/entry.py +508 -0
- package/extensions/services/model_service/module.md +22 -0
- package/extensions/services/watchdog/entry.py +468 -102
- package/extensions/services/watchdog/module.md +3 -0
- package/extensions/services/watchdog/monitor.py +170 -69
- package/extensions/services/web/__init__.py +1 -0
- package/extensions/services/web/config.yaml +149 -0
- package/extensions/services/web/entry.py +390 -0
- package/extensions/services/web/module.md +24 -0
- package/extensions/services/web/routes/__init__.py +1 -0
- package/extensions/services/web/routes/routes_call.py +189 -0
- package/extensions/services/web/routes/routes_config.py +512 -0
- package/extensions/services/web/routes/routes_contacts.py +98 -0
- package/extensions/services/web/routes/routes_devlog.py +99 -0
- package/extensions/services/web/routes/routes_phone.py +81 -0
- package/extensions/services/web/routes/routes_sms.py +48 -0
- package/extensions/services/web/routes/routes_stats.py +17 -0
- package/extensions/services/web/routes/routes_voicechat.py +554 -0
- package/extensions/services/web/routes/schemas.py +216 -0
- package/extensions/services/web/server.py +375 -0
- package/extensions/services/web/static/css/style.css +1064 -0
- package/extensions/services/web/static/index.html +1445 -0
- package/extensions/services/web/static/js/app.js +4671 -0
- package/extensions/services/web/vendor/__init__.py +1 -0
- package/extensions/services/web/vendor/bluetooth/audio.py +348 -0
- package/extensions/services/web/vendor/bluetooth/contacts.py +251 -0
- package/extensions/services/web/vendor/bluetooth/manager.py +395 -0
- package/extensions/services/web/vendor/bluetooth/sms.py +290 -0
- package/extensions/services/web/vendor/bluetooth/telephony.py +274 -0
- package/extensions/services/web/vendor/config.py +139 -0
- package/extensions/services/web/vendor/conversation/asr.py +936 -0
- package/extensions/services/web/vendor/conversation/engine.py +548 -0
- package/extensions/services/web/vendor/conversation/llm.py +534 -0
- package/extensions/services/web/vendor/conversation/mcp_tools.py +190 -0
- package/extensions/services/web/vendor/conversation/tts.py +322 -0
- package/extensions/services/web/vendor/conversation/vad.py +138 -0
- package/extensions/services/web/vendor/storage/__init__.py +1 -0
- package/extensions/services/web/vendor/storage/identity.py +312 -0
- package/extensions/services/web/vendor/storage/store.py +507 -0
- package/extensions/services/web/vendor/task/manager.py +864 -0
- package/extensions/services/web/vendor/task/models.py +45 -0
- package/extensions/services/web/vendor/task/webhook.py +263 -0
- package/extensions/services/web/vendor/tools/registry.py +321 -0
- package/kernel/__init__.py +0 -0
- package/kernel/entry.py +407 -0
- package/{core/event_hub/hub.py → kernel/event_hub.py} +62 -74
- package/kernel/module.md +33 -0
- package/{core/registry/store.py → kernel/registry_store.py} +23 -8
- package/kernel/rpc_router.py +388 -0
- package/kernel/server.py +267 -0
- package/launcher/__init__.py +10 -0
- package/launcher/__main__.py +6 -0
- package/launcher/count_lines.py +258 -0
- package/launcher/entry.py +1778 -0
- package/launcher/logging_setup.py +289 -0
- package/{core/launcher → launcher}/module_scanner.py +11 -6
- package/launcher/process_manager.py +880 -0
- package/main.py +11 -210
- package/package.json +6 -9
- package/__init__.py +0 -1
- package/__main__.py +0 -15
- package/core/event_hub/BENCHMARK.md +0 -94
- package/core/event_hub/bench.py +0 -459
- package/core/event_hub/bench_extreme.py +0 -308
- package/core/event_hub/bench_perf.py +0 -350
- package/core/event_hub/entry.py +0 -157
- package/core/event_hub/module.md +0 -20
- package/core/event_hub/server.py +0 -206
- package/core/launcher/entry.py +0 -1158
- package/core/launcher/process_manager.py +0 -470
- package/core/registry/entry.py +0 -110
- package/core/registry/module.md +0 -30
- package/core/registry/server.py +0 -289
- package/extensions/services/watchdog/server.py +0 -167
- /package/{core → extensions/services/web/vendor/bluetooth}/__init__.py +0 -0
- /package/{core/event_hub → extensions/services/web/vendor/conversation}/__init__.py +0 -0
- /package/{core/launcher → extensions/services/web/vendor/task}/__init__.py +0 -0
- /package/{core/registry → extensions/services/web/vendor/tools}/__init__.py +0 -0
- /package/{core/event_hub → kernel}/dedup.py +0 -0
- /package/{core/event_hub → kernel}/router.py +0 -0
- /package/{core/launcher → launcher}/module.md +0 -0
|
@@ -9,8 +9,6 @@ import json
|
|
|
9
9
|
import time
|
|
10
10
|
from datetime import datetime, timezone
|
|
11
11
|
|
|
12
|
-
import httpx
|
|
13
|
-
|
|
14
12
|
|
|
15
13
|
# Module health states
|
|
16
14
|
HEALTHY = "healthy"
|
|
@@ -68,56 +66,56 @@ class HealthMonitor:
|
|
|
68
66
|
# Check intervals per resource state
|
|
69
67
|
INTERVALS = {NORMAL: 15, WARNING: 5, CRITICAL: 2}
|
|
70
68
|
|
|
71
|
-
def __init__(self, own_token: str,
|
|
72
|
-
publish_event=None):
|
|
69
|
+
def __init__(self, own_token: str, kernel_port: int, publish_event=None):
|
|
73
70
|
self.own_token = own_token
|
|
74
|
-
self.
|
|
75
|
-
self.launcher_url = launcher_url
|
|
71
|
+
self.kernel_port = kernel_port
|
|
76
72
|
self.publish_event = publish_event # async callable(event_dict)
|
|
73
|
+
self.rpc_call = None # set by entry.py: async callable(method, params)
|
|
77
74
|
self.modules: dict[str, ModuleStatus] = {}
|
|
78
75
|
self._running = False
|
|
79
76
|
self._psutil = None # lazy import
|
|
80
77
|
|
|
78
|
+
# Restart decision state (module.exiting / module.stopped / module.ready)
|
|
79
|
+
self._exit_intents: dict[str, str] = {} # module_id -> action from module.exiting
|
|
80
|
+
self._graceful_modules: dict[str, bool] = { # module_id -> supports graceful shutdown
|
|
81
|
+
"kernel": True, # started before Watchdog, default True
|
|
82
|
+
}
|
|
83
|
+
self._system_shutting_down = False
|
|
84
|
+
self._system_ready = False
|
|
85
|
+
self._system_ready_event = asyncio.Event()
|
|
86
|
+
self._crash_counts: dict[str, int] = {} # module_id -> consecutive crash count
|
|
87
|
+
|
|
81
88
|
# ── Module discovery ──
|
|
82
89
|
|
|
83
90
|
async def discover_modules(self):
|
|
84
|
-
"""Fetch monitored modules from Launcher
|
|
85
|
-
# Step 1: Get module list with monitor/pid from Launcher
|
|
86
|
-
monitored = {} # name ->
|
|
91
|
+
"""Fetch monitored modules from Launcher + Registry via RPC."""
|
|
92
|
+
# Step 1: Get module list with monitor/pid from Launcher via RPC
|
|
93
|
+
monitored = {} # name -> pid
|
|
87
94
|
try:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
)
|
|
92
|
-
if
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
continue
|
|
97
|
-
if m.get("actual_state", "").startswith("running"):
|
|
98
|
-
monitored[name] = m.get("pid")
|
|
95
|
+
resp = await self.rpc_call("launcher.list_modules", {})
|
|
96
|
+
result = resp.get("result", {})
|
|
97
|
+
for m in result.get("modules", []):
|
|
98
|
+
name = m.get("name", "")
|
|
99
|
+
if name == "watchdog" or not m.get("monitor", True):
|
|
100
|
+
continue
|
|
101
|
+
if m.get("actual_state", "").startswith("running"):
|
|
102
|
+
monitored[name] = m.get("pid")
|
|
99
103
|
except Exception as e:
|
|
100
|
-
print(f"[watchdog] Launcher
|
|
104
|
+
print(f"[watchdog] Launcher RPC failed: {e}")
|
|
101
105
|
return
|
|
102
106
|
|
|
103
|
-
# Step 2: Get health endpoints from Registry
|
|
107
|
+
# Step 2: Get health endpoints from Registry via RPC
|
|
104
108
|
health_map = {} # name -> {api_endpoint, health_endpoint}
|
|
105
|
-
headers = {"Authorization": f"Bearer {self.own_token}"}
|
|
106
109
|
try:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if mid in monitored:
|
|
117
|
-
health_map[mid] = {
|
|
118
|
-
"api_endpoint": entry.get("api_endpoint", ""),
|
|
119
|
-
"health_endpoint": entry.get("value", "/health"),
|
|
120
|
-
}
|
|
110
|
+
resp = await self.rpc_call("registry.lookup", {"field": "health_endpoint"})
|
|
111
|
+
result = resp.get("result", {})
|
|
112
|
+
for entry in result.get("results", []):
|
|
113
|
+
mid = entry.get("module", "")
|
|
114
|
+
if mid in monitored:
|
|
115
|
+
health_map[mid] = {
|
|
116
|
+
"api_endpoint": entry.get("api_endpoint", ""),
|
|
117
|
+
"health_endpoint": entry.get("value", "/health"),
|
|
118
|
+
}
|
|
121
119
|
except Exception:
|
|
122
120
|
pass
|
|
123
121
|
|
|
@@ -126,15 +124,21 @@ class HealthMonitor:
|
|
|
126
124
|
for mid, pid in monitored.items():
|
|
127
125
|
seen.add(mid)
|
|
128
126
|
h = health_map.get(mid, {})
|
|
127
|
+
ep = h.get("api_endpoint", "")
|
|
128
|
+
hp = h.get("health_endpoint", "/health")
|
|
129
129
|
if mid not in self.modules:
|
|
130
130
|
self.modules[mid] = ModuleStatus(
|
|
131
131
|
module_id=mid,
|
|
132
|
-
api_endpoint=
|
|
133
|
-
health_endpoint=
|
|
132
|
+
api_endpoint=ep,
|
|
133
|
+
health_endpoint=hp,
|
|
134
134
|
pid=pid,
|
|
135
135
|
)
|
|
136
136
|
else:
|
|
137
137
|
self.modules[mid].pid = pid
|
|
138
|
+
# Refresh endpoints once Registry has them
|
|
139
|
+
if ep:
|
|
140
|
+
self.modules[mid].api_endpoint = ep
|
|
141
|
+
self.modules[mid].health_endpoint = hp
|
|
138
142
|
|
|
139
143
|
for mid in list(self.modules):
|
|
140
144
|
if mid not in seen:
|
|
@@ -144,6 +148,8 @@ class HealthMonitor:
|
|
|
144
148
|
|
|
145
149
|
async def _check_one(self, status: ModuleStatus):
|
|
146
150
|
"""Check a single module's /health endpoint."""
|
|
151
|
+
if not status.api_endpoint:
|
|
152
|
+
return # Not yet registered in Registry, will be picked up on next discover
|
|
147
153
|
url = f"{status.api_endpoint}{status.health_endpoint}"
|
|
148
154
|
status.last_check = time.time()
|
|
149
155
|
|
|
@@ -197,28 +203,27 @@ class HealthMonitor:
|
|
|
197
203
|
# ── Restart via Launcher API ──
|
|
198
204
|
|
|
199
205
|
async def _restart_module(self, status: ModuleStatus):
|
|
200
|
-
"""Restart a module via Launcher
|
|
206
|
+
"""Restart a module via Launcher RPC."""
|
|
201
207
|
mid = status.module_id
|
|
202
208
|
print(f"[watchdog] Restarting {mid} (attempt {status.restarted_count + 1}/{self.MAX_RESTARTS})")
|
|
203
209
|
try:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
print(f"[watchdog] {mid} restart failed: HTTP {resp.status_code}")
|
|
210
|
+
resp = await self.rpc_call("launcher.restart_module", {
|
|
211
|
+
"name": mid,
|
|
212
|
+
"reason": "resource_critical" if status.resource_state == CRITICAL else "restart",
|
|
213
|
+
})
|
|
214
|
+
result = resp.get("result", {})
|
|
215
|
+
if result.get("status") == "restarted":
|
|
216
|
+
status.restarted_count += 1
|
|
217
|
+
status.fail_count = 0
|
|
218
|
+
print(f"[watchdog] {mid} restart requested")
|
|
219
|
+
if status.restarted_count >= self.ALERT_AFTER_RESTARTS:
|
|
220
|
+
await self._publish("watchdog.alert", {
|
|
221
|
+
"module_id": mid,
|
|
222
|
+
"restarted_count": status.restarted_count,
|
|
223
|
+
"message": f"{mid} has been restarted {status.restarted_count} times",
|
|
224
|
+
})
|
|
225
|
+
else:
|
|
226
|
+
print(f"[watchdog] {mid} restart failed: {result}")
|
|
222
227
|
except Exception as e:
|
|
223
228
|
print(f"[watchdog] {mid} restart error: {e}")
|
|
224
229
|
|
|
@@ -348,38 +353,134 @@ class HealthMonitor:
|
|
|
348
353
|
# ── Incoming event handler ──
|
|
349
354
|
|
|
350
355
|
async def handle_event(self, msg: dict):
|
|
351
|
-
"""Handle events from
|
|
356
|
+
"""Handle events from Kernel — restart decisions + health tracking."""
|
|
352
357
|
event_type = msg.get("event", "")
|
|
353
358
|
data = msg.get("data", {})
|
|
354
359
|
module_id = data.get("module_id", "")
|
|
355
360
|
|
|
361
|
+
if event_type == "system.ready":
|
|
362
|
+
print("[watchdog] Received system.ready")
|
|
363
|
+
self._system_ready = True
|
|
364
|
+
self._system_ready_event.set()
|
|
365
|
+
return
|
|
366
|
+
|
|
356
367
|
if not module_id or module_id == "watchdog":
|
|
357
368
|
return
|
|
358
369
|
|
|
359
370
|
if event_type == "module.started":
|
|
360
371
|
print(f"[watchdog] Received module.started: {module_id}")
|
|
361
|
-
|
|
372
|
+
self._crash_counts.pop(module_id, None)
|
|
362
373
|
await self.discover_modules()
|
|
363
374
|
|
|
364
375
|
elif event_type == "module.stopped":
|
|
365
376
|
print(f"[watchdog] Received module.stopped: {module_id}")
|
|
366
|
-
# Remove from tracking — it's gone
|
|
367
377
|
self.modules.pop(module_id, None)
|
|
378
|
+
await self._handle_module_stopped(module_id, data)
|
|
379
|
+
|
|
380
|
+
elif event_type == "module.exiting":
|
|
381
|
+
action = data.get("action", "none")
|
|
382
|
+
print(f"[watchdog] Received module.exiting: {module_id}, action={action}")
|
|
383
|
+
self._exit_intents[module_id] = action
|
|
384
|
+
|
|
385
|
+
elif event_type == "module.ready":
|
|
386
|
+
graceful = bool(data.get("graceful_shutdown"))
|
|
387
|
+
print(f"[watchdog] Received module.ready: {module_id}, graceful_shutdown={graceful}")
|
|
388
|
+
self._graceful_modules[module_id] = graceful
|
|
389
|
+
|
|
390
|
+
elif event_type == "module.shutdown":
|
|
391
|
+
reason = data.get("reason", "")
|
|
392
|
+
if reason == "system_shutdown":
|
|
393
|
+
print(f"[watchdog] Received system_shutdown signal")
|
|
394
|
+
self._system_shutting_down = True
|
|
395
|
+
|
|
396
|
+
async def _handle_module_stopped(self, module_id: str, data: dict):
|
|
397
|
+
"""Restart decision engine — called when module.stopped is received.
|
|
398
|
+
|
|
399
|
+
Priority:
|
|
400
|
+
1. System shutting down → no restart
|
|
401
|
+
2. Has exit_intent → follow the declared action (none/restart/restart_delay)
|
|
402
|
+
3. No intent (crash) → increment crash count, restart up to MAX_RESTARTS
|
|
403
|
+
"""
|
|
404
|
+
# Sync graceful_shutdown from Launcher (covers missed module.ready)
|
|
405
|
+
if "graceful_shutdown" in data:
|
|
406
|
+
self._graceful_modules[module_id] = bool(data["graceful_shutdown"])
|
|
407
|
+
|
|
408
|
+
if self._system_shutting_down:
|
|
409
|
+
print(f"[watchdog] {module_id} stopped during shutdown, skipping restart")
|
|
410
|
+
return
|
|
411
|
+
|
|
412
|
+
intent = self._exit_intents.pop(module_id, None)
|
|
413
|
+
if intent is not None:
|
|
414
|
+
if intent == "none":
|
|
415
|
+
print(f"[watchdog] {module_id} exited intentionally (action=none), no restart")
|
|
416
|
+
return
|
|
417
|
+
elif intent == "restart":
|
|
418
|
+
print(f"[watchdog] {module_id} requested restart, restarting now")
|
|
419
|
+
await self._restart_module_by_id(module_id, reason="module_requested")
|
|
420
|
+
return
|
|
421
|
+
elif intent == "restart_delay":
|
|
422
|
+
delay = 5
|
|
423
|
+
print(f"[watchdog] {module_id} requested delayed restart, waiting {delay}s")
|
|
424
|
+
await asyncio.sleep(delay)
|
|
425
|
+
await self._restart_module_by_id(module_id, reason="module_requested_delay")
|
|
426
|
+
return
|
|
427
|
+
else:
|
|
428
|
+
print(f"[watchdog] {module_id} exited with unknown action '{intent}', no restart")
|
|
429
|
+
return
|
|
430
|
+
|
|
431
|
+
# No exit intent → treat as crash
|
|
432
|
+
self._crash_counts[module_id] = self._crash_counts.get(module_id, 0) + 1
|
|
433
|
+
count = self._crash_counts[module_id]
|
|
434
|
+
exit_code = data.get("exit_code", -1)
|
|
435
|
+
|
|
436
|
+
if count <= self.MAX_RESTARTS:
|
|
437
|
+
print(f"[watchdog] {module_id} crashed (exit_code={exit_code}), restarting ({count}/{self.MAX_RESTARTS})")
|
|
438
|
+
await self._restart_module_by_id(module_id, reason="crash")
|
|
439
|
+
else:
|
|
440
|
+
print(f"[watchdog] {module_id} crashed {count} times, giving up")
|
|
441
|
+
await self._publish("watchdog.alert", {
|
|
442
|
+
"module_id": module_id,
|
|
443
|
+
"message": f"{module_id} exceeded {self.MAX_RESTARTS} crash restarts",
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
async def _restart_module_by_id(self, module_id: str, reason: str = "restart"):
|
|
447
|
+
"""Restart a module via Launcher RPC by module_id."""
|
|
448
|
+
print(f"[watchdog] Requesting restart for {module_id} (reason={reason})")
|
|
449
|
+
try:
|
|
450
|
+
resp = await self.rpc_call("launcher.restart_module", {
|
|
451
|
+
"name": module_id,
|
|
452
|
+
"reason": reason,
|
|
453
|
+
})
|
|
454
|
+
result = resp.get("result", {})
|
|
455
|
+
if result.get("status") == "restarted":
|
|
456
|
+
print(f"[watchdog] {module_id} restart requested successfully")
|
|
457
|
+
else:
|
|
458
|
+
print(f"[watchdog] {module_id} restart failed: {result}")
|
|
459
|
+
except Exception as e:
|
|
460
|
+
print(f"[watchdog] {module_id} restart error: {e}")
|
|
368
461
|
|
|
369
462
|
# ── Main loop ──
|
|
370
463
|
|
|
371
464
|
async def run(self):
|
|
372
|
-
"""Main monitoring loop with dynamic intervals per resource state.
|
|
465
|
+
"""Main monitoring loop with dynamic intervals per resource state.
|
|
466
|
+
Waits for system.ready before starting health checks."""
|
|
373
467
|
self._running = True
|
|
374
|
-
print("[watchdog] Monitor started")
|
|
375
|
-
|
|
468
|
+
print("[watchdog] Monitor started, waiting for system.ready...")
|
|
469
|
+
|
|
470
|
+
# Wait for system.ready (or manual start via API later)
|
|
471
|
+
while self._running and not self._system_ready:
|
|
472
|
+
try:
|
|
473
|
+
await asyncio.wait_for(self._system_ready_event.wait(), timeout=1.0)
|
|
474
|
+
except asyncio.TimeoutError:
|
|
475
|
+
continue
|
|
476
|
+
|
|
477
|
+
if not self._running:
|
|
478
|
+
return
|
|
479
|
+
print("[watchdog] system.ready received, starting health checks")
|
|
376
480
|
|
|
377
481
|
while self._running:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if now - last_discover >= 30:
|
|
381
|
-
await self.discover_modules()
|
|
382
|
-
last_discover = now
|
|
482
|
+
# Re-discover every cycle to pick up newly started/stopped modules
|
|
483
|
+
await self.discover_modules()
|
|
383
484
|
|
|
384
485
|
if self.modules:
|
|
385
486
|
tasks = []
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
user:
|
|
2
|
+
phone_number: "" # 当前 AI 使用的号码
|
|
3
|
+
phone_numbers: [] # AI 号码列表,可在配置页面管理
|
|
4
|
+
owners: [] # 主人列表,每项包含 phone/name 等基础信息
|
|
5
|
+
# 示例:
|
|
6
|
+
# phone_numbers:
|
|
7
|
+
# - "13800138000"
|
|
8
|
+
# - "13800138001"
|
|
9
|
+
# owners:
|
|
10
|
+
# - phone: "13900139000"
|
|
11
|
+
# name: "张三"
|
|
12
|
+
# note: "公司CEO"
|
|
13
|
+
# - phone: "13700137000"
|
|
14
|
+
# name: "李四"
|
|
15
|
+
|
|
16
|
+
server:
|
|
17
|
+
host: "0.0.0.0"
|
|
18
|
+
port: 18766
|
|
19
|
+
ssl: true
|
|
20
|
+
|
|
21
|
+
bluetooth:
|
|
22
|
+
auto_connect: true
|
|
23
|
+
reconnect_interval: 5
|
|
24
|
+
reconnect_max_interval: 60
|
|
25
|
+
device_address: ""
|
|
26
|
+
|
|
27
|
+
audio:
|
|
28
|
+
sample_rate: 16000
|
|
29
|
+
channels: 1
|
|
30
|
+
format: "s16le"
|
|
31
|
+
record_calls: true
|
|
32
|
+
|
|
33
|
+
llm:
|
|
34
|
+
active_provider: "openai"
|
|
35
|
+
providers:
|
|
36
|
+
openai:
|
|
37
|
+
base_url: "https://api.openai.com/v1"
|
|
38
|
+
api_key: ""
|
|
39
|
+
model: "gpt-4o"
|
|
40
|
+
temperature: 0.7
|
|
41
|
+
max_tokens: 1024
|
|
42
|
+
claude:
|
|
43
|
+
base_url: "https://api.anthropic.com/v1"
|
|
44
|
+
api_key: ""
|
|
45
|
+
model: "claude-sonnet-4-20250514"
|
|
46
|
+
temperature: 0.7
|
|
47
|
+
max_tokens: 1024
|
|
48
|
+
gemini:
|
|
49
|
+
base_url: "https://generativelanguage.googleapis.com/v1beta"
|
|
50
|
+
api_key: ""
|
|
51
|
+
model: "gemini-2.0-flash"
|
|
52
|
+
temperature: 0.7
|
|
53
|
+
max_tokens: 1024
|
|
54
|
+
|
|
55
|
+
asr:
|
|
56
|
+
provider: "whisper"
|
|
57
|
+
whisper:
|
|
58
|
+
base_url: "https://api.openai.com/v1"
|
|
59
|
+
api_key: ""
|
|
60
|
+
model: "whisper-1"
|
|
61
|
+
language: "zh"
|
|
62
|
+
xunfei:
|
|
63
|
+
app_id: ""
|
|
64
|
+
api_key: ""
|
|
65
|
+
api_secret: ""
|
|
66
|
+
volcengine:
|
|
67
|
+
app_id: ""
|
|
68
|
+
access_token: ""
|
|
69
|
+
resource_id: "volc.bigasr.sauc.duration"
|
|
70
|
+
ws_url: "wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async"
|
|
71
|
+
tencent:
|
|
72
|
+
app_id: ""
|
|
73
|
+
secret_id: ""
|
|
74
|
+
secret_key: ""
|
|
75
|
+
engine_model_type: "16k_zh_large"
|
|
76
|
+
|
|
77
|
+
tts:
|
|
78
|
+
provider: "edge-tts"
|
|
79
|
+
edge_tts:
|
|
80
|
+
voice: "zh-CN-XiaoxiaoNeural"
|
|
81
|
+
rate: "+0%"
|
|
82
|
+
volume: "+0%"
|
|
83
|
+
volcengine:
|
|
84
|
+
app_id: ""
|
|
85
|
+
access_token: ""
|
|
86
|
+
cluster: "volcano_tts"
|
|
87
|
+
voice_type: "BV001_streaming"
|
|
88
|
+
speed_ratio: 1.0
|
|
89
|
+
volume_ratio: 1.0
|
|
90
|
+
pitch_ratio: 1.0
|
|
91
|
+
encoding: "mp3"
|
|
92
|
+
sample_rate: 24000
|
|
93
|
+
tencent:
|
|
94
|
+
app_id: ""
|
|
95
|
+
secret_id: ""
|
|
96
|
+
secret_key: ""
|
|
97
|
+
voice_type: 101001
|
|
98
|
+
codec: "pcm"
|
|
99
|
+
sample_rate: 16000
|
|
100
|
+
speed: 0
|
|
101
|
+
volume: 0
|
|
102
|
+
azure:
|
|
103
|
+
api_key: ""
|
|
104
|
+
region: "eastasia"
|
|
105
|
+
voice: "zh-CN-XiaoxiaoNeural"
|
|
106
|
+
rate: "1.0"
|
|
107
|
+
volume: "100"
|
|
108
|
+
openai:
|
|
109
|
+
api_key: ""
|
|
110
|
+
base_url: "https://api.openai.com/v1"
|
|
111
|
+
model: "tts-1"
|
|
112
|
+
voice: "alloy"
|
|
113
|
+
speed: 1.0
|
|
114
|
+
aliyun:
|
|
115
|
+
access_key_id: ""
|
|
116
|
+
access_key_secret: ""
|
|
117
|
+
app_key: ""
|
|
118
|
+
voice: "zhixiaobai"
|
|
119
|
+
sample_rate: 16000
|
|
120
|
+
speech_rate: 0
|
|
121
|
+
volume: 50
|
|
122
|
+
local:
|
|
123
|
+
engine: "sherpa-onnx"
|
|
124
|
+
model_path: ""
|
|
125
|
+
voice: ""
|
|
126
|
+
sample_rate: 16000
|
|
127
|
+
|
|
128
|
+
vad:
|
|
129
|
+
mode: 3
|
|
130
|
+
energy_threshold: 300
|
|
131
|
+
silence_threshold_ms: 800
|
|
132
|
+
min_speech_ms: 250
|
|
133
|
+
|
|
134
|
+
call:
|
|
135
|
+
max_duration_seconds: 300
|
|
136
|
+
no_response_timeout: 15
|
|
137
|
+
no_response_max_retries: 3
|
|
138
|
+
incoming_confirm_timeout: 15
|
|
139
|
+
incoming_default_action: "reject"
|
|
140
|
+
opening_message: ""
|
|
141
|
+
|
|
142
|
+
webhook:
|
|
143
|
+
default_url: ""
|
|
144
|
+
retry_count: 3
|
|
145
|
+
retry_delays: [1, 5, 30]
|
|
146
|
+
timeout: 10
|
|
147
|
+
|
|
148
|
+
data:
|
|
149
|
+
base_dir: "./data"
|