agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__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.1.dist-info}/METADATA +52 -20
- agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +180 -11
- ksadk/a2a/bootstrap.py +332 -0
- ksadk/a2a/card.py +15 -0
- ksadk/a2a/context_store.py +203 -0
- ksadk/a2a/control_plane.py +1056 -0
- ksadk/a2a/credential.py +99 -0
- ksadk/a2a/event_adapter.py +149 -0
- ksadk/a2a/executor.py +361 -137
- 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 +112 -0
- ksadk/a2a/server.py +102 -62
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +1122 -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/a2a_card.py +101 -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 +872 -0
- ksadk/agui/config.py +92 -0
- ksadk/agui/routes.py +47 -0
- ksadk/api/client.py +238 -204
- ksadk/api/environment.py +27 -0
- ksadk/builders/__init__.py +2 -0
- ksadk/builders/base.py +16 -14
- ksadk/builders/code_builder.py +375 -270
- ksadk/builders/container_builder.py +240 -200
- ksadk/builders/framework_requirements.py +31 -12
- ksadk/builders/ks3_uploader.py +60 -29
- ksadk/builders/managed_runtime_builder.py +173 -0
- ksadk/builders/mcp_builder.py +91 -92
- ksadk/builders/requirements_utils.py +1 -5
- ksadk/cli/__init__.py +97 -171
- ksadk/cli/cmd_a2a.py +255 -84
- 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 +507 -740
- ksadk/cli/cmd_dashboard.py +120 -45
- ksadk/cli/cmd_deploy.py +135 -59
- ksadk/cli/cmd_destroy.py +44 -14
- ksadk/cli/cmd_eval.py +386 -0
- ksadk/cli/cmd_files.py +63 -24
- ksadk/cli/cmd_hermes.py +313 -135
- ksadk/cli/cmd_invoke.py +184 -122
- ksadk/cli/cmd_launch.py +76 -28
- ksadk/cli/cmd_mcp.py +98 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +564 -268
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +58 -73
- ksadk/cli/cmd_status.py +80 -31
- ksadk/cli/cmd_studio.py +110 -0
- ksadk/cli/cmd_version.py +20 -13
- ksadk/cli/cmd_web.py +95 -18
- ksadk/cli/deploy_utils.py +20 -20
- ksadk/cli/dry_run.py +7 -4
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/error_utils.py +37 -12
- ksadk/cli/global_options.py +4 -3
- ksadk/cli/local_runtime.py +1 -2
- ksadk/cli/model_catalog.py +6 -2
- ksadk/cli/network_options.py +41 -19
- ksadk/cli/resource_common.py +6 -2
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/storage.py +0 -1
- ksadk/cli/ui.py +11 -3
- ksadk/cli/workflow_common.py +52 -18
- ksadk/codex/__init__.py +12 -0
- ksadk/codex/client.py +1042 -0
- ksadk/codex/phase.py +143 -0
- ksadk/codex/runtime.py +940 -0
- ksadk/common/aicp_env.py +0 -1
- ksadk/common/auth.py +2 -2
- ksadk/common/constants.py +1 -3
- ksadk/common/kop_client.py +232 -0
- 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 -14
- ksadk/configs/env_registry.py +661 -116
- ksadk/configs/global_config.py +3 -2
- ksadk/configs/settings.py +133 -209
- ksadk/conversations/__init__.py +85 -92
- 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 +660 -0
- ksadk/conversations/runtime_invocation.py +395 -0
- ksadk/conversations/runtime_metadata.py +475 -0
- ksadk/conversations/runtime_observability.py +367 -0
- ksadk/conversations/runtime_payloads.py +272 -0
- ksadk/conversations/runtime_persistence.py +440 -0
- ksadk/conversations/runtime_preparation.py +426 -0
- ksadk/conversations/runtime_resume.py +836 -0
- ksadk/conversations/runtime_stream_events.py +902 -0
- ksadk/conversations/runtime_streaming.py +700 -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/managed_runtime.py +33 -0
- ksadk/deployment/manager.py +85 -71
- ksadk/deployment/providers/serverless.py +379 -210
- 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/evaluation/__init__.py +84 -0
- ksadk/evaluation/a2a_adapter.py +444 -0
- ksadk/evaluation/adapters.py +54 -0
- ksadk/evaluation/contracts.py +409 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +461 -0
- ksadk/evaluation/executor.py +118 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/target.py +73 -0
- ksadk/events/__init__.py +25 -0
- ksadk/events/parser.py +191 -0
- ksadk/events/replay.py +37 -0
- ksadk/events/runtime_event.py +273 -0
- ksadk/events/store.py +317 -0
- ksadk/harness/__init__.py +35 -0
- ksadk/harness/app.py +269 -0
- ksadk/harness/config.py +181 -0
- ksadk/harness/reasoner.py +105 -0
- ksadk/harness/runtime.py +364 -0
- ksadk/harness/sandbox.py +141 -0
- ksadk/harness/tools.py +222 -0
- ksadk/hermes_terminal.py +28 -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 +38 -33
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +195 -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 +36 -36
- ksadk/memory/adk/long_term_memory.py +34 -37
- 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 +16 -6
- ksadk/memory/tool.py +4 -0
- 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 +66 -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 +201 -0
- ksadk/model_proxy/protocol_proxy.py +22 -0
- ksadk/model_proxy/server.py +443 -0
- ksadk/model_proxy/transform.py +735 -0
- ksadk/openclaw_gateway.py +29 -14
- ksadk/runners/adk_runner.py +354 -146
- ksadk/runners/base_runner.py +71 -29
- ksadk/runners/deepagents_runner.py +0 -1
- ksadk/runners/factory.py +18 -5
- ksadk/runners/langchain_runner.py +29 -742
- ksadk/runners/langgraph_runner.py +417 -97
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +134 -33
- ksadk/runners/usage_accumulator.py +1 -0
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +23 -7
- ksadk/runtime/__init__.py +67 -0
- ksadk/runtime/adapter.py +412 -0
- ksadk/runtime/conversation_execution.py +553 -0
- ksadk/runtime/executor.py +251 -0
- ksadk/runtime/factory.py +156 -0
- ksadk/runtime/framework_adapters.py +118 -0
- ksadk/runtime/launch.py +48 -0
- ksadk/runtime/preprocessing.py +131 -0
- ksadk/runtime/runner_adapter.py +1032 -0
- ksadk/runtime/runner_loading.py +36 -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/__init__.py +39 -4
- ksadk/server/api_models.py +15 -5
- ksadk/server/app.py +24 -3928
- ksadk/server/composition.py +47 -0
- ksadk/server/factory.py +622 -0
- ksadk/server/routes/__init__.py +1 -0
- ksadk/server/routes/checkpoint_resolution.py +116 -0
- ksadk/server/routes/common.py +682 -0
- ksadk/server/routes/control.py +331 -0
- ksadk/server/routes/dependencies.py +98 -0
- ksadk/server/routes/misc.py +425 -0
- ksadk/server/routes/models.py +451 -0
- ksadk/server/routes/openai_compat.py +229 -0
- ksadk/server/routes/projection.py +848 -0
- ksadk/server/routes/routers.py +52 -0
- ksadk/server/routes/run.py +519 -0
- ksadk/server/routes/sessions.py +644 -0
- ksadk/server/routes/streaming.py +265 -0
- ksadk/server/routes/workspace.py +414 -0
- ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
- ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
- ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
- ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
- ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
- ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
- ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
- ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
- ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
- ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
- ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
- ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
- ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
- ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
- ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
- ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
- ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
- ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
- ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
- ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
- ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
- ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
- ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
- ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
- ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
- ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
- ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
- ksadk/server/static/assets/index-R52pJJvg.js +177 -0
- ksadk/server/static/assets/index-jJr3_955.css +2 -0
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
- ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
- ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.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 +47 -16
- ksadk/skills/tool_defs.py +15 -10
- ksadk/studio/__init__.py +5 -0
- ksadk/studio/agent_avatar_assets.py +120 -0
- ksadk/studio/agent_lifecycle.py +51 -0
- ksadk/studio/api.py +1046 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +177 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/authoring.py +524 -0
- ksadk/studio/authoring_coordinator.py +285 -0
- ksadk/studio/builder.py +320 -0
- ksadk/studio/capabilities.py +351 -0
- ksadk/studio/cloud.py +278 -0
- ksadk/studio/codex_agent_service.py +583 -0
- ksadk/studio/codex_builder.py +324 -0
- ksadk/studio/codex_manifest.py +278 -0
- ksadk/studio/codex_run.py +352 -0
- ksadk/studio/compiler.py +189 -0
- ksadk/studio/contracts.py +644 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/evaluation.py +192 -0
- ksadk/studio/event_store.py +284 -0
- ksadk/studio/framework_run.py +114 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +543 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +243 -0
- ksadk/studio/otel_trace.py +754 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +306 -0
- ksadk/studio/resource_catalog.py +1217 -0
- ksadk/studio/run_service.py +707 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +339 -0
- ksadk/studio/service.py +1141 -0
- ksadk/studio/shared_web.py +994 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
- ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
- ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
- ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- ksadk/studio/static/index.html +15 -0
- ksadk/studio/templates.py +630 -0
- ksadk/studio/validator.py +246 -0
- ksadk/studio/workspace.py +137 -0
- ksadk/terminal_exec_policy.py +3 -4
- ksadk/tools/gateway.py +101 -15
- ksadk/tools/result_budget.py +12 -4
- ksadk/toolsets/__init__.py +161 -37
- ksadk/toolsets/a2a.py +426 -0
- 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/__init__.py +19 -21
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/setup.py +135 -506
- ksadk/tracing/span_utils.py +0 -41
- 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/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/runners/utils/langfuse.py +0 -230
- 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
- ksadk/tracing/exporters/langfuse_exporter.py +0 -445
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/top_level.txt +0 -0
|
@@ -4,28 +4,33 @@ LangGraphRunner - LangGraph 框架运行时
|
|
|
4
4
|
直接透传 LangGraph 原生能力,最小化封装
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import base64
|
|
8
|
+
import inspect
|
|
7
9
|
import os
|
|
8
|
-
import uuid
|
|
9
10
|
import re
|
|
11
|
+
import uuid
|
|
10
12
|
from typing import Any, AsyncIterator, Dict, Mapping
|
|
11
|
-
import base64
|
|
12
|
-
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
from ksadk.runners.base_runner import BaseRunner
|
|
15
|
-
from ksadk.runners.usage_accumulator import accumulate_usage
|
|
16
|
-
from ksadk.sessions.continuity import LangGraphSessionAdapter
|
|
17
|
-
from ksadk.runners.utils import get_langfuse_callbacks, get_langfuse_metadata, load_agent_module
|
|
18
14
|
from langgraph.types import Command
|
|
15
|
+
|
|
19
16
|
from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
|
|
20
17
|
from ksadk.conversations.reasoning_markup import ReasoningMarkupParser, strip_reasoning_markup
|
|
18
|
+
from ksadk.runners.base_runner import BaseRunner
|
|
19
|
+
from ksadk.runners.usage_accumulator import accumulate_usage
|
|
20
|
+
from ksadk.runners.utils import load_agent_module
|
|
21
|
+
from ksadk.sessions.continuity import LangGraphSessionAdapter
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class LangGraphRunner(BaseRunner):
|
|
24
25
|
"""LangGraph 框架运行时
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
透传原生 LangGraph 功能,支持任意 State 格式
|
|
27
28
|
"""
|
|
28
29
|
|
|
30
|
+
# ToolGateway approvals can arise after an otherwise terminal tool call;
|
|
31
|
+
# this runner opts into the runtime's semantic follow-up continuation.
|
|
32
|
+
supports_gateway_approval_semantic_resume = True
|
|
33
|
+
|
|
29
34
|
def load_agent(self) -> None:
|
|
30
35
|
self._load_agent(force_reload=False)
|
|
31
36
|
|
|
@@ -40,7 +45,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
40
45
|
self._loaded_model_name = self.normalize_requested_model(
|
|
41
46
|
os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
|
|
42
47
|
)
|
|
43
|
-
|
|
48
|
+
|
|
44
49
|
if not hasattr(self._agent, "invoke"):
|
|
45
50
|
raise TypeError("加载的对象不是有效的 LangGraph CompiledGraph")
|
|
46
51
|
|
|
@@ -57,10 +62,10 @@ class LangGraphRunner(BaseRunner):
|
|
|
57
62
|
|
|
58
63
|
def describe_checkpoint_capability(self) -> dict[str, Any]:
|
|
59
64
|
agent = getattr(self, "_agent", None)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if
|
|
65
|
+
checkpointer = getattr(agent, "checkpointer", None)
|
|
66
|
+
if checkpointer is None:
|
|
67
|
+
checkpointer = getattr(agent, "_checkpointer", None)
|
|
68
|
+
if checkpointer is None:
|
|
64
69
|
return {
|
|
65
70
|
"Supported": False,
|
|
66
71
|
"Backend": "none",
|
|
@@ -70,7 +75,16 @@ class LangGraphRunner(BaseRunner):
|
|
|
70
75
|
"Reason": "LangGraph graph has no configured checkpointer",
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
checkpointer_type = type(checkpointer)
|
|
79
|
+
type_name = f"{checkpointer_type.__module__}.{checkpointer_type.__name__}".lower()
|
|
80
|
+
if "memory" in type_name or "inmemory" in type_name:
|
|
81
|
+
backend = "memory"
|
|
82
|
+
elif "sqlite" in type_name:
|
|
83
|
+
backend = "sqlite"
|
|
84
|
+
elif "postgres" in type_name:
|
|
85
|
+
backend = "postgres"
|
|
86
|
+
else:
|
|
87
|
+
backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
|
|
74
88
|
if backend == "local":
|
|
75
89
|
backend = "sqlite"
|
|
76
90
|
if not backend:
|
|
@@ -87,7 +101,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
87
101
|
scope = "pod_local"
|
|
88
102
|
durable = True
|
|
89
103
|
shared = False
|
|
90
|
-
reason =
|
|
104
|
+
reason = (
|
|
105
|
+
"SQLite checkpoint is durable for local web debugging but is not shared across pods"
|
|
106
|
+
)
|
|
91
107
|
elif backend in {"memory", "inmemory"}:
|
|
92
108
|
backend = "memory"
|
|
93
109
|
scope = "process_local"
|
|
@@ -109,26 +125,17 @@ class LangGraphRunner(BaseRunner):
|
|
|
109
125
|
capabilities = super().get_runtime_capabilities()
|
|
110
126
|
capabilities["SessionContinuity"] = {
|
|
111
127
|
"Supported": True,
|
|
112
|
-
"Type":
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"Level": "runtime"
|
|
116
|
-
if capabilities["Checkpoint"].get("Supported")
|
|
117
|
-
else "semantic",
|
|
128
|
+
"Type": (
|
|
129
|
+
"checkpoint" if capabilities["Checkpoint"].get("Supported") else "semantic_replay"
|
|
130
|
+
),
|
|
131
|
+
"Level": "runtime" if capabilities["Checkpoint"].get("Supported") else "semantic",
|
|
118
132
|
"Reason": "",
|
|
119
133
|
}
|
|
120
134
|
return capabilities
|
|
121
135
|
|
|
122
136
|
def _get_config(self, session_id: str) -> dict:
|
|
123
137
|
"""获取运行配置"""
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
langfuse_callbacks = get_langfuse_callbacks()
|
|
127
|
-
if langfuse_callbacks:
|
|
128
|
-
config["callbacks"] = langfuse_callbacks
|
|
129
|
-
config["metadata"] = get_langfuse_metadata(session_id)
|
|
130
|
-
|
|
131
|
-
return config
|
|
138
|
+
return {"configurable": {"thread_id": session_id}}
|
|
132
139
|
|
|
133
140
|
@staticmethod
|
|
134
141
|
def _extract_langgraph_checkpoint_ref(payload: Dict[str, Any]) -> dict[str, Any]:
|
|
@@ -154,7 +161,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
154
161
|
|
|
155
162
|
thread_id = str(checkpoint_ref.get("thread_id") or session_id or "").strip()
|
|
156
163
|
if not thread_id:
|
|
157
|
-
raise ValueError(
|
|
164
|
+
raise ValueError(
|
|
165
|
+
"checkpoint_resume requires session_id or framework_ref.langgraph.thread_id"
|
|
166
|
+
)
|
|
158
167
|
|
|
159
168
|
next_config = dict(config)
|
|
160
169
|
configurable = dict(next_config.get("configurable") or {})
|
|
@@ -164,6 +173,50 @@ class LangGraphRunner(BaseRunner):
|
|
|
164
173
|
next_config["configurable"] = configurable
|
|
165
174
|
return next_config
|
|
166
175
|
|
|
176
|
+
@staticmethod
|
|
177
|
+
def _checkpoint_resume_input(
|
|
178
|
+
value: Any,
|
|
179
|
+
*,
|
|
180
|
+
payload_provided: bool,
|
|
181
|
+
interrupt_id: str,
|
|
182
|
+
) -> Command | None:
|
|
183
|
+
if not payload_provided:
|
|
184
|
+
return None
|
|
185
|
+
resume_value = {interrupt_id: value} if interrupt_id else value
|
|
186
|
+
return Command(resume=resume_value)
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def _is_gateway_approval_semantic_resume(value: Any) -> bool:
|
|
190
|
+
"""Whether ``value`` came from a completed ToolGateway approval.
|
|
191
|
+
|
|
192
|
+
Native LangGraph ``interrupt()`` values must keep using
|
|
193
|
+
``Command(resume=...)``. In contrast, a ToolGateway can return an
|
|
194
|
+
``approval_required`` result from an otherwise normal tool call; the
|
|
195
|
+
graph then reaches its terminal node before KsADK shows the approval
|
|
196
|
+
card. Its approved tool result needs a new semantic graph turn.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
return bool(
|
|
200
|
+
isinstance(value, Mapping)
|
|
201
|
+
and value.get("_ksadk_gateway_approval_resume") is True
|
|
202
|
+
and str(value.get("type") or "") == "function_call_output"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
@staticmethod
|
|
206
|
+
def _gateway_approval_follow_up_input() -> str:
|
|
207
|
+
"""A neutral prompt for the post-approval semantic continuation.
|
|
208
|
+
|
|
209
|
+
The actual approval response and durable tool result are already in
|
|
210
|
+
the session history. Do not repeat their contents here: project hooks
|
|
211
|
+
commonly route by input keywords, and copying a workspace path back
|
|
212
|
+
into a synthetic user message can accidentally trigger another tool.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
"系统已完成此前获批的操作。请基于会话记录中的真实结果,直接向用户说明完成情况;"
|
|
217
|
+
"不要重试,也不要再次要求确认。"
|
|
218
|
+
)
|
|
219
|
+
|
|
167
220
|
@staticmethod
|
|
168
221
|
def _checkpoint_ref_from_state(state: Any) -> dict[str, Any]:
|
|
169
222
|
state_config = None
|
|
@@ -180,17 +233,15 @@ class LangGraphRunner(BaseRunner):
|
|
|
180
233
|
checkpoint_id = str(configurable.get("checkpoint_id") or "").strip()
|
|
181
234
|
if not thread_id or not checkpoint_id:
|
|
182
235
|
return {}
|
|
183
|
-
next_nodes_raw =
|
|
236
|
+
next_nodes_raw = (
|
|
237
|
+
state.get("next") if isinstance(state, dict) else getattr(state, "next", None)
|
|
238
|
+
)
|
|
184
239
|
next_nodes: list[str] = []
|
|
185
240
|
if isinstance(next_nodes_raw, str):
|
|
186
241
|
if next_nodes_raw.strip():
|
|
187
242
|
next_nodes = [next_nodes_raw.strip()]
|
|
188
243
|
elif isinstance(next_nodes_raw, (list, tuple, set)):
|
|
189
|
-
next_nodes = [
|
|
190
|
-
str(item).strip()
|
|
191
|
-
for item in next_nodes_raw
|
|
192
|
-
if str(item or "").strip()
|
|
193
|
-
]
|
|
244
|
+
next_nodes = [str(item).strip() for item in next_nodes_raw if str(item or "").strip()]
|
|
194
245
|
return {
|
|
195
246
|
"langgraph": {
|
|
196
247
|
"thread_id": thread_id,
|
|
@@ -273,7 +324,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
273
324
|
def _ambient_context_text(payload: Dict[str, Any]) -> str:
|
|
274
325
|
sections: list[str] = []
|
|
275
326
|
kb_context = payload.get("kb_context") or {}
|
|
276
|
-
kb_text =
|
|
327
|
+
kb_text = (
|
|
328
|
+
str(kb_context.get("formatted_text") or "").strip()
|
|
329
|
+
if isinstance(kb_context, dict)
|
|
330
|
+
else ""
|
|
331
|
+
)
|
|
277
332
|
if kb_text:
|
|
278
333
|
sections.append(f"Knowledge base context:\n{kb_text}")
|
|
279
334
|
|
|
@@ -325,6 +380,14 @@ class LangGraphRunner(BaseRunner):
|
|
|
325
380
|
prepared = prepare_state(dict(normalized_payload), session_context)
|
|
326
381
|
if not isinstance(prepared, dict):
|
|
327
382
|
raise TypeError("ksadk_prepare_state(payload, session_context) must return a dict")
|
|
383
|
+
# A custom state hook owns application state, but must not accidentally
|
|
384
|
+
# erase protocol namespaces that framework middleware consumes. In
|
|
385
|
+
# particular CopilotKit reads ``ag-ui`` to decide whether to inject the
|
|
386
|
+
# official A2UI tool, and reads ``copilotkit.actions`` for frontend
|
|
387
|
+
# actions. Keep these namespaces narrow and transport-owned.
|
|
388
|
+
for namespace in ("ag-ui", "copilotkit"):
|
|
389
|
+
if namespace in normalized_payload:
|
|
390
|
+
prepared.setdefault(namespace, normalized_payload[namespace])
|
|
328
391
|
return prepared
|
|
329
392
|
|
|
330
393
|
def _to_state(self, payload: Dict[str, Any], history: list) -> Dict[str, Any]:
|
|
@@ -338,7 +401,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
338
401
|
if "input" in normalized_payload and "messages" not in normalized_payload:
|
|
339
402
|
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
|
340
403
|
|
|
341
|
-
messages = []
|
|
404
|
+
messages: list[Any] = []
|
|
342
405
|
if system_text:
|
|
343
406
|
messages.append(SystemMessage(content=system_text))
|
|
344
407
|
for msg in history:
|
|
@@ -442,11 +505,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
442
505
|
)
|
|
443
506
|
continue
|
|
444
507
|
|
|
445
|
-
|
|
446
|
-
if not
|
|
508
|
+
attachment_uri = attachment.get("file_uri")
|
|
509
|
+
if not attachment_uri:
|
|
447
510
|
continue
|
|
448
511
|
|
|
449
|
-
raw = read_attachment_uri_bytes(
|
|
512
|
+
raw = read_attachment_uri_bytes(str(attachment_uri))
|
|
450
513
|
if not raw:
|
|
451
514
|
continue
|
|
452
515
|
|
|
@@ -536,7 +599,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
536
599
|
|
|
537
600
|
async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
538
601
|
"""调用 LangGraph 图
|
|
539
|
-
|
|
602
|
+
|
|
540
603
|
支持两种输入格式:
|
|
541
604
|
1. 简化格式: {"input": "hello"} - 自动转换为 messages
|
|
542
605
|
2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
|
|
@@ -549,11 +612,22 @@ class LangGraphRunner(BaseRunner):
|
|
|
549
612
|
session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
|
|
550
613
|
is_resume = payload.pop("resume", False)
|
|
551
614
|
is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
|
|
615
|
+
resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
|
|
616
|
+
resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
|
|
617
|
+
resume_value = payload.get("input")
|
|
618
|
+
is_gateway_approval_resume = bool(
|
|
619
|
+
is_resume and self._is_gateway_approval_semantic_resume(resume_value)
|
|
620
|
+
)
|
|
621
|
+
if is_gateway_approval_resume:
|
|
622
|
+
# ``Command(resume=...)`` only works for a graph that actually
|
|
623
|
+
# yielded LangGraph's native interrupt. ToolGateway approvals are
|
|
624
|
+
# intercepted after a normal tool result, when the graph already
|
|
625
|
+
# ended, so restart a normal turn from the persisted transcript.
|
|
626
|
+
payload["input"] = self._gateway_approval_follow_up_input()
|
|
627
|
+
resume_value = payload["input"]
|
|
552
628
|
checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
|
|
553
629
|
history = payload.pop("history", [])
|
|
554
630
|
native_context = self.build_native_context(payload.get("platform_context"))
|
|
555
|
-
normalized_payload = self._strip_platform_context_fields(payload)
|
|
556
|
-
|
|
557
631
|
config = self._get_config(session_id)
|
|
558
632
|
if is_checkpoint_resume:
|
|
559
633
|
config = self._apply_checkpoint_resume_config(
|
|
@@ -561,28 +635,38 @@ class LangGraphRunner(BaseRunner):
|
|
|
561
635
|
session_id=session_id,
|
|
562
636
|
checkpoint_ref=checkpoint_ref,
|
|
563
637
|
)
|
|
564
|
-
|
|
638
|
+
|
|
565
639
|
# 判断输入格式 / resume
|
|
566
640
|
if is_checkpoint_resume:
|
|
567
|
-
state =
|
|
641
|
+
state = resume_value
|
|
642
|
+
elif is_resume and not is_gateway_approval_resume:
|
|
643
|
+
# ``Command(resume=...)`` is delivered to the graph's suspended
|
|
644
|
+
# interrupt. A custom prepare-state hook is for new user input;
|
|
645
|
+
# applying it here can rewrite an approval decision into ordinary
|
|
646
|
+
# graph state and turn an approved HITL action into a rejection.
|
|
647
|
+
state = resume_value
|
|
568
648
|
elif self._has_prepare_state_hook():
|
|
569
|
-
state = self._prepare_state_with_hook(
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
649
|
+
state = self._prepare_state_with_hook(
|
|
650
|
+
payload,
|
|
651
|
+
session_id,
|
|
652
|
+
history,
|
|
653
|
+
is_resume=is_gateway_approval_resume,
|
|
654
|
+
)
|
|
575
655
|
else:
|
|
576
656
|
state = self._to_state(payload, history)
|
|
577
657
|
|
|
578
658
|
try:
|
|
579
659
|
if is_checkpoint_resume:
|
|
580
660
|
result = await self._invoke_graph(
|
|
581
|
-
|
|
661
|
+
self._checkpoint_resume_input(
|
|
662
|
+
state,
|
|
663
|
+
payload_provided=resume_payload_provided,
|
|
664
|
+
interrupt_id=resume_interrupt_id,
|
|
665
|
+
),
|
|
582
666
|
config=config,
|
|
583
667
|
context=native_context,
|
|
584
668
|
)
|
|
585
|
-
elif is_resume:
|
|
669
|
+
elif is_resume and not is_gateway_approval_resume:
|
|
586
670
|
result = await self._invoke_graph(
|
|
587
671
|
Command(resume=state),
|
|
588
672
|
config=config,
|
|
@@ -606,7 +690,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
606
690
|
if metadata:
|
|
607
691
|
output["metadata"] = {**(output.get("metadata") or {}), **metadata}
|
|
608
692
|
return output
|
|
609
|
-
|
|
693
|
+
|
|
610
694
|
except Exception as e:
|
|
611
695
|
if "Interrupt" in type(e).__name__:
|
|
612
696
|
interrupt_info = self._get_interrupt_info(self._agent.get_state(config))
|
|
@@ -614,7 +698,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
614
698
|
"type": "interrupt",
|
|
615
699
|
"interrupt_info": interrupt_info,
|
|
616
700
|
"session_id": session_id,
|
|
617
|
-
"output":
|
|
701
|
+
"output": (
|
|
702
|
+
interrupt_info.get("message", "需要用户确认")
|
|
703
|
+
if isinstance(interrupt_info, dict)
|
|
704
|
+
else "需要用户确认"
|
|
705
|
+
),
|
|
618
706
|
}
|
|
619
707
|
raise
|
|
620
708
|
|
|
@@ -623,16 +711,21 @@ class LangGraphRunner(BaseRunner):
|
|
|
623
711
|
if isinstance(result, dict):
|
|
624
712
|
# 自定义 output 字段是业务显式出参,优先于内部 messages state。
|
|
625
713
|
if "output" in result:
|
|
626
|
-
return result["output"]
|
|
714
|
+
return str(result["output"])
|
|
627
715
|
# LangGraph 示例常用 answer 作为最终业务回答字段。
|
|
628
716
|
if "answer" in result:
|
|
629
|
-
return result["answer"]
|
|
717
|
+
return str(result["answer"])
|
|
630
718
|
# 标准 messages 格式
|
|
631
719
|
if "messages" in result:
|
|
632
720
|
messages = result["messages"]
|
|
633
721
|
if messages:
|
|
634
722
|
last = messages[-1]
|
|
635
|
-
|
|
723
|
+
content = (
|
|
724
|
+
last.get("content", str(last))
|
|
725
|
+
if isinstance(last, dict)
|
|
726
|
+
else getattr(last, "content", str(last))
|
|
727
|
+
)
|
|
728
|
+
return str(content)
|
|
636
729
|
return str(result) if result else ""
|
|
637
730
|
|
|
638
731
|
def _get_interrupt_info(self, state) -> dict:
|
|
@@ -642,12 +735,18 @@ class LangGraphRunner(BaseRunner):
|
|
|
642
735
|
if hasattr(task, "interrupts") and task.interrupts:
|
|
643
736
|
for intr in task.interrupts:
|
|
644
737
|
if hasattr(intr, "value"):
|
|
645
|
-
|
|
738
|
+
value = intr.value
|
|
739
|
+
info = dict(value) if isinstance(value, Mapping) else {"value": value}
|
|
740
|
+
interrupt_id = str(getattr(intr, "id", "") or "")
|
|
741
|
+
if interrupt_id:
|
|
742
|
+
info.setdefault("approval_request_id", interrupt_id)
|
|
743
|
+
return info
|
|
646
744
|
return {}
|
|
647
745
|
|
|
648
746
|
async def _stream_checkpoint_resume_updates(
|
|
649
747
|
self,
|
|
650
748
|
*,
|
|
749
|
+
stream_input: Any,
|
|
651
750
|
config: dict[str, Any],
|
|
652
751
|
context: dict[str, Any] | None,
|
|
653
752
|
) -> AsyncIterator[Dict[str, Any]]:
|
|
@@ -664,11 +763,17 @@ class LangGraphRunner(BaseRunner):
|
|
|
664
763
|
|
|
665
764
|
latest_output: Any = None
|
|
666
765
|
emitted_update = False
|
|
667
|
-
|
|
766
|
+
tool_names: dict[str, str] = {}
|
|
767
|
+
async for update in self._agent.astream(stream_input, **kwargs):
|
|
668
768
|
emitted_update = True
|
|
669
769
|
latest_output = update
|
|
670
770
|
if isinstance(update, Mapping):
|
|
671
771
|
for node_name, node_output in update.items():
|
|
772
|
+
for tool_event in self._tool_events_from_graph_update(
|
|
773
|
+
node_output,
|
|
774
|
+
tool_names=tool_names,
|
|
775
|
+
):
|
|
776
|
+
yield tool_event
|
|
672
777
|
yield {
|
|
673
778
|
"type": "graph_update",
|
|
674
779
|
"node": str(node_name),
|
|
@@ -703,6 +808,76 @@ class LangGraphRunner(BaseRunner):
|
|
|
703
808
|
if metadata:
|
|
704
809
|
yield {"type": "checkpoint", "metadata": metadata}
|
|
705
810
|
|
|
811
|
+
@staticmethod
|
|
812
|
+
def _tool_events_from_graph_update(
|
|
813
|
+
node_output: Any,
|
|
814
|
+
*,
|
|
815
|
+
tool_names: dict[str, str],
|
|
816
|
+
) -> list[dict[str, Any]]:
|
|
817
|
+
"""Preserve LangGraph update-mode tool messages as structured runner events."""
|
|
818
|
+
|
|
819
|
+
if isinstance(node_output, Mapping):
|
|
820
|
+
messages = node_output.get("messages")
|
|
821
|
+
else:
|
|
822
|
+
messages = getattr(node_output, "messages", None)
|
|
823
|
+
if not isinstance(messages, (list, tuple)):
|
|
824
|
+
return []
|
|
825
|
+
|
|
826
|
+
events: list[dict[str, Any]] = []
|
|
827
|
+
for message in messages:
|
|
828
|
+
if isinstance(message, Mapping):
|
|
829
|
+
tool_calls = message.get("tool_calls")
|
|
830
|
+
tool_call_id = message.get("tool_call_id")
|
|
831
|
+
message_name = message.get("name")
|
|
832
|
+
message_content = message.get("content")
|
|
833
|
+
message_status = message.get("status")
|
|
834
|
+
else:
|
|
835
|
+
tool_calls = getattr(message, "tool_calls", None)
|
|
836
|
+
tool_call_id = getattr(message, "tool_call_id", None)
|
|
837
|
+
message_name = getattr(message, "name", None)
|
|
838
|
+
message_content = getattr(message, "content", None)
|
|
839
|
+
message_status = getattr(message, "status", None)
|
|
840
|
+
|
|
841
|
+
if isinstance(tool_calls, (list, tuple)):
|
|
842
|
+
for tool_call in tool_calls:
|
|
843
|
+
if isinstance(tool_call, Mapping):
|
|
844
|
+
call_id = str(tool_call.get("id") or tool_call.get("tool_call_id") or "")
|
|
845
|
+
name = str(tool_call.get("name") or "tool")
|
|
846
|
+
args = tool_call.get("args")
|
|
847
|
+
else:
|
|
848
|
+
call_id = str(
|
|
849
|
+
getattr(tool_call, "id", None)
|
|
850
|
+
or getattr(tool_call, "tool_call_id", None)
|
|
851
|
+
or ""
|
|
852
|
+
)
|
|
853
|
+
name = str(getattr(tool_call, "name", None) or "tool")
|
|
854
|
+
args = getattr(tool_call, "args", None)
|
|
855
|
+
if call_id:
|
|
856
|
+
tool_names[call_id] = name
|
|
857
|
+
events.append(
|
|
858
|
+
{
|
|
859
|
+
"type": "tool_call",
|
|
860
|
+
"tool_call_id": call_id or name,
|
|
861
|
+
"tool_name": name,
|
|
862
|
+
"tool_args": args,
|
|
863
|
+
}
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
if tool_call_id:
|
|
867
|
+
call_id = str(tool_call_id)
|
|
868
|
+
name = str(message_name or tool_names.get(call_id) or "tool")
|
|
869
|
+
status = str(message_status or "").lower()
|
|
870
|
+
events.append(
|
|
871
|
+
{
|
|
872
|
+
"type": "tool_result",
|
|
873
|
+
"tool_call_id": call_id,
|
|
874
|
+
"tool_name": name,
|
|
875
|
+
"tool_output": message_content,
|
|
876
|
+
"error": message_content if status in {"error", "failed"} else None,
|
|
877
|
+
}
|
|
878
|
+
)
|
|
879
|
+
return events
|
|
880
|
+
|
|
706
881
|
async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
|
|
707
882
|
"""流式调用 LangGraph 图"""
|
|
708
883
|
payload = dict(input_data)
|
|
@@ -711,19 +886,30 @@ class LangGraphRunner(BaseRunner):
|
|
|
711
886
|
history = payload.pop("history", [])
|
|
712
887
|
is_resume = payload.pop("resume", False)
|
|
713
888
|
is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
|
|
889
|
+
resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
|
|
890
|
+
resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
|
|
891
|
+
resume_value = payload.get("input")
|
|
892
|
+
is_gateway_approval_resume = bool(
|
|
893
|
+
is_resume and self._is_gateway_approval_semantic_resume(resume_value)
|
|
894
|
+
)
|
|
895
|
+
if is_gateway_approval_resume:
|
|
896
|
+
# See ``invoke``: the graph did not suspend at a native interrupt,
|
|
897
|
+
# so use the durable transcript to run the post-tool answer turn.
|
|
898
|
+
payload["input"] = self._gateway_approval_follow_up_input()
|
|
899
|
+
resume_value = payload["input"]
|
|
714
900
|
checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
|
|
715
901
|
native_context = self.build_native_context(payload.get("platform_context"))
|
|
716
|
-
normalized_payload = self._strip_platform_context_fields(payload)
|
|
717
|
-
|
|
718
902
|
invoke_payload = dict(payload)
|
|
719
903
|
invoke_payload["session_id"] = session_id
|
|
720
904
|
if history:
|
|
721
905
|
invoke_payload["history"] = history
|
|
722
|
-
if is_resume:
|
|
906
|
+
if is_resume and not is_gateway_approval_resume:
|
|
723
907
|
invoke_payload["resume"] = True
|
|
724
908
|
if is_checkpoint_resume:
|
|
725
909
|
invoke_payload["checkpoint_resume"] = True
|
|
726
|
-
|
|
910
|
+
invoke_payload["resume_payload_provided"] = resume_payload_provided
|
|
911
|
+
invoke_payload["resume_interrupt_id"] = resume_interrupt_id
|
|
912
|
+
|
|
727
913
|
config = self._get_config(session_id)
|
|
728
914
|
if is_checkpoint_resume:
|
|
729
915
|
config = self._apply_checkpoint_resume_config(
|
|
@@ -733,14 +919,18 @@ class LangGraphRunner(BaseRunner):
|
|
|
733
919
|
)
|
|
734
920
|
|
|
735
921
|
if is_checkpoint_resume:
|
|
736
|
-
state =
|
|
922
|
+
state = resume_value
|
|
923
|
+
elif is_resume and not is_gateway_approval_resume:
|
|
924
|
+
# Keep the interrupt value intact for ``Command(resume=...)``;
|
|
925
|
+
# prepare-state hooks only shape fresh user turns.
|
|
926
|
+
state = resume_value
|
|
737
927
|
elif self._has_prepare_state_hook():
|
|
738
|
-
state = self._prepare_state_with_hook(
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
928
|
+
state = self._prepare_state_with_hook(
|
|
929
|
+
payload,
|
|
930
|
+
session_id,
|
|
931
|
+
history,
|
|
932
|
+
is_resume=is_gateway_approval_resume,
|
|
933
|
+
)
|
|
744
934
|
else:
|
|
745
935
|
state = self._to_state(payload, history)
|
|
746
936
|
|
|
@@ -799,6 +989,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
799
989
|
if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
|
|
800
990
|
try:
|
|
801
991
|
async for chunk in self._stream_checkpoint_resume_updates(
|
|
992
|
+
stream_input=self._checkpoint_resume_input(
|
|
993
|
+
state,
|
|
994
|
+
payload_provided=resume_payload_provided,
|
|
995
|
+
interrupt_id=resume_interrupt_id,
|
|
996
|
+
),
|
|
802
997
|
config=config,
|
|
803
998
|
context=native_context,
|
|
804
999
|
):
|
|
@@ -826,9 +1021,25 @@ class LangGraphRunner(BaseRunner):
|
|
|
826
1021
|
return
|
|
827
1022
|
|
|
828
1023
|
try:
|
|
829
|
-
stream_input =
|
|
1024
|
+
stream_input = (
|
|
1025
|
+
self._checkpoint_resume_input(
|
|
1026
|
+
state,
|
|
1027
|
+
payload_provided=resume_payload_provided,
|
|
1028
|
+
interrupt_id=resume_interrupt_id,
|
|
1029
|
+
)
|
|
1030
|
+
if is_checkpoint_resume
|
|
1031
|
+
else (
|
|
1032
|
+
Command(resume=state) if is_resume and not is_gateway_approval_resume else state
|
|
1033
|
+
)
|
|
1034
|
+
)
|
|
1035
|
+
# stream_mode 含 "custom" 才会产生 on_custom_stream 事件(custom writer);
|
|
1036
|
+
# 保留默认 "values" 以兼容既有 on_chain_end/graph_update 消费。
|
|
830
1037
|
stream_kwargs = {"version": "v2", "config": config}
|
|
831
|
-
if
|
|
1038
|
+
if self._callable_accepts_keyword(self._agent.astream_events, "stream_mode"):
|
|
1039
|
+
stream_kwargs["stream_mode"] = ["values", "custom"]
|
|
1040
|
+
if native_context and self._callable_accepts_keyword(
|
|
1041
|
+
self._agent.astream_events, "context"
|
|
1042
|
+
):
|
|
832
1043
|
stream_kwargs["context"] = native_context
|
|
833
1044
|
async for event in self._agent.astream_events(stream_input, **stream_kwargs):
|
|
834
1045
|
event_kind = event.get("event", "")
|
|
@@ -855,7 +1066,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
855
1066
|
reasoning = getattr(chunk, "reasoning_content", None)
|
|
856
1067
|
if not reasoning and hasattr(chunk, "additional_kwargs"):
|
|
857
1068
|
reasoning = chunk.additional_kwargs.get("reasoning_content")
|
|
858
|
-
|
|
1069
|
+
|
|
859
1070
|
if reasoning:
|
|
860
1071
|
accumulated_reasoning += reasoning
|
|
861
1072
|
yield {"delta": reasoning, "type": "thinking"}
|
|
@@ -865,9 +1076,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
865
1076
|
content = self._filter_tool_tags(chunk.content)
|
|
866
1077
|
if isinstance(content, str):
|
|
867
1078
|
if accumulated_reasoning and content.startswith(accumulated_reasoning):
|
|
868
|
-
content = content[len(accumulated_reasoning):]
|
|
1079
|
+
content = content[len(accumulated_reasoning) :]
|
|
869
1080
|
elif reasoning and content.startswith(reasoning):
|
|
870
|
-
content = content[len(reasoning):]
|
|
1081
|
+
content = content[len(reasoning) :]
|
|
871
1082
|
if content:
|
|
872
1083
|
for part in inline_reasoning_parser.feed(content):
|
|
873
1084
|
if not part.text or not part.text.strip():
|
|
@@ -888,6 +1099,58 @@ class LangGraphRunner(BaseRunner):
|
|
|
888
1099
|
if run_key not in stream_usage_run_keys:
|
|
889
1100
|
record_model_usage(event, last_usage or usage)
|
|
890
1101
|
|
|
1102
|
+
elif event_kind == "on_chain_stream":
|
|
1103
|
+
# node 内 get_stream_writer() 写入的自定义数据,经 stream_mode 含
|
|
1104
|
+
# "custom" 时,astream_events 包成 on_chain_stream,chunk 为
|
|
1105
|
+
# (mode, value) tuple:("custom", value) 是 writer 透传内容,
|
|
1106
|
+
# ("values", state) 是 state 快照(忽略,终态走 on_chain_end)。
|
|
1107
|
+
# 编排方常用 custom writer 把"调远端 agent/子图"的流式增量透传出来。
|
|
1108
|
+
chunk = event.get("data", {}).get("chunk")
|
|
1109
|
+
if not (isinstance(chunk, tuple) and len(chunk) == 2 and chunk[0] == "custom"):
|
|
1110
|
+
continue
|
|
1111
|
+
data = chunk[1]
|
|
1112
|
+
if isinstance(data, str):
|
|
1113
|
+
accumulated_text += data
|
|
1114
|
+
yield {"delta": data, "type": "text"}
|
|
1115
|
+
continue
|
|
1116
|
+
if isinstance(data, Mapping):
|
|
1117
|
+
custom_type = str(data.get("type") or "text")
|
|
1118
|
+
if custom_type in ("tool_call", "tool_result"):
|
|
1119
|
+
# 结构化工具事件:透传完整 payload(tool_name/tool_args/
|
|
1120
|
+
# tool_output 等),不计入正文,供 UI 渲染工具卡片。
|
|
1121
|
+
out = {"type": custom_type}
|
|
1122
|
+
out.update({k: v for k, v in data.items() if k != "type"})
|
|
1123
|
+
yield out
|
|
1124
|
+
continue
|
|
1125
|
+
custom_delta = ""
|
|
1126
|
+
for key in ("delta", "text", "content", "output", "data"):
|
|
1127
|
+
value = data.get(key)
|
|
1128
|
+
if isinstance(value, str) and value:
|
|
1129
|
+
custom_delta = value
|
|
1130
|
+
break
|
|
1131
|
+
if not custom_delta:
|
|
1132
|
+
continue
|
|
1133
|
+
replace = bool(data.get("replace"))
|
|
1134
|
+
if custom_type == "thinking":
|
|
1135
|
+
accumulated_reasoning = (
|
|
1136
|
+
custom_delta if replace else accumulated_reasoning + custom_delta
|
|
1137
|
+
)
|
|
1138
|
+
else:
|
|
1139
|
+
accumulated_text = (
|
|
1140
|
+
custom_delta if replace else accumulated_text + custom_delta
|
|
1141
|
+
)
|
|
1142
|
+
custom_event: dict[str, Any] = {
|
|
1143
|
+
"delta": custom_delta,
|
|
1144
|
+
"type": custom_type,
|
|
1145
|
+
}
|
|
1146
|
+
if replace:
|
|
1147
|
+
custom_event["replace"] = True
|
|
1148
|
+
yield custom_event
|
|
1149
|
+
continue
|
|
1150
|
+
if data is not None:
|
|
1151
|
+
accumulated_text += str(data)
|
|
1152
|
+
yield {"delta": str(data), "type": "text"}
|
|
1153
|
+
|
|
891
1154
|
elif event_kind == "on_tool_start":
|
|
892
1155
|
emitted_non_text_event = True
|
|
893
1156
|
yield {
|
|
@@ -896,23 +1159,35 @@ class LangGraphRunner(BaseRunner):
|
|
|
896
1159
|
"tool_args": event.get("data", {}).get("input", {}),
|
|
897
1160
|
"run_id": event.get("run_id"),
|
|
898
1161
|
}
|
|
899
|
-
|
|
1162
|
+
|
|
900
1163
|
elif event_kind == "on_tool_end":
|
|
901
1164
|
emitted_non_text_event = True
|
|
902
1165
|
tool_output = event.get("data", {}).get("output", "")
|
|
1166
|
+
# LangGraph returns a ToolMessage here for normal tools.
|
|
1167
|
+
# Preserve its content instead of serializing the repr,
|
|
1168
|
+
# otherwise structured output such as A2UI envelopes becomes
|
|
1169
|
+
# unparsable. Keep the callback run_id below: it is paired
|
|
1170
|
+
# with the preceding ``on_tool_start`` event on this stream.
|
|
1171
|
+
normalized_output = getattr(tool_output, "content", tool_output)
|
|
1172
|
+
if isinstance(tool_output, Mapping) and "content" in tool_output:
|
|
1173
|
+
normalized_output = tool_output["content"]
|
|
903
1174
|
yield {
|
|
904
1175
|
"type": "tool_result",
|
|
905
1176
|
"tool_name": event.get("name", "unknown"),
|
|
906
1177
|
"tool_args": event.get("data", {}).get("input", {}),
|
|
907
|
-
"tool_output":
|
|
1178
|
+
"tool_output": normalized_output,
|
|
908
1179
|
"run_id": event.get("run_id"),
|
|
909
1180
|
}
|
|
910
|
-
|
|
1181
|
+
|
|
911
1182
|
elif event_kind == "on_chain_end":
|
|
912
1183
|
output = event.get("data", {}).get("output", {})
|
|
913
1184
|
if isinstance(output, dict) and "__interrupt__" in output:
|
|
914
1185
|
emitted_non_text_event = True
|
|
915
|
-
yield {
|
|
1186
|
+
yield {
|
|
1187
|
+
"type": "interrupt",
|
|
1188
|
+
"interrupt_info": output["__interrupt__"],
|
|
1189
|
+
"session_id": session_id,
|
|
1190
|
+
}
|
|
916
1191
|
return
|
|
917
1192
|
extracted_output = self._extract_output(output)
|
|
918
1193
|
if extracted_output:
|
|
@@ -922,10 +1197,41 @@ class LangGraphRunner(BaseRunner):
|
|
|
922
1197
|
|
|
923
1198
|
except Exception as e:
|
|
924
1199
|
if "Interrupt" in type(e).__name__:
|
|
925
|
-
yield {
|
|
1200
|
+
yield {
|
|
1201
|
+
"type": "interrupt",
|
|
1202
|
+
"interrupt_info": self._get_interrupt_info(self._agent.get_state(config)),
|
|
1203
|
+
"session_id": session_id,
|
|
1204
|
+
}
|
|
926
1205
|
return
|
|
927
1206
|
raise
|
|
928
1207
|
|
|
1208
|
+
# goal-18(ksadk-web 人机交互):图因审批门(HITL)在流式中静默暂停时,
|
|
1209
|
+
# 这里把审批详情(action_requests)作为 approval 事件冒出,供 UI 渲染审批卡。
|
|
1210
|
+
# 此前流式路径只在 checkpoint 标 resumable,UI 拿不到"该批哪个工具/什么参数/允许哪些决定"。
|
|
1211
|
+
# 注:get_state 在部分 agent 上是 async,统一按 awaitable 处理;取不到则跳过,不破坏事件流。
|
|
1212
|
+
pending_approval = None
|
|
1213
|
+
try:
|
|
1214
|
+
_get_state = getattr(self._agent, "aget_state", None) or getattr(
|
|
1215
|
+
self._agent, "get_state", None
|
|
1216
|
+
)
|
|
1217
|
+
if _get_state is not None:
|
|
1218
|
+
_maybe_state = _get_state(config)
|
|
1219
|
+
if inspect.isawaitable(_maybe_state):
|
|
1220
|
+
_maybe_state = await _maybe_state
|
|
1221
|
+
pending_approval = self._get_interrupt_info(_maybe_state)
|
|
1222
|
+
except Exception:
|
|
1223
|
+
pending_approval = None
|
|
1224
|
+
if pending_approval:
|
|
1225
|
+
yield {
|
|
1226
|
+
"type": "approval",
|
|
1227
|
+
"interrupt_info": pending_approval,
|
|
1228
|
+
"session_id": session_id,
|
|
1229
|
+
}
|
|
1230
|
+
metadata = await self._latest_checkpoint_metadata(config)
|
|
1231
|
+
if metadata:
|
|
1232
|
+
yield {"type": "checkpoint", "metadata": metadata}
|
|
1233
|
+
return
|
|
1234
|
+
|
|
929
1235
|
for part in inline_reasoning_parser.flush():
|
|
930
1236
|
if not part.text or not part.text.strip():
|
|
931
1237
|
continue
|
|
@@ -940,35 +1246,49 @@ class LangGraphRunner(BaseRunner):
|
|
|
940
1246
|
if final_output_text:
|
|
941
1247
|
final_chunk = {"output": final_output_text, "type": "final"}
|
|
942
1248
|
usage = accumulated_model_usage() or final_output_usage or latest_stream_usage
|
|
943
|
-
last_usage =
|
|
1249
|
+
last_usage = (
|
|
1250
|
+
latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
|
|
1251
|
+
)
|
|
944
1252
|
if usage:
|
|
945
1253
|
final_chunk["usage"] = usage
|
|
946
1254
|
if last_usage:
|
|
947
1255
|
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
948
1256
|
yield final_chunk
|
|
949
1257
|
elif not emitted_non_text_event:
|
|
950
|
-
result = await self.invoke(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1258
|
+
result = await self.invoke({**invoke_payload, "_ksadk_force_graph_invoke": True})
|
|
1259
|
+
fallback_chunk: dict[str, Any] = {
|
|
1260
|
+
"output": result.get("output", ""),
|
|
1261
|
+
"type": "final",
|
|
1262
|
+
}
|
|
954
1263
|
usage = self._extract_usage(result)
|
|
955
1264
|
if usage:
|
|
956
|
-
|
|
1265
|
+
fallback_chunk["usage"] = usage
|
|
957
1266
|
last_usage = self._extract_last_usage(result)
|
|
958
1267
|
if last_usage:
|
|
959
|
-
|
|
960
|
-
yield
|
|
961
|
-
|
|
962
|
-
if isinstance(
|
|
963
|
-
yield {"type": "checkpoint", "metadata":
|
|
1268
|
+
fallback_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
1269
|
+
yield fallback_chunk
|
|
1270
|
+
checkpoint_metadata = result.get("metadata") if isinstance(result, dict) else None
|
|
1271
|
+
if isinstance(checkpoint_metadata, dict) and checkpoint_metadata.get("agentengine"):
|
|
1272
|
+
yield {"type": "checkpoint", "metadata": checkpoint_metadata}
|
|
964
1273
|
return
|
|
965
1274
|
else:
|
|
966
1275
|
final_chunk = {"output": accumulated_text, "type": "final"}
|
|
967
1276
|
state_usage = await self._latest_state_usage(config)
|
|
968
|
-
usage =
|
|
1277
|
+
usage = (
|
|
1278
|
+
accumulated_model_usage()
|
|
1279
|
+
or state_usage
|
|
1280
|
+
or final_output_usage
|
|
1281
|
+
or latest_stream_usage
|
|
1282
|
+
)
|
|
969
1283
|
if usage:
|
|
970
1284
|
final_chunk["usage"] = usage
|
|
971
|
-
last_usage =
|
|
1285
|
+
last_usage = (
|
|
1286
|
+
latest_model_usage()
|
|
1287
|
+
or state_usage
|
|
1288
|
+
or final_output_last_usage
|
|
1289
|
+
or latest_stream_usage
|
|
1290
|
+
or usage
|
|
1291
|
+
)
|
|
972
1292
|
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
973
1293
|
yield final_chunk
|
|
974
1294
|
|
|
@@ -980,6 +1300,6 @@ class LangGraphRunner(BaseRunner):
|
|
|
980
1300
|
"""过滤 <tool_call> 标签"""
|
|
981
1301
|
if not isinstance(content, str):
|
|
982
1302
|
return content
|
|
983
|
-
content = re.sub(r
|
|
984
|
-
content = re.sub(r
|
|
1303
|
+
content = re.sub(r"<tool_call>.*?</tool_call>", "", content, flags=re.DOTALL)
|
|
1304
|
+
content = re.sub(r"</?(?:tool_call|arg_key|arg_value)>", "", content)
|
|
985
1305
|
return content
|