agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__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 (450) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
  2. agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
  3. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
  4. ksadk/a2a/__init__.py +180 -11
  5. ksadk/a2a/bootstrap.py +332 -0
  6. ksadk/a2a/card.py +15 -0
  7. ksadk/a2a/context_store.py +203 -0
  8. ksadk/a2a/control_plane.py +1056 -0
  9. ksadk/a2a/credential.py +99 -0
  10. ksadk/a2a/event_adapter.py +149 -0
  11. ksadk/a2a/executor.py +361 -137
  12. ksadk/a2a/external_transport.py +516 -0
  13. ksadk/a2a/identity.py +304 -0
  14. ksadk/a2a/ids.py +22 -0
  15. ksadk/a2a/langgraph.py +285 -0
  16. ksadk/a2a/resume_store.py +201 -0
  17. ksadk/a2a/routes.py +112 -0
  18. ksadk/a2a/server.py +102 -62
  19. ksadk/a2a/service_env.py +33 -0
  20. ksadk/a2a/space_client.py +1122 -0
  21. ksadk/a2a/task_adapter.py +423 -0
  22. ksadk/a2a/task_event_dispatcher.py +167 -0
  23. ksadk/a2a/task_event_outbox.py +326 -0
  24. ksadk/a2a/task_store.py +206 -0
  25. ksadk/a2a_card.py +101 -0
  26. ksadk/a2ui/__init__.py +27 -0
  27. ksadk/a2ui/core.py +186 -0
  28. ksadk/a2ui/models.py +166 -0
  29. ksadk/a2ui/renderer.py +198 -0
  30. ksadk/agui/__init__.py +9 -0
  31. ksadk/agui/a2ui_projection.py +92 -0
  32. ksadk/agui/agent.py +872 -0
  33. ksadk/agui/config.py +92 -0
  34. ksadk/agui/routes.py +47 -0
  35. ksadk/api/client.py +238 -204
  36. ksadk/api/environment.py +27 -0
  37. ksadk/builders/__init__.py +2 -0
  38. ksadk/builders/base.py +16 -14
  39. ksadk/builders/code_builder.py +375 -270
  40. ksadk/builders/container_builder.py +240 -200
  41. ksadk/builders/framework_requirements.py +31 -12
  42. ksadk/builders/ks3_uploader.py +60 -29
  43. ksadk/builders/managed_runtime_builder.py +173 -0
  44. ksadk/builders/mcp_builder.py +91 -92
  45. ksadk/builders/requirements_utils.py +1 -5
  46. ksadk/cli/__init__.py +97 -171
  47. ksadk/cli/cmd_a2a.py +255 -84
  48. ksadk/cli/cmd_agent.py +19 -5
  49. ksadk/cli/cmd_build.py +158 -29
  50. ksadk/cli/cmd_completion.py +47 -35
  51. ksadk/cli/cmd_config.py +267 -198
  52. ksadk/cli/cmd_create.py +507 -740
  53. ksadk/cli/cmd_dashboard.py +120 -45
  54. ksadk/cli/cmd_deploy.py +135 -59
  55. ksadk/cli/cmd_destroy.py +44 -14
  56. ksadk/cli/cmd_eval.py +386 -0
  57. ksadk/cli/cmd_files.py +63 -24
  58. ksadk/cli/cmd_hermes.py +313 -135
  59. ksadk/cli/cmd_invoke.py +184 -122
  60. ksadk/cli/cmd_launch.py +76 -28
  61. ksadk/cli/cmd_mcp.py +98 -64
  62. ksadk/cli/cmd_model.py +14 -5
  63. ksadk/cli/cmd_openclaw.py +564 -268
  64. ksadk/cli/cmd_replay.py +71 -0
  65. ksadk/cli/cmd_run.py +58 -73
  66. ksadk/cli/cmd_status.py +80 -31
  67. ksadk/cli/cmd_studio.py +110 -0
  68. ksadk/cli/cmd_version.py +20 -13
  69. ksadk/cli/cmd_web.py +95 -18
  70. ksadk/cli/deploy_utils.py +20 -20
  71. ksadk/cli/dry_run.py +7 -4
  72. ksadk/cli/env_options.py +83 -0
  73. ksadk/cli/error_utils.py +37 -12
  74. ksadk/cli/global_options.py +4 -3
  75. ksadk/cli/local_runtime.py +1 -2
  76. ksadk/cli/model_catalog.py +6 -2
  77. ksadk/cli/network_options.py +41 -19
  78. ksadk/cli/resource_common.py +6 -2
  79. ksadk/cli/runtime_bootstrap.py +51 -0
  80. ksadk/cli/storage.py +0 -1
  81. ksadk/cli/ui.py +11 -3
  82. ksadk/cli/workflow_common.py +52 -18
  83. ksadk/codex/__init__.py +12 -0
  84. ksadk/codex/client.py +1042 -0
  85. ksadk/codex/phase.py +143 -0
  86. ksadk/codex/runtime.py +940 -0
  87. ksadk/common/aicp_env.py +0 -1
  88. ksadk/common/auth.py +2 -2
  89. ksadk/common/constants.py +1 -3
  90. ksadk/common/kop_client.py +232 -0
  91. ksadk/common/llm_utils.py +17 -13
  92. ksadk/compat/__init__.py +5 -0
  93. ksadk/compat/adk_compat.py +200 -0
  94. ksadk/compat/copilotkit_a2ui.py +330 -0
  95. ksadk/configs/__init__.py +12 -14
  96. ksadk/configs/env_registry.py +661 -116
  97. ksadk/configs/global_config.py +3 -2
  98. ksadk/configs/settings.py +133 -209
  99. ksadk/conversations/__init__.py +85 -92
  100. ksadk/conversations/attachment_storage.py +16 -11
  101. ksadk/conversations/attachments.py +44 -36
  102. ksadk/conversations/compaction_pipeline.py +12 -12
  103. ksadk/conversations/compaction_prompt.py +9 -5
  104. ksadk/conversations/context.py +221 -6
  105. ksadk/conversations/message_projection.py +438 -51
  106. ksadk/conversations/model_context.py +12 -6
  107. ksadk/conversations/model_options.py +0 -1
  108. ksadk/conversations/normalize.py +20 -16
  109. ksadk/conversations/reasoning_markup.py +1 -1
  110. ksadk/conversations/runtime.py +339 -5255
  111. ksadk/conversations/runtime_compaction.py +333 -0
  112. ksadk/conversations/runtime_constants.py +27 -0
  113. ksadk/conversations/runtime_governance.py +185 -0
  114. ksadk/conversations/runtime_input.py +660 -0
  115. ksadk/conversations/runtime_invocation.py +395 -0
  116. ksadk/conversations/runtime_metadata.py +475 -0
  117. ksadk/conversations/runtime_observability.py +367 -0
  118. ksadk/conversations/runtime_payloads.py +272 -0
  119. ksadk/conversations/runtime_persistence.py +440 -0
  120. ksadk/conversations/runtime_preparation.py +426 -0
  121. ksadk/conversations/runtime_resume.py +836 -0
  122. ksadk/conversations/runtime_stream_events.py +902 -0
  123. ksadk/conversations/runtime_streaming.py +700 -0
  124. ksadk/conversations/semantic_summary.py +17 -5
  125. ksadk/conversations/session_title.py +6 -3
  126. ksadk/deployment/__init__.py +15 -16
  127. ksadk/deployment/agent_access.py +12 -23
  128. ksadk/deployment/base.py +51 -38
  129. ksadk/deployment/managed_runtime.py +33 -0
  130. ksadk/deployment/manager.py +85 -71
  131. ksadk/deployment/providers/serverless.py +379 -210
  132. ksadk/deployment/registry.py +38 -32
  133. ksadk/deployment/state.py +11 -11
  134. ksadk/deployment/ui_config.py +1 -1
  135. ksadk/detection/__init__.py +1 -1
  136. ksadk/detection/detector.py +146 -83
  137. ksadk/detection/mcp_detector.py +9 -6
  138. ksadk/evaluation/__init__.py +84 -0
  139. ksadk/evaluation/a2a_adapter.py +444 -0
  140. ksadk/evaluation/adapters.py +54 -0
  141. ksadk/evaluation/contracts.py +409 -0
  142. ksadk/evaluation/evalset.py +351 -0
  143. ksadk/evaluation/evaluators.py +461 -0
  144. ksadk/evaluation/executor.py +118 -0
  145. ksadk/evaluation/storage.py +68 -0
  146. ksadk/evaluation/target.py +73 -0
  147. ksadk/events/__init__.py +25 -0
  148. ksadk/events/parser.py +191 -0
  149. ksadk/events/replay.py +37 -0
  150. ksadk/events/runtime_event.py +273 -0
  151. ksadk/events/store.py +317 -0
  152. ksadk/harness/__init__.py +35 -0
  153. ksadk/harness/app.py +269 -0
  154. ksadk/harness/config.py +181 -0
  155. ksadk/harness/reasoner.py +105 -0
  156. ksadk/harness/runtime.py +364 -0
  157. ksadk/harness/sandbox.py +141 -0
  158. ksadk/harness/tools.py +222 -0
  159. ksadk/hermes_terminal.py +28 -12
  160. ksadk/identity/resolver.py +15 -9
  161. ksadk/ids.py +32 -0
  162. ksadk/knowledge_base/adk_tool.py +1 -1
  163. ksadk/knowledge_base/client.py +38 -33
  164. ksadk/managed_a2a_card.py +160 -0
  165. ksadk/managed_runtime.py +195 -0
  166. ksadk/markdown.py +5 -2
  167. ksadk/mcp_runtime/__init__.py +9 -6
  168. ksadk/memory/__init__.py +1 -1
  169. ksadk/memory/adk/__init__.py +2 -0
  170. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  171. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  172. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  173. ksadk/memory/adk/backends/sdk_ltm_backend.py +36 -36
  174. ksadk/memory/adk/long_term_memory.py +34 -37
  175. ksadk/memory/adk/resilient_session_service.py +4 -5
  176. ksadk/memory/adk/short_term_memory.py +12 -21
  177. ksadk/memory/adk_tool.py +3 -2
  178. ksadk/memory/backends/base.py +19 -19
  179. ksadk/memory/backends/memory.py +24 -25
  180. ksadk/memory/backends/redis.py +31 -30
  181. ksadk/memory/langchain_tool.py +2 -1
  182. ksadk/memory/manager.py +52 -47
  183. ksadk/memory/service.py +16 -6
  184. ksadk/memory/tool.py +4 -0
  185. ksadk/model_policy.py +15 -7
  186. ksadk/model_proxy/__init__.py +51 -0
  187. ksadk/model_proxy/bootstrap.py +80 -0
  188. ksadk/model_proxy/cache.py +165 -0
  189. ksadk/model_proxy/config.py +66 -0
  190. ksadk/model_proxy/demo_webui.py +115 -0
  191. ksadk/model_proxy/detect.py +145 -0
  192. ksadk/model_proxy/gate.py +68 -0
  193. ksadk/model_proxy/namespace.py +201 -0
  194. ksadk/model_proxy/protocol_proxy.py +22 -0
  195. ksadk/model_proxy/server.py +443 -0
  196. ksadk/model_proxy/transform.py +735 -0
  197. ksadk/openclaw_gateway.py +29 -14
  198. ksadk/runners/adk_runner.py +354 -146
  199. ksadk/runners/base_runner.py +71 -29
  200. ksadk/runners/deepagents_runner.py +0 -1
  201. ksadk/runners/factory.py +18 -5
  202. ksadk/runners/langchain_runner.py +29 -742
  203. ksadk/runners/langgraph_runner.py +417 -97
  204. ksadk/runners/patch_langchain.py +16 -16
  205. ksadk/runners/remote_runner.py +134 -33
  206. ksadk/runners/usage_accumulator.py +1 -0
  207. ksadk/runners/utils/__init__.py +0 -10
  208. ksadk/runners/utils/loader.py +23 -7
  209. ksadk/runtime/__init__.py +67 -0
  210. ksadk/runtime/adapter.py +412 -0
  211. ksadk/runtime/conversation_execution.py +553 -0
  212. ksadk/runtime/executor.py +251 -0
  213. ksadk/runtime/factory.py +156 -0
  214. ksadk/runtime/framework_adapters.py +118 -0
  215. ksadk/runtime/launch.py +48 -0
  216. ksadk/runtime/preprocessing.py +131 -0
  217. ksadk/runtime/runner_adapter.py +1032 -0
  218. ksadk/runtime/runner_loading.py +36 -0
  219. ksadk/runtime_context.py +18 -3
  220. ksadk/runtime_state.py +0 -1
  221. ksadk/sandbox/__init__.py +4 -1
  222. ksadk/sandbox/backends/__init__.py +4 -1
  223. ksadk/sandbox/backends/e2b.py +13 -5
  224. ksadk/sandbox/backends/local_process.py +11 -3
  225. ksadk/sandbox/base.py +10 -15
  226. ksadk/sandbox/factory.py +14 -4
  227. ksadk/sandbox/registry.py +57 -6
  228. ksadk/server/__init__.py +39 -4
  229. ksadk/server/api_models.py +15 -5
  230. ksadk/server/app.py +24 -3928
  231. ksadk/server/composition.py +47 -0
  232. ksadk/server/factory.py +622 -0
  233. ksadk/server/routes/__init__.py +1 -0
  234. ksadk/server/routes/checkpoint_resolution.py +116 -0
  235. ksadk/server/routes/common.py +682 -0
  236. ksadk/server/routes/control.py +331 -0
  237. ksadk/server/routes/dependencies.py +98 -0
  238. ksadk/server/routes/misc.py +425 -0
  239. ksadk/server/routes/models.py +451 -0
  240. ksadk/server/routes/openai_compat.py +229 -0
  241. ksadk/server/routes/projection.py +848 -0
  242. ksadk/server/routes/routers.py +52 -0
  243. ksadk/server/routes/run.py +519 -0
  244. ksadk/server/routes/sessions.py +644 -0
  245. ksadk/server/routes/streaming.py +265 -0
  246. ksadk/server/routes/workspace.py +414 -0
  247. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
  248. ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
  249. ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
  250. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
  251. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
  252. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
  253. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
  254. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
  255. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
  256. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
  257. ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
  258. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
  259. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
  260. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
  261. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
  262. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
  263. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
  264. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
  265. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
  266. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
  267. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
  268. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
  269. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
  270. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
  271. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
  272. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
  273. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
  274. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
  275. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
  276. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
  277. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
  278. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
  279. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
  280. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
  281. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
  282. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
  283. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
  284. ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
  285. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
  286. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
  287. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
  288. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
  289. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
  290. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
  291. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
  292. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
  293. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
  294. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
  295. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
  296. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
  297. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
  298. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
  299. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
  300. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
  301. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
  302. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
  303. ksadk/server/static/assets/index-R52pJJvg.js +177 -0
  304. ksadk/server/static/assets/index-jJr3_955.css +2 -0
  305. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
  306. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
  307. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
  308. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
  309. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
  310. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
  311. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
  312. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
  313. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
  314. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
  315. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
  316. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
  317. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
  318. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
  319. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
  320. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
  321. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
  322. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
  323. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
  324. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
  325. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
  326. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
  327. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
  328. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
  329. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
  330. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.js} +1 -1
  331. ksadk/server/static/index.html +2 -2
  332. ksadk/server/terminal_sessions.py +111 -58
  333. ksadk/sessions/__init__.py +69 -19
  334. ksadk/sessions/base.py +31 -11
  335. ksadk/sessions/continuity.py +2 -3
  336. ksadk/sessions/in_memory.py +46 -2
  337. ksadk/sessions/local_service.py +148 -17
  338. ksadk/sessions/postgres_service.py +143 -25
  339. ksadk/sessions/resilient.py +36 -8
  340. ksadk/skills/models.py +17 -5
  341. ksadk/skills/package_store.py +5 -2
  342. ksadk/skills/runtime/agent.py +20 -5
  343. ksadk/skills/runtime/artifacts.py +1 -5
  344. ksadk/skills/runtime/backends/e2b.py +10 -4
  345. ksadk/skills/runtime/backends/local.py +15 -3
  346. ksadk/skills/runtime/base.py +1 -2
  347. ksadk/skills/runtime/executor.py +10 -3
  348. ksadk/skills/runtime/factory.py +4 -2
  349. ksadk/skills/runtime/loader.py +1 -2
  350. ksadk/skills/runtime/registry.py +15 -10
  351. ksadk/skills/runtime/request.py +3 -1
  352. ksadk/skills/service_client.py +47 -16
  353. ksadk/skills/tool_defs.py +15 -10
  354. ksadk/studio/__init__.py +5 -0
  355. ksadk/studio/agent_avatar_assets.py +120 -0
  356. ksadk/studio/agent_lifecycle.py +51 -0
  357. ksadk/studio/api.py +1046 -0
  358. ksadk/studio/api_catalog_routes.py +324 -0
  359. ksadk/studio/api_contracts.py +177 -0
  360. ksadk/studio/api_helpers.py +132 -0
  361. ksadk/studio/authoring.py +524 -0
  362. ksadk/studio/authoring_coordinator.py +285 -0
  363. ksadk/studio/builder.py +320 -0
  364. ksadk/studio/capabilities.py +351 -0
  365. ksadk/studio/cloud.py +278 -0
  366. ksadk/studio/codex_agent_service.py +583 -0
  367. ksadk/studio/codex_builder.py +324 -0
  368. ksadk/studio/codex_manifest.py +278 -0
  369. ksadk/studio/codex_run.py +352 -0
  370. ksadk/studio/compiler.py +189 -0
  371. ksadk/studio/contracts.py +644 -0
  372. ksadk/studio/errors.py +47 -0
  373. ksadk/studio/evaluation.py +192 -0
  374. ksadk/studio/event_store.py +284 -0
  375. ksadk/studio/framework_run.py +114 -0
  376. ksadk/studio/identifiers.py +33 -0
  377. ksadk/studio/mcp_runtime.py +229 -0
  378. ksadk/studio/model_client.py +543 -0
  379. ksadk/studio/model_profile_service.py +214 -0
  380. ksadk/studio/operations.py +243 -0
  381. ksadk/studio/otel_trace.py +754 -0
  382. ksadk/studio/pagination.py +182 -0
  383. ksadk/studio/python_tool_inspection.py +183 -0
  384. ksadk/studio/repository.py +306 -0
  385. ksadk/studio/resource_catalog.py +1217 -0
  386. ksadk/studio/run_service.py +707 -0
  387. ksadk/studio/runtime_catalog.py +74 -0
  388. ksadk/studio/runtime_source.py +339 -0
  389. ksadk/studio/service.py +1141 -0
  390. ksadk/studio/shared_web.py +994 -0
  391. ksadk/studio/skill_discovery.py +628 -0
  392. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
  393. ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
  394. ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
  395. ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
  396. ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  397. ksadk/studio/static/index.html +15 -0
  398. ksadk/studio/templates.py +630 -0
  399. ksadk/studio/validator.py +246 -0
  400. ksadk/studio/workspace.py +137 -0
  401. ksadk/terminal_exec_policy.py +3 -4
  402. ksadk/tools/gateway.py +101 -15
  403. ksadk/tools/result_budget.py +12 -4
  404. ksadk/toolsets/__init__.py +161 -37
  405. ksadk/toolsets/a2a.py +426 -0
  406. ksadk/toolsets/platform.py +21 -5
  407. ksadk/toolsets/sandbox.py +82 -16
  408. ksadk/toolsets/skills.py +200 -39
  409. ksadk/toolsets/web.py +123 -26
  410. ksadk/toolsets/workspace.py +212 -101
  411. ksadk/tracing/__init__.py +19 -21
  412. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  413. ksadk/tracing/setup.py +135 -506
  414. ksadk/tracing/span_utils.py +0 -41
  415. ksadk/tui/loop.py +78 -30
  416. ksadk/tui/stream_render.py +1 -0
  417. ksadk/ui_config.py +5 -3
  418. ksadk/version.py +1 -1
  419. ksadk_runtime_common/memory_backend/registry.py +56 -56
  420. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  421. ksadk_runtime_common/workspace_files/router.py +8 -2
  422. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  423. ksadk/a2a/card_builder.py +0 -64
  424. ksadk/a2a/client.py +0 -309
  425. ksadk/runners/utils/langfuse.py +0 -230
  426. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  427. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  428. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  429. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  430. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  431. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  432. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  433. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  434. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  435. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  436. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  437. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  438. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  439. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  440. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  441. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  442. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  443. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  444. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  445. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  446. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  447. ksadk/tracing/exporters/langfuse_exporter.py +0 -445
  448. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
  449. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
  450. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,443 @@
1
+ """app factory + 进程内转换 proxy 生命周期管理。
2
+
3
+ 把 HTTP 层与配置/转换解耦:create_app(config) 返回 FastAPI app;
4
+ ProxyServer 在后台线程懒起一个 localhost uvicorn,幂等 + 加锁 + 干净回收(随 runner 退出)。
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import logging
10
+ import socket
11
+ import threading
12
+ import time
13
+ import uuid
14
+
15
+ import httpx
16
+ import uvicorn
17
+ from fastapi import FastAPI, Request
18
+ from fastapi.responses import JSONResponse, StreamingResponse
19
+
20
+ from .config import _LOOPBACK_HOSTS, ProxyConfig
21
+ from .transform import Streamer, UnsupportedToolsError, chat_to_response, responses_to_chat
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ def _request_id(response: httpx.Response) -> str:
27
+ for name in (
28
+ "x-request-id",
29
+ "request-id",
30
+ "x-ks-request-id",
31
+ "x-amzn-requestid",
32
+ ):
33
+ value = response.headers.get(name)
34
+ if value:
35
+ return str(value)
36
+ return ""
37
+
38
+
39
+ def _reported_usage(raw: object) -> dict[str, int] | None:
40
+ if not isinstance(raw, dict):
41
+ return None
42
+ prompt_details = raw.get("prompt_tokens_details") or {}
43
+ completion_details = raw.get("completion_tokens_details") or {}
44
+ return {
45
+ "inputTokens": int(raw.get("prompt_tokens") or 0),
46
+ "outputTokens": int(raw.get("completion_tokens") or 0),
47
+ "totalTokens": int(raw.get("total_tokens") or 0),
48
+ "cachedInputTokens": int(prompt_details.get("cached_tokens") or 0),
49
+ "reasoningOutputTokens": int(completion_details.get("reasoning_tokens") or 0),
50
+ }
51
+
52
+
53
+ def _emit_completed(
54
+ config: ProxyConfig,
55
+ *,
56
+ response_id: str,
57
+ model: object,
58
+ status_code: int,
59
+ started: float,
60
+ usage: object = None,
61
+ ) -> None:
62
+ payload: dict[str, object] = {
63
+ "responseId": response_id,
64
+ "model": str(model or ""),
65
+ "statusCode": status_code,
66
+ "durationMs": max(0, int((time.monotonic() - started) * 1000)),
67
+ }
68
+ reported = _reported_usage(usage)
69
+ if reported is not None:
70
+ payload["usage"] = reported
71
+ config.emit("proxy.completed", payload)
72
+
73
+
74
+ def _upstream_error(status_code: int) -> JSONResponse:
75
+ """Return a stable public error without relaying an upstream traceback."""
76
+ return JSONResponse(
77
+ status_code=status_code,
78
+ content={
79
+ "error": {
80
+ "type": "upstream_error",
81
+ "message": "The model upstream rejected the request.",
82
+ }
83
+ },
84
+ )
85
+
86
+
87
+ def create_app(config: ProxyConfig) -> FastAPI:
88
+ base = config.upstream_base.rstrip("/")
89
+ headers = {"Authorization": f"Bearer {config.api_key}", "Content-Type": "application/json"}
90
+ app = FastAPI()
91
+ # 应用层取消信号:stop() 时置位,让活动 SSE 的 _stream_gen 主动 break(比单靠 force_exit 可靠)
92
+ cancel = threading.Event()
93
+ app.state.cancel_event = cancel
94
+
95
+ @app.get("/healthz")
96
+ async def healthz():
97
+ return {"ok": True, "upstream": base}
98
+
99
+ @app.post("/v1/chat/completions")
100
+ async def chat_passthrough(req: Request):
101
+ """chat 直通端点(env 框架发 chat 经代理原样转发上游,不转换)。
102
+
103
+ 让代理成为统一模型出口:env 框架(ADK/langchain/deepagents 发 chat)经此直通,
104
+ codex(发 responses)经 /v1/responses 转换。统一出口为后续能力探测/路由打底。
105
+ chat 直通字节级转发 + 流式透传,前缀缓存不受影响。
106
+ """
107
+ if (
108
+ config.local_token
109
+ and req.headers.get("authorization", "") != f"Bearer {config.local_token}"
110
+ ):
111
+ return JSONResponse(
112
+ status_code=401, content={"error": {"type": "authentication_error"}}
113
+ )
114
+ body = await req.json()
115
+ is_stream = bool(body.get("stream"))
116
+ up_headers = {
117
+ "Authorization": f"Bearer {config.api_key}",
118
+ "Content-Type": "application/json",
119
+ }
120
+ if is_stream:
121
+ return StreamingResponse(
122
+ _chat_passthrough_stream(config, base, up_headers, body),
123
+ media_type="text/event-stream",
124
+ headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
125
+ )
126
+ async with httpx.AsyncClient(timeout=config.timeout) as c:
127
+ r = await c.post(f"{base}/chat/completions", json=body, headers=up_headers)
128
+ if r.is_error:
129
+ logger.warning("chat upstream rejected request: status=%s", r.status_code)
130
+ return _upstream_error(r.status_code)
131
+ return JSONResponse(status_code=r.status_code, content=r.json() if r.content else None)
132
+
133
+ @app.post("/v1/responses")
134
+ async def responses(req: Request):
135
+ # 本地鉴权:codex -> 本地 proxy 用 KSADK_PROXY_TOKEN;上游凭证独立保存于 config.api_key
136
+ if (
137
+ config.local_token
138
+ and req.headers.get("authorization", "") != f"Bearer {config.local_token}"
139
+ ):
140
+ return JSONResponse(
141
+ status_code=401,
142
+ content={
143
+ "error": {"type": "authentication_error", "message": "invalid proxy token"}
144
+ },
145
+ )
146
+ body = await req.json()
147
+ try:
148
+ chat_req, restore_map = responses_to_chat(body)
149
+ except UnsupportedToolsError:
150
+ logger.info("responses request uses unsupported tools")
151
+ return JSONResponse(
152
+ status_code=400,
153
+ content={
154
+ "error": {
155
+ "type": "unsupported_tools",
156
+ "message": "The request uses tools unsupported by the model upstream.",
157
+ }
158
+ },
159
+ )
160
+ # codex 会对内建能力发内部伪模型名(如 auto_review guardian 用 codex-auto-review),
161
+ # 单上游代理必须落回配置的真实模型,否则上游按未知模型 403。
162
+ if config.upstream_model and chat_req.get("model") != config.upstream_model:
163
+ logger.debug(
164
+ "responses model rewrite: %s -> %s",
165
+ chat_req.get("model"),
166
+ config.upstream_model,
167
+ )
168
+ chat_req["model"] = config.upstream_model
169
+ rid = "resp_" + uuid.uuid4().hex[:24]
170
+ model = body.get("model")
171
+ started = time.monotonic()
172
+ config.emit(
173
+ "proxy.requested",
174
+ {
175
+ "responseId": rid,
176
+ "model": str(model or ""),
177
+ "protocol": "responses-to-chat",
178
+ "stream": bool(body.get("stream")),
179
+ },
180
+ )
181
+ if body.get("stream"):
182
+ chat_req["stream"] = True
183
+ chat_req["stream_options"] = {"include_usage": True}
184
+ return StreamingResponse(
185
+ _stream_gen(
186
+ config,
187
+ base,
188
+ headers,
189
+ chat_req,
190
+ rid,
191
+ model,
192
+ cancel,
193
+ restore_map,
194
+ started,
195
+ ),
196
+ media_type="text/event-stream",
197
+ headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
198
+ )
199
+ async with httpx.AsyncClient(timeout=config.timeout) as c:
200
+ r = await c.post(f"{base}/chat/completions", json=chat_req, headers=headers)
201
+ config.emit(
202
+ "proxy.upstream",
203
+ {"requestId": _request_id(r), "statusCode": r.status_code},
204
+ )
205
+ if r.status_code != 200:
206
+ _emit_completed(
207
+ config,
208
+ response_id=rid,
209
+ model=model,
210
+ status_code=r.status_code,
211
+ started=started,
212
+ )
213
+ logger.warning("responses upstream rejected request: status=%s", r.status_code)
214
+ return _upstream_error(r.status_code)
215
+ upstream_payload = r.json()
216
+ _emit_completed(
217
+ config,
218
+ response_id=rid,
219
+ model=model,
220
+ status_code=r.status_code,
221
+ started=started,
222
+ usage=upstream_payload.get("usage"),
223
+ )
224
+ return JSONResponse(chat_to_response(upstream_payload, rid, restore_map))
225
+
226
+ return app
227
+
228
+
229
+ async def _chat_passthrough_stream(config: ProxyConfig, base: str, headers: dict, body: dict):
230
+ """chat SSE 字节级透传(不转换),让前缀缓存/流式格式与直连一致。"""
231
+ try:
232
+ async with httpx.AsyncClient(timeout=config.timeout) as c:
233
+ async with c.stream(
234
+ "POST", f"{base}/chat/completions", json=body, headers=headers
235
+ ) as r:
236
+ async for line in r.aiter_lines():
237
+ yield line + "\n"
238
+ except Exception as exc: # noqa: BLE001
239
+ logger.warning("chat upstream stream failed", exc_info=exc)
240
+ yield 'data: {"error":"The model upstream stream failed."}\n\n'
241
+
242
+
243
+ async def _stream_gen(
244
+ config: ProxyConfig,
245
+ base: str,
246
+ headers: dict,
247
+ chat_req: dict,
248
+ rid: str,
249
+ model,
250
+ cancel: threading.Event,
251
+ restore_map: dict | None = None,
252
+ started: float | None = None,
253
+ ):
254
+ started = time.monotonic() if started is None else started
255
+ s = Streamer(rid, model, restore_map)
256
+ for e in s.start():
257
+ yield e
258
+ try:
259
+ # 超时必须有界(config.timeout):无限超时的活动 SSE 会让 stop() 后线程仍存活
260
+ async with httpx.AsyncClient(timeout=config.timeout) as c:
261
+ async with c.stream(
262
+ "POST", f"{base}/chat/completions", json=chat_req, headers=headers
263
+ ) as r:
264
+ config.emit(
265
+ "proxy.upstream",
266
+ {"requestId": _request_id(r), "statusCode": r.status_code},
267
+ )
268
+ if r.status_code != 200:
269
+ _emit_completed(
270
+ config,
271
+ response_id=rid,
272
+ model=model,
273
+ status_code=r.status_code,
274
+ started=started,
275
+ )
276
+ logger.warning(
277
+ "responses upstream stream rejected: status=%s model=%s tools=%s "
278
+ "msgs=%s bytes=%s has_text_format=%s",
279
+ r.status_code,
280
+ chat_req.get("model"),
281
+ len(chat_req.get("tools") or []),
282
+ len(chat_req.get("messages") or []),
283
+ len(json.dumps(chat_req)),
284
+ bool(chat_req.get("response_format")),
285
+ )
286
+ yield Streamer.ev(
287
+ "response.failed",
288
+ {
289
+ "type": "response.failed",
290
+ "response": {
291
+ **s._resp("failed"),
292
+ "error": {"message": "The model upstream rejected the request."},
293
+ },
294
+ },
295
+ )
296
+ return
297
+ async for line in r.aiter_lines():
298
+ if cancel.is_set():
299
+ _emit_completed(
300
+ config,
301
+ response_id=rid,
302
+ model=model,
303
+ status_code=499,
304
+ started=started,
305
+ usage=s.usage,
306
+ )
307
+ return # stop() 置位:主动中断活动 SSE,让线程能及时回收
308
+ line = line.strip()
309
+ if not line.startswith("data:"):
310
+ continue
311
+ data = line[5:].strip()
312
+ if data == "[DONE]":
313
+ break
314
+ try:
315
+ chunk = json.loads(data)
316
+ except ValueError:
317
+ continue
318
+ for e in s.handle(chunk):
319
+ yield e
320
+ except Exception as exc: # noqa: BLE001
321
+ _emit_completed(
322
+ config,
323
+ response_id=rid,
324
+ model=model,
325
+ status_code=502,
326
+ started=started,
327
+ usage=s.usage,
328
+ )
329
+ logger.warning("responses upstream stream failed", exc_info=exc)
330
+ yield Streamer.ev(
331
+ "response.failed",
332
+ {
333
+ "type": "response.failed",
334
+ "response": {
335
+ **s._resp("failed"),
336
+ "error": {"message": "The model upstream stream failed."},
337
+ },
338
+ },
339
+ )
340
+ return
341
+ for e in s.finalize():
342
+ yield e
343
+ _emit_completed(
344
+ config,
345
+ response_id=rid,
346
+ model=model,
347
+ status_code=200,
348
+ started=started,
349
+ usage=s.usage,
350
+ )
351
+
352
+
353
+ class ProxyServer:
354
+ """进程内 localhost 转换 proxy 的生命周期管理。
355
+
356
+ - start 幂等(已启动直接返回)+ 加锁,避免重复 start 覆盖 server/thread 造成泄漏;
357
+ - 端口在 start 时 bind 并**一直持有 socket**(传给 uvicorn),避免"先释放再重绑"的 TOCTOU;
358
+ - 启动超时与 stop 都会干净回收线程与 socket。
359
+ """
360
+
361
+ def __init__(self, config: ProxyConfig, host: str = "127.0.0.1", port: int = 0):
362
+ if host not in _LOOPBACK_HOSTS and not config.local_token:
363
+ raise ValueError(
364
+ f"非回环监听(host={host})必须设置 local_token,否则是无鉴权代持上游凭证的开放代理"
365
+ )
366
+ self.config = config
367
+ self.host = host
368
+ self._requested_port = port
369
+ self.port = port
370
+ self._lock = threading.Lock()
371
+ self._sock: socket.socket | None = None
372
+ self._server: uvicorn.Server | None = None
373
+ self._thread: threading.Thread | None = None
374
+ self._app: FastAPI | None = None
375
+
376
+ @property
377
+ def base_url(self) -> str:
378
+ return f"http://{self.host}:{self.port}/v1"
379
+
380
+ def start(self, wait_ready: float = 10.0) -> str:
381
+ with self._lock:
382
+ if self._server is not None:
383
+ return self.base_url # 幂等
384
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
385
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
386
+ try:
387
+ sock.bind((self.host, self._requested_port))
388
+ except OSError:
389
+ sock.close()
390
+ raise
391
+ self.port = int(sock.getsockname()[1])
392
+ app = create_app(self.config)
393
+ self._app = app
394
+ # timeout_graceful_shutdown 设上限:stop 时挂在 yield 的 SSE generator 不再消费,
395
+ # 若无限等待连接完成(None 默认)会让 shutdown 卡死、线程回收不掉
396
+ cfg = uvicorn.Config(app, log_level="warning", timeout_graceful_shutdown=1)
397
+ self._sock = sock
398
+ server = uvicorn.Server(cfg)
399
+ self._server = server
400
+ # serve(sockets=[sock]) 跨平台:uvicorn 直接复用已绑定 socket,
401
+ # 不走 Config(fd=...) 那条仅 Unix 可用的 socket.fromfd 路径
402
+ self._thread = threading.Thread(
403
+ target=lambda: asyncio.run(server.serve(sockets=[sock])), daemon=True
404
+ )
405
+ self._thread.start()
406
+ deadline = time.time() + wait_ready
407
+ while time.time() < deadline:
408
+ try:
409
+ if (
410
+ httpx.get(f"http://{self.host}:{self.port}/healthz", timeout=1).status_code
411
+ == 200
412
+ ):
413
+ return self.base_url
414
+ except Exception: # noqa: BLE001
415
+ time.sleep(0.05)
416
+ self._teardown() # 启动超时也要清理线程与 socket
417
+ raise RuntimeError(f"ProxyServer 未在 {wait_ready}s 内就绪")
418
+
419
+ def stop(self):
420
+ with self._lock:
421
+ self._teardown()
422
+
423
+ def _teardown(self):
424
+ if self._app is not None:
425
+ # 应用层取消信号:让活动 SSE 的 _stream_gen 主动 break
426
+ self._app.state.cancel_event.set()
427
+ if self._server is not None:
428
+ # should_exit 触发主循环进入 shutdown;force_exit 让 shutdown 跳过等待
429
+ # connections/tasks 完成(活动 SSE 下不再无限等)。两者缺一:只 should_exit
430
+ # 会被活动连接卡死,只 force_exit 主循环根本不进 shutdown。
431
+ self._server.should_exit = True
432
+ self._server.force_exit = True
433
+ if self._thread is not None:
434
+ self._thread.join(timeout=5)
435
+ if self._sock is not None:
436
+ try:
437
+ self._sock.close()
438
+ except OSError:
439
+ pass
440
+ self._server = None
441
+ self._thread = None
442
+ self._sock = None
443
+ self._app = None