agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.0__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 (371) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +233 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +500 -720
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +168 -89
  54. ksadk/cli/cmd_invoke.py +184 -122
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +98 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +483 -227
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +33 -10
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +34 -14
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +500 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +438 -51
  97. ksadk/conversations/model_context.py +12 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5255
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +51 -38
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +309 -202
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +4 -5
  156. ksadk/memory/adk/short_term_memory.py +12 -21
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +345 -120
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -742
  183. ksadk/runners/langgraph_runner.py +352 -90
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +134 -33
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3885
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +69 -19
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +2 -3
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +143 -25
  313. ksadk/sessions/resilient.py +36 -8
  314. ksadk/skills/models.py +17 -5
  315. ksadk/skills/package_store.py +5 -2
  316. ksadk/skills/runtime/agent.py +20 -5
  317. ksadk/skills/runtime/artifacts.py +1 -5
  318. ksadk/skills/runtime/backends/e2b.py +10 -4
  319. ksadk/skills/runtime/backends/local.py +15 -3
  320. ksadk/skills/runtime/base.py +1 -2
  321. ksadk/skills/runtime/executor.py +10 -3
  322. ksadk/skills/runtime/factory.py +4 -2
  323. ksadk/skills/runtime/loader.py +1 -2
  324. ksadk/skills/runtime/registry.py +15 -10
  325. ksadk/skills/runtime/request.py +3 -1
  326. ksadk/skills/service_client.py +38 -17
  327. ksadk/skills/tool_defs.py +15 -10
  328. ksadk/terminal_exec_policy.py +3 -4
  329. ksadk/tools/gateway.py +92 -15
  330. ksadk/tools/result_budget.py +12 -4
  331. ksadk/toolsets/__init__.py +134 -36
  332. ksadk/toolsets/platform.py +21 -5
  333. ksadk/toolsets/sandbox.py +82 -16
  334. ksadk/toolsets/skills.py +200 -39
  335. ksadk/toolsets/web.py +123 -26
  336. ksadk/toolsets/workspace.py +212 -101
  337. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  338. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  339. ksadk/tracing/setup.py +69 -53
  340. ksadk/tracing/span_utils.py +5 -2
  341. ksadk/tui/loop.py +78 -30
  342. ksadk/tui/stream_render.py +1 -0
  343. ksadk/ui_config.py +5 -3
  344. ksadk/version.py +1 -1
  345. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  346. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  347. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  348. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  349. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  350. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  351. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  352. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  353. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  354. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  355. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  356. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  357. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  358. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  359. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  360. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  361. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  362. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  363. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  364. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  365. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  366. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  367. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  368. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  369. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  370. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  371. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,355 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import asynccontextmanager, nullcontext
4
+ from typing import Any, Mapping, Sequence
5
+
6
+ from ksadk.sessions import SessionEvent
7
+
8
+
9
+ def _extract_deferred_tool_names(output: Any) -> list[str]:
10
+ if not isinstance(output, Mapping):
11
+ return []
12
+ raw_names = output.get("deferred_tool_names")
13
+ if not isinstance(raw_names, Sequence) or isinstance(raw_names, (str, bytes, bytearray)):
14
+ return []
15
+ names: list[str] = []
16
+ seen: set[str] = set()
17
+ for item in raw_names:
18
+ name = str(item or "").strip()
19
+ if not name or name in seen:
20
+ continue
21
+ seen.add(name)
22
+ names.append(name)
23
+ return names
24
+
25
+
26
+ def _latest_deferred_tool_names(events: Sequence[SessionEvent]) -> list[str]:
27
+ for event in reversed(events):
28
+ if event.event_type != "run_status":
29
+ continue
30
+ metadata = event.metadata or {}
31
+ if metadata.get("detail") != "deferred_tools_selected":
32
+ continue
33
+ return _extract_deferred_tool_names(metadata)
34
+ return []
35
+
36
+
37
+ def _normalize_usage_payload(usage: Mapping[str, Any] | None) -> dict[str, Any]:
38
+ if not isinstance(usage, Mapping):
39
+ return {}
40
+ normalized: dict[str, Any] = {}
41
+ for key in (
42
+ "input_tokens",
43
+ "output_tokens",
44
+ "total_tokens",
45
+ "prompt_tokens",
46
+ "completion_tokens",
47
+ ):
48
+ value = usage.get(key)
49
+ if value is None:
50
+ continue
51
+ try:
52
+ normalized[key] = int(value)
53
+ except (TypeError, ValueError):
54
+ continue
55
+ for key in ("input_token_details", "output_token_details"):
56
+ value = usage.get(key)
57
+ if isinstance(value, Mapping):
58
+ normalized[key] = dict(value)
59
+ prompt_details = usage.get("prompt_tokens_details")
60
+ if isinstance(prompt_details, Mapping):
61
+ normalized["prompt_tokens_details"] = dict(prompt_details)
62
+ cached_tokens = prompt_details.get("cached_tokens")
63
+ if cached_tokens is not None:
64
+ try:
65
+ normalized.setdefault("input_token_details", {})["cached"] = int(cached_tokens)
66
+ except (TypeError, ValueError):
67
+ pass
68
+ completion_details = usage.get("completion_tokens_details")
69
+ if isinstance(completion_details, Mapping):
70
+ normalized["completion_tokens_details"] = dict(completion_details)
71
+ reasoning_tokens = completion_details.get("reasoning_tokens")
72
+ if reasoning_tokens is not None:
73
+ try:
74
+ normalized.setdefault("output_token_details", {})["reasoning"] = int(
75
+ reasoning_tokens
76
+ )
77
+ except (TypeError, ValueError):
78
+ pass
79
+ return normalized
80
+
81
+
82
+ def _usage_from_metadata(metadata: Mapping[str, Any] | None) -> dict[str, Any]:
83
+ if not isinstance(metadata, Mapping):
84
+ return {}
85
+ return _normalize_usage_payload(metadata.get("usage"))
86
+
87
+
88
+ def _responses_usage_payload(usage: Mapping[str, Any] | None) -> dict[str, Any] | None:
89
+ normalized = _normalize_usage_payload(usage)
90
+ if not normalized:
91
+ return None
92
+ input_tokens = normalized.get("input_tokens", normalized.get("prompt_tokens", 0))
93
+ output_tokens = normalized.get("output_tokens", normalized.get("completion_tokens", 0))
94
+ total_tokens = normalized.get("total_tokens", input_tokens + output_tokens)
95
+ payload = {
96
+ "input_tokens": input_tokens,
97
+ "output_tokens": output_tokens,
98
+ "total_tokens": total_tokens,
99
+ }
100
+ if isinstance(normalized.get("input_token_details"), Mapping):
101
+ payload["input_token_details"] = dict(normalized["input_token_details"])
102
+ if isinstance(normalized.get("output_token_details"), Mapping):
103
+ payload["output_token_details"] = dict(normalized["output_token_details"])
104
+ return payload
105
+
106
+
107
+ def _chat_usage_payload(usage: Mapping[str, Any] | None) -> dict[str, Any] | None:
108
+ normalized = _normalize_usage_payload(usage)
109
+ if not normalized:
110
+ return None
111
+ prompt_tokens = normalized.get("prompt_tokens", normalized.get("input_tokens", 0))
112
+ completion_tokens = normalized.get("completion_tokens", normalized.get("output_tokens", 0))
113
+ total_tokens = normalized.get("total_tokens", prompt_tokens + completion_tokens)
114
+ payload = {
115
+ "prompt_tokens": prompt_tokens,
116
+ "completion_tokens": completion_tokens,
117
+ "total_tokens": total_tokens,
118
+ }
119
+ prompt_details = normalized.get("prompt_tokens_details")
120
+ if isinstance(prompt_details, Mapping):
121
+ payload["prompt_tokens_details"] = dict(prompt_details)
122
+ input_token_details = normalized.get("input_token_details")
123
+ if isinstance(input_token_details, Mapping) and input_token_details:
124
+ prompt_details = dict(payload.get("prompt_tokens_details") or {})
125
+ cached_tokens = input_token_details.get("cached")
126
+ if cached_tokens is not None:
127
+ try:
128
+ prompt_details["cached_tokens"] = int(cached_tokens)
129
+ except (TypeError, ValueError):
130
+ pass
131
+ if prompt_details:
132
+ payload["prompt_tokens_details"] = prompt_details
133
+ completion_details = normalized.get("completion_tokens_details")
134
+ if isinstance(completion_details, Mapping):
135
+ payload["completion_tokens_details"] = dict(completion_details)
136
+ output_token_details = normalized.get("output_token_details")
137
+ if isinstance(output_token_details, Mapping) and output_token_details:
138
+ completion_details = dict(payload.get("completion_tokens_details") or {})
139
+ reasoning_tokens = output_token_details.get("reasoning")
140
+ if reasoning_tokens is not None:
141
+ try:
142
+ completion_details["reasoning_tokens"] = int(reasoning_tokens)
143
+ except (TypeError, ValueError):
144
+ pass
145
+ if completion_details:
146
+ payload["completion_tokens_details"] = completion_details
147
+ return payload
148
+
149
+
150
+ def _get_conversation_tracer() -> Any | None:
151
+ try:
152
+ from opentelemetry import trace
153
+
154
+ return trace.get_tracer("ksadk.conversations")
155
+ except Exception:
156
+ return None
157
+
158
+
159
+ def _current_span_feedback_metadata() -> dict[str, str]:
160
+ try:
161
+ from opentelemetry import trace
162
+
163
+ span = trace.get_current_span()
164
+ except Exception:
165
+ return {}
166
+ return _span_feedback_metadata(span)
167
+
168
+
169
+ def _span_feedback_metadata(span: Any | None) -> dict[str, str]:
170
+ if span is None:
171
+ return {}
172
+ try:
173
+ context = span.get_span_context()
174
+ except Exception:
175
+ return {}
176
+ if not getattr(context, "is_valid", False):
177
+ return {}
178
+ return {
179
+ "trace_id": format(context.trace_id, "032x"),
180
+ "root_span_id": format(context.span_id, "016x"),
181
+ }
182
+
183
+
184
+ def _get_current_span() -> Any | None:
185
+ try:
186
+ from opentelemetry import trace
187
+
188
+ return trace.get_current_span()
189
+ except Exception:
190
+ return None
191
+
192
+
193
+ def _span_current_context(span: Any | None):
194
+ if span is None:
195
+ return nullcontext()
196
+ try:
197
+ from opentelemetry.trace import use_span
198
+
199
+ return use_span(
200
+ span, end_on_exit=False, record_exception=False, set_status_on_exception=False
201
+ )
202
+ except Exception:
203
+ return nullcontext()
204
+
205
+
206
+ @asynccontextmanager
207
+ async def _conversation_span_scope(name: str, *, manual_end: bool = False):
208
+ tracer = _get_conversation_tracer()
209
+ if tracer is None:
210
+ yield None
211
+ return
212
+ if manual_end:
213
+ span = tracer.start_span(name)
214
+ try:
215
+ yield span
216
+ finally:
217
+ try:
218
+ span.end()
219
+ except Exception:
220
+ pass
221
+ return
222
+ span = tracer.start_span(name)
223
+ try:
224
+ yield span
225
+ finally:
226
+ try:
227
+ span.end()
228
+ except Exception:
229
+ pass
230
+
231
+
232
+ def _set_span_attribute(span: Any | None, key: str, value: Any) -> None:
233
+ if span is None:
234
+ return
235
+ if value is None:
236
+ return
237
+ if isinstance(value, str):
238
+ value = value.strip()
239
+ if not value:
240
+ return
241
+ try:
242
+ span.set_attribute(key, value)
243
+ except Exception:
244
+ return
245
+
246
+
247
+ def _set_conversation_input_attributes(span: Any | None, input_text: str | None) -> None:
248
+ text = " ".join(str(input_text or "").split())
249
+ if not text:
250
+ return
251
+ for key in (
252
+ "langfuse.trace.input",
253
+ "langfuse.observation.input",
254
+ "input.value",
255
+ "gen_ai.prompt",
256
+ ):
257
+ _set_span_attribute(span, key, text)
258
+
259
+
260
+ def _set_conversation_output_attributes(span: Any | None, output_text: str | None) -> None:
261
+ text = " ".join(str(output_text or "").split())
262
+ if not text:
263
+ return
264
+ for key in (
265
+ "langfuse.trace.output",
266
+ "langfuse.observation.output",
267
+ "output.value",
268
+ "gen_ai.completion",
269
+ ):
270
+ _set_span_attribute(span, key, text)
271
+
272
+
273
+ def _set_conversation_usage_attributes(
274
+ span: Any | None,
275
+ usage: Mapping[str, Any] | None,
276
+ ) -> None:
277
+ normalized = _normalize_usage_payload(usage)
278
+ if not normalized:
279
+ return
280
+
281
+ def _usage_int(value: Any) -> int:
282
+ try:
283
+ return int(value or 0)
284
+ except (TypeError, ValueError):
285
+ return 0
286
+
287
+ input_tokens = _usage_int(normalized.get("input_tokens"))
288
+ output_tokens = _usage_int(normalized.get("output_tokens"))
289
+ total_tokens = _usage_int(normalized.get("total_tokens") or (input_tokens + output_tokens))
290
+ input_details = normalized.get("input_token_details")
291
+ output_details = normalized.get("output_token_details")
292
+ cache_read_tokens = 0
293
+ reasoning_tokens = 0
294
+ if isinstance(input_details, Mapping):
295
+ cache_read_tokens = _usage_int(
296
+ input_details.get("cache_read")
297
+ or input_details.get("cached")
298
+ or input_details.get("cached_tokens")
299
+ )
300
+ if isinstance(output_details, Mapping):
301
+ reasoning_tokens = _usage_int(
302
+ output_details.get("reasoning") or output_details.get("reasoning_tokens")
303
+ )
304
+
305
+ attributes = {
306
+ "gen_ai.usage.input_tokens": input_tokens,
307
+ "gen_ai.usage.output_tokens": output_tokens,
308
+ "gen_ai.usage.total_tokens": total_tokens,
309
+ "llm.usage.prompt_tokens": input_tokens,
310
+ "llm.usage.completion_tokens": output_tokens,
311
+ "llm.usage.total_tokens": total_tokens,
312
+ }
313
+ if cache_read_tokens:
314
+ attributes["gen_ai.usage.cache_read.input_tokens"] = cache_read_tokens
315
+ attributes["llm.usage.cache_read.input_tokens"] = cache_read_tokens
316
+ if reasoning_tokens:
317
+ attributes["gen_ai.usage.reasoning.output_tokens"] = reasoning_tokens
318
+ attributes["llm.usage.reasoning_tokens"] = reasoning_tokens
319
+
320
+ for key, value in attributes.items():
321
+ if value:
322
+ _set_span_attribute(span, key, value)
323
+
324
+
325
+ def _set_conversation_span_attributes(
326
+ span: Any,
327
+ *,
328
+ agent_id: str,
329
+ user_id: str,
330
+ session_id: str,
331
+ invocation_id: str,
332
+ runner_name: str,
333
+ model: str | None,
334
+ response_id: str | None = None,
335
+ ) -> None:
336
+ if span is None:
337
+ return
338
+ try:
339
+ span.set_attribute("ksadk.agent_id", agent_id)
340
+ span.set_attribute("ksadk.user_id", user_id)
341
+ span.set_attribute("ksadk.session_id", session_id)
342
+ span.set_attribute("ksadk.invocation_id", invocation_id)
343
+ span.set_attribute("ksadk.runner", runner_name)
344
+ span.set_attribute("langfuse.trace.name", runner_name)
345
+ span.set_attribute("langfuse.session.id", session_id)
346
+ span.set_attribute("session.id", session_id)
347
+ span.set_attribute("langfuse.user.id", user_id)
348
+ span.set_attribute("user.id", user_id)
349
+ if model:
350
+ span.set_attribute("llm.model_name", model)
351
+ span.set_attribute("gen_ai.request.model", model)
352
+ if response_id:
353
+ span.set_attribute("ksadk.response_id", response_id)
354
+ except Exception:
355
+ return
@@ -0,0 +1,272 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import time
5
+ import uuid
6
+ from dataclasses import dataclass, field
7
+ from typing import Any, Mapping, Optional, Sequence
8
+
9
+ from ksadk.conversations.run_kinds import (
10
+ RUN_MODE_FOREGROUND,
11
+ RUN_TRIGGER_NEW_RUN,
12
+ )
13
+ from ksadk.conversations.runtime_observability import (
14
+ _chat_usage_payload,
15
+ _responses_usage_payload,
16
+ _usage_from_metadata,
17
+ )
18
+ from ksadk.sessions import SessionEvent
19
+
20
+
21
+ @dataclass
22
+ class PreparedConversationTurn:
23
+ """一次 turn 编排后的标准输入。
24
+
25
+ 这个对象把“会话归属”“用户最新输入”“投影后的上下文 history”
26
+ 和“附件/parts”等运行时所需信息收拢到一起,避免不同 endpoint
27
+ 各自重新拼装。
28
+ """
29
+
30
+ session_id: str
31
+ invocation_id: str
32
+ user_input: str
33
+ user_display_input: str
34
+ history: list[dict[str, str]]
35
+ input_content: list[dict[str, Any]]
36
+ input_messages: list[dict[str, Any]]
37
+ user_parts: list[dict[str, Any]]
38
+ attachments: list[dict[str, Any]]
39
+ attachment_results: list[dict[str, Any]]
40
+ current_attachments: list[dict[str, Any]]
41
+ current_attachment_results: list[dict[str, Any]]
42
+ has_current_files: bool
43
+ model_metadata: dict[str, Any] = field(default_factory=dict)
44
+ model_options: dict[str, Any] = field(default_factory=dict)
45
+ instructions: str = ""
46
+ request_metadata: dict[str, Any] = field(default_factory=dict)
47
+ compaction_triggered: bool = False
48
+ compaction_trigger: str | None = None
49
+ compacted_until_seq_id: int | None = None
50
+ resume_input: dict[str, Any] | None = None
51
+ # 双维度 run 标识:run_mode=怎么跑(background/foreground),
52
+ # run_trigger=怎么开始(new_run/checkpoint_resume/approval_resume)
53
+ run_mode: str = RUN_MODE_FOREGROUND
54
+ run_trigger: str = RUN_TRIGGER_NEW_RUN
55
+ request_history: list[dict[str, str]] = field(default_factory=list)
56
+ request_responses_history: list[dict[str, Any]] = field(default_factory=list)
57
+ responses_history: list[dict[str, Any]] = field(default_factory=list)
58
+
59
+
60
+ @dataclass
61
+ class CompactionPlan:
62
+ """一次 compaction 规划结果。
63
+
64
+ 预览阶段和真正落 checkpoint 阶段都复用这份规划,避免 `/run_sse`
65
+ 与 conversation runtime 各自写一套“是否需要压缩”的条件判断。
66
+ """
67
+
68
+ should_compact: bool
69
+ groups_to_compact: list[list[SessionEvent]]
70
+ total_chars: int
71
+ total_estimated_tokens: int
72
+ group_count: int
73
+ tail_groups: int
74
+ auto_compact_threshold_tokens: int | None = None
75
+ auto_compact_threshold_percentage: int | None = None
76
+ compacted_until_seq_id: int | None = None
77
+ pinned_group_indexes: list[int] = field(default_factory=list)
78
+ pinned_state: dict[str, Any] = field(default_factory=dict)
79
+
80
+
81
+ def build_responses_payload(
82
+ *,
83
+ output_text: str,
84
+ model: Optional[str],
85
+ session_id: str,
86
+ response_id: str | None = None,
87
+ created_at: int | None = None,
88
+ status: str = "completed",
89
+ metadata: Mapping[str, Any] | None = None,
90
+ usage: Mapping[str, Any] | None = None,
91
+ incomplete_details: Mapping[str, Any] | None = None,
92
+ error: Mapping[str, Any] | None = None,
93
+ output_items: Sequence[Mapping[str, Any]] | None = None,
94
+ ) -> dict[str, Any]:
95
+ response_id = response_id or f"resp_{uuid.uuid4().hex}"
96
+ created_at = created_at or int(time.time())
97
+ message_id = f"msg_{uuid.uuid4().hex[:12]}"
98
+ output_item_status = "completed" if status == "completed" else status
99
+ message_item = {
100
+ "id": message_id,
101
+ "type": "message",
102
+ "status": output_item_status,
103
+ "role": "assistant",
104
+ "content": [{"type": "output_text", "text": output_text}],
105
+ }
106
+ normalized_output_items = [
107
+ dict(item) for item in list(output_items or []) if isinstance(item, Mapping)
108
+ ]
109
+ if normalized_output_items:
110
+ output = normalized_output_items
111
+ if not any(str(item.get("type") or "") == "message" for item in output):
112
+ output = [message_item, *output]
113
+ else:
114
+ output = [message_item]
115
+ usage_payload = _responses_usage_payload(usage or _usage_from_metadata(metadata))
116
+ return {
117
+ "id": response_id,
118
+ "object": "response",
119
+ "created_at": created_at,
120
+ "status": status,
121
+ "error": dict(error) if isinstance(error, Mapping) else None,
122
+ "incomplete_details": (
123
+ dict(incomplete_details) if isinstance(incomplete_details, Mapping) else None
124
+ ),
125
+ "instructions": None,
126
+ "metadata": dict(metadata or {}),
127
+ "model": model or "agent",
128
+ "parallel_tool_calls": True,
129
+ "temperature": None,
130
+ "top_p": None,
131
+ "tools": [],
132
+ "output": output,
133
+ "output_text": output_text,
134
+ "usage": usage_payload,
135
+ "session_id": session_id,
136
+ }
137
+
138
+
139
+ def extract_responses_resume_input(input_payload: Any) -> dict[str, Any] | None:
140
+ """Extract OpenAI Responses approval resume input without exposing runner details."""
141
+ if isinstance(input_payload, Mapping):
142
+ candidates = [input_payload]
143
+ elif isinstance(input_payload, Sequence) and not isinstance(
144
+ input_payload, (str, bytes, bytearray)
145
+ ):
146
+ candidates = [item for item in input_payload if isinstance(item, Mapping)]
147
+ else:
148
+ return None
149
+
150
+ for item in candidates:
151
+ item_type = str(item.get("type") or "").strip()
152
+ if item_type == "agentengine.resume_checkpoint":
153
+ checkpoint_resume: dict[str, Any] = {"type": "agentengine.resume_checkpoint"}
154
+ for key in (
155
+ "run_id",
156
+ "checkpoint_id",
157
+ "resume_attempt_id",
158
+ "framework",
159
+ "framework_ref",
160
+ ):
161
+ if key in item:
162
+ checkpoint_resume[key] = item.get(key)
163
+ return checkpoint_resume
164
+
165
+ if item_type == "mcp_approval_response":
166
+ approval_resume: dict[str, Any] = {"type": "mcp_approval_response"}
167
+ if item.get("id"):
168
+ approval_resume["id"] = str(item.get("id"))
169
+ approval_request_id = item.get("approval_request_id")
170
+ if approval_request_id:
171
+ approval_resume["approval_request_id"] = str(approval_request_id)
172
+ if "approve" in item:
173
+ approval_resume["approve"] = item.get("approve")
174
+ elif "approved" in item:
175
+ approval_resume["approve"] = item.get("approved")
176
+ if item.get("reason") is not None:
177
+ approval_resume["reason"] = str(item.get("reason") or "")
178
+ return approval_resume
179
+
180
+ if item_type == "function_call_output":
181
+ call_id = item.get("call_id")
182
+ if not call_id:
183
+ continue
184
+ function_resume: dict[str, Any] = {
185
+ "type": "function_call_output",
186
+ "call_id": str(call_id),
187
+ "output": item.get("output", ""),
188
+ }
189
+ if item.get("id"):
190
+ function_resume["id"] = str(item.get("id"))
191
+ return function_resume
192
+
193
+ if item_type in {"ksadk_resume", "ksadk.approval_response"}:
194
+ ksadk_resume: dict[str, Any] = {"type": "ksadk_resume"}
195
+ interrupt_id = (
196
+ item.get("interrupt_id") or item.get("approval_request_id") or item.get("id")
197
+ )
198
+ if interrupt_id:
199
+ ksadk_resume["interrupt_id"] = str(interrupt_id)
200
+ if "value" in item:
201
+ ksadk_resume["value"] = item.get("value")
202
+ elif "resume" in item:
203
+ ksadk_resume["value"] = item.get("resume")
204
+ else:
205
+ ksadk_resume["value"] = {
206
+ key: value
207
+ for key, value in item.items()
208
+ if key not in {"type", "interrupt_id", "approval_request_id", "id"}
209
+ }
210
+ return ksadk_resume
211
+
212
+ return None
213
+
214
+
215
+ def build_chat_completions_payload(
216
+ *,
217
+ output_text: str,
218
+ model: Optional[str],
219
+ session_id: str,
220
+ metadata: Mapping[str, Any] | None = None,
221
+ ) -> dict[str, Any]:
222
+ usage = _chat_usage_payload(_usage_from_metadata(metadata))
223
+ payload = {
224
+ "id": f"chatcmpl-{uuid.uuid4()}",
225
+ "object": "chat.completion",
226
+ "created": int(time.time()),
227
+ "model": model or "agent",
228
+ "choices": [
229
+ {
230
+ "index": 0,
231
+ "message": {"role": "assistant", "content": output_text},
232
+ "finish_reason": "stop",
233
+ }
234
+ ],
235
+ "usage": usage,
236
+ "session_id": session_id,
237
+ }
238
+ if isinstance(metadata, Mapping) and metadata:
239
+ payload["metadata"] = dict(metadata)
240
+ return payload
241
+
242
+
243
+ def build_compaction_sse_event(
244
+ *,
245
+ phase: str,
246
+ trigger: str,
247
+ compacted_until_seq_id: int | None = None,
248
+ total_chars: int | None = None,
249
+ total_estimated_tokens: int | None = None,
250
+ group_count: int | None = None,
251
+ threshold_percentage: int | None = None,
252
+ ) -> str:
253
+ """统一生成 compaction 相关 SSE,方便不同入口保持同一语义。"""
254
+
255
+ payload: dict[str, Any] = {
256
+ "phase": phase,
257
+ "trigger": trigger,
258
+ "timestamp": int(time.time() * 1000),
259
+ }
260
+ if compacted_until_seq_id is not None:
261
+ payload["compacted_until_seq_id"] = compacted_until_seq_id
262
+ if total_chars is not None:
263
+ payload["total_chars"] = total_chars
264
+ if total_estimated_tokens is not None:
265
+ payload["total_estimated_tokens"] = total_estimated_tokens
266
+ if group_count is not None:
267
+ payload["group_count"] = group_count
268
+ if threshold_percentage is not None:
269
+ payload["threshold_percentage"] = threshold_percentage
270
+ return (
271
+ f"event: response.compaction.{phase}\ndata: {json.dumps(payload, ensure_ascii=False)}\n\n"
272
+ )