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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +22 -2
  2. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
  3. ksadk/a2a/_space_client_events.py +381 -0
  4. ksadk/a2a/event_adapter.py +156 -64
  5. ksadk/a2a/executor.py +179 -63
  6. ksadk/a2a/space_client.py +6 -333
  7. ksadk/a2ui/core.py +222 -53
  8. ksadk/agui/_agent_helpers.py +243 -0
  9. ksadk/agui/a2ui_projection.py +12 -5
  10. ksadk/agui/agent.py +443 -259
  11. ksadk/api/client.py +525 -27
  12. ksadk/builders/code_builder.py +284 -8
  13. ksadk/builders/container_builder.py +8 -1
  14. ksadk/builders/managed_runtime_builder.py +39 -35
  15. ksadk/cli/__init__.py +17 -0
  16. ksadk/cli/cmd_dashboard.py +15 -1
  17. ksadk/cli/cmd_deploy.py +10 -2
  18. ksadk/cli/cmd_eval.py +66 -9
  19. ksadk/cli/cmd_evalset.py +284 -0
  20. ksadk/cli/cmd_files.py +5 -1
  21. ksadk/cli/cmd_hermes.py +1 -126
  22. ksadk/cli/cmd_invoke.py +28 -38
  23. ksadk/cli/cmd_managed_runtime.py +95 -0
  24. ksadk/cli/cmd_observe.py +85 -0
  25. ksadk/cli/cmd_openclaw.py +1 -435
  26. ksadk/cli/cmd_replay.py +28 -4
  27. ksadk/cli/cmd_studio.py +99 -8
  28. ksadk/cli/cmd_web.py +10 -2
  29. ksadk/cli/hermes_env.py +151 -0
  30. ksadk/cli/invoke_payload.py +49 -0
  31. ksadk/cli/openclaw_env.py +458 -0
  32. ksadk/codex/client.py +180 -30
  33. ksadk/codex/runtime.py +428 -326
  34. ksadk/configs/env_registry.py +66 -12
  35. ksadk/configs/env_registry_pcm.py +181 -0
  36. ksadk/configs/env_var_spec.py +18 -0
  37. ksadk/configs/global_config.py +5 -0
  38. ksadk/context_engine/__init__.py +98 -0
  39. ksadk/context_engine/assembler.py +176 -0
  40. ksadk/context_engine/baseline.py +419 -0
  41. ksadk/context_engine/cache_observability.py +228 -0
  42. ksadk/context_engine/capabilities.py +443 -0
  43. ksadk/context_engine/contributors.py +344 -0
  44. ksadk/context_engine/hosted_pipeline.py +417 -0
  45. ksadk/context_engine/models.py +117 -0
  46. ksadk/context_engine/planner.py +698 -0
  47. ksadk/context_engine/policies.py +319 -0
  48. ksadk/context_engine/projection.py +31 -0
  49. ksadk/context_engine/shadow_plan.py +266 -0
  50. ksadk/context_engine/tokenizer.py +164 -0
  51. ksadk/conversations/context.py +126 -6
  52. ksadk/conversations/message_projection.py +292 -28
  53. ksadk/conversations/model_context.py +49 -3
  54. ksadk/conversations/runtime_compaction.py +207 -5
  55. ksadk/conversations/runtime_input.py +184 -2
  56. ksadk/conversations/runtime_invocation.py +81 -3
  57. ksadk/conversations/runtime_observability.py +141 -0
  58. ksadk/conversations/runtime_payloads.py +42 -0
  59. ksadk/conversations/runtime_preparation.py +272 -2
  60. ksadk/conversations/runtime_resume.py +20 -1
  61. ksadk/conversations/runtime_stream_events.py +94 -9
  62. ksadk/conversations/semantic_summary.py +311 -1
  63. ksadk/conversations/session_lock.py +51 -0
  64. ksadk/deployment/env_forward.py +72 -0
  65. ksadk/deployment/managed_runtime.py +5 -0
  66. ksadk/deployment/providers/serverless.py +143 -69
  67. ksadk/evaluation/__init__.py +32 -0
  68. ksadk/evaluation/a2a_adapter.py +9 -1
  69. ksadk/evaluation/adapters.py +18 -5
  70. ksadk/evaluation/agent_eval_client.py +395 -0
  71. ksadk/evaluation/cloud_binding.py +80 -0
  72. ksadk/evaluation/cloud_converter.py +194 -0
  73. ksadk/evaluation/cloud_service.py +199 -0
  74. ksadk/evaluation/contracts.py +73 -4
  75. ksadk/evaluation/evaluators.py +183 -17
  76. ksadk/evaluation/evidence.py +258 -0
  77. ksadk/evaluation/executor.py +31 -6
  78. ksadk/evaluation/local_adapter.py +550 -0
  79. ksadk/evaluation/service_env.py +27 -0
  80. ksadk/evaluation/studio_build_adapter.py +253 -0
  81. ksadk/evaluation/target.py +15 -6
  82. ksadk/events/__init__.py +13 -13
  83. ksadk/events/_v1_compat/__init__.py +1 -0
  84. ksadk/events/_v1_compat/models.py +299 -0
  85. ksadk/events/_v1_compat/parser.py +247 -0
  86. ksadk/events/_v1_compat/projection.py +753 -0
  87. ksadk/events/adapters/__init__.py +34 -0
  88. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  89. ksadk/events/adapters/_a2a_support.py +275 -0
  90. ksadk/events/adapters/_codex_interactions.py +375 -0
  91. ksadk/events/adapters/_codex_items.py +801 -0
  92. ksadk/events/adapters/_codex_validators.py +311 -0
  93. ksadk/events/adapters/_langgraph_support.py +785 -0
  94. ksadk/events/adapters/a2a.py +841 -0
  95. ksadk/events/adapters/adk.py +503 -0
  96. ksadk/events/adapters/codex.py +717 -0
  97. ksadk/events/adapters/langgraph.py +745 -0
  98. ksadk/events/canonical.py +440 -0
  99. ksadk/events/canonical_replay.py +497 -0
  100. ksadk/events/canonical_store.py +468 -0
  101. ksadk/events/cold_recovery.py +257 -0
  102. ksadk/events/content.py +89 -0
  103. ksadk/events/identity.py +86 -0
  104. ksadk/events/pipeline.py +455 -0
  105. ksadk/events/projections.py +66 -0
  106. ksadk/events/reducer.py +661 -0
  107. ksadk/events/replay.py +3 -36
  108. ksadk/events/runtime_event.py +20 -265
  109. ksadk/events/session_event.py +324 -0
  110. ksadk/events/store.py +3 -316
  111. ksadk/events/v1_compat.py +41 -0
  112. ksadk/harness/runtime.py +160 -57
  113. ksadk/identity/resolver.py +4 -3
  114. ksadk/interaction/__init__.py +28 -0
  115. ksadk/interaction/contracts.py +136 -0
  116. ksadk/interaction/ledger.py +189 -0
  117. ksadk/interaction/provider.py +122 -0
  118. ksadk/interaction/providers/__init__.py +52 -0
  119. ksadk/interaction/providers/adk.py +40 -0
  120. ksadk/interaction/providers/codex.py +94 -0
  121. ksadk/interaction/providers/langgraph.py +58 -0
  122. ksadk/kernel/__init__.py +117 -0
  123. ksadk/kernel/authorization.py +258 -0
  124. ksadk/kernel/bootstrap.py +1074 -0
  125. ksadk/kernel/contract_fingerprints.py +61 -0
  126. ksadk/kernel/contracts.py +362 -0
  127. ksadk/kernel/control.py +219 -0
  128. ksadk/kernel/errors.py +79 -0
  129. ksadk/kernel/ingress.py +1102 -0
  130. ksadk/kernel/mapping.py +111 -0
  131. ksadk/kernel/memory_store.py +1133 -0
  132. ksadk/kernel/postgres_store.py +1756 -0
  133. ksadk/kernel/recovery.py +452 -0
  134. ksadk/kernel/runtime_identity.py +103 -0
  135. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  136. ksadk/kernel/sqlite_store.py +1410 -0
  137. ksadk/kernel/state.py +145 -0
  138. ksadk/kernel/store.py +232 -0
  139. ksadk/kernel/worker.py +953 -0
  140. ksadk/knowledge_base/client.py +7 -0
  141. ksadk/knowledge_base/service.py +32 -5
  142. ksadk/memory/__init__.py +61 -0
  143. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  144. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  145. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  146. ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
  147. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  148. ksadk/memory/coordinator.py +461 -0
  149. ksadk/memory/events.py +202 -0
  150. ksadk/memory/extraction.py +228 -0
  151. ksadk/memory/ltm_backend_factory.py +16 -3
  152. ksadk/memory/models.py +316 -0
  153. ksadk/memory/policy.py +215 -0
  154. ksadk/memory/provider.py +47 -0
  155. ksadk/memory/provider_adapter.py +159 -0
  156. ksadk/memory/provider_resolver.py +71 -0
  157. ksadk/memory/providers/__init__.py +5 -0
  158. ksadk/memory/providers/local_sqlite.py +490 -0
  159. ksadk/memory/resolved_policy.py +145 -0
  160. ksadk/memory/service.py +188 -6
  161. ksadk/model_proxy/bootstrap.py +16 -9
  162. ksadk/model_proxy/detect.py +202 -9
  163. ksadk/model_proxy/namespace.py +39 -15
  164. ksadk/model_proxy/server.py +24 -5
  165. ksadk/model_proxy/transform.py +60 -2
  166. ksadk/observability/__init__.py +25 -0
  167. ksadk/observability/session_log.py +420 -0
  168. ksadk/observability/trajectory.py +105 -0
  169. ksadk/prompts/__init__.py +71 -0
  170. ksadk/prompts/compiler.py +215 -0
  171. ksadk/prompts/models.py +82 -0
  172. ksadk/prompts/projection.py +89 -0
  173. ksadk/prompts/resolved.py +137 -0
  174. ksadk/prompts/sources.py +233 -0
  175. ksadk/runners/_langgraph_runner_streams.py +822 -0
  176. ksadk/runners/adk_runner.py +5 -5
  177. ksadk/runners/base_runner.py +16 -0
  178. ksadk/runners/factory.py +1 -2
  179. ksadk/runners/langgraph_runner.py +198 -437
  180. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  181. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  182. ksadk/runtime/adapter.py +118 -2
  183. ksadk/runtime/conversation_execution.py +316 -182
  184. ksadk/runtime/executor.py +144 -3
  185. ksadk/runtime/factory.py +193 -7
  186. ksadk/runtime/hosted_finalizer.py +268 -0
  187. ksadk/runtime/launch.py +14 -1
  188. ksadk/runtime/preprocessing.py +25 -3
  189. ksadk/runtime/runner_adapter.py +288 -508
  190. ksadk/runtime/usage.py +33 -0
  191. ksadk/sandbox/backends/e2b.py +35 -2
  192. ksadk/server/composition.py +8 -0
  193. ksadk/server/factory.py +69 -6
  194. ksadk/server/routes/kernel_ingress.py +258 -0
  195. ksadk/server/routes/openai_compat.py +88 -8
  196. ksadk/server/routes/projection.py +35 -20
  197. ksadk/server/routes/run.py +94 -0
  198. ksadk/server/routes/sessions.py +2 -0
  199. ksadk/server/routes/streaming.py +5 -2
  200. ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  201. ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
  202. ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  203. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  204. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  205. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  206. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  207. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  208. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  209. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  210. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  211. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  212. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  213. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  214. ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  215. ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  216. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  217. ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  218. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  219. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  220. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  221. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  222. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  223. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  224. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  225. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  226. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  227. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  228. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  229. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  230. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  231. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  232. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  233. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  234. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  235. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  236. ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
  237. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  238. ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
  239. ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
  240. ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
  241. ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
  242. ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
  243. ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
  244. ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
  245. ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
  246. ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
  247. ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
  248. ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
  249. ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
  250. ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
  251. ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
  252. ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
  253. ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
  254. ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
  255. ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
  256. ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
  257. ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
  258. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  259. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  260. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  261. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  262. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  263. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  264. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  265. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  266. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  267. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  268. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  269. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  270. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  271. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  272. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  273. ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
  274. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  275. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  276. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  277. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  278. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  279. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  280. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  281. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  282. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  283. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  284. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  285. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  286. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  287. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  288. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  289. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  290. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  291. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  292. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  293. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  294. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  295. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  296. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  297. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  298. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  299. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  300. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  301. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  302. ksadk/server/static/index.html +2 -2
  303. ksadk/sessions/__init__.py +26 -9
  304. ksadk/sessions/_local_service_sync.py +688 -0
  305. ksadk/sessions/_local_tables.py +21 -0
  306. ksadk/sessions/_postgres_schema.py +305 -0
  307. ksadk/sessions/_postgres_tables.py +15 -0
  308. ksadk/sessions/base.py +80 -1
  309. ksadk/sessions/in_memory.py +53 -2
  310. ksadk/sessions/local_service.py +63 -604
  311. ksadk/sessions/postgres_service.py +58 -148
  312. ksadk/sessions/resilient.py +35 -1
  313. ksadk/studio/api.py +577 -25
  314. ksadk/studio/api_contracts.py +114 -5
  315. ksadk/studio/api_memory_routes.py +82 -0
  316. ksadk/studio/authoring.py +252 -27
  317. ksadk/studio/authoring_coordinator.py +799 -67
  318. ksadk/studio/builder.py +88 -11
  319. ksadk/studio/capabilities.py +17 -0
  320. ksadk/studio/cloud.py +1916 -95
  321. ksadk/studio/codex_agent_service.py +125 -14
  322. ksadk/studio/codex_authoring.py +469 -0
  323. ksadk/studio/codex_builder.py +138 -25
  324. ksadk/studio/codex_manifest.py +51 -22
  325. ksadk/studio/codex_run.py +46 -3
  326. ksadk/studio/compiler.py +1 -0
  327. ksadk/studio/contracts.py +114 -3
  328. ksadk/studio/event_store.py +25 -95
  329. ksadk/studio/framework_run.py +231 -3
  330. ksadk/studio/hosted_kernel.py +317 -0
  331. ksadk/studio/manifest_resolver.py +131 -0
  332. ksadk/studio/model_client.py +95 -9
  333. ksadk/studio/model_profile_service.py +1 -1
  334. ksadk/studio/operations.py +37 -5
  335. ksadk/studio/otel_trace.py +348 -42
  336. ksadk/studio/pcm_memory.py +85 -0
  337. ksadk/studio/repository.py +23 -2
  338. ksadk/studio/resource_catalog.py +43 -0
  339. ksadk/studio/run_service.py +563 -137
  340. ksadk/studio/runtime_source.py +42 -16
  341. ksadk/studio/service.py +1180 -72
  342. ksadk/studio/shared_web.py +32 -22
  343. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  344. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  345. ksadk/studio/static/index.html +2 -2
  346. ksadk/studio/templates.py +100 -8
  347. ksadk/studio/validator.py +19 -0
  348. ksadk/studio/workspace.py +30 -32
  349. ksadk/tracing/setup.py +304 -3
  350. ksadk/version.py +1 -1
  351. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  352. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  353. ksadk/events/parser.py +0 -191
  354. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  355. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
  356. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
  357. ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
  358. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
  359. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
  360. ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
  361. ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
  362. ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
  363. ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
  364. ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
  365. ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
  366. ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
  367. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
  368. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
  369. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
  370. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  371. ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
  372. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  373. ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
  374. ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
  375. ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
  376. ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
  377. ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
  378. ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
  379. ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
  380. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
  381. ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
  382. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
  383. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
  384. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
  385. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  386. ksadk/server/static/assets/index-R52pJJvg.js +0 -177
  387. ksadk/server/static/assets/index-jJr3_955.css +0 -2
  388. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
  389. ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
  390. ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
  391. ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
  392. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
  393. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
  394. ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
  395. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
  396. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
  397. ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
  398. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
  399. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
  400. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
  401. ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
  402. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
  403. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
  404. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
  405. ksadk/studio/evaluation.py +0 -192
  406. ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
  407. ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
  408. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +0 -0
  409. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  410. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  411. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -4,8 +4,10 @@ LangGraphRunner - LangGraph 框架运行时
4
4
  直接透传 LangGraph 原生能力,最小化封装
5
5
  """
6
6
 
7
+ from __future__ import annotations
8
+
9
+ import asyncio
7
10
  import base64
8
- import inspect
9
11
  import os
10
12
  import re
11
13
  import uuid
@@ -14,14 +16,13 @@ from typing import Any, AsyncIterator, Dict, Mapping
14
16
  from langgraph.types import Command
15
17
 
16
18
  from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
17
- from ksadk.conversations.reasoning_markup import ReasoningMarkupParser, strip_reasoning_markup
19
+ from ksadk.runners._langgraph_runner_streams import _LangGraphStreamMixin
18
20
  from ksadk.runners.base_runner import BaseRunner
19
- from ksadk.runners.usage_accumulator import accumulate_usage
20
21
  from ksadk.runners.utils import load_agent_module
21
22
  from ksadk.sessions.continuity import LangGraphSessionAdapter
22
23
 
23
24
 
24
- class LangGraphRunner(BaseRunner):
25
+ class LangGraphRunner(_LangGraphStreamMixin, BaseRunner):
25
26
  """LangGraph 框架运行时
26
27
 
27
28
  透传原生 LangGraph 功能,支持任意 State 格式
@@ -31,6 +32,14 @@ class LangGraphRunner(BaseRunner):
31
32
  # this runner opts into the runtime's semantic follow-up continuation.
32
33
  supports_gateway_approval_semantic_resume = True
33
34
 
35
+ def __init__(self, detection_result: Any, project_dir: str):
36
+ super().__init__(detection_result, project_dir)
37
+ self._managed_checkpoint_lock = asyncio.Lock()
38
+ self._managed_checkpoint_prepared = False
39
+ self._managed_checkpoint_error: tuple[str, str] | None = None
40
+ self._managed_checkpoint_pool: Any = None
41
+ self._managed_checkpoint_namespace = ""
42
+
34
43
  def load_agent(self) -> None:
35
44
  self._load_agent(force_reload=False)
36
45
 
@@ -53,7 +62,13 @@ class LangGraphRunner(BaseRunner):
53
62
  normalized = self.sync_process_model_env(model)
54
63
  if normalized is None or self._agent is None:
55
64
  return
56
- if normalized == getattr(self, "_loaded_model_name", None):
65
+ # Studio's generated graph reads the model environment while building
66
+ # each model turn. Reloading it here would discard the managed
67
+ # PostgreSQL checkpointer that was installed asynchronously below.
68
+ if (
69
+ normalized == getattr(self, "_loaded_model_name", None)
70
+ or self._managed_checkpoint_pool is not None
71
+ ):
57
72
  return
58
73
  self._load_agent(force_reload=True)
59
74
 
@@ -66,24 +81,23 @@ class LangGraphRunner(BaseRunner):
66
81
  if checkpointer is None:
67
82
  checkpointer = getattr(agent, "_checkpointer", None)
68
83
  if checkpointer is None:
84
+ error_code, error_reason = self._managed_checkpoint_error or ("", "")
69
85
  return {
70
86
  "Supported": False,
71
87
  "Backend": "none",
72
88
  "Scope": "unknown",
73
89
  "Durable": False,
74
90
  "SharedAcrossPods": False,
75
- "Reason": "LangGraph graph has no configured checkpointer",
91
+ "ResumeMode": "none",
92
+ **({"ReasonCode": error_code} if error_code else {}),
93
+ "Reason": error_reason or "LangGraph graph has no configured checkpointer",
76
94
  }
77
95
 
78
- checkpointer_type = type(checkpointer)
79
- type_name = f"{checkpointer_type.__module__}.{checkpointer_type.__name__}".lower()
80
- if "memory" in type_name or "inmemory" in type_name:
81
- backend = "memory"
82
- elif "sqlite" in type_name:
83
- backend = "sqlite"
84
- elif "postgres" in type_name:
85
- backend = "postgres"
86
- else:
96
+ backend = self._checkpoint_backend_from_saver(checkpointer)
97
+ if backend == "unknown":
98
+ # Some third-party savers hide their concrete type. Preserve the
99
+ # explicit legacy declaration for those cases, but never let it
100
+ # override a detectable in-memory saver.
87
101
  backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
88
102
  if backend == "local":
89
103
  backend = "sqlite"
@@ -112,17 +126,28 @@ class LangGraphRunner(BaseRunner):
112
126
  reason = "In-memory checkpoint cannot be recovered after process restart or across pods"
113
127
 
114
128
  return {
115
- "Supported": True,
129
+ # A local saver may be useful for interactive development, but it
130
+ # is not a native durable-resume capability in a hosted runtime.
131
+ "Supported": backend not in {"memory", "inmemory", "unknown", ""},
116
132
  "Backend": backend,
117
133
  "Scope": scope,
118
134
  "Durable": durable,
119
135
  "SharedAcrossPods": shared,
120
- "ResumeMode": "time_travel",
136
+ "ResumeMode": "time_travel" if durable else "none",
137
+ **(
138
+ {"ReasonCode": "CHECKPOINTER_NOT_DURABLE"}
139
+ if backend in {"memory", "inmemory", "unknown", ""}
140
+ else {}
141
+ ),
121
142
  "Reason": reason,
122
143
  }
123
144
 
124
145
  def get_runtime_capabilities(self) -> dict[str, Any]:
125
146
  capabilities = super().get_runtime_capabilities()
147
+ capabilities["model_call_boundaries"] = True
148
+ reason_code = str(capabilities["Checkpoint"].get("ReasonCode") or "")
149
+ if reason_code:
150
+ capabilities["ResumeRun"]["ReasonCode"] = reason_code
126
151
  capabilities["SessionContinuity"] = {
127
152
  "Supported": True,
128
153
  "Type": (
@@ -133,9 +158,150 @@ class LangGraphRunner(BaseRunner):
133
158
  }
134
159
  return capabilities
135
160
 
161
+ @staticmethod
162
+ def _checkpoint_backend_from_saver(checkpointer: Any) -> str:
163
+ if checkpointer is None:
164
+ return "unknown"
165
+ for saver_type in type(checkpointer).__mro__:
166
+ qualified_name = f"{saver_type.__module__}.{saver_type.__name__}".lower()
167
+ if "checkpoint.postgres" in qualified_name or "postgressaver" in qualified_name:
168
+ return "postgres"
169
+ if "checkpoint.sqlite" in qualified_name or "sqlitesaver" in qualified_name:
170
+ return "sqlite"
171
+ if "checkpoint.memory" in qualified_name or saver_type.__name__.lower() in {
172
+ "memorysaver",
173
+ "inmemorysaver",
174
+ }:
175
+ return "memory"
176
+ return "unknown"
177
+
178
+ @staticmethod
179
+ def _env_flag(name: str) -> bool:
180
+ return str(os.getenv(name) or "").strip().lower() in {"1", "true", "yes", "on"}
181
+
182
+ @staticmethod
183
+ def _resolve_checkpoint_namespace() -> str:
184
+ session_namespace = str(os.getenv("KSADK_SESSION_NAMESPACE") or "").strip()
185
+ if session_namespace:
186
+ return session_namespace
187
+ agent_id = str(
188
+ os.getenv("AGENTENGINE_AGENT_ID") or os.getenv("KSADK_AGENT_ID") or "default"
189
+ ).strip()
190
+ return f"agent:{agent_id}"
191
+
192
+ async def _create_managed_postgres_saver(self, dsn: str) -> tuple[Any, Any]:
193
+ from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
194
+ from psycopg.rows import dict_row
195
+ from psycopg_pool import AsyncConnectionPool
196
+
197
+ timeout = max(0.1, float(os.getenv("KSADK_SESSION_CONNECT_TIMEOUT") or "5"))
198
+ pool = AsyncConnectionPool(
199
+ conninfo=dsn,
200
+ min_size=1,
201
+ max_size=10,
202
+ open=False,
203
+ timeout=timeout,
204
+ kwargs={
205
+ "autocommit": True,
206
+ "prepare_threshold": 0,
207
+ "row_factory": dict_row,
208
+ },
209
+ )
210
+ try:
211
+ await pool.open(wait=True, timeout=timeout)
212
+ saver = AsyncPostgresSaver(pool)
213
+ await saver.setup()
214
+ return saver, pool
215
+ except Exception:
216
+ await pool.close()
217
+ raise
218
+
219
+ async def prepare_runtime_capabilities(self) -> None:
220
+ """Install the managed saver before a graph can begin an interaction.
221
+
222
+ The graph module must opt into this seam by exporting
223
+ ``ksadk_graph_factory(*, checkpointer)``. We never mutate a compiled
224
+ graph's private attributes: failed configuration remains fail-closed
225
+ and capability discovery honestly reports why native resume is absent.
226
+ """
227
+ if self._managed_checkpoint_prepared:
228
+ return
229
+ async with self._managed_checkpoint_lock:
230
+ if self._managed_checkpoint_prepared:
231
+ return
232
+
233
+ checkpointer = getattr(self._agent, "checkpointer", None)
234
+ if checkpointer is None:
235
+ checkpointer = getattr(self._agent, "_checkpointer", None)
236
+ if self._checkpoint_backend_from_saver(checkpointer) == "postgres":
237
+ self._managed_checkpoint_namespace = self._resolve_checkpoint_namespace()
238
+ self._managed_checkpoint_prepared = True
239
+ return
240
+
241
+ dsn = str(
242
+ os.getenv("KSADK_LANGGRAPH_CHECKPOINT_DSN")
243
+ or os.getenv("KSADK_SESSION_DSN")
244
+ or ""
245
+ ).strip()
246
+ if not self._env_flag("KSADK_LANGGRAPH_AUTO_CHECKPOINT") or not dsn:
247
+ self._managed_checkpoint_prepared = True
248
+ return
249
+
250
+ factory = getattr(self._module, "ksadk_graph_factory", None)
251
+ if not callable(factory):
252
+ self._managed_checkpoint_error = (
253
+ "LANGGRAPH_FACTORY_REQUIRED",
254
+ "LangGraph graph has no durable checkpointer; export "
255
+ "ksadk_graph_factory(*, checkpointer) for managed PostgreSQL checkpoints",
256
+ )
257
+ self._managed_checkpoint_prepared = True
258
+ return
259
+
260
+ pool = None
261
+ try:
262
+ saver, pool = await self._create_managed_postgres_saver(dsn)
263
+ managed_graph = factory(checkpointer=saver)
264
+ if not callable(getattr(managed_graph, "invoke", None)):
265
+ raise TypeError("ksadk_graph_factory must return a compiled LangGraph graph")
266
+ self._agent = managed_graph
267
+ self._managed_checkpoint_pool = pool
268
+ self._managed_checkpoint_namespace = self._resolve_checkpoint_namespace()
269
+ self._managed_checkpoint_error = None
270
+ except (ModuleNotFoundError, ImportError):
271
+ self._managed_checkpoint_error = (
272
+ "DEPENDENCY_MISSING",
273
+ "langgraph-checkpoint-postgres and psycopg are required "
274
+ "for managed checkpoints",
275
+ )
276
+ except Exception as exc: # noqa: BLE001
277
+ error_name = type(exc).__name__.lower()
278
+ self._managed_checkpoint_error = (
279
+ "SCHEMA_PERMISSION_DENIED"
280
+ if "privilege" in error_name or "permission" in error_name
281
+ else "DB_UNREACHABLE",
282
+ "Managed LangGraph PostgreSQL checkpointer initialization failed",
283
+ )
284
+ finally:
285
+ if pool is not None and self._managed_checkpoint_pool is None:
286
+ try:
287
+ await pool.close()
288
+ except Exception: # noqa: BLE001
289
+ pass
290
+ self._managed_checkpoint_prepared = True
291
+
292
+ async def close(self) -> None:
293
+ pool = self._managed_checkpoint_pool
294
+ self._managed_checkpoint_pool = None
295
+ if pool is not None:
296
+ await pool.close()
297
+ await super().close()
298
+
136
299
  def _get_config(self, session_id: str) -> dict:
137
300
  """获取运行配置"""
138
- return {"configurable": {"thread_id": session_id}}
301
+ config = {"configurable": {"thread_id": session_id}}
302
+ if self._managed_checkpoint_namespace:
303
+ config["configurable"]["checkpoint_ns"] = self._managed_checkpoint_namespace
304
+ return config
139
305
 
140
306
  @staticmethod
141
307
  def _extract_langgraph_checkpoint_ref(payload: Dict[str, Any]) -> dict[str, Any]:
@@ -407,6 +573,12 @@ class LangGraphRunner(BaseRunner):
407
573
  for msg in history:
408
574
  role = msg.get("role")
409
575
  content = msg.get("content", "")
576
+ # Runtime-owned tool/approval records are preserved in the durable
577
+ # transcript, but must not be taught back to LangGraph as plain text.
578
+ if isinstance(content, str) and content.startswith(
579
+ ("[tool_call]", "[tool_result]", "[approval_request]", "[approval_response]")
580
+ ):
581
+ continue
410
582
  if role == "user":
411
583
  messages.append(HumanMessage(content=content))
412
584
  elif role in ("assistant", "model"):
@@ -604,6 +776,7 @@ class LangGraphRunner(BaseRunner):
604
776
  1. 简化格式: {"input": "hello"} - 自动转换为 messages
605
777
  2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
606
778
  """
779
+ await self.prepare_runtime_capabilities()
607
780
  payload = dict(input_data)
608
781
  force_graph_invoke = bool(payload.pop("_ksadk_force_graph_invoke", False))
609
782
  if not force_graph_invoke and hasattr(self._agent, "astream_events"):
@@ -878,428 +1051,16 @@ class LangGraphRunner(BaseRunner):
878
1051
  )
879
1052
  return events
880
1053
 
881
- async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
882
- """流式调用 LangGraph 图"""
883
- payload = dict(input_data)
884
- payload.pop("_ksadk_force_graph_invoke", None)
885
- session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
886
- history = payload.pop("history", [])
887
- is_resume = payload.pop("resume", False)
888
- is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
889
- resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
890
- resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
891
- resume_value = payload.get("input")
892
- is_gateway_approval_resume = bool(
893
- is_resume and self._is_gateway_approval_semantic_resume(resume_value)
894
- )
895
- if is_gateway_approval_resume:
896
- # See ``invoke``: the graph did not suspend at a native interrupt,
897
- # so use the durable transcript to run the post-tool answer turn.
898
- payload["input"] = self._gateway_approval_follow_up_input()
899
- resume_value = payload["input"]
900
- checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
901
- native_context = self.build_native_context(payload.get("platform_context"))
902
- invoke_payload = dict(payload)
903
- invoke_payload["session_id"] = session_id
904
- if history:
905
- invoke_payload["history"] = history
906
- if is_resume and not is_gateway_approval_resume:
907
- invoke_payload["resume"] = True
908
- if is_checkpoint_resume:
909
- invoke_payload["checkpoint_resume"] = True
910
- invoke_payload["resume_payload_provided"] = resume_payload_provided
911
- invoke_payload["resume_interrupt_id"] = resume_interrupt_id
912
-
913
- config = self._get_config(session_id)
914
- if is_checkpoint_resume:
915
- config = self._apply_checkpoint_resume_config(
916
- config,
917
- session_id=session_id,
918
- checkpoint_ref=checkpoint_ref,
919
- )
920
-
921
- if is_checkpoint_resume:
922
- state = resume_value
923
- elif is_resume and not is_gateway_approval_resume:
924
- # Keep the interrupt value intact for ``Command(resume=...)``;
925
- # prepare-state hooks only shape fresh user turns.
926
- state = resume_value
927
- elif self._has_prepare_state_hook():
928
- state = self._prepare_state_with_hook(
929
- payload,
930
- session_id,
931
- history,
932
- is_resume=is_gateway_approval_resume,
933
- )
934
- else:
935
- state = self._to_state(payload, history)
936
-
937
- accumulated_text = ""
938
- accumulated_reasoning = ""
939
- inline_reasoning_parser = ReasoningMarkupParser()
940
- emitted_non_text_event = False
941
- final_output_text = ""
942
- final_output_usage: dict[str, Any] = {}
943
- final_output_last_usage: dict[str, Any] = {}
944
- model_run_usages: dict[str, dict[str, Any]] = {}
945
- model_run_order: list[str] = []
946
- stream_usage_run_keys: set[str] = set()
947
- latest_stream_usage: dict[str, Any] = {}
948
-
949
- def model_run_key(
950
- event: Mapping[str, Any],
951
- *,
952
- fallback_key: str | None = None,
953
- ) -> str:
954
- raw_run_id = event.get("run_id")
955
- return (
956
- str(raw_run_id)
957
- if raw_run_id
958
- else fallback_key or f"model-event-{len(model_run_order)}"
959
- )
960
-
961
- def record_model_usage(
962
- event: Mapping[str, Any],
963
- usage: dict[str, Any],
964
- *,
965
- fallback_key: str | None = None,
966
- ) -> None:
967
- if not usage:
968
- return
969
- run_key = model_run_key(event, fallback_key=fallback_key)
970
- if run_key not in model_run_usages:
971
- model_run_order.append(run_key)
972
- model_run_usages[run_key] = dict(usage)
973
-
974
- def accumulated_model_usage() -> dict[str, Any]:
975
- if len(model_run_order) == 1:
976
- return dict(model_run_usages.get(model_run_order[0]) or {})
977
- usage: dict[str, Any] = {}
978
- for run_key in model_run_order:
979
- usage = accumulate_usage(usage, model_run_usages.get(run_key) or {})
980
- return usage
981
-
982
- def latest_model_usage() -> dict[str, Any]:
983
- for run_key in reversed(model_run_order):
984
- usage = model_run_usages.get(run_key)
985
- if usage:
986
- return dict(usage)
987
- return {}
988
-
989
- if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
990
- try:
991
- async for chunk in self._stream_checkpoint_resume_updates(
992
- stream_input=self._checkpoint_resume_input(
993
- state,
994
- payload_provided=resume_payload_provided,
995
- interrupt_id=resume_interrupt_id,
996
- ),
997
- config=config,
998
- context=native_context,
999
- ):
1000
- yield chunk
1001
- return
1002
- except Exception as e:
1003
- yield {
1004
- "type": "error",
1005
- "message": str(e) or "LangGraph checkpoint resume failed",
1006
- "checkpoint_id": str(checkpoint_ref.get("checkpoint_id") or ""),
1007
- "exception_type": type(e).__name__,
1008
- }
1009
- return
1010
-
1011
- if not hasattr(self._agent, "astream_events"):
1012
- result = await self.invoke(invoke_payload)
1013
- final_chunk = {"output": result.get("output", ""), "type": "final"}
1014
- usage = self._extract_usage(result)
1015
- if usage:
1016
- final_chunk["usage"] = usage
1017
- last_usage = self._extract_last_usage(result)
1018
- if last_usage:
1019
- final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1020
- yield final_chunk
1021
- return
1022
-
1023
- try:
1024
- stream_input = (
1025
- self._checkpoint_resume_input(
1026
- state,
1027
- payload_provided=resume_payload_provided,
1028
- interrupt_id=resume_interrupt_id,
1029
- )
1030
- if is_checkpoint_resume
1031
- else (
1032
- Command(resume=state) if is_resume and not is_gateway_approval_resume else state
1033
- )
1034
- )
1035
- # stream_mode 含 "custom" 才会产生 on_custom_stream 事件(custom writer);
1036
- # 保留默认 "values" 以兼容既有 on_chain_end/graph_update 消费。
1037
- stream_kwargs = {"version": "v2", "config": config}
1038
- if self._callable_accepts_keyword(self._agent.astream_events, "stream_mode"):
1039
- stream_kwargs["stream_mode"] = ["values", "custom"]
1040
- if native_context and self._callable_accepts_keyword(
1041
- self._agent.astream_events, "context"
1042
- ):
1043
- stream_kwargs["context"] = native_context
1044
- async for event in self._agent.astream_events(stream_input, **stream_kwargs):
1045
- event_kind = event.get("event", "")
1046
-
1047
- if event_kind == "on_chat_model_stream":
1048
- chunk = event.get("data", {}).get("chunk")
1049
- if not chunk:
1050
- continue
1051
- chunk_usage = self._extract_usage(chunk)
1052
- if chunk_usage:
1053
- # Some LangChain providers attach cumulative usage to
1054
- # every stream chunk, and LangChain may then sum those
1055
- # cumulative snapshots into an inflated
1056
- # on_chat_model_end usage. For a concrete model run,
1057
- # keep the latest stream snapshot and ignore the later
1058
- # end usage for that same run_id.
1059
- latest_stream_usage = dict(chunk_usage)
1060
- if event.get("run_id"):
1061
- run_key = model_run_key(event)
1062
- stream_usage_run_keys.add(run_key)
1063
- record_model_usage(event, latest_stream_usage)
1064
-
1065
- # 推理内容
1066
- reasoning = getattr(chunk, "reasoning_content", None)
1067
- if not reasoning and hasattr(chunk, "additional_kwargs"):
1068
- reasoning = chunk.additional_kwargs.get("reasoning_content")
1069
-
1070
- if reasoning:
1071
- accumulated_reasoning += reasoning
1072
- yield {"delta": reasoning, "type": "thinking"}
1073
-
1074
- # 常规内容
1075
- if hasattr(chunk, "content") and chunk.content:
1076
- content = self._filter_tool_tags(chunk.content)
1077
- if isinstance(content, str):
1078
- if accumulated_reasoning and content.startswith(accumulated_reasoning):
1079
- content = content[len(accumulated_reasoning) :]
1080
- elif reasoning and content.startswith(reasoning):
1081
- content = content[len(reasoning) :]
1082
- if content:
1083
- for part in inline_reasoning_parser.feed(content):
1084
- if not part.text or not part.text.strip():
1085
- continue
1086
- if part.kind == "thinking":
1087
- accumulated_reasoning += part.text
1088
- yield {"delta": part.text, "type": "thinking"}
1089
- else:
1090
- accumulated_text += part.text
1091
- yield {"delta": part.text, "type": "text"}
1092
-
1093
- elif event_kind == "on_chat_model_end":
1094
- data = event.get("data") or {}
1095
- output = data.get("output") if isinstance(data, Mapping) else None
1096
- usage = self._extract_usage(output) or self._extract_usage(data)
1097
- last_usage = self._extract_last_usage(output) or self._extract_last_usage(data)
1098
- run_key = model_run_key(event)
1099
- if run_key not in stream_usage_run_keys:
1100
- record_model_usage(event, last_usage or usage)
1101
-
1102
- elif event_kind == "on_chain_stream":
1103
- # node 内 get_stream_writer() 写入的自定义数据,经 stream_mode 含
1104
- # "custom" 时,astream_events 包成 on_chain_stream,chunk 为
1105
- # (mode, value) tuple:("custom", value) 是 writer 透传内容,
1106
- # ("values", state) 是 state 快照(忽略,终态走 on_chain_end)。
1107
- # 编排方常用 custom writer 把"调远端 agent/子图"的流式增量透传出来。
1108
- chunk = event.get("data", {}).get("chunk")
1109
- if not (isinstance(chunk, tuple) and len(chunk) == 2 and chunk[0] == "custom"):
1110
- continue
1111
- data = chunk[1]
1112
- if isinstance(data, str):
1113
- accumulated_text += data
1114
- yield {"delta": data, "type": "text"}
1115
- continue
1116
- if isinstance(data, Mapping):
1117
- custom_type = str(data.get("type") or "text")
1118
- if custom_type in ("tool_call", "tool_result"):
1119
- # 结构化工具事件:透传完整 payload(tool_name/tool_args/
1120
- # tool_output 等),不计入正文,供 UI 渲染工具卡片。
1121
- out = {"type": custom_type}
1122
- out.update({k: v for k, v in data.items() if k != "type"})
1123
- yield out
1124
- continue
1125
- custom_delta = ""
1126
- for key in ("delta", "text", "content", "output", "data"):
1127
- value = data.get(key)
1128
- if isinstance(value, str) and value:
1129
- custom_delta = value
1130
- break
1131
- if not custom_delta:
1132
- continue
1133
- replace = bool(data.get("replace"))
1134
- if custom_type == "thinking":
1135
- accumulated_reasoning = (
1136
- custom_delta if replace else accumulated_reasoning + custom_delta
1137
- )
1138
- else:
1139
- accumulated_text = (
1140
- custom_delta if replace else accumulated_text + custom_delta
1141
- )
1142
- custom_event: dict[str, Any] = {
1143
- "delta": custom_delta,
1144
- "type": custom_type,
1145
- }
1146
- if replace:
1147
- custom_event["replace"] = True
1148
- yield custom_event
1149
- continue
1150
- if data is not None:
1151
- accumulated_text += str(data)
1152
- yield {"delta": str(data), "type": "text"}
1153
-
1154
- elif event_kind == "on_tool_start":
1155
- emitted_non_text_event = True
1156
- yield {
1157
- "type": "tool_call",
1158
- "tool_name": event.get("name", "unknown"),
1159
- "tool_args": event.get("data", {}).get("input", {}),
1160
- "run_id": event.get("run_id"),
1161
- }
1162
-
1163
- elif event_kind == "on_tool_end":
1164
- emitted_non_text_event = True
1165
- tool_output = event.get("data", {}).get("output", "")
1166
- # LangGraph returns a ToolMessage here for normal tools.
1167
- # Preserve its content instead of serializing the repr,
1168
- # otherwise structured output such as A2UI envelopes becomes
1169
- # unparsable. Keep the callback run_id below: it is paired
1170
- # with the preceding ``on_tool_start`` event on this stream.
1171
- normalized_output = getattr(tool_output, "content", tool_output)
1172
- if isinstance(tool_output, Mapping) and "content" in tool_output:
1173
- normalized_output = tool_output["content"]
1174
- yield {
1175
- "type": "tool_result",
1176
- "tool_name": event.get("name", "unknown"),
1177
- "tool_args": event.get("data", {}).get("input", {}),
1178
- "tool_output": normalized_output,
1179
- "run_id": event.get("run_id"),
1180
- }
1181
-
1182
- elif event_kind == "on_chain_end":
1183
- output = event.get("data", {}).get("output", {})
1184
- if isinstance(output, dict) and "__interrupt__" in output:
1185
- emitted_non_text_event = True
1186
- yield {
1187
- "type": "interrupt",
1188
- "interrupt_info": output["__interrupt__"],
1189
- "session_id": session_id,
1190
- }
1191
- return
1192
- extracted_output = self._extract_output(output)
1193
- if extracted_output:
1194
- final_output_text = strip_reasoning_markup(str(extracted_output))
1195
- final_output_usage = self._extract_usage(output)
1196
- final_output_last_usage = self._extract_last_usage(output)
1197
-
1198
- except Exception as e:
1199
- if "Interrupt" in type(e).__name__:
1200
- yield {
1201
- "type": "interrupt",
1202
- "interrupt_info": self._get_interrupt_info(self._agent.get_state(config)),
1203
- "session_id": session_id,
1204
- }
1205
- return
1206
- raise
1207
-
1208
- # goal-18(ksadk-web 人机交互):图因审批门(HITL)在流式中静默暂停时,
1209
- # 这里把审批详情(action_requests)作为 approval 事件冒出,供 UI 渲染审批卡。
1210
- # 此前流式路径只在 checkpoint 标 resumable,UI 拿不到"该批哪个工具/什么参数/允许哪些决定"。
1211
- # 注:get_state 在部分 agent 上是 async,统一按 awaitable 处理;取不到则跳过,不破坏事件流。
1212
- pending_approval = None
1213
- try:
1214
- _get_state = getattr(self._agent, "aget_state", None) or getattr(
1215
- self._agent, "get_state", None
1216
- )
1217
- if _get_state is not None:
1218
- _maybe_state = _get_state(config)
1219
- if inspect.isawaitable(_maybe_state):
1220
- _maybe_state = await _maybe_state
1221
- pending_approval = self._get_interrupt_info(_maybe_state)
1222
- except Exception:
1223
- pending_approval = None
1224
- if pending_approval:
1225
- yield {
1226
- "type": "approval",
1227
- "interrupt_info": pending_approval,
1228
- "session_id": session_id,
1229
- }
1230
- metadata = await self._latest_checkpoint_metadata(config)
1231
- if metadata:
1232
- yield {"type": "checkpoint", "metadata": metadata}
1233
- return
1234
-
1235
- for part in inline_reasoning_parser.flush():
1236
- if not part.text or not part.text.strip():
1237
- continue
1238
- if part.kind == "thinking":
1239
- accumulated_reasoning += part.text
1240
- yield {"delta": part.text, "type": "thinking"}
1241
- else:
1242
- accumulated_text += part.text
1243
- yield {"delta": part.text, "type": "text"}
1244
-
1245
- if not accumulated_text:
1246
- if final_output_text:
1247
- final_chunk = {"output": final_output_text, "type": "final"}
1248
- usage = accumulated_model_usage() or final_output_usage or latest_stream_usage
1249
- last_usage = (
1250
- latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
1251
- )
1252
- if usage:
1253
- final_chunk["usage"] = usage
1254
- if last_usage:
1255
- final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1256
- yield final_chunk
1257
- elif not emitted_non_text_event:
1258
- result = await self.invoke({**invoke_payload, "_ksadk_force_graph_invoke": True})
1259
- fallback_chunk: dict[str, Any] = {
1260
- "output": result.get("output", ""),
1261
- "type": "final",
1262
- }
1263
- usage = self._extract_usage(result)
1264
- if usage:
1265
- fallback_chunk["usage"] = usage
1266
- last_usage = self._extract_last_usage(result)
1267
- if last_usage:
1268
- fallback_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1269
- yield fallback_chunk
1270
- checkpoint_metadata = result.get("metadata") if isinstance(result, dict) else None
1271
- if isinstance(checkpoint_metadata, dict) and checkpoint_metadata.get("agentengine"):
1272
- yield {"type": "checkpoint", "metadata": checkpoint_metadata}
1273
- return
1274
- else:
1275
- final_chunk = {"output": accumulated_text, "type": "final"}
1276
- state_usage = await self._latest_state_usage(config)
1277
- usage = (
1278
- accumulated_model_usage()
1279
- or state_usage
1280
- or final_output_usage
1281
- or latest_stream_usage
1282
- )
1283
- if usage:
1284
- final_chunk["usage"] = usage
1285
- last_usage = (
1286
- latest_model_usage()
1287
- or state_usage
1288
- or final_output_last_usage
1289
- or latest_stream_usage
1290
- or usage
1291
- )
1292
- final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1293
- yield final_chunk
1294
-
1295
- metadata = await self._latest_checkpoint_metadata(config)
1296
- if metadata:
1297
- yield {"type": "checkpoint", "metadata": metadata}
1298
-
1299
1054
  def _filter_tool_tags(self, content: str) -> str:
1300
- """过滤 <tool_call> 标签"""
1055
+ """过滤完整的 XML tool_call 标签。"""
1301
1056
  if not isinstance(content, str):
1302
1057
  return content
1303
1058
  content = re.sub(r"<tool_call>.*?</tool_call>", "", content, flags=re.DOTALL)
1304
1059
  content = re.sub(r"</?(?:tool_call|arg_key|arg_value)>", "", content)
1305
1060
  return content
1061
+
1062
+ async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
1063
+ """Prepare the managed checkpoint before yielding the first event."""
1064
+ await self.prepare_runtime_capabilities()
1065
+ async for event in super().stream(input_data):
1066
+ yield event