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,281 @@
1
+ """Orchestrates the Braintrust connector: connect, select-projects, sync, status.
2
+
3
+ The service is stateless across requests; it loads the connection from
4
+ storage on each call. API keys are encrypted in storage and decrypted
5
+ only when needed for a Braintrust HTTP call.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ import time
12
+ from collections.abc import Callable
13
+ from dataclasses import dataclass, field
14
+
15
+ from reflexio.models.api_schema.braintrust_schema import (
16
+ BraintrustConnection,
17
+ BraintrustProjectSummary,
18
+ BraintrustStatusResponse,
19
+ BraintrustWorkspaceSummary,
20
+ ConnectBraintrustRequest,
21
+ ConnectBraintrustResponse,
22
+ ImportedScore,
23
+ SelectProjectsRequest,
24
+ SelectProjectsResponse,
25
+ SyncBraintrustResponse,
26
+ )
27
+ from reflexio.server.services.braintrust._encryption import decrypt, encrypt
28
+ from reflexio.server.services.braintrust.client import (
29
+ DEFAULT_BASE_URL,
30
+ BraintrustAuthError,
31
+ BraintrustClient,
32
+ BraintrustHTTPError,
33
+ )
34
+
35
+
36
+ def _default_client_factory(api_key: str) -> BraintrustClient:
37
+ """Default factory that honors `BRAINTRUST_BASE_URL` env override.
38
+
39
+ Set the env var to point at a staging or mock Braintrust instance
40
+ (useful for dev / cron testing without hitting the real API).
41
+ """
42
+ import os
43
+
44
+ base_url = os.environ.get("BRAINTRUST_BASE_URL", "").strip() or DEFAULT_BASE_URL
45
+ return BraintrustClient(api_key, base_url=base_url)
46
+
47
+
48
+ logger = logging.getLogger(__name__)
49
+
50
+
51
+ _METADATA_SESSION_KEY = "reflexio_session_id"
52
+
53
+
54
+ @dataclass
55
+ class BraintrustConnectorService:
56
+ """Top-level orchestrator for the Braintrust connector.
57
+
58
+ Args:
59
+ storage: BaseStorage-like; uses save/get/delete_braintrust_connection
60
+ and save_imported_scores. Default no-op storage methods keep
61
+ this workable until per-backend implementations land.
62
+ org_id (str): The Reflexio org for which the connector operates.
63
+ client_factory (Callable[[str], BraintrustClient]): Factory that
64
+ takes an API key and returns a BraintrustClient. Injectable so
65
+ tests can pass mocks.
66
+ """
67
+
68
+ storage: object
69
+ org_id: str
70
+ client_factory: Callable[[str], BraintrustClient] = field(
71
+ default=_default_client_factory
72
+ )
73
+
74
+ # ------------------------------------------------------------------
75
+ # Public API
76
+ # ------------------------------------------------------------------
77
+
78
+ def connect(self, request: ConnectBraintrustRequest) -> ConnectBraintrustResponse:
79
+ """Step 1: validate the API key and return the workspace/project tree.
80
+
81
+ Persists nothing — the caller chooses which projects to sync and
82
+ then calls `select_projects` with the same key.
83
+ """
84
+ client = self.client_factory(request.api_key)
85
+ try:
86
+ if not client.validate_key():
87
+ return ConnectBraintrustResponse(
88
+ success=False, msg="Braintrust rejected the API key."
89
+ )
90
+ workspaces = self._fetch_workspace_tree(client)
91
+ except BraintrustAuthError:
92
+ return ConnectBraintrustResponse(
93
+ success=False, msg="Braintrust rejected the API key."
94
+ )
95
+ except BraintrustHTTPError as e:
96
+ return ConnectBraintrustResponse(
97
+ success=False, msg=f"Braintrust HTTP error: {e}"
98
+ )
99
+ finally:
100
+ client.close()
101
+
102
+ return ConnectBraintrustResponse(success=True, workspaces=workspaces, msg="")
103
+
104
+ def select_projects(self, request: SelectProjectsRequest) -> SelectProjectsResponse:
105
+ """Step 2: persist the connection with the selected projects.
106
+
107
+ Stores the API key encrypted. Overwrites any existing connection
108
+ for the org.
109
+ """
110
+ connection = BraintrustConnection(
111
+ org_id=self.org_id,
112
+ api_key_enc=encrypt(request.api_key),
113
+ workspace_id=request.workspace_id,
114
+ workspace_name=request.workspace_name,
115
+ project_ids=request.project_ids,
116
+ last_sync_ts=None,
117
+ last_error=None,
118
+ )
119
+ self._save_connection(connection)
120
+ return SelectProjectsResponse(success=True, msg="Connected.")
121
+
122
+ def status(self) -> BraintrustStatusResponse:
123
+ """Return whether the org is connected + sync state. Never echoes the key."""
124
+ connection = self._load_connection()
125
+ if connection is None:
126
+ return BraintrustStatusResponse(connected=False)
127
+ return BraintrustStatusResponse(
128
+ connected=True,
129
+ workspace_id=connection.workspace_id,
130
+ workspace_name=connection.workspace_name,
131
+ project_count=len(connection.project_ids),
132
+ last_sync_ts=connection.last_sync_ts,
133
+ last_error=connection.last_error,
134
+ )
135
+
136
+ def disconnect(self) -> None:
137
+ """Delete the persisted connection."""
138
+ self.storage.delete_braintrust_connection(self.org_id) # type: ignore[attr-defined]
139
+
140
+ def sync_once(self, backfill_days: int = 90) -> SyncBraintrustResponse:
141
+ """One-shot sync: walk projects → experiments → spans → write scores.
142
+
143
+ Updates `last_sync_ts` and `last_error` on the persisted connection.
144
+ Idempotent at the storage layer (per-backend overrides should
145
+ upsert by (source, source_run_id, scorer_name)).
146
+ """
147
+ connection = self._load_connection()
148
+ if connection is None:
149
+ return SyncBraintrustResponse(
150
+ success=False, msg="Not connected to Braintrust."
151
+ )
152
+
153
+ try:
154
+ api_key = decrypt(connection.api_key_enc)
155
+ except Exception as e: # noqa: BLE001
156
+ return SyncBraintrustResponse(
157
+ success=False, msg=f"Failed to decrypt API key: {e}"
158
+ )
159
+
160
+ since_ts = connection.last_sync_ts
161
+ if since_ts is None:
162
+ since_ts = max(0, int(time.time()) - backfill_days * 24 * 60 * 60)
163
+
164
+ all_scores: list[ImportedScore] = []
165
+ client = self.client_factory(api_key)
166
+ try:
167
+ for project_id in connection.project_ids:
168
+ experiments = client.list_experiments(project_id, since_ts=since_ts)
169
+ for exp in experiments:
170
+ spans = client.list_spans(exp["id"])
171
+ all_scores.extend(_scores_from_spans(spans, self.org_id))
172
+ except BraintrustAuthError:
173
+ self._persist_sync_outcome(connection, error="API key invalid.")
174
+ return SyncBraintrustResponse(
175
+ success=False, msg="API key invalid; halting sync."
176
+ )
177
+ except BraintrustHTTPError as e:
178
+ self._persist_sync_outcome(connection, error=str(e))
179
+ return SyncBraintrustResponse(success=False, msg=str(e))
180
+ finally:
181
+ client.close()
182
+
183
+ self.storage.save_imported_scores(all_scores) # type: ignore[attr-defined]
184
+ self._persist_sync_outcome(connection, error=None)
185
+ return SyncBraintrustResponse(
186
+ success=True, scored_count=len(all_scores), msg=""
187
+ )
188
+
189
+ # ------------------------------------------------------------------
190
+ # Internals
191
+ # ------------------------------------------------------------------
192
+
193
+ def _fetch_workspace_tree(
194
+ self, client: BraintrustClient
195
+ ) -> list[BraintrustWorkspaceSummary]:
196
+ """Pull workspaces + their projects in one shot for the connect UX."""
197
+ result: list[BraintrustWorkspaceSummary] = []
198
+ for ws in client.list_organizations():
199
+ workspace_id = str(ws.get("id", ""))
200
+ workspace_name = str(ws.get("name", ""))
201
+ if not workspace_id:
202
+ continue
203
+ projects_raw = client.list_projects(workspace_id)
204
+ projects = [
205
+ BraintrustProjectSummary(
206
+ project_id=str(p.get("id", "")),
207
+ project_name=str(p.get("name", "")),
208
+ )
209
+ for p in projects_raw
210
+ if p.get("id")
211
+ ]
212
+ result.append(
213
+ BraintrustWorkspaceSummary(
214
+ workspace_id=workspace_id,
215
+ workspace_name=workspace_name,
216
+ projects=projects,
217
+ )
218
+ )
219
+ return result
220
+
221
+ def _load_connection(self) -> BraintrustConnection | None:
222
+ return self.storage.get_braintrust_connection(self.org_id) # type: ignore[attr-defined]
223
+
224
+ def _save_connection(self, connection: BraintrustConnection) -> None:
225
+ self.storage.save_braintrust_connection(connection) # type: ignore[attr-defined]
226
+
227
+ def _persist_sync_outcome(
228
+ self, connection: BraintrustConnection, *, error: str | None
229
+ ) -> None:
230
+ update: dict[str, int | str | None] = {"last_error": error}
231
+ if error is None:
232
+ update["last_sync_ts"] = int(time.time())
233
+ updated = connection.model_copy(update=update)
234
+ self._save_connection(updated)
235
+
236
+
237
+ def _scores_from_spans(
238
+ spans: list[dict],
239
+ org_id: str,
240
+ ) -> list[ImportedScore]:
241
+ """Extract ImportedScore rows from a list of Braintrust spans.
242
+
243
+ A single span may carry multiple scorers — one row per scorer.
244
+ `session_id` is pulled from `span.metadata.reflexio_session_id` when
245
+ present (the progressive-matching upgrade path).
246
+ """
247
+ out: list[ImportedScore] = []
248
+ for span in spans:
249
+ span_id = str(span.get("id", ""))
250
+ if not span_id:
251
+ continue
252
+ ts_raw = span.get("created_at", 0)
253
+ try:
254
+ ts = int(ts_raw)
255
+ except (TypeError, ValueError):
256
+ ts = 0
257
+ metadata = span.get("metadata") or {}
258
+ session_id: str | None = None
259
+ if isinstance(metadata, dict):
260
+ v = metadata.get(_METADATA_SESSION_KEY)
261
+ if isinstance(v, str) and v:
262
+ session_id = v
263
+ scores = span.get("scores") or {}
264
+ if not isinstance(scores, dict):
265
+ continue
266
+ for scorer_name, value in scores.items():
267
+ try:
268
+ fvalue = float(value)
269
+ except (TypeError, ValueError):
270
+ continue
271
+ out.append(
272
+ ImportedScore(
273
+ org_id=org_id,
274
+ source_run_id=span_id,
275
+ session_id=session_id,
276
+ scorer_name=str(scorer_name),
277
+ value=fvalue,
278
+ ts=ts,
279
+ )
280
+ )
281
+ return out
@@ -0,0 +1,179 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from abc import ABC, abstractmethod
5
+ from collections.abc import Callable
6
+ from typing import Any, ClassVar
7
+
8
+ from pydantic import BaseModel
9
+
10
+ from reflexio.models.config_schema import Config, StorageConfig, StorageConfigTest
11
+ from reflexio.server.services.configurator.config_storage import ConfigStorage
12
+ from reflexio.server.services.storage.error import StorageError
13
+ from reflexio.server.services.storage.storage_base import BaseStorage
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ _CONFIG_NAME_ALIASES = {
18
+ "batch_size": "window_size",
19
+ "batch_interval": "stride_size",
20
+ "extraction_window_size": "window_size",
21
+ "extraction_window_stride": "stride_size",
22
+ }
23
+
24
+
25
+ class BaseConfigurator(ABC):
26
+ """Abstract base for organization configurators.
27
+
28
+ Subclasses must:
29
+ - Set ``_STORAGE_FACTORIES`` and ``_STORAGE_READINESS_CHECKS`` class variables.
30
+ - Implement ``_select_config_storage`` to choose the config persistence backend.
31
+
32
+ All shared configuration and storage-dispatch logic lives here so that OS
33
+ and enterprise implementations only override what differs.
34
+ """
35
+
36
+ _STORAGE_FACTORIES: ClassVar[dict[type[StorageConfig], Callable[..., BaseStorage]]]
37
+ _STORAGE_READINESS_CHECKS: ClassVar[
38
+ dict[type[StorageConfig], Callable[[Any], bool]]
39
+ ]
40
+
41
+ def __init__(
42
+ self,
43
+ org_id: str,
44
+ base_dir: str | None = None,
45
+ config: Config | None = None,
46
+ ) -> None:
47
+ self.org_id = org_id
48
+ self.base_dir = base_dir
49
+
50
+ self.config_storage = self._select_config_storage(org_id, base_dir)
51
+ if not config:
52
+ self.config = self.config_storage.load_config()
53
+ else:
54
+ self.config = config
55
+
56
+ if not self.config:
57
+ raise ValueError(f"Failed to load configuration for organization {org_id}")
58
+
59
+ @abstractmethod
60
+ def _select_config_storage(
61
+ self, org_id: str, base_dir: str | None
62
+ ) -> ConfigStorage:
63
+ """Select and return the appropriate ConfigStorage backend.
64
+
65
+ This is the primary extension point: OS uses LocalJson only, while
66
+ enterprise uses a multi-tier priority system.
67
+ """
68
+
69
+ # ==========================
70
+ # Configuration
71
+ # ==========================
72
+
73
+ def get_config(self) -> Config:
74
+ return self.config
75
+
76
+ def get_config_for_response(self) -> dict[str, Any]:
77
+ """Return config serialized for API responses."""
78
+ return self.config.model_dump(mode="json")
79
+
80
+ def normalize_config_payload(self, config: dict[str, Any]) -> dict[str, Any]:
81
+ """Normalize raw API config payloads before Pydantic validation."""
82
+ return config
83
+
84
+ def get_agent_context(self) -> str:
85
+ context = self.get_config().agent_context_prompt
86
+ if not context:
87
+ return ""
88
+ return context.strip()
89
+
90
+ def set_config(self, config: Config) -> None:
91
+ self.config_storage.save_config(config=config)
92
+ self.config = config
93
+
94
+ def set_config_by_name(
95
+ self,
96
+ config_name: str,
97
+ config_value: str | int | float | bool | list | dict | BaseModel | None,
98
+ ) -> None:
99
+ original_config_name = config_name
100
+ config_name = _CONFIG_NAME_ALIASES.get(config_name, config_name)
101
+ if config_name not in type(self.config).model_fields:
102
+ raise ValueError(f"Invalid config name: {config_name}")
103
+
104
+ # Aliased plural fields (e.g. ``profile_extractor_configs``) map to
105
+ # singular Optional fields on the model. Collapse the list to its
106
+ # first element, or ``None`` if empty, before assignment.
107
+ resolved_value: str | int | float | bool | list | dict | BaseModel | None = (
108
+ config_value
109
+ )
110
+ if (
111
+ original_config_name in _CONFIG_NAME_ALIASES
112
+ and config_name
113
+ in {"profile_extractor_config", "user_playbook_extractor_config"}
114
+ and isinstance(config_value, list)
115
+ ):
116
+ resolved_value = config_value[0] if config_value else None
117
+
118
+ setattr(self.config, config_name, resolved_value)
119
+ self.set_config(config=self.config)
120
+
121
+ # ==========================
122
+ # Storage
123
+ # ==========================
124
+
125
+ def get_current_storage_configuration(self) -> StorageConfig:
126
+ """Return the currently configured storage config."""
127
+ return self.get_config().storage_config
128
+
129
+ def create_storage(self, storage_config: StorageConfig) -> BaseStorage | None:
130
+ """Create a storage from the ``_STORAGE_FACTORIES`` registry.
131
+
132
+ Returns None when *storage_config* is None (enterprise cloud mode
133
+ where storage is not yet configured).
134
+ """
135
+ if storage_config is None:
136
+ return None
137
+ factory = self._STORAGE_FACTORIES.get(type(storage_config))
138
+ if factory is None:
139
+ raise ValueError(
140
+ f"No storage factory registered for {type(storage_config).__name__}"
141
+ )
142
+ return factory(self, storage_config)
143
+
144
+ def is_storage_configured(self) -> bool:
145
+ """Check whether a valid, non-failed storage option is configured."""
146
+ if not self.is_storage_config_ready_to_test(
147
+ storage_config=self.get_current_storage_configuration(),
148
+ ):
149
+ return False
150
+ return self.get_config().storage_config_test != StorageConfigTest.FAILED
151
+
152
+ def is_storage_config_ready_to_test(self, storage_config: StorageConfig) -> bool:
153
+ """Check whether *storage_config* is fully filled in and ready for a test connection."""
154
+ check = self._STORAGE_READINESS_CHECKS.get(type(storage_config))
155
+ return check(storage_config) if check else False
156
+
157
+ def test_and_init_storage_config(
158
+ self, storage_config: StorageConfig
159
+ ) -> tuple[bool, str]:
160
+ """Test whether *storage_config* is valid and initialise the storage.
161
+
162
+ Returns:
163
+ tuple[bool, str]: (success, message)
164
+ """
165
+ if not self.is_storage_config_ready_to_test(storage_config=storage_config):
166
+ return False, "Storage configuration is not ready to test"
167
+
168
+ try:
169
+ storage = self.create_storage(storage_config=storage_config)
170
+ if storage is not None:
171
+ storage.migrate()
172
+ return True, "Storage initialized successfully"
173
+ return False, "Failed to create storage"
174
+ except StorageError as e:
175
+ logger.error("Storage initialization failed: %s", e.message)
176
+ return False, e.message
177
+ except Exception as e:
178
+ logger.error("Storage initialization failed: %s", e)
179
+ return False, str(e)
@@ -0,0 +1,62 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+ from reflexio.models.config_schema import Config
5
+
6
+
7
+ class ConfigStorage(ABC):
8
+ """
9
+ Abstract base class for configuration storage operations.
10
+ Defines the interface for saving/loading configuration to/from persistent storage.
11
+ """
12
+
13
+ def __init__(self, org_id: str):
14
+ self.org_id: str = org_id
15
+
16
+ @abstractmethod
17
+ def get_default_config(self) -> Config:
18
+ """
19
+ Returns a default configuration that is uninitialized.
20
+
21
+ Returns:
22
+ Config: Default configuration object
23
+ """
24
+
25
+ @abstractmethod
26
+ def load_config(self) -> Config:
27
+ """
28
+ Loads the current configuration of the organization. If the organization does
29
+ not exist, or if no config exists, or if the current saved config is no longer valid,
30
+ this routine creates a default one but will not update the persistent storage.
31
+
32
+ Returns:
33
+ Config: Loaded configuration object
34
+ """
35
+
36
+ @abstractmethod
37
+ def save_config(self, config: Config) -> None:
38
+ """
39
+ Saves the configuration to the persistent storage.
40
+
41
+ Args:
42
+ config (Config): Configuration object to save
43
+ """
44
+
45
+ def get_version(self) -> tuple[str, Any] | None:
46
+ """Cheap probe of the persisted config version, used by the per-org cache.
47
+
48
+ Backends that can cheaply detect out-of-band mutations should
49
+ override this. The Reflexio cache stamps the returned tuple at
50
+ load time and re-probes on every cache hit; if the value
51
+ changes, the cached instance is evicted and rebuilt with fresh
52
+ configuration.
53
+
54
+ Returns:
55
+ tuple[str, Any] | None: A ``(kind, value)`` tuple where
56
+ ``kind`` is a short string identifying the probe type
57
+ (e.g. ``"file"`` for mtime, ``"db"`` for a row version).
58
+ Returns ``None`` when probing is unsupported or fails — the
59
+ cache treats ``None`` as "still fresh", deferring to the
60
+ TTL safety net.
61
+ """
62
+ return None
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from collections.abc import Callable
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from reflexio.models.config_schema import (
9
+ StorageConfig,
10
+ StorageConfigSQLite,
11
+ )
12
+ from reflexio.server.services.configurator.base_configurator import BaseConfigurator
13
+ from reflexio.server.services.configurator.config_storage import ConfigStorage
14
+ from reflexio.server.services.configurator.local_file_config_storage import (
15
+ LocalFileConfigStorage,
16
+ )
17
+ from reflexio.server.services.storage.sqlite_storage import SQLiteStorage
18
+ from reflexio.server.services.storage.storage_base import BaseStorage
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # Storage factory functions — one per StorageConfig type
25
+ # ---------------------------------------------------------------------------
26
+
27
+
28
+ def _create_sqlite_storage(
29
+ configurator: BaseConfigurator, config: StorageConfigSQLite
30
+ ) -> BaseStorage:
31
+ logger.info("Using SQLite storage for org %s", configurator.org_id)
32
+ full_config = configurator.get_config()
33
+ api_key_config = full_config.api_key_config if full_config else None
34
+ llm_config = full_config.llm_config if full_config else None
35
+ enable_document_expansion = (
36
+ full_config.enable_document_expansion if full_config else False
37
+ )
38
+ # When base_dir is explicitly provided (e.g. tests with temp dirs)
39
+ # and no db_path is configured, use base_dir for the SQLite DB
40
+ # so the storage is isolated from the shared default database.
41
+ db_path = config.db_path
42
+ if db_path is None and configurator.base_dir:
43
+ db_path = str(Path(configurator.base_dir) / "reflexio.db")
44
+ return SQLiteStorage(
45
+ org_id=configurator.org_id,
46
+ db_path=db_path,
47
+ api_key_config=api_key_config,
48
+ llm_config=llm_config,
49
+ enable_document_expansion=enable_document_expansion,
50
+ )
51
+
52
+
53
+ class DefaultConfigurator(BaseConfigurator):
54
+ """OS configurator with LocalJson config storage and SQLite data storage."""
55
+
56
+ _STORAGE_FACTORIES: dict[type[StorageConfig], Callable[..., BaseStorage]] = {
57
+ StorageConfigSQLite: _create_sqlite_storage,
58
+ }
59
+
60
+ _STORAGE_READINESS_CHECKS: dict[type[StorageConfig], Callable[[Any], bool]] = {
61
+ StorageConfigSQLite: lambda _: True, # db_path defaults via env var if None
62
+ }
63
+
64
+ def _select_config_storage(
65
+ self, org_id: str, base_dir: str | None
66
+ ) -> ConfigStorage:
67
+ if base_dir:
68
+ return LocalFileConfigStorage(org_id=org_id, base_dir=base_dir)
69
+ return LocalFileConfigStorage(org_id=org_id)
70
+
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Configurator class registry — allows enterprise to swap in its own class
74
+ # ---------------------------------------------------------------------------
75
+
76
+ _configurator_class: type[BaseConfigurator] = DefaultConfigurator
77
+
78
+
79
+ def set_configurator_class(cls: type[BaseConfigurator]) -> None:
80
+ """Register a configurator class to be used by RequestContext."""
81
+ global _configurator_class # noqa: PLW0603
82
+ _configurator_class = cls
83
+
84
+
85
+ def get_configurator_class() -> type[BaseConfigurator]:
86
+ """Return the currently registered configurator class."""
87
+ return _configurator_class