agentengine-sdk-python 0.6.4__py3-none-any.whl → 0.7.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/METADATA +181 -0
- agentengine_sdk_python-0.7.0.dist-info/RECORD +482 -0
- {agentengine_sdk_python-0.6.4.dist-info → agentengine_sdk_python-0.7.0.dist-info}/WHEEL +1 -1
- agentengine_sdk_python-0.7.0.dist-info/entry_points.txt +3 -0
- agentengine_sdk_python-0.7.0.dist-info/licenses/LICENSE +175 -0
- agentengine_sdk_python-0.7.0.dist-info/top_level.txt +2 -0
- ksadk/__init__.py +13 -0
- ksadk/__main__.py +4 -0
- ksadk/a2a/__init__.py +15 -0
- ksadk/a2a/card_builder.py +64 -0
- ksadk/a2a/client.py +309 -0
- ksadk/a2a/executor.py +201 -0
- ksadk/a2a/server.py +79 -0
- ksadk/agents/__init__.py +20 -0
- ksadk/agents/base.py +370 -0
- ksadk/agents/context.py +38 -0
- ksadk/agents/event.py +35 -0
- ksadk/agents/loop_agent.py +71 -0
- ksadk/agents/parallel_agent.py +109 -0
- ksadk/agents/runner_adapter.py +24 -0
- ksadk/agents/sequential_agent.py +34 -0
- ksadk/api/__init__.py +3 -0
- ksadk/api/client.py +2246 -0
- ksadk/builders/__init__.py +20 -0
- ksadk/builders/base.py +66 -0
- ksadk/builders/code_builder.py +1990 -0
- ksadk/builders/container_builder.py +786 -0
- ksadk/builders/framework_requirements.py +59 -0
- ksadk/builders/ks3_uploader.py +389 -0
- ksadk/builders/mcp_builder.py +476 -0
- ksadk/builders/requirements_utils.py +75 -0
- ksadk/cli/__init__.py +531 -0
- ksadk/cli/__main__.py +4 -0
- ksadk/cli/agent_ref.py +204 -0
- ksadk/cli/cmd_a2a.py +167 -0
- ksadk/cli/cmd_agent.py +201 -0
- ksadk/cli/cmd_build.py +270 -0
- ksadk/cli/cmd_completion.py +304 -0
- ksadk/cli/cmd_config.py +758 -0
- ksadk/cli/cmd_create.py +2304 -0
- ksadk/cli/cmd_dashboard.py +900 -0
- ksadk/cli/cmd_deploy.py +704 -0
- ksadk/cli/cmd_destroy.py +345 -0
- ksadk/cli/cmd_files.py +1227 -0
- ksadk/cli/cmd_hermes.py +1263 -0
- ksadk/cli/cmd_invoke.py +1503 -0
- ksadk/cli/cmd_launch.py +564 -0
- ksadk/cli/cmd_mcp.py +1179 -0
- ksadk/cli/cmd_model.py +302 -0
- ksadk/cli/cmd_openclaw.py +3922 -0
- ksadk/cli/cmd_run.py +239 -0
- ksadk/cli/cmd_status.py +588 -0
- ksadk/cli/cmd_version.py +522 -0
- ksadk/cli/cmd_web.py +288 -0
- ksadk/cli/deploy_utils.py +87 -0
- ksadk/cli/dry_run.py +214 -0
- ksadk/cli/env_options.py +82 -0
- ksadk/cli/error_utils.py +525 -0
- ksadk/cli/global_options.py +31 -0
- ksadk/cli/local_runtime.py +97 -0
- ksadk/cli/model_catalog.py +136 -0
- ksadk/cli/network_options.py +412 -0
- ksadk/cli/resource_common.py +621 -0
- ksadk/cli/storage.py +84 -0
- ksadk/cli/ui.py +431 -0
- ksadk/cli/workflow_common.py +552 -0
- ksadk/common/__init__.py +14 -0
- ksadk/common/aicp_env.py +128 -0
- ksadk/common/auth.py +190 -0
- ksadk/common/constants.py +84 -0
- ksadk/common/llm_utils.py +61 -0
- ksadk/configs/__init__.py +61 -0
- ksadk/configs/env_registry.py +343 -0
- ksadk/configs/global_config.py +200 -0
- ksadk/configs/settings.py +706 -0
- ksadk/conversations/__init__.py +95 -0
- ksadk/conversations/attachment_storage.py +379 -0
- ksadk/conversations/attachments.py +774 -0
- ksadk/conversations/compaction_pipeline.py +387 -0
- ksadk/conversations/compaction_prompt.py +128 -0
- ksadk/conversations/context.py +303 -0
- ksadk/conversations/message_projection.py +269 -0
- ksadk/conversations/model_context.py +370 -0
- ksadk/conversations/model_options.py +136 -0
- ksadk/conversations/normalize.py +446 -0
- ksadk/conversations/reasoning_markup.py +91 -0
- ksadk/conversations/run_kinds.py +61 -0
- ksadk/conversations/run_status.py +53 -0
- ksadk/conversations/runtime.py +5276 -0
- ksadk/conversations/semantic_summary.py +359 -0
- ksadk/conversations/session_title.py +254 -0
- ksadk/deployment/__init__.py +83 -0
- ksadk/deployment/agent_access.py +226 -0
- ksadk/deployment/base.py +229 -0
- ksadk/deployment/manager.py +332 -0
- ksadk/deployment/providers/__init__.py +10 -0
- ksadk/deployment/providers/serverless.py +1115 -0
- ksadk/deployment/registry.py +120 -0
- ksadk/deployment/state.py +78 -0
- ksadk/deployment/ui_config.py +21 -0
- ksadk/detection/__init__.py +7 -0
- ksadk/detection/detector.py +431 -0
- ksadk/detection/mcp_detector.py +250 -0
- ksadk/hermes_terminal.py +467 -0
- ksadk/identity/__init__.py +17 -0
- ksadk/identity/resolver.py +385 -0
- ksadk/knowledge_base/__init__.py +62 -0
- ksadk/knowledge_base/adk_tool.py +47 -0
- ksadk/knowledge_base/client.py +297 -0
- ksadk/knowledge_base/langchain_tool.py +55 -0
- ksadk/knowledge_base/service.py +66 -0
- ksadk/knowledge_base/tool.py +50 -0
- ksadk/markdown.py +169 -0
- ksadk/mcp_runtime/__init__.py +231 -0
- ksadk/memory/__init__.py +57 -0
- ksadk/memory/adk/__init__.py +51 -0
- ksadk/memory/adk/backends/__init__.py +5 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +52 -0
- ksadk/memory/adk/backends/http_ltm_backend.py +183 -0
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +96 -0
- ksadk/memory/adk/backends/sdk_ltm_backend.py +456 -0
- ksadk/memory/adk/long_term_memory.py +273 -0
- ksadk/memory/adk/resilient_session_service.py +268 -0
- ksadk/memory/adk/short_term_memory.py +304 -0
- ksadk/memory/adk_tool.py +31 -0
- ksadk/memory/backends/__init__.py +6 -0
- ksadk/memory/backends/base.py +103 -0
- ksadk/memory/backends/memory.py +112 -0
- ksadk/memory/backends/redis.py +146 -0
- ksadk/memory/langchain_tool.py +39 -0
- ksadk/memory/ltm_backend_factory.py +24 -0
- ksadk/memory/manager.py +215 -0
- ksadk/memory/service.py +172 -0
- ksadk/memory/tool.py +96 -0
- ksadk/model_policy.py +223 -0
- ksadk/openclaw_gateway.py +378 -0
- ksadk/runners/__init__.py +8 -0
- ksadk/runners/adk_runner.py +1994 -0
- ksadk/runners/base_runner.py +305 -0
- ksadk/runners/deepagents_runner.py +13 -0
- ksadk/runners/factory.py +73 -0
- ksadk/runners/langchain_runner.py +751 -0
- ksadk/runners/langgraph_runner.py +985 -0
- ksadk/runners/patch_langchain.py +187 -0
- ksadk/runners/remote_runner.py +792 -0
- ksadk/runners/usage_accumulator.py +38 -0
- ksadk/runners/utils/__init__.py +19 -0
- ksadk/runners/utils/langfuse.py +230 -0
- ksadk/runners/utils/loader.py +62 -0
- ksadk/runtime_context.py +166 -0
- ksadk/runtime_state.py +25 -0
- ksadk/sandbox/__init__.py +28 -0
- ksadk/sandbox/backends/__init__.py +9 -0
- ksadk/sandbox/backends/e2b.py +154 -0
- ksadk/sandbox/backends/local_process.py +131 -0
- ksadk/sandbox/base.py +101 -0
- ksadk/sandbox/factory.py +53 -0
- ksadk/sandbox/registry.py +187 -0
- ksadk/server/__init__.py +7 -0
- ksadk/server/api_models.py +73 -0
- ksadk/server/app.py +3931 -0
- ksadk/server/static/assets/ArtifactsPanel-D-SSn2ee.js +1 -0
- ksadk/server/static/assets/CodeBlock-P16VOLYi.js +9 -0
- ksadk/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- ksadk/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- ksadk/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- ksadk/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- ksadk/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- ksadk/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- ksadk/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- ksadk/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- ksadk/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- ksadk/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- ksadk/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- ksadk/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- ksadk/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- ksadk/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- ksadk/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- ksadk/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- ksadk/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- ksadk/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- ksadk/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- ksadk/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- ksadk/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +8 -0
- ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +1 -0
- ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-CHOd7Rch.css +1 -0
- ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +1 -0
- ksadk/server/static/assets/addon-fit-DthTIhi3.js +1 -0
- ksadk/server/static/assets/apl-CnwPGSsG.js +1 -0
- ksadk/server/static/assets/arc-BA13sHc9.js +1 -0
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +1 -0
- ksadk/server/static/assets/architectureDiagram-3BPJPVTR-DzJzgVoL.js +36 -0
- ksadk/server/static/assets/asciiarmor-qTkVPQu6.js +1 -0
- ksadk/server/static/assets/asn1-Dr8qZg38.js +1 -0
- ksadk/server/static/assets/asterisk-Bja0s6e1.js +1 -0
- ksadk/server/static/assets/blockDiagram-GPEHLZMM-UTdVcZhn.js +132 -0
- ksadk/server/static/assets/brainfuck-D5EjA2JK.js +1 -0
- ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dix0GD4m.js +10 -0
- ksadk/server/static/assets/channel-CTuydARX.js +1 -0
- ksadk/server/static/assets/chunk-2J33WTMH-C2JxuZ42.js +1 -0
- ksadk/server/static/assets/chunk-4BX2VUAB-Be57QPgD.js +1 -0
- ksadk/server/static/assets/chunk-55IACEB6-WjSuy4nv.js +1 -0
- ksadk/server/static/assets/chunk-727SXJPM-BOovP-b5.js +206 -0
- ksadk/server/static/assets/chunk-AQP2D5EJ-DOmZNmWj.js +231 -0
- ksadk/server/static/assets/chunk-FMBD7UC4-DzwlodkU.js +15 -0
- ksadk/server/static/assets/chunk-ND2GUHAM-DMqAikEU.js +1 -0
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +1 -0
- ksadk/server/static/assets/clike-DHH8Ad3s.js +1 -0
- ksadk/server/static/assets/clojure-BflJGmDX.js +1 -0
- ksadk/server/static/assets/cmake-CyVbuzPu.js +1 -0
- ksadk/server/static/assets/cobol-B_OZ4V-R.js +1 -0
- ksadk/server/static/assets/coffeescript-B2pKbFk8.js +1 -0
- ksadk/server/static/assets/commonlisp-CcllspGY.js +1 -0
- ksadk/server/static/assets/cose-bilkent-S5V4N54A-Zhy2GBnP.js +1 -0
- ksadk/server/static/assets/crystal-M5-qzICs.js +1 -0
- ksadk/server/static/assets/css-CYpP4FRV.js +1 -0
- ksadk/server/static/assets/cypher-p9eYesGn.js +1 -0
- ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +321 -0
- ksadk/server/static/assets/d-BhMjBjQP.js +1 -0
- ksadk/server/static/assets/dagre-BM42HDAG-CbKrtIUz.js +4 -0
- ksadk/server/static/assets/dagre-Bx709z4p.js +1 -0
- ksadk/server/static/assets/defaultLocale-C8Fc0cco.js +1 -0
- ksadk/server/static/assets/diagram-2AECGRRQ-BRApgQRK.js +43 -0
- ksadk/server/static/assets/diagram-5GNKFQAL-D0xbnDiK.js +10 -0
- ksadk/server/static/assets/diagram-KO2AKTUF-CVugVI9L.js +3 -0
- ksadk/server/static/assets/diagram-LMA3HP47-DozUGgee.js +24 -0
- ksadk/server/static/assets/diagram-OG6HWLK6-ZOYsKlru.js +24 -0
- ksadk/server/static/assets/diff-ChtP43wD.js +1 -0
- ksadk/server/static/assets/dist-2hInLS_J.js +9 -0
- ksadk/server/static/assets/dist-8EEyGgDx.js +1 -0
- ksadk/server/static/assets/dist-8_SfK1IP.js +1 -0
- ksadk/server/static/assets/dist-B0NP_Z0z.js +23 -0
- ksadk/server/static/assets/dist-BIJVDMuB.js +1 -0
- ksadk/server/static/assets/dist-BmlMPsCc.js +2 -0
- ksadk/server/static/assets/dist-BrCcQ47T.js +1 -0
- ksadk/server/static/assets/dist-BssVmjIo.js +6 -0
- ksadk/server/static/assets/dist-C3HIIaN5.js +1 -0
- ksadk/server/static/assets/dist-CPBtqRTW.js +1 -0
- ksadk/server/static/assets/dist-CkEKJFtk.js +1 -0
- ksadk/server/static/assets/dist-D-JMdWPQ.js +1 -0
- ksadk/server/static/assets/dist-D-xOD048.js +1 -0
- ksadk/server/static/assets/dist-D57wnGWd.js +1 -0
- ksadk/server/static/assets/dist-DL_NKc_z.js +1 -0
- ksadk/server/static/assets/dist-DMezvBGJ.js +1 -0
- ksadk/server/static/assets/dist-DmFaq_Iy.js +1 -0
- ksadk/server/static/assets/dist-Dp0k7bWI.js +1 -0
- ksadk/server/static/assets/dist-DpKtt4Bo.js +1 -0
- ksadk/server/static/assets/dist-DyOkZmze.js +11 -0
- ksadk/server/static/assets/dist-DyWsb78Q.js +1 -0
- ksadk/server/static/assets/dist-gX6GOLKq.js +1 -0
- ksadk/server/static/assets/dockerfile-BLkNEvjs.js +1 -0
- ksadk/server/static/assets/dtd-DTF72YNO.js +1 -0
- ksadk/server/static/assets/dylan-C6jEEEk-.js +1 -0
- ksadk/server/static/assets/ebnf-D4c0_ac3.js +1 -0
- ksadk/server/static/assets/ecl-BSZaIHnp.js +1 -0
- ksadk/server/static/assets/eiffel-BEjRio4Q.js +1 -0
- ksadk/server/static/assets/elm-Cshzl8qu.js +1 -0
- ksadk/server/static/assets/erDiagram-TEJ5UH35-Bk-9bgE5.js +85 -0
- ksadk/server/static/assets/erlang-DaB2Rkuy.js +1 -0
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +1 -0
- ksadk/server/static/assets/factor-D7LVTn2l.js +1 -0
- ksadk/server/static/assets/fcl-ClOhbFR7.js +1 -0
- ksadk/server/static/assets/flowDiagram-I6XJVG4X-Dn9kZVuZ.js +162 -0
- ksadk/server/static/assets/forth-Cezjo90N.js +1 -0
- ksadk/server/static/assets/fortran-Bt6PBEDR.js +1 -0
- ksadk/server/static/assets/ganttDiagram-6RSMTGT7-BGxXlJqm.js +292 -0
- ksadk/server/static/assets/gas-BHEdbvp9.js +1 -0
- ksadk/server/static/assets/gherkin-oBAE_ms0.js +1 -0
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-C7Q0X-wi.js +106 -0
- ksadk/server/static/assets/graphlib-B8gBHxth.js +1 -0
- ksadk/server/static/assets/groovy-BC3IOsC8.js +1 -0
- ksadk/server/static/assets/haskell-BRsoo5mP.js +1 -0
- ksadk/server/static/assets/haxe-DIz0ZZqd.js +1 -0
- ksadk/server/static/assets/http-CLVgA2GD.js +1 -0
- ksadk/server/static/assets/idl-BUZw3wgd.js +1 -0
- ksadk/server/static/assets/index-C00Lwg3L.js +165 -0
- ksadk/server/static/assets/index-DAURbCsP.css +1 -0
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +1 -0
- ksadk/server/static/assets/infoDiagram-5YYISTIA-BO_CSqq5.js +2 -0
- ksadk/server/static/assets/init-D6jRqBbL.js +1 -0
- ksadk/server/static/assets/ishikawaDiagram-YF4QCWOH-eYLPLd46.js +70 -0
- ksadk/server/static/assets/javascript-9Tg8ixDm.js +1 -0
- ksadk/server/static/assets/journeyDiagram-JHISSGLW-De9geSb0.js +139 -0
- ksadk/server/static/assets/julia-CQfgDRfP.js +1 -0
- ksadk/server/static/assets/kanban-definition-UN3LZRKU-Cs80kQto.js +89 -0
- ksadk/server/static/assets/katex-DSrCFD8n.js +265 -0
- ksadk/server/static/assets/linear-CN9sbKTF.js +1 -0
- ksadk/server/static/assets/livescript-BXxG0Yva.js +1 -0
- ksadk/server/static/assets/lua-CiA1ziua.js +1 -0
- ksadk/server/static/assets/mathematica-u8YMmbU0.js +1 -0
- ksadk/server/static/assets/mbox-BEMVcqjs.js +1 -0
- ksadk/server/static/assets/mermaid-parser.core-DlgSBU8l.js +161 -0
- ksadk/server/static/assets/mindmap-definition-RKZ34NQL-9afz7Oug.js +96 -0
- ksadk/server/static/assets/mirc-C9zpzAZU.js +1 -0
- ksadk/server/static/assets/mllike-B45xgp2S.js +1 -0
- ksadk/server/static/assets/modelica-CqHI_q-D.js +1 -0
- ksadk/server/static/assets/mscgen-qgSLujhx.js +1 -0
- ksadk/server/static/assets/mumps-CW_TBmxz.js +1 -0
- ksadk/server/static/assets/nginx-BMaDbqW3.js +1 -0
- ksadk/server/static/assets/nsis-AQ_alPln.js +1 -0
- ksadk/server/static/assets/ntriples-d28e9m0E.js +1 -0
- ksadk/server/static/assets/octave-D2Q8cUp1.js +1 -0
- ksadk/server/static/assets/ordinal-hYBb2elL.js +1 -0
- ksadk/server/static/assets/oz-BwTct_5T.js +1 -0
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +1 -0
- ksadk/server/static/assets/pascal-Dp_KdFgX.js +1 -0
- ksadk/server/static/assets/perl-CdEHsPfU.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +1 -0
- ksadk/server/static/assets/pieDiagram-4H26LBE5-DPv_VstN.js +30 -0
- ksadk/server/static/assets/pig-AF4Hwhju.js +1 -0
- ksadk/server/static/assets/powershell-CzG71I4L.js +1 -0
- ksadk/server/static/assets/properties-C357ku5U.js +1 -0
- ksadk/server/static/assets/protobuf-D1ij_kNL.js +1 -0
- ksadk/server/static/assets/pug-Bugr-47h.js +1 -0
- ksadk/server/static/assets/puppet-B4hC2X4m.js +1 -0
- ksadk/server/static/assets/python-BqYw9LYJ.js +1 -0
- ksadk/server/static/assets/q-B9NhS7L_.js +1 -0
- ksadk/server/static/assets/quadrantDiagram-W4KKPZXB-o77wZ1XK.js +7 -0
- ksadk/server/static/assets/r-DADKenSm.js +1 -0
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +1 -0
- ksadk/server/static/assets/requirementDiagram-4Y6WPE33-BYVw81VC.js +84 -0
- ksadk/server/static/assets/rpm-gpDK5sbt.js +1 -0
- ksadk/server/static/assets/ruby-DsYpTuWg.js +1 -0
- ksadk/server/static/assets/sankeyDiagram-5OEKKPKP-B7nZFMKT.js +40 -0
- ksadk/server/static/assets/sas-C2OyD2Y6.js +1 -0
- ksadk/server/static/assets/scheme-CyDdOh7e.js +1 -0
- ksadk/server/static/assets/sequenceDiagram-3UESZ5HK-BGRRrFw5.js +162 -0
- ksadk/server/static/assets/shell-Du5Qg9im.js +1 -0
- ksadk/server/static/assets/sieve-BAxa3IjF.js +1 -0
- ksadk/server/static/assets/simple-mode-DRpGK0lJ.js +1 -0
- ksadk/server/static/assets/smalltalk-BLp5-jwC.js +1 -0
- ksadk/server/static/assets/solr-BBopId9w.js +1 -0
- ksadk/server/static/assets/sparql-Dz-mlCAC.js +1 -0
- ksadk/server/static/assets/spreadsheet-CTIncYxj.js +1 -0
- ksadk/server/static/assets/sql-DcCJ9jop.js +1 -0
- ksadk/server/static/assets/stateDiagram-AJRCARHV-BUfHCoPs.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +1 -0
- ksadk/server/static/assets/stex-sDrdk2BW.js +1 -0
- ksadk/server/static/assets/stylus-DLLZ8dgm.js +1 -0
- ksadk/server/static/assets/swift-DKB6_1j6.js +1 -0
- ksadk/server/static/assets/tcl-BGfruO1u.js +1 -0
- ksadk/server/static/assets/textile-DWBqthVk.js +1 -0
- ksadk/server/static/assets/tiddlywiki-VlUiK86J.js +1 -0
- ksadk/server/static/assets/tiki-CxVlf5KZ.js +1 -0
- ksadk/server/static/assets/timeline-definition-PNZ67QCA-FbCTGrf9.js +120 -0
- ksadk/server/static/assets/toml-LvoKBwCs.js +1 -0
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +1 -0
- ksadk/server/static/assets/troff-C2dAgh7P.js +1 -0
- ksadk/server/static/assets/ttcn-BePYh-hG.js +1 -0
- ksadk/server/static/assets/ttcn-cfg-CY0aot7Q.js +1 -0
- ksadk/server/static/assets/turtle-BDLWE2cD.js +1 -0
- ksadk/server/static/assets/vb-BthdM_4N.js +1 -0
- ksadk/server/static/assets/vbscript-D9f6rSsU.js +1 -0
- ksadk/server/static/assets/velocity-CqftRsjg.js +1 -0
- ksadk/server/static/assets/vennDiagram-CIIHVFJN-DWgwtPsJ.js +34 -0
- ksadk/server/static/assets/verilog-D-x6ne0Z.js +1 -0
- ksadk/server/static/assets/vhdl-CMrOq2q1.js +1 -0
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-2x8G-tTM.js +78 -0
- ksadk/server/static/assets/webidl-D5jiJvOU.js +1 -0
- ksadk/server/static/assets/xquery-fj_R-kMw.js +1 -0
- ksadk/server/static/assets/xterm-DooSxjI5.js +36 -0
- ksadk/server/static/assets/xychartDiagram-2RQKCTM6-Drma3AYl.js +7 -0
- ksadk/server/static/assets/yacas-CXXgtw0p.js +1 -0
- ksadk/server/static/assets/z80-CL1naaMV.js +1 -0
- ksadk/server/static/favicon.svg +1 -0
- ksadk/server/static/icons.svg +24 -0
- ksadk/server/static/index.html +14 -0
- ksadk/server/terminal_sessions.py +551 -0
- ksadk/sessions/__init__.py +279 -0
- ksadk/sessions/base.py +354 -0
- ksadk/sessions/continuity.py +337 -0
- ksadk/sessions/errors.py +7 -0
- ksadk/sessions/in_memory.py +273 -0
- ksadk/sessions/local_service.py +949 -0
- ksadk/sessions/postgres_service.py +901 -0
- ksadk/sessions/resilience.py +52 -0
- ksadk/sessions/resilient.py +360 -0
- ksadk/sessions/sqlite_service.py +13 -0
- ksadk/skills/__init__.py +9 -0
- ksadk/skills/loader.py +46 -0
- ksadk/skills/models.py +120 -0
- ksadk/skills/package_store.py +104 -0
- ksadk/skills/runtime/__init__.py +17 -0
- ksadk/skills/runtime/agent.py +183 -0
- ksadk/skills/runtime/artifacts.py +39 -0
- ksadk/skills/runtime/backends/__init__.py +1 -0
- ksadk/skills/runtime/backends/disabled.py +8 -0
- ksadk/skills/runtime/backends/e2b.py +168 -0
- ksadk/skills/runtime/backends/local.py +95 -0
- ksadk/skills/runtime/base.py +103 -0
- ksadk/skills/runtime/executor.py +219 -0
- ksadk/skills/runtime/factory.py +37 -0
- ksadk/skills/runtime/loader.py +131 -0
- ksadk/skills/runtime/registry.py +150 -0
- ksadk/skills/runtime/request.py +67 -0
- ksadk/skills/service_client.py +239 -0
- ksadk/skills/service_env.py +58 -0
- ksadk/skills/tool_defs.py +221 -0
- ksadk/terminal_client.py +69 -0
- ksadk/terminal_exec_policy.py +229 -0
- ksadk/tools/__init__.py +17 -0
- ksadk/tools/gateway.py +230 -0
- ksadk/tools/result_budget.py +154 -0
- ksadk/toolsets/__init__.py +854 -0
- ksadk/toolsets/_langchain.py +11 -0
- ksadk/toolsets/platform.py +93 -0
- ksadk/toolsets/sandbox.py +310 -0
- ksadk/toolsets/skills.py +217 -0
- ksadk/toolsets/web.py +266 -0
- ksadk/toolsets/workspace.py +917 -0
- ksadk/toolsets/workspace_state.py +44 -0
- ksadk/tracing/__init__.py +37 -0
- ksadk/tracing/exporters/__init__.py +1 -0
- ksadk/tracing/exporters/inmemory_exporter.py +116 -0
- ksadk/tracing/exporters/langfuse_exporter.py +445 -0
- ksadk/tracing/setup.py +892 -0
- ksadk/tracing/span_utils.py +114 -0
- ksadk/tui/__init__.py +27 -0
- ksadk/tui/loop.py +1877 -0
- ksadk/tui/stream_render.py +68 -0
- ksadk/ui_config.py +168 -0
- ksadk/version.py +4 -0
- ksadk_runtime_common/__init__.py +20 -0
- ksadk_runtime_common/memory_backend/__init__.py +29 -0
- ksadk_runtime_common/memory_backend/manifest.py +79 -0
- ksadk_runtime_common/memory_backend/providers/__init__.py +6 -0
- ksadk_runtime_common/memory_backend/providers/lancedb.py +151 -0
- ksadk_runtime_common/memory_backend/providers/mem0.py +74 -0
- ksadk_runtime_common/memory_backend/registry.py +56 -0
- ksadk_runtime_common/memory_backend/render.py +71 -0
- ksadk_runtime_common/schemas/memory_backend_manifest.schema.json +148 -0
- ksadk_runtime_common/workspace_files/__init__.py +29 -0
- ksadk_runtime_common/workspace_files/bootstrap.py +56 -0
- ksadk_runtime_common/workspace_files/constants.py +9 -0
- ksadk_runtime_common/workspace_files/path_utils.py +71 -0
- ksadk_runtime_common/workspace_files/preview.py +96 -0
- ksadk_runtime_common/workspace_files/router.py +254 -0
- agentengine_sdk_python/__init__.py +0 -3
- agentengine_sdk_python-0.6.4.dist-info/METADATA +0 -48
- agentengine_sdk_python-0.6.4.dist-info/RECORD +0 -5
- agentengine_sdk_python-0.6.4.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
"""PostgreSQL shared session backend for production multi-pod runtimes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import time
|
|
9
|
+
from typing import Any, Optional
|
|
10
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
11
|
+
|
|
12
|
+
from ksadk.sessions.base import (
|
|
13
|
+
BaseSessionService,
|
|
14
|
+
Session,
|
|
15
|
+
SessionEvent,
|
|
16
|
+
SessionState,
|
|
17
|
+
generate_id,
|
|
18
|
+
)
|
|
19
|
+
from ksadk.sessions.errors import SessionBackendUnavailable
|
|
20
|
+
|
|
21
|
+
KSADK_PG_SESSIONS_TABLE = "ksadk_sessions"
|
|
22
|
+
KSADK_PG_EVENTS_TABLE = "ksadk_events"
|
|
23
|
+
KSADK_PG_STATES_TABLE = "ksadk_states"
|
|
24
|
+
PG_READABLE_EVENTS_VIEW = "ksadk_session_events_readable"
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class PostgresSessionService(BaseSessionService):
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
*,
|
|
33
|
+
dsn: str,
|
|
34
|
+
namespace: str = "default",
|
|
35
|
+
tenant_id: str = "default",
|
|
36
|
+
workspace_id: str = "default",
|
|
37
|
+
min_size: int = 1,
|
|
38
|
+
max_size: int = 10,
|
|
39
|
+
connect_timeout: float = 5.0,
|
|
40
|
+
):
|
|
41
|
+
if not dsn.strip():
|
|
42
|
+
raise ValueError("KSADK_SESSION_DSN is required when KSADK_SESSION_BACKEND=postgres")
|
|
43
|
+
self.dsn = dsn.strip()
|
|
44
|
+
self.namespace = namespace.strip() or "default"
|
|
45
|
+
self.tenant_id = tenant_id.strip() or "default"
|
|
46
|
+
self.workspace_id = workspace_id.strip() or "default"
|
|
47
|
+
self.min_size = min_size
|
|
48
|
+
self.max_size = max_size
|
|
49
|
+
self.connect_timeout = connect_timeout
|
|
50
|
+
self._pool: Any = None
|
|
51
|
+
self._pool_lock = asyncio.Lock()
|
|
52
|
+
self._schema_ready = False
|
|
53
|
+
self._schema_lock = asyncio.Lock()
|
|
54
|
+
|
|
55
|
+
async def create_session(
|
|
56
|
+
self,
|
|
57
|
+
agent_id: str,
|
|
58
|
+
user_id: str,
|
|
59
|
+
session_id: Optional[str] = None,
|
|
60
|
+
) -> Session:
|
|
61
|
+
await self._ensure_schema()
|
|
62
|
+
session_key = session_id or generate_id()
|
|
63
|
+
async with self._pool.acquire() as connection:
|
|
64
|
+
async with connection.transaction():
|
|
65
|
+
existing = await self._get_session_with_connection(connection, session_key)
|
|
66
|
+
if existing is not None:
|
|
67
|
+
return existing
|
|
68
|
+
|
|
69
|
+
now = time.time()
|
|
70
|
+
await connection.execute(
|
|
71
|
+
f"""
|
|
72
|
+
INSERT INTO {KSADK_PG_SESSIONS_TABLE} (
|
|
73
|
+
namespace, tenant_id, workspace_id, id, agent_id, user_id, title, title_source, summary,
|
|
74
|
+
first_prompt, last_prompt, state_json, created_at, updated_at, version
|
|
75
|
+
)
|
|
76
|
+
VALUES ($1, $2, $3, $4, $5, $6, '', '', '', '', '', $7::jsonb, $8, $9, 0)
|
|
77
|
+
ON CONFLICT (namespace, id) DO NOTHING
|
|
78
|
+
""",
|
|
79
|
+
self.namespace,
|
|
80
|
+
self.tenant_id,
|
|
81
|
+
self.workspace_id,
|
|
82
|
+
session_key,
|
|
83
|
+
agent_id,
|
|
84
|
+
user_id,
|
|
85
|
+
"{}",
|
|
86
|
+
now,
|
|
87
|
+
now,
|
|
88
|
+
)
|
|
89
|
+
persisted = await self._get_session_with_connection(
|
|
90
|
+
connection,
|
|
91
|
+
session_key,
|
|
92
|
+
for_update=True,
|
|
93
|
+
)
|
|
94
|
+
if persisted is None:
|
|
95
|
+
raise RuntimeError(f"Failed to create Postgres session {session_key}")
|
|
96
|
+
await connection.execute(
|
|
97
|
+
f"""
|
|
98
|
+
INSERT INTO {KSADK_PG_STATES_TABLE} (
|
|
99
|
+
namespace, tenant_id, workspace_id, scope, agent_id, user_id, session_id, state_json, version, updated_at
|
|
100
|
+
)
|
|
101
|
+
VALUES ($1, $2, $3, 'session', $4, $5, $6, $7::jsonb, 0, $8)
|
|
102
|
+
ON CONFLICT (namespace, scope, agent_id, user_id, session_id)
|
|
103
|
+
DO NOTHING
|
|
104
|
+
""",
|
|
105
|
+
self.namespace,
|
|
106
|
+
self.tenant_id,
|
|
107
|
+
self.workspace_id,
|
|
108
|
+
persisted.agent_id,
|
|
109
|
+
persisted.user_id,
|
|
110
|
+
session_key,
|
|
111
|
+
"{}",
|
|
112
|
+
now,
|
|
113
|
+
)
|
|
114
|
+
return persisted
|
|
115
|
+
|
|
116
|
+
async def get_session(self, session_id: str) -> Optional[Session]:
|
|
117
|
+
await self._ensure_schema()
|
|
118
|
+
async with self._pool.acquire() as connection:
|
|
119
|
+
return await self._get_session_with_connection(connection, session_id)
|
|
120
|
+
|
|
121
|
+
async def list_sessions(
|
|
122
|
+
self,
|
|
123
|
+
agent_id: str,
|
|
124
|
+
user_id: Optional[str] = None,
|
|
125
|
+
offset: Optional[int] = None,
|
|
126
|
+
limit: Optional[int] = None,
|
|
127
|
+
) -> list[Session]:
|
|
128
|
+
await self._ensure_schema()
|
|
129
|
+
async with self._pool.acquire() as connection:
|
|
130
|
+
query = f"""
|
|
131
|
+
SELECT id, agent_id, user_id, title, title_source, summary, first_prompt, last_prompt,
|
|
132
|
+
state_json, created_at, updated_at, version
|
|
133
|
+
FROM {KSADK_PG_SESSIONS_TABLE}
|
|
134
|
+
WHERE namespace = $1 AND agent_id = $2
|
|
135
|
+
"""
|
|
136
|
+
params: list[Any] = [self.namespace, agent_id]
|
|
137
|
+
if user_id is not None:
|
|
138
|
+
params.append(user_id)
|
|
139
|
+
query += f" AND user_id = ${len(params)}"
|
|
140
|
+
query += " ORDER BY updated_at DESC, created_at DESC"
|
|
141
|
+
if limit is not None:
|
|
142
|
+
params.append(limit)
|
|
143
|
+
query += f" LIMIT ${len(params)}"
|
|
144
|
+
if offset is not None:
|
|
145
|
+
params.append(offset)
|
|
146
|
+
query += f" OFFSET ${len(params)}"
|
|
147
|
+
elif offset is not None:
|
|
148
|
+
params.append(offset)
|
|
149
|
+
query += f" OFFSET ${len(params)}"
|
|
150
|
+
rows = await connection.fetch(query, *params)
|
|
151
|
+
return [self._session_from_row(row, events=[]) for row in rows]
|
|
152
|
+
|
|
153
|
+
async def count_sessions(
|
|
154
|
+
self,
|
|
155
|
+
agent_id: str,
|
|
156
|
+
user_id: Optional[str] = None,
|
|
157
|
+
) -> int:
|
|
158
|
+
await self._ensure_schema()
|
|
159
|
+
async with self._pool.acquire() as connection:
|
|
160
|
+
query = f"""
|
|
161
|
+
SELECT COUNT(*) AS total
|
|
162
|
+
FROM {KSADK_PG_SESSIONS_TABLE}
|
|
163
|
+
WHERE namespace = $1 AND agent_id = $2
|
|
164
|
+
"""
|
|
165
|
+
params: list[Any] = [self.namespace, agent_id]
|
|
166
|
+
if user_id is not None:
|
|
167
|
+
params.append(user_id)
|
|
168
|
+
query += f" AND user_id = ${len(params)}"
|
|
169
|
+
return int(await connection.fetchval(query, *params) or 0)
|
|
170
|
+
|
|
171
|
+
async def delete_session(self, session_id: str) -> bool:
|
|
172
|
+
await self._ensure_schema()
|
|
173
|
+
async with self._pool.acquire() as connection:
|
|
174
|
+
async with connection.transaction():
|
|
175
|
+
await connection.execute(
|
|
176
|
+
f"""
|
|
177
|
+
DELETE FROM {KSADK_PG_STATES_TABLE}
|
|
178
|
+
WHERE namespace = $1 AND session_id = $2
|
|
179
|
+
""",
|
|
180
|
+
self.namespace,
|
|
181
|
+
session_id,
|
|
182
|
+
)
|
|
183
|
+
result = await connection.execute(
|
|
184
|
+
f"""
|
|
185
|
+
DELETE FROM {KSADK_PG_SESSIONS_TABLE}
|
|
186
|
+
WHERE namespace = $1 AND id = $2
|
|
187
|
+
""",
|
|
188
|
+
self.namespace,
|
|
189
|
+
session_id,
|
|
190
|
+
)
|
|
191
|
+
return not result.endswith(" 0")
|
|
192
|
+
|
|
193
|
+
async def update_session_metadata(
|
|
194
|
+
self,
|
|
195
|
+
session_id: str,
|
|
196
|
+
*,
|
|
197
|
+
title: Optional[str] = None,
|
|
198
|
+
title_source: Optional[str] = None,
|
|
199
|
+
summary: Optional[str] = None,
|
|
200
|
+
first_prompt: Optional[str] = None,
|
|
201
|
+
last_prompt: Optional[str] = None,
|
|
202
|
+
) -> Session:
|
|
203
|
+
await self._ensure_schema()
|
|
204
|
+
async with self._pool.acquire() as connection:
|
|
205
|
+
async with connection.transaction():
|
|
206
|
+
row = await connection.fetchrow(
|
|
207
|
+
f"""
|
|
208
|
+
SELECT id, agent_id, user_id, title, title_source, summary, first_prompt, last_prompt,
|
|
209
|
+
state_json, created_at, updated_at, version
|
|
210
|
+
FROM {KSADK_PG_SESSIONS_TABLE}
|
|
211
|
+
WHERE namespace = $1 AND id = $2
|
|
212
|
+
FOR UPDATE
|
|
213
|
+
""",
|
|
214
|
+
self.namespace,
|
|
215
|
+
session_id,
|
|
216
|
+
)
|
|
217
|
+
if row is None:
|
|
218
|
+
raise ValueError(f"Session {session_id} not found")
|
|
219
|
+
updated_at = time.time()
|
|
220
|
+
next_title = row["title"] if title is None else title
|
|
221
|
+
next_title_source = row["title_source"] if title_source is None else title_source
|
|
222
|
+
next_summary = row["summary"] if summary is None else summary
|
|
223
|
+
next_first_prompt = row["first_prompt"] if first_prompt is None else first_prompt
|
|
224
|
+
next_last_prompt = row["last_prompt"] if last_prompt is None else last_prompt
|
|
225
|
+
await connection.execute(
|
|
226
|
+
f"""
|
|
227
|
+
UPDATE {KSADK_PG_SESSIONS_TABLE}
|
|
228
|
+
SET title = $1, title_source = $2, summary = $3, first_prompt = $4,
|
|
229
|
+
last_prompt = $5, updated_at = $6
|
|
230
|
+
WHERE namespace = $7 AND id = $8
|
|
231
|
+
""",
|
|
232
|
+
next_title,
|
|
233
|
+
next_title_source,
|
|
234
|
+
next_summary,
|
|
235
|
+
next_first_prompt,
|
|
236
|
+
next_last_prompt,
|
|
237
|
+
updated_at,
|
|
238
|
+
self.namespace,
|
|
239
|
+
session_id,
|
|
240
|
+
)
|
|
241
|
+
return Session(
|
|
242
|
+
id=row["id"],
|
|
243
|
+
agent_id=row["agent_id"],
|
|
244
|
+
user_id=row["user_id"],
|
|
245
|
+
title=next_title,
|
|
246
|
+
title_source=next_title_source,
|
|
247
|
+
summary=next_summary,
|
|
248
|
+
first_prompt=next_first_prompt,
|
|
249
|
+
last_prompt=next_last_prompt,
|
|
250
|
+
state=self._json_to_dict(row["state_json"]),
|
|
251
|
+
created_at=row["created_at"],
|
|
252
|
+
updated_at=updated_at,
|
|
253
|
+
version=row["version"],
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
async def append_event(self, session_id: str, event: SessionEvent) -> SessionEvent:
|
|
257
|
+
await self._ensure_schema()
|
|
258
|
+
async with self._pool.acquire() as connection:
|
|
259
|
+
async with connection.transaction():
|
|
260
|
+
session_row = await connection.fetchrow(
|
|
261
|
+
f"""
|
|
262
|
+
SELECT agent_id, user_id, state_json, version
|
|
263
|
+
FROM {KSADK_PG_SESSIONS_TABLE}
|
|
264
|
+
WHERE namespace = $1 AND id = $2
|
|
265
|
+
FOR UPDATE
|
|
266
|
+
""",
|
|
267
|
+
self.namespace,
|
|
268
|
+
session_id,
|
|
269
|
+
)
|
|
270
|
+
if session_row is None:
|
|
271
|
+
raise ValueError(f"Session {session_id} not found")
|
|
272
|
+
|
|
273
|
+
next_seq = await connection.fetchval(
|
|
274
|
+
f"""
|
|
275
|
+
SELECT COALESCE(MAX(seq_id), 0) + 1
|
|
276
|
+
FROM {KSADK_PG_EVENTS_TABLE}
|
|
277
|
+
WHERE namespace = $1 AND session_id = $2
|
|
278
|
+
""",
|
|
279
|
+
self.namespace,
|
|
280
|
+
session_id,
|
|
281
|
+
)
|
|
282
|
+
stored = SessionEvent(
|
|
283
|
+
id=event.id or generate_id(),
|
|
284
|
+
session_id=session_id,
|
|
285
|
+
author=event.author,
|
|
286
|
+
event_type=event.event_type,
|
|
287
|
+
content=dict(event.content),
|
|
288
|
+
timestamp=event.timestamp,
|
|
289
|
+
state_delta=dict(event.state_delta),
|
|
290
|
+
seq_id=int(next_seq or 1),
|
|
291
|
+
invocation_id=event.invocation_id,
|
|
292
|
+
metadata=dict(event.metadata),
|
|
293
|
+
)
|
|
294
|
+
await connection.execute(
|
|
295
|
+
f"""
|
|
296
|
+
INSERT INTO {KSADK_PG_EVENTS_TABLE} (
|
|
297
|
+
namespace, tenant_id, workspace_id, id, session_id, author, event_type, content_json, timestamp,
|
|
298
|
+
state_delta_json, seq_id, invocation_id, metadata_json
|
|
299
|
+
)
|
|
300
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, $10::jsonb, $11, $12, $13::jsonb)
|
|
301
|
+
""",
|
|
302
|
+
self.namespace,
|
|
303
|
+
self.tenant_id,
|
|
304
|
+
self.workspace_id,
|
|
305
|
+
stored.id,
|
|
306
|
+
stored.session_id,
|
|
307
|
+
stored.author,
|
|
308
|
+
stored.event_type,
|
|
309
|
+
json.dumps(stored.content),
|
|
310
|
+
stored.timestamp,
|
|
311
|
+
json.dumps(stored.state_delta),
|
|
312
|
+
stored.seq_id,
|
|
313
|
+
stored.invocation_id,
|
|
314
|
+
json.dumps(stored.metadata),
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
updated_at = time.time()
|
|
318
|
+
state = self._json_to_dict(session_row["state_json"])
|
|
319
|
+
version = int(session_row["version"] or 0)
|
|
320
|
+
if stored.state_delta:
|
|
321
|
+
state.update(stored.state_delta)
|
|
322
|
+
version += 1
|
|
323
|
+
await self._write_session_state(
|
|
324
|
+
connection,
|
|
325
|
+
session_id=session_id,
|
|
326
|
+
agent_id=session_row["agent_id"],
|
|
327
|
+
user_id=session_row["user_id"],
|
|
328
|
+
state=state,
|
|
329
|
+
version=version,
|
|
330
|
+
updated_at=updated_at,
|
|
331
|
+
)
|
|
332
|
+
return stored
|
|
333
|
+
|
|
334
|
+
async def get_events(
|
|
335
|
+
self,
|
|
336
|
+
session_id: str,
|
|
337
|
+
offset: Optional[int] = None,
|
|
338
|
+
limit: Optional[int] = None,
|
|
339
|
+
after_seq_id: Optional[int] = None,
|
|
340
|
+
before_seq_id: Optional[int] = None,
|
|
341
|
+
) -> list[SessionEvent]:
|
|
342
|
+
await self._ensure_schema()
|
|
343
|
+
# seq 过滤先应用,再对结果集应用"最新 N 条" offset/limit 语义。
|
|
344
|
+
conditions = ["namespace = $1", "session_id = $2"]
|
|
345
|
+
params: list[Any] = [self.namespace, session_id]
|
|
346
|
+
if after_seq_id is not None:
|
|
347
|
+
params.append(after_seq_id)
|
|
348
|
+
conditions.append(f"seq_id > ${len(params)}")
|
|
349
|
+
if before_seq_id is not None:
|
|
350
|
+
params.append(before_seq_id)
|
|
351
|
+
conditions.append(f"seq_id < ${len(params)}")
|
|
352
|
+
where_clause = " AND ".join(conditions)
|
|
353
|
+
async with self._pool.acquire() as connection:
|
|
354
|
+
if limit is not None:
|
|
355
|
+
params.extend([limit, offset or 0])
|
|
356
|
+
limit_param = len(params) - 1
|
|
357
|
+
offset_param = len(params)
|
|
358
|
+
query = f"""
|
|
359
|
+
SELECT id, session_id, author, event_type, content_json, timestamp,
|
|
360
|
+
state_delta_json, seq_id, invocation_id, metadata_json
|
|
361
|
+
FROM (
|
|
362
|
+
SELECT id, session_id, author, event_type, content_json, timestamp,
|
|
363
|
+
state_delta_json, seq_id, invocation_id, metadata_json
|
|
364
|
+
FROM {KSADK_PG_EVENTS_TABLE}
|
|
365
|
+
WHERE {where_clause}
|
|
366
|
+
ORDER BY seq_id DESC
|
|
367
|
+
LIMIT ${limit_param} OFFSET ${offset_param}
|
|
368
|
+
) AS latest_events
|
|
369
|
+
ORDER BY seq_id ASC
|
|
370
|
+
"""
|
|
371
|
+
elif offset is not None:
|
|
372
|
+
params.append(offset)
|
|
373
|
+
offset_param = len(params)
|
|
374
|
+
query = f"""
|
|
375
|
+
SELECT id, session_id, author, event_type, content_json, timestamp,
|
|
376
|
+
state_delta_json, seq_id, invocation_id, metadata_json
|
|
377
|
+
FROM (
|
|
378
|
+
SELECT id, session_id, author, event_type, content_json, timestamp,
|
|
379
|
+
state_delta_json, seq_id, invocation_id, metadata_json
|
|
380
|
+
FROM {KSADK_PG_EVENTS_TABLE}
|
|
381
|
+
WHERE {where_clause}
|
|
382
|
+
ORDER BY seq_id DESC
|
|
383
|
+
OFFSET ${offset_param}
|
|
384
|
+
) AS latest_events
|
|
385
|
+
ORDER BY seq_id ASC
|
|
386
|
+
"""
|
|
387
|
+
else:
|
|
388
|
+
query = f"""
|
|
389
|
+
SELECT id, session_id, author, event_type, content_json, timestamp,
|
|
390
|
+
state_delta_json, seq_id, invocation_id, metadata_json
|
|
391
|
+
FROM {KSADK_PG_EVENTS_TABLE}
|
|
392
|
+
WHERE {where_clause}
|
|
393
|
+
ORDER BY seq_id ASC
|
|
394
|
+
"""
|
|
395
|
+
rows = await connection.fetch(query, *params)
|
|
396
|
+
return [self._event_from_row(row) for row in rows]
|
|
397
|
+
|
|
398
|
+
async def count_events(
|
|
399
|
+
self,
|
|
400
|
+
session_id: str,
|
|
401
|
+
after_seq_id: Optional[int] = None,
|
|
402
|
+
before_seq_id: Optional[int] = None,
|
|
403
|
+
) -> int:
|
|
404
|
+
await self._ensure_schema()
|
|
405
|
+
conditions = ["namespace = $1", "session_id = $2"]
|
|
406
|
+
params: list[Any] = [self.namespace, session_id]
|
|
407
|
+
if after_seq_id is not None:
|
|
408
|
+
params.append(after_seq_id)
|
|
409
|
+
conditions.append(f"seq_id > ${len(params)}")
|
|
410
|
+
if before_seq_id is not None:
|
|
411
|
+
params.append(before_seq_id)
|
|
412
|
+
conditions.append(f"seq_id < ${len(params)}")
|
|
413
|
+
where_clause = " AND ".join(conditions)
|
|
414
|
+
async with self._pool.acquire() as connection:
|
|
415
|
+
query = f"""
|
|
416
|
+
SELECT COUNT(*) AS total
|
|
417
|
+
FROM {KSADK_PG_EVENTS_TABLE}
|
|
418
|
+
WHERE {where_clause}
|
|
419
|
+
"""
|
|
420
|
+
return int(await connection.fetchval(query, *params) or 0)
|
|
421
|
+
|
|
422
|
+
async def get_state(
|
|
423
|
+
self,
|
|
424
|
+
agent_id: str,
|
|
425
|
+
user_id: Optional[str],
|
|
426
|
+
session_id: Optional[str],
|
|
427
|
+
scope: str = "session",
|
|
428
|
+
) -> Optional[SessionState]:
|
|
429
|
+
await self._ensure_schema()
|
|
430
|
+
async with self._pool.acquire() as connection:
|
|
431
|
+
if scope == "session" and session_id:
|
|
432
|
+
session = await self._get_session_with_connection(connection, session_id)
|
|
433
|
+
if session is None:
|
|
434
|
+
return None
|
|
435
|
+
return SessionState(
|
|
436
|
+
scope="session",
|
|
437
|
+
agent_id=session.agent_id,
|
|
438
|
+
user_id=session.user_id,
|
|
439
|
+
session_id=session.id,
|
|
440
|
+
state=dict(session.state),
|
|
441
|
+
version=session.version,
|
|
442
|
+
updated_at=session.updated_at,
|
|
443
|
+
)
|
|
444
|
+
row = await connection.fetchrow(
|
|
445
|
+
f"""
|
|
446
|
+
SELECT scope, agent_id, user_id, session_id, state_json, version, updated_at
|
|
447
|
+
FROM {KSADK_PG_STATES_TABLE}
|
|
448
|
+
WHERE namespace = $1 AND scope = $2 AND agent_id = $3 AND user_id = $4 AND session_id = $5
|
|
449
|
+
""",
|
|
450
|
+
self.namespace,
|
|
451
|
+
scope,
|
|
452
|
+
agent_id,
|
|
453
|
+
user_id or "",
|
|
454
|
+
session_id or "",
|
|
455
|
+
)
|
|
456
|
+
if row is None:
|
|
457
|
+
return None
|
|
458
|
+
return self._state_from_row(row)
|
|
459
|
+
|
|
460
|
+
async def update_state(
|
|
461
|
+
self,
|
|
462
|
+
*,
|
|
463
|
+
agent_id: str,
|
|
464
|
+
user_id: Optional[str],
|
|
465
|
+
session_id: Optional[str],
|
|
466
|
+
scope: str,
|
|
467
|
+
state_delta: dict[str, Any],
|
|
468
|
+
) -> SessionState:
|
|
469
|
+
await self._ensure_schema()
|
|
470
|
+
async with self._pool.acquire() as connection:
|
|
471
|
+
async with connection.transaction():
|
|
472
|
+
updated_at = time.time()
|
|
473
|
+
if scope == "session":
|
|
474
|
+
if not session_id:
|
|
475
|
+
raise ValueError("session_id is required for session scope")
|
|
476
|
+
session = await self._get_session_with_connection(
|
|
477
|
+
connection,
|
|
478
|
+
session_id,
|
|
479
|
+
for_update=True,
|
|
480
|
+
)
|
|
481
|
+
if session is None:
|
|
482
|
+
raise ValueError(f"Session {session_id} not found")
|
|
483
|
+
next_state = dict(session.state)
|
|
484
|
+
next_state.update(state_delta)
|
|
485
|
+
next_version = session.version + 1
|
|
486
|
+
await self._write_session_state(
|
|
487
|
+
connection,
|
|
488
|
+
session_id=session.id,
|
|
489
|
+
agent_id=session.agent_id,
|
|
490
|
+
user_id=session.user_id,
|
|
491
|
+
state=next_state,
|
|
492
|
+
version=next_version,
|
|
493
|
+
updated_at=updated_at,
|
|
494
|
+
)
|
|
495
|
+
return SessionState(
|
|
496
|
+
scope="session",
|
|
497
|
+
agent_id=session.agent_id,
|
|
498
|
+
user_id=session.user_id,
|
|
499
|
+
session_id=session.id,
|
|
500
|
+
state=next_state,
|
|
501
|
+
version=next_version,
|
|
502
|
+
updated_at=updated_at,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
row = await connection.fetchrow(
|
|
506
|
+
f"""
|
|
507
|
+
SELECT state_json, version
|
|
508
|
+
FROM {KSADK_PG_STATES_TABLE}
|
|
509
|
+
WHERE namespace = $1 AND scope = $2 AND agent_id = $3 AND user_id = $4 AND session_id = $5
|
|
510
|
+
FOR UPDATE
|
|
511
|
+
""",
|
|
512
|
+
self.namespace,
|
|
513
|
+
scope,
|
|
514
|
+
agent_id,
|
|
515
|
+
user_id or "",
|
|
516
|
+
session_id or "",
|
|
517
|
+
)
|
|
518
|
+
next_state = self._json_to_dict(row["state_json"]) if row else {}
|
|
519
|
+
next_state.update(state_delta)
|
|
520
|
+
next_version = (int(row["version"] or 0) + 1) if row else 1
|
|
521
|
+
await connection.execute(
|
|
522
|
+
f"""
|
|
523
|
+
INSERT INTO {KSADK_PG_STATES_TABLE} (
|
|
524
|
+
namespace, tenant_id, workspace_id, scope, agent_id, user_id, session_id, state_json, version, updated_at
|
|
525
|
+
)
|
|
526
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, $10)
|
|
527
|
+
ON CONFLICT (namespace, scope, agent_id, user_id, session_id)
|
|
528
|
+
DO UPDATE SET state_json = EXCLUDED.state_json,
|
|
529
|
+
version = EXCLUDED.version,
|
|
530
|
+
updated_at = EXCLUDED.updated_at
|
|
531
|
+
""",
|
|
532
|
+
self.namespace,
|
|
533
|
+
self.tenant_id,
|
|
534
|
+
self.workspace_id,
|
|
535
|
+
scope,
|
|
536
|
+
agent_id,
|
|
537
|
+
user_id or "",
|
|
538
|
+
session_id or "",
|
|
539
|
+
json.dumps(next_state),
|
|
540
|
+
next_version,
|
|
541
|
+
updated_at,
|
|
542
|
+
)
|
|
543
|
+
return SessionState(
|
|
544
|
+
scope=scope,
|
|
545
|
+
agent_id=agent_id,
|
|
546
|
+
user_id=user_id or "",
|
|
547
|
+
session_id=session_id or "",
|
|
548
|
+
state=next_state,
|
|
549
|
+
version=next_version,
|
|
550
|
+
updated_at=updated_at,
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
async def aclose(self) -> None:
|
|
554
|
+
pool = self._pool
|
|
555
|
+
self._pool = None
|
|
556
|
+
self._schema_ready = False
|
|
557
|
+
if pool is None:
|
|
558
|
+
return
|
|
559
|
+
try:
|
|
560
|
+
await asyncio.wait_for(pool.close(), timeout=self.connect_timeout)
|
|
561
|
+
except (TimeoutError, OSError, ConnectionError, asyncio.TimeoutError):
|
|
562
|
+
pool.terminate()
|
|
563
|
+
|
|
564
|
+
async def _ensure_pool(self) -> None:
|
|
565
|
+
if self._pool is not None:
|
|
566
|
+
return
|
|
567
|
+
async with self._pool_lock:
|
|
568
|
+
if self._pool is not None:
|
|
569
|
+
return
|
|
570
|
+
try:
|
|
571
|
+
import asyncpg
|
|
572
|
+
except ImportError as exc:
|
|
573
|
+
raise SessionBackendUnavailable(
|
|
574
|
+
"asyncpg is required for KSADK_SESSION_BACKEND=postgres"
|
|
575
|
+
) from exc
|
|
576
|
+
try:
|
|
577
|
+
self._pool = await asyncpg.create_pool(
|
|
578
|
+
dsn=self.dsn,
|
|
579
|
+
min_size=self.min_size,
|
|
580
|
+
max_size=self.max_size,
|
|
581
|
+
timeout=self.connect_timeout,
|
|
582
|
+
command_timeout=self.connect_timeout,
|
|
583
|
+
)
|
|
584
|
+
except (TimeoutError, OSError, ConnectionError, asyncio.TimeoutError) as exc:
|
|
585
|
+
raise SessionBackendUnavailable(
|
|
586
|
+
"Postgres session backend unavailable: "
|
|
587
|
+
f"could not connect to {mask_postgres_session_dsn(self.dsn)}"
|
|
588
|
+
) from exc
|
|
589
|
+
|
|
590
|
+
async def _ensure_schema(self) -> None:
|
|
591
|
+
if self._schema_ready:
|
|
592
|
+
return
|
|
593
|
+
async with self._schema_lock:
|
|
594
|
+
if self._schema_ready:
|
|
595
|
+
return
|
|
596
|
+
await self._ensure_pool()
|
|
597
|
+
async with self._pool.acquire() as connection:
|
|
598
|
+
await connection.execute(
|
|
599
|
+
f"""
|
|
600
|
+
CREATE TABLE IF NOT EXISTS {KSADK_PG_SESSIONS_TABLE} (
|
|
601
|
+
namespace TEXT NOT NULL,
|
|
602
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
603
|
+
workspace_id TEXT NOT NULL DEFAULT 'default',
|
|
604
|
+
id TEXT NOT NULL,
|
|
605
|
+
agent_id TEXT NOT NULL,
|
|
606
|
+
user_id TEXT NOT NULL,
|
|
607
|
+
title TEXT NOT NULL DEFAULT '',
|
|
608
|
+
title_source TEXT NOT NULL DEFAULT '',
|
|
609
|
+
summary TEXT NOT NULL DEFAULT '',
|
|
610
|
+
first_prompt TEXT NOT NULL DEFAULT '',
|
|
611
|
+
last_prompt TEXT NOT NULL DEFAULT '',
|
|
612
|
+
state_json JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
|
613
|
+
created_at DOUBLE PRECISION NOT NULL,
|
|
614
|
+
updated_at DOUBLE PRECISION NOT NULL,
|
|
615
|
+
version INTEGER NOT NULL DEFAULT 0,
|
|
616
|
+
PRIMARY KEY (namespace, id)
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
CREATE TABLE IF NOT EXISTS {KSADK_PG_EVENTS_TABLE} (
|
|
620
|
+
namespace TEXT NOT NULL,
|
|
621
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
622
|
+
workspace_id TEXT NOT NULL DEFAULT 'default',
|
|
623
|
+
id TEXT NOT NULL,
|
|
624
|
+
session_id TEXT NOT NULL,
|
|
625
|
+
author TEXT NOT NULL,
|
|
626
|
+
event_type TEXT NOT NULL,
|
|
627
|
+
content_json JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
|
628
|
+
timestamp DOUBLE PRECISION NOT NULL,
|
|
629
|
+
state_delta_json JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
|
630
|
+
seq_id INTEGER NOT NULL,
|
|
631
|
+
invocation_id TEXT,
|
|
632
|
+
metadata_json JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
|
633
|
+
PRIMARY KEY (namespace, id),
|
|
634
|
+
UNIQUE (namespace, session_id, seq_id),
|
|
635
|
+
FOREIGN KEY (namespace, session_id)
|
|
636
|
+
REFERENCES {KSADK_PG_SESSIONS_TABLE}(namespace, id)
|
|
637
|
+
ON DELETE CASCADE
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
CREATE INDEX IF NOT EXISTS idx_ksadk_pg_events_session_seq
|
|
641
|
+
ON {KSADK_PG_EVENTS_TABLE} (namespace, session_id, seq_id);
|
|
642
|
+
|
|
643
|
+
CREATE TABLE IF NOT EXISTS {KSADK_PG_STATES_TABLE} (
|
|
644
|
+
namespace TEXT NOT NULL,
|
|
645
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
646
|
+
workspace_id TEXT NOT NULL DEFAULT 'default',
|
|
647
|
+
scope TEXT NOT NULL,
|
|
648
|
+
agent_id TEXT NOT NULL,
|
|
649
|
+
user_id TEXT NOT NULL DEFAULT '',
|
|
650
|
+
session_id TEXT NOT NULL DEFAULT '',
|
|
651
|
+
state_json JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
|
652
|
+
version INTEGER NOT NULL DEFAULT 0,
|
|
653
|
+
updated_at DOUBLE PRECISION NOT NULL,
|
|
654
|
+
PRIMARY KEY (namespace, scope, agent_id, user_id, session_id)
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
ALTER TABLE {KSADK_PG_SESSIONS_TABLE}
|
|
658
|
+
ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'default';
|
|
659
|
+
ALTER TABLE {KSADK_PG_SESSIONS_TABLE}
|
|
660
|
+
ADD COLUMN IF NOT EXISTS workspace_id TEXT NOT NULL DEFAULT 'default';
|
|
661
|
+
ALTER TABLE {KSADK_PG_EVENTS_TABLE}
|
|
662
|
+
ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'default';
|
|
663
|
+
ALTER TABLE {KSADK_PG_EVENTS_TABLE}
|
|
664
|
+
ADD COLUMN IF NOT EXISTS workspace_id TEXT NOT NULL DEFAULT 'default';
|
|
665
|
+
ALTER TABLE {KSADK_PG_STATES_TABLE}
|
|
666
|
+
ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'default';
|
|
667
|
+
ALTER TABLE {KSADK_PG_STATES_TABLE}
|
|
668
|
+
ADD COLUMN IF NOT EXISTS workspace_id TEXT NOT NULL DEFAULT 'default';
|
|
669
|
+
"""
|
|
670
|
+
)
|
|
671
|
+
try:
|
|
672
|
+
await connection.execute(
|
|
673
|
+
f"""
|
|
674
|
+
CREATE OR REPLACE VIEW {PG_READABLE_EVENTS_VIEW} AS
|
|
675
|
+
SELECT
|
|
676
|
+
event_row.namespace,
|
|
677
|
+
event_row.tenant_id,
|
|
678
|
+
event_row.workspace_id,
|
|
679
|
+
session_row.agent_id,
|
|
680
|
+
session_row.user_id,
|
|
681
|
+
session_row.title AS session_title,
|
|
682
|
+
event_row.session_id,
|
|
683
|
+
event_row.seq_id,
|
|
684
|
+
event_row.id AS event_id,
|
|
685
|
+
event_row.invocation_id,
|
|
686
|
+
event_row.author,
|
|
687
|
+
event_row.event_type,
|
|
688
|
+
CASE
|
|
689
|
+
WHEN event_row.event_type = 'user_message' THEN 'user'
|
|
690
|
+
WHEN event_row.event_type IN (
|
|
691
|
+
'assistant_message', 'reasoning', 'tool_call'
|
|
692
|
+
) THEN 'assistant'
|
|
693
|
+
WHEN event_row.event_type = 'tool_result' THEN 'tool'
|
|
694
|
+
ELSE NULL
|
|
695
|
+
END AS message_role,
|
|
696
|
+
COALESCE(
|
|
697
|
+
NULLIF(event_row.content_json #>> '{{parts,0,text}}', ''),
|
|
698
|
+
NULLIF(event_row.content_json ->> 'text', ''),
|
|
699
|
+
NULLIF(event_row.metadata_json ->> 'reasoning', ''),
|
|
700
|
+
NULLIF(event_row.metadata_json ->> 'tool_output', '')
|
|
701
|
+
) AS message_text,
|
|
702
|
+
event_row.metadata_json ->> 'tool_name' AS tool_name,
|
|
703
|
+
CASE
|
|
704
|
+
WHEN event_row.event_type = 'run_status' THEN COALESCE(
|
|
705
|
+
event_row.content_json ->> 'status',
|
|
706
|
+
event_row.metadata_json ->> 'status'
|
|
707
|
+
)
|
|
708
|
+
ELSE NULL
|
|
709
|
+
END AS lifecycle_status,
|
|
710
|
+
to_timestamp(event_row.timestamp) AS created_at,
|
|
711
|
+
event_row.content_json,
|
|
712
|
+
event_row.state_delta_json,
|
|
713
|
+
event_row.metadata_json
|
|
714
|
+
FROM {KSADK_PG_EVENTS_TABLE} AS event_row
|
|
715
|
+
JOIN {KSADK_PG_SESSIONS_TABLE} AS session_row
|
|
716
|
+
ON session_row.namespace = event_row.namespace
|
|
717
|
+
AND session_row.id = event_row.session_id;
|
|
718
|
+
"""
|
|
719
|
+
)
|
|
720
|
+
except Exception as exc:
|
|
721
|
+
logger.warning("Postgres readable session view unavailable: %s", exc)
|
|
722
|
+
self._schema_ready = True
|
|
723
|
+
|
|
724
|
+
async def _get_session_with_connection(
|
|
725
|
+
self,
|
|
726
|
+
connection: Any,
|
|
727
|
+
session_id: str,
|
|
728
|
+
*,
|
|
729
|
+
for_update: bool = False,
|
|
730
|
+
) -> Optional[Session]:
|
|
731
|
+
lock_clause = " FOR UPDATE" if for_update else ""
|
|
732
|
+
row = await connection.fetchrow(
|
|
733
|
+
f"""
|
|
734
|
+
SELECT id, agent_id, user_id, title, title_source, summary, first_prompt, last_prompt,
|
|
735
|
+
state_json, created_at, updated_at, version
|
|
736
|
+
FROM {KSADK_PG_SESSIONS_TABLE}
|
|
737
|
+
WHERE namespace = $1 AND id = $2
|
|
738
|
+
{lock_clause}
|
|
739
|
+
""",
|
|
740
|
+
self.namespace,
|
|
741
|
+
session_id,
|
|
742
|
+
)
|
|
743
|
+
if row is None:
|
|
744
|
+
return None
|
|
745
|
+
events = [] if for_update else await self._get_events_with_connection(connection, session_id)
|
|
746
|
+
return self._session_from_row(row, events=events)
|
|
747
|
+
|
|
748
|
+
async def _get_events_with_connection(self, connection: Any, session_id: str) -> list[SessionEvent]:
|
|
749
|
+
rows = await connection.fetch(
|
|
750
|
+
f"""
|
|
751
|
+
SELECT id, session_id, author, event_type, content_json, timestamp,
|
|
752
|
+
state_delta_json, seq_id, invocation_id, metadata_json
|
|
753
|
+
FROM {KSADK_PG_EVENTS_TABLE}
|
|
754
|
+
WHERE namespace = $1 AND session_id = $2
|
|
755
|
+
ORDER BY seq_id ASC
|
|
756
|
+
""",
|
|
757
|
+
self.namespace,
|
|
758
|
+
session_id,
|
|
759
|
+
)
|
|
760
|
+
return [self._event_from_row(row) for row in rows]
|
|
761
|
+
|
|
762
|
+
async def _write_session_state(
|
|
763
|
+
self,
|
|
764
|
+
connection: Any,
|
|
765
|
+
*,
|
|
766
|
+
session_id: str,
|
|
767
|
+
agent_id: str,
|
|
768
|
+
user_id: str,
|
|
769
|
+
state: dict[str, Any],
|
|
770
|
+
version: int,
|
|
771
|
+
updated_at: float,
|
|
772
|
+
) -> None:
|
|
773
|
+
await connection.execute(
|
|
774
|
+
f"""
|
|
775
|
+
UPDATE {KSADK_PG_SESSIONS_TABLE}
|
|
776
|
+
SET state_json = $1::jsonb, updated_at = $2, version = $3
|
|
777
|
+
WHERE namespace = $4 AND id = $5
|
|
778
|
+
""",
|
|
779
|
+
json.dumps(state),
|
|
780
|
+
updated_at,
|
|
781
|
+
version,
|
|
782
|
+
self.namespace,
|
|
783
|
+
session_id,
|
|
784
|
+
)
|
|
785
|
+
await connection.execute(
|
|
786
|
+
f"""
|
|
787
|
+
INSERT INTO {KSADK_PG_STATES_TABLE} (
|
|
788
|
+
namespace, tenant_id, workspace_id, scope, agent_id, user_id, session_id, state_json, version, updated_at
|
|
789
|
+
)
|
|
790
|
+
VALUES ($1, $2, $3, 'session', $4, $5, $6, $7::jsonb, $8, $9)
|
|
791
|
+
ON CONFLICT (namespace, scope, agent_id, user_id, session_id)
|
|
792
|
+
DO UPDATE SET state_json = EXCLUDED.state_json,
|
|
793
|
+
version = EXCLUDED.version,
|
|
794
|
+
updated_at = EXCLUDED.updated_at
|
|
795
|
+
""",
|
|
796
|
+
self.namespace,
|
|
797
|
+
self.tenant_id,
|
|
798
|
+
self.workspace_id,
|
|
799
|
+
agent_id,
|
|
800
|
+
user_id,
|
|
801
|
+
session_id,
|
|
802
|
+
json.dumps(state),
|
|
803
|
+
version,
|
|
804
|
+
updated_at,
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
@classmethod
|
|
808
|
+
def _session_from_row(cls, row: Any, *, events: list[SessionEvent]) -> Session:
|
|
809
|
+
return Session(
|
|
810
|
+
id=row["id"],
|
|
811
|
+
agent_id=row["agent_id"],
|
|
812
|
+
user_id=row["user_id"],
|
|
813
|
+
title=row["title"],
|
|
814
|
+
title_source=row["title_source"],
|
|
815
|
+
summary=row["summary"],
|
|
816
|
+
first_prompt=row["first_prompt"],
|
|
817
|
+
last_prompt=row["last_prompt"],
|
|
818
|
+
state=cls._json_to_dict(row["state_json"]),
|
|
819
|
+
events=events,
|
|
820
|
+
created_at=float(row["created_at"]),
|
|
821
|
+
updated_at=float(row["updated_at"]),
|
|
822
|
+
version=int(row["version"] or 0),
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
@classmethod
|
|
826
|
+
def _event_from_row(cls, row: Any) -> SessionEvent:
|
|
827
|
+
return SessionEvent(
|
|
828
|
+
id=row["id"],
|
|
829
|
+
session_id=row["session_id"],
|
|
830
|
+
author=row["author"],
|
|
831
|
+
event_type=row["event_type"],
|
|
832
|
+
content=cls._json_to_dict(row["content_json"]),
|
|
833
|
+
timestamp=float(row["timestamp"]),
|
|
834
|
+
state_delta=cls._json_to_dict(row["state_delta_json"]),
|
|
835
|
+
seq_id=int(row["seq_id"] or 0),
|
|
836
|
+
invocation_id=row["invocation_id"],
|
|
837
|
+
metadata=cls._json_to_dict(row["metadata_json"]),
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
@classmethod
|
|
841
|
+
def _state_from_row(cls, row: Any) -> SessionState:
|
|
842
|
+
return SessionState(
|
|
843
|
+
scope=row["scope"],
|
|
844
|
+
agent_id=row["agent_id"],
|
|
845
|
+
user_id=row["user_id"],
|
|
846
|
+
session_id=row["session_id"],
|
|
847
|
+
state=cls._json_to_dict(row["state_json"]),
|
|
848
|
+
version=int(row["version"] or 0),
|
|
849
|
+
updated_at=float(row["updated_at"]),
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
@staticmethod
|
|
853
|
+
def _json_to_dict(value: Any) -> dict[str, Any]:
|
|
854
|
+
if value is None:
|
|
855
|
+
return {}
|
|
856
|
+
if isinstance(value, dict):
|
|
857
|
+
return dict(value)
|
|
858
|
+
if isinstance(value, str):
|
|
859
|
+
return dict(json.loads(value or "{}"))
|
|
860
|
+
return dict(value)
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
def create_postgres_session_service(
|
|
864
|
+
*,
|
|
865
|
+
dsn: str,
|
|
866
|
+
namespace: str = "default",
|
|
867
|
+
tenant_id: str = "default",
|
|
868
|
+
workspace_id: str = "default",
|
|
869
|
+
) -> PostgresSessionService:
|
|
870
|
+
return PostgresSessionService(
|
|
871
|
+
dsn=dsn,
|
|
872
|
+
namespace=namespace,
|
|
873
|
+
tenant_id=tenant_id,
|
|
874
|
+
workspace_id=workspace_id,
|
|
875
|
+
)
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
def mask_postgres_session_dsn(dsn: str) -> str:
|
|
879
|
+
if not dsn:
|
|
880
|
+
return ""
|
|
881
|
+
try:
|
|
882
|
+
parts = urlsplit(dsn)
|
|
883
|
+
except ValueError:
|
|
884
|
+
return "***"
|
|
885
|
+
if not parts.password:
|
|
886
|
+
return dsn
|
|
887
|
+
username = parts.username or ""
|
|
888
|
+
host = parts.hostname or ""
|
|
889
|
+
port = f":{parts.port}" if parts.port else ""
|
|
890
|
+
auth = f"{username}:***@" if username else "***@"
|
|
891
|
+
return urlunsplit((parts.scheme, f"{auth}{host}{port}", parts.path, parts.query, parts.fragment))
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
__all__ = [
|
|
895
|
+
"KSADK_PG_EVENTS_TABLE",
|
|
896
|
+
"KSADK_PG_SESSIONS_TABLE",
|
|
897
|
+
"KSADK_PG_STATES_TABLE",
|
|
898
|
+
"PostgresSessionService",
|
|
899
|
+
"create_postgres_session_service",
|
|
900
|
+
"mask_postgres_session_dsn",
|
|
901
|
+
]
|