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
ksadk/memory/service.py CHANGED
@@ -8,15 +8,25 @@ import os
8
8
  from typing import Any, cast
9
9
 
10
10
  from ksadk.common.aicp_env import resolve_aicp_connection
11
- from ksadk.memory.adk.backends.base_ltm_backend import BaseLongTermMemoryBackend
11
+ from ksadk.memory.adk.backends.base_ltm_backend import (
12
+ CAP_SESSION_STATUS,
13
+ CAP_STRUCTURED_SEARCH,
14
+ BaseLongTermMemoryBackend,
15
+ )
12
16
  from ksadk.memory.ltm_backend_factory import get_long_term_memory_backend_cls
17
+ from ksadk.memory.models import (
18
+ LongTermMemoryRecord,
19
+ MemoryExtractionStatus,
20
+ MemoryMutationResult,
21
+ UnsupportedMemoryOperation,
22
+ )
13
23
 
14
24
  logger = logging.getLogger(__name__)
15
25
 
16
26
 
17
27
  def format_memory_entries(entries: list[str]) -> str:
18
28
  if not entries:
19
- return "未找到相关长期记忆。"
29
+ return "" # empty → "" not "未找到"(§10.8)
20
30
 
21
31
  formatted_entries: list[str] = []
22
32
  for index, entry in enumerate(entries, 1):
@@ -35,6 +45,11 @@ def format_memory_entries(entries: list[str]) -> str:
35
45
  return "\n\n".join(formatted_entries)
36
46
 
37
47
 
48
+ def _normalize_content(text: str) -> str:
49
+ """归一化正文,用于写后确认的可见性匹配(去空白,小写)。"""
50
+ return "".join(str(text or "").split()).lower()
51
+
52
+
38
53
  class LongTermMemoryService:
39
54
  def __init__(
40
55
  self,
@@ -77,10 +92,18 @@ class LongTermMemoryService:
77
92
  connection = resolve_aicp_connection("KSADK_LTM")
78
93
  backend_config = {
79
94
  "access_key": (
80
- os.environ.get("KSADK_LTM_ACCESS_KEY") or os.environ.get("KSYUN_ACCESS_KEY", "")
95
+ os.environ.get("KSADK_LTM_ACCESS_KEY")
96
+ or os.environ.get("KSYUN_ACCESS_KEY")
97
+ or os.environ.get("KSYUN_ACCESS_KEY_ID", "")
81
98
  ),
82
99
  "secret_key": (
83
- os.environ.get("KSADK_LTM_SECRET_KEY") or os.environ.get("KSYUN_SECRET_KEY", "")
100
+ os.environ.get("KSADK_LTM_SECRET_KEY")
101
+ or os.environ.get("KSYUN_SECRET_KEY")
102
+ or os.environ.get("KSYUN_SECRET_ACCESS_KEY", "")
103
+ ),
104
+ "session_token": (
105
+ os.environ.get("KSADK_LTM_SESSION_TOKEN")
106
+ or os.environ.get("KSYUN_SESSION_TOKEN", "")
84
107
  ),
85
108
  "region": connection["region"],
86
109
  "endpoint": connection["endpoint"],
@@ -118,14 +141,137 @@ class LongTermMemoryService:
118
141
  top_k=top_k if top_k is not None else self.top_k,
119
142
  )
120
143
 
144
+ def search_records(
145
+ self, *, user_id: str, query: str, top_k: int | None = None
146
+ ) -> list[LongTermMemoryRecord]:
147
+ """结构化检索(§7.5):优先走 backend.search_records,返回带服务端 ID 的记录。
148
+
149
+ 旧 backend 不支持时抛出 UnsupportedMemoryOperation,
150
+ 由上层降级为只读 search/add,不得伪造 ID。
151
+ """
152
+ return self._backend.search_records(
153
+ user_id=user_id,
154
+ query=query,
155
+ top_k=top_k if top_k is not None else self.top_k,
156
+ )
157
+
158
+ def update_memory(
159
+ self,
160
+ *,
161
+ user_id: str,
162
+ memory_id: str,
163
+ content: str,
164
+ ) -> MemoryMutationResult:
165
+ """按 ID 原地更新记忆(§7.4);不支持的 backend 抛稳定异常。"""
166
+ return self._backend.update_memory(
167
+ user_id=user_id,
168
+ memory_id=memory_id,
169
+ content=content,
170
+ )
171
+
172
+ def delete_memory(self, *, user_id: str, memory_id: str) -> MemoryMutationResult:
173
+ """按 ID 软删除记忆(§7.4);重复删除归一为 already_absent。"""
174
+ return self._backend.delete_memory(user_id=user_id, memory_id=memory_id)
175
+
176
+ def list_memory_records(
177
+ self,
178
+ *,
179
+ user_id: str,
180
+ query: str = "",
181
+ page: int = 1,
182
+ page_size: int = 20,
183
+ ) -> list[LongTermMemoryRecord]:
184
+ """ListMemories 透传:写后确认可见性 / 取当前 MemoryId。"""
185
+ return self._backend.list_memory_records(
186
+ user_id=user_id, query=query, page=page, page_size=page_size
187
+ )
188
+
189
+ def get_extraction_status(
190
+ self,
191
+ *,
192
+ user_id: str,
193
+ session_id: str,
194
+ confirm_searchable: bool = False,
195
+ expected_content: str = "",
196
+ ) -> MemoryExtractionStatus:
197
+ """写后确认(§7.7):查询 Session 后台提取状态。
198
+
199
+ Args:
200
+ confirm_searchable: True 且 State=100 时,进一步通过
201
+ ListMemories 确认目标记录可见(目标达成后 searchable=True)。
202
+ expected_content: 用于在 ListMemories 中定位目标记录的归一化正文。
203
+ """
204
+ if CAP_SESSION_STATUS not in self._backend.capabilities():
205
+ raise UnsupportedMemoryOperation(
206
+ f"{type(self._backend).__name__} does not support session status"
207
+ )
208
+ status = self._backend.get_extraction_status(user_id=user_id, session_id=session_id)
209
+ if (
210
+ confirm_searchable
211
+ and status.status == "extracted"
212
+ and CAP_STRUCTURED_SEARCH in self._backend.capabilities()
213
+ and expected_content.strip()
214
+ ):
215
+ # 用 expected_content 作为 Query 语义过滤,避免大记忆库时目标不在首页。
216
+ records = self.list_memory_records(
217
+ user_id=user_id, query=expected_content, page_size=50
218
+ )
219
+ if self._find_matching_record(records, expected_content) is not None:
220
+ status = MemoryExtractionStatus(
221
+ session_id=status.session_id,
222
+ state=status.state,
223
+ status=status.status,
224
+ searchable=True,
225
+ message=status.message,
226
+ )
227
+ return status
228
+
229
+ @staticmethod
230
+ def _find_matching_record(
231
+ records: list[LongTermMemoryRecord], expected_content: str
232
+ ) -> LongTermMemoryRecord | None:
233
+ """按归一化正文等值/包含匹配目标记录(方案 N3:可见性判定规则)。
234
+
235
+ expected_content 为空时不作匹配(返回 None),避免误判任意记录为可见。
236
+ """
237
+ normalized = _normalize_content(expected_content)
238
+ if not normalized:
239
+ return None
240
+ for record in records:
241
+ if normalized == _normalize_content(record.content):
242
+ return record
243
+ for record in records:
244
+ if normalized in _normalize_content(record.content):
245
+ return record
246
+ return None
247
+
248
+ def capabilities(self) -> set[str]:
249
+ """透传 backend 能力声明(§7.3)。"""
250
+ return set(self._backend.capabilities())
251
+
252
+ @property
253
+ def last_error(self) -> str:
254
+ """最近一次后端失败原因(成功调用前置空,失败时填充)。
255
+
256
+ 后端(SDK/HTTP)失败时可能吞掉异常返空列表而非抛错,这里把该信号暴露给
257
+ ``build_context``,以区分"后端吞错返空"与"真无记忆"。
258
+ """
259
+ return str(getattr(self._backend, "last_error", "") or "")
260
+
121
261
  def search_text(self, *, user_id: str, query: str, top_k: int | None = None) -> str:
262
+ """检索长期记忆并格式化为文本(方案 §10.8:错误不得混入正文)。
263
+
264
+ Provider 异常时返回空字符串而非错误文本——错误文本会被当作记忆正文注入模型上下文,
265
+ 污染回答。需要区分"真无记忆"与"后端失败"的调用方应改用 ``build_context()``
266
+ 或检查 ``self.last_error``。
267
+ """
122
268
  try:
123
269
  return format_memory_entries(
124
270
  self.search_entries(user_id=user_id, query=query, top_k=top_k)
125
271
  )
126
272
  except Exception as exc:
127
273
  logger.error("load_memory failed: %s", exc)
128
- return f"长期记忆检索失败: {exc}"
274
+ return ""
129
275
 
130
276
  def save_event_strings(
131
277
  self,
@@ -133,18 +279,52 @@ class LongTermMemoryService:
133
279
  user_id: str,
134
280
  event_strings: list[str],
135
281
  metadata: dict[str, Any] | None = None,
282
+ session_id: str | None = None,
283
+ flush: bool | None = None,
136
284
  ) -> bool:
285
+ """保存事件字符串。
286
+
287
+ 显式参数优先于 metadata(方案 §7.6):
288
+ flush: None 表示未指定,回退到 metadata["flush"];
289
+ True/False 显式覆盖 metadata。
290
+ session_id: None 回退到 metadata["session_id"]。
291
+ 兼容期旧调用(仅 metadata)行为不变。
292
+ """
293
+ base_metadata = dict(metadata or {})
294
+ effective_flush = flush if flush is not None else base_metadata.get("flush")
295
+ if session_id is not None:
296
+ base_metadata["session_id"] = session_id
297
+ effective_session_id = base_metadata.get("session_id")
298
+ # 只有显式为 True 时才携带 flush;False/未携带时不传,
299
+ # 让服务端走默认累积策略(§5.2)。
300
+ if effective_flush is True:
301
+ base_metadata["flush"] = True
302
+ else:
303
+ base_metadata.pop("flush", None)
137
304
  return bool(
138
305
  self._backend.save_memory(
139
306
  user_id=user_id,
140
307
  event_strings=event_strings,
141
- metadata=metadata or {},
308
+ metadata=base_metadata,
309
+ session_id=effective_session_id,
310
+ flush=effective_flush,
142
311
  )
143
312
  )
144
313
 
145
314
  def save_text(
146
- self, *, user_id: str, content: str, metadata: dict[str, Any] | None = None
315
+ self,
316
+ *,
317
+ user_id: str,
318
+ content: str,
319
+ metadata: dict[str, Any] | None = None,
320
+ session_id: str | None = None,
321
+ flush: bool | None = None,
147
322
  ) -> bool:
323
+ """保存自包含事实文本。
324
+
325
+ 显式 flush=True 用于显式、内容自包含的持久事实保存
326
+ (如 ksadk_memory_add);普通 sync_turn 不传 flush(§3.1/§8.7)。
327
+ """
148
328
  payload = {
149
329
  "role": "user",
150
330
  "parts": [{"text": content}],
@@ -154,6 +334,8 @@ class LongTermMemoryService:
154
334
  user_id=user_id,
155
335
  event_strings=[json.dumps(payload, ensure_ascii=False)],
156
336
  metadata=metadata,
337
+ session_id=session_id,
338
+ flush=flush,
157
339
  )
158
340
 
159
341
  def build_context(
@@ -168,7 +350,15 @@ class LongTermMemoryService:
168
350
  return None
169
351
  if not self.is_configured():
170
352
  return None
353
+ try:
354
+ entries = self.search_entries(user_id=user_id, query=normalized, top_k=top_k)
355
+ except Exception as exc:
356
+ logger.error("load_memory failed: %s", exc)
357
+ return {"query": normalized, "formatted_text": "", "error": str(exc)}
358
+ backend_error = self.last_error
359
+ if not entries and backend_error:
360
+ return {"query": normalized, "formatted_text": "", "error": backend_error}
171
361
  return {
172
362
  "query": normalized,
173
- "formatted_text": self.search_text(user_id=user_id, query=normalized, top_k=top_k),
363
+ "formatted_text": format_memory_entries(entries),
174
364
  }
ksadk/memory/tool.py CHANGED
@@ -78,6 +78,10 @@ def save_memory(content: str) -> dict:
78
78
  "agent_id": context.agent_id,
79
79
  "session_id": context.session_id,
80
80
  "runner_type": context.runner_type,
81
+ # Explicit user saves must become searchable promptly. The
82
+ # SDK backend maps this to CreateMemorySdk.Flush; background
83
+ # turn persistence keeps the service's batched default.
84
+ "flush": True,
81
85
  },
82
86
  )
83
87
  if ok:
@@ -21,6 +21,7 @@ from .server import ProxyServer
21
21
  # 进程级单例:setup_environment 多次调用只起一个 proxy
22
22
  _proxy: Optional[ProxyServer] = None
23
23
  _original_base: Optional[str] = None
24
+ _original_base_env: Optional[dict[str, str | None]] = None
24
25
 
25
26
 
26
27
  def setup_proxy_redirect_if_enabled(
@@ -35,7 +36,7 @@ def setup_proxy_redirect_if_enabled(
35
36
  返回 proxy base_url(已重定向)或 None(未启用)。多次调用幂等(单例)。
36
37
  上游凭证来自 ProxyConfig(从 env 读),不下发给子进程(凭证闭合)。
37
38
  """
38
- global _proxy, _original_base
39
+ global _proxy, _original_base, _original_base_env
39
40
  if _proxy is not None:
40
41
  return _proxy.base_url # 幂等:已起
41
42
  gate = gate or ProxyGate.from_env()
@@ -48,9 +49,7 @@ def setup_proxy_redirect_if_enabled(
48
49
  or os.environ.get("OPENAI_API_BASE")
49
50
  or ""
50
51
  )
51
- key = (
52
- api_key or os.environ.get("OPENAI_API_KEY") or os.environ.get("LLM_API_KEY") or ""
53
- )
52
+ key = api_key or os.environ.get("OPENAI_API_KEY") or os.environ.get("LLM_API_KEY") or ""
54
53
  token = local_token or os.environ.get("KSADK_PROXY_TOKEN") or ""
55
54
  if not upstream or not key:
56
55
  return None # 缺凭证/上游,不启用(保持原 env)
@@ -59,7 +58,11 @@ def setup_proxy_redirect_if_enabled(
59
58
  srv.start()
60
59
  _proxy = srv
61
60
  # 记录原 base 并重定向(双别名都指向 proxy)
62
- _original_base = os.environ.get("OPENAI_BASE_URL") or os.environ.get("OPENAI_API_BASE")
61
+ _original_base_env = {
62
+ "OPENAI_BASE_URL": os.environ.get("OPENAI_BASE_URL"),
63
+ "OPENAI_API_BASE": os.environ.get("OPENAI_API_BASE"),
64
+ }
65
+ _original_base = _original_base_env["OPENAI_BASE_URL"] or _original_base_env["OPENAI_API_BASE"]
63
66
  os.environ["OPENAI_BASE_URL"] = srv.base_url
64
67
  os.environ["OPENAI_API_BASE"] = srv.base_url
65
68
  if not token:
@@ -70,11 +73,15 @@ def setup_proxy_redirect_if_enabled(
70
73
 
71
74
  def teardown_proxy_redirect() -> None:
72
75
  """回收 proxy 并恢复原 OPENAI_BASE_URL(进程退出/卸载时调)。"""
73
- global _proxy, _original_base
76
+ global _proxy, _original_base, _original_base_env
74
77
  if _proxy is not None:
75
78
  _proxy.stop()
76
79
  _proxy = None
77
- if _original_base is not None:
78
- os.environ["OPENAI_BASE_URL"] = _original_base
79
- os.environ["OPENAI_API_BASE"] = _original_base
80
+ if _original_base_env is not None:
81
+ for key, value in _original_base_env.items():
82
+ if value is None:
83
+ os.environ.pop(key, None)
84
+ else:
85
+ os.environ[key] = value
86
+ _original_base_env = None
80
87
  _original_base = None
@@ -1,7 +1,8 @@
1
1
  """ProxyConfig:转换 proxy 的配置(显式注入,不在模块导入时读 env,便于内化与测试)。"""
2
2
 
3
3
  import os
4
- from dataclasses import dataclass
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, Callable
5
6
  from urllib.parse import urlsplit
6
7
 
7
8
  # HTTPS(凭证安全):不再用明文 HTTP 携带 Bearer key。
@@ -15,17 +16,37 @@ class ProxyConfig:
15
16
  upstream_base: str = DEFAULT_UPSTREAM_BASE
16
17
  api_key: str = "" # 上游凭证(proxy -> 上游)
17
18
  local_token: str = "" # 本地 proxy 鉴权 token(codex -> proxy);空则不校验(仅本地调试)
19
+ # 非空时 responses 转换路径强制改写 model(Codex 可能发送内部伪模型名)。
20
+ upstream_model: str = ""
18
21
  timeout: float = 180.0
22
+ event_callback: Callable[[str, dict[str, Any]], None] | None = field(
23
+ default=None,
24
+ repr=False,
25
+ compare=False,
26
+ )
19
27
 
20
28
  def __post_init__(self) -> None:
21
29
  self.upstream_base = _require_secure_upstream(self.upstream_base.rstrip("/"))
22
30
 
31
+ def emit(self, event: str, data: dict[str, Any]) -> None:
32
+ callback = self.event_callback
33
+ if callback is None:
34
+ return
35
+ try:
36
+ callback(event, data)
37
+ except Exception:
38
+ # Observability must never break model traffic.
39
+ return
40
+
23
41
  @classmethod
24
42
  def from_env(cls) -> "ProxyConfig":
25
43
  return cls(
26
44
  upstream_base=os.environ.get("UPSTREAM_BASE", DEFAULT_UPSTREAM_BASE),
27
45
  api_key=os.environ.get("KSPMAS_API_KEY", ""),
28
46
  local_token=os.environ.get("KSADK_PROXY_TOKEN", ""),
47
+ upstream_model=os.environ.get("OPENAI_MODEL_NAME")
48
+ or os.environ.get("MODEL_NAME")
49
+ or "",
29
50
  timeout=float(os.environ.get("UPSTREAM_TIMEOUT", "180")),
30
51
  )
31
52
 
@@ -12,6 +12,9 @@
12
12
  超时/5xx/429/401/403 一律 "unknown",不改变判定(故障 ≠ 能力缺失)。
13
13
  - ``stream_delta_ok`` 需真发流式请求才能判定,成本高,本模块默认 None(不探),
14
14
  由调用方按需触发或默认走转换层(转换层自己生成完整 delta)。
15
+ - Codex 直连还需功能性 tool probes:真实发送当前 Codex 会声明的
16
+ ``additional_tools`` 工具面。任一必需类型被拒时保留
17
+ ``responses_supported=True``,同时推荐经 Chat 转换层执行。
15
18
  """
16
19
 
17
20
  from __future__ import annotations
@@ -24,6 +27,12 @@ import httpx
24
27
 
25
28
  Verdict = Literal["supported", "unsupported", "unknown"]
26
29
 
30
+ # These are Codex wire-contract capabilities, not provider/model allowlists.
31
+ # namespace/custom are required for a native Responses turn; web_search is an
32
+ # optional built-in that may be disabled without downgrading the whole protocol.
33
+ CODEX_DIRECT_REQUIRED_TOOL_TYPES = frozenset({"namespace", "custom"})
34
+ CODEX_OPTIONAL_TOOL_TYPES = frozenset({"web_search"})
35
+
27
36
 
28
37
  @dataclass
29
38
  class ModelCapabilities:
@@ -31,7 +40,7 @@ class ModelCapabilities:
31
40
 
32
41
  responses_supported: bool | None = None # None = 未知(未探/不确定)
33
42
  stream_delta_ok: bool | None = None # 流式增量 delta;None = 未探
34
- tool_types: set[str] = field(default_factory=set) # 支持的工具 namespace
43
+ tool_types: set[str] = field(default_factory=set) # 已实测支持的 Codex 工具类型
35
44
  preferred_protocol: str = "chat" # "responses" | "chat":路由该走哪条
36
45
  checked_at: float = 0.0
37
46
  verdict: Verdict = "unknown" # 最近一次探测结论
@@ -65,7 +74,10 @@ def probe_responses_capability(
65
74
  ):
66
75
  """功能性 probe /v1/responses,返回能力矩阵(sync) 或 coroutine(async)。
67
76
 
68
- - 200 且结构合法(output+status)→ supported,preferred_protocol=responses
77
+ - 纯文本 200 且结构合法后,再逐个探测真实 Codex 工具类型
78
+ - namespace/custom 成功→ preferred_protocol=responses
79
+ - 仅 web_search 被拒→仍为 responses,但不把 web_search 记入 tool_types
80
+ - namespace/custom 被拒→ supported,preferred_protocol=chat
69
81
  - 200 但非 responses 结构(网关伪 200)→ unsupported,preferred_protocol=chat
70
82
  - 404/405/400 unknown → unsupported,preferred_protocol=chat
71
83
  - 超时/5xx/429/401/403 → unknown,preferred_protocol 保持默认 chat(保守走转换层)
@@ -86,16 +98,47 @@ def _probe_sync(
86
98
  caps = ModelCapabilities(checked_at=time.time(), verdict="unknown")
87
99
  url = f"{base.rstrip('/')}/responses"
88
100
  headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}
89
- payload = {"model": model, "input": "hi", "max_output_tokens": 1, "stream": False}
90
101
  try:
91
- r = client.post(url, json=payload, headers=headers, timeout=timeout)
102
+ r = client.post(
103
+ url,
104
+ json=_base_probe_payload(model),
105
+ headers=headers,
106
+ timeout=timeout,
107
+ )
92
108
  except (httpx.TimeoutException, httpx.ConnectError, httpx.HTTPError):
93
109
  return _finalize(caps)
94
110
  try:
95
111
  data = r.json()
96
112
  except ValueError:
97
113
  data = None
98
- return _apply_response(caps, r.status_code, r.text, data)
114
+ caps = _apply_response(caps, r.status_code, r.text, data)
115
+ if not caps.responses_supported:
116
+ return caps
117
+ for tool_type, payload in _codex_tool_probe_payloads(model):
118
+ try:
119
+ tool_response = client.post(
120
+ url,
121
+ json=payload,
122
+ headers=headers,
123
+ timeout=timeout,
124
+ )
125
+ except (httpx.TimeoutException, httpx.ConnectError, httpx.HTTPError):
126
+ caps.verdict = "unknown"
127
+ return _finalize(caps)
128
+ try:
129
+ tool_data = tool_response.json()
130
+ except ValueError:
131
+ tool_data = None
132
+ caps = _apply_tool_response(
133
+ caps,
134
+ tool_type,
135
+ tool_response.status_code,
136
+ tool_response.text,
137
+ tool_data,
138
+ )
139
+ if tool_type not in caps.tool_types:
140
+ return caps
141
+ return _finalize(caps)
99
142
 
100
143
 
101
144
  async def _probe_async(
@@ -104,16 +147,162 @@ async def _probe_async(
104
147
  caps = ModelCapabilities(checked_at=time.time(), verdict="unknown")
105
148
  url = f"{base.rstrip('/')}/responses"
106
149
  headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}
107
- payload = {"model": model, "input": "hi", "max_output_tokens": 1, "stream": False}
108
150
  try:
109
- r = await client.post(url, json=payload, headers=headers, timeout=timeout)
151
+ r = await client.post(
152
+ url,
153
+ json=_base_probe_payload(model),
154
+ headers=headers,
155
+ timeout=timeout,
156
+ )
110
157
  except (httpx.TimeoutException, httpx.ConnectError, httpx.HTTPError):
111
158
  return _finalize(caps)
112
159
  try:
113
160
  data = r.json()
114
161
  except ValueError:
115
162
  data = None
116
- return _apply_response(caps, r.status_code, r.text, data)
163
+ caps = _apply_response(caps, r.status_code, r.text, data)
164
+ if not caps.responses_supported:
165
+ return caps
166
+ for tool_type, payload in _codex_tool_probe_payloads(model):
167
+ try:
168
+ tool_response = await client.post(
169
+ url,
170
+ json=payload,
171
+ headers=headers,
172
+ timeout=timeout,
173
+ )
174
+ except (httpx.TimeoutException, httpx.ConnectError, httpx.HTTPError):
175
+ caps.verdict = "unknown"
176
+ return _finalize(caps)
177
+ try:
178
+ tool_data = tool_response.json()
179
+ except ValueError:
180
+ tool_data = None
181
+ caps = _apply_tool_response(
182
+ caps,
183
+ tool_type,
184
+ tool_response.status_code,
185
+ tool_response.text,
186
+ tool_data,
187
+ )
188
+ if tool_type not in caps.tool_types:
189
+ return caps
190
+ return _finalize(caps)
191
+
192
+
193
+ def _base_probe_payload(model: str) -> dict[str, Any]:
194
+ return {"model": model, "input": "hi", "max_output_tokens": 1, "stream": False}
195
+
196
+
197
+ def _namespace_probe_payload(model: str) -> dict[str, Any]:
198
+ """Return the smallest real Codex 0.147 dynamic-tool declaration."""
199
+
200
+ return {
201
+ "model": model,
202
+ "input": [
203
+ {
204
+ "type": "additional_tools",
205
+ "role": "developer",
206
+ "tools": [
207
+ {
208
+ "type": "namespace",
209
+ "name": "functions",
210
+ "description": "KsADK Codex capability probe",
211
+ "tools": [
212
+ {
213
+ "type": "function",
214
+ "name": "probe",
215
+ "description": "Probe Codex namespace tool support",
216
+ "parameters": {
217
+ "type": "object",
218
+ "properties": {},
219
+ "additionalProperties": False,
220
+ },
221
+ }
222
+ ],
223
+ }
224
+ ],
225
+ },
226
+ {
227
+ "type": "message",
228
+ "role": "user",
229
+ "content": [{"type": "input_text", "text": "Reply with OK."}],
230
+ },
231
+ ],
232
+ "max_output_tokens": 1,
233
+ "stream": False,
234
+ }
235
+
236
+
237
+ def _custom_probe_payload(model: str) -> dict[str, Any]:
238
+ """Return the smallest Codex namespaced freeform-tool declaration."""
239
+
240
+ payload = _namespace_probe_payload(model)
241
+ payload["input"][0]["tools"][0]["tools"] = [
242
+ {
243
+ "type": "custom",
244
+ "name": "probe",
245
+ "description": "Probe Codex custom tool support",
246
+ "format": {
247
+ "type": "grammar",
248
+ "syntax": "lark",
249
+ "definition": 'start: "OK"',
250
+ },
251
+ }
252
+ ]
253
+ return payload
254
+
255
+
256
+ def _web_search_probe_payload(model: str) -> dict[str, Any]:
257
+ """Return Codex's ``additional_tools`` built-in web-search declaration."""
258
+
259
+ payload = _namespace_probe_payload(model)
260
+ payload["input"][0]["tools"] = [{"type": "web_search"}]
261
+ return payload
262
+
263
+
264
+ def _codex_tool_probe_payloads(model: str) -> tuple[tuple[str, dict[str, Any]], ...]:
265
+ """Return the model-agnostic direct tool surface required by Codex."""
266
+
267
+ return (
268
+ ("namespace", _namespace_probe_payload(model)),
269
+ ("custom", _custom_probe_payload(model)),
270
+ ("web_search", _web_search_probe_payload(model)),
271
+ )
272
+
273
+
274
+ def _apply_tool_response(
275
+ caps: ModelCapabilities, tool_type: str, status: int, text: str, data: Any
276
+ ) -> ModelCapabilities:
277
+ valid_envelope = isinstance(data, dict) and "output" in data and "status" in data
278
+ envelope_failed = valid_envelope and (
279
+ str(data.get("status") or "").lower() == "failed" or bool(data.get("error"))
280
+ )
281
+ if status == 200 and valid_envelope and not envelope_failed:
282
+ caps.tool_types.add(tool_type)
283
+ caps.verdict = "supported"
284
+ return _finalize(caps)
285
+
286
+ low = (text or "").lower()
287
+ dialect_marker = any(
288
+ marker in low
289
+ for marker in (
290
+ tool_type.lower(),
291
+ "additional_tools",
292
+ "invalid value",
293
+ "supported values",
294
+ "not supported",
295
+ "unrecognized",
296
+ )
297
+ )
298
+ dialect_rejected = (
299
+ status in (400, 404, 405, 422) and (status in (404, 405) or dialect_marker)
300
+ ) or (status == 200 and envelope_failed and dialect_marker)
301
+ if not dialect_rejected:
302
+ # Transient failures do not establish direct compatibility and should
303
+ # not be cached as a native Codex tool-capable endpoint.
304
+ caps.verdict = "unknown"
305
+ return _finalize(caps)
117
306
 
118
307
 
119
308
  def _apply_response(
@@ -136,7 +325,11 @@ def _apply_response(
136
325
 
137
326
  def _finalize(caps: ModelCapabilities) -> ModelCapabilities:
138
327
  """根据 verdict 定 preferred_protocol(保守:不确定也走转换层 chat)。"""
139
- if caps.verdict == "supported" and caps.responses_supported:
328
+ if (
329
+ caps.verdict == "supported"
330
+ and caps.responses_supported
331
+ and CODEX_DIRECT_REQUIRED_TOOL_TYPES.issubset(caps.tool_types)
332
+ ):
140
333
  caps.preferred_protocol = "responses"
141
334
  else:
142
335
  # unsupported 或 unknown 都默认 chat(走转换层):unknown 时走转换层更安全,