agentengine-sdk-python 0.8.0__py3-none-any.whl → 0.8.2__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.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +43 -7
- agentengine_sdk_python-0.8.2.dist-info/RECORD +778 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +2 -0
- ksadk/a2a/_space_client_events.py +381 -0
- ksadk/a2a/bootstrap.py +0 -2
- ksadk/a2a/card.py +6 -111
- ksadk/a2a/control_plane.py +239 -1
- ksadk/a2a/event_adapter.py +156 -64
- ksadk/a2a/executor.py +195 -172
- ksadk/a2a/routes.py +12 -5
- ksadk/a2a/server.py +1 -8
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +43 -349
- ksadk/a2a_card.py +101 -0
- ksadk/a2ui/core.py +222 -53
- ksadk/agui/_agent_helpers.py +243 -0
- ksadk/agui/a2ui_projection.py +12 -5
- ksadk/agui/agent.py +474 -275
- ksadk/agui/config.py +11 -5
- ksadk/agui/routes.py +7 -15
- ksadk/api/client.py +530 -40
- ksadk/api/environment.py +27 -0
- ksadk/builders/code_builder.py +352 -53
- ksadk/builders/container_builder.py +71 -44
- ksadk/builders/framework_requirements.py +2 -1
- ksadk/builders/ks3_uploader.py +2 -2
- ksadk/builders/managed_runtime_builder.py +60 -28
- ksadk/cli/__init__.py +25 -0
- ksadk/cli/cmd_a2a.py +18 -31
- ksadk/cli/cmd_create.py +13 -26
- ksadk/cli/cmd_dashboard.py +15 -1
- ksadk/cli/cmd_deploy.py +28 -3
- ksadk/cli/cmd_eval.py +443 -0
- ksadk/cli/cmd_evalset.py +284 -0
- ksadk/cli/cmd_files.py +5 -1
- ksadk/cli/cmd_hermes.py +137 -163
- ksadk/cli/cmd_invoke.py +28 -38
- ksadk/cli/cmd_launch.py +12 -0
- ksadk/cli/cmd_managed_runtime.py +95 -0
- ksadk/cli/cmd_observe.py +85 -0
- ksadk/cli/cmd_openclaw.py +85 -479
- ksadk/cli/cmd_replay.py +28 -4
- ksadk/cli/cmd_run.py +29 -67
- ksadk/cli/cmd_studio.py +201 -0
- ksadk/cli/cmd_web.py +15 -6
- ksadk/cli/dry_run.py +2 -1
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/global_options.py +3 -1
- ksadk/cli/hermes_env.py +151 -0
- ksadk/cli/invoke_payload.py +49 -0
- ksadk/cli/network_options.py +7 -5
- ksadk/cli/openclaw_env.py +458 -0
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/ui.py +6 -0
- ksadk/cli/workflow_common.py +5 -5
- ksadk/codex/__init__.py +3 -3
- ksadk/codex/client.py +833 -49
- ksadk/codex/phase.py +1 -1
- ksadk/codex/runtime.py +720 -127
- ksadk/common/kop_client.py +232 -0
- ksadk/configs/__init__.py +1 -3
- ksadk/configs/env_registry.py +244 -45
- ksadk/configs/env_registry_pcm.py +181 -0
- ksadk/configs/env_var_spec.py +18 -0
- ksadk/configs/global_config.py +6 -1
- ksadk/configs/settings.py +5 -108
- ksadk/context_engine/__init__.py +98 -0
- ksadk/context_engine/assembler.py +176 -0
- ksadk/context_engine/baseline.py +419 -0
- ksadk/context_engine/cache_observability.py +228 -0
- ksadk/context_engine/capabilities.py +443 -0
- ksadk/context_engine/contributors.py +344 -0
- ksadk/context_engine/hosted_pipeline.py +417 -0
- ksadk/context_engine/models.py +117 -0
- ksadk/context_engine/planner.py +698 -0
- ksadk/context_engine/policies.py +319 -0
- ksadk/context_engine/projection.py +31 -0
- ksadk/context_engine/shadow_plan.py +266 -0
- ksadk/context_engine/tokenizer.py +164 -0
- ksadk/conversations/__init__.py +85 -94
- ksadk/conversations/context.py +126 -6
- ksadk/conversations/message_projection.py +292 -28
- ksadk/conversations/model_context.py +49 -3
- ksadk/conversations/runtime_compaction.py +207 -5
- ksadk/conversations/runtime_input.py +193 -3
- ksadk/conversations/runtime_invocation.py +81 -3
- ksadk/conversations/runtime_observability.py +157 -4
- ksadk/conversations/runtime_payloads.py +42 -0
- ksadk/conversations/runtime_preparation.py +280 -2
- ksadk/conversations/runtime_resume.py +71 -32
- ksadk/conversations/runtime_stream_events.py +94 -9
- ksadk/conversations/runtime_streaming.py +175 -22
- ksadk/conversations/semantic_summary.py +311 -1
- ksadk/conversations/session_lock.py +51 -0
- ksadk/deployment/env_forward.py +72 -0
- ksadk/deployment/managed_runtime.py +38 -0
- ksadk/deployment/manager.py +24 -8
- ksadk/deployment/providers/serverless.py +228 -92
- ksadk/evaluation/__init__.py +116 -0
- ksadk/evaluation/a2a_adapter.py +452 -0
- ksadk/evaluation/adapters.py +67 -0
- ksadk/evaluation/agent_eval_client.py +395 -0
- ksadk/evaluation/cloud_binding.py +80 -0
- ksadk/evaluation/cloud_converter.py +194 -0
- ksadk/evaluation/cloud_service.py +199 -0
- ksadk/evaluation/contracts.py +478 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +627 -0
- ksadk/evaluation/evidence.py +258 -0
- ksadk/evaluation/executor.py +143 -0
- ksadk/evaluation/local_adapter.py +550 -0
- ksadk/evaluation/service_env.py +27 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/studio_build_adapter.py +253 -0
- ksadk/evaluation/target.py +82 -0
- ksadk/events/__init__.py +13 -13
- ksadk/events/_v1_compat/__init__.py +1 -0
- ksadk/events/_v1_compat/models.py +299 -0
- ksadk/events/_v1_compat/parser.py +247 -0
- ksadk/events/_v1_compat/projection.py +753 -0
- ksadk/events/adapters/__init__.py +34 -0
- ksadk/events/adapters/_a2a_snapshot.py +622 -0
- ksadk/events/adapters/_a2a_support.py +275 -0
- ksadk/events/adapters/_codex_interactions.py +375 -0
- ksadk/events/adapters/_codex_items.py +801 -0
- ksadk/events/adapters/_codex_validators.py +311 -0
- ksadk/events/adapters/_langgraph_support.py +785 -0
- ksadk/events/adapters/a2a.py +841 -0
- ksadk/events/adapters/adk.py +503 -0
- ksadk/events/adapters/codex.py +717 -0
- ksadk/events/adapters/langgraph.py +745 -0
- ksadk/events/canonical.py +440 -0
- ksadk/events/canonical_replay.py +497 -0
- ksadk/events/canonical_store.py +468 -0
- ksadk/events/cold_recovery.py +257 -0
- ksadk/events/content.py +89 -0
- ksadk/events/identity.py +86 -0
- ksadk/events/pipeline.py +455 -0
- ksadk/events/projections.py +66 -0
- ksadk/events/reducer.py +661 -0
- ksadk/events/replay.py +3 -36
- ksadk/events/runtime_event.py +20 -256
- ksadk/events/session_event.py +324 -0
- ksadk/events/store.py +3 -316
- ksadk/events/v1_compat.py +41 -0
- ksadk/harness/__init__.py +3 -2
- ksadk/harness/app.py +114 -123
- ksadk/harness/config.py +1 -1
- ksadk/harness/runtime.py +467 -0
- ksadk/hermes_terminal.py +5 -0
- ksadk/identity/resolver.py +4 -3
- ksadk/interaction/__init__.py +28 -0
- ksadk/interaction/contracts.py +136 -0
- ksadk/interaction/ledger.py +189 -0
- ksadk/interaction/provider.py +122 -0
- ksadk/interaction/providers/__init__.py +52 -0
- ksadk/interaction/providers/adk.py +40 -0
- ksadk/interaction/providers/codex.py +94 -0
- ksadk/interaction/providers/langgraph.py +58 -0
- ksadk/kernel/__init__.py +117 -0
- ksadk/kernel/authorization.py +258 -0
- ksadk/kernel/bootstrap.py +1074 -0
- ksadk/kernel/contract_fingerprints.py +61 -0
- ksadk/kernel/contracts.py +362 -0
- ksadk/kernel/control.py +219 -0
- ksadk/kernel/errors.py +79 -0
- ksadk/kernel/ingress.py +1102 -0
- ksadk/kernel/mapping.py +111 -0
- ksadk/kernel/memory_store.py +1133 -0
- ksadk/kernel/postgres_store.py +1756 -0
- ksadk/kernel/recovery.py +452 -0
- ksadk/kernel/runtime_identity.py +103 -0
- ksadk/kernel/sql/001_agent_kernel.sql +120 -0
- ksadk/kernel/sqlite_store.py +1410 -0
- ksadk/kernel/state.py +145 -0
- ksadk/kernel/store.py +232 -0
- ksadk/kernel/worker.py +953 -0
- ksadk/knowledge_base/client.py +22 -4
- ksadk/knowledge_base/service.py +32 -5
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +16 -9
- ksadk/memory/__init__.py +61 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
- ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
- ksadk/memory/adk/backends/sdk_ltm_backend.py +394 -5
- ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
- ksadk/memory/adk/long_term_memory.py +3 -2
- ksadk/memory/coordinator.py +461 -0
- ksadk/memory/events.py +202 -0
- ksadk/memory/extraction.py +228 -0
- ksadk/memory/ltm_backend_factory.py +16 -3
- ksadk/memory/models.py +316 -0
- ksadk/memory/policy.py +215 -0
- ksadk/memory/provider.py +47 -0
- ksadk/memory/provider_adapter.py +159 -0
- ksadk/memory/provider_resolver.py +71 -0
- ksadk/memory/providers/__init__.py +5 -0
- ksadk/memory/providers/local_sqlite.py +490 -0
- ksadk/memory/resolved_policy.py +145 -0
- ksadk/memory/service.py +198 -8
- ksadk/memory/tool.py +4 -0
- ksadk/model_proxy/bootstrap.py +16 -9
- ksadk/model_proxy/config.py +22 -1
- ksadk/model_proxy/detect.py +202 -9
- ksadk/model_proxy/namespace.py +67 -15
- ksadk/model_proxy/server.py +168 -6
- ksadk/model_proxy/transform.py +105 -3
- ksadk/observability/__init__.py +25 -0
- ksadk/observability/session_log.py +420 -0
- ksadk/observability/trajectory.py +105 -0
- ksadk/prompts/__init__.py +71 -0
- ksadk/prompts/compiler.py +215 -0
- ksadk/prompts/models.py +82 -0
- ksadk/prompts/projection.py +89 -0
- ksadk/prompts/resolved.py +137 -0
- ksadk/prompts/sources.py +233 -0
- ksadk/runners/_langgraph_runner_streams.py +822 -0
- ksadk/runners/adk_runner.py +14 -31
- ksadk/runners/base_runner.py +44 -3
- ksadk/runners/factory.py +3 -3
- ksadk/runners/langgraph_runner.py +252 -433
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +16 -0
- ksadk/runtime/__init__.py +67 -41
- ksadk/runtime/_runner_adapter/__init__.py +1 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
- ksadk/runtime/adapter.py +199 -16
- ksadk/runtime/conversation_execution.py +687 -0
- ksadk/runtime/executor.py +392 -0
- ksadk/runtime/factory.py +342 -0
- ksadk/runtime/framework_adapters.py +6 -12
- ksadk/runtime/hosted_finalizer.py +268 -0
- ksadk/runtime/launch.py +61 -0
- ksadk/runtime/preprocessing.py +49 -14
- ksadk/runtime/runner_adapter.py +330 -391
- ksadk/runtime/runner_loading.py +36 -0
- ksadk/runtime/usage.py +33 -0
- ksadk/sandbox/backends/e2b.py +36 -3
- ksadk/server/__init__.py +39 -4
- ksadk/server/app.py +18 -492
- ksadk/server/composition.py +36 -2
- ksadk/server/factory.py +210 -149
- ksadk/server/routes/common.py +15 -46
- ksadk/server/routes/control.py +13 -9
- ksadk/server/routes/kernel_ingress.py +258 -0
- ksadk/server/routes/misc.py +10 -33
- ksadk/server/routes/models.py +14 -6
- ksadk/server/routes/openai_compat.py +129 -27
- ksadk/server/routes/projection.py +84 -16
- ksadk/server/routes/run.py +347 -460
- ksadk/server/routes/sessions.py +37 -40
- ksadk/server/routes/streaming.py +52 -7
- ksadk/server/routes/workspace.py +54 -21
- ksadk/server/static/assets/{ArtifactsPanel-Cjnqt-8W.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-BqyqZfuB.js → CodeBlock-DOH6MVcn.js} +4 -4
- ksadk/server/static/assets/{MathMessageMarkdown-BpH7jiFM.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
- ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
- ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
- ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
- ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
- ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
- ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
- ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
- ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
- ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
- ksadk/server/static/assets/{chunk-FMBD7UC4-BGhvzDYt.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-D98vPd-E.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
- ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
- ksadk/server/static/assets/{chunk-4BX2VUAB-CsWIQRhc.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
- ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
- ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
- ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
- ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
- ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-CX3yotHq.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
- ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
- ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
- ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
- ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
- ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
- ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
- ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
- ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
- ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
- ksadk/server/static/assets/{dist-D82mtD2s.js → dist-8hqcx0sU.js} +1 -1
- ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
- ksadk/server/static/assets/{dist-DHn6nBzr.js → dist-BE7bu-DL.js} +1 -1
- ksadk/server/static/assets/{dist-eoW6tZXA.js → dist-BVDffZ0U.js} +1 -1
- ksadk/server/static/assets/{dist-C2xjE7cO.js → dist-BX8z72IC.js} +1 -1
- ksadk/server/static/assets/{dist-CgF3fj15.js → dist-Bf2M8m3N.js} +1 -1
- ksadk/server/static/assets/{dist-Bs-jQcRX.js → dist-BjU6y-A2.js} +1 -1
- ksadk/server/static/assets/{dist-Djgl2wVZ.js → dist-BytlxIDT.js} +1 -1
- ksadk/server/static/assets/{dist-CVQRZjPu.js → dist-CSh_DE14.js} +1 -1
- ksadk/server/static/assets/{dist-6CVKuTAe.js → dist-CXYYBw3_.js} +1 -1
- ksadk/server/static/assets/{dist-DlzCRy0S.js → dist-C_wsv-Qd.js} +1 -1
- ksadk/server/static/assets/{dist-DF06ORQo.js → dist-ClxGviKw.js} +1 -1
- ksadk/server/static/assets/{dist-Cf23NY4L.js → dist-CttYUqzS.js} +1 -1
- ksadk/server/static/assets/{dist-ivOya1LD.js → dist-DNp6rLRA.js} +1 -1
- ksadk/server/static/assets/{dist-BibcG1V9.js → dist-DUX-id_F.js} +1 -1
- ksadk/server/static/assets/{dist-BUHRxvDG.js → dist-DmldrHd_.js} +1 -1
- ksadk/server/static/assets/{dist-BZ9n9AjO.js → dist-DnfXs8Vn.js} +2 -2
- ksadk/server/static/assets/{dist-DqNnNFrw.js → dist-Dq9gLD7L.js} +1 -1
- ksadk/server/static/assets/{dist-CGcp2ex8.js → dist-W-TIVntx.js} +1 -1
- ksadk/server/static/assets/{dist-Cwt7koer.js → dist-dDdADKFA.js} +1 -1
- ksadk/server/static/assets/{dist-C7-3jInC.js → dist-dydu68Fl.js} +1 -1
- ksadk/server/static/assets/{dist-C99THtIA.js → dist-ngtN0DJB.js} +1 -1
- ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
- ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
- ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
- ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
- ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
- ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
- ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-DLOHGvWw.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-BIzKeejT.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-BWHoFSw2.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
- ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
- ksadk/server/static/assets/{linear-CvMBw9cA.js → linear-BHjG8b-J.js} +1 -1
- ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-CSAQxEAe.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
- ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
- ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
- ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
- ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-CxIR4JXI.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
- ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
- ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
- ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-DpGa6EAV.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-NTKNGGe5.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
- ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
- ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
- ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-jqoYs3jQ.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
- ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
- ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
- ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
- ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +26 -9
- ksadk/sessions/_local_service_sync.py +688 -0
- ksadk/sessions/_local_tables.py +21 -0
- ksadk/sessions/_postgres_schema.py +305 -0
- ksadk/sessions/_postgres_tables.py +15 -0
- ksadk/sessions/base.py +80 -1
- ksadk/sessions/in_memory.py +53 -2
- ksadk/sessions/local_service.py +63 -604
- ksadk/sessions/postgres_service.py +58 -148
- ksadk/sessions/resilient.py +35 -1
- ksadk/skills/runtime/backends/e2b.py +1 -1
- ksadk/skills/service_client.py +13 -3
- 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 +1598 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +286 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/api_memory_routes.py +82 -0
- ksadk/studio/authoring.py +749 -0
- ksadk/studio/authoring_coordinator.py +1017 -0
- ksadk/studio/builder.py +397 -0
- ksadk/studio/capabilities.py +368 -0
- ksadk/studio/cloud.py +2099 -0
- ksadk/studio/codex_agent_service.py +694 -0
- ksadk/studio/codex_authoring.py +469 -0
- ksadk/studio/codex_builder.py +437 -0
- ksadk/studio/codex_manifest.py +307 -0
- ksadk/studio/codex_run.py +395 -0
- ksadk/studio/compiler.py +190 -0
- ksadk/studio/contracts.py +755 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/event_store.py +214 -0
- ksadk/studio/framework_run.py +342 -0
- ksadk/studio/hosted_kernel.py +317 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/manifest_resolver.py +131 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +629 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +275 -0
- ksadk/studio/otel_trace.py +1060 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/pcm_memory.py +85 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +327 -0
- ksadk/studio/resource_catalog.py +1260 -0
- ksadk/studio/run_service.py +1133 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +365 -0
- ksadk/studio/service.py +2249 -0
- ksadk/studio/shared_web.py +1004 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
- ksadk/studio/static/assets/index-CYekR2Xv.js +260 -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 +722 -0
- ksadk/studio/validator.py +265 -0
- ksadk/studio/workspace.py +135 -0
- ksadk/tools/gateway.py +9 -0
- ksadk/toolsets/__init__.py +27 -1
- ksadk/toolsets/a2a.py +426 -0
- ksadk/tracing/__init__.py +19 -21
- ksadk/tracing/setup.py +317 -403
- ksadk/tracing/span_utils.py +0 -44
- ksadk/version.py +1 -1
- ksadk_runtime_common/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
- ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.8.0.dist-info/RECORD +0 -578
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/events/parser.py +0 -191
- ksadk/harness/runner.py +0 -211
- ksadk/runners/codex_runner.py +0 -132
- ksadk/runners/utils/langfuse.py +0 -230
- ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
- ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +0 -1
- ksadk/server/static/assets/arc-CFKwSk_B.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +0 -1
- ksadk/server/static/assets/architectureDiagram-3BPJPVTR-Dej8jX8c.js +0 -36
- ksadk/server/static/assets/blockDiagram-GPEHLZMM-1F64jqvy.js +0 -132
- ksadk/server/static/assets/c4Diagram-AAUBKEIU-9q7AUAKb.js +0 -10
- ksadk/server/static/assets/channel-iUGv2N24.js +0 -1
- ksadk/server/static/assets/chunk-2J33WTMH-6qgwJm7Q.js +0 -1
- ksadk/server/static/assets/chunk-55IACEB6-BK8paA20.js +0 -1
- ksadk/server/static/assets/chunk-727SXJPM-B-JEhy7h.js +0 -206
- ksadk/server/static/assets/chunk-AQP2D5EJ-DYBvMHAa.js +0 -231
- ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +0 -1
- ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
- ksadk/server/static/assets/dagre-BM42HDAG-C7TguAWo.js +0 -4
- ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
- ksadk/server/static/assets/diagram-2AECGRRQ-Dd17_kws.js +0 -43
- ksadk/server/static/assets/diagram-5GNKFQAL-DVIClIil.js +0 -10
- ksadk/server/static/assets/diagram-KO2AKTUF-Df_3gYQa.js +0 -3
- ksadk/server/static/assets/diagram-LMA3HP47-BhLk6ZtA.js +0 -24
- ksadk/server/static/assets/diagram-OG6HWLK6-D4GNGvuu.js +0 -24
- ksadk/server/static/assets/dist-DyvYLMW9.js +0 -1
- ksadk/server/static/assets/erDiagram-TEJ5UH35-CnfRcY2M.js +0 -85
- ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +0 -1
- ksadk/server/static/assets/flowDiagram-I6XJVG4X-DGaJjGNJ.js +0 -162
- ksadk/server/static/assets/ganttDiagram-6RSMTGT7-DpCoB2g9.js +0 -292
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +0 -1
- ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-9IKVFBVT.js +0 -106
- ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
- ksadk/server/static/assets/index-QSwCHuB8.js +0 -177
- ksadk/server/static/assets/index-jOPmTOZH.css +0 -2
- ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +0 -1
- ksadk/server/static/assets/infoDiagram-5YYISTIA-CYIIsb8y.js +0 -2
- ksadk/server/static/assets/katex-DATssjp1.js +0 -265
- ksadk/server/static/assets/mermaid-parser.core-mGAcEwcq.js +0 -161
- ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +0 -1
- ksadk/server/static/assets/pieDiagram-4H26LBE5-By-ra07M.js +0 -30
- ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +0 -1
- ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DpW90iu3.js +0 -84
- ksadk/server/static/assets/stateDiagram-AJRCARHV-DGpd9yFG.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +0 -1
- ksadk/server/static/assets/vennDiagram-CIIHVFJN-l3Z7-wbP.js +0 -34
- ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +0 -1
- ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-BR54GPmr.js +0 -78
- ksadk/server/static/assets/xychartDiagram-2RQKCTM6-SkiH8GsK.js +0 -7
- ksadk/tracing/exporters/langfuse_exporter.py +0 -460
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
|
@@ -4,8 +4,10 @@ LangGraphRunner - LangGraph 框架运行时
|
|
|
4
4
|
直接透传 LangGraph 原生能力,最小化封装
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
7
10
|
import base64
|
|
8
|
-
import inspect
|
|
9
11
|
import os
|
|
10
12
|
import re
|
|
11
13
|
import uuid
|
|
@@ -14,19 +16,30 @@ from typing import Any, AsyncIterator, Dict, Mapping
|
|
|
14
16
|
from langgraph.types import Command
|
|
15
17
|
|
|
16
18
|
from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
|
|
17
|
-
from ksadk.
|
|
19
|
+
from ksadk.runners._langgraph_runner_streams import _LangGraphStreamMixin
|
|
18
20
|
from ksadk.runners.base_runner import BaseRunner
|
|
19
|
-
from ksadk.runners.
|
|
20
|
-
from ksadk.runners.utils import get_langfuse_callbacks, get_langfuse_metadata, load_agent_module
|
|
21
|
+
from ksadk.runners.utils import load_agent_module
|
|
21
22
|
from ksadk.sessions.continuity import LangGraphSessionAdapter
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
class LangGraphRunner(BaseRunner):
|
|
25
|
+
class LangGraphRunner(_LangGraphStreamMixin, BaseRunner):
|
|
25
26
|
"""LangGraph 框架运行时
|
|
26
27
|
|
|
27
28
|
透传原生 LangGraph 功能,支持任意 State 格式
|
|
28
29
|
"""
|
|
29
30
|
|
|
31
|
+
# ToolGateway approvals can arise after an otherwise terminal tool call;
|
|
32
|
+
# this runner opts into the runtime's semantic follow-up continuation.
|
|
33
|
+
supports_gateway_approval_semantic_resume = True
|
|
34
|
+
|
|
35
|
+
def __init__(self, detection_result: Any, project_dir: str):
|
|
36
|
+
super().__init__(detection_result, project_dir)
|
|
37
|
+
self._managed_checkpoint_lock = asyncio.Lock()
|
|
38
|
+
self._managed_checkpoint_prepared = False
|
|
39
|
+
self._managed_checkpoint_error: tuple[str, str] | None = None
|
|
40
|
+
self._managed_checkpoint_pool: Any = None
|
|
41
|
+
self._managed_checkpoint_namespace = ""
|
|
42
|
+
|
|
30
43
|
def load_agent(self) -> None:
|
|
31
44
|
self._load_agent(force_reload=False)
|
|
32
45
|
|
|
@@ -49,7 +62,13 @@ class LangGraphRunner(BaseRunner):
|
|
|
49
62
|
normalized = self.sync_process_model_env(model)
|
|
50
63
|
if normalized is None or self._agent is None:
|
|
51
64
|
return
|
|
52
|
-
|
|
65
|
+
# Studio's generated graph reads the model environment while building
|
|
66
|
+
# each model turn. Reloading it here would discard the managed
|
|
67
|
+
# PostgreSQL checkpointer that was installed asynchronously below.
|
|
68
|
+
if (
|
|
69
|
+
normalized == getattr(self, "_loaded_model_name", None)
|
|
70
|
+
or self._managed_checkpoint_pool is not None
|
|
71
|
+
):
|
|
53
72
|
return
|
|
54
73
|
self._load_agent(force_reload=True)
|
|
55
74
|
|
|
@@ -62,24 +81,23 @@ class LangGraphRunner(BaseRunner):
|
|
|
62
81
|
if checkpointer is None:
|
|
63
82
|
checkpointer = getattr(agent, "_checkpointer", None)
|
|
64
83
|
if checkpointer is None:
|
|
84
|
+
error_code, error_reason = self._managed_checkpoint_error or ("", "")
|
|
65
85
|
return {
|
|
66
86
|
"Supported": False,
|
|
67
87
|
"Backend": "none",
|
|
68
88
|
"Scope": "unknown",
|
|
69
89
|
"Durable": False,
|
|
70
90
|
"SharedAcrossPods": False,
|
|
71
|
-
"
|
|
91
|
+
"ResumeMode": "none",
|
|
92
|
+
**({"ReasonCode": error_code} if error_code else {}),
|
|
93
|
+
"Reason": error_reason or "LangGraph graph has no configured checkpointer",
|
|
72
94
|
}
|
|
73
95
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
backend = "sqlite"
|
|
80
|
-
elif "postgres" in type_name:
|
|
81
|
-
backend = "postgres"
|
|
82
|
-
else:
|
|
96
|
+
backend = self._checkpoint_backend_from_saver(checkpointer)
|
|
97
|
+
if backend == "unknown":
|
|
98
|
+
# Some third-party savers hide their concrete type. Preserve the
|
|
99
|
+
# explicit legacy declaration for those cases, but never let it
|
|
100
|
+
# override a detectable in-memory saver.
|
|
83
101
|
backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
|
|
84
102
|
if backend == "local":
|
|
85
103
|
backend = "sqlite"
|
|
@@ -108,17 +126,28 @@ class LangGraphRunner(BaseRunner):
|
|
|
108
126
|
reason = "In-memory checkpoint cannot be recovered after process restart or across pods"
|
|
109
127
|
|
|
110
128
|
return {
|
|
111
|
-
|
|
129
|
+
# A local saver may be useful for interactive development, but it
|
|
130
|
+
# is not a native durable-resume capability in a hosted runtime.
|
|
131
|
+
"Supported": backend not in {"memory", "inmemory", "unknown", ""},
|
|
112
132
|
"Backend": backend,
|
|
113
133
|
"Scope": scope,
|
|
114
134
|
"Durable": durable,
|
|
115
135
|
"SharedAcrossPods": shared,
|
|
116
|
-
"ResumeMode": "time_travel",
|
|
136
|
+
"ResumeMode": "time_travel" if durable else "none",
|
|
137
|
+
**(
|
|
138
|
+
{"ReasonCode": "CHECKPOINTER_NOT_DURABLE"}
|
|
139
|
+
if backend in {"memory", "inmemory", "unknown", ""}
|
|
140
|
+
else {}
|
|
141
|
+
),
|
|
117
142
|
"Reason": reason,
|
|
118
143
|
}
|
|
119
144
|
|
|
120
145
|
def get_runtime_capabilities(self) -> dict[str, Any]:
|
|
121
146
|
capabilities = super().get_runtime_capabilities()
|
|
147
|
+
capabilities["model_call_boundaries"] = True
|
|
148
|
+
reason_code = str(capabilities["Checkpoint"].get("ReasonCode") or "")
|
|
149
|
+
if reason_code:
|
|
150
|
+
capabilities["ResumeRun"]["ReasonCode"] = reason_code
|
|
122
151
|
capabilities["SessionContinuity"] = {
|
|
123
152
|
"Supported": True,
|
|
124
153
|
"Type": (
|
|
@@ -129,15 +158,149 @@ class LangGraphRunner(BaseRunner):
|
|
|
129
158
|
}
|
|
130
159
|
return capabilities
|
|
131
160
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
161
|
+
@staticmethod
|
|
162
|
+
def _checkpoint_backend_from_saver(checkpointer: Any) -> str:
|
|
163
|
+
if checkpointer is None:
|
|
164
|
+
return "unknown"
|
|
165
|
+
for saver_type in type(checkpointer).__mro__:
|
|
166
|
+
qualified_name = f"{saver_type.__module__}.{saver_type.__name__}".lower()
|
|
167
|
+
if "checkpoint.postgres" in qualified_name or "postgressaver" in qualified_name:
|
|
168
|
+
return "postgres"
|
|
169
|
+
if "checkpoint.sqlite" in qualified_name or "sqlitesaver" in qualified_name:
|
|
170
|
+
return "sqlite"
|
|
171
|
+
if "checkpoint.memory" in qualified_name or saver_type.__name__.lower() in {
|
|
172
|
+
"memorysaver",
|
|
173
|
+
"inmemorysaver",
|
|
174
|
+
}:
|
|
175
|
+
return "memory"
|
|
176
|
+
return "unknown"
|
|
135
177
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
178
|
+
@staticmethod
|
|
179
|
+
def _env_flag(name: str) -> bool:
|
|
180
|
+
return str(os.getenv(name) or "").strip().lower() in {"1", "true", "yes", "on"}
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def _resolve_checkpoint_namespace() -> str:
|
|
184
|
+
session_namespace = str(os.getenv("KSADK_SESSION_NAMESPACE") or "").strip()
|
|
185
|
+
if session_namespace:
|
|
186
|
+
return session_namespace
|
|
187
|
+
agent_id = str(
|
|
188
|
+
os.getenv("AGENTENGINE_AGENT_ID") or os.getenv("KSADK_AGENT_ID") or "default"
|
|
189
|
+
).strip()
|
|
190
|
+
return f"agent:{agent_id}"
|
|
191
|
+
|
|
192
|
+
async def _create_managed_postgres_saver(self, dsn: str) -> tuple[Any, Any]:
|
|
193
|
+
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
|
194
|
+
from psycopg.rows import dict_row
|
|
195
|
+
from psycopg_pool import AsyncConnectionPool
|
|
196
|
+
|
|
197
|
+
timeout = max(0.1, float(os.getenv("KSADK_SESSION_CONNECT_TIMEOUT") or "5"))
|
|
198
|
+
pool = AsyncConnectionPool(
|
|
199
|
+
conninfo=dsn,
|
|
200
|
+
min_size=1,
|
|
201
|
+
max_size=10,
|
|
202
|
+
open=False,
|
|
203
|
+
timeout=timeout,
|
|
204
|
+
kwargs={
|
|
205
|
+
"autocommit": True,
|
|
206
|
+
"prepare_threshold": 0,
|
|
207
|
+
"row_factory": dict_row,
|
|
208
|
+
},
|
|
209
|
+
)
|
|
210
|
+
try:
|
|
211
|
+
await pool.open(wait=True, timeout=timeout)
|
|
212
|
+
saver = AsyncPostgresSaver(pool)
|
|
213
|
+
await saver.setup()
|
|
214
|
+
return saver, pool
|
|
215
|
+
except Exception:
|
|
216
|
+
await pool.close()
|
|
217
|
+
raise
|
|
140
218
|
|
|
219
|
+
async def prepare_runtime_capabilities(self) -> None:
|
|
220
|
+
"""Install the managed saver before a graph can begin an interaction.
|
|
221
|
+
|
|
222
|
+
The graph module must opt into this seam by exporting
|
|
223
|
+
``ksadk_graph_factory(*, checkpointer)``. We never mutate a compiled
|
|
224
|
+
graph's private attributes: failed configuration remains fail-closed
|
|
225
|
+
and capability discovery honestly reports why native resume is absent.
|
|
226
|
+
"""
|
|
227
|
+
if self._managed_checkpoint_prepared:
|
|
228
|
+
return
|
|
229
|
+
async with self._managed_checkpoint_lock:
|
|
230
|
+
if self._managed_checkpoint_prepared:
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
checkpointer = getattr(self._agent, "checkpointer", None)
|
|
234
|
+
if checkpointer is None:
|
|
235
|
+
checkpointer = getattr(self._agent, "_checkpointer", None)
|
|
236
|
+
if self._checkpoint_backend_from_saver(checkpointer) == "postgres":
|
|
237
|
+
self._managed_checkpoint_namespace = self._resolve_checkpoint_namespace()
|
|
238
|
+
self._managed_checkpoint_prepared = True
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
dsn = str(
|
|
242
|
+
os.getenv("KSADK_LANGGRAPH_CHECKPOINT_DSN")
|
|
243
|
+
or os.getenv("KSADK_SESSION_DSN")
|
|
244
|
+
or ""
|
|
245
|
+
).strip()
|
|
246
|
+
if not self._env_flag("KSADK_LANGGRAPH_AUTO_CHECKPOINT") or not dsn:
|
|
247
|
+
self._managed_checkpoint_prepared = True
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
factory = getattr(self._module, "ksadk_graph_factory", None)
|
|
251
|
+
if not callable(factory):
|
|
252
|
+
self._managed_checkpoint_error = (
|
|
253
|
+
"LANGGRAPH_FACTORY_REQUIRED",
|
|
254
|
+
"LangGraph graph has no durable checkpointer; export "
|
|
255
|
+
"ksadk_graph_factory(*, checkpointer) for managed PostgreSQL checkpoints",
|
|
256
|
+
)
|
|
257
|
+
self._managed_checkpoint_prepared = True
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
pool = None
|
|
261
|
+
try:
|
|
262
|
+
saver, pool = await self._create_managed_postgres_saver(dsn)
|
|
263
|
+
managed_graph = factory(checkpointer=saver)
|
|
264
|
+
if not callable(getattr(managed_graph, "invoke", None)):
|
|
265
|
+
raise TypeError("ksadk_graph_factory must return a compiled LangGraph graph")
|
|
266
|
+
self._agent = managed_graph
|
|
267
|
+
self._managed_checkpoint_pool = pool
|
|
268
|
+
self._managed_checkpoint_namespace = self._resolve_checkpoint_namespace()
|
|
269
|
+
self._managed_checkpoint_error = None
|
|
270
|
+
except (ModuleNotFoundError, ImportError):
|
|
271
|
+
self._managed_checkpoint_error = (
|
|
272
|
+
"DEPENDENCY_MISSING",
|
|
273
|
+
"langgraph-checkpoint-postgres and psycopg are required "
|
|
274
|
+
"for managed checkpoints",
|
|
275
|
+
)
|
|
276
|
+
except Exception as exc: # noqa: BLE001
|
|
277
|
+
error_name = type(exc).__name__.lower()
|
|
278
|
+
self._managed_checkpoint_error = (
|
|
279
|
+
"SCHEMA_PERMISSION_DENIED"
|
|
280
|
+
if "privilege" in error_name or "permission" in error_name
|
|
281
|
+
else "DB_UNREACHABLE",
|
|
282
|
+
"Managed LangGraph PostgreSQL checkpointer initialization failed",
|
|
283
|
+
)
|
|
284
|
+
finally:
|
|
285
|
+
if pool is not None and self._managed_checkpoint_pool is None:
|
|
286
|
+
try:
|
|
287
|
+
await pool.close()
|
|
288
|
+
except Exception: # noqa: BLE001
|
|
289
|
+
pass
|
|
290
|
+
self._managed_checkpoint_prepared = True
|
|
291
|
+
|
|
292
|
+
async def close(self) -> None:
|
|
293
|
+
pool = self._managed_checkpoint_pool
|
|
294
|
+
self._managed_checkpoint_pool = None
|
|
295
|
+
if pool is not None:
|
|
296
|
+
await pool.close()
|
|
297
|
+
await super().close()
|
|
298
|
+
|
|
299
|
+
def _get_config(self, session_id: str) -> dict:
|
|
300
|
+
"""获取运行配置"""
|
|
301
|
+
config = {"configurable": {"thread_id": session_id}}
|
|
302
|
+
if self._managed_checkpoint_namespace:
|
|
303
|
+
config["configurable"]["checkpoint_ns"] = self._managed_checkpoint_namespace
|
|
141
304
|
return config
|
|
142
305
|
|
|
143
306
|
@staticmethod
|
|
@@ -188,6 +351,38 @@ class LangGraphRunner(BaseRunner):
|
|
|
188
351
|
resume_value = {interrupt_id: value} if interrupt_id else value
|
|
189
352
|
return Command(resume=resume_value)
|
|
190
353
|
|
|
354
|
+
@staticmethod
|
|
355
|
+
def _is_gateway_approval_semantic_resume(value: Any) -> bool:
|
|
356
|
+
"""Whether ``value`` came from a completed ToolGateway approval.
|
|
357
|
+
|
|
358
|
+
Native LangGraph ``interrupt()`` values must keep using
|
|
359
|
+
``Command(resume=...)``. In contrast, a ToolGateway can return an
|
|
360
|
+
``approval_required`` result from an otherwise normal tool call; the
|
|
361
|
+
graph then reaches its terminal node before KsADK shows the approval
|
|
362
|
+
card. Its approved tool result needs a new semantic graph turn.
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
return bool(
|
|
366
|
+
isinstance(value, Mapping)
|
|
367
|
+
and value.get("_ksadk_gateway_approval_resume") is True
|
|
368
|
+
and str(value.get("type") or "") == "function_call_output"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
@staticmethod
|
|
372
|
+
def _gateway_approval_follow_up_input() -> str:
|
|
373
|
+
"""A neutral prompt for the post-approval semantic continuation.
|
|
374
|
+
|
|
375
|
+
The actual approval response and durable tool result are already in
|
|
376
|
+
the session history. Do not repeat their contents here: project hooks
|
|
377
|
+
commonly route by input keywords, and copying a workspace path back
|
|
378
|
+
into a synthetic user message can accidentally trigger another tool.
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
return (
|
|
382
|
+
"系统已完成此前获批的操作。请基于会话记录中的真实结果,直接向用户说明完成情况;"
|
|
383
|
+
"不要重试,也不要再次要求确认。"
|
|
384
|
+
)
|
|
385
|
+
|
|
191
386
|
@staticmethod
|
|
192
387
|
def _checkpoint_ref_from_state(state: Any) -> dict[str, Any]:
|
|
193
388
|
state_config = None
|
|
@@ -378,6 +573,12 @@ class LangGraphRunner(BaseRunner):
|
|
|
378
573
|
for msg in history:
|
|
379
574
|
role = msg.get("role")
|
|
380
575
|
content = msg.get("content", "")
|
|
576
|
+
# Runtime-owned tool/approval records are preserved in the durable
|
|
577
|
+
# transcript, but must not be taught back to LangGraph as plain text.
|
|
578
|
+
if isinstance(content, str) and content.startswith(
|
|
579
|
+
("[tool_call]", "[tool_result]", "[approval_request]", "[approval_response]")
|
|
580
|
+
):
|
|
581
|
+
continue
|
|
381
582
|
if role == "user":
|
|
382
583
|
messages.append(HumanMessage(content=content))
|
|
383
584
|
elif role in ("assistant", "model"):
|
|
@@ -575,6 +776,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
575
776
|
1. 简化格式: {"input": "hello"} - 自动转换为 messages
|
|
576
777
|
2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
|
|
577
778
|
"""
|
|
779
|
+
await self.prepare_runtime_capabilities()
|
|
578
780
|
payload = dict(input_data)
|
|
579
781
|
force_graph_invoke = bool(payload.pop("_ksadk_force_graph_invoke", False))
|
|
580
782
|
if not force_graph_invoke and hasattr(self._agent, "astream_events"):
|
|
@@ -586,6 +788,16 @@ class LangGraphRunner(BaseRunner):
|
|
|
586
788
|
resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
|
|
587
789
|
resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
|
|
588
790
|
resume_value = payload.get("input")
|
|
791
|
+
is_gateway_approval_resume = bool(
|
|
792
|
+
is_resume and self._is_gateway_approval_semantic_resume(resume_value)
|
|
793
|
+
)
|
|
794
|
+
if is_gateway_approval_resume:
|
|
795
|
+
# ``Command(resume=...)`` only works for a graph that actually
|
|
796
|
+
# yielded LangGraph's native interrupt. ToolGateway approvals are
|
|
797
|
+
# intercepted after a normal tool result, when the graph already
|
|
798
|
+
# ended, so restart a normal turn from the persisted transcript.
|
|
799
|
+
payload["input"] = self._gateway_approval_follow_up_input()
|
|
800
|
+
resume_value = payload["input"]
|
|
589
801
|
checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
|
|
590
802
|
history = payload.pop("history", [])
|
|
591
803
|
native_context = self.build_native_context(payload.get("platform_context"))
|
|
@@ -600,14 +812,19 @@ class LangGraphRunner(BaseRunner):
|
|
|
600
812
|
# 判断输入格式 / resume
|
|
601
813
|
if is_checkpoint_resume:
|
|
602
814
|
state = resume_value
|
|
603
|
-
elif is_resume:
|
|
815
|
+
elif is_resume and not is_gateway_approval_resume:
|
|
604
816
|
# ``Command(resume=...)`` is delivered to the graph's suspended
|
|
605
817
|
# interrupt. A custom prepare-state hook is for new user input;
|
|
606
818
|
# applying it here can rewrite an approval decision into ordinary
|
|
607
819
|
# graph state and turn an approved HITL action into a rejection.
|
|
608
820
|
state = resume_value
|
|
609
821
|
elif self._has_prepare_state_hook():
|
|
610
|
-
state = self._prepare_state_with_hook(
|
|
822
|
+
state = self._prepare_state_with_hook(
|
|
823
|
+
payload,
|
|
824
|
+
session_id,
|
|
825
|
+
history,
|
|
826
|
+
is_resume=is_gateway_approval_resume,
|
|
827
|
+
)
|
|
611
828
|
else:
|
|
612
829
|
state = self._to_state(payload, history)
|
|
613
830
|
|
|
@@ -622,7 +839,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
622
839
|
config=config,
|
|
623
840
|
context=native_context,
|
|
624
841
|
)
|
|
625
|
-
elif is_resume:
|
|
842
|
+
elif is_resume and not is_gateway_approval_resume:
|
|
626
843
|
result = await self._invoke_graph(
|
|
627
844
|
Command(resume=state),
|
|
628
845
|
config=config,
|
|
@@ -834,414 +1051,16 @@ class LangGraphRunner(BaseRunner):
|
|
|
834
1051
|
)
|
|
835
1052
|
return events
|
|
836
1053
|
|
|
837
|
-
async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
|
|
838
|
-
"""流式调用 LangGraph 图"""
|
|
839
|
-
payload = dict(input_data)
|
|
840
|
-
payload.pop("_ksadk_force_graph_invoke", None)
|
|
841
|
-
session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
|
|
842
|
-
history = payload.pop("history", [])
|
|
843
|
-
is_resume = payload.pop("resume", False)
|
|
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")
|
|
848
|
-
checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
|
|
849
|
-
native_context = self.build_native_context(payload.get("platform_context"))
|
|
850
|
-
invoke_payload = dict(payload)
|
|
851
|
-
invoke_payload["session_id"] = session_id
|
|
852
|
-
if history:
|
|
853
|
-
invoke_payload["history"] = history
|
|
854
|
-
if is_resume:
|
|
855
|
-
invoke_payload["resume"] = True
|
|
856
|
-
if is_checkpoint_resume:
|
|
857
|
-
invoke_payload["checkpoint_resume"] = True
|
|
858
|
-
invoke_payload["resume_payload_provided"] = resume_payload_provided
|
|
859
|
-
invoke_payload["resume_interrupt_id"] = resume_interrupt_id
|
|
860
|
-
|
|
861
|
-
config = self._get_config(session_id)
|
|
862
|
-
if is_checkpoint_resume:
|
|
863
|
-
config = self._apply_checkpoint_resume_config(
|
|
864
|
-
config,
|
|
865
|
-
session_id=session_id,
|
|
866
|
-
checkpoint_ref=checkpoint_ref,
|
|
867
|
-
)
|
|
868
|
-
|
|
869
|
-
if is_checkpoint_resume:
|
|
870
|
-
state = resume_value
|
|
871
|
-
elif is_resume:
|
|
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)
|
|
877
|
-
else:
|
|
878
|
-
state = self._to_state(payload, history)
|
|
879
|
-
|
|
880
|
-
accumulated_text = ""
|
|
881
|
-
accumulated_reasoning = ""
|
|
882
|
-
inline_reasoning_parser = ReasoningMarkupParser()
|
|
883
|
-
emitted_non_text_event = False
|
|
884
|
-
final_output_text = ""
|
|
885
|
-
final_output_usage: dict[str, Any] = {}
|
|
886
|
-
final_output_last_usage: dict[str, Any] = {}
|
|
887
|
-
model_run_usages: dict[str, dict[str, Any]] = {}
|
|
888
|
-
model_run_order: list[str] = []
|
|
889
|
-
stream_usage_run_keys: set[str] = set()
|
|
890
|
-
latest_stream_usage: dict[str, Any] = {}
|
|
891
|
-
|
|
892
|
-
def model_run_key(
|
|
893
|
-
event: Mapping[str, Any],
|
|
894
|
-
*,
|
|
895
|
-
fallback_key: str | None = None,
|
|
896
|
-
) -> str:
|
|
897
|
-
raw_run_id = event.get("run_id")
|
|
898
|
-
return (
|
|
899
|
-
str(raw_run_id)
|
|
900
|
-
if raw_run_id
|
|
901
|
-
else fallback_key or f"model-event-{len(model_run_order)}"
|
|
902
|
-
)
|
|
903
|
-
|
|
904
|
-
def record_model_usage(
|
|
905
|
-
event: Mapping[str, Any],
|
|
906
|
-
usage: dict[str, Any],
|
|
907
|
-
*,
|
|
908
|
-
fallback_key: str | None = None,
|
|
909
|
-
) -> None:
|
|
910
|
-
if not usage:
|
|
911
|
-
return
|
|
912
|
-
run_key = model_run_key(event, fallback_key=fallback_key)
|
|
913
|
-
if run_key not in model_run_usages:
|
|
914
|
-
model_run_order.append(run_key)
|
|
915
|
-
model_run_usages[run_key] = dict(usage)
|
|
916
|
-
|
|
917
|
-
def accumulated_model_usage() -> dict[str, Any]:
|
|
918
|
-
if len(model_run_order) == 1:
|
|
919
|
-
return dict(model_run_usages.get(model_run_order[0]) or {})
|
|
920
|
-
usage: dict[str, Any] = {}
|
|
921
|
-
for run_key in model_run_order:
|
|
922
|
-
usage = accumulate_usage(usage, model_run_usages.get(run_key) or {})
|
|
923
|
-
return usage
|
|
924
|
-
|
|
925
|
-
def latest_model_usage() -> dict[str, Any]:
|
|
926
|
-
for run_key in reversed(model_run_order):
|
|
927
|
-
usage = model_run_usages.get(run_key)
|
|
928
|
-
if usage:
|
|
929
|
-
return dict(usage)
|
|
930
|
-
return {}
|
|
931
|
-
|
|
932
|
-
if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
|
|
933
|
-
try:
|
|
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
|
-
),
|
|
940
|
-
config=config,
|
|
941
|
-
context=native_context,
|
|
942
|
-
):
|
|
943
|
-
yield chunk
|
|
944
|
-
return
|
|
945
|
-
except Exception as e:
|
|
946
|
-
yield {
|
|
947
|
-
"type": "error",
|
|
948
|
-
"message": str(e) or "LangGraph checkpoint resume failed",
|
|
949
|
-
"checkpoint_id": str(checkpoint_ref.get("checkpoint_id") or ""),
|
|
950
|
-
"exception_type": type(e).__name__,
|
|
951
|
-
}
|
|
952
|
-
return
|
|
953
|
-
|
|
954
|
-
if not hasattr(self._agent, "astream_events"):
|
|
955
|
-
result = await self.invoke(invoke_payload)
|
|
956
|
-
final_chunk = {"output": result.get("output", ""), "type": "final"}
|
|
957
|
-
usage = self._extract_usage(result)
|
|
958
|
-
if usage:
|
|
959
|
-
final_chunk["usage"] = usage
|
|
960
|
-
last_usage = self._extract_last_usage(result)
|
|
961
|
-
if last_usage:
|
|
962
|
-
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
963
|
-
yield final_chunk
|
|
964
|
-
return
|
|
965
|
-
|
|
966
|
-
try:
|
|
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 消费。
|
|
978
|
-
stream_kwargs = {"version": "v2", "config": config}
|
|
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
|
-
):
|
|
984
|
-
stream_kwargs["context"] = native_context
|
|
985
|
-
async for event in self._agent.astream_events(stream_input, **stream_kwargs):
|
|
986
|
-
event_kind = event.get("event", "")
|
|
987
|
-
|
|
988
|
-
if event_kind == "on_chat_model_stream":
|
|
989
|
-
chunk = event.get("data", {}).get("chunk")
|
|
990
|
-
if not chunk:
|
|
991
|
-
continue
|
|
992
|
-
chunk_usage = self._extract_usage(chunk)
|
|
993
|
-
if chunk_usage:
|
|
994
|
-
# Some LangChain providers attach cumulative usage to
|
|
995
|
-
# every stream chunk, and LangChain may then sum those
|
|
996
|
-
# cumulative snapshots into an inflated
|
|
997
|
-
# on_chat_model_end usage. For a concrete model run,
|
|
998
|
-
# keep the latest stream snapshot and ignore the later
|
|
999
|
-
# end usage for that same run_id.
|
|
1000
|
-
latest_stream_usage = dict(chunk_usage)
|
|
1001
|
-
if event.get("run_id"):
|
|
1002
|
-
run_key = model_run_key(event)
|
|
1003
|
-
stream_usage_run_keys.add(run_key)
|
|
1004
|
-
record_model_usage(event, latest_stream_usage)
|
|
1005
|
-
|
|
1006
|
-
# 推理内容
|
|
1007
|
-
reasoning = getattr(chunk, "reasoning_content", None)
|
|
1008
|
-
if not reasoning and hasattr(chunk, "additional_kwargs"):
|
|
1009
|
-
reasoning = chunk.additional_kwargs.get("reasoning_content")
|
|
1010
|
-
|
|
1011
|
-
if reasoning:
|
|
1012
|
-
accumulated_reasoning += reasoning
|
|
1013
|
-
yield {"delta": reasoning, "type": "thinking"}
|
|
1014
|
-
|
|
1015
|
-
# 常规内容
|
|
1016
|
-
if hasattr(chunk, "content") and chunk.content:
|
|
1017
|
-
content = self._filter_tool_tags(chunk.content)
|
|
1018
|
-
if isinstance(content, str):
|
|
1019
|
-
if accumulated_reasoning and content.startswith(accumulated_reasoning):
|
|
1020
|
-
content = content[len(accumulated_reasoning) :]
|
|
1021
|
-
elif reasoning and content.startswith(reasoning):
|
|
1022
|
-
content = content[len(reasoning) :]
|
|
1023
|
-
if content:
|
|
1024
|
-
for part in inline_reasoning_parser.feed(content):
|
|
1025
|
-
if not part.text or not part.text.strip():
|
|
1026
|
-
continue
|
|
1027
|
-
if part.kind == "thinking":
|
|
1028
|
-
accumulated_reasoning += part.text
|
|
1029
|
-
yield {"delta": part.text, "type": "thinking"}
|
|
1030
|
-
else:
|
|
1031
|
-
accumulated_text += part.text
|
|
1032
|
-
yield {"delta": part.text, "type": "text"}
|
|
1033
|
-
|
|
1034
|
-
elif event_kind == "on_chat_model_end":
|
|
1035
|
-
data = event.get("data") or {}
|
|
1036
|
-
output = data.get("output") if isinstance(data, Mapping) else None
|
|
1037
|
-
usage = self._extract_usage(output) or self._extract_usage(data)
|
|
1038
|
-
last_usage = self._extract_last_usage(output) or self._extract_last_usage(data)
|
|
1039
|
-
run_key = model_run_key(event)
|
|
1040
|
-
if run_key not in stream_usage_run_keys:
|
|
1041
|
-
record_model_usage(event, last_usage or usage)
|
|
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
|
-
|
|
1096
|
-
elif event_kind == "on_tool_start":
|
|
1097
|
-
emitted_non_text_event = True
|
|
1098
|
-
yield {
|
|
1099
|
-
"type": "tool_call",
|
|
1100
|
-
"tool_name": event.get("name", "unknown"),
|
|
1101
|
-
"tool_args": event.get("data", {}).get("input", {}),
|
|
1102
|
-
"run_id": event.get("run_id"),
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
elif event_kind == "on_tool_end":
|
|
1106
|
-
emitted_non_text_event = True
|
|
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"]
|
|
1116
|
-
yield {
|
|
1117
|
-
"type": "tool_result",
|
|
1118
|
-
"tool_name": event.get("name", "unknown"),
|
|
1119
|
-
"tool_args": event.get("data", {}).get("input", {}),
|
|
1120
|
-
"tool_output": normalized_output,
|
|
1121
|
-
"run_id": event.get("run_id"),
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
elif event_kind == "on_chain_end":
|
|
1125
|
-
output = event.get("data", {}).get("output", {})
|
|
1126
|
-
if isinstance(output, dict) and "__interrupt__" in output:
|
|
1127
|
-
emitted_non_text_event = True
|
|
1128
|
-
yield {
|
|
1129
|
-
"type": "interrupt",
|
|
1130
|
-
"interrupt_info": output["__interrupt__"],
|
|
1131
|
-
"session_id": session_id,
|
|
1132
|
-
}
|
|
1133
|
-
return
|
|
1134
|
-
extracted_output = self._extract_output(output)
|
|
1135
|
-
if extracted_output:
|
|
1136
|
-
final_output_text = strip_reasoning_markup(str(extracted_output))
|
|
1137
|
-
final_output_usage = self._extract_usage(output)
|
|
1138
|
-
final_output_last_usage = self._extract_last_usage(output)
|
|
1139
|
-
|
|
1140
|
-
except Exception as e:
|
|
1141
|
-
if "Interrupt" in type(e).__name__:
|
|
1142
|
-
yield {
|
|
1143
|
-
"type": "interrupt",
|
|
1144
|
-
"interrupt_info": self._get_interrupt_info(self._agent.get_state(config)),
|
|
1145
|
-
"session_id": session_id,
|
|
1146
|
-
}
|
|
1147
|
-
return
|
|
1148
|
-
raise
|
|
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
|
-
|
|
1177
|
-
for part in inline_reasoning_parser.flush():
|
|
1178
|
-
if not part.text or not part.text.strip():
|
|
1179
|
-
continue
|
|
1180
|
-
if part.kind == "thinking":
|
|
1181
|
-
accumulated_reasoning += part.text
|
|
1182
|
-
yield {"delta": part.text, "type": "thinking"}
|
|
1183
|
-
else:
|
|
1184
|
-
accumulated_text += part.text
|
|
1185
|
-
yield {"delta": part.text, "type": "text"}
|
|
1186
|
-
|
|
1187
|
-
if not accumulated_text:
|
|
1188
|
-
if final_output_text:
|
|
1189
|
-
final_chunk = {"output": final_output_text, "type": "final"}
|
|
1190
|
-
usage = accumulated_model_usage() or final_output_usage or latest_stream_usage
|
|
1191
|
-
last_usage = (
|
|
1192
|
-
latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
|
|
1193
|
-
)
|
|
1194
|
-
if usage:
|
|
1195
|
-
final_chunk["usage"] = usage
|
|
1196
|
-
if last_usage:
|
|
1197
|
-
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
1198
|
-
yield final_chunk
|
|
1199
|
-
elif not emitted_non_text_event:
|
|
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
|
-
}
|
|
1205
|
-
usage = self._extract_usage(result)
|
|
1206
|
-
if usage:
|
|
1207
|
-
fallback_chunk["usage"] = usage
|
|
1208
|
-
last_usage = self._extract_last_usage(result)
|
|
1209
|
-
if last_usage:
|
|
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}
|
|
1215
|
-
return
|
|
1216
|
-
else:
|
|
1217
|
-
final_chunk = {"output": accumulated_text, "type": "final"}
|
|
1218
|
-
state_usage = await self._latest_state_usage(config)
|
|
1219
|
-
usage = (
|
|
1220
|
-
accumulated_model_usage()
|
|
1221
|
-
or state_usage
|
|
1222
|
-
or final_output_usage
|
|
1223
|
-
or latest_stream_usage
|
|
1224
|
-
)
|
|
1225
|
-
if usage:
|
|
1226
|
-
final_chunk["usage"] = 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
|
-
)
|
|
1234
|
-
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
1235
|
-
yield final_chunk
|
|
1236
|
-
|
|
1237
|
-
metadata = await self._latest_checkpoint_metadata(config)
|
|
1238
|
-
if metadata:
|
|
1239
|
-
yield {"type": "checkpoint", "metadata": metadata}
|
|
1240
|
-
|
|
1241
1054
|
def _filter_tool_tags(self, content: str) -> str:
|
|
1242
|
-
"""
|
|
1055
|
+
"""过滤完整的 XML tool_call 标签。"""
|
|
1243
1056
|
if not isinstance(content, str):
|
|
1244
1057
|
return content
|
|
1245
1058
|
content = re.sub(r"<tool_call>.*?</tool_call>", "", content, flags=re.DOTALL)
|
|
1246
1059
|
content = re.sub(r"</?(?:tool_call|arg_key|arg_value)>", "", content)
|
|
1247
1060
|
return content
|
|
1061
|
+
|
|
1062
|
+
async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
|
|
1063
|
+
"""Prepare the managed checkpoint before yielding the first event."""
|
|
1064
|
+
await self.prepare_runtime_capabilities()
|
|
1065
|
+
async for event in super().stream(input_data):
|
|
1066
|
+
yield event
|