agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
- agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
- ksadk/a2a/__init__.py +178 -11
- ksadk/a2a/bootstrap.py +334 -0
- ksadk/a2a/card.py +120 -0
- ksadk/a2a/context_store.py +203 -0
- ksadk/a2a/control_plane.py +818 -0
- ksadk/a2a/credential.py +99 -0
- ksadk/a2a/event_adapter.py +149 -0
- ksadk/a2a/executor.py +406 -89
- ksadk/a2a/external_transport.py +516 -0
- ksadk/a2a/identity.py +304 -0
- ksadk/a2a/ids.py +22 -0
- ksadk/a2a/langgraph.py +285 -0
- ksadk/a2a/resume_store.py +201 -0
- ksadk/a2a/routes.py +105 -0
- ksadk/a2a/server.py +107 -60
- ksadk/a2a/space_client.py +1101 -0
- ksadk/a2a/task_adapter.py +423 -0
- ksadk/a2a/task_event_dispatcher.py +167 -0
- ksadk/a2a/task_event_outbox.py +326 -0
- ksadk/a2a/task_store.py +206 -0
- ksadk/a2ui/__init__.py +27 -0
- ksadk/a2ui/core.py +186 -0
- ksadk/a2ui/models.py +166 -0
- ksadk/a2ui/renderer.py +198 -0
- ksadk/agui/__init__.py +9 -0
- ksadk/agui/a2ui_projection.py +92 -0
- ksadk/agui/agent.py +857 -0
- ksadk/agui/config.py +86 -0
- ksadk/agui/routes.py +55 -0
- ksadk/api/client.py +233 -191
- ksadk/builders/__init__.py +2 -0
- ksadk/builders/base.py +16 -14
- ksadk/builders/code_builder.py +318 -236
- ksadk/builders/container_builder.py +187 -167
- ksadk/builders/framework_requirements.py +29 -11
- ksadk/builders/ks3_uploader.py +58 -27
- ksadk/builders/managed_runtime_builder.py +145 -0
- ksadk/builders/mcp_builder.py +91 -92
- ksadk/builders/requirements_utils.py +1 -5
- ksadk/cli/__init__.py +89 -171
- ksadk/cli/cmd_a2a.py +254 -70
- ksadk/cli/cmd_agent.py +19 -5
- ksadk/cli/cmd_build.py +158 -29
- ksadk/cli/cmd_completion.py +47 -35
- ksadk/cli/cmd_config.py +267 -198
- ksadk/cli/cmd_create.py +500 -720
- ksadk/cli/cmd_dashboard.py +120 -45
- ksadk/cli/cmd_deploy.py +117 -58
- ksadk/cli/cmd_destroy.py +44 -14
- ksadk/cli/cmd_files.py +63 -24
- ksadk/cli/cmd_hermes.py +168 -89
- ksadk/cli/cmd_invoke.py +184 -122
- ksadk/cli/cmd_launch.py +64 -28
- ksadk/cli/cmd_mcp.py +98 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +483 -227
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +33 -10
- ksadk/cli/cmd_status.py +80 -31
- ksadk/cli/cmd_version.py +20 -13
- ksadk/cli/cmd_web.py +89 -13
- ksadk/cli/deploy_utils.py +20 -20
- ksadk/cli/dry_run.py +5 -3
- ksadk/cli/error_utils.py +37 -12
- ksadk/cli/global_options.py +1 -2
- ksadk/cli/local_runtime.py +1 -2
- ksadk/cli/model_catalog.py +6 -2
- ksadk/cli/network_options.py +34 -14
- ksadk/cli/resource_common.py +6 -2
- ksadk/cli/storage.py +0 -1
- ksadk/cli/ui.py +5 -3
- ksadk/cli/workflow_common.py +51 -17
- ksadk/codex/__init__.py +12 -0
- ksadk/codex/client.py +408 -0
- ksadk/codex/phase.py +143 -0
- ksadk/codex/runtime.py +449 -0
- ksadk/common/aicp_env.py +0 -1
- ksadk/common/auth.py +2 -2
- ksadk/common/constants.py +1 -3
- ksadk/common/llm_utils.py +17 -13
- ksadk/compat/__init__.py +5 -0
- ksadk/compat/adk_compat.py +200 -0
- ksadk/compat/copilotkit_a2ui.py +330 -0
- ksadk/configs/__init__.py +12 -12
- ksadk/configs/env_registry.py +500 -100
- ksadk/configs/global_config.py +2 -1
- ksadk/configs/settings.py +151 -124
- ksadk/conversations/__init__.py +7 -5
- ksadk/conversations/attachment_storage.py +16 -11
- ksadk/conversations/attachments.py +44 -36
- ksadk/conversations/compaction_pipeline.py +12 -12
- ksadk/conversations/compaction_prompt.py +9 -5
- ksadk/conversations/context.py +221 -6
- ksadk/conversations/message_projection.py +438 -51
- ksadk/conversations/model_context.py +12 -6
- ksadk/conversations/model_options.py +0 -1
- ksadk/conversations/normalize.py +20 -16
- ksadk/conversations/reasoning_markup.py +1 -1
- ksadk/conversations/runtime.py +339 -5255
- ksadk/conversations/runtime_compaction.py +333 -0
- ksadk/conversations/runtime_constants.py +27 -0
- ksadk/conversations/runtime_governance.py +185 -0
- ksadk/conversations/runtime_input.py +652 -0
- ksadk/conversations/runtime_invocation.py +395 -0
- ksadk/conversations/runtime_metadata.py +475 -0
- ksadk/conversations/runtime_observability.py +355 -0
- ksadk/conversations/runtime_payloads.py +272 -0
- ksadk/conversations/runtime_persistence.py +440 -0
- ksadk/conversations/runtime_preparation.py +418 -0
- ksadk/conversations/runtime_resume.py +816 -0
- ksadk/conversations/runtime_stream_events.py +902 -0
- ksadk/conversations/runtime_streaming.py +547 -0
- ksadk/conversations/semantic_summary.py +17 -5
- ksadk/conversations/session_title.py +6 -3
- ksadk/deployment/__init__.py +15 -16
- ksadk/deployment/agent_access.py +12 -23
- ksadk/deployment/base.py +51 -38
- ksadk/deployment/manager.py +63 -65
- ksadk/deployment/providers/serverless.py +309 -202
- ksadk/deployment/registry.py +38 -32
- ksadk/deployment/state.py +11 -11
- ksadk/deployment/ui_config.py +1 -1
- ksadk/detection/__init__.py +1 -1
- ksadk/detection/detector.py +146 -83
- ksadk/detection/mcp_detector.py +9 -6
- ksadk/events/__init__.py +25 -0
- ksadk/events/parser.py +191 -0
- ksadk/events/replay.py +37 -0
- ksadk/events/runtime_event.py +264 -0
- ksadk/events/store.py +317 -0
- ksadk/harness/__init__.py +34 -0
- ksadk/harness/app.py +278 -0
- ksadk/harness/config.py +181 -0
- ksadk/harness/reasoner.py +105 -0
- ksadk/harness/runner.py +211 -0
- ksadk/harness/sandbox.py +141 -0
- ksadk/harness/tools.py +222 -0
- ksadk/hermes_terminal.py +23 -12
- ksadk/identity/resolver.py +15 -9
- ksadk/ids.py +32 -0
- ksadk/knowledge_base/adk_tool.py +1 -1
- ksadk/knowledge_base/client.py +23 -29
- ksadk/managed_runtime.py +188 -0
- ksadk/markdown.py +5 -2
- ksadk/mcp_runtime/__init__.py +9 -6
- ksadk/memory/__init__.py +1 -1
- ksadk/memory/adk/__init__.py +2 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
- ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
- ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
- ksadk/memory/adk/long_term_memory.py +31 -35
- ksadk/memory/adk/resilient_session_service.py +4 -5
- ksadk/memory/adk/short_term_memory.py +12 -21
- ksadk/memory/adk_tool.py +3 -2
- ksadk/memory/backends/base.py +19 -19
- ksadk/memory/backends/memory.py +24 -25
- ksadk/memory/backends/redis.py +31 -30
- ksadk/memory/langchain_tool.py +2 -1
- ksadk/memory/manager.py +52 -47
- ksadk/memory/service.py +10 -8
- ksadk/model_policy.py +15 -7
- ksadk/model_proxy/__init__.py +51 -0
- ksadk/model_proxy/bootstrap.py +80 -0
- ksadk/model_proxy/cache.py +165 -0
- ksadk/model_proxy/config.py +45 -0
- ksadk/model_proxy/demo_webui.py +115 -0
- ksadk/model_proxy/detect.py +145 -0
- ksadk/model_proxy/gate.py +68 -0
- ksadk/model_proxy/namespace.py +173 -0
- ksadk/model_proxy/protocol_proxy.py +22 -0
- ksadk/model_proxy/server.py +300 -0
- ksadk/model_proxy/transform.py +691 -0
- ksadk/openclaw_gateway.py +29 -14
- ksadk/runners/adk_runner.py +345 -120
- ksadk/runners/base_runner.py +46 -29
- ksadk/runners/codex_runner.py +132 -0
- ksadk/runners/deepagents_runner.py +0 -1
- ksadk/runners/factory.py +17 -5
- ksadk/runners/langchain_runner.py +29 -742
- ksadk/runners/langgraph_runner.py +352 -90
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +134 -33
- ksadk/runners/usage_accumulator.py +1 -0
- ksadk/runners/utils/langfuse.py +3 -3
- ksadk/runners/utils/loader.py +7 -7
- ksadk/runtime/__init__.py +41 -0
- ksadk/runtime/adapter.py +345 -0
- ksadk/runtime/framework_adapters.py +124 -0
- ksadk/runtime/preprocessing.py +118 -0
- ksadk/runtime/runner_adapter.py +873 -0
- ksadk/runtime_context.py +18 -3
- ksadk/runtime_state.py +0 -1
- ksadk/sandbox/__init__.py +4 -1
- ksadk/sandbox/backends/__init__.py +4 -1
- ksadk/sandbox/backends/e2b.py +13 -5
- ksadk/sandbox/backends/local_process.py +11 -3
- ksadk/sandbox/base.py +10 -15
- ksadk/sandbox/factory.py +14 -4
- ksadk/sandbox/registry.py +57 -6
- ksadk/server/api_models.py +15 -5
- ksadk/server/app.py +455 -3885
- ksadk/server/composition.py +21 -0
- ksadk/server/factory.py +624 -0
- ksadk/server/routes/__init__.py +1 -0
- ksadk/server/routes/checkpoint_resolution.py +116 -0
- ksadk/server/routes/common.py +713 -0
- ksadk/server/routes/control.py +327 -0
- ksadk/server/routes/dependencies.py +98 -0
- ksadk/server/routes/misc.py +448 -0
- ksadk/server/routes/models.py +443 -0
- ksadk/server/routes/openai_compat.py +207 -0
- ksadk/server/routes/projection.py +795 -0
- ksadk/server/routes/routers.py +52 -0
- ksadk/server/routes/run.py +726 -0
- ksadk/server/routes/sessions.py +649 -0
- ksadk/server/routes/streaming.py +223 -0
- ksadk/server/routes/workspace.py +381 -0
- ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
- ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
- ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
- ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
- ksadk/server/static/assets/{arc-BA13sHc9.js → arc-CFKwSk_B.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
- ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
- ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
- ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
- ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
- ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
- ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
- ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
- ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
- ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
- ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
- ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
- ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
- ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
- ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
- ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
- ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
- ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
- ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
- ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
- ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
- ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
- ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
- ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
- ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
- ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
- ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
- ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
- ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/server/terminal_sessions.py +111 -58
- ksadk/sessions/__init__.py +69 -19
- ksadk/sessions/base.py +31 -11
- ksadk/sessions/continuity.py +2 -3
- ksadk/sessions/in_memory.py +46 -2
- ksadk/sessions/local_service.py +148 -17
- ksadk/sessions/postgres_service.py +143 -25
- ksadk/sessions/resilient.py +36 -8
- ksadk/skills/models.py +17 -5
- ksadk/skills/package_store.py +5 -2
- ksadk/skills/runtime/agent.py +20 -5
- ksadk/skills/runtime/artifacts.py +1 -5
- ksadk/skills/runtime/backends/e2b.py +10 -4
- ksadk/skills/runtime/backends/local.py +15 -3
- ksadk/skills/runtime/base.py +1 -2
- ksadk/skills/runtime/executor.py +10 -3
- ksadk/skills/runtime/factory.py +4 -2
- ksadk/skills/runtime/loader.py +1 -2
- ksadk/skills/runtime/registry.py +15 -10
- ksadk/skills/runtime/request.py +3 -1
- ksadk/skills/service_client.py +38 -17
- ksadk/skills/tool_defs.py +15 -10
- ksadk/terminal_exec_policy.py +3 -4
- ksadk/tools/gateway.py +92 -15
- ksadk/tools/result_budget.py +12 -4
- ksadk/toolsets/__init__.py +134 -36
- ksadk/toolsets/platform.py +21 -5
- ksadk/toolsets/sandbox.py +82 -16
- ksadk/toolsets/skills.py +200 -39
- ksadk/toolsets/web.py +123 -26
- ksadk/toolsets/workspace.py +212 -101
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/exporters/langfuse_exporter.py +91 -76
- ksadk/tracing/setup.py +69 -53
- ksadk/tracing/span_utils.py +5 -2
- ksadk/tui/loop.py +78 -30
- ksadk/tui/stream_render.py +1 -0
- ksadk/ui_config.py +5 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
- agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
- ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
- ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
- ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
- ksadk/server/static/assets/channel-CTuydARX.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
- ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
- ksadk/server/static/assets/index-DAURbCsP.css +0 -1
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
ksadk/cli/cmd_create.py
CHANGED
|
@@ -7,11 +7,12 @@ import platform
|
|
|
7
7
|
import re
|
|
8
8
|
import shlex
|
|
9
9
|
import shutil
|
|
10
|
-
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
|
|
13
12
|
import click
|
|
14
13
|
import questionary
|
|
14
|
+
from rich.markup import escape
|
|
15
|
+
|
|
15
16
|
from ksadk.cli.cmd_config import custom_style
|
|
16
17
|
from ksadk.cli.ui import (
|
|
17
18
|
print_error,
|
|
@@ -112,10 +113,10 @@ model = LiteLlm(
|
|
|
112
113
|
|
|
113
114
|
def hello(name: str) -> dict:
|
|
114
115
|
"""问候工具
|
|
115
|
-
|
|
116
|
+
|
|
116
117
|
Args:
|
|
117
118
|
name: 名字
|
|
118
|
-
|
|
119
|
+
|
|
119
120
|
Returns:
|
|
120
121
|
问候语
|
|
121
122
|
"""
|
|
@@ -133,7 +134,7 @@ root_agent = Agent(
|
|
|
133
134
|
},
|
|
134
135
|
"langchain": {
|
|
135
136
|
"agent.py": '''"""
|
|
136
|
-
{package_name} - LangChain Agent
|
|
137
|
+
{package_name} - LangChain Agent (create_agent)
|
|
137
138
|
"""
|
|
138
139
|
|
|
139
140
|
import os
|
|
@@ -142,9 +143,8 @@ from dotenv import load_dotenv
|
|
|
142
143
|
|
|
143
144
|
load_dotenv(Path(__file__).parent.parent / ".env")
|
|
144
145
|
|
|
146
|
+
from langchain.agents import create_agent
|
|
145
147
|
from langchain_openai import ChatOpenAI
|
|
146
|
-
from langchain_core.prompts import ChatPromptTemplate
|
|
147
|
-
from langchain_core.output_parsers import StrOutputParser
|
|
148
148
|
|
|
149
149
|
llm = ChatOpenAI(
|
|
150
150
|
model=os.getenv("OPENAI_MODEL_NAME", "glm-5.2"),
|
|
@@ -153,12 +153,28 @@ llm = ChatOpenAI(
|
|
|
153
153
|
streaming=True,
|
|
154
154
|
)
|
|
155
155
|
|
|
156
|
-
prompt = ChatPromptTemplate.from_messages([
|
|
157
|
-
("system", "你是一个友好的助手。请用中文回复。"),
|
|
158
|
-
("human", "{{input}}")
|
|
159
|
-
])
|
|
160
156
|
|
|
161
|
-
|
|
157
|
+
def hello(name: str) -> str:
|
|
158
|
+
"""问候工具
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
name: 名字
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
问候语
|
|
165
|
+
"""
|
|
166
|
+
return f"你好, {{name}}!"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# create_agent 是新版 LangChain 定义 agent 的推荐方式,自带工具调用;
|
|
170
|
+
# 可选 middleware=[HumanInTheLoopMiddleware(...)](人工审批 HITL)与
|
|
171
|
+
# checkpointer(会话持久化)。detector 将本项目判为 LANGCHAIN,
|
|
172
|
+
# 经 LangChainRunner 运行。
|
|
173
|
+
root_agent = create_agent(
|
|
174
|
+
model=llm,
|
|
175
|
+
tools=[hello],
|
|
176
|
+
system_prompt="你是一个友好的助手。请用中文回复。",
|
|
177
|
+
)
|
|
162
178
|
''',
|
|
163
179
|
},
|
|
164
180
|
"langgraph": {
|
|
@@ -243,42 +259,46 @@ root_agent = None
|
|
|
243
259
|
|
|
244
260
|
def _detect_framework(content: str) -> str:
|
|
245
261
|
"""检测 Agent 文件使用的框架"""
|
|
246
|
-
if
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return
|
|
252
|
-
elif
|
|
253
|
-
return
|
|
254
|
-
|
|
262
|
+
if (
|
|
263
|
+
"from deepagents" in content
|
|
264
|
+
or "import deepagents" in content
|
|
265
|
+
or "create_deep_agent(" in content
|
|
266
|
+
):
|
|
267
|
+
return "deepagents"
|
|
268
|
+
elif "from langgraph" in content or "import langgraph" in content:
|
|
269
|
+
return "langgraph"
|
|
270
|
+
elif "from google.adk" in content or "import google.adk" in content:
|
|
271
|
+
return "adk"
|
|
272
|
+
elif "from langchain" in content or "import langchain" in content:
|
|
273
|
+
return "langchain"
|
|
274
|
+
return "unknown"
|
|
255
275
|
|
|
256
276
|
|
|
257
277
|
def _detect_agent_variable(content: str) -> str | None:
|
|
258
278
|
"""检测 Agent 变量名"""
|
|
259
279
|
import re
|
|
260
|
-
|
|
280
|
+
|
|
261
281
|
# 优先级匹配: root_agent > compiled graph > StateGraph > Agent()
|
|
262
282
|
# Only module-level exports are valid entry variables. Service-style
|
|
263
283
|
# projects often build a local `graph` inside init_agent_resources(), which
|
|
264
284
|
# must not be treated as importable module state.
|
|
265
285
|
patterns = [
|
|
266
|
-
(r
|
|
267
|
-
(r
|
|
268
|
-
(r
|
|
269
|
-
(r
|
|
270
|
-
(r
|
|
271
|
-
(r
|
|
272
|
-
(r
|
|
273
|
-
(r
|
|
274
|
-
(r
|
|
286
|
+
(r"^(root_agent)\s*=", "root_agent"),
|
|
287
|
+
(r"^(root_agent)\s*:\s*[^=]+\s*=", "root_agent"), # type annotated root_agent
|
|
288
|
+
(r"^(\w+)\s*=\s*\w*graph\w*\.compile\(", None), # e.g., agent = graph.compile()
|
|
289
|
+
(r"^(\w+)\s*=\s*StateGraph", None), # e.g., graph = StateGraph(...)
|
|
290
|
+
(r"^(\w+)\s*=\s*Agent\(", None), # ADK: agent = Agent(...)
|
|
291
|
+
(r"^(\w+)\s*=\s*create_react_agent\(", None), # create_react_agent
|
|
292
|
+
(r"^(\w+)\s*=\s*create_deep_agent\(", None), # deepagents create_deep_agent
|
|
293
|
+
(r"^(\w+)\s*=\s*create_agent\(", None), # langchain create_agent
|
|
294
|
+
(r"^(\w+)\s*=\s*build_agent\(", None), # adapter style build_agent
|
|
275
295
|
]
|
|
276
|
-
|
|
296
|
+
|
|
277
297
|
for pattern, fixed_name in patterns:
|
|
278
298
|
match = re.search(pattern, content, re.MULTILINE | re.IGNORECASE)
|
|
279
299
|
if match:
|
|
280
300
|
return fixed_name if fixed_name else match.group(1)
|
|
281
|
-
|
|
301
|
+
|
|
282
302
|
return None
|
|
283
303
|
|
|
284
304
|
|
|
@@ -367,7 +387,11 @@ def _load_entry_from_langgraph_json(directory: Path) -> tuple[Path, str] | None:
|
|
|
367
387
|
path_part = path_part.strip().removeprefix("./")
|
|
368
388
|
agent_var = agent_var.strip() or "root_agent"
|
|
369
389
|
entry_path = directory / Path(path_part.replace("\\", "/"))
|
|
370
|
-
if
|
|
390
|
+
if (
|
|
391
|
+
entry_path.exists()
|
|
392
|
+
and entry_path.is_file()
|
|
393
|
+
and _entry_exposes_variable(entry_path, agent_var)
|
|
394
|
+
):
|
|
371
395
|
return entry_path, agent_var
|
|
372
396
|
return None
|
|
373
397
|
|
|
@@ -389,7 +413,7 @@ def _load_framework_from_agentengine_yaml(directory: Path) -> str | None:
|
|
|
389
413
|
if not isinstance(framework, str):
|
|
390
414
|
return None
|
|
391
415
|
framework = framework.strip().lower()
|
|
392
|
-
if framework in {"adk", "langchain", "langgraph", "deepagents", "openclaw", "hermes"}:
|
|
416
|
+
if framework in {"adk", "langchain", "langgraph", "deepagents", "openclaw", "hermes", "codex"}:
|
|
393
417
|
return framework
|
|
394
418
|
return None
|
|
395
419
|
|
|
@@ -501,35 +525,37 @@ def _fix_dotenv_paths_in_file(file_path: Path, depth: int = 1) -> bool:
|
|
|
501
525
|
返回是否修改了文件
|
|
502
526
|
"""
|
|
503
527
|
import re
|
|
504
|
-
|
|
528
|
+
|
|
505
529
|
try:
|
|
506
|
-
content = file_path.read_text(encoding=
|
|
530
|
+
content = file_path.read_text(encoding="utf-8")
|
|
507
531
|
except Exception:
|
|
508
532
|
return False
|
|
509
|
-
|
|
533
|
+
|
|
510
534
|
# 构建替换的 parent 链
|
|
511
|
-
parent_chain =
|
|
512
|
-
|
|
535
|
+
parent_chain = ".parent" * depth
|
|
536
|
+
|
|
513
537
|
# 匹配各种 load_dotenv 模式
|
|
514
538
|
patterns = [
|
|
515
539
|
# load_dotenv(Path(__file__).parent / ".env")
|
|
516
|
-
(
|
|
517
|
-
|
|
540
|
+
(
|
|
541
|
+
r'(load_dotenv\s*\(\s*Path\s*\(\s*__file__\s*\)\s*)\.parent(\s*/\s*["\']\.env["\'])',
|
|
542
|
+
rf"\1{parent_chain}\2",
|
|
543
|
+
),
|
|
518
544
|
# load_dotenv(Path(__file__).parent.parent / ".env") -> 根据 depth 调整
|
|
519
545
|
]
|
|
520
|
-
|
|
546
|
+
|
|
521
547
|
modified = False
|
|
522
548
|
new_content = content
|
|
523
|
-
|
|
549
|
+
|
|
524
550
|
for pattern, replacement in patterns:
|
|
525
551
|
new_content_temp = re.sub(pattern, replacement, new_content)
|
|
526
552
|
if new_content_temp != new_content:
|
|
527
553
|
new_content = new_content_temp
|
|
528
554
|
modified = True
|
|
529
|
-
|
|
555
|
+
|
|
530
556
|
if modified:
|
|
531
|
-
file_path.write_text(new_content, encoding=
|
|
532
|
-
|
|
557
|
+
file_path.write_text(new_content, encoding="utf-8")
|
|
558
|
+
|
|
533
559
|
return modified
|
|
534
560
|
|
|
535
561
|
|
|
@@ -539,76 +565,76 @@ def _fix_dotenv_paths_recursive(directory: Path, depth: int = 1) -> int:
|
|
|
539
565
|
返回修复的文件数量
|
|
540
566
|
"""
|
|
541
567
|
fixed_count = 0
|
|
542
|
-
|
|
543
|
-
for py_file in directory.rglob(
|
|
568
|
+
|
|
569
|
+
for py_file in directory.rglob("*.py"):
|
|
544
570
|
# 计算相对深度
|
|
545
571
|
rel_path = py_file.relative_to(directory)
|
|
546
572
|
file_depth = len(rel_path.parts) + depth - 1 # 相对于项目根目录
|
|
547
|
-
|
|
573
|
+
|
|
548
574
|
if _fix_dotenv_paths_in_file(py_file, file_depth):
|
|
549
575
|
fixed_count += 1
|
|
550
|
-
|
|
576
|
+
|
|
551
577
|
return fixed_count
|
|
552
578
|
|
|
553
579
|
|
|
554
580
|
def _fix_nested_imports(file_path: Path, subdirs: list[str]) -> bool:
|
|
555
581
|
"""
|
|
556
582
|
修复文件中的嵌套目录导入
|
|
557
|
-
|
|
583
|
+
|
|
558
584
|
当源目录包含子包目录(如 my_agent/)时,
|
|
559
585
|
入口文件中的 `from my_agent import xxx` 需要改为 `from .my_agent import xxx`
|
|
560
|
-
|
|
586
|
+
|
|
561
587
|
Args:
|
|
562
588
|
file_path: 需要修复的文件
|
|
563
589
|
subdirs: 同级子目录名列表
|
|
564
|
-
|
|
590
|
+
|
|
565
591
|
Returns:
|
|
566
592
|
是否修改了文件
|
|
567
593
|
"""
|
|
568
594
|
import re
|
|
569
|
-
|
|
595
|
+
|
|
570
596
|
if not subdirs:
|
|
571
597
|
return False
|
|
572
|
-
|
|
598
|
+
|
|
573
599
|
try:
|
|
574
|
-
content = file_path.read_text(encoding=
|
|
600
|
+
content = file_path.read_text(encoding="utf-8")
|
|
575
601
|
except Exception:
|
|
576
602
|
return False
|
|
577
|
-
|
|
603
|
+
|
|
578
604
|
original = content
|
|
579
|
-
|
|
605
|
+
|
|
580
606
|
# 为每个子目录生成修复规则
|
|
581
607
|
for subdir in subdirs:
|
|
582
608
|
# from my_agent import xxx -> from .my_agent import xxx
|
|
583
|
-
pattern = rf
|
|
584
|
-
replacement =
|
|
609
|
+
pattern = rf"^(from\s+)({re.escape(subdir)})(\s+import\s+)"
|
|
610
|
+
replacement = r"\1.\2\3"
|
|
585
611
|
content = re.sub(pattern, replacement, content, flags=re.MULTILINE)
|
|
586
|
-
|
|
612
|
+
|
|
587
613
|
# import my_agent -> from . import my_agent
|
|
588
614
|
# 只处理独立的 import 语句
|
|
589
|
-
pattern_import = rf
|
|
590
|
-
replacement_import =
|
|
615
|
+
pattern_import = rf"^import\s+({re.escape(subdir)})\s*$"
|
|
616
|
+
replacement_import = r"from . import \1"
|
|
591
617
|
content = re.sub(pattern_import, replacement_import, content, flags=re.MULTILINE)
|
|
592
|
-
|
|
618
|
+
|
|
593
619
|
if content != original:
|
|
594
|
-
file_path.write_text(content, encoding=
|
|
620
|
+
file_path.write_text(content, encoding="utf-8")
|
|
595
621
|
return True
|
|
596
|
-
|
|
622
|
+
|
|
597
623
|
return False
|
|
598
624
|
|
|
599
625
|
|
|
600
626
|
def _generate_env_content(global_env: dict) -> str:
|
|
601
|
-
"""
|
|
602
|
-
|
|
627
|
+
"""Generate project-local configuration without copying credentials.
|
|
628
|
+
|
|
629
|
+
``init`` may read a global profile to retain non-secret endpoint defaults, but
|
|
630
|
+
it must not persist the caller's credentials in a newly created directory.
|
|
631
|
+
"""
|
|
603
632
|
base_url = global_env.get("OPENAI_BASE_URL", "")
|
|
604
633
|
model_name = global_env.get("OPENAI_MODEL_NAME", "")
|
|
605
|
-
ks_ak = global_env.get("KSYUN_ACCESS_KEY", "")
|
|
606
|
-
ks_sk = global_env.get("KSYUN_SECRET_KEY", "")
|
|
607
634
|
ks_region = global_env.get("KSYUN_REGION", "cn-beijing-6")
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
OPENAI_API_KEY={api_key}
|
|
635
|
+
|
|
636
|
+
env_content = """# 模型配置
|
|
637
|
+
# OPENAI_API_KEY=
|
|
612
638
|
"""
|
|
613
639
|
if base_url:
|
|
614
640
|
env_content += f"OPENAI_BASE_URL={base_url}\n"
|
|
@@ -618,110 +644,131 @@ OPENAI_API_KEY={api_key}
|
|
|
618
644
|
env_content += f"OPENAI_MODEL_NAME={model_name}\n"
|
|
619
645
|
else:
|
|
620
646
|
env_content += "# OPENAI_MODEL_NAME=\n"
|
|
621
|
-
|
|
622
|
-
env_content +=
|
|
647
|
+
|
|
648
|
+
env_content += """
|
|
623
649
|
# 金山云配置
|
|
624
650
|
"""
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
else:
|
|
628
|
-
env_content += "# KSYUN_ACCESS_KEY=\n"
|
|
629
|
-
if ks_sk:
|
|
630
|
-
env_content += f"KSYUN_SECRET_KEY={ks_sk}\n"
|
|
631
|
-
else:
|
|
632
|
-
env_content += "# KSYUN_SECRET_KEY=\n"
|
|
651
|
+
env_content += "# KSYUN_ACCESS_KEY=\n"
|
|
652
|
+
env_content += "# KSYUN_SECRET_KEY=\n"
|
|
633
653
|
env_content += f"KSYUN_REGION={ks_region}\n"
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
else:
|
|
637
|
-
env_content += "# KSYUN_ACCOUNT_ID=\n"
|
|
638
|
-
|
|
654
|
+
env_content += "# KSYUN_ACCOUNT_ID=\n"
|
|
655
|
+
|
|
639
656
|
return env_content
|
|
640
657
|
|
|
641
658
|
|
|
659
|
+
def _generate_codex_env_content(global_env: dict) -> str:
|
|
660
|
+
"""Generate the intentionally small local-only environment for a Codex project.
|
|
661
|
+
|
|
662
|
+
A ManagedRuntime manifest is declarative and must never acquire cloud, storage or
|
|
663
|
+
observability credentials merely because the developer ran ``init``. The three
|
|
664
|
+
OpenAI-compatible values are enough for native ``ksadk web`` debugging. The
|
|
665
|
+
optional proxy knobs are documented rather than copied from the global profile,
|
|
666
|
+
because their upstream key can be different from the model key.
|
|
667
|
+
"""
|
|
668
|
+
base_url = global_env.get("OPENAI_BASE_URL", "")
|
|
669
|
+
model_name = global_env.get("OPENAI_MODEL_NAME", "")
|
|
670
|
+
lines = [
|
|
671
|
+
"# 本地 Codex 模型配置(仅用于 ksadk web,不会进入 ManagedRuntime bundle)",
|
|
672
|
+
"# OPENAI_API_KEY=",
|
|
673
|
+
f"OPENAI_BASE_URL={base_url}" if base_url else "# OPENAI_BASE_URL=",
|
|
674
|
+
f"OPENAI_MODEL_NAME={model_name}" if model_name else "# OPENAI_MODEL_NAME=glm-5.2",
|
|
675
|
+
"",
|
|
676
|
+
"# 可选:自定义上游仅支持 Chat Completions 时,强制启用协议转换代理",
|
|
677
|
+
"# KSADK_CODEX_USE_PROXY=1",
|
|
678
|
+
"# KSADK_PROXY_UPSTREAM_BASE=",
|
|
679
|
+
"# KSADK_PROXY_UPSTREAM_KEY=",
|
|
680
|
+
"",
|
|
681
|
+
]
|
|
682
|
+
return "\n".join(lines)
|
|
683
|
+
|
|
684
|
+
|
|
642
685
|
def _generate_requirements_from_imports(directory: Path, framework: str) -> str:
|
|
643
686
|
"""
|
|
644
687
|
扫描目录中的 Python 文件,从 import 语句生成 requirements.txt
|
|
645
688
|
"""
|
|
646
689
|
import re
|
|
647
|
-
|
|
690
|
+
|
|
648
691
|
# 常用包名到 PyPI 包名的映射
|
|
649
692
|
import_to_package = {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
693
|
+
"langchain": "langchain",
|
|
694
|
+
"langchain_openai": "langchain-openai",
|
|
695
|
+
"langchain_anthropic": "langchain-anthropic",
|
|
696
|
+
"langchain_core": "langchain-core",
|
|
697
|
+
"langchain_community": "langchain-community",
|
|
698
|
+
"langgraph": "langgraph",
|
|
699
|
+
"deepagents": "deepagents",
|
|
700
|
+
"openai": "openai",
|
|
701
|
+
"anthropic": "anthropic",
|
|
702
|
+
"dotenv": "python-dotenv",
|
|
703
|
+
"pydantic": "pydantic",
|
|
704
|
+
"httpx": "httpx",
|
|
705
|
+
"requests": "requests",
|
|
706
|
+
"google.adk": "google-adk",
|
|
707
|
+
"google.genai": "google-genai",
|
|
708
|
+
"tiktoken": "tiktoken",
|
|
709
|
+
"faiss": "faiss-cpu",
|
|
710
|
+
"chromadb": "chromadb",
|
|
711
|
+
"tavily": "tavily-python",
|
|
669
712
|
}
|
|
670
|
-
|
|
713
|
+
|
|
671
714
|
found_packages = set()
|
|
672
|
-
|
|
715
|
+
|
|
673
716
|
# 扫描所有 .py 文件
|
|
674
|
-
for py_file in directory.rglob(
|
|
717
|
+
for py_file in directory.rglob("*.py"):
|
|
675
718
|
try:
|
|
676
|
-
content = py_file.read_text(encoding=
|
|
719
|
+
content = py_file.read_text(encoding="utf-8")
|
|
677
720
|
except Exception:
|
|
678
721
|
continue
|
|
679
|
-
|
|
722
|
+
|
|
680
723
|
# 匹配 import 语句
|
|
681
724
|
# from xxx import yyy
|
|
682
725
|
# import xxx
|
|
683
726
|
import_patterns = [
|
|
684
|
-
r
|
|
685
|
-
r
|
|
727
|
+
r"^from\s+([\w\.]+)",
|
|
728
|
+
r"^import\s+([\w\.]+)",
|
|
686
729
|
]
|
|
687
|
-
|
|
730
|
+
|
|
688
731
|
for pattern in import_patterns:
|
|
689
732
|
for match in re.finditer(pattern, content, re.MULTILINE):
|
|
690
|
-
module = match.group(1).split(
|
|
733
|
+
module = match.group(1).split(".")[0] # 取顶级模块
|
|
691
734
|
full_module = match.group(1)
|
|
692
|
-
|
|
735
|
+
|
|
693
736
|
# 检查是否在映射中
|
|
694
737
|
if full_module in import_to_package:
|
|
695
738
|
found_packages.add(import_to_package[full_module])
|
|
696
739
|
elif module in import_to_package:
|
|
697
740
|
found_packages.add(import_to_package[module])
|
|
698
741
|
# 检查常见的下划线包名
|
|
699
|
-
elif module.replace(
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
742
|
+
elif module.replace("_", "-") in [
|
|
743
|
+
"langchain-openai",
|
|
744
|
+
"langchain-anthropic",
|
|
745
|
+
"langchain-core",
|
|
746
|
+
"langchain-community",
|
|
747
|
+
]:
|
|
748
|
+
found_packages.add(module.replace("_", "-"))
|
|
749
|
+
|
|
703
750
|
# 确保框架相关的核心依赖在列表中
|
|
704
|
-
if framework ==
|
|
705
|
-
found_packages.add(
|
|
706
|
-
found_packages.add(
|
|
707
|
-
found_packages.add(
|
|
708
|
-
elif framework ==
|
|
709
|
-
found_packages.add(
|
|
710
|
-
found_packages.add(
|
|
711
|
-
found_packages.add(
|
|
712
|
-
found_packages.add(
|
|
713
|
-
elif framework ==
|
|
714
|
-
found_packages.add(
|
|
715
|
-
found_packages.add(
|
|
716
|
-
elif framework ==
|
|
717
|
-
found_packages.add(
|
|
718
|
-
|
|
751
|
+
if framework == "langgraph":
|
|
752
|
+
found_packages.add("langgraph")
|
|
753
|
+
found_packages.add("langchain")
|
|
754
|
+
found_packages.add("langchain-openai")
|
|
755
|
+
elif framework == "deepagents":
|
|
756
|
+
found_packages.add("deepagents")
|
|
757
|
+
found_packages.add("langgraph")
|
|
758
|
+
found_packages.add("langchain")
|
|
759
|
+
found_packages.add("langchain-openai")
|
|
760
|
+
elif framework == "langchain":
|
|
761
|
+
found_packages.add("langchain")
|
|
762
|
+
found_packages.add("langchain-openai")
|
|
763
|
+
elif framework == "adk":
|
|
764
|
+
found_packages.add("google-adk")
|
|
765
|
+
|
|
719
766
|
# 总是添加 python-dotenv
|
|
720
|
-
found_packages.add(
|
|
721
|
-
|
|
767
|
+
found_packages.add("python-dotenv")
|
|
768
|
+
|
|
722
769
|
# 按字母顺序排序
|
|
723
770
|
sorted_packages = sorted(found_packages)
|
|
724
|
-
return
|
|
771
|
+
return "\n".join(sorted_packages) + "\n"
|
|
725
772
|
|
|
726
773
|
|
|
727
774
|
def _analyze_langgraph_state(content: str) -> dict:
|
|
@@ -752,7 +799,9 @@ def _analyze_langgraph_state(content: str) -> dict:
|
|
|
752
799
|
preferred = ("query", "question", "prompt", "user_input", "input")
|
|
753
800
|
input_field = next((field for field in preferred if field in candidates), None)
|
|
754
801
|
|
|
755
|
-
if candidates and (
|
|
802
|
+
if candidates and (
|
|
803
|
+
"TypedDict" in content or "StateGraph(" in content or "langgraph" in content
|
|
804
|
+
):
|
|
756
805
|
return {"kind": "custom", "input_field": input_field}
|
|
757
806
|
if "StateGraph(" in content or "from langgraph" in content or "import langgraph" in content:
|
|
758
807
|
return {"kind": "ambiguous", "input_field": input_field}
|
|
@@ -789,15 +838,15 @@ def _generate_langgraph_adapter_content(
|
|
|
789
838
|
) -> str:
|
|
790
839
|
input_field = analysis.get("input_field")
|
|
791
840
|
if input_field:
|
|
792
|
-
return_body = f
|
|
841
|
+
return_body = f""" return {{
|
|
793
842
|
"{input_field}": payload.get("input", ""),
|
|
794
|
-
}}
|
|
843
|
+
}}"""
|
|
795
844
|
else:
|
|
796
|
-
return_body =
|
|
845
|
+
return_body = """ # TODO: Map AgentEngine's chat payload to your LangGraph State.
|
|
797
846
|
# Common examples:
|
|
798
847
|
# return {"query": payload.get("input", "")}
|
|
799
848
|
# return {"question": payload.get("input", ""), "context": []}
|
|
800
|
-
return dict(payload)
|
|
849
|
+
return dict(payload)"""
|
|
801
850
|
|
|
802
851
|
return f'''"""
|
|
803
852
|
AgentEngine adapter generated for a LangGraph project.
|
|
@@ -919,7 +968,9 @@ def _detect_deepagents_service_project(
|
|
|
919
968
|
def _generate_deepagents_service_adapter_content(analysis: dict) -> str:
|
|
920
969
|
init_module = analysis["init_module"]
|
|
921
970
|
runnable_module = analysis.get("runnable_module")
|
|
922
|
-
runnable_module_constant =
|
|
971
|
+
runnable_module_constant = (
|
|
972
|
+
f'RUNNABLE_MODULE = "{runnable_module}"' if runnable_module else "RUNNABLE_MODULE = None"
|
|
973
|
+
)
|
|
923
974
|
runnable_build = " return None\n"
|
|
924
975
|
if runnable_module:
|
|
925
976
|
runnable_build = """ try:
|
|
@@ -1040,7 +1091,14 @@ class AgentEngineDeepAgentsServiceAdapter:
|
|
|
1040
1091
|
return self._normalize_result(result)
|
|
1041
1092
|
|
|
1042
1093
|
if hasattr(self._agent, "ainvoke"):
|
|
1043
|
-
|
|
1094
|
+
agent_payload = (
|
|
1095
|
+
{{"messages": payload.get("messages", [])}}
|
|
1096
|
+
if isinstance(payload, dict) and payload.get("messages")
|
|
1097
|
+
else payload
|
|
1098
|
+
)
|
|
1099
|
+
result = await self._agent.ainvoke(
|
|
1100
|
+
agent_payload, config=config, **kwargs
|
|
1101
|
+
)
|
|
1044
1102
|
return self._normalize_result(result)
|
|
1045
1103
|
result = self._agent.invoke(payload, config=config, **kwargs)
|
|
1046
1104
|
return self._normalize_result(result)
|
|
@@ -1050,7 +1108,10 @@ class AgentEngineDeepAgentsServiceAdapter:
|
|
|
1050
1108
|
loop = asyncio.get_running_loop()
|
|
1051
1109
|
except RuntimeError:
|
|
1052
1110
|
return asyncio.run(self.ainvoke(payload, config=config, **kwargs))
|
|
1053
|
-
raise RuntimeError(
|
|
1111
|
+
raise RuntimeError(
|
|
1112
|
+
"Synchronous invoke cannot run while an event loop is already active; "
|
|
1113
|
+
"use ainvoke instead"
|
|
1114
|
+
)
|
|
1054
1115
|
|
|
1055
1116
|
|
|
1056
1117
|
def ksadk_prepare_state(payload: dict, session_context: dict) -> dict:
|
|
@@ -1093,55 +1154,55 @@ def _wrap_agent_file(from_agent_path: Path, project_name: str, framework: str, a
|
|
|
1093
1154
|
"""包装单个 Agent 文件到新项目"""
|
|
1094
1155
|
project_path = Path(project_name)
|
|
1095
1156
|
package_name = _runtime_package_name_or_exit(project_path.name)
|
|
1096
|
-
|
|
1157
|
+
|
|
1097
1158
|
if project_path.exists():
|
|
1098
1159
|
print_error(f"目录 '{project_name}' 已存在")
|
|
1099
1160
|
raise SystemExit(1)
|
|
1100
|
-
|
|
1161
|
+
|
|
1101
1162
|
print_title("包装 Agent 文件")
|
|
1102
1163
|
print_kv("创建项目", project_name)
|
|
1103
1164
|
print_kv("框架", framework)
|
|
1104
1165
|
print_kv("包装文件", str(from_agent_path))
|
|
1105
1166
|
print_kv("Agent 变量", agent_var)
|
|
1106
|
-
|
|
1167
|
+
|
|
1107
1168
|
# 创建目录
|
|
1108
1169
|
(project_path / package_name).mkdir(parents=True)
|
|
1109
|
-
|
|
1170
|
+
|
|
1110
1171
|
# 复制源文件并修复 .env 路径
|
|
1111
1172
|
source_filename = from_agent_path.name
|
|
1112
1173
|
dest_path = project_path / package_name / source_filename
|
|
1113
|
-
|
|
1174
|
+
|
|
1114
1175
|
# 读取源文件内容
|
|
1115
|
-
source_content = from_agent_path.read_text(encoding=
|
|
1116
|
-
|
|
1176
|
+
source_content = from_agent_path.read_text(encoding="utf-8")
|
|
1177
|
+
|
|
1117
1178
|
# 修复 load_dotenv 路径:parent -> parent.parent (因为文件被移到了子目录)
|
|
1118
1179
|
fixed_content = re.sub(
|
|
1119
1180
|
r'(load_dotenv\s*\(\s*Path\s*\(\s*__file__\s*\)\s*\.parent)(\s*/\s*["\']\.env["\'])',
|
|
1120
|
-
r
|
|
1121
|
-
source_content
|
|
1181
|
+
r"\1.parent\2",
|
|
1182
|
+
source_content,
|
|
1122
1183
|
)
|
|
1123
|
-
|
|
1184
|
+
|
|
1124
1185
|
if fixed_content != source_content:
|
|
1125
1186
|
print_info("已自动修复 .env 加载路径")
|
|
1126
|
-
|
|
1187
|
+
|
|
1127
1188
|
# 写入目标文件
|
|
1128
|
-
dest_path.write_text(fixed_content, encoding=
|
|
1129
|
-
|
|
1189
|
+
dest_path.write_text(fixed_content, encoding="utf-8")
|
|
1190
|
+
|
|
1130
1191
|
# 检测全局配置
|
|
1131
1192
|
from ksadk.configs.global_config import (
|
|
1132
|
-
global_config_exists,
|
|
1133
1193
|
get_env_from_global_config,
|
|
1194
|
+
global_config_exists,
|
|
1134
1195
|
)
|
|
1135
|
-
|
|
1196
|
+
|
|
1136
1197
|
global_env = {}
|
|
1137
1198
|
if global_config_exists():
|
|
1138
1199
|
global_env = get_env_from_global_config()
|
|
1139
1200
|
if global_env:
|
|
1140
|
-
print_info("
|
|
1141
|
-
|
|
1201
|
+
print_info("检测到全局配置;项目 .env 仅保留非敏感默认值,凭证不会被复制")
|
|
1202
|
+
|
|
1142
1203
|
# 生成 .env
|
|
1143
1204
|
(project_path / ".env").write_text(_generate_env_content(global_env), encoding="utf-8-sig")
|
|
1144
|
-
|
|
1205
|
+
|
|
1145
1206
|
# LangGraph custom-state 项目生成 adapter,避免直接猜业务 State 语义。
|
|
1146
1207
|
entry_relative = Path(source_filename)
|
|
1147
1208
|
export_agent_var = agent_var
|
|
@@ -1174,23 +1235,29 @@ def _wrap_agent_file(from_agent_path: Path, project_name: str, framework: str, a
|
|
|
1174
1235
|
|
|
1175
1236
|
# 生成 agentengine.yaml
|
|
1176
1237
|
entry_module = entry_relative.with_suffix("").name
|
|
1177
|
-
(project_path / "agentengine.yaml").write_text(
|
|
1238
|
+
(project_path / "agentengine.yaml").write_text(
|
|
1239
|
+
f"""# AgentEngine 项目配置 (Wrapped)
|
|
1178
1240
|
name: {package_name}
|
|
1179
1241
|
version: "1.0.0"
|
|
1180
1242
|
|
|
1181
1243
|
framework: {framework}
|
|
1182
1244
|
entry_point: {package_name}/{entry_relative}
|
|
1183
1245
|
agent_variable: {export_agent_var}
|
|
1184
|
-
""",
|
|
1185
|
-
|
|
1246
|
+
""",
|
|
1247
|
+
encoding="utf-8-sig",
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1186
1250
|
# 生成 __init__.py
|
|
1187
|
-
(project_path / package_name / "__init__.py").write_text(
|
|
1251
|
+
(project_path / package_name / "__init__.py").write_text(
|
|
1252
|
+
f'''"""
|
|
1188
1253
|
{project_name} - Wrapped Agent
|
|
1189
1254
|
"""
|
|
1190
1255
|
from .{entry_module} import {export_agent_var} as root_agent
|
|
1191
1256
|
__all__ = ["root_agent"]
|
|
1192
|
-
''',
|
|
1193
|
-
|
|
1257
|
+
''',
|
|
1258
|
+
encoding="utf-8-sig",
|
|
1259
|
+
)
|
|
1260
|
+
|
|
1194
1261
|
# 生成 requirements.txt
|
|
1195
1262
|
reqs = ""
|
|
1196
1263
|
if framework == "langchain":
|
|
@@ -1202,9 +1269,10 @@ __all__ = ["root_agent"]
|
|
|
1202
1269
|
elif framework == "adk":
|
|
1203
1270
|
reqs = "google-adk\npython-dotenv\n"
|
|
1204
1271
|
(project_path / "requirements.txt").write_text(reqs, encoding="utf-8")
|
|
1205
|
-
|
|
1272
|
+
|
|
1206
1273
|
# 生成 README.md
|
|
1207
|
-
(project_path / "README.md").write_text(
|
|
1274
|
+
(project_path / "README.md").write_text(
|
|
1275
|
+
f"""# {project_name}
|
|
1208
1276
|
|
|
1209
1277
|
Wrapped Agent project (from `{from_agent_path.name}`).
|
|
1210
1278
|
|
|
@@ -1214,36 +1282,36 @@ Wrapped Agent project (from `{from_agent_path.name}`).
|
|
|
1214
1282
|
cd {project_name}
|
|
1215
1283
|
agentengine run -i .
|
|
1216
1284
|
```
|
|
1217
|
-
""",
|
|
1218
|
-
|
|
1285
|
+
""",
|
|
1286
|
+
encoding="utf-8-sig",
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1219
1289
|
print_success("包装完成")
|
|
1220
1290
|
print_rule("快速开始")
|
|
1221
1291
|
_print_quick_start_commands(project_name, ["agentengine run -i ."])
|
|
1222
1292
|
|
|
1223
1293
|
|
|
1224
|
-
def _wrap_agent_directory(
|
|
1225
|
-
|
|
1294
|
+
def _wrap_agent_directory(
|
|
1295
|
+
from_agent_dir: Path, project_name: str, framework: str, entry_file: Path, agent_var: str
|
|
1296
|
+
):
|
|
1226
1297
|
"""包装 Agent 目录到新项目"""
|
|
1227
|
-
|
|
1228
|
-
|
|
1298
|
+
|
|
1229
1299
|
project_path = Path(project_name)
|
|
1230
1300
|
package_name = _runtime_package_name_or_exit(project_path.name)
|
|
1231
|
-
source_dir_name = from_agent_dir.name
|
|
1232
|
-
|
|
1233
1301
|
if project_path.exists():
|
|
1234
1302
|
print_error(f"目录 '{project_name}' 已存在")
|
|
1235
1303
|
raise SystemExit(1)
|
|
1236
|
-
|
|
1304
|
+
|
|
1237
1305
|
print_title("包装 Agent 目录")
|
|
1238
1306
|
print_kv("创建项目", project_name)
|
|
1239
1307
|
print_kv("框架", framework)
|
|
1240
1308
|
print_kv("包装目录", str(from_agent_dir))
|
|
1241
1309
|
print_kv("入口文件", entry_file.name)
|
|
1242
1310
|
print_kv("Agent 变量", agent_var)
|
|
1243
|
-
|
|
1311
|
+
|
|
1244
1312
|
# 创建项目目录
|
|
1245
1313
|
project_path.mkdir(parents=True)
|
|
1246
|
-
|
|
1314
|
+
|
|
1247
1315
|
# 复制整个源目录到项目中(作为 package)
|
|
1248
1316
|
dest_package_path = project_path / package_name
|
|
1249
1317
|
ignore_names = {
|
|
@@ -1272,42 +1340,46 @@ def _wrap_agent_directory(from_agent_dir: Path, project_name: str, framework: st
|
|
|
1272
1340
|
return ignored
|
|
1273
1341
|
|
|
1274
1342
|
shutil.copytree(from_agent_dir, dest_package_path, ignore=_ignore_copytree)
|
|
1275
|
-
|
|
1343
|
+
|
|
1276
1344
|
print_info(f"已复制 {sum(1 for _ in dest_package_path.rglob('*.py'))} 个 Python 文件")
|
|
1277
|
-
|
|
1345
|
+
|
|
1278
1346
|
# 递归修复 .env 路径
|
|
1279
1347
|
fixed_count = _fix_dotenv_paths_recursive(dest_package_path, depth=2)
|
|
1280
1348
|
if fixed_count > 0:
|
|
1281
1349
|
print_info(f"已自动修复 {fixed_count} 个文件的 .env 加载路径")
|
|
1282
|
-
|
|
1350
|
+
|
|
1283
1351
|
# 修复嵌套目录导入路径
|
|
1284
1352
|
# 查找源目录中的子目录(作为 Python 包)
|
|
1285
|
-
subdirs = [
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1353
|
+
subdirs = [
|
|
1354
|
+
d.name
|
|
1355
|
+
for d in from_agent_dir.iterdir()
|
|
1356
|
+
if d.is_dir()
|
|
1357
|
+
and not d.name.startswith(".")
|
|
1358
|
+
and not d.name.startswith("_")
|
|
1359
|
+
and (d / "__init__.py").exists()
|
|
1360
|
+
]
|
|
1361
|
+
|
|
1289
1362
|
if subdirs:
|
|
1290
1363
|
# 修复入口文件中的导入
|
|
1291
1364
|
dest_entry_file = dest_package_path / entry_file.relative_to(from_agent_dir)
|
|
1292
1365
|
if _fix_nested_imports(dest_entry_file, subdirs):
|
|
1293
1366
|
print_info(f"已修复嵌套目录导入: {', '.join(subdirs)}")
|
|
1294
|
-
|
|
1295
|
-
|
|
1367
|
+
|
|
1296
1368
|
# 检测全局配置
|
|
1297
1369
|
from ksadk.configs.global_config import (
|
|
1298
|
-
global_config_exists,
|
|
1299
1370
|
get_env_from_global_config,
|
|
1371
|
+
global_config_exists,
|
|
1300
1372
|
)
|
|
1301
|
-
|
|
1373
|
+
|
|
1302
1374
|
global_env = {}
|
|
1303
1375
|
if global_config_exists():
|
|
1304
1376
|
global_env = get_env_from_global_config()
|
|
1305
1377
|
if global_env:
|
|
1306
|
-
print_info("
|
|
1307
|
-
|
|
1378
|
+
print_info("检测到全局配置;项目 .env 仅保留非敏感默认值,凭证不会被复制")
|
|
1379
|
+
|
|
1308
1380
|
# 生成 .env
|
|
1309
1381
|
(project_path / ".env").write_text(_generate_env_content(global_env), encoding="utf-8-sig")
|
|
1310
|
-
|
|
1382
|
+
|
|
1311
1383
|
# 生成 agentengine.yaml
|
|
1312
1384
|
# entry_point 相对于项目根目录
|
|
1313
1385
|
entry_relative = entry_file.relative_to(from_agent_dir)
|
|
@@ -1350,72 +1422,79 @@ def _wrap_agent_directory(from_agent_dir: Path, project_name: str, framework: st
|
|
|
1350
1422
|
export_agent_var = "root_agent"
|
|
1351
1423
|
print_warn("LangGraph state 检测: ambiguous adapter generated, review required")
|
|
1352
1424
|
|
|
1353
|
-
(project_path / "agentengine.yaml").write_text(
|
|
1425
|
+
(project_path / "agentengine.yaml").write_text(
|
|
1426
|
+
f"""# AgentEngine 项目配置 (Wrapped Directory)
|
|
1354
1427
|
name: {package_name}
|
|
1355
1428
|
version: "1.0.0"
|
|
1356
1429
|
|
|
1357
1430
|
framework: {framework}
|
|
1358
1431
|
entry_point: {package_name}/{entry_relative}
|
|
1359
1432
|
agent_variable: {export_agent_var}
|
|
1360
|
-
""",
|
|
1361
|
-
|
|
1433
|
+
""",
|
|
1434
|
+
encoding="utf-8-sig",
|
|
1435
|
+
)
|
|
1436
|
+
|
|
1362
1437
|
# 确保 __init__.py 正确导出 root_agent
|
|
1363
1438
|
init_file = dest_package_path / "__init__.py"
|
|
1364
1439
|
entry_module = ".".join(entry_relative.with_suffix("").parts) # e.g. src.agentengine_adapter
|
|
1365
1440
|
expected_export_line = f"from .{entry_module} import {export_agent_var} as root_agent"
|
|
1366
|
-
|
|
1441
|
+
|
|
1367
1442
|
# 检查现有 __init__.py 是否已导出 root_agent
|
|
1368
1443
|
init_has_export = False
|
|
1369
1444
|
if init_file.exists():
|
|
1370
|
-
init_content = init_file.read_text(encoding=
|
|
1445
|
+
init_content = init_file.read_text(encoding="utf-8")
|
|
1371
1446
|
if expected_export_line in init_content:
|
|
1372
1447
|
init_has_export = True
|
|
1373
|
-
|
|
1448
|
+
|
|
1374
1449
|
if not init_has_export:
|
|
1375
1450
|
# 追加或创建导出语句
|
|
1376
|
-
export_code = f
|
|
1451
|
+
export_code = f"""
|
|
1377
1452
|
# AgentEngine 导出 (自动添加)
|
|
1378
1453
|
{expected_export_line}
|
|
1379
1454
|
__all__ = ["root_agent"]
|
|
1380
|
-
|
|
1455
|
+
"""
|
|
1381
1456
|
if init_file.exists():
|
|
1382
1457
|
# 追加到现有文件
|
|
1383
|
-
with open(init_file,
|
|
1458
|
+
with open(init_file, "a", encoding="utf-8") as f:
|
|
1384
1459
|
f.write(export_code)
|
|
1385
1460
|
print_info("已修复 __init__.py 导出")
|
|
1386
1461
|
else:
|
|
1387
|
-
init_file.write_text(
|
|
1462
|
+
init_file.write_text(
|
|
1463
|
+
f'''"""
|
|
1388
1464
|
{project_name} - Wrapped Agent
|
|
1389
1465
|
"""
|
|
1390
1466
|
{expected_export_line}
|
|
1391
1467
|
__all__ = ["root_agent"]
|
|
1392
|
-
''',
|
|
1393
|
-
|
|
1468
|
+
''',
|
|
1469
|
+
encoding="utf-8-sig",
|
|
1470
|
+
)
|
|
1471
|
+
|
|
1394
1472
|
# 处理 requirements.txt
|
|
1395
1473
|
source_requirements = from_agent_dir / "requirements.txt"
|
|
1396
1474
|
dest_requirements = project_path / "requirements.txt"
|
|
1397
|
-
|
|
1475
|
+
|
|
1398
1476
|
if source_requirements.exists():
|
|
1399
1477
|
# 如果源目录有 requirements.txt,复制并补充必要的依赖
|
|
1400
1478
|
import shutil as shutil_req
|
|
1479
|
+
|
|
1401
1480
|
shutil_req.copy(source_requirements, dest_requirements)
|
|
1402
1481
|
print_info("已复制 requirements.txt")
|
|
1403
|
-
|
|
1482
|
+
|
|
1404
1483
|
# 检查是否缺少必要依赖,追加
|
|
1405
|
-
existing_reqs = dest_requirements.read_text(encoding=
|
|
1484
|
+
existing_reqs = dest_requirements.read_text(encoding="utf-8").lower()
|
|
1406
1485
|
missing = []
|
|
1407
|
-
if framework == "langgraph" and
|
|
1486
|
+
if framework == "langgraph" and "langgraph" not in existing_reqs:
|
|
1408
1487
|
missing.append("langgraph")
|
|
1409
1488
|
if framework == "deepagents":
|
|
1410
|
-
if
|
|
1489
|
+
if "deepagents" not in existing_reqs:
|
|
1411
1490
|
missing.append("deepagents")
|
|
1412
|
-
if
|
|
1491
|
+
if "langgraph" not in existing_reqs:
|
|
1413
1492
|
missing.append("langgraph")
|
|
1414
|
-
if
|
|
1493
|
+
if "python-dotenv" not in existing_reqs and "dotenv" not in existing_reqs:
|
|
1415
1494
|
missing.append("python-dotenv")
|
|
1416
|
-
|
|
1495
|
+
|
|
1417
1496
|
if missing:
|
|
1418
|
-
with open(dest_requirements,
|
|
1497
|
+
with open(dest_requirements, "a", encoding="utf-8") as f:
|
|
1419
1498
|
f.write("\n# Added by agentengine\n")
|
|
1420
1499
|
for pkg in missing:
|
|
1421
1500
|
f.write(f"{pkg}\n")
|
|
@@ -1425,9 +1504,10 @@ __all__ = ["root_agent"]
|
|
|
1425
1504
|
reqs = _generate_requirements_from_imports(dest_package_path, framework)
|
|
1426
1505
|
dest_requirements.write_text(reqs, encoding="utf-8")
|
|
1427
1506
|
print_info(f"已自动生成 requirements.txt ({reqs.count(chr(10))} 个依赖)")
|
|
1428
|
-
|
|
1507
|
+
|
|
1429
1508
|
# 生成 README.md
|
|
1430
|
-
(project_path / "README.md").write_text(
|
|
1509
|
+
(project_path / "README.md").write_text(
|
|
1510
|
+
f"""# {project_name}
|
|
1431
1511
|
|
|
1432
1512
|
Wrapped Agent project (from `{from_agent_dir.name}/`).
|
|
1433
1513
|
|
|
@@ -1437,14 +1517,16 @@ Wrapped Agent project (from `{from_agent_dir.name}/`).
|
|
|
1437
1517
|
cd {project_name}
|
|
1438
1518
|
agentengine run -i .
|
|
1439
1519
|
```
|
|
1440
|
-
""",
|
|
1441
|
-
|
|
1520
|
+
""",
|
|
1521
|
+
encoding="utf-8-sig",
|
|
1522
|
+
)
|
|
1523
|
+
|
|
1442
1524
|
# === 运行时验证 ===
|
|
1443
1525
|
print_rule("验证 Agent 加载")
|
|
1444
1526
|
import subprocess
|
|
1445
1527
|
import sys
|
|
1446
|
-
|
|
1447
|
-
verify_code = f
|
|
1528
|
+
|
|
1529
|
+
verify_code = f"""
|
|
1448
1530
|
import sys
|
|
1449
1531
|
sys.path.insert(0, ".")
|
|
1450
1532
|
try:
|
|
@@ -1453,16 +1535,16 @@ try:
|
|
|
1453
1535
|
except Exception as e:
|
|
1454
1536
|
print("ERROR:" + str(e))
|
|
1455
1537
|
sys.exit(1)
|
|
1456
|
-
|
|
1457
|
-
|
|
1538
|
+
"""
|
|
1539
|
+
|
|
1458
1540
|
result = subprocess.run(
|
|
1459
1541
|
[sys.executable, "-c", verify_code],
|
|
1460
1542
|
cwd=str(project_path),
|
|
1461
1543
|
capture_output=True,
|
|
1462
1544
|
text=True,
|
|
1463
|
-
timeout=30
|
|
1545
|
+
timeout=30,
|
|
1464
1546
|
)
|
|
1465
|
-
|
|
1547
|
+
|
|
1466
1548
|
if result.returncode == 0:
|
|
1467
1549
|
output = result.stdout.strip()
|
|
1468
1550
|
if output.startswith("TYPE:"):
|
|
@@ -1474,47 +1556,16 @@ except Exception as e:
|
|
|
1474
1556
|
error_msg = result.stderr or result.stdout
|
|
1475
1557
|
print_warn(f"Agent 加载警告: {error_msg[:200]}")
|
|
1476
1558
|
print_info("提示: 请检查依赖是否已安装,或代码是否有错误")
|
|
1477
|
-
|
|
1559
|
+
|
|
1478
1560
|
print_success("包装完成")
|
|
1479
1561
|
print_rule("快速开始")
|
|
1480
1562
|
_print_quick_start_commands(project_name, ["agentengine run -i ."])
|
|
1481
1563
|
|
|
1482
1564
|
|
|
1483
|
-
def
|
|
1484
|
-
"""
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
replacements = {
|
|
1488
|
-
"{{PROJECT_NAME}}": project_name,
|
|
1489
|
-
"{{PACKAGE_NAME}}": package_name,
|
|
1490
|
-
}
|
|
1491
|
-
for source in repo_template_root.rglob("*"):
|
|
1492
|
-
relative = source.relative_to(repo_template_root)
|
|
1493
|
-
if source.is_dir():
|
|
1494
|
-
(project_path / relative).mkdir(parents=True, exist_ok=True)
|
|
1495
|
-
continue
|
|
1496
|
-
destination_relative = relative
|
|
1497
|
-
if source.name.endswith(".template"):
|
|
1498
|
-
destination_relative = relative.with_name(source.name[:-9])
|
|
1499
|
-
content = source.read_text(encoding="utf-8")
|
|
1500
|
-
for key, value in replacements.items():
|
|
1501
|
-
content = content.replace(key, value)
|
|
1502
|
-
destination = project_path / destination_relative
|
|
1503
|
-
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
1504
|
-
destination.write_text(content, encoding="utf-8-sig")
|
|
1505
|
-
continue
|
|
1506
|
-
destination = project_path / destination_relative
|
|
1507
|
-
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
1508
|
-
shutil.copy2(source, destination)
|
|
1509
|
-
entrypoint = project_path / "entrypoint.sh"
|
|
1510
|
-
if entrypoint.exists():
|
|
1511
|
-
entrypoint.chmod(0o755)
|
|
1512
|
-
return
|
|
1513
|
-
|
|
1514
|
-
runtime_dir = project_path / "runtime"
|
|
1515
|
-
runtime_dir.mkdir(parents=True, exist_ok=True)
|
|
1516
|
-
|
|
1517
|
-
(project_path / "agentengine.yaml").write_text(f"""# AgentEngine Hermes 项目配置
|
|
1565
|
+
def _write_hermes_project_config(project_path: Path, package_name: str) -> None:
|
|
1566
|
+
"""Write the deployable Hermes config; the runtime image is platform-owned."""
|
|
1567
|
+
(project_path / "agentengine.yaml").write_text(
|
|
1568
|
+
f"""# AgentEngine Hermes project configuration
|
|
1518
1569
|
name: {package_name}
|
|
1519
1570
|
version: "1.0.0"
|
|
1520
1571
|
|
|
@@ -1532,353 +1583,90 @@ deploy:
|
|
|
1532
1583
|
min_replicas: 1
|
|
1533
1584
|
max_replicas: 1
|
|
1534
1585
|
concurrency: 1000
|
|
1535
|
-
""",
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
ENV PYTHONUNBUFFERED=1 \\
|
|
1540
|
-
PYTHONDONTWRITEBYTECODE=1 \\
|
|
1541
|
-
PIP_NO_CACHE_DIR=1 \\
|
|
1542
|
-
PORT=8080 \\
|
|
1543
|
-
API_SERVER_HOST=127.0.0.1 \\
|
|
1544
|
-
API_SERVER_PORT=8642 \\
|
|
1545
|
-
HERMES_DASHBOARD_HOST=127.0.0.1 \\
|
|
1546
|
-
HERMES_DASHBOARD_PORT=9119
|
|
1547
|
-
|
|
1548
|
-
WORKDIR /app
|
|
1549
|
-
|
|
1550
|
-
RUN apt-get update \\
|
|
1551
|
-
&& apt-get install -y --no-install-recommends bash curl tini \\
|
|
1552
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
1553
|
-
|
|
1554
|
-
RUN pip install --no-cache-dir \\
|
|
1555
|
-
"fastapi>=0.100" \\
|
|
1556
|
-
"uvicorn[standard]>=0.23" \\
|
|
1557
|
-
"httpx>=0.24" \\
|
|
1558
|
-
"pyyaml>=6.0" \\
|
|
1559
|
-
"python-dotenv>=1.0" \\
|
|
1560
|
-
"hermes-agent==0.9.0"
|
|
1561
|
-
|
|
1562
|
-
COPY runtime ./runtime
|
|
1563
|
-
COPY entrypoint.sh ./entrypoint.sh
|
|
1564
|
-
RUN chmod +x /app/entrypoint.sh
|
|
1565
|
-
|
|
1566
|
-
EXPOSE 8080
|
|
1567
|
-
|
|
1568
|
-
ENTRYPOINT ["tini", "--"]
|
|
1569
|
-
CMD ["/app/entrypoint.sh"]
|
|
1570
|
-
""", encoding="utf-8")
|
|
1571
|
-
|
|
1572
|
-
(project_path / "entrypoint.sh").write_text("""#!/usr/bin/env bash
|
|
1573
|
-
set -euo pipefail
|
|
1574
|
-
|
|
1575
|
-
export HOME="${HOME:-/home/agent}"
|
|
1576
|
-
export PORT="${PORT:-8080}"
|
|
1577
|
-
export API_SERVER_HOST="${API_SERVER_HOST:-127.0.0.1}"
|
|
1578
|
-
export API_SERVER_PORT="${API_SERVER_PORT:-8642}"
|
|
1579
|
-
export HERMES_DASHBOARD_HOST="${HERMES_DASHBOARD_HOST:-127.0.0.1}"
|
|
1580
|
-
export HERMES_DASHBOARD_PORT="${HERMES_DASHBOARD_PORT:-9119}"
|
|
1581
|
-
export API_SERVER_ENABLED="${API_SERVER_ENABLED:-true}"
|
|
1582
|
-
|
|
1583
|
-
mkdir -p "${HOME}/.hermes"
|
|
1584
|
-
|
|
1585
|
-
cat > "${HOME}/.hermes/.env" <<EOF
|
|
1586
|
-
OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
|
1587
|
-
OPENAI_BASE_URL=${OPENAI_BASE_URL:-}
|
|
1588
|
-
OPENAI_MODEL_NAME=${OPENAI_MODEL_NAME:-}
|
|
1589
|
-
API_SERVER_ENABLED=${API_SERVER_ENABLED}
|
|
1590
|
-
API_SERVER_KEY=${API_SERVER_KEY:-}
|
|
1591
|
-
API_SERVER_HOST=${API_SERVER_HOST}
|
|
1592
|
-
API_SERVER_PORT=${API_SERVER_PORT}
|
|
1593
|
-
EOF
|
|
1594
|
-
|
|
1595
|
-
cat > "${HOME}/.hermes/config.yaml" <<EOF
|
|
1596
|
-
model:
|
|
1597
|
-
provider: custom
|
|
1598
|
-
default: "${OPENAI_MODEL_NAME:-}"
|
|
1599
|
-
base_url: "${OPENAI_BASE_URL:-}"
|
|
1600
|
-
api_server:
|
|
1601
|
-
enabled: true
|
|
1602
|
-
host: "${API_SERVER_HOST}"
|
|
1603
|
-
port: ${API_SERVER_PORT}
|
|
1604
|
-
EOF
|
|
1605
|
-
|
|
1606
|
-
hermes gateway run --replace &
|
|
1607
|
-
HERMES_API_PID=$!
|
|
1608
|
-
|
|
1609
|
-
hermes dashboard --host "${HERMES_DASHBOARD_HOST}" --port "${HERMES_DASHBOARD_PORT}" --no-open &
|
|
1610
|
-
HERMES_DASHBOARD_PID=$!
|
|
1611
|
-
|
|
1612
|
-
cleanup() {
|
|
1613
|
-
kill "${HERMES_API_PID}" "${HERMES_DASHBOARD_PID}" 2>/dev/null || true
|
|
1614
|
-
}
|
|
1615
|
-
trap cleanup EXIT
|
|
1616
|
-
|
|
1617
|
-
exec uvicorn runtime.app:app --host 0.0.0.0 --port "${PORT}"
|
|
1618
|
-
""", encoding="utf-8")
|
|
1619
|
-
|
|
1620
|
-
(runtime_dir / "__init__.py").write_text("", encoding="utf-8")
|
|
1621
|
-
(runtime_dir / "app.py").write_text("""from __future__ import annotations
|
|
1622
|
-
|
|
1623
|
-
import asyncio
|
|
1624
|
-
import contextlib
|
|
1625
|
-
import json
|
|
1626
|
-
import os
|
|
1627
|
-
import pty
|
|
1628
|
-
import select
|
|
1629
|
-
import signal
|
|
1630
|
-
import termios
|
|
1631
|
-
from typing import Iterable
|
|
1632
|
-
|
|
1633
|
-
import httpx
|
|
1634
|
-
from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
|
|
1635
|
-
from fastapi.responses import Response
|
|
1636
|
-
from starlette.websockets import WebSocketState
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
TERMINAL_SUBPROTOCOL = "ks-terminal.v1"
|
|
1640
|
-
SHELL_METACHARS = set("|&;<>()$`\\\\\\n\\r")
|
|
1641
|
-
SINGLE_READONLY = {"status", "doctor", "version", "insights"}
|
|
1642
|
-
NESTED_READONLY = {
|
|
1643
|
-
"sessions": {"list": (2, 2), "show": (3, 3), "export": (3, 3)},
|
|
1644
|
-
"config": {"show": (2, 2), "check": (2, 2), "path": (2, 2), "env-path": (2, 2)},
|
|
1645
|
-
"skills": {"list": (2, 2), "audit": (2, 2), "check": (2, 2)},
|
|
1646
|
-
"tools": {"list": (2, 2)},
|
|
1647
|
-
"cron": {"list": (2, 2), "status": (2, 2)},
|
|
1648
|
-
"gateway": {"status": (2, 2)},
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
app = FastAPI()
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
def _api_base() -> str:
|
|
1655
|
-
return f"http://{os.getenv('API_SERVER_HOST', '127.0.0.1')}:{os.getenv('API_SERVER_PORT', '8642')}"
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
def _dashboard_base() -> str:
|
|
1659
|
-
return f"http://{os.getenv('HERMES_DASHBOARD_HOST', '127.0.0.1')}:{os.getenv('HERMES_DASHBOARD_PORT', '9119')}"
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
def _validate_exec_argv(argv: Iterable[str]) -> list[str]:
|
|
1663
|
-
normalized = [str(item).strip() for item in argv]
|
|
1664
|
-
if not normalized:
|
|
1665
|
-
raise ValueError("missing argv")
|
|
1666
|
-
for item in normalized:
|
|
1667
|
-
if not item or item.startswith("-") or any(char in SHELL_METACHARS for char in item):
|
|
1668
|
-
raise ValueError(f"unsafe argv: {item}")
|
|
1669
|
-
if normalized[0] in SINGLE_READONLY:
|
|
1670
|
-
if len(normalized) != 1:
|
|
1671
|
-
raise ValueError("unsupported argv")
|
|
1672
|
-
return normalized
|
|
1673
|
-
nested = NESTED_READONLY.get(normalized[0])
|
|
1674
|
-
if not nested or len(normalized) < 2:
|
|
1675
|
-
raise ValueError("unsupported argv")
|
|
1676
|
-
bounds = nested.get(normalized[1])
|
|
1677
|
-
if not bounds:
|
|
1678
|
-
raise ValueError("unsupported argv")
|
|
1679
|
-
if not (bounds[0] <= len(normalized) <= bounds[1]):
|
|
1680
|
-
raise ValueError("unsupported argv")
|
|
1681
|
-
return normalized
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
async def _proxy_http(request: Request, base_url: str, path: str) -> Response:
|
|
1685
|
-
target = f"{base_url}/{path.lstrip('/')}"
|
|
1686
|
-
if request.url.query:
|
|
1687
|
-
target = f"{target}?{request.url.query}"
|
|
1688
|
-
headers = {
|
|
1689
|
-
key: value
|
|
1690
|
-
for key, value in request.headers.items()
|
|
1691
|
-
if key.lower() not in {"host", "content-length"}
|
|
1692
|
-
}
|
|
1693
|
-
async with httpx.AsyncClient(timeout=None) as client:
|
|
1694
|
-
upstream = await client.request(
|
|
1695
|
-
request.method,
|
|
1696
|
-
target,
|
|
1697
|
-
headers=headers,
|
|
1698
|
-
content=await request.body(),
|
|
1699
|
-
)
|
|
1700
|
-
response_headers = {
|
|
1701
|
-
key: value
|
|
1702
|
-
for key, value in upstream.headers.items()
|
|
1703
|
-
if key.lower() not in {"content-encoding", "transfer-encoding", "connection"}
|
|
1704
|
-
}
|
|
1705
|
-
return Response(upstream.content, status_code=upstream.status_code, headers=response_headers)
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
@app.get("/health")
|
|
1709
|
-
async def health() -> dict:
|
|
1710
|
-
return {"ok": True}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
@app.api_route("/v1/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
|
|
1714
|
-
async def proxy_api(path: str, request: Request) -> Response:
|
|
1715
|
-
return await _proxy_http(request, _api_base(), f"v1/{path}")
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
def _set_winsize(fd: int, rows: int, cols: int) -> None:
|
|
1719
|
-
termios.tcsetwinsize(fd, (int(rows or 24), int(cols or 80)))
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
async def _pty_reader(ws: WebSocket, fd: int) -> None:
|
|
1723
|
-
loop = asyncio.get_running_loop()
|
|
1724
|
-
while True:
|
|
1725
|
-
await loop.run_in_executor(None, lambda: select.select([fd], [], [], None))
|
|
1726
|
-
data = os.read(fd, 4096)
|
|
1727
|
-
if not data:
|
|
1728
|
-
return
|
|
1729
|
-
await ws.send_bytes(data)
|
|
1730
|
-
|
|
1586
|
+
""",
|
|
1587
|
+
encoding="utf-8-sig",
|
|
1588
|
+
)
|
|
1731
1589
|
|
|
1732
|
-
async def _wait_process(pid: int) -> int:
|
|
1733
|
-
loop = asyncio.get_running_loop()
|
|
1734
|
-
_, status = await loop.run_in_executor(None, os.waitpid, pid, 0)
|
|
1735
|
-
if os.WIFEXITED(status):
|
|
1736
|
-
return os.WEXITSTATUS(status)
|
|
1737
|
-
if os.WIFSIGNALED(status):
|
|
1738
|
-
return 128 + os.WTERMSIG(status)
|
|
1739
|
-
return status
|
|
1740
1590
|
|
|
1591
|
+
def _write_codex_project_config(project_path: Path, package_name: str) -> None:
|
|
1592
|
+
"""Write the minimal codex runtime project:yaml 驱动,无 package/agent.py。
|
|
1741
1593
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
receive_task = None
|
|
1751
|
-
reader_task = None
|
|
1752
|
-
wait_task = None
|
|
1753
|
-
try:
|
|
1754
|
-
first = await ws.receive_text()
|
|
1755
|
-
payload = json.loads(first)
|
|
1756
|
-
if payload.get("type") != "start":
|
|
1757
|
-
raise ValueError("first frame must be start")
|
|
1758
|
-
mode = payload.get("mode")
|
|
1759
|
-
argv = payload.get("argv") or []
|
|
1760
|
-
if mode == "tui":
|
|
1761
|
-
command = ["hermes", "chat"]
|
|
1762
|
-
elif mode == "exec":
|
|
1763
|
-
command = ["hermes", *_validate_exec_argv(argv)]
|
|
1764
|
-
else:
|
|
1765
|
-
raise ValueError("unsupported mode")
|
|
1766
|
-
|
|
1767
|
-
pid, fd = pty.fork()
|
|
1768
|
-
if pid == 0:
|
|
1769
|
-
os.execvp(command[0], command)
|
|
1770
|
-
|
|
1771
|
-
_set_winsize(fd, int(payload.get("rows") or 24), int(payload.get("cols") or 80))
|
|
1772
|
-
await ws.send_text(json.dumps({"type": "ready"}))
|
|
1773
|
-
reader_task = asyncio.create_task(_pty_reader(ws, fd))
|
|
1774
|
-
wait_task = asyncio.create_task(_wait_process(pid))
|
|
1775
|
-
receive_task = asyncio.create_task(ws.receive())
|
|
1776
|
-
|
|
1777
|
-
while True:
|
|
1778
|
-
done, _pending = await asyncio.wait({wait_task, receive_task}, return_when=asyncio.FIRST_COMPLETED)
|
|
1779
|
-
if wait_task in done:
|
|
1780
|
-
if reader_task:
|
|
1781
|
-
reader_task.cancel()
|
|
1782
|
-
if receive_task:
|
|
1783
|
-
receive_task.cancel()
|
|
1784
|
-
code = wait_task.result()
|
|
1785
|
-
if ws.client_state == WebSocketState.CONNECTED:
|
|
1786
|
-
await ws.send_text(json.dumps({"type": "exit", "code": code}))
|
|
1787
|
-
return
|
|
1594
|
+
codex 的 agent 逻辑由 prompt 承载(CodexRunner.load_agent 是 no-op),所以只生成
|
|
1595
|
+
canonical agentengine.yaml(framework: codex,model+prompt)+ requirements +
|
|
1596
|
+
README。model/prompt 经 CodexRunner 传入 codex thread(开发者指令)。
|
|
1597
|
+
"""
|
|
1598
|
+
(project_path / "agentengine.yaml").write_text(
|
|
1599
|
+
f"""# AgentEngine codex runtime 项目配置
|
|
1600
|
+
name: {package_name}
|
|
1601
|
+
version: "1.0.0"
|
|
1788
1602
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
await ws.close()
|
|
1812
|
-
finally:
|
|
1813
|
-
for task in (receive_task, reader_task):
|
|
1814
|
-
if task and not task.done():
|
|
1815
|
-
task.cancel()
|
|
1816
|
-
if pid is not None and (wait_task is None or not wait_task.done()):
|
|
1817
|
-
with contextlib.suppress(ProcessLookupError):
|
|
1818
|
-
os.kill(pid, signal.SIGTERM)
|
|
1819
|
-
if wait_task and not wait_task.done():
|
|
1820
|
-
wait_task.cancel()
|
|
1821
|
-
if fd is not None:
|
|
1822
|
-
with contextlib.suppress(OSError):
|
|
1823
|
-
os.close(fd)
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
|
|
1827
|
-
async def proxy_dashboard(path: str, request: Request) -> Response:
|
|
1828
|
-
return await _proxy_http(request, _dashboard_base(), path)
|
|
1829
|
-
""", encoding="utf-8")
|
|
1830
|
-
|
|
1831
|
-
(project_path / "README.md").write_text(f"""# {project_name}
|
|
1832
|
-
|
|
1833
|
-
Hermes AgentEngine container-first 项目。
|
|
1603
|
+
# 框架:codex(开发态 CodexRunner → 部署态 codex-runtime)
|
|
1604
|
+
framework: codex
|
|
1605
|
+
artifact_type: ManagedRuntime
|
|
1606
|
+
|
|
1607
|
+
# 托管运行时版本可选;省略时云端由 Runtime catalog 选择默认值。
|
|
1608
|
+
# 本地 `ksadk web` 会使用已安装的 openai-codex 并提示该版本未锁定。
|
|
1609
|
+
runtime:
|
|
1610
|
+
name: codex
|
|
1611
|
+
|
|
1612
|
+
# codex 的模型与开发者指令(CodexRunner 读取并传入 codex thread)
|
|
1613
|
+
model: glm-5.2
|
|
1614
|
+
prompt: |
|
|
1615
|
+
你是 codex 编码助手。简洁回答,能跑命令验证就跑(shell 工具),中文回复。
|
|
1616
|
+
""",
|
|
1617
|
+
encoding="utf-8-sig",
|
|
1618
|
+
)
|
|
1619
|
+
(project_path / "requirements.txt").write_text(
|
|
1620
|
+
"ksadk[codex]\n",
|
|
1621
|
+
encoding="utf-8", # 无 BOM:pip 解析 BOM 会报 Invalid requirement
|
|
1622
|
+
)
|
|
1623
|
+
(project_path / "README.md").write_text(
|
|
1624
|
+
f"""# {package_name} — codex runtime agent
|
|
1834
1625
|
|
|
1835
1626
|
## 快速开始
|
|
1836
1627
|
|
|
1837
1628
|
```bash
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
# 2. 部署平台预置 Hermes runtime 镜像
|
|
1844
|
-
agentengine hermes deploy
|
|
1845
|
-
|
|
1846
|
-
# 3. 打开 Hermes 管理 UI
|
|
1847
|
-
agentengine hermes open
|
|
1848
|
-
|
|
1849
|
-
# 4. 进入 pod 内 Hermes 原生 TUI
|
|
1850
|
-
agentengine invoke
|
|
1851
|
-
|
|
1852
|
-
# 5. 使用统一 hosted chat
|
|
1853
|
-
agentengine hermes open --chat
|
|
1854
|
-
|
|
1855
|
-
# 6. 受限只读运维子命令
|
|
1856
|
-
agentengine hermes exec <agent> -- status
|
|
1857
|
-
|
|
1858
|
-
# 7. Pairing 审批透传
|
|
1859
|
-
agentengine hermes pairing <agent> -- list
|
|
1629
|
+
# 1. 编辑 .env 填 OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL_NAME
|
|
1630
|
+
# 2. 本地运行(浏览器对话,codex 自动探测并启用代理)
|
|
1631
|
+
agentengine web .
|
|
1632
|
+
# 或
|
|
1633
|
+
ksadk web .
|
|
1860
1634
|
```
|
|
1861
1635
|
|
|
1862
|
-
##
|
|
1636
|
+
## 说明
|
|
1863
1637
|
|
|
1864
|
-
-
|
|
1865
|
-
-
|
|
1866
|
-
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1638
|
+
- codex 的 agent 逻辑由 `agentengine.yaml` 的 `prompt`(开发者指令)承载,无 agent.py。
|
|
1639
|
+
- codex 自动探测模型协议:OpenAI 官方直连,星流 chat 模型自动启用转换代理。
|
|
1640
|
+
- `agentengine build .` 只生成 YAML manifest bundle,不打包本机 Codex 二进制。若
|
|
1641
|
+
`runtime.version` 未指定,build 需要连接到已配置 Runtime catalog;离线构建请显式锁定版本。
|
|
1642
|
+
- 部署时服务端解析 ManagedRuntime 版本和 Linux 镜像 digest。
|
|
1643
|
+
""",
|
|
1644
|
+
encoding="utf-8-sig",
|
|
1645
|
+
)
|
|
1869
1646
|
|
|
1870
1647
|
|
|
1871
|
-
@click.command(context_settings=dict(help_option_names=[
|
|
1872
|
-
@click.argument(
|
|
1873
|
-
@click.option(
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1648
|
+
@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
|
|
1649
|
+
@click.argument("project_name", required=False)
|
|
1650
|
+
@click.option(
|
|
1651
|
+
"--framework",
|
|
1652
|
+
"-f",
|
|
1653
|
+
type=click.Choice(
|
|
1654
|
+
["adk", "langchain", "langgraph", "deepagents", "openclaw", "hermes", "codex"]
|
|
1655
|
+
),
|
|
1656
|
+
default="langgraph",
|
|
1657
|
+
help="框架类型 (default: langgraph)",
|
|
1658
|
+
)
|
|
1659
|
+
@click.option(
|
|
1660
|
+
"--from-agent",
|
|
1661
|
+
"from_agent_path",
|
|
1662
|
+
type=click.Path(exists=True),
|
|
1663
|
+
help="包装现有 Agent 文件或目录",
|
|
1664
|
+
)
|
|
1877
1665
|
def create(project_name: str, framework: str, from_agent_path: str):
|
|
1878
1666
|
"""创建新的 Agent 项目
|
|
1879
|
-
|
|
1667
|
+
|
|
1880
1668
|
PROJECT_NAME: 项目名称
|
|
1881
|
-
|
|
1669
|
+
|
|
1882
1670
|
使用 --from-agent 可包装现有代码:
|
|
1883
1671
|
agentengine init --from-agent ./my_agent.py # 单文件
|
|
1884
1672
|
agentengine init --from-agent ./my_agent/ # 目录
|
|
@@ -1886,158 +1674,151 @@ def create(project_name: str, framework: str, from_agent_path: str):
|
|
|
1886
1674
|
# === 包装模式 ===
|
|
1887
1675
|
if from_agent_path:
|
|
1888
1676
|
from_path = Path(from_agent_path)
|
|
1889
|
-
|
|
1677
|
+
|
|
1890
1678
|
# === 目录模式 ===
|
|
1891
1679
|
if from_path.is_dir():
|
|
1892
1680
|
print_info(f"扫描目录: {from_path}")
|
|
1893
|
-
|
|
1681
|
+
|
|
1894
1682
|
# 查找入口文件
|
|
1895
1683
|
entry_result = _find_entry_file(from_path)
|
|
1896
1684
|
if not entry_result:
|
|
1897
|
-
print_error(
|
|
1685
|
+
print_error(
|
|
1686
|
+
"未找到有效的入口文件 (agent.py, main.py, __init__.py 或包含 Agent 定义的文件)"
|
|
1687
|
+
)
|
|
1898
1688
|
raise SystemExit(1)
|
|
1899
|
-
|
|
1689
|
+
|
|
1900
1690
|
entry_file, detected_var = entry_result
|
|
1901
1691
|
print_info(f"检测到入口: {entry_file.name}")
|
|
1902
1692
|
print_info(f"检测到变量: {detected_var}")
|
|
1903
|
-
|
|
1693
|
+
|
|
1904
1694
|
# 检测框架:优先读取已有 agentengine.yaml
|
|
1905
|
-
detected_framework = _load_framework_from_agentengine_yaml(from_path) or
|
|
1906
|
-
if detected_framework !=
|
|
1695
|
+
detected_framework = _load_framework_from_agentengine_yaml(from_path) or "unknown"
|
|
1696
|
+
if detected_framework != "unknown":
|
|
1907
1697
|
print_info(f"从 agentengine.yaml 检测到框架: {detected_framework}")
|
|
1908
1698
|
else:
|
|
1909
|
-
entry_content = entry_file.read_text(encoding=
|
|
1699
|
+
entry_content = entry_file.read_text(encoding="utf-8")
|
|
1910
1700
|
detected_framework = _detect_framework(entry_content)
|
|
1911
|
-
|
|
1701
|
+
|
|
1912
1702
|
# 如果入口文件未检测到框架,扫描整个目录
|
|
1913
|
-
if detected_framework ==
|
|
1703
|
+
if detected_framework == "unknown":
|
|
1914
1704
|
for py_file in _iter_python_files(from_path):
|
|
1915
1705
|
try:
|
|
1916
|
-
content = py_file.read_text(encoding=
|
|
1706
|
+
content = py_file.read_text(encoding="utf-8")
|
|
1917
1707
|
except Exception:
|
|
1918
1708
|
continue
|
|
1919
1709
|
detected_framework = _detect_framework(content)
|
|
1920
|
-
if detected_framework !=
|
|
1710
|
+
if detected_framework != "unknown":
|
|
1921
1711
|
break
|
|
1922
|
-
|
|
1923
|
-
if detected_framework ==
|
|
1712
|
+
|
|
1713
|
+
if detected_framework == "unknown":
|
|
1924
1714
|
print_warn("无法自动检测框架,将使用默认 langgraph")
|
|
1925
|
-
detected_framework =
|
|
1715
|
+
detected_framework = "langgraph"
|
|
1926
1716
|
else:
|
|
1927
1717
|
print_info(f"检测到框架: {detected_framework}")
|
|
1928
|
-
|
|
1718
|
+
|
|
1929
1719
|
# 如果没有指定项目名,使用目录名
|
|
1930
1720
|
if not project_name:
|
|
1931
|
-
project_name = from_path.name.replace(
|
|
1932
|
-
|
|
1933
|
-
_wrap_agent_directory(
|
|
1721
|
+
project_name = from_path.name.replace("_", "-")
|
|
1722
|
+
|
|
1723
|
+
_wrap_agent_directory(
|
|
1724
|
+
from_path, project_name, detected_framework, entry_file, detected_var
|
|
1725
|
+
)
|
|
1934
1726
|
return
|
|
1935
|
-
|
|
1727
|
+
|
|
1936
1728
|
# === 单文件模式 ===
|
|
1937
1729
|
else:
|
|
1938
|
-
content = from_path.read_text(encoding=
|
|
1939
|
-
|
|
1730
|
+
content = from_path.read_text(encoding="utf-8")
|
|
1731
|
+
|
|
1940
1732
|
# 自动检测框架
|
|
1941
1733
|
detected_framework = _detect_framework(content)
|
|
1942
|
-
if detected_framework ==
|
|
1734
|
+
if detected_framework == "unknown":
|
|
1943
1735
|
print_warn("无法自动检测框架,将使用默认 langgraph")
|
|
1944
|
-
detected_framework =
|
|
1736
|
+
detected_framework = "langgraph"
|
|
1945
1737
|
else:
|
|
1946
1738
|
print_info(f"检测到框架: {detected_framework}")
|
|
1947
|
-
|
|
1739
|
+
|
|
1948
1740
|
# 自动检测 Agent 变量
|
|
1949
|
-
|
|
1950
|
-
if not
|
|
1741
|
+
detected_agent_var = _detect_agent_variable(content)
|
|
1742
|
+
if not detected_agent_var:
|
|
1951
1743
|
print_warn("无法自动检测 Agent 变量,将使用默认 root_agent")
|
|
1952
|
-
|
|
1744
|
+
detected_agent_var = "root_agent"
|
|
1953
1745
|
else:
|
|
1954
|
-
print_info(f"检测到变量: {
|
|
1955
|
-
|
|
1746
|
+
print_info(f"检测到变量: {detected_agent_var}")
|
|
1747
|
+
|
|
1956
1748
|
# 如果没有指定项目名,使用文件名
|
|
1957
1749
|
if not project_name:
|
|
1958
|
-
project_name = from_path.stem.replace(
|
|
1959
|
-
|
|
1960
|
-
_wrap_agent_file(from_path, project_name, detected_framework,
|
|
1750
|
+
project_name = from_path.stem.replace("_", "-")
|
|
1751
|
+
|
|
1752
|
+
_wrap_agent_file(from_path, project_name, detected_framework, detected_agent_var)
|
|
1961
1753
|
return
|
|
1962
|
-
|
|
1754
|
+
|
|
1963
1755
|
# === 正常模板模式 ===
|
|
1964
1756
|
# 如果没有提供项目名称,进入交互模式
|
|
1965
1757
|
if not project_name:
|
|
1966
1758
|
print_title("初始化新项目")
|
|
1967
|
-
|
|
1968
|
-
project_name = questionary.text(
|
|
1969
|
-
|
|
1970
|
-
style=custom_style
|
|
1971
|
-
).ask()
|
|
1972
|
-
|
|
1759
|
+
|
|
1760
|
+
project_name = questionary.text("请输入项目名称:", style=custom_style).ask()
|
|
1761
|
+
|
|
1973
1762
|
if not project_name:
|
|
1974
1763
|
print_error("取消创建")
|
|
1975
1764
|
raise SystemExit(0)
|
|
1976
|
-
|
|
1765
|
+
|
|
1977
1766
|
framework = questionary.select(
|
|
1978
1767
|
"请选择开发框架:",
|
|
1979
|
-
choices=[
|
|
1980
|
-
default=
|
|
1981
|
-
style=custom_style
|
|
1768
|
+
choices=["langgraph", "langchain", "deepagents", "adk", "openclaw", "hermes", "codex"],
|
|
1769
|
+
default="langgraph",
|
|
1770
|
+
style=custom_style,
|
|
1982
1771
|
).ask()
|
|
1983
|
-
|
|
1772
|
+
|
|
1984
1773
|
if not framework:
|
|
1985
1774
|
print_error("取消创建")
|
|
1986
1775
|
raise SystemExit(0)
|
|
1987
|
-
|
|
1776
|
+
|
|
1988
1777
|
project_path = Path(project_name)
|
|
1989
|
-
|
|
1778
|
+
|
|
1990
1779
|
if project_path.exists():
|
|
1991
1780
|
print_error(f"目录 '{project_name}' 已存在")
|
|
1992
1781
|
raise SystemExit(1)
|
|
1993
|
-
|
|
1782
|
+
|
|
1994
1783
|
print_kv("创建项目", project_name)
|
|
1995
1784
|
print_kv("框架", framework)
|
|
1996
|
-
|
|
1785
|
+
|
|
1997
1786
|
package_name = _runtime_package_name_or_exit(project_path.name)
|
|
1998
1787
|
project_path.mkdir(parents=True)
|
|
1999
|
-
if framework not in {"openclaw", "hermes"}:
|
|
1788
|
+
if framework not in {"openclaw", "hermes", "codex"}:
|
|
2000
1789
|
(project_path / package_name).mkdir(parents=True)
|
|
2001
|
-
|
|
1790
|
+
|
|
2002
1791
|
# 检测全局配置
|
|
2003
1792
|
from ksadk.configs.global_config import (
|
|
2004
|
-
global_config_exists,
|
|
2005
1793
|
get_env_from_global_config,
|
|
1794
|
+
global_config_exists,
|
|
2006
1795
|
)
|
|
2007
|
-
|
|
1796
|
+
|
|
2008
1797
|
global_env = {}
|
|
2009
1798
|
if global_config_exists():
|
|
2010
1799
|
global_env = get_env_from_global_config()
|
|
2011
1800
|
if global_env:
|
|
2012
|
-
print_info("
|
|
2013
|
-
|
|
1801
|
+
print_info("检测到全局配置;项目 .env 仅保留非敏感默认值,凭证不会被复制")
|
|
1802
|
+
|
|
2014
1803
|
# .env - 生成配置文件
|
|
2015
|
-
#
|
|
2016
|
-
# 如果有全局配置,使用全局配置的值;否则使用空字符串
|
|
2017
|
-
api_key = global_env.get("OPENAI_API_KEY", "")
|
|
1804
|
+
# 仅继承非敏感的 endpoint/model/region 默认值;绝不复制凭证。
|
|
2018
1805
|
base_url = global_env.get("OPENAI_BASE_URL", "")
|
|
2019
1806
|
model_name = global_env.get("OPENAI_MODEL_NAME", "")
|
|
2020
|
-
|
|
2021
|
-
ks_ak = global_env.get("KSYUN_ACCESS_KEY", "")
|
|
2022
|
-
ks_sk = global_env.get("KSYUN_SECRET_KEY", "")
|
|
1807
|
+
|
|
2023
1808
|
ks_region = global_env.get("KSYUN_REGION", "cn-beijing-6")
|
|
2024
|
-
|
|
2025
|
-
|
|
1809
|
+
|
|
2026
1810
|
# 构建 .env 内容
|
|
2027
1811
|
if framework == "openclaw":
|
|
2028
1812
|
env_content = f"""# ======================
|
|
2029
1813
|
# OpenClaw 标准部署最小配置
|
|
2030
1814
|
# ======================
|
|
2031
|
-
KSYUN_ACCESS_KEY=
|
|
2032
|
-
KSYUN_SECRET_KEY=
|
|
1815
|
+
# KSYUN_ACCESS_KEY=
|
|
1816
|
+
# KSYUN_SECRET_KEY=
|
|
2033
1817
|
KSYUN_REGION={ks_region}
|
|
2034
1818
|
"""
|
|
2035
|
-
|
|
2036
|
-
env_content += f"KSYUN_ACCOUNT_ID={ks_account}\n"
|
|
2037
|
-
else:
|
|
2038
|
-
env_content += "# KSYUN_ACCOUNT_ID=your-account-id\n"
|
|
1819
|
+
env_content += "# KSYUN_ACCOUNT_ID=your-account-id\n"
|
|
2039
1820
|
|
|
2040
|
-
env_content +=
|
|
1821
|
+
env_content += "\n# OPENAI_API_KEY=\n"
|
|
2041
1822
|
if base_url:
|
|
2042
1823
|
env_content += f"OPENAI_BASE_URL={base_url}\n"
|
|
2043
1824
|
else:
|
|
@@ -2051,18 +1832,13 @@ KSYUN_REGION={ks_region}
|
|
|
2051
1832
|
env_content = f"""# ======================
|
|
2052
1833
|
# Hermes 标准部署最小配置
|
|
2053
1834
|
# ======================
|
|
2054
|
-
KSYUN_ACCESS_KEY=
|
|
2055
|
-
KSYUN_SECRET_KEY=
|
|
1835
|
+
# KSYUN_ACCESS_KEY=
|
|
1836
|
+
# KSYUN_SECRET_KEY=
|
|
2056
1837
|
KSYUN_REGION={ks_region}
|
|
2057
1838
|
"""
|
|
2058
|
-
|
|
2059
|
-
env_content += f"KSYUN_ACCOUNT_ID={ks_account}\n"
|
|
2060
|
-
else:
|
|
2061
|
-
env_content += "# KSYUN_ACCOUNT_ID=your-account-id\n"
|
|
1839
|
+
env_content += "# KSYUN_ACCOUNT_ID=your-account-id\n"
|
|
2062
1840
|
|
|
2063
|
-
env_content +=
|
|
2064
|
-
OPENAI_API_KEY={api_key}
|
|
2065
|
-
"""
|
|
1841
|
+
env_content += "\n# OPENAI_API_KEY=\n"
|
|
2066
1842
|
if base_url:
|
|
2067
1843
|
env_content += f"OPENAI_BASE_URL={base_url}\n"
|
|
2068
1844
|
else:
|
|
@@ -2083,7 +1859,6 @@ HERMES_DASHBOARD_PORT=9119
|
|
|
2083
1859
|
PORT=8080
|
|
2084
1860
|
# HERMES_CONTEXT_LENGTH=200000
|
|
2085
1861
|
# HERMES_FALLBACK_MODEL=deepseek-v4-pro
|
|
2086
|
-
# HERMES_IMAGE=hub.kce.ksyun.com/agentengine-public/hermes-agent:2026.5.29.2-ksadk-v1
|
|
2087
1862
|
"""
|
|
2088
1863
|
env_example_content = """# ======================
|
|
2089
1864
|
# Hermes 标准部署最小配置示例
|
|
@@ -2106,17 +1881,16 @@ HERMES_DASHBOARD_PORT=9119
|
|
|
2106
1881
|
PORT=8080
|
|
2107
1882
|
# HERMES_CONTEXT_LENGTH=200000
|
|
2108
1883
|
# HERMES_FALLBACK_MODEL=deepseek-v4-pro
|
|
2109
|
-
# HERMES_IMAGE=hub.kce.ksyun.com/agentengine-public/hermes-agent:2026.5.29.2-ksadk-v1
|
|
2110
1884
|
"""
|
|
1885
|
+
elif framework == "codex":
|
|
1886
|
+
env_content = _generate_codex_env_content(global_env)
|
|
2111
1887
|
else:
|
|
2112
|
-
langfuse_public = global_env.get("LANGFUSE_PUBLIC_KEY", "")
|
|
2113
|
-
langfuse_secret = global_env.get("LANGFUSE_SECRET_KEY", "")
|
|
2114
1888
|
langfuse_url = global_env.get("LANGFUSE_BASE_URL", "")
|
|
2115
1889
|
|
|
2116
|
-
env_content =
|
|
1890
|
+
env_content = """# ======================
|
|
2117
1891
|
# 模型配置 (必填, 可以从星流平台获取https://ksp.console.ksyun.com/#/apiKey)
|
|
2118
1892
|
# ======================
|
|
2119
|
-
OPENAI_API_KEY=
|
|
1893
|
+
# OPENAI_API_KEY=
|
|
2120
1894
|
"""
|
|
2121
1895
|
|
|
2122
1896
|
# 可选字段:如果有值则启用,否则注释掉
|
|
@@ -2135,15 +1909,8 @@ OPENAI_API_KEY={api_key}
|
|
|
2135
1909
|
# 可观测性 (可选)
|
|
2136
1910
|
# ======================
|
|
2137
1911
|
"""
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
else:
|
|
2141
|
-
env_content += "# LANGFUSE_PUBLIC_KEY=pk-xxx\n"
|
|
2142
|
-
|
|
2143
|
-
if langfuse_secret:
|
|
2144
|
-
env_content += f"LANGFUSE_SECRET_KEY={langfuse_secret}\n"
|
|
2145
|
-
else:
|
|
2146
|
-
env_content += "# LANGFUSE_SECRET_KEY=sk-xxx\n"
|
|
1912
|
+
env_content += "# LANGFUSE_PUBLIC_KEY=pk-xxx\n"
|
|
1913
|
+
env_content += "# LANGFUSE_SECRET_KEY=sk-xxx\n"
|
|
2147
1914
|
|
|
2148
1915
|
if langfuse_url:
|
|
2149
1916
|
env_content += f"LANGFUSE_BASE_URL={langfuse_url}\n"
|
|
@@ -2155,23 +1922,13 @@ OPENAI_API_KEY={api_key}
|
|
|
2155
1922
|
# 金山云配置 (可选,需要部署时必选)
|
|
2156
1923
|
# ======================
|
|
2157
1924
|
"""
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
else:
|
|
2161
|
-
env_content += "# KSYUN_ACCESS_KEY=your-api-key-here\n"
|
|
2162
|
-
|
|
2163
|
-
if ks_sk:
|
|
2164
|
-
env_content += f"KSYUN_SECRET_KEY={ks_sk}\n"
|
|
2165
|
-
else:
|
|
2166
|
-
env_content += "# KSYUN_SECRET_KEY=your-api-secret-here\n"
|
|
1925
|
+
env_content += "# KSYUN_ACCESS_KEY=your-api-key-here\n"
|
|
1926
|
+
env_content += "# KSYUN_SECRET_KEY=your-api-secret-here\n"
|
|
2167
1927
|
|
|
2168
1928
|
env_content += f"KSYUN_REGION={ks_region}\n"
|
|
2169
1929
|
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
else:
|
|
2173
|
-
env_content += "# KSYUN_ACCOUNT_ID=your-account-id\n"
|
|
2174
|
-
|
|
1930
|
+
env_content += "# KSYUN_ACCOUNT_ID=your-account-id\n"
|
|
1931
|
+
|
|
2175
1932
|
# 使用 utf-8-sig 编码 (带 BOM),确保 Windows 程序正确识别为 UTF-8
|
|
2176
1933
|
(project_path / ".env").write_text(env_content, encoding="utf-8-sig")
|
|
2177
1934
|
if framework == "hermes":
|
|
@@ -2185,16 +1942,35 @@ OPENAI_API_KEY={api_key}
|
|
|
2185
1942
|
print_info("部署前如需覆盖模型/网关参数,可先编辑 .env")
|
|
2186
1943
|
return
|
|
2187
1944
|
if framework == "hermes":
|
|
2188
|
-
|
|
1945
|
+
_write_hermes_project_config(project_path, package_name)
|
|
2189
1946
|
print_success("项目创建成功")
|
|
2190
1947
|
print_rule("快速开始")
|
|
2191
1948
|
print_info("快速开始 (复制并执行):")
|
|
2192
1949
|
_print_quick_start_commands(project_name, ["agentengine hermes deploy"])
|
|
2193
1950
|
print_info("部署前如需覆盖模型/运行时参数,可先编辑 .env")
|
|
2194
1951
|
return
|
|
2195
|
-
|
|
1952
|
+
if framework == "codex":
|
|
1953
|
+
_write_codex_project_config(project_path, package_name)
|
|
1954
|
+
print_success("项目创建成功")
|
|
1955
|
+
print_rule("快速开始")
|
|
1956
|
+
print_info("快速开始 (复制并执行):")
|
|
1957
|
+
_print_quick_start_commands(project_name, ["ksadk web ."])
|
|
1958
|
+
print_info("编辑 .env 填星流 OPENAI_API_KEY/BASE/MODEL_NAME;codex 自动探测并启用代理")
|
|
1959
|
+
# codex 本地环境检测(不阻断):缺 openai-codex SDK 时提醒
|
|
1960
|
+
import importlib.util
|
|
1961
|
+
|
|
1962
|
+
if importlib.util.find_spec("openai_codex") is None:
|
|
1963
|
+
print_warn(
|
|
1964
|
+
escape(
|
|
1965
|
+
"缺少 codex runtime SDK:请先 `pip install 'ksadk[codex]'`"
|
|
1966
|
+
"(内含 codex CLI 二进制)"
|
|
1967
|
+
)
|
|
1968
|
+
)
|
|
1969
|
+
return
|
|
1970
|
+
|
|
2196
1971
|
# agentengine.yaml - Agent 配置
|
|
2197
|
-
(project_path / "agentengine.yaml").write_text(
|
|
1972
|
+
(project_path / "agentengine.yaml").write_text(
|
|
1973
|
+
f"""# AgentEngine 项目配置
|
|
2198
1974
|
name: {package_name}
|
|
2199
1975
|
version: "1.0.0"
|
|
2200
1976
|
|
|
@@ -2209,23 +1985,27 @@ agent_variable: root_agent
|
|
|
2209
1985
|
# deploy:
|
|
2210
1986
|
# timeout: 300
|
|
2211
1987
|
# memory: 512
|
|
2212
|
-
""",
|
|
2213
|
-
|
|
1988
|
+
""",
|
|
1989
|
+
encoding="utf-8-sig",
|
|
1990
|
+
)
|
|
1991
|
+
|
|
2214
1992
|
# __init__.py
|
|
2215
|
-
(project_path / package_name / "__init__.py").write_text(
|
|
1993
|
+
(project_path / package_name / "__init__.py").write_text(
|
|
1994
|
+
f'''"""
|
|
2216
1995
|
{project_name} - KsADK Agent
|
|
2217
1996
|
"""
|
|
2218
1997
|
from .agent import root_agent
|
|
2219
1998
|
__all__ = ["root_agent"]
|
|
2220
|
-
''',
|
|
2221
|
-
|
|
1999
|
+
''',
|
|
2000
|
+
encoding="utf-8-sig",
|
|
2001
|
+
)
|
|
2002
|
+
|
|
2222
2003
|
# agent.py
|
|
2223
2004
|
template = TEMPLATES[framework]["agent.py"]
|
|
2224
2005
|
(project_path / package_name / "agent.py").write_text(
|
|
2225
|
-
template.format(package_name=package_name),
|
|
2226
|
-
encoding="utf-8-sig"
|
|
2006
|
+
template.format(package_name=package_name), encoding="utf-8-sig"
|
|
2227
2007
|
)
|
|
2228
|
-
|
|
2008
|
+
|
|
2229
2009
|
# README.md
|
|
2230
2010
|
if framework == "openclaw":
|
|
2231
2011
|
readme = f"""# {project_name}
|
|
@@ -2292,12 +2072,12 @@ agentengine deploy . # 部署到云端
|
|
|
2292
2072
|
reqs += "deepagents\nlangchain\nlangchain-openai\nlanggraph\npython-dotenv\n"
|
|
2293
2073
|
elif framework == "adk":
|
|
2294
2074
|
reqs += "google-adk\npython-dotenv\n"
|
|
2295
|
-
|
|
2075
|
+
|
|
2296
2076
|
(project_path / "requirements.txt").write_text(reqs, encoding="utf-8")
|
|
2297
|
-
|
|
2077
|
+
|
|
2298
2078
|
print_success("项目创建成功")
|
|
2299
2079
|
print_rule("快速开始")
|
|
2300
|
-
|
|
2080
|
+
|
|
2301
2081
|
print_info("快速开始 (复制并执行):")
|
|
2302
2082
|
_print_quick_start_commands(project_name, ["agentengine config"])
|
|
2303
2083
|
print_info("或直接运行 (环境变量中需包含模型 API Key):")
|