@agentunion/kite 1.3.2 → 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 (293) hide show
  1. package/CHANGELOG.md +302 -0
  2. package/cli.js +119 -4
  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/entry.py +111 -1
  7. package/extensions/agents/assistant/server.py +279 -215
  8. package/extensions/channels/acp_channel/entry.py +111 -1
  9. package/extensions/channels/acp_channel/module.md +23 -22
  10. package/extensions/channels/acp_channel/server.py +279 -215
  11. package/extensions/event_hub_bench/entry.py +107 -1
  12. package/extensions/services/backup/entry.py +306 -21
  13. package/extensions/services/backup/module.md +24 -22
  14. package/extensions/services/evol/auth_manager.py +443 -0
  15. package/extensions/services/evol/config.yaml +149 -0
  16. package/extensions/services/evol/config_loader.py +117 -0
  17. package/extensions/services/evol/entry.py +406 -0
  18. package/extensions/services/evol/evol_api.py +173 -0
  19. package/extensions/services/evol/evol_config.json5 +29 -0
  20. package/extensions/services/evol/migrate_tokens.py +122 -0
  21. package/extensions/services/evol/module.md +32 -0
  22. package/extensions/services/evol/pairing.py +250 -0
  23. package/extensions/services/evol/pairing_codes.jsonl +1 -0
  24. package/extensions/services/evol/relay.py +682 -0
  25. package/extensions/services/evol/relay_config.json5 +67 -0
  26. package/extensions/services/evol/routes/__init__.py +1 -0
  27. package/extensions/services/evol/routes/routes_management_ws.py +127 -0
  28. package/extensions/services/evol/routes/routes_rpc.py +89 -0
  29. package/extensions/services/evol/routes/routes_test.py +61 -0
  30. package/extensions/services/evol/server.py +875 -0
  31. package/extensions/services/evol/static/css/style.css +1200 -0
  32. package/extensions/services/evol/static/index.html +781 -0
  33. package/extensions/services/evol/static/index_evol.html +14 -0
  34. package/extensions/services/evol/static/js/app.js +6304 -0
  35. package/extensions/services/evol/static/js/auth.js +326 -0
  36. package/extensions/services/evol/static/js/dialog.js +285 -0
  37. package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
  38. package/extensions/services/evol/static/js/evol-app.js +1949 -0
  39. package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
  40. package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
  41. package/extensions/services/evol/static/js/kernel-client.js +396 -0
  42. package/extensions/services/evol/static/js/main.js +141 -0
  43. package/extensions/services/evol/static/js/registry-tests.js +585 -0
  44. package/extensions/services/evol/static/js/stats.js +217 -0
  45. package/extensions/services/evol/static/js/token-manager.js +175 -0
  46. package/extensions/services/evol/static/pairing.html +248 -0
  47. package/extensions/services/evol/static/test_registry.html +262 -0
  48. package/extensions/services/evol/static/test_relay.html +462 -0
  49. package/extensions/services/evol/stats_manager.py +240 -0
  50. package/extensions/services/model_service/entry.py +167 -19
  51. package/extensions/services/model_service/module.md +21 -22
  52. package/extensions/services/proxy/.claude/settings.local.json +13 -0
  53. package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
  54. package/extensions/services/proxy/_fix_prints.py +133 -0
  55. package/extensions/services/proxy/_fix_prints2.py +87 -0
  56. package/extensions/services/proxy/agentcp/LICENCE +178 -0
  57. package/extensions/services/proxy/agentcp/README copy.md +85 -0
  58. package/extensions/services/proxy/agentcp/README.md +260 -0
  59. package/extensions/services/proxy/agentcp/__init__.py +16 -0
  60. package/extensions/services/proxy/agentcp/agent.py +4 -0
  61. package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
  62. package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
  63. package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
  64. package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
  65. package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
  66. package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
  67. package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
  68. package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
  69. package/extensions/services/proxy/agentcp/base/client.py +112 -0
  70. package/extensions/services/proxy/agentcp/base/env.py +34 -0
  71. package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
  72. package/extensions/services/proxy/agentcp/base/log.py +98 -0
  73. package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
  74. package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
  75. package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
  76. package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
  77. package/extensions/services/proxy/agentcp/context/context.py +73 -0
  78. package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
  79. package/extensions/services/proxy/agentcp/create_profile.py +125 -0
  80. package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
  81. package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
  82. package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
  83. package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
  84. package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
  85. package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
  86. package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
  87. package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
  88. package/extensions/services/proxy/agentcp/hcp.py +299 -0
  89. package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
  90. package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
  91. package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
  92. package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
  93. package/extensions/services/proxy/agentcp/llm_server.py +172 -0
  94. package/extensions/services/proxy/agentcp/mermaid.py +210 -0
  95. package/extensions/services/proxy/agentcp/message.py +149 -0
  96. package/extensions/services/proxy/agentcp/metrics.py +256 -0
  97. package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
  98. package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
  99. package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
  100. package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
  101. package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
  102. package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
  103. package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
  104. package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
  105. package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
  106. package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
  107. package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
  108. package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
  109. package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
  110. package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
  111. package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
  112. package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
  113. package/extensions/services/proxy/agentcp/requirements.txt +7 -0
  114. package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
  115. package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
  116. package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
  117. package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
  118. package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
  119. package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
  120. package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
  121. package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
  122. package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
  123. package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
  124. package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
  125. package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
  126. package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
  127. package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
  128. package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
  129. package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
  130. package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
  131. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
  132. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
  133. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
  134. package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
  135. package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
  136. package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
  137. package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
  138. package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
  139. package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
  140. package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
  141. package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
  142. package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
  143. package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
  144. package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
  145. package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
  146. package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
  147. package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
  148. package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
  149. package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
  150. package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
  151. package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
  152. package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
  153. package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
  154. package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
  155. package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
  156. package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
  157. package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
  158. package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
  159. package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
  160. package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
  161. package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
  162. package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
  163. package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
  164. package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
  165. package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
  166. package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
  167. package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
  168. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
  169. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
  170. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
  171. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
  172. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
  173. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
  174. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
  175. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
  176. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
  177. package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
  178. package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
  179. package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
  180. package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
  181. package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
  182. package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
  183. package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
  184. package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
  185. package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
  186. package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
  187. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
  188. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
  189. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
  190. package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
  191. package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
  192. package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
  193. package/extensions/services/proxy/agentcp/workflow.py +203 -0
  194. package/extensions/services/proxy/console_auth.py +109 -0
  195. package/extensions/services/proxy/evol/__init__.py +1 -0
  196. package/extensions/services/proxy/evol/config.py +37 -0
  197. package/extensions/services/proxy/evol/http/__init__.py +1 -0
  198. package/extensions/services/proxy/evol/http/async_http.py +551 -0
  199. package/extensions/services/proxy/evol/log.py +28 -0
  200. package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
  201. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
  202. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +106 -0
  203. package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
  204. package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
  205. package/extensions/services/proxy/evol/server/__init__.py +1 -0
  206. package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -0
  207. package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
  208. package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
  209. package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
  210. package/extensions/services/proxy/evol/version.py +24 -0
  211. package/extensions/services/proxy/logs/websocket.log +260 -0
  212. package/extensions/services/proxy/main.py +240 -0
  213. package/extensions/services/proxy/requirements.txt +13 -0
  214. package/extensions/services/proxy/server.py +271 -0
  215. package/extensions/services/watchdog/entry.py +215 -26
  216. package/extensions/services/watchdog/module.md +1 -0
  217. package/extensions/services/watchdog/monitor.py +178 -38
  218. package/extensions/services/web/WEBSOCKET_STATUS.md +143 -0
  219. package/extensions/services/web/config_example.py +35 -0
  220. package/extensions/services/web/config_loader.py +110 -0
  221. package/extensions/services/web/entry.py +114 -26
  222. package/extensions/services/web/module.md +35 -24
  223. package/extensions/services/web/pairing.py +250 -0
  224. package/extensions/services/web/pairing_codes.jsonl +16 -0
  225. package/extensions/services/web/relay.py +643 -0
  226. package/extensions/services/web/relay_config.json5 +67 -0
  227. package/extensions/services/web/routes/routes_management_ws.py +127 -0
  228. package/extensions/services/web/routes/routes_rpc.py +89 -0
  229. package/extensions/services/web/routes/routes_test.py +61 -0
  230. package/extensions/services/web/routes/schemas.py +0 -22
  231. package/extensions/services/web/server.py +434 -99
  232. package/extensions/services/web/static/css/style.css +67 -28
  233. package/extensions/services/web/static/index.html +234 -44
  234. package/extensions/services/web/static/js/app.js +1335 -48
  235. package/extensions/services/web/static/js/kernel-client-example.js +161 -0
  236. package/extensions/services/web/static/js/kernel-client.js +383 -0
  237. package/extensions/services/web/static/js/registry-tests.js +558 -0
  238. package/extensions/services/web/static/js/token-manager.js +175 -0
  239. package/extensions/services/web/static/pairing.html +248 -0
  240. package/extensions/services/web/static/test_registry.html +262 -0
  241. package/extensions/services/web/web_config.json5 +29 -0
  242. package/kernel/entry.py +120 -32
  243. package/kernel/event_hub.py +141 -16
  244. package/kernel/module.md +60 -33
  245. package/kernel/registry_store.py +45 -36
  246. package/kernel/rpc_router.py +152 -59
  247. package/kernel/server.py +322 -26
  248. package/kite_cli/__init__.py +3 -0
  249. package/kite_cli/__main__.py +5 -0
  250. package/kite_cli/commands/__init__.py +1 -0
  251. package/kite_cli/commands/clean.py +101 -0
  252. package/kite_cli/commands/deps_install.py +67 -0
  253. package/kite_cli/commands/doctor.py +35 -0
  254. package/kite_cli/commands/env_check.py +45 -0
  255. package/kite_cli/commands/history.py +111 -0
  256. package/kite_cli/commands/info.py +96 -0
  257. package/kite_cli/commands/install.py +313 -0
  258. package/kite_cli/commands/list.py +143 -0
  259. package/kite_cli/commands/log.py +81 -0
  260. package/kite_cli/commands/prepare.py +49 -0
  261. package/kite_cli/commands/rollback.py +88 -0
  262. package/kite_cli/commands/search.py +73 -0
  263. package/kite_cli/commands/uninstall.py +85 -0
  264. package/kite_cli/commands/update.py +118 -0
  265. package/kite_cli/commands/venv_setup.py +56 -0
  266. package/kite_cli/core/__init__.py +1 -0
  267. package/kite_cli/core/checker.py +142 -0
  268. package/kite_cli/core/dependency.py +229 -0
  269. package/kite_cli/core/downloader.py +209 -0
  270. package/kite_cli/core/install_info.py +40 -0
  271. package/kite_cli/core/tool_installer.py +397 -0
  272. package/kite_cli/core/validator.py +78 -0
  273. package/kite_cli/main.py +317 -0
  274. package/kite_cli/utils/__init__.py +1 -0
  275. package/kite_cli/utils/i18n.py +252 -0
  276. package/kite_cli/utils/interactive.py +63 -0
  277. package/kite_cli/utils/operation_log.py +77 -0
  278. package/kite_cli/utils/paths.py +34 -0
  279. package/kite_cli/utils/version.py +308 -0
  280. package/launcher/entry.py +1124 -178
  281. package/launcher/logging_setup.py +104 -0
  282. package/launcher/module.md +46 -37
  283. package/launcher/module_scanner.py +11 -1
  284. package/main.py +4 -1
  285. package/package.json +9 -1
  286. package/python_version.json +4 -0
  287. package/requirements.txt +38 -0
  288. package/scripts/env-manager.js +328 -0
  289. package/scripts/plan_manager.py +315 -0
  290. package/scripts/python-env.js +79 -0
  291. package/scripts/scan_dependencies.py +461 -0
  292. package/scripts/setup-python-env.js +191 -0
  293. package/extensions/services/web/routes/routes_modules.py +0 -249
@@ -0,0 +1,249 @@
1
+ # -*- coding:utf-8 -*-
2
+ # Copyright 2025 AgentUnion Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import asyncio
16
+ import json
17
+ import time
18
+ import traceback
19
+ import urllib.parse
20
+
21
+ import requests
22
+ from flask import Response
23
+
24
+ from agentcp.base.log import log_error, log_exception, log_info
25
+
26
+
27
+ class AttrDict(dict):
28
+ """使用属性方式读取字典兼容 openai响应格式"""
29
+
30
+ def __init__(self, *args, **kwargs):
31
+ super().__init__(*args, **kwargs)
32
+ # 递归转换所有字典类型子项
33
+ for key, value in self.items():
34
+ if isinstance(value, dict):
35
+ self[key] = AttrDict(value)
36
+ elif isinstance(value, list):
37
+ self[key] = [AttrDict(v) if isinstance(v, dict) else v for v in value]
38
+
39
+ def __getattr__(self, key):
40
+ if key in self:
41
+ return self[key]
42
+ raise AttributeError(f"{self.__class__.__name__}对象无属性{key}")
43
+
44
+ def model_dump(self, exclude_none: bool = True, **kwargs) -> dict:
45
+ """兼容openai响应的 model_dump"""
46
+
47
+ def _serialize(obj):
48
+ if isinstance(obj, AttrDict):
49
+ return {k: _serialize(v) for k, v in obj.items() if not (exclude_none and v is None)}
50
+ elif isinstance(obj, list):
51
+ return [_serialize(item) for item in obj]
52
+ else:
53
+ return obj
54
+
55
+ return _serialize(self)
56
+
57
+
58
+ def format_date() -> str:
59
+ """获取当前格式化时间"""
60
+ ts = time.time()
61
+ # 格式化输出(带毫秒)
62
+ formatted_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts))
63
+ millis = int(ts * 1000) % 1000
64
+ return f"{formatted_time}.{millis:03d}"
65
+
66
+
67
+ def get_message_type(messages: list) -> str:
68
+ """获取消息类型"""
69
+ if len(messages) > 0:
70
+ msg = messages[0]
71
+ msg_type = msg.get("type")
72
+ return msg_type
73
+ return None
74
+
75
+
76
+ def parse_stream_url(url0: str) -> tuple:
77
+ """
78
+ 解析消息中流地址
79
+ 如:
80
+ https://ts.agentunion.cn/api/text_stream/pull_text_stream?session_id=1831992075507204096&message_id=6
81
+ 解析出独立的url和参数字典
82
+ """
83
+ args = {}
84
+ array = url0.split("?")
85
+ if len(array) != 2:
86
+ return url0, args
87
+ url, args_str = array
88
+ for kv in args_str.split("&"):
89
+ arr = kv.split("=")
90
+ if len(arr) != 2:
91
+ continue
92
+ args[arr[0]] = arr[1]
93
+ return url, args
94
+
95
+
96
+ def get_vaild_json(text):
97
+ try:
98
+ json_data = json.loads(text)
99
+ return json_data
100
+ except Exception:
101
+ return None
102
+
103
+
104
+ def fail_response(content) -> AttrDict:
105
+ # 构造失败默认结果(是否流式只是delta和message的区别)
106
+ return AttrDict(
107
+ {
108
+ "status": "error",
109
+ "code": 400,
110
+ "message": content,
111
+ }
112
+ )
113
+
114
+
115
+ class LLMAgent:
116
+ def __init__(self, llm_agent, aid):
117
+ self.llm_agent_name = llm_agent # 大模型agent名称
118
+ self.aid = aid # 当前agent主体
119
+ self.msg = None # 当前agent主体
120
+ self.result = None
121
+ self.session_id = None # 当前会话id
122
+ self.result_type = (
123
+ None # 结果类型,如text, image, audio, video, file, form, add_friend, create_order, error, empt
124
+ )
125
+
126
+ async def chat_create(self, open_ai_message_body, trace_id: str = ""):
127
+ """大模型agent对话"""
128
+ # 结果值为空
129
+ self.result = AttrDict({})
130
+ llm_message = {
131
+ "type": "llm",
132
+ "status": "success",
133
+ "timestamp": int(time.time() * 1000),
134
+ "content": open_ai_message_body,
135
+ "trace_id": trace_id,
136
+ }
137
+
138
+ # 3、发送消息并异步接收结果
139
+ async def reply_message_handler(reply_msg):
140
+ try:
141
+ # 解析大模型返回结果
142
+ msg_type, response = self.parse_message(reply_msg=reply_msg)
143
+ log_info(
144
+ f"[{format_date()}]: llm agent message parse result msg_type = {msg_type}, response = {response}"
145
+ )
146
+ self.result_type = msg_type
147
+ if msg_type == "error":
148
+ self.result = fail_response(response)
149
+ else:
150
+ self.result = response
151
+ except Exception as e:
152
+ self.result = fail_response(f"消息解析失败{str(e)}")
153
+ self.result_type = "error"
154
+ self.session_id = reply_msg.get("session_id", "")
155
+
156
+ try:
157
+ # 3-1、向大模型agent发送消息
158
+ if self.session_id is None:
159
+ returned_session_id = self.aid.quick_send_message(
160
+ self.llm_agent_name, llm_message, lambda reply_msg: reply_message_handler(reply_msg)
161
+ )
162
+ # 捕获返回的session_id
163
+ if returned_session_id:
164
+ self.session_id = returned_session_id
165
+ else:
166
+ self.aid.add_message_handler(reply_message_handler, session_id=self.session_id)
167
+ self.aid.send_message(self.session_id, [self.llm_agent_name], llm_message)
168
+ # 3-2、异步等待结果(带超时)
169
+ # 4-2、轮询解析结果(最长等待10分钟)
170
+ timeout = 600 # 10分钟超时
171
+ start_time = time.time()
172
+ while len(self.result) == 0:
173
+ # log_info(f'reply_result = {self.result[1]}')
174
+ await asyncio.sleep(0.1) # 每0.1s检查一次
175
+ if time.time() - start_time > timeout:
176
+ return fail_response(f"服务商{self.aid.id}未响应,请检查网络或重启ModelGate客户端,如果问题依旧,请联系客服")
177
+ # 流式响应
178
+ if self.result_type == "text/event-stream":
179
+ return self.read_stream(self.result)
180
+ except Exception as e:
181
+ log_exception(f"{format_date()}消息处理异常: {str(e)}")
182
+ traceback.format_exc()
183
+ return fail_response(f"消息处理异常{str(e)}")
184
+ return self.result
185
+
186
+ def read_stream(self, content):
187
+ url = ""
188
+ try:
189
+ # 增加URL参数验证
190
+ url = content + "&agent_id=" + self.aid.id
191
+ target_response = requests.get(url, stream=True, verify=False, timeout=(60, 600), proxies={}) # 连接超时60秒,读取超时10分钟
192
+
193
+ def generate():
194
+ try:
195
+ for line in target_response.iter_lines():
196
+ if line:
197
+ # 检查是否为合法 SSE 格式(避免污染数据流)
198
+ chunk = urllib.parse.unquote_plus(line.decode("utf-8"))
199
+ # chunk = get_vaild_json(decoded_url)
200
+ key, value = chunk.split(":", 1)
201
+ key = key.strip()
202
+ value = value.strip()
203
+ if key == "event" and value == "done":
204
+ break
205
+ # yield f"event: done\n\n".encode('utf-8')
206
+ else:
207
+ json_data = get_vaild_json(value)
208
+ if json_data is None:
209
+ continue
210
+ # print(f"[llm agent message] {json_data}")
211
+ yield f"data: {json.dumps(json_data)}\n\n".encode("utf-8")
212
+ except requests.exceptions.Timeout:
213
+ # 返回超时错误(SSE 格式)
214
+ error_msg = {"status": "error", "message": json.dumps({"error": "目标服务器响应超时"})}
215
+ yield f"data: {error_msg}\n\n".encode("utf-8") # 编码为字节流
216
+ except requests.exceptions.RequestException as e:
217
+ # 返回其他请求错误(SSE 格式)
218
+ error_msg = {"status": "error", "message": json.dumps({"error": f"目标服务器请求失败: {str(e)}"})}
219
+ yield f"data: {error_msg}\n\n".encode("utf-8") # 编码为字节流
220
+ except Exception as e:
221
+ # 捕获其他异常并返回错误(SSE 格式)
222
+ error_msg = {"status": "error", "message": json.dumps({"error": f"处理过程中发生错误: {str(e)}"})}
223
+ yield f"data: {error_msg}\n\n".encode("utf-8")
224
+
225
+ # 返回流式响应
226
+ return Response(generate(), content_type="text/event-stream", status=target_response.status_code)
227
+ except requests.exceptions.Timeout:
228
+ log_error(f"请求超时: {url}")
229
+ return fail_response("流连接超时")
230
+ except requests.exceptions.RequestException as e:
231
+ log_error(f"{format_date()}连接异常: {str(e)}")
232
+ import traceback
233
+ traceback.format_exc()
234
+ return fail_response(f"连接异常{traceback.format_exc()}")
235
+
236
+ def parse_message(self, reply_msg) -> tuple:
237
+ """解析llm agent消息结果"""
238
+ # 读取消息中的llm响应体
239
+ msg_type = get_message_type(messages=self.aid.get_content_array_from_message(reply_msg))
240
+ content = self.aid.get_content_from_message(reply_msg, message_type=msg_type)
241
+ if msg_type == "error":
242
+ return msg_type, content
243
+
244
+ if msg_type == "text/event-stream":
245
+ return msg_type, content
246
+
247
+ # 解析大模型返回
248
+ content_dict = json.loads(content)
249
+ return msg_type, AttrDict(content_dict)
@@ -0,0 +1,172 @@
1
+ # 在Python文件开头明确指定编码声明
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright 2025 AgentUnion Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ # from flask import Flask, request, Response, jsonify
17
+ # import threading
18
+ # import socket
19
+ # import time
20
+
21
+ # from .llm_agent_utils import LLMAgent,AttrDict
22
+ # from flask import jsonify, make_response
23
+ # import logging
24
+ # log = logging.getLogger('werkzeug')
25
+ # log.setLevel(logging.ERROR)
26
+ # app = Flask(__name__)
27
+ # # app.logger.disabled = True
28
+ # actual_port = 0
29
+
30
+ # llm_aid_app_key_map = {}
31
+ # llm_app_key_aid_map = {}
32
+ # is_running = False
33
+
34
+ # @app.route('/<llm_aid>/chat/completions', methods=['POST']) # 添加methods参数指定POST方法
35
+ # async def llm_request(llm_aid):
36
+ # # 获取请求头并打印
37
+ # headers = dict(request.headers)
38
+ # if request.is_json:
39
+ # body = request.get_json()
40
+ # else:
41
+ # body = request.form.to_dict()
42
+ # global llm_app_key_aid_map
43
+ # auth_str:str = headers.get("Authorization")
44
+ # llm_app_key = auth_str.replace("Bearer ","")
45
+ # aid = llm_app_key_aid_map.get(llm_app_key)
46
+ # if aid is None:
47
+ # return make_response(jsonify({"error": "Unauthorized"}), 401)
48
+ # llm_agent = LLMAgent(llm_agent=llm_aid, aid = aid)
49
+ # response = await llm_agent.chat_create(body)
50
+ # # print(response.get("status",""))
51
+ # if isinstance(response, AttrDict) and response.get("status","") == 'error':
52
+ # # 如果是错误状态,可以进行特殊处理,例如记录日志或返回自定义错误信息
53
+ # return make_response(jsonify({"error": response.get('message', "未知错误")}), response.get('code', 400))
54
+ # return response
55
+
56
+ # @app.route('/', defaults={'path': ''}, methods=['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
57
+ # @app.route('/<path:path>', methods=['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
58
+ # async def proxy(path):
59
+ # try:
60
+ # if request.method == 'OPTIONS':
61
+ # # 返回 CORS 预检响应头
62
+ # response = Response()
63
+ # response.headers['Access-Control-Allow-Origin'] = '*'
64
+ # response.headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
65
+ # response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
66
+ # return response
67
+ # elif request.method == 'POST':
68
+ # # 这个最终来自路由服务器,即模型名称对应一个地址和key
69
+ # # 校验请求数据
70
+ # # data = request.get_json()
71
+ # try:
72
+ # #return llm_request(llm_aid)
73
+ # llm_aid = path.split('/')[0]
74
+ # return await llm_request(llm_aid)
75
+ # except Exception as e:
76
+ # return jsonify({"error": f"{path}"}), 502
77
+ # elif request.method == 'GET':
78
+ # return jsonify({"result": "服务访问正常"}), 200
79
+ # else:
80
+ # return jsonify({"error": "Method not allowed"}), 405
81
+
82
+ # except Exception as e:
83
+ # return jsonify({"error": "Internal server error", "details": str(e)}), 500
84
+
85
+
86
+ def add_llm_aid(aid):
87
+ # global llm_aid_app_key_map, llm_app_key_aid_map
88
+ # import hashlib
89
+ # if aid.id in llm_aid_app_key_map:
90
+ # llm_app_key = llm_aid_app_key_map[aid.id]
91
+ # else:
92
+ # llm_app_key = str(int(time.time())+actual_port)
93
+ # llm_app_key = hashlib.sha256(llm_app_key.encode()).hexdigest()
94
+ # llm_aid_app_key_map[aid.id] = llm_app_key
95
+ # llm_app_key_aid_map[llm_app_key] = aid
96
+ return True
97
+
98
+
99
+ def add_llm_api_key(aid,llm_app_key):
100
+ # global llm_aid_app_key_map, llm_app_key_aid_map
101
+ # llm_aid_app_key_map[aid.id] = llm_app_key
102
+ # llm_app_key_aid_map[llm_app_key] = aid
103
+ return True
104
+
105
+
106
+ def get_base_url(aid,llm_aid):
107
+ # global actual_port
108
+ # # 获取实际分配的端口号
109
+ # return "http://127.0.0.1:"+str(actual_port)+"/"+llm_aid
110
+ pass
111
+
112
+ def get_llm_api_key(aid_str:str):
113
+ # 获取实际分配的端口号
114
+ pass
115
+ # global llm_aid_app_key_map,llm_app_key_aid_map
116
+ # if aid_str not in llm_aid_app_key_map:
117
+ # import secrets,hashlib
118
+ # llm_app_key = secrets.token_hex(16)
119
+ # llm_app_key = hashlib.sha256(llm_app_key.encode()).hexdigest()
120
+ # llm_aid_app_key_map[aid_str] = llm_app_key
121
+ # return llm_aid_app_key_map[aid_str]
122
+
123
+ def llm_server_is_running():
124
+ # global is_running
125
+ # return is_running
126
+ pass
127
+
128
+ def __run_server():
129
+ # 端口设为0让系统自动分配
130
+ pass
131
+ # try:
132
+ # global actual_port
133
+ # if actual_port == 0:
134
+ # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
135
+ # sock.bind(('', 0))
136
+ # actual_port = sock.getsockname()[1]
137
+ # sock.close()
138
+ # app.run(host='127.0.0.1', port = actual_port, debug=False)
139
+ # else:
140
+ # app.run(host='127.0.0.1', port = actual_port, debug=False)
141
+
142
+ # global is_running
143
+ # is_running = True
144
+ # except Exception as e:
145
+ # is_running = False
146
+ # #print(f"Flask服务启动失败,请检查端口占用后,重启服务")
147
+
148
+ def run_server(debug:bool = False,port:int = 0,llm_aid_app_key_map_h = {},llm_app_key_aid_map_h = {}):
149
+ # 创建并启动子线程运行Flask服务
150
+ pass
151
+ # app.logger.disabled = (not debug)
152
+ # global actual_port,llm_aid_app_key_map,llm_app_key_aid_map
153
+ # try:
154
+ # actual_port = int(port)
155
+ # except (ValueError, TypeError):
156
+ # actual_port = 0 # 如果转换失败,设置为默认值0
157
+ # llm_aid_app_key_map = llm_aid_app_key_map_h
158
+ # llm_app_key_aid_map = llm_app_key_aid_map_h
159
+ # server_thread = threading.Thread(target=__run_server)
160
+ # server_thread.daemon = True # 设置为守护线程,主线程退出时会自动结束
161
+ # server_thread.start()
162
+ # 主线程可以继续执行其他任务
163
+
164
+ # 添加一个关闭服务器的路由
165
+ # @app.route('/shutdown', methods=['POST'])
166
+ def shutdown_server():
167
+ pass
168
+ # func = request.environ.get('werkzeug.server.shutdown')
169
+ # if func is None:
170
+ # return jsonify({"status": "error", "message": "Not running with Werkzeug Server"})
171
+ # func()
172
+ # return jsonify({"status": "success", "message": "Server shutting down..."})
@@ -0,0 +1,210 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2025 AgentUnion Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from pathlib import Path
17
+
18
+ from agentcp.base.log import log_error, log_info
19
+
20
+
21
+ class Mermaid:
22
+ def __init__(self, content):
23
+ self.mermaid_code = content
24
+ self.graph_type = None
25
+ self.graph_direction = None # 新增:存储图的方向
26
+ self.nodes = []
27
+ self.node_dict = {}
28
+ self.node_styles = {}
29
+ self.edges = []
30
+ self.parse_mermaid()
31
+
32
+ log_info(f"图类型:{self.graph_type}")
33
+ log_info(f"总共{len(self.nodes)}个节点,节点列表如下:\n\t{self.nodes}")
34
+ for node in self.node_dict.items():
35
+ log_info(f"节点映射:{node}")
36
+ for edge in self.edges:
37
+ log_info(f"边关系:{edge}")
38
+
39
+
40
+ @classmethod
41
+ def from_file(cls, dir_path, file_name):
42
+ return cls(cls.read_mermaid_code(dir_path, file_name))
43
+ def read_mermaid_code(dir_path: str, file_name: str):
44
+ file_path = Path(dir_path) / f"{file_name}.mmd"
45
+ absolute_path = file_path.absolute()
46
+ log_info(f"读取Mermaid文件,绝对路径: {absolute_path}")
47
+ if file_path.is_file():
48
+ try:
49
+ with file_path.open('r', encoding='utf-8') as f:
50
+ content = f.read()
51
+ content = content.replace('&nbsp;', '').replace(u'\xa0', u'')
52
+ return content
53
+ except Exception as e:
54
+ log_error(f"读取文件错误: {e}")
55
+ return ""
56
+ else:
57
+ log_error(f"文件 {file_path} 未找到")
58
+ return ""
59
+
60
+ def parse_mermaid(self):
61
+ if not self.mermaid_code:
62
+ return
63
+
64
+ # 检查图类型和方向
65
+ for line in self.mermaid_code.split('\n'):
66
+ line = line.strip()
67
+ if not line or line.startswith('%'):
68
+ continue
69
+
70
+ if line.startswith('graph'):
71
+ parts = line.split()
72
+ if len(parts) >= 2:
73
+ self.graph_type = 'graph'
74
+ self.graph_direction = parts[1] # 存储方向 (TD, LR, BT, RL等)
75
+ break
76
+ elif line.startswith(('pie', 'sequenceDiagram', 'gantt', 'classDiagram', 'stateDiagram')):
77
+ self.graph_type = line.split()[0]
78
+ log_error(f"不支持 {self.graph_type} 类型的图")
79
+ return
80
+ else:
81
+ log_error("无法识别的图类型")
82
+ return
83
+
84
+ if self.graph_type != 'graph':
85
+ return
86
+
87
+ # 解析内容
88
+ for line in self.mermaid_code.split('\n'):
89
+ line = line.strip()
90
+ if not line or line.startswith(('%', 'classDef', 'linkStyle', 'style', 'graph')):
91
+ continue
92
+
93
+ # 解析节点
94
+ if (any(x in line for x in ['[', '(', '{']) and
95
+ not any(x in line for x in ['-->', '--', '->'])) or \
96
+ (not any(x in line for x in ['-->', '--', '->']) and
97
+ line not in self.node_dict and
98
+ not line.startswith('graph')):
99
+ self.parse_node(line)
100
+
101
+ # 解析边关系
102
+ if any(x in line for x in ['-->', '--', '->']):
103
+ self.parse_edge(line)
104
+
105
+ def parse_node(self, line):
106
+ """解析节点定义"""
107
+ node_name = ''
108
+ node_desc = ''
109
+ style_class = ''
110
+
111
+ # 提取样式类 (:::xxx)
112
+ if ':::' in line:
113
+ parts = line.split(':::')
114
+ line = parts[0].strip()
115
+ style_class = parts[1].strip()
116
+
117
+ # 花括号格式: A{描述}
118
+ if '{' in line and '}' in line:
119
+ start = line.find('{')
120
+ end = line.find('}')
121
+ node_name = line[:start].strip()
122
+ node_desc = line[start + 1:end].strip('" ')
123
+
124
+ # 方括号格式: A["描述"]
125
+ elif '[' in line and ']' in line:
126
+ start = line.find('[')
127
+ end = line.find(']')
128
+ node_name = line[:start].strip()
129
+ node_desc = line[start + 1:end].strip('" ')
130
+
131
+ # 圆括号格式: A(描述)
132
+ elif '(' in line and ')' in line:
133
+ start = line.find('(')
134
+ end = line.find(')')
135
+ node_name = line[:start].strip()
136
+ node_desc = line[start + 1:end].strip('" ')
137
+
138
+ # 无描述节点: A
139
+ else:
140
+ node_name = line.strip()
141
+ node_desc = node_name
142
+
143
+ if node_name and node_name not in self.node_dict:
144
+ self.node_dict[node_name] = node_desc or node_name
145
+ self.nodes.append(self.node_dict[node_name])
146
+ if style_class:
147
+ self.node_styles[node_name] = style_class
148
+
149
+ def parse_edge(self, line):
150
+ """解析边关系"""
151
+ line = line.replace(' ', '')
152
+
153
+ # 处理 A-->|描述|B 格式
154
+ if '-->|' in line and '|' in line[line.find('-->|') + 3:]:
155
+ arrow_pos = line.find('-->|')
156
+ desc_start = arrow_pos + 4
157
+ desc_end = line.find('|', desc_start)
158
+ source = line[:arrow_pos]
159
+ target = line[desc_end + 1:]
160
+ description = line[desc_start:desc_end]
161
+ self.edges.append((source, description, target))
162
+ return
163
+
164
+ # 处理 A-->B 格式
165
+ if '-->' in line:
166
+ parts = line.split('-->')
167
+ if len(parts) == 2:
168
+ self.edges.append((parts[0], '', parts[1]))
169
+ return
170
+
171
+ # 处理 A--描述-->B 格式
172
+ if '--' in line and '-->' in line:
173
+ arrow_pos = line.find('-->')
174
+ source = line[:line.find('--')]
175
+ target = line[arrow_pos + 3:]
176
+ description = line[line.find('--') + 2:arrow_pos]
177
+ self.edges.append((source, description, target))
178
+ return
179
+
180
+ # 处理 A->B 格式
181
+ if '->' in line:
182
+ parts = line.split('->')
183
+ if len(parts) == 2:
184
+ self.edges.append((parts[0], '', parts[1]))
185
+ return
186
+ # 示例使用
187
+ if __name__ == "__main__":
188
+ #mermaid = Mermaid.from_file('.', 'workflow')
189
+ mmd = """
190
+ graph TD
191
+ %% ===== 节点定义 =====
192
+ Z[用户交互层User]:::user
193
+ A[任务拆解/推进/退出]:::Planner
194
+ B(个人助手):::PA
195
+ D[Agent选择 AgentSelector]:::selector
196
+ F[协作Agents]:::action
197
+ G[单步任务执行]:::process
198
+ Z -->|自然语言任务| B
199
+ A -->|任务推进| D
200
+ B -->|简单任务直接执行交付或复杂任务执行完成交付| Z
201
+ B -->|复杂任务| A
202
+ A -->|复杂任务完成所有步骤或者达成退出条件,交付| B
203
+ D -->|任务推进|G
204
+ G -->|调用| F
205
+ F -->|成功/失败| G
206
+ G -->|有失败任务未达到交付条件,重选Agent| D
207
+ G -->|执行成功后交付|A
208
+ """
209
+
210
+ mermaid2 = Mermaid(mmd)