agentengine-sdk-python 0.6.9__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 (384) hide show
  1. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +41 -23
  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 +252 -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 +533 -726
  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 +173 -90
  54. ksadk/cli/cmd_invoke.py +388 -159
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +103 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +488 -228
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +44 -14
  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 +38 -16
  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 +501 -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 +656 -0
  97. ksadk/conversations/model_context.py +22 -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 -5187
  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 +52 -39
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +326 -209
  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 +267 -0
  156. ksadk/memory/adk/short_term_memory.py +33 -27
  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 +1248 -164
  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 -530
  183. ksadk/runners/langgraph_runner.py +490 -95
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +264 -38
  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 -3743
  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-C0meuOdi.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-bj2rZnP5.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-2u1ltDIj.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.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-_N8bJ-EU.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-BMKM66B4.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-D99wf6_X.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-DLMjL01k.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-B67uF2uK.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-S4klkK5W.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.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-DtTmUan-.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.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-BR5s6Pws.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-DYPc5cvh.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.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-DPnuCB8c.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.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-mjB_yYkW.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.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-CwSec0Wa.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-agCVobiF.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-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.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 +86 -40
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +28 -5
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +216 -35
  313. ksadk/sessions/resilience.py +52 -0
  314. ksadk/sessions/resilient.py +388 -0
  315. ksadk/skills/models.py +17 -5
  316. ksadk/skills/package_store.py +5 -2
  317. ksadk/skills/runtime/agent.py +20 -5
  318. ksadk/skills/runtime/artifacts.py +1 -5
  319. ksadk/skills/runtime/backends/e2b.py +10 -4
  320. ksadk/skills/runtime/backends/local.py +15 -3
  321. ksadk/skills/runtime/base.py +1 -2
  322. ksadk/skills/runtime/executor.py +10 -3
  323. ksadk/skills/runtime/factory.py +4 -2
  324. ksadk/skills/runtime/loader.py +1 -2
  325. ksadk/skills/runtime/registry.py +15 -10
  326. ksadk/skills/runtime/request.py +3 -1
  327. ksadk/skills/service_client.py +38 -17
  328. ksadk/skills/tool_defs.py +15 -10
  329. ksadk/terminal_exec_policy.py +3 -4
  330. ksadk/tools/gateway.py +92 -15
  331. ksadk/tools/result_budget.py +12 -4
  332. ksadk/toolsets/__init__.py +136 -36
  333. ksadk/toolsets/platform.py +21 -5
  334. ksadk/toolsets/sandbox.py +82 -16
  335. ksadk/toolsets/skills.py +200 -39
  336. ksadk/toolsets/web.py +123 -26
  337. ksadk/toolsets/workspace.py +264 -114
  338. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  339. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  340. ksadk/tracing/setup.py +132 -53
  341. ksadk/tracing/span_utils.py +5 -2
  342. ksadk/tui/__init__.py +22 -7
  343. ksadk/tui/loop.py +1925 -0
  344. ksadk/tui/stream_render.py +69 -0
  345. ksadk/ui_config.py +5 -3
  346. ksadk/version.py +1 -1
  347. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  348. agentengine_sdk_python-0.6.9.dist-info/RECORD +0 -487
  349. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +0 -9
  350. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +0 -8
  351. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +0 -303
  352. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +0 -1
  353. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
  354. ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
  355. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
  356. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
  357. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
  358. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
  359. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
  360. ksadk/server/static/assets/index-BH7-pC0B.js +0 -165
  361. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  362. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
  363. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
  364. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
  365. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
  366. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
  367. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
  368. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
  369. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
  370. ksadk/tui/app.py +0 -400
  371. ksadk/tui/clipboard.py +0 -100
  372. ksadk/tui/widgets/__init__.py +0 -22
  373. ksadk/tui/widgets/assistant.py +0 -89
  374. ksadk/tui/widgets/base.py +0 -54
  375. ksadk/tui/widgets/chat_input.py +0 -246
  376. ksadk/tui/widgets/history.py +0 -156
  377. ksadk/tui/widgets/system.py +0 -87
  378. ksadk/tui/widgets/thinking.py +0 -110
  379. ksadk/tui/widgets/tool_call.py +0 -161
  380. ksadk/tui/widgets/user.py +0 -37
  381. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  382. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  383. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  384. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from ksadk.conversations.attachments import (
4
+ decode_inline_data,
5
+ resolve_attachment_storage_path,
6
+ resolve_uploads_dir,
7
+ )
3
8
  from ksadk.conversations.context import (
4
9
  CANONICAL_EVENT_TYPES,
5
10
  build_history_from_events,
@@ -16,21 +21,18 @@ from ksadk.conversations.normalize import (
16
21
  attachment_from_part,
17
22
  attachment_prompt_text,
18
23
  compact_attachment_for_session,
19
- decode_inline_data,
20
24
  display_content_from_parts,
21
25
  extract_user_input_from_parts,
22
26
  normalize_kop_message_content,
23
27
  normalize_kop_messages,
24
28
  normalize_parts_content,
25
29
  normalize_responses_input,
26
- resolve_attachment_storage_path,
27
- resolve_uploads_dir,
28
30
  )
29
31
  from ksadk.conversations.runtime import (
30
32
  CompactionPlan,
31
33
  PreparedConversationTurn,
32
- append_conversation_event,
33
34
  append_context_checkpoint_event,
35
+ append_conversation_event,
34
36
  append_reasoning_event,
35
37
  append_run_checkpoint_event,
36
38
  append_run_resume_event,
@@ -43,8 +45,8 @@ from ksadk.conversations.runtime import (
43
45
  ensure_conversation_session,
44
46
  extract_responses_resume_input,
45
47
  invoke_conversation_once,
46
- prime_session_metadata_for_user_turn,
47
48
  preview_auto_compaction,
49
+ prime_session_metadata_for_user_turn,
48
50
  stream_conversation_turn,
49
51
  stream_responses_conversation_turn,
50
52
  )
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import importlib
4
5
  import json
5
6
  import logging
6
7
  import mimetypes
@@ -81,8 +82,7 @@ def _download_hosted_attachment(file_uri: str) -> AttachmentBytes | None:
81
82
  data=content.data,
82
83
  display_name=display_name,
83
84
  mime_type=(
84
- _content_type_without_params(content.content_type)
85
- or guess_mime_type(display_name)
85
+ _content_type_without_params(content.content_type) or guess_mime_type(display_name)
86
86
  ),
87
87
  )
88
88
 
@@ -222,7 +222,9 @@ class AttachmentStorageService:
222
222
  return AttachmentBytes(
223
223
  data=raw,
224
224
  display_name=str(metadata.get("display_name") or local_path.name),
225
- mime_type=str(metadata.get("mime_type") or guess_mime_type(local_path.name)),
225
+ mime_type=str(
226
+ metadata.get("mime_type") or guess_mime_type(local_path.name)
227
+ ),
226
228
  local_path=local_path,
227
229
  )
228
230
 
@@ -272,16 +274,17 @@ class AttachmentStorageService:
272
274
  async def _read_ks3_object(self, *, bucket: str, object_key: str) -> bytes:
273
275
  return await asyncio.to_thread(self._read_ks3_object_sync, bucket, object_key)
274
276
 
275
- def _put_ks3_object_sync(self, bucket: str, object_key: str, data: bytes, mime_type: str) -> None:
277
+ def _put_ks3_object_sync(
278
+ self, bucket: str, object_key: str, data: bytes, mime_type: str
279
+ ) -> None:
276
280
  if not bucket:
277
281
  raise ValueError("KS3 bucket is not available")
278
- from ks3.connection import Connection
279
-
280
282
  ak = os.getenv("KSYUN_ACCESS_KEY") or os.getenv("KS3_ACCESS_KEY")
281
283
  sk = os.getenv("KSYUN_SECRET_KEY") or os.getenv("KS3_SECRET_KEY")
282
284
  if not ak or not sk:
283
285
  raise ValueError("KS3 credentials are not configured")
284
- conn = Connection(ak, sk, host=self._endpoint())
286
+ connection_class = importlib.import_module("ks3.connection").Connection
287
+ conn = connection_class(ak, sk, host=self._endpoint())
285
288
  ks3_bucket = self._ensure_bucket(conn, bucket)
286
289
  key = ks3_bucket.new_key(object_key)
287
290
  key.set_contents_from_string(
@@ -292,17 +295,19 @@ class AttachmentStorageService:
292
295
  def _read_ks3_object_sync(self, bucket: str, object_key: str) -> bytes:
293
296
  if not bucket or not object_key:
294
297
  raise FileNotFoundError(object_key)
295
- from ks3.connection import Connection
296
-
297
298
  ak = os.getenv("KSYUN_ACCESS_KEY") or os.getenv("KS3_ACCESS_KEY")
298
299
  sk = os.getenv("KSYUN_SECRET_KEY") or os.getenv("KS3_SECRET_KEY")
299
300
  if not ak or not sk:
300
301
  raise ValueError("KS3 credentials are not configured")
301
- conn = Connection(ak, sk, host=self._endpoint())
302
+ connection_class = importlib.import_module("ks3.connection").Connection
303
+ conn = connection_class(ak, sk, host=self._endpoint())
302
304
  key = conn.get_bucket(bucket).get_key(object_key)
303
305
  if key is None:
304
306
  raise FileNotFoundError(object_key)
305
- return key.get_contents_as_string()
307
+ content = key.get_contents_as_string()
308
+ if not isinstance(content, bytes):
309
+ raise TypeError("KS3 object content must be bytes")
310
+ return content
306
311
 
307
312
  @staticmethod
308
313
  def _ensure_bucket(conn: Any, bucket_name: str):
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import base64
4
+ import importlib
4
5
  import io
5
- import json
6
6
  import mimetypes
7
7
  import zipfile
8
8
  from pathlib import Path, PurePosixPath
@@ -168,7 +168,9 @@ def resolve_attachment_storage_path(file_uri: str) -> Optional[Path]:
168
168
  return None
169
169
 
170
170
 
171
- def read_attachment_bytes(storage_path: Optional[Path], *, size_limit: Optional[int] = None) -> Optional[bytes]:
171
+ def read_attachment_bytes(
172
+ storage_path: Optional[Path], *, size_limit: Optional[int] = None
173
+ ) -> Optional[bytes]:
172
174
  if storage_path is None or not storage_path.is_file():
173
175
  return None
174
176
 
@@ -272,12 +274,7 @@ def build_attachment_prompt_text(result: Mapping[str, Any]) -> str:
272
274
 
273
275
  size_bytes = result.get("size_bytes")
274
276
  if size_bytes is not None:
275
- return (
276
- "[上传文件: "
277
- f"{display_name}, "
278
- f"mime={mime_type}, "
279
- f"bytes={size_bytes}]"
280
- )
277
+ return "[上传文件: " f"{display_name}, " f"mime={mime_type}, " f"bytes={size_bytes}]"
281
278
 
282
279
  return f"[上传文件引用: {display_name}, mime={mime_type}]"
283
280
 
@@ -353,9 +350,11 @@ def _build_attachment_result_from_raw(
353
350
  if raw is None:
354
351
  result["status"] = "partial" if size_bytes else "failed"
355
352
  result["warnings"] = [
356
- "附件原始内容不可用,当前仅保留元数据摘要。"
357
- if size_bytes
358
- else "附件内容无法读取,请重新上传或检查文件句柄是否仍可访问。"
353
+ (
354
+ "附件原始内容不可用,当前仅保留元数据摘要。"
355
+ if size_bytes
356
+ else "附件内容无法读取,请重新上传或检查文件句柄是否仍可访问。"
357
+ )
359
358
  ]
360
359
  return result
361
360
 
@@ -420,7 +419,9 @@ def _extract_document_attachment(result: dict[str, Any], raw: bytes) -> dict[str
420
419
  if text:
421
420
  return _with_text(result, text=text, extraction_method=extractor[1])
422
421
  result["status"] = "failed"
423
- result["warnings"] = [f"{suffix.lstrip('.').upper()} 文档抽取失败,请检查文件是否损坏或重新导出后重试。"]
422
+ result["warnings"] = [
423
+ f"{suffix.lstrip('.').upper()} 文档抽取失败,请检查文件是否损坏或重新导出后重试。"
424
+ ]
424
425
  result["extraction_method"] = extractor[1]
425
426
  return result
426
427
 
@@ -435,7 +436,9 @@ def _extract_document_attachment(result: dict[str, Any], raw: bytes) -> dict[str
435
436
 
436
437
 
437
438
  def _extract_image_attachment(result: dict[str, Any], raw: bytes) -> dict[str, Any]:
438
- ocr = perform_ocr(raw, str(result.get("mime_type") or ""), str(result.get("display_name") or ""))
439
+ ocr = perform_ocr(
440
+ raw, str(result.get("mime_type") or ""), str(result.get("display_name") or "")
441
+ )
439
442
  result["image"] = {"ocr_engine": ocr.get("engine") or ""}
440
443
  if ocr.get("text"):
441
444
  return _with_text(result, text=ocr["text"], extraction_method="image_ocr")
@@ -524,9 +527,7 @@ def _extract_archive_attachment(result: dict[str, Any], raw: bytes) -> dict[str,
524
527
  }
525
528
  )
526
529
  if child_result.get("text"):
527
- extracted_chunks.append(
528
- f"[{normalized_path}]\n{child_result['text']}"
529
- )
530
+ extracted_chunks.append(f"[{normalized_path}]\n{child_result['text']}")
530
531
 
531
532
  result["archive"] = {
532
533
  "entries": entries,
@@ -578,9 +579,11 @@ def _load_attachment_bytes(attachment: Mapping[str, Any]) -> Optional[bytes]:
578
579
  return None
579
580
  return raw if len(raw) <= _MAX_PROCESS_BYTES else None
580
581
 
581
- raw = read_attachment_uri_bytes(attachment.get("file_uri"), size_limit=_MAX_PROCESS_BYTES)
582
- if raw is not None:
583
- return raw
582
+ stored_raw = read_attachment_uri_bytes(
583
+ attachment.get("file_uri"), size_limit=_MAX_PROCESS_BYTES
584
+ )
585
+ if stored_raw is not None:
586
+ return stored_raw
584
587
  return None
585
588
 
586
589
 
@@ -621,7 +624,11 @@ def _extract_xlsx_text(raw: bytes) -> str:
621
624
  shared_strings = []
622
625
 
623
626
  rows: list[str] = []
624
- for name in sorted(item for item in archive.namelist() if item.startswith("xl/worksheets/") and item.endswith(".xml")):
627
+ for name in sorted(
628
+ item
629
+ for item in archive.namelist()
630
+ if item.startswith("xl/worksheets/") and item.endswith(".xml")
631
+ ):
625
632
  try:
626
633
  root = ET.fromstring(archive.read(name))
627
634
  except Exception:
@@ -648,7 +655,9 @@ def _extract_xlsx_text(raw: bytes) -> str:
648
655
  if isinstance(child.tag, str) and child.tag.endswith("}t")
649
656
  )
650
657
  else:
651
- value_text = next((child.text or "" for child in cell if child.tag.endswith("}v")), "")
658
+ value_text = next(
659
+ (child.text or "" for child in cell if child.tag.endswith("}v")), ""
660
+ )
652
661
  if value_text.strip():
653
662
  values.append(value_text.strip())
654
663
  if values:
@@ -679,17 +688,19 @@ def _extract_xml_text_from_zip(raw: bytes, *, prefixes: tuple[str, ...]) -> str:
679
688
  texts: list[str] = []
680
689
  with archive:
681
690
  for name in sorted(
682
- item for item in archive.namelist() if item.endswith(".xml") and item.startswith(prefixes)
691
+ item
692
+ for item in archive.namelist()
693
+ if item.endswith(".xml") and item.startswith(prefixes)
683
694
  ):
684
695
  try:
685
696
  root = ET.fromstring(archive.read(name))
686
697
  except Exception:
687
698
  continue
688
- fragment = [
689
- node.text.strip()
690
- for node in root.iter()
691
- if isinstance(node.tag, str) and node.tag.endswith("}t") and (node.text or "").strip()
692
- ]
699
+ fragment = []
700
+ for node in root.iter():
701
+ text = node.text or ""
702
+ if isinstance(node.tag, str) and node.tag.endswith("}t") and text.strip():
703
+ fragment.append(text.strip())
693
704
  if fragment:
694
705
  texts.append("\n".join(fragment))
695
706
  return "\n\n".join(texts)
@@ -710,22 +721,18 @@ def _pdf_text_quality_is_poor(text: str) -> bool:
710
721
  normalized = "".join(ch for ch in text if not ch.isspace())
711
722
  if len(normalized) < 20:
712
723
  return True
713
- readable = sum(
714
- 1
715
- for ch in normalized
716
- if ch.isalnum() or ("\u4e00" <= ch <= "\u9fff")
717
- )
724
+ readable = sum(1 for ch in normalized if ch.isalnum() or ("\u4e00" <= ch <= "\u9fff"))
718
725
  return readable / max(len(normalized), 1) < 0.5
719
726
 
720
727
 
721
728
  def _perform_rapidocr(raw: bytes) -> str:
722
729
  try:
723
- from rapidocr_onnxruntime import RapidOCR
730
+ rapidocr_module = importlib.import_module("rapidocr_onnxruntime")
724
731
  except Exception:
725
732
  return ""
726
733
 
727
734
  try:
728
- engine = RapidOCR()
735
+ engine = rapidocr_module.RapidOCR()
729
736
  result, _ = engine(raw)
730
737
  except Exception:
731
738
  return ""
@@ -741,7 +748,8 @@ def _perform_rapidocr(raw: bytes) -> str:
741
748
  def _perform_tesseract_ocr(raw: bytes) -> str:
742
749
  try:
743
750
  from PIL import Image
744
- import pytesseract
751
+
752
+ pytesseract = importlib.import_module("pytesseract")
745
753
  except Exception:
746
754
  return ""
747
755
 
@@ -755,7 +763,7 @@ def _perform_tesseract_ocr(raw: bytes) -> str:
755
763
  text = pytesseract.image_to_string(image, lang=lang)
756
764
  except Exception:
757
765
  continue
758
- if text and text.strip():
766
+ if isinstance(text, str) and text.strip():
759
767
  return text.strip()
760
768
  return ""
761
769
 
@@ -87,9 +87,7 @@ def _bool_env(name: str, default: bool) -> bool:
87
87
  def candidate_tokens(groups: Sequence[Sequence[SessionEvent]]) -> int:
88
88
  """估算 candidate groups 的总 token(用于 pipeline 渐进停止判断)。"""
89
89
  return sum(
90
- estimate_text_tokens(extract_event_text(event))
91
- for group in groups
92
- for event in group
90
+ estimate_text_tokens(extract_event_text(event)) for group in groups for event in group
93
91
  )
94
92
 
95
93
 
@@ -108,12 +106,7 @@ def _tool_signature(event: SessionEvent) -> str | None:
108
106
  return None
109
107
  content = event.content or {}
110
108
  metadata = event.metadata or {}
111
- name = str(
112
- metadata.get("tool_name")
113
- or content.get("name")
114
- or content.get("tool_name")
115
- or ""
116
- )
109
+ name = str(metadata.get("tool_name") or content.get("name") or content.get("tool_name") or "")
117
110
  # 参数优先取 metadata.tool_args(真实 runtime),回退 content.arguments(合成/历史)。
118
111
  arguments = (
119
112
  metadata.get("tool_args")
@@ -264,8 +257,14 @@ def microcompact_cold_groups(
264
257
 
265
258
  # 尾部 N 组保留(它们可能还被模型需要),更早的组视为冷组。
266
259
  cold_rounds = _microcompact_cold_rounds()
267
- cold_groups = list(groups[:-cold_rounds]) if cold_rounds < len(groups) else []
268
- tail_groups = list(groups[-cold_rounds:]) if cold_rounds < len(groups) else list(groups)
260
+ cold_groups = (
261
+ [list(group) for group in groups[:-cold_rounds]] if cold_rounds < len(groups) else []
262
+ )
263
+ tail_groups = (
264
+ [list(group) for group in groups[-cold_rounds:]]
265
+ if cold_rounds < len(groups)
266
+ else [list(group) for group in groups]
267
+ )
269
268
 
270
269
  if not cold_groups:
271
270
  return MicrocompactResult(
@@ -323,7 +322,8 @@ def build_working_set_metadata(
323
322
  ) -> dict[str, Any]:
324
323
  """L5 working set 恢复(保守版):只记 metadata,不读文件内容。
325
324
 
326
- recent_files: 调用方从 workspace_state 取最近 N 个文件的 {path, mtime_ns, size_bytes, read_range}。
325
+ recent_files: 调用方从 workspace_state 取最近 N 个文件的
326
+ {path, mtime_ns, size_bytes, read_range}。
327
327
  active_tools: 调用方从 describe_agentengine_tools 取 enabled 工具名。
328
328
  下一轮 prompt 注入时只提示"这些文件最近读过",不注入内容(避免 token 膨胀)。
329
329
  """
@@ -92,10 +92,12 @@ def build_compaction_prompt_messages(
92
92
  不把 prompt 字符串散落到 runtime 编排里。
93
93
  """
94
94
 
95
- groups_text = "\n\n".join(
96
- _format_group(group, index=index + 1)
97
- for index, group in enumerate(groups_to_compact)
98
- ).strip() or "无可压缩内容"
95
+ groups_text = (
96
+ "\n\n".join(
97
+ _format_group(group, index=index + 1) for index, group in enumerate(groups_to_compact)
98
+ ).strip()
99
+ or "无可压缩内容"
100
+ )
99
101
  user_prompt = _SUMMARY_USER_PROMPT.format(
100
102
  model_metadata=json.dumps(dict(model_metadata or {}), ensure_ascii=False, indent=2),
101
103
  previous_summary=previous_summary.strip() or "无",
@@ -115,7 +117,9 @@ def extract_summary_text(response_text: str) -> str:
115
117
  if not raw:
116
118
  return ""
117
119
 
118
- summary_match = re.search(r"<summary>\s*(.*?)\s*</summary>", raw, flags=re.DOTALL | re.IGNORECASE)
120
+ summary_match = re.search(
121
+ r"<summary>\s*(.*?)\s*</summary>", raw, flags=re.DOTALL | re.IGNORECASE
122
+ )
119
123
  if summary_match:
120
124
  return summary_match.group(1).strip()
121
125
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import re
5
+ from collections.abc import Mapping, Sequence
5
6
  from typing import Any, Dict, Iterable, List
6
7
 
7
8
  from ksadk.sessions.base import SessionEvent
@@ -33,9 +34,7 @@ TRANSCRIPT_EVENT_TYPES = {
33
34
  "context_checkpoint",
34
35
  }
35
36
 
36
- DATA_URL_RE = re.compile(
37
- r"data:(?P<mime>[A-Za-z0-9.+-]+/[A-Za-z0-9.+-]+);base64,[A-Za-z0-9+/=_-]+"
38
- )
37
+ DATA_URL_RE = re.compile(r"data:(?P<mime>[A-Za-z0-9.+-]+/[A-Za-z0-9.+-]+);base64,[A-Za-z0-9+/=_-]+")
39
38
  BASE64_FIELD_RE = re.compile(
40
39
  r"(?P<prefix>['\"](?P<field>file_data|data|bytes|base64)['\"]\s*:\s*['\"])(?P<value>[A-Za-z0-9+/=_-]{512,})(?P<suffix>['\"])",
41
40
  re.IGNORECASE,
@@ -79,7 +78,9 @@ def extract_event_text(event: SessionEvent) -> str:
79
78
  if text:
80
79
  return sanitize_event_text_for_context(text)
81
80
 
82
- return sanitize_event_text_for_context(extract_text_from_event_parts(content.get("parts") or []))
81
+ return sanitize_event_text_for_context(
82
+ extract_text_from_event_parts(content.get("parts") or [])
83
+ )
83
84
 
84
85
 
85
86
  def canonical_event_type(
@@ -109,7 +110,11 @@ def canonical_event_type(
109
110
  return "run_checkpoint"
110
111
  if raw in {"run_resume", "runtime_resume"}:
111
112
  return "run_resume"
112
- if raw in {"assistant", "model"} or role in {"assistant", "model"} or author in {"assistant", "model"}:
113
+ if (
114
+ raw in {"assistant", "model"}
115
+ or role in {"assistant", "model"}
116
+ or author in {"assistant", "model"}
117
+ ):
113
118
  return "assistant_message"
114
119
  return "user_message"
115
120
 
@@ -157,7 +162,9 @@ def build_request_history(messages: Iterable[Dict[str, Any]]) -> List[Dict[str,
157
162
 
158
163
  def compacted_until_seq_id(events: List[SessionEvent]) -> int:
159
164
  """读取最新 checkpoint 覆盖到哪一个 seq_id。"""
160
- checkpoints = [event for event in events if canonical_event_type(event.event_type) == "context_checkpoint"]
165
+ checkpoints = [
166
+ event for event in events if canonical_event_type(event.event_type) == "context_checkpoint"
167
+ ]
161
168
  if not checkpoints:
162
169
  return 0
163
170
  latest = checkpoints[-1]
@@ -301,3 +308,211 @@ def build_history_from_events(events: List[SessionEvent]) -> List[Dict[str, str]
301
308
  if role in {"user", "model"} and content:
302
309
  history.append({"role": role, "content": content})
303
310
  return history
311
+
312
+
313
+ def _responses_json_string(value: Any, *, default: str = "") -> str:
314
+ if value is None:
315
+ return default
316
+ if isinstance(value, str):
317
+ return value or default
318
+ try:
319
+ return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
320
+ except (TypeError, ValueError):
321
+ return str(value)
322
+
323
+
324
+ def _responses_message(role: str, text: Any) -> dict[str, Any] | None:
325
+ normalized_text = sanitize_event_text_for_context(text).strip()
326
+ if not normalized_text:
327
+ return None
328
+ return {
329
+ "role": role,
330
+ "content": [{"type": "input_text", "text": normalized_text}],
331
+ }
332
+
333
+
334
+ def _event_content_function_part(event: SessionEvent, part_name: str) -> Mapping[str, Any] | None:
335
+ content = event.content or {}
336
+ parts = content.get("parts") or []
337
+ for part in parts:
338
+ if not isinstance(part, Mapping):
339
+ continue
340
+ value = part.get(part_name)
341
+ if isinstance(value, Mapping):
342
+ return value
343
+ return None
344
+
345
+
346
+ def _response_tool_call_id(event: SessionEvent) -> str:
347
+ metadata = event.metadata or {}
348
+ for key in ("tool_call_id", "call_id", "run_id"):
349
+ value = metadata.get(key)
350
+ if value:
351
+ return str(value)
352
+ receipt = metadata.get("tool_receipt")
353
+ if isinstance(receipt, Mapping):
354
+ for key in ("tool_call_id", "call_id", "run_id"):
355
+ value = receipt.get(key)
356
+ if value:
357
+ return str(value)
358
+ resume_input = metadata.get("resume_input")
359
+ if isinstance(resume_input, Mapping):
360
+ for key in ("tool_call_id", "call_id", "run_id"):
361
+ value = resume_input.get(key)
362
+ if value:
363
+ return str(value)
364
+ return ""
365
+
366
+
367
+ def _response_tool_call_item(event: SessionEvent) -> dict[str, Any] | None:
368
+ metadata = event.metadata or {}
369
+ content = event.content or {}
370
+ function_call = _event_content_function_part(event, "function_call") or {}
371
+ name = str(
372
+ metadata.get("tool_name")
373
+ or content.get("name")
374
+ or content.get("tool_name")
375
+ or function_call.get("name")
376
+ or ""
377
+ ).strip()
378
+ arguments: Any = metadata.get("tool_args")
379
+ if arguments is None:
380
+ arguments = (
381
+ content.get("arguments")
382
+ or content.get("args")
383
+ or content.get("input")
384
+ or function_call.get("args")
385
+ or function_call.get("arguments")
386
+ or {}
387
+ )
388
+ call_id = _response_tool_call_id(event)
389
+ if not name or not call_id:
390
+ return None
391
+ return {
392
+ "type": "function_call",
393
+ "call_id": call_id,
394
+ "name": name,
395
+ "arguments": _responses_json_string(arguments, default="{}"),
396
+ }
397
+
398
+
399
+ def _response_tool_output_item(event: SessionEvent) -> dict[str, Any] | None:
400
+ metadata = event.metadata or {}
401
+ content = event.content or {}
402
+ function_response = _event_content_function_part(event, "function_response") or {}
403
+ output: Any = metadata.get("tool_output") if "tool_output" in metadata else None
404
+ if output is None:
405
+ resume_input = metadata.get("resume_input")
406
+ if isinstance(resume_input, Mapping) and "output" in resume_input:
407
+ output = resume_input.get("output")
408
+ if output is None:
409
+ output = (
410
+ content.get("output")
411
+ or content.get("result")
412
+ or function_response.get("response")
413
+ or extract_event_text(event)
414
+ )
415
+ call_id = _response_tool_call_id(event)
416
+ if not call_id:
417
+ return None
418
+ return {
419
+ "type": "function_call_output",
420
+ "call_id": call_id,
421
+ "output": _responses_json_string(output),
422
+ }
423
+
424
+
425
+ def project_responses_history(events: List[SessionEvent]) -> List[dict[str, Any]]:
426
+ """Project the compacted transcript into OpenAI Responses input items.
427
+
428
+ The checkpoint summary represents the compacted prefix. Only the retained
429
+ tail is replayed as typed tool items, so an old call_id is never fabricated
430
+ from a text summary. Events without a reliable call id fall back to the
431
+ existing explanatory message representation instead of emitting an invalid
432
+ ``function_call_output`` item.
433
+ """
434
+ projected: List[dict[str, Any]] = []
435
+ projected_call_ids: set[str] = set()
436
+ compacted_until = compacted_until_seq_id(events)
437
+ checkpoint = next(
438
+ (
439
+ event
440
+ for event in reversed(events)
441
+ if canonical_event_type(event.event_type) == "context_checkpoint"
442
+ ),
443
+ None,
444
+ )
445
+ if checkpoint:
446
+ summary_message = _responses_message("assistant", extract_event_text(checkpoint))
447
+ if summary_message:
448
+ projected.append(summary_message)
449
+
450
+ for event in events:
451
+ event_type = canonical_event_type(
452
+ event.event_type,
453
+ author=event.author,
454
+ role=str((event.content or {}).get("role") or ""),
455
+ )
456
+ if event.seq_id <= compacted_until and event_type != "context_checkpoint":
457
+ continue
458
+ if event_type not in TRANSCRIPT_EVENT_TYPES:
459
+ continue
460
+ if event_type in {"context_checkpoint", "compaction_boundary"}:
461
+ continue
462
+
463
+ if event_type == "tool_call":
464
+ item = _response_tool_call_item(event)
465
+ if item:
466
+ projected.append(item)
467
+ projected_call_ids.add(str(item["call_id"]))
468
+ continue
469
+ message = _responses_message("assistant", f"[tool_call] {extract_event_text(event)}")
470
+ elif event_type == "tool_result":
471
+ item = _response_tool_output_item(event)
472
+ if item and str(item["call_id"]) in projected_call_ids:
473
+ projected.append(item)
474
+ continue
475
+ message = _responses_message("user", f"[tool_result] {extract_event_text(event)}")
476
+ elif event_type == "assistant_message":
477
+ message = _responses_message("assistant", extract_event_text(event))
478
+ elif event_type == "approval_request":
479
+ message = _responses_message(
480
+ "assistant", f"[approval_request] {extract_event_text(event)}"
481
+ )
482
+ elif event_type == "approval_response":
483
+ message = _responses_message("user", f"[approval_response] {extract_event_text(event)}")
484
+ elif event_type == "attachment_ref":
485
+ message = _responses_message("user", f"[attachment] {extract_event_text(event)}")
486
+ else:
487
+ message = _responses_message("user", extract_event_text(event))
488
+ if message:
489
+ projected.append(message)
490
+
491
+ return projected
492
+
493
+
494
+ def build_responses_history_from_messages(
495
+ messages: Sequence[Mapping[str, Any]],
496
+ ) -> List[dict[str, Any]]:
497
+ """Normalize request-provided prior messages for Responses history."""
498
+ history: List[dict[str, Any]] = []
499
+ for message in messages or []:
500
+ role = str(message.get("role") or "").strip().lower()
501
+ if role == "model":
502
+ role = "assistant"
503
+ if role not in {"user", "assistant"}:
504
+ continue
505
+
506
+ content = message.get("input_content")
507
+ if not isinstance(content, Sequence) or isinstance(content, (str, bytes, bytearray)):
508
+ content = message.get("content")
509
+ if isinstance(content, Sequence) and not isinstance(content, (str, bytes, bytearray)):
510
+ content_items = [dict(item) for item in content if isinstance(item, Mapping)]
511
+ if content_items:
512
+ history.append({"role": role, "content": content_items})
513
+ continue
514
+
515
+ item = _responses_message(role, message.get("content"))
516
+ if item:
517
+ history.append(item)
518
+ return history