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
ksadk/builders/code_builder.py
CHANGED
|
@@ -7,17 +7,17 @@ Code Builder - zip 打包模式构建
|
|
|
7
7
|
3. 打包 zip (用户代码 + 依赖 + ksadk 源码 + entrypoint)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import ast
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
10
13
|
import os
|
|
11
|
-
import
|
|
14
|
+
import re
|
|
12
15
|
import shutil
|
|
13
16
|
import subprocess
|
|
17
|
+
import sys
|
|
14
18
|
import threading
|
|
15
19
|
import time
|
|
16
20
|
import zipfile
|
|
17
|
-
import re
|
|
18
|
-
import json
|
|
19
|
-
import hashlib
|
|
20
|
-
import ast
|
|
21
21
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import List, Optional, Set
|
|
@@ -25,11 +25,12 @@ from urllib.parse import urlparse
|
|
|
25
25
|
from urllib.request import Request, urlopen
|
|
26
26
|
|
|
27
27
|
import click
|
|
28
|
+
from packaging.requirements import InvalidRequirement, Requirement
|
|
28
29
|
|
|
29
30
|
from ksadk.builders.base import BaseBuilder, BuildResult
|
|
30
31
|
from ksadk.builders.framework_requirements import (
|
|
31
32
|
FASTAPI_REQUIREMENT,
|
|
32
|
-
|
|
33
|
+
code_requirements_for_framework,
|
|
33
34
|
)
|
|
34
35
|
from ksadk.builders.requirements_utils import (
|
|
35
36
|
exclude_requirement_names,
|
|
@@ -85,28 +86,28 @@ class CodeBuilder(BaseBuilder):
|
|
|
85
86
|
}
|
|
86
87
|
IGNORED_FILE_NAMES = {".DS_Store"}
|
|
87
88
|
KSADK_ALLOWED_SUFFIXES = {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
89
|
+
".py",
|
|
90
|
+
".yaml",
|
|
91
|
+
".yml",
|
|
92
|
+
".json",
|
|
93
|
+
".jinja2",
|
|
94
|
+
".j2",
|
|
95
|
+
".txt",
|
|
96
|
+
".md",
|
|
97
|
+
".html",
|
|
98
|
+
".js",
|
|
99
|
+
".css",
|
|
100
|
+
".svg",
|
|
101
|
+
".ico",
|
|
102
|
+
".png",
|
|
103
|
+
".jpg",
|
|
104
|
+
".jpeg",
|
|
105
|
+
".gif",
|
|
106
|
+
".webp",
|
|
107
|
+
".woff",
|
|
108
|
+
".woff2",
|
|
109
|
+
".ttf",
|
|
110
|
+
".map",
|
|
110
111
|
}
|
|
111
112
|
BUNDLED_KSADK_CORE_RUNTIME_REQUIREMENTS = (
|
|
112
113
|
"a2a-sdk>=0.3.22",
|
|
@@ -124,15 +125,16 @@ class CodeBuilder(BaseBuilder):
|
|
|
124
125
|
"mcp>=1.1.0",
|
|
125
126
|
"langchain-mcp-adapters>=0.0.1",
|
|
126
127
|
)
|
|
127
|
-
BUNDLED_KSADK_POSTGRES_SESSION_REQUIREMENTS = (
|
|
128
|
-
"asyncpg>=0.30.0,<1.0.0",
|
|
129
|
-
)
|
|
128
|
+
BUNDLED_KSADK_POSTGRES_SESSION_REQUIREMENTS = ("asyncpg>=0.30.0,<1.0.0",)
|
|
130
129
|
BUNDLED_KSADK_ATTACHMENT_RUNTIME_REQUIREMENTS = (
|
|
131
130
|
"pypdf>=6.0.0",
|
|
132
131
|
"beautifulsoup4>=4.12.0",
|
|
133
132
|
)
|
|
134
|
-
BUNDLED_KSADK_ATTACHMENT_OCR_RUNTIME_REQUIREMENTS = (
|
|
135
|
-
|
|
133
|
+
BUNDLED_KSADK_ATTACHMENT_OCR_RUNTIME_REQUIREMENTS = ("rapidocr-onnxruntime>=1.2.0",)
|
|
134
|
+
BUNDLED_AGUI_RUNTIME_REQUIREMENTS = (
|
|
135
|
+
"ag-ui-protocol==0.1.19",
|
|
136
|
+
"ag-ui-langgraph[fastapi]==0.0.42",
|
|
137
|
+
"copilotkit==0.1.94",
|
|
136
138
|
)
|
|
137
139
|
BUNDLED_KSADK_RUNTIME_REQUIREMENTS = (
|
|
138
140
|
*BUNDLED_KSADK_CORE_RUNTIME_REQUIREMENTS,
|
|
@@ -149,8 +151,8 @@ class CodeBuilder(BaseBuilder):
|
|
|
149
151
|
PIP_INSTALL_TIMEOUT_SECONDS = 45 * 60
|
|
150
152
|
CONTAINER_SUGGESTION_RAW_THRESHOLD_BYTES = 500 * 1024 * 1024
|
|
151
153
|
CONTAINER_SUGGESTION_ZIP_THRESHOLD_BYTES = 300 * 1024 * 1024
|
|
152
|
-
|
|
153
|
-
def __init__(self, project_dir: Path, config: dict = None):
|
|
154
|
+
|
|
155
|
+
def __init__(self, project_dir: Path, config: Optional[dict] = None):
|
|
154
156
|
super().__init__(project_dir, config)
|
|
155
157
|
self.build_dir = self.project_dir / ".agentengine" / "code_build"
|
|
156
158
|
self.deps_dir = self.build_dir / "linux_deps"
|
|
@@ -168,33 +170,38 @@ class CodeBuilder(BaseBuilder):
|
|
|
168
170
|
self._package_progress_logged_milestone_by_label: dict[str, int] = {}
|
|
169
171
|
self._pip_index_candidates_cache: Optional[list[Optional[str]]] = None
|
|
170
172
|
self._pip_index_selection_summary = ""
|
|
171
|
-
|
|
173
|
+
|
|
172
174
|
def build(self) -> BuildResult:
|
|
173
175
|
"""执行 Code 模式构建"""
|
|
174
176
|
from ksadk.detection import FrameworkDetector
|
|
175
|
-
|
|
177
|
+
|
|
176
178
|
self._load_dotenv()
|
|
177
179
|
config = self._load_config()
|
|
178
|
-
|
|
180
|
+
|
|
179
181
|
# 检测框架
|
|
180
182
|
detector = FrameworkDetector(str(self.project_dir))
|
|
181
183
|
detection_result = detector.detect()
|
|
182
|
-
|
|
184
|
+
|
|
183
185
|
if detection_result.type.value == "unknown":
|
|
186
|
+
return BuildResult(success=False, error_message="未检测到支持的框架")
|
|
187
|
+
if detection_result.type.value == "codex":
|
|
184
188
|
return BuildResult(
|
|
185
189
|
success=False,
|
|
186
|
-
error_message=
|
|
190
|
+
error_message=(
|
|
191
|
+
"Codex 是 ManagedRuntime,不能构建宿主机 Code zip;"
|
|
192
|
+
"请使用 `ksadk build .` 的 managed 模式,或显式构建 Container"
|
|
193
|
+
),
|
|
187
194
|
)
|
|
188
|
-
|
|
195
|
+
|
|
189
196
|
click.echo(f"📦 框架: {click.style(detection_result.type.value, fg='green')}")
|
|
190
197
|
click.echo(f"🤖 Agent: {click.style(detection_result.name, fg='blue')}")
|
|
191
|
-
|
|
192
|
-
agent_name = config.get(
|
|
193
|
-
|
|
198
|
+
|
|
199
|
+
agent_name = config.get("name", self.project_dir.name).replace("-", "_").replace(".", "_")
|
|
200
|
+
|
|
194
201
|
# 创建构建目录
|
|
195
202
|
self.build_dir.mkdir(parents=True, exist_ok=True)
|
|
196
203
|
zip_path = self.build_dir / f"{agent_name}.zip"
|
|
197
|
-
|
|
204
|
+
|
|
198
205
|
# 检查是否需要重新构建
|
|
199
206
|
no_cache = self.config.get("no_cache", False) if self.config else False
|
|
200
207
|
repackage = self.config.get("repackage", False) if self.config else False
|
|
@@ -207,31 +214,33 @@ class CodeBuilder(BaseBuilder):
|
|
|
207
214
|
if zip_path.exists() and not rebuild_needed:
|
|
208
215
|
incompatibles = self._scan_incompatible_binaries_in_zip(zip_path)
|
|
209
216
|
if incompatibles:
|
|
210
|
-
click.secho(
|
|
217
|
+
click.secho(
|
|
218
|
+
"\n⚠️ 检测到缓存构建包含非 Linux 兼容关键二进制,自动重建...", fg="yellow"
|
|
219
|
+
)
|
|
211
220
|
for item in incompatibles[:5]:
|
|
212
221
|
click.echo(f" - {item}")
|
|
213
222
|
rebuild_reason = "缓存 zip 存在 Linux 兼容性问题"
|
|
214
223
|
else:
|
|
215
224
|
self._save_input_fingerprint(zip_path, detection_result)
|
|
216
225
|
zip_size = zip_path.stat().st_size / (1024 * 1024)
|
|
217
|
-
click.secho(f"\n✅ 使用已有构建: {zip_path.name} ({zip_size:.2f} MB)", fg=
|
|
218
|
-
click.echo(
|
|
226
|
+
click.secho(f"\n✅ 使用已有构建: {zip_path.name} ({zip_size:.2f} MB)", fg="green")
|
|
227
|
+
click.echo(
|
|
228
|
+
" (如需只重新打包当前代码/runtime,请使用 --repackage;"
|
|
229
|
+
"如需重装依赖,请使用 --no-cache)"
|
|
230
|
+
)
|
|
219
231
|
return BuildResult(
|
|
220
232
|
success=True,
|
|
221
233
|
artifact_path=zip_path,
|
|
222
234
|
artifact_size=zip_path.stat().st_size,
|
|
223
|
-
metadata={
|
|
224
|
-
"agent_name": agent_name,
|
|
225
|
-
"framework": detection_result.type.value
|
|
226
|
-
}
|
|
235
|
+
metadata={"agent_name": agent_name, "framework": detection_result.type.value},
|
|
227
236
|
)
|
|
228
237
|
elif rebuild_reason:
|
|
229
238
|
click.echo(f" 重新打包原因: {rebuild_reason}")
|
|
230
|
-
|
|
239
|
+
|
|
231
240
|
# Step 1: 准备依赖
|
|
232
241
|
click.echo("\n📋 Step 1/3: 准备依赖清单...")
|
|
233
242
|
requirements_path = self._prepare_requirements(detection_result)
|
|
234
|
-
|
|
243
|
+
|
|
235
244
|
# Step 2: 安装依赖
|
|
236
245
|
click.echo("\n📦 Step 2/3: 安装依赖...")
|
|
237
246
|
reuse_dependencies, reuse_reason = self._can_reuse_dependency_cache(
|
|
@@ -241,7 +250,8 @@ class CodeBuilder(BaseBuilder):
|
|
|
241
250
|
if reuse_dependencies:
|
|
242
251
|
stats = self._current_dependency_stats()
|
|
243
252
|
click.secho(
|
|
244
|
-
f" ✓ 复用 Linux 依赖缓存: {stats['file_count']} 个文件,
|
|
253
|
+
f" ✓ 复用 Linux 依赖缓存: {stats['file_count']} 个文件, "
|
|
254
|
+
f"{stats['size_mb']:.1f} MB",
|
|
245
255
|
fg="green",
|
|
246
256
|
)
|
|
247
257
|
click.echo(f" {reuse_reason}")
|
|
@@ -250,25 +260,22 @@ class CodeBuilder(BaseBuilder):
|
|
|
250
260
|
click.echo(f" {reuse_reason}")
|
|
251
261
|
self._clear_dependency_cache()
|
|
252
262
|
self.deps_dir.mkdir(parents=True, exist_ok=True)
|
|
253
|
-
|
|
263
|
+
|
|
254
264
|
if not self._install_dependencies(requirements_path):
|
|
255
|
-
return BuildResult(
|
|
256
|
-
success=False,
|
|
257
|
-
error_message="依赖安装失败"
|
|
258
|
-
)
|
|
265
|
+
return BuildResult(success=False, error_message="依赖安装失败")
|
|
259
266
|
self._save_dependency_fingerprint(requirements_path)
|
|
260
|
-
|
|
267
|
+
|
|
261
268
|
# Step 3: 打包 zip
|
|
262
269
|
click.echo("\n📦 Step 3/3: 打包 zip...")
|
|
263
270
|
package_started_at = time.monotonic()
|
|
264
271
|
self._package_zip(zip_path, detection_result)
|
|
265
272
|
click.echo(f" ✓ 打包耗时: {self._format_elapsed(package_started_at)}")
|
|
266
273
|
self._save_input_fingerprint(zip_path, detection_result)
|
|
267
|
-
|
|
274
|
+
|
|
268
275
|
zip_size = zip_path.stat().st_size
|
|
269
276
|
click.echo(f" zip 文件: {zip_path}")
|
|
270
277
|
click.echo(f" 大小: {zip_size / (1024 * 1024):.2f} MB")
|
|
271
|
-
|
|
278
|
+
|
|
272
279
|
return BuildResult(
|
|
273
280
|
success=True,
|
|
274
281
|
artifact_path=zip_path,
|
|
@@ -276,8 +283,8 @@ class CodeBuilder(BaseBuilder):
|
|
|
276
283
|
metadata={
|
|
277
284
|
"agent_name": agent_name,
|
|
278
285
|
"framework": detection_result.type.value,
|
|
279
|
-
"deps_dir": str(self.deps_dir)
|
|
280
|
-
}
|
|
286
|
+
"deps_dir": str(self.deps_dir),
|
|
287
|
+
},
|
|
281
288
|
)
|
|
282
289
|
|
|
283
290
|
def _rebuild_decision(
|
|
@@ -321,26 +328,32 @@ class CodeBuilder(BaseBuilder):
|
|
|
321
328
|
if changed:
|
|
322
329
|
return "业务代码或项目文件变更"
|
|
323
330
|
return "构建输入指纹变化"
|
|
324
|
-
|
|
331
|
+
|
|
325
332
|
def _need_rebuild(self, zip_path: Path, detection_result) -> bool:
|
|
326
333
|
"""检查是否需要重新构建。优先使用输入内容指纹,缺失时回退到 mtime。"""
|
|
327
334
|
manifest = self._load_input_fingerprint(zip_path)
|
|
328
335
|
if manifest:
|
|
329
336
|
current_fingerprint = self._build_input_fingerprint(detection_result)
|
|
330
|
-
return manifest.get("fingerprint") != current_fingerprint["fingerprint"]
|
|
337
|
+
return bool(manifest.get("fingerprint") != current_fingerprint["fingerprint"])
|
|
331
338
|
return self._need_rebuild_from_mtime(zip_path)
|
|
332
339
|
|
|
333
340
|
def _need_rebuild_from_mtime(self, zip_path: Path) -> bool:
|
|
334
341
|
"""兼容旧缓存:当没有指纹文件时,回退到 mtime 判断。"""
|
|
335
342
|
zip_mtime = zip_path.stat().st_mtime
|
|
336
|
-
|
|
343
|
+
|
|
337
344
|
for item in self.project_dir.iterdir():
|
|
338
|
-
if item.name.startswith(
|
|
345
|
+
if item.name.startswith(".") or item.name in (
|
|
346
|
+
"__pycache__",
|
|
347
|
+
"node_modules",
|
|
348
|
+
".git",
|
|
349
|
+
".venv",
|
|
350
|
+
"venv",
|
|
351
|
+
):
|
|
339
352
|
continue
|
|
340
353
|
if item.is_file() and item.stat().st_mtime > zip_mtime:
|
|
341
354
|
return True
|
|
342
355
|
if item.is_dir():
|
|
343
|
-
for file_path in item.rglob(
|
|
356
|
+
for file_path in item.rglob("*.py"):
|
|
344
357
|
if file_path.stat().st_mtime > zip_mtime:
|
|
345
358
|
return True
|
|
346
359
|
return False
|
|
@@ -360,7 +373,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
360
373
|
data = json.load(f)
|
|
361
374
|
if data.get("version") != self.INPUT_FINGERPRINT_VERSION:
|
|
362
375
|
return None
|
|
363
|
-
return data
|
|
376
|
+
return data if isinstance(data, dict) else None
|
|
364
377
|
except Exception:
|
|
365
378
|
return None
|
|
366
379
|
|
|
@@ -380,7 +393,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
380
393
|
data = json.load(f)
|
|
381
394
|
if data.get("version") != self.DEPENDENCY_FINGERPRINT_VERSION:
|
|
382
395
|
return None
|
|
383
|
-
return data
|
|
396
|
+
return data if isinstance(data, dict) else None
|
|
384
397
|
except Exception:
|
|
385
398
|
return None
|
|
386
399
|
|
|
@@ -429,7 +442,12 @@ class CodeBuilder(BaseBuilder):
|
|
|
429
442
|
return True
|
|
430
443
|
if self._project_env_has_configured_value("KSADK_MCP_SERVERS"):
|
|
431
444
|
return True
|
|
432
|
-
if self._project_env_value("KSADK_ENABLE_MCP_TOOLS").strip().lower() in {
|
|
445
|
+
if self._project_env_value("KSADK_ENABLE_MCP_TOOLS").strip().lower() in {
|
|
446
|
+
"1",
|
|
447
|
+
"true",
|
|
448
|
+
"yes",
|
|
449
|
+
"on",
|
|
450
|
+
}:
|
|
433
451
|
return True
|
|
434
452
|
return self._project_imports_any({"mcp", "langchain_mcp_adapters"})
|
|
435
453
|
|
|
@@ -515,13 +533,17 @@ class CodeBuilder(BaseBuilder):
|
|
|
515
533
|
digest = hashlib.sha256()
|
|
516
534
|
requirements_text = requirements_path.read_text(encoding="utf-8")
|
|
517
535
|
|
|
518
|
-
digest.update(
|
|
536
|
+
digest.update(
|
|
537
|
+
f"deps-fingerprint-version:{self.DEPENDENCY_FINGERPRINT_VERSION}\n".encode("utf-8")
|
|
538
|
+
)
|
|
519
539
|
digest.update(f"builder-platform:{sys.platform}\n".encode("utf-8"))
|
|
520
540
|
digest.update(
|
|
521
541
|
f"builder-python:{sys.version_info.major}.{sys.version_info.minor}\n".encode("utf-8")
|
|
522
542
|
)
|
|
523
543
|
digest.update(f"target-python:{self.TARGET_PYTHON_VERSION}\n".encode("utf-8"))
|
|
524
|
-
digest.update(
|
|
544
|
+
digest.update(
|
|
545
|
+
f"target-platforms:{','.join(self.TARGET_INSTALL_PLATFORMS)}\n".encode("utf-8")
|
|
546
|
+
)
|
|
525
547
|
digest.update(requirements_text.encode("utf-8"))
|
|
526
548
|
|
|
527
549
|
requirements = [line for line in requirements_text.splitlines() if line.strip()]
|
|
@@ -641,13 +663,13 @@ class CodeBuilder(BaseBuilder):
|
|
|
641
663
|
)
|
|
642
664
|
|
|
643
665
|
def _iter_bundled_source_package(self, package_name: str, package_root: Path):
|
|
644
|
-
for file_path in sorted(package_root.rglob(
|
|
666
|
+
for file_path in sorted(package_root.rglob("*")):
|
|
645
667
|
if not file_path.is_file():
|
|
646
668
|
continue
|
|
647
669
|
relative_path = file_path.relative_to(package_root)
|
|
648
670
|
if package_name == "ksadk" and self._should_skip_ksadk_relative_path(relative_path):
|
|
649
671
|
continue
|
|
650
|
-
if
|
|
672
|
+
if "__pycache__" in file_path.parts:
|
|
651
673
|
continue
|
|
652
674
|
suffix = file_path.suffix.lower()
|
|
653
675
|
if suffix not in self.KSADK_ALLOWED_SUFFIXES:
|
|
@@ -709,26 +731,26 @@ class CodeBuilder(BaseBuilder):
|
|
|
709
731
|
file_name.startswith(".env.")
|
|
710
732
|
and file_name not in {".env.example", ".env.sample", ".env.template"}
|
|
711
733
|
)
|
|
712
|
-
|
|
734
|
+
|
|
713
735
|
def _prepare_requirements(self, detection_result) -> Path:
|
|
714
736
|
"""准备 requirements.txt"""
|
|
715
737
|
final_deps = self._build_requirements_list(detection_result)
|
|
716
738
|
|
|
717
739
|
if (self.project_dir / "requirements.txt").exists():
|
|
718
|
-
click.echo(
|
|
740
|
+
click.echo(" 发现 requirements.txt,正在合并...")
|
|
719
741
|
else:
|
|
720
|
-
click.echo(
|
|
721
|
-
|
|
742
|
+
click.echo(" 自动生成依赖清单")
|
|
743
|
+
|
|
722
744
|
# 写入构建目录
|
|
723
745
|
requirements_path = self.build_dir / "requirements.txt"
|
|
724
746
|
requirements_path.write_text("\n".join(final_deps))
|
|
725
|
-
|
|
747
|
+
|
|
726
748
|
click.echo(f" 共 {len(final_deps)} 个依赖包:")
|
|
727
749
|
for dep in final_deps[:5]:
|
|
728
750
|
click.echo(f" • {dep}")
|
|
729
751
|
if len(final_deps) > 5:
|
|
730
752
|
click.echo(f" ... 及其他 {len(final_deps) - 5} 个")
|
|
731
|
-
|
|
753
|
+
|
|
732
754
|
return requirements_path
|
|
733
755
|
|
|
734
756
|
def _build_requirements_list(self, detection_result) -> List[str]:
|
|
@@ -740,14 +762,35 @@ class CodeBuilder(BaseBuilder):
|
|
|
740
762
|
user_requirements = self.project_dir / "requirements.txt"
|
|
741
763
|
if user_requirements.exists():
|
|
742
764
|
user_content = user_requirements.read_text(encoding="utf-8")
|
|
765
|
+
parsed_user_requirements = parse_requirements_text(user_content)
|
|
766
|
+
final_deps = merge_requirement_lists(
|
|
767
|
+
final_deps,
|
|
768
|
+
self._bundled_ksadk_extra_requirements(parsed_user_requirements),
|
|
769
|
+
)
|
|
743
770
|
user_deps = exclude_requirement_names(
|
|
744
|
-
|
|
771
|
+
parsed_user_requirements,
|
|
745
772
|
excluded_names={"ksadk"},
|
|
746
773
|
)
|
|
747
774
|
final_deps = merge_requirement_lists(final_deps, user_deps)
|
|
748
775
|
|
|
749
776
|
return final_deps
|
|
750
|
-
|
|
777
|
+
|
|
778
|
+
def _bundled_ksadk_extra_requirements(self, requirements: List[str]) -> tuple[str, ...]:
|
|
779
|
+
extras: set[str] = set()
|
|
780
|
+
for raw_requirement in requirements:
|
|
781
|
+
try:
|
|
782
|
+
parsed = Requirement(raw_requirement)
|
|
783
|
+
except InvalidRequirement:
|
|
784
|
+
continue
|
|
785
|
+
if parsed.name.lower().replace("_", "-") != "ksadk":
|
|
786
|
+
continue
|
|
787
|
+
extras.update(extra.lower() for extra in parsed.extras)
|
|
788
|
+
|
|
789
|
+
bundled: list[str] = []
|
|
790
|
+
if "agui" in extras:
|
|
791
|
+
bundled.extend(self.BUNDLED_AGUI_RUNTIME_REQUIREMENTS)
|
|
792
|
+
return tuple(bundled)
|
|
793
|
+
|
|
751
794
|
def _get_base_requirements(self, detection_result) -> List[str]:
|
|
752
795
|
"""获取基础依赖列表"""
|
|
753
796
|
deps = [
|
|
@@ -765,12 +808,12 @@ class CodeBuilder(BaseBuilder):
|
|
|
765
808
|
"openinference-instrumentation-langchain>=0.1.0",
|
|
766
809
|
"langfuse>=2.0.0",
|
|
767
810
|
]
|
|
768
|
-
|
|
811
|
+
|
|
769
812
|
framework = detection_result.type.value
|
|
770
|
-
deps +=
|
|
771
|
-
|
|
813
|
+
deps += code_requirements_for_framework(framework)
|
|
814
|
+
|
|
772
815
|
return deps
|
|
773
|
-
|
|
816
|
+
|
|
774
817
|
# 目标 Python 版本 (必须与容器运行时一致)
|
|
775
818
|
TARGET_PYTHON_VERSION = "312" # 容器中为 Python 3.12
|
|
776
819
|
|
|
@@ -875,9 +918,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
875
918
|
def _result_error_summary(self, result: subprocess.CompletedProcess[str] | None) -> str:
|
|
876
919
|
if result is None:
|
|
877
920
|
return "unknown"
|
|
878
|
-
combined = "\n".join(
|
|
879
|
-
part for part in (result.stderr or "", result.stdout or "") if part
|
|
880
|
-
)
|
|
921
|
+
combined = "\n".join(part for part in (result.stderr or "", result.stdout or "") if part)
|
|
881
922
|
for line in reversed(combined.splitlines()):
|
|
882
923
|
normalized = line.strip()
|
|
883
924
|
if normalized:
|
|
@@ -887,9 +928,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
887
928
|
def _pip_missing_from_result(self, result: subprocess.CompletedProcess[str] | None) -> bool:
|
|
888
929
|
if result is None:
|
|
889
930
|
return False
|
|
890
|
-
combined = "\n".join(
|
|
891
|
-
part for part in (result.stderr or "", result.stdout or "") if part
|
|
892
|
-
)
|
|
931
|
+
combined = "\n".join(part for part in (result.stderr or "", result.stdout or "") if part)
|
|
893
932
|
return "No module named pip" in combined
|
|
894
933
|
|
|
895
934
|
def _bootstrap_pip_if_missing(self, result: subprocess.CompletedProcess[str] | None) -> bool:
|
|
@@ -952,7 +991,9 @@ class CodeBuilder(BaseBuilder):
|
|
|
952
991
|
if not normalized:
|
|
953
992
|
return None
|
|
954
993
|
|
|
955
|
-
force_emit =
|
|
994
|
+
force_emit = (
|
|
995
|
+
percent > self._install_progress_percent or stage != self._install_progress_stage_name
|
|
996
|
+
)
|
|
956
997
|
count = self._install_progress_event_counts.get(stage, 0) + 1
|
|
957
998
|
self._install_progress_event_counts[stage] = count
|
|
958
999
|
|
|
@@ -960,7 +1001,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
960
1001
|
return self._truncate_progress_summary(normalized)
|
|
961
1002
|
|
|
962
1003
|
if stage == "解析依赖":
|
|
963
|
-
detail = normalized[len("Collecting "):].split(" (from ", 1)[0]
|
|
1004
|
+
detail = normalized[len("Collecting ") :].split(" (from ", 1)[0]
|
|
964
1005
|
summary = f"已解析 {count} 个依赖,最近: {detail}"
|
|
965
1006
|
if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 10 == 0:
|
|
966
1007
|
return summary
|
|
@@ -973,35 +1014,36 @@ class CodeBuilder(BaseBuilder):
|
|
|
973
1014
|
|
|
974
1015
|
if stage == "下载依赖":
|
|
975
1016
|
if normalized.startswith("Using cached "):
|
|
976
|
-
payload = normalized[len("Using cached "):]
|
|
1017
|
+
payload = normalized[len("Using cached ") :]
|
|
977
1018
|
elif normalized.startswith("Downloading "):
|
|
978
|
-
payload = normalized[len("Downloading "):]
|
|
1019
|
+
payload = normalized[len("Downloading ") :]
|
|
979
1020
|
else:
|
|
980
1021
|
payload = normalized
|
|
981
1022
|
target = self._extract_pip_artifact_name(payload)
|
|
982
|
-
|
|
1023
|
+
elapsed = self._format_elapsed(self._install_progress_started_at)
|
|
1024
|
+
summary = f"已处理 {count} 个 wheel,耗时 {elapsed},最近: {target}"
|
|
983
1025
|
if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 5 == 0:
|
|
984
1026
|
return summary
|
|
985
1027
|
return None
|
|
986
1028
|
|
|
987
1029
|
if stage == "构建 wheel":
|
|
988
|
-
detail = normalized[len("Building wheel for "):].split(" ", 1)[0]
|
|
1030
|
+
detail = normalized[len("Building wheel for ") :].split(" ", 1)[0]
|
|
989
1031
|
if force_emit or count == 1 or count % 5 == 0:
|
|
990
1032
|
return f"构建 wheel: {detail}"
|
|
991
1033
|
return None
|
|
992
1034
|
|
|
993
1035
|
if stage == "安装依赖":
|
|
994
|
-
detail = normalized[len("Installing collected packages:"):].strip()
|
|
1036
|
+
detail = normalized[len("Installing collected packages:") :].strip()
|
|
995
1037
|
return f"安装包: {self._truncate_progress_summary(detail, max_length=60)}"
|
|
996
1038
|
|
|
997
1039
|
if stage == "复用已安装依赖":
|
|
998
|
-
detail = normalized[len("Requirement already satisfied:"):].split(" in ", 1)[0].strip()
|
|
1040
|
+
detail = normalized[len("Requirement already satisfied:") :].split(" in ", 1)[0].strip()
|
|
999
1041
|
if force_emit or count in self.INSTALL_PROGRESS_EVENT_MILESTONES or count % 10 == 0:
|
|
1000
1042
|
return f"复用依赖: {detail}"
|
|
1001
1043
|
return None
|
|
1002
1044
|
|
|
1003
1045
|
if stage == "安装完成":
|
|
1004
|
-
detail = normalized[len("Successfully installed "):].strip()
|
|
1046
|
+
detail = normalized[len("Successfully installed ") :].strip()
|
|
1005
1047
|
return f"最近结果: {self._truncate_progress_summary(detail, max_length=60)}"
|
|
1006
1048
|
|
|
1007
1049
|
return self._truncate_progress_summary(normalized)
|
|
@@ -1094,8 +1136,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1094
1136
|
timings: dict[str, Optional[float]] = {}
|
|
1095
1137
|
with ThreadPoolExecutor(max_workers=min(4, len(urls))) as executor:
|
|
1096
1138
|
future_to_url = {
|
|
1097
|
-
executor.submit(self._probe_pip_index_latency, url): url
|
|
1098
|
-
for url in urls
|
|
1139
|
+
executor.submit(self._probe_pip_index_latency, url): url for url in urls
|
|
1099
1140
|
}
|
|
1100
1141
|
for future in as_completed(future_to_url):
|
|
1101
1142
|
url = future_to_url[future]
|
|
@@ -1116,7 +1157,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1116
1157
|
best = ordered[0]
|
|
1117
1158
|
best_ms = round((timings[best] or 0.0) * 1000)
|
|
1118
1159
|
return ordered, f"测速优先 {self._short_pip_index_label(best)} ({best_ms} ms)"
|
|
1119
|
-
|
|
1160
|
+
|
|
1120
1161
|
def _install_dependencies(self, requirements_path: Path) -> bool:
|
|
1121
1162
|
"""安装依赖到 deps_dir"""
|
|
1122
1163
|
current_python = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
@@ -1128,9 +1169,8 @@ class CodeBuilder(BaseBuilder):
|
|
|
1128
1169
|
)
|
|
1129
1170
|
|
|
1130
1171
|
try:
|
|
1131
|
-
need_binary_replacement = (
|
|
1132
|
-
sys.platform
|
|
1133
|
-
or (sys.platform.startswith("linux") and current_python != target_python)
|
|
1172
|
+
need_binary_replacement = sys.platform in ("darwin", "win32") or (
|
|
1173
|
+
sys.platform.startswith("linux") and current_python != target_python
|
|
1134
1174
|
)
|
|
1135
1175
|
used_target_runtime_wheels = False
|
|
1136
1176
|
result = None
|
|
@@ -1167,16 +1207,18 @@ class CodeBuilder(BaseBuilder):
|
|
|
1167
1207
|
requirements_path,
|
|
1168
1208
|
target_runtime_wheels=False,
|
|
1169
1209
|
)
|
|
1170
|
-
|
|
1210
|
+
|
|
1171
1211
|
if result.returncode != 0:
|
|
1172
1212
|
self._finish_install_progress()
|
|
1173
|
-
click.secho(" ✗ 安装失败", fg=
|
|
1213
|
+
click.secho(" ✗ 安装失败", fg="red")
|
|
1174
1214
|
if result.stderr:
|
|
1175
|
-
error_lines = [
|
|
1215
|
+
error_lines = [
|
|
1216
|
+
line for line in result.stderr.split("\n") if "ERROR" in line.upper()
|
|
1217
|
+
][:3]
|
|
1176
1218
|
for line in error_lines:
|
|
1177
1219
|
click.echo(f" {line}")
|
|
1178
1220
|
return False
|
|
1179
|
-
|
|
1221
|
+
|
|
1180
1222
|
# 替换非 Linux 平台二进制,或 Linux 下非目标 Python ABI 的二进制
|
|
1181
1223
|
if need_binary_replacement and not used_target_runtime_wheels:
|
|
1182
1224
|
self._emit_install_progress(
|
|
@@ -1185,7 +1227,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1185
1227
|
"检测并替换平台相关二进制",
|
|
1186
1228
|
)
|
|
1187
1229
|
self._replace_platform_binaries()
|
|
1188
|
-
|
|
1230
|
+
|
|
1189
1231
|
# 二进制兼容性校验(避免把不可运行的包部署到 Linux Runtime)
|
|
1190
1232
|
self._emit_install_progress(
|
|
1191
1233
|
96,
|
|
@@ -1195,7 +1237,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1195
1237
|
incompatibles = self._scan_incompatible_binaries_in_deps()
|
|
1196
1238
|
if incompatibles:
|
|
1197
1239
|
self._finish_install_progress()
|
|
1198
|
-
click.secho(" ✗ 检测到与 Linux Runtime 不兼容的关键二进制,构建终止", fg=
|
|
1240
|
+
click.secho(" ✗ 检测到与 Linux Runtime 不兼容的关键二进制,构建终止", fg="red")
|
|
1199
1241
|
for item in incompatibles[:10]:
|
|
1200
1242
|
click.echo(f" - {item}")
|
|
1201
1243
|
if any(i.startswith("python-abi-mismatch:") for i in incompatibles):
|
|
@@ -1204,213 +1246,243 @@ class CodeBuilder(BaseBuilder):
|
|
|
1204
1246
|
f"请使用 Python {target_python} 构建,或改用 Container 模式部署"
|
|
1205
1247
|
)
|
|
1206
1248
|
if any("tiktoken" in i for i in incompatibles):
|
|
1207
|
-
click.echo(
|
|
1249
|
+
click.echo(
|
|
1250
|
+
" 提示: tiktoken 为 langchain-openai 必需;"
|
|
1251
|
+
"若替换失败可重试或检查网络/镜像"
|
|
1252
|
+
)
|
|
1208
1253
|
click.echo(" 建议: 删除 .agentengine/*_build 后重新构建,或在 Linux 环境重新打包")
|
|
1209
1254
|
return False
|
|
1210
|
-
|
|
1211
|
-
deps_count = sum(1 for _ in self.deps_dir.rglob(
|
|
1212
|
-
deps_size = sum(f.stat().st_size for f in self.deps_dir.rglob(
|
|
1255
|
+
|
|
1256
|
+
deps_count = sum(1 for _ in self.deps_dir.rglob("*") if _.is_file())
|
|
1257
|
+
deps_size = sum(f.stat().st_size for f in self.deps_dir.rglob("*") if f.is_file()) / (
|
|
1258
|
+
1024 * 1024
|
|
1259
|
+
)
|
|
1213
1260
|
self._emit_install_progress(
|
|
1214
1261
|
100,
|
|
1215
1262
|
"依赖安装完成",
|
|
1216
1263
|
f"{deps_count} 个文件, {deps_size:.1f} MB",
|
|
1217
1264
|
)
|
|
1218
1265
|
self._finish_install_progress()
|
|
1219
|
-
|
|
1266
|
+
|
|
1220
1267
|
return True
|
|
1221
|
-
|
|
1268
|
+
|
|
1222
1269
|
except subprocess.TimeoutExpired:
|
|
1223
1270
|
self._finish_install_progress()
|
|
1224
1271
|
timeout_minutes = max(1, round(self._pip_install_timeout_seconds() / 60))
|
|
1225
|
-
click.secho(f" ✗ 安装超时 ({timeout_minutes}分钟)", fg=
|
|
1272
|
+
click.secho(f" ✗ 安装超时 ({timeout_minutes}分钟)", fg="red")
|
|
1226
1273
|
return False
|
|
1227
1274
|
except Exception as e:
|
|
1228
1275
|
self._finish_install_progress()
|
|
1229
|
-
click.secho(f" ✗ 依赖安装失败: {e}", fg=
|
|
1276
|
+
click.secho(f" ✗ 依赖安装失败: {e}", fg="red")
|
|
1230
1277
|
return False
|
|
1231
|
-
|
|
1278
|
+
|
|
1232
1279
|
def _replace_platform_binaries(self) -> None:
|
|
1233
1280
|
"""替换非目标运行时平台/ABI 的 C 扩展为 Linux 目标版本。"""
|
|
1234
1281
|
# 模块名到 pip 包名的映射
|
|
1235
1282
|
MODULE_TO_PACKAGE = {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1283
|
+
"_cffi_backend": "cffi",
|
|
1284
|
+
"yaml": "pyyaml",
|
|
1285
|
+
"_yaml": "pyyaml",
|
|
1286
|
+
"rpds": "rpds-py",
|
|
1287
|
+
"PIL": "pillow",
|
|
1288
|
+
"cv2": "opencv-python",
|
|
1289
|
+
"sklearn": "scikit-learn",
|
|
1290
|
+
"_watchdog_fsevents": "watchdog",
|
|
1291
|
+
"google": None, # 跳过命名空间包
|
|
1292
|
+
"grpc": "grpcio",
|
|
1293
|
+
"_grpc": "grpcio",
|
|
1294
|
+
"uuid_utils": "uuid-utils",
|
|
1295
|
+
"pydantic_core": "pydantic-core",
|
|
1296
|
+
"_pydantic_core": "pydantic-core",
|
|
1250
1297
|
# tiktoken: langchain-openai 核心依赖, Rust 编译的 C 扩展
|
|
1251
|
-
|
|
1252
|
-
|
|
1298
|
+
"tiktoken": "tiktoken",
|
|
1299
|
+
"_tiktoken": "tiktoken",
|
|
1253
1300
|
# 其他常见原生扩展
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1301
|
+
"regex": "regex",
|
|
1302
|
+
"_regex": "regex",
|
|
1303
|
+
"multidict": "multidict",
|
|
1304
|
+
"yarl": "yarl",
|
|
1305
|
+
"aiohttp": "aiohttp",
|
|
1306
|
+
"frozenlist": "frozenlist",
|
|
1307
|
+
"charset_normalizer": "charset-normalizer",
|
|
1308
|
+
"msgpack": "msgpack",
|
|
1262
1309
|
# Windows 特有
|
|
1263
|
-
|
|
1264
|
-
|
|
1310
|
+
"win32": "pywin32",
|
|
1311
|
+
"win32com": "pywin32",
|
|
1265
1312
|
}
|
|
1266
|
-
|
|
1313
|
+
|
|
1267
1314
|
# 找到所有二进制文件
|
|
1268
1315
|
binary_files = []
|
|
1269
|
-
if sys.platform ==
|
|
1270
|
-
binary_files = list(self.deps_dir.rglob(
|
|
1271
|
-
elif sys.platform ==
|
|
1272
|
-
binary_files = list(self.deps_dir.rglob(
|
|
1273
|
-
elif sys.platform.startswith(
|
|
1274
|
-
binary_files = list(self.deps_dir.rglob(
|
|
1275
|
-
|
|
1316
|
+
if sys.platform == "darwin":
|
|
1317
|
+
binary_files = list(self.deps_dir.rglob("*.so")) + list(self.deps_dir.rglob("*.dylib"))
|
|
1318
|
+
elif sys.platform == "win32":
|
|
1319
|
+
binary_files = list(self.deps_dir.rglob("*.pyd")) + list(self.deps_dir.rglob("*.dll"))
|
|
1320
|
+
elif sys.platform.startswith("linux"):
|
|
1321
|
+
binary_files = list(self.deps_dir.rglob("*.so"))
|
|
1322
|
+
|
|
1276
1323
|
if not binary_files:
|
|
1277
1324
|
return
|
|
1278
|
-
|
|
1325
|
+
|
|
1279
1326
|
# 提取需要替换的包名
|
|
1280
1327
|
packages_to_replace: Set[str] = set()
|
|
1281
1328
|
for bin_file in binary_files:
|
|
1282
1329
|
rel_path = bin_file.relative_to(self.deps_dir)
|
|
1283
1330
|
parts = rel_path.parts
|
|
1284
|
-
|
|
1331
|
+
|
|
1285
1332
|
# 忽略 bin 目录下的 dll (通常是 runtime)
|
|
1286
|
-
if
|
|
1333
|
+
if "bin" in parts:
|
|
1287
1334
|
continue
|
|
1288
|
-
|
|
1335
|
+
|
|
1289
1336
|
if len(parts) > 1:
|
|
1290
1337
|
detected_name = parts[0]
|
|
1291
1338
|
else:
|
|
1292
|
-
detected_name = bin_file.name.split(
|
|
1293
|
-
|
|
1339
|
+
detected_name = bin_file.name.split(".")[0]
|
|
1340
|
+
|
|
1294
1341
|
# 跳过特定文件夹
|
|
1295
|
-
if detected_name in (
|
|
1342
|
+
if detected_name in ("__pycache__", "bin", "include", "lib", "Scripts"):
|
|
1296
1343
|
continue
|
|
1297
|
-
|
|
1344
|
+
|
|
1298
1345
|
if detected_name in MODULE_TO_PACKAGE:
|
|
1299
1346
|
pkg_name = MODULE_TO_PACKAGE[detected_name]
|
|
1300
1347
|
if pkg_name:
|
|
1301
1348
|
packages_to_replace.add(pkg_name)
|
|
1302
1349
|
else:
|
|
1303
1350
|
packages_to_replace.add(detected_name)
|
|
1304
|
-
|
|
1351
|
+
|
|
1305
1352
|
if not packages_to_replace:
|
|
1306
1353
|
return
|
|
1307
|
-
|
|
1308
|
-
click.echo(
|
|
1309
|
-
|
|
1354
|
+
|
|
1355
|
+
click.echo(
|
|
1356
|
+
f"\r 检测到 {len(binary_files)} 个二进制文件 ({sys.platform}), "
|
|
1357
|
+
f"替换 {len(packages_to_replace)} 个包为 Linux 版本"
|
|
1358
|
+
)
|
|
1359
|
+
|
|
1310
1360
|
# 下载 Linux wheels
|
|
1311
1361
|
wheels_dir = self.build_dir / "linux_wheels"
|
|
1312
1362
|
if wheels_dir.exists():
|
|
1313
1363
|
shutil.rmtree(wheels_dir)
|
|
1314
1364
|
wheels_dir.mkdir(parents=True)
|
|
1315
|
-
|
|
1365
|
+
|
|
1316
1366
|
replaced_count = 0
|
|
1317
1367
|
total_packages = len(packages_to_replace)
|
|
1318
1368
|
for index, pkg_name in enumerate(sorted(packages_to_replace), start=1):
|
|
1319
1369
|
# 提取确切的版本号以避免不兼容问题 (如 pydantic-core)
|
|
1320
1370
|
target_version = ""
|
|
1321
|
-
search_name = pkg_name.replace(
|
|
1371
|
+
search_name = pkg_name.replace("-", "_").lower()
|
|
1322
1372
|
for info_dir in self.deps_dir.glob(f"{search_name}-*.dist-info"):
|
|
1323
|
-
version_str = info_dir.name[
|
|
1373
|
+
version_str = info_dir.name[
|
|
1374
|
+
len(search_name) + 1 : -10
|
|
1375
|
+
] # remove search_name + '-' and '.dist-info'
|
|
1324
1376
|
target_version = f"=={version_str}"
|
|
1325
1377
|
break
|
|
1326
|
-
|
|
1378
|
+
|
|
1327
1379
|
pkg_with_version = f"{pkg_name}{target_version}"
|
|
1328
1380
|
self._emit_install_progress(
|
|
1329
1381
|
88 + min(6, round((index / max(total_packages, 1)) * 6)),
|
|
1330
1382
|
"替换 Linux wheels",
|
|
1331
1383
|
f"{index}/{total_packages} {pkg_with_version}",
|
|
1332
1384
|
)
|
|
1333
|
-
|
|
1385
|
+
|
|
1334
1386
|
try:
|
|
1335
1387
|
downloaded = False
|
|
1336
1388
|
for index_url in self._pip_index_candidates():
|
|
1337
1389
|
download_cmd = [
|
|
1338
|
-
sys.executable,
|
|
1390
|
+
sys.executable,
|
|
1391
|
+
"-m",
|
|
1392
|
+
"pip",
|
|
1393
|
+
"download",
|
|
1339
1394
|
pkg_with_version,
|
|
1340
|
-
"-d",
|
|
1341
|
-
|
|
1342
|
-
"--platform",
|
|
1343
|
-
"
|
|
1344
|
-
"--platform",
|
|
1345
|
-
"
|
|
1346
|
-
"--
|
|
1395
|
+
"-d",
|
|
1396
|
+
str(wheels_dir),
|
|
1397
|
+
"--platform",
|
|
1398
|
+
"manylinux2014_x86_64",
|
|
1399
|
+
"--platform",
|
|
1400
|
+
"manylinux_2_17_x86_64",
|
|
1401
|
+
"--platform",
|
|
1402
|
+
"manylinux_2_28_x86_64",
|
|
1403
|
+
"--platform",
|
|
1404
|
+
"musllinux_1_2_x86_64",
|
|
1405
|
+
"--platform",
|
|
1406
|
+
"linux_x86_64",
|
|
1407
|
+
"--python-version",
|
|
1408
|
+
self.TARGET_PYTHON_VERSION,
|
|
1347
1409
|
"--only-binary=:all:",
|
|
1348
|
-
"--implementation",
|
|
1410
|
+
"--implementation",
|
|
1411
|
+
"cp",
|
|
1349
1412
|
"--no-deps",
|
|
1350
1413
|
"--quiet",
|
|
1351
1414
|
"--disable-pip-version-check",
|
|
1352
|
-
"--retries",
|
|
1353
|
-
"
|
|
1354
|
-
"--
|
|
1415
|
+
"--retries",
|
|
1416
|
+
"2",
|
|
1417
|
+
"--timeout",
|
|
1418
|
+
"30",
|
|
1419
|
+
"--cache-dir",
|
|
1420
|
+
str(self.pip_cache_dir),
|
|
1355
1421
|
]
|
|
1356
1422
|
if index_url:
|
|
1357
1423
|
download_cmd += ["-i", index_url]
|
|
1358
|
-
result = subprocess.run(
|
|
1424
|
+
result = subprocess.run(
|
|
1425
|
+
download_cmd, capture_output=True, text=True, timeout=90
|
|
1426
|
+
)
|
|
1359
1427
|
if result.returncode == 0:
|
|
1360
1428
|
downloaded = True
|
|
1361
1429
|
break
|
|
1362
1430
|
if downloaded:
|
|
1363
1431
|
replaced_count += 1
|
|
1364
1432
|
else:
|
|
1365
|
-
failed_msg =
|
|
1366
|
-
|
|
1433
|
+
failed_msg = (
|
|
1434
|
+
result.stderr.strip().split("\n")[-1]
|
|
1435
|
+
if result and result.stderr
|
|
1436
|
+
else "unknown"
|
|
1437
|
+
)
|
|
1438
|
+
click.secho(f" ⚠ 替换失败: {pkg_with_version} ({failed_msg})", fg="yellow")
|
|
1367
1439
|
except Exception as e:
|
|
1368
|
-
click.secho(f" ⚠ 替换异常: {pkg_name} ({e})", fg=
|
|
1369
|
-
|
|
1440
|
+
click.secho(f" ⚠ 替换异常: {pkg_name} ({e})", fg="yellow")
|
|
1441
|
+
|
|
1370
1442
|
# 解压 wheel 覆盖到 deps_dir
|
|
1371
1443
|
for wheel_file in wheels_dir.glob("*.whl"):
|
|
1372
1444
|
try:
|
|
1373
|
-
wheel_name = wheel_file.name.split(
|
|
1374
|
-
|
|
1445
|
+
wheel_name = wheel_file.name.split("-")[0].lower().replace("_", "-")
|
|
1446
|
+
|
|
1375
1447
|
# 删除旧的包目录
|
|
1376
1448
|
for old_dir in self.deps_dir.iterdir():
|
|
1377
|
-
if old_dir.is_dir() and old_dir.name.lower().replace(
|
|
1449
|
+
if old_dir.is_dir() and old_dir.name.lower().replace("_", "-") == wheel_name:
|
|
1378
1450
|
shutil.rmtree(old_dir)
|
|
1379
|
-
# 不要 break
|
|
1380
|
-
|
|
1451
|
+
# 不要 break:一个包可能有多个目录,例如 dist-info。
|
|
1452
|
+
|
|
1381
1453
|
# 删除根目录下的二进制文件
|
|
1382
|
-
for ext in (
|
|
1454
|
+
for ext in ("*.so", "*.dylib", "*.pyd", "*.dll"):
|
|
1383
1455
|
for bin_file in self.deps_dir.glob(f"{wheel_name}*{ext[1:]}"):
|
|
1384
1456
|
try:
|
|
1385
1457
|
bin_file.unlink()
|
|
1386
|
-
except:
|
|
1458
|
+
except OSError:
|
|
1387
1459
|
pass
|
|
1388
1460
|
for bin_file in self.deps_dir.glob(f"{wheel_name.replace('-', '_')}*{ext[1:]}"):
|
|
1389
1461
|
try:
|
|
1390
1462
|
bin_file.unlink()
|
|
1391
|
-
except:
|
|
1463
|
+
except OSError:
|
|
1392
1464
|
pass
|
|
1393
|
-
|
|
1465
|
+
|
|
1394
1466
|
# 解压新的 wheel
|
|
1395
|
-
with zipfile.ZipFile(wheel_file,
|
|
1467
|
+
with zipfile.ZipFile(wheel_file, "r") as zf:
|
|
1396
1468
|
zf.extractall(self.deps_dir)
|
|
1397
1469
|
except Exception:
|
|
1398
1470
|
pass
|
|
1399
|
-
|
|
1471
|
+
|
|
1400
1472
|
shutil.rmtree(wheels_dir, ignore_errors=True)
|
|
1401
|
-
|
|
1473
|
+
|
|
1402
1474
|
# 清理所有残留的非 Linux 平台二进制文件
|
|
1403
1475
|
# (wheel 解压后可能有旧的 darwin/win .so 文件未被覆盖)
|
|
1404
1476
|
cleaned_count = 0
|
|
1405
|
-
for so_file in list(self.deps_dir.rglob(
|
|
1477
|
+
for so_file in list(self.deps_dir.rglob("*.so")):
|
|
1406
1478
|
name = so_file.name.lower()
|
|
1407
|
-
if
|
|
1479
|
+
if "darwin" in name or "win" in name:
|
|
1408
1480
|
try:
|
|
1409
1481
|
so_file.unlink()
|
|
1410
1482
|
cleaned_count += 1
|
|
1411
1483
|
except Exception:
|
|
1412
1484
|
pass
|
|
1413
|
-
for dylib_file in list(self.deps_dir.rglob(
|
|
1485
|
+
for dylib_file in list(self.deps_dir.rglob("*.dylib")):
|
|
1414
1486
|
try:
|
|
1415
1487
|
dylib_file.unlink()
|
|
1416
1488
|
cleaned_count += 1
|
|
@@ -1418,7 +1490,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1418
1490
|
pass
|
|
1419
1491
|
if cleaned_count > 0:
|
|
1420
1492
|
click.echo(f" ✓ 清理 {cleaned_count} 个残留平台二进制文件")
|
|
1421
|
-
|
|
1493
|
+
|
|
1422
1494
|
click.echo(f" ✓ 成功替换 {replaced_count}/{len(packages_to_replace)} 个包")
|
|
1423
1495
|
|
|
1424
1496
|
def _is_linux_so(self, name: str) -> bool:
|
|
@@ -1463,7 +1535,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1463
1535
|
|
|
1464
1536
|
def _detect_critical_binary_issues(self, names: List[str]) -> List[str]:
|
|
1465
1537
|
issues: List[str] = []
|
|
1466
|
-
|
|
1538
|
+
|
|
1467
1539
|
# 定义需要检查的关键原生扩展模块
|
|
1468
1540
|
# 格式: (描述, 正则模式)
|
|
1469
1541
|
critical_modules = [
|
|
@@ -1474,7 +1546,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1474
1546
|
# tiktoken/_tiktoken: langchain-openai 核心依赖 (Rust 编译)
|
|
1475
1547
|
("tiktoken/_tiktoken", r"tiktoken/_tiktoken.*\.(so|pyd)$"),
|
|
1476
1548
|
]
|
|
1477
|
-
|
|
1549
|
+
|
|
1478
1550
|
for module_name, pattern in critical_modules:
|
|
1479
1551
|
matched_bins = [n for n in names if re.search(pattern, n)]
|
|
1480
1552
|
if matched_bins:
|
|
@@ -1487,19 +1559,21 @@ class CodeBuilder(BaseBuilder):
|
|
|
1487
1559
|
f"python-abi-mismatch:{module_name}:"
|
|
1488
1560
|
f"expected-cpython-{self.TARGET_PYTHON_VERSION}-or-abi3"
|
|
1489
1561
|
)
|
|
1490
|
-
|
|
1562
|
+
|
|
1491
1563
|
# 通用检查: 所有 .so 文件中不应包含 darwin/win 平台标识
|
|
1492
|
-
all_so_files = [n for n in names if n.endswith(
|
|
1493
|
-
darwin_so_count = sum(1 for n in all_so_files if
|
|
1564
|
+
all_so_files = [n for n in names if n.endswith(".so")]
|
|
1565
|
+
darwin_so_count = sum(1 for n in all_so_files if "darwin" in n.lower())
|
|
1494
1566
|
if darwin_so_count > 0:
|
|
1495
1567
|
issues.append(f"warning:found-{darwin_so_count}-darwin-so-files")
|
|
1496
|
-
|
|
1568
|
+
|
|
1497
1569
|
return issues
|
|
1498
|
-
|
|
1570
|
+
|
|
1499
1571
|
def _package_zip(self, zip_path: Path, detection_result) -> None:
|
|
1500
1572
|
"""打包 zip 文件"""
|
|
1501
1573
|
project_files = list(self._iter_project_files())
|
|
1502
|
-
dependency_files = [
|
|
1574
|
+
dependency_files = [
|
|
1575
|
+
file_path for file_path in self.deps_dir.rglob("*") if file_path.is_file()
|
|
1576
|
+
]
|
|
1503
1577
|
bundled_source_files = list(self._iter_bundled_source_files())
|
|
1504
1578
|
dependency_size = sum(file_path.stat().st_size for file_path in dependency_files)
|
|
1505
1579
|
if dependency_files:
|
|
@@ -1509,12 +1583,12 @@ class CodeBuilder(BaseBuilder):
|
|
|
1509
1583
|
f"{len(bundled_source_files)} 个 runtime 文件"
|
|
1510
1584
|
)
|
|
1511
1585
|
|
|
1512
|
-
with zipfile.ZipFile(zip_path,
|
|
1586
|
+
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
1513
1587
|
# 添加项目文件
|
|
1514
1588
|
for file_path in project_files:
|
|
1515
1589
|
arcname = file_path.relative_to(self.project_dir).as_posix()
|
|
1516
1590
|
zf.write(file_path, arcname)
|
|
1517
|
-
|
|
1591
|
+
|
|
1518
1592
|
# 添加依赖
|
|
1519
1593
|
deps_count = 0
|
|
1520
1594
|
for file_path in dependency_files:
|
|
@@ -1523,7 +1597,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1523
1597
|
deps_count += 1
|
|
1524
1598
|
self._emit_package_progress("打包依赖", deps_count, len(dependency_files))
|
|
1525
1599
|
self._finish_package_progress()
|
|
1526
|
-
|
|
1600
|
+
|
|
1527
1601
|
# 添加随运行时下发的 ksadk 源码
|
|
1528
1602
|
bundled_source_count = 0
|
|
1529
1603
|
for package_name, relative, file_path in bundled_source_files:
|
|
@@ -1536,13 +1610,13 @@ class CodeBuilder(BaseBuilder):
|
|
|
1536
1610
|
len(bundled_source_files),
|
|
1537
1611
|
)
|
|
1538
1612
|
self._finish_package_progress()
|
|
1539
|
-
|
|
1613
|
+
|
|
1540
1614
|
click.echo(f" ✓ 打包运行时源码: {bundled_source_count} 个文件")
|
|
1541
|
-
|
|
1615
|
+
|
|
1542
1616
|
# 添加 entrypoint
|
|
1543
1617
|
entrypoint_content = self._generate_entrypoint(detection_result)
|
|
1544
1618
|
zf.writestr("entrypoint.py", entrypoint_content)
|
|
1545
|
-
|
|
1619
|
+
|
|
1546
1620
|
click.echo(f" ✓ 打包完成: {len(project_files)} 个项目文件 + {deps_count} 个依赖文件")
|
|
1547
1621
|
self._emit_package_size_report(zip_path)
|
|
1548
1622
|
|
|
@@ -1586,10 +1660,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1586
1660
|
)
|
|
1587
1661
|
if by_top_level:
|
|
1588
1662
|
top_items = sorted(by_top_level.items(), key=lambda item: item[1], reverse=True)[:limit]
|
|
1589
|
-
summary = ", ".join(
|
|
1590
|
-
f"{name} {size / (1024 * 1024):.1f} MB"
|
|
1591
|
-
for name, size in top_items
|
|
1592
|
-
)
|
|
1663
|
+
summary = ", ".join(f"{name} {size / (1024 * 1024):.1f} MB" for name, size in top_items)
|
|
1593
1664
|
click.echo(f" 体积 Top{len(top_items)}: {summary}")
|
|
1594
1665
|
if (
|
|
1595
1666
|
raw_total > self.CONTAINER_SUGGESTION_RAW_THRESHOLD_BYTES
|
|
@@ -1660,7 +1731,7 @@ class CodeBuilder(BaseBuilder):
|
|
|
1660
1731
|
self._package_progress_last_line = ""
|
|
1661
1732
|
self._package_progress_last_percent_by_label = {}
|
|
1662
1733
|
self._package_progress_logged_milestone_by_label = {}
|
|
1663
|
-
|
|
1734
|
+
|
|
1664
1735
|
def _generate_entrypoint(self, detection_result) -> str:
|
|
1665
1736
|
"""生成 entrypoint.py"""
|
|
1666
1737
|
package_name = Path(detection_result.package_path).name
|
|
@@ -1787,7 +1858,10 @@ logger.info(f"框架: {{detection_result.name}}")
|
|
|
1787
1858
|
logger.info(f"入口: {{detection_result.entry_point}}")
|
|
1788
1859
|
|
|
1789
1860
|
# 初始化 Tracing (如果配置了 Langfuse、标准 OTLP HTTP 或 CloudMonitor OTLP)
|
|
1790
|
-
has_otlp = bool(
|
|
1861
|
+
has_otlp = bool(
|
|
1862
|
+
os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
|
|
1863
|
+
or os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
|
|
1864
|
+
)
|
|
1791
1865
|
has_cloud_monitor_otlp = bool(
|
|
1792
1866
|
os.environ.get("CLOUD_MONITOR_APP_KEY")
|
|
1793
1867
|
or os.environ.get("CLOUD_MONITOR_OTLP_ENDPOINT")
|
|
@@ -1798,11 +1872,19 @@ has_cloud_monitor_langfuse = bool(
|
|
|
1798
1872
|
or os.environ.get("CLOUD_MONITOR_LANGFUSE_SECRET_KEY")
|
|
1799
1873
|
or os.environ.get("CLOUD_MONITOR_LANGFUSE_HOST")
|
|
1800
1874
|
)
|
|
1801
|
-
if
|
|
1875
|
+
if (
|
|
1876
|
+
os.environ.get("LANGFUSE_PUBLIC_KEY")
|
|
1877
|
+
or has_otlp
|
|
1878
|
+
or has_cloud_monitor_otlp
|
|
1879
|
+
or has_cloud_monitor_langfuse
|
|
1880
|
+
):
|
|
1802
1881
|
try:
|
|
1803
1882
|
from ksadk.tracing import setup_tracing
|
|
1804
|
-
|
|
1805
|
-
use_callback_only =
|
|
1883
|
+
|
|
1884
|
+
use_callback_only = (
|
|
1885
|
+
os.environ.get("LANGFUSE_USE_CALLBACK", "").strip().lower()
|
|
1886
|
+
in ("1", "true", "yes", "on")
|
|
1887
|
+
)
|
|
1806
1888
|
|
|
1807
1889
|
setup_tracing(use_callback_only=use_callback_only)
|
|
1808
1890
|
logger.info(
|
|
@@ -1818,7 +1900,7 @@ if os.environ.get("LANGFUSE_PUBLIC_KEY") or has_otlp or has_cloud_monitor_otlp o
|
|
|
1818
1900
|
logger.info("正在加载 Agent...")
|
|
1819
1901
|
runner = create_runner(detection_result, CODE_ROOT)
|
|
1820
1902
|
runner.load_agent()
|
|
1821
|
-
set_runner(runner)
|
|
1903
|
+
set_runner(runner, loaded=True)
|
|
1822
1904
|
logger.info("Agent 加载成功!")
|
|
1823
1905
|
|
|
1824
1906
|
if __name__ == "__main__":
|
|
@@ -1848,13 +1930,13 @@ if __name__ == "__main__":
|
|
|
1848
1930
|
self._pip_index_candidates_cache = candidates
|
|
1849
1931
|
return list(candidates)
|
|
1850
1932
|
|
|
1851
|
-
|
|
1933
|
+
fallback_seen: Set[str] = set()
|
|
1852
1934
|
urls: list[str] = []
|
|
1853
1935
|
for candidate in self.PIP_INDEX_FALLBACKS:
|
|
1854
1936
|
normalized = self._normalize_pip_index_url(candidate)
|
|
1855
|
-
if normalized in
|
|
1937
|
+
if normalized in fallback_seen:
|
|
1856
1938
|
continue
|
|
1857
|
-
|
|
1939
|
+
fallback_seen.add(normalized)
|
|
1858
1940
|
urls.append(normalized)
|
|
1859
1941
|
|
|
1860
1942
|
ordered, summary = self._rank_pip_index_urls(urls)
|