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/a2a/executor.py
CHANGED
|
@@ -1,78 +1,288 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""A2ARuntimeExecutor — 把 ksadk runner 桥接进 A2A 请求生命周期 (goal-05)。
|
|
2
|
+
|
|
3
|
+
契约 §7.2:A2A ``context_id`` ↔ Runtime ``session_id``;``canceled`` ↔
|
|
4
|
+
``RuntimeAdapter.cancel(invocation_id)``。executor 不在此自造 cancel,而是委托
|
|
5
|
+
:class:`~ksadk.a2a.task_adapter.A2ARuntimeTaskAdapter`(其内部走 RuntimeAdapter.cancel,
|
|
6
|
+
G0.3 已冻结)。
|
|
7
|
+
|
|
8
|
+
wire 对象在 a2a-sdk 1.1.0 是 protobuf(``a2a_pb2``);文本用 ``Part(text=...)``,
|
|
9
|
+
消息用 ``Message(role=Role.ROLE_AGENT, parts=[...])``。
|
|
10
|
+
"""
|
|
2
11
|
|
|
3
12
|
from __future__ import annotations
|
|
4
13
|
|
|
14
|
+
import inspect
|
|
5
15
|
import json
|
|
6
16
|
import logging
|
|
7
|
-
from
|
|
8
|
-
from typing import Any
|
|
17
|
+
from typing import Any, Literal
|
|
9
18
|
|
|
10
19
|
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
11
20
|
from a2a.server.events import EventQueue
|
|
12
21
|
from a2a.server.tasks import TaskUpdater
|
|
13
|
-
from a2a.types import Part,
|
|
22
|
+
from a2a.types import Part, Task, TaskState, TaskStatus
|
|
23
|
+
from a2a.utils.errors import TaskNotCancelableError
|
|
24
|
+
|
|
25
|
+
from ksadk.a2a.resume_store import A2AResumePayloadKind
|
|
26
|
+
from ksadk.events import EventType, RuntimeEvent
|
|
27
|
+
from ksadk.runtime import CancelResult, RunHandle
|
|
14
28
|
|
|
15
29
|
logger = logging.getLogger(__name__)
|
|
16
30
|
|
|
31
|
+
#: ADK v2 A2A 集成扩展标记 URI(adk.dev/a2a/a2a-extension)。
|
|
32
|
+
#: 在 Task/status metadata 里塞入该 key 且值非空时,ADK ``RemoteA2aAgent`` 走
|
|
33
|
+
#: ``_handle_a2a_response_v2``——处理每个 ``artifact_update``(含 append=True 增量),
|
|
34
|
+
#: 不再像 v1 那样只放行首块+末块、丢弃中间增量(remote_a2a_agent.py:546/762)。
|
|
35
|
+
#: ksadk 自研 executor 必须带这个标记,否则编排侧 sub-agent 流式被压成"一次性"。
|
|
36
|
+
ADK_V2_INTEGRATION_EXTENSION_URI = "https://google.github.io/adk-docs/a2a/a2a-extension/"
|
|
17
37
|
|
|
18
|
-
|
|
19
|
-
|
|
38
|
+
#: 扩展标记值(与 google.adk ``A2aAgentExecutor._get_invocation_metadata`` 对齐)。
|
|
39
|
+
ADK_V2_INTEGRATION_METADATA: dict[str, Any] = {
|
|
40
|
+
ADK_V2_INTEGRATION_EXTENSION_URI: {"adk_agent_executor_v2": True}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_OUTPUT_SNAPSHOT_METADATA = {"ksadk_output_snapshot": True}
|
|
44
|
+
_ArtifactKind = Literal["text", "thinking"]
|
|
20
45
|
|
|
21
|
-
def __init__(self, runner: Any, prefer_stream: bool = True) -> None:
|
|
22
|
-
self.runner = runner
|
|
23
|
-
self.prefer_stream = prefer_stream
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
def _thought_part(text: str) -> Part:
|
|
48
|
+
"""构造可被 ADK RemoteA2aAgent 识别为 thought 的 A2A Part。"""
|
|
49
|
+
return Part(text=text, metadata={"adk_thought": True})
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class _ArtifactStreamEmitter:
|
|
53
|
+
"""按连续类型分段输出 A2A artifact,并正确终止每个 artifact stream。"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, updater: TaskUpdater, task_id: str) -> None:
|
|
56
|
+
self._updater = updater
|
|
57
|
+
self._task_id = task_id
|
|
58
|
+
self._active_kind: _ArtifactKind | None = None
|
|
59
|
+
self._segments: dict[_ArtifactKind, int] = {"text": 0, "thinking": 0}
|
|
60
|
+
self._emitted: dict[tuple[_ArtifactKind, int], int] = {}
|
|
61
|
+
self._pending: tuple[_ArtifactKind, str, bool, int] | None = None
|
|
62
|
+
|
|
63
|
+
async def push(
|
|
26
64
|
self,
|
|
27
|
-
|
|
28
|
-
|
|
65
|
+
kind: _ArtifactKind,
|
|
66
|
+
text: str,
|
|
67
|
+
*,
|
|
68
|
+
replace_snapshot: bool = False,
|
|
69
|
+
) -> None:
|
|
70
|
+
if self._pending is not None:
|
|
71
|
+
switched = self._pending[0] != kind
|
|
72
|
+
await self._emit(self._pending, last_chunk=switched)
|
|
73
|
+
if self._active_kind != kind:
|
|
74
|
+
self._segments[kind] += 1
|
|
75
|
+
self._active_kind = kind
|
|
76
|
+
self._pending = (kind, text, replace_snapshot, self._segments[kind])
|
|
77
|
+
|
|
78
|
+
async def close(self) -> None:
|
|
79
|
+
if self._pending is None:
|
|
80
|
+
return
|
|
81
|
+
await self._emit(self._pending, last_chunk=True)
|
|
82
|
+
self._pending = None
|
|
83
|
+
|
|
84
|
+
async def _emit(
|
|
85
|
+
self,
|
|
86
|
+
pending: tuple[_ArtifactKind, str, bool, int],
|
|
87
|
+
*,
|
|
88
|
+
last_chunk: bool,
|
|
89
|
+
) -> None:
|
|
90
|
+
kind, text, replace_snapshot, segment = pending
|
|
91
|
+
key = (kind, segment)
|
|
92
|
+
self._emitted[key] = self._emitted.get(key, 0) + 1
|
|
93
|
+
is_reasoning = kind == "thinking"
|
|
94
|
+
base_id = (
|
|
95
|
+
f"{self._task_id}-reasoning"
|
|
96
|
+
if is_reasoning
|
|
97
|
+
else f"{self._task_id}-response"
|
|
98
|
+
)
|
|
99
|
+
artifact_id = base_id if segment == 1 else f"{base_id}-{segment}"
|
|
100
|
+
part = (
|
|
101
|
+
_thought_part(text)
|
|
102
|
+
if is_reasoning
|
|
103
|
+
else Part(
|
|
104
|
+
text=text,
|
|
105
|
+
metadata=(
|
|
106
|
+
dict(_OUTPUT_SNAPSHOT_METADATA)
|
|
107
|
+
if replace_snapshot
|
|
108
|
+
else None
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
await self._updater.add_artifact(
|
|
113
|
+
parts=[part],
|
|
114
|
+
artifact_id=artifact_id,
|
|
115
|
+
name="reasoning" if is_reasoning else "response",
|
|
116
|
+
append=False if replace_snapshot else self._emitted[key] > 1,
|
|
117
|
+
last_chunk=last_chunk,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def _enqueue_initial_task(context: RequestContext, event_queue: EventQueue) -> None:
|
|
122
|
+
"""先入队初始 ``Task`` 对象,再发状态更新(a2a-sdk 1.1.0 生命周期要求:
|
|
123
|
+
TaskStatusUpdateEvent 之前必须已有 Task)。``current_task`` 为续跑任务时直接用,
|
|
124
|
+
否则新建 submitted 任务。Task metadata 带 ADK v2 扩展标记,让 RemoteA2aAgent
|
|
125
|
+
走 v2 handler 保留全部流式增量。
|
|
126
|
+
"""
|
|
127
|
+
task = getattr(context, "current_task", None)
|
|
128
|
+
if task is None:
|
|
129
|
+
task = Task(
|
|
130
|
+
id=context.task_id,
|
|
131
|
+
context_id=context.context_id,
|
|
132
|
+
status=TaskStatus(state=TaskState.TASK_STATE_SUBMITTED),
|
|
133
|
+
metadata=dict(ADK_V2_INTEGRATION_METADATA),
|
|
134
|
+
)
|
|
135
|
+
await event_queue.enqueue_event(task)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class _InputRequired(Exception):
|
|
139
|
+
"""runner 请求用户输入的信号(内部用于跳出 execute,task 停 input-required)。"""
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class _RunCanceled(Exception):
|
|
143
|
+
"""Runtime 已取消本次执行,executor 不得再发 completed。"""
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class A2ARuntimeExecutor(AgentExecutor):
|
|
147
|
+
"""在 A2A 请求生命周期内执行 ksadk runner。
|
|
148
|
+
|
|
149
|
+
- sync: ``runner.invoke``。
|
|
150
|
+
- streaming: ``runner.stream``,逐 chunk 发 artifact。
|
|
151
|
+
- cancel: 委托 ``task_adapter.cancel_task``(内部走 RuntimeAdapter.cancel)。
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
def __init__(
|
|
155
|
+
self,
|
|
156
|
+
runner: Any,
|
|
157
|
+
task_adapter: Any = None,
|
|
158
|
+
prefer_stream: bool = True,
|
|
159
|
+
include_reasoning: bool = False,
|
|
29
160
|
) -> None:
|
|
161
|
+
self.runner = runner
|
|
162
|
+
self.task_adapter = task_adapter
|
|
163
|
+
self.prefer_stream = prefer_stream
|
|
164
|
+
self.include_reasoning = include_reasoning
|
|
165
|
+
|
|
166
|
+
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
167
|
+
current_task = getattr(context, "current_task", None)
|
|
168
|
+
durable_context_id = str(
|
|
169
|
+
getattr(current_task, "context_id", "") or context.context_id or "unknown-context"
|
|
170
|
+
)
|
|
30
171
|
updater = TaskUpdater(
|
|
31
172
|
event_queue=event_queue,
|
|
32
173
|
task_id=context.task_id or "unknown-task",
|
|
33
|
-
context_id=
|
|
174
|
+
context_id=durable_context_id,
|
|
175
|
+
)
|
|
176
|
+
is_resume = (
|
|
177
|
+
current_task is not None
|
|
178
|
+
and getattr(getattr(current_task, "status", None), "state", None)
|
|
179
|
+
== TaskState.TASK_STATE_INPUT_REQUIRED
|
|
34
180
|
)
|
|
181
|
+
interaction_response: Any = None
|
|
182
|
+
if self.task_adapter is not None:
|
|
183
|
+
# Third-party/local adapters written before durable context mapping
|
|
184
|
+
# do not necessarily provide this optional lifecycle hook.
|
|
185
|
+
prepare_context = getattr(self.task_adapter, "prepare_context", None)
|
|
186
|
+
if callable(prepare_context):
|
|
187
|
+
await prepare_context(context)
|
|
188
|
+
if is_resume and self.task_adapter is not None:
|
|
189
|
+
interaction_response = self.task_adapter.answer_from_context(context)
|
|
190
|
+
# Invalid resume tokens/decisions are request errors. Validate before emitting
|
|
191
|
+
# working so the durable Task remains input-required and retryable.
|
|
192
|
+
await self.task_adapter.validate_resume_task(
|
|
193
|
+
context.task_id or "",
|
|
194
|
+
context,
|
|
195
|
+
answer=interaction_response,
|
|
196
|
+
)
|
|
35
197
|
|
|
198
|
+
handle: RunHandle | None = None
|
|
36
199
|
try:
|
|
37
|
-
|
|
200
|
+
# §7.2:当前任务处于 input-required 时,本条消息是续跑回包(checkpoint/resume)。
|
|
201
|
+
if not is_resume:
|
|
202
|
+
await _enqueue_initial_task(context, event_queue)
|
|
203
|
+
# start_work 不透传 metadata;直接 update_status 以带 ADK v2 扩展标记
|
|
204
|
+
# (RemoteA2aAgent 读 task/status metadata 决定走 v2 全增量 handler)。
|
|
205
|
+
await updater.update_status(
|
|
206
|
+
TaskState.TASK_STATE_WORKING,
|
|
207
|
+
metadata=dict(ADK_V2_INTEGRATION_METADATA),
|
|
208
|
+
)
|
|
38
209
|
runner_input = self._build_runner_input(context)
|
|
39
|
-
|
|
210
|
+
if is_resume:
|
|
211
|
+
if self.task_adapter is None:
|
|
212
|
+
raise RuntimeError("runtime task adapter is required to resume A2A task")
|
|
213
|
+
handle = await self.task_adapter.resume_task(
|
|
214
|
+
context.task_id or "",
|
|
215
|
+
context,
|
|
216
|
+
answer=interaction_response,
|
|
217
|
+
)
|
|
218
|
+
output = await self._run_runtime(context, updater, handle)
|
|
219
|
+
elif self.task_adapter is not None:
|
|
220
|
+
handle = await self.task_adapter.start_task(
|
|
221
|
+
task_id=str(context.task_id or ""),
|
|
222
|
+
context=context,
|
|
223
|
+
input_data=runner_input.get("input"),
|
|
224
|
+
)
|
|
225
|
+
output = await self._run_runtime(context, updater, handle)
|
|
226
|
+
else:
|
|
227
|
+
output = await self._run_runner(context, updater, runner_input)
|
|
228
|
+
# completed 携带全文消息:非流式消费端与 text.completed 投影(§ event_adapter
|
|
229
|
+
# message_to_event final)依赖它拿最终结果;流式消费端的重复由 adk_runner
|
|
230
|
+
# 在 handoff 分支对"增量累积"去重解决(不在此处删消息)。
|
|
40
231
|
completion_message = (
|
|
41
|
-
updater.new_agent_message(parts=[Part(
|
|
42
|
-
if output
|
|
43
|
-
else None
|
|
232
|
+
updater.new_agent_message(parts=[Part(text=output)]) if output else None
|
|
44
233
|
)
|
|
45
234
|
await updater.complete(message=completion_message)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
235
|
+
await self._forget_task(context, handle)
|
|
236
|
+
except _InputRequired:
|
|
237
|
+
# runner 请求输入:task 已停在 input-required(附 resume token),不 complete。
|
|
238
|
+
logger.info("A2A task %s 进入 input-required", context.task_id)
|
|
239
|
+
except _RunCanceled:
|
|
240
|
+
logger.info("A2A task %s 已由 runtime 取消", context.task_id)
|
|
241
|
+
await self._forget_task(context, handle)
|
|
242
|
+
except Exception as exc: # noqa: BLE001
|
|
243
|
+
logger.error("A2A task execution failed (%s)", type(exc).__name__)
|
|
49
244
|
await updater.failed(
|
|
50
|
-
message=updater.new_agent_message(
|
|
51
|
-
parts=[Part(root=TextPart(text=error_text))]
|
|
52
|
-
)
|
|
245
|
+
message=updater.new_agent_message(parts=[Part(text="A2A task execution failed")])
|
|
53
246
|
)
|
|
247
|
+
await self._forget_task(context, handle)
|
|
54
248
|
|
|
55
|
-
async def cancel(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
249
|
+
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
250
|
+
# §7.4:cancel 统一由 adapter 提供。有 RuntimeAdapter → 尊重其 CancelResult,
|
|
251
|
+
# 只有底层真取消(CANCELLED)才把协议 Task 置 canceled;其余状态如实抛
|
|
252
|
+
# TaskNotCancelableError 拒绝,不伪造终态。无 adapter 时底层状态未知,
|
|
253
|
+
# 同样拒绝取消。
|
|
254
|
+
current_task = getattr(context, "current_task", None)
|
|
60
255
|
updater = TaskUpdater(
|
|
61
256
|
event_queue=event_queue,
|
|
62
257
|
task_id=context.task_id or "unknown-task",
|
|
63
|
-
context_id=
|
|
258
|
+
context_id=str(
|
|
259
|
+
getattr(current_task, "context_id", "") or context.context_id or "unknown-context"
|
|
260
|
+
),
|
|
64
261
|
)
|
|
65
|
-
|
|
66
|
-
message=
|
|
67
|
-
|
|
262
|
+
if self.task_adapter is None:
|
|
263
|
+
raise TaskNotCancelableError(message="runtime task adapter is not configured")
|
|
264
|
+
result = await self.task_adapter.cancel_task(context.task_id or "", context)
|
|
265
|
+
# 只有底层真的接受了取消(已中断活跃 turn,或已登记 pending cancel)才把
|
|
266
|
+
# 协议 Task 置 canceled;其他结果如实拒绝,包括未来新增的 UNSUPPORTED。
|
|
267
|
+
if result not in (
|
|
268
|
+
CancelResult.INTERRUPTED_ACTIVE_TURN,
|
|
269
|
+
CancelResult.PENDING_CANCEL_RECORDED,
|
|
270
|
+
):
|
|
271
|
+
result_value = getattr(result, "value", result)
|
|
272
|
+
raise TaskNotCancelableError(
|
|
273
|
+
message=f"underlying runtime cancel did not apply: {result_value}"
|
|
68
274
|
)
|
|
275
|
+
await updater.cancel(
|
|
276
|
+
message=updater.new_agent_message(parts=[Part(text="Request canceled")])
|
|
69
277
|
)
|
|
278
|
+
clear_resume_state = getattr(self.task_adapter, "clear_resume_state", None)
|
|
279
|
+
if callable(clear_resume_state):
|
|
280
|
+
result = clear_resume_state(context.task_id or "", context)
|
|
281
|
+
if inspect.isawaitable(result):
|
|
282
|
+
await result
|
|
70
283
|
|
|
71
284
|
async def _run_runner(
|
|
72
|
-
self,
|
|
73
|
-
context: RequestContext,
|
|
74
|
-
updater: TaskUpdater,
|
|
75
|
-
runner_input: dict[str, Any],
|
|
285
|
+
self, context: RequestContext, updater: TaskUpdater, runner_input: dict[str, Any]
|
|
76
286
|
) -> str:
|
|
77
287
|
stream = getattr(self.runner, "stream", None)
|
|
78
288
|
if self.prefer_stream and callable(stream):
|
|
@@ -82,7 +292,7 @@ class KsAgentExecutor(AgentExecutor):
|
|
|
82
292
|
text = self._coerce_text(result)
|
|
83
293
|
if text:
|
|
84
294
|
await updater.add_artifact(
|
|
85
|
-
parts=[Part(
|
|
295
|
+
parts=[Part(text=text)],
|
|
86
296
|
artifact_id=f"{context.task_id}-response",
|
|
87
297
|
name="response",
|
|
88
298
|
last_chunk=True,
|
|
@@ -97,85 +307,53 @@ class KsAgentExecutor(AgentExecutor):
|
|
|
97
307
|
runner_input: dict[str, Any],
|
|
98
308
|
) -> str:
|
|
99
309
|
output_text = ""
|
|
100
|
-
|
|
101
|
-
artifact_id = f"{context.task_id}-response"
|
|
310
|
+
artifacts = _ArtifactStreamEmitter(updater, str(context.task_id))
|
|
102
311
|
|
|
103
|
-
async for chunk
|
|
312
|
+
async for chunk in stream(runner_input):
|
|
104
313
|
chunk_type = chunk.get("type") if isinstance(chunk, dict) else None
|
|
314
|
+
if chunk_type == "input_required":
|
|
315
|
+
raise RuntimeError(
|
|
316
|
+
"runner emitted input_required without a RuntimeAdapter execution path"
|
|
317
|
+
)
|
|
105
318
|
if chunk_type == "final":
|
|
106
319
|
final_text = (
|
|
107
|
-
self._coerce_text(chunk.get("output"))
|
|
108
|
-
if isinstance(chunk, dict)
|
|
109
|
-
else ""
|
|
320
|
+
self._coerce_text(chunk.get("output")) if isinstance(chunk, dict) else ""
|
|
110
321
|
)
|
|
111
322
|
if not final_text:
|
|
112
323
|
continue
|
|
113
324
|
if not output_text:
|
|
114
325
|
output_text = final_text
|
|
326
|
+
await artifacts.push("text", final_text)
|
|
115
327
|
elif final_text.startswith(output_text):
|
|
116
328
|
suffix = final_text[len(output_text) :]
|
|
117
329
|
output_text = final_text
|
|
118
330
|
if suffix:
|
|
119
|
-
|
|
120
|
-
await updater.add_artifact(
|
|
121
|
-
parts=[Part(root=TextPart(text=suffix))],
|
|
122
|
-
artifact_id=artifact_id,
|
|
123
|
-
name="response",
|
|
124
|
-
append=bool(emitted_chunks > 1),
|
|
125
|
-
last_chunk=last_chunk,
|
|
126
|
-
)
|
|
331
|
+
await artifacts.push("text", suffix)
|
|
127
332
|
continue
|
|
128
333
|
else:
|
|
129
334
|
output_text = final_text
|
|
130
|
-
await
|
|
131
|
-
parts=[Part(root=TextPart(text=final_text))],
|
|
132
|
-
artifact_id=artifact_id,
|
|
133
|
-
name="response",
|
|
134
|
-
append=False,
|
|
135
|
-
last_chunk=last_chunk,
|
|
136
|
-
)
|
|
335
|
+
await artifacts.push("text", final_text, replace_snapshot=True)
|
|
137
336
|
continue
|
|
138
337
|
|
|
139
338
|
text = self._coerce_text(chunk)
|
|
140
339
|
if not text:
|
|
141
340
|
continue
|
|
341
|
+
if chunk_type == "thinking":
|
|
342
|
+
if self.include_reasoning:
|
|
343
|
+
await artifacts.push("thinking", text)
|
|
344
|
+
continue
|
|
142
345
|
output_text += text
|
|
143
|
-
|
|
144
|
-
await updater.add_artifact(
|
|
145
|
-
parts=[Part(root=TextPart(text=text))],
|
|
146
|
-
artifact_id=artifact_id,
|
|
147
|
-
name="response",
|
|
148
|
-
append=bool(emitted_chunks > 1),
|
|
149
|
-
last_chunk=last_chunk,
|
|
150
|
-
)
|
|
346
|
+
await artifacts.push("text", text)
|
|
151
347
|
|
|
348
|
+
await artifacts.close()
|
|
152
349
|
return output_text
|
|
153
350
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
iterator: AsyncIterator[Any],
|
|
157
|
-
) -> AsyncIterator[tuple[Any, bool]]:
|
|
158
|
-
try:
|
|
159
|
-
previous = await anext(iterator)
|
|
160
|
-
except StopAsyncIteration:
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
while True:
|
|
164
|
-
try:
|
|
165
|
-
current = await anext(iterator)
|
|
166
|
-
except StopAsyncIteration:
|
|
167
|
-
yield previous, True
|
|
168
|
-
return
|
|
169
|
-
yield previous, False
|
|
170
|
-
previous = current
|
|
171
|
-
|
|
172
|
-
@staticmethod
|
|
173
|
-
def _build_runner_input(context: RequestContext) -> dict[str, Any]:
|
|
174
|
-
metadata = dict(context.metadata)
|
|
351
|
+
def _build_runner_input(self, context: RequestContext) -> dict[str, Any]:
|
|
352
|
+
metadata = dict(getattr(context, "metadata", None) or {})
|
|
175
353
|
state = metadata.get("state", {})
|
|
176
354
|
if not isinstance(state, dict):
|
|
177
355
|
state = {}
|
|
178
|
-
|
|
356
|
+
# §7.2: A2A context_id ↔ Runtime session_id。
|
|
179
357
|
return {
|
|
180
358
|
"input": context.get_user_input(),
|
|
181
359
|
"task_id": context.task_id,
|
|
@@ -186,6 +364,142 @@ class KsAgentExecutor(AgentExecutor):
|
|
|
186
364
|
"metadata": metadata,
|
|
187
365
|
}
|
|
188
366
|
|
|
367
|
+
async def _run_runtime(
|
|
368
|
+
self,
|
|
369
|
+
context: RequestContext,
|
|
370
|
+
updater: TaskUpdater,
|
|
371
|
+
handle: RunHandle,
|
|
372
|
+
) -> str:
|
|
373
|
+
output_text = ""
|
|
374
|
+
artifacts = _ArtifactStreamEmitter(updater, str(context.task_id))
|
|
375
|
+
reasoning_text = ""
|
|
376
|
+
input_required = False
|
|
377
|
+
input_prompt = "Input required"
|
|
378
|
+
checkpoint_id: str | None = None
|
|
379
|
+
call_id: str | None = None
|
|
380
|
+
payload_kind: A2AResumePayloadKind = "hitl_answer"
|
|
381
|
+
|
|
382
|
+
async for event in self.task_adapter.stream_task(handle):
|
|
383
|
+
if not isinstance(event, RuntimeEvent):
|
|
384
|
+
raise TypeError("RuntimeAdapter.stream must yield RuntimeEvent")
|
|
385
|
+
if event.event_type == EventType.RUN_FAILED:
|
|
386
|
+
raise RuntimeError(self._coerce_text(event.payload.get("error")))
|
|
387
|
+
if event.event_type == EventType.RUN_CANCELED:
|
|
388
|
+
await artifacts.close()
|
|
389
|
+
if not self._cancel_was_accepted(context, handle):
|
|
390
|
+
await updater.cancel(
|
|
391
|
+
message=updater.new_agent_message(parts=[Part(text="Request canceled")])
|
|
392
|
+
)
|
|
393
|
+
raise _RunCanceled()
|
|
394
|
+
if event.event_type == EventType.APPROVAL_REQUESTED:
|
|
395
|
+
input_required = True
|
|
396
|
+
payload_kind = "approval_decision"
|
|
397
|
+
call_id = (
|
|
398
|
+
str(event.payload.get("call_id") or event.payload.get("approval_id") or "")
|
|
399
|
+
or None
|
|
400
|
+
)
|
|
401
|
+
detail = event.payload.get("detail")
|
|
402
|
+
if isinstance(detail, dict):
|
|
403
|
+
input_prompt = self._coerce_text(
|
|
404
|
+
detail.get("prompt") or detail.get("message") or input_prompt
|
|
405
|
+
)
|
|
406
|
+
continue
|
|
407
|
+
if event.event_type == EventType.CHECKPOINT_CREATED:
|
|
408
|
+
checkpoint_id = str(event.payload.get("checkpoint_id") or "") or None
|
|
409
|
+
continue
|
|
410
|
+
if event.event_type == EventType.RUN_INTERRUPTED:
|
|
411
|
+
input_required = True
|
|
412
|
+
input_prompt = self._coerce_text(
|
|
413
|
+
event.payload.get("prompt") or event.payload.get("message") or input_prompt
|
|
414
|
+
)
|
|
415
|
+
continue
|
|
416
|
+
if event.event_type not in {
|
|
417
|
+
EventType.TEXT_DELTA,
|
|
418
|
+
EventType.TEXT_COMPLETED,
|
|
419
|
+
EventType.REASONING_DELTA,
|
|
420
|
+
EventType.REASONING_COMPLETED,
|
|
421
|
+
}:
|
|
422
|
+
continue
|
|
423
|
+
text = self._coerce_text(event.payload.get("text"))
|
|
424
|
+
if not text:
|
|
425
|
+
continue
|
|
426
|
+
if event.event_type == EventType.REASONING_COMPLETED:
|
|
427
|
+
if not self.include_reasoning:
|
|
428
|
+
continue
|
|
429
|
+
if not reasoning_text:
|
|
430
|
+
delta = text
|
|
431
|
+
reasoning_text = text
|
|
432
|
+
elif text.startswith(reasoning_text):
|
|
433
|
+
delta = text[len(reasoning_text) :]
|
|
434
|
+
reasoning_text = text
|
|
435
|
+
else:
|
|
436
|
+
delta = text
|
|
437
|
+
reasoning_text += text
|
|
438
|
+
if delta:
|
|
439
|
+
await artifacts.push("thinking", delta)
|
|
440
|
+
continue
|
|
441
|
+
if event.event_type == EventType.REASONING_DELTA:
|
|
442
|
+
if not self.include_reasoning:
|
|
443
|
+
continue
|
|
444
|
+
reasoning_text += text
|
|
445
|
+
await artifacts.push("thinking", text)
|
|
446
|
+
continue
|
|
447
|
+
# TEXT_COMPLETED 是累计全文,去重只发新增 suffix;TEXT_DELTA 是增量直接透传。
|
|
448
|
+
if event.event_type == EventType.TEXT_COMPLETED:
|
|
449
|
+
if not output_text:
|
|
450
|
+
delta = text
|
|
451
|
+
output_text = text
|
|
452
|
+
replace_snapshot = False
|
|
453
|
+
elif text.startswith(output_text):
|
|
454
|
+
delta = text[len(output_text) :]
|
|
455
|
+
output_text = text
|
|
456
|
+
replace_snapshot = False
|
|
457
|
+
else:
|
|
458
|
+
delta = text
|
|
459
|
+
output_text = text
|
|
460
|
+
replace_snapshot = True
|
|
461
|
+
else:
|
|
462
|
+
delta = text
|
|
463
|
+
output_text += text
|
|
464
|
+
replace_snapshot = False
|
|
465
|
+
if not delta:
|
|
466
|
+
continue
|
|
467
|
+
await artifacts.push("text", delta, replace_snapshot=replace_snapshot)
|
|
468
|
+
if self._cancel_was_accepted(context, handle):
|
|
469
|
+
await artifacts.close()
|
|
470
|
+
raise _RunCanceled()
|
|
471
|
+
await artifacts.close()
|
|
472
|
+
if input_required:
|
|
473
|
+
await self.task_adapter.persist_resume_state(
|
|
474
|
+
task_id=str(context.task_id or ""),
|
|
475
|
+
context=context,
|
|
476
|
+
handle=handle,
|
|
477
|
+
checkpoint_id=checkpoint_id,
|
|
478
|
+
call_id=call_id,
|
|
479
|
+
payload_kind=payload_kind,
|
|
480
|
+
)
|
|
481
|
+
await updater.update_status(
|
|
482
|
+
TaskState.TASK_STATE_INPUT_REQUIRED,
|
|
483
|
+
message=updater.new_agent_message(parts=[Part(text=input_prompt)]),
|
|
484
|
+
metadata=dict(ADK_V2_INTEGRATION_METADATA),
|
|
485
|
+
)
|
|
486
|
+
raise _InputRequired()
|
|
487
|
+
return output_text
|
|
488
|
+
|
|
489
|
+
def _cancel_was_accepted(self, context: RequestContext, handle: RunHandle) -> bool:
|
|
490
|
+
was_cancel_accepted = getattr(self.task_adapter, "was_cancel_accepted", None)
|
|
491
|
+
return bool(
|
|
492
|
+
callable(was_cancel_accepted)
|
|
493
|
+
and was_cancel_accepted(str(context.task_id or ""), context, handle)
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
async def _forget_task(self, context: RequestContext, handle: RunHandle | None) -> None:
|
|
497
|
+
forget_task = getattr(self.task_adapter, "forget_task", None)
|
|
498
|
+
if handle is not None and callable(forget_task):
|
|
499
|
+
result = forget_task(str(context.task_id or ""), context, handle)
|
|
500
|
+
if inspect.isawaitable(result):
|
|
501
|
+
await result
|
|
502
|
+
|
|
189
503
|
@classmethod
|
|
190
504
|
def _coerce_text(cls, payload: Any) -> str:
|
|
191
505
|
if payload is None:
|
|
@@ -199,3 +513,6 @@ class KsAgentExecutor(AgentExecutor):
|
|
|
199
513
|
return cls._coerce_text(value)
|
|
200
514
|
return json.dumps(payload, ensure_ascii=False, default=str)
|
|
201
515
|
return str(payload)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
__all__ = ["A2ARuntimeExecutor"]
|