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
ksadk/memory/service.py CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  import logging
7
7
  import os
8
- from typing import Any
8
+ from typing import Any, cast
9
9
 
10
10
  from ksadk.common.aicp_env import resolve_aicp_connection
11
11
  from ksadk.memory.adk.backends.base_ltm_backend import BaseLongTermMemoryBackend
@@ -49,7 +49,9 @@ class LongTermMemoryService:
49
49
  self.backend_config = dict(backend_config or {})
50
50
  self.top_k = top_k
51
51
  self.app_name = app_name
52
- backend_index = getattr(backend, "index", "") if isinstance(backend, BaseLongTermMemoryBackend) else ""
52
+ backend_index = (
53
+ getattr(backend, "index", "") if isinstance(backend, BaseLongTermMemoryBackend) else ""
54
+ )
53
55
  self.index = index or backend_index or app_name or "default_app"
54
56
  self._backend = self._resolve_backend()
55
57
 
@@ -76,11 +78,17 @@ class LongTermMemoryService:
76
78
  backend_config = {
77
79
  "access_key": (
78
80
  os.environ.get("KSADK_LTM_ACCESS_KEY")
79
- or os.environ.get("KSYUN_ACCESS_KEY", "")
81
+ or os.environ.get("KSYUN_ACCESS_KEY")
82
+ or os.environ.get("KSYUN_ACCESS_KEY_ID", "")
80
83
  ),
81
84
  "secret_key": (
82
85
  os.environ.get("KSADK_LTM_SECRET_KEY")
83
- or os.environ.get("KSYUN_SECRET_KEY", "")
86
+ or os.environ.get("KSYUN_SECRET_KEY")
87
+ or os.environ.get("KSYUN_SECRET_ACCESS_KEY", "")
88
+ ),
89
+ "session_token": (
90
+ os.environ.get("KSADK_LTM_SESSION_TOKEN")
91
+ or os.environ.get("KSYUN_SESSION_TOKEN", "")
84
92
  ),
85
93
  "region": connection["region"],
86
94
  "endpoint": connection["endpoint"],
@@ -109,7 +117,7 @@ class LongTermMemoryService:
109
117
  backend_cls = get_long_term_memory_backend_cls(str(self.backend))
110
118
  config = dict(self.backend_config)
111
119
  config.setdefault("index", self.index)
112
- return backend_cls(**config)
120
+ return cast(BaseLongTermMemoryBackend, backend_cls(**config))
113
121
 
114
122
  def search_entries(self, *, user_id: str, query: str, top_k: int | None = None) -> list[str]:
115
123
  return self._backend.search_memory(
@@ -142,7 +150,9 @@ class LongTermMemoryService:
142
150
  )
143
151
  )
144
152
 
145
- def save_text(self, *, user_id: str, content: str, metadata: dict[str, Any] | None = None) -> bool:
153
+ def save_text(
154
+ self, *, user_id: str, content: str, metadata: dict[str, Any] | None = None
155
+ ) -> bool:
146
156
  payload = {
147
157
  "role": "user",
148
158
  "parts": [{"text": content}],
ksadk/memory/tool.py CHANGED
@@ -78,6 +78,10 @@ def save_memory(content: str) -> dict:
78
78
  "agent_id": context.agent_id,
79
79
  "session_id": context.session_id,
80
80
  "runner_type": context.runner_type,
81
+ # Explicit user saves must become searchable promptly. The
82
+ # SDK backend maps this to CreateMemorySdk.Flush; background
83
+ # turn persistence keeps the service's batched default.
84
+ "flush": True,
81
85
  },
82
86
  )
83
87
  if ok:
ksadk/model_policy.py CHANGED
@@ -58,10 +58,7 @@ DEFAULT_MODEL_POLICY: dict[str, Any] = {
58
58
  def _deep_merge(base: dict[str, Any], override: Mapping[str, Any]) -> dict[str, Any]:
59
59
  merged = copy.deepcopy(base)
60
60
  for key, value in override.items():
61
- if (
62
- isinstance(value, Mapping)
63
- and isinstance(merged.get(key), dict)
64
- ):
61
+ if isinstance(value, Mapping) and isinstance(merged.get(key), dict):
65
62
  merged[key] = _deep_merge(merged[key], value)
66
63
  else:
67
64
  merged[key] = copy.deepcopy(value)
@@ -196,7 +193,12 @@ def build_runtime_model_policy_env(
196
193
  multimodal = _role_model(normalized, "multimodal")
197
194
  has_primary = any(
198
195
  str(env.get(key) or "").strip()
199
- for key in ("OPENCLAW_DEFAULT_MODEL", "HERMES_DEFAULT_MODEL", "OPENAI_MODEL_NAME", "MODEL_NAME")
196
+ for key in (
197
+ "OPENCLAW_DEFAULT_MODEL",
198
+ "HERMES_DEFAULT_MODEL",
199
+ "OPENAI_MODEL_NAME",
200
+ "MODEL_NAME",
201
+ )
200
202
  )
201
203
  runtime_name = str(runtime or "").strip().lower()
202
204
  if runtime_name == "openclaw":
@@ -206,7 +208,10 @@ def build_runtime_model_policy_env(
206
208
  env.setdefault("OPENCLAW_FALLBACK_MODEL", _provider_ref(fallback))
207
209
  if multimodal:
208
210
  env.setdefault("OPENCLAW_IMAGE_MODEL", _provider_ref(multimodal))
209
- env.setdefault("OPENCLAW_MODEL_CATALOG_JSON", json.dumps(_catalog_from_policy(normalized), ensure_ascii=False))
211
+ env.setdefault(
212
+ "OPENCLAW_MODEL_CATALOG_JSON",
213
+ json.dumps(_catalog_from_policy(normalized), ensure_ascii=False),
214
+ )
210
215
  return env
211
216
  if runtime_name == "hermes":
212
217
  if primary and not has_primary:
@@ -214,7 +219,10 @@ def build_runtime_model_policy_env(
214
219
  env["HERMES_DEFAULT_MODEL"] = primary
215
220
  if fallback:
216
221
  env.setdefault("HERMES_FALLBACK_MODEL", fallback)
217
- env.setdefault("HERMES_MODEL_CATALOG_JSON", json.dumps(_catalog_from_policy(normalized), ensure_ascii=False))
222
+ env.setdefault(
223
+ "HERMES_MODEL_CATALOG_JSON",
224
+ json.dumps(_catalog_from_policy(normalized), ensure_ascii=False),
225
+ )
218
226
  return env
219
227
  if primary and not has_primary:
220
228
  env["OPENAI_MODEL_NAME"] = primary
@@ -0,0 +1,51 @@
1
+ """ksadk 模型出口协议转换层(P1 草案,待 codex review).
2
+
3
+ 把 OpenAI Responses API <-> Chat Completions API 的转换内化为 ksadk 的模型出口中间层,
4
+ 让只发 responses 的 runtime(codex)能透明使用所有 chat 协议模型,用户零感知。
5
+
6
+ 设计见 docs/responses-chat-protocol-proxy-plan.md。
7
+
8
+ 模块边界:
9
+ - transform: 纯转换器(无环境依赖),请求/响应/流式状态机。
10
+ - config: ProxyConfig 显式注入,不在导入时读 env。
11
+ - server: create_app(config) app factory + ProxyServer 生命周期管理。
12
+ - protocol_proxy: 可独立运行的入口(读 env → config → uvicorn)。
13
+ """
14
+
15
+ from .bootstrap import setup_proxy_redirect_if_enabled, teardown_proxy_redirect
16
+ from .cache import CapabilityCache
17
+ from .config import ProxyConfig
18
+ from .detect import ModelCapabilities, probe_responses_capability
19
+ from .gate import ProxyGate
20
+ from .namespace import build_restore_map, flatten_namespace_tool_name, flatten_request_namespaces
21
+ from .server import ProxyServer # noqa: F401 (re-export)
22
+ from .transform import (
23
+ Streamer,
24
+ chat_to_response,
25
+ convert_tool_choice,
26
+ convert_tools,
27
+ convert_usage,
28
+ input_to_messages,
29
+ responses_to_chat,
30
+ )
31
+
32
+ __all__ = [
33
+ "CapabilityCache",
34
+ "ModelCapabilities",
35
+ "ProxyConfig",
36
+ "ProxyGate",
37
+ "ProxyServer",
38
+ "Streamer",
39
+ "build_restore_map",
40
+ "chat_to_response",
41
+ "convert_tool_choice",
42
+ "convert_tools",
43
+ "convert_usage",
44
+ "flatten_namespace_tool_name",
45
+ "flatten_request_namespaces",
46
+ "input_to_messages",
47
+ "probe_responses_capability",
48
+ "responses_to_chat",
49
+ "setup_proxy_redirect_if_enabled",
50
+ "teardown_proxy_redirect",
51
+ ]
@@ -0,0 +1,80 @@
1
+ """env 型框架接线(v2.2):按需懒起 ProxyServer 并重定向 OPENAI_BASE_URL。
2
+
3
+ review v1 约束:setup_environment 在框架检测之前执行,无法判断 runtime。
4
+ 故本模块用 :class:`ProxyGate`(模型白名单,不依赖 runtime 判断)控制:
5
+ gate.is_on(model) 为真时起一个进程内 ProxyServer,把 OPENAI_BASE_URL/
6
+ OPENAI_API_BASE 重定向到它;env 框架(ADK/langchain/deepagents 发 chat)经代理
7
+ 的 /v1/chat/completions 直通上游(字节级,缓存不受影响),codex 走 v2.1 不经此。
8
+
9
+ 默认 gate 关(ProxyGate.enabled=False),不重定向,历史路径零影响。
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ from typing import Optional
16
+
17
+ from .config import ProxyConfig
18
+ from .gate import ProxyGate
19
+ from .server import ProxyServer
20
+
21
+ # 进程级单例:setup_environment 多次调用只起一个 proxy
22
+ _proxy: Optional[ProxyServer] = None
23
+ _original_base: Optional[str] = None
24
+
25
+
26
+ def setup_proxy_redirect_if_enabled(
27
+ gate: ProxyGate | None = None,
28
+ model: str | None = None,
29
+ upstream_base: str | None = None,
30
+ api_key: str | None = None,
31
+ local_token: str | None = None,
32
+ ) -> str | None:
33
+ """gate 开启且模型在白名单时起 ProxyServer 并重定向 OPENAI_BASE_URL。
34
+
35
+ 返回 proxy base_url(已重定向)或 None(未启用)。多次调用幂等(单例)。
36
+ 上游凭证来自 ProxyConfig(从 env 读),不下发给子进程(凭证闭合)。
37
+ """
38
+ global _proxy, _original_base
39
+ if _proxy is not None:
40
+ return _proxy.base_url # 幂等:已起
41
+ gate = gate or ProxyGate.from_env()
42
+ model = model or os.environ.get("OPENAI_MODEL_NAME") or os.environ.get("MODEL_NAME") or ""
43
+ if not gate.is_on(model=model or None):
44
+ return None
45
+ upstream = (
46
+ upstream_base
47
+ or os.environ.get("OPENAI_BASE_URL")
48
+ or os.environ.get("OPENAI_API_BASE")
49
+ or ""
50
+ )
51
+ key = (
52
+ api_key or os.environ.get("OPENAI_API_KEY") or os.environ.get("LLM_API_KEY") or ""
53
+ )
54
+ token = local_token or os.environ.get("KSADK_PROXY_TOKEN") or ""
55
+ if not upstream or not key:
56
+ return None # 缺凭证/上游,不启用(保持原 env)
57
+ cfg = ProxyConfig(upstream_base=upstream, api_key=key, local_token=token)
58
+ srv = ProxyServer(cfg)
59
+ srv.start()
60
+ _proxy = srv
61
+ # 记录原 base 并重定向(双别名都指向 proxy)
62
+ _original_base = os.environ.get("OPENAI_BASE_URL") or os.environ.get("OPENAI_API_BASE")
63
+ os.environ["OPENAI_BASE_URL"] = srv.base_url
64
+ os.environ["OPENAI_API_BASE"] = srv.base_url
65
+ if not token:
66
+ # 非 codex 路径(env 框架)无 token:代理 local_token 为空,仅回环监听才允许
67
+ os.environ["KSADK_PROXY_TOKEN"] = ""
68
+ return srv.base_url
69
+
70
+
71
+ def teardown_proxy_redirect() -> None:
72
+ """回收 proxy 并恢复原 OPENAI_BASE_URL(进程退出/卸载时调)。"""
73
+ global _proxy, _original_base
74
+ if _proxy is not None:
75
+ _proxy.stop()
76
+ _proxy = None
77
+ if _original_base is not None:
78
+ os.environ["OPENAI_BASE_URL"] = _original_base
79
+ os.environ["OPENAI_API_BASE"] = _original_base
80
+ _original_base = None
@@ -0,0 +1,165 @@
1
+ """能力探测缓存(v2.4):租户隔离键 + TTL + singleflight + 主动失效。
2
+
3
+ review v1 指出 ``(model, base_url)`` 缺 credential/tenant 维度:同一网关不同 key
4
+ 能力可能不同(如星流 glm-5.1 的 responses 403 按 consumer 鉴权),404 也可能是
5
+ "模型不存在"而非"endpoint 不存在"。本模块:
6
+
7
+ - 键 = ``(model, base_url, credential_scope)``;credential_scope 用进程内 HMAC
8
+ 标识符,避免明文 key 入键/日志,又能区分不同 key。
9
+ - **明确判定**(supported/unsupported)长缓存(TTL);**不确定**(unknown)不缓存,
10
+ 下次重探——避免把一次抖动固化成长期误判。
11
+ - **singleflight**:并发同键只探一次,其余等结果(用 per-key Future)。
12
+ - **主动失效**:`invalidate(model, base_url, scope)` 与 `clear()` 入口。
13
+ - 撞名/模型不存在的结构化区分由 detect.py 的错误分类负责,本缓存只存结论。
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+ import hashlib
20
+ import secrets
21
+ import threading
22
+ import time
23
+ from dataclasses import dataclass
24
+ from typing import Any, Callable
25
+
26
+ from .detect import ModelCapabilities
27
+
28
+ # The capability cache is process-local, so its credential namespace need not
29
+ # survive a restart. A random salt prevents cache identifiers from being used
30
+ # as an offline oracle for API keys and avoids retaining the original credential.
31
+ _SCOPE_SALT = secrets.token_bytes(16)
32
+ _SCOPE_ITERATIONS = 100_000
33
+
34
+
35
+ def credential_scope(credential: str) -> str:
36
+ """Return a process-local, non-secret cache scope for a credential."""
37
+ return hashlib.pbkdf2_hmac(
38
+ "sha256", credential.encode("utf-8"), _SCOPE_SALT, _SCOPE_ITERATIONS, dklen=16
39
+ ).hex()
40
+
41
+
42
+ @dataclass
43
+ class _Entry:
44
+ caps: ModelCapabilities
45
+ expires_at: float # 0 = 永不过期(明确判定)
46
+
47
+
48
+ class CapabilityCache:
49
+ """能力矩阵缓存:线程安全 + asyncio 安全(单进程内)。"""
50
+
51
+ def __init__(self, ttl: float = 3600.0):
52
+ self._ttl = ttl
53
+ self._lock = threading.Lock()
54
+ self._entries: dict[tuple[str, str, str], _Entry] = {}
55
+ # singleflight:sync per-key Event,async per-key Future
56
+ self._inflight_sync: dict[tuple[str, str, str], threading.Event] = {}
57
+ self._inflight_async: dict[tuple[str, str, str], asyncio.Future] = {}
58
+
59
+ def _key(self, model: str, base: str, scope: str) -> tuple[str, str, str]:
60
+ return (model, base.rstrip("/"), scope)
61
+
62
+ def get(self, model: str, base: str, scope: str) -> ModelCapabilities | None:
63
+ """命中且未过期返回 caps;过期/不存在/不确定(不缓存)返回 None。"""
64
+ k = self._key(model, base, scope)
65
+ with self._lock:
66
+ e = self._entries.get(k)
67
+ if e is None:
68
+ return None
69
+ if e.expires_at and time.time() > e.expires_at:
70
+ self._entries.pop(k, None)
71
+ return None
72
+ return e.caps
73
+
74
+ def put(self, model: str, base: str, scope: str, caps: ModelCapabilities) -> None:
75
+ """只缓存明确判定(supported/unsupported);unknown 不缓存。"""
76
+ if caps.verdict == "unknown":
77
+ return
78
+ k = self._key(model, base, scope)
79
+ expires = time.time() + self._ttl if self._ttl > 0 else 0
80
+ with self._lock:
81
+ self._entries[k] = _Entry(caps=caps, expires_at=expires)
82
+
83
+ def invalidate(self, model: str, base: str, scope: str) -> None:
84
+ k = self._key(model, base, scope)
85
+ with self._lock:
86
+ self._entries.pop(k, None)
87
+
88
+ def clear(self) -> None:
89
+ with self._lock:
90
+ self._entries.clear()
91
+
92
+ # ---- singleflight:并发同键只探一次 ----
93
+ def get_or_probe(
94
+ self,
95
+ model: str,
96
+ base: str,
97
+ scope: str,
98
+ probe: Callable[[str, str], ModelCapabilities],
99
+ ) -> ModelCapabilities:
100
+ """命中直接返回;否则 singleflight 探测(并发同键只探一次),结果入缓存。
101
+
102
+ ``scope`` is a pre-derived credential namespace. ``probe`` is a sync
103
+ callable(model, base) -> ModelCapabilities and closes over credentials.
104
+ """
105
+ cached = self.get(model, base, scope)
106
+ if cached is not None:
107
+ return cached
108
+ k = self._key(model, base, scope)
109
+ with self._lock:
110
+ ev = self._inflight_sync.get(k)
111
+ if ev is not None:
112
+ # 已有并发探测在进行:等它完成,读缓存(探测方负责 put)
113
+ pass
114
+ else:
115
+ ev = threading.Event()
116
+ self._inflight_sync[k] = ev
117
+ ev = None # 标记本线程负责探测
118
+ if ev is not None:
119
+ ev.wait(timeout=30.0)
120
+ return self.get(model, base, scope) or ModelCapabilities(verdict="unknown")
121
+ # 本线程负责探测
122
+ try:
123
+ caps = probe(model, base)
124
+ self.put(model, base, scope, caps)
125
+ return caps
126
+ finally:
127
+ with self._lock:
128
+ ev_done = self._inflight_sync.pop(k, None)
129
+ if ev_done is not None:
130
+ ev_done.set()
131
+
132
+ async def aget_or_probe(
133
+ self,
134
+ model: str,
135
+ base: str,
136
+ scope: str,
137
+ probe: Callable[[str, str], Any],
138
+ ) -> ModelCapabilities:
139
+ """async singleflight 版:probe 是 async callable -> ModelCapabilities。"""
140
+ cached = self.get(model, base, scope)
141
+ if cached is not None:
142
+ return cached
143
+ k = self._key(model, base, scope)
144
+ loop = asyncio.get_running_loop()
145
+ with self._lock:
146
+ fut = self._inflight_async.get(k)
147
+ if fut is None:
148
+ fut = loop.create_future()
149
+ self._inflight_async[k] = fut
150
+ own = True
151
+ else:
152
+ own = False
153
+ if not own:
154
+ await fut # type: ignore[no-any-return]
155
+ # owner 已 put 入缓存;从缓存读结果(不用 fut 的值,它是 None 占位)
156
+ return self.get(model, base, scope) or ModelCapabilities(verdict="unknown")
157
+ try:
158
+ caps: ModelCapabilities = await probe(model, base)
159
+ self.put(model, base, scope, caps)
160
+ return caps
161
+ finally:
162
+ with self._lock:
163
+ self._inflight_async.pop(k, None)
164
+ if not fut.done():
165
+ fut.set_result(None) # 唤醒等待者(它们会读缓存)
@@ -0,0 +1,66 @@
1
+ """ProxyConfig:转换 proxy 的配置(显式注入,不在模块导入时读 env,便于内化与测试)。"""
2
+
3
+ import os
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, Callable
6
+ from urllib.parse import urlsplit
7
+
8
+ # HTTPS(凭证安全):不再用明文 HTTP 携带 Bearer key。
9
+ DEFAULT_UPSTREAM_BASE = "https://kspmas.ksyun.com/v1"
10
+
11
+ _LOOPBACK_HOSTS = {"localhost", "127.0.0.1", "::1"}
12
+
13
+
14
+ @dataclass
15
+ class ProxyConfig:
16
+ upstream_base: str = DEFAULT_UPSTREAM_BASE
17
+ api_key: str = "" # 上游凭证(proxy -> 上游)
18
+ local_token: str = "" # 本地 proxy 鉴权 token(codex -> proxy);空则不校验(仅本地调试)
19
+ # 非空时 responses 转换路径强制改写 model(Codex 可能发送内部伪模型名)。
20
+ upstream_model: str = ""
21
+ timeout: float = 180.0
22
+ event_callback: Callable[[str, dict[str, Any]], None] | None = field(
23
+ default=None,
24
+ repr=False,
25
+ compare=False,
26
+ )
27
+
28
+ def __post_init__(self) -> None:
29
+ self.upstream_base = _require_secure_upstream(self.upstream_base.rstrip("/"))
30
+
31
+ def emit(self, event: str, data: dict[str, Any]) -> None:
32
+ callback = self.event_callback
33
+ if callback is None:
34
+ return
35
+ try:
36
+ callback(event, data)
37
+ except Exception:
38
+ # Observability must never break model traffic.
39
+ return
40
+
41
+ @classmethod
42
+ def from_env(cls) -> "ProxyConfig":
43
+ return cls(
44
+ upstream_base=os.environ.get("UPSTREAM_BASE", DEFAULT_UPSTREAM_BASE),
45
+ api_key=os.environ.get("KSPMAS_API_KEY", ""),
46
+ local_token=os.environ.get("KSADK_PROXY_TOKEN", ""),
47
+ upstream_model=os.environ.get("OPENAI_MODEL_NAME")
48
+ or os.environ.get("MODEL_NAME")
49
+ or "",
50
+ timeout=float(os.environ.get("UPSTREAM_TIMEOUT", "180")),
51
+ )
52
+
53
+
54
+ def _require_secure_upstream(upstream: str) -> str:
55
+ """凭证安全:scheme 仅 http/https;非回环主机必须 https(避免明文携带 Bearer)。
56
+
57
+ 用 urlsplit 规范化 scheme/hostname,堵 `HTTP://`(大小写)与 `ftp://` 绕过。
58
+ """
59
+ parts = urlsplit(upstream)
60
+ scheme = parts.scheme.lower()
61
+ host = (parts.hostname or "").lower()
62
+ if scheme not in ("http", "https"):
63
+ raise ValueError(f"不支持的上游 scheme(仅 http/https): {upstream}")
64
+ if scheme == "http" and host not in _LOOPBACK_HOSTS:
65
+ raise ValueError(f"上游必须使用 https(携带凭证,禁明文 http): {upstream}")
66
+ return upstream
@@ -0,0 +1,115 @@
1
+ """极简 codex 对话 webui(端到端验证用,非生产入口)。
2
+
3
+ 绕过 ksadk web 的框架检测(它不认 codex):直接用 AsyncCodexClient + 一个
4
+ 最小 FastAPI 提供 HTML 页 + /chat SSE,在浏览器里展示 codex runtime 经
5
+ model_proxy 代理跑星流 chat 模型的真实对话流。
6
+
7
+ 用法:
8
+ KSADK_CODEX_USE_PROXY=1 OPENAI_API_BASE=https://kspmas.ksyun.com/v1 \
9
+ OPENAI_API_KEY=xxx CODEX_HOME=/tmp/codex_clean_runtime \
10
+ uv run python -m ksadk.model_proxy.demo_webui --port 8877
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import logging
17
+ import os
18
+
19
+ import uvicorn
20
+ from fastapi import FastAPI, Request
21
+ from fastapi.responses import HTMLResponse, StreamingResponse
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # ruff: noqa: E501 内联 HTML/CSS/JS 演示字面量,长行不拆
26
+
27
+ _PAGE = """<!doctype html><html lang="zh"><head><meta charset="utf-8">
28
+ <title>codex runtime e2e</title>
29
+ <style>
30
+ body{font-family:system-ui,sans-serif;max-width:760px;margin:2rem auto;padding:0 1rem}
31
+ #log{border:1px solid #ddd;border-radius:8px;padding:1rem;min-height:200px;white-space:pre-wrap;font-family:ui-monospace,monospace}
32
+ .u{color:#0969da}.a{color:#1a7f37}.t{color:#666}
33
+ input{width:70%;padding:.5rem}button{padding:.5rem 1rem}
34
+ </style></head><body>
35
+ <h3>codex runtime → model_proxy → 星流 chat</h3>
36
+ <form id="f"><input id="q" placeholder="说点什么..." autocomplete="off">
37
+ <button>发送</button></form>
38
+ <div id="log"></div>
39
+ <script>
40
+ const log=document.getElementById('log');
41
+ function add(cls,text){const d=document.createElement('div');d.className=cls;d.textContent=text;log.appendChild(d);log.scrollTop=log.scrollHeight;}
42
+ document.getElementById('f').onsubmit=async e=>{
43
+ e.preventDefault();const q=document.getElementById('q').value;if(!q)return;
44
+ add('u','你: '+q);document.getElementById('q').value='';const t=document.createElement('div');t.className='t';t.textContent='(流式中...)';log.appendChild(t);
45
+ const r=await fetch('/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({q})});
46
+ const reader=r.body.getReader();const dec=new TextDecoder();t.remove();let buf='';
47
+ while(true){const{value,done}=await reader.read();if(done)break;buf+=dec.decode(value);const lines=buf.split('\\n');buf=lines.pop();
48
+ for(const ln of lines){if(!ln.startsWith('data:'))continue;try{const ev=JSON.parse(ln.slice(5).trim());
49
+ if(ev.delta)add('a',ev.delta);if(ev.reply)add('a','✅ '+ev.reply);if(ev.error)add('t','❌ '+ev.error);}catch{}}}
50
+ };
51
+ </script></body></html>"""
52
+
53
+
54
+ def create_demo_app() -> FastAPI:
55
+ from ksadk.codex.client import AsyncCodexClient
56
+
57
+ app = FastAPI()
58
+ client: dict = {"c": None, "tid": None}
59
+
60
+ @app.get("/", response_class=HTMLResponse)
61
+ def index():
62
+ return _PAGE
63
+
64
+ @app.post("/chat")
65
+ async def chat(req: Request):
66
+ body = await req.json()
67
+ q = body.get("q", "")
68
+
69
+ async def gen():
70
+ if client["c"] is None:
71
+ c = AsyncCodexClient()
72
+ client["c"] = c
73
+ client["tid"] = await c.start_thread(
74
+ config={
75
+ "model": os.environ.get("OPENAI_MODEL_NAME", "glm-5.2"),
76
+ "sandbox_read_only": True,
77
+ }
78
+ )
79
+ try:
80
+ async for ev in client["c"].run_turn(client["tid"], q):
81
+ m = ev.get("method", "")
82
+ if m == "item/agentMessage/delta":
83
+ yield f"data: {json.dumps({'delta': ev['params'].get('delta', '')}, ensure_ascii=False)}\n\n"
84
+ elif m == "item/completed":
85
+ it = ev["params"]["item"]
86
+ if it.get("type") == "agentMessage" and it.get("text"):
87
+ yield f"data: {json.dumps({'reply': it['text']}, ensure_ascii=False)}\n\n"
88
+ elif m == "error":
89
+ logger.warning("Codex demo runtime reported an error")
90
+ yield 'data: {"error":"Codex runtime failed; see local logs."}\n\n'
91
+ except Exception as e: # noqa: BLE001
92
+ logger.warning("Codex demo runtime failed", exc_info=e)
93
+ yield 'data: {"error":"Codex runtime failed; see local logs."}\n\n'
94
+
95
+ return StreamingResponse(gen(), media_type="text/event-stream")
96
+
97
+ @app.on_event("shutdown")
98
+ async def shutdown():
99
+ if client["c"] is not None:
100
+ await client["c"].close()
101
+
102
+ return app
103
+
104
+
105
+ def main():
106
+ import argparse
107
+
108
+ p = argparse.ArgumentParser()
109
+ p.add_argument("--port", type=int, default=8877)
110
+ args = p.parse_args()
111
+ uvicorn.run(create_demo_app(), host="127.0.0.1", port=args.port, log_level="warning")
112
+
113
+
114
+ if __name__ == "__main__":
115
+ main()