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
|
@@ -4,25 +4,26 @@ 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 get_langfuse_callbacks, get_langfuse_metadata, 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
|
|
|
@@ -40,7 +41,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
40
41
|
self._loaded_model_name = self.normalize_requested_model(
|
|
41
42
|
os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
|
|
42
43
|
)
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
if not hasattr(self._agent, "invoke"):
|
|
45
46
|
raise TypeError("加载的对象不是有效的 LangGraph CompiledGraph")
|
|
46
47
|
|
|
@@ -57,10 +58,10 @@ class LangGraphRunner(BaseRunner):
|
|
|
57
58
|
|
|
58
59
|
def describe_checkpoint_capability(self) -> dict[str, Any]:
|
|
59
60
|
agent = getattr(self, "_agent", None)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if
|
|
61
|
+
checkpointer = getattr(agent, "checkpointer", None)
|
|
62
|
+
if checkpointer is None:
|
|
63
|
+
checkpointer = getattr(agent, "_checkpointer", None)
|
|
64
|
+
if checkpointer is None:
|
|
64
65
|
return {
|
|
65
66
|
"Supported": False,
|
|
66
67
|
"Backend": "none",
|
|
@@ -70,7 +71,16 @@ class LangGraphRunner(BaseRunner):
|
|
|
70
71
|
"Reason": "LangGraph graph has no configured checkpointer",
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
checkpointer_type = type(checkpointer)
|
|
75
|
+
type_name = f"{checkpointer_type.__module__}.{checkpointer_type.__name__}".lower()
|
|
76
|
+
if "memory" in type_name or "inmemory" in type_name:
|
|
77
|
+
backend = "memory"
|
|
78
|
+
elif "sqlite" in type_name:
|
|
79
|
+
backend = "sqlite"
|
|
80
|
+
elif "postgres" in type_name:
|
|
81
|
+
backend = "postgres"
|
|
82
|
+
else:
|
|
83
|
+
backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
|
|
74
84
|
if backend == "local":
|
|
75
85
|
backend = "sqlite"
|
|
76
86
|
if not backend:
|
|
@@ -87,7 +97,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
87
97
|
scope = "pod_local"
|
|
88
98
|
durable = True
|
|
89
99
|
shared = False
|
|
90
|
-
reason =
|
|
100
|
+
reason = (
|
|
101
|
+
"SQLite checkpoint is durable for local web debugging but is not shared across pods"
|
|
102
|
+
)
|
|
91
103
|
elif backend in {"memory", "inmemory"}:
|
|
92
104
|
backend = "memory"
|
|
93
105
|
scope = "process_local"
|
|
@@ -109,25 +121,23 @@ class LangGraphRunner(BaseRunner):
|
|
|
109
121
|
capabilities = super().get_runtime_capabilities()
|
|
110
122
|
capabilities["SessionContinuity"] = {
|
|
111
123
|
"Supported": True,
|
|
112
|
-
"Type":
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"Level": "runtime"
|
|
116
|
-
if capabilities["Checkpoint"].get("Supported")
|
|
117
|
-
else "semantic",
|
|
124
|
+
"Type": (
|
|
125
|
+
"checkpoint" if capabilities["Checkpoint"].get("Supported") else "semantic_replay"
|
|
126
|
+
),
|
|
127
|
+
"Level": "runtime" if capabilities["Checkpoint"].get("Supported") else "semantic",
|
|
118
128
|
"Reason": "",
|
|
119
129
|
}
|
|
120
130
|
return capabilities
|
|
121
131
|
|
|
122
132
|
def _get_config(self, session_id: str) -> dict:
|
|
123
133
|
"""获取运行配置"""
|
|
124
|
-
config = {"configurable": {"thread_id": session_id}}
|
|
125
|
-
|
|
134
|
+
config: dict[str, Any] = {"configurable": {"thread_id": session_id}}
|
|
135
|
+
|
|
126
136
|
langfuse_callbacks = get_langfuse_callbacks()
|
|
127
137
|
if langfuse_callbacks:
|
|
128
138
|
config["callbacks"] = langfuse_callbacks
|
|
129
139
|
config["metadata"] = get_langfuse_metadata(session_id)
|
|
130
|
-
|
|
140
|
+
|
|
131
141
|
return config
|
|
132
142
|
|
|
133
143
|
@staticmethod
|
|
@@ -154,7 +164,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
154
164
|
|
|
155
165
|
thread_id = str(checkpoint_ref.get("thread_id") or session_id or "").strip()
|
|
156
166
|
if not thread_id:
|
|
157
|
-
raise ValueError(
|
|
167
|
+
raise ValueError(
|
|
168
|
+
"checkpoint_resume requires session_id or framework_ref.langgraph.thread_id"
|
|
169
|
+
)
|
|
158
170
|
|
|
159
171
|
next_config = dict(config)
|
|
160
172
|
configurable = dict(next_config.get("configurable") or {})
|
|
@@ -164,6 +176,18 @@ class LangGraphRunner(BaseRunner):
|
|
|
164
176
|
next_config["configurable"] = configurable
|
|
165
177
|
return next_config
|
|
166
178
|
|
|
179
|
+
@staticmethod
|
|
180
|
+
def _checkpoint_resume_input(
|
|
181
|
+
value: Any,
|
|
182
|
+
*,
|
|
183
|
+
payload_provided: bool,
|
|
184
|
+
interrupt_id: str,
|
|
185
|
+
) -> Command | None:
|
|
186
|
+
if not payload_provided:
|
|
187
|
+
return None
|
|
188
|
+
resume_value = {interrupt_id: value} if interrupt_id else value
|
|
189
|
+
return Command(resume=resume_value)
|
|
190
|
+
|
|
167
191
|
@staticmethod
|
|
168
192
|
def _checkpoint_ref_from_state(state: Any) -> dict[str, Any]:
|
|
169
193
|
state_config = None
|
|
@@ -180,17 +204,15 @@ class LangGraphRunner(BaseRunner):
|
|
|
180
204
|
checkpoint_id = str(configurable.get("checkpoint_id") or "").strip()
|
|
181
205
|
if not thread_id or not checkpoint_id:
|
|
182
206
|
return {}
|
|
183
|
-
next_nodes_raw =
|
|
207
|
+
next_nodes_raw = (
|
|
208
|
+
state.get("next") if isinstance(state, dict) else getattr(state, "next", None)
|
|
209
|
+
)
|
|
184
210
|
next_nodes: list[str] = []
|
|
185
211
|
if isinstance(next_nodes_raw, str):
|
|
186
212
|
if next_nodes_raw.strip():
|
|
187
213
|
next_nodes = [next_nodes_raw.strip()]
|
|
188
214
|
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
|
-
]
|
|
215
|
+
next_nodes = [str(item).strip() for item in next_nodes_raw if str(item or "").strip()]
|
|
194
216
|
return {
|
|
195
217
|
"langgraph": {
|
|
196
218
|
"thread_id": thread_id,
|
|
@@ -273,7 +295,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
273
295
|
def _ambient_context_text(payload: Dict[str, Any]) -> str:
|
|
274
296
|
sections: list[str] = []
|
|
275
297
|
kb_context = payload.get("kb_context") or {}
|
|
276
|
-
kb_text =
|
|
298
|
+
kb_text = (
|
|
299
|
+
str(kb_context.get("formatted_text") or "").strip()
|
|
300
|
+
if isinstance(kb_context, dict)
|
|
301
|
+
else ""
|
|
302
|
+
)
|
|
277
303
|
if kb_text:
|
|
278
304
|
sections.append(f"Knowledge base context:\n{kb_text}")
|
|
279
305
|
|
|
@@ -325,6 +351,14 @@ class LangGraphRunner(BaseRunner):
|
|
|
325
351
|
prepared = prepare_state(dict(normalized_payload), session_context)
|
|
326
352
|
if not isinstance(prepared, dict):
|
|
327
353
|
raise TypeError("ksadk_prepare_state(payload, session_context) must return a dict")
|
|
354
|
+
# A custom state hook owns application state, but must not accidentally
|
|
355
|
+
# erase protocol namespaces that framework middleware consumes. In
|
|
356
|
+
# particular CopilotKit reads ``ag-ui`` to decide whether to inject the
|
|
357
|
+
# official A2UI tool, and reads ``copilotkit.actions`` for frontend
|
|
358
|
+
# actions. Keep these namespaces narrow and transport-owned.
|
|
359
|
+
for namespace in ("ag-ui", "copilotkit"):
|
|
360
|
+
if namespace in normalized_payload:
|
|
361
|
+
prepared.setdefault(namespace, normalized_payload[namespace])
|
|
328
362
|
return prepared
|
|
329
363
|
|
|
330
364
|
def _to_state(self, payload: Dict[str, Any], history: list) -> Dict[str, Any]:
|
|
@@ -338,7 +372,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
338
372
|
if "input" in normalized_payload and "messages" not in normalized_payload:
|
|
339
373
|
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
|
340
374
|
|
|
341
|
-
messages = []
|
|
375
|
+
messages: list[Any] = []
|
|
342
376
|
if system_text:
|
|
343
377
|
messages.append(SystemMessage(content=system_text))
|
|
344
378
|
for msg in history:
|
|
@@ -442,11 +476,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
442
476
|
)
|
|
443
477
|
continue
|
|
444
478
|
|
|
445
|
-
|
|
446
|
-
if not
|
|
479
|
+
attachment_uri = attachment.get("file_uri")
|
|
480
|
+
if not attachment_uri:
|
|
447
481
|
continue
|
|
448
482
|
|
|
449
|
-
raw = read_attachment_uri_bytes(
|
|
483
|
+
raw = read_attachment_uri_bytes(str(attachment_uri))
|
|
450
484
|
if not raw:
|
|
451
485
|
continue
|
|
452
486
|
|
|
@@ -536,7 +570,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
536
570
|
|
|
537
571
|
async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
538
572
|
"""调用 LangGraph 图
|
|
539
|
-
|
|
573
|
+
|
|
540
574
|
支持两种输入格式:
|
|
541
575
|
1. 简化格式: {"input": "hello"} - 自动转换为 messages
|
|
542
576
|
2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
|
|
@@ -549,11 +583,12 @@ class LangGraphRunner(BaseRunner):
|
|
|
549
583
|
session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
|
|
550
584
|
is_resume = payload.pop("resume", False)
|
|
551
585
|
is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
|
|
586
|
+
resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
|
|
587
|
+
resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
|
|
588
|
+
resume_value = payload.get("input")
|
|
552
589
|
checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
|
|
553
590
|
history = payload.pop("history", [])
|
|
554
591
|
native_context = self.build_native_context(payload.get("platform_context"))
|
|
555
|
-
normalized_payload = self._strip_platform_context_fields(payload)
|
|
556
|
-
|
|
557
592
|
config = self._get_config(session_id)
|
|
558
593
|
if is_checkpoint_resume:
|
|
559
594
|
config = self._apply_checkpoint_resume_config(
|
|
@@ -561,24 +596,29 @@ class LangGraphRunner(BaseRunner):
|
|
|
561
596
|
session_id=session_id,
|
|
562
597
|
checkpoint_ref=checkpoint_ref,
|
|
563
598
|
)
|
|
564
|
-
|
|
599
|
+
|
|
565
600
|
# 判断输入格式 / resume
|
|
566
601
|
if is_checkpoint_resume:
|
|
567
|
-
state =
|
|
568
|
-
elif self._has_prepare_state_hook():
|
|
569
|
-
state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
|
|
602
|
+
state = resume_value
|
|
570
603
|
elif is_resume:
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
604
|
+
# ``Command(resume=...)`` is delivered to the graph's suspended
|
|
605
|
+
# interrupt. A custom prepare-state hook is for new user input;
|
|
606
|
+
# applying it here can rewrite an approval decision into ordinary
|
|
607
|
+
# graph state and turn an approved HITL action into a rejection.
|
|
608
|
+
state = resume_value
|
|
609
|
+
elif self._has_prepare_state_hook():
|
|
610
|
+
state = self._prepare_state_with_hook(payload, session_id, history)
|
|
575
611
|
else:
|
|
576
612
|
state = self._to_state(payload, history)
|
|
577
613
|
|
|
578
614
|
try:
|
|
579
615
|
if is_checkpoint_resume:
|
|
580
616
|
result = await self._invoke_graph(
|
|
581
|
-
|
|
617
|
+
self._checkpoint_resume_input(
|
|
618
|
+
state,
|
|
619
|
+
payload_provided=resume_payload_provided,
|
|
620
|
+
interrupt_id=resume_interrupt_id,
|
|
621
|
+
),
|
|
582
622
|
config=config,
|
|
583
623
|
context=native_context,
|
|
584
624
|
)
|
|
@@ -606,7 +646,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
606
646
|
if metadata:
|
|
607
647
|
output["metadata"] = {**(output.get("metadata") or {}), **metadata}
|
|
608
648
|
return output
|
|
609
|
-
|
|
649
|
+
|
|
610
650
|
except Exception as e:
|
|
611
651
|
if "Interrupt" in type(e).__name__:
|
|
612
652
|
interrupt_info = self._get_interrupt_info(self._agent.get_state(config))
|
|
@@ -614,7 +654,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
614
654
|
"type": "interrupt",
|
|
615
655
|
"interrupt_info": interrupt_info,
|
|
616
656
|
"session_id": session_id,
|
|
617
|
-
"output":
|
|
657
|
+
"output": (
|
|
658
|
+
interrupt_info.get("message", "需要用户确认")
|
|
659
|
+
if isinstance(interrupt_info, dict)
|
|
660
|
+
else "需要用户确认"
|
|
661
|
+
),
|
|
618
662
|
}
|
|
619
663
|
raise
|
|
620
664
|
|
|
@@ -623,16 +667,21 @@ class LangGraphRunner(BaseRunner):
|
|
|
623
667
|
if isinstance(result, dict):
|
|
624
668
|
# 自定义 output 字段是业务显式出参,优先于内部 messages state。
|
|
625
669
|
if "output" in result:
|
|
626
|
-
return result["output"]
|
|
670
|
+
return str(result["output"])
|
|
627
671
|
# LangGraph 示例常用 answer 作为最终业务回答字段。
|
|
628
672
|
if "answer" in result:
|
|
629
|
-
return result["answer"]
|
|
673
|
+
return str(result["answer"])
|
|
630
674
|
# 标准 messages 格式
|
|
631
675
|
if "messages" in result:
|
|
632
676
|
messages = result["messages"]
|
|
633
677
|
if messages:
|
|
634
678
|
last = messages[-1]
|
|
635
|
-
|
|
679
|
+
content = (
|
|
680
|
+
last.get("content", str(last))
|
|
681
|
+
if isinstance(last, dict)
|
|
682
|
+
else getattr(last, "content", str(last))
|
|
683
|
+
)
|
|
684
|
+
return str(content)
|
|
636
685
|
return str(result) if result else ""
|
|
637
686
|
|
|
638
687
|
def _get_interrupt_info(self, state) -> dict:
|
|
@@ -642,12 +691,18 @@ class LangGraphRunner(BaseRunner):
|
|
|
642
691
|
if hasattr(task, "interrupts") and task.interrupts:
|
|
643
692
|
for intr in task.interrupts:
|
|
644
693
|
if hasattr(intr, "value"):
|
|
645
|
-
|
|
694
|
+
value = intr.value
|
|
695
|
+
info = dict(value) if isinstance(value, Mapping) else {"value": value}
|
|
696
|
+
interrupt_id = str(getattr(intr, "id", "") or "")
|
|
697
|
+
if interrupt_id:
|
|
698
|
+
info.setdefault("approval_request_id", interrupt_id)
|
|
699
|
+
return info
|
|
646
700
|
return {}
|
|
647
701
|
|
|
648
702
|
async def _stream_checkpoint_resume_updates(
|
|
649
703
|
self,
|
|
650
704
|
*,
|
|
705
|
+
stream_input: Any,
|
|
651
706
|
config: dict[str, Any],
|
|
652
707
|
context: dict[str, Any] | None,
|
|
653
708
|
) -> AsyncIterator[Dict[str, Any]]:
|
|
@@ -664,11 +719,17 @@ class LangGraphRunner(BaseRunner):
|
|
|
664
719
|
|
|
665
720
|
latest_output: Any = None
|
|
666
721
|
emitted_update = False
|
|
667
|
-
|
|
722
|
+
tool_names: dict[str, str] = {}
|
|
723
|
+
async for update in self._agent.astream(stream_input, **kwargs):
|
|
668
724
|
emitted_update = True
|
|
669
725
|
latest_output = update
|
|
670
726
|
if isinstance(update, Mapping):
|
|
671
727
|
for node_name, node_output in update.items():
|
|
728
|
+
for tool_event in self._tool_events_from_graph_update(
|
|
729
|
+
node_output,
|
|
730
|
+
tool_names=tool_names,
|
|
731
|
+
):
|
|
732
|
+
yield tool_event
|
|
672
733
|
yield {
|
|
673
734
|
"type": "graph_update",
|
|
674
735
|
"node": str(node_name),
|
|
@@ -703,6 +764,76 @@ class LangGraphRunner(BaseRunner):
|
|
|
703
764
|
if metadata:
|
|
704
765
|
yield {"type": "checkpoint", "metadata": metadata}
|
|
705
766
|
|
|
767
|
+
@staticmethod
|
|
768
|
+
def _tool_events_from_graph_update(
|
|
769
|
+
node_output: Any,
|
|
770
|
+
*,
|
|
771
|
+
tool_names: dict[str, str],
|
|
772
|
+
) -> list[dict[str, Any]]:
|
|
773
|
+
"""Preserve LangGraph update-mode tool messages as structured runner events."""
|
|
774
|
+
|
|
775
|
+
if isinstance(node_output, Mapping):
|
|
776
|
+
messages = node_output.get("messages")
|
|
777
|
+
else:
|
|
778
|
+
messages = getattr(node_output, "messages", None)
|
|
779
|
+
if not isinstance(messages, (list, tuple)):
|
|
780
|
+
return []
|
|
781
|
+
|
|
782
|
+
events: list[dict[str, Any]] = []
|
|
783
|
+
for message in messages:
|
|
784
|
+
if isinstance(message, Mapping):
|
|
785
|
+
tool_calls = message.get("tool_calls")
|
|
786
|
+
tool_call_id = message.get("tool_call_id")
|
|
787
|
+
message_name = message.get("name")
|
|
788
|
+
message_content = message.get("content")
|
|
789
|
+
message_status = message.get("status")
|
|
790
|
+
else:
|
|
791
|
+
tool_calls = getattr(message, "tool_calls", None)
|
|
792
|
+
tool_call_id = getattr(message, "tool_call_id", None)
|
|
793
|
+
message_name = getattr(message, "name", None)
|
|
794
|
+
message_content = getattr(message, "content", None)
|
|
795
|
+
message_status = getattr(message, "status", None)
|
|
796
|
+
|
|
797
|
+
if isinstance(tool_calls, (list, tuple)):
|
|
798
|
+
for tool_call in tool_calls:
|
|
799
|
+
if isinstance(tool_call, Mapping):
|
|
800
|
+
call_id = str(tool_call.get("id") or tool_call.get("tool_call_id") or "")
|
|
801
|
+
name = str(tool_call.get("name") or "tool")
|
|
802
|
+
args = tool_call.get("args")
|
|
803
|
+
else:
|
|
804
|
+
call_id = str(
|
|
805
|
+
getattr(tool_call, "id", None)
|
|
806
|
+
or getattr(tool_call, "tool_call_id", None)
|
|
807
|
+
or ""
|
|
808
|
+
)
|
|
809
|
+
name = str(getattr(tool_call, "name", None) or "tool")
|
|
810
|
+
args = getattr(tool_call, "args", None)
|
|
811
|
+
if call_id:
|
|
812
|
+
tool_names[call_id] = name
|
|
813
|
+
events.append(
|
|
814
|
+
{
|
|
815
|
+
"type": "tool_call",
|
|
816
|
+
"tool_call_id": call_id or name,
|
|
817
|
+
"tool_name": name,
|
|
818
|
+
"tool_args": args,
|
|
819
|
+
}
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
if tool_call_id:
|
|
823
|
+
call_id = str(tool_call_id)
|
|
824
|
+
name = str(message_name or tool_names.get(call_id) or "tool")
|
|
825
|
+
status = str(message_status or "").lower()
|
|
826
|
+
events.append(
|
|
827
|
+
{
|
|
828
|
+
"type": "tool_result",
|
|
829
|
+
"tool_call_id": call_id,
|
|
830
|
+
"tool_name": name,
|
|
831
|
+
"tool_output": message_content,
|
|
832
|
+
"error": message_content if status in {"error", "failed"} else None,
|
|
833
|
+
}
|
|
834
|
+
)
|
|
835
|
+
return events
|
|
836
|
+
|
|
706
837
|
async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
|
|
707
838
|
"""流式调用 LangGraph 图"""
|
|
708
839
|
payload = dict(input_data)
|
|
@@ -711,10 +842,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
711
842
|
history = payload.pop("history", [])
|
|
712
843
|
is_resume = payload.pop("resume", False)
|
|
713
844
|
is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
|
|
845
|
+
resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
|
|
846
|
+
resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
|
|
847
|
+
resume_value = payload.get("input")
|
|
714
848
|
checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
|
|
715
849
|
native_context = self.build_native_context(payload.get("platform_context"))
|
|
716
|
-
normalized_payload = self._strip_platform_context_fields(payload)
|
|
717
|
-
|
|
718
850
|
invoke_payload = dict(payload)
|
|
719
851
|
invoke_payload["session_id"] = session_id
|
|
720
852
|
if history:
|
|
@@ -723,7 +855,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
723
855
|
invoke_payload["resume"] = True
|
|
724
856
|
if is_checkpoint_resume:
|
|
725
857
|
invoke_payload["checkpoint_resume"] = True
|
|
726
|
-
|
|
858
|
+
invoke_payload["resume_payload_provided"] = resume_payload_provided
|
|
859
|
+
invoke_payload["resume_interrupt_id"] = resume_interrupt_id
|
|
860
|
+
|
|
727
861
|
config = self._get_config(session_id)
|
|
728
862
|
if is_checkpoint_resume:
|
|
729
863
|
config = self._apply_checkpoint_resume_config(
|
|
@@ -733,14 +867,13 @@ class LangGraphRunner(BaseRunner):
|
|
|
733
867
|
)
|
|
734
868
|
|
|
735
869
|
if is_checkpoint_resume:
|
|
736
|
-
state =
|
|
737
|
-
elif self._has_prepare_state_hook():
|
|
738
|
-
state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
|
|
870
|
+
state = resume_value
|
|
739
871
|
elif is_resume:
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
872
|
+
# Keep the interrupt value intact for ``Command(resume=...)``;
|
|
873
|
+
# prepare-state hooks only shape fresh user turns.
|
|
874
|
+
state = resume_value
|
|
875
|
+
elif self._has_prepare_state_hook():
|
|
876
|
+
state = self._prepare_state_with_hook(payload, session_id, history)
|
|
744
877
|
else:
|
|
745
878
|
state = self._to_state(payload, history)
|
|
746
879
|
|
|
@@ -799,6 +932,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
799
932
|
if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
|
|
800
933
|
try:
|
|
801
934
|
async for chunk in self._stream_checkpoint_resume_updates(
|
|
935
|
+
stream_input=self._checkpoint_resume_input(
|
|
936
|
+
state,
|
|
937
|
+
payload_provided=resume_payload_provided,
|
|
938
|
+
interrupt_id=resume_interrupt_id,
|
|
939
|
+
),
|
|
802
940
|
config=config,
|
|
803
941
|
context=native_context,
|
|
804
942
|
):
|
|
@@ -826,9 +964,23 @@ class LangGraphRunner(BaseRunner):
|
|
|
826
964
|
return
|
|
827
965
|
|
|
828
966
|
try:
|
|
829
|
-
stream_input =
|
|
967
|
+
stream_input = (
|
|
968
|
+
self._checkpoint_resume_input(
|
|
969
|
+
state,
|
|
970
|
+
payload_provided=resume_payload_provided,
|
|
971
|
+
interrupt_id=resume_interrupt_id,
|
|
972
|
+
)
|
|
973
|
+
if is_checkpoint_resume
|
|
974
|
+
else (Command(resume=state) if is_resume else state)
|
|
975
|
+
)
|
|
976
|
+
# stream_mode 含 "custom" 才会产生 on_custom_stream 事件(custom writer);
|
|
977
|
+
# 保留默认 "values" 以兼容既有 on_chain_end/graph_update 消费。
|
|
830
978
|
stream_kwargs = {"version": "v2", "config": config}
|
|
831
|
-
if
|
|
979
|
+
if self._callable_accepts_keyword(self._agent.astream_events, "stream_mode"):
|
|
980
|
+
stream_kwargs["stream_mode"] = ["values", "custom"]
|
|
981
|
+
if native_context and self._callable_accepts_keyword(
|
|
982
|
+
self._agent.astream_events, "context"
|
|
983
|
+
):
|
|
832
984
|
stream_kwargs["context"] = native_context
|
|
833
985
|
async for event in self._agent.astream_events(stream_input, **stream_kwargs):
|
|
834
986
|
event_kind = event.get("event", "")
|
|
@@ -855,7 +1007,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
855
1007
|
reasoning = getattr(chunk, "reasoning_content", None)
|
|
856
1008
|
if not reasoning and hasattr(chunk, "additional_kwargs"):
|
|
857
1009
|
reasoning = chunk.additional_kwargs.get("reasoning_content")
|
|
858
|
-
|
|
1010
|
+
|
|
859
1011
|
if reasoning:
|
|
860
1012
|
accumulated_reasoning += reasoning
|
|
861
1013
|
yield {"delta": reasoning, "type": "thinking"}
|
|
@@ -865,9 +1017,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
865
1017
|
content = self._filter_tool_tags(chunk.content)
|
|
866
1018
|
if isinstance(content, str):
|
|
867
1019
|
if accumulated_reasoning and content.startswith(accumulated_reasoning):
|
|
868
|
-
content = content[len(accumulated_reasoning):]
|
|
1020
|
+
content = content[len(accumulated_reasoning) :]
|
|
869
1021
|
elif reasoning and content.startswith(reasoning):
|
|
870
|
-
content = content[len(reasoning):]
|
|
1022
|
+
content = content[len(reasoning) :]
|
|
871
1023
|
if content:
|
|
872
1024
|
for part in inline_reasoning_parser.feed(content):
|
|
873
1025
|
if not part.text or not part.text.strip():
|
|
@@ -888,6 +1040,59 @@ class LangGraphRunner(BaseRunner):
|
|
|
888
1040
|
if run_key not in stream_usage_run_keys:
|
|
889
1041
|
record_model_usage(event, last_usage or usage)
|
|
890
1042
|
|
|
1043
|
+
elif event_kind == "on_chain_stream":
|
|
1044
|
+
# node 内 get_stream_writer() 写入的自定义数据,经 stream_mode 含
|
|
1045
|
+
# "custom" 时,astream_events 包成 on_chain_stream,chunk 为
|
|
1046
|
+
# (mode, value) tuple:("custom", value) 是 writer 透传内容,
|
|
1047
|
+
# ("values", state) 是 state 快照(忽略,终态走 on_chain_end)。
|
|
1048
|
+
# 编排方常用 custom writer 把"调远端 agent/子图"的流式增量透传出来。
|
|
1049
|
+
chunk = event.get("data", {}).get("chunk")
|
|
1050
|
+
if not (
|
|
1051
|
+
isinstance(chunk, tuple) and len(chunk) == 2 and chunk[0] == "custom"
|
|
1052
|
+
):
|
|
1053
|
+
continue
|
|
1054
|
+
data = chunk[1]
|
|
1055
|
+
if isinstance(data, str):
|
|
1056
|
+
accumulated_text += data
|
|
1057
|
+
yield {"delta": data, "type": "text"}
|
|
1058
|
+
continue
|
|
1059
|
+
if isinstance(data, Mapping):
|
|
1060
|
+
custom_type = str(data.get("type") or "text")
|
|
1061
|
+
if custom_type in ("tool_call", "tool_result"):
|
|
1062
|
+
# 结构化工具事件:透传完整 payload(tool_name/tool_args/
|
|
1063
|
+
# tool_output 等),不计入正文,供 UI 渲染工具卡片。
|
|
1064
|
+
out = {"type": custom_type}
|
|
1065
|
+
out.update({k: v for k, v in data.items() if k != "type"})
|
|
1066
|
+
yield out
|
|
1067
|
+
continue
|
|
1068
|
+
custom_delta = ""
|
|
1069
|
+
for key in ("delta", "text", "content", "output", "data"):
|
|
1070
|
+
value = data.get(key)
|
|
1071
|
+
if isinstance(value, str) and value:
|
|
1072
|
+
custom_delta = value
|
|
1073
|
+
break
|
|
1074
|
+
if not custom_delta:
|
|
1075
|
+
continue
|
|
1076
|
+
replace = bool(data.get("replace"))
|
|
1077
|
+
if custom_type == "thinking":
|
|
1078
|
+
accumulated_reasoning = (
|
|
1079
|
+
custom_delta
|
|
1080
|
+
if replace
|
|
1081
|
+
else accumulated_reasoning + custom_delta
|
|
1082
|
+
)
|
|
1083
|
+
else:
|
|
1084
|
+
accumulated_text = (
|
|
1085
|
+
custom_delta if replace else accumulated_text + custom_delta
|
|
1086
|
+
)
|
|
1087
|
+
out = {"delta": custom_delta, "type": custom_type}
|
|
1088
|
+
if replace:
|
|
1089
|
+
out["replace"] = True
|
|
1090
|
+
yield out
|
|
1091
|
+
continue
|
|
1092
|
+
if data is not None:
|
|
1093
|
+
accumulated_text += str(data)
|
|
1094
|
+
yield {"delta": str(data), "type": "text"}
|
|
1095
|
+
|
|
891
1096
|
elif event_kind == "on_tool_start":
|
|
892
1097
|
emitted_non_text_event = True
|
|
893
1098
|
yield {
|
|
@@ -896,23 +1101,35 @@ class LangGraphRunner(BaseRunner):
|
|
|
896
1101
|
"tool_args": event.get("data", {}).get("input", {}),
|
|
897
1102
|
"run_id": event.get("run_id"),
|
|
898
1103
|
}
|
|
899
|
-
|
|
1104
|
+
|
|
900
1105
|
elif event_kind == "on_tool_end":
|
|
901
1106
|
emitted_non_text_event = True
|
|
902
1107
|
tool_output = event.get("data", {}).get("output", "")
|
|
1108
|
+
# LangGraph returns a ToolMessage here for normal tools.
|
|
1109
|
+
# Preserve its content instead of serializing the repr,
|
|
1110
|
+
# otherwise structured output such as A2UI envelopes becomes
|
|
1111
|
+
# unparsable. Keep the callback run_id below: it is paired
|
|
1112
|
+
# with the preceding ``on_tool_start`` event on this stream.
|
|
1113
|
+
normalized_output = getattr(tool_output, "content", tool_output)
|
|
1114
|
+
if isinstance(tool_output, Mapping) and "content" in tool_output:
|
|
1115
|
+
normalized_output = tool_output["content"]
|
|
903
1116
|
yield {
|
|
904
1117
|
"type": "tool_result",
|
|
905
1118
|
"tool_name": event.get("name", "unknown"),
|
|
906
1119
|
"tool_args": event.get("data", {}).get("input", {}),
|
|
907
|
-
"tool_output":
|
|
1120
|
+
"tool_output": normalized_output,
|
|
908
1121
|
"run_id": event.get("run_id"),
|
|
909
1122
|
}
|
|
910
|
-
|
|
1123
|
+
|
|
911
1124
|
elif event_kind == "on_chain_end":
|
|
912
1125
|
output = event.get("data", {}).get("output", {})
|
|
913
1126
|
if isinstance(output, dict) and "__interrupt__" in output:
|
|
914
1127
|
emitted_non_text_event = True
|
|
915
|
-
yield {
|
|
1128
|
+
yield {
|
|
1129
|
+
"type": "interrupt",
|
|
1130
|
+
"interrupt_info": output["__interrupt__"],
|
|
1131
|
+
"session_id": session_id,
|
|
1132
|
+
}
|
|
916
1133
|
return
|
|
917
1134
|
extracted_output = self._extract_output(output)
|
|
918
1135
|
if extracted_output:
|
|
@@ -922,10 +1139,41 @@ class LangGraphRunner(BaseRunner):
|
|
|
922
1139
|
|
|
923
1140
|
except Exception as e:
|
|
924
1141
|
if "Interrupt" in type(e).__name__:
|
|
925
|
-
yield {
|
|
1142
|
+
yield {
|
|
1143
|
+
"type": "interrupt",
|
|
1144
|
+
"interrupt_info": self._get_interrupt_info(self._agent.get_state(config)),
|
|
1145
|
+
"session_id": session_id,
|
|
1146
|
+
}
|
|
926
1147
|
return
|
|
927
1148
|
raise
|
|
928
1149
|
|
|
1150
|
+
# goal-18(ksadk-web 人机交互):图因审批门(HITL)在流式中静默暂停时,
|
|
1151
|
+
# 这里把审批详情(action_requests)作为 approval 事件冒出,供 UI 渲染审批卡。
|
|
1152
|
+
# 此前流式路径只在 checkpoint 标 resumable,UI 拿不到"该批哪个工具/什么参数/允许哪些决定"。
|
|
1153
|
+
# 注:get_state 在部分 agent 上是 async,统一按 awaitable 处理;取不到则跳过,不破坏事件流。
|
|
1154
|
+
pending_approval = None
|
|
1155
|
+
try:
|
|
1156
|
+
_get_state = getattr(self._agent, "aget_state", None) or getattr(
|
|
1157
|
+
self._agent, "get_state", None
|
|
1158
|
+
)
|
|
1159
|
+
if _get_state is not None:
|
|
1160
|
+
_maybe_state = _get_state(config)
|
|
1161
|
+
if inspect.isawaitable(_maybe_state):
|
|
1162
|
+
_maybe_state = await _maybe_state
|
|
1163
|
+
pending_approval = self._get_interrupt_info(_maybe_state)
|
|
1164
|
+
except Exception:
|
|
1165
|
+
pending_approval = None
|
|
1166
|
+
if pending_approval:
|
|
1167
|
+
yield {
|
|
1168
|
+
"type": "approval",
|
|
1169
|
+
"interrupt_info": pending_approval,
|
|
1170
|
+
"session_id": session_id,
|
|
1171
|
+
}
|
|
1172
|
+
metadata = await self._latest_checkpoint_metadata(config)
|
|
1173
|
+
if metadata:
|
|
1174
|
+
yield {"type": "checkpoint", "metadata": metadata}
|
|
1175
|
+
return
|
|
1176
|
+
|
|
929
1177
|
for part in inline_reasoning_parser.flush():
|
|
930
1178
|
if not part.text or not part.text.strip():
|
|
931
1179
|
continue
|
|
@@ -940,35 +1188,49 @@ class LangGraphRunner(BaseRunner):
|
|
|
940
1188
|
if final_output_text:
|
|
941
1189
|
final_chunk = {"output": final_output_text, "type": "final"}
|
|
942
1190
|
usage = accumulated_model_usage() or final_output_usage or latest_stream_usage
|
|
943
|
-
last_usage =
|
|
1191
|
+
last_usage = (
|
|
1192
|
+
latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
|
|
1193
|
+
)
|
|
944
1194
|
if usage:
|
|
945
1195
|
final_chunk["usage"] = usage
|
|
946
1196
|
if last_usage:
|
|
947
1197
|
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
948
1198
|
yield final_chunk
|
|
949
1199
|
elif not emitted_non_text_event:
|
|
950
|
-
result = await self.invoke(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1200
|
+
result = await self.invoke({**invoke_payload, "_ksadk_force_graph_invoke": True})
|
|
1201
|
+
fallback_chunk: dict[str, Any] = {
|
|
1202
|
+
"output": result.get("output", ""),
|
|
1203
|
+
"type": "final",
|
|
1204
|
+
}
|
|
954
1205
|
usage = self._extract_usage(result)
|
|
955
1206
|
if usage:
|
|
956
|
-
|
|
1207
|
+
fallback_chunk["usage"] = usage
|
|
957
1208
|
last_usage = self._extract_last_usage(result)
|
|
958
1209
|
if last_usage:
|
|
959
|
-
|
|
960
|
-
yield
|
|
961
|
-
|
|
962
|
-
if isinstance(
|
|
963
|
-
yield {"type": "checkpoint", "metadata":
|
|
1210
|
+
fallback_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
1211
|
+
yield fallback_chunk
|
|
1212
|
+
checkpoint_metadata = result.get("metadata") if isinstance(result, dict) else None
|
|
1213
|
+
if isinstance(checkpoint_metadata, dict) and checkpoint_metadata.get("agentengine"):
|
|
1214
|
+
yield {"type": "checkpoint", "metadata": checkpoint_metadata}
|
|
964
1215
|
return
|
|
965
1216
|
else:
|
|
966
1217
|
final_chunk = {"output": accumulated_text, "type": "final"}
|
|
967
1218
|
state_usage = await self._latest_state_usage(config)
|
|
968
|
-
usage =
|
|
1219
|
+
usage = (
|
|
1220
|
+
accumulated_model_usage()
|
|
1221
|
+
or state_usage
|
|
1222
|
+
or final_output_usage
|
|
1223
|
+
or latest_stream_usage
|
|
1224
|
+
)
|
|
969
1225
|
if usage:
|
|
970
1226
|
final_chunk["usage"] = usage
|
|
971
|
-
last_usage =
|
|
1227
|
+
last_usage = (
|
|
1228
|
+
latest_model_usage()
|
|
1229
|
+
or state_usage
|
|
1230
|
+
or final_output_last_usage
|
|
1231
|
+
or latest_stream_usage
|
|
1232
|
+
or usage
|
|
1233
|
+
)
|
|
972
1234
|
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
973
1235
|
yield final_chunk
|
|
974
1236
|
|
|
@@ -980,6 +1242,6 @@ class LangGraphRunner(BaseRunner):
|
|
|
980
1242
|
"""过滤 <tool_call> 标签"""
|
|
981
1243
|
if not isinstance(content, str):
|
|
982
1244
|
return content
|
|
983
|
-
content = re.sub(r
|
|
984
|
-
content = re.sub(r
|
|
1245
|
+
content = re.sub(r"<tool_call>.*?</tool_call>", "", content, flags=re.DOTALL)
|
|
1246
|
+
content = re.sub(r"</?(?:tool_call|arg_key|arg_value)>", "", content)
|
|
985
1247
|
return content
|