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
|
@@ -1,751 +1,38 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""LangChainRunner - LangChain 框架运行时(复用 LangGraph 基座,deepagents 同款薄壳)。
|
|
2
|
+
|
|
3
|
+
新 LangChain(``langchain.agents.create_agent``)返回的就是 LangGraph
|
|
4
|
+
``CompiledStateGraph``,因此运行时复用 :class:`LangGraphRunner`,与
|
|
5
|
+
``DeepAgentsRunner`` 同款薄壳模式,保持原生能力(工具调用 / 人工审批 HITL /
|
|
6
|
+
checkpoint / cancel)和行为一致。
|
|
7
|
+
|
|
8
|
+
**legacy LangChain 不再支持运行**:``AgentExecutor`` / ``LLMChain`` / LCEL 链
|
|
9
|
+
(``prompt | llm | parser``)只有 ``invoke``、没有 ``get_state``/checkpoint,不是
|
|
10
|
+
LangGraph 图。本 runner 在 ``load_agent`` 时识别并拒绝,给出迁移指引,而不是等
|
|
11
|
+
``stream()`` 时才崩一个莫名其妙的 ``AttributeError``。
|
|
12
|
+
"""
|
|
2
13
|
|
|
3
14
|
from __future__ import annotations
|
|
4
15
|
|
|
5
|
-
import
|
|
6
|
-
import logging
|
|
7
|
-
import os
|
|
8
|
-
import uuid
|
|
9
|
-
from typing import Any, AsyncIterator, Dict, Optional
|
|
16
|
+
from ksadk.runners.langgraph_runner import LangGraphRunner
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
_LEGACY_LANGCHAIN_ERROR = (
|
|
19
|
+
"legacy LangChain(AgentExecutor / LLMChain / LCEL 链 `prompt | llm | parser`)"
|
|
20
|
+
"不再支持运行。\n"
|
|
21
|
+
"请迁移到 LangGraph 基座:\n"
|
|
22
|
+
" • 用 `langchain.agents.create_agent(...)` 定义 agent"
|
|
23
|
+
"(产出 LangGraph 图,自带工具调用 / 人工审批 HITL / checkpoint),或\n"
|
|
24
|
+
" • 直接用 `langgraph.graph.StateGraph` 编排。\n"
|
|
25
|
+
"迁移后 detector 会识别为 LANGCHAIN/LANGGRAPH,复用 LangGraphRunner 运行。"
|
|
17
26
|
)
|
|
18
|
-
from ksadk.sessions.continuity import LangChainSessionAdapter
|
|
19
|
-
|
|
20
|
-
logger = logging.getLogger(__name__)
|
|
21
27
|
|
|
22
28
|
|
|
23
|
-
class LangChainRunner(
|
|
24
|
-
"""LangChain
|
|
29
|
+
class LangChainRunner(LangGraphRunner):
|
|
30
|
+
"""LangChain 运行时(复用 :class:`LangGraphRunner`)。"""
|
|
25
31
|
|
|
26
32
|
def load_agent(self) -> None:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
self.detection_result.agent_variable,
|
|
34
|
-
force_reload=force_reload,
|
|
35
|
-
)
|
|
36
|
-
self._loaded_model_name = self.normalize_requested_model(
|
|
37
|
-
os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
def prepare_for_request(self, model: Optional[str]) -> None:
|
|
41
|
-
normalized = self.sync_process_model_env(model)
|
|
42
|
-
if normalized is None or self._agent is None:
|
|
43
|
-
return
|
|
44
|
-
if normalized == getattr(self, "_loaded_model_name", None):
|
|
45
|
-
return
|
|
46
|
-
self._load_agent(force_reload=True)
|
|
47
|
-
|
|
48
|
-
def get_session_adapter(self):
|
|
49
|
-
return LangChainSessionAdapter()
|
|
50
|
-
|
|
51
|
-
def _get_config(self, session_id: Optional[str] = None) -> Optional[dict[str, Any]]:
|
|
52
|
-
config: dict[str, Any] = {}
|
|
53
|
-
|
|
54
|
-
langfuse_callbacks = get_langfuse_callbacks()
|
|
55
|
-
if langfuse_callbacks:
|
|
56
|
-
config["callbacks"] = langfuse_callbacks
|
|
57
|
-
|
|
58
|
-
metadata = get_langfuse_metadata(session_id)
|
|
59
|
-
user_id, tags, _, _ = prepare_trace_metadata(session_id)
|
|
60
|
-
if user_id:
|
|
61
|
-
metadata["langfuse_user_id"] = user_id
|
|
62
|
-
if tags:
|
|
63
|
-
metadata["langfuse_tags"] = tags
|
|
64
|
-
config["metadata"] = metadata
|
|
65
|
-
|
|
66
|
-
return config or None
|
|
67
|
-
|
|
68
|
-
async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
69
|
-
session_id = input_data.get("session_id") or str(uuid.uuid4())[:8]
|
|
70
|
-
path = self._resolve_request_path()
|
|
71
|
-
config = self._get_config(session_id)
|
|
72
|
-
native_context = self.build_native_context(input_data.get("platform_context"))
|
|
73
|
-
|
|
74
|
-
if path == "standard_hook":
|
|
75
|
-
payload = self._prepare_with_standard_hook(input_data, session_id)
|
|
76
|
-
result = await self._invoke_agent(payload, config=config, context=native_context)
|
|
77
|
-
elif path == "runnable_with_message_history":
|
|
78
|
-
result = await self._invoke_with_message_history(
|
|
79
|
-
input_data,
|
|
80
|
-
session_id,
|
|
81
|
-
config=config,
|
|
82
|
-
context=native_context,
|
|
83
|
-
)
|
|
84
|
-
else:
|
|
85
|
-
payload = self._prepare_with_replay(input_data)
|
|
86
|
-
result = await self._invoke_agent(payload, config=config, context=native_context)
|
|
87
|
-
|
|
88
|
-
output = {"output": self._extract_output(result)}
|
|
89
|
-
usage = self._extract_usage(result)
|
|
90
|
-
if usage:
|
|
91
|
-
output["usage"] = usage
|
|
92
|
-
last_usage = self._extract_last_usage(result)
|
|
93
|
-
if last_usage:
|
|
94
|
-
output.setdefault("metadata", {})["last_usage"] = last_usage
|
|
95
|
-
return output
|
|
96
|
-
|
|
97
|
-
async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
|
|
98
|
-
session_id = input_data.get("session_id") or str(uuid.uuid4())[:8]
|
|
99
|
-
path = self._resolve_request_path()
|
|
100
|
-
config = self._get_config(session_id)
|
|
101
|
-
native_context = self.build_native_context(input_data.get("platform_context"))
|
|
102
|
-
|
|
103
|
-
if path == "standard_hook":
|
|
104
|
-
payload = self._prepare_with_standard_hook(input_data, session_id)
|
|
105
|
-
elif path == "runnable_with_message_history":
|
|
106
|
-
payload = {"input": self._prepare_message_history_input(input_data)}
|
|
107
|
-
config = self._with_session_config(config, session_id)
|
|
108
|
-
else:
|
|
109
|
-
payload = self._prepare_with_replay(input_data)
|
|
110
|
-
|
|
111
|
-
accumulated_text = ""
|
|
112
|
-
last_chunk: Any = None
|
|
113
|
-
final_output_text = ""
|
|
114
|
-
emitted_non_text_event = False
|
|
115
|
-
message_snapshots: dict[str, str] = {}
|
|
116
|
-
|
|
117
|
-
try:
|
|
118
|
-
if self._should_stream_events(payload):
|
|
119
|
-
kwargs = self._build_optional_call_kwargs(
|
|
120
|
-
self._agent.astream_events,
|
|
121
|
-
config=config,
|
|
122
|
-
context=native_context,
|
|
123
|
-
)
|
|
124
|
-
kwargs["version"] = "v2"
|
|
125
|
-
async for event in self._agent.astream_events(payload, **kwargs):
|
|
126
|
-
if not isinstance(event, dict):
|
|
127
|
-
continue
|
|
128
|
-
event_kind = event.get("event", "")
|
|
129
|
-
data = event.get("data") or {}
|
|
130
|
-
|
|
131
|
-
if event_kind == "on_chat_model_stream":
|
|
132
|
-
chunk = data.get("chunk") if isinstance(data, dict) else None
|
|
133
|
-
if chunk is None:
|
|
134
|
-
continue
|
|
135
|
-
last_chunk = chunk
|
|
136
|
-
delta, chunk_type = self._extract_chunk(chunk)
|
|
137
|
-
if delta:
|
|
138
|
-
if chunk_type == "text":
|
|
139
|
-
accumulated_text += delta
|
|
140
|
-
else:
|
|
141
|
-
emitted_non_text_event = True
|
|
142
|
-
yield {"delta": delta, "type": chunk_type}
|
|
143
|
-
elif event_kind == "on_tool_start":
|
|
144
|
-
emitted_non_text_event = True
|
|
145
|
-
yield {
|
|
146
|
-
"type": "tool_call",
|
|
147
|
-
"tool_name": event.get("name", "unknown"),
|
|
148
|
-
"tool_args": data.get("input", {}) if isinstance(data, dict) else {},
|
|
149
|
-
"run_id": event.get("run_id"),
|
|
150
|
-
}
|
|
151
|
-
elif event_kind == "on_tool_end":
|
|
152
|
-
emitted_non_text_event = True
|
|
153
|
-
tool_output = data.get("output", "") if isinstance(data, dict) else ""
|
|
154
|
-
yield {
|
|
155
|
-
"type": "tool_result",
|
|
156
|
-
"tool_name": event.get("name", "unknown"),
|
|
157
|
-
"tool_args": data.get("input", {}) if isinstance(data, dict) else {},
|
|
158
|
-
"tool_output": (
|
|
159
|
-
tool_output
|
|
160
|
-
if isinstance(tool_output, dict)
|
|
161
|
-
else (str(tool_output) if tool_output else "")
|
|
162
|
-
),
|
|
163
|
-
"run_id": event.get("run_id"),
|
|
164
|
-
}
|
|
165
|
-
elif event_kind == "on_chain_end":
|
|
166
|
-
output = data.get("output") if isinstance(data, dict) else None
|
|
167
|
-
extracted_output = self._extract_recognized_output(output)
|
|
168
|
-
if extracted_output:
|
|
169
|
-
final_output_text = extracted_output
|
|
170
|
-
if self._extract_usage(output):
|
|
171
|
-
last_chunk = output
|
|
172
|
-
elif hasattr(self._agent, "astream"):
|
|
173
|
-
kwargs = self._build_optional_call_kwargs(
|
|
174
|
-
self._agent.astream,
|
|
175
|
-
config=config,
|
|
176
|
-
context=native_context,
|
|
177
|
-
)
|
|
178
|
-
async for chunk in self._agent.astream(payload, **kwargs):
|
|
179
|
-
last_chunk = chunk
|
|
180
|
-
message_state = self._extract_message_state(chunk)
|
|
181
|
-
if message_state:
|
|
182
|
-
content, message_key = message_state
|
|
183
|
-
previous = message_snapshots.get(message_key, "")
|
|
184
|
-
delta = self._snapshot_delta(content, previous)
|
|
185
|
-
message_snapshots[message_key] = content
|
|
186
|
-
chunk_type = "text"
|
|
187
|
-
else:
|
|
188
|
-
delta, chunk_type = self._extract_chunk(chunk)
|
|
189
|
-
if delta:
|
|
190
|
-
accumulated_text += delta
|
|
191
|
-
yield {"delta": delta, "type": chunk_type}
|
|
192
|
-
elif hasattr(self._agent, "stream"):
|
|
193
|
-
kwargs = self._build_optional_call_kwargs(
|
|
194
|
-
self._agent.stream,
|
|
195
|
-
config=config,
|
|
196
|
-
context=native_context,
|
|
197
|
-
)
|
|
198
|
-
for chunk in self._agent.stream(payload, **kwargs):
|
|
199
|
-
last_chunk = chunk
|
|
200
|
-
message_state = self._extract_message_state(chunk)
|
|
201
|
-
if message_state:
|
|
202
|
-
content, message_key = message_state
|
|
203
|
-
previous = message_snapshots.get(message_key, "")
|
|
204
|
-
delta = self._snapshot_delta(content, previous)
|
|
205
|
-
message_snapshots[message_key] = content
|
|
206
|
-
chunk_type = "text"
|
|
207
|
-
else:
|
|
208
|
-
delta, chunk_type = self._extract_chunk(chunk)
|
|
209
|
-
if delta:
|
|
210
|
-
accumulated_text += delta
|
|
211
|
-
yield {"delta": delta, "type": chunk_type}
|
|
212
|
-
except Exception as exc:
|
|
213
|
-
logger.warning("LangChain stream failed: %s", exc)
|
|
214
|
-
|
|
215
|
-
if not accumulated_text:
|
|
216
|
-
if final_output_text or emitted_non_text_event:
|
|
217
|
-
final_chunk = {"output": final_output_text, "type": "final"}
|
|
218
|
-
usage = self._extract_usage(last_chunk)
|
|
219
|
-
if usage:
|
|
220
|
-
final_chunk["usage"] = usage
|
|
221
|
-
last_usage = self._extract_last_usage(last_chunk)
|
|
222
|
-
if last_usage:
|
|
223
|
-
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
224
|
-
yield final_chunk
|
|
225
|
-
return
|
|
226
|
-
result = await self.invoke(input_data)
|
|
227
|
-
final_chunk = {"output": result.get("output", ""), "type": "final"}
|
|
228
|
-
usage = self._extract_usage(result)
|
|
229
|
-
if usage:
|
|
230
|
-
final_chunk["usage"] = usage
|
|
231
|
-
last_usage = self._extract_last_usage(result)
|
|
232
|
-
if last_usage:
|
|
233
|
-
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
234
|
-
yield final_chunk
|
|
235
|
-
return
|
|
236
|
-
|
|
237
|
-
final_chunk = {"output": accumulated_text, "type": "final"}
|
|
238
|
-
usage = self._extract_usage(last_chunk)
|
|
239
|
-
if usage:
|
|
240
|
-
final_chunk["usage"] = usage
|
|
241
|
-
last_usage = self._extract_last_usage(last_chunk)
|
|
242
|
-
if last_usage:
|
|
243
|
-
final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
|
|
244
|
-
yield final_chunk
|
|
245
|
-
|
|
246
|
-
def _should_stream_events(self, payload: dict[str, Any]) -> bool:
|
|
247
|
-
"""Use LangGraph events for LangChain create_agent message-state agents."""
|
|
248
|
-
return isinstance(payload.get("messages"), list) and hasattr(
|
|
249
|
-
self._agent, "astream_events"
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
def _resolve_request_path(self) -> str:
|
|
253
|
-
module = getattr(self, "_module", None)
|
|
254
|
-
if callable(getattr(module, "ksadk_prepare_input", None)):
|
|
255
|
-
return "standard_hook"
|
|
256
|
-
if self._is_runnable_with_message_history():
|
|
257
|
-
return "runnable_with_message_history"
|
|
258
|
-
return "replay"
|
|
259
|
-
|
|
260
|
-
def _prepare_with_standard_hook(
|
|
261
|
-
self, input_data: Dict[str, Any], session_id: str
|
|
262
|
-
) -> dict[str, Any]:
|
|
263
|
-
module = getattr(self, "_module", None)
|
|
264
|
-
prepare_input = getattr(module, "ksadk_prepare_input", None)
|
|
265
|
-
if not callable(prepare_input):
|
|
266
|
-
return {"input": input_data.get("input", "")}
|
|
267
|
-
|
|
268
|
-
payload = {"input": input_data.get("input", "")}
|
|
269
|
-
session_context = {
|
|
270
|
-
"session_id": session_id,
|
|
271
|
-
"history": list(input_data.get("history") or []),
|
|
272
|
-
"input_parts": list(input_data.get("input_parts") or []),
|
|
273
|
-
"attachments": list(input_data.get("attachments") or []),
|
|
274
|
-
"attachment_results": list(input_data.get("attachment_results") or []),
|
|
275
|
-
"instructions": input_data.get("instructions"),
|
|
276
|
-
"platform_context": input_data.get("platform_context"),
|
|
277
|
-
"kb_context": input_data.get("kb_context"),
|
|
278
|
-
"memory_context": input_data.get("memory_context"),
|
|
279
|
-
}
|
|
280
|
-
builtin_context = self._ksadk_builtin_tool_context()
|
|
281
|
-
if builtin_context:
|
|
282
|
-
session_context.update(builtin_context)
|
|
283
|
-
prepared = prepare_input(payload, session_context)
|
|
284
|
-
return prepared if isinstance(prepared, dict) else payload
|
|
285
|
-
|
|
286
|
-
@staticmethod
|
|
287
|
-
def _ksadk_builtin_tool_context() -> dict[str, Any]:
|
|
288
|
-
try:
|
|
289
|
-
from ksadk.toolsets import (
|
|
290
|
-
builtin_tool_descriptors_for_runtime,
|
|
291
|
-
builtin_tools_mode,
|
|
292
|
-
builtin_tools_profile,
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
mode = builtin_tools_mode(default="off")
|
|
296
|
-
descriptors = builtin_tool_descriptors_for_runtime(mode=mode)
|
|
297
|
-
if not descriptors:
|
|
298
|
-
return {}
|
|
299
|
-
return {
|
|
300
|
-
"ksadk_tools": descriptors,
|
|
301
|
-
"ksadk_builtin_tools_mode": mode,
|
|
302
|
-
"ksadk_builtin_tools_profile": builtin_tools_profile("default"),
|
|
303
|
-
"deferred_direct_injection_supported": False,
|
|
304
|
-
}
|
|
305
|
-
except Exception as exc:
|
|
306
|
-
logger.warning("Failed to prepare ksadk built-in tool descriptors: %s", exc)
|
|
307
|
-
return {}
|
|
308
|
-
|
|
309
|
-
def _prepare_with_replay(self, input_data: Dict[str, Any]) -> dict[str, Any]:
|
|
310
|
-
user_input = str(input_data.get("input", "") or "")
|
|
311
|
-
history = list(input_data.get("history") or [])
|
|
312
|
-
ambient_text = self._ambient_context_text(input_data)
|
|
313
|
-
instructions = str(input_data.get("instructions") or "").strip()
|
|
314
|
-
if not history and not ambient_text and not instructions:
|
|
315
|
-
return {"input": user_input}
|
|
316
|
-
return {
|
|
317
|
-
"input": self._format_replay_prompt(
|
|
318
|
-
user_input,
|
|
319
|
-
history,
|
|
320
|
-
ambient_text=ambient_text,
|
|
321
|
-
instructions=instructions,
|
|
322
|
-
)
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
@staticmethod
|
|
326
|
-
def _ambient_context_text(input_data: Dict[str, Any]) -> str:
|
|
327
|
-
sections: list[str] = []
|
|
328
|
-
kb_context = input_data.get("kb_context") or {}
|
|
329
|
-
kb_text = (
|
|
330
|
-
str(kb_context.get("formatted_text") or "").strip()
|
|
331
|
-
if isinstance(kb_context, dict)
|
|
332
|
-
else ""
|
|
333
|
-
)
|
|
334
|
-
if kb_text:
|
|
335
|
-
sections.append(f"Knowledge base context:\n{kb_text}")
|
|
336
|
-
|
|
337
|
-
memory_context = input_data.get("memory_context") or {}
|
|
338
|
-
memory_text = (
|
|
339
|
-
str(memory_context.get("formatted_text") or "").strip()
|
|
340
|
-
if isinstance(memory_context, dict)
|
|
341
|
-
else ""
|
|
342
|
-
)
|
|
343
|
-
if memory_text:
|
|
344
|
-
sections.append(f"Long-term memory context:\n{memory_text}")
|
|
345
|
-
|
|
346
|
-
return "\n\n".join(section for section in sections if section)
|
|
347
|
-
|
|
348
|
-
def _prepare_message_history_input(self, input_data: Dict[str, Any]) -> str:
|
|
349
|
-
user_input = str(input_data.get("input", "") or "")
|
|
350
|
-
context_text = self._request_context_text(input_data)
|
|
351
|
-
if not context_text:
|
|
352
|
-
return user_input
|
|
353
|
-
current_input = user_input.strip() or "[empty message]"
|
|
354
|
-
return f"{context_text}\n\nCurrent user input:\n{current_input}"
|
|
355
|
-
|
|
356
|
-
def _request_context_text(self, input_data: Dict[str, Any]) -> str:
|
|
357
|
-
ambient_text = self._ambient_context_text(input_data)
|
|
358
|
-
instructions = str(input_data.get("instructions") or "").strip()
|
|
359
|
-
return "\n\n".join(section for section in (instructions, ambient_text) if section)
|
|
360
|
-
|
|
361
|
-
def _format_replay_prompt(
|
|
362
|
-
self,
|
|
363
|
-
user_input: str,
|
|
364
|
-
history: list[dict[str, Any]],
|
|
365
|
-
*,
|
|
366
|
-
ambient_text: str = "",
|
|
367
|
-
instructions: str = "",
|
|
368
|
-
) -> str:
|
|
369
|
-
lines: list[str] = []
|
|
370
|
-
if instructions:
|
|
371
|
-
lines.append(instructions)
|
|
372
|
-
if ambient_text:
|
|
373
|
-
lines.append(ambient_text)
|
|
374
|
-
if history:
|
|
375
|
-
lines.append("Conversation history:")
|
|
376
|
-
normalized_history: list[tuple[str, str]] = []
|
|
377
|
-
for item in history:
|
|
378
|
-
role = self._normalize_history_role(item.get("role"))
|
|
379
|
-
content = str(item.get("content", "") or "").strip()
|
|
380
|
-
if not role or not content:
|
|
381
|
-
continue
|
|
382
|
-
normalized_history.append((role, content))
|
|
383
|
-
lines.append(f"{role}: {content}")
|
|
384
|
-
|
|
385
|
-
if user_input.strip():
|
|
386
|
-
if not normalized_history or normalized_history[-1] != ("user", user_input.strip()):
|
|
387
|
-
lines.append(f"user: {user_input.strip()}")
|
|
388
|
-
elif ambient_text and not history:
|
|
389
|
-
lines.append("Current user input:\n[empty message]")
|
|
390
|
-
|
|
391
|
-
return "\n".join(lines)
|
|
392
|
-
|
|
393
|
-
@staticmethod
|
|
394
|
-
def _normalize_history_role(role: Any) -> str:
|
|
395
|
-
normalized = str(role or "").strip().lower()
|
|
396
|
-
if normalized in {"assistant", "model"}:
|
|
397
|
-
return "assistant"
|
|
398
|
-
if normalized == "user":
|
|
399
|
-
return "user"
|
|
400
|
-
return normalized
|
|
401
|
-
|
|
402
|
-
@staticmethod
|
|
403
|
-
def _with_session_config(
|
|
404
|
-
config: Optional[dict[str, Any]],
|
|
405
|
-
session_id: str,
|
|
406
|
-
) -> dict[str, Any]:
|
|
407
|
-
merged = dict(config or {})
|
|
408
|
-
configurable = dict(merged.get("configurable") or {})
|
|
409
|
-
configurable["session_id"] = session_id
|
|
410
|
-
merged["configurable"] = configurable
|
|
411
|
-
return merged
|
|
412
|
-
|
|
413
|
-
async def _invoke_agent(
|
|
414
|
-
self,
|
|
415
|
-
payload: Any,
|
|
416
|
-
*,
|
|
417
|
-
config: Optional[dict[str, Any]],
|
|
418
|
-
context: Optional[dict[str, Any]],
|
|
419
|
-
) -> Any:
|
|
420
|
-
if hasattr(self._agent, "ainvoke"):
|
|
421
|
-
kwargs = self._build_optional_call_kwargs(
|
|
422
|
-
self._agent.ainvoke,
|
|
423
|
-
config=config,
|
|
424
|
-
context=context,
|
|
425
|
-
)
|
|
426
|
-
return await self._agent.ainvoke(payload, **kwargs)
|
|
427
|
-
if hasattr(self._agent, "invoke"):
|
|
428
|
-
kwargs = self._build_optional_call_kwargs(
|
|
429
|
-
self._agent.invoke,
|
|
430
|
-
config=config,
|
|
431
|
-
context=context,
|
|
432
|
-
)
|
|
433
|
-
return self._agent.invoke(payload, **kwargs)
|
|
434
|
-
if callable(self._agent):
|
|
435
|
-
return self._agent(payload)
|
|
436
|
-
raise TypeError("Agent 不支持 invoke 调用")
|
|
437
|
-
|
|
438
|
-
def _is_runnable_with_message_history(self) -> bool:
|
|
439
|
-
try:
|
|
440
|
-
from langchain_core.runnables.history import RunnableWithMessageHistory
|
|
441
|
-
|
|
442
|
-
return isinstance(self._agent, RunnableWithMessageHistory)
|
|
443
|
-
except Exception:
|
|
444
|
-
return False
|
|
445
|
-
|
|
446
|
-
async def _invoke_with_message_history(
|
|
447
|
-
self,
|
|
448
|
-
input_data: Dict[str, Any],
|
|
449
|
-
session_id: str,
|
|
450
|
-
*,
|
|
451
|
-
config: Optional[dict[str, Any]],
|
|
452
|
-
context: Optional[dict[str, Any]],
|
|
453
|
-
) -> Any:
|
|
454
|
-
context_text = self._request_context_text(input_data)
|
|
455
|
-
payload = {"input": self._prepare_message_history_input(input_data)}
|
|
456
|
-
session_config = self._with_session_config(config, session_id)
|
|
457
|
-
wrapped_runnable = self._extract_wrapped_history_runnable()
|
|
458
|
-
message_history = self._get_message_history_store(session_id)
|
|
459
|
-
|
|
460
|
-
if context_text and wrapped_runnable is not None and message_history is not None:
|
|
461
|
-
return await self._invoke_wrapped_history_with_ambient_context(
|
|
462
|
-
input_data=input_data,
|
|
463
|
-
wrapped_runnable=wrapped_runnable,
|
|
464
|
-
message_history=message_history,
|
|
465
|
-
session_config=session_config,
|
|
466
|
-
context=context,
|
|
467
|
-
ambient_text=context_text,
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
try:
|
|
471
|
-
return await self._invoke_agent(payload, config=session_config, context=context)
|
|
472
|
-
except Exception:
|
|
473
|
-
if wrapped_runnable is None or message_history is None:
|
|
474
|
-
raise
|
|
475
|
-
return await self._invoke_wrapped_history_with_ambient_context(
|
|
476
|
-
input_data=input_data,
|
|
477
|
-
wrapped_runnable=wrapped_runnable,
|
|
478
|
-
message_history=message_history,
|
|
479
|
-
session_config=session_config,
|
|
480
|
-
context=context,
|
|
481
|
-
ambient_text=context_text,
|
|
482
|
-
)
|
|
483
|
-
|
|
484
|
-
async def _invoke_wrapped_runnable(
|
|
485
|
-
self,
|
|
486
|
-
runnable: Any,
|
|
487
|
-
payload: Any,
|
|
488
|
-
config: Optional[dict[str, Any]],
|
|
489
|
-
context: Optional[dict[str, Any]],
|
|
490
|
-
) -> Any:
|
|
491
|
-
if hasattr(runnable, "ainvoke"):
|
|
492
|
-
kwargs = self._build_optional_call_kwargs(
|
|
493
|
-
runnable.ainvoke,
|
|
494
|
-
config=config,
|
|
495
|
-
context=context,
|
|
496
|
-
)
|
|
497
|
-
return await runnable.ainvoke(payload, **kwargs)
|
|
498
|
-
if hasattr(runnable, "invoke"):
|
|
499
|
-
kwargs = self._build_optional_call_kwargs(
|
|
500
|
-
runnable.invoke,
|
|
501
|
-
config=config,
|
|
502
|
-
context=context,
|
|
503
|
-
)
|
|
504
|
-
return runnable.invoke(payload, **kwargs)
|
|
505
|
-
raise TypeError("Wrapped runnable does not support invoke")
|
|
506
|
-
|
|
507
|
-
async def _invoke_wrapped_history_with_ambient_context(
|
|
508
|
-
self,
|
|
509
|
-
*,
|
|
510
|
-
input_data: Dict[str, Any],
|
|
511
|
-
wrapped_runnable: Any,
|
|
512
|
-
message_history: Any,
|
|
513
|
-
session_config: Optional[dict[str, Any]],
|
|
514
|
-
context: Optional[dict[str, Any]],
|
|
515
|
-
ambient_text: str,
|
|
516
|
-
) -> Any:
|
|
517
|
-
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
|
518
|
-
|
|
519
|
-
user_input = str(input_data.get("input", "") or "")
|
|
520
|
-
prompt_messages = list(getattr(message_history, "messages", []))
|
|
521
|
-
if ambient_text:
|
|
522
|
-
prompt_messages = [SystemMessage(content=ambient_text), *prompt_messages]
|
|
523
|
-
prompt_messages.append(HumanMessage(content=user_input or "[empty message]"))
|
|
524
|
-
result = await self._invoke_wrapped_runnable(
|
|
525
|
-
wrapped_runnable,
|
|
526
|
-
{"input": prompt_messages},
|
|
527
|
-
session_config,
|
|
528
|
-
context,
|
|
529
|
-
)
|
|
530
|
-
output_text = self._extract_output(result)
|
|
531
|
-
await self._append_message_history(
|
|
532
|
-
message_history,
|
|
533
|
-
[
|
|
534
|
-
HumanMessage(content=user_input or "[empty message]"),
|
|
535
|
-
AIMessage(content=output_text),
|
|
536
|
-
],
|
|
537
|
-
)
|
|
538
|
-
return result
|
|
539
|
-
|
|
540
|
-
def _extract_wrapped_history_runnable(self) -> Any | None:
|
|
541
|
-
runnable_lambda = self._get_nested_attr(
|
|
542
|
-
self._agent,
|
|
543
|
-
("bound", "bound", "last", "bound"),
|
|
544
|
-
)
|
|
545
|
-
if runnable_lambda is None:
|
|
546
|
-
logger.debug(
|
|
547
|
-
"Unable to inspect RunnableWithMessageHistory wrapper: "
|
|
548
|
-
"missing bound.bound.last.bound"
|
|
549
|
-
)
|
|
550
|
-
return None
|
|
551
|
-
|
|
552
|
-
func = getattr(runnable_lambda, "func", None)
|
|
553
|
-
if func is None:
|
|
554
|
-
logger.debug(
|
|
555
|
-
"Unable to inspect RunnableWithMessageHistory wrapper: missing lambda func"
|
|
556
|
-
)
|
|
557
|
-
return None
|
|
558
|
-
|
|
559
|
-
try:
|
|
560
|
-
closure = inspect.getclosurevars(func).nonlocals
|
|
561
|
-
except Exception as exc:
|
|
562
|
-
logger.debug(
|
|
563
|
-
"Unable to inspect RunnableWithMessageHistory wrapper: %s",
|
|
564
|
-
exc,
|
|
565
|
-
)
|
|
566
|
-
return None
|
|
567
|
-
return closure.get("runnable_async") or closure.get("runnable_sync")
|
|
568
|
-
|
|
569
|
-
@staticmethod
|
|
570
|
-
def _get_nested_attr(obj: Any, path: tuple[str, ...]) -> Any | None:
|
|
571
|
-
current = obj
|
|
572
|
-
for name in path:
|
|
573
|
-
current = getattr(current, name, None)
|
|
574
|
-
if current is None:
|
|
575
|
-
return None
|
|
576
|
-
return current
|
|
577
|
-
|
|
578
|
-
def _get_message_history_store(self, session_id: str) -> Any | None:
|
|
579
|
-
get_session_history = getattr(self._agent, "get_session_history", None)
|
|
580
|
-
if not callable(get_session_history):
|
|
581
|
-
return None
|
|
582
|
-
return get_session_history(session_id)
|
|
583
|
-
|
|
584
|
-
async def _append_message_history(self, history_store: Any, messages: list[Any]) -> None:
|
|
585
|
-
if hasattr(history_store, "aadd_messages"):
|
|
586
|
-
await history_store.aadd_messages(messages)
|
|
587
|
-
return
|
|
588
|
-
if hasattr(history_store, "add_messages"):
|
|
589
|
-
history_store.add_messages(messages)
|
|
590
|
-
return
|
|
591
|
-
for message in messages:
|
|
592
|
-
role = getattr(message, "type", "")
|
|
593
|
-
content = getattr(message, "content", "")
|
|
594
|
-
if role == "human" and hasattr(history_store, "add_user_message"):
|
|
595
|
-
history_store.add_user_message(content)
|
|
596
|
-
elif role == "ai" and hasattr(history_store, "add_ai_message"):
|
|
597
|
-
history_store.add_ai_message(content)
|
|
598
|
-
|
|
599
|
-
@staticmethod
|
|
600
|
-
def _extract_output(result: Any) -> str:
|
|
601
|
-
if isinstance(result, dict):
|
|
602
|
-
if "output" in result:
|
|
603
|
-
return result["output"]
|
|
604
|
-
if "text" in result:
|
|
605
|
-
return result["text"]
|
|
606
|
-
messages = result.get("messages")
|
|
607
|
-
if isinstance(messages, list) and messages:
|
|
608
|
-
last = messages[-1]
|
|
609
|
-
if isinstance(last, dict):
|
|
610
|
-
return str(last.get("content", str(last)))
|
|
611
|
-
content = getattr(last, "content", None)
|
|
612
|
-
if content is not None:
|
|
613
|
-
return str(content)
|
|
614
|
-
return str(result)
|
|
615
|
-
content = getattr(result, "content", None)
|
|
616
|
-
if content is not None:
|
|
617
|
-
return str(content)
|
|
618
|
-
return str(result)
|
|
619
|
-
|
|
620
|
-
@classmethod
|
|
621
|
-
def _extract_recognized_output(cls, result: Any) -> str:
|
|
622
|
-
if result is None:
|
|
623
|
-
return ""
|
|
624
|
-
if isinstance(result, str):
|
|
625
|
-
return result
|
|
626
|
-
|
|
627
|
-
if isinstance(result, dict):
|
|
628
|
-
for key in ("output", "text"):
|
|
629
|
-
if key not in result:
|
|
630
|
-
continue
|
|
631
|
-
value = result[key]
|
|
632
|
-
if isinstance(value, str):
|
|
633
|
-
return value
|
|
634
|
-
extracted = cls._extract_recognized_output(value)
|
|
635
|
-
if extracted:
|
|
636
|
-
return extracted
|
|
637
|
-
if value is not None and not isinstance(value, (dict, list, tuple)):
|
|
638
|
-
return str(value)
|
|
639
|
-
|
|
640
|
-
message_state = cls._extract_message_state(result)
|
|
641
|
-
return message_state[0] if message_state else ""
|
|
642
|
-
|
|
643
|
-
if isinstance(result, (list, tuple)):
|
|
644
|
-
for item in reversed(result):
|
|
645
|
-
extracted = cls._extract_recognized_output(item)
|
|
646
|
-
if extracted:
|
|
647
|
-
return extracted
|
|
648
|
-
return ""
|
|
649
|
-
|
|
650
|
-
command_update = getattr(result, "update", None)
|
|
651
|
-
if isinstance(command_update, dict):
|
|
652
|
-
return cls._extract_recognized_output(command_update)
|
|
653
|
-
|
|
654
|
-
content = cls._ai_message_content(result)
|
|
655
|
-
return content if content is not None else ""
|
|
656
|
-
|
|
657
|
-
@classmethod
|
|
658
|
-
def _extract_message_state(cls, chunk: Any) -> tuple[str, str] | None:
|
|
659
|
-
"""Extract the newest AI message from LangGraph values/updates snapshots."""
|
|
660
|
-
|
|
661
|
-
def visit(value: Any, path: tuple[str, ...]) -> tuple[str, str] | None:
|
|
662
|
-
if not isinstance(value, dict):
|
|
663
|
-
return None
|
|
664
|
-
|
|
665
|
-
messages = value.get("messages")
|
|
666
|
-
if isinstance(messages, list):
|
|
667
|
-
for index in range(len(messages) - 1, -1, -1):
|
|
668
|
-
message = messages[index]
|
|
669
|
-
content = cls._ai_message_content(message)
|
|
670
|
-
if content is None:
|
|
671
|
-
continue
|
|
672
|
-
message_id = (
|
|
673
|
-
message.get("id")
|
|
674
|
-
if isinstance(message, dict)
|
|
675
|
-
else getattr(message, "id", None)
|
|
676
|
-
)
|
|
677
|
-
fallback_key = "/".join((*path, "messages", str(index)))
|
|
678
|
-
return content, str(message_id or fallback_key)
|
|
679
|
-
|
|
680
|
-
for key, nested in value.items():
|
|
681
|
-
if key == "messages" or not isinstance(nested, dict):
|
|
682
|
-
continue
|
|
683
|
-
result = visit(nested, (*path, str(key)))
|
|
684
|
-
if result:
|
|
685
|
-
return result
|
|
686
|
-
return None
|
|
687
|
-
|
|
688
|
-
return visit(chunk, ())
|
|
689
|
-
|
|
690
|
-
@staticmethod
|
|
691
|
-
def _ai_message_content(message: Any) -> str | None:
|
|
692
|
-
if isinstance(message, dict):
|
|
693
|
-
role = str(message.get("role") or message.get("type") or "").lower()
|
|
694
|
-
if role not in {"ai", "assistant", "model", "aimessage", "aimessagechunk"}:
|
|
695
|
-
return None
|
|
696
|
-
content = message.get("content")
|
|
697
|
-
else:
|
|
698
|
-
role = str(getattr(message, "type", "") or "").lower()
|
|
699
|
-
class_name = type(message).__name__.lower()
|
|
700
|
-
if role not in {
|
|
701
|
-
"ai",
|
|
702
|
-
"assistant",
|
|
703
|
-
"model",
|
|
704
|
-
"aimessage",
|
|
705
|
-
"aimessagechunk",
|
|
706
|
-
} and not class_name.startswith("aimessage"):
|
|
707
|
-
return None
|
|
708
|
-
content = getattr(message, "content", None)
|
|
709
|
-
|
|
710
|
-
if isinstance(content, str):
|
|
711
|
-
return content
|
|
712
|
-
if not isinstance(content, list):
|
|
713
|
-
return None
|
|
714
|
-
|
|
715
|
-
parts: list[str] = []
|
|
716
|
-
for part in content:
|
|
717
|
-
if isinstance(part, str):
|
|
718
|
-
parts.append(part)
|
|
719
|
-
elif isinstance(part, dict):
|
|
720
|
-
text = part.get("text")
|
|
721
|
-
if isinstance(text, str):
|
|
722
|
-
parts.append(text)
|
|
723
|
-
return "".join(parts)
|
|
724
|
-
|
|
725
|
-
@staticmethod
|
|
726
|
-
def _snapshot_delta(content: str, previous: str) -> str:
|
|
727
|
-
if content.startswith(previous):
|
|
728
|
-
return content[len(previous) :]
|
|
729
|
-
if previous.startswith(content):
|
|
730
|
-
return ""
|
|
731
|
-
return content
|
|
732
|
-
|
|
733
|
-
def _extract_chunk(self, chunk: Any) -> tuple[Optional[str], Optional[str]]:
|
|
734
|
-
if isinstance(chunk, dict):
|
|
735
|
-
if "output" in chunk:
|
|
736
|
-
return chunk["output"], "text"
|
|
737
|
-
if "text" in chunk:
|
|
738
|
-
return chunk["text"], "text"
|
|
739
|
-
return None, None
|
|
740
|
-
|
|
741
|
-
reasoning = None
|
|
742
|
-
if hasattr(chunk, "reasoning_content") and chunk.reasoning_content:
|
|
743
|
-
reasoning = chunk.reasoning_content
|
|
744
|
-
elif hasattr(chunk, "additional_kwargs"):
|
|
745
|
-
reasoning = chunk.additional_kwargs.get("reasoning_content")
|
|
746
|
-
|
|
747
|
-
if reasoning:
|
|
748
|
-
return reasoning, "thinking"
|
|
749
|
-
|
|
750
|
-
content = chunk.content if hasattr(chunk, "content") else str(chunk)
|
|
751
|
-
return content if content else None, "text"
|
|
33
|
+
super().load_agent()
|
|
34
|
+
# 复用 LangGraphRunner 的前提:agent 必须是 LangGraph 图(create_agent /
|
|
35
|
+
# StateGraph 编译产物,具备 get_state/checkpoint)。legacy 链(LCEL /
|
|
36
|
+
# AgentExecutor)只有 invoke、无 get_state —— 在此拒绝,而不是运行时才崩。
|
|
37
|
+
if not hasattr(self._agent, "get_state"):
|
|
38
|
+
raise ValueError(_LEGACY_LANGCHAIN_ERROR)
|