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,682 @@
1
+ """Shared runtime server helpers, integrated resources, and health routes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import io
7
+ import logging
8
+ import os
9
+ import uuid
10
+ from pathlib import Path
11
+ from typing import Any, Dict, List, Mapping, Optional
12
+
13
+ import httpx
14
+ from fastapi import FastAPI, HTTPException, Request
15
+ from fastapi.responses import FileResponse, Response
16
+
17
+ from ksadk.conversations.attachments import compact_attachment_result_for_session
18
+ from ksadk.conversations.model_context import normalize_model_metadata
19
+ from ksadk.runtime_state import load_state as load_runtime_state
20
+ from ksadk.server.factory import (
21
+ RuntimeAppState,
22
+ bind_runtime_state,
23
+ get_state,
24
+ )
25
+ from ksadk.server.terminal_sessions import (
26
+ TerminalSessionManager,
27
+ native_terminal_supported,
28
+ register_terminal_routes,
29
+ )
30
+ from ksadk.sessions import Session
31
+ from ksadk.sessions.local_service import resolve_local_session_dir
32
+ from ksadk.ui_config import UI_PROFILE_CUSTOM, resolve_ui_config
33
+ from ksadk_runtime_common.workspace_files import (
34
+ create_workspace_files_router,
35
+ workspace_files_enabled,
36
+ )
37
+
38
+ from . import dependencies as deps
39
+ from .routers import health_meta_router
40
+
41
+ logger = logging.getLogger(__name__)
42
+ STATIC_DIR = Path(__file__).parent.parent / "static"
43
+
44
+ _RESERVED_UI_PATHS = {"/", "/chat", "/build", "/deploy"}
45
+ _CUSTOM_API_PROXY_ENV_KEYS = ("KSADK_USER_BACKEND_URL", "LUOLUO_USER_BACKEND_URL")
46
+ _HOP_BY_HOP_HEADERS = {
47
+ "connection",
48
+ "keep-alive",
49
+ "proxy-authenticate",
50
+ "proxy-authorization",
51
+ "te",
52
+ "trailer",
53
+ "transfer-encoding",
54
+ "upgrade",
55
+ "host",
56
+ "content-length",
57
+ }
58
+
59
+ _TEXT_MIME_PREFIXES = ("text/",)
60
+ _TEXT_MIME_TYPES = {
61
+ "application/json",
62
+ "application/pdf",
63
+ "application/xml",
64
+ "application/yaml",
65
+ "application/x-yaml",
66
+ "application/x-ndjson",
67
+ }
68
+ _TEXT_FILE_EXTENSIONS = {
69
+ ".txt",
70
+ ".md",
71
+ ".markdown",
72
+ ".json",
73
+ ".yaml",
74
+ ".yml",
75
+ ".csv",
76
+ ".tsv",
77
+ ".log",
78
+ ".py",
79
+ ".js",
80
+ ".ts",
81
+ ".jsx",
82
+ ".tsx",
83
+ ".html",
84
+ ".css",
85
+ ".sql",
86
+ ".xml",
87
+ ".sh",
88
+ }
89
+ _MAX_INLINE_BASE64_CHARS = 4_000_000
90
+ _MAX_INLINE_TEXT_CHARS = 20_000
91
+ _MAX_REFERENCE_TEXT_BYTES = 3_000_000
92
+ _UPLOAD_URI_SCHEME = "ksadk-upload://"
93
+
94
+
95
+ def _workspace_root_dir() -> Path:
96
+ return Path(resolve_local_session_dir()) / "workspace"
97
+
98
+
99
+ _NATIVE_TUI_FRAMEWORKS = {"hermes", "openclaw"}
100
+
101
+
102
+ def _current_framework() -> str:
103
+ context = get_state().launch_context
104
+ return str(context.runtime_type if context is not None else "").strip().lower()
105
+
106
+
107
+ def _build_native_terminal_capability(framework: str) -> dict[str, Any]:
108
+ enabled = (
109
+ native_terminal_supported()
110
+ and str(framework or "").strip().lower() in _NATIVE_TUI_FRAMEWORKS
111
+ )
112
+ return {
113
+ "Enabled": enabled,
114
+ "Mode": "tui" if enabled else None,
115
+ "Protocol": "ks-terminal.v1",
116
+ "Path": "/_ksadk/terminal/ws" if enabled else None,
117
+ }
118
+
119
+
120
+ def _register_integrated_routers(app: FastAPI, state: RuntimeAppState) -> None:
121
+ """装配随 app 走的内嵌 router(workspace files + terminal ws)。
122
+
123
+ goal-01:由模块级 ``app.include_router(...)`` 改为在 factory 的 configure
124
+ 回调里调用,普通 app 与 HarnessApp 各自装配,互不共享。
125
+ """
126
+ app.include_router(
127
+ create_workspace_files_router(
128
+ root_getter=_workspace_root_dir,
129
+ enabled_getter=lambda: workspace_files_enabled(default=True),
130
+ )
131
+ )
132
+ state.terminal_manager = TerminalSessionManager(
133
+ workspace_root_getter=_workspace_root_dir,
134
+ framework_getter=_current_framework,
135
+ )
136
+ register_terminal_routes(
137
+ app,
138
+ state.terminal_manager,
139
+ bind_context=lambda: bind_runtime_state(state),
140
+ )
141
+
142
+
143
+ def _resolve_current_model() -> tuple[Optional[str], Optional[str]]:
144
+ candidates = (
145
+ ("OPENAI_MODEL_NAME", os.getenv("OPENAI_MODEL_NAME")),
146
+ ("MODEL_NAME", os.getenv("MODEL_NAME")),
147
+ ("COZE_MODEL_NAME", os.getenv("COZE_MODEL_NAME")),
148
+ )
149
+ for source, value in candidates:
150
+ model = str(value or "").strip()
151
+ if model:
152
+ return model, source
153
+ return None, None
154
+
155
+
156
+ def _build_bootstrap_model_payload() -> Optional[dict[str, Any]]:
157
+ current_model, source = _resolve_current_model()
158
+ if not current_model:
159
+ return None
160
+
161
+ payload = dict(normalize_model_metadata({"id": current_model}))
162
+ payload["source"] = source
163
+ return payload
164
+
165
+
166
+ def _runtime_project_dir() -> Path:
167
+ context = get_state().launch_context
168
+ if context is not None:
169
+ try:
170
+ return Path(str(context.project_dir)).resolve()
171
+ except Exception:
172
+ pass
173
+ return Path(".").resolve()
174
+
175
+
176
+ def _default_custom_ui_bundle_dir(project_dir: Path) -> Path:
177
+ return project_dir / "research-ui" / "dist"
178
+
179
+
180
+ def _ui_state_with_env_fallback(state: dict[str, Any]) -> dict[str, Any]:
181
+ merged = dict(state or {})
182
+ env_fallbacks = {
183
+ "ui_profile": os.environ.get("KSADK_UI_PROFILE"),
184
+ "ui_path": os.environ.get("KSADK_UI_PATH"),
185
+ "ui_url": os.environ.get("KSADK_UI_URL"),
186
+ "ui_bundle_path": os.environ.get("KSADK_UI_BUNDLE_PATH"),
187
+ }
188
+ for key, value in env_fallbacks.items():
189
+ if value and not merged.get(key):
190
+ merged[key] = value
191
+ return merged
192
+
193
+
194
+ def _resolve_agent_ui_spec() -> dict[str, Any]:
195
+ project_dir = _runtime_project_dir()
196
+ state = _ui_state_with_env_fallback(load_runtime_state(project_dir))
197
+ framework = _current_framework()
198
+ auto_custom_bundle_dir = _default_custom_ui_bundle_dir(project_dir)
199
+ if (
200
+ not state.get("ui_profile")
201
+ and not state.get("ui_path")
202
+ and not state.get("ui_url")
203
+ and (auto_custom_bundle_dir / "index.html").exists()
204
+ ):
205
+ state["ui_profile"] = UI_PROFILE_CUSTOM
206
+ state["ui_path"] = "/"
207
+ state["ui_bundle_path"] = str(auto_custom_bundle_dir)
208
+ config = resolve_ui_config(
209
+ framework=framework,
210
+ state=state,
211
+ cli_profile=None,
212
+ cli_path=None,
213
+ cli_url=None,
214
+ )
215
+
216
+ if config.profile == UI_PROFILE_CUSTOM:
217
+ bundle_dir_value = state.get("ui_bundle_path") or state.get("ui_bundle_dir")
218
+ bundle_dir = None
219
+ if bundle_dir_value:
220
+ candidate = Path(str(bundle_dir_value))
221
+ if not candidate.is_absolute():
222
+ candidate = project_dir / candidate
223
+ if candidate.exists():
224
+ bundle_dir = candidate.resolve()
225
+ if bundle_dir is None:
226
+ bundle_dir = _default_custom_ui_bundle_dir(project_dir)
227
+ index_file = bundle_dir / "index.html"
228
+ enabled = bundle_dir.exists() and index_file.exists()
229
+ return {
230
+ "enabled": enabled,
231
+ "profile": config.profile,
232
+ "ui_profile": config.profile,
233
+ "path": config.path,
234
+ "ui_path": config.path,
235
+ "url": config.url,
236
+ "ui_url": config.url,
237
+ "bundle_path": str(bundle_dir),
238
+ "ui_bundle_path": str(bundle_dir),
239
+ "index_path": str(index_file),
240
+ "source": "custom",
241
+ }
242
+
243
+ bundle_dir = STATIC_DIR
244
+ index_file = bundle_dir / "index.html"
245
+ enabled = bundle_dir.exists() and index_file.exists()
246
+ return {
247
+ "enabled": enabled,
248
+ "profile": config.profile,
249
+ "ui_profile": config.profile,
250
+ "path": config.path,
251
+ "ui_path": config.path,
252
+ "url": config.url,
253
+ "ui_url": config.url,
254
+ "bundle_path": str(bundle_dir),
255
+ "ui_bundle_path": str(bundle_dir),
256
+ "index_path": str(index_file),
257
+ "source": "builtin",
258
+ }
259
+
260
+
261
+ def _normalize_request_ui_path(request_path: str) -> str:
262
+ path = "/" + str(request_path or "").lstrip("/")
263
+ return path if path != "//" else "/"
264
+
265
+
266
+ def _is_custom_ui_static_asset_path(relative_path: str) -> bool:
267
+ path = str(relative_path or "").strip("/")
268
+ if not path:
269
+ return False
270
+ first_segment = path.split("/", 1)[0]
271
+ return first_segment == "assets" or bool(Path(path).suffix)
272
+
273
+
274
+ def _find_ui_static_asset(bundle_dir: Path, requested_relative_path: str) -> Path | None:
275
+ """Look up an asset without constructing a filesystem path from HTTP input."""
276
+ requested = str(requested_relative_path or "").strip("/")
277
+ if not requested:
278
+ return None
279
+ root = bundle_dir.resolve()
280
+ if not root.is_dir():
281
+ return None
282
+ for asset in root.rglob("*"):
283
+ if not asset.is_file():
284
+ continue
285
+ resolved = asset.resolve()
286
+ try:
287
+ relative = resolved.relative_to(root).as_posix()
288
+ except ValueError:
289
+ continue
290
+ if relative == requested:
291
+ return resolved
292
+ return None
293
+
294
+
295
+ def _resolve_ui_static_response(request_path: str) -> Optional[FileResponse]:
296
+ spec = _resolve_agent_ui_spec()
297
+ if not spec.get("enabled"):
298
+ return None
299
+
300
+ bundle_dir = Path(str(spec["bundle_path"]))
301
+ index_file = Path(str(spec["index_path"]))
302
+ path = _normalize_request_ui_path(request_path)
303
+
304
+ if spec.get("source") == "custom":
305
+ ui_path = _normalize_request_ui_path(str(spec.get("path") or "/")).rstrip("/") or "/"
306
+ if path == ui_path or path == f"{ui_path}/":
307
+ return FileResponse(index_file)
308
+ if ui_path != "/" and not path.startswith(f"{ui_path}/"):
309
+ return None
310
+ relative = path[len(ui_path) :].lstrip("/") if ui_path != "/" else path.lstrip("/")
311
+ if not relative:
312
+ return FileResponse(index_file)
313
+ asset = _find_ui_static_asset(bundle_dir, relative)
314
+ if asset is not None:
315
+ return FileResponse(asset)
316
+ if not _is_custom_ui_static_asset_path(relative):
317
+ return FileResponse(index_file)
318
+ return None
319
+
320
+ if path in _RESERVED_UI_PATHS:
321
+ return FileResponse(index_file)
322
+
323
+ asset = _find_ui_static_asset(bundle_dir, path.lstrip("/"))
324
+ if asset is not None:
325
+ return FileResponse(asset)
326
+ return None
327
+
328
+
329
+ def _is_textual_mime(mime_type: str) -> bool:
330
+ mime = (mime_type or "").lower()
331
+ if not mime:
332
+ return False
333
+ return mime.startswith(_TEXT_MIME_PREFIXES) or mime in _TEXT_MIME_TYPES
334
+
335
+
336
+ def _looks_like_textual_attachment(mime_type: str, display_name: str) -> bool:
337
+ suffix = Path(display_name or "").suffix.lower()
338
+ return _is_textual_mime(mime_type) or suffix in _TEXT_FILE_EXTENSIONS
339
+
340
+
341
+ def _extract_pdf_text(raw: bytes) -> str:
342
+ try:
343
+ from pypdf import PdfReader
344
+ except Exception:
345
+ return ""
346
+
347
+ try:
348
+ reader = PdfReader(io.BytesIO(raw))
349
+ except Exception:
350
+ return ""
351
+
352
+ segments: List[str] = []
353
+ for page in reader.pages[:10]:
354
+ try:
355
+ page_text = page.extract_text() or ""
356
+ except Exception:
357
+ page_text = ""
358
+ if page_text:
359
+ segments.append(page_text)
360
+
361
+ return "\n".join(segments).strip()
362
+
363
+
364
+ def _decode_inline_data(data_b64: str) -> bytes:
365
+ return bytes(base64.b64decode((data_b64 or "").strip() + "==="))
366
+
367
+
368
+ def _resolve_uploads_dir() -> Path:
369
+ uploads_dir = Path(resolve_local_session_dir()) / "files"
370
+ uploads_dir.mkdir(parents=True, exist_ok=True)
371
+ return uploads_dir
372
+
373
+
374
+ def _resolve_attachment_storage_path(file_uri: str) -> Optional[Path]:
375
+ normalized_uri = (file_uri or "").strip()
376
+ if not normalized_uri:
377
+ return None
378
+
379
+ if normalized_uri.startswith("local:"):
380
+ path = Path(normalized_uri[6:]).expanduser()
381
+ return path.resolve()
382
+
383
+ if normalized_uri.startswith(_UPLOAD_URI_SCHEME):
384
+ file_id = normalized_uri.removeprefix(_UPLOAD_URI_SCHEME).strip("/")
385
+ if not file_id:
386
+ return None
387
+
388
+ for candidate in sorted(_resolve_uploads_dir().glob(f"{file_id}*")):
389
+ if candidate.is_file():
390
+ return candidate.resolve()
391
+
392
+ return None
393
+
394
+
395
+ def _custom_api_proxy_base_url() -> str:
396
+ for key in _CUSTOM_API_PROXY_ENV_KEYS:
397
+ value = os.environ.get(key)
398
+ if value and value.strip():
399
+ return value.strip().rstrip("/")
400
+ return ""
401
+
402
+
403
+ def _proxy_headers(headers: Mapping[str, str]) -> dict[str, str]:
404
+ return {key: value for key, value in headers.items() if key.lower() not in _HOP_BY_HOP_HEADERS}
405
+
406
+
407
+ def _response_headers(headers: Mapping[str, str]) -> dict[str, str]:
408
+ return {key: value for key, value in headers.items() if key.lower() not in _HOP_BY_HOP_HEADERS}
409
+
410
+
411
+ @health_meta_router.api_route(
412
+ "/api/{proxy_path:path}",
413
+ methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
414
+ include_in_schema=False,
415
+ )
416
+ async def custom_api_proxy(proxy_path: str, request: Request):
417
+ base_url = _custom_api_proxy_base_url()
418
+ if not base_url:
419
+ raise HTTPException(status_code=404, detail="Custom API backend is not configured")
420
+
421
+ path = f"/api/{proxy_path.lstrip('/')}"
422
+ query = request.url.query
423
+ target_url = f"{base_url}{path}"
424
+ if query:
425
+ target_url = f"{target_url}?{query}"
426
+
427
+ try:
428
+ async with httpx.AsyncClient(timeout=60.0) as client:
429
+ upstream = await client.request(
430
+ request.method,
431
+ target_url,
432
+ content=await request.body(),
433
+ headers=_proxy_headers(request.headers),
434
+ )
435
+ except httpx.HTTPError as exc:
436
+ raise HTTPException(
437
+ status_code=502, detail=f"Custom API backend unavailable: {exc}"
438
+ ) from exc
439
+
440
+ return Response(
441
+ content=upstream.content,
442
+ status_code=upstream.status_code,
443
+ headers=_response_headers(upstream.headers),
444
+ media_type=upstream.headers.get("content-type"),
445
+ )
446
+
447
+
448
+ def _read_attachment_bytes(
449
+ storage_path: Optional[Path], *, size_limit: Optional[int] = None
450
+ ) -> Optional[bytes]:
451
+ if storage_path is None or not storage_path.is_file():
452
+ return None
453
+
454
+ try:
455
+ if size_limit is not None and storage_path.stat().st_size > size_limit:
456
+ return None
457
+ return storage_path.read_bytes()
458
+ except OSError:
459
+ return None
460
+
461
+
462
+ def _extract_inline_attachment_text(*, display_name: str, mime_type: str, raw: bytes) -> str:
463
+ if mime_type == "application/pdf" or display_name.lower().endswith(".pdf"):
464
+ text = _extract_pdf_text(raw)
465
+ if not text:
466
+ return ""
467
+ if len(text) > _MAX_INLINE_TEXT_CHARS:
468
+ return text[:_MAX_INLINE_TEXT_CHARS] + "\n...[内容已截断]"
469
+ return text
470
+
471
+ if _looks_like_textual_attachment(mime_type, display_name):
472
+ text = raw.decode("utf-8", errors="ignore")
473
+ if len(text) > _MAX_INLINE_TEXT_CHARS:
474
+ return text[:_MAX_INLINE_TEXT_CHARS] + "\n...[内容已截断]"
475
+ return text
476
+
477
+ return ""
478
+
479
+
480
+ def _attachment_prompt_text(attachment: Dict[str, Any]) -> str:
481
+ display_name = str(attachment.get("display_name") or "uploaded_file")
482
+ mime_type = str(attachment.get("mime_type") or "application/octet-stream")
483
+ transport = str(attachment.get("transport") or "")
484
+
485
+ if transport == "inline":
486
+ data_b64 = str(attachment.get("data") or "").strip()
487
+ if len(data_b64) > _MAX_INLINE_BASE64_CHARS:
488
+ return (
489
+ f"[上传文件: {display_name}, mime={mime_type or 'unknown'}, 内容过大,未直接展开]"
490
+ )
491
+
492
+ try:
493
+ raw = _decode_inline_data(data_b64)
494
+ except Exception:
495
+ return f"[上传文件: {display_name}, 内容解码失败]"
496
+
497
+ text = _extract_inline_attachment_text(
498
+ display_name=display_name,
499
+ mime_type=mime_type,
500
+ raw=raw,
501
+ )
502
+ if text:
503
+ return f"[上传文件: {display_name}]\n{text}"
504
+ return (
505
+ "[上传文件: "
506
+ f"{display_name}, "
507
+ f"mime={mime_type or 'application/octet-stream'}, "
508
+ f"bytes={len(raw)}]"
509
+ )
510
+
511
+ storage_path_value = attachment.get("storage_path")
512
+ storage_path = Path(str(storage_path_value)) if storage_path_value else None
513
+ size_bytes = attachment.get("size_bytes")
514
+ if size_bytes is None and storage_path is not None and storage_path.exists():
515
+ try:
516
+ size_bytes = storage_path.stat().st_size
517
+ except OSError:
518
+ size_bytes = None
519
+
520
+ reference_bytes = _read_attachment_bytes(storage_path, size_limit=_MAX_REFERENCE_TEXT_BYTES)
521
+ if reference_bytes is not None:
522
+ text = _extract_inline_attachment_text(
523
+ display_name=display_name,
524
+ mime_type=mime_type,
525
+ raw=reference_bytes,
526
+ )
527
+ if text:
528
+ return f"[上传文件: {display_name}]\n{text}"
529
+ return (
530
+ "[上传文件: "
531
+ f"{display_name}, "
532
+ f"mime={mime_type or 'application/octet-stream'}, "
533
+ f"bytes={len(reference_bytes)}]"
534
+ )
535
+
536
+ if size_bytes and size_bytes > _MAX_REFERENCE_TEXT_BYTES:
537
+ return (
538
+ "[上传文件: "
539
+ f"{display_name}, "
540
+ f"mime={mime_type or 'unknown'}, "
541
+ f"bytes={size_bytes}, "
542
+ "内容过大,未直接展开]"
543
+ )
544
+
545
+ file_uri = attachment.get("file_uri") or ""
546
+ return f"[上传文件引用: {display_name or file_uri}, mime={mime_type or 'unknown'}]"
547
+
548
+
549
+ def _extract_user_input_from_parts(parts: List[Any]) -> str:
550
+ """兼容旧测试/旧调用点,统一复用 conversations 层的规范化逻辑。"""
551
+
552
+ return str(deps.conversation().extract_user_input_from_parts(parts))
553
+
554
+
555
+ def _attachment_from_part(part: Any) -> Optional[Dict[str, Any]]:
556
+ """兼容旧入口,真实实现已经收口到 conversations.normalize。"""
557
+
558
+ attachment = deps.conversation().attachment_from_part(part)
559
+ return dict(attachment) if isinstance(attachment, Mapping) else None
560
+
561
+
562
+ async def _hydrate_session(session: Optional[Session]) -> Optional[Session]:
563
+ if not session:
564
+ return None
565
+ session.events = await deps.resolve_session_service().get_events(session.id)
566
+ return session
567
+
568
+
569
+ async def _ensure_session(agent_id: str, user_id: str, session_id: Optional[str]) -> Session:
570
+ service = deps.resolve_session_service()
571
+ if session_id:
572
+ existing = await service.get_session(session_id)
573
+ if existing:
574
+ if existing.agent_id != agent_id or existing.user_id != user_id:
575
+ raise HTTPException(
576
+ status_code=409,
577
+ detail="Session id belongs to a different agent or user",
578
+ )
579
+ return await _hydrate_session(existing) or existing
580
+ created = await service.create_session(agent_id, user_id, session_id=session_id)
581
+ return await _hydrate_session(created) or created
582
+
583
+ created = await service.create_session(agent_id, user_id)
584
+ return await _hydrate_session(created) or created
585
+
586
+
587
+ def _sanitize_session_state_for_action(state: Mapping[str, Any] | None) -> dict[str, Any]:
588
+ sanitized = dict(state or {})
589
+ attachment_context = sanitized.get(deps.conversation().runtime.ATTACHMENT_CONTEXT_STATE_KEY)
590
+ if not isinstance(attachment_context, Mapping):
591
+ return sanitized
592
+
593
+ attachments = [
594
+ deps.conversation().compact_attachment_for_session(item)
595
+ for item in attachment_context.get("attachments") or []
596
+ if isinstance(item, dict)
597
+ ]
598
+ attachment_results = [
599
+ compact_attachment_result_for_session(item)
600
+ for item in attachment_context.get("attachment_results") or []
601
+ if isinstance(item, dict)
602
+ ]
603
+ sanitized[deps.conversation().runtime.ATTACHMENT_CONTEXT_STATE_KEY] = {
604
+ "attachments": attachments,
605
+ "attachment_results": attachment_results,
606
+ }
607
+ return sanitized
608
+
609
+
610
+ def _request_id() -> str:
611
+ return f"req-{uuid.uuid4().hex[:12]}"
612
+
613
+
614
+ def _action_response(
615
+ action: str, data: Any, *, request_id: Optional[str] = None, message: str = "Success"
616
+ ) -> dict:
617
+ payload = {
618
+ "Code": 0,
619
+ "Message": message,
620
+ "RequestId": request_id or _request_id(),
621
+ "Data": data,
622
+ }
623
+ if action:
624
+ payload["Action"] = action
625
+ return payload
626
+
627
+
628
+ async def _workspace_runtime_request(
629
+ method: str,
630
+ runtime_path: str,
631
+ *,
632
+ params: Optional[Dict[str, Any]] = None,
633
+ files: Optional[Dict[str, Any]] = None,
634
+ ) -> httpx.Response:
635
+ runtime_app = get_state().app
636
+ if runtime_app is None:
637
+ raise RuntimeError("runtime app is not bound to the current request")
638
+ transport = httpx.ASGITransport(app=runtime_app)
639
+ async with httpx.AsyncClient(transport=transport, base_url="http://ksadk.local") as client:
640
+ response = await client.request(
641
+ method,
642
+ runtime_path,
643
+ params=params,
644
+ files=files,
645
+ )
646
+
647
+ if response.status_code >= 400:
648
+ detail = response.text
649
+ try:
650
+ payload = response.json()
651
+ except Exception:
652
+ payload = None
653
+ if isinstance(payload, dict):
654
+ detail = str(payload.get("detail") or detail)
655
+ raise HTTPException(
656
+ status_code=response.status_code, detail=detail or "Workspace request failed"
657
+ )
658
+ return response
659
+
660
+
661
+ # ============================================================
662
+ # Core ADK API Endpoints
663
+ # ============================================================
664
+
665
+
666
+ @health_meta_router.get("/health")
667
+ async def health_check():
668
+ context = get_state().launch_context
669
+ framework = context.runtime_type if context is not None else "unknown"
670
+ detection = context.detection if context is not None else None
671
+ agent_name = str(getattr(detection, "name", "") or "unknown")
672
+ return {"status": "ok", "framework": framework, "agent": agent_name}
673
+
674
+
675
+ @health_meta_router.get("/list-apps")
676
+ async def list_apps(relative_path: str = "./"):
677
+ """Return available apps. For KsADK single-agent mode, returns the current agent."""
678
+ del relative_path
679
+ context = get_state().launch_context
680
+ detection = context.detection if context is not None else None
681
+ name = str(getattr(detection, "name", "") or "default_agent")
682
+ return [name]