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,330 @@
1
+ """
2
+ Embedding cache + deterministic fake embedder for retrieval latency benchmarks.
3
+
4
+ This module controls the cost of embedding generation during a benchmark run.
5
+ Retrieval latency measurement should not be dominated by embedder API calls,
6
+ but query-side vectors must still be realistic so the vector-search path
7
+ computes meaningful cosine similarities.
8
+
9
+ Two helpers are provided:
10
+
11
+ 1. :class:`QueryEmbedCache` — on-disk cache of real embeddings for the fixed
12
+ query set. Populated once via :class:`LiteLLMClient`, then reused on every
13
+ subsequent run. First-time population requires a configured embedding API
14
+ key (typically ``OPENAI_API_KEY``).
15
+ 2. :func:`make_fake_document_embedder` — deterministic pseudo-random vector
16
+ generator used during corpus seeding. Document vector *content* does not
17
+ affect query-time latency (only row count does), so fake vectors are a
18
+ sound optimization for a latency benchmark.
19
+
20
+ Both helpers expose a :func:`patch_storage_get_embedding` style context
21
+ manager that swaps out the storage instance's bound ``_get_embedding`` method
22
+ for the duration of a ``with`` block.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ import json
29
+ import logging
30
+ import struct
31
+ from collections.abc import Callable, Iterable, Iterator
32
+ from contextlib import contextmanager
33
+ from pathlib import Path
34
+ from typing import Any, Literal
35
+
36
+ from reflexio.models.config_schema import EMBEDDING_DIMENSIONS
37
+ from reflexio.server.llm.litellm_client import LiteLLMClient, LiteLLMConfig
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+ # Track the storage-level canonical dimension so fake document vectors
42
+ # always fit the vec0 column width. Query cache entries are populated
43
+ # through LiteLLMClient at this same dimension for consistency.
44
+ DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small"
45
+ DEFAULT_EMBEDDING_DIMS = EMBEDDING_DIMENSIONS
46
+
47
+ _CACHE_DIR = Path.home() / ".cache" / "reflexio-benchmarks"
48
+ _DOCUMENT_PREFIX = "search_document: "
49
+ _QUERY_PREFIX = "search_query: "
50
+
51
+
52
+ EmbeddingPurpose = Literal["document", "query"]
53
+
54
+ EmbeddingFn = Callable[[str, EmbeddingPurpose], list[float]]
55
+
56
+
57
+ def _cache_path(model: str) -> Path:
58
+ """
59
+ Return the on-disk cache path for embeddings of a given model.
60
+
61
+ Args:
62
+ model (str): Embedding model identifier (e.g. text-embedding-3-small).
63
+
64
+ Returns:
65
+ Path: JSON cache file path. Parent directory is not created here.
66
+ """
67
+ safe = model.replace("/", "_")
68
+ return _CACHE_DIR / f"embeddings-{safe}.json"
69
+
70
+
71
+ def _hash_key(text: str) -> str:
72
+ """
73
+ Return a stable short hash key for a full (prefixed) embedding input.
74
+
75
+ Args:
76
+ text (str): The full text that will be passed to the embedding model.
77
+
78
+ Returns:
79
+ str: Hex digest suitable for use as a JSON dict key.
80
+ """
81
+ return hashlib.sha256(text.encode("utf-8")).hexdigest()
82
+
83
+
84
+ class QueryEmbedCache:
85
+ """
86
+ On-disk cache of real embedding vectors for the benchmark query set.
87
+
88
+ The cache file is a flat JSON dict mapping SHA256(prefixed_text) → vector.
89
+ Population happens lazily via :meth:`ensure` and only touches the network
90
+ when the cache is missing an entry.
91
+
92
+ Attributes:
93
+ model (str): Embedding model used to populate the cache.
94
+ dimensions (int): Expected vector dimensionality.
95
+ """
96
+
97
+ def __init__(
98
+ self,
99
+ model: str = DEFAULT_EMBEDDING_MODEL,
100
+ dimensions: int = DEFAULT_EMBEDDING_DIMS,
101
+ ) -> None:
102
+ """
103
+ Initialize the cache, loading any existing on-disk entries.
104
+
105
+ Args:
106
+ model (str): Embedding model name. Must match whatever the live
107
+ storage backend is configured with.
108
+ dimensions (int): Expected embedding dimensionality. Must match
109
+ the storage vector column width.
110
+ """
111
+ self.model = model
112
+ self.dimensions = dimensions
113
+ self._path = _cache_path(model)
114
+ self._data: dict[str, list[float]] = {}
115
+ if self._path.exists():
116
+ try:
117
+ self._data = json.loads(self._path.read_text())
118
+ except (OSError, json.JSONDecodeError) as err:
119
+ logger.warning(
120
+ "Failed to load embed cache at %s: %s — starting fresh",
121
+ self._path,
122
+ err,
123
+ )
124
+ self._data = {}
125
+
126
+ def ensure(self, queries: Iterable[str]) -> None:
127
+ """
128
+ Make sure every query has a real embedding in the cache.
129
+
130
+ Populates missing entries via the live embedding API in a single
131
+ batched call. Writes the cache to disk at the end of population so
132
+ subsequent runs skip the network entirely.
133
+
134
+ Args:
135
+ queries (Iterable[str]): Raw query strings (no prefix). The
136
+ ``search_query:`` prefix is applied internally.
137
+
138
+ Raises:
139
+ RuntimeError: If the embedder cannot be reached and the cache is
140
+ not already populated for every query.
141
+ """
142
+ missing: list[str] = []
143
+ missing_prefixed: list[str] = []
144
+ for q in queries:
145
+ prefixed = _QUERY_PREFIX + q
146
+ if _hash_key(prefixed) not in self._data:
147
+ missing.append(q)
148
+ missing_prefixed.append(prefixed)
149
+ if not missing:
150
+ return
151
+
152
+ logger.info(
153
+ "Embed cache miss for %d queries (model=%s) — fetching",
154
+ len(missing),
155
+ self.model,
156
+ )
157
+ client = LiteLLMClient(LiteLLMConfig(model=self.model))
158
+ try:
159
+ vectors = client.get_embeddings(
160
+ missing_prefixed, self.model, self.dimensions
161
+ )
162
+ except Exception as err: # noqa: BLE001
163
+ raise RuntimeError(
164
+ f"Failed to populate query embed cache for model {self.model}: "
165
+ f"{err}. Set OPENAI_API_KEY (or the appropriate provider key) "
166
+ "and re-run."
167
+ ) from err
168
+
169
+ for prefixed, vec in zip(missing_prefixed, vectors, strict=True):
170
+ if len(vec) != self.dimensions:
171
+ raise RuntimeError(
172
+ f"Embed cache got vector of length {len(vec)}, expected "
173
+ f"{self.dimensions} for model {self.model}"
174
+ )
175
+ self._data[_hash_key(prefixed)] = list(vec)
176
+
177
+ self._path.parent.mkdir(parents=True, exist_ok=True)
178
+ self._path.write_text(json.dumps(self._data))
179
+
180
+ def lookup(self, text: str, purpose: EmbeddingPurpose) -> list[float]:
181
+ """
182
+ Return a cached embedding for the given raw text.
183
+
184
+ Args:
185
+ text (str): Raw text (no prefix).
186
+ purpose (EmbeddingPurpose): ``"document"`` or ``"query"``.
187
+
188
+ Returns:
189
+ list[float]: The cached embedding vector.
190
+
191
+ Raises:
192
+ KeyError: If the text is not in the cache.
193
+ """
194
+ prefix = _DOCUMENT_PREFIX if purpose == "document" else _QUERY_PREFIX
195
+ key = _hash_key(prefix + text)
196
+ try:
197
+ return self._data[key]
198
+ except KeyError as err:
199
+ raise KeyError(
200
+ f"Query embed cache miss for text={text!r} purpose={purpose}. "
201
+ "Did you forget to call QueryEmbedCache.ensure() for the query set?"
202
+ ) from err
203
+
204
+
205
+ def _deterministic_vector(text: str, dimensions: int) -> list[float]:
206
+ """
207
+ Generate a deterministic pseudo-random unit vector from text.
208
+
209
+ Uses SHA256 as a cheap keyed PRNG, converts the digest stream into
210
+ little-endian float32 values, then normalizes to unit length. The
211
+ output is stable across runs and machines.
212
+
213
+ Args:
214
+ text (str): Input text (the hash seed).
215
+ dimensions (int): Desired vector length.
216
+
217
+ Returns:
218
+ list[float]: Unit-length vector of ``dimensions`` floats.
219
+ """
220
+ # Derive 4*dimensions pseudo-random bytes from chained SHA256 of
221
+ # (text, counter). This is not cryptographic — it just needs to be
222
+ # stable and unbiased enough to exercise cosine similarity paths.
223
+ need = dimensions * 4
224
+ buf = bytearray()
225
+ counter = 0
226
+ while len(buf) < need:
227
+ h = hashlib.sha256(f"{text}:{counter}".encode()).digest()
228
+ buf.extend(h)
229
+ counter += 1
230
+ floats = [
231
+ struct.unpack("<i", bytes(buf[i * 4 : (i + 1) * 4]))[0] / 2**31
232
+ for i in range(dimensions)
233
+ ]
234
+ norm = sum(x * x for x in floats) ** 0.5
235
+ if norm == 0.0:
236
+ return [0.0] * dimensions
237
+ return [x / norm for x in floats]
238
+
239
+
240
+ def make_fake_document_embedder(
241
+ dimensions: int = DEFAULT_EMBEDDING_DIMS,
242
+ ) -> Callable[[str, EmbeddingPurpose], list[float]]:
243
+ """
244
+ Build a fake ``_get_embedding`` replacement for corpus seeding.
245
+
246
+ Returns deterministic hash-derived vectors so seeding is fast (no API
247
+ calls) while preserving vector column width and cosine-similarity
248
+ determinism across runs. Only valid for seeding — query-time retrieval
249
+ should use a real :class:`QueryEmbedCache` so BM25/vector ranking is
250
+ meaningful.
251
+
252
+ Args:
253
+ dimensions (int): Vector length to produce. Must match the storage
254
+ backend's vector column width.
255
+
256
+ Returns:
257
+ Callable: A bound-method-compatible ``(text, purpose) → vector``
258
+ function ready to patch onto a storage instance.
259
+ """
260
+
261
+ def fake(text: str, purpose: EmbeddingPurpose = "document") -> list[float]:
262
+ # Include purpose in the seed so doc and query versions of the same
263
+ # text get different vectors — mirrors how the real embedder treats
264
+ # the search_document/search_query prefixes as distinct inputs.
265
+ return _deterministic_vector(f"{purpose}:{text}", dimensions)
266
+
267
+ return fake
268
+
269
+
270
+ def make_cached_query_embedder(
271
+ cache: QueryEmbedCache,
272
+ fallback_dims: int = DEFAULT_EMBEDDING_DIMS,
273
+ ) -> Callable[[str, EmbeddingPurpose], list[float]]:
274
+ """
275
+ Build a ``_get_embedding`` replacement backed by :class:`QueryEmbedCache`.
276
+
277
+ Document lookups fall back to fake deterministic vectors (seeding already
278
+ happened; any ``purpose='document'`` call during the timed loop would be
279
+ an oversight but must not crash). Query lookups raise if the cache is
280
+ empty — that indicates :meth:`QueryEmbedCache.ensure` was skipped.
281
+
282
+ Args:
283
+ cache (QueryEmbedCache): Populated cache for the benchmark query set.
284
+ fallback_dims (int): Vector width for any document-side lookups that
285
+ leak into the timed loop.
286
+
287
+ Returns:
288
+ Callable: A bound-method-compatible ``(text, purpose) → vector`` fn.
289
+ """
290
+
291
+ def cached(text: str, purpose: EmbeddingPurpose = "document") -> list[float]:
292
+ if purpose == "query":
293
+ return cache.lookup(text, "query")
294
+ return _deterministic_vector(f"document:{text}", fallback_dims)
295
+
296
+ return cached
297
+
298
+
299
+ @contextmanager
300
+ def patch_storage_get_embedding(
301
+ storage: Any,
302
+ replacement: EmbeddingFn,
303
+ ) -> Iterator[None]:
304
+ """
305
+ Temporarily replace a storage instance's ``_get_embedding`` bound method.
306
+
307
+ Used to swap in either the fake seeding embedder or the cached query
308
+ embedder for the duration of a benchmark phase.
309
+
310
+ ``storage`` is typed ``Any`` because ``_get_embedding`` is declared on the
311
+ sqlite storage mixins rather than on ``BaseStorage`` itself — a Protocol
312
+ that required the attribute would reject real storage instances.
313
+
314
+ Args:
315
+ storage (Any): A BaseStorage-like instance exposing a
316
+ ``_get_embedding`` method. Writing to ``storage._get_embedding``
317
+ goes into the instance dict, so subsequent lookups resolve to the
318
+ plain replacement function (no auto-binding).
319
+ replacement (EmbeddingFn): The ``(text, purpose) → vector`` fn to bind.
320
+
321
+ Yields:
322
+ None: During the ``with`` block, ``storage._get_embedding`` routes to
323
+ ``replacement``. On exit, the original bound method is restored.
324
+ """
325
+ original = storage._get_embedding
326
+ storage._get_embedding = replacement
327
+ try:
328
+ yield
329
+ finally:
330
+ storage._get_embedding = original
@@ -0,0 +1,317 @@
1
+ """
2
+ Statistics and markdown rendering for retrieval latency benchmark results.
3
+
4
+ Input: a flat list of trial rows produced by :mod:`bench`, each with
5
+ ``{backend, retrieval_type, layer, n, trial, elapsed_ms}``.
6
+
7
+ Output: markdown tables grouped by retrieval type, optional diff column
8
+ against a committed baseline file.
9
+
10
+ All quantile math uses :mod:`statistics` to avoid pulling in numpy.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import statistics
17
+ from dataclasses import asdict, dataclass, field
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from reflexio.benchmarks.retrieval_latency.scenarios import QUERIES
22
+
23
+ # Flag a cell when p95 has grown by more than this factor since the baseline.
24
+ REGRESSION_THRESHOLD = 1.20
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class CellKey:
29
+ """
30
+ Immutable key identifying one (backend, retrieval_type, layer, n) cell.
31
+
32
+ Attributes:
33
+ backend (str): Storage backend name, e.g. ``"sqlite"``.
34
+ retrieval_type (str): One of ``profile``, ``user_playbook``,
35
+ ``agent_playbook``, ``unified``.
36
+ layer (str): ``service`` or ``http``.
37
+ n (int): Corpus size for this cell.
38
+ """
39
+
40
+ backend: str
41
+ retrieval_type: str
42
+ layer: str
43
+ n: int
44
+
45
+
46
+ @dataclass
47
+ class CellStats:
48
+ """
49
+ Aggregated latency stats for one benchmark cell.
50
+
51
+ Attributes:
52
+ trials (int): Number of timed samples summarized.
53
+ mean_ms (float): Arithmetic mean latency in milliseconds.
54
+ p50_ms (float): Median latency.
55
+ p95_ms (float): 95th percentile.
56
+ p99_ms (float): 99th percentile.
57
+ max_ms (float): Slowest sample observed.
58
+ """
59
+
60
+ trials: int
61
+ mean_ms: float
62
+ p50_ms: float
63
+ p95_ms: float
64
+ p99_ms: float
65
+ max_ms: float
66
+
67
+
68
+ @dataclass
69
+ class RunResults:
70
+ """
71
+ Full results of a benchmark run, aggregated and raw.
72
+
73
+ Attributes:
74
+ config (dict): Top-level metadata (CLI args, git sha, timestamp).
75
+ raw (list[dict]): All trial rows, unmodified.
76
+ stats (dict[str, CellStats]): Aggregated stats keyed by
77
+ ``backend|retrieval_type|layer|n``.
78
+ """
79
+
80
+ config: dict[str, Any]
81
+ raw: list[dict[str, Any]] = field(default_factory=list)
82
+ stats: dict[str, CellStats] = field(default_factory=dict)
83
+
84
+
85
+ def _quantile(sorted_samples: list[float], q: float) -> float:
86
+ """
87
+ Return the ``q``-th quantile of an already-sorted sample list.
88
+
89
+ Uses the linear interpolation method so small N (e.g. 10 samples for
90
+ the smoke test) still gives a stable number.
91
+
92
+ Args:
93
+ sorted_samples (list[float]): Sorted list of samples.
94
+ q (float): Target quantile in ``[0, 1]``.
95
+
96
+ Returns:
97
+ float: Interpolated quantile value.
98
+ """
99
+ if not sorted_samples:
100
+ return 0.0
101
+ if len(sorted_samples) == 1:
102
+ return sorted_samples[0]
103
+ pos = q * (len(sorted_samples) - 1)
104
+ lo = int(pos)
105
+ hi = min(lo + 1, len(sorted_samples) - 1)
106
+ frac = pos - lo
107
+ return sorted_samples[lo] * (1 - frac) + sorted_samples[hi] * frac
108
+
109
+
110
+ def compute_stats(samples: list[float]) -> CellStats:
111
+ """
112
+ Compute mean / p50 / p95 / p99 / max for one cell's samples.
113
+
114
+ Args:
115
+ samples (list[float]): Latency samples in milliseconds.
116
+
117
+ Returns:
118
+ CellStats: Aggregated stats. Empty input returns zeros.
119
+ """
120
+ if not samples:
121
+ return CellStats(
122
+ trials=0, mean_ms=0.0, p50_ms=0.0, p95_ms=0.0, p99_ms=0.0, max_ms=0.0
123
+ )
124
+ sorted_s = sorted(samples)
125
+ return CellStats(
126
+ trials=len(samples),
127
+ mean_ms=statistics.fmean(sorted_s),
128
+ p50_ms=_quantile(sorted_s, 0.50),
129
+ p95_ms=_quantile(sorted_s, 0.95),
130
+ p99_ms=_quantile(sorted_s, 0.99),
131
+ max_ms=sorted_s[-1],
132
+ )
133
+
134
+
135
+ def cell_key_str(k: CellKey) -> str:
136
+ """
137
+ Serialize a :class:`CellKey` into a flat string for JSON keys.
138
+
139
+ Args:
140
+ k (CellKey): The cell identifier.
141
+
142
+ Returns:
143
+ str: ``backend|retrieval_type|layer|n`` form.
144
+ """
145
+ return f"{k.backend}|{k.retrieval_type}|{k.layer}|{k.n}"
146
+
147
+
148
+ def aggregate(raw: list[dict[str, Any]]) -> dict[str, CellStats]:
149
+ """
150
+ Bucket raw trial rows by cell and compute aggregated stats per cell.
151
+
152
+ Args:
153
+ raw (list[dict]): Trial rows as emitted by :mod:`bench`.
154
+
155
+ Returns:
156
+ dict[str, CellStats]: Mapping from flat cell key to stats.
157
+ """
158
+ buckets: dict[str, list[float]] = {}
159
+ for row in raw:
160
+ k = CellKey(
161
+ backend=row["backend"],
162
+ retrieval_type=row["retrieval_type"],
163
+ layer=row["layer"],
164
+ n=row["n"],
165
+ )
166
+ buckets.setdefault(cell_key_str(k), []).append(row["elapsed_ms"])
167
+ return {key: compute_stats(samples) for key, samples in buckets.items()}
168
+
169
+
170
+ def write_results_json(results: RunResults, path: Path) -> None:
171
+ """
172
+ Persist aggregated + raw results to disk as JSON.
173
+
174
+ Args:
175
+ results (RunResults): Fully populated run results.
176
+ path (Path): Output file path. Parent directory is created.
177
+ """
178
+ payload = {
179
+ "config": results.config,
180
+ "stats": {k: asdict(v) for k, v in results.stats.items()},
181
+ "raw": results.raw,
182
+ }
183
+ path.parent.mkdir(parents=True, exist_ok=True)
184
+ path.write_text(json.dumps(payload, indent=2))
185
+
186
+
187
+ def load_baseline(path: Path) -> dict[str, CellStats]:
188
+ """
189
+ Load aggregated baseline stats from a previously-written results file.
190
+
191
+ Args:
192
+ path (Path): Baseline results.json file.
193
+
194
+ Returns:
195
+ dict[str, CellStats]: Stats indexed by flat cell key. Empty dict
196
+ if the file is missing or malformed.
197
+ """
198
+ if not path.exists():
199
+ return {}
200
+ data = json.loads(path.read_text())
201
+ return {
202
+ k: CellStats(**v)
203
+ for k, v in data.get("stats", {}).items() # type: ignore[arg-type]
204
+ }
205
+
206
+
207
+ def _fmt_cell(stats: CellStats) -> str:
208
+ """
209
+ Render a single cell's stats as ``p50 / p95 (mean)`` ms.
210
+
211
+ Args:
212
+ stats (CellStats): The cell's aggregated stats.
213
+
214
+ Returns:
215
+ str: Human-readable one-line summary, or ``"—"`` for empty cells.
216
+ """
217
+ if stats.trials == 0:
218
+ return "—"
219
+ return f"{stats.p50_ms:.1f} / {stats.p95_ms:.1f} ({stats.mean_ms:.1f})"
220
+
221
+
222
+ def _diff_cell(current: CellStats, baseline: CellStats | None) -> str:
223
+ """
224
+ Render a baseline-diff cell showing ΔP95 and a regression marker.
225
+
226
+ Args:
227
+ current (CellStats): This run's stats for the cell.
228
+ baseline (CellStats | None): Prior run's stats, if any.
229
+
230
+ Returns:
231
+ str: ``+42%`` or ``-8%`` with a ``⚠`` marker if current p95 is
232
+ above the regression threshold.
233
+ """
234
+ if baseline is None or baseline.p95_ms == 0 or current.trials == 0:
235
+ return "—"
236
+ ratio = current.p95_ms / baseline.p95_ms
237
+ delta = (ratio - 1.0) * 100
238
+ marker = " ⚠" if ratio >= REGRESSION_THRESHOLD else ""
239
+ return f"{delta:+.0f}%{marker}"
240
+
241
+
242
+ def render_markdown(
243
+ results: RunResults, baseline: dict[str, CellStats] | None = None
244
+ ) -> str:
245
+ """
246
+ Render the full run results as a markdown report.
247
+
248
+ One table per retrieval type, rows grouped by (backend, layer, N),
249
+ with an optional ΔP95 column against the committed baseline.
250
+
251
+ Args:
252
+ results (RunResults): Fully populated run results.
253
+ baseline (dict[str, CellStats] | None): Optional baseline stats.
254
+
255
+ Returns:
256
+ str: Markdown report suitable for writing to ``report.md``.
257
+ """
258
+ retrieval_types = sorted({row["retrieval_type"] for row in results.raw})
259
+ backends = sorted({row["backend"] for row in results.raw})
260
+ layers = sorted({row["layer"] for row in results.raw})
261
+ sizes = sorted({row["n"] for row in results.raw})
262
+
263
+ lines: list[str] = ["# Retrieval latency benchmark", ""]
264
+ lines.append(f"- Trials per cell: {results.config.get('trials', '?')}")
265
+ lines.append(f"- Warmup per cell: {results.config.get('warmup', '?')}")
266
+ lines.append(f"- Query set: {len(QUERIES)} fixed queries (see scenarios.py)")
267
+ lines.append("- Cell format: `p50 / p95 (mean)` in ms")
268
+ if baseline:
269
+ lines.append(f"- Regression threshold: p95 ≥ {REGRESSION_THRESHOLD:.0%}")
270
+ lines.append("")
271
+
272
+ for rt in retrieval_types:
273
+ lines.append(f"## {rt}")
274
+ lines.append("")
275
+ header = ["backend", "layer", *[f"N={n}" for n in sizes]]
276
+ if baseline:
277
+ header.append("ΔP95")
278
+ lines.append("| " + " | ".join(header) + " |")
279
+ lines.append("|" + "|".join(["---"] * len(header)) + "|")
280
+
281
+ for backend in backends:
282
+ for layer in layers:
283
+ row_cells: list[str] = [backend, layer]
284
+ latest_stats: CellStats | None = None
285
+ for n in sizes:
286
+ key = cell_key_str(CellKey(backend, rt, layer, n))
287
+ stats = results.stats.get(
288
+ key,
289
+ CellStats(0, 0.0, 0.0, 0.0, 0.0, 0.0),
290
+ )
291
+ row_cells.append(_fmt_cell(stats))
292
+ if stats.trials > 0:
293
+ latest_stats = stats
294
+ if baseline and latest_stats is not None:
295
+ # Diff against the largest-N cell we have for this row.
296
+ largest_n = max(
297
+ (
298
+ n
299
+ for n in sizes
300
+ if cell_key_str(CellKey(backend, rt, layer, n))
301
+ in results.stats
302
+ ),
303
+ default=None,
304
+ )
305
+ base_key = (
306
+ cell_key_str(CellKey(backend, rt, layer, largest_n))
307
+ if largest_n is not None
308
+ else None
309
+ )
310
+ base_stats = baseline.get(base_key) if base_key else None
311
+ row_cells.append(_diff_cell(latest_stats, base_stats))
312
+ elif baseline:
313
+ row_cells.append("—")
314
+ lines.append("| " + " | ".join(row_cells) + " |")
315
+ lines.append("")
316
+
317
+ return "\n".join(lines)
@@ -0,0 +1,43 @@
1
+ # Retrieval latency benchmark
2
+
3
+ - Trials per cell: 30
4
+ - Warmup per cell: 3
5
+ - Query set: 20 fixed queries (see scenarios.py)
6
+ - Cell format: `p50 / p95 (mean)` in ms
7
+
8
+ ## agent_playbook
9
+
10
+
11
+ | backend | layer | N=100 | N=1000 |
12
+ | ------- | ------- | ------------------ | ------------------ |
13
+ | sqlite | http | 18.8 / 39.1 (21.4) | 19.2 / 21.3 (19.7) |
14
+ | sqlite | service | 15.2 / 16.9 (15.5) | 16.9 / 21.6 (17.6) |
15
+
16
+
17
+ ## profile
18
+
19
+
20
+ | backend | layer | N=100 | N=1000 |
21
+ | ------- | ------- | --------------- | ------------------ |
22
+ | sqlite | http | 4.1 / 5.6 (4.4) | 20.7 / 25.6 (21.2) |
23
+ | sqlite | service | 1.8 / 2.2 (2.0) | 17.4 / 21.2 (17.9) |
24
+
25
+
26
+ ## unified
27
+
28
+
29
+ | backend | layer | N=100 | N=1000 |
30
+ | ------- | ------- | ------------------ | ------------------ |
31
+ | sqlite | http | 24.7 / 30.3 (25.4) | 58.8 / 71.8 (59.9) |
32
+ | sqlite | service | 20.1 / 21.5 (20.1) | 57.5 / 72.7 (58.3) |
33
+
34
+
35
+ ## user_playbook
36
+
37
+
38
+ | backend | layer | N=100 | N=1000 |
39
+ | ------- | ------- | --------------- | ------------------ |
40
+ | sqlite | http | 3.9 / 4.8 (4.2) | 19.5 / 22.0 (19.8) |
41
+ | sqlite | service | 1.6 / 2.0 (1.7) | 17.1 / 17.8 (17.1) |
42
+
43
+