agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +22 -2
  2. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
  3. ksadk/a2a/_space_client_events.py +381 -0
  4. ksadk/a2a/event_adapter.py +156 -64
  5. ksadk/a2a/executor.py +179 -63
  6. ksadk/a2a/space_client.py +6 -333
  7. ksadk/a2ui/core.py +222 -53
  8. ksadk/agui/_agent_helpers.py +243 -0
  9. ksadk/agui/a2ui_projection.py +12 -5
  10. ksadk/agui/agent.py +443 -259
  11. ksadk/api/client.py +525 -27
  12. ksadk/builders/code_builder.py +284 -8
  13. ksadk/builders/container_builder.py +8 -1
  14. ksadk/builders/managed_runtime_builder.py +39 -35
  15. ksadk/cli/__init__.py +17 -0
  16. ksadk/cli/cmd_dashboard.py +15 -1
  17. ksadk/cli/cmd_deploy.py +10 -2
  18. ksadk/cli/cmd_eval.py +66 -9
  19. ksadk/cli/cmd_evalset.py +284 -0
  20. ksadk/cli/cmd_files.py +5 -1
  21. ksadk/cli/cmd_hermes.py +1 -126
  22. ksadk/cli/cmd_invoke.py +28 -38
  23. ksadk/cli/cmd_managed_runtime.py +95 -0
  24. ksadk/cli/cmd_observe.py +85 -0
  25. ksadk/cli/cmd_openclaw.py +1 -435
  26. ksadk/cli/cmd_replay.py +28 -4
  27. ksadk/cli/cmd_studio.py +99 -8
  28. ksadk/cli/cmd_web.py +10 -2
  29. ksadk/cli/hermes_env.py +151 -0
  30. ksadk/cli/invoke_payload.py +49 -0
  31. ksadk/cli/openclaw_env.py +458 -0
  32. ksadk/codex/client.py +180 -30
  33. ksadk/codex/runtime.py +428 -326
  34. ksadk/configs/env_registry.py +66 -12
  35. ksadk/configs/env_registry_pcm.py +181 -0
  36. ksadk/configs/env_var_spec.py +18 -0
  37. ksadk/configs/global_config.py +5 -0
  38. ksadk/context_engine/__init__.py +98 -0
  39. ksadk/context_engine/assembler.py +176 -0
  40. ksadk/context_engine/baseline.py +419 -0
  41. ksadk/context_engine/cache_observability.py +228 -0
  42. ksadk/context_engine/capabilities.py +443 -0
  43. ksadk/context_engine/contributors.py +344 -0
  44. ksadk/context_engine/hosted_pipeline.py +417 -0
  45. ksadk/context_engine/models.py +117 -0
  46. ksadk/context_engine/planner.py +698 -0
  47. ksadk/context_engine/policies.py +319 -0
  48. ksadk/context_engine/projection.py +31 -0
  49. ksadk/context_engine/shadow_plan.py +266 -0
  50. ksadk/context_engine/tokenizer.py +164 -0
  51. ksadk/conversations/context.py +126 -6
  52. ksadk/conversations/message_projection.py +292 -28
  53. ksadk/conversations/model_context.py +49 -3
  54. ksadk/conversations/runtime_compaction.py +207 -5
  55. ksadk/conversations/runtime_input.py +184 -2
  56. ksadk/conversations/runtime_invocation.py +81 -3
  57. ksadk/conversations/runtime_observability.py +141 -0
  58. ksadk/conversations/runtime_payloads.py +42 -0
  59. ksadk/conversations/runtime_preparation.py +272 -2
  60. ksadk/conversations/runtime_resume.py +20 -1
  61. ksadk/conversations/runtime_stream_events.py +94 -9
  62. ksadk/conversations/semantic_summary.py +311 -1
  63. ksadk/conversations/session_lock.py +51 -0
  64. ksadk/deployment/env_forward.py +72 -0
  65. ksadk/deployment/managed_runtime.py +5 -0
  66. ksadk/deployment/providers/serverless.py +143 -69
  67. ksadk/evaluation/__init__.py +32 -0
  68. ksadk/evaluation/a2a_adapter.py +9 -1
  69. ksadk/evaluation/adapters.py +18 -5
  70. ksadk/evaluation/agent_eval_client.py +395 -0
  71. ksadk/evaluation/cloud_binding.py +80 -0
  72. ksadk/evaluation/cloud_converter.py +194 -0
  73. ksadk/evaluation/cloud_service.py +199 -0
  74. ksadk/evaluation/contracts.py +73 -4
  75. ksadk/evaluation/evaluators.py +183 -17
  76. ksadk/evaluation/evidence.py +258 -0
  77. ksadk/evaluation/executor.py +31 -6
  78. ksadk/evaluation/local_adapter.py +550 -0
  79. ksadk/evaluation/service_env.py +27 -0
  80. ksadk/evaluation/studio_build_adapter.py +253 -0
  81. ksadk/evaluation/target.py +15 -6
  82. ksadk/events/__init__.py +13 -13
  83. ksadk/events/_v1_compat/__init__.py +1 -0
  84. ksadk/events/_v1_compat/models.py +299 -0
  85. ksadk/events/_v1_compat/parser.py +247 -0
  86. ksadk/events/_v1_compat/projection.py +753 -0
  87. ksadk/events/adapters/__init__.py +34 -0
  88. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  89. ksadk/events/adapters/_a2a_support.py +275 -0
  90. ksadk/events/adapters/_codex_interactions.py +375 -0
  91. ksadk/events/adapters/_codex_items.py +801 -0
  92. ksadk/events/adapters/_codex_validators.py +311 -0
  93. ksadk/events/adapters/_langgraph_support.py +785 -0
  94. ksadk/events/adapters/a2a.py +841 -0
  95. ksadk/events/adapters/adk.py +503 -0
  96. ksadk/events/adapters/codex.py +717 -0
  97. ksadk/events/adapters/langgraph.py +745 -0
  98. ksadk/events/canonical.py +440 -0
  99. ksadk/events/canonical_replay.py +497 -0
  100. ksadk/events/canonical_store.py +468 -0
  101. ksadk/events/cold_recovery.py +257 -0
  102. ksadk/events/content.py +89 -0
  103. ksadk/events/identity.py +86 -0
  104. ksadk/events/pipeline.py +455 -0
  105. ksadk/events/projections.py +66 -0
  106. ksadk/events/reducer.py +661 -0
  107. ksadk/events/replay.py +3 -36
  108. ksadk/events/runtime_event.py +20 -265
  109. ksadk/events/session_event.py +324 -0
  110. ksadk/events/store.py +3 -316
  111. ksadk/events/v1_compat.py +41 -0
  112. ksadk/harness/runtime.py +160 -57
  113. ksadk/identity/resolver.py +4 -3
  114. ksadk/interaction/__init__.py +28 -0
  115. ksadk/interaction/contracts.py +136 -0
  116. ksadk/interaction/ledger.py +189 -0
  117. ksadk/interaction/provider.py +122 -0
  118. ksadk/interaction/providers/__init__.py +52 -0
  119. ksadk/interaction/providers/adk.py +40 -0
  120. ksadk/interaction/providers/codex.py +94 -0
  121. ksadk/interaction/providers/langgraph.py +58 -0
  122. ksadk/kernel/__init__.py +117 -0
  123. ksadk/kernel/authorization.py +258 -0
  124. ksadk/kernel/bootstrap.py +1074 -0
  125. ksadk/kernel/contract_fingerprints.py +61 -0
  126. ksadk/kernel/contracts.py +362 -0
  127. ksadk/kernel/control.py +219 -0
  128. ksadk/kernel/errors.py +79 -0
  129. ksadk/kernel/ingress.py +1102 -0
  130. ksadk/kernel/mapping.py +111 -0
  131. ksadk/kernel/memory_store.py +1133 -0
  132. ksadk/kernel/postgres_store.py +1756 -0
  133. ksadk/kernel/recovery.py +452 -0
  134. ksadk/kernel/runtime_identity.py +103 -0
  135. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  136. ksadk/kernel/sqlite_store.py +1410 -0
  137. ksadk/kernel/state.py +145 -0
  138. ksadk/kernel/store.py +232 -0
  139. ksadk/kernel/worker.py +953 -0
  140. ksadk/knowledge_base/client.py +7 -0
  141. ksadk/knowledge_base/service.py +32 -5
  142. ksadk/memory/__init__.py +61 -0
  143. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  144. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  145. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  146. ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
  147. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  148. ksadk/memory/coordinator.py +461 -0
  149. ksadk/memory/events.py +202 -0
  150. ksadk/memory/extraction.py +228 -0
  151. ksadk/memory/ltm_backend_factory.py +16 -3
  152. ksadk/memory/models.py +316 -0
  153. ksadk/memory/policy.py +215 -0
  154. ksadk/memory/provider.py +47 -0
  155. ksadk/memory/provider_adapter.py +159 -0
  156. ksadk/memory/provider_resolver.py +71 -0
  157. ksadk/memory/providers/__init__.py +5 -0
  158. ksadk/memory/providers/local_sqlite.py +490 -0
  159. ksadk/memory/resolved_policy.py +145 -0
  160. ksadk/memory/service.py +188 -6
  161. ksadk/model_proxy/bootstrap.py +16 -9
  162. ksadk/model_proxy/detect.py +202 -9
  163. ksadk/model_proxy/namespace.py +39 -15
  164. ksadk/model_proxy/server.py +24 -5
  165. ksadk/model_proxy/transform.py +60 -2
  166. ksadk/observability/__init__.py +25 -0
  167. ksadk/observability/session_log.py +420 -0
  168. ksadk/observability/trajectory.py +105 -0
  169. ksadk/prompts/__init__.py +71 -0
  170. ksadk/prompts/compiler.py +215 -0
  171. ksadk/prompts/models.py +82 -0
  172. ksadk/prompts/projection.py +89 -0
  173. ksadk/prompts/resolved.py +137 -0
  174. ksadk/prompts/sources.py +233 -0
  175. ksadk/runners/_langgraph_runner_streams.py +822 -0
  176. ksadk/runners/adk_runner.py +5 -5
  177. ksadk/runners/base_runner.py +16 -0
  178. ksadk/runners/factory.py +1 -2
  179. ksadk/runners/langgraph_runner.py +198 -437
  180. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  181. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  182. ksadk/runtime/adapter.py +118 -2
  183. ksadk/runtime/conversation_execution.py +316 -182
  184. ksadk/runtime/executor.py +144 -3
  185. ksadk/runtime/factory.py +193 -7
  186. ksadk/runtime/hosted_finalizer.py +268 -0
  187. ksadk/runtime/launch.py +14 -1
  188. ksadk/runtime/preprocessing.py +25 -3
  189. ksadk/runtime/runner_adapter.py +288 -508
  190. ksadk/runtime/usage.py +33 -0
  191. ksadk/sandbox/backends/e2b.py +35 -2
  192. ksadk/server/composition.py +8 -0
  193. ksadk/server/factory.py +69 -6
  194. ksadk/server/routes/kernel_ingress.py +258 -0
  195. ksadk/server/routes/openai_compat.py +88 -8
  196. ksadk/server/routes/projection.py +35 -20
  197. ksadk/server/routes/run.py +94 -0
  198. ksadk/server/routes/sessions.py +2 -0
  199. ksadk/server/routes/streaming.py +5 -2
  200. ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  201. ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
  202. ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  203. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  204. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  205. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  206. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  207. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  208. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  209. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  210. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  211. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  212. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  213. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  214. ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  215. ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  216. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  217. ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  218. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  219. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  220. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  221. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  222. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  223. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  224. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  225. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  226. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  227. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  228. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  229. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  230. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  231. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  232. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  233. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  234. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  235. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  236. ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
  237. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  238. ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
  239. ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
  240. ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
  241. ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
  242. ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
  243. ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
  244. ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
  245. ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
  246. ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
  247. ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
  248. ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
  249. ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
  250. ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
  251. ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
  252. ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
  253. ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
  254. ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
  255. ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
  256. ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
  257. ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
  258. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  259. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  260. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  261. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  262. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  263. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  264. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  265. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  266. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  267. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  268. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  269. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  270. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  271. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  272. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  273. ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
  274. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  275. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  276. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  277. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  278. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  279. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  280. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  281. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  282. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  283. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  284. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  285. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  286. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  287. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  288. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  289. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  290. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  291. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  292. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  293. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  294. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  295. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  296. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  297. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  298. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  299. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  300. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  301. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  302. ksadk/server/static/index.html +2 -2
  303. ksadk/sessions/__init__.py +26 -9
  304. ksadk/sessions/_local_service_sync.py +688 -0
  305. ksadk/sessions/_local_tables.py +21 -0
  306. ksadk/sessions/_postgres_schema.py +305 -0
  307. ksadk/sessions/_postgres_tables.py +15 -0
  308. ksadk/sessions/base.py +80 -1
  309. ksadk/sessions/in_memory.py +53 -2
  310. ksadk/sessions/local_service.py +63 -604
  311. ksadk/sessions/postgres_service.py +58 -148
  312. ksadk/sessions/resilient.py +35 -1
  313. ksadk/studio/api.py +577 -25
  314. ksadk/studio/api_contracts.py +114 -5
  315. ksadk/studio/api_memory_routes.py +82 -0
  316. ksadk/studio/authoring.py +252 -27
  317. ksadk/studio/authoring_coordinator.py +799 -67
  318. ksadk/studio/builder.py +88 -11
  319. ksadk/studio/capabilities.py +17 -0
  320. ksadk/studio/cloud.py +1916 -95
  321. ksadk/studio/codex_agent_service.py +125 -14
  322. ksadk/studio/codex_authoring.py +469 -0
  323. ksadk/studio/codex_builder.py +138 -25
  324. ksadk/studio/codex_manifest.py +51 -22
  325. ksadk/studio/codex_run.py +46 -3
  326. ksadk/studio/compiler.py +1 -0
  327. ksadk/studio/contracts.py +114 -3
  328. ksadk/studio/event_store.py +25 -95
  329. ksadk/studio/framework_run.py +231 -3
  330. ksadk/studio/hosted_kernel.py +317 -0
  331. ksadk/studio/manifest_resolver.py +131 -0
  332. ksadk/studio/model_client.py +95 -9
  333. ksadk/studio/model_profile_service.py +1 -1
  334. ksadk/studio/operations.py +37 -5
  335. ksadk/studio/otel_trace.py +348 -42
  336. ksadk/studio/pcm_memory.py +85 -0
  337. ksadk/studio/repository.py +23 -2
  338. ksadk/studio/resource_catalog.py +43 -0
  339. ksadk/studio/run_service.py +563 -137
  340. ksadk/studio/runtime_source.py +42 -16
  341. ksadk/studio/service.py +1180 -72
  342. ksadk/studio/shared_web.py +32 -22
  343. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  344. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  345. ksadk/studio/static/index.html +2 -2
  346. ksadk/studio/templates.py +100 -8
  347. ksadk/studio/validator.py +19 -0
  348. ksadk/studio/workspace.py +30 -32
  349. ksadk/tracing/setup.py +304 -3
  350. ksadk/version.py +1 -1
  351. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  352. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  353. ksadk/events/parser.py +0 -191
  354. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  355. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
  356. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
  357. ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
  358. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
  359. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
  360. ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
  361. ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
  362. ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
  363. ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
  364. ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
  365. ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
  366. ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
  367. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
  368. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
  369. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
  370. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  371. ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
  372. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  373. ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
  374. ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
  375. ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
  376. ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
  377. ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
  378. ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
  379. ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
  380. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
  381. ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
  382. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
  383. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
  384. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
  385. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  386. ksadk/server/static/assets/index-R52pJJvg.js +0 -177
  387. ksadk/server/static/assets/index-jJr3_955.css +0 -2
  388. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
  389. ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
  390. ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
  391. ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
  392. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
  393. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
  394. ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
  395. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
  396. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
  397. ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
  398. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
  399. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
  400. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
  401. ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
  402. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
  403. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
  404. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
  405. ksadk/studio/evaluation.py +0 -192
  406. ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
  407. ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
  408. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +0 -0
  409. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  410. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  411. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -21,18 +21,41 @@
21
21
 
22
22
  import json
23
23
  import logging
24
+ import re
24
25
  import time
25
26
  import uuid
26
- from typing import Any
27
+ from typing import Any, List, Set
27
28
 
28
29
  from pydantic import ConfigDict, Field
29
30
 
30
- from ksadk.memory.adk.backends.base_ltm_backend import BaseLongTermMemoryBackend
31
+ from ksadk.memory.adk.backends.base_ltm_backend import (
32
+ CAP_ADD,
33
+ CAP_DELETE,
34
+ CAP_FLUSH,
35
+ CAP_SEARCH,
36
+ CAP_SESSION_STATUS,
37
+ CAP_STRUCTURED_SEARCH,
38
+ CAP_UPDATE,
39
+ BaseLongTermMemoryBackend,
40
+ )
41
+ from ksadk.memory.models import (
42
+ LongTermMemoryRecord,
43
+ MemoryExtractionStatus,
44
+ MemoryMutationResult,
45
+ map_session_state,
46
+ )
31
47
 
32
48
  logger = logging.getLogger(__name__)
33
49
 
34
50
  DEFAULT_SCENE_ID = "_sys_general"
35
51
 
52
+ # "记忆不存在"识别模式(方案 §17.4:准确错误码待真实 fixture 固化,
53
+ # 首版按保守中英文模式匹配,fixture 到位后收敛为精确匹配)。
54
+ _NOT_EXIST_RE = re.compile(
55
+ r"not[ _]?exist|does not exist|memory.*不存在|记忆不存在|记忆已被删除|resourcenotfound|notfound",
56
+ re.IGNORECASE,
57
+ )
58
+
36
59
 
37
60
  class SdkLTMBackend(BaseLongTermMemoryBackend):
38
61
  """金山云 AICP 记忆库 SDK 后端
@@ -329,6 +352,365 @@ class SdkLTMBackend(BaseLongTermMemoryBackend):
329
352
  logger.error(f"QueryMemorySdk failed: {e}")
330
353
  return []
331
354
 
355
+ # ------------------------------------------------------------------
356
+ # 结构化扩展协议(方案 §7.3/§7.4):走通用 client.call 通道。
357
+ # kingsoftcloud-sdk-python 1.5.8.101 的 AICP client 未提供
358
+ # ListMemories/UpdateMemory/DeleteMemory 类型化方法(§7.4.2)。
359
+ # 所有解析对已确认 schema 严格校验;未知结构 fail closed,不伪造结果。
360
+ # ------------------------------------------------------------------
361
+
362
+ def search_records(
363
+ self, user_id: str, query: str, top_k: int = 5, **kwargs
364
+ ) -> List[LongTermMemoryRecord]:
365
+ """QueryMemorySdk 结构化检索:返回带服务端 MemoryId 的记录。
366
+
367
+ 按已确认 schema(§7.4.1)从 ``Data[].Memories[]`` 解析
368
+ MemoryId / Memory / Score / OccurredStart / OccurredEnd。
369
+ 缺少 MemoryId 的条目跳过(无法支撑后续 mutation)。
370
+ 未知响应结构返回空列表(fail closed)。
371
+ """
372
+ client = self._get_client()
373
+ memory_collection_id = self._effective_memory_collection_id()
374
+ params = {
375
+ "MemoryCollectionId": memory_collection_id,
376
+ "AgentUserId": user_id,
377
+ "Query": query,
378
+ "Limit": top_k,
379
+ "SceneId": self._effective_scene_id(),
380
+ }
381
+ response = client.call("QueryMemorySdk", params, options={"IsPostJson": True})
382
+ records = self._parse_query_records_response(response, user_id=user_id)
383
+ logger.info(
384
+ f"QueryMemorySdk structured: user={user_id}, records={len(records)}"
385
+ )
386
+ return records
387
+
388
+ def list_memory_records(
389
+ self,
390
+ *,
391
+ user_id: str,
392
+ query: str = "",
393
+ page: int = 1,
394
+ page_size: int = 20,
395
+ ) -> List[LongTermMemoryRecord]:
396
+ """ListMemories:精确确认提取完成后的可见性 / 取得当前 MemoryId。
397
+
398
+ 按已确认 schema(§7.4.1)从顶层 ``MemoryList[]`` 解析。
399
+ 未知响应结构返回空列表(fail closed,不宣称可见)。
400
+ """
401
+ client = self._get_client()
402
+ params: dict[str, Any] = {
403
+ "MemoryCollectionId": self._effective_memory_collection_id(),
404
+ "AgentUserId": user_id,
405
+ "Page": page,
406
+ "PageSize": page_size,
407
+ }
408
+ if query:
409
+ params["Query"] = query
410
+ response = client.call("ListMemories", params, options={"IsPostJson": True})
411
+ records = self._parse_list_memories_response(response, user_id=user_id)
412
+ logger.info(f"ListMemories: user={user_id}, records={len(records)}")
413
+ return records
414
+
415
+ def update_memory(
416
+ self,
417
+ *,
418
+ user_id: str,
419
+ memory_id: str,
420
+ content: str,
421
+ **kwargs,
422
+ ) -> MemoryMutationResult:
423
+ """UpdateMemory:按 ID 原地更新正文,解析 new_memory_id。
424
+
425
+ Update 不幂等:重复调用会重复触发(§17.7),由上层控制重试。
426
+ "记忆不存在"按 not_found 归一(§7.4.1,错误码待 fixture 收敛)。
427
+ """
428
+ client = self._get_client()
429
+ params = {
430
+ "MemoryCollectionId": self._effective_memory_collection_id(),
431
+ "MemoryId": memory_id,
432
+ "Content": content,
433
+ "AgentUserId": user_id,
434
+ }
435
+ try:
436
+ response = client.call("UpdateMemory", params, options={"IsPostJson": True})
437
+ except Exception as exc:
438
+ if self._is_not_exist_error(exc):
439
+ return MemoryMutationResult(
440
+ ok=False,
441
+ memory_id=memory_id,
442
+ status="not_found",
443
+ message="目标记忆不存在",
444
+ )
445
+ self.last_error = str(exc)
446
+ logger.error("UpdateMemory failed: %s", type(exc).__name__)
447
+ return MemoryMutationResult(
448
+ ok=False,
449
+ memory_id=memory_id,
450
+ status="failed",
451
+ message="记忆更新失败",
452
+ )
453
+
454
+ data = self._parse_json_response(response)
455
+ response_memory_id = self._parse_new_memory_id(data)
456
+ new_memory_id = response_memory_id
457
+ if not response_memory_id or response_memory_id == memory_id:
458
+ # The service can merge an edited memory into another record while
459
+ # returning no new ID (or echoing the old one). Do not expose that
460
+ # stale handle to callers: confirm the current handle by listing
461
+ # records whose final content exactly matches the update.
462
+ try:
463
+ matches = [
464
+ record
465
+ for record in self.list_memory_records(
466
+ user_id=user_id,
467
+ query=content,
468
+ page=1,
469
+ page_size=100,
470
+ )
471
+ if record.content.strip() == content.strip()
472
+ ]
473
+ except Exception as exc:
474
+ logger.warning(
475
+ "ListMemories failed while reconciling updated memory ID: %s",
476
+ type(exc).__name__,
477
+ )
478
+ matches = []
479
+ unique_ids = {record.memory_id for record in matches}
480
+ new_memory_id = unique_ids.pop() if len(unique_ids) == 1 else ""
481
+
482
+ if new_memory_id and new_memory_id != memory_id:
483
+ message = f"更新成功,新记忆 ID: {new_memory_id}"
484
+ elif new_memory_id == memory_id:
485
+ message = "更新成功,记忆 ID 未变化"
486
+ else:
487
+ message = "更新成功,但未能唯一确认更新后的记忆 ID,请重新搜索后再操作"
488
+ return MemoryMutationResult(
489
+ ok=True,
490
+ memory_id=memory_id,
491
+ new_memory_id=new_memory_id,
492
+ status="updated",
493
+ message=message,
494
+ )
495
+
496
+ def delete_memory(
497
+ self,
498
+ *,
499
+ user_id: str,
500
+ memory_id: str,
501
+ **kwargs,
502
+ ) -> MemoryMutationResult:
503
+ """DeleteMemory:按 ID 软删除。
504
+
505
+ 重复删除返回"记忆不存在",归一为 already_absent,
506
+ 与首次成功 deleted 分开审计(§7.4)。
507
+ """
508
+ client = self._get_client()
509
+ params = {
510
+ "MemoryCollectionId": self._effective_memory_collection_id(),
511
+ "MemoryId": memory_id,
512
+ "AgentUserId": user_id,
513
+ }
514
+ try:
515
+ client.call("DeleteMemory", params, options={"IsPostJson": True})
516
+ except Exception as exc:
517
+ if self._is_not_exist_error(exc):
518
+ return MemoryMutationResult(
519
+ ok=True,
520
+ memory_id=memory_id,
521
+ status="already_absent",
522
+ message="目标记忆已不存在(可能已删除)",
523
+ )
524
+ self.last_error = str(exc)
525
+ logger.error("DeleteMemory failed: %s", type(exc).__name__)
526
+ return MemoryMutationResult(
527
+ ok=False,
528
+ memory_id=memory_id,
529
+ status="failed",
530
+ message="记忆删除失败",
531
+ )
532
+ return MemoryMutationResult(
533
+ ok=True,
534
+ memory_id=memory_id,
535
+ status="deleted",
536
+ message="已删除",
537
+ )
538
+
539
+ def get_extraction_status(
540
+ self,
541
+ *,
542
+ user_id: str,
543
+ session_id: str,
544
+ ) -> MemoryExtractionStatus:
545
+ """ListSessions 查询 Session 后台提取状态(§7.7)。
546
+
547
+ State 映射 0/50/100/-50/-100;未找到 Session 返回 unknown。
548
+ searchable 需 Service 层结合 ListMemories 确认后置位。
549
+ """
550
+ item = self.get_session_status(user_id=user_id, session_id=session_id)
551
+ if not isinstance(item, dict):
552
+ return MemoryExtractionStatus(
553
+ session_id=session_id,
554
+ state=None,
555
+ status="unknown",
556
+ message="Session 状态未知",
557
+ )
558
+ state = item.get("State")
559
+ state_int = int(state) if isinstance(state, (int, float, str)) and str(state).lstrip("-").isdigit() else None
560
+ status = map_session_state(state_int)
561
+ message = {
562
+ "queued": "排队中",
563
+ "extracting": "提取中",
564
+ "extracted": "提取完成",
565
+ "duplicate_skipped": "内容重复,已跳过提取",
566
+ "failed": "提取失败,可稍后重新明确保存",
567
+ }.get(status, "状态未知")
568
+ return MemoryExtractionStatus(
569
+ session_id=session_id,
570
+ state=state_int,
571
+ status=status,
572
+ message=message,
573
+ )
574
+
575
+ def capabilities(self) -> Set[str]:
576
+ return {
577
+ CAP_SEARCH,
578
+ CAP_ADD,
579
+ CAP_FLUSH,
580
+ CAP_STRUCTURED_SEARCH,
581
+ CAP_UPDATE,
582
+ CAP_DELETE,
583
+ CAP_SESSION_STATUS,
584
+ }
585
+
586
+ @staticmethod
587
+ def _is_not_exist_error(exc: Exception) -> bool:
588
+ """识别"记忆不存在"类错误。
589
+
590
+ §17.4:准确错误码/结构待真实 fixture 固化;首版按保守模式匹配
591
+ code/message,fixture 到位后收敛为精确匹配。
592
+ """
593
+ text = " ".join(
594
+ str(part) for part in (getattr(exc, "code", ""), str(exc)) if part
595
+ )
596
+ return bool(_NOT_EXIST_RE.search(text))
597
+
598
+ @staticmethod
599
+ def _parse_new_memory_id(data: Any) -> str:
600
+ """从 UpdateMemory 响应解析 new_memory_id(§7.4.1)。
601
+
602
+ 兼容 snake/camel 两种命名;都不存在时返回空串,由调用方通过
603
+ ListMemories 核验当前句柄,不能据此推断 ID 未变化。
604
+ """
605
+ if not isinstance(data, dict):
606
+ return ""
607
+ for key in ("new_memory_id", "NewMemoryId", "NewMemoryID"):
608
+ value = data.get(key)
609
+ if isinstance(value, str) and value.strip():
610
+ return value.strip()
611
+ nested = data.get("Data")
612
+ if isinstance(nested, dict):
613
+ for key in ("new_memory_id", "NewMemoryId", "NewMemoryID"):
614
+ value = nested.get(key)
615
+ if isinstance(value, str) and value.strip():
616
+ return value.strip()
617
+ return ""
618
+
619
+ def _parse_query_records_response(
620
+ self, response: Any, *, user_id: str
621
+ ) -> List[LongTermMemoryRecord]:
622
+ """严格解析 QueryMemorySdk 结构化响应:Data[].Memories[]。"""
623
+ try:
624
+ data = self._parse_json_response(response)
625
+ except (json.JSONDecodeError, TypeError):
626
+ logger.error("QueryMemorySdk records: invalid JSON response")
627
+ return []
628
+ if not isinstance(data, dict):
629
+ logger.error("QueryMemorySdk records: unexpected payload type")
630
+ return []
631
+ items = data.get("Data")
632
+ if not isinstance(items, list):
633
+ logger.warning(
634
+ "QueryMemorySdk records: unknown schema, keys=%s; fail closed",
635
+ list(data.keys()),
636
+ )
637
+ return []
638
+ records: List[LongTermMemoryRecord] = []
639
+ for item in items:
640
+ memories = item.get("Memories") if isinstance(item, dict) else None
641
+ if not isinstance(memories, list):
642
+ continue
643
+ for memory in memories:
644
+ record = self._record_from_item(memory, user_id=user_id)
645
+ if record is not None:
646
+ records.append(record)
647
+ return records
648
+
649
+ def _parse_list_memories_response(
650
+ self, response: Any, *, user_id: str
651
+ ) -> List[LongTermMemoryRecord]:
652
+ """严格解析 ListMemories 响应:顶层 MemoryList[](§7.4.1)。"""
653
+ try:
654
+ data = self._parse_json_response(response)
655
+ except (json.JSONDecodeError, TypeError):
656
+ logger.error("ListMemories: invalid JSON response")
657
+ return []
658
+ if not isinstance(data, dict):
659
+ logger.error("ListMemories: unexpected payload type")
660
+ return []
661
+ items = data.get("MemoryList")
662
+ if not isinstance(items, list):
663
+ logger.warning(
664
+ "ListMemories: unknown schema, keys=%s; fail closed",
665
+ list(data.keys()),
666
+ )
667
+ return []
668
+ records: List[LongTermMemoryRecord] = []
669
+ for item in items:
670
+ record = self._record_from_item(item, user_id=user_id)
671
+ if record is not None:
672
+ records.append(record)
673
+ return records
674
+
675
+ def _record_from_item(
676
+ self, item: Any, *, user_id: str
677
+ ) -> LongTermMemoryRecord | None:
678
+ """将单个记忆条目解析为 LongTermMemoryRecord。
679
+
680
+ 缺少 MemoryId 或正文的条目返回 None(无法支撑 mutation,fail closed)。
681
+ """
682
+ if not isinstance(item, dict):
683
+ return None
684
+ memory_id = item.get("MemoryId")
685
+ content = item.get("Memory")
686
+ if not isinstance(memory_id, str) or not memory_id.strip():
687
+ logger.warning("memory item without MemoryId skipped")
688
+ return None
689
+ if not isinstance(content, str) or not content.strip():
690
+ logger.warning("memory item without content skipped")
691
+ return None
692
+ score = item.get("Score")
693
+ parsed_score: float | None = None
694
+ if isinstance(score, (int, float)):
695
+ parsed_score = float(score)
696
+ metadata: dict[str, Any] = {}
697
+ for key in ("OccurredStart", "OccurredEnd"):
698
+ value = item.get(key)
699
+ if value is not None:
700
+ metadata[key] = value
701
+ agent_user_id = item.get("AgentUserId")
702
+ if isinstance(agent_user_id, str) and agent_user_id:
703
+ metadata["AgentUserId"] = agent_user_id
704
+ return LongTermMemoryRecord(
705
+ memory_id=memory_id.strip(),
706
+ content=content.strip(),
707
+ score=parsed_score,
708
+ user_id=user_id,
709
+ created_at=item.get("CreatedAt") if isinstance(item.get("CreatedAt"), str) else None,
710
+ updated_at=item.get("UpdatedAt") if isinstance(item.get("UpdatedAt"), str) else None,
711
+ metadata=metadata,
712
+ )
713
+
332
714
  def get_session_status(
333
715
  self,
334
716
  *,
@@ -0,0 +1,95 @@
1
+ """SQLite 长期记忆后端 — 持久化 LTM(适配 BaseLongTermMemoryBackend 接口)。
2
+
3
+ 用 ``SqliteMemoryProvider`` 的持久 SQLite 路径,解决 ``InMemoryLTMBackend`` 进程退出后
4
+ 数据丢失、每次新实例数据不延续的问题。recall 和 flush 共用同一 SQLite 文件。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from typing import List
12
+
13
+ from pydantic import PrivateAttr
14
+
15
+ from ksadk.memory.adk.backends.base_ltm_backend import BaseLongTermMemoryBackend
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class SqliteLTMBackend(BaseLongTermMemoryBackend):
21
+ """SQLite 持久长期记忆后端。
22
+
23
+ 使用 ``SqliteMemoryProvider`` 的持久化路径(``KSADK_MEMORY_DB_PATH`` 或本地 session dir),
24
+ 通过 ``MemoryCoordinator`` 做检索。recall 和 flush 共用同一文件,数据跨进程延续。
25
+
26
+ 适配 ``BaseLongTermMemoryBackend`` 接口(save_memory/search_memory),供
27
+ ``LongTermMemoryService`` 的 "local" backend 使用。
28
+ """
29
+
30
+ _provider: object = PrivateAttr(default=None)
31
+
32
+ def model_post_init(self, __context) -> None:
33
+ from ksadk.memory.providers.local_sqlite import (
34
+ SqliteMemoryProvider,
35
+ _resolve_default_db_path,
36
+ )
37
+
38
+ path = _resolve_default_db_path()
39
+ self._provider = SqliteMemoryProvider(db_path=path, tenant_id="local", workspace_id="local")
40
+ logger.info("SqliteLTMBackend initialized: index=%s, db=%s", self.index, path)
41
+
42
+ def save_memory(self, user_id: str, event_strings: List[str], **kwargs) -> bool:
43
+ """保存记忆到持久 SQLite。"""
44
+ if not event_strings:
45
+ return True
46
+ for event_str in event_strings:
47
+ try:
48
+ payload = json.loads(event_str)
49
+ content = str(payload.get("parts", [{}])[0].get("text", "") or event_str)
50
+ except (json.JSONDecodeError, TypeError, IndexError):
51
+ content = event_str
52
+ from ksadk.memory.models import MemoryCandidate
53
+
54
+ candidate = MemoryCandidate(
55
+ candidate_id=f"ltm_{abs(hash(content)) % 10**16}",
56
+ operation="add",
57
+ memory_type="profile",
58
+ scope="user",
59
+ scope_id=user_id,
60
+ content=content,
61
+ confidence=0.9,
62
+ importance=0.7,
63
+ source_event_ids=[],
64
+ reason="explicit_user_request",
65
+ )
66
+ from ksadk.memory.coordinator import MemoryCoordinator
67
+
68
+ coordinator = MemoryCoordinator(self._provider)
69
+ coordinator.flush_candidates([candidate])
70
+ return True
71
+
72
+ def search_memory(self, user_id: str, query: str, top_k: int = 5, **kwargs) -> List[str]:
73
+ """从持久 SQLite 检索记忆。"""
74
+ from ksadk.memory.coordinator import MemoryCoordinator, build_search_request
75
+
76
+ coordinator = MemoryCoordinator(self._provider)
77
+ request = build_search_request(query=query, user_id=user_id, top_k=top_k)
78
+ result = coordinator.recall(request)
79
+ if result.status != "ok":
80
+ return []
81
+ entries: list[str] = []
82
+ for record in result.records:
83
+ entries.append(
84
+ json.dumps(
85
+ {
86
+ "parts": [{"text": record.content}],
87
+ "metadata": {"memory_id": record.memory_id},
88
+ },
89
+ ensure_ascii=False,
90
+ )
91
+ )
92
+ return entries
93
+
94
+
95
+ __all__ = ["SqliteLTMBackend"]