agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
- agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +180 -11
- ksadk/a2a/bootstrap.py +332 -0
- ksadk/a2a/card.py +15 -0
- ksadk/a2a/context_store.py +203 -0
- ksadk/a2a/control_plane.py +1056 -0
- ksadk/a2a/credential.py +99 -0
- ksadk/a2a/event_adapter.py +149 -0
- ksadk/a2a/executor.py +361 -137
- 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 +112 -0
- ksadk/a2a/server.py +102 -62
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +1122 -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/a2a_card.py +101 -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 +872 -0
- ksadk/agui/config.py +92 -0
- ksadk/agui/routes.py +47 -0
- ksadk/api/client.py +238 -204
- ksadk/api/environment.py +27 -0
- ksadk/builders/__init__.py +2 -0
- ksadk/builders/base.py +16 -14
- ksadk/builders/code_builder.py +375 -270
- ksadk/builders/container_builder.py +240 -200
- ksadk/builders/framework_requirements.py +31 -12
- ksadk/builders/ks3_uploader.py +60 -29
- ksadk/builders/managed_runtime_builder.py +173 -0
- ksadk/builders/mcp_builder.py +91 -92
- ksadk/builders/requirements_utils.py +1 -5
- ksadk/cli/__init__.py +97 -171
- ksadk/cli/cmd_a2a.py +255 -84
- 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 +507 -740
- ksadk/cli/cmd_dashboard.py +120 -45
- ksadk/cli/cmd_deploy.py +135 -59
- ksadk/cli/cmd_destroy.py +44 -14
- ksadk/cli/cmd_eval.py +386 -0
- ksadk/cli/cmd_files.py +63 -24
- ksadk/cli/cmd_hermes.py +313 -135
- ksadk/cli/cmd_invoke.py +184 -122
- ksadk/cli/cmd_launch.py +76 -28
- ksadk/cli/cmd_mcp.py +98 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +564 -268
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +58 -73
- ksadk/cli/cmd_status.py +80 -31
- ksadk/cli/cmd_studio.py +110 -0
- ksadk/cli/cmd_version.py +20 -13
- ksadk/cli/cmd_web.py +95 -18
- ksadk/cli/deploy_utils.py +20 -20
- ksadk/cli/dry_run.py +7 -4
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/error_utils.py +37 -12
- ksadk/cli/global_options.py +4 -3
- ksadk/cli/local_runtime.py +1 -2
- ksadk/cli/model_catalog.py +6 -2
- ksadk/cli/network_options.py +41 -19
- ksadk/cli/resource_common.py +6 -2
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/storage.py +0 -1
- ksadk/cli/ui.py +11 -3
- ksadk/cli/workflow_common.py +52 -18
- ksadk/codex/__init__.py +12 -0
- ksadk/codex/client.py +1042 -0
- ksadk/codex/phase.py +143 -0
- ksadk/codex/runtime.py +940 -0
- ksadk/common/aicp_env.py +0 -1
- ksadk/common/auth.py +2 -2
- ksadk/common/constants.py +1 -3
- ksadk/common/kop_client.py +232 -0
- 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 -14
- ksadk/configs/env_registry.py +661 -116
- ksadk/configs/global_config.py +3 -2
- ksadk/configs/settings.py +133 -209
- ksadk/conversations/__init__.py +85 -92
- 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 +660 -0
- ksadk/conversations/runtime_invocation.py +395 -0
- ksadk/conversations/runtime_metadata.py +475 -0
- ksadk/conversations/runtime_observability.py +367 -0
- ksadk/conversations/runtime_payloads.py +272 -0
- ksadk/conversations/runtime_persistence.py +440 -0
- ksadk/conversations/runtime_preparation.py +426 -0
- ksadk/conversations/runtime_resume.py +836 -0
- ksadk/conversations/runtime_stream_events.py +902 -0
- ksadk/conversations/runtime_streaming.py +700 -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/managed_runtime.py +33 -0
- ksadk/deployment/manager.py +85 -71
- ksadk/deployment/providers/serverless.py +379 -210
- 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/evaluation/__init__.py +84 -0
- ksadk/evaluation/a2a_adapter.py +444 -0
- ksadk/evaluation/adapters.py +54 -0
- ksadk/evaluation/contracts.py +409 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +461 -0
- ksadk/evaluation/executor.py +118 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/target.py +73 -0
- ksadk/events/__init__.py +25 -0
- ksadk/events/parser.py +191 -0
- ksadk/events/replay.py +37 -0
- ksadk/events/runtime_event.py +273 -0
- ksadk/events/store.py +317 -0
- ksadk/harness/__init__.py +35 -0
- ksadk/harness/app.py +269 -0
- ksadk/harness/config.py +181 -0
- ksadk/harness/reasoner.py +105 -0
- ksadk/harness/runtime.py +364 -0
- ksadk/harness/sandbox.py +141 -0
- ksadk/harness/tools.py +222 -0
- ksadk/hermes_terminal.py +28 -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 +38 -33
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +195 -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 +36 -36
- ksadk/memory/adk/long_term_memory.py +34 -37
- 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 +16 -6
- ksadk/memory/tool.py +4 -0
- 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 +66 -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 +201 -0
- ksadk/model_proxy/protocol_proxy.py +22 -0
- ksadk/model_proxy/server.py +443 -0
- ksadk/model_proxy/transform.py +735 -0
- ksadk/openclaw_gateway.py +29 -14
- ksadk/runners/adk_runner.py +354 -146
- ksadk/runners/base_runner.py +71 -29
- ksadk/runners/deepagents_runner.py +0 -1
- ksadk/runners/factory.py +18 -5
- ksadk/runners/langchain_runner.py +29 -742
- ksadk/runners/langgraph_runner.py +417 -97
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +134 -33
- ksadk/runners/usage_accumulator.py +1 -0
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +23 -7
- ksadk/runtime/__init__.py +67 -0
- ksadk/runtime/adapter.py +412 -0
- ksadk/runtime/conversation_execution.py +553 -0
- ksadk/runtime/executor.py +251 -0
- ksadk/runtime/factory.py +156 -0
- ksadk/runtime/framework_adapters.py +118 -0
- ksadk/runtime/launch.py +48 -0
- ksadk/runtime/preprocessing.py +131 -0
- ksadk/runtime/runner_adapter.py +1032 -0
- ksadk/runtime/runner_loading.py +36 -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/__init__.py +39 -4
- ksadk/server/api_models.py +15 -5
- ksadk/server/app.py +24 -3928
- ksadk/server/composition.py +47 -0
- ksadk/server/factory.py +622 -0
- ksadk/server/routes/__init__.py +1 -0
- ksadk/server/routes/checkpoint_resolution.py +116 -0
- ksadk/server/routes/common.py +682 -0
- ksadk/server/routes/control.py +331 -0
- ksadk/server/routes/dependencies.py +98 -0
- ksadk/server/routes/misc.py +425 -0
- ksadk/server/routes/models.py +451 -0
- ksadk/server/routes/openai_compat.py +229 -0
- ksadk/server/routes/projection.py +848 -0
- ksadk/server/routes/routers.py +52 -0
- ksadk/server/routes/run.py +519 -0
- ksadk/server/routes/sessions.py +644 -0
- ksadk/server/routes/streaming.py +265 -0
- ksadk/server/routes/workspace.py +414 -0
- ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
- ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
- ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
- ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
- ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
- ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
- ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
- ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
- ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
- ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
- ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
- ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
- ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
- ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
- ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
- ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
- ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
- ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
- ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
- ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
- ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
- ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
- ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
- ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
- ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
- ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
- ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
- ksadk/server/static/assets/index-R52pJJvg.js +177 -0
- ksadk/server/static/assets/index-jJr3_955.css +2 -0
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
- ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
- ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.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 +47 -16
- ksadk/skills/tool_defs.py +15 -10
- ksadk/studio/__init__.py +5 -0
- ksadk/studio/agent_avatar_assets.py +120 -0
- ksadk/studio/agent_lifecycle.py +51 -0
- ksadk/studio/api.py +1046 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +177 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/authoring.py +524 -0
- ksadk/studio/authoring_coordinator.py +285 -0
- ksadk/studio/builder.py +320 -0
- ksadk/studio/capabilities.py +351 -0
- ksadk/studio/cloud.py +278 -0
- ksadk/studio/codex_agent_service.py +583 -0
- ksadk/studio/codex_builder.py +324 -0
- ksadk/studio/codex_manifest.py +278 -0
- ksadk/studio/codex_run.py +352 -0
- ksadk/studio/compiler.py +189 -0
- ksadk/studio/contracts.py +644 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/evaluation.py +192 -0
- ksadk/studio/event_store.py +284 -0
- ksadk/studio/framework_run.py +114 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +543 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +243 -0
- ksadk/studio/otel_trace.py +754 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +306 -0
- ksadk/studio/resource_catalog.py +1217 -0
- ksadk/studio/run_service.py +707 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +339 -0
- ksadk/studio/service.py +1141 -0
- ksadk/studio/shared_web.py +994 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
- ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
- ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
- ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- ksadk/studio/static/index.html +15 -0
- ksadk/studio/templates.py +630 -0
- ksadk/studio/validator.py +246 -0
- ksadk/studio/workspace.py +137 -0
- ksadk/terminal_exec_policy.py +3 -4
- ksadk/tools/gateway.py +101 -15
- ksadk/tools/result_budget.py +12 -4
- ksadk/toolsets/__init__.py +161 -37
- ksadk/toolsets/a2a.py +426 -0
- 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/__init__.py +19 -21
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/setup.py +135 -506
- ksadk/tracing/span_utils.py +0 -41
- 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/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/runners/utils/langfuse.py +0 -230
- 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
- ksadk/tracing/exporters/langfuse_exporter.py +0 -445
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/top_level.txt +0 -0
|
@@ -6,38 +6,38 @@ Serverless Provider - 金山云 Serverless 计算引擎 (AgentEngine 托管)
|
|
|
6
6
|
- Deploy 阶段: 客户端调用 AgentEngine Server API 发起部署
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import os
|
|
10
9
|
import json
|
|
11
10
|
import logging
|
|
12
|
-
import
|
|
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
|
+
from ksadk.builders.base import BaseBuilder
|
|
20
21
|
from ksadk.builders.code_builder import CodeBuilder
|
|
22
|
+
from ksadk.builders.container_builder import (
|
|
23
|
+
ContainerBuilder,
|
|
24
|
+
registry_kind_label,
|
|
25
|
+
resolve_registry_credentials,
|
|
26
|
+
)
|
|
27
|
+
from ksadk.builders.ks3_uploader import KS3Uploader
|
|
28
|
+
from ksadk.configs.env_registry import ENV_VAR_REGISTRY
|
|
29
|
+
from ksadk.configs.global_config import get_env_from_global_config
|
|
30
|
+
from ksadk.configs.settings import DEFAULT_RUNTIME_TIMEZONE
|
|
21
31
|
from ksadk.deployment.agent_access import get_latest_agent_access
|
|
22
32
|
from ksadk.deployment.base import (
|
|
23
33
|
BaseDeployProvider,
|
|
24
|
-
DeployTarget,
|
|
25
34
|
DeployResult,
|
|
26
35
|
DeployStatus,
|
|
36
|
+
DeployTarget,
|
|
27
37
|
PackageInfo,
|
|
28
38
|
)
|
|
29
39
|
from ksadk.deployment.registry import DeployProviderRegistry
|
|
30
40
|
from ksadk.deployment.ui_config import resolve_ui_config, ui_config_to_state_fields
|
|
31
|
-
from ksadk.builders.container_builder import (
|
|
32
|
-
ContainerBuilder,
|
|
33
|
-
registry_kind_label,
|
|
34
|
-
resolve_registry_credentials,
|
|
35
|
-
)
|
|
36
|
-
from ksadk.configs.settings import DEFAULT_RUNTIME_TIMEZONE
|
|
37
|
-
from ksadk.configs.env_registry import ENV_VAR_REGISTRY
|
|
38
|
-
from ksadk.configs.global_config import get_env_from_global_config
|
|
39
|
-
from ksadk.api import AgentEngineClient, DryRunExit
|
|
40
|
-
|
|
41
41
|
|
|
42
42
|
logger = logging.getLogger(__name__)
|
|
43
43
|
|
|
@@ -71,11 +71,7 @@ _DEPLOY_PROCESS_ENV_ALLOWLIST = frozenset(
|
|
|
71
71
|
)
|
|
72
72
|
_DEPLOY_PROCESS_ENV_PREFIXES = ("KSADK_", "OPENAI_", "KSYUN_", "E2B_")
|
|
73
73
|
_DEPLOY_PROCESS_ENV_DENYLIST = frozenset(
|
|
74
|
-
{
|
|
75
|
-
spec.name
|
|
76
|
-
for spec in ENV_VAR_REGISTRY
|
|
77
|
-
if spec.module in {"builders", "cli", "configs", "web"}
|
|
78
|
-
}
|
|
74
|
+
{spec.name for spec in ENV_VAR_REGISTRY if spec.module in {"builders", "cli", "configs", "web"}}
|
|
79
75
|
) | frozenset(
|
|
80
76
|
{
|
|
81
77
|
"KSADK_GLOBAL_CONFIG_ENV_KEYS",
|
|
@@ -96,7 +92,7 @@ def _should_forward_process_env(name: str) -> bool:
|
|
|
96
92
|
@DeployProviderRegistry.register("kce")
|
|
97
93
|
class ServerlessProvider(BaseDeployProvider):
|
|
98
94
|
"""金山云 Serverless 计算引擎 (AgentEngine Server 托管)
|
|
99
|
-
|
|
95
|
+
|
|
100
96
|
重构后直接继承 BaseDeployProvider,不再依赖 DockerProvider。
|
|
101
97
|
Container 模式使用 ContainerBuilder 进行打包和构建。
|
|
102
98
|
"""
|
|
@@ -109,7 +105,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
109
105
|
supports_scaling = True
|
|
110
106
|
requires_image_registry = False
|
|
111
107
|
|
|
112
|
-
def __init__(self, config: Dict[str, Any] = None):
|
|
108
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
113
109
|
self.config = config or {}
|
|
114
110
|
|
|
115
111
|
@staticmethod
|
|
@@ -124,13 +120,15 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
124
120
|
|
|
125
121
|
if password and not username and registry_kind != "personal_kcr":
|
|
126
122
|
click.secho(
|
|
127
|
-
f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME
|
|
123
|
+
f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,"
|
|
124
|
+
f"已忽略{registry_kind_label(registry_kind)}镜像凭证;"
|
|
128
125
|
"企业版 KCR 和第三方镜像仓库必须配置 KCR_USERNAME + KCR_PASSWORD",
|
|
129
126
|
fg="yellow",
|
|
130
127
|
)
|
|
131
128
|
elif registry_kind == "personal_kcr":
|
|
132
129
|
click.secho(
|
|
133
|
-
" ⚠️ 未配置个人版 KCR 镜像凭证
|
|
130
|
+
" ⚠️ 未配置个人版 KCR 镜像凭证 "
|
|
131
|
+
"(KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取",
|
|
134
132
|
fg="yellow",
|
|
135
133
|
)
|
|
136
134
|
else:
|
|
@@ -196,19 +194,25 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
196
194
|
) -> tuple[Dict[str, str], bool, int]:
|
|
197
195
|
"""读取部署时注入到托管运行时的环境变量。
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
Code/Container 制品打包,只通过 deploy payload 注入到 Pod 环境变量。
|
|
197
|
+
优先级: --env/--env-file (explicit) > shell env (转发白名单前缀) > 项目 .env > 全局配置。
|
|
198
|
+
真实 .env 文件不会随 Code/Container 制品打包,只通过 deploy payload 注入到 Pod 环境变量。
|
|
201
199
|
"""
|
|
200
|
+
shell_keys = set(os.environ)
|
|
202
201
|
env_vars: Dict[str, str] = dict(get_env_from_global_config())
|
|
203
202
|
env_file = Path(project_dir) / ".env"
|
|
204
203
|
project_env_count = 0
|
|
205
|
-
for key, value in sorted(os.environ.items()):
|
|
206
|
-
if value and _should_forward_process_env(key):
|
|
207
|
-
env_vars.setdefault(key, value)
|
|
208
204
|
if env_file.exists():
|
|
209
205
|
project_env = cls._load_project_env_vars(env_file)
|
|
210
206
|
project_env_count = len(project_env)
|
|
211
|
-
|
|
207
|
+
# auto .env 覆盖 global_config,但不覆盖 shell (shell 优先于 auto .env)
|
|
208
|
+
for key, value in project_env.items():
|
|
209
|
+
if key not in shell_keys:
|
|
210
|
+
env_vars[key] = value
|
|
211
|
+
# shell 转发 (仅 KSADK_/OPENAI_/KSYUN_/E2B_ 前缀 + 白名单);shell 覆盖 auto .env 与全局配置
|
|
212
|
+
for key, value in sorted(os.environ.items()):
|
|
213
|
+
if value and _should_forward_process_env(key):
|
|
214
|
+
env_vars[key] = value
|
|
215
|
+
# explicit --env/--env-file (显式 CLI 意图最高)
|
|
212
216
|
env_vars.update(explicit_env_vars or {})
|
|
213
217
|
env_vars.setdefault("TZ", DEFAULT_RUNTIME_TIMEZONE)
|
|
214
218
|
return env_vars, env_file.exists(), project_env_count
|
|
@@ -269,7 +273,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
269
273
|
return None
|
|
270
274
|
|
|
271
275
|
public_access = getattr(network, "enable_public_access", None)
|
|
272
|
-
# 三态:None=未指定。create 默认开公网;update 未指定则不发 network
|
|
276
|
+
# 三态:None=未指定。create 默认开公网;update 未指定则不发 network 字段。
|
|
273
277
|
if public_access is None:
|
|
274
278
|
public_value: Optional[bool] = None if is_update else True
|
|
275
279
|
else:
|
|
@@ -317,19 +321,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
317
321
|
seconds = int(elapsed % 60)
|
|
318
322
|
return f"{minutes}m{seconds:02d}s"
|
|
319
323
|
|
|
320
|
-
|
|
321
324
|
async def validate_config(self, target: DeployTarget) -> tuple[bool, str]:
|
|
322
325
|
"""验证配置: 确保已配置 AgentEngine Server"""
|
|
323
|
-
|
|
326
|
+
|
|
324
327
|
server_url = os.getenv("AGENTENGINE_SERVER_URL")
|
|
325
|
-
|
|
328
|
+
|
|
326
329
|
if not server_url:
|
|
327
330
|
click.echo("⚠️ 未配置 AGENTENGINE_SERVER_URL,将尝试使用默认 Region 配置")
|
|
328
|
-
|
|
331
|
+
|
|
329
332
|
# Container 模式: 检查 Docker 是否可用
|
|
330
333
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
331
334
|
if artifact_type == "Container":
|
|
332
335
|
import subprocess
|
|
336
|
+
|
|
333
337
|
try:
|
|
334
338
|
result = subprocess.run(["docker", "version"], capture_output=True, timeout=10)
|
|
335
339
|
if result.returncode != 0:
|
|
@@ -341,14 +345,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
341
345
|
|
|
342
346
|
return True, ""
|
|
343
347
|
|
|
344
|
-
async def package(
|
|
348
|
+
async def package(
|
|
349
|
+
self,
|
|
350
|
+
project_dir: str,
|
|
351
|
+
detection_result: Any,
|
|
352
|
+
config: Optional[Dict[str, Any]] = None,
|
|
353
|
+
) -> PackageInfo:
|
|
345
354
|
"""打包项目
|
|
346
|
-
|
|
355
|
+
|
|
347
356
|
Code 模式: 返回基本 PackageInfo,实际构建在 build() 中进行
|
|
348
357
|
Container 模式: 使用 ContainerBuilder 打包
|
|
349
358
|
"""
|
|
350
359
|
project_path = Path(project_dir)
|
|
351
|
-
|
|
360
|
+
|
|
352
361
|
# 创建基本 PackageInfo
|
|
353
362
|
package_info = PackageInfo(
|
|
354
363
|
name=detection_result.name or project_path.name,
|
|
@@ -356,14 +365,15 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
356
365
|
build_dir=str(project_path / ".agentengine" / "build"),
|
|
357
366
|
project_dir=str(project_path),
|
|
358
367
|
entry_point=detection_result.entry_point,
|
|
359
|
-
metadata={}
|
|
368
|
+
metadata={},
|
|
360
369
|
)
|
|
361
|
-
|
|
370
|
+
|
|
362
371
|
# 尝试加载之前保存的构建元数据 (ks3_path 等)
|
|
363
372
|
try:
|
|
364
373
|
metadata_file = project_path / ".agentengine" / "build-metadata.json"
|
|
365
374
|
if metadata_file.exists():
|
|
366
375
|
import json
|
|
376
|
+
|
|
367
377
|
with open(metadata_file, "r") as f:
|
|
368
378
|
saved_data = json.load(f)
|
|
369
379
|
if "metadata" in saved_data:
|
|
@@ -376,15 +386,18 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
376
386
|
click.echo(f" 📦 已加载上次构建元数据: {count} 项")
|
|
377
387
|
except Exception as e:
|
|
378
388
|
click.secho(f"⚠️ 加载构建元数据失败: {e}", fg="yellow")
|
|
379
|
-
|
|
380
|
-
return package_info
|
|
381
|
-
|
|
382
389
|
|
|
390
|
+
return package_info
|
|
383
391
|
|
|
384
392
|
async def build(self, package_info: PackageInfo, target: DeployTarget) -> PackageInfo:
|
|
385
393
|
"""构建 & 上传 (客户端直传 KS3)"""
|
|
386
394
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
387
395
|
|
|
396
|
+
if artifact_type == "ManagedRuntime":
|
|
397
|
+
from ksadk.deployment.managed_runtime import build_managed_runtime_package
|
|
398
|
+
|
|
399
|
+
return build_managed_runtime_package(package_info, target)
|
|
400
|
+
|
|
388
401
|
if artifact_type == "Code":
|
|
389
402
|
# 1. 检查是否已有 KS3 路径
|
|
390
403
|
# 优先级: CLI传入 > Metadata缓存 (仅当 !no_cache)
|
|
@@ -392,58 +405,77 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
392
405
|
cached_ks3_path = package_info.metadata.get("ks3_path")
|
|
393
406
|
no_cache = target.extra.get("no_cache", False)
|
|
394
407
|
repackage = target.extra.get("repackage", False)
|
|
395
|
-
|
|
408
|
+
|
|
396
409
|
# 如果显式传入了 ks3-path,直接使用
|
|
397
410
|
if cli_ks3_path:
|
|
398
411
|
package_info.metadata["ks3_path"] = cli_ks3_path
|
|
399
412
|
return package_info
|
|
400
|
-
|
|
413
|
+
|
|
401
414
|
# 如果没有 no_cache 且有缓存,才使用缓存
|
|
402
415
|
if not no_cache and not repackage and cached_ks3_path:
|
|
403
416
|
logger.info(f"Using cached bundle: {cached_ks3_path}")
|
|
404
417
|
return package_info
|
|
405
418
|
|
|
406
|
-
# 2. 构建 ZIP 包
|
|
407
|
-
|
|
419
|
+
# 2. 构建 ZIP 包
|
|
420
|
+
|
|
408
421
|
# 传递配置,包括 no_cache
|
|
409
422
|
builder_config = target.extra.copy()
|
|
410
423
|
builder_config["no_cache"] = no_cache
|
|
411
424
|
builder_config["repackage"] = repackage
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
425
|
+
|
|
426
|
+
artifact_builder: BaseBuilder
|
|
427
|
+
if artifact_type == "ManagedRuntime":
|
|
428
|
+
from ksadk.builders.managed_runtime_builder import ManagedRuntimeBuilder
|
|
429
|
+
|
|
430
|
+
artifact_builder = ManagedRuntimeBuilder(
|
|
431
|
+
Path(package_info.project_dir),
|
|
432
|
+
config=builder_config,
|
|
433
|
+
runtime_version=str(target.extra.get("runtime_version") or ""),
|
|
434
|
+
)
|
|
435
|
+
else:
|
|
436
|
+
# CodeBuilder 直接操作原始 project_dir。
|
|
437
|
+
artifact_builder = CodeBuilder(
|
|
438
|
+
Path(package_info.project_dir),
|
|
439
|
+
config=builder_config,
|
|
440
|
+
)
|
|
441
|
+
|
|
418
442
|
# 执行构建
|
|
419
|
-
build_result =
|
|
420
|
-
|
|
443
|
+
build_result = artifact_builder.build()
|
|
444
|
+
|
|
421
445
|
if not build_result.success:
|
|
422
446
|
raise Exception(f"构建失败: {build_result.error_message}")
|
|
423
|
-
|
|
447
|
+
|
|
424
448
|
zip_path = build_result.artifact_path
|
|
449
|
+
if zip_path is None:
|
|
450
|
+
raise RuntimeError("代码构建成功但未生成 artifact_path")
|
|
451
|
+
package_info.metadata.update(build_result.metadata)
|
|
452
|
+
if build_result.metadata.get("manifest_sha256"):
|
|
453
|
+
target.extra["manifest_sha256"] = build_result.metadata["manifest_sha256"]
|
|
425
454
|
# click.echo(f" ✅ ZIP 已生成: {zip_path}")
|
|
426
|
-
|
|
455
|
+
|
|
427
456
|
# 3. 直接上传 KS3 (使用本地 AK/SK)
|
|
428
457
|
# 3. 直接上传 KS3 (使用本地 AK/SK)
|
|
429
|
-
|
|
458
|
+
artifact_label = "Runtime manifest" if artifact_type == "ManagedRuntime" else "代码包"
|
|
459
|
+
click.echo(f"\n正在上传{artifact_label}到 KS3...")
|
|
430
460
|
upload_started_at = time.monotonic()
|
|
431
|
-
|
|
461
|
+
|
|
432
462
|
ks3_bucket = target.extra.get("ks3_bucket")
|
|
433
463
|
upload_region = "cn-beijing-6" if target.region == "pre-online" else target.region
|
|
434
|
-
|
|
464
|
+
|
|
435
465
|
uploader = KS3Uploader(region=upload_region, bucket=ks3_bucket)
|
|
436
|
-
|
|
466
|
+
|
|
437
467
|
# 使用时间戳确保每次上传的代码包路径唯一,支持真正的版本回滚
|
|
438
468
|
from datetime import datetime
|
|
469
|
+
|
|
439
470
|
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
440
|
-
|
|
441
|
-
|
|
471
|
+
object_prefix = "runtime" if artifact_type == "ManagedRuntime" else "code"
|
|
472
|
+
object_key = f"agents/{package_info.name}/{object_prefix}_{timestamp}.zip"
|
|
473
|
+
|
|
442
474
|
ks3_path = await uploader.upload(zip_path, object_key)
|
|
443
|
-
|
|
475
|
+
|
|
444
476
|
if not ks3_path:
|
|
445
477
|
raise Exception("KS3 上传失败")
|
|
446
|
-
|
|
478
|
+
|
|
447
479
|
logger.info(f"Upload success: {ks3_path}")
|
|
448
480
|
click.echo(f" ✓ 上传耗时: {self._format_elapsed(upload_started_at)}")
|
|
449
481
|
package_info.metadata["ks3_path"] = ks3_path
|
|
@@ -466,45 +498,47 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
466
498
|
logger.info(f"Using cached image: {cached_image}")
|
|
467
499
|
package_info.image = cached_image
|
|
468
500
|
return package_info
|
|
469
|
-
|
|
470
|
-
|
|
501
|
+
|
|
502
|
+
container_builder = ContainerBuilder(
|
|
471
503
|
project_dir=Path(package_info.project_dir),
|
|
472
504
|
tag=tag,
|
|
473
505
|
registry=registry,
|
|
474
|
-
no_cache=no_cache
|
|
506
|
+
no_cache=no_cache,
|
|
475
507
|
)
|
|
476
|
-
|
|
508
|
+
|
|
477
509
|
# 构建镜像
|
|
478
|
-
build_result =
|
|
510
|
+
build_result = container_builder.build()
|
|
479
511
|
if not build_result.success:
|
|
480
512
|
raise Exception(f"镜像构建失败: {build_result.error_message}")
|
|
481
|
-
|
|
482
|
-
image_name = build_result.metadata.get("image")
|
|
513
|
+
|
|
514
|
+
image_name = str(build_result.metadata.get("image") or "").strip()
|
|
515
|
+
if not image_name:
|
|
516
|
+
raise RuntimeError("容器构建成功但未生成 image")
|
|
483
517
|
package_info.image = image_name
|
|
484
518
|
package_info.metadata["image"] = image_name
|
|
485
|
-
|
|
519
|
+
|
|
486
520
|
# 推送镜像
|
|
487
|
-
if not
|
|
521
|
+
if not container_builder.push(image_name):
|
|
488
522
|
raise Exception("镜像推送失败")
|
|
489
|
-
|
|
523
|
+
|
|
490
524
|
click.secho(f"✅ 镜像已推送: {image_name}", fg="green")
|
|
491
525
|
|
|
492
526
|
self._persist_build_metadata(package_info)
|
|
493
|
-
|
|
527
|
+
|
|
494
528
|
return package_info
|
|
495
529
|
|
|
496
530
|
async def deploy(self, package_info: PackageInfo, target: DeployTarget) -> DeployResult:
|
|
497
531
|
"""通过 AgentEngine Server 部署
|
|
498
|
-
|
|
532
|
+
|
|
499
533
|
逻辑:
|
|
500
534
|
- 读取本地 .agentengine.state 文件获取 agent_id
|
|
501
535
|
- 如果有 agent_id → 执行更新 (endpoint 不变)
|
|
502
536
|
- 如果没有 → 创建新 Agent,保存 agent_id 到状态文件
|
|
503
537
|
"""
|
|
504
|
-
|
|
538
|
+
|
|
505
539
|
server_url = os.getenv("AGENTENGINE_SERVER_URL")
|
|
506
540
|
click.echo(f"\n🚀 开始部署到 Serverless (Managed by {server_url})...")
|
|
507
|
-
|
|
541
|
+
|
|
508
542
|
# 读取本地状态文件
|
|
509
543
|
project_dir = package_info.project_dir
|
|
510
544
|
state_file = Path(project_dir) / ".agentengine.state"
|
|
@@ -527,26 +561,29 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
527
561
|
ui_state["ui_bundle_path"] = project_config.get("ui_bundle_path")
|
|
528
562
|
if project_config.get("ui_bundle_dir") and not ui_state.get("ui_bundle_dir"):
|
|
529
563
|
ui_state["ui_bundle_dir"] = project_config.get("ui_bundle_dir")
|
|
530
|
-
|
|
564
|
+
|
|
531
565
|
# 构造请求 payload
|
|
532
566
|
artifact_type = target.extra.get("artifact_type", "Code")
|
|
533
567
|
artifact_path = ""
|
|
534
|
-
|
|
568
|
+
code_backed = artifact_type == "Code"
|
|
569
|
+
if code_backed:
|
|
535
570
|
original_artifact_path = package_info.metadata.get("ks3_path", "")
|
|
536
571
|
artifact_path = original_artifact_path
|
|
537
|
-
|
|
572
|
+
|
|
538
573
|
# 转换为内网地址 (如果 serverless 无法解析 ks3://)
|
|
539
574
|
if artifact_path.startswith("ks3://"):
|
|
540
575
|
try:
|
|
541
576
|
from ksadk.common.constants import get_ks3_endpoints
|
|
542
|
-
|
|
577
|
+
|
|
543
578
|
# 确定 KS3 Region code (pre-online -> cn-beijing)
|
|
544
579
|
ks3_region = target.extra.get("ks3_region")
|
|
545
580
|
if not ks3_region:
|
|
546
|
-
|
|
547
|
-
|
|
581
|
+
ks3_region = (
|
|
582
|
+
"cn-beijing-6" if target.region == "pre-online" else target.region
|
|
583
|
+
)
|
|
584
|
+
|
|
548
585
|
_, internal_endpoint = get_ks3_endpoints(ks3_region)
|
|
549
|
-
|
|
586
|
+
|
|
550
587
|
if internal_endpoint:
|
|
551
588
|
# ks3://bucket/key -> http://bucket.internal_endpoint/key
|
|
552
589
|
bucket, key = self._parse_code_artifact_path(artifact_path)
|
|
@@ -555,32 +592,39 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
555
592
|
click.echo(f" Converted artifact path: {artifact_path}")
|
|
556
593
|
except Exception as e:
|
|
557
594
|
logger.warning(f"Failed to convert ks3 path to internal URL: {e}")
|
|
558
|
-
|
|
595
|
+
|
|
559
596
|
if not artifact_path:
|
|
560
597
|
raise ValueError(
|
|
561
|
-
"❌
|
|
562
|
-
" 在 Serverless
|
|
563
|
-
" 👉 请先执行: agentengine build --
|
|
598
|
+
"❌ 未找到 KS3 artifact 路径 (ks3_path)。\n"
|
|
599
|
+
" 在 Serverless 模式下,必须先上传构建产物。\n"
|
|
600
|
+
" 👉 请先执行: agentengine build --push\n"
|
|
564
601
|
" 或者在 deploy 命令中使用 --ks3-path 指定路径。"
|
|
565
602
|
)
|
|
566
603
|
|
|
567
|
-
parsed_bucket, parsed_object_key = self._parse_code_artifact_path(
|
|
604
|
+
parsed_bucket, parsed_object_key = self._parse_code_artifact_path(
|
|
605
|
+
original_artifact_path
|
|
606
|
+
)
|
|
568
607
|
if not parsed_bucket or not parsed_object_key:
|
|
569
608
|
raise ValueError(
|
|
570
|
-
"❌ ks3_path
|
|
609
|
+
"❌ ks3_path 格式无效,缺少 artifact 对象路径。\n"
|
|
571
610
|
f" 当前值: {original_artifact_path or '(empty)'}\n"
|
|
572
611
|
" 期望格式: ks3://<bucket>/<object-key>\n"
|
|
573
|
-
" 👉 请重新执行: agentengine build --
|
|
612
|
+
" 👉 请重新执行: agentengine build --push\n"
|
|
574
613
|
" 或者传入完整的 --ks3-path。"
|
|
575
614
|
)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
artifact_path = package_info.image
|
|
579
|
-
|
|
615
|
+
|
|
616
|
+
elif artifact_type == "Container":
|
|
617
|
+
artifact_path = str(package_info.image or "").strip()
|
|
618
|
+
if not artifact_path:
|
|
619
|
+
raise ValueError("Container 部署缺少 image")
|
|
620
|
+
elif artifact_type != "ManagedRuntime":
|
|
621
|
+
raise ValueError(f"Unsupported artifact type: {artifact_type}")
|
|
622
|
+
|
|
580
623
|
# 构建 KS3 凭证
|
|
581
624
|
ks3_config = None
|
|
582
|
-
if
|
|
625
|
+
if code_backed:
|
|
583
626
|
from ksadk.common.auth import AWSV4Auth
|
|
627
|
+
|
|
584
628
|
auth = AWSV4Auth() # 读取本地 AK/SK
|
|
585
629
|
if auth.access_key_id and auth.secret_access_key:
|
|
586
630
|
# 智能推断 Bucket 和 Region
|
|
@@ -589,7 +633,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
589
633
|
bucket_name, _ = self._parse_code_artifact_path(
|
|
590
634
|
package_info.metadata.get("ks3_path", "")
|
|
591
635
|
)
|
|
592
|
-
|
|
636
|
+
|
|
593
637
|
# 如果没有提供,尝试从 artifact_path 解析
|
|
594
638
|
if not bucket_name:
|
|
595
639
|
if artifact_path.startswith("ks3://"):
|
|
@@ -599,35 +643,38 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
599
643
|
logger.info(f"Extracted bucket from ks3:// path: {bucket_name}")
|
|
600
644
|
except IndexError:
|
|
601
645
|
pass
|
|
602
|
-
elif artifact_path.startswith("http")
|
|
646
|
+
elif artifact_path.startswith("http") and "." in artifact_path:
|
|
603
647
|
try:
|
|
604
648
|
# http://bucket.endpoint/key -> bucket
|
|
605
649
|
from urllib.parse import urlparse
|
|
650
|
+
|
|
606
651
|
parsed = urlparse(artifact_path)
|
|
607
652
|
bucket_name = parsed.netloc.split(".")[0]
|
|
608
653
|
logger.info(f"Extracted bucket from HTTP URL: {bucket_name}")
|
|
609
654
|
except Exception as e:
|
|
610
655
|
logger.warning(f"Failed to extract bucket from URL: {e}")
|
|
611
|
-
|
|
656
|
+
|
|
612
657
|
# 如果仍然没有,使用智能默认值(与 KS3Uploader 逻辑一致)
|
|
613
658
|
if not bucket_name:
|
|
614
659
|
account_id = os.getenv("KSYUN_ACCOUNT_ID")
|
|
615
|
-
upload_region =
|
|
616
|
-
|
|
660
|
+
upload_region = (
|
|
661
|
+
"cn-beijing-6" if target.region == "pre-online" else target.region
|
|
662
|
+
)
|
|
663
|
+
|
|
617
664
|
if not account_id:
|
|
618
665
|
raise ValueError(
|
|
619
666
|
"❌ 缺少 KSYUN_ACCOUNT_ID 环境变量\n"
|
|
620
667
|
" Bucket 名称格式必须为: agentengine-{account_id}-{region}\n"
|
|
621
668
|
" 请在 .env 文件中设置: KSYUN_ACCOUNT_ID=你的账号ID"
|
|
622
669
|
)
|
|
623
|
-
|
|
670
|
+
|
|
624
671
|
bucket_name = f"agentengine-{account_id}-{upload_region}"
|
|
625
672
|
logger.info(f"Using default bucket: {bucket_name}")
|
|
626
|
-
|
|
673
|
+
|
|
627
674
|
# Region 逻辑需与 Build 阶段保持一致 (pre-online -> cn-beijing-6)
|
|
628
675
|
ks3_region = target.extra.get("ks3_region")
|
|
629
676
|
if not ks3_region:
|
|
630
|
-
|
|
677
|
+
ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
|
|
631
678
|
|
|
632
679
|
ks3_config = {
|
|
633
680
|
"access_key": auth.access_key_id,
|
|
@@ -636,18 +683,96 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
636
683
|
"bucket": bucket_name,
|
|
637
684
|
}
|
|
638
685
|
logger.info(f"📦 KS3 Config: bucket={bucket_name}, region={ks3_region}")
|
|
639
|
-
|
|
686
|
+
|
|
687
|
+
runtime_config = None
|
|
688
|
+
if artifact_type == "ManagedRuntime":
|
|
689
|
+
runtime_config = {
|
|
690
|
+
"name": str(target.extra.get("runtime_name") or "").strip(),
|
|
691
|
+
"version": str(target.extra.get("runtime_version") or "").strip(),
|
|
692
|
+
"manifest_sha256": str(target.extra.get("manifest_sha256") or "").strip(),
|
|
693
|
+
}
|
|
694
|
+
missing = [key for key, value in runtime_config.items() if not value]
|
|
695
|
+
if missing:
|
|
696
|
+
raise ValueError(
|
|
697
|
+
"ManagedRuntime 部署缺少 "
|
|
698
|
+
+ ", ".join(f"runtime_config.{key}" for key in missing)
|
|
699
|
+
)
|
|
700
|
+
|
|
640
701
|
try:
|
|
641
702
|
# 获取 dry_run 标识
|
|
642
703
|
is_dry_run = target.extra.get("dry_run", False)
|
|
643
704
|
|
|
644
705
|
async with AgentEngineClient(region=target.region, dry_run=is_dry_run) as client:
|
|
645
706
|
agent_exists = False
|
|
646
|
-
|
|
647
|
-
|
|
707
|
+
|
|
708
|
+
# 显式 --agent-id:优先于本地 state,用于状态丢失后重新关联已有 Agent。
|
|
709
|
+
explicit_agent_id = (target.extra.get("agent_id") or "").strip() or None
|
|
710
|
+
if explicit_agent_id:
|
|
711
|
+
if existing_agent_id and existing_agent_id != explicit_agent_id:
|
|
712
|
+
click.secho(
|
|
713
|
+
f" ⚠️ --agent-id ({explicit_agent_id}) 与本地状态 "
|
|
714
|
+
f"({existing_agent_id}) 不一致,以 --agent-id 为准",
|
|
715
|
+
fg="yellow",
|
|
716
|
+
)
|
|
717
|
+
if is_dry_run:
|
|
718
|
+
# DryRun 无法真实校验,假设存在并走更新路径
|
|
719
|
+
existing_agent_id = explicit_agent_id
|
|
720
|
+
agent_exists = True
|
|
721
|
+
click.secho(
|
|
722
|
+
f" [Dry Run] 假设 Agent {explicit_agent_id} 存在", fg="cyan"
|
|
723
|
+
)
|
|
724
|
+
else:
|
|
725
|
+
try:
|
|
726
|
+
detail = await client.get_agent(
|
|
727
|
+
explicit_agent_id, include_api_key=True
|
|
728
|
+
)
|
|
729
|
+
except Exception as e:
|
|
730
|
+
return DeployResult(
|
|
731
|
+
status=DeployStatus.FAILED,
|
|
732
|
+
agent_id=explicit_agent_id,
|
|
733
|
+
message=(
|
|
734
|
+
f"❌ 指定的 Agent ID '{explicit_agent_id}' 不存在,"
|
|
735
|
+
f"或当前凭证无权限访问。\n"
|
|
736
|
+
f" 详情: {e}\n"
|
|
737
|
+
" 👉 请确认 agent_id 正确,且当前 AK/SK / 账号"
|
|
738
|
+
"有该 Agent 的权限。"
|
|
739
|
+
),
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
# 校验通过 → 关联并回填 state,走热更新
|
|
743
|
+
qa = detail.get("quick_access", {}) or {}
|
|
744
|
+
basic = detail.get("basic", {}) or {}
|
|
745
|
+
recovered_state = local_state.copy()
|
|
746
|
+
recovered_state.update(
|
|
747
|
+
{
|
|
748
|
+
"agent_id": explicit_agent_id,
|
|
749
|
+
"name": basic.get("name") or package_info.name,
|
|
750
|
+
"region": target.region,
|
|
751
|
+
"endpoint": qa.get("public_endpoint"),
|
|
752
|
+
"updated_at": self._now_iso(),
|
|
753
|
+
}
|
|
754
|
+
)
|
|
755
|
+
if qa.get("api_key"):
|
|
756
|
+
recovered_state["api_key"] = qa["api_key"]
|
|
757
|
+
# 去掉 None 值,避免覆盖掉旧的有效字段
|
|
758
|
+
recovered_state = {
|
|
759
|
+
k: v for k, v in recovered_state.items() if v is not None
|
|
760
|
+
}
|
|
761
|
+
self._save_state(state_file, recovered_state)
|
|
762
|
+
local_state = recovered_state
|
|
763
|
+
|
|
764
|
+
existing_agent_id = explicit_agent_id
|
|
765
|
+
agent_exists = True
|
|
766
|
+
click.secho(
|
|
767
|
+
f" 🔗 已通过 --agent-id 关联 Agent: {explicit_agent_id} "
|
|
768
|
+
f"(已回填 .agentengine.state)",
|
|
769
|
+
fg="green",
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
if existing_agent_id and not agent_exists:
|
|
648
773
|
# 有本地状态 → 先检查服务器上是否存在
|
|
649
774
|
click.echo(f" 检测到本地状态: {existing_agent_id}")
|
|
650
|
-
|
|
775
|
+
|
|
651
776
|
try:
|
|
652
777
|
# 尝试获取 agent,确认是否存在
|
|
653
778
|
existing_agent = await client.get_agent(existing_agent_id)
|
|
@@ -657,32 +782,37 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
657
782
|
# Agent 不存在或查询失败
|
|
658
783
|
err_msg = str(e).lower()
|
|
659
784
|
if "not found" in err_msg or "404" in err_msg or "不存在" in err_msg:
|
|
660
|
-
click.secho(
|
|
785
|
+
click.secho(
|
|
786
|
+
f" ⚠️ 服务器上未找到 Agent {existing_agent_id},将创建新 Agent",
|
|
787
|
+
fg="yellow",
|
|
788
|
+
)
|
|
661
789
|
agent_exists = False
|
|
662
|
-
#
|
|
663
|
-
#
|
|
790
|
+
# DryRun 异常表示真实请求被拦截,无法确认 Agent 是否存在。
|
|
791
|
+
# 为安全起见,DryRun 假设它存在并走更新路径。
|
|
664
792
|
elif "Dry Run" in str(e):
|
|
665
|
-
|
|
666
|
-
|
|
793
|
+
click.secho(
|
|
794
|
+
f" [Dry Run] 假设 Agent {existing_agent_id} 存在", fg="cyan"
|
|
795
|
+
)
|
|
796
|
+
agent_exists = True
|
|
667
797
|
else:
|
|
668
798
|
# 其他错误,重新抛出
|
|
669
799
|
raise
|
|
670
|
-
|
|
800
|
+
|
|
671
801
|
if agent_exists:
|
|
672
802
|
# Agent 存在 → 执行更新
|
|
673
|
-
click.echo(
|
|
674
|
-
|
|
803
|
+
click.echo(" 执行热更新 (endpoint 保持不变)...")
|
|
804
|
+
|
|
675
805
|
update_data = {
|
|
676
806
|
"artifact_type": artifact_type,
|
|
677
807
|
"artifact_path": artifact_path,
|
|
678
808
|
"resources": {
|
|
679
809
|
"cpu": target.resources.cpu,
|
|
680
|
-
"memory": target.resources.memory
|
|
810
|
+
"memory": target.resources.memory,
|
|
681
811
|
},
|
|
682
812
|
"scaling": {
|
|
683
813
|
"min_replicas": target.scaling.min_replicas,
|
|
684
814
|
"max_replicas": target.scaling.max_replicas,
|
|
685
|
-
"concurrency": target.scaling.concurrency
|
|
815
|
+
"concurrency": target.scaling.concurrency,
|
|
686
816
|
},
|
|
687
817
|
"observability": {
|
|
688
818
|
"langfuse_enabled": target.extra.get("enable_observability", True)
|
|
@@ -693,16 +823,20 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
693
823
|
"url": resolved_ui.url,
|
|
694
824
|
},
|
|
695
825
|
}
|
|
696
|
-
|
|
826
|
+
if runtime_config:
|
|
827
|
+
update_data["runtime_config"] = runtime_config
|
|
828
|
+
|
|
697
829
|
if ks3_config:
|
|
698
830
|
update_data["ks3"] = ks3_config
|
|
699
831
|
elif artifact_type == "Container":
|
|
700
832
|
image_credential = self._image_credential_from_env(artifact_path)
|
|
701
833
|
if image_credential:
|
|
702
834
|
update_data["image_credential"] = image_credential
|
|
703
|
-
|
|
704
|
-
f"
|
|
835
|
+
credential_target = (
|
|
836
|
+
f"{image_credential['username']}@"
|
|
837
|
+
f"{image_credential['endpoint']}"
|
|
705
838
|
)
|
|
839
|
+
click.echo(f" 🔑 镜像凭证: {credential_target}")
|
|
706
840
|
|
|
707
841
|
# 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
|
|
708
842
|
env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
|
|
@@ -727,17 +861,19 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
727
861
|
storage_config = self._serialize_storage_config(target)
|
|
728
862
|
if storage_config:
|
|
729
863
|
update_data["storage"] = storage_config
|
|
730
|
-
|
|
864
|
+
|
|
731
865
|
# 注入更新时间戳,强制触发 Rolling Update (Pod 重启)
|
|
732
866
|
if "env_vars" not in update_data:
|
|
733
867
|
update_data["env_vars"] = {}
|
|
734
|
-
|
|
868
|
+
|
|
735
869
|
import time
|
|
870
|
+
|
|
736
871
|
update_data["env_vars"]["KSADK_UPDATED_AT"] = str(int(time.time()))
|
|
737
|
-
|
|
872
|
+
|
|
738
873
|
# DEBUG: 打印 Payload 确认 trigger 是否存在
|
|
739
|
-
|
|
740
|
-
|
|
874
|
+
updated_at_value = update_data["env_vars"]["KSADK_UPDATED_AT"]
|
|
875
|
+
click.echo(f" 🔄 更新 Trigger: KSADK_UPDATED_AT={updated_at_value}")
|
|
876
|
+
|
|
741
877
|
res = await client.update_agent(existing_agent_id, update_data)
|
|
742
878
|
latest_access = {}
|
|
743
879
|
if not is_dry_run:
|
|
@@ -751,38 +887,40 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
751
887
|
exc,
|
|
752
888
|
),
|
|
753
889
|
)
|
|
754
|
-
|
|
890
|
+
|
|
755
891
|
# 如果是 Dry Run,手动构造假响应以避免崩溃
|
|
756
892
|
if is_dry_run and not res:
|
|
757
893
|
res = {"name": package_info.name, "endpoint": "http://dry-run-endpoint"}
|
|
758
|
-
|
|
894
|
+
|
|
759
895
|
# 更新本地状态 (保留旧字段如 api_key)
|
|
760
896
|
new_state = local_state.copy()
|
|
761
|
-
new_state.update(
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
897
|
+
new_state.update(
|
|
898
|
+
{
|
|
899
|
+
"agent_id": latest_access.get("agent_id") or existing_agent_id,
|
|
900
|
+
"name": latest_access.get("name") or res.get("name"),
|
|
901
|
+
"region": target.region,
|
|
902
|
+
"endpoint": latest_access.get("endpoint") or res.get("endpoint"),
|
|
903
|
+
"updated_at": self._now_iso(),
|
|
904
|
+
**ui_state,
|
|
905
|
+
}
|
|
906
|
+
)
|
|
769
907
|
if latest_access.get("api_key"):
|
|
770
908
|
new_state["api_key"] = latest_access["api_key"]
|
|
771
909
|
self._save_state(state_file, new_state)
|
|
772
|
-
|
|
910
|
+
|
|
773
911
|
return DeployResult(
|
|
774
|
-
status=DeployStatus.DEPLOYING,
|
|
912
|
+
status=DeployStatus.DEPLOYING,
|
|
775
913
|
agent_id=existing_agent_id,
|
|
776
914
|
agent_name=res.get("name"),
|
|
777
915
|
endpoint=res.get("endpoint"),
|
|
778
|
-
message=f"✅ Agent 已更新: {existing_agent_id}"
|
|
916
|
+
message=f"✅ Agent 已更新: {existing_agent_id}",
|
|
779
917
|
)
|
|
780
918
|
# else: agent_exists = False,继续执行创建逻辑
|
|
781
|
-
|
|
919
|
+
|
|
782
920
|
# 没有本地状态,或本地状态对应的 Agent 在服务器上不存在 → 创建新 Agent
|
|
783
921
|
if not existing_agent_id or not agent_exists:
|
|
784
922
|
click.echo(f" 创建新 Agent: {package_info.name}")
|
|
785
|
-
|
|
923
|
+
|
|
786
924
|
request_data = {
|
|
787
925
|
"name": package_info.name,
|
|
788
926
|
"framework": package_info.framework,
|
|
@@ -792,12 +930,12 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
792
930
|
"instance_id": target.extra.get("instance_id", "default"),
|
|
793
931
|
"resources": {
|
|
794
932
|
"cpu": target.resources.cpu,
|
|
795
|
-
"memory": target.resources.memory
|
|
933
|
+
"memory": target.resources.memory,
|
|
796
934
|
},
|
|
797
935
|
"scaling": {
|
|
798
936
|
"min_replicas": target.scaling.min_replicas,
|
|
799
937
|
"max_replicas": target.scaling.max_replicas,
|
|
800
|
-
"concurrency": target.scaling.concurrency
|
|
938
|
+
"concurrency": target.scaling.concurrency,
|
|
801
939
|
},
|
|
802
940
|
"observability": {
|
|
803
941
|
"langfuse_enabled": target.extra.get("enable_observability", True)
|
|
@@ -808,7 +946,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
808
946
|
"url": resolved_ui.url,
|
|
809
947
|
},
|
|
810
948
|
}
|
|
811
|
-
|
|
949
|
+
if runtime_config:
|
|
950
|
+
request_data["runtime_config"] = runtime_config
|
|
951
|
+
|
|
812
952
|
if ks3_config:
|
|
813
953
|
request_data["ks3"] = ks3_config
|
|
814
954
|
|
|
@@ -817,9 +957,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
817
957
|
image_credential = self._image_credential_from_env(artifact_path)
|
|
818
958
|
if image_credential:
|
|
819
959
|
request_data["image_credential"] = image_credential
|
|
820
|
-
|
|
821
|
-
f"
|
|
960
|
+
credential_target = (
|
|
961
|
+
f"{image_credential['username']}@" f"{image_credential['endpoint']}"
|
|
822
962
|
)
|
|
963
|
+
click.echo(f" 🔑 镜像凭证: {credential_target}")
|
|
823
964
|
|
|
824
965
|
# 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
|
|
825
966
|
env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
|
|
@@ -837,7 +978,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
837
978
|
click.echo(f" 📦 加载环境变量: {len(env_vars)} 项 from 全局配置")
|
|
838
979
|
|
|
839
980
|
if env_vars:
|
|
840
|
-
|
|
981
|
+
request_data["env_vars"] = env_vars
|
|
841
982
|
|
|
842
983
|
network_config = self._serialize_network_config(target, is_update=False)
|
|
843
984
|
if network_config:
|
|
@@ -852,20 +993,22 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
852
993
|
ksyun_account_id = os.getenv("KSYUN_ACCOUNT_ID")
|
|
853
994
|
if ksyun_account_id:
|
|
854
995
|
extra_headers["X-Ksc-Account-Id"] = ksyun_account_id
|
|
855
|
-
|
|
996
|
+
|
|
856
997
|
# 重新构造一个带 Header 的 client
|
|
857
|
-
async with AgentEngineClient(
|
|
998
|
+
async with AgentEngineClient(
|
|
999
|
+
region=target.region, extra_headers=extra_headers, dry_run=is_dry_run
|
|
1000
|
+
) as new_client:
|
|
858
1001
|
res = await new_client.create_agent(request_data)
|
|
859
|
-
|
|
1002
|
+
|
|
860
1003
|
# 如果是 Dry Run,手动构造假响应
|
|
861
1004
|
if is_dry_run and not res:
|
|
862
1005
|
res = {
|
|
863
|
-
"agent_id": "dry-run-agent-id",
|
|
864
|
-
"name": package_info.name,
|
|
865
|
-
"endpoint": "http://dry-run-endpoint",
|
|
866
|
-
"api_key": "dry-run-key"
|
|
1006
|
+
"agent_id": "dry-run-agent-id",
|
|
1007
|
+
"name": package_info.name,
|
|
1008
|
+
"endpoint": "http://dry-run-endpoint",
|
|
1009
|
+
"api_key": "dry-run-key",
|
|
867
1010
|
}
|
|
868
|
-
|
|
1011
|
+
|
|
869
1012
|
# CreateAgentProduct 返回 order_id,需要轮询 list_agents 获取 agent_id
|
|
870
1013
|
new_agent_id = res.get("agent_id")
|
|
871
1014
|
order_id = res.get("order_id")
|
|
@@ -876,10 +1019,13 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
876
1019
|
if order_id and not new_agent_id:
|
|
877
1020
|
click.echo(f" 📋 订单已创建: {order_id},等待实例创建...")
|
|
878
1021
|
import time
|
|
1022
|
+
|
|
879
1023
|
for i in range(12): # 最多等 60s
|
|
880
1024
|
time.sleep(5)
|
|
881
1025
|
try:
|
|
882
|
-
detail = await client.get_agent(
|
|
1026
|
+
detail = await client.get_agent(
|
|
1027
|
+
name=package_info.name, include_api_key=True
|
|
1028
|
+
)
|
|
883
1029
|
qa = detail.get("quick_access", {})
|
|
884
1030
|
basic = detail.get("basic", {})
|
|
885
1031
|
new_agent_id = basic.get("agent_id")
|
|
@@ -894,7 +1040,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
894
1040
|
click.echo(f" ⏳ 等待中... ({(i+1)*5}s)")
|
|
895
1041
|
|
|
896
1042
|
if not new_agent_id:
|
|
897
|
-
click.secho(
|
|
1043
|
+
click.secho(
|
|
1044
|
+
" ⚠️ 实例仍在创建中,稍后使用 'agentengine status' 查看",
|
|
1045
|
+
fg="yellow",
|
|
1046
|
+
)
|
|
898
1047
|
|
|
899
1048
|
latest_access = {}
|
|
900
1049
|
if new_agent_id and not is_dry_run:
|
|
@@ -913,54 +1062,63 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
913
1062
|
agent_name = latest_access.get("name") or agent_name
|
|
914
1063
|
agent_endpoint = latest_access.get("endpoint") or agent_endpoint
|
|
915
1064
|
agent_api_key = latest_access.get("api_key") or agent_api_key
|
|
916
|
-
|
|
1065
|
+
|
|
917
1066
|
# 保存 agent_id 到本地状态文件
|
|
918
|
-
self._save_state(
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1067
|
+
self._save_state(
|
|
1068
|
+
state_file,
|
|
1069
|
+
{
|
|
1070
|
+
"agent_id": new_agent_id,
|
|
1071
|
+
"name": agent_name,
|
|
1072
|
+
"region": target.region,
|
|
1073
|
+
"endpoint": agent_endpoint,
|
|
1074
|
+
"api_key": agent_api_key, # 只在首次保存
|
|
1075
|
+
"order_id": order_id,
|
|
1076
|
+
"created_at": self._now_iso(),
|
|
1077
|
+
**ui_state,
|
|
1078
|
+
},
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
click.echo(" 💾 已保存状态到 .agentengine.state")
|
|
1082
|
+
|
|
931
1083
|
return DeployResult(
|
|
932
|
-
status=DeployStatus.DEPLOYING,
|
|
1084
|
+
status=DeployStatus.DEPLOYING,
|
|
933
1085
|
agent_id=new_agent_id,
|
|
934
1086
|
agent_name=agent_name,
|
|
935
|
-
endpoint=agent_endpoint,
|
|
1087
|
+
endpoint=agent_endpoint,
|
|
936
1088
|
api_key=agent_api_key,
|
|
937
|
-
message=
|
|
1089
|
+
message=(
|
|
1090
|
+
f"✅ Agent ID: {new_agent_id or '(创建中)'} "
|
|
1091
|
+
f"(首次部署, 订单: {order_id or '-'})"
|
|
1092
|
+
),
|
|
938
1093
|
)
|
|
939
1094
|
|
|
1095
|
+
return DeployResult(
|
|
1096
|
+
status=DeployStatus.FAILED,
|
|
1097
|
+
message="部署流程未返回 Agent 状态",
|
|
1098
|
+
)
|
|
1099
|
+
|
|
940
1100
|
except DryRunExit as e:
|
|
941
1101
|
return DeployResult(
|
|
942
1102
|
status=DeployStatus.SKIPPED,
|
|
943
1103
|
message="✅ Dry Run Completed: 请求已打印,未执行实际变更。",
|
|
944
1104
|
metadata={"dry_run_request": e.payload or {}},
|
|
945
1105
|
)
|
|
946
|
-
|
|
1106
|
+
|
|
947
1107
|
except Exception as e:
|
|
948
1108
|
logger.error(f"Deploy failed: {e}")
|
|
949
|
-
|
|
1109
|
+
|
|
950
1110
|
# 检测名称冲突
|
|
951
1111
|
err_msg = str(e)
|
|
952
1112
|
if "Conflict" in err_msg or "409" in err_msg or "already exists" in err_msg:
|
|
953
1113
|
return DeployResult(
|
|
954
1114
|
status=DeployStatus.FAILED,
|
|
955
1115
|
message=f"❌ 部署失败: Agent 名称 '{package_info.name}' 已存在。\n"
|
|
956
|
-
|
|
957
|
-
|
|
1116
|
+
f" 提示: 请检查是否重复创建。\n"
|
|
1117
|
+
" 👉 解决方法: 请在 agentengine.yaml 中修改 'name' 字段 "
|
|
1118
|
+
"(如添加后缀) 后重试。",
|
|
958
1119
|
)
|
|
959
|
-
|
|
960
|
-
return DeployResult(
|
|
961
|
-
status=DeployStatus.FAILED,
|
|
962
|
-
message=f"Server 请求失败: {str(e)}"
|
|
963
|
-
)
|
|
1120
|
+
|
|
1121
|
+
return DeployResult(status=DeployStatus.FAILED, message=f"Server 请求失败: {str(e)}")
|
|
964
1122
|
|
|
965
1123
|
async def get_status(self, agent_id: str, target: DeployTarget) -> DeployResult:
|
|
966
1124
|
"""获取 Agent 状态"""
|
|
@@ -968,7 +1126,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
968
1126
|
try:
|
|
969
1127
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
970
1128
|
res = await client.get_agent(agent_id)
|
|
971
|
-
|
|
1129
|
+
|
|
972
1130
|
status_map = {
|
|
973
1131
|
"running": DeployStatus.RUNNING,
|
|
974
1132
|
"ready": DeployStatus.RUNNING,
|
|
@@ -978,9 +1136,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
978
1136
|
"scaling": DeployStatus.UPDATING,
|
|
979
1137
|
"failed": DeployStatus.FAILED,
|
|
980
1138
|
"error": DeployStatus.FAILED,
|
|
981
|
-
"unknown": DeployStatus.UNKNOWN
|
|
1139
|
+
"unknown": DeployStatus.UNKNOWN,
|
|
982
1140
|
}
|
|
983
|
-
|
|
1141
|
+
|
|
984
1142
|
# 使状态匹配不区分大小写,以兼容服务端的改动 (Creating -> CREATING)
|
|
985
1143
|
current_status = (res.get("status") or "").lower()
|
|
986
1144
|
return DeployResult(
|
|
@@ -988,21 +1146,20 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
988
1146
|
agent_id=res.get("agent_id"),
|
|
989
1147
|
agent_name=res.get("name"),
|
|
990
1148
|
endpoint=res.get("endpoint"),
|
|
991
|
-
message=f"Status: {res.get('status')} ({res.get('phase')})"
|
|
1149
|
+
message=f"Status: {res.get('status')} ({res.get('phase')})",
|
|
992
1150
|
)
|
|
993
1151
|
except DryRunExit:
|
|
994
1152
|
return DeployResult(status=DeployStatus.SKIPPED, message="Dry Run executed.")
|
|
995
1153
|
except Exception as e:
|
|
996
|
-
return DeployResult(
|
|
997
|
-
status=DeployStatus.UNKNOWN,
|
|
998
|
-
message=f"查询失败: {e}"
|
|
999
|
-
)
|
|
1154
|
+
return DeployResult(status=DeployStatus.UNKNOWN, message=f"查询失败: {e}")
|
|
1000
1155
|
|
|
1001
1156
|
async def destroy(self, agent_id: str, target: DeployTarget) -> bool:
|
|
1002
1157
|
"""销毁 Agent"""
|
|
1003
1158
|
dry_run = target.extra.get("dry_run", False)
|
|
1004
1159
|
project_dir = str(target.extra.get("project_dir") or "").strip()
|
|
1005
|
-
state_file = (
|
|
1160
|
+
state_file = (
|
|
1161
|
+
Path(project_dir).resolve() if project_dir else Path(".").resolve()
|
|
1162
|
+
) / ".agentengine.state"
|
|
1006
1163
|
|
|
1007
1164
|
try:
|
|
1008
1165
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
@@ -1015,8 +1172,10 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1015
1172
|
os.remove(state_file)
|
|
1016
1173
|
logger.info(f"Deleted local state file: {state_file}")
|
|
1017
1174
|
except Exception as cleanup_error:
|
|
1018
|
-
logger.warning(
|
|
1019
|
-
|
|
1175
|
+
logger.warning(
|
|
1176
|
+
f"Failed to delete local state file {state_file}: {cleanup_error}"
|
|
1177
|
+
)
|
|
1178
|
+
return bool(success)
|
|
1020
1179
|
except DryRunExit:
|
|
1021
1180
|
# 让异常冒泡给 CLI 处理
|
|
1022
1181
|
raise
|
|
@@ -1030,19 +1189,27 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1030
1189
|
try:
|
|
1031
1190
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
1032
1191
|
res = await client.list_agents()
|
|
1033
|
-
|
|
1192
|
+
|
|
1034
1193
|
results = []
|
|
1035
1194
|
for agent in res.get("Agents", []):
|
|
1036
1195
|
current_status = (agent.get("status") or "").lower()
|
|
1037
|
-
results.append(
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1196
|
+
results.append(
|
|
1197
|
+
DeployResult(
|
|
1198
|
+
status=(
|
|
1199
|
+
DeployStatus.RUNNING
|
|
1200
|
+
if current_status in ("running", "ready")
|
|
1201
|
+
else (
|
|
1202
|
+
DeployStatus.DEPLOYING
|
|
1203
|
+
if current_status == "creating"
|
|
1204
|
+
else DeployStatus.UNKNOWN
|
|
1205
|
+
)
|
|
1206
|
+
),
|
|
1207
|
+
agent_id=agent.get("agent_id"),
|
|
1208
|
+
agent_name=agent.get("name"),
|
|
1209
|
+
endpoint=agent.get("endpoint"),
|
|
1210
|
+
message=agent.get("status"),
|
|
1211
|
+
)
|
|
1212
|
+
)
|
|
1046
1213
|
return results
|
|
1047
1214
|
except DryRunExit:
|
|
1048
1215
|
raise
|
|
@@ -1056,7 +1223,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1056
1223
|
try:
|
|
1057
1224
|
async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
|
|
1058
1225
|
response = await client.chat(agent_id, message, stream=False)
|
|
1059
|
-
return response.get("output", "")
|
|
1226
|
+
return str(response.get("output", ""))
|
|
1060
1227
|
except DryRunExit:
|
|
1061
1228
|
raise
|
|
1062
1229
|
except Exception as e:
|
|
@@ -1066,7 +1233,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1066
1233
|
"""读取本地状态文件"""
|
|
1067
1234
|
if state_file.exists():
|
|
1068
1235
|
try:
|
|
1069
|
-
with open(state_file,
|
|
1236
|
+
with open(state_file, "r", encoding="utf-8") as f:
|
|
1070
1237
|
return yaml.safe_load(f) or {}
|
|
1071
1238
|
except Exception as e:
|
|
1072
1239
|
logger.warning(f"Failed to load state file: {e}")
|
|
@@ -1102,8 +1269,9 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1102
1269
|
def _save_state(self, state_file: Path, state: Dict[str, Any]) -> None:
|
|
1103
1270
|
"""保存状态到本地文件"""
|
|
1104
1271
|
import yaml
|
|
1272
|
+
|
|
1105
1273
|
try:
|
|
1106
|
-
with open(state_file,
|
|
1274
|
+
with open(state_file, "w", encoding="utf-8") as f:
|
|
1107
1275
|
yaml.dump(state, f, default_flow_style=False, allow_unicode=True)
|
|
1108
1276
|
logger.debug(f"State saved to {state_file}")
|
|
1109
1277
|
except Exception as e:
|
|
@@ -1112,4 +1280,5 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
1112
1280
|
def _now_iso(self) -> str:
|
|
1113
1281
|
"""返回当前时间 ISO 格式"""
|
|
1114
1282
|
from datetime import datetime
|
|
1283
|
+
|
|
1115
1284
|
return datetime.now().isoformat()
|