agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.3__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 (476) hide show
  1. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/METADATA +25 -12
  2. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/RECORD +421 -232
  3. ksadk/_build_provenance.json +1 -0
  4. ksadk/a2a/_space_client_events.py +381 -0
  5. ksadk/a2a/event_adapter.py +156 -64
  6. ksadk/a2a/executor.py +179 -63
  7. ksadk/a2a/space_client.py +6 -333
  8. ksadk/a2ui/core.py +222 -53
  9. ksadk/agui/_agent_helpers.py +247 -0
  10. ksadk/agui/a2ui_projection.py +12 -5
  11. ksadk/agui/agent.py +443 -259
  12. ksadk/api/client.py +528 -27
  13. ksadk/builders/code_builder.py +284 -8
  14. ksadk/builders/container_builder.py +8 -1
  15. ksadk/builders/managed_runtime_builder.py +42 -35
  16. ksadk/cli/__init__.py +23 -0
  17. ksadk/cli/cmd_dashboard.py +15 -1
  18. ksadk/cli/cmd_deploy.py +10 -2
  19. ksadk/cli/cmd_eval.py +66 -9
  20. ksadk/cli/cmd_evalset.py +284 -0
  21. ksadk/cli/cmd_files.py +5 -1
  22. ksadk/cli/cmd_hermes.py +1 -126
  23. ksadk/cli/cmd_invoke.py +28 -38
  24. ksadk/cli/cmd_managed_runtime.py +95 -0
  25. ksadk/cli/cmd_observe.py +85 -0
  26. ksadk/cli/cmd_openclaw.py +1 -435
  27. ksadk/cli/cmd_plugin.py +657 -0
  28. ksadk/cli/cmd_replay.py +28 -4
  29. ksadk/cli/cmd_studio.py +99 -8
  30. ksadk/cli/cmd_web.py +10 -2
  31. ksadk/cli/hermes_env.py +151 -0
  32. ksadk/cli/invoke_payload.py +49 -0
  33. ksadk/cli/openclaw_env.py +458 -0
  34. ksadk/codex/client.py +180 -30
  35. ksadk/codex/runtime.py +475 -331
  36. ksadk/configs/env_registry.py +94 -12
  37. ksadk/configs/env_registry_pcm.py +181 -0
  38. ksadk/configs/env_var_spec.py +18 -0
  39. ksadk/configs/global_config.py +5 -0
  40. ksadk/configs/settings.py +9 -0
  41. ksadk/context_engine/__init__.py +98 -0
  42. ksadk/context_engine/assembler.py +176 -0
  43. ksadk/context_engine/baseline.py +419 -0
  44. ksadk/context_engine/cache_observability.py +228 -0
  45. ksadk/context_engine/capabilities.py +443 -0
  46. ksadk/context_engine/contributors.py +409 -0
  47. ksadk/context_engine/hosted_pipeline.py +417 -0
  48. ksadk/context_engine/models.py +117 -0
  49. ksadk/context_engine/planner.py +698 -0
  50. ksadk/context_engine/policies.py +319 -0
  51. ksadk/context_engine/projection.py +31 -0
  52. ksadk/context_engine/shadow_plan.py +266 -0
  53. ksadk/context_engine/tokenizer.py +164 -0
  54. ksadk/conversations/__init__.py +19 -0
  55. ksadk/conversations/context.py +230 -30
  56. ksadk/conversations/contracts.py +266 -0
  57. ksadk/conversations/message_projection.py +308 -26
  58. ksadk/conversations/model_context.py +49 -3
  59. ksadk/conversations/projector.py +616 -0
  60. ksadk/conversations/reducer.py +98 -0
  61. ksadk/conversations/runtime_compaction.py +207 -5
  62. ksadk/conversations/runtime_input.py +184 -2
  63. ksadk/conversations/runtime_invocation.py +81 -3
  64. ksadk/conversations/runtime_observability.py +141 -0
  65. ksadk/conversations/runtime_payloads.py +42 -0
  66. ksadk/conversations/runtime_preparation.py +272 -2
  67. ksadk/conversations/runtime_resume.py +20 -1
  68. ksadk/conversations/runtime_stream_events.py +94 -9
  69. ksadk/conversations/semantic_summary.py +311 -1
  70. ksadk/conversations/session_lock.py +51 -0
  71. ksadk/deployment/env_forward.py +72 -0
  72. ksadk/deployment/managed_runtime.py +5 -0
  73. ksadk/deployment/providers/serverless.py +143 -69
  74. ksadk/evaluation/__init__.py +32 -0
  75. ksadk/evaluation/a2a_adapter.py +9 -1
  76. ksadk/evaluation/adapters.py +18 -5
  77. ksadk/evaluation/agent_eval_client.py +395 -0
  78. ksadk/evaluation/cloud_binding.py +80 -0
  79. ksadk/evaluation/cloud_converter.py +194 -0
  80. ksadk/evaluation/cloud_service.py +199 -0
  81. ksadk/evaluation/contracts.py +73 -4
  82. ksadk/evaluation/evaluators.py +183 -17
  83. ksadk/evaluation/evidence.py +258 -0
  84. ksadk/evaluation/executor.py +31 -6
  85. ksadk/evaluation/local_adapter.py +550 -0
  86. ksadk/evaluation/service_env.py +27 -0
  87. ksadk/evaluation/studio_build_adapter.py +253 -0
  88. ksadk/evaluation/target.py +15 -6
  89. ksadk/events/__init__.py +13 -13
  90. ksadk/events/_v1_compat/__init__.py +1 -0
  91. ksadk/events/_v1_compat/models.py +299 -0
  92. ksadk/events/_v1_compat/parser.py +247 -0
  93. ksadk/events/_v1_compat/projection.py +755 -0
  94. ksadk/events/adapters/__init__.py +34 -0
  95. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  96. ksadk/events/adapters/_a2a_support.py +275 -0
  97. ksadk/events/adapters/_codex_interactions.py +375 -0
  98. ksadk/events/adapters/_codex_items.py +801 -0
  99. ksadk/events/adapters/_codex_validators.py +311 -0
  100. ksadk/events/adapters/_langgraph_support.py +785 -0
  101. ksadk/events/adapters/a2a.py +841 -0
  102. ksadk/events/adapters/adk.py +503 -0
  103. ksadk/events/adapters/codex.py +907 -0
  104. ksadk/events/adapters/langgraph.py +745 -0
  105. ksadk/events/canonical.py +440 -0
  106. ksadk/events/canonical_replay.py +497 -0
  107. ksadk/events/canonical_store.py +468 -0
  108. ksadk/events/cold_recovery.py +257 -0
  109. ksadk/events/content.py +89 -0
  110. ksadk/events/identity.py +86 -0
  111. ksadk/events/pipeline.py +455 -0
  112. ksadk/events/projections.py +66 -0
  113. ksadk/events/reducer.py +661 -0
  114. ksadk/events/replay.py +3 -36
  115. ksadk/events/runtime_event.py +20 -265
  116. ksadk/events/session_event.py +324 -0
  117. ksadk/events/store.py +3 -316
  118. ksadk/events/v1_compat.py +41 -0
  119. ksadk/harness/__init__.py +45 -35
  120. ksadk/harness/runtime.py +220 -58
  121. ksadk/identity/resolver.py +4 -3
  122. ksadk/interaction/__init__.py +28 -0
  123. ksadk/interaction/contracts.py +136 -0
  124. ksadk/interaction/ledger.py +189 -0
  125. ksadk/interaction/provider.py +122 -0
  126. ksadk/interaction/providers/__init__.py +52 -0
  127. ksadk/interaction/providers/adk.py +40 -0
  128. ksadk/interaction/providers/codex.py +94 -0
  129. ksadk/interaction/providers/langgraph.py +58 -0
  130. ksadk/kernel/__init__.py +117 -0
  131. ksadk/kernel/authorization.py +258 -0
  132. ksadk/kernel/bootstrap.py +1074 -0
  133. ksadk/kernel/contract_fingerprints.py +62 -0
  134. ksadk/kernel/contracts.py +370 -0
  135. ksadk/kernel/control.py +220 -0
  136. ksadk/kernel/errors.py +79 -0
  137. ksadk/kernel/ingress.py +1123 -0
  138. ksadk/kernel/mapping.py +111 -0
  139. ksadk/kernel/memory_store.py +1134 -0
  140. ksadk/kernel/postgres_store.py +1757 -0
  141. ksadk/kernel/recovery.py +452 -0
  142. ksadk/kernel/runtime_identity.py +103 -0
  143. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  144. ksadk/kernel/sqlite_store.py +1411 -0
  145. ksadk/kernel/state.py +145 -0
  146. ksadk/kernel/store.py +232 -0
  147. ksadk/kernel/worker.py +964 -0
  148. ksadk/knowledge_base/client.py +7 -0
  149. ksadk/knowledge_base/service.py +32 -5
  150. ksadk/memory/__init__.py +61 -0
  151. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  152. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  153. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  154. ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
  155. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  156. ksadk/memory/coordinator.py +461 -0
  157. ksadk/memory/events.py +202 -0
  158. ksadk/memory/extraction.py +228 -0
  159. ksadk/memory/ltm_backend_factory.py +16 -3
  160. ksadk/memory/models.py +316 -0
  161. ksadk/memory/policy.py +215 -0
  162. ksadk/memory/provider.py +47 -0
  163. ksadk/memory/provider_adapter.py +159 -0
  164. ksadk/memory/provider_resolver.py +71 -0
  165. ksadk/memory/providers/__init__.py +5 -0
  166. ksadk/memory/providers/local_sqlite.py +497 -0
  167. ksadk/memory/resolved_policy.py +145 -0
  168. ksadk/memory/service.py +188 -6
  169. ksadk/model_proxy/bootstrap.py +16 -9
  170. ksadk/model_proxy/detect.py +202 -9
  171. ksadk/model_proxy/namespace.py +39 -15
  172. ksadk/model_proxy/server.py +24 -5
  173. ksadk/model_proxy/transform.py +60 -2
  174. ksadk/observability/__init__.py +25 -0
  175. ksadk/observability/session_log.py +420 -0
  176. ksadk/observability/trajectory.py +105 -0
  177. ksadk/plugins/__init__.py +167 -0
  178. ksadk/plugins/bridges/__init__.py +45 -0
  179. ksadk/plugins/bridges/codex.py +552 -0
  180. ksadk/plugins/bridges/dsh.py +1116 -0
  181. ksadk/plugins/builtins.py +747 -0
  182. ksadk/plugins/bundle.py +246 -0
  183. ksadk/plugins/bundle_security.py +167 -0
  184. ksadk/plugins/composition.py +529 -0
  185. ksadk/plugins/context_contributor.py +437 -0
  186. ksadk/plugins/contracts.py +451 -0
  187. ksadk/plugins/dsh_toolchain.py +935 -0
  188. ksadk/plugins/ecosystem_bridge.py +1105 -0
  189. ksadk/plugins/ecosystem_probe.py +191 -0
  190. ksadk/plugins/host.py +724 -0
  191. ksadk/plugins/providers/__init__.py +85 -0
  192. ksadk/plugins/providers/bundles/ksadk-codex/cordis.patch.yml +3 -0
  193. ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +18 -0
  194. ksadk/plugins/providers/bundles/ksadk-codex/package.json +19 -0
  195. ksadk/plugins/providers/bundles/ksadk-harness/cordis.patch.yml +3 -0
  196. ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +19 -0
  197. ksadk/plugins/providers/bundles/ksadk-harness/package.json +19 -0
  198. ksadk/plugins/providers/codex.py +722 -0
  199. ksadk/plugins/providers/codex_dsh.py +83 -0
  200. ksadk/plugins/providers/dsh.py +951 -0
  201. ksadk/plugins/providers/dsh_descriptor_host.py +226 -0
  202. ksadk/plugins/providers/harness.py +627 -0
  203. ksadk/plugins/providers/harness_dsh.py +91 -0
  204. ksadk/plugins/providers/legacy.py +215 -0
  205. ksadk/plugins/providers/legacy_catalog.py +100 -0
  206. ksadk/plugins/providers/shipped_dsh.py +370 -0
  207. ksadk/plugins/resolver.py +331 -0
  208. ksadk/plugins/subagent_providers/__init__.py +12 -0
  209. ksadk/plugins/subagent_providers/codex.py +443 -0
  210. ksadk/plugins/subagents.py +333 -0
  211. ksadk/prompts/__init__.py +71 -0
  212. ksadk/prompts/compiler.py +215 -0
  213. ksadk/prompts/models.py +82 -0
  214. ksadk/prompts/projection.py +89 -0
  215. ksadk/prompts/resolved.py +137 -0
  216. ksadk/prompts/sources.py +233 -0
  217. ksadk/runners/_langgraph_runner_streams.py +822 -0
  218. ksadk/runners/adk_runner.py +5 -5
  219. ksadk/runners/base_runner.py +16 -0
  220. ksadk/runners/factory.py +1 -2
  221. ksadk/runners/langgraph_runner.py +198 -437
  222. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  223. ksadk/runtime/_runner_adapter/stream_mapping.py +797 -0
  224. ksadk/runtime/adapter.py +119 -2
  225. ksadk/runtime/conversation_execution.py +316 -182
  226. ksadk/runtime/executor.py +144 -3
  227. ksadk/runtime/factory.py +268 -8
  228. ksadk/runtime/framework_adapters.py +18 -0
  229. ksadk/runtime/hosted_finalizer.py +268 -0
  230. ksadk/runtime/launch.py +14 -1
  231. ksadk/runtime/preprocessing.py +25 -3
  232. ksadk/runtime/runner_adapter.py +293 -508
  233. ksadk/runtime/usage.py +33 -0
  234. ksadk/sandbox/__init__.py +2 -1
  235. ksadk/sandbox/backends/e2b.py +35 -2
  236. ksadk/sandbox/factory.py +72 -0
  237. ksadk/scheduler/__init__.py +18 -0
  238. ksadk/scheduler/calendar.py +102 -0
  239. ksadk/scheduler/contracts.py +254 -0
  240. ksadk/scheduler/dispatcher.py +129 -0
  241. ksadk/scheduler/engine.py +416 -0
  242. ksadk/scheduler/sqlite_store.py +563 -0
  243. ksadk/server/composition.py +8 -0
  244. ksadk/server/factory.py +69 -6
  245. ksadk/server/routes/kernel_ingress.py +258 -0
  246. ksadk/server/routes/openai_compat.py +88 -8
  247. ksadk/server/routes/projection.py +35 -20
  248. ksadk/server/routes/run.py +94 -0
  249. ksadk/server/routes/sessions.py +2 -0
  250. ksadk/server/routes/streaming.py +5 -2
  251. ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-10GPXIJ6.js} +1 -1
  252. ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-BXk1l-G1.js} +4 -4
  253. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  254. ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-D9uB55Oh.js} +4 -4
  255. ksadk/server/static/assets/MermaidBlock--OEYoXIJ.js +312 -0
  256. ksadk/server/static/assets/NativeTerminalPanel-DrEe1syq.js +1 -0
  257. ksadk/server/static/assets/abnfDiagram-VCTEODGH-eeTFJLvo.js +1 -0
  258. ksadk/server/static/assets/arc-S3ZNbIAd.js +1 -0
  259. ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +1 -0
  260. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-CRjgEJDX.js +36 -0
  261. ksadk/server/static/assets/blockDiagram-NRAW4CY4-C7bsBoNH.js +129 -0
  262. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-BX3c33Ob.js +38 -0
  263. ksadk/server/static/assets/channel-s354Yo6o.js +1 -0
  264. ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +1 -0
  265. ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-bZ7ws-8m.js} +1 -1
  266. ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-D2r0pvhY.js} +1 -1
  267. ksadk/server/static/assets/chunk-G27WJ6UU-CZwF3oUP.js +231 -0
  268. ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-DigFYCML.js} +1 -1
  269. ksadk/server/static/assets/chunk-LCL6LL3I-CnorGV5q.js +206 -0
  270. ksadk/server/static/assets/chunk-POPQ4Y6H-CexntQA-.js +1 -0
  271. ksadk/server/static/assets/chunk-RHFEMEQ7-CHTC7UxA.js +168 -0
  272. ksadk/server/static/assets/chunk-SVP7TREG-BC1EWt_-.js +88 -0
  273. ksadk/server/static/assets/chunk-XXDRQBXY-BTG24xN0.js +1 -0
  274. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +1 -0
  275. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +1 -0
  276. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-CkonRdAS.js} +1 -1
  277. ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +1 -0
  278. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-DEOCBe15.js +62 -0
  279. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  280. ksadk/server/static/assets/dagre-3AP2YEHR-DBThPuLa.js +4 -0
  281. ksadk/server/static/assets/dagre-ClTPrTht.js +1 -0
  282. ksadk/server/static/assets/diagram-S7CK7UJ4-DsZHrnpJ.js +30 -0
  283. ksadk/server/static/assets/diagram-UQ7AKVKN-BJR4nhVr.js +41 -0
  284. ksadk/server/static/assets/diagram-VSXAHHWV-xzcERX0a.js +3 -0
  285. ksadk/server/static/assets/diagram-VX7I27RA-tn2xM1xu.js +24 -0
  286. ksadk/server/static/assets/diagram-Z3DM3KII-BZPGBkw5.js +24 -0
  287. ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-4zN1_tq5.js} +1 -1
  288. ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-8VWI-2bZ.js} +1 -1
  289. ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-B-46q3Hk.js} +1 -1
  290. ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-B0gdk4rl.js} +1 -1
  291. ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-B1oWRmrH.js} +1 -1
  292. ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-BI1qQH4n.js} +1 -1
  293. ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-BJLUwH6W.js} +1 -1
  294. ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Bbwqa8TG.js} +1 -1
  295. ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-Btyo4vX4.js} +1 -1
  296. ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-C71CsujO.js} +1 -1
  297. ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-CXpH_Rdd.js} +1 -1
  298. ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-Cq4OL6nO.js} +1 -1
  299. ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-D1V3f1B4.js} +1 -1
  300. ksadk/server/static/assets/{dist-fhOuWds5.js → dist-DJmCPRVB.js} +1 -1
  301. ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DQAB0qn_.js} +2 -2
  302. ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DUwKa_l6.js} +1 -1
  303. ksadk/server/static/assets/dist-DdRBIceA.js +1 -0
  304. ksadk/server/static/assets/{dist-DWKByxWV.js → dist-DfGFrb0v.js} +1 -1
  305. ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-DjhwGUgZ.js} +1 -1
  306. ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-DyqV4fL4.js} +1 -1
  307. ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-M67Hwu0n.js} +1 -1
  308. ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-u8tVH5Ak.js} +1 -1
  309. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-DVfMsLge.js +1 -0
  310. ksadk/server/static/assets/erDiagram-SSCWMZ5O-CWEZSYaw.js +99 -0
  311. ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +1 -0
  312. ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +1 -0
  313. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-TWIxoyYL.js +292 -0
  314. ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +1 -0
  315. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-DDh2hd2S.js +106 -0
  316. ksadk/server/static/assets/index-B2k_urY8.js +241 -0
  317. ksadk/server/static/assets/index-ILPt6IWF.css +2 -0
  318. ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +1 -0
  319. ksadk/server/static/assets/infoDiagram-RXCK75RN-CgoyWiDR.js +2 -0
  320. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-CyRIu_om.js} +6 -6
  321. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DN5JXHzN.js} +6 -6
  322. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-CNP3ui66.js} +10 -10
  323. ksadk/server/static/assets/katex-DWEzQy1d.js +257 -0
  324. ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-C-qHp7d2.js} +1 -1
  325. ksadk/server/static/assets/mermaid-parser.core-Cl-K943T.js +166 -0
  326. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-D5DB5mYm.js} +29 -29
  327. ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +1 -0
  328. ksadk/server/static/assets/pegDiagram-XKGWAZYB-BQVc9ia_.js +1 -0
  329. ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +1 -0
  330. ksadk/server/static/assets/pieDiagram-E7YTZNPT-B6UCAvtY.js +39 -0
  331. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-mIr2d3eb.js} +6 -6
  332. ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +1 -0
  333. ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +1 -0
  334. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +1 -0
  335. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +1 -0
  336. ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +1 -0
  337. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-Dt3FFDmK.js +1 -0
  338. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-Cz_vQtgF.js +84 -0
  339. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-wlO1TsH5.js} +7 -7
  340. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-CHRbElYq.js} +10 -10
  341. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-DXX54iyO.js +1 -0
  342. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-Dp_lw9II.js +1 -0
  343. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +1 -0
  344. ksadk/server/static/assets/swimlanes-XN3QIQJK-D8O5UlLG.js +1 -0
  345. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +8 -0
  346. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-8mfKcxvG.js} +7 -7
  347. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +1 -0
  348. ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +1 -0
  349. ksadk/server/static/assets/vennDiagram-4TSXK5OY-EnNl0DE7.js +34 -0
  350. ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +1 -0
  351. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DBu5fbYr.js +78 -0
  352. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-BSVXs4IY.js +7 -0
  353. ksadk/server/static/index.html +2 -2
  354. ksadk/sessions/__init__.py +26 -9
  355. ksadk/sessions/_local_service_sync.py +688 -0
  356. ksadk/sessions/_local_tables.py +21 -0
  357. ksadk/sessions/_postgres_schema.py +305 -0
  358. ksadk/sessions/_postgres_tables.py +15 -0
  359. ksadk/sessions/base.py +80 -1
  360. ksadk/sessions/in_memory.py +53 -2
  361. ksadk/sessions/local_service.py +63 -604
  362. ksadk/sessions/postgres_service.py +58 -148
  363. ksadk/sessions/resilient.py +35 -1
  364. ksadk/skills/service_client.py +48 -0
  365. ksadk/studio/agent_lifecycle.py +26 -20
  366. ksadk/studio/api.py +1063 -69
  367. ksadk/studio/api_catalog_routes.py +5 -0
  368. ksadk/studio/api_contracts.py +148 -6
  369. ksadk/studio/api_memory_routes.py +82 -0
  370. ksadk/studio/api_plugin_routes.py +480 -0
  371. ksadk/studio/attachment_store.py +187 -0
  372. ksadk/studio/authoring.py +259 -28
  373. ksadk/studio/authoring_coordinator.py +809 -67
  374. ksadk/studio/builder.py +197 -40
  375. ksadk/studio/capabilities.py +17 -0
  376. ksadk/studio/cloud.py +1920 -95
  377. ksadk/studio/codex_agent_service.py +137 -15
  378. ksadk/studio/codex_authoring.py +469 -0
  379. ksadk/studio/codex_builder.py +177 -25
  380. ksadk/studio/codex_manifest.py +71 -22
  381. ksadk/studio/codex_run.py +59 -4
  382. ksadk/studio/compatibility_report.py +445 -0
  383. ksadk/studio/compiler.py +7 -1
  384. ksadk/studio/contracts.py +234 -4
  385. ksadk/studio/dsh_provider_registration.py +768 -0
  386. ksadk/studio/event_store.py +79 -101
  387. ksadk/studio/framework_run.py +264 -3
  388. ksadk/studio/hosted_kernel.py +317 -0
  389. ksadk/studio/manifest_resolver.py +131 -0
  390. ksadk/studio/model_client.py +95 -9
  391. ksadk/studio/model_profile_service.py +1 -1
  392. ksadk/studio/operations.py +47 -5
  393. ksadk/studio/otel_trace.py +348 -42
  394. ksadk/studio/pcm_memory.py +85 -0
  395. ksadk/studio/plugin_composition.py +301 -0
  396. ksadk/studio/plugin_kernel_adapter.py +128 -0
  397. ksadk/studio/plugin_runtime.py +652 -0
  398. ksadk/studio/repository.py +23 -2
  399. ksadk/studio/resource_catalog.py +49 -1
  400. ksadk/studio/run_service.py +1266 -202
  401. ksadk/studio/runtime_source.py +51 -17
  402. ksadk/studio/scheduler_runtime.py +374 -0
  403. ksadk/studio/scheduler_service.py +211 -0
  404. ksadk/studio/service.py +1815 -117
  405. ksadk/studio/shared_web.py +199 -50
  406. ksadk/studio/soul.py +52 -0
  407. ksadk/studio/static/assets/index-DXs1Eq4M.css +1 -0
  408. ksadk/studio/static/assets/index-DeDqwTsD.js +283 -0
  409. ksadk/studio/static/index.html +2 -2
  410. ksadk/studio/templates.py +100 -8
  411. ksadk/studio/validator.py +20 -1
  412. ksadk/studio/workspace.py +31 -32
  413. ksadk/tracing/setup.py +304 -3
  414. ksadk/version.py +1 -1
  415. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  416. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  417. ksadk_runtime_common/workspace_files/path_utils.py +41 -0
  418. ksadk/events/parser.py +0 -191
  419. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  420. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
  421. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
  422. ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
  423. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
  424. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
  425. ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
  426. ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
  427. ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
  428. ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
  429. ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
  430. ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
  431. ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
  432. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
  433. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
  434. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
  435. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  436. ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
  437. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  438. ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
  439. ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
  440. ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
  441. ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
  442. ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
  443. ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
  444. ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
  445. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
  446. ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
  447. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
  448. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
  449. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
  450. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  451. ksadk/server/static/assets/index-R52pJJvg.js +0 -177
  452. ksadk/server/static/assets/index-jJr3_955.css +0 -2
  453. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
  454. ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
  455. ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
  456. ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
  457. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
  458. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
  459. ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
  460. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
  461. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
  462. ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
  463. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
  464. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
  465. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
  466. ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
  467. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
  468. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
  469. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
  470. ksadk/studio/evaluation.py +0 -192
  471. ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
  472. ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
  473. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/WHEEL +0 -0
  474. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/entry_points.txt +0 -0
  475. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/licenses/LICENSE +0 -0
  476. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,419 @@
1
+ """Shadow 基线采集器(评测方案第 10 节阶段 1:建立 Baseline)。
2
+
3
+ 当前处于 shadow 阶段:shadow plan 不改运行行为,本采集器只把每次 turn 的可观测信号
4
+ 落盘成结构化记录,作为后续 A/B 对比的基准。对齐评测方案:
5
+
6
+ - §3 A/B 记录字段(commit / runner_type / model / policy_version / prompt_hash ...)
7
+ - §5.4 效率指标(input/output token、PTL rate、compaction、accounting accuracy)
8
+ - §7.8 采集指标(prompt/history/memory/tool token、prompt_hash、capability hash、
9
+ planned/projected/actual accuracy)
10
+
11
+ 采集器只读 shadow plan dict 与 runtime usage/事件,不接触模型正文、凭证或敏感内容
12
+ (安全要求 §19:默认只记录 hash、长度、类型和脱敏摘要)。输出 JSONL,每行一条 turn 记录。
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import atexit
18
+ import json
19
+ import os
20
+ import threading
21
+ import time
22
+ from dataclasses import asdict, dataclass, field
23
+ from pathlib import Path
24
+ from typing import Any, Mapping
25
+
26
+
27
+ def _safe_commit() -> str:
28
+ """取当前 git commit(脱敏:只取短 hash,失败时返回占位符,不抛异常)。"""
29
+ import subprocess
30
+
31
+ try:
32
+ result = subprocess.run(
33
+ ["git", "rev-parse", "--short", "HEAD"],
34
+ capture_output=True,
35
+ text=True,
36
+ timeout=2,
37
+ )
38
+ if result.returncode == 0:
39
+ return result.stdout.strip() or "unknown"
40
+ except Exception: # noqa: BLE001
41
+ pass
42
+ return os.environ.get("KSADK_EVAL_COMMIT", "unknown")
43
+
44
+
45
+ def _ksadk_version() -> str:
46
+ try:
47
+ from ksadk.version import VERSION
48
+
49
+ return str(VERSION)
50
+ except Exception: # noqa: BLE001
51
+ return "unknown"
52
+
53
+
54
+ @dataclass
55
+ class BaselineTurnRecord:
56
+ """单次 turn 的基线记录(评测方案 §12.4 单 Case 记录 + §7.8 采集指标)。"""
57
+
58
+ # 版本与环境(§3)
59
+ ksadk_commit: str = ""
60
+ ksadk_version: str = ""
61
+ context_policy_version: str = ""
62
+ runner_type: str = ""
63
+ deployment_mode: str = ""
64
+ integration_mode: str = ""
65
+ accounting_accuracy: str = ""
66
+ capability_hash: str = ""
67
+ model: str = ""
68
+ execution_target: str = ""
69
+
70
+ # 上下文计划标识与 hash(§7.8)
71
+ plan_id: str = ""
72
+ prompt_content_hash: str = ""
73
+ prompt_stable_prefix_hash: str = ""
74
+ tokenizer: str = ""
75
+
76
+ # token 分类(§7.8 prompt/history/memory/tool token)
77
+ tokens_by_kind: dict[str, int] = field(default_factory=dict)
78
+ prompt_tokens_by_section: dict[str, int] = field(default_factory=dict)
79
+ planned_input_tokens: int = 0
80
+
81
+ # runtime 实际 usage(§5.4,runtime_reported 时才有)
82
+ runtime_reported_input_tokens: int | None = None
83
+ runtime_output_tokens: int | None = None
84
+ cache_read_tokens: int | None = None
85
+ cache_creation_tokens: int | None = None
86
+ cache_status: str = ""
87
+ unexpected_break: bool = False
88
+
89
+ # 效率与稳定性(§5.4)
90
+ compaction_triggered: bool = False
91
+ compaction_trigger: str = ""
92
+ prompt_too_long: bool = False
93
+ retry_attempts: int = 0
94
+ turn_latency_ms: int | None = None
95
+ time_to_first_token_ms: int | None = None
96
+
97
+ # 可诊断性(§5.4 opaque request rate)
98
+ capability_mismatch: bool = False
99
+
100
+ # 元数据
101
+ session_id: str = ""
102
+ invocation_id: str = ""
103
+ recorded_at: str = ""
104
+
105
+
106
+ class BaselineCollector:
107
+ """进程内基线采集器:从 shadow plan dict + runtime usage 累积 turn 记录。
108
+
109
+ 用法:在 conversation runtime 旁路(shadow plan 已挂在 prepared.shadow_context_plan)
110
+ 调用 ``record_turn(plan, usage=..., latency_ms=...)``;运行结束后 ``dump(path)`` 落盘。
111
+ 采集器本身不挂任何 span/不进决策路径,纯旁路只读。
112
+ """
113
+
114
+ def __init__(self, *, execution_target: str = "local") -> None:
115
+ self._records: list[BaselineTurnRecord] = []
116
+ self._records_lock = threading.RLock()
117
+ self._commit = _safe_commit()
118
+ self._version = _ksadk_version()
119
+ self._execution_target = execution_target
120
+
121
+ @property
122
+ def records(self) -> list[BaselineTurnRecord]:
123
+ with self._records_lock:
124
+ return list(self._records)
125
+
126
+ def record_turn(
127
+ self,
128
+ plan: Mapping[str, Any] | None,
129
+ *,
130
+ session_id: str = "",
131
+ invocation_id: str = "",
132
+ model: str = "",
133
+ usage: Mapping[str, Any] | None = None,
134
+ compaction_triggered: bool = False,
135
+ compaction_trigger: str = "",
136
+ prompt_too_long: bool = False,
137
+ retry_attempts: int = 0,
138
+ turn_latency_ms: int | None = None,
139
+ time_to_first_token_ms: int | None = None,
140
+ capability_mismatch: bool = False,
141
+ ) -> BaselineTurnRecord:
142
+ """从 shadow plan dict 构造一条 turn 记录并累积。
143
+
144
+ ``plan`` 为 None(如未生成 shadow plan)时仍记录一条最小记录,标注
145
+ ``accounting_accuracy=opaque``,便于统计 opaque request rate(§5.4)。
146
+ """
147
+ record = BaselineTurnRecord(
148
+ ksadk_commit=self._commit,
149
+ ksadk_version=self._version,
150
+ execution_target=self._execution_target,
151
+ session_id=session_id,
152
+ invocation_id=invocation_id,
153
+ model=str(model or ""),
154
+ recorded_at=_utc_now_iso(),
155
+ compaction_triggered=compaction_triggered,
156
+ compaction_trigger=compaction_trigger,
157
+ prompt_too_long=prompt_too_long,
158
+ retry_attempts=retry_attempts,
159
+ turn_latency_ms=turn_latency_ms,
160
+ time_to_first_token_ms=time_to_first_token_ms,
161
+ capability_mismatch=capability_mismatch,
162
+ )
163
+ if isinstance(plan, Mapping) and plan:
164
+ record.context_policy_version = str(plan.get("policy_version") or "")
165
+ record.runner_type = str(plan.get("runtime_type") or "")
166
+ record.deployment_mode = str(plan.get("deployment_mode") or "local")
167
+ record.integration_mode = str(plan.get("integration_mode") or "")
168
+ record.accounting_accuracy = str(plan.get("accounting_accuracy") or "opaque")
169
+ record.capability_hash = str(plan.get("capability_hash") or "")
170
+ record.plan_id = str(plan.get("plan_id") or "")
171
+ record.prompt_content_hash = str(plan.get("prompt_content_hash") or "")
172
+ record.prompt_stable_prefix_hash = str(plan.get("prompt_stable_prefix_hash") or "")
173
+ record.tokenizer = str(plan.get("tokenizer") or "")
174
+ tbk = plan.get("tokens_by_kind")
175
+ if isinstance(tbk, Mapping):
176
+ record.tokens_by_kind = {str(k): int(v or 0) for k, v in tbk.items()}
177
+ tbs = plan.get("prompt_tokens_by_section")
178
+ if isinstance(tbs, Mapping):
179
+ record.prompt_tokens_by_section = {str(k): int(v or 0) for k, v in tbs.items()}
180
+ record.planned_input_tokens = int(plan.get("planned_input_tokens") or 0)
181
+ else:
182
+ record.accounting_accuracy = "opaque"
183
+
184
+ if isinstance(usage, Mapping) and usage:
185
+ record.runtime_reported_input_tokens = _opt_int(
186
+ usage.get("input_tokens") or usage.get("prompt_tokens")
187
+ )
188
+ record.runtime_output_tokens = _opt_int(
189
+ usage.get("output_tokens") or usage.get("completion_tokens")
190
+ )
191
+ details = usage.get("input_token_details") or usage.get("input_tokens_details")
192
+ if isinstance(details, Mapping):
193
+ record.cache_read_tokens = _opt_int(
194
+ details.get("cached_tokens")
195
+ or details.get("cached")
196
+ or details.get("cache_read")
197
+ )
198
+ record.cache_read_tokens = (
199
+ _opt_int(usage.get("cache_read_input_tokens")) or record.cache_read_tokens
200
+ )
201
+ record.cache_creation_tokens = _opt_int(usage.get("cache_creation_input_tokens"))
202
+
203
+ # cache_status/unexpected_break 由 span 路径(_set_prompt_cache_attributes)同源诊断
204
+ # 并写入 trace;baseline 只记录 raw cache tokens,不重复跑 registry(避免与 span 路径
205
+ # 共享 registry 时的记录顺序污染)。summary 的 unexpected_cache_break_count 据此如实
206
+ # 为 0;完整诊断看 trace。如需 baseline 独立诊断,后续 PR 用独立 registry。
207
+ with self._records_lock:
208
+ self._records.append(record)
209
+ if _flush_each_turn_enabled():
210
+ self.dump(os.environ.get(_BASELINE_PATH_ENV, _DEFAULT_BASELINE_PATH))
211
+ return record
212
+
213
+ def summary(self) -> dict[str, Any]:
214
+ """汇总指标(评测方案 §12.2 Scorecard 的基线版)。"""
215
+ with self._records_lock:
216
+ if not self._records:
217
+ return {"turn_count": 0}
218
+ total = len(self._records)
219
+ planned = [r.planned_input_tokens for r in self._records if r.planned_input_tokens]
220
+ reported = [
221
+ r.runtime_reported_input_tokens
222
+ for r in self._records
223
+ if r.runtime_reported_input_tokens is not None
224
+ ]
225
+ latencies = [r.turn_latency_ms for r in self._records if r.turn_latency_ms is not None]
226
+ return {
227
+ "turn_count": total,
228
+ "ptl_rate": _ratio(sum(1 for r in self._records if r.prompt_too_long), total),
229
+ "compaction_count": sum(1 for r in self._records if r.compaction_triggered),
230
+ "ptl_recovery_count": sum(
231
+ 1
232
+ for r in self._records
233
+ if r.prompt_too_long and r.retry_attempts >= 1 and not _is_failed(r)
234
+ ),
235
+ "opaque_request_rate": _ratio(
236
+ sum(1 for r in self._records if r.accounting_accuracy == "opaque"), total
237
+ ),
238
+ "capability_mismatch_count": sum(1 for r in self._records if r.capability_mismatch),
239
+ "unexpected_cache_break_count": sum(1 for r in self._records if r.unexpected_break),
240
+ "planned_input_tokens": _stats(planned),
241
+ "runtime_reported_input_tokens": _stats(reported),
242
+ "turn_latency_ms": _stats(latencies),
243
+ "runner_type_breakdown": _count_by([r.runner_type for r in self._records]),
244
+ "accounting_accuracy_breakdown": _count_by(
245
+ [r.accounting_accuracy for r in self._records]
246
+ ),
247
+ "stable_prefix_hash_changes": _count_distinct(
248
+ [
249
+ r.prompt_stable_prefix_hash
250
+ for r in self._records
251
+ if r.prompt_stable_prefix_hash
252
+ ]
253
+ ),
254
+ }
255
+
256
+ def dump(self, path: str | Path) -> Path:
257
+ """落盘 JSONL(每行一条 turn 记录)+ 末尾一条 ``__summary__`` 汇总。"""
258
+ out = Path(path)
259
+ out.parent.mkdir(parents=True, exist_ok=True)
260
+ temporary = out.with_name(f".{out.name}.{os.getpid()}.tmp")
261
+ with self._records_lock:
262
+ with temporary.open("w", encoding="utf-8") as fh:
263
+ for record in self._records:
264
+ fh.write(json.dumps(asdict(record), ensure_ascii=False) + "\n")
265
+ fh.write(json.dumps({"__summary__": self.summary()}, ensure_ascii=False) + "\n")
266
+ os.replace(temporary, out)
267
+ return out
268
+
269
+ def clear(self) -> None:
270
+ with self._records_lock:
271
+ self._records.clear()
272
+
273
+
274
+ def _opt_int(value: Any) -> int | None:
275
+ if value is None:
276
+ return None
277
+ try:
278
+ return int(value)
279
+ except (TypeError, ValueError):
280
+ return None
281
+
282
+
283
+ def _ratio(numerator: int, denominator: int) -> float:
284
+ return round(numerator / denominator, 4) if denominator else 0.0
285
+
286
+
287
+ def _stats(values: list[int]) -> dict[str, float]:
288
+ if not values:
289
+ return {"count": 0}
290
+ sorted_vals = sorted(values)
291
+ n = len(sorted_vals)
292
+ p95_idx = min(n - 1, int(n * 0.95))
293
+ return {
294
+ "count": n,
295
+ "mean": round(sum(values) / n, 2),
296
+ "median": sorted_vals[n // 2],
297
+ "p95": sorted_vals[p95_idx],
298
+ }
299
+
300
+
301
+ def _count_by(values: list[str]) -> dict[str, int]:
302
+ counts: dict[str, int] = {}
303
+ for value in values:
304
+ counts[value] = counts.get(value, 0) + 1
305
+ return counts
306
+
307
+
308
+ def _count_distinct(values: list[str]) -> int:
309
+ return len(set(values))
310
+
311
+
312
+ def _is_failed(record: BaselineTurnRecord) -> bool:
313
+ # 没有 runtime output 且无 planned token 视为失败 turn(粗略,供 PTL recovery 统计)。
314
+ return record.runtime_output_tokens is None and record.planned_input_tokens == 0
315
+
316
+
317
+ def _utc_now_iso() -> str:
318
+ # 不能用 datetime.now()(脚本环境可能受限);用 time + 手动格式化 UTC。
319
+ t = time.time()
320
+ secs = int(t)
321
+ millis = int((t - secs) * 1000)
322
+ g = time.gmtime(secs)
323
+ return (
324
+ f"{g.tm_year:04d}-{g.tm_mon:02d}-{g.tm_mday:02d}T"
325
+ f"{g.tm_hour:02d}:{g.tm_min:02d}:{g.tm_sec:02d}.{millis:03d}Z"
326
+ )
327
+
328
+
329
+ # ---------------------------------------------------------------------------
330
+ # 进程级单例 + env-gated 采集挂载
331
+ # ---------------------------------------------------------------------------
332
+
333
+ _BASELINE_COLLECT_ENV = "KSADK_BASELINE_COLLECT"
334
+ _BASELINE_PATH_ENV = "KSADK_BASELINE_PATH"
335
+ _DEFAULT_BASELINE_PATH = "/tmp/ksadk-context-baseline.jsonl"
336
+
337
+
338
+ def _flush_each_turn_enabled() -> bool:
339
+ return str(os.environ.get("KSADK_BASELINE_FLUSH_EACH_TURN", "")).strip().lower() in {
340
+ "1",
341
+ "true",
342
+ "yes",
343
+ "on",
344
+ }
345
+
346
+
347
+ _singleton_lock = threading.Lock()
348
+ _singleton: BaselineCollector | None = None
349
+ _atexit_registered = False
350
+
351
+
352
+ def baseline_collection_enabled() -> bool:
353
+ """是否启用基线采集(env ``KSADK_BASELINE_COLLECT=1/true/on``)。"""
354
+ return str(os.environ.get(_BASELINE_COLLECT_ENV, "")).strip().lower() in (
355
+ "1",
356
+ "true",
357
+ "yes",
358
+ "on",
359
+ )
360
+
361
+
362
+ def _baseline_path() -> str:
363
+ return str(os.environ.get(_BASELINE_PATH_ENV, "") or _DEFAULT_BASELINE_PATH)
364
+
365
+
366
+ def get_baseline_collector() -> BaselineCollector | None:
367
+ """返回进程级采集器单例;未启用(env 未开)时返回 None。
368
+
369
+ 首次启用时注册 atexit dump,保证进程结束自动落盘(评测方案 §10 阶段 1)。
370
+ """
371
+ global _singleton, _atexit_registered
372
+ if not baseline_collection_enabled():
373
+ return None
374
+ with _singleton_lock:
375
+ if _singleton is None:
376
+ execution_target = str(os.environ.get("KSADK_BASELINE_EXECUTION_TARGET", "runtime"))
377
+ _singleton = BaselineCollector(execution_target=execution_target)
378
+ if not _atexit_registered:
379
+ atexit.register(_atexit_dump)
380
+ _atexit_registered = True
381
+ return _singleton
382
+
383
+
384
+ def _atexit_dump() -> None:
385
+ global _singleton
386
+ if _singleton is None or not _singleton.records:
387
+ return
388
+ try:
389
+ _singleton.dump(_baseline_path())
390
+ except Exception: # noqa: BLE001
391
+ # 采集不能影响进程退出/线上行为。
392
+ pass
393
+
394
+
395
+ def reset_baseline_collector_for_tests() -> None:
396
+ """测试用:重置单例与 atexit 标记。"""
397
+ global _singleton, _atexit_registered
398
+ with _singleton_lock:
399
+ _singleton = None
400
+ _atexit_registered = False
401
+
402
+
403
+ def record_baseline_turn(
404
+ plan: Mapping[str, Any] | None,
405
+ **kwargs: Any,
406
+ ) -> None:
407
+ """env-gated 旁路采集入口:未启用时 no-op,启用时委托给单例 ``record_turn``。
408
+
409
+ 供 conversation runtime 旁路调用:传入 ``prepared.shadow_context_plan`` 与
410
+ usage/compaction/PTL/latency 等真实信号。不抛异常、不进决策路径、不改线上行为。
411
+ """
412
+ collector = get_baseline_collector()
413
+ if collector is None:
414
+ return
415
+ try:
416
+ collector.record_turn(plan, **kwargs)
417
+ except Exception: # noqa: BLE001
418
+ # 采集失败绝不影响主链路。
419
+ pass
@@ -0,0 +1,228 @@
1
+ """Prompt Cache 失效诊断(方案 7.5)。
2
+
3
+ KsADK 不实现通用 Completion Cache(ADR-013)。本模块只消费 Provider/Runtime 返回的
4
+ prompt cache usage,诊断稳定前缀是否意外失效:
5
+
6
+ - AgentVersion/模型/稳定 section/projection version 变化 → ``expected_invalidation``。
7
+ - 稳定前缀未变但 cache_read 大幅下降(cache_creation>0 且 cache_read≈0)→ 疑似
8
+ ``unexpected_break``。
9
+ - 无 runtime usage 或无稳定前缀 → ``opaque`` / ``no_cache_info``,不推断命中率。
10
+
11
+ PR2 只落地诊断原语 + 把 raw 信号记到 span;跨 turn 的"大幅下降"需要历史 hash,本 PR 用
12
+ 进程内 best-effort 的"上一稳定前缀"记录(见 ``CacheBreakRegistry``),pod 重启后清空,
13
+ 精度如实标注。完成正式跨 session 历史留后续 PR。
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from dataclasses import dataclass, field
19
+ from typing import Any, Mapping
20
+
21
+ from ksadk.context_engine.capabilities import ContextAccuracy
22
+
23
+ CacheBreakStatus = str
24
+ """``cached`` / ``expected_invalidation`` / ``unexpected_break``
25
+ / ``no_cache_info`` / ``opaque``."""
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class CacheBreakDiagnosis:
30
+ """单次请求的 prompt cache 失效诊断结果。"""
31
+
32
+ status: CacheBreakStatus
33
+ cache_read_tokens: int = 0
34
+ cache_creation_tokens: int = 0
35
+ stable_prefix_hash: str = ""
36
+ previous_stable_prefix_hash: str = ""
37
+ unexpected_break: bool = False
38
+ expected_invalidation: bool = False
39
+ break_reason: str = ""
40
+ accounting_accuracy: ContextAccuracy = "opaque"
41
+ metadata: dict[str, Any] = field(default_factory=dict)
42
+
43
+
44
+ def _extract_cache_tokens(usage: Mapping[str, Any] | None) -> tuple[int, int]:
45
+ """从 runtime usage 取 (cache_read_tokens, cache_creation_tokens)。
46
+
47
+ 兼容 OpenAI(prompt_tokens_details.cached_tokens / cache_read)与 Anthropic
48
+ (cache_read_input_tokens / cache_creation_input_tokens)两种字段命名。
49
+ """
50
+ if not isinstance(usage, Mapping):
51
+ return 0, 0
52
+ cache_read = 0
53
+ cache_creation = 0
54
+
55
+ # Anthropic 风格顶层字段
56
+ for read_key in ("cache_read_input_tokens", "cache_read_tokens"):
57
+ value = usage.get(read_key)
58
+ if value is not None:
59
+ try:
60
+ cache_read = max(cache_read, int(value))
61
+ except (TypeError, ValueError):
62
+ pass
63
+ for creation_key in ("cache_creation_input_tokens", "cache_creation_tokens"):
64
+ value = usage.get(creation_key)
65
+ if value is not None:
66
+ try:
67
+ cache_creation = max(cache_creation, int(value))
68
+ except (TypeError, ValueError):
69
+ pass
70
+
71
+ # OpenAI / 通用 input_token_details.cached
72
+ input_details = usage.get("input_token_details") or usage.get("input_tokens_details")
73
+ if isinstance(input_details, Mapping):
74
+ cached = (
75
+ input_details.get("cached_tokens")
76
+ or input_details.get("cached")
77
+ or input_details.get("cache_read")
78
+ )
79
+ if cached is not None:
80
+ try:
81
+ cache_read = max(cache_read, int(cached))
82
+ except (TypeError, ValueError):
83
+ pass
84
+ prompt_details = usage.get("prompt_tokens_details")
85
+ if isinstance(prompt_details, Mapping):
86
+ cached = prompt_details.get("cached_tokens")
87
+ if cached is not None:
88
+ try:
89
+ cache_read = max(cache_read, int(cached))
90
+ except (TypeError, ValueError):
91
+ pass
92
+ return cache_read, cache_creation
93
+
94
+
95
+ # 阈值:稳定前缀未变但 cache_read 低于此比例 + 本轮有 cache_creation → 疑似 unexpected break。
96
+ _UNEXPECTED_CACHE_READ_RATIO = 0.10
97
+
98
+
99
+ def diagnose_cache_break(
100
+ *,
101
+ stable_prefix_hash: str,
102
+ previous_stable_prefix_hash: str | None,
103
+ usage: Mapping[str, Any] | None,
104
+ accounting_accuracy: ContextAccuracy = "opaque",
105
+ expected_invalidation_signal: bool = False,
106
+ ) -> CacheBreakDiagnosis:
107
+ """诊断本次请求的 prompt cache 失效情况(方案 7.5)。
108
+
109
+ Args:
110
+ stable_prefix_hash: 本轮稳定前缀 hash(来自 ``CompiledPrompt``)。
111
+ previous_stable_prefix_hash: 上一轮稳定前缀 hash(None 表示无历史/首次)。
112
+ usage: Runtime/Provider 返回的 usage mapping(含 cache_read/creation)。
113
+ accounting_accuracy: 该 Runner 的 token 观测精度。
114
+ expected_invalidation_signal: 调用方已知本轮发生了版本/模型/projection 变化。
115
+ """
116
+ cache_read, cache_creation = _extract_cache_tokens(usage)
117
+
118
+ # opaque:Runner 不暴露可靠 usage(方案 6.3)。
119
+ if accounting_accuracy == "opaque":
120
+ return CacheBreakDiagnosis(
121
+ status="opaque",
122
+ cache_read_tokens=cache_read,
123
+ cache_creation_tokens=cache_creation,
124
+ stable_prefix_hash=stable_prefix_hash,
125
+ accounting_accuracy=accounting_accuracy,
126
+ )
127
+
128
+ # 无稳定前缀 → 无法判断是否意外失效,只记录 raw 信号。
129
+ if not stable_prefix_hash:
130
+ return CacheBreakDiagnosis(
131
+ status="no_cache_info",
132
+ cache_read_tokens=cache_read,
133
+ cache_creation_tokens=cache_creation,
134
+ stable_prefix_hash=stable_prefix_hash,
135
+ accounting_accuracy=accounting_accuracy,
136
+ break_reason="no stable prefix to diagnose",
137
+ )
138
+
139
+ # 无 runtime usage → runtime_reported/estimated 都可能拿不到 cache 字段。
140
+ if cache_read == 0 and cache_creation == 0 and not isinstance(usage, Mapping):
141
+ return CacheBreakDiagnosis(
142
+ status="no_cache_info",
143
+ stable_prefix_hash=stable_prefix_hash,
144
+ accounting_accuracy=accounting_accuracy,
145
+ break_reason="no runtime usage reported",
146
+ )
147
+
148
+ hash_changed = (
149
+ bool(previous_stable_prefix_hash) and previous_stable_prefix_hash != stable_prefix_hash
150
+ )
151
+ if hash_changed or expected_invalidation_signal:
152
+ return CacheBreakDiagnosis(
153
+ status="expected_invalidation",
154
+ cache_read_tokens=cache_read,
155
+ cache_creation_tokens=cache_creation,
156
+ stable_prefix_hash=stable_prefix_hash,
157
+ previous_stable_prefix_hash=previous_stable_prefix_hash or "",
158
+ expected_invalidation=True,
159
+ accounting_accuracy=accounting_accuracy,
160
+ break_reason="stable prefix changed or explicit invalidation signal",
161
+ )
162
+
163
+ if cache_read > 0:
164
+ # 稳定前缀未变且命中 → cached。
165
+ return CacheBreakDiagnosis(
166
+ status="cached",
167
+ cache_read_tokens=cache_read,
168
+ cache_creation_tokens=cache_creation,
169
+ stable_prefix_hash=stable_prefix_hash,
170
+ previous_stable_prefix_hash=previous_stable_prefix_hash or "",
171
+ accounting_accuracy=accounting_accuracy,
172
+ )
173
+
174
+ # 稳定前缀未变、无 cache_read、但有 cache_creation → 疑似 unexpected break。
175
+ if cache_creation > 0 and previous_stable_prefix_hash == stable_prefix_hash:
176
+ return CacheBreakDiagnosis(
177
+ status="unexpected_break",
178
+ cache_read_tokens=cache_read,
179
+ cache_creation_tokens=cache_creation,
180
+ stable_prefix_hash=stable_prefix_hash,
181
+ previous_stable_prefix_hash=previous_stable_prefix_hash or "",
182
+ unexpected_break=True,
183
+ accounting_accuracy=accounting_accuracy,
184
+ break_reason="stable prefix unchanged but cache created instead of read",
185
+ )
186
+
187
+ return CacheBreakDiagnosis(
188
+ status="no_cache_info",
189
+ cache_read_tokens=cache_read,
190
+ cache_creation_tokens=cache_creation,
191
+ stable_prefix_hash=stable_prefix_hash,
192
+ previous_stable_prefix_hash=previous_stable_prefix_hash or "",
193
+ accounting_accuracy=accounting_accuracy,
194
+ break_reason="no decisive cache signal",
195
+ )
196
+
197
+
198
+ class CacheBreakRegistry:
199
+ """进程内 best-effort 的"上一稳定前缀"记录,按 session 维度。
200
+
201
+ 用于跨 turn 检测稳定前缀是否变化。仅存活于进程内,pod 重启后清空;精度如实标注为
202
+ ``estimated``/``runtime_reported``(由调用方传入),不伪装成持久事实源。
203
+ """
204
+
205
+ def __init__(self, *, limit: int = 1024) -> None:
206
+ self._limit = limit
207
+ self._store: dict[str, str] = {}
208
+
209
+ def previous(self, session_id: str) -> str | None:
210
+ return self._store.get(session_id)
211
+
212
+ def record(self, session_id: str, stable_prefix_hash: str) -> None:
213
+ if not session_id or not stable_prefix_hash:
214
+ return
215
+ if session_id not in self._store and len(self._store) >= self._limit:
216
+ # 简单淘汰:丢一个最早的(dict 保序)。不做 LRU 复杂度。
217
+ self._store.pop(next(iter(self._store)))
218
+ self._store[session_id] = stable_prefix_hash
219
+
220
+ def clear(self) -> None:
221
+ self._store.clear()
222
+
223
+
224
+ _DEFAULT_REGISTRY = CacheBreakRegistry()
225
+
226
+
227
+ def get_default_cache_break_registry() -> CacheBreakRegistry:
228
+ return _DEFAULT_REGISTRY