agentengine-sdk-python 0.6.9__py3-none-any.whl → 0.8.0__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 (384) hide show
  1. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +41 -23
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +252 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +533 -726
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +173 -90
  54. ksadk/cli/cmd_invoke.py +388 -159
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +103 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +488 -228
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +44 -14
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +38 -16
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +501 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +656 -0
  97. ksadk/conversations/model_context.py +22 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5187
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +52 -39
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +326 -209
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +267 -0
  156. ksadk/memory/adk/short_term_memory.py +33 -27
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +1248 -164
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -530
  183. ksadk/runners/langgraph_runner.py +490 -95
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +264 -38
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3743
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-C0meuOdi.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-bj2rZnP5.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-2u1ltDIj.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-_N8bJ-EU.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-BMKM66B4.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-D99wf6_X.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-DLMjL01k.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-B67uF2uK.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-S4klkK5W.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-DtTmUan-.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-BR5s6Pws.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-DYPc5cvh.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPnuCB8c.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-mjB_yYkW.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-CwSec0Wa.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-agCVobiF.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +86 -40
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +28 -5
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +216 -35
  313. ksadk/sessions/resilience.py +52 -0
  314. ksadk/sessions/resilient.py +388 -0
  315. ksadk/skills/models.py +17 -5
  316. ksadk/skills/package_store.py +5 -2
  317. ksadk/skills/runtime/agent.py +20 -5
  318. ksadk/skills/runtime/artifacts.py +1 -5
  319. ksadk/skills/runtime/backends/e2b.py +10 -4
  320. ksadk/skills/runtime/backends/local.py +15 -3
  321. ksadk/skills/runtime/base.py +1 -2
  322. ksadk/skills/runtime/executor.py +10 -3
  323. ksadk/skills/runtime/factory.py +4 -2
  324. ksadk/skills/runtime/loader.py +1 -2
  325. ksadk/skills/runtime/registry.py +15 -10
  326. ksadk/skills/runtime/request.py +3 -1
  327. ksadk/skills/service_client.py +38 -17
  328. ksadk/skills/tool_defs.py +15 -10
  329. ksadk/terminal_exec_policy.py +3 -4
  330. ksadk/tools/gateway.py +92 -15
  331. ksadk/tools/result_budget.py +12 -4
  332. ksadk/toolsets/__init__.py +136 -36
  333. ksadk/toolsets/platform.py +21 -5
  334. ksadk/toolsets/sandbox.py +82 -16
  335. ksadk/toolsets/skills.py +200 -39
  336. ksadk/toolsets/web.py +123 -26
  337. ksadk/toolsets/workspace.py +264 -114
  338. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  339. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  340. ksadk/tracing/setup.py +132 -53
  341. ksadk/tracing/span_utils.py +5 -2
  342. ksadk/tui/__init__.py +22 -7
  343. ksadk/tui/loop.py +1925 -0
  344. ksadk/tui/stream_render.py +69 -0
  345. ksadk/ui_config.py +5 -3
  346. ksadk/version.py +1 -1
  347. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  348. agentengine_sdk_python-0.6.9.dist-info/RECORD +0 -487
  349. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +0 -9
  350. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +0 -8
  351. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +0 -303
  352. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +0 -1
  353. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
  354. ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
  355. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
  356. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
  357. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
  358. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
  359. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
  360. ksadk/server/static/assets/index-BH7-pC0B.js +0 -165
  361. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  362. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
  363. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
  364. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
  365. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
  366. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
  367. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
  368. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
  369. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
  370. ksadk/tui/app.py +0 -400
  371. ksadk/tui/clipboard.py +0 -100
  372. ksadk/tui/widgets/__init__.py +0 -22
  373. ksadk/tui/widgets/assistant.py +0 -89
  374. ksadk/tui/widgets/base.py +0 -54
  375. ksadk/tui/widgets/chat_input.py +0 -246
  376. ksadk/tui/widgets/history.py +0 -156
  377. ksadk/tui/widgets/system.py +0 -87
  378. ksadk/tui/widgets/thinking.py +0 -110
  379. ksadk/tui/widgets/tool_call.py +0 -161
  380. ksadk/tui/widgets/user.py +0 -37
  381. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  382. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  383. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  384. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.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,12 @@ 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
+ code_requirements_for_framework,
33
34
  )
34
35
  from ksadk.builders.requirements_utils import (
35
36
  exclude_requirement_names,
@@ -85,28 +86,28 @@ class CodeBuilder(BaseBuilder):
85
86
  }
86
87
  IGNORED_FILE_NAMES = {".DS_Store"}
87
88
  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',
89
+ ".py",
90
+ ".yaml",
91
+ ".yml",
92
+ ".json",
93
+ ".jinja2",
94
+ ".j2",
95
+ ".txt",
96
+ ".md",
97
+ ".html",
98
+ ".js",
99
+ ".css",
100
+ ".svg",
101
+ ".ico",
102
+ ".png",
103
+ ".jpg",
104
+ ".jpeg",
105
+ ".gif",
106
+ ".webp",
107
+ ".woff",
108
+ ".woff2",
109
+ ".ttf",
110
+ ".map",
110
111
  }
111
112
  BUNDLED_KSADK_CORE_RUNTIME_REQUIREMENTS = (
112
113
  "a2a-sdk>=0.3.22",
@@ -124,15 +125,16 @@ class CodeBuilder(BaseBuilder):
124
125
  "mcp>=1.1.0",
125
126
  "langchain-mcp-adapters>=0.0.1",
126
127
  )
127
- BUNDLED_KSADK_POSTGRES_SESSION_REQUIREMENTS = (
128
- "asyncpg>=0.30.0,<1.0.0",
129
- )
128
+ BUNDLED_KSADK_POSTGRES_SESSION_REQUIREMENTS = ("asyncpg>=0.30.0,<1.0.0",)
130
129
  BUNDLED_KSADK_ATTACHMENT_RUNTIME_REQUIREMENTS = (
131
130
  "pypdf>=6.0.0",
132
131
  "beautifulsoup4>=4.12.0",
133
132
  )
134
- BUNDLED_KSADK_ATTACHMENT_OCR_RUNTIME_REQUIREMENTS = (
135
- "rapidocr-onnxruntime>=1.2.0",
133
+ BUNDLED_KSADK_ATTACHMENT_OCR_RUNTIME_REQUIREMENTS = ("rapidocr-onnxruntime>=1.2.0",)
134
+ BUNDLED_AGUI_RUNTIME_REQUIREMENTS = (
135
+ "ag-ui-protocol==0.1.19",
136
+ "ag-ui-langgraph[fastapi]==0.0.42",
137
+ "copilotkit==0.1.94",
136
138
  )
137
139
  BUNDLED_KSADK_RUNTIME_REQUIREMENTS = (
138
140
  *BUNDLED_KSADK_CORE_RUNTIME_REQUIREMENTS,
@@ -149,8 +151,8 @@ class CodeBuilder(BaseBuilder):
149
151
  PIP_INSTALL_TIMEOUT_SECONDS = 45 * 60
150
152
  CONTAINER_SUGGESTION_RAW_THRESHOLD_BYTES = 500 * 1024 * 1024
151
153
  CONTAINER_SUGGESTION_ZIP_THRESHOLD_BYTES = 300 * 1024 * 1024
152
-
153
- def __init__(self, project_dir: Path, config: dict = None):
154
+
155
+ def __init__(self, project_dir: Path, config: Optional[dict] = None):
154
156
  super().__init__(project_dir, config)
155
157
  self.build_dir = self.project_dir / ".agentengine" / "code_build"
156
158
  self.deps_dir = self.build_dir / "linux_deps"
@@ -168,33 +170,38 @@ class CodeBuilder(BaseBuilder):
168
170
  self._package_progress_logged_milestone_by_label: dict[str, int] = {}
169
171
  self._pip_index_candidates_cache: Optional[list[Optional[str]]] = None
170
172
  self._pip_index_selection_summary = ""
171
-
173
+
172
174
  def build(self) -> BuildResult:
173
175
  """执行 Code 模式构建"""
174
176
  from ksadk.detection import FrameworkDetector
175
-
177
+
176
178
  self._load_dotenv()
177
179
  config = self._load_config()
178
-
180
+
179
181
  # 检测框架
180
182
  detector = FrameworkDetector(str(self.project_dir))
181
183
  detection_result = detector.detect()
182
-
184
+
183
185
  if detection_result.type.value == "unknown":
186
+ return BuildResult(success=False, error_message="未检测到支持的框架")
187
+ if detection_result.type.value == "codex":
184
188
  return BuildResult(
185
189
  success=False,
186
- error_message="未检测到支持的框架"
190
+ error_message=(
191
+ "Codex 是 ManagedRuntime,不能构建宿主机 Code zip;"
192
+ "请使用 `ksadk build .` 的 managed 模式,或显式构建 Container"
193
+ ),
187
194
  )
188
-
195
+
189
196
  click.echo(f"📦 框架: {click.style(detection_result.type.value, fg='green')}")
190
197
  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
-
198
+
199
+ agent_name = config.get("name", self.project_dir.name).replace("-", "_").replace(".", "_")
200
+
194
201
  # 创建构建目录
195
202
  self.build_dir.mkdir(parents=True, exist_ok=True)
196
203
  zip_path = self.build_dir / f"{agent_name}.zip"
197
-
204
+
198
205
  # 检查是否需要重新构建
199
206
  no_cache = self.config.get("no_cache", False) if self.config else False
200
207
  repackage = self.config.get("repackage", False) if self.config else False
@@ -207,31 +214,33 @@ class CodeBuilder(BaseBuilder):
207
214
  if zip_path.exists() and not rebuild_needed:
208
215
  incompatibles = self._scan_incompatible_binaries_in_zip(zip_path)
209
216
  if incompatibles:
210
- click.secho("\n⚠️ 检测到缓存构建包含非 Linux 兼容关键二进制,自动重建...", fg='yellow')
217
+ click.secho(
218
+ "\n⚠️ 检测到缓存构建包含非 Linux 兼容关键二进制,自动重建...", fg="yellow"
219
+ )
211
220
  for item in incompatibles[:5]:
212
221
  click.echo(f" - {item}")
213
222
  rebuild_reason = "缓存 zip 存在 Linux 兼容性问题"
214
223
  else:
215
224
  self._save_input_fingerprint(zip_path, detection_result)
216
225
  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)")
226
+ click.secho(f"\n✅ 使用已有构建: {zip_path.name} ({zip_size:.2f} MB)", fg="green")
227
+ click.echo(
228
+ " (如需只重新打包当前代码/runtime,请使用 --repackage;"
229
+ "如需重装依赖,请使用 --no-cache)"
230
+ )
219
231
  return BuildResult(
220
232
  success=True,
221
233
  artifact_path=zip_path,
222
234
  artifact_size=zip_path.stat().st_size,
223
- metadata={
224
- "agent_name": agent_name,
225
- "framework": detection_result.type.value
226
- }
235
+ metadata={"agent_name": agent_name, "framework": detection_result.type.value},
227
236
  )
228
237
  elif rebuild_reason:
229
238
  click.echo(f" 重新打包原因: {rebuild_reason}")
230
-
239
+
231
240
  # Step 1: 准备依赖
232
241
  click.echo("\n📋 Step 1/3: 准备依赖清单...")
233
242
  requirements_path = self._prepare_requirements(detection_result)
234
-
243
+
235
244
  # Step 2: 安装依赖
236
245
  click.echo("\n📦 Step 2/3: 安装依赖...")
237
246
  reuse_dependencies, reuse_reason = self._can_reuse_dependency_cache(
@@ -241,7 +250,8 @@ class CodeBuilder(BaseBuilder):
241
250
  if reuse_dependencies:
242
251
  stats = self._current_dependency_stats()
243
252
  click.secho(
244
- f" ✓ 复用 Linux 依赖缓存: {stats['file_count']} 个文件, {stats['size_mb']:.1f} MB",
253
+ f" ✓ 复用 Linux 依赖缓存: {stats['file_count']} 个文件, "
254
+ f"{stats['size_mb']:.1f} MB",
245
255
  fg="green",
246
256
  )
247
257
  click.echo(f" {reuse_reason}")
@@ -250,25 +260,22 @@ class CodeBuilder(BaseBuilder):
250
260
  click.echo(f" {reuse_reason}")
251
261
  self._clear_dependency_cache()
252
262
  self.deps_dir.mkdir(parents=True, exist_ok=True)
253
-
263
+
254
264
  if not self._install_dependencies(requirements_path):
255
- return BuildResult(
256
- success=False,
257
- error_message="依赖安装失败"
258
- )
265
+ return BuildResult(success=False, error_message="依赖安装失败")
259
266
  self._save_dependency_fingerprint(requirements_path)
260
-
267
+
261
268
  # Step 3: 打包 zip
262
269
  click.echo("\n📦 Step 3/3: 打包 zip...")
263
270
  package_started_at = time.monotonic()
264
271
  self._package_zip(zip_path, detection_result)
265
272
  click.echo(f" ✓ 打包耗时: {self._format_elapsed(package_started_at)}")
266
273
  self._save_input_fingerprint(zip_path, detection_result)
267
-
274
+
268
275
  zip_size = zip_path.stat().st_size
269
276
  click.echo(f" zip 文件: {zip_path}")
270
277
  click.echo(f" 大小: {zip_size / (1024 * 1024):.2f} MB")
271
-
278
+
272
279
  return BuildResult(
273
280
  success=True,
274
281
  artifact_path=zip_path,
@@ -276,8 +283,8 @@ class CodeBuilder(BaseBuilder):
276
283
  metadata={
277
284
  "agent_name": agent_name,
278
285
  "framework": detection_result.type.value,
279
- "deps_dir": str(self.deps_dir)
280
- }
286
+ "deps_dir": str(self.deps_dir),
287
+ },
281
288
  )
282
289
 
283
290
  def _rebuild_decision(
@@ -321,26 +328,32 @@ class CodeBuilder(BaseBuilder):
321
328
  if changed:
322
329
  return "业务代码或项目文件变更"
323
330
  return "构建输入指纹变化"
324
-
331
+
325
332
  def _need_rebuild(self, zip_path: Path, detection_result) -> bool:
326
333
  """检查是否需要重新构建。优先使用输入内容指纹,缺失时回退到 mtime。"""
327
334
  manifest = self._load_input_fingerprint(zip_path)
328
335
  if manifest:
329
336
  current_fingerprint = self._build_input_fingerprint(detection_result)
330
- return manifest.get("fingerprint") != current_fingerprint["fingerprint"]
337
+ return bool(manifest.get("fingerprint") != current_fingerprint["fingerprint"])
331
338
  return self._need_rebuild_from_mtime(zip_path)
332
339
 
333
340
  def _need_rebuild_from_mtime(self, zip_path: Path) -> bool:
334
341
  """兼容旧缓存:当没有指纹文件时,回退到 mtime 判断。"""
335
342
  zip_mtime = zip_path.stat().st_mtime
336
-
343
+
337
344
  for item in self.project_dir.iterdir():
338
- if item.name.startswith('.') or item.name in ('__pycache__', 'node_modules', '.git', '.venv', 'venv'):
345
+ if item.name.startswith(".") or item.name in (
346
+ "__pycache__",
347
+ "node_modules",
348
+ ".git",
349
+ ".venv",
350
+ "venv",
351
+ ):
339
352
  continue
340
353
  if item.is_file() and item.stat().st_mtime > zip_mtime:
341
354
  return True
342
355
  if item.is_dir():
343
- for file_path in item.rglob('*.py'):
356
+ for file_path in item.rglob("*.py"):
344
357
  if file_path.stat().st_mtime > zip_mtime:
345
358
  return True
346
359
  return False
@@ -360,7 +373,7 @@ class CodeBuilder(BaseBuilder):
360
373
  data = json.load(f)
361
374
  if data.get("version") != self.INPUT_FINGERPRINT_VERSION:
362
375
  return None
363
- return data
376
+ return data if isinstance(data, dict) else None
364
377
  except Exception:
365
378
  return None
366
379
 
@@ -380,7 +393,7 @@ class CodeBuilder(BaseBuilder):
380
393
  data = json.load(f)
381
394
  if data.get("version") != self.DEPENDENCY_FINGERPRINT_VERSION:
382
395
  return None
383
- return data
396
+ return data if isinstance(data, dict) else None
384
397
  except Exception:
385
398
  return None
386
399
 
@@ -429,7 +442,12 @@ class CodeBuilder(BaseBuilder):
429
442
  return True
430
443
  if self._project_env_has_configured_value("KSADK_MCP_SERVERS"):
431
444
  return True
432
- if self._project_env_value("KSADK_ENABLE_MCP_TOOLS").strip().lower() in {"1", "true", "yes", "on"}:
445
+ if self._project_env_value("KSADK_ENABLE_MCP_TOOLS").strip().lower() in {
446
+ "1",
447
+ "true",
448
+ "yes",
449
+ "on",
450
+ }:
433
451
  return True
434
452
  return self._project_imports_any({"mcp", "langchain_mcp_adapters"})
435
453
 
@@ -515,13 +533,17 @@ class CodeBuilder(BaseBuilder):
515
533
  digest = hashlib.sha256()
516
534
  requirements_text = requirements_path.read_text(encoding="utf-8")
517
535
 
518
- digest.update(f"deps-fingerprint-version:{self.DEPENDENCY_FINGERPRINT_VERSION}\n".encode("utf-8"))
536
+ digest.update(
537
+ f"deps-fingerprint-version:{self.DEPENDENCY_FINGERPRINT_VERSION}\n".encode("utf-8")
538
+ )
519
539
  digest.update(f"builder-platform:{sys.platform}\n".encode("utf-8"))
520
540
  digest.update(
521
541
  f"builder-python:{sys.version_info.major}.{sys.version_info.minor}\n".encode("utf-8")
522
542
  )
523
543
  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"))
544
+ digest.update(
545
+ f"target-platforms:{','.join(self.TARGET_INSTALL_PLATFORMS)}\n".encode("utf-8")
546
+ )
525
547
  digest.update(requirements_text.encode("utf-8"))
526
548
 
527
549
  requirements = [line for line in requirements_text.splitlines() if line.strip()]
@@ -641,13 +663,13 @@ class CodeBuilder(BaseBuilder):
641
663
  )
642
664
 
643
665
  def _iter_bundled_source_package(self, package_name: str, package_root: Path):
644
- for file_path in sorted(package_root.rglob('*')):
666
+ for file_path in sorted(package_root.rglob("*")):
645
667
  if not file_path.is_file():
646
668
  continue
647
669
  relative_path = file_path.relative_to(package_root)
648
670
  if package_name == "ksadk" and self._should_skip_ksadk_relative_path(relative_path):
649
671
  continue
650
- if '__pycache__' in file_path.parts:
672
+ if "__pycache__" in file_path.parts:
651
673
  continue
652
674
  suffix = file_path.suffix.lower()
653
675
  if suffix not in self.KSADK_ALLOWED_SUFFIXES:
@@ -709,26 +731,26 @@ class CodeBuilder(BaseBuilder):
709
731
  file_name.startswith(".env.")
710
732
  and file_name not in {".env.example", ".env.sample", ".env.template"}
711
733
  )
712
-
734
+
713
735
  def _prepare_requirements(self, detection_result) -> Path:
714
736
  """准备 requirements.txt"""
715
737
  final_deps = self._build_requirements_list(detection_result)
716
738
 
717
739
  if (self.project_dir / "requirements.txt").exists():
718
- click.echo(f" 发现 requirements.txt,正在合并...")
740
+ click.echo(" 发现 requirements.txt,正在合并...")
719
741
  else:
720
- click.echo(f" 自动生成依赖清单")
721
-
742
+ click.echo(" 自动生成依赖清单")
743
+
722
744
  # 写入构建目录
723
745
  requirements_path = self.build_dir / "requirements.txt"
724
746
  requirements_path.write_text("\n".join(final_deps))
725
-
747
+
726
748
  click.echo(f" 共 {len(final_deps)} 个依赖包:")
727
749
  for dep in final_deps[:5]:
728
750
  click.echo(f" • {dep}")
729
751
  if len(final_deps) > 5:
730
752
  click.echo(f" ... 及其他 {len(final_deps) - 5} 个")
731
-
753
+
732
754
  return requirements_path
733
755
 
734
756
  def _build_requirements_list(self, detection_result) -> List[str]:
@@ -740,14 +762,35 @@ class CodeBuilder(BaseBuilder):
740
762
  user_requirements = self.project_dir / "requirements.txt"
741
763
  if user_requirements.exists():
742
764
  user_content = user_requirements.read_text(encoding="utf-8")
765
+ parsed_user_requirements = parse_requirements_text(user_content)
766
+ final_deps = merge_requirement_lists(
767
+ final_deps,
768
+ self._bundled_ksadk_extra_requirements(parsed_user_requirements),
769
+ )
743
770
  user_deps = exclude_requirement_names(
744
- parse_requirements_text(user_content),
771
+ parsed_user_requirements,
745
772
  excluded_names={"ksadk"},
746
773
  )
747
774
  final_deps = merge_requirement_lists(final_deps, user_deps)
748
775
 
749
776
  return final_deps
750
-
777
+
778
+ def _bundled_ksadk_extra_requirements(self, requirements: List[str]) -> tuple[str, ...]:
779
+ extras: set[str] = set()
780
+ for raw_requirement in requirements:
781
+ try:
782
+ parsed = Requirement(raw_requirement)
783
+ except InvalidRequirement:
784
+ continue
785
+ if parsed.name.lower().replace("_", "-") != "ksadk":
786
+ continue
787
+ extras.update(extra.lower() for extra in parsed.extras)
788
+
789
+ bundled: list[str] = []
790
+ if "agui" in extras:
791
+ bundled.extend(self.BUNDLED_AGUI_RUNTIME_REQUIREMENTS)
792
+ return tuple(bundled)
793
+
751
794
  def _get_base_requirements(self, detection_result) -> List[str]:
752
795
  """获取基础依赖列表"""
753
796
  deps = [
@@ -765,12 +808,12 @@ class CodeBuilder(BaseBuilder):
765
808
  "openinference-instrumentation-langchain>=0.1.0",
766
809
  "langfuse>=2.0.0",
767
810
  ]
768
-
811
+
769
812
  framework = detection_result.type.value
770
- deps += requirements_for_framework(framework)
771
-
813
+ deps += code_requirements_for_framework(framework)
814
+
772
815
  return deps
773
-
816
+
774
817
  # 目标 Python 版本 (必须与容器运行时一致)
775
818
  TARGET_PYTHON_VERSION = "312" # 容器中为 Python 3.12
776
819
 
@@ -875,9 +918,7 @@ class CodeBuilder(BaseBuilder):
875
918
  def _result_error_summary(self, result: subprocess.CompletedProcess[str] | None) -> str:
876
919
  if result is None:
877
920
  return "unknown"
878
- combined = "\n".join(
879
- part for part in (result.stderr or "", result.stdout or "") if part
880
- )
921
+ combined = "\n".join(part for part in (result.stderr or "", result.stdout or "") if part)
881
922
  for line in reversed(combined.splitlines()):
882
923
  normalized = line.strip()
883
924
  if normalized:
@@ -887,9 +928,7 @@ class CodeBuilder(BaseBuilder):
887
928
  def _pip_missing_from_result(self, result: subprocess.CompletedProcess[str] | None) -> bool:
888
929
  if result is None:
889
930
  return False
890
- combined = "\n".join(
891
- part for part in (result.stderr or "", result.stdout or "") if part
892
- )
931
+ combined = "\n".join(part for part in (result.stderr or "", result.stdout or "") if part)
893
932
  return "No module named pip" in combined
894
933
 
895
934
  def _bootstrap_pip_if_missing(self, result: subprocess.CompletedProcess[str] | None) -> bool:
@@ -952,7 +991,9 @@ class CodeBuilder(BaseBuilder):
952
991
  if not normalized:
953
992
  return None
954
993
 
955
- force_emit = percent > self._install_progress_percent or stage != self._install_progress_stage_name
994
+ force_emit = (
995
+ percent > self._install_progress_percent or stage != self._install_progress_stage_name
996
+ )
956
997
  count = self._install_progress_event_counts.get(stage, 0) + 1
957
998
  self._install_progress_event_counts[stage] = count
958
999
 
@@ -960,7 +1001,7 @@ class CodeBuilder(BaseBuilder):
960
1001
  return self._truncate_progress_summary(normalized)
961
1002
 
962
1003
  if stage == "解析依赖":
963
- detail = normalized[len("Collecting "):].split(" (from ", 1)[0]
1004
+ detail = normalized[len("Collecting ") :].split(" (from ", 1)[0]
964
1005
  summary = f"已解析 {count} 个依赖,最近: {detail}"
965
1006
  if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 10 == 0:
966
1007
  return summary
@@ -973,35 +1014,36 @@ class CodeBuilder(BaseBuilder):
973
1014
 
974
1015
  if stage == "下载依赖":
975
1016
  if normalized.startswith("Using cached "):
976
- payload = normalized[len("Using cached "):]
1017
+ payload = normalized[len("Using cached ") :]
977
1018
  elif normalized.startswith("Downloading "):
978
- payload = normalized[len("Downloading "):]
1019
+ payload = normalized[len("Downloading ") :]
979
1020
  else:
980
1021
  payload = normalized
981
1022
  target = self._extract_pip_artifact_name(payload)
982
- summary = f"已处理 {count} 个 wheel,耗时 {self._format_elapsed(self._install_progress_started_at)},最近: {target}"
1023
+ elapsed = self._format_elapsed(self._install_progress_started_at)
1024
+ summary = f"已处理 {count} 个 wheel,耗时 {elapsed},最近: {target}"
983
1025
  if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 5 == 0:
984
1026
  return summary
985
1027
  return None
986
1028
 
987
1029
  if stage == "构建 wheel":
988
- detail = normalized[len("Building wheel for "):].split(" ", 1)[0]
1030
+ detail = normalized[len("Building wheel for ") :].split(" ", 1)[0]
989
1031
  if force_emit or count == 1 or count % 5 == 0:
990
1032
  return f"构建 wheel: {detail}"
991
1033
  return None
992
1034
 
993
1035
  if stage == "安装依赖":
994
- detail = normalized[len("Installing collected packages:"):].strip()
1036
+ detail = normalized[len("Installing collected packages:") :].strip()
995
1037
  return f"安装包: {self._truncate_progress_summary(detail, max_length=60)}"
996
1038
 
997
1039
  if stage == "复用已安装依赖":
998
- detail = normalized[len("Requirement already satisfied:"):].split(" in ", 1)[0].strip()
1040
+ detail = normalized[len("Requirement already satisfied:") :].split(" in ", 1)[0].strip()
999
1041
  if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 10 == 0:
1000
1042
  return f"复用依赖: {detail}"
1001
1043
  return None
1002
1044
 
1003
1045
  if stage == "安装完成":
1004
- detail = normalized[len("Successfully installed "):].strip()
1046
+ detail = normalized[len("Successfully installed ") :].strip()
1005
1047
  return f"最近结果: {self._truncate_progress_summary(detail, max_length=60)}"
1006
1048
 
1007
1049
  return self._truncate_progress_summary(normalized)
@@ -1094,8 +1136,7 @@ class CodeBuilder(BaseBuilder):
1094
1136
  timings: dict[str, Optional[float]] = {}
1095
1137
  with ThreadPoolExecutor(max_workers=min(4, len(urls))) as executor:
1096
1138
  future_to_url = {
1097
- executor.submit(self._probe_pip_index_latency, url): url
1098
- for url in urls
1139
+ executor.submit(self._probe_pip_index_latency, url): url for url in urls
1099
1140
  }
1100
1141
  for future in as_completed(future_to_url):
1101
1142
  url = future_to_url[future]
@@ -1116,7 +1157,7 @@ class CodeBuilder(BaseBuilder):
1116
1157
  best = ordered[0]
1117
1158
  best_ms = round((timings[best] or 0.0) * 1000)
1118
1159
  return ordered, f"测速优先 {self._short_pip_index_label(best)} ({best_ms} ms)"
1119
-
1160
+
1120
1161
  def _install_dependencies(self, requirements_path: Path) -> bool:
1121
1162
  """安装依赖到 deps_dir"""
1122
1163
  current_python = f"{sys.version_info.major}.{sys.version_info.minor}"
@@ -1128,9 +1169,8 @@ class CodeBuilder(BaseBuilder):
1128
1169
  )
1129
1170
 
1130
1171
  try:
1131
- need_binary_replacement = (
1132
- sys.platform in ("darwin", "win32")
1133
- or (sys.platform.startswith("linux") and current_python != target_python)
1172
+ need_binary_replacement = sys.platform in ("darwin", "win32") or (
1173
+ sys.platform.startswith("linux") and current_python != target_python
1134
1174
  )
1135
1175
  used_target_runtime_wheels = False
1136
1176
  result = None
@@ -1167,16 +1207,18 @@ class CodeBuilder(BaseBuilder):
1167
1207
  requirements_path,
1168
1208
  target_runtime_wheels=False,
1169
1209
  )
1170
-
1210
+
1171
1211
  if result.returncode != 0:
1172
1212
  self._finish_install_progress()
1173
- click.secho(" ✗ 安装失败", fg='red')
1213
+ click.secho(" ✗ 安装失败", fg="red")
1174
1214
  if result.stderr:
1175
- error_lines = [l for l in result.stderr.split('\n') if 'ERROR' in l.upper()][:3]
1215
+ error_lines = [
1216
+ line for line in result.stderr.split("\n") if "ERROR" in line.upper()
1217
+ ][:3]
1176
1218
  for line in error_lines:
1177
1219
  click.echo(f" {line}")
1178
1220
  return False
1179
-
1221
+
1180
1222
  # 替换非 Linux 平台二进制,或 Linux 下非目标 Python ABI 的二进制
1181
1223
  if need_binary_replacement and not used_target_runtime_wheels:
1182
1224
  self._emit_install_progress(
@@ -1185,7 +1227,7 @@ class CodeBuilder(BaseBuilder):
1185
1227
  "检测并替换平台相关二进制",
1186
1228
  )
1187
1229
  self._replace_platform_binaries()
1188
-
1230
+
1189
1231
  # 二进制兼容性校验(避免把不可运行的包部署到 Linux Runtime)
1190
1232
  self._emit_install_progress(
1191
1233
  96,
@@ -1195,7 +1237,7 @@ class CodeBuilder(BaseBuilder):
1195
1237
  incompatibles = self._scan_incompatible_binaries_in_deps()
1196
1238
  if incompatibles:
1197
1239
  self._finish_install_progress()
1198
- click.secho(" ✗ 检测到与 Linux Runtime 不兼容的关键二进制,构建终止", fg='red')
1240
+ click.secho(" ✗ 检测到与 Linux Runtime 不兼容的关键二进制,构建终止", fg="red")
1199
1241
  for item in incompatibles[:10]:
1200
1242
  click.echo(f" - {item}")
1201
1243
  if any(i.startswith("python-abi-mismatch:") for i in incompatibles):
@@ -1204,213 +1246,243 @@ class CodeBuilder(BaseBuilder):
1204
1246
  f"请使用 Python {target_python} 构建,或改用 Container 模式部署"
1205
1247
  )
1206
1248
  if any("tiktoken" in i for i in incompatibles):
1207
- click.echo(" 提示: tiktoken 为 langchain-openai 必需;若替换失败可重试或检查网络/镜像")
1249
+ click.echo(
1250
+ " 提示: tiktoken 为 langchain-openai 必需;"
1251
+ "若替换失败可重试或检查网络/镜像"
1252
+ )
1208
1253
  click.echo(" 建议: 删除 .agentengine/*_build 后重新构建,或在 Linux 环境重新打包")
1209
1254
  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)
1255
+
1256
+ deps_count = sum(1 for _ in self.deps_dir.rglob("*") if _.is_file())
1257
+ deps_size = sum(f.stat().st_size for f in self.deps_dir.rglob("*") if f.is_file()) / (
1258
+ 1024 * 1024
1259
+ )
1213
1260
  self._emit_install_progress(
1214
1261
  100,
1215
1262
  "依赖安装完成",
1216
1263
  f"{deps_count} 个文件, {deps_size:.1f} MB",
1217
1264
  )
1218
1265
  self._finish_install_progress()
1219
-
1266
+
1220
1267
  return True
1221
-
1268
+
1222
1269
  except subprocess.TimeoutExpired:
1223
1270
  self._finish_install_progress()
1224
1271
  timeout_minutes = max(1, round(self._pip_install_timeout_seconds() / 60))
1225
- click.secho(f" ✗ 安装超时 ({timeout_minutes}分钟)", fg='red')
1272
+ click.secho(f" ✗ 安装超时 ({timeout_minutes}分钟)", fg="red")
1226
1273
  return False
1227
1274
  except Exception as e:
1228
1275
  self._finish_install_progress()
1229
- click.secho(f" ✗ 依赖安装失败: {e}", fg='red')
1276
+ click.secho(f" ✗ 依赖安装失败: {e}", fg="red")
1230
1277
  return False
1231
-
1278
+
1232
1279
  def _replace_platform_binaries(self) -> None:
1233
1280
  """替换非目标运行时平台/ABI 的 C 扩展为 Linux 目标版本。"""
1234
1281
  # 模块名到 pip 包名的映射
1235
1282
  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',
1283
+ "_cffi_backend": "cffi",
1284
+ "yaml": "pyyaml",
1285
+ "_yaml": "pyyaml",
1286
+ "rpds": "rpds-py",
1287
+ "PIL": "pillow",
1288
+ "cv2": "opencv-python",
1289
+ "sklearn": "scikit-learn",
1290
+ "_watchdog_fsevents": "watchdog",
1291
+ "google": None, # 跳过命名空间包
1292
+ "grpc": "grpcio",
1293
+ "_grpc": "grpcio",
1294
+ "uuid_utils": "uuid-utils",
1295
+ "pydantic_core": "pydantic-core",
1296
+ "_pydantic_core": "pydantic-core",
1250
1297
  # tiktoken: langchain-openai 核心依赖, Rust 编译的 C 扩展
1251
- 'tiktoken': 'tiktoken',
1252
- '_tiktoken': 'tiktoken',
1298
+ "tiktoken": "tiktoken",
1299
+ "_tiktoken": "tiktoken",
1253
1300
  # 其他常见原生扩展
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',
1301
+ "regex": "regex",
1302
+ "_regex": "regex",
1303
+ "multidict": "multidict",
1304
+ "yarl": "yarl",
1305
+ "aiohttp": "aiohttp",
1306
+ "frozenlist": "frozenlist",
1307
+ "charset_normalizer": "charset-normalizer",
1308
+ "msgpack": "msgpack",
1262
1309
  # Windows 特有
1263
- 'win32': 'pywin32',
1264
- 'win32com': 'pywin32',
1310
+ "win32": "pywin32",
1311
+ "win32com": "pywin32",
1265
1312
  }
1266
-
1313
+
1267
1314
  # 找到所有二进制文件
1268
1315
  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
-
1316
+ if sys.platform == "darwin":
1317
+ binary_files = list(self.deps_dir.rglob("*.so")) + list(self.deps_dir.rglob("*.dylib"))
1318
+ elif sys.platform == "win32":
1319
+ binary_files = list(self.deps_dir.rglob("*.pyd")) + list(self.deps_dir.rglob("*.dll"))
1320
+ elif sys.platform.startswith("linux"):
1321
+ binary_files = list(self.deps_dir.rglob("*.so"))
1322
+
1276
1323
  if not binary_files:
1277
1324
  return
1278
-
1325
+
1279
1326
  # 提取需要替换的包名
1280
1327
  packages_to_replace: Set[str] = set()
1281
1328
  for bin_file in binary_files:
1282
1329
  rel_path = bin_file.relative_to(self.deps_dir)
1283
1330
  parts = rel_path.parts
1284
-
1331
+
1285
1332
  # 忽略 bin 目录下的 dll (通常是 runtime)
1286
- if 'bin' in parts:
1333
+ if "bin" in parts:
1287
1334
  continue
1288
-
1335
+
1289
1336
  if len(parts) > 1:
1290
1337
  detected_name = parts[0]
1291
1338
  else:
1292
- detected_name = bin_file.name.split('.')[0]
1293
-
1339
+ detected_name = bin_file.name.split(".")[0]
1340
+
1294
1341
  # 跳过特定文件夹
1295
- if detected_name in ('__pycache__', 'bin', 'include', 'lib', 'Scripts'):
1342
+ if detected_name in ("__pycache__", "bin", "include", "lib", "Scripts"):
1296
1343
  continue
1297
-
1344
+
1298
1345
  if detected_name in MODULE_TO_PACKAGE:
1299
1346
  pkg_name = MODULE_TO_PACKAGE[detected_name]
1300
1347
  if pkg_name:
1301
1348
  packages_to_replace.add(pkg_name)
1302
1349
  else:
1303
1350
  packages_to_replace.add(detected_name)
1304
-
1351
+
1305
1352
  if not packages_to_replace:
1306
1353
  return
1307
-
1308
- click.echo(f"\r 检测到 {len(binary_files)} 个二进制文件 ({sys.platform}), 替换 {len(packages_to_replace)} 个包为 Linux 版本")
1309
-
1354
+
1355
+ click.echo(
1356
+ f"\r 检测到 {len(binary_files)} 个二进制文件 ({sys.platform}), "
1357
+ f"替换 {len(packages_to_replace)} 个包为 Linux 版本"
1358
+ )
1359
+
1310
1360
  # 下载 Linux wheels
1311
1361
  wheels_dir = self.build_dir / "linux_wheels"
1312
1362
  if wheels_dir.exists():
1313
1363
  shutil.rmtree(wheels_dir)
1314
1364
  wheels_dir.mkdir(parents=True)
1315
-
1365
+
1316
1366
  replaced_count = 0
1317
1367
  total_packages = len(packages_to_replace)
1318
1368
  for index, pkg_name in enumerate(sorted(packages_to_replace), start=1):
1319
1369
  # 提取确切的版本号以避免不兼容问题 (如 pydantic-core)
1320
1370
  target_version = ""
1321
- search_name = pkg_name.replace('-', '_').lower()
1371
+ search_name = pkg_name.replace("-", "_").lower()
1322
1372
  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'
1373
+ version_str = info_dir.name[
1374
+ len(search_name) + 1 : -10
1375
+ ] # remove search_name + '-' and '.dist-info'
1324
1376
  target_version = f"=={version_str}"
1325
1377
  break
1326
-
1378
+
1327
1379
  pkg_with_version = f"{pkg_name}{target_version}"
1328
1380
  self._emit_install_progress(
1329
1381
  88 + min(6, round((index / max(total_packages, 1)) * 6)),
1330
1382
  "替换 Linux wheels",
1331
1383
  f"{index}/{total_packages} {pkg_with_version}",
1332
1384
  )
1333
-
1385
+
1334
1386
  try:
1335
1387
  downloaded = False
1336
1388
  for index_url in self._pip_index_candidates():
1337
1389
  download_cmd = [
1338
- sys.executable, "-m", "pip", "download",
1390
+ sys.executable,
1391
+ "-m",
1392
+ "pip",
1393
+ "download",
1339
1394
  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,
1395
+ "-d",
1396
+ str(wheels_dir),
1397
+ "--platform",
1398
+ "manylinux2014_x86_64",
1399
+ "--platform",
1400
+ "manylinux_2_17_x86_64",
1401
+ "--platform",
1402
+ "manylinux_2_28_x86_64",
1403
+ "--platform",
1404
+ "musllinux_1_2_x86_64",
1405
+ "--platform",
1406
+ "linux_x86_64",
1407
+ "--python-version",
1408
+ self.TARGET_PYTHON_VERSION,
1347
1409
  "--only-binary=:all:",
1348
- "--implementation", "cp",
1410
+ "--implementation",
1411
+ "cp",
1349
1412
  "--no-deps",
1350
1413
  "--quiet",
1351
1414
  "--disable-pip-version-check",
1352
- "--retries", "2",
1353
- "--timeout", "30",
1354
- "--cache-dir", str(self.pip_cache_dir),
1415
+ "--retries",
1416
+ "2",
1417
+ "--timeout",
1418
+ "30",
1419
+ "--cache-dir",
1420
+ str(self.pip_cache_dir),
1355
1421
  ]
1356
1422
  if index_url:
1357
1423
  download_cmd += ["-i", index_url]
1358
- result = subprocess.run(download_cmd, capture_output=True, text=True, timeout=90)
1424
+ result = subprocess.run(
1425
+ download_cmd, capture_output=True, text=True, timeout=90
1426
+ )
1359
1427
  if result.returncode == 0:
1360
1428
  downloaded = True
1361
1429
  break
1362
1430
  if downloaded:
1363
1431
  replaced_count += 1
1364
1432
  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')
1433
+ failed_msg = (
1434
+ result.stderr.strip().split("\n")[-1]
1435
+ if result and result.stderr
1436
+ else "unknown"
1437
+ )
1438
+ click.secho(f" ⚠ 替换失败: {pkg_with_version} ({failed_msg})", fg="yellow")
1367
1439
  except Exception as e:
1368
- click.secho(f" ⚠ 替换异常: {pkg_name} ({e})", fg='yellow')
1369
-
1440
+ click.secho(f" ⚠ 替换异常: {pkg_name} ({e})", fg="yellow")
1441
+
1370
1442
  # 解压 wheel 覆盖到 deps_dir
1371
1443
  for wheel_file in wheels_dir.glob("*.whl"):
1372
1444
  try:
1373
- wheel_name = wheel_file.name.split('-')[0].lower().replace('_', '-')
1374
-
1445
+ wheel_name = wheel_file.name.split("-")[0].lower().replace("_", "-")
1446
+
1375
1447
  # 删除旧的包目录
1376
1448
  for old_dir in self.deps_dir.iterdir():
1377
- if old_dir.is_dir() and old_dir.name.lower().replace('_', '-') == wheel_name:
1449
+ if old_dir.is_dir() and old_dir.name.lower().replace("_", "-") == wheel_name:
1378
1450
  shutil.rmtree(old_dir)
1379
- # 不要 break,可能由多个目录 (e.g. pydantic_core, pydantic_core-2.x.dist-info)
1380
-
1451
+ # 不要 break:一个包可能有多个目录,例如 dist-info。
1452
+
1381
1453
  # 删除根目录下的二进制文件
1382
- for ext in ('*.so', '*.dylib', '*.pyd', '*.dll'):
1454
+ for ext in ("*.so", "*.dylib", "*.pyd", "*.dll"):
1383
1455
  for bin_file in self.deps_dir.glob(f"{wheel_name}*{ext[1:]}"):
1384
1456
  try:
1385
1457
  bin_file.unlink()
1386
- except:
1458
+ except OSError:
1387
1459
  pass
1388
1460
  for bin_file in self.deps_dir.glob(f"{wheel_name.replace('-', '_')}*{ext[1:]}"):
1389
1461
  try:
1390
1462
  bin_file.unlink()
1391
- except:
1463
+ except OSError:
1392
1464
  pass
1393
-
1465
+
1394
1466
  # 解压新的 wheel
1395
- with zipfile.ZipFile(wheel_file, 'r') as zf:
1467
+ with zipfile.ZipFile(wheel_file, "r") as zf:
1396
1468
  zf.extractall(self.deps_dir)
1397
1469
  except Exception:
1398
1470
  pass
1399
-
1471
+
1400
1472
  shutil.rmtree(wheels_dir, ignore_errors=True)
1401
-
1473
+
1402
1474
  # 清理所有残留的非 Linux 平台二进制文件
1403
1475
  # (wheel 解压后可能有旧的 darwin/win .so 文件未被覆盖)
1404
1476
  cleaned_count = 0
1405
- for so_file in list(self.deps_dir.rglob('*.so')):
1477
+ for so_file in list(self.deps_dir.rglob("*.so")):
1406
1478
  name = so_file.name.lower()
1407
- if 'darwin' in name or 'win' in name:
1479
+ if "darwin" in name or "win" in name:
1408
1480
  try:
1409
1481
  so_file.unlink()
1410
1482
  cleaned_count += 1
1411
1483
  except Exception:
1412
1484
  pass
1413
- for dylib_file in list(self.deps_dir.rglob('*.dylib')):
1485
+ for dylib_file in list(self.deps_dir.rglob("*.dylib")):
1414
1486
  try:
1415
1487
  dylib_file.unlink()
1416
1488
  cleaned_count += 1
@@ -1418,7 +1490,7 @@ class CodeBuilder(BaseBuilder):
1418
1490
  pass
1419
1491
  if cleaned_count > 0:
1420
1492
  click.echo(f" ✓ 清理 {cleaned_count} 个残留平台二进制文件")
1421
-
1493
+
1422
1494
  click.echo(f" ✓ 成功替换 {replaced_count}/{len(packages_to_replace)} 个包")
1423
1495
 
1424
1496
  def _is_linux_so(self, name: str) -> bool:
@@ -1463,7 +1535,7 @@ class CodeBuilder(BaseBuilder):
1463
1535
 
1464
1536
  def _detect_critical_binary_issues(self, names: List[str]) -> List[str]:
1465
1537
  issues: List[str] = []
1466
-
1538
+
1467
1539
  # 定义需要检查的关键原生扩展模块
1468
1540
  # 格式: (描述, 正则模式)
1469
1541
  critical_modules = [
@@ -1474,7 +1546,7 @@ class CodeBuilder(BaseBuilder):
1474
1546
  # tiktoken/_tiktoken: langchain-openai 核心依赖 (Rust 编译)
1475
1547
  ("tiktoken/_tiktoken", r"tiktoken/_tiktoken.*\.(so|pyd)$"),
1476
1548
  ]
1477
-
1549
+
1478
1550
  for module_name, pattern in critical_modules:
1479
1551
  matched_bins = [n for n in names if re.search(pattern, n)]
1480
1552
  if matched_bins:
@@ -1487,19 +1559,21 @@ class CodeBuilder(BaseBuilder):
1487
1559
  f"python-abi-mismatch:{module_name}:"
1488
1560
  f"expected-cpython-{self.TARGET_PYTHON_VERSION}-or-abi3"
1489
1561
  )
1490
-
1562
+
1491
1563
  # 通用检查: 所有 .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())
1564
+ all_so_files = [n for n in names if n.endswith(".so")]
1565
+ darwin_so_count = sum(1 for n in all_so_files if "darwin" in n.lower())
1494
1566
  if darwin_so_count > 0:
1495
1567
  issues.append(f"warning:found-{darwin_so_count}-darwin-so-files")
1496
-
1568
+
1497
1569
  return issues
1498
-
1570
+
1499
1571
  def _package_zip(self, zip_path: Path, detection_result) -> None:
1500
1572
  """打包 zip 文件"""
1501
1573
  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()]
1574
+ dependency_files = [
1575
+ file_path for file_path in self.deps_dir.rglob("*") if file_path.is_file()
1576
+ ]
1503
1577
  bundled_source_files = list(self._iter_bundled_source_files())
1504
1578
  dependency_size = sum(file_path.stat().st_size for file_path in dependency_files)
1505
1579
  if dependency_files:
@@ -1509,12 +1583,12 @@ class CodeBuilder(BaseBuilder):
1509
1583
  f"{len(bundled_source_files)} 个 runtime 文件"
1510
1584
  )
1511
1585
 
1512
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
1586
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
1513
1587
  # 添加项目文件
1514
1588
  for file_path in project_files:
1515
1589
  arcname = file_path.relative_to(self.project_dir).as_posix()
1516
1590
  zf.write(file_path, arcname)
1517
-
1591
+
1518
1592
  # 添加依赖
1519
1593
  deps_count = 0
1520
1594
  for file_path in dependency_files:
@@ -1523,7 +1597,7 @@ class CodeBuilder(BaseBuilder):
1523
1597
  deps_count += 1
1524
1598
  self._emit_package_progress("打包依赖", deps_count, len(dependency_files))
1525
1599
  self._finish_package_progress()
1526
-
1600
+
1527
1601
  # 添加随运行时下发的 ksadk 源码
1528
1602
  bundled_source_count = 0
1529
1603
  for package_name, relative, file_path in bundled_source_files:
@@ -1536,13 +1610,13 @@ class CodeBuilder(BaseBuilder):
1536
1610
  len(bundled_source_files),
1537
1611
  )
1538
1612
  self._finish_package_progress()
1539
-
1613
+
1540
1614
  click.echo(f" ✓ 打包运行时源码: {bundled_source_count} 个文件")
1541
-
1615
+
1542
1616
  # 添加 entrypoint
1543
1617
  entrypoint_content = self._generate_entrypoint(detection_result)
1544
1618
  zf.writestr("entrypoint.py", entrypoint_content)
1545
-
1619
+
1546
1620
  click.echo(f" ✓ 打包完成: {len(project_files)} 个项目文件 + {deps_count} 个依赖文件")
1547
1621
  self._emit_package_size_report(zip_path)
1548
1622
 
@@ -1586,10 +1660,7 @@ class CodeBuilder(BaseBuilder):
1586
1660
  )
1587
1661
  if by_top_level:
1588
1662
  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
- )
1663
+ summary = ", ".join(f"{name} {size / (1024 * 1024):.1f} MB" for name, size in top_items)
1593
1664
  click.echo(f" 体积 Top{len(top_items)}: {summary}")
1594
1665
  if (
1595
1666
  raw_total > self.CONTAINER_SUGGESTION_RAW_THRESHOLD_BYTES
@@ -1660,7 +1731,7 @@ class CodeBuilder(BaseBuilder):
1660
1731
  self._package_progress_last_line = ""
1661
1732
  self._package_progress_last_percent_by_label = {}
1662
1733
  self._package_progress_logged_milestone_by_label = {}
1663
-
1734
+
1664
1735
  def _generate_entrypoint(self, detection_result) -> str:
1665
1736
  """生成 entrypoint.py"""
1666
1737
  package_name = Path(detection_result.package_path).name
@@ -1787,7 +1858,10 @@ logger.info(f"框架: {{detection_result.name}}")
1787
1858
  logger.info(f"入口: {{detection_result.entry_point}}")
1788
1859
 
1789
1860
  # 初始化 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"))
1861
+ has_otlp = bool(
1862
+ os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
1863
+ or os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
1864
+ )
1791
1865
  has_cloud_monitor_otlp = bool(
1792
1866
  os.environ.get("CLOUD_MONITOR_APP_KEY")
1793
1867
  or os.environ.get("CLOUD_MONITOR_OTLP_ENDPOINT")
@@ -1798,11 +1872,19 @@ has_cloud_monitor_langfuse = bool(
1798
1872
  or os.environ.get("CLOUD_MONITOR_LANGFUSE_SECRET_KEY")
1799
1873
  or os.environ.get("CLOUD_MONITOR_LANGFUSE_HOST")
1800
1874
  )
1801
- if os.environ.get("LANGFUSE_PUBLIC_KEY") or has_otlp or has_cloud_monitor_otlp or has_cloud_monitor_langfuse:
1875
+ if (
1876
+ os.environ.get("LANGFUSE_PUBLIC_KEY")
1877
+ or has_otlp
1878
+ or has_cloud_monitor_otlp
1879
+ or has_cloud_monitor_langfuse
1880
+ ):
1802
1881
  try:
1803
1882
  from ksadk.tracing import setup_tracing
1804
-
1805
- use_callback_only = os.environ.get("LANGFUSE_USE_CALLBACK", "").strip().lower() in ("1", "true", "yes", "on")
1883
+
1884
+ use_callback_only = (
1885
+ os.environ.get("LANGFUSE_USE_CALLBACK", "").strip().lower()
1886
+ in ("1", "true", "yes", "on")
1887
+ )
1806
1888
 
1807
1889
  setup_tracing(use_callback_only=use_callback_only)
1808
1890
  logger.info(
@@ -1818,7 +1900,7 @@ if os.environ.get("LANGFUSE_PUBLIC_KEY") or has_otlp or has_cloud_monitor_otlp o
1818
1900
  logger.info("正在加载 Agent...")
1819
1901
  runner = create_runner(detection_result, CODE_ROOT)
1820
1902
  runner.load_agent()
1821
- set_runner(runner)
1903
+ set_runner(runner, loaded=True)
1822
1904
  logger.info("Agent 加载成功!")
1823
1905
 
1824
1906
  if __name__ == "__main__":
@@ -1848,13 +1930,13 @@ if __name__ == "__main__":
1848
1930
  self._pip_index_candidates_cache = candidates
1849
1931
  return list(candidates)
1850
1932
 
1851
- seen: Set[str] = set()
1933
+ fallback_seen: Set[str] = set()
1852
1934
  urls: list[str] = []
1853
1935
  for candidate in self.PIP_INDEX_FALLBACKS:
1854
1936
  normalized = self._normalize_pip_index_url(candidate)
1855
- if normalized in seen:
1937
+ if normalized in fallback_seen:
1856
1938
  continue
1857
- seen.add(normalized)
1939
+ fallback_seen.add(normalized)
1858
1940
  urls.append(normalized)
1859
1941
 
1860
1942
  ordered, summary = self._rank_pip_index_urls(urls)