claude-smart 0.2.42 → 0.2.44

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 (402) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +1 -1
  3. package/bin/claude-smart.js +2 -2
  4. package/package.json +9 -3
  5. package/plugin/.claude-plugin/plugin.json +9 -3
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +23 -3
  8. package/plugin/pyproject.toml +3 -3
  9. package/plugin/scripts/_lib.sh +91 -0
  10. package/plugin/scripts/backend-service.sh +51 -4
  11. package/plugin/scripts/cli.sh +3 -1
  12. package/plugin/scripts/codex-hook.js +72 -4
  13. package/plugin/scripts/dashboard-build.sh +1 -0
  14. package/plugin/scripts/dashboard-service.sh +1 -0
  15. package/plugin/scripts/ensure-plugin-root.sh +1 -0
  16. package/plugin/scripts/hook_entry.sh +6 -3
  17. package/plugin/scripts/smart-install.sh +3 -2
  18. package/plugin/src/README.md +57 -0
  19. package/plugin/src/claude_smart/context_format.py +11 -12
  20. package/plugin/src/claude_smart/cs_cite.py +26 -12
  21. package/plugin/src/claude_smart/ids.py +13 -5
  22. package/plugin/uv.lock +126 -5
  23. package/plugin/vendor/reflexio/.env.example +62 -0
  24. package/plugin/vendor/reflexio/LICENSE +201 -0
  25. package/plugin/vendor/reflexio/README.md +338 -0
  26. package/plugin/vendor/reflexio/pyproject.toml +274 -0
  27. package/plugin/vendor/reflexio/reflexio/README.md +184 -0
  28. package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
  29. package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
  30. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
  31. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
  32. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
  33. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
  34. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
  35. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
  36. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
  37. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
  38. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
  39. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
  40. package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
  41. package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
  42. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
  43. package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
  44. package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
  45. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +265 -0
  46. package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
  47. package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
  48. package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
  49. package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
  50. package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
  51. package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
  52. package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
  53. package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
  54. package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +964 -0
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
  62. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
  63. package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
  64. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
  65. package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
  66. package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
  67. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
  68. package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
  69. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
  70. package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
  71. package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
  72. package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
  73. package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
  74. package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
  75. package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
  145. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
  146. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
  147. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
  148. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
  149. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
  150. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
  151. package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
  152. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
  153. package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
  154. package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
  155. package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
  156. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
  157. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
  158. package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
  159. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
  160. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
  161. package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
  162. package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
  163. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
  164. package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
  165. package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
  166. package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
  167. package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
  168. package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
  169. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
  170. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1112 -0
  171. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
  172. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
  173. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
  174. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
  175. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +768 -0
  176. package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
  177. package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
  178. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
  179. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
  180. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
  181. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
  182. package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +333 -0
  183. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
  184. package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
  185. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
  186. package/plugin/vendor/reflexio/reflexio/server/README.md +622 -0
  187. package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
  188. package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
  189. package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
  190. package/plugin/vendor/reflexio/reflexio/server/api.py +2868 -0
  191. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +34 -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 +562 -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 +359 -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 +1871 -0
  206. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +140 -0
  207. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +479 -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 +338 -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 +288 -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 +187 -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 +716 -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_consolidation/v2.3.1.prompt.md +69 -0
  245. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +71 -0
  246. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
  247. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
  248. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +283 -0
  249. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.2.prompt.md +234 -0
  250. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +244 -0
  251. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
  252. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
  253. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
  254. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
  255. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
  256. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +137 -0
  257. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
  258. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
  259. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
  260. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
  261. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
  262. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
  263. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
  264. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
  265. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
  266. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
  267. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
  268. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
  269. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
  270. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
  271. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
  272. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
  273. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
  274. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
  275. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
  276. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
  277. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
  278. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
  279. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
  280. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
  281. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
  282. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
  283. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/README.md +58 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
  289. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
  290. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +336 -0
  292. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1668 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
  296. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
  297. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/embedding_text.py +62 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
  311. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
  312. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
  313. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
  318. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +358 -0
  319. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
  320. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
  321. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +535 -0
  322. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
  323. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +779 -0
  324. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
  325. package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +94 -0
  326. package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
  327. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +702 -0
  328. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
  329. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
  330. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
  331. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +1045 -0
  332. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
  333. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
  334. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
  335. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
  336. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
  337. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
  338. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
  339. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
  340. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
  341. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
  342. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
  343. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
  344. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
  345. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
  346. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
  347. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
  348. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +772 -0
  349. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
  350. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +737 -0
  351. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
  352. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
  353. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
  354. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +803 -0
  355. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
  356. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
  357. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +80 -0
  358. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
  359. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +756 -0
  360. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
  361. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
  362. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
  363. package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
  364. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
  365. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
  366. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
  367. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
  368. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1298 -0
  369. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
  370. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
  371. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
  372. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
  373. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
  374. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
  375. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
  376. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
  377. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
  378. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
  379. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +384 -0
  380. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
  381. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
  382. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
  383. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
  384. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
  385. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
  386. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
  387. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
  388. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
  389. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +572 -0
  390. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
  391. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
  392. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
  393. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
  394. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
  395. package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
  396. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
  397. package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
  398. package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
  399. package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
  400. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
  401. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
  402. package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
@@ -0,0 +1,187 @@
1
+ import contextlib
2
+ import json
3
+ import logging
4
+ import os
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from reflexio.cli.paths import reflexio_home
10
+ from reflexio.models.config_schema import (
11
+ Config,
12
+ PlaybookConfig,
13
+ ProfileExtractorConfig,
14
+ StorageConfigSQLite,
15
+ normalize_legacy_config_shape,
16
+ )
17
+ from reflexio.server.services.configurator.config_storage import ConfigStorage
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class LocalFileConfigStorage(ConfigStorage):
23
+ """
24
+ Local JSON file-based configuration storage implementation.
25
+ Saves/loads configuration to/from local JSON files.
26
+ """
27
+
28
+ def __init__(self, org_id: str, base_dir: str | None = None):
29
+ super().__init__(org_id=org_id)
30
+ if base_dir:
31
+ # Ensure base_dir is absolute
32
+ base_path = Path(base_dir)
33
+ abs_base_dir = (
34
+ str(base_path.resolve()) if not base_path.is_absolute() else base_dir
35
+ )
36
+ self.base_dir = str(Path(abs_base_dir) / "configs")
37
+ self.config_file = str(Path(self.base_dir) / f"config_{org_id}.json")
38
+ print(
39
+ f"LocalFileConfigStorage will save config for {org_id} to a local file at {self.config_file}"
40
+ )
41
+ else:
42
+ self.base_dir = str(reflexio_home() / "configs")
43
+ self.config_file = str(Path(self.base_dir) / f"config_{org_id}.json")
44
+
45
+ def _default_storage_config(self) -> StorageConfigSQLite:
46
+ """Return the default storage config (always SQLite for local file storage)."""
47
+ return StorageConfigSQLite()
48
+
49
+ def get_default_config(self) -> Config:
50
+ """
51
+ Returns a default configuration with SQLite storage.
52
+
53
+ Returns:
54
+ Config: Default configuration with SQLite storage
55
+ """
56
+ return Config(
57
+ storage_config=self._default_storage_config(),
58
+ profile_extractor_config=ProfileExtractorConfig(
59
+ extraction_definition_prompt="Extract key user information including name, role, preferences, and any other relevant profile details from the conversation.",
60
+ ),
61
+ user_playbook_extractor_config=PlaybookConfig(
62
+ extraction_definition_prompt="Extract playbook rules about agent performance, including areas where the agent was helpful, areas for improvement, and any issues encountered during the interaction.",
63
+ ),
64
+ )
65
+
66
+ def load_config(self) -> Config:
67
+ """
68
+ Loads the current configuration from local JSON file. If the file doesn't exist,
69
+ creates a default configuration and saves it.
70
+
71
+ Returns:
72
+ Config: Loaded configuration object
73
+ """
74
+ if not Path(self.config_file).exists():
75
+ config = self.get_default_config()
76
+ self._save_config_to_local_dir(config=config)
77
+ return config
78
+
79
+ try:
80
+ with Path(self.config_file).open(encoding="utf-8") as f:
81
+ config_content = f.read()
82
+ data = json.loads(str(config_content))
83
+ # Upgrade retired list-valued extractor fields (e.g.
84
+ # agent_success_configs) to their singular replacements before
85
+ # validation. Without this, Config would drop the unknown legacy
86
+ # keys and silently lose the user's customization.
87
+ if isinstance(data, dict):
88
+ data = normalize_legacy_config_shape(data)
89
+ # Detect legacy on-disk configs that used the removed "disk"
90
+ # storage backend and rewrite only the storage_config field
91
+ # to default SQLite. Other persisted fields (extractors,
92
+ # prompts, etc.) are preserved so the migration doesn't
93
+ # silently lose user customizations. Without this guard,
94
+ # legacy configs would fail Pydantic validation and the
95
+ # broad-except path below would discard everything.
96
+ storage_cfg = (
97
+ data.get("storage_config") if isinstance(data, dict) else None
98
+ )
99
+ if isinstance(storage_cfg, dict) and storage_cfg.get("type") == "disk":
100
+ logger.warning(
101
+ "Legacy storage_config.type='disk' detected in %s. "
102
+ "The disk backend was removed; rewriting storage_config "
103
+ "to default SQLite and preserving all other fields.",
104
+ self.config_file,
105
+ )
106
+ data = dict(data)
107
+ data["storage_config"] = self._default_storage_config().model_dump()
108
+ config: Config = Config(**data)
109
+ return config
110
+ except Exception:
111
+ logger.exception(
112
+ "Failed to load config from %s; falling back to default config.",
113
+ self.config_file,
114
+ )
115
+ # Create a default config if anything goes wrong.
116
+ return self.get_default_config()
117
+
118
+ def save_config(self, config: Config) -> None:
119
+ """
120
+ Saves the configuration to the local JSON file.
121
+
122
+ Args:
123
+ config (Config): Configuration object to save
124
+ """
125
+ if self.base_dir and self.config_file:
126
+ self._save_config_to_local_dir(config=config)
127
+ else:
128
+ print(
129
+ f"Cannot save config for org {self.org_id}: no local directory configured"
130
+ )
131
+
132
+ def get_version(self) -> tuple[str, Any] | None:
133
+ """Return the on-disk mtime of the org's config file, if it exists.
134
+
135
+ The Reflexio cache uses this to detect out-of-band edits to
136
+ ``~/.reflexio/configs/config_<org-id>.json`` (e.g. an operator
137
+ editing the file directly while the server is running). If the
138
+ file is missing — for example, before the first ``set_config``
139
+ write — return None so the cache leaves the entry alone rather
140
+ than thrashing every request.
141
+
142
+ Returns:
143
+ tuple[str, float] | None: ``("file", mtime_seconds)`` when
144
+ the config file exists, ``None`` otherwise (missing file or
145
+ stat failure).
146
+ """
147
+ try:
148
+ return ("file", Path(self.config_file).stat().st_mtime)
149
+ except OSError:
150
+ return None
151
+
152
+ def _save_config_to_local_dir(self, config: Config) -> None:
153
+ """
154
+ Saves configuration to the local JSON file atomically.
155
+
156
+ Writes to a per-write unique ``.tmp`` file first, then renames it
157
+ over the final path. ``Path.replace`` is atomic on POSIX (same
158
+ filesystem), so concurrent ``set_config`` calls across multiple
159
+ workers cannot observe a partially-written file, and a crash
160
+ mid-write leaves the previous good file intact. The tmp filename
161
+ embeds the pid and a nanosecond timestamp so concurrent writers
162
+ do not race on the same temp path.
163
+
164
+ Args:
165
+ config (Config): Configuration object to save
166
+
167
+ Raises:
168
+ OSError: If the write or atomic rename fails. The tmp file is
169
+ cleaned up on failure (best-effort), but the original
170
+ exception propagates so callers see the failure rather
171
+ than a false success.
172
+ """
173
+ if not (self.base_dir and self.config_file):
174
+ raise ValueError("base_dir and config_file must be set")
175
+
176
+ final_path = Path(self.config_file)
177
+ final_path.parent.mkdir(parents=True, exist_ok=True)
178
+ tmp_path = final_path.with_name(
179
+ f"{final_path.name}.{os.getpid()}.{time.time_ns()}.tmp"
180
+ )
181
+ try:
182
+ tmp_path.write_text(config.model_dump_json(), encoding="utf-8")
183
+ tmp_path.replace(final_path)
184
+ except OSError:
185
+ with contextlib.suppress(OSError):
186
+ tmp_path.unlink(missing_ok=True)
187
+ raise
@@ -0,0 +1,162 @@
1
+ # ruff: noqa: S101
2
+ #!/usr/bin/env python3
3
+ """
4
+ Simple test script to verify the new config storage classes work correctly.
5
+ """
6
+
7
+ import json
8
+ import tempfile
9
+ from pathlib import Path
10
+
11
+ from reflexio.models.config_schema import (
12
+ Config,
13
+ StorageConfigSQLite,
14
+ )
15
+ from reflexio.server.services.configurator.local_file_config_storage import (
16
+ LocalFileConfigStorage,
17
+ )
18
+
19
+
20
+ def test_local_file_config_storage():
21
+ """Test LocalFileConfigStorage functionality."""
22
+ print("Testing LocalFileConfigStorage...")
23
+
24
+ # Create a temporary directory for testing
25
+ with tempfile.TemporaryDirectory() as temp_dir:
26
+ org_id = "test_org_123"
27
+
28
+ # Create storage instance
29
+ storage = LocalFileConfigStorage(org_id=org_id, base_dir=temp_dir)
30
+
31
+ # Test get_default_config
32
+ default_config = storage.get_default_config()
33
+ print(
34
+ f" Default config storage type: {type(default_config.storage_config).__name__}"
35
+ )
36
+ assert isinstance(default_config.storage_config, StorageConfigSQLite)
37
+
38
+ # Test load_config (should create default if file doesn't exist)
39
+ loaded_config = storage.load_config()
40
+ print(
41
+ f" Loaded config storage type: {type(loaded_config.storage_config).__name__}"
42
+ )
43
+ assert isinstance(loaded_config.storage_config, StorageConfigSQLite)
44
+
45
+ # Test save_config
46
+ test_config = Config(
47
+ storage_config=StorageConfigSQLite(),
48
+ agent_context_prompt="Test context prompt",
49
+ )
50
+ storage.save_config(test_config)
51
+ print(" Config saved successfully")
52
+
53
+ # Test load_config again (should load the saved config)
54
+ reloaded_config = storage.load_config()
55
+ print(
56
+ f" Reloaded config agent context: {reloaded_config.agent_context_prompt}"
57
+ )
58
+ assert reloaded_config.agent_context_prompt == "Test context prompt"
59
+
60
+ print(" LocalFileConfigStorage tests passed!")
61
+
62
+
63
+ def test_configurator_integration():
64
+ """Test that the configurator works with the new storage classes."""
65
+ print("Testing configurator integration...")
66
+
67
+ # Test with local storage
68
+ with tempfile.TemporaryDirectory() as temp_dir:
69
+ from reflexio.server.services.configurator.configurator import (
70
+ DefaultConfigurator,
71
+ )
72
+
73
+ org_id = "test_org_789"
74
+ configurator = DefaultConfigurator(org_id=org_id, base_dir=temp_dir)
75
+
76
+ storage_config = configurator.get_current_storage_configuration()
77
+ print(f" Configurator storage type: {type(storage_config).__name__}")
78
+ assert isinstance(storage_config, StorageConfigSQLite)
79
+
80
+ # Test setting and getting config
81
+ configurator.set_config_by_name(
82
+ "agent_context_prompt", "Integration test prompt"
83
+ )
84
+ context = configurator.get_agent_context()
85
+ print(f" Agent context: {context}")
86
+ assert context == "Integration test prompt"
87
+
88
+ print(" Configurator integration tests passed!")
89
+
90
+
91
+ def test_load_config_upgrades_legacy_list_shape():
92
+ """Legacy list-shaped extractor fields on disk are migrated to singular fields.
93
+
94
+ Configs persisted before the single-extractor refactor store
95
+ ``*_extractor_configs`` lists and ``agent_success_configs``. Without
96
+ migration at the load boundary, ``Config`` would drop these unknown keys and
97
+ silently lose the user's customization.
98
+ """
99
+ with tempfile.TemporaryDirectory() as temp_dir:
100
+ org_id = "legacy_org"
101
+ storage = LocalFileConfigStorage(org_id=org_id, base_dir=temp_dir)
102
+
103
+ legacy_payload = {
104
+ "storage_config": StorageConfigSQLite().model_dump(),
105
+ "profile_extractor_configs": [
106
+ {
107
+ "extractor_name": "legacy_profile",
108
+ "extraction_definition_prompt": "Extract legacy profile data.",
109
+ }
110
+ ],
111
+ "user_playbook_extractor_configs": [
112
+ {
113
+ "extractor_name": "legacy_playbook",
114
+ "extraction_definition_prompt": "Extract legacy playbook rules.",
115
+ }
116
+ ],
117
+ "agent_success_configs": [
118
+ {
119
+ "evaluation_name": "legacy_success",
120
+ "success_definition_prompt": "Evaluate legacy agent success.",
121
+ }
122
+ ],
123
+ }
124
+ Path(storage.config_file).parent.mkdir(parents=True, exist_ok=True)
125
+ Path(storage.config_file).write_text(
126
+ json.dumps(legacy_payload), encoding="utf-8"
127
+ )
128
+
129
+ loaded = storage.load_config()
130
+
131
+ assert loaded.profile_extractor_config is not None
132
+ assert loaded.profile_extractor_config.extractor_name == "legacy_profile"
133
+ assert loaded.user_playbook_extractor_config is not None
134
+ assert loaded.user_playbook_extractor_config.extractor_name == "legacy_playbook"
135
+ assert loaded.agent_success_config is not None
136
+ assert loaded.agent_success_config.evaluation_name == "legacy_success"
137
+
138
+
139
+ def test_load_config():
140
+ """Test that the configurator loads the config correctly."""
141
+ print("Testing load_config...")
142
+
143
+ new_config = Config(
144
+ storage_config=StorageConfigSQLite(
145
+ db_path="/tmp/test.db",
146
+ ),
147
+ profile_extractor_config=None,
148
+ )
149
+
150
+ json_config = new_config.model_dump_json()
151
+ config = Config.model_validate_json(json_config)
152
+
153
+ assert isinstance(config.storage_config, StorageConfigSQLite)
154
+ assert config.storage_config.db_path == "/tmp/test.db"
155
+
156
+ print(" Load config tests passed!")
157
+
158
+
159
+ if __name__ == "__main__":
160
+ import pytest
161
+
162
+ pytest.main([__file__])
@@ -0,0 +1,112 @@
1
+ """
2
+ Shared utilities for deduplication services.
3
+
4
+ This module contains base classes and utility functions used by both
5
+ ProfileDeduplicator and PlaybookConsolidator.
6
+ """
7
+
8
+ import logging
9
+ from abc import ABC
10
+ from datetime import UTC, datetime
11
+
12
+ from reflexio.server.api_endpoints.request_context import RequestContext
13
+ from reflexio.server.llm.litellm_client import LiteLLMClient
14
+ from reflexio.server.llm.model_defaults import ModelRole, resolve_model_name
15
+ from reflexio.server.site_var.site_var_manager import SiteVarManager
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Format used for "Last Modified" timestamps shown to deduplication LLMs.
20
+ # Includes hours and minutes so same-day contradictions (morning vs evening)
21
+ # can be distinguished.
22
+ DEDUP_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M UTC"
23
+ DEDUP_TIMESTAMP_FALLBACK = "unknown"
24
+
25
+
26
+ def format_dedup_timestamp(ts: int) -> str:
27
+ """Format a Unix timestamp as a UTC date string for deduplication prompts.
28
+
29
+ Wraps ``datetime.fromtimestamp`` in a try/except so a single malformed
30
+ timestamp (negative, zero, or out-of-range integer) cannot abort an entire
31
+ deduplication batch. Returns ``DEDUP_TIMESTAMP_FALLBACK`` on failure.
32
+
33
+ Args:
34
+ ts (int): Unix timestamp (seconds since epoch).
35
+
36
+ Returns:
37
+ str: Human-readable UTC timestamp like ``"2026-04-11 14:30 UTC"``,
38
+ or ``"unknown"`` if the value is unparseable.
39
+ """
40
+ try:
41
+ return datetime.fromtimestamp(ts, tz=UTC).strftime(DEDUP_TIMESTAMP_FORMAT)
42
+ except (OverflowError, ValueError, OSError, TypeError) as exc:
43
+ logger.warning("Failed to format dedup timestamp %r: %s", ts, exc)
44
+ return DEDUP_TIMESTAMP_FALLBACK
45
+
46
+
47
+ def parse_item_id(item_id: str) -> tuple[str, int] | None:
48
+ """
49
+ Parse a prompt-format item ID like 'NEW-0' or 'EXISTING-1' into its prefix and index.
50
+
51
+ Args:
52
+ item_id (str): Item ID string in the format 'PREFIX-N' (e.g., 'NEW-0', 'EXISTING-1')
53
+
54
+ Returns:
55
+ Optional[tuple[str, int]]: A tuple of (prefix, index) where prefix is 'NEW' or 'EXISTING',
56
+ or None if the item ID is invalid
57
+ """
58
+ parts = item_id.rsplit("-", 1)
59
+ if len(parts) != 2:
60
+ logger.warning("Invalid item ID format: %s", item_id)
61
+ return None
62
+ prefix, idx_str = parts
63
+ prefix = prefix.upper()
64
+ if prefix not in ("NEW", "EXISTING"):
65
+ logger.warning("Invalid prefix in item ID: %s", item_id)
66
+ return None
67
+ try:
68
+ return prefix, int(idx_str)
69
+ except ValueError:
70
+ logger.warning("Invalid index in item ID: %s", item_id)
71
+ return None
72
+
73
+
74
+ # ===============================
75
+ # Base Deduplicator ABC
76
+ # ===============================
77
+
78
+
79
+ class BaseDeduplicator(ABC): # noqa: B024
80
+ """
81
+ Abstract base class for deduplicators that use LLM-based semantic matching.
82
+
83
+ Provides shared initialization (LLM client, model name).
84
+ Subclasses implement their own deduplicate() method with domain-specific
85
+ prompt building, hybrid search, and result construction.
86
+ """
87
+
88
+ def __init__(
89
+ self,
90
+ request_context: RequestContext,
91
+ llm_client: LiteLLMClient,
92
+ ):
93
+ """
94
+ Initialize the deduplicator.
95
+
96
+ Args:
97
+ request_context: Request context with storage and prompt manager
98
+ llm_client: Unified LLM client for LLM calls
99
+ """
100
+ self.request_context = request_context
101
+ self.client = llm_client
102
+
103
+ # Resolve model name: site var → auto-detect
104
+ model_setting = SiteVarManager().get_site_var("llm_model_setting")
105
+ site_var = model_setting if isinstance(model_setting, dict) else {}
106
+ api_key_config = self.request_context.configurator.get_config().api_key_config
107
+
108
+ self.model_name = resolve_model_name(
109
+ ModelRole.GENERATION,
110
+ site_var_value=site_var.get("default_generation_model_name"),
111
+ api_key_config=api_key_config,
112
+ )
@@ -0,0 +1,62 @@
1
+ """Canonical text and prefixes used for vector embeddings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Literal
6
+
7
+ from reflexio.models.api_schema.service_schemas import (
8
+ AgentPlaybook,
9
+ AgentSuccessEvaluationResult,
10
+ Interaction,
11
+ UserPlaybook,
12
+ UserProfile,
13
+ )
14
+
15
+ SEARCH_DOCUMENT_PREFIX = "search_document: "
16
+ SEARCH_QUERY_PREFIX = "search_query: "
17
+
18
+ EmbeddingTextEntity = (
19
+ Interaction
20
+ | UserProfile
21
+ | UserPlaybook
22
+ | AgentPlaybook
23
+ | AgentSuccessEvaluationResult
24
+ )
25
+
26
+
27
+ def embedding_text(entity: EmbeddingTextEntity) -> str:
28
+ """Return the exact text used for an entity's stored embedding."""
29
+ if isinstance(entity, Interaction):
30
+ return f"{entity.content}\n{entity.user_action_description}"
31
+ if isinstance(entity, UserProfile):
32
+ parts = [entity.content]
33
+ if entity.custom_features:
34
+ parts.append(str(entity.custom_features))
35
+ return "\n".join(parts)
36
+ if isinstance(entity, (UserPlaybook, AgentPlaybook)):
37
+ return entity.trigger or entity.content
38
+ if isinstance(entity, AgentSuccessEvaluationResult):
39
+ return " ".join(
40
+ part
41
+ for part in (entity.failure_type, entity.failure_reason)
42
+ if part and part.strip()
43
+ )
44
+ raise TypeError(f"Unsupported embedding text entity: {type(entity).__name__}")
45
+
46
+
47
+ def embedding_input(
48
+ text: str, *, purpose: Literal["document", "query"] = "document"
49
+ ) -> str:
50
+ """Apply the asymmetric search prefix used before embedding calls.
51
+
52
+ ``purpose`` must be ``"document"`` or ``"query"``; any other value raises so a
53
+ misspelled call site fails fast instead of silently writing or searching the
54
+ wrong vector space.
55
+ """
56
+ if purpose == "document":
57
+ return SEARCH_DOCUMENT_PREFIX + text
58
+ if purpose == "query":
59
+ return SEARCH_QUERY_PREFIX + text
60
+ raise ValueError(
61
+ f"Unknown embedding purpose {purpose!r}; expected 'document' or 'query'"
62
+ )
@@ -0,0 +1,33 @@
1
+ """Bucket per-session correction counts into a fixed 6-bin histogram.
2
+
3
+ The spec §4.5 called for a continuous 0.0-1.0 score distribution; the actual
4
+ schema only has `is_success` and `number_of_correction_per_session`, so we
5
+ distribute by correction count instead. Six bins keep the visualization
6
+ readable while preserving the shape of session quality.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Iterable
12
+
13
+ BUCKET_LABELS: tuple[str, ...] = ("0", "1", "2", "3", "4", "5+")
14
+ _BUCKET_COUNT = len(BUCKET_LABELS)
15
+
16
+
17
+ def bucket_corrections(
18
+ corrections: Iterable[int],
19
+ ) -> tuple[int, int, int, int, int, int]:
20
+ """Return per-bin counts for the corrections histogram.
21
+
22
+ Args:
23
+ corrections (Iterable[int]): One non-negative integer per session in
24
+ the window. Values >= 5 fall into the final "5+" bin.
25
+
26
+ Returns:
27
+ tuple[int, ...]: Six non-negative counts in BUCKET_LABELS order.
28
+ """
29
+ bins = [0] * _BUCKET_COUNT
30
+ for c in corrections:
31
+ idx = min(_BUCKET_COUNT - 1, max(0, c))
32
+ bins[idx] += 1
33
+ return tuple(bins) # type: ignore[return-value]
@@ -0,0 +1,126 @@
1
+ """Pure stratified sampler for the F3 eval-regen pipeline.
2
+
3
+ DB-free and LLM-free. Operates on SampleCandidate tuples emitted by the
4
+ regen worker's candidate-discovery step; returns the sampled subset.
5
+
6
+ Stratification key: (day_bucket, F2 group). Day buckets are aligned to
7
+ 86400-second boundaries. F2 group is derived via the existing
8
+ `assign_group_from_metadata` so the per-day cap is honored separately
9
+ for treatment / control / untagged.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import random
15
+ from collections import defaultdict
16
+ from collections.abc import Iterable
17
+ from dataclasses import dataclass
18
+ from typing import Any
19
+
20
+ from reflexio.server.services.evaluation_overview.group_aggregation import (
21
+ GroupAssignment,
22
+ assign_group_from_metadata,
23
+ )
24
+
25
+ # Day bucket width in seconds. Strata are aligned to (ts // _DAY) * _DAY.
26
+ _DAY = 86_400
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class SampleCandidate:
31
+ """One candidate for the regen pipeline's sampling step.
32
+
33
+ Carries everything the regen worker needs to dispatch a per-session
34
+ evaluation, plus the first-request metadata used to assign the F2
35
+ group for stratification.
36
+
37
+ Attributes:
38
+ session_id: The session to evaluate.
39
+ user_id: Owner of the session's requests.
40
+ agent_version: Agent version to filter on.
41
+ source: Optional source tag (passed through to run_group_evaluation).
42
+ created_at: Unix epoch seconds of the session's most relevant
43
+ timestamp; used for day-bucket alignment.
44
+ first_request_metadata: The metadata dict on the session's first
45
+ request, read once during candidate discovery.
46
+ """
47
+
48
+ session_id: str
49
+ user_id: str
50
+ agent_version: str
51
+ source: str | None
52
+ created_at: int
53
+ first_request_metadata: dict[str, Any]
54
+
55
+
56
+ def stratify_by_day_and_group(
57
+ candidates: Iterable[SampleCandidate],
58
+ ) -> dict[tuple[int, GroupAssignment], list[SampleCandidate]]:
59
+ """Bucket candidates by (day_bucket, F2 group).
60
+
61
+ Args:
62
+ candidates: Candidate tuples emitted by the regen candidate-discovery
63
+ step.
64
+
65
+ Returns:
66
+ Dict from (day_bucket_start, group) to the candidate list for that
67
+ stratum. Empty strata are absent; the dict has only populated keys.
68
+ """
69
+ strata: dict[tuple[int, GroupAssignment], list[SampleCandidate]] = defaultdict(list)
70
+ for c in candidates:
71
+ day_bucket = (c.created_at // _DAY) * _DAY
72
+ group = assign_group_from_metadata(c.first_request_metadata)
73
+ strata[(day_bucket, group)].append(c)
74
+ return dict(strata)
75
+
76
+
77
+ def sample_per_stratum(
78
+ candidates: list[SampleCandidate],
79
+ n: int,
80
+ rng: random.Random,
81
+ ) -> list[SampleCandidate]:
82
+ """Sample up to ``n`` candidates from a single stratum.
83
+
84
+ Args:
85
+ candidates: All candidates in one stratum.
86
+ n: Cap. Strata with fewer than ``n`` items are returned whole.
87
+ rng: Seeded random for reproducibility.
88
+
89
+ Returns:
90
+ Sampled subset. Order is ``random.Random.sample``'s implementation
91
+ detail — callers should not depend on it.
92
+
93
+ Raises:
94
+ ValueError: When ``n`` is not positive.
95
+ """
96
+ if n <= 0:
97
+ raise ValueError(f"n must be positive, got {n}")
98
+ if len(candidates) <= n:
99
+ return list(candidates)
100
+ return rng.sample(candidates, n)
101
+
102
+
103
+ def sample_candidates(
104
+ candidates: Iterable[SampleCandidate],
105
+ n_per_stratum: int,
106
+ rng: random.Random,
107
+ ) -> list[SampleCandidate]:
108
+ """Stratify by (day x group), then sample per stratum, then flatten.
109
+
110
+ Predictable cost across runs: at most ``n_per_stratum * num_strata``
111
+ items returned, regardless of traffic volume.
112
+
113
+ Args:
114
+ candidates: All candidates in the regen window.
115
+ n_per_stratum: Per-stratum cap (typically
116
+ ``Config.eval_sample_n_per_stratum``).
117
+ rng: Seeded random for reproducibility.
118
+
119
+ Returns:
120
+ Flattened sampled list.
121
+ """
122
+ strata = stratify_by_day_and_group(candidates)
123
+ sampled: list[SampleCandidate] = []
124
+ for stratum_candidates in strata.values():
125
+ sampled.extend(sample_per_stratum(stratum_candidates, n_per_stratum, rng))
126
+ return sampled