claude-smart 0.2.41 → 0.2.43

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 (396) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/README.md +1 -1
  3. package/bin/claude-smart.js +86 -48
  4. package/package.json +10 -3
  5. package/plugin/.claude-plugin/plugin.json +9 -3
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +2 -2
  8. package/plugin/dashboard/next.config.ts +9 -1
  9. package/plugin/pyproject.toml +2 -2
  10. package/plugin/scripts/_lib.sh +91 -0
  11. package/plugin/scripts/backend-service.sh +46 -15
  12. package/plugin/scripts/cli.sh +29 -1
  13. package/plugin/scripts/codex-hook.js +72 -4
  14. package/plugin/scripts/dashboard-build.sh +1 -0
  15. package/plugin/scripts/dashboard-service.sh +1 -0
  16. package/plugin/scripts/ensure-plugin-root.sh +7 -14
  17. package/plugin/scripts/hook_entry.sh +1 -0
  18. package/plugin/scripts/smart-install.sh +18 -2
  19. package/plugin/src/claude_smart/cli.py +72 -38
  20. package/plugin/src/claude_smart/context_format.py +11 -12
  21. package/plugin/src/claude_smart/cs_cite.py +26 -12
  22. package/plugin/src/claude_smart/ids.py +13 -5
  23. package/plugin/uv.lock +1 -1
  24. package/plugin/vendor/reflexio/.env.example +53 -0
  25. package/plugin/vendor/reflexio/LICENSE +201 -0
  26. package/plugin/vendor/reflexio/README.md +338 -0
  27. package/plugin/vendor/reflexio/pyproject.toml +271 -0
  28. package/plugin/vendor/reflexio/reflexio/README.md +184 -0
  29. package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
  30. package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
  31. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
  32. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
  33. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
  34. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
  35. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
  36. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
  37. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
  38. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
  39. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
  40. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
  41. package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
  42. package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
  43. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
  44. package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
  45. package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
  46. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
  47. package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
  48. package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
  49. package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
  50. package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
  51. package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
  52. package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
  53. package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
  54. package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
  63. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
  64. package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
  65. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
  66. package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
  67. package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
  68. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
  69. package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
  70. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
  71. package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
  72. package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
  73. package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
  74. package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
  75. package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
  145. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
  146. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
  147. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
  148. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
  149. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
  150. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
  151. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
  152. package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
  153. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
  154. package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
  155. package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
  156. package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
  157. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
  158. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
  159. package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
  160. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
  161. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
  162. package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
  163. package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
  164. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
  165. package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
  166. package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
  167. package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
  168. package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
  169. package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
  170. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
  171. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
  172. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
  173. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
  174. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
  175. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
  176. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
  177. package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
  178. package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
  179. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
  180. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
  181. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
  182. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
  183. package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
  184. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
  185. package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
  186. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
  187. package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
  188. package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
  189. package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
  190. package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
  191. package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -0
  192. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
  193. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
  194. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
  195. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
  196. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +540 -0
  197. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
  198. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
  199. package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
  200. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
  201. package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
  202. package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
  203. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +110 -0
  204. package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
  205. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1595 -0
  206. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
  207. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -0
  208. package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
  209. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
  210. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
  211. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +210 -0
  212. package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
  213. package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +255 -0
  214. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
  215. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +177 -0
  216. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
  217. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +699 -0
  218. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
  219. package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
  220. package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
  221. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
  222. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
  223. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
  224. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
  225. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
  226. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
  227. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
  228. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
  229. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
  230. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
  231. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
  232. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
  233. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
  234. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
  235. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
  236. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
  237. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
  238. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
  239. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
  240. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
  241. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
  242. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
  243. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
  244. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
  245. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
  246. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
  247. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
  248. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
  249. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
  250. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
  251. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
  252. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
  253. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
  254. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
  255. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
  256. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
  257. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
  258. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
  259. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
  260. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
  261. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
  262. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
  263. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
  264. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
  265. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
  266. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
  267. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
  268. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
  269. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
  270. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
  271. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
  272. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
  273. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
  274. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
  275. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
  276. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
  277. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
  278. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
  279. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
  281. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
  282. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
  283. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
  289. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
  290. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
  292. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
  296. package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
  297. package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
  311. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
  312. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
  313. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
  318. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
  319. package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
  320. package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
  321. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
  322. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
  323. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
  324. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
  325. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
  326. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
  327. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
  328. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
  329. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
  330. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
  331. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
  332. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
  333. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
  334. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
  335. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
  336. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
  337. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
  338. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
  339. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
  340. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
  341. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
  342. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
  343. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
  344. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
  345. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
  346. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
  347. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
  348. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
  349. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
  350. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
  351. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
  352. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
  353. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
  354. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
  355. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
  356. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
  357. package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
  358. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
  359. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
  360. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
  361. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
  362. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
  363. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
  364. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
  365. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
  366. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
  367. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
  368. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
  369. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
  370. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
  371. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
  372. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
  373. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
  374. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
  375. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
  376. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
  377. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
  378. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
  379. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
  380. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
  381. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
  382. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
  383. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
  384. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
  385. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
  386. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
  387. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
  388. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
  389. package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
  390. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
  391. package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
  392. package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
  393. package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
  394. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
  395. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
  396. package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
@@ -0,0 +1,515 @@
1
+ """Aggregator that composes hero, tiles, rule attribution, and distribution.
2
+
3
+ The service does three reads (results, citations, playbook stats)
4
+ and returns a GetEvaluationOverviewResponse. It's invoked from the FastAPI
5
+ route handler; the storage is the same BaseStorage the rest of the server
6
+ uses, so the same instance is reused via request_context.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections import defaultdict
12
+ from collections.abc import Iterable
13
+ from dataclasses import dataclass
14
+ from datetime import UTC, datetime
15
+
16
+ from reflexio.models.api_schema.braintrust_schema import ImportedScore
17
+ from reflexio.models.api_schema.domain.entities import (
18
+ AgentSuccessEvaluationResult,
19
+ Request,
20
+ )
21
+ from reflexio.models.api_schema.eval_overview_schema import (
22
+ BraintrustTileRow,
23
+ BucketLiteral,
24
+ ContextTile,
25
+ GetEvaluationOverviewRequest,
26
+ GetEvaluationOverviewResponse,
27
+ HeroBlock,
28
+ HeroBucket,
29
+ NumberWithDelta,
30
+ PercentWithDelta,
31
+ RuleAttributionRow,
32
+ ScoreDistribution,
33
+ ShadowWinRateTrend,
34
+ SuccessRateTrendByGroup,
35
+ )
36
+ from reflexio.models.config_schema import Config
37
+ from reflexio.server.services.evaluation_overview.distribution import (
38
+ BUCKET_LABELS,
39
+ bucket_corrections,
40
+ )
41
+ from reflexio.server.services.evaluation_overview.group_aggregation import (
42
+ compute_trend_by_group,
43
+ )
44
+ from reflexio.server.services.evaluation_overview.hero_state import (
45
+ compute_hero_state,
46
+ )
47
+ from reflexio.server.services.evaluation_overview.rule_attribution import (
48
+ compute_net_sessions,
49
+ )
50
+ from reflexio.server.services.evaluation_overview.shadow_aggregation import (
51
+ compute_shadow_win_rate_trend,
52
+ )
53
+
54
+ _DAY_SECONDS = 24 * 60 * 60
55
+ _WEEK_SECONDS = 7 * 24 * 60 * 60
56
+ _TOP_N_RULES = 5
57
+
58
+
59
+ @dataclass
60
+ class EvaluationOverviewService:
61
+ """Builds the full /api/get_evaluation_overview payload.
62
+
63
+ The service holds a storage handle and the org's current Config. Each
64
+ call to `run` performs the three reads and returns a fresh response.
65
+ Stateless across calls — safe to reuse the instance across requests.
66
+ """
67
+
68
+ storage: object
69
+ config: Config
70
+
71
+ def run(
72
+ self, request: GetEvaluationOverviewRequest
73
+ ) -> GetEvaluationOverviewResponse:
74
+ """Build the overview payload for the requested window.
75
+
76
+ Time windows used here:
77
+ - ``[request.from_ts, request.to_ts]`` is the *trend* window. The hero
78
+ chart, rule attribution, and the rule-attribution session set are
79
+ all computed over it. The frontend sends an 8-week range so the
80
+ trend chart has shape.
81
+ - The *tile baseline* and *distribution baseline* are tighter:
82
+ ``last_7d`` (≤ to_ts) vs ``prior_7d`` (the 7d before that).
83
+ Tying these to ``request.from_ts`` is wrong — with an 8-week
84
+ request, ``prior`` would land 9 weeks back and always be empty,
85
+ so every tile would display "no baseline" regardless of how much
86
+ data the org has.
87
+ """
88
+ all_results = self.storage.get_agent_success_evaluation_results( # type: ignore[attr-defined]
89
+ agent_version=None, limit=10_000
90
+ )
91
+ results = [
92
+ r for r in all_results if request.from_ts <= r.created_at <= request.to_ts
93
+ ]
94
+
95
+ # Tile + distribution baselines are always last-7d-vs-prior-7d, anchored
96
+ # to ``request.to_ts`` (which is "now" from the frontend's perspective).
97
+ cur_7d_from = max(request.from_ts, request.to_ts - _WEEK_SECONDS)
98
+ prev_to = cur_7d_from
99
+ prev_from = max(0, prev_to - _WEEK_SECONDS)
100
+ results_current_7d = [
101
+ r for r in all_results if cur_7d_from <= r.created_at <= request.to_ts
102
+ ]
103
+ results_prev_7d = [
104
+ r for r in all_results if prev_from <= r.created_at < prev_to
105
+ ]
106
+
107
+ earliest_eval_ts = min((r.created_at for r in all_results), default=None)
108
+ hero = self._build_hero(request, results, earliest_eval_ts)
109
+ tiles = self._build_tiles(results_current_7d, results_prev_7d)
110
+ attribution = self._build_attribution(results)
111
+ distribution = self._build_distribution(results_current_7d, results_prev_7d)
112
+ braintrust_tiles = self._build_braintrust_tiles(
113
+ cur_7d_from, request.to_ts, prev_from, prev_to
114
+ )
115
+ # F2: group-aware success-rate trend curves. Joins each eval result
116
+ # with its session's first request metadata to bucket sessions into
117
+ # treatment / control / untagged, then time-buckets each curve at
118
+ # the same granularity as the hero chart.
119
+ success_rate_trend_by_group = self._build_group_trend(results, request.bucket)
120
+ # F1: per-turn shadow win-rate trend. Filters verdicts to the org's
121
+ # pinned judge prompt version so a future rubric bump doesn't
122
+ # silently mix epochs into the headline.
123
+ shadow_win_rate_trend = self._build_shadow_win_rate_trend(
124
+ request.from_ts, request.to_ts
125
+ )
126
+
127
+ return GetEvaluationOverviewResponse(
128
+ hero=hero,
129
+ context_tiles=tiles,
130
+ rule_attribution=attribution,
131
+ score_distribution=distribution,
132
+ braintrust_tiles=braintrust_tiles,
133
+ success_rate_trend_by_group=success_rate_trend_by_group,
134
+ shadow_win_rate_trend=shadow_win_rate_trend,
135
+ )
136
+
137
+ # --- private helpers ---
138
+
139
+ def _build_hero(
140
+ self,
141
+ request: GetEvaluationOverviewRequest,
142
+ results: list[AgentSuccessEvaluationResult],
143
+ earliest_eval_ts: int | None,
144
+ ) -> HeroBlock:
145
+ if earliest_eval_ts is None:
146
+ days_since = None
147
+ else:
148
+ days_since = (
149
+ int(datetime.now(UTC).timestamp()) - earliest_eval_ts
150
+ ) // 86_400
151
+ # Shadow direct-grade has been removed; counterfactual measurement is
152
+ # pending a methodologically sound replacement (see the validity spec).
153
+ # The state machine still runs so the EMPTY / SHADOW_OFF surfaces work,
154
+ # but the shadow-side fields are always None.
155
+ state = compute_hero_state(
156
+ shadow_enabled=False,
157
+ days_since_first_eval=days_since,
158
+ n_shadow_in_window=0,
159
+ total_results=len(results),
160
+ )
161
+ success_rate = _success_rate(results) * 100
162
+ return HeroBlock(
163
+ state=state.value, # type: ignore[arg-type]
164
+ regular_success_rate_pp=success_rate,
165
+ shadow_success_rate_pp=None,
166
+ delta_pp=None,
167
+ buckets=_buckets(results, request.bucket),
168
+ )
169
+
170
+ def _build_tiles(
171
+ self,
172
+ current: list[AgentSuccessEvaluationResult],
173
+ previous: list[AgentSuccessEvaluationResult],
174
+ ) -> ContextTile:
175
+ cur_success = _success_rate(current) * 100
176
+ prev_success = _success_rate(previous) * 100
177
+ cur_corr = _mean(r.number_of_correction_per_session for r in current)
178
+ prev_corr = _mean(r.number_of_correction_per_session for r in previous)
179
+ cur_turns = _mean(
180
+ r.user_turns_to_resolution
181
+ for r in current
182
+ if r.user_turns_to_resolution is not None
183
+ )
184
+ prev_turns = _mean(
185
+ r.user_turns_to_resolution
186
+ for r in previous
187
+ if r.user_turns_to_resolution is not None
188
+ )
189
+ cur_esc = _escalation_rate(current) * 100
190
+ prev_esc = _escalation_rate(previous) * 100
191
+ return ContextTile(
192
+ success=PercentWithDelta(
193
+ current=cur_success, delta_pp=cur_success - prev_success
194
+ ),
195
+ corrections=NumberWithDelta(current=cur_corr, delta=cur_corr - prev_corr),
196
+ turns=NumberWithDelta(current=cur_turns, delta=cur_turns - prev_turns),
197
+ escalation=PercentWithDelta(current=cur_esc, delta_pp=cur_esc - prev_esc),
198
+ )
199
+
200
+ def _build_attribution(
201
+ self, results: list[AgentSuccessEvaluationResult]
202
+ ) -> list[RuleAttributionRow]:
203
+ is_success_by_session = {r.session_id: r.is_success for r in results}
204
+ citations_by_session, rule_titles = self._load_citations(
205
+ list(is_success_by_session.keys())
206
+ )
207
+ rows = compute_net_sessions(
208
+ citations_by_session=citations_by_session,
209
+ is_success_by_session=is_success_by_session,
210
+ rule_titles=rule_titles,
211
+ top_n=_TOP_N_RULES,
212
+ )
213
+ return [
214
+ RuleAttributionRow(
215
+ rule_id=r.rule_id,
216
+ kind=r.kind, # type: ignore[arg-type]
217
+ title=r.title,
218
+ successes_with=r.successes_with,
219
+ failures_with=r.failures_with,
220
+ net_sessions=r.net_sessions,
221
+ cited_session_ids=list(r.cited_session_ids),
222
+ )
223
+ for r in rows
224
+ ]
225
+
226
+ def _load_citations(
227
+ self, session_ids: list[str]
228
+ ) -> tuple[dict[str, list[tuple[str, str]]], dict[tuple[str, str], str]]:
229
+ """Pull `Interaction.citations` keyed by session, with title lookup.
230
+
231
+ Falls back to empty data when the underlying storage method returns
232
+ no interactions (default behavior on backends that haven't yet
233
+ implemented `get_interactions_by_session`).
234
+ """
235
+ citations_by_session: dict[str, list[tuple[str, str]]] = defaultdict(list)
236
+ for sid in session_ids:
237
+ interactions = self.storage.get_interactions_by_session(sid) # type: ignore[attr-defined]
238
+ for interaction in interactions:
239
+ for cite in getattr(interaction, "citations", []) or []:
240
+ # Citations may arrive as Pydantic Citation objects (from
241
+ # the normal storage path) or as plain dicts (e.g. when
242
+ # tests stub the storage). Handle both shapes.
243
+ if isinstance(cite, dict):
244
+ kind = cite.get("kind")
245
+ rid = cite.get("real_id")
246
+ else:
247
+ kind = getattr(cite, "kind", None)
248
+ rid = getattr(cite, "real_id", None)
249
+ if kind and rid:
250
+ citations_by_session[sid].append((kind, str(rid)))
251
+ # Titles via existing playbook_application_stats lookup
252
+ stats = self.storage.get_playbook_application_stats(days_back=30) # type: ignore[attr-defined]
253
+ rule_titles = {(s.kind, s.real_id): s.title for s in stats}
254
+ return citations_by_session, rule_titles
255
+
256
+ def _build_braintrust_tiles(
257
+ self, from_ts: int, to_ts: int, prev_from: int, prev_to: int
258
+ ) -> list[BraintrustTileRow]:
259
+ """Aggregate imported_score rows per scorer_name for current + prior windows.
260
+
261
+ Returns [] when the org has no Braintrust connection (default no-op
262
+ storage returns []). The frontend treats an empty list as "not
263
+ connected" and hides the Braintrust strip entirely.
264
+ """
265
+ org_id = self._org_id()
266
+ if not org_id:
267
+ return []
268
+ current = self.storage.get_imported_scores(org_id, from_ts, to_ts) # type: ignore[attr-defined]
269
+ if not current:
270
+ return []
271
+ prior = self.storage.get_imported_scores(org_id, prev_from, prev_to) # type: ignore[attr-defined]
272
+ cur_agg = _aggregate_imported_scores(current)
273
+ prior_agg = _aggregate_imported_scores(prior)
274
+ rows: list[BraintrustTileRow] = []
275
+ for scorer_name, (mean, n) in sorted(cur_agg.items()):
276
+ # No prior data → set delta = current so the frontend's
277
+ # `delta == current` check renders "no baseline" honestly.
278
+ # With prior data → real difference.
279
+ prior_entry = prior_agg.get(scorer_name)
280
+ delta = mean if prior_entry is None else mean - prior_entry[0]
281
+ rows.append(
282
+ BraintrustTileRow(
283
+ scorer_name=scorer_name,
284
+ current=mean,
285
+ n=n,
286
+ delta=delta,
287
+ )
288
+ )
289
+ return rows
290
+
291
+ def _org_id(self) -> str:
292
+ """Resolve org_id from request_context when available; else empty string."""
293
+ # The service is constructed with `storage` + `config`; org_id isn't a
294
+ # direct attribute. For Plan C-overview, we read it via the storage
295
+ # instance's org_id when present (every BaseStorage carries one).
296
+ return str(getattr(self.storage, "org_id", "") or "")
297
+
298
+ def _build_shadow_win_rate_trend(
299
+ self,
300
+ from_ts: int,
301
+ to_ts: int,
302
+ ) -> ShadowWinRateTrend:
303
+ """Fetch shadow verdicts in the window and aggregate them per day.
304
+
305
+ Filters verdicts to the org's pinned ``shadow_comparison`` prompt
306
+ version (``Config.shadow_comparison_judge_prompt_version``) so a
307
+ future rubric bump never silently mixes epochs into the headline.
308
+ Backends that don't yet implement the verdicts table raise
309
+ ``NotImplementedError``; we degrade to the empty trend default so
310
+ the dashboard still renders the rest of the overview.
311
+
312
+ Args:
313
+ from_ts (int): Window start, Unix epoch seconds (UTC).
314
+ to_ts (int): Window end, Unix epoch seconds (UTC).
315
+
316
+ Returns:
317
+ ShadowWinRateTrend: Daily buckets + window total. Empty when
318
+ the window has no verdicts or the backend doesn't support
319
+ verdict storage.
320
+ """
321
+ pinned_version = self.config.shadow_comparison_judge_prompt_version
322
+ try:
323
+ verdicts = self.storage.get_shadow_comparison_verdicts( # type: ignore[attr-defined]
324
+ from_ts=from_ts,
325
+ to_ts=to_ts,
326
+ judge_prompt_version=pinned_version,
327
+ )
328
+ except NotImplementedError:
329
+ return ShadowWinRateTrend(judge_prompt_version=pinned_version)
330
+ return compute_shadow_win_rate_trend(
331
+ verdicts, judge_prompt_version=pinned_version
332
+ )
333
+
334
+ def _build_group_trend(
335
+ self,
336
+ results: list[AgentSuccessEvaluationResult],
337
+ bucket: str,
338
+ ) -> SuccessRateTrendByGroup:
339
+ """Build the dual+untagged-curve trend payload for the window.
340
+
341
+ Joins each eval result with the first request of its session to read
342
+ ``metadata.reflexio_retrieval_enabled``, then delegates to the pure
343
+ ``compute_trend_by_group`` aggregator. Returns the default empty
344
+ ``SuccessRateTrendByGroup`` when ``results`` is empty.
345
+
346
+ Args:
347
+ results (list[AgentSuccessEvaluationResult]): Eval results in the
348
+ trend window.
349
+ bucket (str): Bucket granularity literal (``"week"`` or ``"day"``).
350
+
351
+ Returns:
352
+ SuccessRateTrendByGroup: Three curves (any of which may be empty).
353
+ """
354
+ if not results:
355
+ return SuccessRateTrendByGroup()
356
+ bucket_seconds = _WEEK_SECONDS if bucket == "week" else _DAY_SECONDS
357
+ outcomes = self._build_group_outcomes(results)
358
+ return compute_trend_by_group(outcomes, bucket_seconds)
359
+
360
+ def _build_group_outcomes(
361
+ self,
362
+ results: list[AgentSuccessEvaluationResult],
363
+ ) -> list[tuple[str, int, bool, dict]]:
364
+ """Pair each eval result with its session's first-request metadata.
365
+
366
+ Caches ``session_id → first_request_metadata`` so storage is hit
367
+ once per distinct session, not once per result. Sessions without a
368
+ matching request fall through with an empty-dict metadata, which the
369
+ downstream aggregator routes to the UNTAGGED bucket.
370
+
371
+ Args:
372
+ results (list[AgentSuccessEvaluationResult]): Eval results to
373
+ annotate with their session's first-request metadata.
374
+
375
+ Returns:
376
+ list[tuple[str, int, bool, dict]]: Tuples of
377
+ ``(session_id, created_at, is_success, first_request_metadata)``
378
+ suitable for :func:`compute_trend_by_group`.
379
+ """
380
+ session_ids = {r.session_id for r in results if r.session_id}
381
+ first_request_metadata: dict[str, dict] = {}
382
+ for sid in session_ids:
383
+ reqs = self._get_session_requests(sid)
384
+ if reqs:
385
+ first = min(reqs, key=lambda r: r.created_at)
386
+ first_request_metadata[sid] = first.metadata or {}
387
+ else:
388
+ first_request_metadata[sid] = {}
389
+
390
+ return [
391
+ (
392
+ r.session_id or "",
393
+ r.created_at,
394
+ r.is_success,
395
+ first_request_metadata.get(r.session_id or "", {}),
396
+ )
397
+ for r in results
398
+ ]
399
+
400
+ def _get_session_requests(self, session_id: str) -> list[Request]:
401
+ """Return every request in ``session_id`` regardless of user.
402
+
403
+ Uses ``BaseStorage.get_sessions(session_id=...)`` because the
404
+ per-session, user-id-required ``get_requests_by_session`` doesn't
405
+ fit our caller (eval results don't carry ``user_id``). All locally
406
+ testable backends ignore the ``user_id`` filter when it's ``None``,
407
+ and ``top_k`` is raised well above realistic per-session counts so
408
+ we don't truncate.
409
+
410
+ Args:
411
+ session_id (str): The session whose requests to fetch.
412
+
413
+ Returns:
414
+ list[Request]: All requests in the session, in storage order.
415
+ """
416
+ grouped = self.storage.get_sessions( # type: ignore[attr-defined]
417
+ session_id=session_id, top_k=1000
418
+ )
419
+ return [
420
+ entry.request
421
+ for entries in grouped.values()
422
+ for entry in entries
423
+ if entry.request is not None
424
+ ]
425
+
426
+ def _build_distribution(
427
+ self,
428
+ current: list[AgentSuccessEvaluationResult],
429
+ previous: list[AgentSuccessEvaluationResult],
430
+ ) -> ScoreDistribution:
431
+ cur_bins = bucket_corrections(
432
+ r.number_of_correction_per_session for r in current
433
+ )
434
+ prev_bins = bucket_corrections(
435
+ r.number_of_correction_per_session for r in previous
436
+ )
437
+ return ScoreDistribution(
438
+ current_bins=list(cur_bins),
439
+ baseline_bins=list(prev_bins),
440
+ labels=list(BUCKET_LABELS),
441
+ )
442
+
443
+
444
+ # --- module-level helpers (pure) ---
445
+
446
+
447
+ def _success_rate(results: list[AgentSuccessEvaluationResult]) -> float:
448
+ if not results:
449
+ return 0.0
450
+ return sum(1 for r in results if r.is_success) / len(results)
451
+
452
+
453
+ def _escalation_rate(results: list[AgentSuccessEvaluationResult]) -> float:
454
+ if not results:
455
+ return 0.0
456
+ return sum(1 for r in results if r.is_escalated) / len(results)
457
+
458
+
459
+ def _mean(values: Iterable[float | int | None]) -> float:
460
+ nums = [float(v) for v in values if v is not None]
461
+ if not nums:
462
+ return 0.0
463
+ return sum(nums) / len(nums)
464
+
465
+
466
+ def _aggregate_imported_scores(
467
+ scores: list[ImportedScore],
468
+ ) -> dict[str, tuple[float, int]]:
469
+ """Group imported scores by scorer_name → (mean, count)."""
470
+ bucket: dict[str, list[float]] = defaultdict(list)
471
+ for s in scores:
472
+ bucket[s.scorer_name].append(s.value)
473
+ return {name: (sum(vs) / len(vs), len(vs)) for name, vs in bucket.items() if vs}
474
+
475
+
476
+ def _buckets(
477
+ results: list[AgentSuccessEvaluationResult], bucket: BucketLiteral
478
+ ) -> list[HeroBucket]:
479
+ """Build day- or week-sized buckets across the given results.
480
+
481
+ Bucket granularity follows the request: ``"day"`` for the frontend's
482
+ daily trend mode (tooltip activates on every X position, smoother
483
+ curves on narrow ranges), ``"week"`` otherwise. Each bucket carries
484
+ success rate, average corrections, and escalation rate so the metric
485
+ mini-trends stay aligned with the headline numbers.
486
+ """
487
+ if not results:
488
+ return []
489
+ buckets: dict[int, list[AgentSuccessEvaluationResult]] = defaultdict(list)
490
+ step = _DAY_SECONDS if bucket == "day" else _WEEK_SECONDS
491
+ for r in results:
492
+ # Anchor each result to the START of its bucket (epoch-aligned).
493
+ bucket_start = (r.created_at // step) * step
494
+ buckets[bucket_start].append(r)
495
+ out: list[HeroBucket] = []
496
+ for ts in sorted(buckets):
497
+ bucket_results = buckets[ts]
498
+ avg_corr = _mean(r.number_of_correction_per_session for r in bucket_results)
499
+ # is_escalated may be None on legacy rows; coerce to False so the
500
+ # bucket mean reflects "fraction of sessions we know escalated".
501
+ escalation_rate = _mean(
502
+ (1.0 if (r.is_escalated is True) else 0.0) for r in bucket_results
503
+ )
504
+ out.append(
505
+ HeroBucket(
506
+ ts=ts,
507
+ regular_rate=_success_rate(bucket_results),
508
+ shadow_rate=None,
509
+ regular_n=len(bucket_results),
510
+ shadow_n=0,
511
+ avg_corrections=avg_corr,
512
+ escalation_rate=escalation_rate,
513
+ )
514
+ )
515
+ return out
@@ -0,0 +1,90 @@
1
+ """F1 shadow win-rate aggregator: ShadowComparisonVerdict[] → ShadowWinRateTrend.
2
+
3
+ Pure: DB-free, LLM-free. Bucketed by UTC calendar date; the window total is
4
+ computed by summing the daily buckets, so it is consistent with what the
5
+ dashboard renders by construction.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections import defaultdict
11
+ from collections.abc import Iterable
12
+ from datetime import UTC
13
+
14
+ from reflexio.models.api_schema.eval_overview_schema import (
15
+ ShadowComparisonVerdict,
16
+ ShadowWinRateTrend,
17
+ ShadowWinRateTrendPoint,
18
+ ShadowWinRateTrendWindowTotal,
19
+ )
20
+ from reflexio.server.services.shadow_comparison.outcome import (
21
+ Outcome,
22
+ derive_reflexio_outcome,
23
+ )
24
+
25
+ # Map outcome enum → bucket key used in the per-day accumulator below.
26
+ _OUTCOME_TO_KEY: dict[Outcome, str] = {
27
+ Outcome.WIN: "wins",
28
+ Outcome.LOSS: "losses",
29
+ Outcome.TIE: "ties",
30
+ }
31
+
32
+
33
+ def compute_shadow_win_rate_trend(
34
+ verdicts: Iterable[ShadowComparisonVerdict],
35
+ judge_prompt_version: str = "v1.0.0",
36
+ ) -> ShadowWinRateTrend:
37
+ """
38
+ Bucket verdicts by UTC date, derive win/loss/tie per verdict, sum.
39
+
40
+ Args:
41
+ verdicts (Iterable[ShadowComparisonVerdict]): Position-randomized
42
+ verdicts in the trend window. Caller is expected to have
43
+ already filtered to the pinned prompt version.
44
+ judge_prompt_version (str): Informational; echoed on the response
45
+ so the dashboard knows which rubric epoch the numbers came
46
+ from. Defaults to ``"v1.0.0"``.
47
+
48
+ Returns:
49
+ ShadowWinRateTrend: Daily buckets sorted ascending by date, plus
50
+ the window total computed from those same buckets.
51
+ """
52
+ by_day: dict[str, dict[str, int]] = defaultdict(
53
+ lambda: {"n": 0, "wins": 0, "losses": 0, "ties": 0}
54
+ )
55
+ for verdict in verdicts:
56
+ date_key = verdict.created_at.astimezone(UTC).strftime("%Y-%m-%d")
57
+ bucket = by_day[date_key]
58
+ bucket["n"] += 1
59
+ bucket[_OUTCOME_TO_KEY[derive_reflexio_outcome(verdict)]] += 1
60
+
61
+ daily = [
62
+ ShadowWinRateTrendPoint(
63
+ date=date,
64
+ n=bucket["n"],
65
+ wins=bucket["wins"],
66
+ losses=bucket["losses"],
67
+ ties=bucket["ties"],
68
+ )
69
+ for date, bucket in sorted(by_day.items())
70
+ ]
71
+
72
+ total_n = sum(p.n for p in daily)
73
+ total_wins = sum(p.wins for p in daily)
74
+ total_losses = sum(p.losses for p in daily)
75
+ total_ties = sum(p.ties for p in daily)
76
+ win_rate = total_wins / total_n if total_n > 0 else 0.0
77
+ net_win = (total_wins - total_losses) / total_n if total_n > 0 else 0.0
78
+
79
+ return ShadowWinRateTrend(
80
+ daily=daily,
81
+ window_total=ShadowWinRateTrendWindowTotal(
82
+ n=total_n,
83
+ wins=total_wins,
84
+ losses=total_losses,
85
+ ties=total_ties,
86
+ win_rate=win_rate,
87
+ net_win=net_win,
88
+ ),
89
+ judge_prompt_version=judge_prompt_version,
90
+ )
@@ -0,0 +1,91 @@
1
+ """Helpers for constructing durable extraction agent run records."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+ from dataclasses import asdict, is_dataclass
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel
10
+
11
+ from reflexio.models.api_schema.internal_schema import RequestInteractionDataModel
12
+ from reflexio.server.services.storage.storage_base import (
13
+ AgentBinding,
14
+ AgentRunRecord,
15
+ AgentRunStatus,
16
+ build_scope_hash,
17
+ )
18
+
19
+
20
+ def _jsonable(value: Any) -> Any:
21
+ if isinstance(value, BaseModel):
22
+ return value.model_dump(mode="json")
23
+ if is_dataclass(value) and not isinstance(value, type):
24
+ return _jsonable(asdict(value))
25
+ if isinstance(value, dict):
26
+ return {str(k): _jsonable(v) for k, v in value.items()}
27
+ if isinstance(value, (list, tuple, set)):
28
+ return [_jsonable(item) for item in value]
29
+ if value is None or isinstance(value, str | int | float | bool):
30
+ return value
31
+ return str(value)
32
+
33
+
34
+ def extract_source_interaction_ids(
35
+ request_interaction_data_models: list[RequestInteractionDataModel],
36
+ ) -> list[int]:
37
+ return [
38
+ interaction.interaction_id
39
+ for data_model in request_interaction_data_models
40
+ for interaction in data_model.interactions
41
+ if interaction.interaction_id is not None
42
+ ]
43
+
44
+
45
+ def build_extractor_agent_run_record(
46
+ *,
47
+ org_id: str,
48
+ extractor_kind: str,
49
+ user_id: str | None,
50
+ request_id: str,
51
+ agent_version: str | None,
52
+ source: str | None,
53
+ request_interaction_data_models: list[RequestInteractionDataModel],
54
+ extractor_config: BaseModel,
55
+ service_config: Any,
56
+ agent_context: str,
57
+ ) -> AgentRunRecord:
58
+ source_interaction_ids = extract_source_interaction_ids(
59
+ request_interaction_data_models
60
+ )
61
+ extractor_config_snapshot = extractor_config.model_dump(mode="json")
62
+
63
+ return AgentRunRecord(
64
+ id=f"ar_{uuid.uuid4().hex}",
65
+ binding=AgentBinding(
66
+ org_id=org_id,
67
+ extractor_kind=extractor_kind,
68
+ user_id=user_id,
69
+ request_id=request_id,
70
+ agent_version=agent_version,
71
+ source=source,
72
+ source_interaction_ids=source_interaction_ids,
73
+ window_start_interaction_id=(
74
+ min(source_interaction_ids) if source_interaction_ids else None
75
+ ),
76
+ window_end_interaction_id=(
77
+ max(source_interaction_ids) if source_interaction_ids else None
78
+ ),
79
+ extractor_config_hash=build_scope_hash(extractor_config_snapshot),
80
+ ),
81
+ status=AgentRunStatus.RUNNING,
82
+ generation_request_snapshot={
83
+ "request_id": request_id,
84
+ "source": source,
85
+ "source_interaction_ids": source_interaction_ids,
86
+ "session_count": len(request_interaction_data_models),
87
+ "extractor_config": extractor_config_snapshot,
88
+ },
89
+ service_config_snapshot=_jsonable(service_config),
90
+ agent_context_snapshot=agent_context,
91
+ )