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/runners/adk_runner.py
CHANGED
|
@@ -15,11 +15,11 @@ from dataclasses import dataclass
|
|
|
15
15
|
from importlib.metadata import PackageNotFoundError
|
|
16
16
|
from importlib.metadata import version as _pkg_version
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Any, AsyncIterator, Dict, Mapping, Optional
|
|
18
|
+
from typing import Any, AsyncIterator, Dict, Mapping, Optional, cast
|
|
19
19
|
|
|
20
|
-
from google.genai import types
|
|
21
20
|
from opentelemetry import trace
|
|
22
21
|
|
|
22
|
+
from ksadk.compat.adk_compat import genai_types as types
|
|
23
23
|
from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
|
|
24
24
|
from ksadk.conversations.model_context import supports_native_image_input
|
|
25
25
|
from ksadk.runners.base_runner import BaseRunner
|
|
@@ -31,22 +31,36 @@ logger = logging.getLogger(__name__)
|
|
|
31
31
|
tracer = trace.get_tracer(__name__)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def _part_metadata_flag(part: Any, key: str) -> bool:
|
|
35
|
+
"""读取 A2A→GenAI Part 保留下来的扩展 metadata 标记。"""
|
|
36
|
+
metadata = getattr(part, "part_metadata", None)
|
|
37
|
+
if isinstance(metadata, Mapping):
|
|
38
|
+
return bool(metadata.get(key))
|
|
39
|
+
getter = getattr(metadata, "get", None)
|
|
40
|
+
if callable(getter):
|
|
41
|
+
try:
|
|
42
|
+
return bool(getter(key))
|
|
43
|
+
except (KeyError, TypeError, ValueError):
|
|
44
|
+
return False
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
34
48
|
class ADKRunner(BaseRunner):
|
|
35
49
|
"""ADK 框架运行时"""
|
|
36
50
|
|
|
37
51
|
def __init__(self, detection_result: Any, project_dir: str):
|
|
38
52
|
super().__init__(detection_result, project_dir)
|
|
39
|
-
self._runner = None
|
|
40
|
-
self._session_service = None
|
|
53
|
+
self._runner: Any = None
|
|
54
|
+
self._session_service: Any = None
|
|
41
55
|
# Map external session_ids (e.g. from run_interactive or web) to ADK internal session IDs
|
|
42
56
|
self._session_map: Dict[str, str] = {}
|
|
43
57
|
# Fallback default session
|
|
44
58
|
self._default_session_id: Optional[str] = None
|
|
45
59
|
# Memory integration
|
|
46
|
-
self._short_term_memory = None
|
|
47
|
-
self._long_term_memory = None
|
|
60
|
+
self._short_term_memory: Any = None
|
|
61
|
+
self._long_term_memory: Any = None
|
|
48
62
|
# Knowledge base integration
|
|
49
|
-
self._knowledge_base = None
|
|
63
|
+
self._knowledge_base: Any = None
|
|
50
64
|
# Keep runtime toolsets alive for the lifetime of the runner.
|
|
51
65
|
self._runtime_toolsets: list[Any] = []
|
|
52
66
|
# ADK resumability state
|
|
@@ -55,7 +69,7 @@ class ADKRunner(BaseRunner):
|
|
|
55
69
|
# P1.1 sub-issue: guard invocation_map read-modify-write so concurrent
|
|
56
70
|
# invocations on the same session don't lose each other's mappings.
|
|
57
71
|
self._invocation_map_lock = asyncio.Lock()
|
|
58
|
-
self._module = None
|
|
72
|
+
self._module: Any = None
|
|
59
73
|
self._adk_resume_min_version = os.environ.get(
|
|
60
74
|
"GOOGLE_ADK_RESUME_MIN_VERSION", "1.16.0"
|
|
61
75
|
).strip()
|
|
@@ -98,7 +112,9 @@ class ADKRunner(BaseRunner):
|
|
|
98
112
|
import inspect as _inspect
|
|
99
113
|
import json as _stdlib_json
|
|
100
114
|
|
|
101
|
-
|
|
115
|
+
from ksadk.compat.adk_compat import lite_llm_module
|
|
116
|
+
|
|
117
|
+
adk_lite_llm = lite_llm_module()
|
|
102
118
|
|
|
103
119
|
_original_fn = adk_lite_llm._message_to_generate_content_response
|
|
104
120
|
if getattr(_original_fn, "__ksadk_json_patch__", False):
|
|
@@ -106,9 +122,7 @@ class ADKRunner(BaseRunner):
|
|
|
106
122
|
# Determine which kwargs the original function actually accepts,
|
|
107
123
|
# so the patch is compatible across ADK versions that may have
|
|
108
124
|
# added or removed `model_version` / `thought_parts`.
|
|
109
|
-
_orig_params = set(
|
|
110
|
-
_inspect.signature(_original_fn).parameters.keys()
|
|
111
|
-
)
|
|
125
|
+
_orig_params = set(_inspect.signature(_original_fn).parameters.keys())
|
|
112
126
|
|
|
113
127
|
def _patched_message_to_generate_content_response(
|
|
114
128
|
message, *, is_partial=False, model_version=None, thought_parts=None
|
|
@@ -129,8 +143,8 @@ class ADKRunner(BaseRunner):
|
|
|
129
143
|
"ADKRunner: caught JSONDecodeError in args parsing, "
|
|
130
144
|
"returning empty response"
|
|
131
145
|
)
|
|
132
|
-
from
|
|
133
|
-
from
|
|
146
|
+
from ksadk.compat.adk_compat import LlmResponse
|
|
147
|
+
from ksadk.compat.adk_compat import genai_types as _genai_types
|
|
134
148
|
|
|
135
149
|
return LlmResponse(
|
|
136
150
|
content=_genai_types.Content(role="model", parts=[]),
|
|
@@ -149,15 +163,11 @@ class ADKRunner(BaseRunner):
|
|
|
149
163
|
for i, part in enumerate(result.content.parts):
|
|
150
164
|
if part.function_call and part.function_call.args is None:
|
|
151
165
|
part.function_call.args = {}
|
|
152
|
-
if (
|
|
153
|
-
part.function_call
|
|
154
|
-
and not (part.function_call.name or "").strip()
|
|
155
|
-
):
|
|
166
|
+
if part.function_call and not (part.function_call.name or "").strip():
|
|
156
167
|
phantom_indices.add(i)
|
|
157
168
|
if phantom_indices:
|
|
158
169
|
result.content.parts = [
|
|
159
|
-
p for i, p in enumerate(result.content.parts)
|
|
160
|
-
if i not in phantom_indices
|
|
170
|
+
p for i, p in enumerate(result.content.parts) if i not in phantom_indices
|
|
161
171
|
]
|
|
162
172
|
return result
|
|
163
173
|
|
|
@@ -180,7 +190,6 @@ class ADKRunner(BaseRunner):
|
|
|
180
190
|
except Exception:
|
|
181
191
|
pass
|
|
182
192
|
|
|
183
|
-
|
|
184
193
|
def _apply_mcp_result_patch(self):
|
|
185
194
|
"""Patch ADK McpTool to convert CallToolResult to dict.
|
|
186
195
|
|
|
@@ -192,17 +201,17 @@ class ADKRunner(BaseRunner):
|
|
|
192
201
|
for the old version.
|
|
193
202
|
"""
|
|
194
203
|
try:
|
|
195
|
-
from
|
|
204
|
+
from ksadk.compat.adk_compat import McpTool
|
|
196
205
|
|
|
197
206
|
_original_run_async = McpTool._run_async_impl
|
|
198
207
|
if getattr(_original_run_async, "__ksadk_mcp_result_patch__", False):
|
|
199
208
|
return
|
|
200
209
|
|
|
201
|
-
async def _patched_run_async_impl(
|
|
202
|
-
self, *, args, tool_context, credential
|
|
203
|
-
):
|
|
210
|
+
async def _patched_run_async_impl(self, *, args, tool_context, credential):
|
|
204
211
|
response = await _original_run_async(
|
|
205
|
-
self,
|
|
212
|
+
self,
|
|
213
|
+
args=args,
|
|
214
|
+
tool_context=tool_context,
|
|
206
215
|
credential=credential,
|
|
207
216
|
)
|
|
208
217
|
# If response is a Pydantic model (e.g. CallToolResult),
|
|
@@ -224,7 +233,6 @@ class ADKRunner(BaseRunner):
|
|
|
224
233
|
except Exception as exc:
|
|
225
234
|
logger.debug("ADKRunner: MCP result patch failed: %s", exc)
|
|
226
235
|
|
|
227
|
-
|
|
228
236
|
def _init_short_term_memory(self):
|
|
229
237
|
"""从环境变量初始化短期记忆
|
|
230
238
|
|
|
@@ -268,8 +276,7 @@ class ADKRunner(BaseRunner):
|
|
|
268
276
|
def describe_checkpoint_capability(self) -> dict[str, Any]:
|
|
269
277
|
resumable = getattr(self, "_resumable", False)
|
|
270
278
|
stm_backend = (
|
|
271
|
-
getattr(self._short_term_memory, "backend", None)
|
|
272
|
-
if self._short_term_memory else None
|
|
279
|
+
getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
|
|
273
280
|
)
|
|
274
281
|
if resumable:
|
|
275
282
|
backend = "adk_invocation"
|
|
@@ -312,8 +319,7 @@ class ADKRunner(BaseRunner):
|
|
|
312
319
|
capabilities = super().get_runtime_capabilities()
|
|
313
320
|
resumable = getattr(self, "_resumable", False)
|
|
314
321
|
stm_backend = (
|
|
315
|
-
getattr(self._short_term_memory, "backend", None)
|
|
316
|
-
if self._short_term_memory else None
|
|
322
|
+
getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
|
|
317
323
|
)
|
|
318
324
|
is_durable = stm_backend is not None and stm_backend != "local"
|
|
319
325
|
if resumable:
|
|
@@ -352,9 +358,11 @@ class ADKRunner(BaseRunner):
|
|
|
352
358
|
"Supported": True,
|
|
353
359
|
"Type": "native_session" if has_native_session else "semantic_replay",
|
|
354
360
|
"Level": "semantic",
|
|
355
|
-
"Reason":
|
|
356
|
-
|
|
357
|
-
|
|
361
|
+
"Reason": (
|
|
362
|
+
"ADK native session can continue conversation context"
|
|
363
|
+
if has_native_session
|
|
364
|
+
else "conversation transcript can be replayed"
|
|
365
|
+
),
|
|
358
366
|
}
|
|
359
367
|
capabilities["ResumeRun"]["Reason"] = capabilities["Checkpoint"]["Reason"]
|
|
360
368
|
return capabilities
|
|
@@ -362,8 +370,8 @@ class ADKRunner(BaseRunner):
|
|
|
362
370
|
@dataclass
|
|
363
371
|
class _ResolvabilityResult:
|
|
364
372
|
enabled: bool
|
|
365
|
-
source: str
|
|
366
|
-
app: Any
|
|
373
|
+
source: str # "agent_module" | "env" | "auto_persistent_session" | "default"
|
|
374
|
+
app: Any # User module exported App object (if any)
|
|
367
375
|
|
|
368
376
|
def _resolve_resumability(self) -> "_ResolvabilityResult":
|
|
369
377
|
"""从环境变量或 agent 模块推断是否启用 ADK 可恢复性。"""
|
|
@@ -371,13 +379,15 @@ class ADKRunner(BaseRunner):
|
|
|
371
379
|
module = getattr(self, "_module", None)
|
|
372
380
|
if module is not None:
|
|
373
381
|
try:
|
|
374
|
-
from
|
|
382
|
+
from ksadk.compat.adk_compat import App
|
|
383
|
+
|
|
375
384
|
for attr_name in ("app", "application"):
|
|
376
385
|
candidate = getattr(module, attr_name, None)
|
|
377
386
|
if isinstance(candidate, App) and candidate.resumability_config:
|
|
378
387
|
if candidate.resumability_config.is_resumable:
|
|
379
388
|
return self._ResolvabilityResult(
|
|
380
|
-
enabled=True, source="agent_module", app=candidate
|
|
389
|
+
enabled=True, source="agent_module", app=candidate
|
|
390
|
+
)
|
|
381
391
|
except ImportError:
|
|
382
392
|
pass
|
|
383
393
|
|
|
@@ -388,12 +398,12 @@ class ADKRunner(BaseRunner):
|
|
|
388
398
|
|
|
389
399
|
# Priority 3: persistent session backend auto-enable
|
|
390
400
|
stm_backend = (
|
|
391
|
-
getattr(self._short_term_memory, "backend", None)
|
|
392
|
-
if self._short_term_memory else None
|
|
401
|
+
getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
|
|
393
402
|
)
|
|
394
403
|
if stm_backend and stm_backend != "local":
|
|
395
404
|
return self._ResolvabilityResult(
|
|
396
|
-
enabled=True, source="auto_persistent_session", app=None
|
|
405
|
+
enabled=True, source="auto_persistent_session", app=None
|
|
406
|
+
)
|
|
397
407
|
|
|
398
408
|
return self._ResolvabilityResult(enabled=False, source="default", app=None)
|
|
399
409
|
|
|
@@ -417,6 +427,7 @@ class ADKRunner(BaseRunner):
|
|
|
417
427
|
return False, "google-adk version unknown, cannot guarantee invocation_id support"
|
|
418
428
|
try:
|
|
419
429
|
from packaging.version import Version
|
|
430
|
+
|
|
420
431
|
if Version(adk_ver) < Version(self._adk_resume_min_version):
|
|
421
432
|
return (
|
|
422
433
|
False,
|
|
@@ -430,7 +441,7 @@ class ADKRunner(BaseRunner):
|
|
|
430
441
|
|
|
431
442
|
def _build_runner(self) -> None:
|
|
432
443
|
"""构造 ADK Runner,优先使用 App 对象以启用 ResumabilityConfig。"""
|
|
433
|
-
from
|
|
444
|
+
from ksadk.compat.adk_compat import Runner
|
|
434
445
|
|
|
435
446
|
resumable = self._resolve_resumability()
|
|
436
447
|
resumability_enabled = resumable.enabled
|
|
@@ -438,9 +449,7 @@ class ADKRunner(BaseRunner):
|
|
|
438
449
|
# 版本兼容性检查:低于最低版本时强制关闭恢复
|
|
439
450
|
resume_compatible, resume_reason = self._check_adk_resume_compatibility()
|
|
440
451
|
if not resume_compatible and resumable.enabled:
|
|
441
|
-
logger.warning(
|
|
442
|
-
"ADKRunner: resumability disabled — %s", resume_reason
|
|
443
|
-
)
|
|
452
|
+
logger.warning("ADKRunner: resumability disabled — %s", resume_reason)
|
|
444
453
|
resumable = self._ResolvabilityResult(enabled=False, source="version_check", app=None)
|
|
445
454
|
resumability_enabled = False
|
|
446
455
|
self._resume_disabled_reason = resume_reason
|
|
@@ -452,7 +461,8 @@ class ADKRunner(BaseRunner):
|
|
|
452
461
|
)
|
|
453
462
|
elif resumable.enabled:
|
|
454
463
|
try:
|
|
455
|
-
from
|
|
464
|
+
from ksadk.compat.adk_compat import App, ResumabilityConfig
|
|
465
|
+
|
|
456
466
|
app = App(
|
|
457
467
|
name=self._agent.name,
|
|
458
468
|
root_agent=self._agent,
|
|
@@ -491,7 +501,8 @@ class ADKRunner(BaseRunner):
|
|
|
491
501
|
if resumability_enabled:
|
|
492
502
|
stm_backend = (
|
|
493
503
|
getattr(self._short_term_memory, "backend", None)
|
|
494
|
-
if self._short_term_memory
|
|
504
|
+
if self._short_term_memory
|
|
505
|
+
else None
|
|
495
506
|
)
|
|
496
507
|
logger.info(
|
|
497
508
|
"ADKRunner: resumability enabled (source=%s, backend=%s)",
|
|
@@ -519,10 +530,7 @@ class ADKRunner(BaseRunner):
|
|
|
519
530
|
|
|
520
531
|
agent_name = self._agent.name if self._agent else "default"
|
|
521
532
|
ltm = LongTermMemory.from_env(app_name=agent_name)
|
|
522
|
-
logger.info(
|
|
523
|
-
f"LongTermMemory initialized: backend={backend}, "
|
|
524
|
-
f"app_name={agent_name}"
|
|
525
|
-
)
|
|
533
|
+
logger.info(f"LongTermMemory initialized: backend={backend}, " f"app_name={agent_name}")
|
|
526
534
|
return ltm
|
|
527
535
|
except Exception as e:
|
|
528
536
|
logger.warning(f"Failed to init LongTermMemory: {e}.")
|
|
@@ -546,8 +554,7 @@ class ADKRunner(BaseRunner):
|
|
|
546
554
|
|
|
547
555
|
kb = KnowledgeBaseClient.from_env()
|
|
548
556
|
logger.info(
|
|
549
|
-
f"KnowledgeBase initialized: dataset_id={kb.dataset_id}, "
|
|
550
|
-
f"region={kb.region}"
|
|
557
|
+
f"KnowledgeBase initialized: dataset_id={kb.dataset_id}, " f"region={kb.region}"
|
|
551
558
|
)
|
|
552
559
|
return kb
|
|
553
560
|
except ImportError:
|
|
@@ -582,7 +589,7 @@ class ADKRunner(BaseRunner):
|
|
|
582
589
|
def _inject_load_memory_tool(self):
|
|
583
590
|
"""自动注入 load_memory 工具到 Agent"""
|
|
584
591
|
try:
|
|
585
|
-
from
|
|
592
|
+
from ksadk.compat.adk_compat import load_memory
|
|
586
593
|
|
|
587
594
|
added = self._append_tools_by_name([load_memory])
|
|
588
595
|
if added:
|
|
@@ -810,7 +817,7 @@ class ADKRunner(BaseRunner):
|
|
|
810
817
|
|
|
811
818
|
@staticmethod
|
|
812
819
|
def _tool_name(tool: Any) -> str:
|
|
813
|
-
return getattr(tool, "name", None) or getattr(tool, "__name__", "")
|
|
820
|
+
return str(getattr(tool, "name", None) or getattr(tool, "__name__", ""))
|
|
814
821
|
|
|
815
822
|
def _append_tools_by_name(self, tools: list[Any]) -> list[str]:
|
|
816
823
|
if not hasattr(self._agent, "tools"):
|
|
@@ -821,9 +828,7 @@ class ADKRunner(BaseRunner):
|
|
|
821
828
|
self._agent.tools = []
|
|
822
829
|
|
|
823
830
|
existing_names = {
|
|
824
|
-
self._tool_name(tool)
|
|
825
|
-
for tool in self._agent.tools
|
|
826
|
-
if self._tool_name(tool)
|
|
831
|
+
self._tool_name(tool) for tool in self._agent.tools if self._tool_name(tool)
|
|
827
832
|
}
|
|
828
833
|
added_names: list[str] = []
|
|
829
834
|
for tool in tools:
|
|
@@ -845,9 +850,7 @@ class ADKRunner(BaseRunner):
|
|
|
845
850
|
self._agent.tools = []
|
|
846
851
|
|
|
847
852
|
existing_keys = {
|
|
848
|
-
getattr(tool, key_attr)
|
|
849
|
-
for tool in self._agent.tools
|
|
850
|
-
if getattr(tool, key_attr, None)
|
|
853
|
+
getattr(tool, key_attr) for tool in self._agent.tools if getattr(tool, key_attr, None)
|
|
851
854
|
}
|
|
852
855
|
added_keys: list[str] = []
|
|
853
856
|
for toolset in toolsets:
|
|
@@ -879,11 +882,13 @@ class ADKRunner(BaseRunner):
|
|
|
879
882
|
name = detail.get("input")
|
|
880
883
|
if location not in (("name",), ["name"]) or not isinstance(name, str):
|
|
881
884
|
continue
|
|
882
|
-
|
|
885
|
+
# adk 1.34 报 "...valid identifier";adk 2.x 报 "...valid Python identifier"。
|
|
886
|
+
# 放宽匹配以同时覆盖两种措辞,保证两版都给出 actionable hint。
|
|
887
|
+
msg_lower = message.lower()
|
|
888
|
+
if "identifier" not in msg_lower or "valid" not in msg_lower:
|
|
883
889
|
continue
|
|
884
890
|
safe_name = "".join(
|
|
885
|
-
char if char.isascii() and (char.isalnum() or char == "_") else "_"
|
|
886
|
-
for char in name
|
|
891
|
+
char if char.isascii() and (char.isalnum() or char == "_") else "_" for char in name
|
|
887
892
|
)
|
|
888
893
|
if not safe_name or safe_name[0].isdigit():
|
|
889
894
|
safe_name = f"agent_{safe_name}"
|
|
@@ -960,7 +965,7 @@ class ADKRunner(BaseRunner):
|
|
|
960
965
|
self._inject_search_knowledge_tool()
|
|
961
966
|
|
|
962
967
|
# 初始化 SessionService
|
|
963
|
-
from
|
|
968
|
+
from ksadk.compat.adk_compat import InMemorySessionService
|
|
964
969
|
|
|
965
970
|
if self._short_term_memory:
|
|
966
971
|
self._session_service = self._short_term_memory.session_service
|
|
@@ -983,10 +988,7 @@ class ADKRunner(BaseRunner):
|
|
|
983
988
|
os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
|
|
984
989
|
)
|
|
985
990
|
self._default_model_reference = self._discover_model_reference(self._agent)
|
|
986
|
-
self._active_model_name =
|
|
987
|
-
self._default_model_reference
|
|
988
|
-
or self._default_model_name
|
|
989
|
-
)
|
|
991
|
+
self._active_model_name = self._default_model_reference or self._default_model_name
|
|
990
992
|
|
|
991
993
|
def _discover_model_reference(self, agent: Any) -> Optional[str]:
|
|
992
994
|
visited: set[int] = set()
|
|
@@ -1058,18 +1060,14 @@ class ADKRunner(BaseRunner):
|
|
|
1058
1060
|
def prepare_for_request(self, model: str | None) -> None:
|
|
1059
1061
|
normalized = self.sync_process_model_env(model)
|
|
1060
1062
|
if normalized is None:
|
|
1061
|
-
default_model_name = (
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
)
|
|
1063
|
+
default_model_name = getattr(
|
|
1064
|
+
self, "_default_model_name", None
|
|
1065
|
+
) or self.normalize_requested_model(
|
|
1066
|
+
os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
|
|
1066
1067
|
)
|
|
1067
1068
|
if default_model_name:
|
|
1068
1069
|
self.sync_process_model_env(default_model_name)
|
|
1069
|
-
target_reference = (
|
|
1070
|
-
getattr(self, "_default_model_reference", None)
|
|
1071
|
-
or default_model_name
|
|
1072
|
-
)
|
|
1070
|
+
target_reference = getattr(self, "_default_model_reference", None) or default_model_name
|
|
1073
1071
|
if target_reference and self._agent is not None:
|
|
1074
1072
|
current_reference = self._discover_model_reference(self._agent)
|
|
1075
1073
|
if current_reference != target_reference:
|
|
@@ -1095,14 +1093,13 @@ class ADKRunner(BaseRunner):
|
|
|
1095
1093
|
return
|
|
1096
1094
|
self._active_model_name = target_reference
|
|
1097
1095
|
|
|
1098
|
-
def _prepare_trace_metadata(self, session_id: str):
|
|
1096
|
+
def _prepare_trace_metadata(self, session_id: Optional[str]):
|
|
1099
1097
|
"""准备 Trace 元数据 (Tags, UserID, etc.)"""
|
|
1100
1098
|
from ksadk.tracing.span_utils import prepare_trace_metadata
|
|
1101
|
-
return prepare_trace_metadata(
|
|
1102
|
-
detection_result=getattr(self, "detection_result", None)
|
|
1103
|
-
)
|
|
1104
1099
|
|
|
1105
|
-
|
|
1100
|
+
return prepare_trace_metadata(detection_result=getattr(self, "detection_result", None))
|
|
1101
|
+
|
|
1102
|
+
async def _ensure_session(self, external_session_id: Optional[str] = None) -> str:
|
|
1106
1103
|
"""Get or create ADK session ID based on external ID
|
|
1107
1104
|
|
|
1108
1105
|
When ShortTermMemory is configured, uses its create_session method
|
|
@@ -1121,11 +1118,13 @@ class ADKRunner(BaseRunner):
|
|
|
1121
1118
|
session_id=external_session_id,
|
|
1122
1119
|
)
|
|
1123
1120
|
else:
|
|
1121
|
+
if self._session_service is None:
|
|
1122
|
+
raise RuntimeError("ADK session service is not initialized")
|
|
1124
1123
|
session = await self._session_service.create_session(
|
|
1125
1124
|
app_name=self._agent.name, user_id="ksadk_user"
|
|
1126
1125
|
)
|
|
1127
1126
|
self._session_map[external_session_id] = session.id
|
|
1128
|
-
return session.id
|
|
1127
|
+
return str(session.id)
|
|
1129
1128
|
|
|
1130
1129
|
# Case 2: No external ID (use default singleton)
|
|
1131
1130
|
if self._default_session_id is None:
|
|
@@ -1135,11 +1134,13 @@ class ADKRunner(BaseRunner):
|
|
|
1135
1134
|
user_id="ksadk_user",
|
|
1136
1135
|
)
|
|
1137
1136
|
else:
|
|
1137
|
+
if self._session_service is None:
|
|
1138
|
+
raise RuntimeError("ADK session service is not initialized")
|
|
1138
1139
|
session = await self._session_service.create_session(
|
|
1139
1140
|
app_name=self._agent.name, user_id="ksadk_user"
|
|
1140
1141
|
)
|
|
1141
1142
|
self._default_session_id = session.id
|
|
1142
|
-
return self._default_session_id
|
|
1143
|
+
return str(self._default_session_id)
|
|
1143
1144
|
|
|
1144
1145
|
async def save_session_to_long_term_memory(
|
|
1145
1146
|
self, session_id: str, user_id: str = "ksadk_user"
|
|
@@ -1323,8 +1324,11 @@ class ADKRunner(BaseRunner):
|
|
|
1323
1324
|
usage_metadata.get("total_token_count") or (input_tokens + output_tokens)
|
|
1324
1325
|
)
|
|
1325
1326
|
if not (
|
|
1326
|
-
input_tokens
|
|
1327
|
-
or
|
|
1327
|
+
input_tokens
|
|
1328
|
+
or output_tokens
|
|
1329
|
+
or total_tokens
|
|
1330
|
+
or input_token_details
|
|
1331
|
+
or output_token_details
|
|
1328
1332
|
):
|
|
1329
1333
|
return {}
|
|
1330
1334
|
return {
|
|
@@ -1339,7 +1343,6 @@ class ADKRunner(BaseRunner):
|
|
|
1339
1343
|
def _extract_event_usage(cls, event: Any) -> dict[str, Any]:
|
|
1340
1344
|
return cls._normalize_usage_metadata(getattr(event, "usage_metadata", None))
|
|
1341
1345
|
|
|
1342
|
-
|
|
1343
1346
|
# --- ADK invocation_id & checkpoint helpers ---
|
|
1344
1347
|
|
|
1345
1348
|
async def _get_max_checkpoint_seq_for_run(
|
|
@@ -1380,9 +1383,9 @@ class ADKRunner(BaseRunner):
|
|
|
1380
1383
|
return max_seq
|
|
1381
1384
|
except Exception as exc:
|
|
1382
1385
|
logger.warning(
|
|
1383
|
-
"ADKRunner: failed to query max checkpoint_seq "
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
+
"ADKRunner: failed to query max checkpoint_seq " "for run_id=%s: %s",
|
|
1387
|
+
run_id,
|
|
1388
|
+
exc,
|
|
1386
1389
|
)
|
|
1387
1390
|
return 0
|
|
1388
1391
|
|
|
@@ -1406,7 +1409,8 @@ class ADKRunner(BaseRunner):
|
|
|
1406
1409
|
captured_adk_invocation_id = ""
|
|
1407
1410
|
# 从已有 checkpoint 的最大 seq 继续,避免恢复模式下 ID 碰撞被去重丢弃
|
|
1408
1411
|
checkpoint_seq = await self._get_max_checkpoint_seq_for_run(
|
|
1409
|
-
session_id=session_id,
|
|
1412
|
+
session_id=session_id,
|
|
1413
|
+
run_id=effective_run_id,
|
|
1410
1414
|
)
|
|
1411
1415
|
async for event in events_async:
|
|
1412
1416
|
if not first_event_captured and hasattr(event, "invocation_id") and event.invocation_id:
|
|
@@ -1513,7 +1517,6 @@ class ADKRunner(BaseRunner):
|
|
|
1513
1517
|
except Exception:
|
|
1514
1518
|
return None
|
|
1515
1519
|
|
|
1516
|
-
|
|
1517
1520
|
def _is_resumable_boundary(self, event: Any) -> bool:
|
|
1518
1521
|
"""判断 ADK event 是否为可恢复边界。"""
|
|
1519
1522
|
# 工具调用请求
|
|
@@ -1527,6 +1530,83 @@ class ADKRunner(BaseRunner):
|
|
|
1527
1530
|
return True
|
|
1528
1531
|
return False
|
|
1529
1532
|
|
|
1533
|
+
def _extract_approval_signals(self, event: Any) -> list[dict[str, Any]]:
|
|
1534
|
+
"""从 ADK event 提取原生审批信号,翻译成统一 interrupt_info(供 runtime:4645 消费)。
|
|
1535
|
+
|
|
1536
|
+
覆盖 ADK 两套原生 HITL 机制,经 ``adk_compat`` 能力探测,兼容 1.34.x → 2.x:
|
|
1537
|
+
|
|
1538
|
+
1. **tool-confirmation**(``actions.requested_tool_confirmations``):工具执行前
|
|
1539
|
+
yes/no 审批,1.34+ 均有。→ ``kind="tool"``。
|
|
1540
|
+
2. **workflow HITL**(v2.0+):``event.interrupted=True`` + ``RequestInput`` 信号
|
|
1541
|
+
(message/payload/response_schema)。→ ``kind="input"``。
|
|
1542
|
+
|
|
1543
|
+
命中任一即返回 ``[{"approval_request_id","tool_name","args","kind","message"}, ...]``,
|
|
1544
|
+
否则返回 ``[]``。不引入对 v2.0 API 的硬依赖——字段缺失时跳过,不报错。
|
|
1545
|
+
"""
|
|
1546
|
+
signals: list[dict[str, Any]] = []
|
|
1547
|
+
actions = getattr(event, "actions", None)
|
|
1548
|
+
|
|
1549
|
+
# 1) tool-confirmation:requested_tool_confirmations(1.34+,经 getattr 容错)
|
|
1550
|
+
if actions is not None:
|
|
1551
|
+
confirmations = getattr(actions, "requested_tool_confirmations", None)
|
|
1552
|
+
if confirmations:
|
|
1553
|
+
for conf in confirmations:
|
|
1554
|
+
if conf is None:
|
|
1555
|
+
continue
|
|
1556
|
+
conf_id = str(
|
|
1557
|
+
getattr(conf, "id", "") or getattr(conf, "approval_request_id", "") or ""
|
|
1558
|
+
)
|
|
1559
|
+
if not conf_id:
|
|
1560
|
+
continue
|
|
1561
|
+
signals.append(
|
|
1562
|
+
{
|
|
1563
|
+
"approval_request_id": conf_id,
|
|
1564
|
+
"tool_name": str(
|
|
1565
|
+
getattr(conf, "tool_name", "")
|
|
1566
|
+
or getattr(conf, "name", "")
|
|
1567
|
+
or "tool"
|
|
1568
|
+
),
|
|
1569
|
+
"args": dict(getattr(conf, "args", None) or {}),
|
|
1570
|
+
"kind": "tool",
|
|
1571
|
+
"message": str(
|
|
1572
|
+
getattr(conf, "message", "") or getattr(conf, "hint", "") or ""
|
|
1573
|
+
),
|
|
1574
|
+
}
|
|
1575
|
+
)
|
|
1576
|
+
|
|
1577
|
+
# 2) workflow HITL(v2.0+):event.interrupted + RequestInput 信号
|
|
1578
|
+
# 仅当 adk_compat 探测到 v2.0+ 时检查,避免在 1.34 上访问不存在字段。
|
|
1579
|
+
try:
|
|
1580
|
+
from ksadk.compat.adk_compat import adk_version_at_least
|
|
1581
|
+
|
|
1582
|
+
if adk_version_at_least("2.0.0") and getattr(event, "interrupted", False):
|
|
1583
|
+
# RequestInput 信号可能挂在 actions 或 event 上(字段名跨小版本未冻结,
|
|
1584
|
+
# 用 getattr 容错,缺失即视为纯 interrupt 无 payload)。
|
|
1585
|
+
req_input = None
|
|
1586
|
+
if actions is not None:
|
|
1587
|
+
req_input = getattr(actions, "requested_input", None) or getattr(
|
|
1588
|
+
actions, "request_input", None
|
|
1589
|
+
)
|
|
1590
|
+
if req_input is None:
|
|
1591
|
+
req_input = getattr(event, "requested_input", None)
|
|
1592
|
+
interrupt_id = "adk-hitl-" + str(getattr(event, "invocation_id", "") or "unknown")
|
|
1593
|
+
signals.append(
|
|
1594
|
+
{
|
|
1595
|
+
"approval_request_id": interrupt_id,
|
|
1596
|
+
"tool_name": "RequestInput",
|
|
1597
|
+
"args": dict(getattr(req_input, "payload", None) or {}),
|
|
1598
|
+
"kind": "input",
|
|
1599
|
+
"message": str(
|
|
1600
|
+
getattr(req_input, "message", "") or "ADK workflow 请求人工输入"
|
|
1601
|
+
),
|
|
1602
|
+
}
|
|
1603
|
+
)
|
|
1604
|
+
except Exception:
|
|
1605
|
+
# 能力探测失败时不阻塞主流(审批 surface 是 best-effort,不降级为报错)。
|
|
1606
|
+
pass
|
|
1607
|
+
|
|
1608
|
+
return signals
|
|
1609
|
+
|
|
1530
1610
|
async def _maybe_write_checkpoint(
|
|
1531
1611
|
self,
|
|
1532
1612
|
*,
|
|
@@ -1573,9 +1653,10 @@ class ADKRunner(BaseRunner):
|
|
|
1573
1653
|
metadata=metadata,
|
|
1574
1654
|
)
|
|
1575
1655
|
logger.debug(
|
|
1576
|
-
"ADKRunner: wrote checkpoint adk-ckpt-%d at boundary "
|
|
1577
|
-
|
|
1578
|
-
|
|
1656
|
+
"ADKRunner: wrote checkpoint adk-ckpt-%d at boundary " "(session=%s, invocation_id=%s)",
|
|
1657
|
+
checkpoint_seq,
|
|
1658
|
+
session_id,
|
|
1659
|
+
adk_invocation_id,
|
|
1579
1660
|
)
|
|
1580
1661
|
|
|
1581
1662
|
def _extract_checkpoint_metadata(self, event: Any) -> dict[str, Any]:
|
|
@@ -1602,8 +1683,7 @@ class ADKRunner(BaseRunner):
|
|
|
1602
1683
|
|
|
1603
1684
|
# 是否可恢复
|
|
1604
1685
|
stm_backend = (
|
|
1605
|
-
getattr(self._short_term_memory, "backend", None)
|
|
1606
|
-
if self._short_term_memory else None
|
|
1686
|
+
getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
|
|
1607
1687
|
)
|
|
1608
1688
|
shared_across_pods = stm_backend == "database"
|
|
1609
1689
|
platform_resumable = self._resumable and shared_across_pods
|
|
@@ -1655,9 +1735,9 @@ class ADKRunner(BaseRunner):
|
|
|
1655
1735
|
)
|
|
1656
1736
|
if not adk_invocation_id:
|
|
1657
1737
|
logger.error(
|
|
1658
|
-
"Resume requested but ADK invocation_id not found for "
|
|
1659
|
-
|
|
1660
|
-
|
|
1738
|
+
"Resume requested but ADK invocation_id not found for " "session=%s ksadk_inv=%s",
|
|
1739
|
+
session_id,
|
|
1740
|
+
ksadk_invocation_id,
|
|
1661
1741
|
)
|
|
1662
1742
|
raise ValueError(
|
|
1663
1743
|
f"checkpoint_not_resumable: ADK invocation_id not found for "
|
|
@@ -1665,7 +1745,7 @@ class ADKRunner(BaseRunner):
|
|
|
1665
1745
|
f"The checkpoint data may have been lost or the invocation was "
|
|
1666
1746
|
f"never persisted."
|
|
1667
1747
|
)
|
|
1668
|
-
return adk_invocation_id
|
|
1748
|
+
return str(adk_invocation_id)
|
|
1669
1749
|
|
|
1670
1750
|
async def _prepare_run_events(
|
|
1671
1751
|
self,
|
|
@@ -1691,9 +1771,7 @@ class ADKRunner(BaseRunner):
|
|
|
1691
1771
|
else:
|
|
1692
1772
|
new_message = None
|
|
1693
1773
|
|
|
1694
|
-
ksadk_invocation_id = str(
|
|
1695
|
-
input_data.get("invocation_id") or input_data.get("run_id") or ""
|
|
1696
|
-
)
|
|
1774
|
+
ksadk_invocation_id = str(input_data.get("invocation_id") or input_data.get("run_id") or "")
|
|
1697
1775
|
|
|
1698
1776
|
checkpoint_run_id = (
|
|
1699
1777
|
str(input_data.get("run_id") or "").strip()
|
|
@@ -1725,6 +1803,8 @@ class ADKRunner(BaseRunner):
|
|
|
1725
1803
|
run_kwargs["new_message"] = new_message
|
|
1726
1804
|
run_kwargs["state_delta"] = self._build_state_delta(input_data) or None
|
|
1727
1805
|
|
|
1806
|
+
if self._runner is None:
|
|
1807
|
+
raise RuntimeError("ADK runner is not initialized")
|
|
1728
1808
|
events_async = self._runner.run_async(**run_kwargs)
|
|
1729
1809
|
|
|
1730
1810
|
if ksadk_invocation_id and self._resumable:
|
|
@@ -1740,7 +1820,7 @@ class ADKRunner(BaseRunner):
|
|
|
1740
1820
|
session_id=session_id,
|
|
1741
1821
|
ksadk_invocation_id=ksadk_invocation_id,
|
|
1742
1822
|
)
|
|
1743
|
-
return wrapped_async
|
|
1823
|
+
return cast(AsyncIterator[Any], wrapped_async)
|
|
1744
1824
|
|
|
1745
1825
|
async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
1746
1826
|
"""调用 ADK Agent"""
|
|
@@ -1764,9 +1844,7 @@ class ADKRunner(BaseRunner):
|
|
|
1764
1844
|
# Set input.value for Langfuse top-level input display
|
|
1765
1845
|
span.set_attribute("input.value", user_input)
|
|
1766
1846
|
truncated_input = (
|
|
1767
|
-
user_input[:200]
|
|
1768
|
-
if isinstance(user_input, str)
|
|
1769
|
-
else str(user_input)[:200]
|
|
1847
|
+
user_input[:200] if isinstance(user_input, str) else str(user_input)[:200]
|
|
1770
1848
|
)
|
|
1771
1849
|
span.set_attribute("user.input", truncated_input)
|
|
1772
1850
|
|
|
@@ -1819,7 +1897,7 @@ class ADKRunner(BaseRunner):
|
|
|
1819
1897
|
# final_response may hold only intermediate fragments.
|
|
1820
1898
|
last_event_text = ""
|
|
1821
1899
|
if last_event is not None and hasattr(last_event, "content") and last_event.content:
|
|
1822
|
-
for part in
|
|
1900
|
+
for part in last_event.content.parts or []:
|
|
1823
1901
|
is_thought = getattr(part, "thought", False)
|
|
1824
1902
|
if hasattr(part, "text") and part.text and not is_thought:
|
|
1825
1903
|
last_event_text += part.text
|
|
@@ -1835,7 +1913,10 @@ class ADKRunner(BaseRunner):
|
|
|
1835
1913
|
# Set output.value for Langfuse top-level output display
|
|
1836
1914
|
span.set_attribute("output.value", final_response[:5000] if final_response else "")
|
|
1837
1915
|
span.set_attribute("agent.output", final_response[:500] if final_response else "")
|
|
1838
|
-
result
|
|
1916
|
+
result: dict[str, Any] = {
|
|
1917
|
+
"output": final_response,
|
|
1918
|
+
"events": events_list,
|
|
1919
|
+
}
|
|
1839
1920
|
if usage:
|
|
1840
1921
|
result["usage"] = usage
|
|
1841
1922
|
# last_usage = 最后一次 LLM 调用快照(input_tokens = 当前上下文窗口占用)
|
|
@@ -1847,7 +1928,7 @@ class ADKRunner(BaseRunner):
|
|
|
1847
1928
|
|
|
1848
1929
|
使用 StreamingMode.SSE 启用真正的流式 token 输出
|
|
1849
1930
|
"""
|
|
1850
|
-
from
|
|
1931
|
+
from ksadk.compat.adk_compat import RunConfig, StreamingMode
|
|
1851
1932
|
|
|
1852
1933
|
# 判断是否为恢复调用 — 提前判断以避免将 resume_input dict 当作 string 处理
|
|
1853
1934
|
is_resume = bool(input_data.get("checkpoint_resume"))
|
|
@@ -1868,9 +1949,7 @@ class ADKRunner(BaseRunner):
|
|
|
1868
1949
|
# Set input.value for Langfuse top-level input display
|
|
1869
1950
|
span.set_attribute("input.value", user_input)
|
|
1870
1951
|
truncated_input = (
|
|
1871
|
-
user_input[:200]
|
|
1872
|
-
if isinstance(user_input, str)
|
|
1873
|
-
else str(user_input)[:200]
|
|
1952
|
+
user_input[:200] if isinstance(user_input, str) else str(user_input)[:200]
|
|
1874
1953
|
)
|
|
1875
1954
|
span.set_attribute("user.input", truncated_input)
|
|
1876
1955
|
|
|
@@ -1899,6 +1978,13 @@ class ADKRunner(BaseRunner):
|
|
|
1899
1978
|
accumulated_text = ""
|
|
1900
1979
|
usage: dict[str, Any] = {}
|
|
1901
1980
|
last_usage: dict[str, Any] = {}
|
|
1981
|
+
# handoff/sub-agent(如 RemoteA2aAgent)回复事件 partial=None,不进上面
|
|
1982
|
+
# partial 分支;且远端 agent 经 A2A 流式返回时,ADK 会产出多个"累积快照"
|
|
1983
|
+
# 事件(后一个含前一个内容)。按 author 记录已输出快照,只补发增量去重。
|
|
1984
|
+
sub_agent_snapshots: dict[str, str] = {}
|
|
1985
|
+
sub_agent_thought_snapshots: dict[str, str] = {}
|
|
1986
|
+
sub_agent_last_output_kind: dict[str, str] = {}
|
|
1987
|
+
top_agent_name = getattr(self._agent, "name", None)
|
|
1902
1988
|
|
|
1903
1989
|
async for event in wrapped_async:
|
|
1904
1990
|
event_usage = self._extract_event_usage(event)
|
|
@@ -1908,15 +1994,137 @@ class ADKRunner(BaseRunner):
|
|
|
1908
1994
|
# Only yield text delta if event is partial to avoid duplication of final summary
|
|
1909
1995
|
if hasattr(event, "content") and event.content and getattr(event, "partial", False):
|
|
1910
1996
|
if hasattr(event.content, "parts"):
|
|
1997
|
+
author = getattr(event, "author", None)
|
|
1998
|
+
is_sub_agent = bool(author and top_agent_name and author != top_agent_name)
|
|
1999
|
+
author_key = str(author)
|
|
1911
2000
|
for part in event.content.parts:
|
|
1912
2001
|
if hasattr(part, "text") and part.text:
|
|
1913
2002
|
is_thought = getattr(part, "thought", False)
|
|
1914
|
-
|
|
2003
|
+
replace_snapshot = bool(
|
|
2004
|
+
is_sub_agent
|
|
2005
|
+
and _part_metadata_flag(part, "ksadk_output_snapshot")
|
|
2006
|
+
)
|
|
2007
|
+
output_delta = part.text
|
|
2008
|
+
# 思考内容只作为 thinking delta 流出,不计入最终输出,
|
|
2009
|
+
# 否则最终回复会把思考过程再重复一遍(与 invoke() 语义对齐)。
|
|
2010
|
+
if is_thought and is_sub_agent:
|
|
2011
|
+
if sub_agent_last_output_kind.get(author_key) == "text":
|
|
2012
|
+
# 正文之后的 thought 是一个新的 reasoning segment。
|
|
2013
|
+
sub_agent_thought_snapshots[author_key] = ""
|
|
2014
|
+
previous_thought = sub_agent_thought_snapshots.get(
|
|
2015
|
+
author_key, ""
|
|
2016
|
+
)
|
|
2017
|
+
if part.text.startswith(previous_thought):
|
|
2018
|
+
output_delta = part.text[len(previous_thought) :]
|
|
2019
|
+
sub_agent_thought_snapshots[author_key] = part.text
|
|
2020
|
+
else:
|
|
2021
|
+
sub_agent_thought_snapshots[author_key] = (
|
|
2022
|
+
previous_thought + part.text
|
|
2023
|
+
)
|
|
2024
|
+
elif not is_thought:
|
|
2025
|
+
accumulated_text = (
|
|
2026
|
+
part.text
|
|
2027
|
+
if replace_snapshot
|
|
2028
|
+
else accumulated_text + part.text
|
|
2029
|
+
)
|
|
2030
|
+
# sub-agent(RemoteA2aAgent)增量也累积进快照,供后续
|
|
2031
|
+
# completed 全文消息(同一份结果)在 handoff 分支去重。
|
|
2032
|
+
if is_sub_agent:
|
|
2033
|
+
sub_agent_snapshots[author_key] = (
|
|
2034
|
+
part.text
|
|
2035
|
+
if replace_snapshot
|
|
2036
|
+
else sub_agent_snapshots.get(author_key, "") + part.text
|
|
2037
|
+
)
|
|
1915
2038
|
# 标记思考内容,前端可以选择是否展示
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2039
|
+
if output_delta:
|
|
2040
|
+
output_chunk: dict[str, Any] = {
|
|
2041
|
+
"delta": output_delta,
|
|
2042
|
+
"type": "thinking" if is_thought else "text",
|
|
2043
|
+
}
|
|
2044
|
+
if replace_snapshot and not is_thought:
|
|
2045
|
+
output_chunk["replace"] = True
|
|
2046
|
+
yield output_chunk
|
|
2047
|
+
if is_sub_agent:
|
|
2048
|
+
sub_agent_last_output_kind[author_key] = (
|
|
2049
|
+
"thinking" if is_thought else "text"
|
|
2050
|
+
)
|
|
2051
|
+
# handoff/sub-agent 回复:partial 为 None/False,上面分支跳过,这里补上。
|
|
2052
|
+
elif hasattr(event, "content") and event.content:
|
|
2053
|
+
author = getattr(event, "author", None)
|
|
2054
|
+
if (
|
|
2055
|
+
author
|
|
2056
|
+
and top_agent_name
|
|
2057
|
+
and author != top_agent_name
|
|
2058
|
+
and hasattr(event.content, "parts")
|
|
2059
|
+
):
|
|
2060
|
+
author_key = str(author)
|
|
2061
|
+
for part in event.content.parts:
|
|
2062
|
+
if (
|
|
2063
|
+
hasattr(part, "text")
|
|
2064
|
+
and part.text
|
|
2065
|
+
and getattr(part, "thought", False)
|
|
2066
|
+
):
|
|
2067
|
+
# A2A's terminal artifact is converted by ADK to
|
|
2068
|
+
# partial=False. Once that sub-agent has already
|
|
2069
|
+
# emitted response text, a trailing thought here is
|
|
2070
|
+
# a terminal snapshot/replacement (not a new
|
|
2071
|
+
# interleaved reasoning step). Showing it makes
|
|
2072
|
+
# the UI appear to end at "thinking" and hides the
|
|
2073
|
+
# just-emitted final response.
|
|
2074
|
+
if sub_agent_last_output_kind.get(author_key) == "text":
|
|
2075
|
+
continue
|
|
2076
|
+
# RemoteA2aAgent 将 last_chunk=True 映射成
|
|
2077
|
+
# partial=False;这可能是一个此前 partial thought
|
|
2078
|
+
# 的终态快照。只补发新增内容,避免正文之后再显示一遍
|
|
2079
|
+
# 相同的思考块;若此前没有 partial thought,仍完整透传。
|
|
2080
|
+
previous_thought = sub_agent_thought_snapshots.get(
|
|
2081
|
+
author_key, ""
|
|
2082
|
+
)
|
|
2083
|
+
if part.text.startswith(previous_thought):
|
|
2084
|
+
thought_delta = part.text[len(previous_thought) :]
|
|
2085
|
+
sub_agent_thought_snapshots[author_key] = part.text
|
|
2086
|
+
elif previous_thought.startswith(part.text):
|
|
2087
|
+
thought_delta = ""
|
|
2088
|
+
else:
|
|
2089
|
+
thought_delta = part.text
|
|
2090
|
+
sub_agent_thought_snapshots[author_key] = (
|
|
2091
|
+
previous_thought + part.text
|
|
2092
|
+
)
|
|
2093
|
+
if thought_delta:
|
|
2094
|
+
yield {"delta": thought_delta, "type": "thinking"}
|
|
2095
|
+
snapshot = ""
|
|
2096
|
+
replace_snapshot = False
|
|
2097
|
+
for part in event.content.parts:
|
|
2098
|
+
if hasattr(part, "text") and part.text and not getattr(
|
|
2099
|
+
part, "thought", False
|
|
2100
|
+
):
|
|
2101
|
+
snapshot += part.text
|
|
2102
|
+
replace_snapshot = replace_snapshot or _part_metadata_flag(
|
|
2103
|
+
part,
|
|
2104
|
+
"ksadk_output_snapshot",
|
|
2105
|
+
)
|
|
2106
|
+
if snapshot:
|
|
2107
|
+
prev = sub_agent_snapshots.get(author_key, "")
|
|
2108
|
+
if replace_snapshot:
|
|
2109
|
+
delta = snapshot
|
|
2110
|
+
sub_agent_snapshots[author_key] = snapshot
|
|
2111
|
+
accumulated_text = snapshot
|
|
2112
|
+
elif snapshot.startswith(prev):
|
|
2113
|
+
# 累计快照(如 completed 全文):只补发超出已累积的部分;
|
|
2114
|
+
# 若增量已发全,delta 为空,避免 completed 再渲染一遍。
|
|
2115
|
+
delta = snapshot[len(prev) :]
|
|
2116
|
+
sub_agent_snapshots[author_key] = snapshot
|
|
2117
|
+
else:
|
|
2118
|
+
# final 增量(小块):追加进累积,不覆盖。
|
|
2119
|
+
delta = snapshot
|
|
2120
|
+
sub_agent_snapshots[author_key] = prev + snapshot
|
|
2121
|
+
if delta:
|
|
2122
|
+
if not replace_snapshot:
|
|
2123
|
+
accumulated_text += delta
|
|
2124
|
+
output_chunk = {"delta": delta, "type": "text"}
|
|
2125
|
+
if replace_snapshot:
|
|
2126
|
+
output_chunk["replace"] = True
|
|
2127
|
+
yield output_chunk
|
|
1920
2128
|
|
|
1921
2129
|
# 处理工具调用事件 — ADK 通过 event.content.parts[].function_call
|
|
1922
2130
|
# 发出工具调用(即 event.get_function_calls()),而非
|
|
@@ -1935,6 +2143,7 @@ class ADKRunner(BaseRunner):
|
|
|
1935
2143
|
if not isinstance(fc_args, dict):
|
|
1936
2144
|
try:
|
|
1937
2145
|
import json as _json
|
|
2146
|
+
|
|
1938
2147
|
fc_args = _json.loads(fc_args) if isinstance(fc_args, str) else {}
|
|
1939
2148
|
except Exception:
|
|
1940
2149
|
fc_args = {}
|
|
@@ -1958,6 +2167,21 @@ class ADKRunner(BaseRunner):
|
|
|
1958
2167
|
"tool_args": getattr(tool_call, "input", {}),
|
|
1959
2168
|
}
|
|
1960
2169
|
|
|
2170
|
+
# ADK 原生审批 surface(tool-confirmation + v2.0 workflow HITL):
|
|
2171
|
+
# 翻译成统一 interrupt chunk,经 runtime:4645 既有 approval_request 通道
|
|
2172
|
+
# 消费 → 审批卡。能力探测兼容 1.34.x→2.x,缺字段则跳过。
|
|
2173
|
+
emitted_approval_ids: set[str] = set()
|
|
2174
|
+
for signal in self._extract_approval_signals(event):
|
|
2175
|
+
aid = signal.get("approval_request_id", "")
|
|
2176
|
+
if not aid or aid in emitted_approval_ids:
|
|
2177
|
+
continue
|
|
2178
|
+
emitted_approval_ids.add(aid)
|
|
2179
|
+
yield {
|
|
2180
|
+
"type": "interrupt",
|
|
2181
|
+
"interrupt_info": signal,
|
|
2182
|
+
"session_id": session_id,
|
|
2183
|
+
}
|
|
2184
|
+
|
|
1961
2185
|
# 处理工具返回结果 — ADK 通过 event.content.parts[].function_response
|
|
1962
2186
|
# 发出工具执行结果。当工具执行完毕后 ADK 会产生一个包含
|
|
1963
2187
|
# function_response 的事件,此处将其转为 tool_result 语义事件,
|
|
@@ -1971,6 +2195,7 @@ class ADKRunner(BaseRunner):
|
|
|
1971
2195
|
if not isinstance(fr_output, dict):
|
|
1972
2196
|
try:
|
|
1973
2197
|
import json as _json2
|
|
2198
|
+
|
|
1974
2199
|
if isinstance(fr_output, str):
|
|
1975
2200
|
fr_output = _json2.loads(fr_output)
|
|
1976
2201
|
else:
|