@agentunion/kite 1.4.0 → 1.5.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 (235) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/cli.js +44 -5
  3. package/core/dependency_checker.py +250 -0
  4. package/core/env_checker.py +490 -0
  5. package/dependencies_lock.json +128 -0
  6. package/extensions/agents/assistant/server.py +33 -17
  7. package/extensions/channels/acp_channel/server.py +33 -17
  8. package/extensions/services/backup/entry.py +23 -16
  9. package/extensions/services/evol/auth_manager.py +443 -0
  10. package/extensions/services/evol/config.yaml +149 -0
  11. package/extensions/services/evol/config_loader.py +117 -0
  12. package/extensions/services/evol/entry.py +406 -0
  13. package/extensions/services/evol/evol_api.py +173 -0
  14. package/extensions/services/evol/evol_config.json5 +29 -0
  15. package/extensions/services/evol/migrate_tokens.py +122 -0
  16. package/extensions/services/evol/module.md +32 -0
  17. package/extensions/services/evol/pairing.py +250 -0
  18. package/extensions/services/evol/pairing_codes.jsonl +1 -0
  19. package/extensions/services/evol/relay.py +682 -0
  20. package/extensions/services/evol/relay_config.json5 +67 -0
  21. package/extensions/services/evol/routes/__init__.py +1 -0
  22. package/extensions/services/evol/routes/routes_management_ws.py +127 -0
  23. package/extensions/services/evol/routes/routes_rpc.py +89 -0
  24. package/extensions/services/evol/routes/routes_test.py +61 -0
  25. package/extensions/services/evol/server.py +875 -0
  26. package/extensions/services/evol/static/css/style.css +1200 -0
  27. package/extensions/services/evol/static/index.html +781 -0
  28. package/extensions/services/evol/static/index_evol.html +14 -0
  29. package/extensions/services/evol/static/js/app.js +6304 -0
  30. package/extensions/services/evol/static/js/auth.js +326 -0
  31. package/extensions/services/evol/static/js/dialog.js +285 -0
  32. package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
  33. package/extensions/services/evol/static/js/evol-app.js +1949 -0
  34. package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
  35. package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
  36. package/extensions/services/evol/static/js/kernel-client.js +396 -0
  37. package/extensions/services/evol/static/js/main.js +141 -0
  38. package/extensions/services/evol/static/js/registry-tests.js +585 -0
  39. package/extensions/services/evol/static/js/stats.js +217 -0
  40. package/extensions/services/evol/static/js/token-manager.js +175 -0
  41. package/extensions/services/evol/static/pairing.html +248 -0
  42. package/extensions/services/evol/static/test_registry.html +262 -0
  43. package/extensions/services/evol/static/test_relay.html +462 -0
  44. package/extensions/services/evol/stats_manager.py +240 -0
  45. package/extensions/services/model_service/entry.py +23 -1
  46. package/extensions/services/proxy/.claude/settings.local.json +13 -0
  47. package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
  48. package/extensions/services/proxy/_fix_prints.py +133 -0
  49. package/extensions/services/proxy/_fix_prints2.py +87 -0
  50. package/extensions/services/proxy/agentcp/LICENCE +178 -0
  51. package/extensions/services/proxy/agentcp/README copy.md +85 -0
  52. package/extensions/services/proxy/agentcp/README.md +260 -0
  53. package/extensions/services/proxy/agentcp/__init__.py +16 -0
  54. package/extensions/services/proxy/agentcp/agent.py +4 -0
  55. package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
  56. package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
  57. package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
  58. package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
  59. package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
  60. package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
  61. package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
  62. package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
  63. package/extensions/services/proxy/agentcp/base/client.py +112 -0
  64. package/extensions/services/proxy/agentcp/base/env.py +34 -0
  65. package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
  66. package/extensions/services/proxy/agentcp/base/log.py +98 -0
  67. package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
  68. package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
  69. package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
  70. package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
  71. package/extensions/services/proxy/agentcp/context/context.py +73 -0
  72. package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
  73. package/extensions/services/proxy/agentcp/create_profile.py +125 -0
  74. package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
  75. package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
  76. package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
  77. package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
  78. package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
  79. package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
  80. package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
  81. package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
  82. package/extensions/services/proxy/agentcp/hcp.py +299 -0
  83. package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
  84. package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
  85. package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
  86. package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
  87. package/extensions/services/proxy/agentcp/llm_server.py +172 -0
  88. package/extensions/services/proxy/agentcp/mermaid.py +210 -0
  89. package/extensions/services/proxy/agentcp/message.py +149 -0
  90. package/extensions/services/proxy/agentcp/metrics.py +256 -0
  91. package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
  92. package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
  93. package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
  94. package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
  95. package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
  96. package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
  97. package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
  98. package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
  99. package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
  100. package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
  101. package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
  102. package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
  103. package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
  104. package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
  105. package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
  106. package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
  107. package/extensions/services/proxy/agentcp/requirements.txt +7 -0
  108. package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
  109. package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
  110. package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
  111. package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
  112. package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
  113. package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
  114. package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
  115. package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
  116. package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
  117. package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
  118. package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
  119. package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
  120. package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
  121. package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
  122. package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
  123. package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
  124. package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
  125. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
  126. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
  127. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
  128. package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
  129. package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
  130. package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
  131. package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
  132. package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
  133. package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
  134. package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
  135. package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
  136. package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
  137. package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
  138. package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
  139. package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
  140. package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
  141. package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
  142. package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
  143. package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
  144. package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
  145. package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
  146. package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
  147. package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
  148. package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
  149. package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
  150. package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
  151. package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
  152. package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
  153. package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
  154. package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
  155. package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
  156. package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
  157. package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
  158. package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
  159. package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
  160. package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
  161. package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
  162. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
  163. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
  164. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
  165. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
  166. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
  167. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
  168. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
  169. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
  170. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
  171. package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
  172. package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
  173. package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
  174. package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
  175. package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
  176. package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
  177. package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
  178. package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
  179. package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
  180. package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
  181. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
  182. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
  183. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
  184. package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
  185. package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
  186. package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
  187. package/extensions/services/proxy/agentcp/workflow.py +203 -0
  188. package/extensions/services/proxy/console_auth.py +109 -0
  189. package/extensions/services/proxy/evol/__init__.py +1 -0
  190. package/extensions/services/proxy/evol/config.py +37 -0
  191. package/extensions/services/proxy/evol/http/__init__.py +1 -0
  192. package/extensions/services/proxy/evol/http/async_http.py +551 -0
  193. package/extensions/services/proxy/evol/log.py +28 -0
  194. package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
  195. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
  196. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +106 -0
  197. package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
  198. package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
  199. package/extensions/services/proxy/evol/server/__init__.py +1 -0
  200. package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -0
  201. package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
  202. package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
  203. package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
  204. package/extensions/services/proxy/evol/version.py +24 -0
  205. package/extensions/services/proxy/logs/websocket.log +260 -0
  206. package/extensions/services/proxy/main.py +240 -0
  207. package/extensions/services/proxy/requirements.txt +13 -0
  208. package/extensions/services/proxy/server.py +271 -0
  209. package/extensions/services/watchdog/entry.py +42 -16
  210. package/extensions/services/watchdog/module.md +1 -0
  211. package/extensions/services/watchdog/monitor.py +34 -4
  212. package/extensions/services/web/module.md +1 -1
  213. package/extensions/services/web/server.py +30 -18
  214. package/extensions/services/web/static/js/token-manager.js +10 -10
  215. package/kernel/entry.py +1 -1
  216. package/kernel/module.md +25 -1
  217. package/kernel/registry_store.py +2 -26
  218. package/kernel/rpc_router.py +36 -10
  219. package/kernel/server.py +106 -17
  220. package/kite_cli/commands/deps_install.py +67 -0
  221. package/kite_cli/commands/env_check.py +45 -0
  222. package/kite_cli/commands/prepare.py +49 -0
  223. package/kite_cli/commands/venv_setup.py +56 -0
  224. package/kite_cli/main.py +29 -1
  225. package/launcher/entry.py +306 -21
  226. package/launcher/module.md +9 -0
  227. package/launcher/module_scanner.py +11 -1
  228. package/main.py +4 -1
  229. package/package.json +8 -1
  230. package/python_version.json +4 -0
  231. package/requirements.txt +38 -0
  232. package/scripts/env-manager.js +328 -0
  233. package/scripts/python-env.js +79 -0
  234. package/scripts/scan_dependencies.py +461 -0
  235. package/scripts/setup-python-env.js +191 -0
@@ -0,0 +1,222 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 滑动窗口指标统计
4
+
5
+ 支持多时间窗口的实时统计分析
6
+ """
7
+
8
+ import time
9
+ from typing import Dict, List, Tuple, Any
10
+
11
+
12
+ class TimeWindow:
13
+ """单个时间窗口
14
+
15
+ 维护一个固定时长的滑动窗口,自动清理过期数据点
16
+ """
17
+
18
+ def __init__(self, duration_seconds: int):
19
+ """初始化时间窗口
20
+
21
+ Args:
22
+ duration_seconds: 窗口时长(秒)
23
+ """
24
+ self.duration = duration_seconds
25
+ self.data_points: List[Tuple[float, dict]] = [] # [(timestamp, metrics), ...]
26
+
27
+ def add_snapshot(self, timestamp: float, metrics: dict):
28
+ """添加一个时间点的指标快照
29
+
30
+ Args:
31
+ timestamp: 时间戳
32
+ metrics: 指标字典,必须包含以下字段:
33
+ - received_delta: 增量接收消息数
34
+ - success_delta: 增量成功派发数
35
+ - failed_delta: 增量失败数
36
+ - avg_latency: 平均延迟(毫秒)
37
+ - queue_size: 当前队列大小
38
+ """
39
+ self.data_points.append((timestamp, metrics))
40
+
41
+ # 清理过期数据(保留窗口时长内的数据)
42
+ cutoff = timestamp - self.duration
43
+ self.data_points = [(t, m) for t, m in self.data_points if t >= cutoff]
44
+
45
+ def get_stats(self) -> Dict[str, Any]:
46
+ """计算窗口内的统计数据
47
+
48
+ Returns:
49
+ 包含以下统计指标的字典:
50
+ - throughput_per_second: 吞吐量(消息/秒)
51
+ - avg_latency_ms: 平均延迟(毫秒)
52
+ - success_rate: 成功率(%)
53
+ - total_messages: 窗口内总消息数
54
+ - failed_messages: 窗口内失败消息数
55
+ - avg_queue_size: 平均队列大小
56
+ - window_duration: 窗口时长(秒)
57
+ - data_points_count: 数据点数量
58
+ """
59
+ if not self.data_points:
60
+ return {
61
+ 'throughput_per_second': 0.0,
62
+ 'avg_latency_ms': 0.0,
63
+ 'success_rate': 0.0,
64
+ 'total_messages': 0,
65
+ 'failed_messages': 0,
66
+ 'avg_queue_size': 0,
67
+ 'window_duration': self.duration,
68
+ 'data_points_count': 0,
69
+ }
70
+
71
+ # 计算总量
72
+ total_received = sum(m.get('received_delta', 0) for _, m in self.data_points)
73
+ total_success = sum(m.get('success_delta', 0) for _, m in self.data_points)
74
+ total_failed = sum(m.get('failed_delta', 0) for _, m in self.data_points)
75
+
76
+ # 计算实际时间跨度(可能小于窗口时长)
77
+ actual_duration = self.data_points[-1][0] - self.data_points[0][0]
78
+ if actual_duration < 1:
79
+ actual_duration = 1 # 避免除零
80
+
81
+ # 计算吞吐量 (msg/s)
82
+ throughput = total_received / actual_duration
83
+
84
+ # 计算平均延迟(只考虑有延迟数据的点)
85
+ latencies = [m.get('avg_latency', 0) for _, m in self.data_points if m.get('avg_latency', 0) > 0]
86
+ avg_latency = sum(latencies) / len(latencies) if latencies else 0.0
87
+
88
+ # 计算成功率
89
+ success_rate = (total_success / max(total_received, 1)) * 100
90
+
91
+ # 计算平均队列大小
92
+ queue_sizes = [m.get('queue_size', 0) for _, m in self.data_points]
93
+ avg_queue_size = sum(queue_sizes) / len(queue_sizes) if queue_sizes else 0
94
+
95
+ return {
96
+ 'throughput_per_second': round(throughput, 2),
97
+ 'avg_latency_ms': round(avg_latency, 2),
98
+ 'success_rate': round(success_rate, 2),
99
+ 'total_messages': total_received,
100
+ 'failed_messages': total_failed,
101
+ 'avg_queue_size': round(avg_queue_size, 1),
102
+ 'window_duration': self.duration,
103
+ 'data_points_count': len(self.data_points),
104
+ }
105
+
106
+
107
+ class SlidingWindowMetrics:
108
+ """多时间窗口管理器
109
+
110
+ 管理多个不同时长的滑动窗口,提供多粒度的统计视图
111
+ 支持的窗口:1分钟、3分钟、5分钟、10分钟、15分钟
112
+ """
113
+
114
+ def __init__(self):
115
+ """初始化多时间窗口管理器"""
116
+ # 创建多个时间窗口
117
+ self.windows: Dict[str, TimeWindow] = {
118
+ '1m': TimeWindow(60), # 1分钟
119
+ '3m': TimeWindow(180), # 3分钟
120
+ '5m': TimeWindow(300), # 5分钟
121
+ '10m': TimeWindow(600), # 10分钟
122
+ '15m': TimeWindow(900), # 15分钟
123
+ }
124
+
125
+ # 记录上一次快照的状态
126
+ self.last_snapshot_time = 0
127
+ self.last_metrics_snapshot: Dict[str, Any] = {}
128
+
129
+ def update(self, current_metrics: dict):
130
+ """更新所有窗口(应该每10秒调用一次)
131
+
132
+ Args:
133
+ current_metrics: 当前累计指标,必须包含以下字段:
134
+ - received_total: 累计接收消息总数
135
+ - dispatched_success: 累计派发成功数
136
+ - dispatched_failed: 累计派发失败数
137
+ - dispatch_queue_size: 当前派发队列大小
138
+ - avg_dispatch_latency_ms: 平均派发延迟(可选)
139
+ - avg_handler_latency_ms: 平均处理延迟(可选)
140
+ """
141
+ now = time.time()
142
+
143
+ # 计算增量指标 (delta)
144
+ metrics_delta = self._calculate_delta(current_metrics)
145
+
146
+ # 更新所有窗口
147
+ for window in self.windows.values():
148
+ window.add_snapshot(now, metrics_delta)
149
+
150
+ # 保存当前快照状态
151
+ self.last_snapshot_time = now
152
+ self.last_metrics_snapshot = current_metrics.copy()
153
+
154
+ def _calculate_delta(self, current: dict) -> dict:
155
+ """计算两次快照之间的增量
156
+
157
+ Args:
158
+ current: 当前累计指标
159
+
160
+ Returns:
161
+ 增量指标字典
162
+ """
163
+ if not self.last_metrics_snapshot:
164
+ # 第一次快照,返回零增量
165
+ return {
166
+ 'received_delta': 0,
167
+ 'success_delta': 0,
168
+ 'failed_delta': 0,
169
+ 'avg_latency': 0.0,
170
+ 'queue_size': current.get('dispatch_queue_size', 0),
171
+ }
172
+
173
+ # 计算增量
174
+ received_delta = current.get('received_total', 0) - self.last_metrics_snapshot.get('received_total', 0)
175
+ success_delta = current.get('dispatched_success', 0) - self.last_metrics_snapshot.get('dispatched_success', 0)
176
+ failed_delta = current.get('dispatched_failed', 0) - self.last_metrics_snapshot.get('dispatched_failed', 0)
177
+
178
+ # 获取平均延迟(优先使用 dispatch_latency,其次使用 handler_latency)
179
+ avg_latency = 0.0
180
+ if 'avg_dispatch_latency_ms' in current:
181
+ avg_latency = current['avg_dispatch_latency_ms']
182
+ elif 'avg_handler_latency_ms' in current:
183
+ avg_latency = current['avg_handler_latency_ms']
184
+
185
+ return {
186
+ 'received_delta': max(0, received_delta), # 防止负数
187
+ 'success_delta': max(0, success_delta),
188
+ 'failed_delta': max(0, failed_delta),
189
+ 'avg_latency': avg_latency,
190
+ 'queue_size': current.get('dispatch_queue_size', 0),
191
+ }
192
+
193
+ def get_window_stats(self, window_name: str) -> Dict[str, Any]:
194
+ """获取指定窗口的统计数据
195
+
196
+ Args:
197
+ window_name: 窗口名称('1m', '3m', '5m', '10m', '15m')
198
+
199
+ Returns:
200
+ 窗口统计数据字典,如果窗口不存在则返回空字典
201
+ """
202
+ if window_name not in self.windows:
203
+ return {}
204
+ return self.windows[window_name].get_stats()
205
+
206
+ def get_all_windows(self) -> Dict[str, Dict[str, Any]]:
207
+ """获取所有窗口的统计数据
208
+
209
+ Returns:
210
+ 字典,key为窗口名称,value为统计数据
211
+ """
212
+ return {
213
+ name: window.get_stats()
214
+ for name, window in self.windows.items()
215
+ }
216
+
217
+ def reset(self):
218
+ """重置所有窗口数据"""
219
+ for window in self.windows.values():
220
+ window.data_points.clear()
221
+ self.last_snapshot_time = 0
222
+ self.last_metrics_snapshot.clear()
@@ -0,0 +1,224 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 独立监控数据读取器
4
+
5
+ 支持跨进程读取监控数据,无需依赖全局变量或进程内通信
6
+ """
7
+
8
+ import os
9
+ import time
10
+ from typing import Dict, Any, Optional, List
11
+
12
+ from .metrics_store import MetricsStore
13
+ from .sliding_window import SlidingWindowMetrics
14
+
15
+
16
+ class StandaloneMonitoringReader:
17
+ """独立监控数据读取器
18
+
19
+ 可以在任何进程中使用,通过直接读取 SQLite 数据库获取监控数据
20
+ """
21
+
22
+ def __init__(self, db_path: str = None):
23
+ """初始化读取器
24
+
25
+ Args:
26
+ db_path: 数据库路径,如果为空则使用默认路径
27
+ """
28
+ if db_path is None:
29
+ # 使用默认路径
30
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
31
+ db_path = os.path.join(project_root, 'backup', 'metrics_timeseries.db')
32
+
33
+ self.db_path = db_path
34
+ self.metrics_store = MetricsStore(db_path)
35
+
36
+ def get_realtime_metrics(self) -> Dict[str, Any]:
37
+ """获取实时监控指标
38
+
39
+ Returns:
40
+ 包含累计指标和时间窗口统计的字典
41
+ """
42
+ # 检查数据库文件是否存在
43
+ if not os.path.exists(self.db_path):
44
+ raise FileNotFoundError(f"监控数据库不存在: {self.db_path}")
45
+
46
+ # 获取最新的一条记录作为累计指标
47
+ latest_records = self.metrics_store.query_latest(limit=1)
48
+ if not latest_records:
49
+ raise ValueError("数据库中没有监控数据")
50
+
51
+ cumulative = latest_records[0]
52
+
53
+ # 计算时间窗口统计
54
+ windows = self._calculate_windows()
55
+
56
+ return {
57
+ 'agent_id': cumulative.get('agent_id', 'unknown'),
58
+ 'timestamp': cumulative.get('timestamp', time.time()),
59
+ 'cumulative': self._format_cumulative(cumulative),
60
+ 'windows': windows
61
+ }
62
+
63
+ def _format_cumulative(self, record: dict) -> dict:
64
+ """格式化累计指标"""
65
+ # 计算成功率
66
+ received_total = record.get('received_total', 0)
67
+ dispatched_success = record.get('dispatched_success', 0)
68
+
69
+ dispatch_success_rate = "0.00%"
70
+ if received_total > 0:
71
+ rate = (dispatched_success / received_total) * 100
72
+ dispatch_success_rate = f"{rate:.2f}%"
73
+
74
+ # 计算运行时长(使用最早的记录时间)
75
+ store_stats = self.metrics_store.get_stats()
76
+ min_ts = store_stats.get('min_timestamp', record['timestamp'])
77
+ uptime_seconds = record['timestamp'] - min_ts if min_ts else 0
78
+
79
+ return {
80
+ 'received_total': record.get('received_total', 0),
81
+ 'dispatched_success': record.get('dispatched_success', 0),
82
+ 'dispatched_failed': record.get('dispatched_failed', 0),
83
+ 'handler_success': record.get('handler_success', 0),
84
+ 'handler_failed': record.get('handler_failed', 0),
85
+ 'dispatch_queue_size': record.get('dispatch_queue_size', 0),
86
+ 'dispatch_success_rate': dispatch_success_rate,
87
+ 'uptime_seconds': str(int(uptime_seconds)),
88
+ 'dispatch_latency': {
89
+ 'avg_ms': f"{record.get('avg_dispatch_latency_ms', 0):.2f}",
90
+ 'p50_ms': f"{record.get('p50_dispatch_latency_ms', 0):.2f}",
91
+ 'p95_ms': f"{record.get('p95_dispatch_latency_ms', 0):.2f}",
92
+ 'p99_ms': f"{record.get('p99_dispatch_latency_ms', 0):.2f}",
93
+ }
94
+ }
95
+
96
+ def _calculate_windows(self) -> Dict[str, Dict[str, Any]]:
97
+ """计算时间窗口统计"""
98
+ now = int(time.time())
99
+
100
+ windows = {
101
+ '1m': self._calculate_window(now, 60),
102
+ '3m': self._calculate_window(now, 180),
103
+ '5m': self._calculate_window(now, 300),
104
+ '10m': self._calculate_window(now, 600),
105
+ '15m': self._calculate_window(now, 900),
106
+ }
107
+
108
+ return windows
109
+
110
+ def _calculate_window(self, now: int, duration: int) -> Dict[str, Any]:
111
+ """计算单个时间窗口的统计数据"""
112
+ from_ts = now - duration
113
+ to_ts = now
114
+
115
+ # 查询时间范围内的数据
116
+ records = self.metrics_store.query_range(from_ts, to_ts)
117
+
118
+ if len(records) < 2:
119
+ # 数据不足,返回空统计
120
+ return {
121
+ 'throughput_per_second': 0.0,
122
+ 'avg_latency_ms': 0.0,
123
+ 'success_rate': 0.0,
124
+ 'total_messages': 0,
125
+ 'failed_messages': 0,
126
+ 'avg_queue_size': 0.0,
127
+ 'window_duration': duration,
128
+ 'data_points_count': len(records)
129
+ }
130
+
131
+ # 计算增量
132
+ first_record = records[0]
133
+ last_record = records[-1]
134
+
135
+ received_delta = last_record['received_total'] - first_record['received_total']
136
+ success_delta = last_record['dispatched_success'] - first_record['dispatched_success']
137
+ failed_delta = last_record['dispatched_failed'] - first_record['dispatched_failed']
138
+
139
+ # 计算实际时间跨度
140
+ actual_duration = last_record['timestamp'] - first_record['timestamp']
141
+ if actual_duration < 1:
142
+ actual_duration = 1
143
+
144
+ # 计算吞吐量
145
+ throughput = received_delta / actual_duration
146
+
147
+ # 计算成功率
148
+ success_rate = 0.0
149
+ if received_delta > 0:
150
+ success_rate = (success_delta / received_delta) * 100
151
+
152
+ # 计算平均延迟
153
+ latencies = [r['avg_dispatch_latency_ms'] for r in records if r['avg_dispatch_latency_ms'] > 0]
154
+ avg_latency = sum(latencies) / len(latencies) if latencies else 0.0
155
+
156
+ # 计算平均队列大小
157
+ queue_sizes = [r['dispatch_queue_size'] for r in records]
158
+ avg_queue_size = sum(queue_sizes) / len(queue_sizes) if queue_sizes else 0.0
159
+
160
+ return {
161
+ 'throughput_per_second': round(throughput, 2),
162
+ 'avg_latency_ms': round(avg_latency, 2),
163
+ 'success_rate': round(success_rate, 2),
164
+ 'total_messages': received_delta,
165
+ 'failed_messages': failed_delta,
166
+ 'avg_queue_size': round(avg_queue_size, 1),
167
+ 'window_duration': duration,
168
+ 'data_points_count': len(records)
169
+ }
170
+
171
+ def get_window_metrics(self, window_names: List[str]) -> Dict[str, Dict[str, Any]]:
172
+ """获取指定时间窗口的指标"""
173
+ now = int(time.time())
174
+
175
+ window_durations = {
176
+ '1m': 60,
177
+ '3m': 180,
178
+ '5m': 300,
179
+ '10m': 600,
180
+ '15m': 900,
181
+ }
182
+
183
+ result = {}
184
+ for name in window_names:
185
+ if name in window_durations:
186
+ result[name] = self._calculate_window(now, window_durations[name])
187
+
188
+ return result
189
+
190
+ def get_history(self, from_ts: int, to_ts: int, limit: int = 1000) -> List[Dict[str, Any]]:
191
+ """获取历史数据"""
192
+ return self.metrics_store.query_range(from_ts, to_ts, limit=limit)
193
+
194
+ def get_latest_history(self, limit: int = 100) -> List[Dict[str, Any]]:
195
+ """获取最新的历史数据"""
196
+ return self.metrics_store.query_latest(limit=limit)
197
+
198
+ def get_service_info(self) -> Dict[str, Any]:
199
+ """获取监控服务信息"""
200
+ store_stats = self.metrics_store.get_stats()
201
+
202
+ # 检查是否有最近的数据更新(最近30秒内)
203
+ max_ts = store_stats.get('max_timestamp', 0)
204
+ is_running = (time.time() - max_ts) < 30 if max_ts else False
205
+
206
+ return {
207
+ 'agent_id': 'unknown',
208
+ 'running': is_running,
209
+ 'snapshot_interval': 10,
210
+ 'snapshot_count': store_stats.get('total_records', 0),
211
+ 'store_stats': store_stats
212
+ }
213
+
214
+
215
+ def get_standalone_reader(db_path: str = None) -> StandaloneMonitoringReader:
216
+ """获取独立监控读取器实例
217
+
218
+ Args:
219
+ db_path: 数据库路径,如果为空则使用默认路径
220
+
221
+ Returns:
222
+ StandaloneMonitoringReader 实例
223
+ """
224
+ return StandaloneMonitoringReader(db_path)
@@ -0,0 +1,21 @@
1
+ # Copyright 2025 AgentUnion Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from . import message_client
16
+ from . import open_ai_message
17
+ from . import message_serialize
18
+ from . import session_manager
19
+ from . import stream_client
20
+ from . import wss_binary_message
21
+ from . import websocket_file_receiver