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/a2a/identity.py ADDED
@@ -0,0 +1,304 @@
1
+ """Trusted Gateway identity boundary for inbound A2A protocol requests."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from dataclasses import dataclass
7
+ from typing import Any, Awaitable, Callable
8
+
9
+ from starlette.middleware.base import BaseHTTPMiddleware
10
+ from starlette.requests import Request
11
+ from starlette.responses import JSONResponse
12
+
13
+ from ksadk.a2a.context_store import A2AContextIdentity
14
+ from ksadk.a2a.ids import require_a2a_resource_id
15
+
16
+ _CALLER_PRINCIPAL_TYPES = frozenset({"user", "runtime", "service"})
17
+ _AUTHN_MODE_BY_CALLER_TYPE = {
18
+ "user": "api_key",
19
+ "runtime": "workload_permit",
20
+ "service": "gateway_service",
21
+ }
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class A2AIngressTargetBinding:
26
+ """The Gateway-verified destination to which one Runtime is bound."""
27
+
28
+ account_id: str
29
+ tenant_id: str
30
+ agent_id: str
31
+ runtime_id: str
32
+ a2a_agent_id: str
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class A2AIngressIdentity:
37
+ """Caller identity verified by Gateway mTLS and target-bound forwarding."""
38
+
39
+ account_id: str
40
+ tenant_id: str
41
+ caller_principal_type: str
42
+ caller_principal_id: str
43
+ target_agent_id: str
44
+ target_runtime_id: str
45
+ target_a2a_agent_id: str
46
+ authn_mode: str
47
+ caller_runtime_id: str | None = None
48
+
49
+ def validate(self) -> None:
50
+ values = {
51
+ "account_id": self.account_id,
52
+ "caller_principal_type": self.caller_principal_type,
53
+ "caller_principal_id": self.caller_principal_id,
54
+ "target_agent_id": self.target_agent_id,
55
+ "target_runtime_id": self.target_runtime_id,
56
+ "target_a2a_agent_id": self.target_a2a_agent_id,
57
+ "authn_mode": self.authn_mode,
58
+ }
59
+ missing = [name for name, value in values.items() if not str(value).strip()]
60
+ if missing:
61
+ raise PermissionError(
62
+ "verified Gateway identity is missing " + ", ".join(missing)
63
+ )
64
+ if self.caller_principal_type not in _CALLER_PRINCIPAL_TYPES:
65
+ raise PermissionError("verified Gateway identity has an unsupported caller type")
66
+ if self.authn_mode != _AUTHN_MODE_BY_CALLER_TYPE[self.caller_principal_type]:
67
+ raise PermissionError("verified Gateway identity has an invalid authentication mode")
68
+ if self.caller_principal_type == "runtime" and not str(
69
+ self.caller_runtime_id or ""
70
+ ).strip():
71
+ raise PermissionError("verified runtime caller identity is missing caller runtime")
72
+ if self.caller_principal_type != "runtime" and self.caller_runtime_id is not None:
73
+ raise PermissionError("verified non-runtime caller identity includes a caller runtime")
74
+ for field_name, limit in (
75
+ ("account_id", 64),
76
+ ("tenant_id", 64),
77
+ ("caller_principal_type", 16),
78
+ ("caller_principal_id", 128),
79
+ ("target_agent_id", 64),
80
+ ("target_runtime_id", 64),
81
+ ("target_a2a_agent_id", 64),
82
+ ("authn_mode", 32),
83
+ ("caller_runtime_id", 64),
84
+ ):
85
+ value = getattr(self, field_name)
86
+ if value is not None and len(str(value)) > limit:
87
+ raise PermissionError(
88
+ f"verified Gateway identity field {field_name} exceeds {limit} characters"
89
+ )
90
+ if not self.target_agent_id.startswith("ar-"):
91
+ raise PermissionError("verified Gateway identity has an invalid target Agent")
92
+ try:
93
+ require_a2a_resource_id(
94
+ self.target_a2a_agent_id,
95
+ "a2a-agent-",
96
+ field_name="verified Gateway identity target A2A Agent",
97
+ )
98
+ except ValueError as exc:
99
+ raise PermissionError(
100
+ "verified Gateway identity has an invalid target A2A Agent"
101
+ ) from exc
102
+
103
+ def context_identity(self) -> A2AContextIdentity:
104
+ return A2AContextIdentity(
105
+ account_id=self.account_id,
106
+ tenant_id=self.tenant_id,
107
+ caller_principal_type=self.caller_principal_type,
108
+ caller_principal_id=self.caller_principal_id,
109
+ )
110
+
111
+ def owner_key(self) -> str:
112
+ return "/".join(
113
+ (
114
+ self.account_id.strip(),
115
+ self.tenant_id.strip(),
116
+ self.caller_principal_type.strip(),
117
+ self.caller_principal_id.strip(),
118
+ )
119
+ )
120
+
121
+
122
+ class GatewayIdentityVerifier(ABC):
123
+ """Platform adapter that verifies Gateway mTLS and target-bound forwarding."""
124
+
125
+ @abstractmethod
126
+ async def verify(self, request: Request) -> A2AIngressIdentity:
127
+ raise NotImplementedError
128
+
129
+
130
+ class GatewayProbeVerifier(ABC):
131
+ """Verifies a trusted Gateway/server probe for the Runtime-local AgentCard."""
132
+
133
+ @abstractmethod
134
+ async def verify_probe(self, request: Request) -> None:
135
+ raise NotImplementedError
136
+
137
+
138
+ class CallableGatewayIdentityVerifier(GatewayIdentityVerifier):
139
+ """Small adapter for product injectors and tests."""
140
+
141
+ def __init__(
142
+ self,
143
+ verify: Callable[[Request], Awaitable[A2AIngressIdentity]],
144
+ ) -> None:
145
+ self._verify = verify
146
+
147
+ async def verify(self, request: Request) -> A2AIngressIdentity:
148
+ return await self._verify(request)
149
+
150
+
151
+ class CallableGatewayProbeVerifier(GatewayProbeVerifier):
152
+ """Small probe-verifier adapter for product injectors and tests."""
153
+
154
+ def __init__(self, verify_probe: Callable[[Request], Awaitable[None]]) -> None:
155
+ self._verify_probe = verify_probe
156
+
157
+ async def verify_probe(self, request: Request) -> None:
158
+ await self._verify_probe(request)
159
+
160
+
161
+ class A2ATrustedIdentityResolver:
162
+ """Reads only identity written by verified ingress middleware."""
163
+
164
+ state_key = "a2a_identity"
165
+
166
+ def __init__(
167
+ self,
168
+ *,
169
+ expected_account_id: str | None = None,
170
+ expected_tenant_id: str | None = None,
171
+ expected_target_agent_id: str | None = None,
172
+ expected_target_runtime_id: str | None = None,
173
+ expected_target_a2a_agent_id: str | None = None,
174
+ target_binding: A2AIngressTargetBinding | None = None,
175
+ ) -> None:
176
+ if target_binding is not None:
177
+ if any(
178
+ value is not None
179
+ for value in (
180
+ expected_account_id,
181
+ expected_tenant_id,
182
+ expected_target_agent_id,
183
+ expected_target_runtime_id,
184
+ expected_target_a2a_agent_id,
185
+ )
186
+ ):
187
+ raise ValueError(
188
+ "pass target_binding or individual expected target fields, not both"
189
+ )
190
+ expected_account_id = target_binding.account_id
191
+ expected_tenant_id = target_binding.tenant_id
192
+ expected_target_agent_id = target_binding.agent_id
193
+ expected_target_runtime_id = target_binding.runtime_id
194
+ expected_target_a2a_agent_id = target_binding.a2a_agent_id
195
+ self._expected_account_id = str(expected_account_id or "").strip() or None
196
+ self._expected_tenant_id = expected_tenant_id
197
+ self._expected_target_agent_id = str(expected_target_agent_id or "").strip() or None
198
+ self._expected_target_runtime_id = str(expected_target_runtime_id or "").strip() or None
199
+ self._expected_target_a2a_agent_id = str(expected_target_a2a_agent_id or "").strip() or None
200
+
201
+ def resolve(self, request: Request) -> A2AIngressIdentity:
202
+ raw_state = request.scope.get("state")
203
+ state = raw_state if isinstance(raw_state, dict) else {}
204
+ identity = state.get(self.state_key)
205
+ if not isinstance(identity, A2AIngressIdentity):
206
+ raise PermissionError("verified Gateway identity is required for inbound A2A")
207
+ self._validate(identity)
208
+ return identity
209
+
210
+ def _validate(self, identity: A2AIngressIdentity) -> None:
211
+ identity.validate()
212
+ identity.context_identity().canonical_owner()
213
+ if (
214
+ self._expected_account_id is not None
215
+ and identity.account_id != self._expected_account_id
216
+ ):
217
+ raise PermissionError("verified Gateway identity targets a different account")
218
+ if (
219
+ self._expected_tenant_id is not None
220
+ and identity.tenant_id != self._expected_tenant_id
221
+ ):
222
+ raise PermissionError("verified Gateway identity targets a different tenant")
223
+ if (
224
+ self._expected_target_agent_id is not None
225
+ and identity.target_agent_id != self._expected_target_agent_id
226
+ ):
227
+ raise PermissionError("verified Gateway identity targets a different Agent")
228
+ if (
229
+ self._expected_target_runtime_id is not None
230
+ and identity.target_runtime_id != self._expected_target_runtime_id
231
+ ):
232
+ raise PermissionError("verified Gateway identity targets a different Runtime")
233
+ if (
234
+ self._expected_target_a2a_agent_id is not None
235
+ and identity.target_a2a_agent_id != self._expected_target_a2a_agent_id
236
+ ):
237
+ raise PermissionError("verified Gateway identity targets a different A2A Agent")
238
+
239
+
240
+ class A2AGatewayIdentityMiddleware(BaseHTTPMiddleware):
241
+ """Verify Gateway identity before JSON-RPC or HTTP+JSON A2A handlers run."""
242
+
243
+ def __init__(
244
+ self,
245
+ app: Any,
246
+ *,
247
+ verifier: GatewayIdentityVerifier,
248
+ probe_verifier: GatewayProbeVerifier | None = None,
249
+ expected_account_id: str | None = None,
250
+ expected_tenant_id: str | None = None,
251
+ expected_target_agent_id: str | None = None,
252
+ expected_target_runtime_id: str | None = None,
253
+ expected_target_a2a_agent_id: str | None = None,
254
+ target_binding: A2AIngressTargetBinding | None = None,
255
+ ) -> None:
256
+ super().__init__(app)
257
+ self._verifier = verifier
258
+ self._probe_verifier = probe_verifier
259
+ self._identity_resolver = A2ATrustedIdentityResolver(
260
+ expected_account_id=expected_account_id,
261
+ expected_tenant_id=expected_tenant_id,
262
+ expected_target_agent_id=expected_target_agent_id,
263
+ expected_target_runtime_id=expected_target_runtime_id,
264
+ expected_target_a2a_agent_id=expected_target_a2a_agent_id,
265
+ target_binding=target_binding,
266
+ )
267
+
268
+ async def dispatch(self, request: Request, call_next: Any):
269
+ if request.url.path == "/.well-known/agent-card.json":
270
+ try:
271
+ if self._probe_verifier is None:
272
+ raise PermissionError("A2A AgentCard probe verifier is not configured")
273
+ await self._probe_verifier.verify_probe(request)
274
+ except Exception:
275
+ return JSONResponse(
276
+ status_code=401,
277
+ content={"detail": "trusted Gateway identity is required for A2A AgentCard"},
278
+ )
279
+ elif request.url.path == "/a2a/jsonrpc" or request.url.path.startswith("/a2a/v1/"):
280
+ try:
281
+ identity = await self._verifier.verify(request)
282
+ self._identity_resolver._validate(identity)
283
+ except Exception:
284
+ return JSONResponse(
285
+ status_code=401,
286
+ content={"detail": "verified Gateway identity is required for inbound A2A"},
287
+ )
288
+ raw_state = request.scope.get("state")
289
+ state = raw_state if isinstance(raw_state, dict) else {}
290
+ state[A2ATrustedIdentityResolver.state_key] = identity
291
+ request.scope["state"] = state
292
+ return await call_next(request)
293
+
294
+
295
+ __all__ = [
296
+ "A2AGatewayIdentityMiddleware",
297
+ "A2AIngressTargetBinding",
298
+ "A2AIngressIdentity",
299
+ "A2ATrustedIdentityResolver",
300
+ "CallableGatewayIdentityVerifier",
301
+ "CallableGatewayProbeVerifier",
302
+ "GatewayIdentityVerifier",
303
+ "GatewayProbeVerifier",
304
+ ]
ksadk/a2a/ids.py ADDED
@@ -0,0 +1,22 @@
1
+ """AgentEngine A2A resource ID validation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ _UUID4_HEX = re.compile(r"^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$")
8
+
9
+
10
+ def is_a2a_resource_id(value: str, prefix: str) -> bool:
11
+ """Return whether value is `<prefix><uuid4_hex>` with a lowercase compact UUID."""
12
+ return value.startswith(prefix) and bool(_UUID4_HEX.fullmatch(value.removeprefix(prefix)))
13
+
14
+
15
+ def require_a2a_resource_id(value: str, prefix: str, *, field_name: str) -> str:
16
+ """Validate an AgentEngine A2A resource ID and return it unchanged."""
17
+ if not is_a2a_resource_id(value, prefix):
18
+ raise ValueError(f"{field_name} must use the {prefix}<uuid4_hex> format")
19
+ return value
20
+
21
+
22
+ __all__ = ["is_a2a_resource_id", "require_a2a_resource_id"]
ksadk/a2a/langgraph.py ADDED
@@ -0,0 +1,285 @@
1
+ """LangGraph 编排方调远端 A2A agent 的流式 helper。
2
+
3
+ ADK 有原生 ``RemoteA2aAgent``;LangGraph 没有等价封装。这里提供结构化流式
4
+ helper,保留远端 ``thinking`` / ``text`` 类型,并可直接写入 LangGraph custom
5
+ stream,无需 task store、无需 Space 绑定——直接走 a2a-sdk 原生 client。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import inspect
11
+ import uuid
12
+ from collections.abc import AsyncIterator
13
+ from typing import Any, Callable, Literal, TypedDict
14
+
15
+ import httpx
16
+ from a2a.client import ClientCallContext, ClientConfig, create_client
17
+ from a2a.client.card_resolver import A2ACardResolver
18
+ from a2a.types import Message, Part, Role, SendMessageRequest
19
+
20
+
21
+ class A2AStreamEvent(TypedDict):
22
+ """可直接交给 LangGraph custom writer 的远端流式事件。"""
23
+
24
+ type: Literal["thinking", "text"]
25
+ delta: str
26
+ replace: bool
27
+
28
+
29
+ def _base_url(agent_card_url: str) -> str:
30
+ """从 agent_card_url 取 base_url(剥掉 well-known 路径后缀)。"""
31
+ url = agent_card_url.strip().rstrip("/")
32
+ for suffix in ("/.well-known/agent-card.json", "/.well-known/agent.json"):
33
+ if url.endswith(suffix):
34
+ url = url[: -len(suffix)]
35
+ return url
36
+
37
+
38
+ async def stream_a2a_agent_events(
39
+ agent_card_url: str,
40
+ message: str,
41
+ *,
42
+ httpx_client: httpx.AsyncClient | None = None,
43
+ timeout: float = 60.0,
44
+ ) -> AsyncIterator[A2AStreamEvent]:
45
+ """流式调用远端 A2A agent,保留 reasoning 与正文的结构化类型。
46
+
47
+ 在 langgraph node 里配合 custom stream 透传::
48
+
49
+ from langgraph.config import get_stream_writer
50
+ from ksadk.a2a.langgraph import stream_a2a_agent_events
51
+
52
+ async def call_demo(state, config):
53
+ writer = get_stream_writer()
54
+ collected = ""
55
+ async for event in stream_a2a_agent_events(
56
+ "http://127.0.0.1:8094", state["query"]
57
+ ):
58
+ writer(event)
59
+ if event["type"] == "text":
60
+ collected += event["delta"]
61
+ return {"result": collected}
62
+
63
+ Args:
64
+ agent_card_url: 远端 agent 的 base URL(如 ``http://127.0.0.1:8094``)
65
+ 或完整 agent-card.json URL。
66
+ message: 发给远端 agent 的用户消息。
67
+ httpx_client: 可选复用的 ``httpx.AsyncClient``;不传则内部创建。
68
+ timeout: httpx 请求超时秒数。
69
+
70
+ Yields:
71
+ ``{"type": "thinking"|"text", "delta": str, "replace": bool}``。
72
+ reasoning 由 ``adk_thought`` Part metadata 识别,status_update 不伪装
73
+ 成思考;``replace`` 标识权威快照替换既有同类输出。
74
+ """
75
+ own_client = httpx_client is None
76
+ client_httpx = httpx_client or httpx.AsyncClient(timeout=timeout)
77
+ try:
78
+ resolver = A2ACardResolver(httpx_client=client_httpx, base_url=_base_url(agent_card_url))
79
+ card = await resolver.get_agent_card()
80
+ client = await create_client(
81
+ agent=card,
82
+ client_config=ClientConfig(httpx_client=client_httpx, streaming=True),
83
+ )
84
+ request = SendMessageRequest(
85
+ message=Message(
86
+ role=Role.ROLE_USER,
87
+ parts=[Part(text=message)],
88
+ message_id=f"lg-a2a-{uuid.uuid4().hex}",
89
+ )
90
+ )
91
+ snapshots: dict[tuple[str, str], str] = {}
92
+ async for event in client.send_message(request, context=ClientCallContext()):
93
+ for stream_event in _extract_artifact_events(event, snapshots):
94
+ yield stream_event
95
+ finally:
96
+ if own_client:
97
+ await client_httpx.aclose()
98
+
99
+
100
+ async def stream_a2a_agent(
101
+ agent_card_url: str,
102
+ message: str,
103
+ *,
104
+ httpx_client: httpx.AsyncClient | None = None,
105
+ timeout: float = 60.0,
106
+ ) -> AsyncIterator[str]:
107
+ """兼容旧接口:只产出正文文本增量,reasoning 不混入最终答案。
108
+
109
+ 该字符串流无法表达 ``replace`` 语义。需要正确处理远端权威快照的调用方
110
+ 应使用 ``stream_a2a_agent_events`` 或 ``stream_a2a_agent_to_writer``。
111
+ """
112
+ async for event in stream_a2a_agent_events(
113
+ agent_card_url,
114
+ message,
115
+ httpx_client=httpx_client,
116
+ timeout=timeout,
117
+ ):
118
+ if event["type"] == "text":
119
+ yield event["delta"]
120
+
121
+
122
+ async def stream_a2a_agent_to_writer(
123
+ agent_card_url: str,
124
+ message: str,
125
+ *,
126
+ writer: Callable[[A2AStreamEvent], Any] | None = None,
127
+ httpx_client: httpx.AsyncClient | None = None,
128
+ timeout: float = 60.0,
129
+ ) -> str:
130
+ """将远端 typed events 写入 LangGraph custom stream,返回纯正文结果。
131
+
132
+ ``writer`` 不传时自动使用当前 LangGraph node 的 ``get_stream_writer()``。
133
+ demo 只需调用本函数,无需了解 A2A wire artifact、append 或快照去重。
134
+ """
135
+ if writer is None:
136
+ from langgraph.config import get_stream_writer
137
+
138
+ writer = get_stream_writer()
139
+
140
+ output: list[str] = []
141
+ async for event in stream_a2a_agent_events(
142
+ agent_card_url,
143
+ message,
144
+ httpx_client=httpx_client,
145
+ timeout=timeout,
146
+ ):
147
+ write_result = writer(event)
148
+ if inspect.isawaitable(write_result):
149
+ await write_result
150
+ if event["type"] == "text":
151
+ if event["replace"]:
152
+ output = [event["delta"]]
153
+ else:
154
+ output.append(event["delta"])
155
+ return "".join(output)
156
+
157
+
158
+ def _extract_artifact_events(
159
+ event: Any,
160
+ snapshots: dict[tuple[str, str], str],
161
+ ) -> list[A2AStreamEvent]:
162
+ """从 A2A artifact_update 提取去重后的 typed events。
163
+
164
+ 快照按 ``(artifact_id, type)`` 隔离,避免 reasoning 与 response 互相覆盖。
165
+ ``append=False`` 按累计快照 diff,``append=True`` 按增量直接透传。
166
+ """
167
+ which = None
168
+ try:
169
+ which = event.WhichOneof("payload")
170
+ except Exception: # 非 protobuf 或结构不符时静默跳过
171
+ which = None
172
+ if which == "artifact_update":
173
+ artifact_update = getattr(event, "artifact_update", None)
174
+ artifact = getattr(artifact_update, "artifact", None)
175
+ if artifact is None:
176
+ return []
177
+ return _events_from_artifact(
178
+ artifact,
179
+ append=bool(getattr(artifact_update, "append", False)),
180
+ snapshots=snapshots,
181
+ )
182
+ if which == "task":
183
+ result: list[A2AStreamEvent] = []
184
+ task = getattr(event, "task", None)
185
+ for artifact in getattr(task, "artifacts", []):
186
+ result.extend(_events_from_artifact(artifact, append=False, snapshots=snapshots))
187
+ return result
188
+ if which == "message":
189
+ message = getattr(event, "message", None)
190
+ if message is None:
191
+ return []
192
+ artifact = _MessageArtifact(message)
193
+ return _events_from_artifact(artifact, append=False, snapshots=snapshots)
194
+ return []
195
+
196
+
197
+ class _MessageArtifact:
198
+ """把直接 Message 响应投影成 artifact-like 对象复用解析逻辑。"""
199
+
200
+ def __init__(self, message: Any) -> None:
201
+ self.artifact_id = f"message:{getattr(message, 'message_id', '') or 'anonymous'}"
202
+ self.name = "response"
203
+ self.parts = getattr(message, "parts", [])
204
+ self.metadata = getattr(message, "metadata", None)
205
+
206
+
207
+ def _events_from_artifact(
208
+ artifact: Any,
209
+ *,
210
+ append: bool,
211
+ snapshots: dict[tuple[str, str], str],
212
+ ) -> list[A2AStreamEvent]:
213
+ """解析一个 artifact-like 对象,支持增量、累计快照和权威替换。"""
214
+
215
+ artifact_id = str(
216
+ getattr(artifact, "artifact_id", "")
217
+ or getattr(artifact, "name", "")
218
+ or "anonymous"
219
+ )
220
+ artifact_is_reasoning = (
221
+ str(getattr(artifact, "name", "")).lower() == "reasoning"
222
+ or _metadata_flag(getattr(artifact, "metadata", None), "adk_thought")
223
+ )
224
+ grouped: list[tuple[str, str, bool]] = []
225
+ for part in getattr(artifact, "parts", []):
226
+ text = str(getattr(part, "text", "") or "")
227
+ if not text:
228
+ continue
229
+ event_type = (
230
+ "thinking"
231
+ if artifact_is_reasoning
232
+ or _metadata_flag(getattr(part, "metadata", None), "adk_thought")
233
+ else "text"
234
+ )
235
+ replaces_output = _metadata_flag(
236
+ getattr(part, "metadata", None),
237
+ "ksadk_output_snapshot",
238
+ )
239
+ if grouped and grouped[-1][0] == event_type:
240
+ grouped[-1] = (
241
+ event_type,
242
+ grouped[-1][1] + text,
243
+ grouped[-1][2] or replaces_output,
244
+ )
245
+ else:
246
+ grouped.append((event_type, text, replaces_output))
247
+
248
+ result: list[A2AStreamEvent] = []
249
+ for event_type, text, replaces_output in grouped:
250
+ key = (artifact_id, event_type)
251
+ snapshot = snapshots.get(key, "")
252
+ if append:
253
+ delta = text
254
+ snapshots[key] = snapshot + text
255
+ replace = False
256
+ else:
257
+ delta = text[len(snapshot) :] if text.startswith(snapshot) else text
258
+ snapshots[key] = text
259
+ replace = replaces_output or bool(snapshot and not text.startswith(snapshot))
260
+ if delta:
261
+ result.append(
262
+ {
263
+ "type": event_type, # type: ignore[typeddict-item]
264
+ "delta": delta,
265
+ "replace": replace,
266
+ }
267
+ )
268
+ return result
269
+
270
+
271
+ def _metadata_flag(metadata: Any, key: str) -> bool:
272
+ if metadata is None:
273
+ return False
274
+ try:
275
+ return bool(metadata[key])
276
+ except (KeyError, TypeError, ValueError):
277
+ return False
278
+
279
+
280
+ __all__ = [
281
+ "A2AStreamEvent",
282
+ "stream_a2a_agent",
283
+ "stream_a2a_agent_events",
284
+ "stream_a2a_agent_to_writer",
285
+ ]