agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__py3-none-any.whl

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 (450) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
  2. agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
  3. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
  4. ksadk/a2a/__init__.py +180 -11
  5. ksadk/a2a/bootstrap.py +332 -0
  6. ksadk/a2a/card.py +15 -0
  7. ksadk/a2a/context_store.py +203 -0
  8. ksadk/a2a/control_plane.py +1056 -0
  9. ksadk/a2a/credential.py +99 -0
  10. ksadk/a2a/event_adapter.py +149 -0
  11. ksadk/a2a/executor.py +361 -137
  12. ksadk/a2a/external_transport.py +516 -0
  13. ksadk/a2a/identity.py +304 -0
  14. ksadk/a2a/ids.py +22 -0
  15. ksadk/a2a/langgraph.py +285 -0
  16. ksadk/a2a/resume_store.py +201 -0
  17. ksadk/a2a/routes.py +112 -0
  18. ksadk/a2a/server.py +102 -62
  19. ksadk/a2a/service_env.py +33 -0
  20. ksadk/a2a/space_client.py +1122 -0
  21. ksadk/a2a/task_adapter.py +423 -0
  22. ksadk/a2a/task_event_dispatcher.py +167 -0
  23. ksadk/a2a/task_event_outbox.py +326 -0
  24. ksadk/a2a/task_store.py +206 -0
  25. ksadk/a2a_card.py +101 -0
  26. ksadk/a2ui/__init__.py +27 -0
  27. ksadk/a2ui/core.py +186 -0
  28. ksadk/a2ui/models.py +166 -0
  29. ksadk/a2ui/renderer.py +198 -0
  30. ksadk/agui/__init__.py +9 -0
  31. ksadk/agui/a2ui_projection.py +92 -0
  32. ksadk/agui/agent.py +872 -0
  33. ksadk/agui/config.py +92 -0
  34. ksadk/agui/routes.py +47 -0
  35. ksadk/api/client.py +238 -204
  36. ksadk/api/environment.py +27 -0
  37. ksadk/builders/__init__.py +2 -0
  38. ksadk/builders/base.py +16 -14
  39. ksadk/builders/code_builder.py +375 -270
  40. ksadk/builders/container_builder.py +240 -200
  41. ksadk/builders/framework_requirements.py +31 -12
  42. ksadk/builders/ks3_uploader.py +60 -29
  43. ksadk/builders/managed_runtime_builder.py +173 -0
  44. ksadk/builders/mcp_builder.py +91 -92
  45. ksadk/builders/requirements_utils.py +1 -5
  46. ksadk/cli/__init__.py +97 -171
  47. ksadk/cli/cmd_a2a.py +255 -84
  48. ksadk/cli/cmd_agent.py +19 -5
  49. ksadk/cli/cmd_build.py +158 -29
  50. ksadk/cli/cmd_completion.py +47 -35
  51. ksadk/cli/cmd_config.py +267 -198
  52. ksadk/cli/cmd_create.py +507 -740
  53. ksadk/cli/cmd_dashboard.py +120 -45
  54. ksadk/cli/cmd_deploy.py +135 -59
  55. ksadk/cli/cmd_destroy.py +44 -14
  56. ksadk/cli/cmd_eval.py +386 -0
  57. ksadk/cli/cmd_files.py +63 -24
  58. ksadk/cli/cmd_hermes.py +313 -135
  59. ksadk/cli/cmd_invoke.py +184 -122
  60. ksadk/cli/cmd_launch.py +76 -28
  61. ksadk/cli/cmd_mcp.py +98 -64
  62. ksadk/cli/cmd_model.py +14 -5
  63. ksadk/cli/cmd_openclaw.py +564 -268
  64. ksadk/cli/cmd_replay.py +71 -0
  65. ksadk/cli/cmd_run.py +58 -73
  66. ksadk/cli/cmd_status.py +80 -31
  67. ksadk/cli/cmd_studio.py +110 -0
  68. ksadk/cli/cmd_version.py +20 -13
  69. ksadk/cli/cmd_web.py +95 -18
  70. ksadk/cli/deploy_utils.py +20 -20
  71. ksadk/cli/dry_run.py +7 -4
  72. ksadk/cli/env_options.py +83 -0
  73. ksadk/cli/error_utils.py +37 -12
  74. ksadk/cli/global_options.py +4 -3
  75. ksadk/cli/local_runtime.py +1 -2
  76. ksadk/cli/model_catalog.py +6 -2
  77. ksadk/cli/network_options.py +41 -19
  78. ksadk/cli/resource_common.py +6 -2
  79. ksadk/cli/runtime_bootstrap.py +51 -0
  80. ksadk/cli/storage.py +0 -1
  81. ksadk/cli/ui.py +11 -3
  82. ksadk/cli/workflow_common.py +52 -18
  83. ksadk/codex/__init__.py +12 -0
  84. ksadk/codex/client.py +1042 -0
  85. ksadk/codex/phase.py +143 -0
  86. ksadk/codex/runtime.py +940 -0
  87. ksadk/common/aicp_env.py +0 -1
  88. ksadk/common/auth.py +2 -2
  89. ksadk/common/constants.py +1 -3
  90. ksadk/common/kop_client.py +232 -0
  91. ksadk/common/llm_utils.py +17 -13
  92. ksadk/compat/__init__.py +5 -0
  93. ksadk/compat/adk_compat.py +200 -0
  94. ksadk/compat/copilotkit_a2ui.py +330 -0
  95. ksadk/configs/__init__.py +12 -14
  96. ksadk/configs/env_registry.py +661 -116
  97. ksadk/configs/global_config.py +3 -2
  98. ksadk/configs/settings.py +133 -209
  99. ksadk/conversations/__init__.py +85 -92
  100. ksadk/conversations/attachment_storage.py +16 -11
  101. ksadk/conversations/attachments.py +44 -36
  102. ksadk/conversations/compaction_pipeline.py +12 -12
  103. ksadk/conversations/compaction_prompt.py +9 -5
  104. ksadk/conversations/context.py +221 -6
  105. ksadk/conversations/message_projection.py +438 -51
  106. ksadk/conversations/model_context.py +12 -6
  107. ksadk/conversations/model_options.py +0 -1
  108. ksadk/conversations/normalize.py +20 -16
  109. ksadk/conversations/reasoning_markup.py +1 -1
  110. ksadk/conversations/runtime.py +339 -5255
  111. ksadk/conversations/runtime_compaction.py +333 -0
  112. ksadk/conversations/runtime_constants.py +27 -0
  113. ksadk/conversations/runtime_governance.py +185 -0
  114. ksadk/conversations/runtime_input.py +660 -0
  115. ksadk/conversations/runtime_invocation.py +395 -0
  116. ksadk/conversations/runtime_metadata.py +475 -0
  117. ksadk/conversations/runtime_observability.py +367 -0
  118. ksadk/conversations/runtime_payloads.py +272 -0
  119. ksadk/conversations/runtime_persistence.py +440 -0
  120. ksadk/conversations/runtime_preparation.py +426 -0
  121. ksadk/conversations/runtime_resume.py +836 -0
  122. ksadk/conversations/runtime_stream_events.py +902 -0
  123. ksadk/conversations/runtime_streaming.py +700 -0
  124. ksadk/conversations/semantic_summary.py +17 -5
  125. ksadk/conversations/session_title.py +6 -3
  126. ksadk/deployment/__init__.py +15 -16
  127. ksadk/deployment/agent_access.py +12 -23
  128. ksadk/deployment/base.py +51 -38
  129. ksadk/deployment/managed_runtime.py +33 -0
  130. ksadk/deployment/manager.py +85 -71
  131. ksadk/deployment/providers/serverless.py +379 -210
  132. ksadk/deployment/registry.py +38 -32
  133. ksadk/deployment/state.py +11 -11
  134. ksadk/deployment/ui_config.py +1 -1
  135. ksadk/detection/__init__.py +1 -1
  136. ksadk/detection/detector.py +146 -83
  137. ksadk/detection/mcp_detector.py +9 -6
  138. ksadk/evaluation/__init__.py +84 -0
  139. ksadk/evaluation/a2a_adapter.py +444 -0
  140. ksadk/evaluation/adapters.py +54 -0
  141. ksadk/evaluation/contracts.py +409 -0
  142. ksadk/evaluation/evalset.py +351 -0
  143. ksadk/evaluation/evaluators.py +461 -0
  144. ksadk/evaluation/executor.py +118 -0
  145. ksadk/evaluation/storage.py +68 -0
  146. ksadk/evaluation/target.py +73 -0
  147. ksadk/events/__init__.py +25 -0
  148. ksadk/events/parser.py +191 -0
  149. ksadk/events/replay.py +37 -0
  150. ksadk/events/runtime_event.py +273 -0
  151. ksadk/events/store.py +317 -0
  152. ksadk/harness/__init__.py +35 -0
  153. ksadk/harness/app.py +269 -0
  154. ksadk/harness/config.py +181 -0
  155. ksadk/harness/reasoner.py +105 -0
  156. ksadk/harness/runtime.py +364 -0
  157. ksadk/harness/sandbox.py +141 -0
  158. ksadk/harness/tools.py +222 -0
  159. ksadk/hermes_terminal.py +28 -12
  160. ksadk/identity/resolver.py +15 -9
  161. ksadk/ids.py +32 -0
  162. ksadk/knowledge_base/adk_tool.py +1 -1
  163. ksadk/knowledge_base/client.py +38 -33
  164. ksadk/managed_a2a_card.py +160 -0
  165. ksadk/managed_runtime.py +195 -0
  166. ksadk/markdown.py +5 -2
  167. ksadk/mcp_runtime/__init__.py +9 -6
  168. ksadk/memory/__init__.py +1 -1
  169. ksadk/memory/adk/__init__.py +2 -0
  170. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  171. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  172. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  173. ksadk/memory/adk/backends/sdk_ltm_backend.py +36 -36
  174. ksadk/memory/adk/long_term_memory.py +34 -37
  175. ksadk/memory/adk/resilient_session_service.py +4 -5
  176. ksadk/memory/adk/short_term_memory.py +12 -21
  177. ksadk/memory/adk_tool.py +3 -2
  178. ksadk/memory/backends/base.py +19 -19
  179. ksadk/memory/backends/memory.py +24 -25
  180. ksadk/memory/backends/redis.py +31 -30
  181. ksadk/memory/langchain_tool.py +2 -1
  182. ksadk/memory/manager.py +52 -47
  183. ksadk/memory/service.py +16 -6
  184. ksadk/memory/tool.py +4 -0
  185. ksadk/model_policy.py +15 -7
  186. ksadk/model_proxy/__init__.py +51 -0
  187. ksadk/model_proxy/bootstrap.py +80 -0
  188. ksadk/model_proxy/cache.py +165 -0
  189. ksadk/model_proxy/config.py +66 -0
  190. ksadk/model_proxy/demo_webui.py +115 -0
  191. ksadk/model_proxy/detect.py +145 -0
  192. ksadk/model_proxy/gate.py +68 -0
  193. ksadk/model_proxy/namespace.py +201 -0
  194. ksadk/model_proxy/protocol_proxy.py +22 -0
  195. ksadk/model_proxy/server.py +443 -0
  196. ksadk/model_proxy/transform.py +735 -0
  197. ksadk/openclaw_gateway.py +29 -14
  198. ksadk/runners/adk_runner.py +354 -146
  199. ksadk/runners/base_runner.py +71 -29
  200. ksadk/runners/deepagents_runner.py +0 -1
  201. ksadk/runners/factory.py +18 -5
  202. ksadk/runners/langchain_runner.py +29 -742
  203. ksadk/runners/langgraph_runner.py +417 -97
  204. ksadk/runners/patch_langchain.py +16 -16
  205. ksadk/runners/remote_runner.py +134 -33
  206. ksadk/runners/usage_accumulator.py +1 -0
  207. ksadk/runners/utils/__init__.py +0 -10
  208. ksadk/runners/utils/loader.py +23 -7
  209. ksadk/runtime/__init__.py +67 -0
  210. ksadk/runtime/adapter.py +412 -0
  211. ksadk/runtime/conversation_execution.py +553 -0
  212. ksadk/runtime/executor.py +251 -0
  213. ksadk/runtime/factory.py +156 -0
  214. ksadk/runtime/framework_adapters.py +118 -0
  215. ksadk/runtime/launch.py +48 -0
  216. ksadk/runtime/preprocessing.py +131 -0
  217. ksadk/runtime/runner_adapter.py +1032 -0
  218. ksadk/runtime/runner_loading.py +36 -0
  219. ksadk/runtime_context.py +18 -3
  220. ksadk/runtime_state.py +0 -1
  221. ksadk/sandbox/__init__.py +4 -1
  222. ksadk/sandbox/backends/__init__.py +4 -1
  223. ksadk/sandbox/backends/e2b.py +13 -5
  224. ksadk/sandbox/backends/local_process.py +11 -3
  225. ksadk/sandbox/base.py +10 -15
  226. ksadk/sandbox/factory.py +14 -4
  227. ksadk/sandbox/registry.py +57 -6
  228. ksadk/server/__init__.py +39 -4
  229. ksadk/server/api_models.py +15 -5
  230. ksadk/server/app.py +24 -3928
  231. ksadk/server/composition.py +47 -0
  232. ksadk/server/factory.py +622 -0
  233. ksadk/server/routes/__init__.py +1 -0
  234. ksadk/server/routes/checkpoint_resolution.py +116 -0
  235. ksadk/server/routes/common.py +682 -0
  236. ksadk/server/routes/control.py +331 -0
  237. ksadk/server/routes/dependencies.py +98 -0
  238. ksadk/server/routes/misc.py +425 -0
  239. ksadk/server/routes/models.py +451 -0
  240. ksadk/server/routes/openai_compat.py +229 -0
  241. ksadk/server/routes/projection.py +848 -0
  242. ksadk/server/routes/routers.py +52 -0
  243. ksadk/server/routes/run.py +519 -0
  244. ksadk/server/routes/sessions.py +644 -0
  245. ksadk/server/routes/streaming.py +265 -0
  246. ksadk/server/routes/workspace.py +414 -0
  247. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
  248. ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
  249. ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
  250. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
  251. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
  252. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
  253. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
  254. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
  255. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
  256. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
  257. ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
  258. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
  259. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
  260. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
  261. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
  262. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
  263. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
  264. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
  265. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
  266. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
  267. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
  268. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
  269. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
  270. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
  271. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
  272. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
  273. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
  274. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
  275. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
  276. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
  277. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
  278. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
  279. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
  280. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
  281. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
  282. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
  283. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
  284. ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
  285. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
  286. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
  287. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
  288. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
  289. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
  290. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
  291. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
  292. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
  293. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
  294. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
  295. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
  296. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
  297. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
  298. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
  299. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
  300. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
  301. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
  302. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
  303. ksadk/server/static/assets/index-R52pJJvg.js +177 -0
  304. ksadk/server/static/assets/index-jJr3_955.css +2 -0
  305. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
  306. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
  307. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
  308. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
  309. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
  310. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
  311. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
  312. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
  313. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
  314. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
  315. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
  316. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
  317. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
  318. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
  319. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
  320. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
  321. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
  322. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
  323. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
  324. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
  325. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
  326. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
  327. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
  328. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
  329. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
  330. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.js} +1 -1
  331. ksadk/server/static/index.html +2 -2
  332. ksadk/server/terminal_sessions.py +111 -58
  333. ksadk/sessions/__init__.py +69 -19
  334. ksadk/sessions/base.py +31 -11
  335. ksadk/sessions/continuity.py +2 -3
  336. ksadk/sessions/in_memory.py +46 -2
  337. ksadk/sessions/local_service.py +148 -17
  338. ksadk/sessions/postgres_service.py +143 -25
  339. ksadk/sessions/resilient.py +36 -8
  340. ksadk/skills/models.py +17 -5
  341. ksadk/skills/package_store.py +5 -2
  342. ksadk/skills/runtime/agent.py +20 -5
  343. ksadk/skills/runtime/artifacts.py +1 -5
  344. ksadk/skills/runtime/backends/e2b.py +10 -4
  345. ksadk/skills/runtime/backends/local.py +15 -3
  346. ksadk/skills/runtime/base.py +1 -2
  347. ksadk/skills/runtime/executor.py +10 -3
  348. ksadk/skills/runtime/factory.py +4 -2
  349. ksadk/skills/runtime/loader.py +1 -2
  350. ksadk/skills/runtime/registry.py +15 -10
  351. ksadk/skills/runtime/request.py +3 -1
  352. ksadk/skills/service_client.py +47 -16
  353. ksadk/skills/tool_defs.py +15 -10
  354. ksadk/studio/__init__.py +5 -0
  355. ksadk/studio/agent_avatar_assets.py +120 -0
  356. ksadk/studio/agent_lifecycle.py +51 -0
  357. ksadk/studio/api.py +1046 -0
  358. ksadk/studio/api_catalog_routes.py +324 -0
  359. ksadk/studio/api_contracts.py +177 -0
  360. ksadk/studio/api_helpers.py +132 -0
  361. ksadk/studio/authoring.py +524 -0
  362. ksadk/studio/authoring_coordinator.py +285 -0
  363. ksadk/studio/builder.py +320 -0
  364. ksadk/studio/capabilities.py +351 -0
  365. ksadk/studio/cloud.py +278 -0
  366. ksadk/studio/codex_agent_service.py +583 -0
  367. ksadk/studio/codex_builder.py +324 -0
  368. ksadk/studio/codex_manifest.py +278 -0
  369. ksadk/studio/codex_run.py +352 -0
  370. ksadk/studio/compiler.py +189 -0
  371. ksadk/studio/contracts.py +644 -0
  372. ksadk/studio/errors.py +47 -0
  373. ksadk/studio/evaluation.py +192 -0
  374. ksadk/studio/event_store.py +284 -0
  375. ksadk/studio/framework_run.py +114 -0
  376. ksadk/studio/identifiers.py +33 -0
  377. ksadk/studio/mcp_runtime.py +229 -0
  378. ksadk/studio/model_client.py +543 -0
  379. ksadk/studio/model_profile_service.py +214 -0
  380. ksadk/studio/operations.py +243 -0
  381. ksadk/studio/otel_trace.py +754 -0
  382. ksadk/studio/pagination.py +182 -0
  383. ksadk/studio/python_tool_inspection.py +183 -0
  384. ksadk/studio/repository.py +306 -0
  385. ksadk/studio/resource_catalog.py +1217 -0
  386. ksadk/studio/run_service.py +707 -0
  387. ksadk/studio/runtime_catalog.py +74 -0
  388. ksadk/studio/runtime_source.py +339 -0
  389. ksadk/studio/service.py +1141 -0
  390. ksadk/studio/shared_web.py +994 -0
  391. ksadk/studio/skill_discovery.py +628 -0
  392. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
  393. ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
  394. ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
  395. ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
  396. ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  397. ksadk/studio/static/index.html +15 -0
  398. ksadk/studio/templates.py +630 -0
  399. ksadk/studio/validator.py +246 -0
  400. ksadk/studio/workspace.py +137 -0
  401. ksadk/terminal_exec_policy.py +3 -4
  402. ksadk/tools/gateway.py +101 -15
  403. ksadk/tools/result_budget.py +12 -4
  404. ksadk/toolsets/__init__.py +161 -37
  405. ksadk/toolsets/a2a.py +426 -0
  406. ksadk/toolsets/platform.py +21 -5
  407. ksadk/toolsets/sandbox.py +82 -16
  408. ksadk/toolsets/skills.py +200 -39
  409. ksadk/toolsets/web.py +123 -26
  410. ksadk/toolsets/workspace.py +212 -101
  411. ksadk/tracing/__init__.py +19 -21
  412. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  413. ksadk/tracing/setup.py +135 -506
  414. ksadk/tracing/span_utils.py +0 -41
  415. ksadk/tui/loop.py +78 -30
  416. ksadk/tui/stream_render.py +1 -0
  417. ksadk/ui_config.py +5 -3
  418. ksadk/version.py +1 -1
  419. ksadk_runtime_common/memory_backend/registry.py +56 -56
  420. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  421. ksadk_runtime_common/workspace_files/router.py +8 -2
  422. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  423. ksadk/a2a/card_builder.py +0 -64
  424. ksadk/a2a/client.py +0 -309
  425. ksadk/runners/utils/langfuse.py +0 -230
  426. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  427. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  428. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  429. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  430. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  431. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  432. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  433. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  434. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  435. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  436. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  437. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  438. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  439. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  440. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  441. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  442. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  443. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  444. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  445. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  446. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  447. ksadk/tracing/exporters/langfuse_exporter.py +0 -445
  448. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
  449. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
  450. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/top_level.txt +0 -0
@@ -7,17 +7,17 @@ Code Builder - zip 打包模式构建
7
7
  3. 打包 zip (用户代码 + 依赖 + ksadk 源码 + entrypoint)
8
8
  """
9
9
 
10
+ import ast
11
+ import hashlib
12
+ import json
10
13
  import os
11
- import sys
14
+ import re
12
15
  import shutil
13
16
  import subprocess
17
+ import sys
14
18
  import threading
15
19
  import time
16
20
  import zipfile
17
- import re
18
- import json
19
- import hashlib
20
- import ast
21
21
  from concurrent.futures import ThreadPoolExecutor, as_completed
22
22
  from pathlib import Path
23
23
  from typing import List, Optional, Set
@@ -25,11 +25,13 @@ from urllib.parse import urlparse
25
25
  from urllib.request import Request, urlopen
26
26
 
27
27
  import click
28
+ from packaging.requirements import InvalidRequirement, Requirement
28
29
 
29
30
  from ksadk.builders.base import BaseBuilder, BuildResult
30
31
  from ksadk.builders.framework_requirements import (
31
32
  FASTAPI_REQUIREMENT,
32
- requirements_for_framework,
33
+ STARLETTE_REQUIREMENT,
34
+ code_requirements_for_framework,
33
35
  )
34
36
  from ksadk.builders.requirements_utils import (
35
37
  exclude_requirement_names,
@@ -85,35 +87,39 @@ class CodeBuilder(BaseBuilder):
85
87
  }
86
88
  IGNORED_FILE_NAMES = {".DS_Store"}
87
89
  KSADK_ALLOWED_SUFFIXES = {
88
- '.py',
89
- '.yaml',
90
- '.yml',
91
- '.json',
92
- '.jinja2',
93
- '.j2',
94
- '.txt',
95
- '.md',
96
- '.html',
97
- '.js',
98
- '.css',
99
- '.svg',
100
- '.ico',
101
- '.png',
102
- '.jpg',
103
- '.jpeg',
104
- '.gif',
105
- '.webp',
106
- '.woff',
107
- '.woff2',
108
- '.ttf',
109
- '.map',
90
+ ".py",
91
+ ".yaml",
92
+ ".yml",
93
+ ".json",
94
+ ".jinja2",
95
+ ".j2",
96
+ ".txt",
97
+ ".md",
98
+ ".html",
99
+ ".js",
100
+ ".css",
101
+ ".svg",
102
+ ".ico",
103
+ ".png",
104
+ ".jpg",
105
+ ".jpeg",
106
+ ".gif",
107
+ ".webp",
108
+ ".woff",
109
+ ".woff2",
110
+ ".ttf",
111
+ ".map",
110
112
  }
111
113
  BUNDLED_KSADK_CORE_RUNTIME_REQUIREMENTS = (
112
114
  "a2a-sdk>=0.3.22",
115
+ "culsans>=0.11.0",
113
116
  "httpx-sse>=0.4.0",
114
117
  "sse-starlette>=2.1.0",
115
118
  "python-multipart>=0.0.9,<1.0.0",
116
119
  "requests>=2.28.0",
120
+ "sqlalchemy>=2.0.0",
121
+ "aiosqlite>=0.19.0",
122
+ "greenlet>=3.0.0",
117
123
  "requests-aws4auth>=1.2.0",
118
124
  "kingsoftcloud-sdk-python>=1.5.8.94",
119
125
  "cryptography>=44.0.0",
@@ -124,15 +130,16 @@ class CodeBuilder(BaseBuilder):
124
130
  "mcp>=1.1.0",
125
131
  "langchain-mcp-adapters>=0.0.1",
126
132
  )
127
- BUNDLED_KSADK_POSTGRES_SESSION_REQUIREMENTS = (
128
- "asyncpg>=0.30.0,<1.0.0",
129
- )
133
+ BUNDLED_KSADK_POSTGRES_SESSION_REQUIREMENTS = ("asyncpg>=0.30.0,<1.0.0",)
130
134
  BUNDLED_KSADK_ATTACHMENT_RUNTIME_REQUIREMENTS = (
131
135
  "pypdf>=6.0.0",
132
136
  "beautifulsoup4>=4.12.0",
133
137
  )
134
- BUNDLED_KSADK_ATTACHMENT_OCR_RUNTIME_REQUIREMENTS = (
135
- "rapidocr-onnxruntime>=1.2.0",
138
+ BUNDLED_KSADK_ATTACHMENT_OCR_RUNTIME_REQUIREMENTS = ("rapidocr-onnxruntime>=1.2.0",)
139
+ BUNDLED_AGUI_RUNTIME_REQUIREMENTS = (
140
+ "ag-ui-protocol==0.1.19",
141
+ "ag-ui-langgraph[fastapi]==0.0.42",
142
+ "copilotkit==0.1.94",
136
143
  )
137
144
  BUNDLED_KSADK_RUNTIME_REQUIREMENTS = (
138
145
  *BUNDLED_KSADK_CORE_RUNTIME_REQUIREMENTS,
@@ -149,8 +156,8 @@ class CodeBuilder(BaseBuilder):
149
156
  PIP_INSTALL_TIMEOUT_SECONDS = 45 * 60
150
157
  CONTAINER_SUGGESTION_RAW_THRESHOLD_BYTES = 500 * 1024 * 1024
151
158
  CONTAINER_SUGGESTION_ZIP_THRESHOLD_BYTES = 300 * 1024 * 1024
152
-
153
- def __init__(self, project_dir: Path, config: dict = None):
159
+
160
+ def __init__(self, project_dir: Path, config: Optional[dict] = None):
154
161
  super().__init__(project_dir, config)
155
162
  self.build_dir = self.project_dir / ".agentengine" / "code_build"
156
163
  self.deps_dir = self.build_dir / "linux_deps"
@@ -168,33 +175,38 @@ class CodeBuilder(BaseBuilder):
168
175
  self._package_progress_logged_milestone_by_label: dict[str, int] = {}
169
176
  self._pip_index_candidates_cache: Optional[list[Optional[str]]] = None
170
177
  self._pip_index_selection_summary = ""
171
-
178
+
172
179
  def build(self) -> BuildResult:
173
180
  """执行 Code 模式构建"""
174
181
  from ksadk.detection import FrameworkDetector
175
-
182
+
176
183
  self._load_dotenv()
177
184
  config = self._load_config()
178
-
185
+
179
186
  # 检测框架
180
187
  detector = FrameworkDetector(str(self.project_dir))
181
188
  detection_result = detector.detect()
182
-
189
+
183
190
  if detection_result.type.value == "unknown":
191
+ return BuildResult(success=False, error_message="未检测到支持的框架")
192
+ if detection_result.type.value == "codex":
184
193
  return BuildResult(
185
194
  success=False,
186
- error_message="未检测到支持的框架"
195
+ error_message=(
196
+ "Codex 是 ManagedRuntime,不能构建宿主机 Code zip;"
197
+ "请使用 `ksadk build .` 的 managed 模式,或显式构建 Container"
198
+ ),
187
199
  )
188
-
200
+
189
201
  click.echo(f"📦 框架: {click.style(detection_result.type.value, fg='green')}")
190
202
  click.echo(f"🤖 Agent: {click.style(detection_result.name, fg='blue')}")
191
-
192
- agent_name = config.get('name', self.project_dir.name).replace('-', '_').replace('.', '_')
193
-
203
+
204
+ agent_name = config.get("name", self.project_dir.name).replace("-", "_").replace(".", "_")
205
+
194
206
  # 创建构建目录
195
207
  self.build_dir.mkdir(parents=True, exist_ok=True)
196
208
  zip_path = self.build_dir / f"{agent_name}.zip"
197
-
209
+
198
210
  # 检查是否需要重新构建
199
211
  no_cache = self.config.get("no_cache", False) if self.config else False
200
212
  repackage = self.config.get("repackage", False) if self.config else False
@@ -207,31 +219,33 @@ class CodeBuilder(BaseBuilder):
207
219
  if zip_path.exists() and not rebuild_needed:
208
220
  incompatibles = self._scan_incompatible_binaries_in_zip(zip_path)
209
221
  if incompatibles:
210
- click.secho("\n⚠️ 检测到缓存构建包含非 Linux 兼容关键二进制,自动重建...", fg='yellow')
222
+ click.secho(
223
+ "\n⚠️ 检测到缓存构建包含非 Linux 兼容关键二进制,自动重建...", fg="yellow"
224
+ )
211
225
  for item in incompatibles[:5]:
212
226
  click.echo(f" - {item}")
213
227
  rebuild_reason = "缓存 zip 存在 Linux 兼容性问题"
214
228
  else:
215
229
  self._save_input_fingerprint(zip_path, detection_result)
216
230
  zip_size = zip_path.stat().st_size / (1024 * 1024)
217
- click.secho(f"\n✅ 使用已有构建: {zip_path.name} ({zip_size:.2f} MB)", fg='green')
218
- click.echo(" (如需只重新打包当前代码/runtime,请使用 --repackage;如需重装依赖,请使用 --no-cache)")
231
+ click.secho(f"\n✅ 使用已有构建: {zip_path.name} ({zip_size:.2f} MB)", fg="green")
232
+ click.echo(
233
+ " (如需只重新打包当前代码/runtime,请使用 --repackage;"
234
+ "如需重装依赖,请使用 --no-cache)"
235
+ )
219
236
  return BuildResult(
220
237
  success=True,
221
238
  artifact_path=zip_path,
222
239
  artifact_size=zip_path.stat().st_size,
223
- metadata={
224
- "agent_name": agent_name,
225
- "framework": detection_result.type.value
226
- }
240
+ metadata={"agent_name": agent_name, "framework": detection_result.type.value},
227
241
  )
228
242
  elif rebuild_reason:
229
243
  click.echo(f" 重新打包原因: {rebuild_reason}")
230
-
244
+
231
245
  # Step 1: 准备依赖
232
246
  click.echo("\n📋 Step 1/3: 准备依赖清单...")
233
247
  requirements_path = self._prepare_requirements(detection_result)
234
-
248
+
235
249
  # Step 2: 安装依赖
236
250
  click.echo("\n📦 Step 2/3: 安装依赖...")
237
251
  reuse_dependencies, reuse_reason = self._can_reuse_dependency_cache(
@@ -241,7 +255,8 @@ class CodeBuilder(BaseBuilder):
241
255
  if reuse_dependencies:
242
256
  stats = self._current_dependency_stats()
243
257
  click.secho(
244
- f" ✓ 复用 Linux 依赖缓存: {stats['file_count']} 个文件, {stats['size_mb']:.1f} MB",
258
+ f" ✓ 复用 Linux 依赖缓存: {stats['file_count']} 个文件, "
259
+ f"{stats['size_mb']:.1f} MB",
245
260
  fg="green",
246
261
  )
247
262
  click.echo(f" {reuse_reason}")
@@ -250,25 +265,22 @@ class CodeBuilder(BaseBuilder):
250
265
  click.echo(f" {reuse_reason}")
251
266
  self._clear_dependency_cache()
252
267
  self.deps_dir.mkdir(parents=True, exist_ok=True)
253
-
268
+
254
269
  if not self._install_dependencies(requirements_path):
255
- return BuildResult(
256
- success=False,
257
- error_message="依赖安装失败"
258
- )
270
+ return BuildResult(success=False, error_message="依赖安装失败")
259
271
  self._save_dependency_fingerprint(requirements_path)
260
-
272
+
261
273
  # Step 3: 打包 zip
262
274
  click.echo("\n📦 Step 3/3: 打包 zip...")
263
275
  package_started_at = time.monotonic()
264
276
  self._package_zip(zip_path, detection_result)
265
277
  click.echo(f" ✓ 打包耗时: {self._format_elapsed(package_started_at)}")
266
278
  self._save_input_fingerprint(zip_path, detection_result)
267
-
279
+
268
280
  zip_size = zip_path.stat().st_size
269
281
  click.echo(f" zip 文件: {zip_path}")
270
282
  click.echo(f" 大小: {zip_size / (1024 * 1024):.2f} MB")
271
-
283
+
272
284
  return BuildResult(
273
285
  success=True,
274
286
  artifact_path=zip_path,
@@ -276,8 +288,8 @@ class CodeBuilder(BaseBuilder):
276
288
  metadata={
277
289
  "agent_name": agent_name,
278
290
  "framework": detection_result.type.value,
279
- "deps_dir": str(self.deps_dir)
280
- }
291
+ "deps_dir": str(self.deps_dir),
292
+ },
281
293
  )
282
294
 
283
295
  def _rebuild_decision(
@@ -321,26 +333,32 @@ class CodeBuilder(BaseBuilder):
321
333
  if changed:
322
334
  return "业务代码或项目文件变更"
323
335
  return "构建输入指纹变化"
324
-
336
+
325
337
  def _need_rebuild(self, zip_path: Path, detection_result) -> bool:
326
338
  """检查是否需要重新构建。优先使用输入内容指纹,缺失时回退到 mtime。"""
327
339
  manifest = self._load_input_fingerprint(zip_path)
328
340
  if manifest:
329
341
  current_fingerprint = self._build_input_fingerprint(detection_result)
330
- return manifest.get("fingerprint") != current_fingerprint["fingerprint"]
342
+ return bool(manifest.get("fingerprint") != current_fingerprint["fingerprint"])
331
343
  return self._need_rebuild_from_mtime(zip_path)
332
344
 
333
345
  def _need_rebuild_from_mtime(self, zip_path: Path) -> bool:
334
346
  """兼容旧缓存:当没有指纹文件时,回退到 mtime 判断。"""
335
347
  zip_mtime = zip_path.stat().st_mtime
336
-
348
+
337
349
  for item in self.project_dir.iterdir():
338
- if item.name.startswith('.') or item.name in ('__pycache__', 'node_modules', '.git', '.venv', 'venv'):
350
+ if item.name.startswith(".") or item.name in (
351
+ "__pycache__",
352
+ "node_modules",
353
+ ".git",
354
+ ".venv",
355
+ "venv",
356
+ ):
339
357
  continue
340
358
  if item.is_file() and item.stat().st_mtime > zip_mtime:
341
359
  return True
342
360
  if item.is_dir():
343
- for file_path in item.rglob('*.py'):
361
+ for file_path in item.rglob("*.py"):
344
362
  if file_path.stat().st_mtime > zip_mtime:
345
363
  return True
346
364
  return False
@@ -360,7 +378,7 @@ class CodeBuilder(BaseBuilder):
360
378
  data = json.load(f)
361
379
  if data.get("version") != self.INPUT_FINGERPRINT_VERSION:
362
380
  return None
363
- return data
381
+ return data if isinstance(data, dict) else None
364
382
  except Exception:
365
383
  return None
366
384
 
@@ -380,7 +398,7 @@ class CodeBuilder(BaseBuilder):
380
398
  data = json.load(f)
381
399
  if data.get("version") != self.DEPENDENCY_FINGERPRINT_VERSION:
382
400
  return None
383
- return data
401
+ return data if isinstance(data, dict) else None
384
402
  except Exception:
385
403
  return None
386
404
 
@@ -429,7 +447,12 @@ class CodeBuilder(BaseBuilder):
429
447
  return True
430
448
  if self._project_env_has_configured_value("KSADK_MCP_SERVERS"):
431
449
  return True
432
- if self._project_env_value("KSADK_ENABLE_MCP_TOOLS").strip().lower() in {"1", "true", "yes", "on"}:
450
+ if self._project_env_value("KSADK_ENABLE_MCP_TOOLS").strip().lower() in {
451
+ "1",
452
+ "true",
453
+ "yes",
454
+ "on",
455
+ }:
433
456
  return True
434
457
  return self._project_imports_any({"mcp", "langchain_mcp_adapters"})
435
458
 
@@ -515,13 +538,17 @@ class CodeBuilder(BaseBuilder):
515
538
  digest = hashlib.sha256()
516
539
  requirements_text = requirements_path.read_text(encoding="utf-8")
517
540
 
518
- digest.update(f"deps-fingerprint-version:{self.DEPENDENCY_FINGERPRINT_VERSION}\n".encode("utf-8"))
541
+ digest.update(
542
+ f"deps-fingerprint-version:{self.DEPENDENCY_FINGERPRINT_VERSION}\n".encode("utf-8")
543
+ )
519
544
  digest.update(f"builder-platform:{sys.platform}\n".encode("utf-8"))
520
545
  digest.update(
521
546
  f"builder-python:{sys.version_info.major}.{sys.version_info.minor}\n".encode("utf-8")
522
547
  )
523
548
  digest.update(f"target-python:{self.TARGET_PYTHON_VERSION}\n".encode("utf-8"))
524
- digest.update(f"target-platforms:{','.join(self.TARGET_INSTALL_PLATFORMS)}\n".encode("utf-8"))
549
+ digest.update(
550
+ f"target-platforms:{','.join(self.TARGET_INSTALL_PLATFORMS)}\n".encode("utf-8")
551
+ )
525
552
  digest.update(requirements_text.encode("utf-8"))
526
553
 
527
554
  requirements = [line for line in requirements_text.splitlines() if line.strip()]
@@ -641,13 +668,13 @@ class CodeBuilder(BaseBuilder):
641
668
  )
642
669
 
643
670
  def _iter_bundled_source_package(self, package_name: str, package_root: Path):
644
- for file_path in sorted(package_root.rglob('*')):
671
+ for file_path in sorted(package_root.rglob("*")):
645
672
  if not file_path.is_file():
646
673
  continue
647
674
  relative_path = file_path.relative_to(package_root)
648
675
  if package_name == "ksadk" and self._should_skip_ksadk_relative_path(relative_path):
649
676
  continue
650
- if '__pycache__' in file_path.parts:
677
+ if "__pycache__" in file_path.parts:
651
678
  continue
652
679
  suffix = file_path.suffix.lower()
653
680
  if suffix not in self.KSADK_ALLOWED_SUFFIXES:
@@ -709,26 +736,26 @@ class CodeBuilder(BaseBuilder):
709
736
  file_name.startswith(".env.")
710
737
  and file_name not in {".env.example", ".env.sample", ".env.template"}
711
738
  )
712
-
739
+
713
740
  def _prepare_requirements(self, detection_result) -> Path:
714
741
  """准备 requirements.txt"""
715
742
  final_deps = self._build_requirements_list(detection_result)
716
743
 
717
744
  if (self.project_dir / "requirements.txt").exists():
718
- click.echo(f" 发现 requirements.txt,正在合并...")
745
+ click.echo(" 发现 requirements.txt,正在合并...")
719
746
  else:
720
- click.echo(f" 自动生成依赖清单")
721
-
747
+ click.echo(" 自动生成依赖清单")
748
+
722
749
  # 写入构建目录
723
750
  requirements_path = self.build_dir / "requirements.txt"
724
751
  requirements_path.write_text("\n".join(final_deps))
725
-
752
+
726
753
  click.echo(f" 共 {len(final_deps)} 个依赖包:")
727
754
  for dep in final_deps[:5]:
728
755
  click.echo(f" • {dep}")
729
756
  if len(final_deps) > 5:
730
757
  click.echo(f" ... 及其他 {len(final_deps) - 5} 个")
731
-
758
+
732
759
  return requirements_path
733
760
 
734
761
  def _build_requirements_list(self, detection_result) -> List[str]:
@@ -740,37 +767,59 @@ class CodeBuilder(BaseBuilder):
740
767
  user_requirements = self.project_dir / "requirements.txt"
741
768
  if user_requirements.exists():
742
769
  user_content = user_requirements.read_text(encoding="utf-8")
770
+ parsed_user_requirements = parse_requirements_text(user_content)
771
+ final_deps = merge_requirement_lists(
772
+ final_deps,
773
+ self._bundled_ksadk_extra_requirements(parsed_user_requirements),
774
+ )
743
775
  user_deps = exclude_requirement_names(
744
- parse_requirements_text(user_content),
776
+ parsed_user_requirements,
745
777
  excluded_names={"ksadk"},
746
778
  )
747
779
  final_deps = merge_requirement_lists(final_deps, user_deps)
748
780
 
749
781
  return final_deps
750
-
782
+
783
+ def _bundled_ksadk_extra_requirements(self, requirements: List[str]) -> tuple[str, ...]:
784
+ extras: set[str] = set()
785
+ for raw_requirement in requirements:
786
+ try:
787
+ parsed = Requirement(raw_requirement)
788
+ except InvalidRequirement:
789
+ continue
790
+ if parsed.name.lower().replace("_", "-") != "ksadk":
791
+ continue
792
+ extras.update(extra.lower() for extra in parsed.extras)
793
+
794
+ bundled: list[str] = []
795
+ if "agui" in extras:
796
+ bundled.extend(self.BUNDLED_AGUI_RUNTIME_REQUIREMENTS)
797
+ return tuple(bundled)
798
+
751
799
  def _get_base_requirements(self, detection_result) -> List[str]:
752
800
  """获取基础依赖列表"""
753
801
  deps = [
754
802
  # Core
755
803
  FASTAPI_REQUIREMENT,
804
+ STARLETTE_REQUIREMENT,
756
805
  "uvicorn>=0.23.0",
757
806
  "python-dotenv>=1.0.0",
758
807
  "pydantic>=2.0.0",
759
808
  "pyyaml>=6.0.0",
760
809
  "httpx>=0.24.0",
761
- # Tracing
810
+ # Tracing — OTLP 双写收敛:只用 OTel + openinference 自动插桩,
811
+ # 不再安装 Langfuse SDK(callback 路径已删除,Langfuse 通过标准 OTLP 接收)。
762
812
  "opentelemetry-api>=1.37.0",
763
813
  "opentelemetry-sdk>=1.37.0",
764
814
  "opentelemetry-exporter-otlp>=1.37.0",
765
815
  "openinference-instrumentation-langchain>=0.1.0",
766
- "langfuse>=2.0.0",
767
816
  ]
768
-
817
+
769
818
  framework = detection_result.type.value
770
- deps += requirements_for_framework(framework)
771
-
819
+ deps += code_requirements_for_framework(framework)
820
+
772
821
  return deps
773
-
822
+
774
823
  # 目标 Python 版本 (必须与容器运行时一致)
775
824
  TARGET_PYTHON_VERSION = "312" # 容器中为 Python 3.12
776
825
 
@@ -875,9 +924,7 @@ class CodeBuilder(BaseBuilder):
875
924
  def _result_error_summary(self, result: subprocess.CompletedProcess[str] | None) -> str:
876
925
  if result is None:
877
926
  return "unknown"
878
- combined = "\n".join(
879
- part for part in (result.stderr or "", result.stdout or "") if part
880
- )
927
+ combined = "\n".join(part for part in (result.stderr or "", result.stdout or "") if part)
881
928
  for line in reversed(combined.splitlines()):
882
929
  normalized = line.strip()
883
930
  if normalized:
@@ -887,9 +934,7 @@ class CodeBuilder(BaseBuilder):
887
934
  def _pip_missing_from_result(self, result: subprocess.CompletedProcess[str] | None) -> bool:
888
935
  if result is None:
889
936
  return False
890
- combined = "\n".join(
891
- part for part in (result.stderr or "", result.stdout or "") if part
892
- )
937
+ combined = "\n".join(part for part in (result.stderr or "", result.stdout or "") if part)
893
938
  return "No module named pip" in combined
894
939
 
895
940
  def _bootstrap_pip_if_missing(self, result: subprocess.CompletedProcess[str] | None) -> bool:
@@ -952,7 +997,9 @@ class CodeBuilder(BaseBuilder):
952
997
  if not normalized:
953
998
  return None
954
999
 
955
- force_emit = percent > self._install_progress_percent or stage != self._install_progress_stage_name
1000
+ force_emit = (
1001
+ percent > self._install_progress_percent or stage != self._install_progress_stage_name
1002
+ )
956
1003
  count = self._install_progress_event_counts.get(stage, 0) + 1
957
1004
  self._install_progress_event_counts[stage] = count
958
1005
 
@@ -960,7 +1007,7 @@ class CodeBuilder(BaseBuilder):
960
1007
  return self._truncate_progress_summary(normalized)
961
1008
 
962
1009
  if stage == "解析依赖":
963
- detail = normalized[len("Collecting "):].split(" (from ", 1)[0]
1010
+ detail = normalized[len("Collecting ") :].split(" (from ", 1)[0]
964
1011
  summary = f"已解析 {count} 个依赖,最近: {detail}"
965
1012
  if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 10 == 0:
966
1013
  return summary
@@ -973,35 +1020,36 @@ class CodeBuilder(BaseBuilder):
973
1020
 
974
1021
  if stage == "下载依赖":
975
1022
  if normalized.startswith("Using cached "):
976
- payload = normalized[len("Using cached "):]
1023
+ payload = normalized[len("Using cached ") :]
977
1024
  elif normalized.startswith("Downloading "):
978
- payload = normalized[len("Downloading "):]
1025
+ payload = normalized[len("Downloading ") :]
979
1026
  else:
980
1027
  payload = normalized
981
1028
  target = self._extract_pip_artifact_name(payload)
982
- summary = f"已处理 {count} 个 wheel,耗时 {self._format_elapsed(self._install_progress_started_at)},最近: {target}"
1029
+ elapsed = self._format_elapsed(self._install_progress_started_at)
1030
+ summary = f"已处理 {count} 个 wheel,耗时 {elapsed},最近: {target}"
983
1031
  if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 5 == 0:
984
1032
  return summary
985
1033
  return None
986
1034
 
987
1035
  if stage == "构建 wheel":
988
- detail = normalized[len("Building wheel for "):].split(" ", 1)[0]
1036
+ detail = normalized[len("Building wheel for ") :].split(" ", 1)[0]
989
1037
  if force_emit or count == 1 or count % 5 == 0:
990
1038
  return f"构建 wheel: {detail}"
991
1039
  return None
992
1040
 
993
1041
  if stage == "安装依赖":
994
- detail = normalized[len("Installing collected packages:"):].strip()
1042
+ detail = normalized[len("Installing collected packages:") :].strip()
995
1043
  return f"安装包: {self._truncate_progress_summary(detail, max_length=60)}"
996
1044
 
997
1045
  if stage == "复用已安装依赖":
998
- detail = normalized[len("Requirement already satisfied:"):].split(" in ", 1)[0].strip()
1046
+ detail = normalized[len("Requirement already satisfied:") :].split(" in ", 1)[0].strip()
999
1047
  if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 10 == 0:
1000
1048
  return f"复用依赖: {detail}"
1001
1049
  return None
1002
1050
 
1003
1051
  if stage == "安装完成":
1004
- detail = normalized[len("Successfully installed "):].strip()
1052
+ detail = normalized[len("Successfully installed ") :].strip()
1005
1053
  return f"最近结果: {self._truncate_progress_summary(detail, max_length=60)}"
1006
1054
 
1007
1055
  return self._truncate_progress_summary(normalized)
@@ -1094,8 +1142,7 @@ class CodeBuilder(BaseBuilder):
1094
1142
  timings: dict[str, Optional[float]] = {}
1095
1143
  with ThreadPoolExecutor(max_workers=min(4, len(urls))) as executor:
1096
1144
  future_to_url = {
1097
- executor.submit(self._probe_pip_index_latency, url): url
1098
- for url in urls
1145
+ executor.submit(self._probe_pip_index_latency, url): url for url in urls
1099
1146
  }
1100
1147
  for future in as_completed(future_to_url):
1101
1148
  url = future_to_url[future]
@@ -1116,7 +1163,7 @@ class CodeBuilder(BaseBuilder):
1116
1163
  best = ordered[0]
1117
1164
  best_ms = round((timings[best] or 0.0) * 1000)
1118
1165
  return ordered, f"测速优先 {self._short_pip_index_label(best)} ({best_ms} ms)"
1119
-
1166
+
1120
1167
  def _install_dependencies(self, requirements_path: Path) -> bool:
1121
1168
  """安装依赖到 deps_dir"""
1122
1169
  current_python = f"{sys.version_info.major}.{sys.version_info.minor}"
@@ -1128,9 +1175,8 @@ class CodeBuilder(BaseBuilder):
1128
1175
  )
1129
1176
 
1130
1177
  try:
1131
- need_binary_replacement = (
1132
- sys.platform in ("darwin", "win32")
1133
- or (sys.platform.startswith("linux") and current_python != target_python)
1178
+ need_binary_replacement = sys.platform in ("darwin", "win32") or (
1179
+ sys.platform.startswith("linux") and current_python != target_python
1134
1180
  )
1135
1181
  used_target_runtime_wheels = False
1136
1182
  result = None
@@ -1167,16 +1213,18 @@ class CodeBuilder(BaseBuilder):
1167
1213
  requirements_path,
1168
1214
  target_runtime_wheels=False,
1169
1215
  )
1170
-
1216
+
1171
1217
  if result.returncode != 0:
1172
1218
  self._finish_install_progress()
1173
- click.secho(" ✗ 安装失败", fg='red')
1219
+ click.secho(" ✗ 安装失败", fg="red")
1174
1220
  if result.stderr:
1175
- error_lines = [l for l in result.stderr.split('\n') if 'ERROR' in l.upper()][:3]
1221
+ error_lines = [
1222
+ line for line in result.stderr.split("\n") if "ERROR" in line.upper()
1223
+ ][:3]
1176
1224
  for line in error_lines:
1177
1225
  click.echo(f" {line}")
1178
1226
  return False
1179
-
1227
+
1180
1228
  # 替换非 Linux 平台二进制,或 Linux 下非目标 Python ABI 的二进制
1181
1229
  if need_binary_replacement and not used_target_runtime_wheels:
1182
1230
  self._emit_install_progress(
@@ -1185,7 +1233,7 @@ class CodeBuilder(BaseBuilder):
1185
1233
  "检测并替换平台相关二进制",
1186
1234
  )
1187
1235
  self._replace_platform_binaries()
1188
-
1236
+
1189
1237
  # 二进制兼容性校验(避免把不可运行的包部署到 Linux Runtime)
1190
1238
  self._emit_install_progress(
1191
1239
  96,
@@ -1195,7 +1243,7 @@ class CodeBuilder(BaseBuilder):
1195
1243
  incompatibles = self._scan_incompatible_binaries_in_deps()
1196
1244
  if incompatibles:
1197
1245
  self._finish_install_progress()
1198
- click.secho(" ✗ 检测到与 Linux Runtime 不兼容的关键二进制,构建终止", fg='red')
1246
+ click.secho(" ✗ 检测到与 Linux Runtime 不兼容的关键二进制,构建终止", fg="red")
1199
1247
  for item in incompatibles[:10]:
1200
1248
  click.echo(f" - {item}")
1201
1249
  if any(i.startswith("python-abi-mismatch:") for i in incompatibles):
@@ -1204,213 +1252,243 @@ class CodeBuilder(BaseBuilder):
1204
1252
  f"请使用 Python {target_python} 构建,或改用 Container 模式部署"
1205
1253
  )
1206
1254
  if any("tiktoken" in i for i in incompatibles):
1207
- click.echo(" 提示: tiktoken 为 langchain-openai 必需;若替换失败可重试或检查网络/镜像")
1255
+ click.echo(
1256
+ " 提示: tiktoken 为 langchain-openai 必需;"
1257
+ "若替换失败可重试或检查网络/镜像"
1258
+ )
1208
1259
  click.echo(" 建议: 删除 .agentengine/*_build 后重新构建,或在 Linux 环境重新打包")
1209
1260
  return False
1210
-
1211
- deps_count = sum(1 for _ in self.deps_dir.rglob('*') if _.is_file())
1212
- deps_size = sum(f.stat().st_size for f in self.deps_dir.rglob('*') if f.is_file()) / (1024 * 1024)
1261
+
1262
+ deps_count = sum(1 for _ in self.deps_dir.rglob("*") if _.is_file())
1263
+ deps_size = sum(f.stat().st_size for f in self.deps_dir.rglob("*") if f.is_file()) / (
1264
+ 1024 * 1024
1265
+ )
1213
1266
  self._emit_install_progress(
1214
1267
  100,
1215
1268
  "依赖安装完成",
1216
1269
  f"{deps_count} 个文件, {deps_size:.1f} MB",
1217
1270
  )
1218
1271
  self._finish_install_progress()
1219
-
1272
+
1220
1273
  return True
1221
-
1274
+
1222
1275
  except subprocess.TimeoutExpired:
1223
1276
  self._finish_install_progress()
1224
1277
  timeout_minutes = max(1, round(self._pip_install_timeout_seconds() / 60))
1225
- click.secho(f" ✗ 安装超时 ({timeout_minutes}分钟)", fg='red')
1278
+ click.secho(f" ✗ 安装超时 ({timeout_minutes}分钟)", fg="red")
1226
1279
  return False
1227
1280
  except Exception as e:
1228
1281
  self._finish_install_progress()
1229
- click.secho(f" ✗ 依赖安装失败: {e}", fg='red')
1282
+ click.secho(f" ✗ 依赖安装失败: {e}", fg="red")
1230
1283
  return False
1231
-
1284
+
1232
1285
  def _replace_platform_binaries(self) -> None:
1233
1286
  """替换非目标运行时平台/ABI 的 C 扩展为 Linux 目标版本。"""
1234
1287
  # 模块名到 pip 包名的映射
1235
1288
  MODULE_TO_PACKAGE = {
1236
- '_cffi_backend': 'cffi',
1237
- 'yaml': 'pyyaml',
1238
- '_yaml': 'pyyaml',
1239
- 'rpds': 'rpds-py',
1240
- 'PIL': 'pillow',
1241
- 'cv2': 'opencv-python',
1242
- 'sklearn': 'scikit-learn',
1243
- '_watchdog_fsevents': 'watchdog',
1244
- 'google': None, # 跳过命名空间包
1245
- 'grpc': 'grpcio',
1246
- '_grpc': 'grpcio',
1247
- 'uuid_utils': 'uuid-utils',
1248
- 'pydantic_core': 'pydantic-core',
1249
- '_pydantic_core': 'pydantic-core',
1289
+ "_cffi_backend": "cffi",
1290
+ "yaml": "pyyaml",
1291
+ "_yaml": "pyyaml",
1292
+ "rpds": "rpds-py",
1293
+ "PIL": "pillow",
1294
+ "cv2": "opencv-python",
1295
+ "sklearn": "scikit-learn",
1296
+ "_watchdog_fsevents": "watchdog",
1297
+ "google": None, # 跳过命名空间包
1298
+ "grpc": "grpcio",
1299
+ "_grpc": "grpcio",
1300
+ "uuid_utils": "uuid-utils",
1301
+ "pydantic_core": "pydantic-core",
1302
+ "_pydantic_core": "pydantic-core",
1250
1303
  # tiktoken: langchain-openai 核心依赖, Rust 编译的 C 扩展
1251
- 'tiktoken': 'tiktoken',
1252
- '_tiktoken': 'tiktoken',
1304
+ "tiktoken": "tiktoken",
1305
+ "_tiktoken": "tiktoken",
1253
1306
  # 其他常见原生扩展
1254
- 'regex': 'regex',
1255
- '_regex': 'regex',
1256
- 'multidict': 'multidict',
1257
- 'yarl': 'yarl',
1258
- 'aiohttp': 'aiohttp',
1259
- 'frozenlist': 'frozenlist',
1260
- 'charset_normalizer': 'charset-normalizer',
1261
- 'msgpack': 'msgpack',
1307
+ "regex": "regex",
1308
+ "_regex": "regex",
1309
+ "multidict": "multidict",
1310
+ "yarl": "yarl",
1311
+ "aiohttp": "aiohttp",
1312
+ "frozenlist": "frozenlist",
1313
+ "charset_normalizer": "charset-normalizer",
1314
+ "msgpack": "msgpack",
1262
1315
  # Windows 特有
1263
- 'win32': 'pywin32',
1264
- 'win32com': 'pywin32',
1316
+ "win32": "pywin32",
1317
+ "win32com": "pywin32",
1265
1318
  }
1266
-
1319
+
1267
1320
  # 找到所有二进制文件
1268
1321
  binary_files = []
1269
- if sys.platform == 'darwin':
1270
- binary_files = list(self.deps_dir.rglob('*.so')) + list(self.deps_dir.rglob('*.dylib'))
1271
- elif sys.platform == 'win32':
1272
- binary_files = list(self.deps_dir.rglob('*.pyd')) + list(self.deps_dir.rglob('*.dll'))
1273
- elif sys.platform.startswith('linux'):
1274
- binary_files = list(self.deps_dir.rglob('*.so'))
1275
-
1322
+ if sys.platform == "darwin":
1323
+ binary_files = list(self.deps_dir.rglob("*.so")) + list(self.deps_dir.rglob("*.dylib"))
1324
+ elif sys.platform == "win32":
1325
+ binary_files = list(self.deps_dir.rglob("*.pyd")) + list(self.deps_dir.rglob("*.dll"))
1326
+ elif sys.platform.startswith("linux"):
1327
+ binary_files = list(self.deps_dir.rglob("*.so"))
1328
+
1276
1329
  if not binary_files:
1277
1330
  return
1278
-
1331
+
1279
1332
  # 提取需要替换的包名
1280
1333
  packages_to_replace: Set[str] = set()
1281
1334
  for bin_file in binary_files:
1282
1335
  rel_path = bin_file.relative_to(self.deps_dir)
1283
1336
  parts = rel_path.parts
1284
-
1337
+
1285
1338
  # 忽略 bin 目录下的 dll (通常是 runtime)
1286
- if 'bin' in parts:
1339
+ if "bin" in parts:
1287
1340
  continue
1288
-
1341
+
1289
1342
  if len(parts) > 1:
1290
1343
  detected_name = parts[0]
1291
1344
  else:
1292
- detected_name = bin_file.name.split('.')[0]
1293
-
1345
+ detected_name = bin_file.name.split(".")[0]
1346
+
1294
1347
  # 跳过特定文件夹
1295
- if detected_name in ('__pycache__', 'bin', 'include', 'lib', 'Scripts'):
1348
+ if detected_name in ("__pycache__", "bin", "include", "lib", "Scripts"):
1296
1349
  continue
1297
-
1350
+
1298
1351
  if detected_name in MODULE_TO_PACKAGE:
1299
1352
  pkg_name = MODULE_TO_PACKAGE[detected_name]
1300
1353
  if pkg_name:
1301
1354
  packages_to_replace.add(pkg_name)
1302
1355
  else:
1303
1356
  packages_to_replace.add(detected_name)
1304
-
1357
+
1305
1358
  if not packages_to_replace:
1306
1359
  return
1307
-
1308
- click.echo(f"\r 检测到 {len(binary_files)} 个二进制文件 ({sys.platform}), 替换 {len(packages_to_replace)} 个包为 Linux 版本")
1309
-
1360
+
1361
+ click.echo(
1362
+ f"\r 检测到 {len(binary_files)} 个二进制文件 ({sys.platform}), "
1363
+ f"替换 {len(packages_to_replace)} 个包为 Linux 版本"
1364
+ )
1365
+
1310
1366
  # 下载 Linux wheels
1311
1367
  wheels_dir = self.build_dir / "linux_wheels"
1312
1368
  if wheels_dir.exists():
1313
1369
  shutil.rmtree(wheels_dir)
1314
1370
  wheels_dir.mkdir(parents=True)
1315
-
1371
+
1316
1372
  replaced_count = 0
1317
1373
  total_packages = len(packages_to_replace)
1318
1374
  for index, pkg_name in enumerate(sorted(packages_to_replace), start=1):
1319
1375
  # 提取确切的版本号以避免不兼容问题 (如 pydantic-core)
1320
1376
  target_version = ""
1321
- search_name = pkg_name.replace('-', '_').lower()
1377
+ search_name = pkg_name.replace("-", "_").lower()
1322
1378
  for info_dir in self.deps_dir.glob(f"{search_name}-*.dist-info"):
1323
- version_str = info_dir.name[len(search_name)+1:-10] # remove search_name + '-' and '.dist-info'
1379
+ version_str = info_dir.name[
1380
+ len(search_name) + 1 : -10
1381
+ ] # remove search_name + '-' and '.dist-info'
1324
1382
  target_version = f"=={version_str}"
1325
1383
  break
1326
-
1384
+
1327
1385
  pkg_with_version = f"{pkg_name}{target_version}"
1328
1386
  self._emit_install_progress(
1329
1387
  88 + min(6, round((index / max(total_packages, 1)) * 6)),
1330
1388
  "替换 Linux wheels",
1331
1389
  f"{index}/{total_packages} {pkg_with_version}",
1332
1390
  )
1333
-
1391
+
1334
1392
  try:
1335
1393
  downloaded = False
1336
1394
  for index_url in self._pip_index_candidates():
1337
1395
  download_cmd = [
1338
- sys.executable, "-m", "pip", "download",
1396
+ sys.executable,
1397
+ "-m",
1398
+ "pip",
1399
+ "download",
1339
1400
  pkg_with_version,
1340
- "-d", str(wheels_dir),
1341
- "--platform", "manylinux2014_x86_64",
1342
- "--platform", "manylinux_2_17_x86_64",
1343
- "--platform", "manylinux_2_28_x86_64",
1344
- "--platform", "musllinux_1_2_x86_64",
1345
- "--platform", "linux_x86_64",
1346
- "--python-version", self.TARGET_PYTHON_VERSION,
1401
+ "-d",
1402
+ str(wheels_dir),
1403
+ "--platform",
1404
+ "manylinux2014_x86_64",
1405
+ "--platform",
1406
+ "manylinux_2_17_x86_64",
1407
+ "--platform",
1408
+ "manylinux_2_28_x86_64",
1409
+ "--platform",
1410
+ "musllinux_1_2_x86_64",
1411
+ "--platform",
1412
+ "linux_x86_64",
1413
+ "--python-version",
1414
+ self.TARGET_PYTHON_VERSION,
1347
1415
  "--only-binary=:all:",
1348
- "--implementation", "cp",
1416
+ "--implementation",
1417
+ "cp",
1349
1418
  "--no-deps",
1350
1419
  "--quiet",
1351
1420
  "--disable-pip-version-check",
1352
- "--retries", "2",
1353
- "--timeout", "30",
1354
- "--cache-dir", str(self.pip_cache_dir),
1421
+ "--retries",
1422
+ "2",
1423
+ "--timeout",
1424
+ "30",
1425
+ "--cache-dir",
1426
+ str(self.pip_cache_dir),
1355
1427
  ]
1356
1428
  if index_url:
1357
1429
  download_cmd += ["-i", index_url]
1358
- result = subprocess.run(download_cmd, capture_output=True, text=True, timeout=90)
1430
+ result = subprocess.run(
1431
+ download_cmd, capture_output=True, text=True, timeout=90
1432
+ )
1359
1433
  if result.returncode == 0:
1360
1434
  downloaded = True
1361
1435
  break
1362
1436
  if downloaded:
1363
1437
  replaced_count += 1
1364
1438
  else:
1365
- failed_msg = result.stderr.strip().split('\n')[-1] if result and result.stderr else 'unknown'
1366
- click.secho(f" ⚠ 替换失败: {pkg_with_version} ({failed_msg})", fg='yellow')
1439
+ failed_msg = (
1440
+ result.stderr.strip().split("\n")[-1]
1441
+ if result and result.stderr
1442
+ else "unknown"
1443
+ )
1444
+ click.secho(f" ⚠ 替换失败: {pkg_with_version} ({failed_msg})", fg="yellow")
1367
1445
  except Exception as e:
1368
- click.secho(f" ⚠ 替换异常: {pkg_name} ({e})", fg='yellow')
1369
-
1446
+ click.secho(f" ⚠ 替换异常: {pkg_name} ({e})", fg="yellow")
1447
+
1370
1448
  # 解压 wheel 覆盖到 deps_dir
1371
1449
  for wheel_file in wheels_dir.glob("*.whl"):
1372
1450
  try:
1373
- wheel_name = wheel_file.name.split('-')[0].lower().replace('_', '-')
1374
-
1451
+ wheel_name = wheel_file.name.split("-")[0].lower().replace("_", "-")
1452
+
1375
1453
  # 删除旧的包目录
1376
1454
  for old_dir in self.deps_dir.iterdir():
1377
- if old_dir.is_dir() and old_dir.name.lower().replace('_', '-') == wheel_name:
1455
+ if old_dir.is_dir() and old_dir.name.lower().replace("_", "-") == wheel_name:
1378
1456
  shutil.rmtree(old_dir)
1379
- # 不要 break,可能由多个目录 (e.g. pydantic_core, pydantic_core-2.x.dist-info)
1380
-
1457
+ # 不要 break:一个包可能有多个目录,例如 dist-info。
1458
+
1381
1459
  # 删除根目录下的二进制文件
1382
- for ext in ('*.so', '*.dylib', '*.pyd', '*.dll'):
1460
+ for ext in ("*.so", "*.dylib", "*.pyd", "*.dll"):
1383
1461
  for bin_file in self.deps_dir.glob(f"{wheel_name}*{ext[1:]}"):
1384
1462
  try:
1385
1463
  bin_file.unlink()
1386
- except:
1464
+ except OSError:
1387
1465
  pass
1388
1466
  for bin_file in self.deps_dir.glob(f"{wheel_name.replace('-', '_')}*{ext[1:]}"):
1389
1467
  try:
1390
1468
  bin_file.unlink()
1391
- except:
1469
+ except OSError:
1392
1470
  pass
1393
-
1471
+
1394
1472
  # 解压新的 wheel
1395
- with zipfile.ZipFile(wheel_file, 'r') as zf:
1473
+ with zipfile.ZipFile(wheel_file, "r") as zf:
1396
1474
  zf.extractall(self.deps_dir)
1397
1475
  except Exception:
1398
1476
  pass
1399
-
1477
+
1400
1478
  shutil.rmtree(wheels_dir, ignore_errors=True)
1401
-
1479
+
1402
1480
  # 清理所有残留的非 Linux 平台二进制文件
1403
1481
  # (wheel 解压后可能有旧的 darwin/win .so 文件未被覆盖)
1404
1482
  cleaned_count = 0
1405
- for so_file in list(self.deps_dir.rglob('*.so')):
1483
+ for so_file in list(self.deps_dir.rglob("*.so")):
1406
1484
  name = so_file.name.lower()
1407
- if 'darwin' in name or 'win' in name:
1485
+ if "darwin" in name or "win" in name:
1408
1486
  try:
1409
1487
  so_file.unlink()
1410
1488
  cleaned_count += 1
1411
1489
  except Exception:
1412
1490
  pass
1413
- for dylib_file in list(self.deps_dir.rglob('*.dylib')):
1491
+ for dylib_file in list(self.deps_dir.rglob("*.dylib")):
1414
1492
  try:
1415
1493
  dylib_file.unlink()
1416
1494
  cleaned_count += 1
@@ -1418,7 +1496,7 @@ class CodeBuilder(BaseBuilder):
1418
1496
  pass
1419
1497
  if cleaned_count > 0:
1420
1498
  click.echo(f" ✓ 清理 {cleaned_count} 个残留平台二进制文件")
1421
-
1499
+
1422
1500
  click.echo(f" ✓ 成功替换 {replaced_count}/{len(packages_to_replace)} 个包")
1423
1501
 
1424
1502
  def _is_linux_so(self, name: str) -> bool:
@@ -1463,7 +1541,7 @@ class CodeBuilder(BaseBuilder):
1463
1541
 
1464
1542
  def _detect_critical_binary_issues(self, names: List[str]) -> List[str]:
1465
1543
  issues: List[str] = []
1466
-
1544
+
1467
1545
  # 定义需要检查的关键原生扩展模块
1468
1546
  # 格式: (描述, 正则模式)
1469
1547
  critical_modules = [
@@ -1474,7 +1552,7 @@ class CodeBuilder(BaseBuilder):
1474
1552
  # tiktoken/_tiktoken: langchain-openai 核心依赖 (Rust 编译)
1475
1553
  ("tiktoken/_tiktoken", r"tiktoken/_tiktoken.*\.(so|pyd)$"),
1476
1554
  ]
1477
-
1555
+
1478
1556
  for module_name, pattern in critical_modules:
1479
1557
  matched_bins = [n for n in names if re.search(pattern, n)]
1480
1558
  if matched_bins:
@@ -1487,19 +1565,21 @@ class CodeBuilder(BaseBuilder):
1487
1565
  f"python-abi-mismatch:{module_name}:"
1488
1566
  f"expected-cpython-{self.TARGET_PYTHON_VERSION}-or-abi3"
1489
1567
  )
1490
-
1568
+
1491
1569
  # 通用检查: 所有 .so 文件中不应包含 darwin/win 平台标识
1492
- all_so_files = [n for n in names if n.endswith('.so')]
1493
- darwin_so_count = sum(1 for n in all_so_files if 'darwin' in n.lower())
1570
+ all_so_files = [n for n in names if n.endswith(".so")]
1571
+ darwin_so_count = sum(1 for n in all_so_files if "darwin" in n.lower())
1494
1572
  if darwin_so_count > 0:
1495
1573
  issues.append(f"warning:found-{darwin_so_count}-darwin-so-files")
1496
-
1574
+
1497
1575
  return issues
1498
-
1576
+
1499
1577
  def _package_zip(self, zip_path: Path, detection_result) -> None:
1500
1578
  """打包 zip 文件"""
1501
1579
  project_files = list(self._iter_project_files())
1502
- dependency_files = [file_path for file_path in self.deps_dir.rglob("*") if file_path.is_file()]
1580
+ dependency_files = [
1581
+ file_path for file_path in self.deps_dir.rglob("*") if file_path.is_file()
1582
+ ]
1503
1583
  bundled_source_files = list(self._iter_bundled_source_files())
1504
1584
  dependency_size = sum(file_path.stat().st_size for file_path in dependency_files)
1505
1585
  if dependency_files:
@@ -1509,12 +1589,12 @@ class CodeBuilder(BaseBuilder):
1509
1589
  f"{len(bundled_source_files)} 个 runtime 文件"
1510
1590
  )
1511
1591
 
1512
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
1592
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
1513
1593
  # 添加项目文件
1514
1594
  for file_path in project_files:
1515
1595
  arcname = file_path.relative_to(self.project_dir).as_posix()
1516
1596
  zf.write(file_path, arcname)
1517
-
1597
+
1518
1598
  # 添加依赖
1519
1599
  deps_count = 0
1520
1600
  for file_path in dependency_files:
@@ -1523,7 +1603,7 @@ class CodeBuilder(BaseBuilder):
1523
1603
  deps_count += 1
1524
1604
  self._emit_package_progress("打包依赖", deps_count, len(dependency_files))
1525
1605
  self._finish_package_progress()
1526
-
1606
+
1527
1607
  # 添加随运行时下发的 ksadk 源码
1528
1608
  bundled_source_count = 0
1529
1609
  for package_name, relative, file_path in bundled_source_files:
@@ -1536,13 +1616,13 @@ class CodeBuilder(BaseBuilder):
1536
1616
  len(bundled_source_files),
1537
1617
  )
1538
1618
  self._finish_package_progress()
1539
-
1619
+
1540
1620
  click.echo(f" ✓ 打包运行时源码: {bundled_source_count} 个文件")
1541
-
1621
+
1542
1622
  # 添加 entrypoint
1543
1623
  entrypoint_content = self._generate_entrypoint(detection_result)
1544
1624
  zf.writestr("entrypoint.py", entrypoint_content)
1545
-
1625
+
1546
1626
  click.echo(f" ✓ 打包完成: {len(project_files)} 个项目文件 + {deps_count} 个依赖文件")
1547
1627
  self._emit_package_size_report(zip_path)
1548
1628
 
@@ -1586,10 +1666,7 @@ class CodeBuilder(BaseBuilder):
1586
1666
  )
1587
1667
  if by_top_level:
1588
1668
  top_items = sorted(by_top_level.items(), key=lambda item: item[1], reverse=True)[:limit]
1589
- summary = ", ".join(
1590
- f"{name} {size / (1024 * 1024):.1f} MB"
1591
- for name, size in top_items
1592
- )
1669
+ summary = ", ".join(f"{name} {size / (1024 * 1024):.1f} MB" for name, size in top_items)
1593
1670
  click.echo(f" 体积 Top{len(top_items)}: {summary}")
1594
1671
  if (
1595
1672
  raw_total > self.CONTAINER_SUGGESTION_RAW_THRESHOLD_BYTES
@@ -1660,7 +1737,7 @@ class CodeBuilder(BaseBuilder):
1660
1737
  self._package_progress_last_line = ""
1661
1738
  self._package_progress_last_percent_by_label = {}
1662
1739
  self._package_progress_logged_milestone_by_label = {}
1663
-
1740
+
1664
1741
  def _generate_entrypoint(self, detection_result) -> str:
1665
1742
  """生成 entrypoint.py"""
1666
1743
  package_name = Path(detection_result.package_path).name
@@ -1737,15 +1814,11 @@ logger.info(f"PYTHONPATH: {{os.environ.get('PYTHONPATH', 'N/A')}}")
1737
1814
  # 打印关键环境变量 (隐藏敏感信息)
1738
1815
  env_keys = [
1739
1816
  "AGENT_RUNTIME_NAME", "AGENT_RUNTIME_ID", "ACCOUNT_ID", "PORT",
1740
- "LANGFUSE_BASE_URL", "LANGFUSE_HOST", "LANGFUSE_PUBLIC_KEY",
1741
- "LANGFUSE_SECRET_KEY", "LANGFUSE_USE_CALLBACK",
1742
1817
  "OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
1743
1818
  "OTEL_EXPORTER_OTLP_HEADERS", "OTEL_EXPORTER_OTLP_TRACES_HEADERS",
1744
- "OTEL_SERVICE_NAME", "CLOUD_MONITOR_APP_KEY",
1745
- "CLOUD_MONITOR_OTLP_ENABLED", "CLOUD_MONITOR_OTLP_ENDPOINT",
1746
- "CLOUD_MONITOR_OTLP_TRACES_ENDPOINT", "CLOUD_MONITOR_OTLP_HEADERS",
1747
- "CLOUD_MONITOR_LANGFUSE_HOST", "CLOUD_MONITOR_LANGFUSE_PUBLIC_KEY",
1748
- "CLOUD_MONITOR_LANGFUSE_SECRET_KEY", "CLOUD_MONITOR_LANGFUSE_ENABLED",
1819
+ "OTEL_SERVICE_NAME",
1820
+ "CLOUD_MONITOR_OTLP_ENDPOINT", "CLOUD_MONITOR_OTLP_TRACES_ENDPOINT",
1821
+ "CLOUD_MONITOR_OTLP_HEADERS", "CLOUD_MONITOR_OTLP_TRACES_HEADERS",
1749
1822
  "LANGCHAIN_TRACING_V2", "MODEL_NAME",
1750
1823
  ]
1751
1824
  for key in env_keys:
@@ -1762,15 +1835,9 @@ logger.info("=" * 60)
1762
1835
  from ksadk.configs import setup_environment
1763
1836
  setup_environment(Path(CODE_ROOT))
1764
1837
 
1765
- try:
1766
- from ksadk.runners.patch_langchain import apply_patch as apply_langchain_patch
1767
- apply_langchain_patch()
1768
- except ImportError:
1769
- pass
1770
-
1771
- from ksadk.runners import create_runner
1772
1838
  from ksadk.detection import DetectionResult, FrameworkType
1773
- from ksadk.server import app, set_runner
1839
+ from ksadk.runtime import RuntimeExecutor, RuntimeLaunchContext, build_default_runtime_registry
1840
+ from ksadk.server import RuntimeAppConfig, configure_runtime_app, create_runtime_app
1774
1841
  import uvicorn
1775
1842
 
1776
1843
  # 检测结果 (构建时固化)
@@ -1780,46 +1847,84 @@ detection_result = DetectionResult(
1780
1847
  entry_point="{detection_result.entry_point}",
1781
1848
  package_path=os.path.join(CODE_ROOT, "{package_name}"),
1782
1849
  agent_variable="{detection_result.agent_variable}",
1783
- runner_class="{getattr(detection_result, 'runner_class', '')}"
1850
+ runner_class="{getattr(detection_result, "runner_class", "")}"
1784
1851
  )
1785
1852
 
1786
1853
  logger.info(f"框架: {{detection_result.name}}")
1787
1854
  logger.info(f"入口: {{detection_result.entry_point}}")
1788
1855
 
1789
- # 初始化 Tracing (如果配置了 Langfuse、标准 OTLP HTTP 或 CloudMonitor OTLP)
1790
- has_otlp = bool(os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT") or os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"))
1856
+ # 初始化 Tracing (如果配置了标准 OTLP HTTP 或 CloudMonitor OTLP)
1857
+ has_otlp = bool(
1858
+ os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
1859
+ or os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
1860
+ )
1791
1861
  has_cloud_monitor_otlp = bool(
1792
- os.environ.get("CLOUD_MONITOR_APP_KEY")
1862
+ os.environ.get("CLOUD_MONITOR_OTLP_TRACES_HEADERS")
1863
+ or os.environ.get("CLOUD_MONITOR_OTLP_HEADERS")
1793
1864
  or os.environ.get("CLOUD_MONITOR_OTLP_ENDPOINT")
1794
1865
  or os.environ.get("CLOUD_MONITOR_OTLP_TRACES_ENDPOINT")
1866
+ or os.environ.get("CLOUD_MONITOR_APP_KEY")
1795
1867
  )
1796
- has_cloud_monitor_langfuse = bool(
1797
- os.environ.get("CLOUD_MONITOR_LANGFUSE_PUBLIC_KEY")
1798
- or os.environ.get("CLOUD_MONITOR_LANGFUSE_SECRET_KEY")
1799
- or os.environ.get("CLOUD_MONITOR_LANGFUSE_HOST")
1800
- )
1801
- if os.environ.get("LANGFUSE_PUBLIC_KEY") or has_otlp or has_cloud_monitor_otlp or has_cloud_monitor_langfuse:
1868
+ if has_otlp or has_cloud_monitor_otlp:
1802
1869
  try:
1803
1870
  from ksadk.tracing import setup_tracing
1804
-
1805
- use_callback_only = os.environ.get("LANGFUSE_USE_CALLBACK", "").strip().lower() in ("1", "true", "yes", "on")
1806
1871
 
1807
- setup_tracing(use_callback_only=use_callback_only)
1872
+ setup_tracing()
1808
1873
  logger.info(
1809
1874
  f"Tracing 已启用 (OTLP={{has_otlp}}, "
1810
- f"CloudMonitorOTLP={{has_cloud_monitor_otlp}}, "
1811
- f"CloudMonitorLangfuse={{has_cloud_monitor_langfuse}}, "
1812
- f"CallbackOnly={{use_callback_only}})"
1875
+ f"CloudMonitorOTLP={{has_cloud_monitor_otlp}})"
1813
1876
  )
1814
1877
  except Exception as e:
1815
1878
  logger.warning(f"Tracing 初始化失败: {{e}}")
1816
1879
 
1817
- # 创建 Runner 并加载 Agent
1818
- logger.info("正在加载 Agent...")
1819
- runner = create_runner(detection_result, CODE_ROOT)
1820
- runner.load_agent()
1821
- set_runner(runner)
1822
- logger.info("Agent 加载成功!")
1880
+ # 只装配统一 RuntimeAdapter 执行链;具体 Adapter 在请求开始时由 Registry 创建。
1881
+ runtime_context = RuntimeLaunchContext(
1882
+ runtime_type=detection_result.type.value,
1883
+ project_dir=Path(CODE_ROOT),
1884
+ detection=detection_result,
1885
+ config=dict(getattr(detection_result, "raw_config", None) or {{}}),
1886
+ )
1887
+ # managed A2A:KSADK_A2A_RUNTIME_ID 非空时挂 discovery card + 完整数据面 route。
1888
+ _a2a_config = None
1889
+ _a2a_adapter = None
1890
+ if os.environ.get("KSADK_A2A_RUNTIME_ID", "").strip():
1891
+ from ksadk.managed_a2a_card import build_managed_a2a_card_if_configured
1892
+
1893
+ _managed_a2a_card = build_managed_a2a_card_if_configured()
1894
+ try:
1895
+ from ksadk.a2a.routes import A2AConfig
1896
+ from ksadk.runtime.factory import create_runtime_adapter
1897
+
1898
+ _a2a_adapter = create_runtime_adapter(runtime_context)
1899
+ _base = (
1900
+ os.environ.get("KSADK_A2A_INTERNAL_BASE_URL", "").strip()
1901
+ or "http://localhost:8080"
1902
+ )
1903
+ _a2a_config = A2AConfig(
1904
+ enabled=True,
1905
+ base_url=_base,
1906
+ agent_name=(
1907
+ os.environ.get("KSADK_A2A_AGENT_NAME", "").strip()
1908
+ or os.environ.get("KSADK_A2A_RUNTIME_ID", "").strip()
1909
+ ),
1910
+ streaming=True,
1911
+ task_store_dsn="sqlite+aiosqlite:///.agentengine/a2a_tasks.db",
1912
+ )
1913
+ except Exception as _e:
1914
+ logger.warning(f"managed A2A 数据面装配失败,回退 discovery-only: {{_e}}")
1915
+ _a2a_config = None
1916
+ _a2a_adapter = None
1917
+ app = create_runtime_app(
1918
+ RuntimeAppConfig(
1919
+ runtime_type=detection_result.type.value,
1920
+ runtime_executor=RuntimeExecutor(build_default_runtime_registry()),
1921
+ launch_context=runtime_context,
1922
+ a2a=_a2a_config or _managed_a2a_card,
1923
+ a2a_runtime_adapter=_a2a_adapter,
1924
+ ),
1925
+ configure_runtime_app,
1926
+ )
1927
+ logger.info("RuntimeAdapter 执行链装配成功!")
1823
1928
 
1824
1929
  if __name__ == "__main__":
1825
1930
  port = int(os.environ.get("PORT", 8080))
@@ -1848,13 +1953,13 @@ if __name__ == "__main__":
1848
1953
  self._pip_index_candidates_cache = candidates
1849
1954
  return list(candidates)
1850
1955
 
1851
- seen: Set[str] = set()
1956
+ fallback_seen: Set[str] = set()
1852
1957
  urls: list[str] = []
1853
1958
  for candidate in self.PIP_INDEX_FALLBACKS:
1854
1959
  normalized = self._normalize_pip_index_url(candidate)
1855
- if normalized in seen:
1960
+ if normalized in fallback_seen:
1856
1961
  continue
1857
- seen.add(normalized)
1962
+ fallback_seen.add(normalized)
1858
1963
  urls.append(normalized)
1859
1964
 
1860
1965
  ordered, summary = self._rank_pip_index_urls(urls)