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/events/store.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""RuntimeEventStore — 统一事件 store + 两类订阅 + projection (goal-10,H2 §4.3)。
|
|
2
|
+
|
|
3
|
+
复用现有 ``SessionEvent(seq_id cursor)`` 持久化骨架(session service),**不另造存储、
|
|
4
|
+
不改表**:RuntimeEvent 的 ``phase``/``payload``/``schema_version``/``user_id`` 打包进
|
|
5
|
+
``SessionEvent.content``/``metadata``(均为自由 dict),并以 ``_RUNTIME_MARKER`` 标记区分
|
|
6
|
+
legacy session 事件(assistant_message/run_status 等),读取时只还原 runtime 事件。
|
|
7
|
+
|
|
8
|
+
- ``append`` / ``list``:RuntimeEvent ↔ SessionEvent 双向映射。
|
|
9
|
+
- ``subscribe_run``:单 invocation,终态(completed/failed/canceled)后关闭(对齐现有
|
|
10
|
+
run 级 SSE 语义,新 schema)。
|
|
11
|
+
- ``subscribe_session``:session 级 cursor stream,跨 invocation,支持 run 后 action 与
|
|
12
|
+
replay(A2UI 依赖)。
|
|
13
|
+
- ``project``:replay / 增量 projection(fold)。
|
|
14
|
+
- cursor 断线续传:订阅方持 ``last_seq_id``,断线后按 ``after_seq_id`` 重连,不丢不重。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
import logging
|
|
21
|
+
from typing import Any, AsyncIterator, Callable, Iterable, Optional
|
|
22
|
+
|
|
23
|
+
from ksadk.events.runtime_event import EventType, RuntimeEvent
|
|
24
|
+
from ksadk.sessions.base import SessionEvent
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
#: SessionEvent.metadata 中的标记:该条是由 RuntimeEvent 持久化来的(区分 legacy 事件)。
|
|
29
|
+
_RUNTIME_MARKER = "ksadk_runtime_event"
|
|
30
|
+
_A2A_TASK_AGENT_STATE_KEY = "__ksadk_a2a_task_agents"
|
|
31
|
+
|
|
32
|
+
#: run 终态(subscribe_run 遇到即关闭;interrupted 是 input-required 暂停,非终态)。
|
|
33
|
+
_RUN_TERMINAL_EVENT_TYPES = frozenset(
|
|
34
|
+
{
|
|
35
|
+
EventType.RUN_COMPLETED,
|
|
36
|
+
EventType.RUN_FAILED,
|
|
37
|
+
EventType.RUN_CANCELED,
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
#: 默认订阅轮询间隔(秒)与单条流上限(秒,防泄漏)。
|
|
42
|
+
_DEFAULT_POLL_INTERVAL = 0.25
|
|
43
|
+
_DEFAULT_STREAM_TIMEOUT = 5 * 60
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# RuntimeEvent ↔ SessionEvent 映射
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def runtime_event_to_session_event(event: RuntimeEvent) -> SessionEvent:
|
|
52
|
+
"""把 RuntimeEvent 打包为 SessionEvent(content/metadata 承载新 schema 字段,不改表)。"""
|
|
53
|
+
event.validate_conformance()
|
|
54
|
+
return SessionEvent(
|
|
55
|
+
id=event.event_id,
|
|
56
|
+
session_id=event.session_id,
|
|
57
|
+
author=event.agent_id,
|
|
58
|
+
event_type=event.event_type,
|
|
59
|
+
content={"phase": event.phase, "payload": dict(event.payload)},
|
|
60
|
+
timestamp=event.timestamp,
|
|
61
|
+
seq_id=event.seq_id,
|
|
62
|
+
invocation_id=event.invocation_id,
|
|
63
|
+
metadata={
|
|
64
|
+
_RUNTIME_MARKER: True,
|
|
65
|
+
"user_id": event.user_id,
|
|
66
|
+
"schema_version": event.schema_version,
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def session_event_to_runtime_event(event: SessionEvent) -> Optional[RuntimeEvent]:
|
|
72
|
+
"""把 SessionEvent 还原为 RuntimeEvent;非 runtime 事件(无标记)返回 None。"""
|
|
73
|
+
if not (event.metadata or {}).get(_RUNTIME_MARKER):
|
|
74
|
+
return None
|
|
75
|
+
content = event.content or {}
|
|
76
|
+
return RuntimeEvent.create(
|
|
77
|
+
event.event_type,
|
|
78
|
+
agent_id=event.author,
|
|
79
|
+
user_id=str(event.metadata.get("user_id") or ""),
|
|
80
|
+
session_id=event.session_id,
|
|
81
|
+
invocation_id=event.invocation_id or "",
|
|
82
|
+
seq_id=event.seq_id,
|
|
83
|
+
payload=dict(content.get("payload") or {}),
|
|
84
|
+
phase=content.get("phase"),
|
|
85
|
+
event_id=event.id,
|
|
86
|
+
timestamp=event.timestamp,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
# RuntimeEventStore
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class RuntimeEventStore:
|
|
96
|
+
"""统一事件 store(复用 session service 的 seq_id cursor 持久化骨架)。"""
|
|
97
|
+
|
|
98
|
+
def __init__(self, session_service: Any) -> None:
|
|
99
|
+
self._service = session_service
|
|
100
|
+
|
|
101
|
+
# ---- append ----
|
|
102
|
+
|
|
103
|
+
async def append(self, events: Iterable[RuntimeEvent]) -> list[RuntimeEvent]:
|
|
104
|
+
"""持久化一组 RuntimeEvent,返回存储层分配 cursor 后的事件。"""
|
|
105
|
+
appended: list[RuntimeEvent] = []
|
|
106
|
+
for event in events:
|
|
107
|
+
appended.append(await self.append_one(event))
|
|
108
|
+
return appended
|
|
109
|
+
|
|
110
|
+
async def append_one(self, event: RuntimeEvent) -> RuntimeEvent:
|
|
111
|
+
"""Idempotently persist one event using its durable ``event_id``.
|
|
112
|
+
|
|
113
|
+
Replayed wire events return their original store-assigned cursor. Reuse
|
|
114
|
+
of an ID for different content is rejected rather than silently losing a
|
|
115
|
+
legal event.
|
|
116
|
+
"""
|
|
117
|
+
persisted, _created = await self.reserve_once(event)
|
|
118
|
+
return persisted
|
|
119
|
+
|
|
120
|
+
async def reserve_once(self, event: RuntimeEvent) -> tuple[RuntimeEvent, bool]:
|
|
121
|
+
"""Durably claim ``event.event_id`` and report whether this caller won.
|
|
122
|
+
|
|
123
|
+
SQL backends enforce a unique event id, so this is also the command
|
|
124
|
+
reservation seam for side effects such as checkpoint resume. A loser
|
|
125
|
+
receives the existing identical fact with ``created=False`` and must
|
|
126
|
+
not repeat the side effect.
|
|
127
|
+
"""
|
|
128
|
+
existing = await self._event_by_id(event.session_id, event.event_id)
|
|
129
|
+
if existing is not None:
|
|
130
|
+
self._assert_same_event(existing, event)
|
|
131
|
+
return existing, False
|
|
132
|
+
try:
|
|
133
|
+
stored = await self._service.append_event(
|
|
134
|
+
event.session_id, runtime_event_to_session_event(event)
|
|
135
|
+
)
|
|
136
|
+
except Exception:
|
|
137
|
+
# Durable backends enforce a unique event id. A concurrent writer
|
|
138
|
+
# may win between the read and append; resolve that race by reading
|
|
139
|
+
# the persisted fact and validating its content.
|
|
140
|
+
existing = await self._event_by_id(event.session_id, event.event_id)
|
|
141
|
+
if existing is None:
|
|
142
|
+
raise
|
|
143
|
+
self._assert_same_event(existing, event)
|
|
144
|
+
return existing, False
|
|
145
|
+
persisted = session_event_to_runtime_event(stored)
|
|
146
|
+
if persisted is None: # pragma: no cover - marker is set above by construction
|
|
147
|
+
raise RuntimeError("RuntimeEvent 持久化后缺少 runtime marker")
|
|
148
|
+
return persisted, True
|
|
149
|
+
|
|
150
|
+
async def _event_by_id(self, session_id: str, event_id: str) -> RuntimeEvent | None:
|
|
151
|
+
raw = await self._service.get_events(session_id)
|
|
152
|
+
for stored in raw:
|
|
153
|
+
if stored.id != event_id:
|
|
154
|
+
continue
|
|
155
|
+
return session_event_to_runtime_event(stored)
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def _assert_same_event(existing: RuntimeEvent, candidate: RuntimeEvent) -> None:
|
|
160
|
+
comparable = (
|
|
161
|
+
"event_type",
|
|
162
|
+
"agent_id",
|
|
163
|
+
"user_id",
|
|
164
|
+
"session_id",
|
|
165
|
+
"invocation_id",
|
|
166
|
+
"phase",
|
|
167
|
+
"payload",
|
|
168
|
+
)
|
|
169
|
+
if any(getattr(existing, field) != getattr(candidate, field) for field in comparable):
|
|
170
|
+
raise ValueError(f"RuntimeEvent id collision for {candidate.event_id!r}")
|
|
171
|
+
|
|
172
|
+
async def set_task_agent(self, session_id: str, task_id: str, agent_id: str) -> None:
|
|
173
|
+
"""Persist the outbound A2A task locator in session state."""
|
|
174
|
+
session = await self._service.get_session_metadata(session_id)
|
|
175
|
+
if session is None:
|
|
176
|
+
raise ValueError(f"A2A space session {session_id!r} not found")
|
|
177
|
+
current = await self._service.get_state(
|
|
178
|
+
session.agent_id,
|
|
179
|
+
session.user_id,
|
|
180
|
+
session.id,
|
|
181
|
+
scope="session",
|
|
182
|
+
)
|
|
183
|
+
mapping = dict((current.state if current else {}).get(_A2A_TASK_AGENT_STATE_KEY) or {})
|
|
184
|
+
existing = mapping.get(task_id)
|
|
185
|
+
if existing and existing != agent_id:
|
|
186
|
+
raise ValueError(f"A2A task {task_id!r} is already bound to another agent")
|
|
187
|
+
mapping[task_id] = agent_id
|
|
188
|
+
await self._service.update_state(
|
|
189
|
+
agent_id=session.agent_id,
|
|
190
|
+
user_id=session.user_id,
|
|
191
|
+
session_id=session.id,
|
|
192
|
+
scope="session",
|
|
193
|
+
state_delta={_A2A_TASK_AGENT_STATE_KEY: mapping},
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
async def get_task_agent(self, session_id: str, task_id: str) -> str | None:
|
|
197
|
+
"""Resolve a persisted outbound A2A task locator."""
|
|
198
|
+
session = await self._service.get_session_metadata(session_id)
|
|
199
|
+
if session is None:
|
|
200
|
+
return None
|
|
201
|
+
current = await self._service.get_state(
|
|
202
|
+
session.agent_id,
|
|
203
|
+
session.user_id,
|
|
204
|
+
session.id,
|
|
205
|
+
scope="session",
|
|
206
|
+
)
|
|
207
|
+
mapping = dict((current.state if current else {}).get(_A2A_TASK_AGENT_STATE_KEY) or {})
|
|
208
|
+
value = mapping.get(task_id)
|
|
209
|
+
return str(value) if value else None
|
|
210
|
+
|
|
211
|
+
# ---- list ----
|
|
212
|
+
|
|
213
|
+
async def list(
|
|
214
|
+
self,
|
|
215
|
+
session_id: str,
|
|
216
|
+
*,
|
|
217
|
+
after_seq_id: int = 0,
|
|
218
|
+
before_seq_id: Optional[int] = None,
|
|
219
|
+
invocation_id: Optional[str] = None,
|
|
220
|
+
limit: Optional[int] = None,
|
|
221
|
+
) -> list[RuntimeEvent]:
|
|
222
|
+
"""按 seq cursor 读 RuntimeEvent(升序;可按 invocation 过滤 / before 上界回放)。"""
|
|
223
|
+
raw = await self._service.get_events(
|
|
224
|
+
session_id,
|
|
225
|
+
after_seq_id=after_seq_id,
|
|
226
|
+
before_seq_id=before_seq_id,
|
|
227
|
+
limit=limit,
|
|
228
|
+
)
|
|
229
|
+
events = [e for e in (session_event_to_runtime_event(se) for se in raw) if e is not None]
|
|
230
|
+
if invocation_id is not None:
|
|
231
|
+
events = [e for e in events if e.invocation_id == invocation_id]
|
|
232
|
+
events.sort(key=lambda e: e.seq_id)
|
|
233
|
+
return events
|
|
234
|
+
|
|
235
|
+
# ---- 两类订阅 ----
|
|
236
|
+
|
|
237
|
+
async def subscribe_run(
|
|
238
|
+
self,
|
|
239
|
+
session_id: str,
|
|
240
|
+
invocation_id: str,
|
|
241
|
+
*,
|
|
242
|
+
after_seq_id: int = 0,
|
|
243
|
+
poll_interval: float = _DEFAULT_POLL_INTERVAL,
|
|
244
|
+
timeout: float = _DEFAULT_STREAM_TIMEOUT,
|
|
245
|
+
) -> AsyncIterator[RuntimeEvent]:
|
|
246
|
+
"""单 invocation 订阅:只产该 invocation 的 RuntimeEvent,终态后关闭。
|
|
247
|
+
|
|
248
|
+
断线续传:调用方持返回事件的 ``seq_id``,断线后以 ``after_seq_id`` 重连即可续传,
|
|
249
|
+
不丢(>after 的全部重发)、不重(<=after 的不重发)。
|
|
250
|
+
"""
|
|
251
|
+
last = int(after_seq_id or 0)
|
|
252
|
+
deadline = asyncio.get_event_loop().time() + timeout
|
|
253
|
+
while True:
|
|
254
|
+
events = await self.list(session_id, after_seq_id=last, invocation_id=invocation_id)
|
|
255
|
+
for event in events:
|
|
256
|
+
last = max(last, event.seq_id)
|
|
257
|
+
yield event
|
|
258
|
+
if event.event_type in _RUN_TERMINAL_EVENT_TYPES:
|
|
259
|
+
return
|
|
260
|
+
if asyncio.get_event_loop().time() > deadline:
|
|
261
|
+
return
|
|
262
|
+
await asyncio.sleep(poll_interval)
|
|
263
|
+
|
|
264
|
+
async def subscribe_session(
|
|
265
|
+
self,
|
|
266
|
+
session_id: str,
|
|
267
|
+
*,
|
|
268
|
+
after_seq_id: int = 0,
|
|
269
|
+
poll_interval: float = _DEFAULT_POLL_INTERVAL,
|
|
270
|
+
timeout: float = _DEFAULT_STREAM_TIMEOUT,
|
|
271
|
+
) -> AsyncIterator[RuntimeEvent]:
|
|
272
|
+
"""session 级 cursor stream:跨 invocation 产全部 RuntimeEvent(replay + live)。
|
|
273
|
+
|
|
274
|
+
支持 run 后 action 与跨 invocation replay(A2UI 依赖);断线续传同 subscribe_run。
|
|
275
|
+
"""
|
|
276
|
+
last = int(after_seq_id or 0)
|
|
277
|
+
deadline = asyncio.get_event_loop().time() + timeout
|
|
278
|
+
while True:
|
|
279
|
+
events = await self.list(session_id, after_seq_id=last)
|
|
280
|
+
for event in events:
|
|
281
|
+
last = max(last, event.seq_id)
|
|
282
|
+
yield event
|
|
283
|
+
if asyncio.get_event_loop().time() > deadline:
|
|
284
|
+
return
|
|
285
|
+
await asyncio.sleep(poll_interval)
|
|
286
|
+
|
|
287
|
+
# ---- projection / replay ----
|
|
288
|
+
|
|
289
|
+
async def project(
|
|
290
|
+
self,
|
|
291
|
+
session_id: str,
|
|
292
|
+
projection: Optional[Callable[[Any, RuntimeEvent], Any]] = None,
|
|
293
|
+
*,
|
|
294
|
+
initial: Any = None,
|
|
295
|
+
after_seq_id: int = 0,
|
|
296
|
+
before_seq_id: Optional[int] = None,
|
|
297
|
+
) -> Any:
|
|
298
|
+
"""replay / projection。
|
|
299
|
+
|
|
300
|
+
默认(``projection=None``):返回按 seq 升序的 RuntimeEvent 序列(replay)。
|
|
301
|
+
给定 ``projection(acc, event) -> acc``:自 ``initial`` 起 fold 全部事件,支持
|
|
302
|
+
增量 projection(以 ``after_seq_id`` 从某个 checkpoint 续投影)。
|
|
303
|
+
"""
|
|
304
|
+
events = await self.list(session_id, after_seq_id=after_seq_id, before_seq_id=before_seq_id)
|
|
305
|
+
if projection is None:
|
|
306
|
+
return events
|
|
307
|
+
acc = initial
|
|
308
|
+
for event in events:
|
|
309
|
+
acc = projection(acc, event)
|
|
310
|
+
return acc
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
__all__ = [
|
|
314
|
+
"RuntimeEventStore",
|
|
315
|
+
"runtime_event_to_session_event",
|
|
316
|
+
"session_event_to_runtime_event",
|
|
317
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""HarnessApp — 统一 Runtime 的可部署交付物 / composition root (goal-08)。"""
|
|
2
|
+
|
|
3
|
+
from ksadk.harness.app import HarnessApp, HarnessCapabilities, HarnessPlugin
|
|
4
|
+
from ksadk.harness.config import (
|
|
5
|
+
HarnessConfig,
|
|
6
|
+
HarnessConfigError,
|
|
7
|
+
McpToolSpec,
|
|
8
|
+
SandboxPolicy,
|
|
9
|
+
)
|
|
10
|
+
from ksadk.harness.reasoner import (
|
|
11
|
+
HarnessReasoner,
|
|
12
|
+
HarnessReasoningTurn,
|
|
13
|
+
HarnessToolCall,
|
|
14
|
+
LiteLLMHarnessReasoner,
|
|
15
|
+
)
|
|
16
|
+
from ksadk.harness.runner import YamlAgentRunner
|
|
17
|
+
from ksadk.harness.sandbox import HarnessSandboxExecutor, SandboxPolicyDenied
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"HarnessApp",
|
|
21
|
+
"HarnessCapabilities",
|
|
22
|
+
"HarnessConfig",
|
|
23
|
+
"HarnessConfigError",
|
|
24
|
+
"HarnessPlugin",
|
|
25
|
+
"HarnessReasoner",
|
|
26
|
+
"HarnessReasoningTurn",
|
|
27
|
+
"HarnessSandboxExecutor",
|
|
28
|
+
"HarnessToolCall",
|
|
29
|
+
"LiteLLMHarnessReasoner",
|
|
30
|
+
"McpToolSpec",
|
|
31
|
+
"SandboxPolicy",
|
|
32
|
+
"SandboxPolicyDenied",
|
|
33
|
+
"YamlAgentRunner",
|
|
34
|
+
]
|
ksadk/harness/app.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""HarnessApp — 统一 Runtime 的可部署交付物 / composition root。
|
|
2
|
+
|
|
3
|
+
H2 §4.2:HarnessApp 是 composition root,**不包装复制 3000+ 行全局 app**——复用
|
|
4
|
+
G0.1 ``create_runtime_app`` 装配;A2A、Responses、session、files 等**数据面** route
|
|
5
|
+
group 装配,**控制面 action 不进入**;不 import 全局 ``base_app`` 再过滤复制路由。
|
|
6
|
+
|
|
7
|
+
本层保持 AgentDraft 的最小字段面,但执行路径会运行真实模型推理、MCP transport 与
|
|
8
|
+
Harness read-only sandbox policy。不含完整 AgentDraft v1 字段面、plugin 生态或 deploy 自动化。
|
|
9
|
+
|
|
10
|
+
分层职责:yaml → RuntimeAdapter ``start(request)`` 的映射在**本层**;平台请求 →
|
|
11
|
+
codex 配置(config.toml/AGENTS.md/mcp_servers)的翻译是 goal-09 CodexRuntime 的职责,
|
|
12
|
+
不在本层做。
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import tempfile
|
|
19
|
+
from dataclasses import dataclass, replace
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Callable, Optional, Protocol, Sequence
|
|
22
|
+
|
|
23
|
+
from fastapi import FastAPI
|
|
24
|
+
|
|
25
|
+
from ksadk.harness.config import HarnessConfig
|
|
26
|
+
from ksadk.harness.reasoner import HarnessReasoner
|
|
27
|
+
from ksadk.harness.runner import YamlAgentRunner
|
|
28
|
+
from ksadk.runners.base_runner import BaseRunner
|
|
29
|
+
from ksadk.runtime.adapter import RuntimeAdapter, StartRequest
|
|
30
|
+
from ksadk.runtime.runner_adapter import RunnerRuntimeAdapter
|
|
31
|
+
from ksadk.server.factory import RuntimeAppConfig, create_runtime_app
|
|
32
|
+
from ksadk.server.routes.routers import GROUP_ROUTERS, load_route_modules
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class HarnessPlugin(Protocol):
|
|
38
|
+
"""基础 plugin host 的插件协议(骨架期最小钩子)。"""
|
|
39
|
+
|
|
40
|
+
def before_build(self, config: HarnessConfig) -> None: # pragma: no cover - 协议
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
def after_build(self, app: FastAPI, config: HarnessConfig) -> None: # pragma: no cover
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
#: per-invocation override 允许覆盖的字段(最小子集内)。
|
|
48
|
+
_OVERRIDEABLE = ("model", "prompt")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class HarnessCapabilities:
|
|
53
|
+
"""Protocol/data surfaces backed by this Harness composition."""
|
|
54
|
+
|
|
55
|
+
responses: bool = True
|
|
56
|
+
sessions: bool = True
|
|
57
|
+
files: bool = False
|
|
58
|
+
a2a: bool = False
|
|
59
|
+
agui: bool = False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class HarnessApp:
|
|
63
|
+
"""统一 Runtime 的 composition root(skeleton)。"""
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
config: HarnessConfig,
|
|
68
|
+
*,
|
|
69
|
+
plugins: Sequence[HarnessPlugin] = (),
|
|
70
|
+
adapter_builder: Optional[Callable[[BaseRunner], RuntimeAdapter]] = None,
|
|
71
|
+
reasoner: HarnessReasoner | None = None,
|
|
72
|
+
workspace_root: str | Path | None = None,
|
|
73
|
+
a2a: Any | None = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
self._config = config
|
|
76
|
+
self._plugins = list(plugins)
|
|
77
|
+
self._adapter_builder = adapter_builder
|
|
78
|
+
self._reasoner = reasoner
|
|
79
|
+
self._a2a = a2a
|
|
80
|
+
self._owned_workspace: tempfile.TemporaryDirectory[str] | None
|
|
81
|
+
if workspace_root is None:
|
|
82
|
+
self._owned_workspace = tempfile.TemporaryDirectory(prefix="ksadk-harness-")
|
|
83
|
+
resolved_workspace: str | Path = self._owned_workspace.name
|
|
84
|
+
else:
|
|
85
|
+
self._owned_workspace = None
|
|
86
|
+
resolved_workspace = workspace_root
|
|
87
|
+
self._workspace_root = Path(resolved_workspace).resolve()
|
|
88
|
+
self._runner: BaseRunner | None = None
|
|
89
|
+
self._adapter: RuntimeAdapter | None = None
|
|
90
|
+
self._fastapi_app: FastAPI | None = None
|
|
91
|
+
self._capabilities = HarnessCapabilities(a2a=a2a is not None)
|
|
92
|
+
# Each Harness owns its session backend. Route handlers may use this
|
|
93
|
+
# marker even when a host does not provide the optional session seam.
|
|
94
|
+
from ksadk.sessions import create_session_service
|
|
95
|
+
|
|
96
|
+
self._session_service = create_session_service(
|
|
97
|
+
backend="memory", project_dir=str(self._workspace_root)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_yaml(cls, path: str | Path, **kwargs: Any) -> "HarnessApp":
|
|
102
|
+
"""从 yaml 装配(最小子集,超出明确报错)。"""
|
|
103
|
+
return cls(HarnessConfig.from_yaml(path), **kwargs)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def config(self) -> HarnessConfig:
|
|
107
|
+
return self._config
|
|
108
|
+
|
|
109
|
+
# ---- override ----
|
|
110
|
+
|
|
111
|
+
def apply_overrides(self, overrides: Optional[dict[str, Any]]) -> HarnessConfig:
|
|
112
|
+
"""per-invocation override:只允许最小子集内字段,超出报错(不静默忽略)。"""
|
|
113
|
+
if not overrides:
|
|
114
|
+
return self._config
|
|
115
|
+
unknown = set(overrides) - set(_OVERRIDEABLE)
|
|
116
|
+
if unknown:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"override 仅允许 {list(_OVERRIDEABLE)}(最小子集),不支持: {sorted(unknown)}"
|
|
119
|
+
)
|
|
120
|
+
return replace(self._config, **overrides)
|
|
121
|
+
|
|
122
|
+
# ---- 组装 ----
|
|
123
|
+
|
|
124
|
+
def build_runner(self, overrides: Optional[dict[str, Any]] = None) -> BaseRunner:
|
|
125
|
+
if overrides is not None:
|
|
126
|
+
raise ValueError("build_runner 不接受 invocation override;请传给 start(request)")
|
|
127
|
+
if self._runner is None:
|
|
128
|
+
self._runner = self._build_runner(self._config)
|
|
129
|
+
return self._runner
|
|
130
|
+
|
|
131
|
+
def _build_runner(self, config: HarnessConfig) -> BaseRunner:
|
|
132
|
+
for plugin in self._plugins:
|
|
133
|
+
plugin.before_build(config)
|
|
134
|
+
if config.runtime == "codex":
|
|
135
|
+
# codex runtime:CodexRunner(经 ksadk web 已验证路径),detector stub 用 CODEX
|
|
136
|
+
from ksadk.detection.detector import FrameworkType
|
|
137
|
+
from ksadk.runners.codex_runner import CodexRunner
|
|
138
|
+
|
|
139
|
+
detection = type(
|
|
140
|
+
"D", (), {"name": config.model, "type": FrameworkType.CODEX}
|
|
141
|
+
)()
|
|
142
|
+
return CodexRunner(detection, str(self._workspace_root))
|
|
143
|
+
return YamlAgentRunner(
|
|
144
|
+
config,
|
|
145
|
+
reasoner=self._reasoner,
|
|
146
|
+
workspace_root=self._workspace_root,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def adapter(self, overrides: Optional[dict[str, Any]] = None) -> RuntimeAdapter:
|
|
150
|
+
"""yaml → RuntimeAdapter(goal-07);start(request) 在此层映射。"""
|
|
151
|
+
if overrides is not None:
|
|
152
|
+
raise ValueError("adapter 不接受 invocation override;请传给 start(request)")
|
|
153
|
+
if self._adapter is not None:
|
|
154
|
+
return self._adapter
|
|
155
|
+
rt = "codex" if self._config.runtime == "codex" else "harness"
|
|
156
|
+
|
|
157
|
+
def _default_builder(runner: BaseRunner) -> RuntimeAdapter:
|
|
158
|
+
return RunnerRuntimeAdapter(runner, runtime_type=rt)
|
|
159
|
+
|
|
160
|
+
builder = self._adapter_builder or _default_builder
|
|
161
|
+
self._adapter = builder(self.build_runner())
|
|
162
|
+
return self._adapter
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def runner(self) -> BaseRunner:
|
|
166
|
+
"""The one runner owned by this Harness composition root."""
|
|
167
|
+
return self.build_runner()
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def session_service(self) -> Any:
|
|
171
|
+
return self._session_service
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def capabilities(self) -> HarnessCapabilities:
|
|
175
|
+
return self._capabilities
|
|
176
|
+
|
|
177
|
+
def _route_groups(self) -> set[str]:
|
|
178
|
+
groups = {"health_meta", "models", "feedback", "tools", "ui_bootstrap"}
|
|
179
|
+
if self._capabilities.responses:
|
|
180
|
+
groups.update({"run", "openai_compat"})
|
|
181
|
+
if self._capabilities.sessions:
|
|
182
|
+
groups.update({"sessions", "sessions_adk_compat"})
|
|
183
|
+
if self._capabilities.files:
|
|
184
|
+
groups.add("workspace")
|
|
185
|
+
if self._capabilities.agui:
|
|
186
|
+
groups.add("agui")
|
|
187
|
+
return groups
|
|
188
|
+
|
|
189
|
+
def _request_with_overrides(
|
|
190
|
+
self, request: StartRequest, overrides: Optional[dict[str, Any]]
|
|
191
|
+
) -> StartRequest:
|
|
192
|
+
metadata = dict(request.metadata or {})
|
|
193
|
+
changed = False
|
|
194
|
+
if request.model:
|
|
195
|
+
metadata["model_override"] = request.model
|
|
196
|
+
changed = True
|
|
197
|
+
request_prompt = request.config.get("prompt") or request.config.get("instructions")
|
|
198
|
+
if request_prompt is not None:
|
|
199
|
+
metadata["prompt_override"] = str(request_prompt)
|
|
200
|
+
changed = True
|
|
201
|
+
if overrides:
|
|
202
|
+
config = self.apply_overrides(overrides)
|
|
203
|
+
metadata["model_override"] = config.model
|
|
204
|
+
metadata["prompt_override"] = config.prompt
|
|
205
|
+
changed = True
|
|
206
|
+
if not changed:
|
|
207
|
+
return request
|
|
208
|
+
if hasattr(request, "model_copy"):
|
|
209
|
+
return request.model_copy(update={"metadata": metadata})
|
|
210
|
+
return request.copy(update={"metadata": metadata})
|
|
211
|
+
|
|
212
|
+
async def start(self, request: StartRequest, overrides: Optional[dict[str, Any]] = None):
|
|
213
|
+
"""yaml → RuntimeAdapter.start(request)(本层映射)。"""
|
|
214
|
+
return await self.adapter().start(self._request_with_overrides(request, overrides))
|
|
215
|
+
|
|
216
|
+
def stream(self, handle: Any):
|
|
217
|
+
return self.adapter().stream(handle)
|
|
218
|
+
|
|
219
|
+
async def cancel(self, handle: Any):
|
|
220
|
+
return await self.adapter().cancel(handle)
|
|
221
|
+
|
|
222
|
+
async def resume(self, handle: Any, target: Any, payload: Any = None):
|
|
223
|
+
return await self.adapter().resume(handle, target, payload)
|
|
224
|
+
|
|
225
|
+
async def checkpoint(self, handle: Any):
|
|
226
|
+
return await self.adapter().checkpoint(handle)
|
|
227
|
+
|
|
228
|
+
async def close(self, handle: Any):
|
|
229
|
+
return await self.adapter().close(handle)
|
|
230
|
+
|
|
231
|
+
def _configure_routes(self, app: FastAPI, state: Any, groups: set[str]) -> None:
|
|
232
|
+
"""Attach only route groups; never install integrated workspace/terminal routes."""
|
|
233
|
+
del state
|
|
234
|
+
load_route_modules()
|
|
235
|
+
ordered = sorted(group for group in groups if group != "health_meta")
|
|
236
|
+
if "health_meta" in groups:
|
|
237
|
+
ordered.append("health_meta")
|
|
238
|
+
for group in ordered:
|
|
239
|
+
router = GROUP_ROUTERS.get(group)
|
|
240
|
+
if router is not None:
|
|
241
|
+
app.include_router(router)
|
|
242
|
+
|
|
243
|
+
def build_app(self, overrides: Optional[dict[str, Any]] = None) -> FastAPI:
|
|
244
|
+
"""装配可部署 app:复用 create_runtime_app,只挂数据面 route group(控制面不进)。"""
|
|
245
|
+
if overrides is not None:
|
|
246
|
+
# Overrides belong to StartRequest and must never create a second
|
|
247
|
+
# process-wide runner for the deployed app.
|
|
248
|
+
raise ValueError("build_app 不接受 invocation override;请传给 start(request)")
|
|
249
|
+
if self._fastapi_app is not None:
|
|
250
|
+
return self._fastapi_app
|
|
251
|
+
runner = self.build_runner()
|
|
252
|
+
app = create_runtime_app(
|
|
253
|
+
RuntimeAppConfig(
|
|
254
|
+
runner=runner,
|
|
255
|
+
runtime_type="codex" if self._config.runtime == "codex" else "harness",
|
|
256
|
+
route_groups=self._route_groups(),
|
|
257
|
+
a2a=self._a2a,
|
|
258
|
+
runtime_adapter=self.adapter() if self._a2a is not None else None,
|
|
259
|
+
session_backend_provider=lambda: {
|
|
260
|
+
"Backend": "memory",
|
|
261
|
+
"Shared": False,
|
|
262
|
+
"ProductionSafe": False,
|
|
263
|
+
"ContinuityDefault": "local_only",
|
|
264
|
+
},
|
|
265
|
+
),
|
|
266
|
+
self._configure_routes,
|
|
267
|
+
)
|
|
268
|
+
app.state.runtime.runtime_adapter = self.adapter()
|
|
269
|
+
app.state.runtime.session_service = self._session_service
|
|
270
|
+
app.state.runtime.harness_capabilities = self._capabilities
|
|
271
|
+
|
|
272
|
+
for plugin in self._plugins:
|
|
273
|
+
plugin.after_build(app, self._config)
|
|
274
|
+
self._fastapi_app = app
|
|
275
|
+
return app
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
__all__ = ["HarnessApp", "HarnessCapabilities", "HarnessPlugin"]
|