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
@@ -6,38 +6,38 @@ Serverless Provider - 金山云 Serverless 计算引擎 (AgentEngine 托管)
6
6
  - Deploy 阶段: 客户端调用 AgentEngine Server API 发起部署
7
7
  """
8
8
 
9
- import os
10
9
  import json
11
10
  import logging
12
- import shutil
11
+ import os
13
12
  import time
14
- import yaml
15
13
  from pathlib import Path
16
- from typing import Dict, Any, List, Optional
14
+ from typing import Any, Dict, List, Optional
15
+
17
16
  import click
17
+ import yaml
18
18
 
19
- from ksadk.builders.ks3_uploader import KS3Uploader
19
+ from ksadk.api import AgentEngineClient, DryRunExit
20
+ from ksadk.builders.base import BaseBuilder
20
21
  from ksadk.builders.code_builder import CodeBuilder
22
+ from ksadk.builders.container_builder import (
23
+ ContainerBuilder,
24
+ registry_kind_label,
25
+ resolve_registry_credentials,
26
+ )
27
+ from ksadk.builders.ks3_uploader import KS3Uploader
28
+ from ksadk.configs.env_registry import ENV_VAR_REGISTRY
29
+ from ksadk.configs.global_config import get_env_from_global_config
30
+ from ksadk.configs.settings import DEFAULT_RUNTIME_TIMEZONE
21
31
  from ksadk.deployment.agent_access import get_latest_agent_access
22
32
  from ksadk.deployment.base import (
23
33
  BaseDeployProvider,
24
- DeployTarget,
25
34
  DeployResult,
26
35
  DeployStatus,
36
+ DeployTarget,
27
37
  PackageInfo,
28
38
  )
29
39
  from ksadk.deployment.registry import DeployProviderRegistry
30
40
  from ksadk.deployment.ui_config import resolve_ui_config, ui_config_to_state_fields
31
- from ksadk.builders.container_builder import (
32
- ContainerBuilder,
33
- registry_kind_label,
34
- resolve_registry_credentials,
35
- )
36
- from ksadk.configs.settings import DEFAULT_RUNTIME_TIMEZONE
37
- from ksadk.configs.env_registry import ENV_VAR_REGISTRY
38
- from ksadk.configs.global_config import get_env_from_global_config
39
- from ksadk.api import AgentEngineClient, DryRunExit
40
-
41
41
 
42
42
  logger = logging.getLogger(__name__)
43
43
 
@@ -71,11 +71,7 @@ _DEPLOY_PROCESS_ENV_ALLOWLIST = frozenset(
71
71
  )
72
72
  _DEPLOY_PROCESS_ENV_PREFIXES = ("KSADK_", "OPENAI_", "KSYUN_", "E2B_")
73
73
  _DEPLOY_PROCESS_ENV_DENYLIST = frozenset(
74
- {
75
- spec.name
76
- for spec in ENV_VAR_REGISTRY
77
- if spec.module in {"builders", "cli", "configs", "web"}
78
- }
74
+ {spec.name for spec in ENV_VAR_REGISTRY if spec.module in {"builders", "cli", "configs", "web"}}
79
75
  ) | frozenset(
80
76
  {
81
77
  "KSADK_GLOBAL_CONFIG_ENV_KEYS",
@@ -96,7 +92,7 @@ def _should_forward_process_env(name: str) -> bool:
96
92
  @DeployProviderRegistry.register("kce")
97
93
  class ServerlessProvider(BaseDeployProvider):
98
94
  """金山云 Serverless 计算引擎 (AgentEngine Server 托管)
99
-
95
+
100
96
  重构后直接继承 BaseDeployProvider,不再依赖 DockerProvider。
101
97
  Container 模式使用 ContainerBuilder 进行打包和构建。
102
98
  """
@@ -109,7 +105,7 @@ class ServerlessProvider(BaseDeployProvider):
109
105
  supports_scaling = True
110
106
  requires_image_registry = False
111
107
 
112
- def __init__(self, config: Dict[str, Any] = None):
108
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
113
109
  self.config = config or {}
114
110
 
115
111
  @staticmethod
@@ -124,13 +120,15 @@ class ServerlessProvider(BaseDeployProvider):
124
120
 
125
121
  if password and not username and registry_kind != "personal_kcr":
126
122
  click.secho(
127
- f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,已忽略{registry_kind_label(registry_kind)}镜像凭证;"
123
+ f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,"
124
+ f"已忽略{registry_kind_label(registry_kind)}镜像凭证;"
128
125
  "企业版 KCR 和第三方镜像仓库必须配置 KCR_USERNAME + KCR_PASSWORD",
129
126
  fg="yellow",
130
127
  )
131
128
  elif registry_kind == "personal_kcr":
132
129
  click.secho(
133
- " ⚠️ 未配置个人版 KCR 镜像凭证 (KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取",
130
+ " ⚠️ 未配置个人版 KCR 镜像凭证 "
131
+ "(KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取",
134
132
  fg="yellow",
135
133
  )
136
134
  else:
@@ -196,19 +194,25 @@ class ServerlessProvider(BaseDeployProvider):
196
194
  ) -> tuple[Dict[str, str], bool, int]:
197
195
  """读取部署时注入到托管运行时的环境变量。
198
196
 
199
- 全局配置作为兜底,项目 .env 作为项目级覆盖;真实 .env 文件不会随
200
- Code/Container 制品打包,只通过 deploy payload 注入到 Pod 环境变量。
197
+ 优先级: --env/--env-file (explicit) > shell env (转发白名单前缀) > 项目 .env > 全局配置。
198
+ 真实 .env 文件不会随 Code/Container 制品打包,只通过 deploy payload 注入到 Pod 环境变量。
201
199
  """
200
+ shell_keys = set(os.environ)
202
201
  env_vars: Dict[str, str] = dict(get_env_from_global_config())
203
202
  env_file = Path(project_dir) / ".env"
204
203
  project_env_count = 0
205
- for key, value in sorted(os.environ.items()):
206
- if value and _should_forward_process_env(key):
207
- env_vars.setdefault(key, value)
208
204
  if env_file.exists():
209
205
  project_env = cls._load_project_env_vars(env_file)
210
206
  project_env_count = len(project_env)
211
- env_vars.update(project_env)
207
+ # auto .env 覆盖 global_config,但不覆盖 shell (shell 优先于 auto .env)
208
+ for key, value in project_env.items():
209
+ if key not in shell_keys:
210
+ env_vars[key] = value
211
+ # shell 转发 (仅 KSADK_/OPENAI_/KSYUN_/E2B_ 前缀 + 白名单);shell 覆盖 auto .env 与全局配置
212
+ for key, value in sorted(os.environ.items()):
213
+ if value and _should_forward_process_env(key):
214
+ env_vars[key] = value
215
+ # explicit --env/--env-file (显式 CLI 意图最高)
212
216
  env_vars.update(explicit_env_vars or {})
213
217
  env_vars.setdefault("TZ", DEFAULT_RUNTIME_TIMEZONE)
214
218
  return env_vars, env_file.exists(), project_env_count
@@ -269,7 +273,7 @@ class ServerlessProvider(BaseDeployProvider):
269
273
  return None
270
274
 
271
275
  public_access = getattr(network, "enable_public_access", None)
272
- # 三态:None=未指定。create 默认开公网;update 未指定则不发 network 字段(保留服务端现有配置)。
276
+ # 三态:None=未指定。create 默认开公网;update 未指定则不发 network 字段。
273
277
  if public_access is None:
274
278
  public_value: Optional[bool] = None if is_update else True
275
279
  else:
@@ -317,19 +321,19 @@ class ServerlessProvider(BaseDeployProvider):
317
321
  seconds = int(elapsed % 60)
318
322
  return f"{minutes}m{seconds:02d}s"
319
323
 
320
-
321
324
  async def validate_config(self, target: DeployTarget) -> tuple[bool, str]:
322
325
  """验证配置: 确保已配置 AgentEngine Server"""
323
-
326
+
324
327
  server_url = os.getenv("AGENTENGINE_SERVER_URL")
325
-
328
+
326
329
  if not server_url:
327
330
  click.echo("⚠️ 未配置 AGENTENGINE_SERVER_URL,将尝试使用默认 Region 配置")
328
-
331
+
329
332
  # Container 模式: 检查 Docker 是否可用
330
333
  artifact_type = target.extra.get("artifact_type", "Code")
331
334
  if artifact_type == "Container":
332
335
  import subprocess
336
+
333
337
  try:
334
338
  result = subprocess.run(["docker", "version"], capture_output=True, timeout=10)
335
339
  if result.returncode != 0:
@@ -341,14 +345,19 @@ class ServerlessProvider(BaseDeployProvider):
341
345
 
342
346
  return True, ""
343
347
 
344
- async def package(self, project_dir: str, detection_result: Any, config: Dict[str, Any] = None) -> PackageInfo:
348
+ async def package(
349
+ self,
350
+ project_dir: str,
351
+ detection_result: Any,
352
+ config: Optional[Dict[str, Any]] = None,
353
+ ) -> PackageInfo:
345
354
  """打包项目
346
-
355
+
347
356
  Code 模式: 返回基本 PackageInfo,实际构建在 build() 中进行
348
357
  Container 模式: 使用 ContainerBuilder 打包
349
358
  """
350
359
  project_path = Path(project_dir)
351
-
360
+
352
361
  # 创建基本 PackageInfo
353
362
  package_info = PackageInfo(
354
363
  name=detection_result.name or project_path.name,
@@ -356,14 +365,15 @@ class ServerlessProvider(BaseDeployProvider):
356
365
  build_dir=str(project_path / ".agentengine" / "build"),
357
366
  project_dir=str(project_path),
358
367
  entry_point=detection_result.entry_point,
359
- metadata={}
368
+ metadata={},
360
369
  )
361
-
370
+
362
371
  # 尝试加载之前保存的构建元数据 (ks3_path 等)
363
372
  try:
364
373
  metadata_file = project_path / ".agentengine" / "build-metadata.json"
365
374
  if metadata_file.exists():
366
375
  import json
376
+
367
377
  with open(metadata_file, "r") as f:
368
378
  saved_data = json.load(f)
369
379
  if "metadata" in saved_data:
@@ -376,15 +386,18 @@ class ServerlessProvider(BaseDeployProvider):
376
386
  click.echo(f" 📦 已加载上次构建元数据: {count} 项")
377
387
  except Exception as e:
378
388
  click.secho(f"⚠️ 加载构建元数据失败: {e}", fg="yellow")
379
-
380
- return package_info
381
-
382
389
 
390
+ return package_info
383
391
 
384
392
  async def build(self, package_info: PackageInfo, target: DeployTarget) -> PackageInfo:
385
393
  """构建 & 上传 (客户端直传 KS3)"""
386
394
  artifact_type = target.extra.get("artifact_type", "Code")
387
395
 
396
+ if artifact_type == "ManagedRuntime":
397
+ from ksadk.deployment.managed_runtime import build_managed_runtime_package
398
+
399
+ return build_managed_runtime_package(package_info, target)
400
+
388
401
  if artifact_type == "Code":
389
402
  # 1. 检查是否已有 KS3 路径
390
403
  # 优先级: CLI传入 > Metadata缓存 (仅当 !no_cache)
@@ -392,58 +405,77 @@ class ServerlessProvider(BaseDeployProvider):
392
405
  cached_ks3_path = package_info.metadata.get("ks3_path")
393
406
  no_cache = target.extra.get("no_cache", False)
394
407
  repackage = target.extra.get("repackage", False)
395
-
408
+
396
409
  # 如果显式传入了 ks3-path,直接使用
397
410
  if cli_ks3_path:
398
411
  package_info.metadata["ks3_path"] = cli_ks3_path
399
412
  return package_info
400
-
413
+
401
414
  # 如果没有 no_cache 且有缓存,才使用缓存
402
415
  if not no_cache and not repackage and cached_ks3_path:
403
416
  logger.info(f"Using cached bundle: {cached_ks3_path}")
404
417
  return package_info
405
418
 
406
- # 2. 构建 ZIP 包 (委托给 CodeBuilder)
407
-
419
+ # 2. 构建 ZIP 包
420
+
408
421
  # 传递配置,包括 no_cache
409
422
  builder_config = target.extra.copy()
410
423
  builder_config["no_cache"] = no_cache
411
424
  builder_config["repackage"] = repackage
412
-
413
- # 实例化 Builder
414
- # 注意: CodeBuilder 目前设计为直接操作 project_dir,
415
- # 这里传入原始 project_dir (package_info.project_dir)
416
- builder = CodeBuilder(Path(package_info.project_dir), config=builder_config)
417
-
425
+
426
+ artifact_builder: BaseBuilder
427
+ if artifact_type == "ManagedRuntime":
428
+ from ksadk.builders.managed_runtime_builder import ManagedRuntimeBuilder
429
+
430
+ artifact_builder = ManagedRuntimeBuilder(
431
+ Path(package_info.project_dir),
432
+ config=builder_config,
433
+ runtime_version=str(target.extra.get("runtime_version") or ""),
434
+ )
435
+ else:
436
+ # CodeBuilder 直接操作原始 project_dir。
437
+ artifact_builder = CodeBuilder(
438
+ Path(package_info.project_dir),
439
+ config=builder_config,
440
+ )
441
+
418
442
  # 执行构建
419
- build_result = builder.build()
420
-
443
+ build_result = artifact_builder.build()
444
+
421
445
  if not build_result.success:
422
446
  raise Exception(f"构建失败: {build_result.error_message}")
423
-
447
+
424
448
  zip_path = build_result.artifact_path
449
+ if zip_path is None:
450
+ raise RuntimeError("代码构建成功但未生成 artifact_path")
451
+ package_info.metadata.update(build_result.metadata)
452
+ if build_result.metadata.get("manifest_sha256"):
453
+ target.extra["manifest_sha256"] = build_result.metadata["manifest_sha256"]
425
454
  # click.echo(f" ✅ ZIP 已生成: {zip_path}")
426
-
455
+
427
456
  # 3. 直接上传 KS3 (使用本地 AK/SK)
428
457
  # 3. 直接上传 KS3 (使用本地 AK/SK)
429
- click.echo("\n正在上传代码包到 KS3...")
458
+ artifact_label = "Runtime manifest" if artifact_type == "ManagedRuntime" else "代码包"
459
+ click.echo(f"\n正在上传{artifact_label}到 KS3...")
430
460
  upload_started_at = time.monotonic()
431
-
461
+
432
462
  ks3_bucket = target.extra.get("ks3_bucket")
433
463
  upload_region = "cn-beijing-6" if target.region == "pre-online" else target.region
434
-
464
+
435
465
  uploader = KS3Uploader(region=upload_region, bucket=ks3_bucket)
436
-
466
+
437
467
  # 使用时间戳确保每次上传的代码包路径唯一,支持真正的版本回滚
438
468
  from datetime import datetime
469
+
439
470
  timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
440
- object_key = f"agents/{package_info.name}/code_{timestamp}.zip"
441
-
471
+ object_prefix = "runtime" if artifact_type == "ManagedRuntime" else "code"
472
+ object_key = f"agents/{package_info.name}/{object_prefix}_{timestamp}.zip"
473
+
442
474
  ks3_path = await uploader.upload(zip_path, object_key)
443
-
475
+
444
476
  if not ks3_path:
445
477
  raise Exception("KS3 上传失败")
446
-
478
+
447
479
  logger.info(f"Upload success: {ks3_path}")
448
480
  click.echo(f" ✓ 上传耗时: {self._format_elapsed(upload_started_at)}")
449
481
  package_info.metadata["ks3_path"] = ks3_path
@@ -466,45 +498,47 @@ class ServerlessProvider(BaseDeployProvider):
466
498
  logger.info(f"Using cached image: {cached_image}")
467
499
  package_info.image = cached_image
468
500
  return package_info
469
-
470
- builder = ContainerBuilder(
501
+
502
+ container_builder = ContainerBuilder(
471
503
  project_dir=Path(package_info.project_dir),
472
504
  tag=tag,
473
505
  registry=registry,
474
- no_cache=no_cache
506
+ no_cache=no_cache,
475
507
  )
476
-
508
+
477
509
  # 构建镜像
478
- build_result = builder.build()
510
+ build_result = container_builder.build()
479
511
  if not build_result.success:
480
512
  raise Exception(f"镜像构建失败: {build_result.error_message}")
481
-
482
- image_name = build_result.metadata.get("image")
513
+
514
+ image_name = str(build_result.metadata.get("image") or "").strip()
515
+ if not image_name:
516
+ raise RuntimeError("容器构建成功但未生成 image")
483
517
  package_info.image = image_name
484
518
  package_info.metadata["image"] = image_name
485
-
519
+
486
520
  # 推送镜像
487
- if not builder.push(image_name):
521
+ if not container_builder.push(image_name):
488
522
  raise Exception("镜像推送失败")
489
-
523
+
490
524
  click.secho(f"✅ 镜像已推送: {image_name}", fg="green")
491
525
 
492
526
  self._persist_build_metadata(package_info)
493
-
527
+
494
528
  return package_info
495
529
 
496
530
  async def deploy(self, package_info: PackageInfo, target: DeployTarget) -> DeployResult:
497
531
  """通过 AgentEngine Server 部署
498
-
532
+
499
533
  逻辑:
500
534
  - 读取本地 .agentengine.state 文件获取 agent_id
501
535
  - 如果有 agent_id → 执行更新 (endpoint 不变)
502
536
  - 如果没有 → 创建新 Agent,保存 agent_id 到状态文件
503
537
  """
504
-
538
+
505
539
  server_url = os.getenv("AGENTENGINE_SERVER_URL")
506
540
  click.echo(f"\n🚀 开始部署到 Serverless (Managed by {server_url})...")
507
-
541
+
508
542
  # 读取本地状态文件
509
543
  project_dir = package_info.project_dir
510
544
  state_file = Path(project_dir) / ".agentengine.state"
@@ -527,26 +561,29 @@ class ServerlessProvider(BaseDeployProvider):
527
561
  ui_state["ui_bundle_path"] = project_config.get("ui_bundle_path")
528
562
  if project_config.get("ui_bundle_dir") and not ui_state.get("ui_bundle_dir"):
529
563
  ui_state["ui_bundle_dir"] = project_config.get("ui_bundle_dir")
530
-
564
+
531
565
  # 构造请求 payload
532
566
  artifact_type = target.extra.get("artifact_type", "Code")
533
567
  artifact_path = ""
534
- if artifact_type == "Code":
568
+ code_backed = artifact_type == "Code"
569
+ if code_backed:
535
570
  original_artifact_path = package_info.metadata.get("ks3_path", "")
536
571
  artifact_path = original_artifact_path
537
-
572
+
538
573
  # 转换为内网地址 (如果 serverless 无法解析 ks3://)
539
574
  if artifact_path.startswith("ks3://"):
540
575
  try:
541
576
  from ksadk.common.constants import get_ks3_endpoints
542
-
577
+
543
578
  # 确定 KS3 Region code (pre-online -> cn-beijing)
544
579
  ks3_region = target.extra.get("ks3_region")
545
580
  if not ks3_region:
546
- ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
547
-
581
+ ks3_region = (
582
+ "cn-beijing-6" if target.region == "pre-online" else target.region
583
+ )
584
+
548
585
  _, internal_endpoint = get_ks3_endpoints(ks3_region)
549
-
586
+
550
587
  if internal_endpoint:
551
588
  # ks3://bucket/key -> http://bucket.internal_endpoint/key
552
589
  bucket, key = self._parse_code_artifact_path(artifact_path)
@@ -555,32 +592,39 @@ class ServerlessProvider(BaseDeployProvider):
555
592
  click.echo(f" Converted artifact path: {artifact_path}")
556
593
  except Exception as e:
557
594
  logger.warning(f"Failed to convert ks3 path to internal URL: {e}")
558
-
595
+
559
596
  if not artifact_path:
560
597
  raise ValueError(
561
- "❌ 未找到代码包路径 (ks3_path)。\n"
562
- " 在 Serverless 模式下,必须先上传代码包。\n"
563
- " 👉 请先执行: agentengine build --mode code --push\n"
598
+ "❌ 未找到 KS3 artifact 路径 (ks3_path)。\n"
599
+ " 在 Serverless 模式下,必须先上传构建产物。\n"
600
+ " 👉 请先执行: agentengine build --push\n"
564
601
  " 或者在 deploy 命令中使用 --ks3-path 指定路径。"
565
602
  )
566
603
 
567
- parsed_bucket, parsed_object_key = self._parse_code_artifact_path(original_artifact_path)
604
+ parsed_bucket, parsed_object_key = self._parse_code_artifact_path(
605
+ original_artifact_path
606
+ )
568
607
  if not parsed_bucket or not parsed_object_key:
569
608
  raise ValueError(
570
- "❌ ks3_path 格式无效,缺少代码包对象路径。\n"
609
+ "❌ ks3_path 格式无效,缺少 artifact 对象路径。\n"
571
610
  f" 当前值: {original_artifact_path or '(empty)'}\n"
572
611
  " 期望格式: ks3://<bucket>/<object-key>\n"
573
- " 👉 请重新执行: agentengine build --mode code --push\n"
612
+ " 👉 请重新执行: agentengine build --push\n"
574
613
  " 或者传入完整的 --ks3-path。"
575
614
  )
576
-
577
- else:
578
- artifact_path = package_info.image
579
-
615
+
616
+ elif artifact_type == "Container":
617
+ artifact_path = str(package_info.image or "").strip()
618
+ if not artifact_path:
619
+ raise ValueError("Container 部署缺少 image")
620
+ elif artifact_type != "ManagedRuntime":
621
+ raise ValueError(f"Unsupported artifact type: {artifact_type}")
622
+
580
623
  # 构建 KS3 凭证
581
624
  ks3_config = None
582
- if artifact_type == "Code":
625
+ if code_backed:
583
626
  from ksadk.common.auth import AWSV4Auth
627
+
584
628
  auth = AWSV4Auth() # 读取本地 AK/SK
585
629
  if auth.access_key_id and auth.secret_access_key:
586
630
  # 智能推断 Bucket 和 Region
@@ -589,7 +633,7 @@ class ServerlessProvider(BaseDeployProvider):
589
633
  bucket_name, _ = self._parse_code_artifact_path(
590
634
  package_info.metadata.get("ks3_path", "")
591
635
  )
592
-
636
+
593
637
  # 如果没有提供,尝试从 artifact_path 解析
594
638
  if not bucket_name:
595
639
  if artifact_path.startswith("ks3://"):
@@ -599,35 +643,38 @@ class ServerlessProvider(BaseDeployProvider):
599
643
  logger.info(f"Extracted bucket from ks3:// path: {bucket_name}")
600
644
  except IndexError:
601
645
  pass
602
- elif artifact_path.startswith("http") and "." in artifact_path:
646
+ elif artifact_path.startswith("http") and "." in artifact_path:
603
647
  try:
604
648
  # http://bucket.endpoint/key -> bucket
605
649
  from urllib.parse import urlparse
650
+
606
651
  parsed = urlparse(artifact_path)
607
652
  bucket_name = parsed.netloc.split(".")[0]
608
653
  logger.info(f"Extracted bucket from HTTP URL: {bucket_name}")
609
654
  except Exception as e:
610
655
  logger.warning(f"Failed to extract bucket from URL: {e}")
611
-
656
+
612
657
  # 如果仍然没有,使用智能默认值(与 KS3Uploader 逻辑一致)
613
658
  if not bucket_name:
614
659
  account_id = os.getenv("KSYUN_ACCOUNT_ID")
615
- upload_region = "cn-beijing-6" if target.region == "pre-online" else target.region
616
-
660
+ upload_region = (
661
+ "cn-beijing-6" if target.region == "pre-online" else target.region
662
+ )
663
+
617
664
  if not account_id:
618
665
  raise ValueError(
619
666
  "❌ 缺少 KSYUN_ACCOUNT_ID 环境变量\n"
620
667
  " Bucket 名称格式必须为: agentengine-{account_id}-{region}\n"
621
668
  " 请在 .env 文件中设置: KSYUN_ACCOUNT_ID=你的账号ID"
622
669
  )
623
-
670
+
624
671
  bucket_name = f"agentengine-{account_id}-{upload_region}"
625
672
  logger.info(f"Using default bucket: {bucket_name}")
626
-
673
+
627
674
  # Region 逻辑需与 Build 阶段保持一致 (pre-online -> cn-beijing-6)
628
675
  ks3_region = target.extra.get("ks3_region")
629
676
  if not ks3_region:
630
- ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
677
+ ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
631
678
 
632
679
  ks3_config = {
633
680
  "access_key": auth.access_key_id,
@@ -636,18 +683,96 @@ class ServerlessProvider(BaseDeployProvider):
636
683
  "bucket": bucket_name,
637
684
  }
638
685
  logger.info(f"📦 KS3 Config: bucket={bucket_name}, region={ks3_region}")
639
-
686
+
687
+ runtime_config = None
688
+ if artifact_type == "ManagedRuntime":
689
+ runtime_config = {
690
+ "name": str(target.extra.get("runtime_name") or "").strip(),
691
+ "version": str(target.extra.get("runtime_version") or "").strip(),
692
+ "manifest_sha256": str(target.extra.get("manifest_sha256") or "").strip(),
693
+ }
694
+ missing = [key for key, value in runtime_config.items() if not value]
695
+ if missing:
696
+ raise ValueError(
697
+ "ManagedRuntime 部署缺少 "
698
+ + ", ".join(f"runtime_config.{key}" for key in missing)
699
+ )
700
+
640
701
  try:
641
702
  # 获取 dry_run 标识
642
703
  is_dry_run = target.extra.get("dry_run", False)
643
704
 
644
705
  async with AgentEngineClient(region=target.region, dry_run=is_dry_run) as client:
645
706
  agent_exists = False
646
-
647
- if existing_agent_id:
707
+
708
+ # 显式 --agent-id:优先于本地 state,用于状态丢失后重新关联已有 Agent。
709
+ explicit_agent_id = (target.extra.get("agent_id") or "").strip() or None
710
+ if explicit_agent_id:
711
+ if existing_agent_id and existing_agent_id != explicit_agent_id:
712
+ click.secho(
713
+ f" ⚠️ --agent-id ({explicit_agent_id}) 与本地状态 "
714
+ f"({existing_agent_id}) 不一致,以 --agent-id 为准",
715
+ fg="yellow",
716
+ )
717
+ if is_dry_run:
718
+ # DryRun 无法真实校验,假设存在并走更新路径
719
+ existing_agent_id = explicit_agent_id
720
+ agent_exists = True
721
+ click.secho(
722
+ f" [Dry Run] 假设 Agent {explicit_agent_id} 存在", fg="cyan"
723
+ )
724
+ else:
725
+ try:
726
+ detail = await client.get_agent(
727
+ explicit_agent_id, include_api_key=True
728
+ )
729
+ except Exception as e:
730
+ return DeployResult(
731
+ status=DeployStatus.FAILED,
732
+ agent_id=explicit_agent_id,
733
+ message=(
734
+ f"❌ 指定的 Agent ID '{explicit_agent_id}' 不存在,"
735
+ f"或当前凭证无权限访问。\n"
736
+ f" 详情: {e}\n"
737
+ " 👉 请确认 agent_id 正确,且当前 AK/SK / 账号"
738
+ "有该 Agent 的权限。"
739
+ ),
740
+ )
741
+
742
+ # 校验通过 → 关联并回填 state,走热更新
743
+ qa = detail.get("quick_access", {}) or {}
744
+ basic = detail.get("basic", {}) or {}
745
+ recovered_state = local_state.copy()
746
+ recovered_state.update(
747
+ {
748
+ "agent_id": explicit_agent_id,
749
+ "name": basic.get("name") or package_info.name,
750
+ "region": target.region,
751
+ "endpoint": qa.get("public_endpoint"),
752
+ "updated_at": self._now_iso(),
753
+ }
754
+ )
755
+ if qa.get("api_key"):
756
+ recovered_state["api_key"] = qa["api_key"]
757
+ # 去掉 None 值,避免覆盖掉旧的有效字段
758
+ recovered_state = {
759
+ k: v for k, v in recovered_state.items() if v is not None
760
+ }
761
+ self._save_state(state_file, recovered_state)
762
+ local_state = recovered_state
763
+
764
+ existing_agent_id = explicit_agent_id
765
+ agent_exists = True
766
+ click.secho(
767
+ f" 🔗 已通过 --agent-id 关联 Agent: {explicit_agent_id} "
768
+ f"(已回填 .agentengine.state)",
769
+ fg="green",
770
+ )
771
+
772
+ if existing_agent_id and not agent_exists:
648
773
  # 有本地状态 → 先检查服务器上是否存在
649
774
  click.echo(f" 检测到本地状态: {existing_agent_id}")
650
-
775
+
651
776
  try:
652
777
  # 尝试获取 agent,确认是否存在
653
778
  existing_agent = await client.get_agent(existing_agent_id)
@@ -657,32 +782,37 @@ class ServerlessProvider(BaseDeployProvider):
657
782
  # Agent 不存在或查询失败
658
783
  err_msg = str(e).lower()
659
784
  if "not found" in err_msg or "404" in err_msg or "不存在" in err_msg:
660
- click.secho(f" ⚠️ 服务器上未找到 Agent {existing_agent_id},将创建新 Agent", fg="yellow")
785
+ click.secho(
786
+ f" ⚠️ 服务器上未找到 Agent {existing_agent_id},将创建新 Agent",
787
+ fg="yellow",
788
+ )
661
789
  agent_exists = False
662
- # 如果是 DryRun 抛出的异常(表明请求本来会发出去但被拦截了),我们认为 Agent 可能存在也可能不存在
663
- # 但为了安全起见,在 DryRun 模式下我们假设它存在并走更新路径,或者简单地打印日志
790
+ # DryRun 异常表示真实请求被拦截,无法确认 Agent 是否存在。
791
+ # 为安全起见,DryRun 假设它存在并走更新路径。
664
792
  elif "Dry Run" in str(e):
665
- click.secho(f" [Dry Run] 假设 Agent {existing_agent_id} 存在", fg="cyan")
666
- agent_exists = True
793
+ click.secho(
794
+ f" [Dry Run] 假设 Agent {existing_agent_id} 存在", fg="cyan"
795
+ )
796
+ agent_exists = True
667
797
  else:
668
798
  # 其他错误,重新抛出
669
799
  raise
670
-
800
+
671
801
  if agent_exists:
672
802
  # Agent 存在 → 执行更新
673
- click.echo(f" 执行热更新 (endpoint 保持不变)...")
674
-
803
+ click.echo(" 执行热更新 (endpoint 保持不变)...")
804
+
675
805
  update_data = {
676
806
  "artifact_type": artifact_type,
677
807
  "artifact_path": artifact_path,
678
808
  "resources": {
679
809
  "cpu": target.resources.cpu,
680
- "memory": target.resources.memory
810
+ "memory": target.resources.memory,
681
811
  },
682
812
  "scaling": {
683
813
  "min_replicas": target.scaling.min_replicas,
684
814
  "max_replicas": target.scaling.max_replicas,
685
- "concurrency": target.scaling.concurrency
815
+ "concurrency": target.scaling.concurrency,
686
816
  },
687
817
  "observability": {
688
818
  "langfuse_enabled": target.extra.get("enable_observability", True)
@@ -693,16 +823,20 @@ class ServerlessProvider(BaseDeployProvider):
693
823
  "url": resolved_ui.url,
694
824
  },
695
825
  }
696
-
826
+ if runtime_config:
827
+ update_data["runtime_config"] = runtime_config
828
+
697
829
  if ks3_config:
698
830
  update_data["ks3"] = ks3_config
699
831
  elif artifact_type == "Container":
700
832
  image_credential = self._image_credential_from_env(artifact_path)
701
833
  if image_credential:
702
834
  update_data["image_credential"] = image_credential
703
- click.echo(
704
- f" 🔑 镜像凭证: {image_credential['username']}@{image_credential['endpoint']}"
835
+ credential_target = (
836
+ f"{image_credential['username']}@"
837
+ f"{image_credential['endpoint']}"
705
838
  )
839
+ click.echo(f" 🔑 镜像凭证: {credential_target}")
706
840
 
707
841
  # 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
708
842
  env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
@@ -727,17 +861,19 @@ class ServerlessProvider(BaseDeployProvider):
727
861
  storage_config = self._serialize_storage_config(target)
728
862
  if storage_config:
729
863
  update_data["storage"] = storage_config
730
-
864
+
731
865
  # 注入更新时间戳,强制触发 Rolling Update (Pod 重启)
732
866
  if "env_vars" not in update_data:
733
867
  update_data["env_vars"] = {}
734
-
868
+
735
869
  import time
870
+
736
871
  update_data["env_vars"]["KSADK_UPDATED_AT"] = str(int(time.time()))
737
-
872
+
738
873
  # DEBUG: 打印 Payload 确认 trigger 是否存在
739
- click.echo(f" 🔄 更新 Trigger: KSADK_UPDATED_AT={update_data['env_vars']['KSADK_UPDATED_AT']}")
740
-
874
+ updated_at_value = update_data["env_vars"]["KSADK_UPDATED_AT"]
875
+ click.echo(f" 🔄 更新 Trigger: KSADK_UPDATED_AT={updated_at_value}")
876
+
741
877
  res = await client.update_agent(existing_agent_id, update_data)
742
878
  latest_access = {}
743
879
  if not is_dry_run:
@@ -751,38 +887,40 @@ class ServerlessProvider(BaseDeployProvider):
751
887
  exc,
752
888
  ),
753
889
  )
754
-
890
+
755
891
  # 如果是 Dry Run,手动构造假响应以避免崩溃
756
892
  if is_dry_run and not res:
757
893
  res = {"name": package_info.name, "endpoint": "http://dry-run-endpoint"}
758
-
894
+
759
895
  # 更新本地状态 (保留旧字段如 api_key)
760
896
  new_state = local_state.copy()
761
- new_state.update({
762
- "agent_id": latest_access.get("agent_id") or existing_agent_id,
763
- "name": latest_access.get("name") or res.get("name"),
764
- "region": target.region,
765
- "endpoint": latest_access.get("endpoint") or res.get("endpoint"),
766
- "updated_at": self._now_iso(),
767
- **ui_state,
768
- })
897
+ new_state.update(
898
+ {
899
+ "agent_id": latest_access.get("agent_id") or existing_agent_id,
900
+ "name": latest_access.get("name") or res.get("name"),
901
+ "region": target.region,
902
+ "endpoint": latest_access.get("endpoint") or res.get("endpoint"),
903
+ "updated_at": self._now_iso(),
904
+ **ui_state,
905
+ }
906
+ )
769
907
  if latest_access.get("api_key"):
770
908
  new_state["api_key"] = latest_access["api_key"]
771
909
  self._save_state(state_file, new_state)
772
-
910
+
773
911
  return DeployResult(
774
- status=DeployStatus.DEPLOYING,
912
+ status=DeployStatus.DEPLOYING,
775
913
  agent_id=existing_agent_id,
776
914
  agent_name=res.get("name"),
777
915
  endpoint=res.get("endpoint"),
778
- message=f"✅ Agent 已更新: {existing_agent_id}"
916
+ message=f"✅ Agent 已更新: {existing_agent_id}",
779
917
  )
780
918
  # else: agent_exists = False,继续执行创建逻辑
781
-
919
+
782
920
  # 没有本地状态,或本地状态对应的 Agent 在服务器上不存在 → 创建新 Agent
783
921
  if not existing_agent_id or not agent_exists:
784
922
  click.echo(f" 创建新 Agent: {package_info.name}")
785
-
923
+
786
924
  request_data = {
787
925
  "name": package_info.name,
788
926
  "framework": package_info.framework,
@@ -792,12 +930,12 @@ class ServerlessProvider(BaseDeployProvider):
792
930
  "instance_id": target.extra.get("instance_id", "default"),
793
931
  "resources": {
794
932
  "cpu": target.resources.cpu,
795
- "memory": target.resources.memory
933
+ "memory": target.resources.memory,
796
934
  },
797
935
  "scaling": {
798
936
  "min_replicas": target.scaling.min_replicas,
799
937
  "max_replicas": target.scaling.max_replicas,
800
- "concurrency": target.scaling.concurrency
938
+ "concurrency": target.scaling.concurrency,
801
939
  },
802
940
  "observability": {
803
941
  "langfuse_enabled": target.extra.get("enable_observability", True)
@@ -808,7 +946,9 @@ class ServerlessProvider(BaseDeployProvider):
808
946
  "url": resolved_ui.url,
809
947
  },
810
948
  }
811
-
949
+ if runtime_config:
950
+ request_data["runtime_config"] = runtime_config
951
+
812
952
  if ks3_config:
813
953
  request_data["ks3"] = ks3_config
814
954
 
@@ -817,9 +957,10 @@ class ServerlessProvider(BaseDeployProvider):
817
957
  image_credential = self._image_credential_from_env(artifact_path)
818
958
  if image_credential:
819
959
  request_data["image_credential"] = image_credential
820
- click.echo(
821
- f" 🔑 镜像凭证: {image_credential['username']}@{image_credential['endpoint']}"
960
+ credential_target = (
961
+ f"{image_credential['username']}@" f"{image_credential['endpoint']}"
822
962
  )
963
+ click.echo(f" 🔑 镜像凭证: {credential_target}")
823
964
 
824
965
  # 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
825
966
  env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
@@ -837,7 +978,7 @@ class ServerlessProvider(BaseDeployProvider):
837
978
  click.echo(f" 📦 加载环境变量: {len(env_vars)} 项 from 全局配置")
838
979
 
839
980
  if env_vars:
840
- request_data["env_vars"] = env_vars
981
+ request_data["env_vars"] = env_vars
841
982
 
842
983
  network_config = self._serialize_network_config(target, is_update=False)
843
984
  if network_config:
@@ -852,20 +993,22 @@ class ServerlessProvider(BaseDeployProvider):
852
993
  ksyun_account_id = os.getenv("KSYUN_ACCOUNT_ID")
853
994
  if ksyun_account_id:
854
995
  extra_headers["X-Ksc-Account-Id"] = ksyun_account_id
855
-
996
+
856
997
  # 重新构造一个带 Header 的 client
857
- async with AgentEngineClient(region=target.region, extra_headers=extra_headers, dry_run=is_dry_run) as new_client:
998
+ async with AgentEngineClient(
999
+ region=target.region, extra_headers=extra_headers, dry_run=is_dry_run
1000
+ ) as new_client:
858
1001
  res = await new_client.create_agent(request_data)
859
-
1002
+
860
1003
  # 如果是 Dry Run,手动构造假响应
861
1004
  if is_dry_run and not res:
862
1005
  res = {
863
- "agent_id": "dry-run-agent-id",
864
- "name": package_info.name,
865
- "endpoint": "http://dry-run-endpoint",
866
- "api_key": "dry-run-key"
1006
+ "agent_id": "dry-run-agent-id",
1007
+ "name": package_info.name,
1008
+ "endpoint": "http://dry-run-endpoint",
1009
+ "api_key": "dry-run-key",
867
1010
  }
868
-
1011
+
869
1012
  # CreateAgentProduct 返回 order_id,需要轮询 list_agents 获取 agent_id
870
1013
  new_agent_id = res.get("agent_id")
871
1014
  order_id = res.get("order_id")
@@ -876,10 +1019,13 @@ class ServerlessProvider(BaseDeployProvider):
876
1019
  if order_id and not new_agent_id:
877
1020
  click.echo(f" 📋 订单已创建: {order_id},等待实例创建...")
878
1021
  import time
1022
+
879
1023
  for i in range(12): # 最多等 60s
880
1024
  time.sleep(5)
881
1025
  try:
882
- detail = await client.get_agent(name=package_info.name, include_api_key=True)
1026
+ detail = await client.get_agent(
1027
+ name=package_info.name, include_api_key=True
1028
+ )
883
1029
  qa = detail.get("quick_access", {})
884
1030
  basic = detail.get("basic", {})
885
1031
  new_agent_id = basic.get("agent_id")
@@ -894,7 +1040,10 @@ class ServerlessProvider(BaseDeployProvider):
894
1040
  click.echo(f" ⏳ 等待中... ({(i+1)*5}s)")
895
1041
 
896
1042
  if not new_agent_id:
897
- click.secho(" ⚠️ 实例仍在创建中,稍后使用 'agentengine status' 查看", fg="yellow")
1043
+ click.secho(
1044
+ " ⚠️ 实例仍在创建中,稍后使用 'agentengine status' 查看",
1045
+ fg="yellow",
1046
+ )
898
1047
 
899
1048
  latest_access = {}
900
1049
  if new_agent_id and not is_dry_run:
@@ -913,54 +1062,63 @@ class ServerlessProvider(BaseDeployProvider):
913
1062
  agent_name = latest_access.get("name") or agent_name
914
1063
  agent_endpoint = latest_access.get("endpoint") or agent_endpoint
915
1064
  agent_api_key = latest_access.get("api_key") or agent_api_key
916
-
1065
+
917
1066
  # 保存 agent_id 到本地状态文件
918
- self._save_state(state_file, {
919
- "agent_id": new_agent_id,
920
- "name": agent_name,
921
- "region": target.region,
922
- "endpoint": agent_endpoint,
923
- "api_key": agent_api_key, # 只在首次保存
924
- "order_id": order_id,
925
- "created_at": self._now_iso(),
926
- **ui_state,
927
- })
928
-
929
- click.echo(f" 💾 已保存状态到 .agentengine.state")
930
-
1067
+ self._save_state(
1068
+ state_file,
1069
+ {
1070
+ "agent_id": new_agent_id,
1071
+ "name": agent_name,
1072
+ "region": target.region,
1073
+ "endpoint": agent_endpoint,
1074
+ "api_key": agent_api_key, # 只在首次保存
1075
+ "order_id": order_id,
1076
+ "created_at": self._now_iso(),
1077
+ **ui_state,
1078
+ },
1079
+ )
1080
+
1081
+ click.echo(" 💾 已保存状态到 .agentengine.state")
1082
+
931
1083
  return DeployResult(
932
- status=DeployStatus.DEPLOYING,
1084
+ status=DeployStatus.DEPLOYING,
933
1085
  agent_id=new_agent_id,
934
1086
  agent_name=agent_name,
935
- endpoint=agent_endpoint,
1087
+ endpoint=agent_endpoint,
936
1088
  api_key=agent_api_key,
937
- message=f"✅ Agent ID: {new_agent_id or '(创建中)'} (首次部署, 订单: {order_id or '-'})"
1089
+ message=(
1090
+ f"✅ Agent ID: {new_agent_id or '(创建中)'} "
1091
+ f"(首次部署, 订单: {order_id or '-'})"
1092
+ ),
938
1093
  )
939
1094
 
1095
+ return DeployResult(
1096
+ status=DeployStatus.FAILED,
1097
+ message="部署流程未返回 Agent 状态",
1098
+ )
1099
+
940
1100
  except DryRunExit as e:
941
1101
  return DeployResult(
942
1102
  status=DeployStatus.SKIPPED,
943
1103
  message="✅ Dry Run Completed: 请求已打印,未执行实际变更。",
944
1104
  metadata={"dry_run_request": e.payload or {}},
945
1105
  )
946
-
1106
+
947
1107
  except Exception as e:
948
1108
  logger.error(f"Deploy failed: {e}")
949
-
1109
+
950
1110
  # 检测名称冲突
951
1111
  err_msg = str(e)
952
1112
  if "Conflict" in err_msg or "409" in err_msg or "already exists" in err_msg:
953
1113
  return DeployResult(
954
1114
  status=DeployStatus.FAILED,
955
1115
  message=f"❌ 部署失败: Agent 名称 '{package_info.name}' 已存在。\n"
956
- f" 提示: 请检查是否重复创建。\n"
957
- f" 👉 解决方法: 请在 agentengine.yaml 中修改 'name' 字段 (如添加后缀) 后重试。"
1116
+ f" 提示: 请检查是否重复创建。\n"
1117
+ " 👉 解决方法: 请在 agentengine.yaml 中修改 'name' 字段 "
1118
+ "(如添加后缀) 后重试。",
958
1119
  )
959
-
960
- return DeployResult(
961
- status=DeployStatus.FAILED,
962
- message=f"Server 请求失败: {str(e)}"
963
- )
1120
+
1121
+ return DeployResult(status=DeployStatus.FAILED, message=f"Server 请求失败: {str(e)}")
964
1122
 
965
1123
  async def get_status(self, agent_id: str, target: DeployTarget) -> DeployResult:
966
1124
  """获取 Agent 状态"""
@@ -968,7 +1126,7 @@ class ServerlessProvider(BaseDeployProvider):
968
1126
  try:
969
1127
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
970
1128
  res = await client.get_agent(agent_id)
971
-
1129
+
972
1130
  status_map = {
973
1131
  "running": DeployStatus.RUNNING,
974
1132
  "ready": DeployStatus.RUNNING,
@@ -978,9 +1136,9 @@ class ServerlessProvider(BaseDeployProvider):
978
1136
  "scaling": DeployStatus.UPDATING,
979
1137
  "failed": DeployStatus.FAILED,
980
1138
  "error": DeployStatus.FAILED,
981
- "unknown": DeployStatus.UNKNOWN
1139
+ "unknown": DeployStatus.UNKNOWN,
982
1140
  }
983
-
1141
+
984
1142
  # 使状态匹配不区分大小写,以兼容服务端的改动 (Creating -> CREATING)
985
1143
  current_status = (res.get("status") or "").lower()
986
1144
  return DeployResult(
@@ -988,21 +1146,20 @@ class ServerlessProvider(BaseDeployProvider):
988
1146
  agent_id=res.get("agent_id"),
989
1147
  agent_name=res.get("name"),
990
1148
  endpoint=res.get("endpoint"),
991
- message=f"Status: {res.get('status')} ({res.get('phase')})"
1149
+ message=f"Status: {res.get('status')} ({res.get('phase')})",
992
1150
  )
993
1151
  except DryRunExit:
994
1152
  return DeployResult(status=DeployStatus.SKIPPED, message="Dry Run executed.")
995
1153
  except Exception as e:
996
- return DeployResult(
997
- status=DeployStatus.UNKNOWN,
998
- message=f"查询失败: {e}"
999
- )
1154
+ return DeployResult(status=DeployStatus.UNKNOWN, message=f"查询失败: {e}")
1000
1155
 
1001
1156
  async def destroy(self, agent_id: str, target: DeployTarget) -> bool:
1002
1157
  """销毁 Agent"""
1003
1158
  dry_run = target.extra.get("dry_run", False)
1004
1159
  project_dir = str(target.extra.get("project_dir") or "").strip()
1005
- state_file = (Path(project_dir).resolve() if project_dir else Path(".").resolve()) / ".agentengine.state"
1160
+ state_file = (
1161
+ Path(project_dir).resolve() if project_dir else Path(".").resolve()
1162
+ ) / ".agentengine.state"
1006
1163
 
1007
1164
  try:
1008
1165
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
@@ -1015,8 +1172,10 @@ class ServerlessProvider(BaseDeployProvider):
1015
1172
  os.remove(state_file)
1016
1173
  logger.info(f"Deleted local state file: {state_file}")
1017
1174
  except Exception as cleanup_error:
1018
- logger.warning(f"Failed to delete local state file {state_file}: {cleanup_error}")
1019
- return success
1175
+ logger.warning(
1176
+ f"Failed to delete local state file {state_file}: {cleanup_error}"
1177
+ )
1178
+ return bool(success)
1020
1179
  except DryRunExit:
1021
1180
  # 让异常冒泡给 CLI 处理
1022
1181
  raise
@@ -1030,19 +1189,27 @@ class ServerlessProvider(BaseDeployProvider):
1030
1189
  try:
1031
1190
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
1032
1191
  res = await client.list_agents()
1033
-
1192
+
1034
1193
  results = []
1035
1194
  for agent in res.get("Agents", []):
1036
1195
  current_status = (agent.get("status") or "").lower()
1037
- results.append(DeployResult(
1038
- status=DeployStatus.RUNNING if current_status in ("running", "ready") else (
1039
- DeployStatus.DEPLOYING if current_status == "creating" else DeployStatus.UNKNOWN
1040
- ),
1041
- agent_id=agent.get("agent_id"),
1042
- agent_name=agent.get("name"),
1043
- endpoint=agent.get("endpoint"),
1044
- message=agent.get("status")
1045
- ))
1196
+ results.append(
1197
+ DeployResult(
1198
+ status=(
1199
+ DeployStatus.RUNNING
1200
+ if current_status in ("running", "ready")
1201
+ else (
1202
+ DeployStatus.DEPLOYING
1203
+ if current_status == "creating"
1204
+ else DeployStatus.UNKNOWN
1205
+ )
1206
+ ),
1207
+ agent_id=agent.get("agent_id"),
1208
+ agent_name=agent.get("name"),
1209
+ endpoint=agent.get("endpoint"),
1210
+ message=agent.get("status"),
1211
+ )
1212
+ )
1046
1213
  return results
1047
1214
  except DryRunExit:
1048
1215
  raise
@@ -1056,7 +1223,7 @@ class ServerlessProvider(BaseDeployProvider):
1056
1223
  try:
1057
1224
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
1058
1225
  response = await client.chat(agent_id, message, stream=False)
1059
- return response.get("output", "")
1226
+ return str(response.get("output", ""))
1060
1227
  except DryRunExit:
1061
1228
  raise
1062
1229
  except Exception as e:
@@ -1066,7 +1233,7 @@ class ServerlessProvider(BaseDeployProvider):
1066
1233
  """读取本地状态文件"""
1067
1234
  if state_file.exists():
1068
1235
  try:
1069
- with open(state_file, 'r', encoding='utf-8') as f:
1236
+ with open(state_file, "r", encoding="utf-8") as f:
1070
1237
  return yaml.safe_load(f) or {}
1071
1238
  except Exception as e:
1072
1239
  logger.warning(f"Failed to load state file: {e}")
@@ -1102,8 +1269,9 @@ class ServerlessProvider(BaseDeployProvider):
1102
1269
  def _save_state(self, state_file: Path, state: Dict[str, Any]) -> None:
1103
1270
  """保存状态到本地文件"""
1104
1271
  import yaml
1272
+
1105
1273
  try:
1106
- with open(state_file, 'w', encoding='utf-8') as f:
1274
+ with open(state_file, "w", encoding="utf-8") as f:
1107
1275
  yaml.dump(state, f, default_flow_style=False, allow_unicode=True)
1108
1276
  logger.debug(f"State saved to {state_file}")
1109
1277
  except Exception as e:
@@ -1112,4 +1280,5 @@ class ServerlessProvider(BaseDeployProvider):
1112
1280
  def _now_iso(self) -> str:
1113
1281
  """返回当前时间 ISO 格式"""
1114
1282
  from datetime import datetime
1283
+
1115
1284
  return datetime.now().isoformat()