@agentunion/kite 1.0.6 → 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.
Files changed (112) hide show
  1. package/cli.js +127 -25
  2. package/core/event_hub/entry.py +384 -61
  3. package/core/event_hub/hub.py +8 -0
  4. package/core/event_hub/module.md +0 -1
  5. package/core/event_hub/server.py +169 -38
  6. package/core/kite_log.py +241 -0
  7. package/core/launcher/entry.py +1306 -425
  8. package/core/launcher/module_scanner.py +10 -9
  9. package/core/launcher/process_manager.py +555 -121
  10. package/core/registry/entry.py +335 -30
  11. package/core/registry/server.py +339 -256
  12. package/core/registry/store.py +13 -2
  13. package/extensions/agents/__init__.py +1 -0
  14. package/extensions/agents/assistant/__init__.py +1 -0
  15. package/extensions/agents/assistant/entry.py +380 -0
  16. package/extensions/agents/assistant/module.md +22 -0
  17. package/extensions/agents/assistant/server.py +236 -0
  18. package/extensions/channels/__init__.py +1 -0
  19. package/extensions/channels/acp_channel/__init__.py +1 -0
  20. package/extensions/channels/acp_channel/entry.py +380 -0
  21. package/extensions/channels/acp_channel/module.md +22 -0
  22. package/extensions/channels/acp_channel/server.py +236 -0
  23. package/{core → extensions}/event_hub_bench/entry.py +664 -371
  24. package/{core → extensions}/event_hub_bench/module.md +4 -2
  25. package/extensions/services/backup/__init__.py +1 -0
  26. package/extensions/services/backup/entry.py +380 -0
  27. package/extensions/services/backup/module.md +22 -0
  28. package/extensions/services/backup/server.py +244 -0
  29. package/extensions/services/model_service/__init__.py +1 -0
  30. package/extensions/services/model_service/entry.py +380 -0
  31. package/extensions/services/model_service/module.md +22 -0
  32. package/extensions/services/model_service/server.py +236 -0
  33. package/extensions/services/watchdog/entry.py +460 -143
  34. package/extensions/services/watchdog/module.md +3 -0
  35. package/extensions/services/watchdog/monitor.py +128 -13
  36. package/extensions/services/watchdog/server.py +75 -13
  37. package/extensions/services/web/__init__.py +1 -0
  38. package/extensions/services/web/config.yaml +149 -0
  39. package/extensions/services/web/entry.py +487 -0
  40. package/extensions/services/web/module.md +24 -0
  41. package/extensions/services/web/routes/__init__.py +1 -0
  42. package/extensions/services/web/routes/routes_call.py +189 -0
  43. package/extensions/services/web/routes/routes_config.py +512 -0
  44. package/extensions/services/web/routes/routes_contacts.py +98 -0
  45. package/extensions/services/web/routes/routes_devlog.py +99 -0
  46. package/extensions/services/web/routes/routes_phone.py +81 -0
  47. package/extensions/services/web/routes/routes_sms.py +48 -0
  48. package/extensions/services/web/routes/routes_stats.py +17 -0
  49. package/extensions/services/web/routes/routes_voicechat.py +554 -0
  50. package/extensions/services/web/routes/schemas.py +216 -0
  51. package/extensions/services/web/server.py +332 -0
  52. package/extensions/services/web/static/css/style.css +1064 -0
  53. package/extensions/services/web/static/index.html +1445 -0
  54. package/extensions/services/web/static/js/app.js +4671 -0
  55. package/extensions/services/web/vendor/__init__.py +1 -0
  56. package/extensions/services/web/vendor/bluetooth/audio.py +348 -0
  57. package/extensions/services/web/vendor/bluetooth/contacts.py +251 -0
  58. package/extensions/services/web/vendor/bluetooth/manager.py +395 -0
  59. package/extensions/services/web/vendor/bluetooth/sms.py +290 -0
  60. package/extensions/services/web/vendor/bluetooth/telephony.py +274 -0
  61. package/extensions/services/web/vendor/config.py +139 -0
  62. package/extensions/services/web/vendor/conversation/__init__.py +0 -0
  63. package/extensions/services/web/vendor/conversation/asr.py +936 -0
  64. package/extensions/services/web/vendor/conversation/engine.py +548 -0
  65. package/extensions/services/web/vendor/conversation/llm.py +534 -0
  66. package/extensions/services/web/vendor/conversation/mcp_tools.py +190 -0
  67. package/extensions/services/web/vendor/conversation/tts.py +322 -0
  68. package/extensions/services/web/vendor/conversation/vad.py +138 -0
  69. package/extensions/services/web/vendor/storage/__init__.py +1 -0
  70. package/extensions/services/web/vendor/storage/identity.py +312 -0
  71. package/extensions/services/web/vendor/storage/store.py +507 -0
  72. package/extensions/services/web/vendor/task/__init__.py +0 -0
  73. package/extensions/services/web/vendor/task/manager.py +864 -0
  74. package/extensions/services/web/vendor/task/models.py +45 -0
  75. package/extensions/services/web/vendor/task/webhook.py +263 -0
  76. package/extensions/services/web/vendor/tools/__init__.py +0 -0
  77. package/extensions/services/web/vendor/tools/registry.py +321 -0
  78. package/main.py +344 -4
  79. package/package.json +11 -2
  80. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  81. package/core/__pycache__/data_dir.cpython-313.pyc +0 -0
  82. package/core/data_dir.py +0 -62
  83. package/core/event_hub/__pycache__/__init__.cpython-313.pyc +0 -0
  84. package/core/event_hub/__pycache__/bench.cpython-313.pyc +0 -0
  85. package/core/event_hub/__pycache__/bench_perf.cpython-313.pyc +0 -0
  86. package/core/event_hub/__pycache__/dedup.cpython-313.pyc +0 -0
  87. package/core/event_hub/__pycache__/entry.cpython-313.pyc +0 -0
  88. package/core/event_hub/__pycache__/hub.cpython-313.pyc +0 -0
  89. package/core/event_hub/__pycache__/router.cpython-313.pyc +0 -0
  90. package/core/event_hub/__pycache__/server.cpython-313.pyc +0 -0
  91. package/core/event_hub/bench_results/2026-02-28_13-26-48.json +0 -51
  92. package/core/event_hub/bench_results/2026-02-28_13-44-45.json +0 -51
  93. package/core/event_hub/bench_results/2026-02-28_13-45-39.json +0 -51
  94. package/core/launcher/__pycache__/__init__.cpython-313.pyc +0 -0
  95. package/core/launcher/__pycache__/entry.cpython-313.pyc +0 -0
  96. package/core/launcher/__pycache__/module_scanner.cpython-313.pyc +0 -0
  97. package/core/launcher/__pycache__/process_manager.cpython-313.pyc +0 -0
  98. package/core/launcher/data/log/lifecycle.jsonl +0 -1158
  99. package/core/launcher/data/token.txt +0 -1
  100. package/core/registry/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/core/registry/__pycache__/entry.cpython-313.pyc +0 -0
  102. package/core/registry/__pycache__/server.cpython-313.pyc +0 -0
  103. package/core/registry/__pycache__/store.cpython-313.pyc +0 -0
  104. package/core/registry/data/port.txt +0 -1
  105. package/core/registry/data/port_484.txt +0 -1
  106. package/extensions/__pycache__/__init__.cpython-313.pyc +0 -0
  107. package/extensions/services/__pycache__/__init__.cpython-313.pyc +0 -0
  108. package/extensions/services/watchdog/__pycache__/__init__.cpython-313.pyc +0 -0
  109. package/extensions/services/watchdog/__pycache__/entry.cpython-313.pyc +0 -0
  110. package/extensions/services/watchdog/__pycache__/monitor.cpython-313.pyc +0 -0
  111. package/extensions/services/watchdog/__pycache__/server.cpython-313.pyc +0 -0
  112. /package/{core/event_hub/bench_results/.gitkeep → extensions/services/web/vendor/bluetooth/__init__.py} +0 -0
@@ -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=h.get("api_endpoint", ""),
133
- health_endpoint=h.get("health_endpoint", "/health"),
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 (module.started / module.stopped)."""
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
- # Trigger immediate discovery to pick up the new module
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
- last_discover = 0
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
- now = time.time()
379
- # Rediscover every 30s
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
- while True:
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
- await asyncio.sleep(5) # reconnect delay
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
- async with websockets.connect(url) as ws:
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
- msg_type = msg.get("type", "")
114
- if msg_type == "event":
115
- await self.monitor.handle_event(msg)
116
- elif msg_type == "ack":
117
- pass # publish confirmed
118
- elif msg_type == "error":
119
- print(f"[watchdog] Event Hub error: {msg.get('message')}")
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,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"