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
@@ -82,6 +82,10 @@ class KnowledgeBaseClient(BaseModel):
82
82
  score_threshold: float = 0.0
83
83
  score_threshold_enabled: bool = False
84
84
  reranking_enable: bool = False
85
+ # 最近一次检索失败原因(成功调用前置空,失败时填充)。供
86
+ # KnowledgeBaseService.build_context 区分"后端吞错返空"与"真无结果",
87
+ # 避免错误伪装成"未找到"注入模型上下文。
88
+ last_error: str = ""
85
89
 
86
90
  _aicp_client: Any = None
87
91
 
@@ -188,6 +192,7 @@ class KnowledgeBaseClient(BaseModel):
188
192
  try:
189
193
  data = json.loads(response) if isinstance(response, str) else response
190
194
  except (json.JSONDecodeError, TypeError):
195
+ self.last_error = f"Failed to parse response: {str(response)[:200]}"
191
196
  logger.error(f"Failed to parse response: {str(response)[:200]}")
192
197
  return []
193
198
 
@@ -230,6 +235,7 @@ class KnowledgeBaseClient(BaseModel):
230
235
  f"Searching knowledge base: dataset_id={self.dataset_id}, " f"query='{query[:50]}'"
231
236
  )
232
237
 
238
+ self.last_error = ""
233
239
  try:
234
240
  response = client.call("RetrieveKnowledge", params, options={"IsPostJson": True})
235
241
  results = self._parse_response(response)
@@ -238,6 +244,7 @@ class KnowledgeBaseClient(BaseModel):
238
244
  )
239
245
  return results
240
246
  except Exception as e:
247
+ self.last_error = str(e)
241
248
  logger.error(f"Knowledge base search failed: {e}")
242
249
  raise
243
250
 
@@ -44,6 +44,15 @@ class KnowledgeBaseService:
44
44
  self._client = KnowledgeBaseClient.from_env()
45
45
  return self._client
46
46
 
47
+ @property
48
+ def last_error(self) -> str:
49
+ """最近一次检索失败原因(成功前置空,失败填充;含响应解析失败)。
50
+
51
+ 供 ``build_context`` 区分"后端吞错/解析失败返空"与"真无结果"。
52
+ 客户端尚未懒加载时视为无错误。
53
+ """
54
+ return str(getattr(self._client, "last_error", "") or "")
55
+
47
56
  def search(self, query: str, top_k: Optional[int] = None) -> list[KnowledgeBaseResult]:
48
57
  return self._get_client().search(query, top_k)
49
58
 
@@ -54,13 +63,31 @@ class KnowledgeBaseService:
54
63
  logger.error("search_knowledge failed: %s", exc)
55
64
  return f"知识库检索失败: {exc}"
56
65
 
57
- def build_context(self, query: str, top_k: Optional[int] = None) -> dict[str, str] | None:
66
+ def build_context(
67
+ self,
68
+ query: str,
69
+ top_k: Optional[int] = None,
70
+ ) -> dict[str, str] | None:
71
+ """构造环境知识库上下文。失败时返回 ``formatted_text=""`` + 独立 ``error`` 字段,
72
+ 不把错误字符串塞进 ``formatted_text``(避免错误伪装成知识库正文注入模型)。
73
+
74
+ - 检索抛错(网络/鉴权失败)→ except 捕获,返 ``error`` 字段。
75
+ - 响应解析失败返空列表(``_parse_response``)→ client ``last_error`` 非空,
76
+ 返回 ``error`` 字段。
77
+ - 真无结果(检索正常返空)→ ``formatted_text`` 为"未找到…"(语义真实,可注入)。
78
+ """
58
79
  normalized = str(query or "").strip()
59
- if not normalized:
60
- return None
61
- if not self.is_configured():
80
+ if not normalized or not self.is_configured():
62
81
  return None
82
+ try:
83
+ results = self.search(normalized, top_k)
84
+ except Exception as exc:
85
+ logger.error("search_knowledge failed: %s", exc)
86
+ return {"query": normalized, "formatted_text": "", "error": str(exc)}
87
+ client_error = self.last_error
88
+ if not results and client_error:
89
+ return {"query": normalized, "formatted_text": "", "error": client_error}
63
90
  return {
64
91
  "query": normalized,
65
- "formatted_text": self.search_text(normalized, top_k),
92
+ "formatted_text": format_knowledge_results(results),
66
93
  }
ksadk/memory/__init__.py CHANGED
@@ -54,4 +54,65 @@ def __getattr__(name):
54
54
  from ksadk.memory.service import LongTermMemoryService
55
55
 
56
56
  return LongTermMemoryService
57
+ # Memory v2(方案 §10):lazy import,避免在仅用旧 API 时强制加载 SQLite/tiktoken 依赖。
58
+ _v2_names = {
59
+ "MemoryRecord",
60
+ "MemoryCandidate",
61
+ "MemorySearchRequest",
62
+ "MemorySearchResult",
63
+ "MemoryCapabilities",
64
+ "CoreMemoryBlock",
65
+ "MemoryDeleteRequest",
66
+ "MemoryDeleteResult",
67
+ "MemoryProvider",
68
+ "MemoryPolicy",
69
+ "MemoryCoordinator",
70
+ "MemoryExtractor",
71
+ "SqliteMemoryProvider",
72
+ "build_search_request",
73
+ "recall_to_context_item",
74
+ "propose_memory_candidates",
75
+ }
76
+ if name in _v2_names:
77
+ if name in {
78
+ "MemoryRecord",
79
+ "MemoryCandidate",
80
+ "MemorySearchRequest",
81
+ "MemorySearchResult",
82
+ "MemoryCapabilities",
83
+ "CoreMemoryBlock",
84
+ "MemoryDeleteRequest",
85
+ "MemoryDeleteResult",
86
+ }:
87
+ from ksadk.memory import models as _models
88
+
89
+ return getattr(_models, name)
90
+ if name == "MemoryProvider":
91
+ from ksadk.memory.provider import MemoryProvider
92
+
93
+ return MemoryProvider
94
+ if name == "MemoryPolicy":
95
+ from ksadk.memory.policy import MemoryPolicy
96
+
97
+ return MemoryPolicy
98
+ if name == "MemoryCoordinator":
99
+ from ksadk.memory.coordinator import MemoryCoordinator
100
+
101
+ return MemoryCoordinator
102
+ if name in {"build_search_request", "recall_to_context_item"}:
103
+ from ksadk.memory import coordinator as _coord
104
+
105
+ return getattr(_coord, name)
106
+ if name == "MemoryExtractor":
107
+ from ksadk.memory.extraction import MemoryExtractor
108
+
109
+ return MemoryExtractor
110
+ if name == "propose_memory_candidates":
111
+ from ksadk.memory.extraction import propose_memory_candidates
112
+
113
+ return propose_memory_candidates
114
+ if name == "SqliteMemoryProvider":
115
+ from ksadk.memory.providers.local_sqlite import SqliteMemoryProvider
116
+
117
+ return SqliteMemoryProvider
57
118
  raise AttributeError(f"module 'ksadk.memory' has no attribute {name!r}")
@@ -3,22 +3,48 @@
3
3
  所有长期记忆后端必须继承此类并实现 save_memory / search_memory 方法。
4
4
 
5
5
  参考 VeADK: veadk/memory/long_term_memory_backends/base_backend.py
6
+
7
+ 扩展协议(技术改造方案 §7.3):
8
+ - search_records: 结构化检索,返回带 memory_id 的 LongTermMemoryRecord
9
+ - update_memory / delete_memory: 按 ID 原地更新/软删除
10
+ - capabilities: 声明 backend 支持的能力集合
11
+ 基类对扩展方法提供默认实现:抛出 UnsupportedMemoryOperation 并在
12
+ capabilities 中不声明对应能力,旧 backend 无需改动即保持兼容。
6
13
  """
7
14
 
8
15
  from abc import ABC, abstractmethod
9
- from typing import List
16
+ from typing import List, Set
10
17
 
11
18
  from pydantic import BaseModel
12
19
 
20
+ from ksadk.memory.models import (
21
+ LongTermMemoryRecord,
22
+ MemoryExtractionStatus,
23
+ MemoryMutationResult,
24
+ UnsupportedMemoryOperation,
25
+ )
26
+
27
+ # 能力常量(§7.3)
28
+ CAP_SEARCH = "search"
29
+ CAP_ADD = "add"
30
+ CAP_FLUSH = "flush"
31
+ CAP_STRUCTURED_SEARCH = "structured_search"
32
+ CAP_UPDATE = "update"
33
+ CAP_DELETE = "delete"
34
+ CAP_SESSION_STATUS = "session_status"
35
+
13
36
 
14
37
  class BaseLongTermMemoryBackend(ABC, BaseModel):
15
38
  """长期记忆存储后端抽象基类
16
39
 
17
40
  Attributes:
18
41
  index: 索引/集合名称,用于隔离不同应用的记忆数据
42
+ last_error: 最近一次 search/save 失败的原因。成功调用前置空,失败时填充。
43
+ 上层(LongTermMemoryService.build_context)据此区分"后端吞错返空"与"真无记忆"。
19
44
  """
20
45
 
21
46
  index: str = ""
47
+ last_error: str = ""
22
48
 
23
49
  @abstractmethod
24
50
  def save_memory(self, user_id: str, event_strings: List[str], **kwargs) -> bool:
@@ -46,3 +72,72 @@ class BaseLongTermMemoryBackend(ABC, BaseModel):
46
72
  匹配的记忆字符串列表
47
73
  """
48
74
  pass
75
+
76
+ # ---- 扩展协议(可选能力,默认 unsupported) ----
77
+
78
+ def search_records(
79
+ self,
80
+ user_id: str,
81
+ query: str,
82
+ top_k: int = 5,
83
+ **kwargs,
84
+ ) -> List[LongTermMemoryRecord]:
85
+ """结构化检索:返回带服务端 memory_id 的记录列表。
86
+
87
+ 不支持的 backend 抛出 UnsupportedMemoryOperation,
88
+ 不得降级为返回正文 hash 伪造的 ID。
89
+ """
90
+ raise UnsupportedMemoryOperation(
91
+ f"{type(self).__name__} does not support structured search"
92
+ )
93
+
94
+ def update_memory(
95
+ self,
96
+ *,
97
+ user_id: str,
98
+ memory_id: str,
99
+ content: str,
100
+ **kwargs,
101
+ ) -> MemoryMutationResult:
102
+ """按 memory_id 原地更新记忆正文。
103
+
104
+ 不支持的 backend 抛出 UnsupportedMemoryOperation,
105
+ 不得静默追加一条新记忆来模拟 update。
106
+ """
107
+ raise UnsupportedMemoryOperation(f"{type(self).__name__} does not support update")
108
+
109
+ def delete_memory(
110
+ self,
111
+ *,
112
+ user_id: str,
113
+ memory_id: str,
114
+ **kwargs,
115
+ ) -> MemoryMutationResult:
116
+ """按 memory_id 软删除指定记忆。
117
+
118
+ 不支持的 backend 抛出 UnsupportedMemoryOperation。
119
+ """
120
+ raise UnsupportedMemoryOperation(f"{type(self).__name__} does not support delete")
121
+
122
+ def get_extraction_status(
123
+ self,
124
+ *,
125
+ user_id: str,
126
+ session_id: str,
127
+ ) -> MemoryExtractionStatus:
128
+ """查询 Session 后台提取状态(写后确认,§7.7)。
129
+
130
+ 不支持的 backend 抛出 UnsupportedMemoryOperation。
131
+ """
132
+ raise UnsupportedMemoryOperation(f"{type(self).__name__} does not support session status")
133
+
134
+ def capabilities(self) -> Set[str]:
135
+ """声明本 backend 支持的能力集合。
136
+
137
+ 基类默认只声明基础读写能力;子类按真实实现覆写,
138
+ 声明必须与实际可用方法一致(§11.1)。
139
+ """
140
+ return {CAP_SEARCH, CAP_ADD}
141
+
142
+ def has_capability(self, capability: str) -> bool:
143
+ return capability in self.capabilities()
@@ -52,10 +52,10 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
52
52
  def model_post_init(self, __context) -> None:
53
53
  if not self.base_url:
54
54
  logger.warning(
55
- "HttpLTMBackend: base_url is empty. " "Set KSADK_LTM_HTTP_URL environment variable."
55
+ "HttpLTMBackend: base_url is empty. Set KSADK_LTM_HTTP_URL environment variable."
56
56
  )
57
57
  logger.info(
58
- f"HttpLTMBackend initialized: base_url={self.base_url[:50]}... " f"index={self.index}"
58
+ f"HttpLTMBackend initialized: base_url={self.base_url[:50]}... index={self.index}"
59
59
  )
60
60
 
61
61
  @property
@@ -101,13 +101,13 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
101
101
  response.raise_for_status()
102
102
 
103
103
  logger.info(
104
- f"Saved {len(event_strings)} events to remote memory service " f"for user={user_id}"
104
+ f"Saved {len(event_strings)} events to remote memory service for user={user_id}"
105
105
  )
106
106
  return True
107
107
 
108
108
  except httpx.HTTPStatusError as e:
109
109
  logger.error(
110
- f"HTTP error saving memory: {e.response.status_code} " f"{e.response.text[:200]}"
110
+ f"HTTP error saving memory: {e.response.status_code} {e.response.text[:200]}"
111
111
  )
112
112
  return False
113
113
  except Exception as e:
@@ -133,6 +133,7 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
133
133
  """
134
134
  if not self.base_url:
135
135
  logger.warning("HttpLTMBackend: base_url not configured, return empty results.")
136
+ self.last_error = "base_url not configured"
136
137
  return []
137
138
 
138
139
  try:
@@ -161,7 +162,7 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
161
162
 
162
163
  except httpx.HTTPStatusError as e:
163
164
  logger.error(
164
- f"HTTP error searching memory: {e.response.status_code} " f"{e.response.text[:200]}"
165
+ f"HTTP error searching memory: {e.response.status_code} {e.response.text[:200]}"
165
166
  )
166
167
  return []
167
168
  except Exception as e:
@@ -173,3 +174,11 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
173
174
  if self._client:
174
175
  self._client.close()
175
176
  self._client = None
177
+
178
+ # ------------------------------------------------------------------
179
+ # 扩展协议(方案 §7.3):HTTP backend 为框架预留,结构化能力未定。
180
+ # 远程 API 对接细节待提供(见 save_memory TODO),因此不声明
181
+ # structured/update/delete/session_status 能力;search_records 也不降级
182
+ # 伪造 ID,保持 base 的 UnsupportedMemoryOperation 默认行为。
183
+ # 远端 schema 确认后,在此接入对应端点并覆写 capabilities()。
184
+ # ------------------------------------------------------------------
@@ -2,15 +2,35 @@
2
2
 
3
3
  使用简单的内存字典存储和文本匹配检索。
4
4
  数据在进程退出后丢失,仅适用于开发和测试场景。
5
+
6
+ 扩展协议(技术改造方案 §7.3):内存实现结构化检索与 update/delete,
7
+ 用于本地开发和 fake 场景下的契约验证;不声明 flush/session_status 能力
8
+ (没有后台提取过程,写入即"可见")。
9
+
10
+ 兼容设计:_storage 保存原始事件字符串不改写,search_memory 返回原始
11
+ 字符串列表(保留旧契约,§7.5);结构化能力通过平行 ID 索引提供。
5
12
  """
6
13
 
14
+ import json
7
15
  import logging
16
+ import uuid
8
17
  from collections import defaultdict
9
- from typing import List
18
+ from typing import List, Set
10
19
 
11
20
  from pydantic import PrivateAttr
12
21
 
13
- from ksadk.memory.adk.backends.base_ltm_backend import BaseLongTermMemoryBackend
22
+ from ksadk.memory.adk.backends.base_ltm_backend import (
23
+ CAP_ADD,
24
+ CAP_DELETE,
25
+ CAP_SEARCH,
26
+ CAP_STRUCTURED_SEARCH,
27
+ CAP_UPDATE,
28
+ BaseLongTermMemoryBackend,
29
+ )
30
+ from ksadk.memory.models import (
31
+ LongTermMemoryRecord,
32
+ MemoryMutationResult,
33
+ )
14
34
 
15
35
  logger = logging.getLogger(__name__)
16
36
 
@@ -30,6 +50,12 @@ class InMemoryLTMBackend(BaseLongTermMemoryBackend):
30
50
  """
31
51
 
32
52
  _storage: defaultdict[str, list[str]] = PrivateAttr(default_factory=lambda: defaultdict(list))
53
+ # memory_id -> 原始事件字符串(不改写 _storage,保留旧 search_memory 契约)。
54
+ _entry_ids: dict[str, str] = PrivateAttr(default_factory=dict)
55
+ # (user_id) -> {memory_id -> 原始事件字符串},用于快速定位与更新/删除。
56
+ _user_entry_index: defaultdict[str, dict[str, str]] = PrivateAttr(
57
+ default_factory=lambda: defaultdict(dict)
58
+ )
33
59
 
34
60
  def model_post_init(self, __context) -> None:
35
61
  # {user_id: [event_string, ...]}
@@ -40,7 +66,11 @@ class InMemoryLTMBackend(BaseLongTermMemoryBackend):
40
66
  if not event_strings:
41
67
  return True
42
68
 
43
- self._storage[user_id].extend(event_strings)
69
+ for entry in event_strings:
70
+ memory_id = f"mem-{uuid.uuid4().hex[:12]}"
71
+ self._entry_ids[memory_id] = entry
72
+ self._user_entry_index[user_id][memory_id] = entry
73
+ self._storage[user_id].append(entry)
44
74
  logger.debug(
45
75
  f"Saved {len(event_strings)} events for user={user_id}, "
46
76
  f"total={len(self._storage[user_id])}"
@@ -88,3 +118,106 @@ class InMemoryLTMBackend(BaseLongTermMemoryBackend):
88
118
  f"found {len(results)} results from {len(user_memories)} total"
89
119
  )
90
120
  return results
121
+
122
+ # ---- 扩展协议实现(§7.3) ----
123
+
124
+ def capabilities(self) -> Set[str]:
125
+ return {
126
+ CAP_SEARCH,
127
+ CAP_ADD,
128
+ CAP_STRUCTURED_SEARCH,
129
+ CAP_UPDATE,
130
+ CAP_DELETE,
131
+ }
132
+
133
+ def search_records(
134
+ self, user_id: str, query: str, top_k: int = 5, **kwargs
135
+ ) -> List[LongTermMemoryRecord]:
136
+ """结构化检索:复用关键词匹配,返回稳定生成的本地 ID。"""
137
+ entries = self.search_memory(user_id, query, top_k=top_k)
138
+ records: List[LongTermMemoryRecord] = []
139
+ for entry in entries:
140
+ memory_id = self._find_entry_id(user_id, entry)
141
+ if memory_id is None:
142
+ continue
143
+ records.append(
144
+ LongTermMemoryRecord(
145
+ memory_id=memory_id,
146
+ content=self._entry_text(entry),
147
+ score=None,
148
+ user_id=user_id,
149
+ )
150
+ )
151
+ return records
152
+
153
+ def update_memory(
154
+ self,
155
+ *,
156
+ user_id: str,
157
+ memory_id: str,
158
+ content: str,
159
+ **kwargs,
160
+ ) -> MemoryMutationResult:
161
+ user_index = self._user_entry_index.get(user_id, {})
162
+ old_entry = user_index.get(memory_id)
163
+ if old_entry is None:
164
+ return MemoryMutationResult(ok=False, memory_id=memory_id, status="not_found")
165
+ new_entry = json.dumps(
166
+ {"role": "user", "parts": [{"text": content}]}, ensure_ascii=False
167
+ )
168
+ memories = self._storage[user_id]
169
+ for i, entry in enumerate(memories):
170
+ if entry is old_entry:
171
+ memories[i] = new_entry
172
+ break
173
+ self._entry_ids[memory_id] = new_entry
174
+ user_index[memory_id] = new_entry
175
+ return MemoryMutationResult(
176
+ ok=True,
177
+ memory_id=memory_id,
178
+ new_memory_id=memory_id,
179
+ status="updated",
180
+ )
181
+
182
+ def delete_memory(
183
+ self,
184
+ *,
185
+ user_id: str,
186
+ memory_id: str,
187
+ **kwargs,
188
+ ) -> MemoryMutationResult:
189
+ user_index = self._user_entry_index.get(user_id, {})
190
+ entry = user_index.pop(memory_id, None)
191
+ if entry is None:
192
+ return MemoryMutationResult(
193
+ ok=True, memory_id=memory_id, status="already_absent", message="目标记忆已不存在"
194
+ )
195
+ self._entry_ids.pop(memory_id, None)
196
+ memories = self._storage.get(user_id, [])
197
+ try:
198
+ memories.remove(entry)
199
+ except ValueError:
200
+ pass
201
+ return MemoryMutationResult(ok=True, memory_id=memory_id, status="deleted")
202
+
203
+ def _find_entry_id(self, user_id: str, entry: str) -> str | None:
204
+ """根据原始条目定位 memory_id(反向查表)。"""
205
+ for memory_id, stored in self._user_entry_index.get(user_id, {}).items():
206
+ if stored is entry or stored == entry:
207
+ return memory_id
208
+ return None
209
+
210
+ # ---- 内部工具 ----
211
+
212
+ @staticmethod
213
+ def _entry_text(entry: str) -> str:
214
+ """提取事件字符串里的正文(兼容 JSON 事件与纯文本)。"""
215
+ try:
216
+ payload = json.loads(entry)
217
+ except (json.JSONDecodeError, TypeError):
218
+ return entry
219
+ if isinstance(payload, dict):
220
+ parts = payload.get("parts")
221
+ if isinstance(parts, list) and parts and isinstance(parts[0], dict):
222
+ return str(parts[0].get("text", entry))
223
+ return entry