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
@@ -11,25 +11,51 @@ from typing import TYPE_CHECKING, Any
11
11
 
12
12
  from fastapi.responses import StreamingResponse
13
13
 
14
+ from ksadk.conversations.normalize import (
15
+ normalize_kop_messages,
16
+ normalize_parts_content,
17
+ normalize_responses_input,
18
+ )
14
19
  from ksadk.conversations.run_kinds import (
15
20
  RUN_MODE_BACKGROUND,
16
21
  RUN_MODE_FOREGROUND,
17
- RUN_TRIGGER_NEW_RUN,
18
22
  trigger_from_resume_input,
19
23
  )
24
+ from ksadk.conversations.runtime_metadata import prime_session_metadata_for_user_turn
25
+ from ksadk.conversations.runtime_payloads import (
26
+ build_chat_completions_payload,
27
+ build_responses_payload,
28
+ extract_responses_resume_input,
29
+ )
30
+ from ksadk.conversations.runtime_persistence import (
31
+ append_run_status_event,
32
+ ensure_conversation_session,
33
+ )
34
+ from ksadk.conversations.runtime_streaming import (
35
+ stream_runtime_conversation_turn,
36
+ stream_runtime_responses_conversation_turn,
37
+ )
20
38
 
21
39
  if TYPE_CHECKING:
22
- from ksadk.conversations.runtime_payloads import PreparedConversationTurn
40
+ pass
23
41
  from ksadk.ids import new_run_id
42
+ from ksadk.kernel.ingress import kernel_route_active
43
+ from ksadk.runtime.conversation_execution import (
44
+ invoke_runtime_conversation_once,
45
+ iter_runtime_conversation_semantic_events,
46
+ )
24
47
  from ksadk.server.api_models import AgentRunRequest
48
+ from ksadk.server.factory import get_runtime_execution
25
49
 
26
50
  from . import dependencies as deps
27
51
  from .checkpoint_resolution import _resolve_checkpoint_resume_input_from_session
28
52
  from .common import (
29
53
  _action_response,
30
- _ensure_runner_loaded,
31
- _prepare_runner_for_model,
32
- _resolve_active_runner,
54
+ )
55
+ from .kernel_ingress import (
56
+ _kernel_error_response,
57
+ kernel_conversation_turn,
58
+ kernel_stream_response,
33
59
  )
34
60
  from .models import (
35
61
  RunAgentActionRequest,
@@ -38,11 +64,11 @@ from .models import (
38
64
  _runtime_agent_id,
39
65
  _split_custom_metadata,
40
66
  )
41
- from .projection import _iter_with_idle_heartbeat
42
67
  from .routers import control_router, run_router
43
68
  from .streaming import (
44
69
  _clear_detached_resume_key,
45
70
  _detached_resume_key_from_input,
71
+ _DetachedSSEStream,
46
72
  _reject_if_detached_resume_active,
47
73
  )
48
74
 
@@ -51,12 +77,15 @@ logger = logging.getLogger(__name__)
51
77
 
52
78
  @run_router.post("/agentengine/api/v1/RunAgent")
53
79
  async def run_agent_action(request: RunAgentActionRequest):
80
+ executor, launch_context = get_runtime_execution()
81
+ if kernel_route_active():
82
+ return await _kernel_run_agent_action(request, launch_context)
54
83
  api_format = (request.ApiFormat or "responses").strip().lower()
55
84
  run_user_id = _clean_optional_string(request.UserId) or "user"
56
85
  account_id = _clean_optional_string(request.AccountId)
57
86
  service = deps.resolve_session_service()
58
87
  resume_input = (
59
- deps.conversation().extract_responses_resume_input(request.ResponsesInput)
88
+ extract_responses_resume_input(request.ResponsesInput)
60
89
  if request.ResponsesInput is not None
61
90
  else None
62
91
  )
@@ -69,9 +98,9 @@ async def run_agent_action(request: RunAgentActionRequest):
69
98
  if resume_input is not None:
70
99
  messages = []
71
100
  elif request.ResponsesInput is not None and api_format == "responses":
72
- messages = deps.conversation().normalize_responses_input(request.ResponsesInput)
101
+ messages = normalize_responses_input(request.ResponsesInput)
73
102
  else:
74
- messages = deps.conversation().normalize_kop_messages(request.Messages)
103
+ messages = normalize_kop_messages(request.Messages)
75
104
  request_metadata: dict[str, Any] = (
76
105
  {"previous_response_id": request.PreviousResponseId} if request.PreviousResponseId else {}
77
106
  )
@@ -81,11 +110,16 @@ async def run_agent_action(request: RunAgentActionRequest):
81
110
  request_metadata["responses_conversation"] = True
82
111
 
83
112
  if request.Background:
113
+ runtime_preparation = (
114
+ None
115
+ if resume_input is not None
116
+ else await executor.prepare_start(launch_context)
117
+ )
84
118
  invocation_id = request.InvocationId or new_run_id(request.SessionId)
85
119
  # 后台 stream 在 detached task 里才被消费(lazy),此时 session 尚未创建。
86
120
  # 先 ensure 出 session,才能立刻写 run_status=in_progress(供 SubscribeRunEvents
87
121
  # 拉到起始态),并把 resolved session_id 回填给 detached stream 的终态写入与 SubscribeUrl。
88
- background_session = await deps.conversation().ensure_conversation_session(
122
+ background_session = await ensure_conversation_session(
89
123
  agent_id=request.AgentId,
90
124
  user_id=run_user_id,
91
125
  session_id=request.SessionId,
@@ -93,14 +127,14 @@ async def run_agent_action(request: RunAgentActionRequest):
93
127
  )
94
128
  resolved_background_session_id = background_session.id
95
129
  if resume_input is None:
96
- await deps.conversation().prime_session_metadata_for_user_turn(
130
+ await prime_session_metadata_for_user_turn(
97
131
  service=service,
98
132
  session=background_session,
99
133
  messages=messages,
100
134
  )
101
- await deps.conversation().append_run_status_event(
135
+ await append_run_status_event(
102
136
  session_id=resolved_background_session_id,
103
- author=_resolve_active_runner().detection_result.name,
137
+ author=_runtime_agent_id(launch_context),
104
138
  status="in_progress",
105
139
  invocation_id=invocation_id,
106
140
  session_service_provider=deps.resolve_session_service,
@@ -109,9 +143,10 @@ async def run_agent_action(request: RunAgentActionRequest):
109
143
  )
110
144
  resume_key = _detached_resume_key_from_input(resolved_background_session_id, resume_input)
111
145
  _reject_if_detached_resume_active(resume_key)
112
- detached = deps.detached_stream_class()(
113
- deps.conversation().stream_responses_conversation_turn(
114
- runner=_resolve_active_runner(),
146
+ detached = _DetachedSSEStream(
147
+ stream_runtime_responses_conversation_turn(
148
+ executor=executor,
149
+ launch_context=launch_context,
115
150
  agent_id=request.AgentId,
116
151
  user_id=run_user_id,
117
152
  messages=messages,
@@ -125,9 +160,9 @@ async def run_agent_action(request: RunAgentActionRequest):
125
160
  resume_input=resume_input,
126
161
  account_id=account_id,
127
162
  invocation_id=invocation_id,
128
- prepare_runner=_prepare_runner_for_model,
129
163
  session_service_provider=deps.resolve_session_service,
130
164
  run_mode=RUN_MODE_BACKGROUND,
165
+ runtime_preparation=runtime_preparation,
131
166
  ),
132
167
  invocation_id=invocation_id,
133
168
  session_id=resolved_background_session_id,
@@ -135,14 +170,15 @@ async def run_agent_action(request: RunAgentActionRequest):
135
170
  run_trigger=trigger_from_resume_input(resume_input),
136
171
  )
137
172
  if invocation_id and resume_key:
173
+ resolved_resume_key = resume_key
138
174
  registry = detached._registry
139
- registry.resume_keys_by_invocation[invocation_id] = resume_key
140
- registry.active_resume_invocation_by_key[resume_key] = invocation_id
141
- detached._task.add_done_callback(
142
- lambda _t, inv=invocation_id, rk=resume_key: _clear_detached_resume_key(
143
- registry, inv, rk
144
- )
145
- )
175
+ registry.resume_keys_by_invocation[invocation_id] = resolved_resume_key
176
+ registry.active_resume_invocation_by_key[resolved_resume_key] = invocation_id
177
+
178
+ def clear_resume_key(_task: Any) -> None:
179
+ _clear_detached_resume_key(registry, invocation_id, resolved_resume_key)
180
+
181
+ detached._task.add_done_callback(clear_resume_key)
146
182
  return _action_response(
147
183
  "RunAgent",
148
184
  {
@@ -159,12 +195,17 @@ async def run_agent_action(request: RunAgentActionRequest):
159
195
  )
160
196
 
161
197
  if request.Stream:
198
+ runtime_preparation = (
199
+ None
200
+ if resume_input is not None
201
+ else await executor.prepare_start(launch_context)
202
+ )
162
203
  if api_format == "chat_completions":
163
- active_runner = _resolve_active_runner()
164
204
  return StreamingResponse(
165
- deps.conversation().stream_conversation_turn(
166
- runner=active_runner,
167
- agent_id=_runtime_agent_id(active_runner),
205
+ stream_runtime_conversation_turn(
206
+ executor=executor,
207
+ launch_context=launch_context,
208
+ agent_id=_runtime_agent_id(launch_context),
168
209
  user_id=run_user_id,
169
210
  messages=messages,
170
211
  session_id=request.SessionId,
@@ -175,17 +216,18 @@ async def run_agent_action(request: RunAgentActionRequest):
175
216
  custom_metadata=custom_metadata,
176
217
  account_id=account_id,
177
218
  invocation_id=request.InvocationId,
178
- prepare_runner=_prepare_runner_for_model,
179
219
  session_service_provider=deps.resolve_session_service,
180
220
  run_mode=RUN_MODE_FOREGROUND,
221
+ runtime_preparation=runtime_preparation,
181
222
  ),
182
223
  media_type="text/event-stream",
183
224
  )
184
225
  resume_key = _detached_resume_key_from_input(request.SessionId, resume_input)
185
226
  _reject_if_detached_resume_active(resume_key)
186
227
  return deps.detached_streaming_response(
187
- deps.conversation().stream_responses_conversation_turn(
188
- runner=_resolve_active_runner(),
228
+ stream_runtime_responses_conversation_turn(
229
+ executor=executor,
230
+ launch_context=launch_context,
189
231
  agent_id=request.AgentId,
190
232
  user_id=run_user_id,
191
233
  messages=messages,
@@ -199,9 +241,9 @@ async def run_agent_action(request: RunAgentActionRequest):
199
241
  resume_input=resume_input,
200
242
  account_id=account_id,
201
243
  invocation_id=request.InvocationId,
202
- prepare_runner=_prepare_runner_for_model,
203
244
  session_service_provider=deps.resolve_session_service,
204
245
  run_mode=RUN_MODE_FOREGROUND,
246
+ runtime_preparation=runtime_preparation,
205
247
  ),
206
248
  invocation_id=request.InvocationId,
207
249
  resume_key=resume_key,
@@ -210,8 +252,9 @@ async def run_agent_action(request: RunAgentActionRequest):
210
252
  )
211
253
 
212
254
  responses_response_id = f"resp_{uuid.uuid4().hex}" if api_format != "chat_completions" else None
213
- resolved_session_id, result = await deps.conversation().invoke_conversation_once(
214
- runner=_resolve_active_runner(),
255
+ resolved_session_id, result = await invoke_runtime_conversation_once(
256
+ executor=executor,
257
+ launch_context=launch_context,
215
258
  agent_id=request.AgentId,
216
259
  user_id=run_user_id,
217
260
  messages=messages,
@@ -225,20 +268,19 @@ async def run_agent_action(request: RunAgentActionRequest):
225
268
  response_id=responses_response_id,
226
269
  account_id=account_id,
227
270
  invocation_id=request.InvocationId,
228
- prepare_runner=_prepare_runner_for_model,
229
271
  session_service_provider=deps.resolve_session_service,
230
272
  run_mode=RUN_MODE_FOREGROUND,
231
273
  )
232
274
  output_text = result["output_text"]
233
275
  if api_format == "chat_completions":
234
- payload = deps.conversation().build_chat_completions_payload(
276
+ payload = build_chat_completions_payload(
235
277
  output_text=output_text,
236
278
  model=request.Model,
237
279
  session_id=resolved_session_id,
238
280
  metadata=result.get("metadata"),
239
281
  )
240
282
  else:
241
- payload = deps.conversation().build_responses_payload(
283
+ payload = build_responses_payload(
242
284
  output_text=output_text,
243
285
  model=request.Model,
244
286
  session_id=resolved_session_id,
@@ -249,472 +291,317 @@ async def run_agent_action(request: RunAgentActionRequest):
249
291
  return _action_response("RunAgent", payload)
250
292
 
251
293
 
294
+ async def _kernel_run_agent_action(request: RunAgentActionRequest, launch_context):
295
+ """kernel 路径(灰度 opt-in):RunAgent -> AgentControlCommand -> receipt。
296
+
297
+ 旧响应 shape 不变;receipt 状态走 RECEIPT_HTTP_STATUS 映射;
298
+ stream 从 SessionEventSubscription 统一 cursor 读取。
299
+ """
300
+ from .kernel_ingress import _kernel_submit
301
+
302
+ run_user_id = _clean_optional_string(request.UserId) or "user"
303
+ session = await ensure_conversation_session(
304
+ agent_id=request.AgentId,
305
+ user_id=run_user_id,
306
+ session_id=request.SessionId,
307
+ session_service_provider=deps.resolve_session_service,
308
+ )
309
+ session_id = session.id
310
+ idempotency_key = (
311
+ _clean_optional_string(request.InvocationId)
312
+ or _clean_optional_string(
313
+ (request.Metadata or {}).get("IdempotencyKey")
314
+ if isinstance(request.Metadata, dict)
315
+ else None
316
+ )
317
+ or new_run_id(session_id)
318
+ )
319
+ messages = (
320
+ normalize_responses_input(request.ResponsesInput)
321
+ if request.ResponsesInput is not None
322
+ and (request.ApiFormat or "responses").strip().lower() == "responses"
323
+ else normalize_kop_messages(request.Messages)
324
+ )
325
+ # RunAgent 的 Model 覆盖走 runtime_options.model:与 agentengine-server 的
326
+ # _kernel_runtime_options 投影同构;worker 侧按部署 defaults/白名单校验。
327
+ runtime_options: dict[str, Any] = {}
328
+ requested_model = _clean_optional_string(request.Model)
329
+ if requested_model:
330
+ runtime_options["model"] = requested_model
331
+ receipt, trusted = await _kernel_submit(
332
+ mapper="map_run_request",
333
+ session_id=session_id,
334
+ idempotency_key=idempotency_key,
335
+ content=messages,
336
+ correlation_ref=request.InvocationId,
337
+ source_kind="system",
338
+ runtime_options=runtime_options or None,
339
+ )
340
+ if receipt.status not in ("accepted", "duplicate"):
341
+ return _kernel_error_response(receipt)
342
+
343
+ def build_payload(output_text: str):
344
+ if (request.ApiFormat or "responses").strip().lower() == "chat_completions":
345
+ return _action_response(
346
+ "RunAgent",
347
+ build_chat_completions_payload(
348
+ output_text=output_text,
349
+ model=request.Model,
350
+ session_id=session_id,
351
+ metadata=None,
352
+ ),
353
+ )
354
+ return _action_response(
355
+ "RunAgent",
356
+ build_responses_payload(
357
+ output_text=output_text,
358
+ model=request.Model,
359
+ session_id=session_id,
360
+ response_id=f"resp_{uuid.uuid4().hex}",
361
+ metadata=None,
362
+ usage=None,
363
+ ),
364
+ )
365
+
366
+ if request.Stream or request.Background:
367
+ return kernel_stream_response(
368
+ receipt=receipt,
369
+ trusted=trusted,
370
+ session_id=session_id,
371
+ )
372
+ return await kernel_conversation_turn(
373
+ receipt=receipt,
374
+ trusted=trusted,
375
+ session_id=session_id,
376
+ build_payload=build_payload,
377
+ )
378
+
379
+
252
380
  # ============================================================
253
381
  # Session Management API (ADK Web Compatible)
254
382
  # ============================================================
255
383
 
256
384
 
257
- @run_router.post("/run_sse")
258
- async def run_sse(request: AgentRunRequest):
259
- """Unified Streaming Endpoint compatible with ADK Web
385
+ def _adk_sse_event(payload: Mapping[str, Any], *, event: str | None = None) -> str:
386
+ prefix = f"event: {event}\n" if event else ""
387
+ return f"{prefix}data: {json.dumps(dict(payload), ensure_ascii=False)}\n\n"
260
388
 
261
- Respects the `streaming` parameter:
262
- - streaming=False: Accumulate full response, send as single event
263
- - streaming=True: Stream tokens as they arrive (real-time)
264
- """
265
- active_runner = _ensure_runner_loaded()
266
- _prepare_runner_for_model(active_runner, request.model)
267
- use_streaming = request.streaming
268
- normalized_message = deps.conversation().normalize_parts_content(
269
- request.newMessage.parts if request.newMessage else []
389
+
390
+ def _adk_usage_metadata(usage: Mapping[str, Any] | None) -> dict[str, int] | None:
391
+ if not isinstance(usage, Mapping):
392
+ return None
393
+ prompt = usage.get("input_tokens", usage.get("prompt_tokens"))
394
+ candidates = usage.get("output_tokens", usage.get("completion_tokens"))
395
+ if not isinstance(prompt, int) or not isinstance(candidates, int):
396
+ return None
397
+ return {
398
+ "promptTokenCount": prompt,
399
+ "candidatesTokenCount": candidates,
400
+ "totalTokenCount": prompt + candidates,
401
+ }
402
+
403
+
404
+ async def _runtime_run_sse(request: AgentRunRequest) -> StreamingResponse:
405
+ executor, launch_context = get_runtime_execution()
406
+ # Validate explicit ownership before StreamingResponse commits HTTP 200.
407
+ # Also resolve an omitted id exactly once so preparation cannot create a
408
+ # second, orphaned session after the response generator starts.
409
+ execution_session = await ensure_conversation_session(
410
+ agent_id=request.appName,
411
+ user_id=request.userId,
412
+ session_id=request.sessionId,
413
+ session_service_provider=deps.resolve_session_service,
270
414
  )
271
- user_message = {
415
+ execution_session_id = execution_session.id
416
+ normalized = normalize_parts_content(request.newMessage.parts)
417
+ message = {
272
418
  "role": "user",
273
- "content": str(normalized_message.get("content") or ""),
274
- "display_content": str(normalized_message.get("display_content") or ""),
275
- "parts": list(normalized_message.get("parts") or []),
276
- "attachments": list(normalized_message.get("attachments") or []),
277
- "attachment_results": list(normalized_message.get("attachment_results") or []),
419
+ "content": str(normalized.get("content") or ""),
420
+ "display_content": str(normalized.get("display_content") or ""),
421
+ "parts": list(normalized.get("parts") or []),
422
+ "attachments": list(normalized.get("attachments") or []),
423
+ "attachment_results": list(normalized.get("attachment_results") or []),
278
424
  }
279
-
280
- model_version = "models/gemini-pro" if "gemini" in request.appName.lower() else "models/unknown"
281
- prepared_non_stream: PreparedConversationTurn | None = None
282
- if request.sessionId:
283
- await deps.conversation().ensure_conversation_session(
284
- agent_id=request.appName,
285
- user_id=request.userId,
286
- session_id=request.sessionId,
287
- session_service_provider=deps.resolve_session_service,
288
- )
289
- if not use_streaming:
290
- prepared_non_stream = await deps.conversation().build_run_input(
291
- agent_id=request.appName,
292
- user_id=request.userId,
293
- session_id=request.sessionId,
294
- messages=[user_message],
295
- state_delta=request.stateDelta or {},
296
- invocation_id=request.invocationId,
297
- session_service_provider=deps.resolve_session_service,
298
- )
299
- await deps.conversation().append_run_status_event(
300
- session_id=prepared_non_stream.session_id,
301
- author=active_runner.detection_result.name,
302
- status="in_progress",
303
- invocation_id=prepared_non_stream.invocation_id,
304
- session_service_provider=deps.resolve_session_service,
305
- run_mode=RUN_MODE_FOREGROUND,
306
- run_trigger=RUN_TRIGGER_NEW_RUN,
307
- )
425
+ invocation_id = request.invocationId or new_run_id(execution_session_id)
426
+ author = _runtime_agent_id(launch_context)
308
427
 
309
428
  async def event_generator():
310
- if not use_streaming:
429
+ if not request.streaming:
311
430
  try:
312
- assert prepared_non_stream is not None
313
- session_id = prepared_non_stream.session_id
314
- user_input = prepared_non_stream.user_input
315
- attachments = prepared_non_stream.attachments
316
- attachment_results = prepared_non_stream.attachment_results
317
- current_attachments = prepared_non_stream.current_attachments
318
- current_attachment_results = prepared_non_stream.current_attachment_results
319
- input_content = prepared_non_stream.input_content
320
- input_messages = prepared_non_stream.input_messages
321
- user_parts = prepared_non_stream.user_parts
322
- history = prepared_non_stream.history
323
- invocation_id = prepared_non_stream.invocation_id
324
- common_metadata = {
325
- "modelVersion": model_version,
326
- "usageMetadata": {
327
- "promptTokenCount": len(user_input),
328
- "candidatesTokenCount": 0,
329
- "totalTokenCount": len(user_input),
330
- },
331
- }
332
- input_data = {
333
- "session_id": session_id,
334
- "input": user_input,
335
- "history": history,
336
- "input_content": list(input_content),
337
- "input_messages": list(input_messages),
338
- "input_parts": list(user_parts),
339
- "attachments": attachments,
340
- "attachment_results": attachment_results,
341
- "current_attachments": current_attachments,
342
- "current_attachment_results": current_attachment_results,
343
- "has_current_files": prepared_non_stream.has_current_files,
344
- "model": request.model,
345
- }
346
- result = await active_runner.invoke(input_data)
347
- final_text = result.get("output", "")
348
- response_event = {
349
- "id": str(uuid.uuid4()),
350
- "author": active_runner.detection_result.name,
351
- "sessionId": session_id,
352
- "invocationId": invocation_id,
353
- "content": {"role": "model", "parts": [{"text": final_text}]},
354
- "actions": {"finishReason": "STOP"},
355
- "modelVersion": common_metadata["modelVersion"],
356
- "usageMetadata": {
357
- "promptTokenCount": len(user_input),
358
- "candidatesTokenCount": len(final_text),
359
- "totalTokenCount": len(user_input) + len(final_text),
360
- },
361
- "timestamp": int(time.time() * 1000),
362
- }
363
- yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
364
- if final_text:
365
- await deps.conversation().append_conversation_event(
366
- session_id=session_id,
367
- author=active_runner.detection_result.name,
368
- role="model",
369
- text=final_text,
370
- invocation_id=invocation_id,
371
- event_type="assistant_message",
372
- session_service_provider=deps.resolve_session_service,
373
- )
374
- await deps.conversation().append_run_status_event(
375
- session_id=session_id,
376
- author=active_runner.detection_result.name,
377
- status="completed",
378
- invocation_id=invocation_id,
379
- session_service_provider=deps.resolve_session_service,
380
- run_mode=RUN_MODE_FOREGROUND,
381
- run_trigger=RUN_TRIGGER_NEW_RUN,
382
- )
383
-
384
- except Exception:
385
- logger.exception("Error in invoke")
386
- public_error = "The agent run failed. See server logs for details."
387
- await deps.conversation().append_run_status_event(
388
- session_id=session_id,
389
- author=active_runner.detection_result.name,
390
- status="failed",
431
+ session_id, result = await invoke_runtime_conversation_once(
432
+ executor=executor,
433
+ launch_context=launch_context,
434
+ agent_id=request.appName,
435
+ user_id=request.userId,
436
+ messages=[message],
437
+ session_id=execution_session_id,
438
+ model=request.model,
439
+ state_delta=request.stateDelta or {},
391
440
  invocation_id=invocation_id,
392
- detail=public_error,
393
441
  session_service_provider=deps.resolve_session_service,
394
442
  run_mode=RUN_MODE_FOREGROUND,
395
- run_trigger=RUN_TRIGGER_NEW_RUN,
396
443
  )
397
- error_event = {
444
+ output_text = str(result.get("output_text") or "")
445
+ payload: dict[str, Any] = {
398
446
  "id": str(uuid.uuid4()),
447
+ "author": author,
399
448
  "sessionId": session_id,
400
449
  "invocationId": invocation_id,
401
- "error": "agent_run_failed",
402
- "errorMessage": public_error,
450
+ "content": {"role": "model", "parts": [{"text": output_text}]},
451
+ "actions": {"finishReason": "STOP"},
452
+ "modelVersion": request.model or "models/unknown",
403
453
  "timestamp": int(time.time() * 1000),
404
454
  }
405
- yield f"data: {json.dumps(error_event, ensure_ascii=False)}\n\n"
406
- else:
407
- try:
408
- compaction_preview = await deps.conversation().preview_auto_compaction(
409
- agent_id=request.appName,
410
- user_id=request.userId,
411
- session_id=request.sessionId,
412
- messages=[user_message],
413
- session_service_provider=deps.resolve_session_service,
455
+ usage_metadata = _adk_usage_metadata(result.get("usage"))
456
+ if usage_metadata is not None:
457
+ payload["usageMetadata"] = usage_metadata
458
+ yield _adk_sse_event(payload)
459
+ except Exception:
460
+ logger.exception("Error in RuntimeAdapter invoke")
461
+ yield _adk_sse_event(
462
+ {
463
+ "id": str(uuid.uuid4()),
464
+ "sessionId": execution_session_id,
465
+ "invocationId": invocation_id,
466
+ "error": "agent_run_failed",
467
+ "errorMessage": "The agent run failed. See server logs for details.",
468
+ "timestamp": int(time.time() * 1000),
469
+ }
414
470
  )
415
- if compaction_preview.should_compact:
416
- yield deps.conversation().build_compaction_sse_event(
417
- phase="start",
418
- trigger="auto",
419
- total_chars=compaction_preview.total_chars,
420
- group_count=compaction_preview.group_count,
421
- )
471
+ return
422
472
 
423
- prepared = await deps.conversation().build_run_input(
424
- agent_id=request.appName,
425
- user_id=request.userId,
426
- session_id=request.sessionId,
427
- messages=[user_message],
428
- state_delta=request.stateDelta or {},
429
- invocation_id=request.invocationId,
430
- session_service_provider=deps.resolve_session_service,
431
- )
432
- if prepared.compaction_triggered:
433
- yield deps.conversation().build_compaction_sse_event(
434
- phase="done",
435
- trigger=str(prepared.compaction_trigger or "auto"),
436
- compacted_until_seq_id=prepared.compacted_until_seq_id,
437
- total_chars=(
438
- compaction_preview.total_chars
439
- if compaction_preview.should_compact
440
- else None
441
- ),
442
- group_count=(
443
- compaction_preview.group_count
444
- if compaction_preview.should_compact
445
- else None
446
- ),
473
+ session_id = execution_session_id
474
+ visible_text = ""
475
+ try:
476
+ async for event in iter_runtime_conversation_semantic_events(
477
+ executor=executor,
478
+ launch_context=launch_context,
479
+ agent_id=request.appName,
480
+ user_id=request.userId,
481
+ messages=[message],
482
+ session_id=execution_session_id,
483
+ model=request.model,
484
+ state_delta=request.stateDelta or {},
485
+ invocation_id=invocation_id,
486
+ session_service_provider=deps.resolve_session_service,
487
+ run_mode=RUN_MODE_FOREGROUND,
488
+ ):
489
+ event_type = event.get("type")
490
+ session_id = str(event.get("session_id") or session_id)
491
+ if event_type == "thinking":
492
+ yield _adk_sse_event(
493
+ {"delta": str(event.get("delta") or "")},
494
+ event="response.reasoning.delta",
447
495
  )
448
-
449
- session_id = prepared.session_id
450
- user_input = prepared.user_input
451
- attachments = prepared.attachments
452
- attachment_results = prepared.attachment_results
453
- current_attachments = prepared.current_attachments
454
- current_attachment_results = prepared.current_attachment_results
455
- input_content = prepared.input_content
456
- input_messages = prepared.input_messages
457
- user_parts = prepared.user_parts
458
- history = prepared.history
459
- invocation_id = prepared.invocation_id
460
- common_metadata = {
461
- "modelVersion": model_version,
462
- "usageMetadata": {
463
- "promptTokenCount": len(user_input),
464
- "candidatesTokenCount": 0,
465
- "totalTokenCount": len(user_input),
466
- },
467
- }
468
- await deps.conversation().append_run_status_event(
469
- session_id=session_id,
470
- author=active_runner.detection_result.name,
471
- status="in_progress",
472
- invocation_id=invocation_id,
473
- session_service_provider=deps.resolve_session_service,
474
- run_mode=RUN_MODE_FOREGROUND,
475
- run_trigger=RUN_TRIGGER_NEW_RUN,
476
- )
477
-
478
- client_visible_text = ""
479
- authoritative_text = ""
480
- responses_output: list[Any] = []
481
- responses_response_id: str | None = None
482
- stream_iter = active_runner.stream(
483
- {
484
- "session_id": session_id,
485
- "input": user_input,
486
- "history": history,
487
- "input_content": list(input_content),
488
- "input_messages": list(input_messages),
489
- "input_parts": list(user_parts),
490
- "attachments": attachments,
491
- "attachment_results": attachment_results,
492
- "current_attachments": current_attachments,
493
- "current_attachment_results": current_attachment_results,
494
- "has_current_files": prepared.has_current_files,
495
- "model": request.model,
496
+ elif event_type == "compaction":
497
+ phase = str(event.get("phase") or "done")
498
+ yield _adk_sse_event(
499
+ {
500
+ key: value
501
+ for key, value in event.items()
502
+ if key not in {"type", "phase"} and value is not None
503
+ },
504
+ event=f"response.compaction.{phase}",
505
+ )
506
+ elif event_type == "text":
507
+ delta = str(event.get("delta") or "")
508
+ replace = bool(event.get("replace"))
509
+ visible_text = delta if replace else visible_text + delta
510
+ text_payload: dict[str, Any] = {
511
+ "id": str(uuid.uuid4()),
512
+ "author": author,
513
+ "sessionId": session_id,
514
+ "invocationId": invocation_id,
515
+ "content": {"role": "model", "parts": [{"text": delta}]},
516
+ "timestamp": int(time.time() * 1000),
496
517
  }
497
- )
498
- async for kind, chunk in _iter_with_idle_heartbeat(stream_iter):
499
- if kind == "heartbeat":
500
- yield ": ping\n\n"
501
- continue
502
- event_id = str(uuid.uuid4())
503
- if chunk.get("type") == "responses_output":
504
- raw_output = chunk.get("output")
505
- responses_output = raw_output if isinstance(raw_output, list) else []
506
- raw_response_id = chunk.get("response_id")
507
- responses_response_id = (
508
- str(raw_response_id) if raw_response_id else responses_response_id
518
+ if replace:
519
+ # A replacement snapshot is authoritative final output,
520
+ # not another partial token. Keep the replacement hint
521
+ # so clients can discard previously rendered deltas.
522
+ text_payload.update(
523
+ {
524
+ "replace": True,
525
+ "actions": {"finishReason": "STOP"},
526
+ "modelVersion": request.model or "models/unknown",
527
+ }
509
528
  )
510
- continue
511
- if chunk.get("type") == "thinking":
512
- delta = str(chunk.get("delta", ""))
513
- if delta:
514
- await deps.conversation().append_reasoning_event(
515
- session_id=session_id,
516
- author=active_runner.detection_result.name,
517
- text=delta,
518
- invocation_id=invocation_id,
519
- session_service_provider=deps.resolve_session_service,
520
- )
521
- yield (
522
- "event: response.reasoning.delta\n"
523
- f"data: {json.dumps({'delta': delta}, ensure_ascii=False)}\n\n"
524
- )
525
- continue
526
- if chunk.get("type") == "text":
527
- delta_text = chunk.get("delta", "")
528
- client_visible_text += delta_text
529
- authoritative_text = client_visible_text
530
- response_event = {
531
- "id": event_id,
532
- "author": chunk.get("node", active_runner.detection_result.name),
533
- "sessionId": session_id,
534
- "invocationId": invocation_id,
535
- "content": {"role": "model", "parts": [{"text": delta_text}]},
536
- "partial": True,
537
- "timestamp": int(time.time() * 1000),
538
- }
539
- yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
540
- continue
541
- if chunk.get("type") == "tool_call":
542
- yield (
543
- "event: response.tool_call\n"
544
- "data: "
545
- + json.dumps(
546
- {
547
- "name": chunk.get("tool_name"),
548
- "args": chunk.get("tool_args", {}),
529
+ else:
530
+ text_payload.update({"partial": True, "replace": False})
531
+ yield _adk_sse_event(
532
+ text_payload
533
+ )
534
+ elif event_type == "tool_call":
535
+ yield _adk_sse_event(
536
+ {"name": event.get("name"), "args": event.get("args") or {}},
537
+ event="response.tool_call",
538
+ )
539
+ elif event_type == "tool_result":
540
+ yield _adk_sse_event(
541
+ {"name": event.get("name"), "output": event.get("output")},
542
+ event="response.tool_result",
543
+ )
544
+ elif event_type == "interrupt":
545
+ yield _adk_sse_event(
546
+ {"interrupt_info": event.get("interrupt_info")},
547
+ event="response.approval_request",
548
+ )
549
+ elif event_type == "completed":
550
+ final_text = str(event.get("output_text") or visible_text)
551
+ if final_text != visible_text:
552
+ yield _adk_sse_event(
553
+ {
554
+ "id": str(uuid.uuid4()),
555
+ "author": author,
556
+ "sessionId": session_id,
557
+ "invocationId": invocation_id,
558
+ "content": {
559
+ "role": "model",
560
+ "parts": [{"text": final_text}],
549
561
  },
550
- ensure_ascii=False,
551
- )
552
- + "\n\n"
562
+ "actions": {"finishReason": "STOP"},
563
+ "modelVersion": request.model or "models/unknown",
564
+ "timestamp": int(time.time() * 1000),
565
+ }
553
566
  )
554
- tool_event = {
555
- "id": event_id,
556
- "author": chunk.get("node", "tool"),
567
+ elif event_type == "error":
568
+ yield _adk_sse_event(
569
+ {
570
+ "id": str(uuid.uuid4()),
557
571
  "sessionId": session_id,
558
572
  "invocationId": invocation_id,
559
- "content": {
560
- "role": "model",
561
- "parts": [
562
- {
563
- "functionCall": {
564
- "name": chunk.get("tool_name", "unknown"),
565
- "args": chunk.get("tool_args", {}),
566
- }
567
- }
568
- ],
569
- },
570
- "actions": {
571
- "finishReason": "STOP",
572
- "stateDelta": {},
573
- },
574
- "modelVersion": common_metadata["modelVersion"],
573
+ "error": "agent_run_failed",
574
+ "errorMessage": str(event.get("message") or "Agent run failed"),
575
575
  "timestamp": int(time.time() * 1000),
576
576
  }
577
- yield f"data: {json.dumps(tool_event, ensure_ascii=False)}\n\n"
578
- await deps.conversation().append_conversation_event(
579
- session_id=session_id,
580
- author=chunk.get("node", "tool"),
581
- role="model",
582
- text="",
583
- invocation_id=invocation_id,
584
- event_type="tool_call",
585
- session_service_provider=deps.resolve_session_service,
586
- metadata={
587
- "tool_name": chunk.get("tool_name", "unknown"),
588
- "tool_args": chunk.get("tool_args", {}),
589
- },
590
- )
591
- continue
592
- if chunk.get("type") == "tool_result":
593
- await deps.conversation().append_conversation_event(
594
- session_id=session_id,
595
- author=active_runner.detection_result.name,
596
- role="user",
597
- text=str(chunk.get("tool_output", "")),
598
- invocation_id=invocation_id,
599
- event_type="tool_result",
600
- session_service_provider=deps.resolve_session_service,
601
- metadata={
602
- "tool_name": chunk.get("tool_name"),
603
- "tool_output": chunk.get("tool_output", {}),
604
- },
605
- )
606
- yield (
607
- "event: response.tool_result\n"
608
- "data: "
609
- + json.dumps(
610
- {
611
- "name": chunk.get("tool_name"),
612
- "output": chunk.get("tool_output", {}),
613
- },
614
- ensure_ascii=False,
615
- )
616
- + "\n\n"
617
- )
618
- continue
619
- if chunk.get("type") == "interrupt":
620
- await deps.conversation().append_conversation_event(
621
- session_id=session_id,
622
- author=active_runner.detection_result.name,
623
- role="model",
624
- text="approval requested",
625
- invocation_id=invocation_id,
626
- event_type="approval_request",
627
- session_service_provider=deps.resolve_session_service,
628
- metadata={"interrupt_info": chunk.get("interrupt_info")},
629
- )
630
- yield (
631
- "event: response.approval_request\n"
632
- "data: "
633
- + json.dumps(
634
- {"interrupt_info": chunk.get("interrupt_info")},
635
- ensure_ascii=False,
636
- )
637
- + "\n\n"
638
- )
639
- continue
640
- if chunk.get("type") == "final":
641
- final_text = chunk.get("output", "")
642
- if not final_text:
643
- continue
644
- authoritative_text = final_text
645
- if final_text != client_visible_text:
646
- final_event = {
647
- "id": event_id,
648
- "author": active_runner.detection_result.name,
649
- "sessionId": session_id,
650
- "invocationId": invocation_id,
651
- "content": {"role": "model", "parts": [{"text": final_text}]},
652
- "actions": {"finishReason": "STOP"},
653
- "modelVersion": common_metadata["modelVersion"],
654
- "usageMetadata": {
655
- "promptTokenCount": len(user_input),
656
- "candidatesTokenCount": len(final_text),
657
- "totalTokenCount": len(user_input) + len(final_text),
658
- },
659
- "timestamp": int(time.time() * 1000),
660
- }
661
- yield f"data: {json.dumps(final_event, ensure_ascii=False)}\n\n"
662
- client_visible_text = final_text
663
-
664
- if authoritative_text:
665
- await deps.conversation().append_conversation_event(
666
- session_id=session_id,
667
- author=active_runner.detection_result.name,
668
- role="model",
669
- text=authoritative_text,
670
- invocation_id=invocation_id,
671
- event_type="assistant_message",
672
- metadata={
673
- **({"responses_output": responses_output} if responses_output else {}),
674
- **(
675
- {"response_id": responses_response_id}
676
- if responses_response_id
677
- else {}
678
- ),
679
- },
680
- session_service_provider=deps.resolve_session_service,
681
577
  )
682
- await deps.conversation().append_run_status_event(
683
- session_id=session_id,
684
- author=active_runner.detection_result.name,
685
- status="completed",
686
- invocation_id=invocation_id,
687
- session_service_provider=deps.resolve_session_service,
688
- run_mode=RUN_MODE_FOREGROUND,
689
- run_trigger=RUN_TRIGGER_NEW_RUN,
690
- )
691
-
692
- except Exception:
693
- logger.exception("Error in stream")
694
- public_error = "The agent run failed. See server logs for details."
695
- await deps.conversation().append_run_status_event(
696
- session_id=session_id,
697
- author=active_runner.detection_result.name,
698
- status="failed",
699
- invocation_id=invocation_id,
700
- detail=public_error,
701
- session_service_provider=deps.resolve_session_service,
702
- run_mode=RUN_MODE_FOREGROUND,
703
- run_trigger=RUN_TRIGGER_NEW_RUN,
704
- )
705
- error_event = {
578
+ except Exception:
579
+ logger.exception("Error in RuntimeAdapter stream")
580
+ yield _adk_sse_event(
581
+ {
706
582
  "id": str(uuid.uuid4()),
707
583
  "sessionId": session_id,
708
584
  "invocationId": invocation_id,
709
585
  "error": "agent_run_failed",
710
- "errorMessage": public_error,
586
+ "errorMessage": "The agent run failed. See server logs for details.",
711
587
  "timestamp": int(time.time() * 1000),
712
588
  }
713
- yield f"data: {json.dumps(error_event, ensure_ascii=False)}\n\n"
589
+ )
714
590
 
715
591
  return StreamingResponse(event_generator(), media_type="text/event-stream")
716
592
 
717
593
 
594
+ @run_router.post("/run_sse")
595
+ async def run_sse(request: AgentRunRequest):
596
+ """Unified Streaming Endpoint compatible with ADK Web
597
+
598
+ Respects the `streaming` parameter:
599
+ - streaming=False: Accumulate full response, send as single event
600
+ - streaming=True: Stream tokens as they arrive (real-time)
601
+ """
602
+ return await _runtime_run_sse(request)
603
+
604
+
718
605
  # ============================================================
719
606
  # Trace / Debug API (ADK Web Compatible)
720
607
  # ============================================================