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,461 @@
1
+ """Deterministic evaluators for normalized target results."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import re
9
+ from collections import Counter
10
+ from collections.abc import Callable
11
+ from dataclasses import dataclass
12
+ from typing import Any
13
+
14
+ from jsonschema import ValidationError as JSONSchemaValidationError
15
+ from jsonschema import validate as validate_json
16
+
17
+ from .contracts import (
18
+ AssertionSpec,
19
+ AssertionType,
20
+ DataPolicy,
21
+ EvalCase,
22
+ EvaluationConfig,
23
+ MetricResult,
24
+ MetricStatus,
25
+ TargetRun,
26
+ )
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class _EvaluationContext:
31
+ """Inputs shared by every evaluator for one target invocation."""
32
+
33
+ case: EvalCase
34
+ target_run: TargetRun
35
+ config: EvaluationConfig
36
+
37
+
38
+ EvaluatorStrategy = Callable[[_EvaluationContext], list[MetricResult]]
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class _EvaluatorDefinition:
43
+ """Stable evaluator metadata and its stateless scoring strategy."""
44
+
45
+ evaluator_id: str
46
+ metric_name: str
47
+ strategy: EvaluatorStrategy
48
+
49
+
50
+ REFERENCE_MATCH_EVALUATOR = "reference_match@v1"
51
+ LLM_JUDGE_EVALUATOR = "llm_judge@v1"
52
+ DEFAULT_EVALUATORS = (
53
+ "response_contract@v1",
54
+ "runtime_budget@v1",
55
+ "tool_trajectory@v1",
56
+ )
57
+ _RESPONSE_MATCH_THRESHOLD = 0.8
58
+ _TOKEN_PATTERN = re.compile(r"[A-Za-z0-9_]+|[\u4e00-\u9fff]")
59
+
60
+
61
+ def evaluate_case(
62
+ case: EvalCase,
63
+ target_run: TargetRun,
64
+ evaluator_names: list[str],
65
+ config: EvaluationConfig | None = None,
66
+ ) -> list[MetricResult]:
67
+ """Run selected evaluators in request order."""
68
+
69
+ context = _EvaluationContext(case, target_run, config or EvaluationConfig())
70
+ evaluators = _resolve_evaluators(evaluator_names)
71
+ return _run_evaluators(context, evaluators)
72
+
73
+
74
+ async def evaluate_case_async(
75
+ case: EvalCase,
76
+ target_run: TargetRun,
77
+ evaluator_names: list[str],
78
+ config: EvaluationConfig,
79
+ ) -> list[MetricResult]:
80
+ """Run evaluators without blocking the target invocation event loop."""
81
+
82
+ return await asyncio.to_thread(evaluate_case, case, target_run, evaluator_names, config)
83
+
84
+
85
+ def _resolve_evaluators(evaluator_names: list[str]) -> list[_EvaluatorDefinition]:
86
+ selected_names = evaluator_names or DEFAULT_EVALUATORS
87
+ unsupported_names = [name for name in selected_names if name not in _EVALUATOR_REGISTRY]
88
+ if unsupported_names:
89
+ raise ValueError(f"不支持的评估器: {', '.join(unsupported_names)}")
90
+ return [_EVALUATOR_REGISTRY[name] for name in selected_names]
91
+
92
+
93
+ def _run_evaluators(
94
+ context: _EvaluationContext,
95
+ evaluators: list[_EvaluatorDefinition],
96
+ ) -> list[MetricResult]:
97
+ metrics: list[MetricResult] = []
98
+ for evaluator in evaluators:
99
+ metrics.extend(_run_evaluator(evaluator, context))
100
+ return metrics
101
+
102
+
103
+ def _run_evaluator(
104
+ evaluator: _EvaluatorDefinition,
105
+ context: _EvaluationContext,
106
+ ) -> list[MetricResult]:
107
+ try:
108
+ return evaluator.strategy(context)
109
+ except Exception as exc:
110
+ return [_evaluator_error_metric(evaluator, exc)]
111
+
112
+
113
+ def _evaluate_reference_match(context: _EvaluationContext) -> list[MetricResult]:
114
+ """Score the final response against the case's reference output."""
115
+
116
+ expected_output = _final_expected_output(context.case)
117
+ if not expected_output:
118
+ return []
119
+
120
+ score = _rouge_1_f1(context.target_run.output, expected_output)
121
+ return [
122
+ _score_metric(
123
+ name="response_match",
124
+ score=score,
125
+ evidence={
126
+ "evaluator": REFERENCE_MATCH_EVALUATOR,
127
+ "method": "rouge_1_f1",
128
+ "threshold": _RESPONSE_MATCH_THRESHOLD,
129
+ },
130
+ )
131
+ ]
132
+
133
+
134
+ def _evaluate_llm_judge(
135
+ context: _EvaluationContext,
136
+ ) -> list[MetricResult]:
137
+ """Use the configured LLM Judge to score a final response."""
138
+
139
+ expected_output = _final_expected_output(context.case)
140
+ if not expected_output:
141
+ return []
142
+
143
+ unavailable_reason = _judge_unavailable_reason(context.config)
144
+ if unavailable_reason:
145
+ return [_judge_unavailable_metric(unavailable_reason)]
146
+
147
+ try:
148
+ score = _judge_score(
149
+ input_text=context.case.input,
150
+ actual_output=context.target_run.output,
151
+ expected_output=expected_output,
152
+ config=context.config,
153
+ )
154
+ except ImportError:
155
+ return [_judge_unavailable_metric("未安装 ksadk[judge]")]
156
+ except Exception as exc:
157
+ return [_judge_error_metric(exc)]
158
+
159
+ return [_judge_score_metric(score, context.config.judge_model)]
160
+
161
+
162
+ def _evaluate_response_contract(
163
+ context: _EvaluationContext,
164
+ ) -> list[MetricResult]:
165
+ """Evaluate response assertions against the normalized target output."""
166
+
167
+ return [
168
+ _response_metric(assertion, context.target_run.output)
169
+ for assertion in _response_assertions(context.case)
170
+ ]
171
+
172
+
173
+ def _evaluate_runtime_budget(
174
+ context: _EvaluationContext,
175
+ ) -> list[MetricResult]:
176
+ """Evaluate runtime assertions against adapter-reported measurements."""
177
+
178
+ return [
179
+ _runtime_metric(assertion, context.target_run)
180
+ for assertion in _runtime_assertions(context.case)
181
+ ]
182
+
183
+
184
+ def _evaluate_tool_trajectory(
185
+ context: _EvaluationContext,
186
+ ) -> list[MetricResult]:
187
+ """Report unavailable until A2A exposes normalized tool trajectories."""
188
+
189
+ requirements = _tool_requirements(context.case)
190
+ if not requirements:
191
+ return []
192
+
193
+ return [
194
+ MetricResult(
195
+ name="tool_trajectory",
196
+ status=MetricStatus.UNAVAILABLE,
197
+ required=required,
198
+ evidence={
199
+ "assertion": assertion_type,
200
+ "reason": "A2A target 未提供标准化工具轨迹",
201
+ },
202
+ )
203
+ for assertion_type, required in requirements
204
+ ]
205
+
206
+
207
+ def _response_assertions(case: EvalCase) -> list[AssertionSpec]:
208
+ response_types = {
209
+ AssertionType.RESPONSE_EQUALS,
210
+ AssertionType.RESPONSE_CONTAINS,
211
+ AssertionType.RESPONSE_NOT_CONTAINS,
212
+ AssertionType.RESPONSE_JSON_SCHEMA,
213
+ }
214
+ return [assertion for assertion in case.assertions if assertion.type in response_types]
215
+
216
+
217
+ def _runtime_assertions(case: EvalCase) -> list[AssertionSpec]:
218
+ return [
219
+ assertion for assertion in case.assertions if assertion.type.value.startswith("runtime.")
220
+ ]
221
+
222
+
223
+ def _tool_requirements(case: EvalCase) -> list[tuple[str, bool]]:
224
+ requirements = [
225
+ (assertion.type.value, assertion.required)
226
+ for assertion in case.assertions
227
+ if assertion.type in {AssertionType.TOOL_CALLED, AssertionType.TOOL_NOT_CALLED}
228
+ ]
229
+ requirements.extend(("tool.expected", True) for turn in case.turns for _ in turn.expected_tools)
230
+ return requirements
231
+
232
+
233
+ def _response_metric(assertion: AssertionSpec, output: str) -> MetricResult:
234
+ reason = ""
235
+ if assertion.type is AssertionType.RESPONSE_EQUALS:
236
+ passed = output == assertion.value
237
+ elif assertion.type is AssertionType.RESPONSE_CONTAINS:
238
+ passed = assertion.value in output
239
+ elif assertion.type is AssertionType.RESPONSE_NOT_CONTAINS:
240
+ passed = assertion.value not in output
241
+ else:
242
+ try:
243
+ validate_json(json.loads(output), assertion.value)
244
+ passed = True
245
+ except (json.JSONDecodeError, JSONSchemaValidationError) as exc:
246
+ passed = False
247
+ reason = str(exc)
248
+
249
+ return _metric(
250
+ "response_contract",
251
+ assertion,
252
+ passed=passed,
253
+ reason=reason,
254
+ )
255
+
256
+
257
+ def _runtime_metric(assertion: AssertionSpec, target_run: TargetRun) -> MetricResult:
258
+ actual: int | None
259
+ if assertion.type is AssertionType.RUNTIME_MAX_LATENCY_MS:
260
+ actual = target_run.duration_ms
261
+ elif not target_run.usage.reported:
262
+ actual = None
263
+ elif assertion.type is AssertionType.RUNTIME_MAX_INPUT_TOKENS:
264
+ actual = target_run.usage.input_tokens
265
+ else:
266
+ actual = target_run.usage.output_tokens
267
+
268
+ if actual is None:
269
+ return MetricResult(
270
+ name="runtime_budget",
271
+ status=MetricStatus.UNAVAILABLE,
272
+ required=assertion.required,
273
+ evidence={
274
+ "assertion": assertion.type.value,
275
+ "reason": "Target 未提供该运行指标",
276
+ },
277
+ )
278
+ return _metric(
279
+ "runtime_budget",
280
+ assertion,
281
+ passed=actual <= assertion.value,
282
+ evidence={"actual": actual},
283
+ )
284
+
285
+
286
+ def _metric(
287
+ name: str,
288
+ assertion: AssertionSpec,
289
+ *,
290
+ passed: bool,
291
+ reason: str = "",
292
+ evidence: dict[str, Any] | None = None,
293
+ ) -> MetricResult:
294
+ details = {"assertion": assertion.type.value, **(evidence or {})}
295
+ if reason:
296
+ details["reason"] = reason
297
+ return MetricResult(
298
+ name=name,
299
+ status=MetricStatus.PASS if passed else MetricStatus.FAIL,
300
+ score=1.0 if passed else 0.0,
301
+ required=assertion.required,
302
+ evidence=details,
303
+ )
304
+
305
+
306
+ def _evaluator_error_metric(
307
+ evaluator: _EvaluatorDefinition,
308
+ error: Exception,
309
+ ) -> MetricResult:
310
+ return MetricResult(
311
+ name=evaluator.metric_name,
312
+ status=MetricStatus.ERROR,
313
+ evidence={
314
+ "evaluator": evaluator.evaluator_id,
315
+ "reason": "评估器执行失败",
316
+ "errorType": type(error).__name__,
317
+ },
318
+ )
319
+
320
+
321
+ def _score_metric(
322
+ *,
323
+ name: str,
324
+ score: float,
325
+ evidence: dict[str, Any],
326
+ ) -> MetricResult:
327
+ return MetricResult(
328
+ name=name,
329
+ status=MetricStatus.PASS if score >= _RESPONSE_MATCH_THRESHOLD else MetricStatus.FAIL,
330
+ score=score,
331
+ evidence=evidence,
332
+ )
333
+
334
+
335
+ def _final_expected_output(case: EvalCase) -> str | None:
336
+ return case.turns[-1].expected_output
337
+
338
+
339
+ def _judge_unavailable_reason(config: EvaluationConfig) -> str | None:
340
+ if config.data_policy is not DataPolicy.FULL_TRACE:
341
+ return "Judge 需要 dataPolicy=full_trace"
342
+ if not config.judge_model:
343
+ return "未配置 judgeModel"
344
+ if not config.judge_api_base:
345
+ return "未配置 judgeApiBase"
346
+ if not os.getenv(config.judge_api_key_env):
347
+ return f"未设置 Judge 密钥环境变量: {config.judge_api_key_env}"
348
+ return None
349
+
350
+
351
+ def _judge_score(
352
+ *,
353
+ input_text: str,
354
+ actual_output: str,
355
+ expected_output: str,
356
+ config: EvaluationConfig,
357
+ ) -> float:
358
+ return _run_llm_judge(
359
+ input_text=input_text,
360
+ actual_output=actual_output,
361
+ expected_output=expected_output,
362
+ model=config.judge_model or "",
363
+ api_base=config.judge_api_base or "",
364
+ api_key=os.environ[config.judge_api_key_env],
365
+ )
366
+
367
+
368
+ def _judge_unavailable_metric(reason: str) -> MetricResult:
369
+ return MetricResult(
370
+ name="response_quality",
371
+ status=MetricStatus.UNAVAILABLE,
372
+ evidence={"evaluator": LLM_JUDGE_EVALUATOR, "reason": reason},
373
+ )
374
+
375
+
376
+ def _judge_error_metric(error: Exception) -> MetricResult:
377
+ return MetricResult(
378
+ name="response_quality",
379
+ status=MetricStatus.ERROR,
380
+ evidence={
381
+ "evaluator": LLM_JUDGE_EVALUATOR,
382
+ "reason": "Judge 执行失败",
383
+ "errorType": type(error).__name__,
384
+ },
385
+ )
386
+
387
+
388
+ def _judge_score_metric(score: float, judge_model: str | None) -> MetricResult:
389
+ return _score_metric(
390
+ name="response_quality",
391
+ score=score,
392
+ evidence={
393
+ "evaluator": LLM_JUDGE_EVALUATOR,
394
+ "threshold": _RESPONSE_MATCH_THRESHOLD,
395
+ "judgeModel": judge_model,
396
+ },
397
+ )
398
+
399
+
400
+ def _rouge_1_f1(actual: str, expected: str) -> float:
401
+ actual_tokens = Counter(_TOKEN_PATTERN.findall(actual.lower()))
402
+ expected_tokens = Counter(_TOKEN_PATTERN.findall(expected.lower()))
403
+ if not actual_tokens or not expected_tokens:
404
+ return float(actual.strip() == expected.strip())
405
+
406
+ overlap = sum((actual_tokens & expected_tokens).values())
407
+ precision = overlap / sum(actual_tokens.values())
408
+ recall = overlap / sum(expected_tokens.values())
409
+ return 2 * precision * recall / (precision + recall) if precision + recall else 0.0
410
+
411
+
412
+ def _run_llm_judge(
413
+ *,
414
+ input_text: str,
415
+ actual_output: str,
416
+ expected_output: str,
417
+ model: str,
418
+ api_base: str,
419
+ api_key: str,
420
+ ) -> float:
421
+ """Run the minimal external Judge configuration."""
422
+
423
+ os.environ.setdefault("DEEPEVAL_TELEMETRY_OPT_OUT", "YES")
424
+ from deepeval.metrics import GEval
425
+ from deepeval.models import LocalModel
426
+ from deepeval.test_case import LLMTestCase, LLMTestCaseParams
427
+
428
+ judge = LocalModel(model=model, base_url=api_base, api_key=api_key)
429
+ metric = GEval(
430
+ name="Response quality",
431
+ criteria=(
432
+ "Determine whether the actual output is factually correct "
433
+ "based on the expected output."
434
+ ),
435
+ evaluation_params=[
436
+ LLMTestCaseParams.INPUT,
437
+ LLMTestCaseParams.ACTUAL_OUTPUT,
438
+ LLMTestCaseParams.EXPECTED_OUTPUT,
439
+ ],
440
+ threshold=_RESPONSE_MATCH_THRESHOLD,
441
+ model=judge,
442
+ )
443
+ metric.measure(
444
+ LLMTestCase(
445
+ input=input_text,
446
+ actual_output=actual_output,
447
+ expected_output=expected_output,
448
+ )
449
+ )
450
+ return float(metric.score)
451
+
452
+
453
+ _EVALUATORS = (
454
+ _EvaluatorDefinition("response_contract@v1", "response_contract", _evaluate_response_contract),
455
+ _EvaluatorDefinition("runtime_budget@v1", "runtime_budget", _evaluate_runtime_budget),
456
+ _EvaluatorDefinition("tool_trajectory@v1", "tool_trajectory", _evaluate_tool_trajectory),
457
+ _EvaluatorDefinition(REFERENCE_MATCH_EVALUATOR, "response_match", _evaluate_reference_match),
458
+ _EvaluatorDefinition(LLM_JUDGE_EVALUATOR, "response_quality", _evaluate_llm_judge),
459
+ )
460
+ _EVALUATOR_REGISTRY = {evaluator.evaluator_id: evaluator for evaluator in _EVALUATORS}
461
+ SUPPORTED_EVALUATORS = tuple(evaluator.evaluator_id for evaluator in _EVALUATORS)
@@ -0,0 +1,118 @@
1
+ """Single public evaluation handoff shared by CLI and Studio."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from uuid import uuid4
7
+
8
+ from .adapters import EvaluationNotImplementedError, TargetAdapter
9
+ from .contracts import (
10
+ CaseRun,
11
+ EvalRunReport,
12
+ EvalRunSpec,
13
+ EvalRunStatus,
14
+ EvaluationRequest,
15
+ MetricStatus,
16
+ TargetRunStatus,
17
+ )
18
+ from .evaluators import evaluate_case_async
19
+ from .storage import EvaluationStorage, EvaluationStorageError
20
+ from .target import EvaluationExecutionError, EvaluationTarget
21
+
22
+ __all__ = [
23
+ "EvaluationExecutionError",
24
+ "EvaluationNotImplementedError",
25
+ "TargetAdapter",
26
+ "execute_evaluation",
27
+ ]
28
+
29
+
30
+ async def execute_evaluation(
31
+ request: EvaluationRequest,
32
+ *,
33
+ on_case_started: Callable[[str, int, int], None] | None = None,
34
+ ) -> EvalRunReport:
35
+ """Execute and persist one evaluation request."""
36
+
37
+ target = EvaluationTarget(request.target, request.config)
38
+ snapshot = await target.snapshot()
39
+ spec = EvalRunSpec(
40
+ id=f"eval_{uuid4().hex}",
41
+ evalset=request.evalset,
42
+ target=snapshot,
43
+ config=request.config,
44
+ )
45
+ case_runs = await _run_cases(target, spec, on_case_started=on_case_started)
46
+ report = EvalRunReport(
47
+ spec=spec,
48
+ status=_report_status(case_runs),
49
+ case_runs=case_runs,
50
+ )
51
+ _persist_report(request, report)
52
+ return report
53
+
54
+
55
+ async def _run_cases(
56
+ target: EvaluationTarget,
57
+ spec: EvalRunSpec,
58
+ *,
59
+ on_case_started: Callable[[str, int, int], None] | None,
60
+ ) -> list[CaseRun]:
61
+ case_runs: list[CaseRun] = []
62
+ total_cases = len(spec.evalset.cases)
63
+ for index, case in enumerate(spec.evalset.cases, start=1):
64
+ _notify_case_started(on_case_started, case.id, index, total_cases)
65
+ target_run = await target.run_case(spec, case)
66
+ try:
67
+ metrics = await evaluate_case_async(
68
+ case, target_run, spec.config.evaluators, spec.config
69
+ )
70
+ except ValueError as exc:
71
+ raise EvaluationExecutionError(str(exc)) from exc
72
+ case_run = CaseRun(
73
+ case_id=case.id,
74
+ attempt=spec.attempt,
75
+ target_run=target_run,
76
+ metrics=metrics,
77
+ )
78
+ case_runs.append(case_run)
79
+ if spec.config.fail_fast and not case_run.passed:
80
+ break
81
+ return case_runs
82
+
83
+
84
+ def _notify_case_started(
85
+ callback: Callable[[str, int, int], None] | None,
86
+ case_id: str,
87
+ index: int,
88
+ total_cases: int,
89
+ ) -> None:
90
+ if callback is None:
91
+ return
92
+ try:
93
+ callback(case_id, index, total_cases)
94
+ except Exception:
95
+ pass
96
+
97
+
98
+ def _report_status(case_runs: list[CaseRun]) -> EvalRunStatus:
99
+ if any(
100
+ case.target_run.status is TargetRunStatus.ERROR
101
+ or any(metric.required and metric.status is MetricStatus.ERROR for metric in case.metrics)
102
+ for case in case_runs
103
+ ):
104
+ return EvalRunStatus.ERROR
105
+ if any(case.target_run.status is TargetRunStatus.CANCELLED for case in case_runs):
106
+ return EvalRunStatus.CANCELLED
107
+ if any(not case.passed for case in case_runs):
108
+ return EvalRunStatus.FAILED
109
+ return EvalRunStatus.PASSED
110
+
111
+
112
+ def _persist_report(request: EvaluationRequest, report: EvalRunReport) -> None:
113
+ if not request.report_dir:
114
+ return
115
+ try:
116
+ EvaluationStorage(request.report_dir).write_report(report)
117
+ except EvaluationStorageError as exc:
118
+ raise EvaluationExecutionError("评测报告写入失败") from exc
@@ -0,0 +1,68 @@
1
+ """Local, atomic storage for the shared evaluation report contract."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from .contracts import EvalRunReport
9
+
10
+
11
+ class EvaluationStorageError(RuntimeError):
12
+ """Raised when a report cannot be read or safely persisted."""
13
+
14
+
15
+ class EvaluationStorage:
16
+ """Persist reports below one workspace-owned evaluation directory."""
17
+
18
+ def __init__(self, root: str | Path):
19
+ self.root = Path(root).expanduser().resolve()
20
+
21
+ def report_path(self, run_id: str) -> Path:
22
+ """Return the only report location allowed for a run identifier."""
23
+
24
+ if not run_id or Path(run_id).name != run_id:
25
+ raise EvaluationStorageError("run_id 只能包含文件名,不允许路径穿越")
26
+ return self.root / run_id / "report.json"
27
+
28
+ def write_report(self, report: EvalRunReport) -> Path:
29
+ """Write the completed report atomically after its artifact directory exists."""
30
+
31
+ path = self.report_path(report.spec.id)
32
+ path.parent.mkdir(parents=True, exist_ok=True)
33
+ temporary = path.with_name(f".{path.name}.{report.report_digest}.tmp")
34
+ payload = json.dumps(
35
+ report.model_dump(mode="json", by_alias=True, exclude_none=True),
36
+ ensure_ascii=False,
37
+ sort_keys=True,
38
+ indent=2,
39
+ ) + "\n"
40
+ try:
41
+ temporary.write_text(payload, encoding="utf-8")
42
+ temporary.replace(path)
43
+ except OSError as exc:
44
+ temporary.unlink(missing_ok=True)
45
+ raise EvaluationStorageError("评测报告写入失败") from exc
46
+ return path
47
+
48
+ def read_report(self, run_id: str) -> EvalRunReport:
49
+ path = self.report_path(run_id)
50
+ try:
51
+ return EvalRunReport.model_validate_json(path.read_text(encoding="utf-8"))
52
+ except FileNotFoundError as exc:
53
+ raise EvaluationStorageError("评测报告不存在") from exc
54
+ except (OSError, ValueError) as exc:
55
+ raise EvaluationStorageError("评测报告损坏或不可读") from exc
56
+
57
+ def list_reports(self) -> list[EvalRunReport]:
58
+ if not self.root.is_dir():
59
+ return []
60
+ reports: list[EvalRunReport] = []
61
+ for path in sorted(self.root.glob("*/report.json")):
62
+ try:
63
+ reports.append(
64
+ EvalRunReport.model_validate_json(path.read_text(encoding="utf-8"))
65
+ )
66
+ except (OSError, ValueError):
67
+ continue
68
+ return reports