agentengine-sdk-python 0.7.0__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.
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
- agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
- ksadk/a2a/__init__.py +178 -11
- ksadk/a2a/bootstrap.py +334 -0
- ksadk/a2a/card.py +120 -0
- ksadk/a2a/context_store.py +203 -0
- ksadk/a2a/control_plane.py +818 -0
- ksadk/a2a/credential.py +99 -0
- ksadk/a2a/event_adapter.py +149 -0
- ksadk/a2a/executor.py +406 -89
- ksadk/a2a/external_transport.py +516 -0
- ksadk/a2a/identity.py +304 -0
- ksadk/a2a/ids.py +22 -0
- ksadk/a2a/langgraph.py +285 -0
- ksadk/a2a/resume_store.py +201 -0
- ksadk/a2a/routes.py +105 -0
- ksadk/a2a/server.py +107 -60
- ksadk/a2a/space_client.py +1101 -0
- ksadk/a2a/task_adapter.py +423 -0
- ksadk/a2a/task_event_dispatcher.py +167 -0
- ksadk/a2a/task_event_outbox.py +326 -0
- ksadk/a2a/task_store.py +206 -0
- ksadk/a2ui/__init__.py +27 -0
- ksadk/a2ui/core.py +186 -0
- ksadk/a2ui/models.py +166 -0
- ksadk/a2ui/renderer.py +198 -0
- ksadk/agui/__init__.py +9 -0
- ksadk/agui/a2ui_projection.py +92 -0
- ksadk/agui/agent.py +857 -0
- ksadk/agui/config.py +86 -0
- ksadk/agui/routes.py +55 -0
- ksadk/api/client.py +233 -191
- ksadk/builders/__init__.py +2 -0
- ksadk/builders/base.py +16 -14
- ksadk/builders/code_builder.py +318 -236
- ksadk/builders/container_builder.py +187 -167
- ksadk/builders/framework_requirements.py +29 -11
- ksadk/builders/ks3_uploader.py +58 -27
- ksadk/builders/managed_runtime_builder.py +145 -0
- ksadk/builders/mcp_builder.py +91 -92
- ksadk/builders/requirements_utils.py +1 -5
- ksadk/cli/__init__.py +89 -171
- ksadk/cli/cmd_a2a.py +254 -70
- ksadk/cli/cmd_agent.py +19 -5
- ksadk/cli/cmd_build.py +158 -29
- ksadk/cli/cmd_completion.py +47 -35
- ksadk/cli/cmd_config.py +267 -198
- ksadk/cli/cmd_create.py +500 -720
- ksadk/cli/cmd_dashboard.py +120 -45
- ksadk/cli/cmd_deploy.py +117 -58
- ksadk/cli/cmd_destroy.py +44 -14
- ksadk/cli/cmd_files.py +63 -24
- ksadk/cli/cmd_hermes.py +168 -89
- ksadk/cli/cmd_invoke.py +184 -122
- ksadk/cli/cmd_launch.py +64 -28
- ksadk/cli/cmd_mcp.py +98 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +483 -227
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +33 -10
- ksadk/cli/cmd_status.py +80 -31
- ksadk/cli/cmd_version.py +20 -13
- ksadk/cli/cmd_web.py +89 -13
- ksadk/cli/deploy_utils.py +20 -20
- ksadk/cli/dry_run.py +5 -3
- ksadk/cli/error_utils.py +37 -12
- ksadk/cli/global_options.py +1 -2
- ksadk/cli/local_runtime.py +1 -2
- ksadk/cli/model_catalog.py +6 -2
- ksadk/cli/network_options.py +34 -14
- ksadk/cli/resource_common.py +6 -2
- ksadk/cli/storage.py +0 -1
- ksadk/cli/ui.py +5 -3
- ksadk/cli/workflow_common.py +51 -17
- ksadk/codex/__init__.py +12 -0
- ksadk/codex/client.py +408 -0
- ksadk/codex/phase.py +143 -0
- ksadk/codex/runtime.py +449 -0
- ksadk/common/aicp_env.py +0 -1
- ksadk/common/auth.py +2 -2
- ksadk/common/constants.py +1 -3
- ksadk/common/llm_utils.py +17 -13
- ksadk/compat/__init__.py +5 -0
- ksadk/compat/adk_compat.py +200 -0
- ksadk/compat/copilotkit_a2ui.py +330 -0
- ksadk/configs/__init__.py +12 -12
- ksadk/configs/env_registry.py +500 -100
- ksadk/configs/global_config.py +2 -1
- ksadk/configs/settings.py +151 -124
- ksadk/conversations/__init__.py +7 -5
- ksadk/conversations/attachment_storage.py +16 -11
- ksadk/conversations/attachments.py +44 -36
- ksadk/conversations/compaction_pipeline.py +12 -12
- ksadk/conversations/compaction_prompt.py +9 -5
- ksadk/conversations/context.py +221 -6
- ksadk/conversations/message_projection.py +438 -51
- ksadk/conversations/model_context.py +12 -6
- ksadk/conversations/model_options.py +0 -1
- ksadk/conversations/normalize.py +20 -16
- ksadk/conversations/reasoning_markup.py +1 -1
- ksadk/conversations/runtime.py +339 -5255
- ksadk/conversations/runtime_compaction.py +333 -0
- ksadk/conversations/runtime_constants.py +27 -0
- ksadk/conversations/runtime_governance.py +185 -0
- ksadk/conversations/runtime_input.py +652 -0
- ksadk/conversations/runtime_invocation.py +395 -0
- ksadk/conversations/runtime_metadata.py +475 -0
- ksadk/conversations/runtime_observability.py +355 -0
- ksadk/conversations/runtime_payloads.py +272 -0
- ksadk/conversations/runtime_persistence.py +440 -0
- ksadk/conversations/runtime_preparation.py +418 -0
- ksadk/conversations/runtime_resume.py +816 -0
- ksadk/conversations/runtime_stream_events.py +902 -0
- ksadk/conversations/runtime_streaming.py +547 -0
- ksadk/conversations/semantic_summary.py +17 -5
- ksadk/conversations/session_title.py +6 -3
- ksadk/deployment/__init__.py +15 -16
- ksadk/deployment/agent_access.py +12 -23
- ksadk/deployment/base.py +51 -38
- ksadk/deployment/manager.py +63 -65
- ksadk/deployment/providers/serverless.py +309 -202
- ksadk/deployment/registry.py +38 -32
- ksadk/deployment/state.py +11 -11
- ksadk/deployment/ui_config.py +1 -1
- ksadk/detection/__init__.py +1 -1
- ksadk/detection/detector.py +146 -83
- ksadk/detection/mcp_detector.py +9 -6
- ksadk/events/__init__.py +25 -0
- ksadk/events/parser.py +191 -0
- ksadk/events/replay.py +37 -0
- ksadk/events/runtime_event.py +264 -0
- ksadk/events/store.py +317 -0
- ksadk/harness/__init__.py +34 -0
- ksadk/harness/app.py +278 -0
- ksadk/harness/config.py +181 -0
- ksadk/harness/reasoner.py +105 -0
- ksadk/harness/runner.py +211 -0
- ksadk/harness/sandbox.py +141 -0
- ksadk/harness/tools.py +222 -0
- ksadk/hermes_terminal.py +23 -12
- ksadk/identity/resolver.py +15 -9
- ksadk/ids.py +32 -0
- ksadk/knowledge_base/adk_tool.py +1 -1
- ksadk/knowledge_base/client.py +23 -29
- ksadk/managed_runtime.py +188 -0
- ksadk/markdown.py +5 -2
- ksadk/mcp_runtime/__init__.py +9 -6
- ksadk/memory/__init__.py +1 -1
- ksadk/memory/adk/__init__.py +2 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
- ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
- ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
- ksadk/memory/adk/long_term_memory.py +31 -35
- ksadk/memory/adk/resilient_session_service.py +4 -5
- ksadk/memory/adk/short_term_memory.py +12 -21
- ksadk/memory/adk_tool.py +3 -2
- ksadk/memory/backends/base.py +19 -19
- ksadk/memory/backends/memory.py +24 -25
- ksadk/memory/backends/redis.py +31 -30
- ksadk/memory/langchain_tool.py +2 -1
- ksadk/memory/manager.py +52 -47
- ksadk/memory/service.py +10 -8
- ksadk/model_policy.py +15 -7
- ksadk/model_proxy/__init__.py +51 -0
- ksadk/model_proxy/bootstrap.py +80 -0
- ksadk/model_proxy/cache.py +165 -0
- ksadk/model_proxy/config.py +45 -0
- ksadk/model_proxy/demo_webui.py +115 -0
- ksadk/model_proxy/detect.py +145 -0
- ksadk/model_proxy/gate.py +68 -0
- ksadk/model_proxy/namespace.py +173 -0
- ksadk/model_proxy/protocol_proxy.py +22 -0
- ksadk/model_proxy/server.py +300 -0
- ksadk/model_proxy/transform.py +691 -0
- ksadk/openclaw_gateway.py +29 -14
- ksadk/runners/adk_runner.py +345 -120
- ksadk/runners/base_runner.py +46 -29
- ksadk/runners/codex_runner.py +132 -0
- ksadk/runners/deepagents_runner.py +0 -1
- ksadk/runners/factory.py +17 -5
- ksadk/runners/langchain_runner.py +29 -742
- ksadk/runners/langgraph_runner.py +352 -90
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +134 -33
- ksadk/runners/usage_accumulator.py +1 -0
- ksadk/runners/utils/langfuse.py +3 -3
- ksadk/runners/utils/loader.py +7 -7
- ksadk/runtime/__init__.py +41 -0
- ksadk/runtime/adapter.py +345 -0
- ksadk/runtime/framework_adapters.py +124 -0
- ksadk/runtime/preprocessing.py +118 -0
- ksadk/runtime/runner_adapter.py +873 -0
- ksadk/runtime_context.py +18 -3
- ksadk/runtime_state.py +0 -1
- ksadk/sandbox/__init__.py +4 -1
- ksadk/sandbox/backends/__init__.py +4 -1
- ksadk/sandbox/backends/e2b.py +13 -5
- ksadk/sandbox/backends/local_process.py +11 -3
- ksadk/sandbox/base.py +10 -15
- ksadk/sandbox/factory.py +14 -4
- ksadk/sandbox/registry.py +57 -6
- ksadk/server/api_models.py +15 -5
- ksadk/server/app.py +455 -3885
- ksadk/server/composition.py +21 -0
- ksadk/server/factory.py +624 -0
- ksadk/server/routes/__init__.py +1 -0
- ksadk/server/routes/checkpoint_resolution.py +116 -0
- ksadk/server/routes/common.py +713 -0
- ksadk/server/routes/control.py +327 -0
- ksadk/server/routes/dependencies.py +98 -0
- ksadk/server/routes/misc.py +448 -0
- ksadk/server/routes/models.py +443 -0
- ksadk/server/routes/openai_compat.py +207 -0
- ksadk/server/routes/projection.py +795 -0
- ksadk/server/routes/routers.py +52 -0
- ksadk/server/routes/run.py +726 -0
- ksadk/server/routes/sessions.py +649 -0
- ksadk/server/routes/streaming.py +223 -0
- ksadk/server/routes/workspace.py +381 -0
- ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
- ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
- ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
- ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
- ksadk/server/static/assets/{arc-BA13sHc9.js → arc-CFKwSk_B.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
- ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
- ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
- ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
- ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
- ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
- ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
- ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
- ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
- ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
- ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
- ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
- ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
- ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
- ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
- ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
- ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
- ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
- ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
- ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
- ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
- ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
- ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
- ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
- ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
- ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
- ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
- ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
- ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/server/terminal_sessions.py +111 -58
- ksadk/sessions/__init__.py +69 -19
- ksadk/sessions/base.py +31 -11
- ksadk/sessions/continuity.py +2 -3
- ksadk/sessions/in_memory.py +46 -2
- ksadk/sessions/local_service.py +148 -17
- ksadk/sessions/postgres_service.py +143 -25
- ksadk/sessions/resilient.py +36 -8
- ksadk/skills/models.py +17 -5
- ksadk/skills/package_store.py +5 -2
- ksadk/skills/runtime/agent.py +20 -5
- ksadk/skills/runtime/artifacts.py +1 -5
- ksadk/skills/runtime/backends/e2b.py +10 -4
- ksadk/skills/runtime/backends/local.py +15 -3
- ksadk/skills/runtime/base.py +1 -2
- ksadk/skills/runtime/executor.py +10 -3
- ksadk/skills/runtime/factory.py +4 -2
- ksadk/skills/runtime/loader.py +1 -2
- ksadk/skills/runtime/registry.py +15 -10
- ksadk/skills/runtime/request.py +3 -1
- ksadk/skills/service_client.py +38 -17
- ksadk/skills/tool_defs.py +15 -10
- ksadk/terminal_exec_policy.py +3 -4
- ksadk/tools/gateway.py +92 -15
- ksadk/tools/result_budget.py +12 -4
- ksadk/toolsets/__init__.py +134 -36
- ksadk/toolsets/platform.py +21 -5
- ksadk/toolsets/sandbox.py +82 -16
- ksadk/toolsets/skills.py +200 -39
- ksadk/toolsets/web.py +123 -26
- ksadk/toolsets/workspace.py +212 -101
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/exporters/langfuse_exporter.py +91 -76
- ksadk/tracing/setup.py +69 -53
- ksadk/tracing/span_utils.py +5 -2
- ksadk/tui/loop.py +78 -30
- ksadk/tui/stream_render.py +1 -0
- ksadk/ui_config.py +5 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
- agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
- ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
- ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
- ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
- ksadk/server/static/assets/channel-CTuydARX.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
- ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
- ksadk/server/static/assets/index-DAURbCsP.css +0 -1
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
|
@@ -6,38 +6,37 @@ 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
|
|
11
|
+
import os
|
|
13
12
|
import time
|
|
14
|
-
import yaml
|
|
15
13
|
from pathlib import Path
|
|
16
|
-
from typing import
|
|
14
|
+
from typing import Any, Dict, List, Optional
|
|
15
|
+
|
|
17
16
|
import click
|
|
17
|
+
import yaml
|
|
18
18
|
|
|
19
|
-
from ksadk.
|
|
19
|
+
from ksadk.api import AgentEngineClient, DryRunExit
|
|
20
20
|
from ksadk.builders.code_builder import CodeBuilder
|
|
21
|
+
from ksadk.builders.container_builder import (
|
|
22
|
+
ContainerBuilder,
|
|
23
|
+
registry_kind_label,
|
|
24
|
+
resolve_registry_credentials,
|
|
25
|
+
)
|
|
26
|
+
from ksadk.builders.ks3_uploader import KS3Uploader
|
|
27
|
+
from ksadk.configs.env_registry import ENV_VAR_REGISTRY
|
|
28
|
+
from ksadk.configs.global_config import get_env_from_global_config
|
|
29
|
+
from ksadk.configs.settings import DEFAULT_RUNTIME_TIMEZONE
|
|
21
30
|
from ksadk.deployment.agent_access import get_latest_agent_access
|
|
22
31
|
from ksadk.deployment.base import (
|
|
23
32
|
BaseDeployProvider,
|
|
24
|
-
DeployTarget,
|
|
25
33
|
DeployResult,
|
|
26
34
|
DeployStatus,
|
|
35
|
+
DeployTarget,
|
|
27
36
|
PackageInfo,
|
|
28
37
|
)
|
|
29
38
|
from ksadk.deployment.registry import DeployProviderRegistry
|
|
30
39
|
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
40
|
|
|
42
41
|
logger = logging.getLogger(__name__)
|
|
43
42
|
|
|
@@ -71,11 +70,7 @@ _DEPLOY_PROCESS_ENV_ALLOWLIST = frozenset(
|
|
|
71
70
|
)
|
|
72
71
|
_DEPLOY_PROCESS_ENV_PREFIXES = ("KSADK_", "OPENAI_", "KSYUN_", "E2B_")
|
|
73
72
|
_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
|
-
}
|
|
73
|
+
{spec.name for spec in ENV_VAR_REGISTRY if spec.module in {"builders", "cli", "configs", "web"}}
|
|
79
74
|
) | frozenset(
|
|
80
75
|
{
|
|
81
76
|
"KSADK_GLOBAL_CONFIG_ENV_KEYS",
|
|
@@ -96,7 +91,7 @@ def _should_forward_process_env(name: str) -> bool:
|
|
|
96
91
|
@DeployProviderRegistry.register("kce")
|
|
97
92
|
class ServerlessProvider(BaseDeployProvider):
|
|
98
93
|
"""金山云 Serverless 计算引擎 (AgentEngine Server 托管)
|
|
99
|
-
|
|
94
|
+
|
|
100
95
|
重构后直接继承 BaseDeployProvider,不再依赖 DockerProvider。
|
|
101
96
|
Container 模式使用 ContainerBuilder 进行打包和构建。
|
|
102
97
|
"""
|
|
@@ -109,7 +104,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
109
104
|
supports_scaling = True
|
|
110
105
|
requires_image_registry = False
|
|
111
106
|
|
|
112
|
-
def __init__(self, config: Dict[str, Any] = None):
|
|
107
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
113
108
|
self.config = config or {}
|
|
114
109
|
|
|
115
110
|
@staticmethod
|
|
@@ -124,13 +119,15 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
124
119
|
|
|
125
120
|
if password and not username and registry_kind != "personal_kcr":
|
|
126
121
|
click.secho(
|
|
127
|
-
f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME
|
|
122
|
+
f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,"
|
|
123
|
+
f"已忽略{registry_kind_label(registry_kind)}镜像凭证;"
|
|
128
124
|
"企业版 KCR 和第三方镜像仓库必须配置 KCR_USERNAME + KCR_PASSWORD",
|
|
129
125
|
fg="yellow",
|
|
130
126
|
)
|
|
131
127
|
elif registry_kind == "personal_kcr":
|
|
132
128
|
click.secho(
|
|
133
|
-
" ⚠️ 未配置个人版 KCR 镜像凭证
|
|
129
|
+
" ⚠️ 未配置个人版 KCR 镜像凭证 "
|
|
130
|
+
"(KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取",
|
|
134
131
|
fg="yellow",
|
|
135
132
|
)
|
|
136
133
|
else:
|
|
@@ -269,7 +266,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
269
266
|
return None
|
|
270
267
|
|
|
271
268
|
public_access = getattr(network, "enable_public_access", None)
|
|
272
|
-
# 三态:None=未指定。create 默认开公网;update 未指定则不发 network
|
|
269
|
+
# 三态:None=未指定。create 默认开公网;update 未指定则不发 network 字段。
|
|
273
270
|
if public_access is None:
|
|
274
271
|
public_value: Optional[bool] = None if is_update else True
|
|
275
272
|
else:
|
|
@@ -317,19 +314,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
317
314
|
seconds = int(elapsed % 60)
|
|
318
315
|
return f"{minutes}m{seconds:02d}s"
|
|
319
316
|
|
|
320
|
-
|
|
321
317
|
async def validate_config(self, target: DeployTarget) -> tuple[bool, str]:
|
|
322
318
|
"""验证配置: 确保已配置 AgentEngine Server"""
|
|
323
|
-
|
|
319
|
+
|
|
324
320
|
server_url = os.getenv("AGENTENGINE_SERVER_URL")
|
|
325
|
-
|
|
321
|
+
|
|
326
322
|
if not server_url:
|
|
327
323
|
click.echo("⚠️ 未配置 AGENTENGINE_SERVER_URL,将尝试使用默认 Region 配置")
|
|
328
|
-
|
|
324
|
+
|
|
329
325
|
# Container 模式: 检查 Docker 是否可用
|
|
330
326
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
331
327
|
if artifact_type == "Container":
|
|
332
328
|
import subprocess
|
|
329
|
+
|
|
333
330
|
try:
|
|
334
331
|
result = subprocess.run(["docker", "version"], capture_output=True, timeout=10)
|
|
335
332
|
if result.returncode != 0:
|
|
@@ -341,14 +338,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
341
338
|
|
|
342
339
|
return True, ""
|
|
343
340
|
|
|
344
|
-
async def package(
|
|
341
|
+
async def package(
|
|
342
|
+
self,
|
|
343
|
+
project_dir: str,
|
|
344
|
+
detection_result: Any,
|
|
345
|
+
config: Optional[Dict[str, Any]] = None,
|
|
346
|
+
) -> PackageInfo:
|
|
345
347
|
"""打包项目
|
|
346
|
-
|
|
348
|
+
|
|
347
349
|
Code 模式: 返回基本 PackageInfo,实际构建在 build() 中进行
|
|
348
350
|
Container 模式: 使用 ContainerBuilder 打包
|
|
349
351
|
"""
|
|
350
352
|
project_path = Path(project_dir)
|
|
351
|
-
|
|
353
|
+
|
|
352
354
|
# 创建基本 PackageInfo
|
|
353
355
|
package_info = PackageInfo(
|
|
354
356
|
name=detection_result.name or project_path.name,
|
|
@@ -356,14 +358,15 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
356
358
|
build_dir=str(project_path / ".agentengine" / "build"),
|
|
357
359
|
project_dir=str(project_path),
|
|
358
360
|
entry_point=detection_result.entry_point,
|
|
359
|
-
metadata={}
|
|
361
|
+
metadata={},
|
|
360
362
|
)
|
|
361
|
-
|
|
363
|
+
|
|
362
364
|
# 尝试加载之前保存的构建元数据 (ks3_path 等)
|
|
363
365
|
try:
|
|
364
366
|
metadata_file = project_path / ".agentengine" / "build-metadata.json"
|
|
365
367
|
if metadata_file.exists():
|
|
366
368
|
import json
|
|
369
|
+
|
|
367
370
|
with open(metadata_file, "r") as f:
|
|
368
371
|
saved_data = json.load(f)
|
|
369
372
|
if "metadata" in saved_data:
|
|
@@ -376,15 +379,32 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
376
379
|
click.echo(f" 📦 已加载上次构建元数据: {count} 项")
|
|
377
380
|
except Exception as e:
|
|
378
381
|
click.secho(f"⚠️ 加载构建元数据失败: {e}", fg="yellow")
|
|
379
|
-
|
|
380
|
-
return package_info
|
|
381
|
-
|
|
382
382
|
|
|
383
|
+
return package_info
|
|
383
384
|
|
|
384
385
|
async def build(self, package_info: PackageInfo, target: DeployTarget) -> PackageInfo:
|
|
385
386
|
"""构建 & 上传 (客户端直传 KS3)"""
|
|
386
387
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
387
388
|
|
|
389
|
+
if artifact_type == "ManagedRuntime":
|
|
390
|
+
# Declarative runtimes are delivered by Server -> Runtime Service as
|
|
391
|
+
# an inline manifest. Keep a local bundle only for inspection; do
|
|
392
|
+
# not acquire credentials or upload it to KS3.
|
|
393
|
+
from ksadk.builders.managed_runtime_builder import ManagedRuntimeBuilder
|
|
394
|
+
|
|
395
|
+
builder = ManagedRuntimeBuilder(
|
|
396
|
+
Path(package_info.project_dir),
|
|
397
|
+
config=target.extra.copy(),
|
|
398
|
+
runtime_version=str(target.extra.get("runtime_version") or ""),
|
|
399
|
+
)
|
|
400
|
+
build_result = builder.build()
|
|
401
|
+
if not build_result.success:
|
|
402
|
+
raise Exception(f"构建失败: {build_result.error_message}")
|
|
403
|
+
package_info.metadata.update(build_result.metadata)
|
|
404
|
+
if build_result.artifact_path is not None:
|
|
405
|
+
package_info.metadata["managed_manifest_path"] = str(build_result.artifact_path)
|
|
406
|
+
return package_info
|
|
407
|
+
|
|
388
408
|
if artifact_type == "Code":
|
|
389
409
|
# 1. 检查是否已有 KS3 路径
|
|
390
410
|
# 优先级: CLI传入 > Metadata缓存 (仅当 !no_cache)
|
|
@@ -392,58 +412,73 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
392
412
|
cached_ks3_path = package_info.metadata.get("ks3_path")
|
|
393
413
|
no_cache = target.extra.get("no_cache", False)
|
|
394
414
|
repackage = target.extra.get("repackage", False)
|
|
395
|
-
|
|
415
|
+
|
|
396
416
|
# 如果显式传入了 ks3-path,直接使用
|
|
397
417
|
if cli_ks3_path:
|
|
398
418
|
package_info.metadata["ks3_path"] = cli_ks3_path
|
|
399
419
|
return package_info
|
|
400
|
-
|
|
420
|
+
|
|
401
421
|
# 如果没有 no_cache 且有缓存,才使用缓存
|
|
402
422
|
if not no_cache and not repackage and cached_ks3_path:
|
|
403
423
|
logger.info(f"Using cached bundle: {cached_ks3_path}")
|
|
404
424
|
return package_info
|
|
405
425
|
|
|
406
|
-
# 2. 构建 ZIP 包
|
|
407
|
-
|
|
426
|
+
# 2. 构建 ZIP 包
|
|
427
|
+
|
|
408
428
|
# 传递配置,包括 no_cache
|
|
409
429
|
builder_config = target.extra.copy()
|
|
410
430
|
builder_config["no_cache"] = no_cache
|
|
411
431
|
builder_config["repackage"] = repackage
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
432
|
+
|
|
433
|
+
if artifact_type == "ManagedRuntime":
|
|
434
|
+
from ksadk.builders.managed_runtime_builder import ManagedRuntimeBuilder
|
|
435
|
+
|
|
436
|
+
builder = ManagedRuntimeBuilder(
|
|
437
|
+
Path(package_info.project_dir),
|
|
438
|
+
config=builder_config,
|
|
439
|
+
runtime_version=str(target.extra.get("runtime_version") or ""),
|
|
440
|
+
)
|
|
441
|
+
else:
|
|
442
|
+
# CodeBuilder 直接操作原始 project_dir。
|
|
443
|
+
builder = CodeBuilder(Path(package_info.project_dir), config=builder_config)
|
|
444
|
+
|
|
418
445
|
# 执行构建
|
|
419
446
|
build_result = builder.build()
|
|
420
|
-
|
|
447
|
+
|
|
421
448
|
if not build_result.success:
|
|
422
449
|
raise Exception(f"构建失败: {build_result.error_message}")
|
|
423
|
-
|
|
450
|
+
|
|
424
451
|
zip_path = build_result.artifact_path
|
|
452
|
+
if zip_path is None:
|
|
453
|
+
raise RuntimeError("代码构建成功但未生成 artifact_path")
|
|
454
|
+
package_info.metadata.update(build_result.metadata)
|
|
455
|
+
if build_result.metadata.get("manifest_sha256"):
|
|
456
|
+
target.extra["manifest_sha256"] = build_result.metadata["manifest_sha256"]
|
|
425
457
|
# click.echo(f" ✅ ZIP 已生成: {zip_path}")
|
|
426
|
-
|
|
458
|
+
|
|
427
459
|
# 3. 直接上传 KS3 (使用本地 AK/SK)
|
|
428
460
|
# 3. 直接上传 KS3 (使用本地 AK/SK)
|
|
429
|
-
|
|
461
|
+
artifact_label = "Runtime manifest" if artifact_type == "ManagedRuntime" else "代码包"
|
|
462
|
+
click.echo(f"\n正在上传{artifact_label}到 KS3...")
|
|
430
463
|
upload_started_at = time.monotonic()
|
|
431
|
-
|
|
464
|
+
|
|
432
465
|
ks3_bucket = target.extra.get("ks3_bucket")
|
|
433
466
|
upload_region = "cn-beijing-6" if target.region == "pre-online" else target.region
|
|
434
|
-
|
|
467
|
+
|
|
435
468
|
uploader = KS3Uploader(region=upload_region, bucket=ks3_bucket)
|
|
436
|
-
|
|
469
|
+
|
|
437
470
|
# 使用时间戳确保每次上传的代码包路径唯一,支持真正的版本回滚
|
|
438
471
|
from datetime import datetime
|
|
472
|
+
|
|
439
473
|
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
440
|
-
|
|
441
|
-
|
|
474
|
+
object_prefix = "runtime" if artifact_type == "ManagedRuntime" else "code"
|
|
475
|
+
object_key = f"agents/{package_info.name}/{object_prefix}_{timestamp}.zip"
|
|
476
|
+
|
|
442
477
|
ks3_path = await uploader.upload(zip_path, object_key)
|
|
443
|
-
|
|
478
|
+
|
|
444
479
|
if not ks3_path:
|
|
445
480
|
raise Exception("KS3 上传失败")
|
|
446
|
-
|
|
481
|
+
|
|
447
482
|
logger.info(f"Upload success: {ks3_path}")
|
|
448
483
|
click.echo(f" ✓ 上传耗时: {self._format_elapsed(upload_started_at)}")
|
|
449
484
|
package_info.metadata["ks3_path"] = ks3_path
|
|
@@ -466,45 +501,47 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
466
501
|
logger.info(f"Using cached image: {cached_image}")
|
|
467
502
|
package_info.image = cached_image
|
|
468
503
|
return package_info
|
|
469
|
-
|
|
470
|
-
|
|
504
|
+
|
|
505
|
+
container_builder = ContainerBuilder(
|
|
471
506
|
project_dir=Path(package_info.project_dir),
|
|
472
507
|
tag=tag,
|
|
473
508
|
registry=registry,
|
|
474
|
-
no_cache=no_cache
|
|
509
|
+
no_cache=no_cache,
|
|
475
510
|
)
|
|
476
|
-
|
|
511
|
+
|
|
477
512
|
# 构建镜像
|
|
478
|
-
build_result =
|
|
513
|
+
build_result = container_builder.build()
|
|
479
514
|
if not build_result.success:
|
|
480
515
|
raise Exception(f"镜像构建失败: {build_result.error_message}")
|
|
481
|
-
|
|
482
|
-
image_name = build_result.metadata.get("image")
|
|
516
|
+
|
|
517
|
+
image_name = str(build_result.metadata.get("image") or "").strip()
|
|
518
|
+
if not image_name:
|
|
519
|
+
raise RuntimeError("容器构建成功但未生成 image")
|
|
483
520
|
package_info.image = image_name
|
|
484
521
|
package_info.metadata["image"] = image_name
|
|
485
|
-
|
|
522
|
+
|
|
486
523
|
# 推送镜像
|
|
487
|
-
if not
|
|
524
|
+
if not container_builder.push(image_name):
|
|
488
525
|
raise Exception("镜像推送失败")
|
|
489
|
-
|
|
526
|
+
|
|
490
527
|
click.secho(f"✅ 镜像已推送: {image_name}", fg="green")
|
|
491
528
|
|
|
492
529
|
self._persist_build_metadata(package_info)
|
|
493
|
-
|
|
530
|
+
|
|
494
531
|
return package_info
|
|
495
532
|
|
|
496
533
|
async def deploy(self, package_info: PackageInfo, target: DeployTarget) -> DeployResult:
|
|
497
534
|
"""通过 AgentEngine Server 部署
|
|
498
|
-
|
|
535
|
+
|
|
499
536
|
逻辑:
|
|
500
537
|
- 读取本地 .agentengine.state 文件获取 agent_id
|
|
501
538
|
- 如果有 agent_id → 执行更新 (endpoint 不变)
|
|
502
539
|
- 如果没有 → 创建新 Agent,保存 agent_id 到状态文件
|
|
503
540
|
"""
|
|
504
|
-
|
|
541
|
+
|
|
505
542
|
server_url = os.getenv("AGENTENGINE_SERVER_URL")
|
|
506
543
|
click.echo(f"\n🚀 开始部署到 Serverless (Managed by {server_url})...")
|
|
507
|
-
|
|
544
|
+
|
|
508
545
|
# 读取本地状态文件
|
|
509
546
|
project_dir = package_info.project_dir
|
|
510
547
|
state_file = Path(project_dir) / ".agentengine.state"
|
|
@@ -527,26 +564,29 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
527
564
|
ui_state["ui_bundle_path"] = project_config.get("ui_bundle_path")
|
|
528
565
|
if project_config.get("ui_bundle_dir") and not ui_state.get("ui_bundle_dir"):
|
|
529
566
|
ui_state["ui_bundle_dir"] = project_config.get("ui_bundle_dir")
|
|
530
|
-
|
|
567
|
+
|
|
531
568
|
# 构造请求 payload
|
|
532
569
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
533
570
|
artifact_path = ""
|
|
534
|
-
|
|
571
|
+
code_backed = artifact_type == "Code"
|
|
572
|
+
if code_backed:
|
|
535
573
|
original_artifact_path = package_info.metadata.get("ks3_path", "")
|
|
536
574
|
artifact_path = original_artifact_path
|
|
537
|
-
|
|
575
|
+
|
|
538
576
|
# 转换为内网地址 (如果 serverless 无法解析 ks3://)
|
|
539
577
|
if artifact_path.startswith("ks3://"):
|
|
540
578
|
try:
|
|
541
579
|
from ksadk.common.constants import get_ks3_endpoints
|
|
542
|
-
|
|
580
|
+
|
|
543
581
|
# 确定 KS3 Region code (pre-online -> cn-beijing)
|
|
544
582
|
ks3_region = target.extra.get("ks3_region")
|
|
545
583
|
if not ks3_region:
|
|
546
|
-
|
|
547
|
-
|
|
584
|
+
ks3_region = (
|
|
585
|
+
"cn-beijing-6" if target.region == "pre-online" else target.region
|
|
586
|
+
)
|
|
587
|
+
|
|
548
588
|
_, internal_endpoint = get_ks3_endpoints(ks3_region)
|
|
549
|
-
|
|
589
|
+
|
|
550
590
|
if internal_endpoint:
|
|
551
591
|
# ks3://bucket/key -> http://bucket.internal_endpoint/key
|
|
552
592
|
bucket, key = self._parse_code_artifact_path(artifact_path)
|
|
@@ -555,32 +595,39 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
555
595
|
click.echo(f" Converted artifact path: {artifact_path}")
|
|
556
596
|
except Exception as e:
|
|
557
597
|
logger.warning(f"Failed to convert ks3 path to internal URL: {e}")
|
|
558
|
-
|
|
598
|
+
|
|
559
599
|
if not artifact_path:
|
|
560
600
|
raise ValueError(
|
|
561
|
-
"❌
|
|
562
|
-
" 在 Serverless
|
|
563
|
-
" 👉 请先执行: agentengine build --
|
|
601
|
+
"❌ 未找到 KS3 artifact 路径 (ks3_path)。\n"
|
|
602
|
+
" 在 Serverless 模式下,必须先上传构建产物。\n"
|
|
603
|
+
" 👉 请先执行: agentengine build --push\n"
|
|
564
604
|
" 或者在 deploy 命令中使用 --ks3-path 指定路径。"
|
|
565
605
|
)
|
|
566
606
|
|
|
567
|
-
parsed_bucket, parsed_object_key = self._parse_code_artifact_path(
|
|
607
|
+
parsed_bucket, parsed_object_key = self._parse_code_artifact_path(
|
|
608
|
+
original_artifact_path
|
|
609
|
+
)
|
|
568
610
|
if not parsed_bucket or not parsed_object_key:
|
|
569
611
|
raise ValueError(
|
|
570
|
-
"❌ ks3_path
|
|
612
|
+
"❌ ks3_path 格式无效,缺少 artifact 对象路径。\n"
|
|
571
613
|
f" 当前值: {original_artifact_path or '(empty)'}\n"
|
|
572
614
|
" 期望格式: ks3://<bucket>/<object-key>\n"
|
|
573
|
-
" 👉 请重新执行: agentengine build --
|
|
615
|
+
" 👉 请重新执行: agentengine build --push\n"
|
|
574
616
|
" 或者传入完整的 --ks3-path。"
|
|
575
617
|
)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
artifact_path = package_info.image
|
|
579
|
-
|
|
618
|
+
|
|
619
|
+
elif artifact_type == "Container":
|
|
620
|
+
artifact_path = str(package_info.image or "").strip()
|
|
621
|
+
if not artifact_path:
|
|
622
|
+
raise ValueError("Container 部署缺少 image")
|
|
623
|
+
elif artifact_type != "ManagedRuntime":
|
|
624
|
+
raise ValueError(f"Unsupported artifact type: {artifact_type}")
|
|
625
|
+
|
|
580
626
|
# 构建 KS3 凭证
|
|
581
627
|
ks3_config = None
|
|
582
|
-
if
|
|
628
|
+
if code_backed:
|
|
583
629
|
from ksadk.common.auth import AWSV4Auth
|
|
630
|
+
|
|
584
631
|
auth = AWSV4Auth() # 读取本地 AK/SK
|
|
585
632
|
if auth.access_key_id and auth.secret_access_key:
|
|
586
633
|
# 智能推断 Bucket 和 Region
|
|
@@ -589,7 +636,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
589
636
|
bucket_name, _ = self._parse_code_artifact_path(
|
|
590
637
|
package_info.metadata.get("ks3_path", "")
|
|
591
638
|
)
|
|
592
|
-
|
|
639
|
+
|
|
593
640
|
# 如果没有提供,尝试从 artifact_path 解析
|
|
594
641
|
if not bucket_name:
|
|
595
642
|
if artifact_path.startswith("ks3://"):
|
|
@@ -599,35 +646,38 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
599
646
|
logger.info(f"Extracted bucket from ks3:// path: {bucket_name}")
|
|
600
647
|
except IndexError:
|
|
601
648
|
pass
|
|
602
|
-
elif artifact_path.startswith("http")
|
|
649
|
+
elif artifact_path.startswith("http") and "." in artifact_path:
|
|
603
650
|
try:
|
|
604
651
|
# http://bucket.endpoint/key -> bucket
|
|
605
652
|
from urllib.parse import urlparse
|
|
653
|
+
|
|
606
654
|
parsed = urlparse(artifact_path)
|
|
607
655
|
bucket_name = parsed.netloc.split(".")[0]
|
|
608
656
|
logger.info(f"Extracted bucket from HTTP URL: {bucket_name}")
|
|
609
657
|
except Exception as e:
|
|
610
658
|
logger.warning(f"Failed to extract bucket from URL: {e}")
|
|
611
|
-
|
|
659
|
+
|
|
612
660
|
# 如果仍然没有,使用智能默认值(与 KS3Uploader 逻辑一致)
|
|
613
661
|
if not bucket_name:
|
|
614
662
|
account_id = os.getenv("KSYUN_ACCOUNT_ID")
|
|
615
|
-
upload_region =
|
|
616
|
-
|
|
663
|
+
upload_region = (
|
|
664
|
+
"cn-beijing-6" if target.region == "pre-online" else target.region
|
|
665
|
+
)
|
|
666
|
+
|
|
617
667
|
if not account_id:
|
|
618
668
|
raise ValueError(
|
|
619
669
|
"❌ 缺少 KSYUN_ACCOUNT_ID 环境变量\n"
|
|
620
670
|
" Bucket 名称格式必须为: agentengine-{account_id}-{region}\n"
|
|
621
671
|
" 请在 .env 文件中设置: KSYUN_ACCOUNT_ID=你的账号ID"
|
|
622
672
|
)
|
|
623
|
-
|
|
673
|
+
|
|
624
674
|
bucket_name = f"agentengine-{account_id}-{upload_region}"
|
|
625
675
|
logger.info(f"Using default bucket: {bucket_name}")
|
|
626
|
-
|
|
676
|
+
|
|
627
677
|
# Region 逻辑需与 Build 阶段保持一致 (pre-online -> cn-beijing-6)
|
|
628
678
|
ks3_region = target.extra.get("ks3_region")
|
|
629
679
|
if not ks3_region:
|
|
630
|
-
|
|
680
|
+
ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
|
|
631
681
|
|
|
632
682
|
ks3_config = {
|
|
633
683
|
"access_key": auth.access_key_id,
|
|
@@ -636,18 +686,31 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
636
686
|
"bucket": bucket_name,
|
|
637
687
|
}
|
|
638
688
|
logger.info(f"📦 KS3 Config: bucket={bucket_name}, region={ks3_region}")
|
|
639
|
-
|
|
689
|
+
|
|
690
|
+
runtime_config = None
|
|
691
|
+
if artifact_type == "ManagedRuntime":
|
|
692
|
+
runtime_config = {
|
|
693
|
+
"name": str(target.extra.get("runtime_name") or "").strip(),
|
|
694
|
+
"version": str(target.extra.get("runtime_version") or "").strip(),
|
|
695
|
+
}
|
|
696
|
+
missing = [key for key, value in runtime_config.items() if not value]
|
|
697
|
+
if missing:
|
|
698
|
+
raise ValueError(
|
|
699
|
+
"ManagedRuntime 部署缺少 "
|
|
700
|
+
+ ", ".join(f"runtime_config.{key}" for key in missing)
|
|
701
|
+
)
|
|
702
|
+
|
|
640
703
|
try:
|
|
641
704
|
# 获取 dry_run 标识
|
|
642
705
|
is_dry_run = target.extra.get("dry_run", False)
|
|
643
706
|
|
|
644
707
|
async with AgentEngineClient(region=target.region, dry_run=is_dry_run) as client:
|
|
645
708
|
agent_exists = False
|
|
646
|
-
|
|
709
|
+
|
|
647
710
|
if existing_agent_id:
|
|
648
711
|
# 有本地状态 → 先检查服务器上是否存在
|
|
649
712
|
click.echo(f" 检测到本地状态: {existing_agent_id}")
|
|
650
|
-
|
|
713
|
+
|
|
651
714
|
try:
|
|
652
715
|
# 尝试获取 agent,确认是否存在
|
|
653
716
|
existing_agent = await client.get_agent(existing_agent_id)
|
|
@@ -657,32 +720,37 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
657
720
|
# Agent 不存在或查询失败
|
|
658
721
|
err_msg = str(e).lower()
|
|
659
722
|
if "not found" in err_msg or "404" in err_msg or "不存在" in err_msg:
|
|
660
|
-
click.secho(
|
|
723
|
+
click.secho(
|
|
724
|
+
f" ⚠️ 服务器上未找到 Agent {existing_agent_id},将创建新 Agent",
|
|
725
|
+
fg="yellow",
|
|
726
|
+
)
|
|
661
727
|
agent_exists = False
|
|
662
|
-
#
|
|
663
|
-
#
|
|
728
|
+
# DryRun 异常表示真实请求被拦截,无法确认 Agent 是否存在。
|
|
729
|
+
# 为安全起见,DryRun 假设它存在并走更新路径。
|
|
664
730
|
elif "Dry Run" in str(e):
|
|
665
|
-
|
|
666
|
-
|
|
731
|
+
click.secho(
|
|
732
|
+
f" [Dry Run] 假设 Agent {existing_agent_id} 存在", fg="cyan"
|
|
733
|
+
)
|
|
734
|
+
agent_exists = True
|
|
667
735
|
else:
|
|
668
736
|
# 其他错误,重新抛出
|
|
669
737
|
raise
|
|
670
|
-
|
|
738
|
+
|
|
671
739
|
if agent_exists:
|
|
672
740
|
# Agent 存在 → 执行更新
|
|
673
|
-
click.echo(
|
|
674
|
-
|
|
741
|
+
click.echo(" 执行热更新 (endpoint 保持不变)...")
|
|
742
|
+
|
|
675
743
|
update_data = {
|
|
676
744
|
"artifact_type": artifact_type,
|
|
677
745
|
"artifact_path": artifact_path,
|
|
678
746
|
"resources": {
|
|
679
747
|
"cpu": target.resources.cpu,
|
|
680
|
-
"memory": target.resources.memory
|
|
748
|
+
"memory": target.resources.memory,
|
|
681
749
|
},
|
|
682
750
|
"scaling": {
|
|
683
751
|
"min_replicas": target.scaling.min_replicas,
|
|
684
752
|
"max_replicas": target.scaling.max_replicas,
|
|
685
|
-
"concurrency": target.scaling.concurrency
|
|
753
|
+
"concurrency": target.scaling.concurrency,
|
|
686
754
|
},
|
|
687
755
|
"observability": {
|
|
688
756
|
"langfuse_enabled": target.extra.get("enable_observability", True)
|
|
@@ -693,16 +761,20 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
693
761
|
"url": resolved_ui.url,
|
|
694
762
|
},
|
|
695
763
|
}
|
|
696
|
-
|
|
764
|
+
if runtime_config:
|
|
765
|
+
update_data["runtime_config"] = runtime_config
|
|
766
|
+
|
|
697
767
|
if ks3_config:
|
|
698
768
|
update_data["ks3"] = ks3_config
|
|
699
769
|
elif artifact_type == "Container":
|
|
700
770
|
image_credential = self._image_credential_from_env(artifact_path)
|
|
701
771
|
if image_credential:
|
|
702
772
|
update_data["image_credential"] = image_credential
|
|
703
|
-
|
|
704
|
-
f"
|
|
773
|
+
credential_target = (
|
|
774
|
+
f"{image_credential['username']}@"
|
|
775
|
+
f"{image_credential['endpoint']}"
|
|
705
776
|
)
|
|
777
|
+
click.echo(f" 🔑 镜像凭证: {credential_target}")
|
|
706
778
|
|
|
707
779
|
# 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
|
|
708
780
|
env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
|
|
@@ -727,17 +799,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
727
799
|
storage_config = self._serialize_storage_config(target)
|
|
728
800
|
if storage_config:
|
|
729
801
|
update_data["storage"] = storage_config
|
|
730
|
-
|
|
802
|
+
|
|
731
803
|
# 注入更新时间戳,强制触发 Rolling Update (Pod 重启)
|
|
732
804
|
if "env_vars" not in update_data:
|
|
733
805
|
update_data["env_vars"] = {}
|
|
734
|
-
|
|
806
|
+
|
|
735
807
|
import time
|
|
808
|
+
|
|
736
809
|
update_data["env_vars"]["KSADK_UPDATED_AT"] = str(int(time.time()))
|
|
737
|
-
|
|
810
|
+
|
|
738
811
|
# DEBUG: 打印 Payload 确认 trigger 是否存在
|
|
739
|
-
|
|
740
|
-
|
|
812
|
+
updated_at_value = update_data["env_vars"]["KSADK_UPDATED_AT"]
|
|
813
|
+
click.echo(f" 🔄 更新 Trigger: KSADK_UPDATED_AT={updated_at_value}")
|
|
814
|
+
|
|
741
815
|
res = await client.update_agent(existing_agent_id, update_data)
|
|
742
816
|
latest_access = {}
|
|
743
817
|
if not is_dry_run:
|
|
@@ -751,38 +825,40 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
751
825
|
exc,
|
|
752
826
|
),
|
|
753
827
|
)
|
|
754
|
-
|
|
828
|
+
|
|
755
829
|
# 如果是 Dry Run,手动构造假响应以避免崩溃
|
|
756
830
|
if is_dry_run and not res:
|
|
757
831
|
res = {"name": package_info.name, "endpoint": "http://dry-run-endpoint"}
|
|
758
|
-
|
|
832
|
+
|
|
759
833
|
# 更新本地状态 (保留旧字段如 api_key)
|
|
760
834
|
new_state = local_state.copy()
|
|
761
|
-
new_state.update(
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
835
|
+
new_state.update(
|
|
836
|
+
{
|
|
837
|
+
"agent_id": latest_access.get("agent_id") or existing_agent_id,
|
|
838
|
+
"name": latest_access.get("name") or res.get("name"),
|
|
839
|
+
"region": target.region,
|
|
840
|
+
"endpoint": latest_access.get("endpoint") or res.get("endpoint"),
|
|
841
|
+
"updated_at": self._now_iso(),
|
|
842
|
+
**ui_state,
|
|
843
|
+
}
|
|
844
|
+
)
|
|
769
845
|
if latest_access.get("api_key"):
|
|
770
846
|
new_state["api_key"] = latest_access["api_key"]
|
|
771
847
|
self._save_state(state_file, new_state)
|
|
772
|
-
|
|
848
|
+
|
|
773
849
|
return DeployResult(
|
|
774
|
-
status=DeployStatus.DEPLOYING,
|
|
850
|
+
status=DeployStatus.DEPLOYING,
|
|
775
851
|
agent_id=existing_agent_id,
|
|
776
852
|
agent_name=res.get("name"),
|
|
777
853
|
endpoint=res.get("endpoint"),
|
|
778
|
-
message=f"✅ Agent 已更新: {existing_agent_id}"
|
|
854
|
+
message=f"✅ Agent 已更新: {existing_agent_id}",
|
|
779
855
|
)
|
|
780
856
|
# else: agent_exists = False,继续执行创建逻辑
|
|
781
|
-
|
|
857
|
+
|
|
782
858
|
# 没有本地状态,或本地状态对应的 Agent 在服务器上不存在 → 创建新 Agent
|
|
783
859
|
if not existing_agent_id or not agent_exists:
|
|
784
860
|
click.echo(f" 创建新 Agent: {package_info.name}")
|
|
785
|
-
|
|
861
|
+
|
|
786
862
|
request_data = {
|
|
787
863
|
"name": package_info.name,
|
|
788
864
|
"framework": package_info.framework,
|
|
@@ -792,12 +868,12 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
792
868
|
"instance_id": target.extra.get("instance_id", "default"),
|
|
793
869
|
"resources": {
|
|
794
870
|
"cpu": target.resources.cpu,
|
|
795
|
-
"memory": target.resources.memory
|
|
871
|
+
"memory": target.resources.memory,
|
|
796
872
|
},
|
|
797
873
|
"scaling": {
|
|
798
874
|
"min_replicas": target.scaling.min_replicas,
|
|
799
875
|
"max_replicas": target.scaling.max_replicas,
|
|
800
|
-
"concurrency": target.scaling.concurrency
|
|
876
|
+
"concurrency": target.scaling.concurrency,
|
|
801
877
|
},
|
|
802
878
|
"observability": {
|
|
803
879
|
"langfuse_enabled": target.extra.get("enable_observability", True)
|
|
@@ -808,7 +884,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
808
884
|
"url": resolved_ui.url,
|
|
809
885
|
},
|
|
810
886
|
}
|
|
811
|
-
|
|
887
|
+
if runtime_config:
|
|
888
|
+
request_data["runtime_config"] = runtime_config
|
|
889
|
+
|
|
812
890
|
if ks3_config:
|
|
813
891
|
request_data["ks3"] = ks3_config
|
|
814
892
|
|
|
@@ -817,9 +895,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
817
895
|
image_credential = self._image_credential_from_env(artifact_path)
|
|
818
896
|
if image_credential:
|
|
819
897
|
request_data["image_credential"] = image_credential
|
|
820
|
-
|
|
821
|
-
f"
|
|
898
|
+
credential_target = (
|
|
899
|
+
f"{image_credential['username']}@" f"{image_credential['endpoint']}"
|
|
822
900
|
)
|
|
901
|
+
click.echo(f" 🔑 镜像凭证: {credential_target}")
|
|
823
902
|
|
|
824
903
|
# 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
|
|
825
904
|
env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
|
|
@@ -837,7 +916,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
837
916
|
click.echo(f" 📦 加载环境变量: {len(env_vars)} 项 from 全局配置")
|
|
838
917
|
|
|
839
918
|
if env_vars:
|
|
840
|
-
|
|
919
|
+
request_data["env_vars"] = env_vars
|
|
841
920
|
|
|
842
921
|
network_config = self._serialize_network_config(target, is_update=False)
|
|
843
922
|
if network_config:
|
|
@@ -852,20 +931,22 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
852
931
|
ksyun_account_id = os.getenv("KSYUN_ACCOUNT_ID")
|
|
853
932
|
if ksyun_account_id:
|
|
854
933
|
extra_headers["X-Ksc-Account-Id"] = ksyun_account_id
|
|
855
|
-
|
|
934
|
+
|
|
856
935
|
# 重新构造一个带 Header 的 client
|
|
857
|
-
async with AgentEngineClient(
|
|
936
|
+
async with AgentEngineClient(
|
|
937
|
+
region=target.region, extra_headers=extra_headers, dry_run=is_dry_run
|
|
938
|
+
) as new_client:
|
|
858
939
|
res = await new_client.create_agent(request_data)
|
|
859
|
-
|
|
940
|
+
|
|
860
941
|
# 如果是 Dry Run,手动构造假响应
|
|
861
942
|
if is_dry_run and not res:
|
|
862
943
|
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"
|
|
944
|
+
"agent_id": "dry-run-agent-id",
|
|
945
|
+
"name": package_info.name,
|
|
946
|
+
"endpoint": "http://dry-run-endpoint",
|
|
947
|
+
"api_key": "dry-run-key",
|
|
867
948
|
}
|
|
868
|
-
|
|
949
|
+
|
|
869
950
|
# CreateAgentProduct 返回 order_id,需要轮询 list_agents 获取 agent_id
|
|
870
951
|
new_agent_id = res.get("agent_id")
|
|
871
952
|
order_id = res.get("order_id")
|
|
@@ -876,10 +957,13 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
876
957
|
if order_id and not new_agent_id:
|
|
877
958
|
click.echo(f" 📋 订单已创建: {order_id},等待实例创建...")
|
|
878
959
|
import time
|
|
960
|
+
|
|
879
961
|
for i in range(12): # 最多等 60s
|
|
880
962
|
time.sleep(5)
|
|
881
963
|
try:
|
|
882
|
-
detail = await client.get_agent(
|
|
964
|
+
detail = await client.get_agent(
|
|
965
|
+
name=package_info.name, include_api_key=True
|
|
966
|
+
)
|
|
883
967
|
qa = detail.get("quick_access", {})
|
|
884
968
|
basic = detail.get("basic", {})
|
|
885
969
|
new_agent_id = basic.get("agent_id")
|
|
@@ -894,7 +978,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
894
978
|
click.echo(f" ⏳ 等待中... ({(i+1)*5}s)")
|
|
895
979
|
|
|
896
980
|
if not new_agent_id:
|
|
897
|
-
click.secho(
|
|
981
|
+
click.secho(
|
|
982
|
+
" ⚠️ 实例仍在创建中,稍后使用 'agentengine status' 查看",
|
|
983
|
+
fg="yellow",
|
|
984
|
+
)
|
|
898
985
|
|
|
899
986
|
latest_access = {}
|
|
900
987
|
if new_agent_id and not is_dry_run:
|
|
@@ -913,54 +1000,63 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
913
1000
|
agent_name = latest_access.get("name") or agent_name
|
|
914
1001
|
agent_endpoint = latest_access.get("endpoint") or agent_endpoint
|
|
915
1002
|
agent_api_key = latest_access.get("api_key") or agent_api_key
|
|
916
|
-
|
|
1003
|
+
|
|
917
1004
|
# 保存 agent_id 到本地状态文件
|
|
918
|
-
self._save_state(
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1005
|
+
self._save_state(
|
|
1006
|
+
state_file,
|
|
1007
|
+
{
|
|
1008
|
+
"agent_id": new_agent_id,
|
|
1009
|
+
"name": agent_name,
|
|
1010
|
+
"region": target.region,
|
|
1011
|
+
"endpoint": agent_endpoint,
|
|
1012
|
+
"api_key": agent_api_key, # 只在首次保存
|
|
1013
|
+
"order_id": order_id,
|
|
1014
|
+
"created_at": self._now_iso(),
|
|
1015
|
+
**ui_state,
|
|
1016
|
+
},
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
click.echo(" 💾 已保存状态到 .agentengine.state")
|
|
1020
|
+
|
|
931
1021
|
return DeployResult(
|
|
932
|
-
status=DeployStatus.DEPLOYING,
|
|
1022
|
+
status=DeployStatus.DEPLOYING,
|
|
933
1023
|
agent_id=new_agent_id,
|
|
934
1024
|
agent_name=agent_name,
|
|
935
|
-
endpoint=agent_endpoint,
|
|
1025
|
+
endpoint=agent_endpoint,
|
|
936
1026
|
api_key=agent_api_key,
|
|
937
|
-
message=
|
|
1027
|
+
message=(
|
|
1028
|
+
f"✅ Agent ID: {new_agent_id or '(创建中)'} "
|
|
1029
|
+
f"(首次部署, 订单: {order_id or '-'})"
|
|
1030
|
+
),
|
|
938
1031
|
)
|
|
939
1032
|
|
|
1033
|
+
return DeployResult(
|
|
1034
|
+
status=DeployStatus.FAILED,
|
|
1035
|
+
message="部署流程未返回 Agent 状态",
|
|
1036
|
+
)
|
|
1037
|
+
|
|
940
1038
|
except DryRunExit as e:
|
|
941
1039
|
return DeployResult(
|
|
942
1040
|
status=DeployStatus.SKIPPED,
|
|
943
1041
|
message="✅ Dry Run Completed: 请求已打印,未执行实际变更。",
|
|
944
1042
|
metadata={"dry_run_request": e.payload or {}},
|
|
945
1043
|
)
|
|
946
|
-
|
|
1044
|
+
|
|
947
1045
|
except Exception as e:
|
|
948
1046
|
logger.error(f"Deploy failed: {e}")
|
|
949
|
-
|
|
1047
|
+
|
|
950
1048
|
# 检测名称冲突
|
|
951
1049
|
err_msg = str(e)
|
|
952
1050
|
if "Conflict" in err_msg or "409" in err_msg or "already exists" in err_msg:
|
|
953
1051
|
return DeployResult(
|
|
954
1052
|
status=DeployStatus.FAILED,
|
|
955
1053
|
message=f"❌ 部署失败: Agent 名称 '{package_info.name}' 已存在。\n"
|
|
956
|
-
|
|
957
|
-
|
|
1054
|
+
f" 提示: 请检查是否重复创建。\n"
|
|
1055
|
+
" 👉 解决方法: 请在 agentengine.yaml 中修改 'name' 字段 "
|
|
1056
|
+
"(如添加后缀) 后重试。",
|
|
958
1057
|
)
|
|
959
|
-
|
|
960
|
-
return DeployResult(
|
|
961
|
-
status=DeployStatus.FAILED,
|
|
962
|
-
message=f"Server 请求失败: {str(e)}"
|
|
963
|
-
)
|
|
1058
|
+
|
|
1059
|
+
return DeployResult(status=DeployStatus.FAILED, message=f"Server 请求失败: {str(e)}")
|
|
964
1060
|
|
|
965
1061
|
async def get_status(self, agent_id: str, target: DeployTarget) -> DeployResult:
|
|
966
1062
|
"""获取 Agent 状态"""
|
|
@@ -968,7 +1064,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
968
1064
|
try:
|
|
969
1065
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
970
1066
|
res = await client.get_agent(agent_id)
|
|
971
|
-
|
|
1067
|
+
|
|
972
1068
|
status_map = {
|
|
973
1069
|
"running": DeployStatus.RUNNING,
|
|
974
1070
|
"ready": DeployStatus.RUNNING,
|
|
@@ -978,9 +1074,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
978
1074
|
"scaling": DeployStatus.UPDATING,
|
|
979
1075
|
"failed": DeployStatus.FAILED,
|
|
980
1076
|
"error": DeployStatus.FAILED,
|
|
981
|
-
"unknown": DeployStatus.UNKNOWN
|
|
1077
|
+
"unknown": DeployStatus.UNKNOWN,
|
|
982
1078
|
}
|
|
983
|
-
|
|
1079
|
+
|
|
984
1080
|
# 使状态匹配不区分大小写,以兼容服务端的改动 (Creating -> CREATING)
|
|
985
1081
|
current_status = (res.get("status") or "").lower()
|
|
986
1082
|
return DeployResult(
|
|
@@ -988,21 +1084,20 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
988
1084
|
agent_id=res.get("agent_id"),
|
|
989
1085
|
agent_name=res.get("name"),
|
|
990
1086
|
endpoint=res.get("endpoint"),
|
|
991
|
-
message=f"Status: {res.get('status')} ({res.get('phase')})"
|
|
1087
|
+
message=f"Status: {res.get('status')} ({res.get('phase')})",
|
|
992
1088
|
)
|
|
993
1089
|
except DryRunExit:
|
|
994
1090
|
return DeployResult(status=DeployStatus.SKIPPED, message="Dry Run executed.")
|
|
995
1091
|
except Exception as e:
|
|
996
|
-
return DeployResult(
|
|
997
|
-
status=DeployStatus.UNKNOWN,
|
|
998
|
-
message=f"查询失败: {e}"
|
|
999
|
-
)
|
|
1092
|
+
return DeployResult(status=DeployStatus.UNKNOWN, message=f"查询失败: {e}")
|
|
1000
1093
|
|
|
1001
1094
|
async def destroy(self, agent_id: str, target: DeployTarget) -> bool:
|
|
1002
1095
|
"""销毁 Agent"""
|
|
1003
1096
|
dry_run = target.extra.get("dry_run", False)
|
|
1004
1097
|
project_dir = str(target.extra.get("project_dir") or "").strip()
|
|
1005
|
-
state_file = (
|
|
1098
|
+
state_file = (
|
|
1099
|
+
Path(project_dir).resolve() if project_dir else Path(".").resolve()
|
|
1100
|
+
) / ".agentengine.state"
|
|
1006
1101
|
|
|
1007
1102
|
try:
|
|
1008
1103
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
@@ -1015,8 +1110,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1015
1110
|
os.remove(state_file)
|
|
1016
1111
|
logger.info(f"Deleted local state file: {state_file}")
|
|
1017
1112
|
except Exception as cleanup_error:
|
|
1018
|
-
logger.warning(
|
|
1019
|
-
|
|
1113
|
+
logger.warning(
|
|
1114
|
+
f"Failed to delete local state file {state_file}: {cleanup_error}"
|
|
1115
|
+
)
|
|
1116
|
+
return bool(success)
|
|
1020
1117
|
except DryRunExit:
|
|
1021
1118
|
# 让异常冒泡给 CLI 处理
|
|
1022
1119
|
raise
|
|
@@ -1030,19 +1127,27 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1030
1127
|
try:
|
|
1031
1128
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
1032
1129
|
res = await client.list_agents()
|
|
1033
|
-
|
|
1130
|
+
|
|
1034
1131
|
results = []
|
|
1035
1132
|
for agent in res.get("Agents", []):
|
|
1036
1133
|
current_status = (agent.get("status") or "").lower()
|
|
1037
|
-
results.append(
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1134
|
+
results.append(
|
|
1135
|
+
DeployResult(
|
|
1136
|
+
status=(
|
|
1137
|
+
DeployStatus.RUNNING
|
|
1138
|
+
if current_status in ("running", "ready")
|
|
1139
|
+
else (
|
|
1140
|
+
DeployStatus.DEPLOYING
|
|
1141
|
+
if current_status == "creating"
|
|
1142
|
+
else DeployStatus.UNKNOWN
|
|
1143
|
+
)
|
|
1144
|
+
),
|
|
1145
|
+
agent_id=agent.get("agent_id"),
|
|
1146
|
+
agent_name=agent.get("name"),
|
|
1147
|
+
endpoint=agent.get("endpoint"),
|
|
1148
|
+
message=agent.get("status"),
|
|
1149
|
+
)
|
|
1150
|
+
)
|
|
1046
1151
|
return results
|
|
1047
1152
|
except DryRunExit:
|
|
1048
1153
|
raise
|
|
@@ -1056,7 +1161,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1056
1161
|
try:
|
|
1057
1162
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
1058
1163
|
response = await client.chat(agent_id, message, stream=False)
|
|
1059
|
-
return response.get("output", "")
|
|
1164
|
+
return str(response.get("output", ""))
|
|
1060
1165
|
except DryRunExit:
|
|
1061
1166
|
raise
|
|
1062
1167
|
except Exception as e:
|
|
@@ -1066,7 +1171,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1066
1171
|
"""读取本地状态文件"""
|
|
1067
1172
|
if state_file.exists():
|
|
1068
1173
|
try:
|
|
1069
|
-
with open(state_file,
|
|
1174
|
+
with open(state_file, "r", encoding="utf-8") as f:
|
|
1070
1175
|
return yaml.safe_load(f) or {}
|
|
1071
1176
|
except Exception as e:
|
|
1072
1177
|
logger.warning(f"Failed to load state file: {e}")
|
|
@@ -1102,8 +1207,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1102
1207
|
def _save_state(self, state_file: Path, state: Dict[str, Any]) -> None:
|
|
1103
1208
|
"""保存状态到本地文件"""
|
|
1104
1209
|
import yaml
|
|
1210
|
+
|
|
1105
1211
|
try:
|
|
1106
|
-
with open(state_file,
|
|
1212
|
+
with open(state_file, "w", encoding="utf-8") as f:
|
|
1107
1213
|
yaml.dump(state, f, default_flow_style=False, allow_unicode=True)
|
|
1108
1214
|
logger.debug(f"State saved to {state_file}")
|
|
1109
1215
|
except Exception as e:
|
|
@@ -1112,4 +1218,5 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1112
1218
|
def _now_iso(self) -> str:
|
|
1113
1219
|
"""返回当前时间 ISO 格式"""
|
|
1114
1220
|
from datetime import datetime
|
|
1221
|
+
|
|
1115
1222
|
return datetime.now().isoformat()
|