@agentunion/kite 1.0.7 → 1.2.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/core/event_hub/entry.py +305 -26
- package/core/event_hub/hub.py +8 -0
- package/core/event_hub/server.py +80 -17
- package/core/kite_log.py +241 -0
- package/core/launcher/entry.py +978 -284
- package/core/launcher/process_manager.py +456 -46
- package/core/registry/entry.py +272 -3
- package/core/registry/server.py +339 -289
- package/core/registry/store.py +10 -4
- package/extensions/agents/__init__.py +1 -0
- package/extensions/agents/assistant/__init__.py +1 -0
- package/extensions/agents/assistant/entry.py +380 -0
- package/extensions/agents/assistant/module.md +22 -0
- package/extensions/agents/assistant/server.py +236 -0
- package/extensions/channels/__init__.py +1 -0
- package/extensions/channels/acp_channel/__init__.py +1 -0
- package/extensions/channels/acp_channel/entry.py +380 -0
- package/extensions/channels/acp_channel/module.md +22 -0
- package/extensions/channels/acp_channel/server.py +236 -0
- package/extensions/event_hub_bench/entry.py +664 -379
- package/extensions/event_hub_bench/module.md +2 -1
- package/extensions/services/backup/__init__.py +1 -0
- package/extensions/services/backup/entry.py +380 -0
- package/extensions/services/backup/module.md +22 -0
- package/extensions/services/backup/server.py +244 -0
- package/extensions/services/model_service/__init__.py +1 -0
- package/extensions/services/model_service/entry.py +380 -0
- package/extensions/services/model_service/module.md +22 -0
- package/extensions/services/model_service/server.py +236 -0
- package/extensions/services/watchdog/entry.py +460 -147
- package/extensions/services/watchdog/module.md +3 -0
- package/extensions/services/watchdog/monitor.py +128 -13
- package/extensions/services/watchdog/server.py +75 -13
- package/extensions/services/web/__init__.py +1 -0
- package/extensions/services/web/config.yaml +149 -0
- package/extensions/services/web/entry.py +487 -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 +332 -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/__init__.py +0 -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/__init__.py +0 -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/__init__.py +0 -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/__init__.py +0 -0
- package/extensions/services/web/vendor/tools/registry.py +321 -0
- package/main.py +230 -90
- package/package.json +1 -1
|
@@ -78,6 +78,16 @@ class HealthMonitor:
|
|
|
78
78
|
self._running = False
|
|
79
79
|
self._psutil = None # lazy import
|
|
80
80
|
|
|
81
|
+
# Restart decision state (module.exiting / module.stopped / module.ready)
|
|
82
|
+
self._exit_intents: dict[str, str] = {} # module_id -> action from module.exiting
|
|
83
|
+
self._graceful_modules: dict[str, bool] = { # module_id -> supports graceful shutdown
|
|
84
|
+
"registry": True, "event_hub": True, # started before Watchdog, default True
|
|
85
|
+
}
|
|
86
|
+
self._system_shutting_down = False
|
|
87
|
+
self._system_ready = False
|
|
88
|
+
self._system_ready_event = asyncio.Event()
|
|
89
|
+
self._crash_counts: dict[str, int] = {} # module_id -> consecutive crash count
|
|
90
|
+
|
|
81
91
|
# ── Module discovery ──
|
|
82
92
|
|
|
83
93
|
async def discover_modules(self):
|
|
@@ -126,15 +136,21 @@ class HealthMonitor:
|
|
|
126
136
|
for mid, pid in monitored.items():
|
|
127
137
|
seen.add(mid)
|
|
128
138
|
h = health_map.get(mid, {})
|
|
139
|
+
ep = h.get("api_endpoint", "")
|
|
140
|
+
hp = h.get("health_endpoint", "/health")
|
|
129
141
|
if mid not in self.modules:
|
|
130
142
|
self.modules[mid] = ModuleStatus(
|
|
131
143
|
module_id=mid,
|
|
132
|
-
api_endpoint=
|
|
133
|
-
health_endpoint=
|
|
144
|
+
api_endpoint=ep,
|
|
145
|
+
health_endpoint=hp,
|
|
134
146
|
pid=pid,
|
|
135
147
|
)
|
|
136
148
|
else:
|
|
137
149
|
self.modules[mid].pid = pid
|
|
150
|
+
# Refresh endpoints once Registry has them
|
|
151
|
+
if ep:
|
|
152
|
+
self.modules[mid].api_endpoint = ep
|
|
153
|
+
self.modules[mid].health_endpoint = hp
|
|
138
154
|
|
|
139
155
|
for mid in list(self.modules):
|
|
140
156
|
if mid not in seen:
|
|
@@ -144,6 +160,8 @@ class HealthMonitor:
|
|
|
144
160
|
|
|
145
161
|
async def _check_one(self, status: ModuleStatus):
|
|
146
162
|
"""Check a single module's /health endpoint."""
|
|
163
|
+
if not status.api_endpoint:
|
|
164
|
+
return # Not yet registered in Registry, will be picked up on next discover
|
|
147
165
|
url = f"{status.api_endpoint}{status.health_endpoint}"
|
|
148
166
|
status.last_check = time.time()
|
|
149
167
|
|
|
@@ -348,38 +366,135 @@ class HealthMonitor:
|
|
|
348
366
|
# ── Incoming event handler ──
|
|
349
367
|
|
|
350
368
|
async def handle_event(self, msg: dict):
|
|
351
|
-
"""Handle events from Event Hub
|
|
369
|
+
"""Handle events from Event Hub — restart decisions + health tracking."""
|
|
352
370
|
event_type = msg.get("event", "")
|
|
353
371
|
data = msg.get("data", {})
|
|
354
372
|
module_id = data.get("module_id", "")
|
|
355
373
|
|
|
374
|
+
if event_type == "system.ready":
|
|
375
|
+
print("[watchdog] Received system.ready")
|
|
376
|
+
self._system_ready = True
|
|
377
|
+
self._system_ready_event.set()
|
|
378
|
+
return
|
|
379
|
+
|
|
356
380
|
if not module_id or module_id == "watchdog":
|
|
357
381
|
return
|
|
358
382
|
|
|
359
383
|
if event_type == "module.started":
|
|
360
384
|
print(f"[watchdog] Received module.started: {module_id}")
|
|
361
|
-
|
|
385
|
+
self._crash_counts.pop(module_id, None)
|
|
362
386
|
await self.discover_modules()
|
|
363
387
|
|
|
364
388
|
elif event_type == "module.stopped":
|
|
365
389
|
print(f"[watchdog] Received module.stopped: {module_id}")
|
|
366
|
-
# Remove from tracking — it's gone
|
|
367
390
|
self.modules.pop(module_id, None)
|
|
391
|
+
await self._handle_module_stopped(module_id, data)
|
|
392
|
+
|
|
393
|
+
elif event_type == "module.exiting":
|
|
394
|
+
action = data.get("action", "none")
|
|
395
|
+
print(f"[watchdog] Received module.exiting: {module_id}, action={action}")
|
|
396
|
+
self._exit_intents[module_id] = action
|
|
397
|
+
|
|
398
|
+
elif event_type == "module.ready":
|
|
399
|
+
graceful = bool(data.get("graceful_shutdown"))
|
|
400
|
+
print(f"[watchdog] Received module.ready: {module_id}, graceful_shutdown={graceful}")
|
|
401
|
+
self._graceful_modules[module_id] = graceful
|
|
402
|
+
|
|
403
|
+
elif event_type == "module.shutdown":
|
|
404
|
+
reason = data.get("reason", "")
|
|
405
|
+
if reason == "system_shutdown":
|
|
406
|
+
print(f"[watchdog] Received system_shutdown signal")
|
|
407
|
+
self._system_shutting_down = True
|
|
408
|
+
|
|
409
|
+
async def _handle_module_stopped(self, module_id: str, data: dict):
|
|
410
|
+
"""Restart decision engine — called when module.stopped is received.
|
|
411
|
+
|
|
412
|
+
Priority:
|
|
413
|
+
1. System shutting down → no restart
|
|
414
|
+
2. Has exit_intent → follow the declared action (none/restart/restart_delay)
|
|
415
|
+
3. No intent (crash) → increment crash count, restart up to MAX_RESTARTS
|
|
416
|
+
"""
|
|
417
|
+
# Sync graceful_shutdown from Launcher (covers missed module.ready)
|
|
418
|
+
if "graceful_shutdown" in data:
|
|
419
|
+
self._graceful_modules[module_id] = bool(data["graceful_shutdown"])
|
|
420
|
+
|
|
421
|
+
if self._system_shutting_down:
|
|
422
|
+
print(f"[watchdog] {module_id} stopped during shutdown, skipping restart")
|
|
423
|
+
return
|
|
424
|
+
|
|
425
|
+
intent = self._exit_intents.pop(module_id, None)
|
|
426
|
+
if intent is not None:
|
|
427
|
+
if intent == "none":
|
|
428
|
+
print(f"[watchdog] {module_id} exited intentionally (action=none), no restart")
|
|
429
|
+
return
|
|
430
|
+
elif intent == "restart":
|
|
431
|
+
print(f"[watchdog] {module_id} requested restart, restarting now")
|
|
432
|
+
await self._restart_module_by_id(module_id, reason="module_requested")
|
|
433
|
+
return
|
|
434
|
+
elif intent == "restart_delay":
|
|
435
|
+
delay = 5
|
|
436
|
+
print(f"[watchdog] {module_id} requested delayed restart, waiting {delay}s")
|
|
437
|
+
await asyncio.sleep(delay)
|
|
438
|
+
await self._restart_module_by_id(module_id, reason="module_requested_delay")
|
|
439
|
+
return
|
|
440
|
+
else:
|
|
441
|
+
print(f"[watchdog] {module_id} exited with unknown action '{intent}', no restart")
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
# No exit intent → treat as crash
|
|
445
|
+
self._crash_counts[module_id] = self._crash_counts.get(module_id, 0) + 1
|
|
446
|
+
count = self._crash_counts[module_id]
|
|
447
|
+
exit_code = data.get("exit_code", -1)
|
|
448
|
+
|
|
449
|
+
if count <= self.MAX_RESTARTS:
|
|
450
|
+
print(f"[watchdog] {module_id} crashed (exit_code={exit_code}), restarting ({count}/{self.MAX_RESTARTS})")
|
|
451
|
+
await self._restart_module_by_id(module_id, reason="crash")
|
|
452
|
+
else:
|
|
453
|
+
print(f"[watchdog] {module_id} crashed {count} times, giving up")
|
|
454
|
+
await self._publish("watchdog.alert", {
|
|
455
|
+
"module_id": module_id,
|
|
456
|
+
"message": f"{module_id} exceeded {self.MAX_RESTARTS} crash restarts",
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
async def _restart_module_by_id(self, module_id: str, reason: str = "restart"):
|
|
460
|
+
"""Restart a module via Launcher API by module_id."""
|
|
461
|
+
print(f"[watchdog] Requesting restart for {module_id} (reason={reason})")
|
|
462
|
+
try:
|
|
463
|
+
async with httpx.AsyncClient() as client:
|
|
464
|
+
resp = await client.post(
|
|
465
|
+
f"{self.launcher_url}/launcher/modules/{module_id}/restart",
|
|
466
|
+
json={"reason": reason},
|
|
467
|
+
timeout=15,
|
|
468
|
+
)
|
|
469
|
+
if resp.status_code == 200:
|
|
470
|
+
print(f"[watchdog] {module_id} restart requested successfully")
|
|
471
|
+
else:
|
|
472
|
+
print(f"[watchdog] {module_id} restart failed: HTTP {resp.status_code}")
|
|
473
|
+
except Exception as e:
|
|
474
|
+
print(f"[watchdog] {module_id} restart error: {e}")
|
|
368
475
|
|
|
369
476
|
# ── Main loop ──
|
|
370
477
|
|
|
371
478
|
async def run(self):
|
|
372
|
-
"""Main monitoring loop with dynamic intervals per resource state.
|
|
479
|
+
"""Main monitoring loop with dynamic intervals per resource state.
|
|
480
|
+
Waits for system.ready before starting health checks."""
|
|
373
481
|
self._running = True
|
|
374
|
-
print("[watchdog] Monitor started")
|
|
375
|
-
|
|
482
|
+
print("[watchdog] Monitor started, waiting for system.ready...")
|
|
483
|
+
|
|
484
|
+
# Wait for system.ready (or manual start via API later)
|
|
485
|
+
while self._running and not self._system_ready:
|
|
486
|
+
try:
|
|
487
|
+
await asyncio.wait_for(self._system_ready_event.wait(), timeout=1.0)
|
|
488
|
+
except asyncio.TimeoutError:
|
|
489
|
+
continue
|
|
490
|
+
|
|
491
|
+
if not self._running:
|
|
492
|
+
return
|
|
493
|
+
print("[watchdog] system.ready received, starting health checks")
|
|
376
494
|
|
|
377
495
|
while self._running:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if now - last_discover >= 30:
|
|
381
|
-
await self.discover_modules()
|
|
382
|
-
last_discover = now
|
|
496
|
+
# Re-discover every cycle to pick up newly started/stopped modules
|
|
497
|
+
await self.discover_modules()
|
|
383
498
|
|
|
384
499
|
if self.modules:
|
|
385
500
|
tasks = []
|
|
@@ -27,7 +27,10 @@ class WatchdogServer:
|
|
|
27
27
|
self._ws_task: asyncio.Task | None = None
|
|
28
28
|
self._heartbeat_task: asyncio.Task | None = None
|
|
29
29
|
self._ws: object | None = None
|
|
30
|
+
self._ready_sent = False
|
|
30
31
|
self._start_time = time.time()
|
|
32
|
+
self._uvicorn_server = None # set by entry.py for graceful shutdown
|
|
33
|
+
self._shutting_down = False
|
|
31
34
|
self.app = self._create_app()
|
|
32
35
|
|
|
33
36
|
# Wire up publish callback on monitor
|
|
@@ -80,29 +83,53 @@ class WatchdogServer:
|
|
|
80
83
|
|
|
81
84
|
async def _ws_loop(self):
|
|
82
85
|
"""Connect to Event Hub, subscribe, and listen. Reconnect on failure."""
|
|
83
|
-
|
|
86
|
+
retry_delay = 0.5 # start with 0.5s
|
|
87
|
+
max_delay = 30 # cap at 30s
|
|
88
|
+
while not self._shutting_down:
|
|
84
89
|
try:
|
|
85
90
|
await self._ws_connect()
|
|
91
|
+
retry_delay = 0.5 # reset on successful connection
|
|
86
92
|
except asyncio.CancelledError:
|
|
87
93
|
return
|
|
88
94
|
except Exception as e:
|
|
89
|
-
print(f"[watchdog] Event Hub connection error: {e}")
|
|
95
|
+
print(f"[watchdog] Event Hub connection error: {e}, retrying in {retry_delay:.1f}s")
|
|
90
96
|
self._ws = None
|
|
91
|
-
|
|
97
|
+
if self._shutting_down:
|
|
98
|
+
return
|
|
99
|
+
await asyncio.sleep(retry_delay)
|
|
100
|
+
retry_delay = min(retry_delay * 2, max_delay) # exponential backoff
|
|
92
101
|
|
|
93
102
|
async def _ws_connect(self):
|
|
94
103
|
"""Single WebSocket session: connect, subscribe, receive loop."""
|
|
95
|
-
url = f"{self.event_hub_ws}?token={self.token}"
|
|
96
|
-
|
|
104
|
+
url = f"{self.event_hub_ws}?token={self.token}&id=watchdog"
|
|
105
|
+
print(f"[watchdog] WS connecting to {self.event_hub_ws}")
|
|
106
|
+
async with websockets.connect(url, open_timeout=3, ping_interval=None, ping_timeout=None, close_timeout=10) as ws:
|
|
97
107
|
self._ws = ws
|
|
98
108
|
print("[watchdog] Connected to Event Hub")
|
|
99
109
|
|
|
100
110
|
# Subscribe to module lifecycle events
|
|
101
111
|
await ws.send(json.dumps({
|
|
102
112
|
"type": "subscribe",
|
|
103
|
-
"events": ["module.started", "module.stopped"],
|
|
113
|
+
"events": ["system.ready", "module.started", "module.stopped", "module.exiting", "module.ready", "module.shutdown"],
|
|
104
114
|
}))
|
|
105
115
|
|
|
116
|
+
# Send module.ready (once) so Launcher knows we're up
|
|
117
|
+
if not self._ready_sent:
|
|
118
|
+
from datetime import datetime, timezone
|
|
119
|
+
ready_msg = {
|
|
120
|
+
"type": "event",
|
|
121
|
+
"event_id": str(uuid.uuid4()),
|
|
122
|
+
"event": "module.ready",
|
|
123
|
+
"source": "watchdog",
|
|
124
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
125
|
+
"data": {
|
|
126
|
+
"module_id": "watchdog",
|
|
127
|
+
"graceful_shutdown": True,
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
await ws.send(json.dumps(ready_msg))
|
|
131
|
+
self._ready_sent = True
|
|
132
|
+
|
|
106
133
|
# Receive loop
|
|
107
134
|
async for raw in ws:
|
|
108
135
|
try:
|
|
@@ -110,13 +137,48 @@ class WatchdogServer:
|
|
|
110
137
|
except (json.JSONDecodeError, TypeError):
|
|
111
138
|
continue
|
|
112
139
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
140
|
+
try:
|
|
141
|
+
msg_type = msg.get("type", "")
|
|
142
|
+
if msg_type == "event":
|
|
143
|
+
event = msg.get("event", "")
|
|
144
|
+
data = msg.get("data") if isinstance(msg.get("data"), dict) else {}
|
|
145
|
+
if event == "module.shutdown" and data.get("module_id") == "watchdog":
|
|
146
|
+
await self._handle_shutdown(data)
|
|
147
|
+
return
|
|
148
|
+
await self.monitor.handle_event(msg)
|
|
149
|
+
elif msg_type == "ack":
|
|
150
|
+
pass # publish confirmed
|
|
151
|
+
elif msg_type == "error":
|
|
152
|
+
print(f"[watchdog] Event Hub error: {msg.get('message')}")
|
|
153
|
+
except Exception as e:
|
|
154
|
+
print(f"[watchdog] 事件处理异常(已忽略): {e}")
|
|
155
|
+
|
|
156
|
+
async def _handle_shutdown(self, data: dict):
|
|
157
|
+
"""Handle module.shutdown event — ack, cleanup, ready, exit."""
|
|
158
|
+
print("[watchdog] Received shutdown request")
|
|
159
|
+
self._shutting_down = True
|
|
160
|
+
# Step 1: Send ack
|
|
161
|
+
await self._publish_event({
|
|
162
|
+
"event": "module.shutdown.ack",
|
|
163
|
+
"data": {"module_id": "watchdog", "estimated_cleanup": 2},
|
|
164
|
+
})
|
|
165
|
+
# Step 2: Cleanup
|
|
166
|
+
self.monitor.stop()
|
|
167
|
+
if self._monitor_task:
|
|
168
|
+
self._monitor_task.cancel()
|
|
169
|
+
if self._heartbeat_task:
|
|
170
|
+
self._heartbeat_task.cancel()
|
|
171
|
+
if hasattr(self, '_test_task') and self._test_task:
|
|
172
|
+
self._test_task.cancel()
|
|
173
|
+
# Step 3: Send ready (before closing WS!)
|
|
174
|
+
await self._publish_event({
|
|
175
|
+
"event": "module.shutdown.ready",
|
|
176
|
+
"data": {"module_id": "watchdog"},
|
|
177
|
+
})
|
|
178
|
+
print("[watchdog] Shutdown ready, exiting")
|
|
179
|
+
# Step 4: Trigger uvicorn exit (WS will close when uvicorn shuts down)
|
|
180
|
+
if self._uvicorn_server:
|
|
181
|
+
self._uvicorn_server.should_exit = True
|
|
120
182
|
|
|
121
183
|
async def _publish_event(self, event: dict):
|
|
122
184
|
"""Publish an event to Event Hub via WebSocket."""
|
|
@@ -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"
|