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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (494) hide show
  1. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +43 -7
  2. agentengine_sdk_python-0.8.2.dist-info/RECORD +778 -0
  3. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +1 -1
  4. ksadk/a2a/__init__.py +2 -0
  5. ksadk/a2a/_space_client_events.py +381 -0
  6. ksadk/a2a/bootstrap.py +0 -2
  7. ksadk/a2a/card.py +6 -111
  8. ksadk/a2a/control_plane.py +239 -1
  9. ksadk/a2a/event_adapter.py +156 -64
  10. ksadk/a2a/executor.py +195 -172
  11. ksadk/a2a/routes.py +12 -5
  12. ksadk/a2a/server.py +1 -8
  13. ksadk/a2a/service_env.py +33 -0
  14. ksadk/a2a/space_client.py +43 -349
  15. ksadk/a2a_card.py +101 -0
  16. ksadk/a2ui/core.py +222 -53
  17. ksadk/agui/_agent_helpers.py +243 -0
  18. ksadk/agui/a2ui_projection.py +12 -5
  19. ksadk/agui/agent.py +474 -275
  20. ksadk/agui/config.py +11 -5
  21. ksadk/agui/routes.py +7 -15
  22. ksadk/api/client.py +530 -40
  23. ksadk/api/environment.py +27 -0
  24. ksadk/builders/code_builder.py +352 -53
  25. ksadk/builders/container_builder.py +71 -44
  26. ksadk/builders/framework_requirements.py +2 -1
  27. ksadk/builders/ks3_uploader.py +2 -2
  28. ksadk/builders/managed_runtime_builder.py +60 -28
  29. ksadk/cli/__init__.py +25 -0
  30. ksadk/cli/cmd_a2a.py +18 -31
  31. ksadk/cli/cmd_create.py +13 -26
  32. ksadk/cli/cmd_dashboard.py +15 -1
  33. ksadk/cli/cmd_deploy.py +28 -3
  34. ksadk/cli/cmd_eval.py +443 -0
  35. ksadk/cli/cmd_evalset.py +284 -0
  36. ksadk/cli/cmd_files.py +5 -1
  37. ksadk/cli/cmd_hermes.py +137 -163
  38. ksadk/cli/cmd_invoke.py +28 -38
  39. ksadk/cli/cmd_launch.py +12 -0
  40. ksadk/cli/cmd_managed_runtime.py +95 -0
  41. ksadk/cli/cmd_observe.py +85 -0
  42. ksadk/cli/cmd_openclaw.py +85 -479
  43. ksadk/cli/cmd_replay.py +28 -4
  44. ksadk/cli/cmd_run.py +29 -67
  45. ksadk/cli/cmd_studio.py +201 -0
  46. ksadk/cli/cmd_web.py +15 -6
  47. ksadk/cli/dry_run.py +2 -1
  48. ksadk/cli/env_options.py +83 -0
  49. ksadk/cli/global_options.py +3 -1
  50. ksadk/cli/hermes_env.py +151 -0
  51. ksadk/cli/invoke_payload.py +49 -0
  52. ksadk/cli/network_options.py +7 -5
  53. ksadk/cli/openclaw_env.py +458 -0
  54. ksadk/cli/runtime_bootstrap.py +51 -0
  55. ksadk/cli/ui.py +6 -0
  56. ksadk/cli/workflow_common.py +5 -5
  57. ksadk/codex/__init__.py +3 -3
  58. ksadk/codex/client.py +833 -49
  59. ksadk/codex/phase.py +1 -1
  60. ksadk/codex/runtime.py +720 -127
  61. ksadk/common/kop_client.py +232 -0
  62. ksadk/configs/__init__.py +1 -3
  63. ksadk/configs/env_registry.py +244 -45
  64. ksadk/configs/env_registry_pcm.py +181 -0
  65. ksadk/configs/env_var_spec.py +18 -0
  66. ksadk/configs/global_config.py +6 -1
  67. ksadk/configs/settings.py +5 -108
  68. ksadk/context_engine/__init__.py +98 -0
  69. ksadk/context_engine/assembler.py +176 -0
  70. ksadk/context_engine/baseline.py +419 -0
  71. ksadk/context_engine/cache_observability.py +228 -0
  72. ksadk/context_engine/capabilities.py +443 -0
  73. ksadk/context_engine/contributors.py +344 -0
  74. ksadk/context_engine/hosted_pipeline.py +417 -0
  75. ksadk/context_engine/models.py +117 -0
  76. ksadk/context_engine/planner.py +698 -0
  77. ksadk/context_engine/policies.py +319 -0
  78. ksadk/context_engine/projection.py +31 -0
  79. ksadk/context_engine/shadow_plan.py +266 -0
  80. ksadk/context_engine/tokenizer.py +164 -0
  81. ksadk/conversations/__init__.py +85 -94
  82. ksadk/conversations/context.py +126 -6
  83. ksadk/conversations/message_projection.py +292 -28
  84. ksadk/conversations/model_context.py +49 -3
  85. ksadk/conversations/runtime_compaction.py +207 -5
  86. ksadk/conversations/runtime_input.py +193 -3
  87. ksadk/conversations/runtime_invocation.py +81 -3
  88. ksadk/conversations/runtime_observability.py +157 -4
  89. ksadk/conversations/runtime_payloads.py +42 -0
  90. ksadk/conversations/runtime_preparation.py +280 -2
  91. ksadk/conversations/runtime_resume.py +71 -32
  92. ksadk/conversations/runtime_stream_events.py +94 -9
  93. ksadk/conversations/runtime_streaming.py +175 -22
  94. ksadk/conversations/semantic_summary.py +311 -1
  95. ksadk/conversations/session_lock.py +51 -0
  96. ksadk/deployment/env_forward.py +72 -0
  97. ksadk/deployment/managed_runtime.py +38 -0
  98. ksadk/deployment/manager.py +24 -8
  99. ksadk/deployment/providers/serverless.py +228 -92
  100. ksadk/evaluation/__init__.py +116 -0
  101. ksadk/evaluation/a2a_adapter.py +452 -0
  102. ksadk/evaluation/adapters.py +67 -0
  103. ksadk/evaluation/agent_eval_client.py +395 -0
  104. ksadk/evaluation/cloud_binding.py +80 -0
  105. ksadk/evaluation/cloud_converter.py +194 -0
  106. ksadk/evaluation/cloud_service.py +199 -0
  107. ksadk/evaluation/contracts.py +478 -0
  108. ksadk/evaluation/evalset.py +351 -0
  109. ksadk/evaluation/evaluators.py +627 -0
  110. ksadk/evaluation/evidence.py +258 -0
  111. ksadk/evaluation/executor.py +143 -0
  112. ksadk/evaluation/local_adapter.py +550 -0
  113. ksadk/evaluation/service_env.py +27 -0
  114. ksadk/evaluation/storage.py +68 -0
  115. ksadk/evaluation/studio_build_adapter.py +253 -0
  116. ksadk/evaluation/target.py +82 -0
  117. ksadk/events/__init__.py +13 -13
  118. ksadk/events/_v1_compat/__init__.py +1 -0
  119. ksadk/events/_v1_compat/models.py +299 -0
  120. ksadk/events/_v1_compat/parser.py +247 -0
  121. ksadk/events/_v1_compat/projection.py +753 -0
  122. ksadk/events/adapters/__init__.py +34 -0
  123. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  124. ksadk/events/adapters/_a2a_support.py +275 -0
  125. ksadk/events/adapters/_codex_interactions.py +375 -0
  126. ksadk/events/adapters/_codex_items.py +801 -0
  127. ksadk/events/adapters/_codex_validators.py +311 -0
  128. ksadk/events/adapters/_langgraph_support.py +785 -0
  129. ksadk/events/adapters/a2a.py +841 -0
  130. ksadk/events/adapters/adk.py +503 -0
  131. ksadk/events/adapters/codex.py +717 -0
  132. ksadk/events/adapters/langgraph.py +745 -0
  133. ksadk/events/canonical.py +440 -0
  134. ksadk/events/canonical_replay.py +497 -0
  135. ksadk/events/canonical_store.py +468 -0
  136. ksadk/events/cold_recovery.py +257 -0
  137. ksadk/events/content.py +89 -0
  138. ksadk/events/identity.py +86 -0
  139. ksadk/events/pipeline.py +455 -0
  140. ksadk/events/projections.py +66 -0
  141. ksadk/events/reducer.py +661 -0
  142. ksadk/events/replay.py +3 -36
  143. ksadk/events/runtime_event.py +20 -256
  144. ksadk/events/session_event.py +324 -0
  145. ksadk/events/store.py +3 -316
  146. ksadk/events/v1_compat.py +41 -0
  147. ksadk/harness/__init__.py +3 -2
  148. ksadk/harness/app.py +114 -123
  149. ksadk/harness/config.py +1 -1
  150. ksadk/harness/runtime.py +467 -0
  151. ksadk/hermes_terminal.py +5 -0
  152. ksadk/identity/resolver.py +4 -3
  153. ksadk/interaction/__init__.py +28 -0
  154. ksadk/interaction/contracts.py +136 -0
  155. ksadk/interaction/ledger.py +189 -0
  156. ksadk/interaction/provider.py +122 -0
  157. ksadk/interaction/providers/__init__.py +52 -0
  158. ksadk/interaction/providers/adk.py +40 -0
  159. ksadk/interaction/providers/codex.py +94 -0
  160. ksadk/interaction/providers/langgraph.py +58 -0
  161. ksadk/kernel/__init__.py +117 -0
  162. ksadk/kernel/authorization.py +258 -0
  163. ksadk/kernel/bootstrap.py +1074 -0
  164. ksadk/kernel/contract_fingerprints.py +61 -0
  165. ksadk/kernel/contracts.py +362 -0
  166. ksadk/kernel/control.py +219 -0
  167. ksadk/kernel/errors.py +79 -0
  168. ksadk/kernel/ingress.py +1102 -0
  169. ksadk/kernel/mapping.py +111 -0
  170. ksadk/kernel/memory_store.py +1133 -0
  171. ksadk/kernel/postgres_store.py +1756 -0
  172. ksadk/kernel/recovery.py +452 -0
  173. ksadk/kernel/runtime_identity.py +103 -0
  174. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  175. ksadk/kernel/sqlite_store.py +1410 -0
  176. ksadk/kernel/state.py +145 -0
  177. ksadk/kernel/store.py +232 -0
  178. ksadk/kernel/worker.py +953 -0
  179. ksadk/knowledge_base/client.py +22 -4
  180. ksadk/knowledge_base/service.py +32 -5
  181. ksadk/managed_a2a_card.py +160 -0
  182. ksadk/managed_runtime.py +16 -9
  183. ksadk/memory/__init__.py +61 -0
  184. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  185. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  186. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  187. ksadk/memory/adk/backends/sdk_ltm_backend.py +394 -5
  188. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  189. ksadk/memory/adk/long_term_memory.py +3 -2
  190. ksadk/memory/coordinator.py +461 -0
  191. ksadk/memory/events.py +202 -0
  192. ksadk/memory/extraction.py +228 -0
  193. ksadk/memory/ltm_backend_factory.py +16 -3
  194. ksadk/memory/models.py +316 -0
  195. ksadk/memory/policy.py +215 -0
  196. ksadk/memory/provider.py +47 -0
  197. ksadk/memory/provider_adapter.py +159 -0
  198. ksadk/memory/provider_resolver.py +71 -0
  199. ksadk/memory/providers/__init__.py +5 -0
  200. ksadk/memory/providers/local_sqlite.py +490 -0
  201. ksadk/memory/resolved_policy.py +145 -0
  202. ksadk/memory/service.py +198 -8
  203. ksadk/memory/tool.py +4 -0
  204. ksadk/model_proxy/bootstrap.py +16 -9
  205. ksadk/model_proxy/config.py +22 -1
  206. ksadk/model_proxy/detect.py +202 -9
  207. ksadk/model_proxy/namespace.py +67 -15
  208. ksadk/model_proxy/server.py +168 -6
  209. ksadk/model_proxy/transform.py +105 -3
  210. ksadk/observability/__init__.py +25 -0
  211. ksadk/observability/session_log.py +420 -0
  212. ksadk/observability/trajectory.py +105 -0
  213. ksadk/prompts/__init__.py +71 -0
  214. ksadk/prompts/compiler.py +215 -0
  215. ksadk/prompts/models.py +82 -0
  216. ksadk/prompts/projection.py +89 -0
  217. ksadk/prompts/resolved.py +137 -0
  218. ksadk/prompts/sources.py +233 -0
  219. ksadk/runners/_langgraph_runner_streams.py +822 -0
  220. ksadk/runners/adk_runner.py +14 -31
  221. ksadk/runners/base_runner.py +44 -3
  222. ksadk/runners/factory.py +3 -3
  223. ksadk/runners/langgraph_runner.py +252 -433
  224. ksadk/runners/utils/__init__.py +0 -10
  225. ksadk/runners/utils/loader.py +16 -0
  226. ksadk/runtime/__init__.py +67 -41
  227. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  228. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  229. ksadk/runtime/adapter.py +199 -16
  230. ksadk/runtime/conversation_execution.py +687 -0
  231. ksadk/runtime/executor.py +392 -0
  232. ksadk/runtime/factory.py +342 -0
  233. ksadk/runtime/framework_adapters.py +6 -12
  234. ksadk/runtime/hosted_finalizer.py +268 -0
  235. ksadk/runtime/launch.py +61 -0
  236. ksadk/runtime/preprocessing.py +49 -14
  237. ksadk/runtime/runner_adapter.py +330 -391
  238. ksadk/runtime/runner_loading.py +36 -0
  239. ksadk/runtime/usage.py +33 -0
  240. ksadk/sandbox/backends/e2b.py +36 -3
  241. ksadk/server/__init__.py +39 -4
  242. ksadk/server/app.py +18 -492
  243. ksadk/server/composition.py +36 -2
  244. ksadk/server/factory.py +210 -149
  245. ksadk/server/routes/common.py +15 -46
  246. ksadk/server/routes/control.py +13 -9
  247. ksadk/server/routes/kernel_ingress.py +258 -0
  248. ksadk/server/routes/misc.py +10 -33
  249. ksadk/server/routes/models.py +14 -6
  250. ksadk/server/routes/openai_compat.py +129 -27
  251. ksadk/server/routes/projection.py +84 -16
  252. ksadk/server/routes/run.py +347 -460
  253. ksadk/server/routes/sessions.py +37 -40
  254. ksadk/server/routes/streaming.py +52 -7
  255. ksadk/server/routes/workspace.py +54 -21
  256. ksadk/server/static/assets/{ArtifactsPanel-Cjnqt-8W.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  257. ksadk/server/static/assets/{CodeBlock-BqyqZfuB.js → CodeBlock-DOH6MVcn.js} +4 -4
  258. ksadk/server/static/assets/{MathMessageMarkdown-BpH7jiFM.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  259. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  260. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  261. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  262. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  263. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  264. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  265. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  266. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  267. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  268. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  269. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  270. ksadk/server/static/assets/{chunk-FMBD7UC4-BGhvzDYt.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  271. ksadk/server/static/assets/{chunk-ND2GUHAM-D98vPd-E.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  272. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  273. ksadk/server/static/assets/{chunk-4BX2VUAB-CsWIQRhc.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  274. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  275. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  276. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  277. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  278. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  279. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  280. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  281. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-CX3yotHq.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  282. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  283. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  284. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  285. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  286. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  287. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  288. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  289. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  290. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  291. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  292. ksadk/server/static/assets/{dist-D82mtD2s.js → dist-8hqcx0sU.js} +1 -1
  293. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  294. ksadk/server/static/assets/{dist-DHn6nBzr.js → dist-BE7bu-DL.js} +1 -1
  295. ksadk/server/static/assets/{dist-eoW6tZXA.js → dist-BVDffZ0U.js} +1 -1
  296. ksadk/server/static/assets/{dist-C2xjE7cO.js → dist-BX8z72IC.js} +1 -1
  297. ksadk/server/static/assets/{dist-CgF3fj15.js → dist-Bf2M8m3N.js} +1 -1
  298. ksadk/server/static/assets/{dist-Bs-jQcRX.js → dist-BjU6y-A2.js} +1 -1
  299. ksadk/server/static/assets/{dist-Djgl2wVZ.js → dist-BytlxIDT.js} +1 -1
  300. ksadk/server/static/assets/{dist-CVQRZjPu.js → dist-CSh_DE14.js} +1 -1
  301. ksadk/server/static/assets/{dist-6CVKuTAe.js → dist-CXYYBw3_.js} +1 -1
  302. ksadk/server/static/assets/{dist-DlzCRy0S.js → dist-C_wsv-Qd.js} +1 -1
  303. ksadk/server/static/assets/{dist-DF06ORQo.js → dist-ClxGviKw.js} +1 -1
  304. ksadk/server/static/assets/{dist-Cf23NY4L.js → dist-CttYUqzS.js} +1 -1
  305. ksadk/server/static/assets/{dist-ivOya1LD.js → dist-DNp6rLRA.js} +1 -1
  306. ksadk/server/static/assets/{dist-BibcG1V9.js → dist-DUX-id_F.js} +1 -1
  307. ksadk/server/static/assets/{dist-BUHRxvDG.js → dist-DmldrHd_.js} +1 -1
  308. ksadk/server/static/assets/{dist-BZ9n9AjO.js → dist-DnfXs8Vn.js} +2 -2
  309. ksadk/server/static/assets/{dist-DqNnNFrw.js → dist-Dq9gLD7L.js} +1 -1
  310. ksadk/server/static/assets/{dist-CGcp2ex8.js → dist-W-TIVntx.js} +1 -1
  311. ksadk/server/static/assets/{dist-Cwt7koer.js → dist-dDdADKFA.js} +1 -1
  312. ksadk/server/static/assets/{dist-C7-3jInC.js → dist-dydu68Fl.js} +1 -1
  313. ksadk/server/static/assets/{dist-C99THtIA.js → dist-ngtN0DJB.js} +1 -1
  314. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  315. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  316. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  317. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  318. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  319. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  320. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  321. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  322. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  323. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  324. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  325. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-DLOHGvWw.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  326. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-BIzKeejT.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  327. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-BWHoFSw2.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  328. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  329. ksadk/server/static/assets/{linear-CvMBw9cA.js → linear-BHjG8b-J.js} +1 -1
  330. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  331. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-CSAQxEAe.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  332. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  333. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  334. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  335. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  336. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-CxIR4JXI.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  337. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  338. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  339. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  340. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  341. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  342. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  343. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  344. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-DpGa6EAV.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  345. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-NTKNGGe5.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  346. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  347. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  348. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  349. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  350. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  351. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-jqoYs3jQ.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  352. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  353. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  354. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  355. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  356. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  357. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  358. ksadk/server/static/index.html +2 -2
  359. ksadk/sessions/__init__.py +26 -9
  360. ksadk/sessions/_local_service_sync.py +688 -0
  361. ksadk/sessions/_local_tables.py +21 -0
  362. ksadk/sessions/_postgres_schema.py +305 -0
  363. ksadk/sessions/_postgres_tables.py +15 -0
  364. ksadk/sessions/base.py +80 -1
  365. ksadk/sessions/in_memory.py +53 -2
  366. ksadk/sessions/local_service.py +63 -604
  367. ksadk/sessions/postgres_service.py +58 -148
  368. ksadk/sessions/resilient.py +35 -1
  369. ksadk/skills/runtime/backends/e2b.py +1 -1
  370. ksadk/skills/service_client.py +13 -3
  371. ksadk/studio/__init__.py +5 -0
  372. ksadk/studio/agent_avatar_assets.py +120 -0
  373. ksadk/studio/agent_lifecycle.py +51 -0
  374. ksadk/studio/api.py +1598 -0
  375. ksadk/studio/api_catalog_routes.py +324 -0
  376. ksadk/studio/api_contracts.py +286 -0
  377. ksadk/studio/api_helpers.py +132 -0
  378. ksadk/studio/api_memory_routes.py +82 -0
  379. ksadk/studio/authoring.py +749 -0
  380. ksadk/studio/authoring_coordinator.py +1017 -0
  381. ksadk/studio/builder.py +397 -0
  382. ksadk/studio/capabilities.py +368 -0
  383. ksadk/studio/cloud.py +2099 -0
  384. ksadk/studio/codex_agent_service.py +694 -0
  385. ksadk/studio/codex_authoring.py +469 -0
  386. ksadk/studio/codex_builder.py +437 -0
  387. ksadk/studio/codex_manifest.py +307 -0
  388. ksadk/studio/codex_run.py +395 -0
  389. ksadk/studio/compiler.py +190 -0
  390. ksadk/studio/contracts.py +755 -0
  391. ksadk/studio/errors.py +47 -0
  392. ksadk/studio/event_store.py +214 -0
  393. ksadk/studio/framework_run.py +342 -0
  394. ksadk/studio/hosted_kernel.py +317 -0
  395. ksadk/studio/identifiers.py +33 -0
  396. ksadk/studio/manifest_resolver.py +131 -0
  397. ksadk/studio/mcp_runtime.py +229 -0
  398. ksadk/studio/model_client.py +629 -0
  399. ksadk/studio/model_profile_service.py +214 -0
  400. ksadk/studio/operations.py +275 -0
  401. ksadk/studio/otel_trace.py +1060 -0
  402. ksadk/studio/pagination.py +182 -0
  403. ksadk/studio/pcm_memory.py +85 -0
  404. ksadk/studio/python_tool_inspection.py +183 -0
  405. ksadk/studio/repository.py +327 -0
  406. ksadk/studio/resource_catalog.py +1260 -0
  407. ksadk/studio/run_service.py +1133 -0
  408. ksadk/studio/runtime_catalog.py +74 -0
  409. ksadk/studio/runtime_source.py +365 -0
  410. ksadk/studio/service.py +2249 -0
  411. ksadk/studio/shared_web.py +1004 -0
  412. ksadk/studio/skill_discovery.py +628 -0
  413. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
  414. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  415. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  416. ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
  417. ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  418. ksadk/studio/static/index.html +15 -0
  419. ksadk/studio/templates.py +722 -0
  420. ksadk/studio/validator.py +265 -0
  421. ksadk/studio/workspace.py +135 -0
  422. ksadk/tools/gateway.py +9 -0
  423. ksadk/toolsets/__init__.py +27 -1
  424. ksadk/toolsets/a2a.py +426 -0
  425. ksadk/tracing/__init__.py +19 -21
  426. ksadk/tracing/setup.py +317 -403
  427. ksadk/tracing/span_utils.py +0 -44
  428. ksadk/version.py +1 -1
  429. ksadk_runtime_common/memory_backend/registry.py +56 -56
  430. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  431. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  432. ksadk_runtime_common/workspace_files/router.py +8 -2
  433. agentengine_sdk_python-0.8.0.dist-info/RECORD +0 -578
  434. ksadk/a2a/card_builder.py +0 -64
  435. ksadk/a2a/client.py +0 -309
  436. ksadk/events/parser.py +0 -191
  437. ksadk/harness/runner.py +0 -211
  438. ksadk/runners/codex_runner.py +0 -132
  439. ksadk/runners/utils/langfuse.py +0 -230
  440. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  441. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +0 -303
  442. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +0 -1
  443. ksadk/server/static/assets/arc-CFKwSk_B.js +0 -1
  444. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +0 -1
  445. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-Dej8jX8c.js +0 -36
  446. ksadk/server/static/assets/blockDiagram-GPEHLZMM-1F64jqvy.js +0 -132
  447. ksadk/server/static/assets/c4Diagram-AAUBKEIU-9q7AUAKb.js +0 -10
  448. ksadk/server/static/assets/channel-iUGv2N24.js +0 -1
  449. ksadk/server/static/assets/chunk-2J33WTMH-6qgwJm7Q.js +0 -1
  450. ksadk/server/static/assets/chunk-55IACEB6-BK8paA20.js +0 -1
  451. ksadk/server/static/assets/chunk-727SXJPM-B-JEhy7h.js +0 -206
  452. ksadk/server/static/assets/chunk-AQP2D5EJ-DYBvMHAa.js +0 -231
  453. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +0 -1
  454. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +0 -1
  455. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +0 -1
  456. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  457. ksadk/server/static/assets/dagre-BM42HDAG-C7TguAWo.js +0 -4
  458. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  459. ksadk/server/static/assets/diagram-2AECGRRQ-Dd17_kws.js +0 -43
  460. ksadk/server/static/assets/diagram-5GNKFQAL-DVIClIil.js +0 -10
  461. ksadk/server/static/assets/diagram-KO2AKTUF-Df_3gYQa.js +0 -3
  462. ksadk/server/static/assets/diagram-LMA3HP47-BhLk6ZtA.js +0 -24
  463. ksadk/server/static/assets/diagram-OG6HWLK6-D4GNGvuu.js +0 -24
  464. ksadk/server/static/assets/dist-DyvYLMW9.js +0 -1
  465. ksadk/server/static/assets/erDiagram-TEJ5UH35-CnfRcY2M.js +0 -85
  466. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +0 -1
  467. ksadk/server/static/assets/flowDiagram-I6XJVG4X-DGaJjGNJ.js +0 -162
  468. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-DpCoB2g9.js +0 -292
  469. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +0 -1
  470. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-9IKVFBVT.js +0 -106
  471. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  472. ksadk/server/static/assets/index-QSwCHuB8.js +0 -177
  473. ksadk/server/static/assets/index-jOPmTOZH.css +0 -2
  474. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +0 -1
  475. ksadk/server/static/assets/infoDiagram-5YYISTIA-CYIIsb8y.js +0 -2
  476. ksadk/server/static/assets/katex-DATssjp1.js +0 -265
  477. ksadk/server/static/assets/mermaid-parser.core-mGAcEwcq.js +0 -161
  478. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +0 -1
  479. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +0 -1
  480. ksadk/server/static/assets/pieDiagram-4H26LBE5-By-ra07M.js +0 -30
  481. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +0 -1
  482. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DpW90iu3.js +0 -84
  483. ksadk/server/static/assets/stateDiagram-AJRCARHV-DGpd9yFG.js +0 -1
  484. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +0 -1
  485. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +0 -1
  486. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +0 -1
  487. ksadk/server/static/assets/vennDiagram-CIIHVFJN-l3Z7-wbP.js +0 -34
  488. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +0 -1
  489. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-BR54GPmr.js +0 -78
  490. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-SkiH8GsK.js +0 -7
  491. ksadk/tracing/exporters/langfuse_exporter.py +0 -460
  492. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  493. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  494. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -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
@@ -8,8 +8,9 @@
8
8
 
9
9
  环境变量:
10
10
  KSADK_LTM_BACKEND: sdk
11
- KSADK_LTM_ACCESS_KEY: AK (可选,默认取 KSYUN_ACCESS_KEY)
12
- KSADK_LTM_SECRET_KEY: SK (可选,默认取 KSYUN_SECRET_KEY)
11
+ KSADK_LTM_ACCESS_KEY: AK (可选,默认取 KSYUN_ACCESS_KEY / KSYUN_ACCESS_KEY_ID)
12
+ KSADK_LTM_SECRET_KEY: SK (可选,默认取 KSYUN_SECRET_KEY / KSYUN_SECRET_ACCESS_KEY)
13
+ KSADK_LTM_SESSION_TOKEN: STS 临时会话 token (可选,默认取 KSYUN_SESSION_TOKEN)
13
14
  KSADK_LTM_REGION: 区域 (默认 cn-beijing-6)
14
15
  KSADK_LTM_ENDPOINT: API 端点 (默认 aicp.api.ksyun.com)
15
16
  KSADK_LTM_SCHEME: http/https (默认 https)
@@ -20,18 +21,41 @@
20
21
 
21
22
  import json
22
23
  import logging
24
+ import re
23
25
  import time
24
26
  import uuid
25
- from typing import Any
27
+ from typing import Any, List, Set
26
28
 
27
29
  from pydantic import ConfigDict, Field
28
30
 
29
- 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
+ )
30
47
 
31
48
  logger = logging.getLogger(__name__)
32
49
 
33
50
  DEFAULT_SCENE_ID = "_sys_general"
34
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
+
35
59
 
36
60
  class SdkLTMBackend(BaseLongTermMemoryBackend):
37
61
  """金山云 AICP 记忆库 SDK 后端
@@ -42,6 +66,7 @@ class SdkLTMBackend(BaseLongTermMemoryBackend):
42
66
  Attributes:
43
67
  access_key: 访问密钥 ID (AK)
44
68
  secret_key: 访问密钥 (SK)
69
+ session_token: STS 临时会话 token
45
70
  region: API 区域
46
71
  endpoint: API 端点
47
72
  scheme: http 或 https
@@ -64,6 +89,7 @@ class SdkLTMBackend(BaseLongTermMemoryBackend):
64
89
 
65
90
  access_key: str = ""
66
91
  secret_key: str = ""
92
+ session_token: str = ""
67
93
  region: str = "cn-beijing-6"
68
94
  endpoint: str = "aicp.api.ksyun.com"
69
95
  scheme: str = "https"
@@ -137,7 +163,9 @@ class SdkLTMBackend(BaseLongTermMemoryBackend):
137
163
  "Ensure kingsoftcloud-sdk-python>=1.5.8.94 is installed."
138
164
  )
139
165
 
140
- cred = credential.Credential(self.access_key, self.secret_key)
166
+ cred = credential.Credential(
167
+ self.access_key, self.secret_key, self.session_token or None
168
+ )
141
169
 
142
170
  http_profile = HttpProfile()
143
171
  http_profile.endpoint = self.endpoint
@@ -240,6 +268,8 @@ class SdkLTMBackend(BaseLongTermMemoryBackend):
240
268
  params["AgentId"] = agent_id
241
269
  if session_id:
242
270
  params["SessionId"] = session_id
271
+ if metadata.get("flush") is True or kwargs.get("flush") is True:
272
+ params["Flush"] = True
243
273
 
244
274
  logger.info(
245
275
  f"CreateMemorySdk: memory_collection_id={memory_collection_id}, "
@@ -322,6 +352,365 @@ class SdkLTMBackend(BaseLongTermMemoryBackend):
322
352
  logger.error(f"QueryMemorySdk failed: {e}")
323
353
  return []
324
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
+
325
714
  def get_session_status(
326
715
  self,
327
716
  *,