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
@@ -0,0 +1,691 @@
1
+ """OpenAI Responses API <-> Chat Completions API 纯转换器(无环境依赖).
2
+
3
+ 映射逻辑参照 cc-switch transform_codex_chat.rs / streaming_codex_chat.rs /
4
+ codex_responses_sse.rs,用 Python 重写。本模块只做纯函数/纯状态机转换,
5
+ 不读环境变量、不持有网络/配置 —— 配置由 config.py 注入,便于内化与测试。
6
+ """
7
+
8
+ import json
9
+ import time
10
+
11
+
12
+ class UnsupportedToolsError(ValueError):
13
+ """codex 请求携带了当前不支持转换的工具类型(namespace/MCP/web_search 等)。
14
+
15
+ 应 fail-fast 返回明确错误,而不是静默丢弃工具降级请求;根治靠 namespace 拍平。
16
+ """
17
+
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # 文本 / role / system 处理
21
+ # ---------------------------------------------------------------------------
22
+
23
+
24
+ def extract_text(content):
25
+ """content: str | [{type,text}] -> str"""
26
+ if isinstance(content, str):
27
+ return content
28
+ parts = []
29
+ for p in content or []:
30
+ if isinstance(p, dict) and p.get("text"):
31
+ parts.append(p["text"])
32
+ return "".join(parts)
33
+
34
+
35
+ def _chat_role(role):
36
+ """responses role -> chat role(developer->system; latest_reminder/未知->user)。
37
+
38
+ kimi-k3 等严格网关对 developer role 报 tokenization failed,必须映射。
39
+ """
40
+ if role in ("system", "developer"):
41
+ return "system"
42
+ if role == "assistant":
43
+ return "assistant"
44
+ if role == "tool":
45
+ return "tool"
46
+ return "user"
47
+
48
+
49
+ def collapse_system(msgs):
50
+ """把所有 system 消息合并到头部一条(cc-switch collapse_system_messages_to_head)。"""
51
+ chunks, rest = [], []
52
+ for m in msgs:
53
+ if m.get("role") == "system" and isinstance(m.get("content"), str) and m["content"].strip():
54
+ chunks.append(m["content"])
55
+ else:
56
+ rest.append(m)
57
+ return ([{"role": "system", "content": "\n\n".join(chunks)}] if chunks else []) + rest
58
+
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # 请求: responses -> chat
62
+ # ---------------------------------------------------------------------------
63
+
64
+ # 除 messages/tools 外原样透传的字段(cc-switch EXTRA_CHAT_PASSTHROUGH_FIELDS 子集)
65
+ _PASSTHROUGH = (
66
+ "temperature",
67
+ "top_p",
68
+ "top_logprobs",
69
+ "logprobs",
70
+ "stop",
71
+ "seed",
72
+ "frequency_penalty",
73
+ "presence_penalty",
74
+ "logit_bias",
75
+ "response_format",
76
+ "user",
77
+ "metadata",
78
+ "n",
79
+ "service_tier",
80
+ )
81
+
82
+
83
+ def input_to_messages(inp):
84
+ """responses 的 input(str | [items]) -> chat messages[]。
85
+
86
+ 处理 message / function_call / function_call_output;reasoning 历史不转发。
87
+ """
88
+ if inp is None:
89
+ return []
90
+ if isinstance(inp, str):
91
+ return [{"role": "user", "content": inp}]
92
+ if isinstance(inp, dict):
93
+ inp = [inp]
94
+ messages = []
95
+ pending_tc = []
96
+
97
+ def flush():
98
+ if pending_tc:
99
+ messages.append({"role": "assistant", "content": None, "tool_calls": list(pending_tc)})
100
+ pending_tc.clear()
101
+
102
+ for item in inp:
103
+ if not isinstance(item, dict):
104
+ continue
105
+ t = item.get("type")
106
+ if t in (None, "message"):
107
+ flush()
108
+ messages.append(
109
+ {
110
+ "role": _chat_role(item.get("role", "user")),
111
+ "content": extract_text(item.get("content")),
112
+ }
113
+ )
114
+ elif t == "function_call":
115
+ pending_tc.append(
116
+ {
117
+ "id": item.get("call_id") or f"call_{len(pending_tc)}",
118
+ "type": "function",
119
+ "function": {
120
+ "name": item.get("name"),
121
+ "arguments": item.get("arguments") or "",
122
+ },
123
+ }
124
+ )
125
+ elif t == "function_call_output":
126
+ flush()
127
+ out = item.get("output")
128
+ messages.append(
129
+ {
130
+ "role": "tool",
131
+ "tool_call_id": item.get("call_id") or "",
132
+ "content": out if isinstance(out, str) else json.dumps(out, ensure_ascii=False),
133
+ }
134
+ )
135
+ # reasoning / 其他类型: 跳过
136
+ flush()
137
+ return messages
138
+
139
+
140
+ def convert_tools(tools):
141
+ """codex 扁平 function tool -> chat 嵌套 function tool,保留 strict。
142
+
143
+ 遇非 function 工具(namespace/MCP/web_search 等)fail-fast 抛 UnsupportedToolsError,
144
+ 不静默丢弃降级请求(codex 端禁用配置注入前的安全网)。
145
+ """
146
+ out = []
147
+ unsupported = []
148
+ for t in tools or []:
149
+ if isinstance(t, dict) and t.get("type") == "function":
150
+ fn = {
151
+ "name": t.get("name"),
152
+ "description": t.get("description", ""),
153
+ "parameters": t.get("parameters", {"type": "object", "properties": {}}),
154
+ }
155
+ if "strict" in t:
156
+ fn["strict"] = t["strict"]
157
+ out.append({"type": "function", "function": fn})
158
+ else:
159
+ t = t or {}
160
+ unsupported.append(t.get("type") or t.get("name") or "unknown")
161
+ if unsupported:
162
+ raise UnsupportedToolsError(
163
+ f"不支持转换的 codex 工具类型: {unsupported};"
164
+ "请在 codex 端禁用 multi_agent/web_search(v2 优先),或实现 namespace 拍平"
165
+ )
166
+ return out
167
+
168
+
169
+ def convert_tool_choice(tc):
170
+ """responses tool_choice -> chat tool_choice。
171
+
172
+ named 形式 {"type":"function","name":"x"} 必须嵌套为
173
+ {"type":"function","function":{"name":"x"}}(chat 要求 function.name 嵌套)。
174
+ """
175
+ if tc is None or isinstance(tc, str):
176
+ return tc # auto / none / required
177
+ if isinstance(tc, dict) and tc.get("type") == "function":
178
+ name = tc.get("name") or (tc.get("function") or {}).get("name")
179
+ return {"type": "function", "function": {"name": name}}
180
+ return tc
181
+
182
+
183
+ def _convert_text_format(fmt):
184
+ """responses text.format -> chat response_format(structured output 结构重组)。
185
+
186
+ responses: {"type":"json_schema","name":...,"schema":...,"strict":...}
187
+ chat: {"type":"json_schema","json_schema":{"name":...,"schema":...,"strict":...}}
188
+ """
189
+ if not isinstance(fmt, dict):
190
+ return None
191
+ t = fmt.get("type")
192
+ if t == "json_schema":
193
+ js = {"name": fmt.get("name"), "schema": fmt.get("schema")}
194
+ if "strict" in fmt:
195
+ js["strict"] = fmt["strict"]
196
+ return {"type": "json_schema", "json_schema": js}
197
+ if t in ("json_object", "text"):
198
+ return {"type": t}
199
+ return None
200
+
201
+
202
+ def responses_to_chat(body):
203
+ """responses 请求 -> chat 请求;返回 (chat_req, restore_map)。
204
+
205
+ 先拍平 namespace 工具(namespace.py),再用 convert_tools 转 chat 嵌套 function。
206
+ restore_map 供响应侧把 flat function_call 名字还原回 {name, namespace}。
207
+ """
208
+ from .namespace import flatten_request_namespaces
209
+
210
+ restore_map = flatten_request_namespaces(body)
211
+ out = {"model": body.get("model")}
212
+ msgs = []
213
+ if body.get("instructions"):
214
+ msgs.append({"role": "system", "content": extract_text(body["instructions"])})
215
+ msgs += input_to_messages(body.get("input"))
216
+ out["messages"] = collapse_system(msgs)
217
+ if body.get("max_output_tokens") is not None:
218
+ out["max_tokens"] = body["max_output_tokens"]
219
+ for k in _PASSTHROUGH:
220
+ if k in body:
221
+ out[k] = body[k]
222
+ # codex 特有字段(client.rs:862 固定发送):
223
+ # reasoning effort / structured output(在 text.format,非顶层 response_format) / 显式缓存 key
224
+ reasoning = body.get("reasoning")
225
+ if isinstance(reasoning, dict) and reasoning.get("effort"):
226
+ out["reasoning_effort"] = reasoning["effort"]
227
+ text = body.get("text")
228
+ if isinstance(text, dict):
229
+ rf = _convert_text_format(text.get("format"))
230
+ if rf is not None:
231
+ out["response_format"] = rf
232
+ if text.get("verbosity"):
233
+ out["verbosity"] = text["verbosity"]
234
+ if body.get("prompt_cache_key"):
235
+ out["prompt_cache_key"] = body["prompt_cache_key"]
236
+ tools = convert_tools(body.get("tools"))
237
+ if tools:
238
+ out["tools"] = tools
239
+ if "tool_choice" in body:
240
+ out["tool_choice"] = convert_tool_choice(body["tool_choice"])
241
+ if "parallel_tool_calls" in body:
242
+ out["parallel_tool_calls"] = body["parallel_tool_calls"]
243
+ return out, restore_map
244
+
245
+
246
+ # ---------------------------------------------------------------------------
247
+ # 响应(非流式): chat -> responses
248
+ # ---------------------------------------------------------------------------
249
+
250
+
251
+ def convert_usage(u):
252
+ """chat usage -> responses usage;透传前缀缓存命中数(否则 codex 看到 cached 恒 0)。"""
253
+ u = u or {}
254
+ ct = u.get("completion_tokens_details") or {}
255
+ pt = u.get("prompt_tokens_details") or {} # 平台有时返回 None
256
+ return {
257
+ "input_tokens": u.get("prompt_tokens", 0),
258
+ "input_tokens_details": {"cached_tokens": pt.get("cached_tokens", 0)},
259
+ "output_tokens": u.get("completion_tokens", 0),
260
+ "total_tokens": u.get("total_tokens", 0),
261
+ "output_tokens_details": {"reasoning_tokens": ct.get("reasoning_tokens", 0)},
262
+ }
263
+
264
+
265
+ def _status_and_incomplete(finish_reason):
266
+ """finish_reason -> (status, incomplete_reason)。
267
+
268
+ length/content_filter 都不是 completed。
269
+ """
270
+ if finish_reason == "length":
271
+ return "incomplete", "max_output_tokens"
272
+ if finish_reason == "content_filter":
273
+ return "incomplete", "content_filter"
274
+ return "completed", None
275
+
276
+
277
+ def chat_to_response(chat, rid, restore_map=None):
278
+ choice = (chat.get("choices") or [{}])[0]
279
+ msg = choice.get("message") or {}
280
+ output = []
281
+ rc = msg.get("reasoning_content")
282
+ if rc:
283
+ output.append(
284
+ {
285
+ "id": f"rs_{rid}",
286
+ "type": "reasoning",
287
+ "summary": [{"type": "summary_text", "text": rc}],
288
+ }
289
+ )
290
+ if msg.get("content"):
291
+ output.append(
292
+ {
293
+ "id": f"{rid}_msg",
294
+ "type": "message",
295
+ "status": "completed",
296
+ "role": "assistant",
297
+ "content": [{"type": "output_text", "text": msg["content"], "annotations": []}],
298
+ }
299
+ )
300
+ from .namespace import restore_function_call
301
+
302
+ for i, tc in enumerate(msg.get("tool_calls") or []):
303
+ fn = tc.get("function") or {}
304
+ cid = tc.get("id") or f"call_{i}"
305
+ item = restore_function_call(
306
+ {
307
+ "id": f"fc_{cid}",
308
+ "type": "function_call",
309
+ "status": "completed",
310
+ "call_id": cid,
311
+ "name": fn.get("name"),
312
+ "arguments": fn.get("arguments") or "",
313
+ },
314
+ restore_map or {},
315
+ )
316
+ output.append(item)
317
+ status, incomplete = _status_and_incomplete(choice.get("finish_reason"))
318
+ resp = {
319
+ "id": rid,
320
+ "object": "response",
321
+ "created_at": chat.get("created", int(time.time())),
322
+ "status": status,
323
+ "model": chat.get("model"),
324
+ "output": output,
325
+ "usage": convert_usage(chat.get("usage")),
326
+ }
327
+ if incomplete:
328
+ resp["incomplete_details"] = {"reason": incomplete}
329
+ return resp
330
+
331
+
332
+ # ---------------------------------------------------------------------------
333
+ # 流式: chat SSE -> responses SSE(每个 tool call 独立状态,修复并行截断)
334
+ # ---------------------------------------------------------------------------
335
+
336
+
337
+ class Streamer:
338
+ """把 chat completions 流式 chunk 转成 responses SSE 事件。
339
+
340
+ 关键不变量:每个 tool call(按 index)独立维护 open/output_index/arguments,
341
+ **index 切换时不互相关闭**,统一在 finalize 关闭 —— 修复并行 tool call 参数被截断。
342
+ """
343
+
344
+ def __init__(self, rid, model, restore_map=None):
345
+ self.rid = rid
346
+ self.model = model
347
+ self.restore_map = restore_map or {}
348
+ self.created = int(time.time())
349
+ self._next_oi = 0
350
+ self._reasoning = None # {"oi","id","text"}
351
+ self._message = None # {"oi","id","text"}
352
+ self._tool_calls = {} # index -> {"oi","item_id","call_id","name","args"}
353
+ self._completed = [] # [(oi, item)] 完成顺序
354
+ self.usage = None
355
+ self.finish = None
356
+
357
+ # ---- SSE 信封(格式照 codex_responses_sse.rs) ----
358
+ @staticmethod
359
+ def ev(event, data):
360
+ return f"event: {event}\ndata: {json.dumps(data, ensure_ascii=False)}\n\n"
361
+
362
+ def _resp(self, status, output=None, usage=None):
363
+ r = {
364
+ "id": self.rid,
365
+ "object": "response",
366
+ "created_at": self.created,
367
+ "status": status,
368
+ "model": self.model,
369
+ "output": output or [],
370
+ }
371
+ if usage is not None:
372
+ r["usage"] = usage
373
+ return r
374
+
375
+ def _alloc_oi(self):
376
+ oi = self._next_oi
377
+ self._next_oi += 1
378
+ return oi
379
+
380
+ def start(self):
381
+ return [
382
+ self.ev(
383
+ "response.created",
384
+ {"type": "response.created", "response": self._resp("in_progress")},
385
+ ),
386
+ self.ev(
387
+ "response.in_progress",
388
+ {"type": "response.in_progress", "response": self._resp("in_progress")},
389
+ ),
390
+ ]
391
+
392
+ # ---- reasoning ----
393
+ def _open_reasoning(self):
394
+ oi = self._alloc_oi()
395
+ rid = f"rs_{self.rid}"
396
+ self._reasoning = {"oi": oi, "id": rid, "text": ""}
397
+ return [
398
+ self.ev(
399
+ "response.output_item.added",
400
+ {
401
+ "type": "response.output_item.added",
402
+ "output_index": oi,
403
+ "item": {
404
+ "id": rid,
405
+ "type": "reasoning",
406
+ "status": "in_progress",
407
+ "summary": [],
408
+ },
409
+ },
410
+ ),
411
+ self.ev(
412
+ "response.reasoning_summary_part.added",
413
+ {
414
+ "type": "response.reasoning_summary_part.added",
415
+ "item_id": rid,
416
+ "output_index": oi,
417
+ "summary_index": 0,
418
+ "part": {"type": "summary_text", "text": ""},
419
+ },
420
+ ),
421
+ ]
422
+
423
+ def _close_reasoning(self):
424
+ r = self._reasoning
425
+ self._reasoning = None
426
+ item = {
427
+ "id": r["id"],
428
+ "type": "reasoning",
429
+ "summary": [{"type": "summary_text", "text": r["text"]}],
430
+ }
431
+ self._completed.append((r["oi"], item))
432
+ return [
433
+ self.ev(
434
+ "response.reasoning_summary_text.done",
435
+ {
436
+ "type": "response.reasoning_summary_text.done",
437
+ "item_id": r["id"],
438
+ "output_index": r["oi"],
439
+ "summary_index": 0,
440
+ "text": r["text"],
441
+ },
442
+ ),
443
+ self.ev(
444
+ "response.reasoning_summary_part.done",
445
+ {
446
+ "type": "response.reasoning_summary_part.done",
447
+ "item_id": r["id"],
448
+ "output_index": r["oi"],
449
+ "summary_index": 0,
450
+ "part": {"type": "summary_text", "text": r["text"]},
451
+ },
452
+ ),
453
+ self.ev(
454
+ "response.output_item.done",
455
+ {"type": "response.output_item.done", "output_index": r["oi"], "item": item},
456
+ ),
457
+ ]
458
+
459
+ # ---- message ----
460
+ def _open_message(self):
461
+ oi = self._alloc_oi()
462
+ mid = f"{self.rid}_msg"
463
+ self._message = {"oi": oi, "id": mid, "text": ""}
464
+ return [
465
+ self.ev(
466
+ "response.output_item.added",
467
+ {
468
+ "type": "response.output_item.added",
469
+ "output_index": oi,
470
+ "item": {
471
+ "id": mid,
472
+ "type": "message",
473
+ "status": "in_progress",
474
+ "role": "assistant",
475
+ "content": [],
476
+ },
477
+ },
478
+ ),
479
+ self.ev(
480
+ "response.content_part.added",
481
+ {
482
+ "type": "response.content_part.added",
483
+ "item_id": mid,
484
+ "output_index": oi,
485
+ "content_index": 0,
486
+ "part": {"type": "output_text", "text": "", "annotations": []},
487
+ },
488
+ ),
489
+ ]
490
+
491
+ def _close_message(self):
492
+ m = self._message
493
+ self._message = None
494
+ item = {
495
+ "id": m["id"],
496
+ "type": "message",
497
+ "status": "completed",
498
+ "role": "assistant",
499
+ "content": [{"type": "output_text", "text": m["text"], "annotations": []}],
500
+ }
501
+ self._completed.append((m["oi"], item))
502
+ return [
503
+ self.ev(
504
+ "response.output_text.done",
505
+ {
506
+ "type": "response.output_text.done",
507
+ "item_id": m["id"],
508
+ "output_index": m["oi"],
509
+ "content_index": 0,
510
+ "text": m["text"],
511
+ },
512
+ ),
513
+ self.ev(
514
+ "response.content_part.done",
515
+ {
516
+ "type": "response.content_part.done",
517
+ "item_id": m["id"],
518
+ "output_index": m["oi"],
519
+ "content_index": 0,
520
+ "part": {"type": "output_text", "text": m["text"], "annotations": []},
521
+ },
522
+ ),
523
+ self.ev(
524
+ "response.output_item.done",
525
+ {"type": "response.output_item.done", "output_index": m["oi"], "item": item},
526
+ ),
527
+ ]
528
+
529
+ # ---- function_call(每个 index 独立) ----
530
+ def _open_tool_call(self, idx, call_id, name):
531
+ oi = self._alloc_oi()
532
+ item_id = f"fc_{call_id}"
533
+ self._tool_calls[idx] = {
534
+ "oi": oi,
535
+ "item_id": item_id,
536
+ "call_id": call_id,
537
+ "name": name,
538
+ "args": "",
539
+ }
540
+ return [
541
+ self.ev(
542
+ "response.output_item.added",
543
+ {
544
+ "type": "response.output_item.added",
545
+ "output_index": oi,
546
+ "item": {
547
+ "id": item_id,
548
+ "type": "function_call",
549
+ "status": "in_progress",
550
+ "call_id": call_id,
551
+ "name": name,
552
+ "arguments": "",
553
+ },
554
+ },
555
+ )
556
+ ]
557
+
558
+ def _close_tool_call(self, idx):
559
+ from .namespace import restore_function_call
560
+
561
+ tc = self._tool_calls.pop(idx)
562
+ item = restore_function_call(
563
+ {
564
+ "id": tc["item_id"],
565
+ "type": "function_call",
566
+ "status": "completed",
567
+ "call_id": tc["call_id"],
568
+ "name": tc["name"],
569
+ "arguments": tc["args"],
570
+ },
571
+ self.restore_map,
572
+ )
573
+ self._completed.append((tc["oi"], item))
574
+ return [
575
+ self.ev(
576
+ "response.function_call_arguments.done",
577
+ {
578
+ "type": "response.function_call_arguments.done",
579
+ "item_id": tc["item_id"],
580
+ "output_index": tc["oi"],
581
+ "arguments": tc["args"],
582
+ },
583
+ ),
584
+ self.ev(
585
+ "response.output_item.done",
586
+ {"type": "response.output_item.done", "output_index": tc["oi"], "item": item},
587
+ ),
588
+ ]
589
+
590
+ def handle(self, chunk):
591
+ evts = []
592
+ if chunk.get("usage"):
593
+ self.usage = chunk["usage"]
594
+ for choice in chunk.get("choices") or []:
595
+ delta = choice.get("delta") or {}
596
+
597
+ rt = delta.get("reasoning_content") or delta.get("reasoning")
598
+ if rt:
599
+ if self._reasoning is None:
600
+ evts += self._open_reasoning()
601
+ self._reasoning["text"] += rt
602
+ evts.append(
603
+ self.ev(
604
+ "response.reasoning_summary_text.delta",
605
+ {
606
+ "type": "response.reasoning_summary_text.delta",
607
+ "item_id": self._reasoning["id"],
608
+ "output_index": self._reasoning["oi"],
609
+ "summary_index": 0,
610
+ "delta": rt,
611
+ },
612
+ )
613
+ )
614
+
615
+ ct = delta.get("content")
616
+ if ct:
617
+ if self._reasoning is not None:
618
+ evts += self._close_reasoning()
619
+ if self._message is None:
620
+ evts += self._open_message()
621
+ self._message["text"] += ct
622
+ evts.append(
623
+ self.ev(
624
+ "response.output_text.delta",
625
+ {
626
+ "type": "response.output_text.delta",
627
+ "item_id": self._message["id"],
628
+ "output_index": self._message["oi"],
629
+ "content_index": 0,
630
+ "delta": ct,
631
+ },
632
+ )
633
+ )
634
+
635
+ tcs = delta.get("tool_calls")
636
+ if tcs:
637
+ # 进入工具调用阶段:reasoning/message 与 tool call 不并行,先关掉
638
+ if self._reasoning is not None:
639
+ evts += self._close_reasoning()
640
+ if self._message is not None:
641
+ evts += self._close_message()
642
+ for tc in tcs:
643
+ idx = tc.get("index", 0)
644
+ fn = tc.get("function") or {}
645
+ if idx not in self._tool_calls:
646
+ evts += self._open_tool_call(
647
+ idx, tc.get("id") or f"call_{idx}", fn.get("name") or ""
648
+ )
649
+ elif fn.get("name") and not self._tool_calls[idx]["name"]:
650
+ self._tool_calls[idx]["name"] = fn["name"]
651
+ if fn.get("arguments"):
652
+ self._tool_calls[idx]["args"] += fn["arguments"]
653
+ evts.append(
654
+ self.ev(
655
+ "response.function_call_arguments.delta",
656
+ {
657
+ "type": "response.function_call_arguments.delta",
658
+ "item_id": self._tool_calls[idx]["item_id"],
659
+ "output_index": self._tool_calls[idx]["oi"],
660
+ "delta": fn["arguments"],
661
+ },
662
+ )
663
+ )
664
+
665
+ if choice.get("finish_reason"):
666
+ self.finish = choice["finish_reason"]
667
+ return evts
668
+
669
+ def finalize(self):
670
+ evts = []
671
+ if self._reasoning is not None:
672
+ evts += self._close_reasoning()
673
+ if self._message is not None:
674
+ evts += self._close_message()
675
+ for idx in sorted(self._tool_calls):
676
+ evts += self._close_tool_call(idx)
677
+ items = [item for _, item in sorted(self._completed, key=lambda x: x[0])]
678
+ if self.finish is None:
679
+ # 断流:无合法 incomplete reason(官方仅 max_output_tokens/content_filter),发 failed
680
+ resp = self._resp("failed", output=items, usage=convert_usage(self.usage))
681
+ resp["error"] = {"message": "上游流中断(未收到 finish_reason)"}
682
+ evts.append(self.ev("response.failed", {"type": "response.failed", "response": resp}))
683
+ return evts
684
+ status, incomplete = _status_and_incomplete(self.finish)
685
+ resp = self._resp(status, output=items, usage=convert_usage(self.usage))
686
+ if incomplete:
687
+ resp["incomplete_details"] = {"reason": incomplete}
688
+ # codex 仅在收到 response.incomplete 时才报错;incomplete 不能误发 completed
689
+ event = "response.completed" if status == "completed" else "response.incomplete"
690
+ evts.append(self.ev(event, {"type": event, "response": resp}))
691
+ return evts