agentengine-sdk-python 0.6.9__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +41 -23
- 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 +252 -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 +533 -726
- 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 +173 -90
- ksadk/cli/cmd_invoke.py +388 -159
- ksadk/cli/cmd_launch.py +64 -28
- ksadk/cli/cmd_mcp.py +103 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +488 -228
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +44 -14
- 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 +38 -16
- 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 +501 -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 +656 -0
- ksadk/conversations/model_context.py +22 -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 -5187
- 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 +52 -39
- ksadk/deployment/manager.py +63 -65
- ksadk/deployment/providers/serverless.py +326 -209
- 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 +267 -0
- ksadk/memory/adk/short_term_memory.py +33 -27
- 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 +1248 -164
- 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 -530
- ksadk/runners/langgraph_runner.py +490 -95
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +264 -38
- 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 -3743
- 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-C0meuOdi.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-bj2rZnP5.js → arc-CFKwSk_B.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-2u1ltDIj.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
- ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-BK8paA20.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.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-_N8bJ-EU.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
- ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-6CVKuTAe.js} +1 -1
- ksadk/server/static/assets/{dist-BMKM66B4.js → dist-BUHRxvDG.js} +1 -1
- ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-BZ9n9AjO.js} +1 -1
- ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-BibcG1V9.js} +1 -1
- ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-Bs-jQcRX.js} +1 -1
- ksadk/server/static/assets/{dist-D99wf6_X.js → dist-C2xjE7cO.js} +1 -1
- ksadk/server/static/assets/{dist-DLMjL01k.js → dist-C7-3jInC.js} +1 -1
- ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-C99THtIA.js} +1 -1
- ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CGcp2ex8.js} +1 -1
- ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-CVQRZjPu.js} +1 -1
- ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-Cf23NY4L.js} +1 -1
- ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-CgF3fj15.js} +1 -1
- ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-Cwt7koer.js} +1 -1
- ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-D82mtD2s.js} +1 -1
- ksadk/server/static/assets/{dist-B67uF2uK.js → dist-DF06ORQo.js} +1 -1
- ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-DHn6nBzr.js} +1 -1
- ksadk/server/static/assets/{dist-S4klkK5W.js → dist-Djgl2wVZ.js} +1 -1
- ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DlzCRy0S.js} +1 -1
- ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-DqNnNFrw.js} +1 -1
- ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-DyvYLMW9.js} +1 -1
- ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-eoW6tZXA.js} +1 -1
- ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-ivOya1LD.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-DtTmUan-.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.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-BR5s6Pws.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-DYPc5cvh.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
- ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DATssjp1.js} +1 -1
- ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CvMBw9cA.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.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-DPnuCB8c.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-mjB_yYkW.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.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-CwSec0Wa.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-agCVobiF.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.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 +86 -40
- ksadk/sessions/base.py +31 -11
- ksadk/sessions/continuity.py +28 -5
- ksadk/sessions/in_memory.py +46 -2
- ksadk/sessions/local_service.py +148 -17
- ksadk/sessions/postgres_service.py +216 -35
- ksadk/sessions/resilience.py +52 -0
- ksadk/sessions/resilient.py +388 -0
- 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 +136 -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 +264 -114
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/exporters/langfuse_exporter.py +91 -76
- ksadk/tracing/setup.py +132 -53
- ksadk/tracing/span_utils.py +5 -2
- ksadk/tui/__init__.py +22 -7
- ksadk/tui/loop.py +1925 -0
- ksadk/tui/stream_render.py +69 -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.6.9.dist-info/RECORD +0 -487
- ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +0 -9
- ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +0 -8
- ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
- ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
- ksadk/server/static/assets/index-BH7-pC0B.js +0 -165
- ksadk/server/static/assets/index-DAURbCsP.css +0 -1
- ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
- ksadk/tui/app.py +0 -400
- ksadk/tui/clipboard.py +0 -100
- ksadk/tui/widgets/__init__.py +0 -22
- ksadk/tui/widgets/assistant.py +0 -89
- ksadk/tui/widgets/base.py +0 -54
- ksadk/tui/widgets/chat_input.py +0 -246
- ksadk/tui/widgets/history.py +0 -156
- ksadk/tui/widgets/system.py +0 -87
- ksadk/tui/widgets/thinking.py +0 -110
- ksadk/tui/widgets/tool_call.py +0 -161
- ksadk/tui/widgets/user.py +0 -37
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.6.9.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:
|
|
@@ -261,22 +258,32 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
261
258
|
json.dump(payload, f, indent=2, ensure_ascii=False)
|
|
262
259
|
|
|
263
260
|
@staticmethod
|
|
264
|
-
def _serialize_network_config(
|
|
261
|
+
def _serialize_network_config(
|
|
262
|
+
target: DeployTarget, *, is_update: bool = False
|
|
263
|
+
) -> Optional[Dict[str, Any]]:
|
|
265
264
|
network = getattr(target, "network", None)
|
|
266
265
|
if network is None:
|
|
267
266
|
return None
|
|
268
267
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
public_access = getattr(network, "enable_public_access", None)
|
|
269
|
+
# 三态:None=未指定。create 默认开公网;update 未指定则不发 network 字段。
|
|
270
|
+
if public_access is None:
|
|
271
|
+
public_value: Optional[bool] = None if is_update else True
|
|
272
|
+
else:
|
|
273
|
+
public_value = bool(public_access)
|
|
274
|
+
|
|
275
|
+
payload: Dict[str, Any] = {}
|
|
276
|
+
if public_value is not None:
|
|
277
|
+
payload["enable_public_access"] = public_value
|
|
278
|
+
if bool(getattr(network, "enable_vpc_access", False)):
|
|
279
|
+
payload["enable_vpc_access"] = True
|
|
273
280
|
|
|
274
281
|
for field in ("vpc_id", "subnet_id", "security_group_id", "availability_zone"):
|
|
275
282
|
value = str(getattr(network, field, "") or "").strip()
|
|
276
283
|
if value:
|
|
277
284
|
payload[field] = value
|
|
278
285
|
|
|
279
|
-
if not payload
|
|
286
|
+
if not payload:
|
|
280
287
|
return None
|
|
281
288
|
return payload
|
|
282
289
|
|
|
@@ -307,19 +314,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
307
314
|
seconds = int(elapsed % 60)
|
|
308
315
|
return f"{minutes}m{seconds:02d}s"
|
|
309
316
|
|
|
310
|
-
|
|
311
317
|
async def validate_config(self, target: DeployTarget) -> tuple[bool, str]:
|
|
312
318
|
"""验证配置: 确保已配置 AgentEngine Server"""
|
|
313
|
-
|
|
319
|
+
|
|
314
320
|
server_url = os.getenv("AGENTENGINE_SERVER_URL")
|
|
315
|
-
|
|
321
|
+
|
|
316
322
|
if not server_url:
|
|
317
323
|
click.echo("⚠️ 未配置 AGENTENGINE_SERVER_URL,将尝试使用默认 Region 配置")
|
|
318
|
-
|
|
324
|
+
|
|
319
325
|
# Container 模式: 检查 Docker 是否可用
|
|
320
326
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
321
327
|
if artifact_type == "Container":
|
|
322
328
|
import subprocess
|
|
329
|
+
|
|
323
330
|
try:
|
|
324
331
|
result = subprocess.run(["docker", "version"], capture_output=True, timeout=10)
|
|
325
332
|
if result.returncode != 0:
|
|
@@ -331,14 +338,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
331
338
|
|
|
332
339
|
return True, ""
|
|
333
340
|
|
|
334
|
-
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:
|
|
335
347
|
"""打包项目
|
|
336
|
-
|
|
348
|
+
|
|
337
349
|
Code 模式: 返回基本 PackageInfo,实际构建在 build() 中进行
|
|
338
350
|
Container 模式: 使用 ContainerBuilder 打包
|
|
339
351
|
"""
|
|
340
352
|
project_path = Path(project_dir)
|
|
341
|
-
|
|
353
|
+
|
|
342
354
|
# 创建基本 PackageInfo
|
|
343
355
|
package_info = PackageInfo(
|
|
344
356
|
name=detection_result.name or project_path.name,
|
|
@@ -346,14 +358,15 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
346
358
|
build_dir=str(project_path / ".agentengine" / "build"),
|
|
347
359
|
project_dir=str(project_path),
|
|
348
360
|
entry_point=detection_result.entry_point,
|
|
349
|
-
metadata={}
|
|
361
|
+
metadata={},
|
|
350
362
|
)
|
|
351
|
-
|
|
363
|
+
|
|
352
364
|
# 尝试加载之前保存的构建元数据 (ks3_path 等)
|
|
353
365
|
try:
|
|
354
366
|
metadata_file = project_path / ".agentengine" / "build-metadata.json"
|
|
355
367
|
if metadata_file.exists():
|
|
356
368
|
import json
|
|
369
|
+
|
|
357
370
|
with open(metadata_file, "r") as f:
|
|
358
371
|
saved_data = json.load(f)
|
|
359
372
|
if "metadata" in saved_data:
|
|
@@ -366,15 +379,32 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
366
379
|
click.echo(f" 📦 已加载上次构建元数据: {count} 项")
|
|
367
380
|
except Exception as e:
|
|
368
381
|
click.secho(f"⚠️ 加载构建元数据失败: {e}", fg="yellow")
|
|
369
|
-
|
|
370
|
-
return package_info
|
|
371
|
-
|
|
372
382
|
|
|
383
|
+
return package_info
|
|
373
384
|
|
|
374
385
|
async def build(self, package_info: PackageInfo, target: DeployTarget) -> PackageInfo:
|
|
375
386
|
"""构建 & 上传 (客户端直传 KS3)"""
|
|
376
387
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
377
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
|
+
|
|
378
408
|
if artifact_type == "Code":
|
|
379
409
|
# 1. 检查是否已有 KS3 路径
|
|
380
410
|
# 优先级: CLI传入 > Metadata缓存 (仅当 !no_cache)
|
|
@@ -382,58 +412,73 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
382
412
|
cached_ks3_path = package_info.metadata.get("ks3_path")
|
|
383
413
|
no_cache = target.extra.get("no_cache", False)
|
|
384
414
|
repackage = target.extra.get("repackage", False)
|
|
385
|
-
|
|
415
|
+
|
|
386
416
|
# 如果显式传入了 ks3-path,直接使用
|
|
387
417
|
if cli_ks3_path:
|
|
388
418
|
package_info.metadata["ks3_path"] = cli_ks3_path
|
|
389
419
|
return package_info
|
|
390
|
-
|
|
420
|
+
|
|
391
421
|
# 如果没有 no_cache 且有缓存,才使用缓存
|
|
392
422
|
if not no_cache and not repackage and cached_ks3_path:
|
|
393
423
|
logger.info(f"Using cached bundle: {cached_ks3_path}")
|
|
394
424
|
return package_info
|
|
395
425
|
|
|
396
|
-
# 2. 构建 ZIP 包
|
|
397
|
-
|
|
426
|
+
# 2. 构建 ZIP 包
|
|
427
|
+
|
|
398
428
|
# 传递配置,包括 no_cache
|
|
399
429
|
builder_config = target.extra.copy()
|
|
400
430
|
builder_config["no_cache"] = no_cache
|
|
401
431
|
builder_config["repackage"] = repackage
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
+
|
|
408
445
|
# 执行构建
|
|
409
446
|
build_result = builder.build()
|
|
410
|
-
|
|
447
|
+
|
|
411
448
|
if not build_result.success:
|
|
412
449
|
raise Exception(f"构建失败: {build_result.error_message}")
|
|
413
|
-
|
|
450
|
+
|
|
414
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"]
|
|
415
457
|
# click.echo(f" ✅ ZIP 已生成: {zip_path}")
|
|
416
|
-
|
|
458
|
+
|
|
417
459
|
# 3. 直接上传 KS3 (使用本地 AK/SK)
|
|
418
460
|
# 3. 直接上传 KS3 (使用本地 AK/SK)
|
|
419
|
-
|
|
461
|
+
artifact_label = "Runtime manifest" if artifact_type == "ManagedRuntime" else "代码包"
|
|
462
|
+
click.echo(f"\n正在上传{artifact_label}到 KS3...")
|
|
420
463
|
upload_started_at = time.monotonic()
|
|
421
|
-
|
|
464
|
+
|
|
422
465
|
ks3_bucket = target.extra.get("ks3_bucket")
|
|
423
466
|
upload_region = "cn-beijing-6" if target.region == "pre-online" else target.region
|
|
424
|
-
|
|
467
|
+
|
|
425
468
|
uploader = KS3Uploader(region=upload_region, bucket=ks3_bucket)
|
|
426
|
-
|
|
469
|
+
|
|
427
470
|
# 使用时间戳确保每次上传的代码包路径唯一,支持真正的版本回滚
|
|
428
471
|
from datetime import datetime
|
|
472
|
+
|
|
429
473
|
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
430
|
-
|
|
431
|
-
|
|
474
|
+
object_prefix = "runtime" if artifact_type == "ManagedRuntime" else "code"
|
|
475
|
+
object_key = f"agents/{package_info.name}/{object_prefix}_{timestamp}.zip"
|
|
476
|
+
|
|
432
477
|
ks3_path = await uploader.upload(zip_path, object_key)
|
|
433
|
-
|
|
478
|
+
|
|
434
479
|
if not ks3_path:
|
|
435
480
|
raise Exception("KS3 上传失败")
|
|
436
|
-
|
|
481
|
+
|
|
437
482
|
logger.info(f"Upload success: {ks3_path}")
|
|
438
483
|
click.echo(f" ✓ 上传耗时: {self._format_elapsed(upload_started_at)}")
|
|
439
484
|
package_info.metadata["ks3_path"] = ks3_path
|
|
@@ -456,45 +501,47 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
456
501
|
logger.info(f"Using cached image: {cached_image}")
|
|
457
502
|
package_info.image = cached_image
|
|
458
503
|
return package_info
|
|
459
|
-
|
|
460
|
-
|
|
504
|
+
|
|
505
|
+
container_builder = ContainerBuilder(
|
|
461
506
|
project_dir=Path(package_info.project_dir),
|
|
462
507
|
tag=tag,
|
|
463
508
|
registry=registry,
|
|
464
|
-
no_cache=no_cache
|
|
509
|
+
no_cache=no_cache,
|
|
465
510
|
)
|
|
466
|
-
|
|
511
|
+
|
|
467
512
|
# 构建镜像
|
|
468
|
-
build_result =
|
|
513
|
+
build_result = container_builder.build()
|
|
469
514
|
if not build_result.success:
|
|
470
515
|
raise Exception(f"镜像构建失败: {build_result.error_message}")
|
|
471
|
-
|
|
472
|
-
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")
|
|
473
520
|
package_info.image = image_name
|
|
474
521
|
package_info.metadata["image"] = image_name
|
|
475
|
-
|
|
522
|
+
|
|
476
523
|
# 推送镜像
|
|
477
|
-
if not
|
|
524
|
+
if not container_builder.push(image_name):
|
|
478
525
|
raise Exception("镜像推送失败")
|
|
479
|
-
|
|
526
|
+
|
|
480
527
|
click.secho(f"✅ 镜像已推送: {image_name}", fg="green")
|
|
481
528
|
|
|
482
529
|
self._persist_build_metadata(package_info)
|
|
483
|
-
|
|
530
|
+
|
|
484
531
|
return package_info
|
|
485
532
|
|
|
486
533
|
async def deploy(self, package_info: PackageInfo, target: DeployTarget) -> DeployResult:
|
|
487
534
|
"""通过 AgentEngine Server 部署
|
|
488
|
-
|
|
535
|
+
|
|
489
536
|
逻辑:
|
|
490
537
|
- 读取本地 .agentengine.state 文件获取 agent_id
|
|
491
538
|
- 如果有 agent_id → 执行更新 (endpoint 不变)
|
|
492
539
|
- 如果没有 → 创建新 Agent,保存 agent_id 到状态文件
|
|
493
540
|
"""
|
|
494
|
-
|
|
541
|
+
|
|
495
542
|
server_url = os.getenv("AGENTENGINE_SERVER_URL")
|
|
496
543
|
click.echo(f"\n🚀 开始部署到 Serverless (Managed by {server_url})...")
|
|
497
|
-
|
|
544
|
+
|
|
498
545
|
# 读取本地状态文件
|
|
499
546
|
project_dir = package_info.project_dir
|
|
500
547
|
state_file = Path(project_dir) / ".agentengine.state"
|
|
@@ -517,26 +564,29 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
517
564
|
ui_state["ui_bundle_path"] = project_config.get("ui_bundle_path")
|
|
518
565
|
if project_config.get("ui_bundle_dir") and not ui_state.get("ui_bundle_dir"):
|
|
519
566
|
ui_state["ui_bundle_dir"] = project_config.get("ui_bundle_dir")
|
|
520
|
-
|
|
567
|
+
|
|
521
568
|
# 构造请求 payload
|
|
522
569
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
523
570
|
artifact_path = ""
|
|
524
|
-
|
|
571
|
+
code_backed = artifact_type == "Code"
|
|
572
|
+
if code_backed:
|
|
525
573
|
original_artifact_path = package_info.metadata.get("ks3_path", "")
|
|
526
574
|
artifact_path = original_artifact_path
|
|
527
|
-
|
|
575
|
+
|
|
528
576
|
# 转换为内网地址 (如果 serverless 无法解析 ks3://)
|
|
529
577
|
if artifact_path.startswith("ks3://"):
|
|
530
578
|
try:
|
|
531
579
|
from ksadk.common.constants import get_ks3_endpoints
|
|
532
|
-
|
|
580
|
+
|
|
533
581
|
# 确定 KS3 Region code (pre-online -> cn-beijing)
|
|
534
582
|
ks3_region = target.extra.get("ks3_region")
|
|
535
583
|
if not ks3_region:
|
|
536
|
-
|
|
537
|
-
|
|
584
|
+
ks3_region = (
|
|
585
|
+
"cn-beijing-6" if target.region == "pre-online" else target.region
|
|
586
|
+
)
|
|
587
|
+
|
|
538
588
|
_, internal_endpoint = get_ks3_endpoints(ks3_region)
|
|
539
|
-
|
|
589
|
+
|
|
540
590
|
if internal_endpoint:
|
|
541
591
|
# ks3://bucket/key -> http://bucket.internal_endpoint/key
|
|
542
592
|
bucket, key = self._parse_code_artifact_path(artifact_path)
|
|
@@ -545,32 +595,39 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
545
595
|
click.echo(f" Converted artifact path: {artifact_path}")
|
|
546
596
|
except Exception as e:
|
|
547
597
|
logger.warning(f"Failed to convert ks3 path to internal URL: {e}")
|
|
548
|
-
|
|
598
|
+
|
|
549
599
|
if not artifact_path:
|
|
550
600
|
raise ValueError(
|
|
551
|
-
"❌
|
|
552
|
-
" 在 Serverless
|
|
553
|
-
" 👉 请先执行: agentengine build --
|
|
601
|
+
"❌ 未找到 KS3 artifact 路径 (ks3_path)。\n"
|
|
602
|
+
" 在 Serverless 模式下,必须先上传构建产物。\n"
|
|
603
|
+
" 👉 请先执行: agentengine build --push\n"
|
|
554
604
|
" 或者在 deploy 命令中使用 --ks3-path 指定路径。"
|
|
555
605
|
)
|
|
556
606
|
|
|
557
|
-
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
|
+
)
|
|
558
610
|
if not parsed_bucket or not parsed_object_key:
|
|
559
611
|
raise ValueError(
|
|
560
|
-
"❌ ks3_path
|
|
612
|
+
"❌ ks3_path 格式无效,缺少 artifact 对象路径。\n"
|
|
561
613
|
f" 当前值: {original_artifact_path or '(empty)'}\n"
|
|
562
614
|
" 期望格式: ks3://<bucket>/<object-key>\n"
|
|
563
|
-
" 👉 请重新执行: agentengine build --
|
|
615
|
+
" 👉 请重新执行: agentengine build --push\n"
|
|
564
616
|
" 或者传入完整的 --ks3-path。"
|
|
565
617
|
)
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
artifact_path = package_info.image
|
|
569
|
-
|
|
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
|
+
|
|
570
626
|
# 构建 KS3 凭证
|
|
571
627
|
ks3_config = None
|
|
572
|
-
if
|
|
628
|
+
if code_backed:
|
|
573
629
|
from ksadk.common.auth import AWSV4Auth
|
|
630
|
+
|
|
574
631
|
auth = AWSV4Auth() # 读取本地 AK/SK
|
|
575
632
|
if auth.access_key_id and auth.secret_access_key:
|
|
576
633
|
# 智能推断 Bucket 和 Region
|
|
@@ -579,7 +636,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
579
636
|
bucket_name, _ = self._parse_code_artifact_path(
|
|
580
637
|
package_info.metadata.get("ks3_path", "")
|
|
581
638
|
)
|
|
582
|
-
|
|
639
|
+
|
|
583
640
|
# 如果没有提供,尝试从 artifact_path 解析
|
|
584
641
|
if not bucket_name:
|
|
585
642
|
if artifact_path.startswith("ks3://"):
|
|
@@ -589,35 +646,38 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
589
646
|
logger.info(f"Extracted bucket from ks3:// path: {bucket_name}")
|
|
590
647
|
except IndexError:
|
|
591
648
|
pass
|
|
592
|
-
elif artifact_path.startswith("http")
|
|
649
|
+
elif artifact_path.startswith("http") and "." in artifact_path:
|
|
593
650
|
try:
|
|
594
651
|
# http://bucket.endpoint/key -> bucket
|
|
595
652
|
from urllib.parse import urlparse
|
|
653
|
+
|
|
596
654
|
parsed = urlparse(artifact_path)
|
|
597
655
|
bucket_name = parsed.netloc.split(".")[0]
|
|
598
656
|
logger.info(f"Extracted bucket from HTTP URL: {bucket_name}")
|
|
599
657
|
except Exception as e:
|
|
600
658
|
logger.warning(f"Failed to extract bucket from URL: {e}")
|
|
601
|
-
|
|
659
|
+
|
|
602
660
|
# 如果仍然没有,使用智能默认值(与 KS3Uploader 逻辑一致)
|
|
603
661
|
if not bucket_name:
|
|
604
662
|
account_id = os.getenv("KSYUN_ACCOUNT_ID")
|
|
605
|
-
upload_region =
|
|
606
|
-
|
|
663
|
+
upload_region = (
|
|
664
|
+
"cn-beijing-6" if target.region == "pre-online" else target.region
|
|
665
|
+
)
|
|
666
|
+
|
|
607
667
|
if not account_id:
|
|
608
668
|
raise ValueError(
|
|
609
669
|
"❌ 缺少 KSYUN_ACCOUNT_ID 环境变量\n"
|
|
610
670
|
" Bucket 名称格式必须为: agentengine-{account_id}-{region}\n"
|
|
611
671
|
" 请在 .env 文件中设置: KSYUN_ACCOUNT_ID=你的账号ID"
|
|
612
672
|
)
|
|
613
|
-
|
|
673
|
+
|
|
614
674
|
bucket_name = f"agentengine-{account_id}-{upload_region}"
|
|
615
675
|
logger.info(f"Using default bucket: {bucket_name}")
|
|
616
|
-
|
|
676
|
+
|
|
617
677
|
# Region 逻辑需与 Build 阶段保持一致 (pre-online -> cn-beijing-6)
|
|
618
678
|
ks3_region = target.extra.get("ks3_region")
|
|
619
679
|
if not ks3_region:
|
|
620
|
-
|
|
680
|
+
ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
|
|
621
681
|
|
|
622
682
|
ks3_config = {
|
|
623
683
|
"access_key": auth.access_key_id,
|
|
@@ -626,18 +686,31 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
626
686
|
"bucket": bucket_name,
|
|
627
687
|
}
|
|
628
688
|
logger.info(f"📦 KS3 Config: bucket={bucket_name}, region={ks3_region}")
|
|
629
|
-
|
|
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
|
+
|
|
630
703
|
try:
|
|
631
704
|
# 获取 dry_run 标识
|
|
632
705
|
is_dry_run = target.extra.get("dry_run", False)
|
|
633
706
|
|
|
634
707
|
async with AgentEngineClient(region=target.region, dry_run=is_dry_run) as client:
|
|
635
708
|
agent_exists = False
|
|
636
|
-
|
|
709
|
+
|
|
637
710
|
if existing_agent_id:
|
|
638
711
|
# 有本地状态 → 先检查服务器上是否存在
|
|
639
712
|
click.echo(f" 检测到本地状态: {existing_agent_id}")
|
|
640
|
-
|
|
713
|
+
|
|
641
714
|
try:
|
|
642
715
|
# 尝试获取 agent,确认是否存在
|
|
643
716
|
existing_agent = await client.get_agent(existing_agent_id)
|
|
@@ -647,32 +720,37 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
647
720
|
# Agent 不存在或查询失败
|
|
648
721
|
err_msg = str(e).lower()
|
|
649
722
|
if "not found" in err_msg or "404" in err_msg or "不存在" in err_msg:
|
|
650
|
-
click.secho(
|
|
723
|
+
click.secho(
|
|
724
|
+
f" ⚠️ 服务器上未找到 Agent {existing_agent_id},将创建新 Agent",
|
|
725
|
+
fg="yellow",
|
|
726
|
+
)
|
|
651
727
|
agent_exists = False
|
|
652
|
-
#
|
|
653
|
-
#
|
|
728
|
+
# DryRun 异常表示真实请求被拦截,无法确认 Agent 是否存在。
|
|
729
|
+
# 为安全起见,DryRun 假设它存在并走更新路径。
|
|
654
730
|
elif "Dry Run" in str(e):
|
|
655
|
-
|
|
656
|
-
|
|
731
|
+
click.secho(
|
|
732
|
+
f" [Dry Run] 假设 Agent {existing_agent_id} 存在", fg="cyan"
|
|
733
|
+
)
|
|
734
|
+
agent_exists = True
|
|
657
735
|
else:
|
|
658
736
|
# 其他错误,重新抛出
|
|
659
737
|
raise
|
|
660
|
-
|
|
738
|
+
|
|
661
739
|
if agent_exists:
|
|
662
740
|
# Agent 存在 → 执行更新
|
|
663
|
-
click.echo(
|
|
664
|
-
|
|
741
|
+
click.echo(" 执行热更新 (endpoint 保持不变)...")
|
|
742
|
+
|
|
665
743
|
update_data = {
|
|
666
744
|
"artifact_type": artifact_type,
|
|
667
745
|
"artifact_path": artifact_path,
|
|
668
746
|
"resources": {
|
|
669
747
|
"cpu": target.resources.cpu,
|
|
670
|
-
"memory": target.resources.memory
|
|
748
|
+
"memory": target.resources.memory,
|
|
671
749
|
},
|
|
672
750
|
"scaling": {
|
|
673
751
|
"min_replicas": target.scaling.min_replicas,
|
|
674
752
|
"max_replicas": target.scaling.max_replicas,
|
|
675
|
-
"concurrency": target.scaling.concurrency
|
|
753
|
+
"concurrency": target.scaling.concurrency,
|
|
676
754
|
},
|
|
677
755
|
"observability": {
|
|
678
756
|
"langfuse_enabled": target.extra.get("enable_observability", True)
|
|
@@ -683,16 +761,20 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
683
761
|
"url": resolved_ui.url,
|
|
684
762
|
},
|
|
685
763
|
}
|
|
686
|
-
|
|
764
|
+
if runtime_config:
|
|
765
|
+
update_data["runtime_config"] = runtime_config
|
|
766
|
+
|
|
687
767
|
if ks3_config:
|
|
688
768
|
update_data["ks3"] = ks3_config
|
|
689
769
|
elif artifact_type == "Container":
|
|
690
770
|
image_credential = self._image_credential_from_env(artifact_path)
|
|
691
771
|
if image_credential:
|
|
692
772
|
update_data["image_credential"] = image_credential
|
|
693
|
-
|
|
694
|
-
f"
|
|
773
|
+
credential_target = (
|
|
774
|
+
f"{image_credential['username']}@"
|
|
775
|
+
f"{image_credential['endpoint']}"
|
|
695
776
|
)
|
|
777
|
+
click.echo(f" 🔑 镜像凭证: {credential_target}")
|
|
696
778
|
|
|
697
779
|
# 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
|
|
698
780
|
env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
|
|
@@ -710,24 +792,26 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
710
792
|
else:
|
|
711
793
|
click.echo(f" 📦 更新环境变量: {len(env_vars)} 项 from 全局配置")
|
|
712
794
|
|
|
713
|
-
network_config = self._serialize_network_config(target)
|
|
795
|
+
network_config = self._serialize_network_config(target, is_update=True)
|
|
714
796
|
if network_config:
|
|
715
797
|
update_data["network"] = network_config
|
|
716
798
|
|
|
717
799
|
storage_config = self._serialize_storage_config(target)
|
|
718
800
|
if storage_config:
|
|
719
801
|
update_data["storage"] = storage_config
|
|
720
|
-
|
|
802
|
+
|
|
721
803
|
# 注入更新时间戳,强制触发 Rolling Update (Pod 重启)
|
|
722
804
|
if "env_vars" not in update_data:
|
|
723
805
|
update_data["env_vars"] = {}
|
|
724
|
-
|
|
806
|
+
|
|
725
807
|
import time
|
|
808
|
+
|
|
726
809
|
update_data["env_vars"]["KSADK_UPDATED_AT"] = str(int(time.time()))
|
|
727
|
-
|
|
810
|
+
|
|
728
811
|
# DEBUG: 打印 Payload 确认 trigger 是否存在
|
|
729
|
-
|
|
730
|
-
|
|
812
|
+
updated_at_value = update_data["env_vars"]["KSADK_UPDATED_AT"]
|
|
813
|
+
click.echo(f" 🔄 更新 Trigger: KSADK_UPDATED_AT={updated_at_value}")
|
|
814
|
+
|
|
731
815
|
res = await client.update_agent(existing_agent_id, update_data)
|
|
732
816
|
latest_access = {}
|
|
733
817
|
if not is_dry_run:
|
|
@@ -741,38 +825,40 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
741
825
|
exc,
|
|
742
826
|
),
|
|
743
827
|
)
|
|
744
|
-
|
|
828
|
+
|
|
745
829
|
# 如果是 Dry Run,手动构造假响应以避免崩溃
|
|
746
830
|
if is_dry_run and not res:
|
|
747
831
|
res = {"name": package_info.name, "endpoint": "http://dry-run-endpoint"}
|
|
748
|
-
|
|
832
|
+
|
|
749
833
|
# 更新本地状态 (保留旧字段如 api_key)
|
|
750
834
|
new_state = local_state.copy()
|
|
751
|
-
new_state.update(
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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
|
+
)
|
|
759
845
|
if latest_access.get("api_key"):
|
|
760
846
|
new_state["api_key"] = latest_access["api_key"]
|
|
761
847
|
self._save_state(state_file, new_state)
|
|
762
|
-
|
|
848
|
+
|
|
763
849
|
return DeployResult(
|
|
764
|
-
status=DeployStatus.DEPLOYING,
|
|
850
|
+
status=DeployStatus.DEPLOYING,
|
|
765
851
|
agent_id=existing_agent_id,
|
|
766
852
|
agent_name=res.get("name"),
|
|
767
853
|
endpoint=res.get("endpoint"),
|
|
768
|
-
message=f"✅ Agent 已更新: {existing_agent_id}"
|
|
854
|
+
message=f"✅ Agent 已更新: {existing_agent_id}",
|
|
769
855
|
)
|
|
770
856
|
# else: agent_exists = False,继续执行创建逻辑
|
|
771
|
-
|
|
857
|
+
|
|
772
858
|
# 没有本地状态,或本地状态对应的 Agent 在服务器上不存在 → 创建新 Agent
|
|
773
859
|
if not existing_agent_id or not agent_exists:
|
|
774
860
|
click.echo(f" 创建新 Agent: {package_info.name}")
|
|
775
|
-
|
|
861
|
+
|
|
776
862
|
request_data = {
|
|
777
863
|
"name": package_info.name,
|
|
778
864
|
"framework": package_info.framework,
|
|
@@ -782,12 +868,12 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
782
868
|
"instance_id": target.extra.get("instance_id", "default"),
|
|
783
869
|
"resources": {
|
|
784
870
|
"cpu": target.resources.cpu,
|
|
785
|
-
"memory": target.resources.memory
|
|
871
|
+
"memory": target.resources.memory,
|
|
786
872
|
},
|
|
787
873
|
"scaling": {
|
|
788
874
|
"min_replicas": target.scaling.min_replicas,
|
|
789
875
|
"max_replicas": target.scaling.max_replicas,
|
|
790
|
-
"concurrency": target.scaling.concurrency
|
|
876
|
+
"concurrency": target.scaling.concurrency,
|
|
791
877
|
},
|
|
792
878
|
"observability": {
|
|
793
879
|
"langfuse_enabled": target.extra.get("enable_observability", True)
|
|
@@ -798,7 +884,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
798
884
|
"url": resolved_ui.url,
|
|
799
885
|
},
|
|
800
886
|
}
|
|
801
|
-
|
|
887
|
+
if runtime_config:
|
|
888
|
+
request_data["runtime_config"] = runtime_config
|
|
889
|
+
|
|
802
890
|
if ks3_config:
|
|
803
891
|
request_data["ks3"] = ks3_config
|
|
804
892
|
|
|
@@ -807,9 +895,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
807
895
|
image_credential = self._image_credential_from_env(artifact_path)
|
|
808
896
|
if image_credential:
|
|
809
897
|
request_data["image_credential"] = image_credential
|
|
810
|
-
|
|
811
|
-
f"
|
|
898
|
+
credential_target = (
|
|
899
|
+
f"{image_credential['username']}@" f"{image_credential['endpoint']}"
|
|
812
900
|
)
|
|
901
|
+
click.echo(f" 🔑 镜像凭证: {credential_target}")
|
|
813
902
|
|
|
814
903
|
# 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
|
|
815
904
|
env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
|
|
@@ -827,9 +916,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
827
916
|
click.echo(f" 📦 加载环境变量: {len(env_vars)} 项 from 全局配置")
|
|
828
917
|
|
|
829
918
|
if env_vars:
|
|
830
|
-
|
|
919
|
+
request_data["env_vars"] = env_vars
|
|
831
920
|
|
|
832
|
-
network_config = self._serialize_network_config(target)
|
|
921
|
+
network_config = self._serialize_network_config(target, is_update=False)
|
|
833
922
|
if network_config:
|
|
834
923
|
request_data["network"] = network_config
|
|
835
924
|
|
|
@@ -842,20 +931,22 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
842
931
|
ksyun_account_id = os.getenv("KSYUN_ACCOUNT_ID")
|
|
843
932
|
if ksyun_account_id:
|
|
844
933
|
extra_headers["X-Ksc-Account-Id"] = ksyun_account_id
|
|
845
|
-
|
|
934
|
+
|
|
846
935
|
# 重新构造一个带 Header 的 client
|
|
847
|
-
async with AgentEngineClient(
|
|
936
|
+
async with AgentEngineClient(
|
|
937
|
+
region=target.region, extra_headers=extra_headers, dry_run=is_dry_run
|
|
938
|
+
) as new_client:
|
|
848
939
|
res = await new_client.create_agent(request_data)
|
|
849
|
-
|
|
940
|
+
|
|
850
941
|
# 如果是 Dry Run,手动构造假响应
|
|
851
942
|
if is_dry_run and not res:
|
|
852
943
|
res = {
|
|
853
|
-
"agent_id": "dry-run-agent-id",
|
|
854
|
-
"name": package_info.name,
|
|
855
|
-
"endpoint": "http://dry-run-endpoint",
|
|
856
|
-
"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",
|
|
857
948
|
}
|
|
858
|
-
|
|
949
|
+
|
|
859
950
|
# CreateAgentProduct 返回 order_id,需要轮询 list_agents 获取 agent_id
|
|
860
951
|
new_agent_id = res.get("agent_id")
|
|
861
952
|
order_id = res.get("order_id")
|
|
@@ -866,10 +957,13 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
866
957
|
if order_id and not new_agent_id:
|
|
867
958
|
click.echo(f" 📋 订单已创建: {order_id},等待实例创建...")
|
|
868
959
|
import time
|
|
960
|
+
|
|
869
961
|
for i in range(12): # 最多等 60s
|
|
870
962
|
time.sleep(5)
|
|
871
963
|
try:
|
|
872
|
-
detail = await client.get_agent(
|
|
964
|
+
detail = await client.get_agent(
|
|
965
|
+
name=package_info.name, include_api_key=True
|
|
966
|
+
)
|
|
873
967
|
qa = detail.get("quick_access", {})
|
|
874
968
|
basic = detail.get("basic", {})
|
|
875
969
|
new_agent_id = basic.get("agent_id")
|
|
@@ -884,7 +978,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
884
978
|
click.echo(f" ⏳ 等待中... ({(i+1)*5}s)")
|
|
885
979
|
|
|
886
980
|
if not new_agent_id:
|
|
887
|
-
click.secho(
|
|
981
|
+
click.secho(
|
|
982
|
+
" ⚠️ 实例仍在创建中,稍后使用 'agentengine status' 查看",
|
|
983
|
+
fg="yellow",
|
|
984
|
+
)
|
|
888
985
|
|
|
889
986
|
latest_access = {}
|
|
890
987
|
if new_agent_id and not is_dry_run:
|
|
@@ -903,54 +1000,63 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
903
1000
|
agent_name = latest_access.get("name") or agent_name
|
|
904
1001
|
agent_endpoint = latest_access.get("endpoint") or agent_endpoint
|
|
905
1002
|
agent_api_key = latest_access.get("api_key") or agent_api_key
|
|
906
|
-
|
|
1003
|
+
|
|
907
1004
|
# 保存 agent_id 到本地状态文件
|
|
908
|
-
self._save_state(
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
+
|
|
921
1021
|
return DeployResult(
|
|
922
|
-
status=DeployStatus.DEPLOYING,
|
|
1022
|
+
status=DeployStatus.DEPLOYING,
|
|
923
1023
|
agent_id=new_agent_id,
|
|
924
1024
|
agent_name=agent_name,
|
|
925
|
-
endpoint=agent_endpoint,
|
|
1025
|
+
endpoint=agent_endpoint,
|
|
926
1026
|
api_key=agent_api_key,
|
|
927
|
-
message=
|
|
1027
|
+
message=(
|
|
1028
|
+
f"✅ Agent ID: {new_agent_id or '(创建中)'} "
|
|
1029
|
+
f"(首次部署, 订单: {order_id or '-'})"
|
|
1030
|
+
),
|
|
928
1031
|
)
|
|
929
1032
|
|
|
1033
|
+
return DeployResult(
|
|
1034
|
+
status=DeployStatus.FAILED,
|
|
1035
|
+
message="部署流程未返回 Agent 状态",
|
|
1036
|
+
)
|
|
1037
|
+
|
|
930
1038
|
except DryRunExit as e:
|
|
931
1039
|
return DeployResult(
|
|
932
1040
|
status=DeployStatus.SKIPPED,
|
|
933
1041
|
message="✅ Dry Run Completed: 请求已打印,未执行实际变更。",
|
|
934
1042
|
metadata={"dry_run_request": e.payload or {}},
|
|
935
1043
|
)
|
|
936
|
-
|
|
1044
|
+
|
|
937
1045
|
except Exception as e:
|
|
938
1046
|
logger.error(f"Deploy failed: {e}")
|
|
939
|
-
|
|
1047
|
+
|
|
940
1048
|
# 检测名称冲突
|
|
941
1049
|
err_msg = str(e)
|
|
942
1050
|
if "Conflict" in err_msg or "409" in err_msg or "already exists" in err_msg:
|
|
943
1051
|
return DeployResult(
|
|
944
1052
|
status=DeployStatus.FAILED,
|
|
945
1053
|
message=f"❌ 部署失败: Agent 名称 '{package_info.name}' 已存在。\n"
|
|
946
|
-
|
|
947
|
-
|
|
1054
|
+
f" 提示: 请检查是否重复创建。\n"
|
|
1055
|
+
" 👉 解决方法: 请在 agentengine.yaml 中修改 'name' 字段 "
|
|
1056
|
+
"(如添加后缀) 后重试。",
|
|
948
1057
|
)
|
|
949
|
-
|
|
950
|
-
return DeployResult(
|
|
951
|
-
status=DeployStatus.FAILED,
|
|
952
|
-
message=f"Server 请求失败: {str(e)}"
|
|
953
|
-
)
|
|
1058
|
+
|
|
1059
|
+
return DeployResult(status=DeployStatus.FAILED, message=f"Server 请求失败: {str(e)}")
|
|
954
1060
|
|
|
955
1061
|
async def get_status(self, agent_id: str, target: DeployTarget) -> DeployResult:
|
|
956
1062
|
"""获取 Agent 状态"""
|
|
@@ -958,7 +1064,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
958
1064
|
try:
|
|
959
1065
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
960
1066
|
res = await client.get_agent(agent_id)
|
|
961
|
-
|
|
1067
|
+
|
|
962
1068
|
status_map = {
|
|
963
1069
|
"running": DeployStatus.RUNNING,
|
|
964
1070
|
"ready": DeployStatus.RUNNING,
|
|
@@ -968,9 +1074,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
968
1074
|
"scaling": DeployStatus.UPDATING,
|
|
969
1075
|
"failed": DeployStatus.FAILED,
|
|
970
1076
|
"error": DeployStatus.FAILED,
|
|
971
|
-
"unknown": DeployStatus.UNKNOWN
|
|
1077
|
+
"unknown": DeployStatus.UNKNOWN,
|
|
972
1078
|
}
|
|
973
|
-
|
|
1079
|
+
|
|
974
1080
|
# 使状态匹配不区分大小写,以兼容服务端的改动 (Creating -> CREATING)
|
|
975
1081
|
current_status = (res.get("status") or "").lower()
|
|
976
1082
|
return DeployResult(
|
|
@@ -978,21 +1084,20 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
978
1084
|
agent_id=res.get("agent_id"),
|
|
979
1085
|
agent_name=res.get("name"),
|
|
980
1086
|
endpoint=res.get("endpoint"),
|
|
981
|
-
message=f"Status: {res.get('status')} ({res.get('phase')})"
|
|
1087
|
+
message=f"Status: {res.get('status')} ({res.get('phase')})",
|
|
982
1088
|
)
|
|
983
1089
|
except DryRunExit:
|
|
984
1090
|
return DeployResult(status=DeployStatus.SKIPPED, message="Dry Run executed.")
|
|
985
1091
|
except Exception as e:
|
|
986
|
-
return DeployResult(
|
|
987
|
-
status=DeployStatus.UNKNOWN,
|
|
988
|
-
message=f"查询失败: {e}"
|
|
989
|
-
)
|
|
1092
|
+
return DeployResult(status=DeployStatus.UNKNOWN, message=f"查询失败: {e}")
|
|
990
1093
|
|
|
991
1094
|
async def destroy(self, agent_id: str, target: DeployTarget) -> bool:
|
|
992
1095
|
"""销毁 Agent"""
|
|
993
1096
|
dry_run = target.extra.get("dry_run", False)
|
|
994
1097
|
project_dir = str(target.extra.get("project_dir") or "").strip()
|
|
995
|
-
state_file = (
|
|
1098
|
+
state_file = (
|
|
1099
|
+
Path(project_dir).resolve() if project_dir else Path(".").resolve()
|
|
1100
|
+
) / ".agentengine.state"
|
|
996
1101
|
|
|
997
1102
|
try:
|
|
998
1103
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
@@ -1005,8 +1110,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1005
1110
|
os.remove(state_file)
|
|
1006
1111
|
logger.info(f"Deleted local state file: {state_file}")
|
|
1007
1112
|
except Exception as cleanup_error:
|
|
1008
|
-
logger.warning(
|
|
1009
|
-
|
|
1113
|
+
logger.warning(
|
|
1114
|
+
f"Failed to delete local state file {state_file}: {cleanup_error}"
|
|
1115
|
+
)
|
|
1116
|
+
return bool(success)
|
|
1010
1117
|
except DryRunExit:
|
|
1011
1118
|
# 让异常冒泡给 CLI 处理
|
|
1012
1119
|
raise
|
|
@@ -1020,19 +1127,27 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1020
1127
|
try:
|
|
1021
1128
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
1022
1129
|
res = await client.list_agents()
|
|
1023
|
-
|
|
1130
|
+
|
|
1024
1131
|
results = []
|
|
1025
1132
|
for agent in res.get("Agents", []):
|
|
1026
1133
|
current_status = (agent.get("status") or "").lower()
|
|
1027
|
-
results.append(
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
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
|
+
)
|
|
1036
1151
|
return results
|
|
1037
1152
|
except DryRunExit:
|
|
1038
1153
|
raise
|
|
@@ -1046,7 +1161,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1046
1161
|
try:
|
|
1047
1162
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
1048
1163
|
response = await client.chat(agent_id, message, stream=False)
|
|
1049
|
-
return response.get("output", "")
|
|
1164
|
+
return str(response.get("output", ""))
|
|
1050
1165
|
except DryRunExit:
|
|
1051
1166
|
raise
|
|
1052
1167
|
except Exception as e:
|
|
@@ -1056,7 +1171,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1056
1171
|
"""读取本地状态文件"""
|
|
1057
1172
|
if state_file.exists():
|
|
1058
1173
|
try:
|
|
1059
|
-
with open(state_file,
|
|
1174
|
+
with open(state_file, "r", encoding="utf-8") as f:
|
|
1060
1175
|
return yaml.safe_load(f) or {}
|
|
1061
1176
|
except Exception as e:
|
|
1062
1177
|
logger.warning(f"Failed to load state file: {e}")
|
|
@@ -1092,8 +1207,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1092
1207
|
def _save_state(self, state_file: Path, state: Dict[str, Any]) -> None:
|
|
1093
1208
|
"""保存状态到本地文件"""
|
|
1094
1209
|
import yaml
|
|
1210
|
+
|
|
1095
1211
|
try:
|
|
1096
|
-
with open(state_file,
|
|
1212
|
+
with open(state_file, "w", encoding="utf-8") as f:
|
|
1097
1213
|
yaml.dump(state, f, default_flow_style=False, allow_unicode=True)
|
|
1098
1214
|
logger.debug(f"State saved to {state_file}")
|
|
1099
1215
|
except Exception as e:
|
|
@@ -1102,4 +1218,5 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1102
1218
|
def _now_iso(self) -> str:
|
|
1103
1219
|
"""返回当前时间 ISO 格式"""
|
|
1104
1220
|
from datetime import datetime
|
|
1221
|
+
|
|
1105
1222
|
return datetime.now().isoformat()
|