claude-smart 0.2.41 → 0.2.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/README.md +1 -1
  3. package/bin/claude-smart.js +86 -48
  4. package/package.json +10 -3
  5. package/plugin/.claude-plugin/plugin.json +9 -3
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +2 -2
  8. package/plugin/dashboard/next.config.ts +9 -1
  9. package/plugin/pyproject.toml +2 -2
  10. package/plugin/scripts/_lib.sh +91 -0
  11. package/plugin/scripts/backend-service.sh +46 -15
  12. package/plugin/scripts/cli.sh +29 -1
  13. package/plugin/scripts/codex-hook.js +72 -4
  14. package/plugin/scripts/dashboard-build.sh +1 -0
  15. package/plugin/scripts/dashboard-service.sh +1 -0
  16. package/plugin/scripts/ensure-plugin-root.sh +7 -14
  17. package/plugin/scripts/hook_entry.sh +1 -0
  18. package/plugin/scripts/smart-install.sh +18 -2
  19. package/plugin/src/claude_smart/cli.py +72 -38
  20. package/plugin/src/claude_smart/context_format.py +11 -12
  21. package/plugin/src/claude_smart/cs_cite.py +26 -12
  22. package/plugin/src/claude_smart/ids.py +13 -5
  23. package/plugin/uv.lock +1 -1
  24. package/plugin/vendor/reflexio/.env.example +53 -0
  25. package/plugin/vendor/reflexio/LICENSE +201 -0
  26. package/plugin/vendor/reflexio/README.md +338 -0
  27. package/plugin/vendor/reflexio/pyproject.toml +271 -0
  28. package/plugin/vendor/reflexio/reflexio/README.md +184 -0
  29. package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
  30. package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
  31. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
  32. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
  33. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
  34. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
  35. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
  36. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
  37. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
  38. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
  39. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
  40. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
  41. package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
  42. package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
  43. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
  44. package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
  45. package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
  46. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
  47. package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
  48. package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
  49. package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
  50. package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
  51. package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
  52. package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
  53. package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
  54. package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
  63. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
  64. package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
  65. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
  66. package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
  67. package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
  68. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
  69. package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
  70. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
  71. package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
  72. package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
  73. package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
  74. package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
  75. package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
  145. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
  146. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
  147. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
  148. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
  149. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
  150. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
  151. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
  152. package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
  153. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
  154. package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
  155. package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
  156. package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
  157. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
  158. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
  159. package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
  160. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
  161. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
  162. package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
  163. package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
  164. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
  165. package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
  166. package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
  167. package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
  168. package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
  169. package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
  170. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
  171. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
  172. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
  173. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
  174. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
  175. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
  176. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
  177. package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
  178. package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
  179. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
  180. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
  181. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
  182. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
  183. package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
  184. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
  185. package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
  186. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
  187. package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
  188. package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
  189. package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
  190. package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
  191. package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -0
  192. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
  193. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
  194. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
  195. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
  196. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +540 -0
  197. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
  198. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
  199. package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
  200. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
  201. package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
  202. package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
  203. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +110 -0
  204. package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
  205. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1595 -0
  206. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
  207. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -0
  208. package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
  209. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
  210. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
  211. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +210 -0
  212. package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
  213. package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +255 -0
  214. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
  215. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +177 -0
  216. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
  217. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +699 -0
  218. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
  219. package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
  220. package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
  221. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
  222. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
  223. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
  224. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
  225. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
  226. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
  227. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
  228. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
  229. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
  230. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
  231. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
  232. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
  233. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
  234. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
  235. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
  236. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
  237. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
  238. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
  239. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
  240. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
  241. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
  242. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
  243. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
  244. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
  245. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
  246. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
  247. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
  248. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
  249. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
  250. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
  251. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
  252. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
  253. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
  254. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
  255. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
  256. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
  257. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
  258. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
  259. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
  260. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
  261. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
  262. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
  263. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
  264. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
  265. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
  266. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
  267. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
  268. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
  269. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
  270. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
  271. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
  272. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
  273. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
  274. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
  275. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
  276. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
  277. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
  278. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
  279. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
  281. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
  282. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
  283. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
  289. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
  290. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
  292. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
  296. package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
  297. package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
  311. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
  312. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
  313. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
  318. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
  319. package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
  320. package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
  321. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
  322. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
  323. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
  324. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
  325. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
  326. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
  327. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
  328. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
  329. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
  330. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
  331. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
  332. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
  333. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
  334. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
  335. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
  336. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
  337. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
  338. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
  339. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
  340. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
  341. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
  342. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
  343. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
  344. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
  345. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
  346. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
  347. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
  348. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
  349. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
  350. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
  351. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
  352. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
  353. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
  354. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
  355. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
  356. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
  357. package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
  358. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
  359. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
  360. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
  361. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
  362. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
  363. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
  364. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
  365. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
  366. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
  367. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
  368. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
  369. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
  370. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
  371. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
  372. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
  373. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
  374. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
  375. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
  376. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
  377. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
  378. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
  379. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
  380. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
  381. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
  382. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
  383. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
  384. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
  385. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
  386. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
  387. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
  388. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
  389. package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
  390. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
  391. package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
  392. package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
  393. package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
  394. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
  395. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
  396. package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
@@ -0,0 +1,908 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from enum import IntEnum, StrEnum
5
+ from typing import Any, Literal, Self
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
8
+
9
+ from .api_schema.validators import (
10
+ NonEmptyStr,
11
+ SafeHttpUrl,
12
+ SanitizedNonEmptyStr,
13
+ )
14
+
15
+ # Embedding vector dimensions. Changing this requires a DB migration and re-embedding,
16
+ # so it is intentionally a constant rather than a configurable setting.
17
+ EMBEDDING_DIMENSIONS = 512
18
+
19
+ # Default sliding window parameters for extraction
20
+ DEFAULT_WINDOW_SIZE = 10
21
+ DEFAULT_STRIDE_SIZE = 5
22
+
23
+ # Deprecated aliases kept for older imports.
24
+ DEFAULT_BATCH_SIZE = DEFAULT_WINDOW_SIZE
25
+ DEFAULT_BATCH_INTERVAL = DEFAULT_STRIDE_SIZE
26
+
27
+
28
+ class ExtractionPreset(StrEnum):
29
+ """Named extraction presets that bundle window_size and stride_size.
30
+
31
+ Each preset targets a specific conversation pattern:
32
+ - quick_chat: Short conversations (support bots, quick Q&A)
33
+ - standard: General-purpose conversational agents (default)
34
+ - long_form: Long conversations (coding assistants, research)
35
+ - high_volume: High-traffic agents (1000+ daily interactions)
36
+ """
37
+
38
+ QUICK_CHAT = "quick_chat"
39
+ STANDARD = "standard"
40
+ LONG_FORM = "long_form"
41
+ HIGH_VOLUME = "high_volume"
42
+
43
+
44
+ # Preset parameter values: (window_size, stride_size)
45
+ _PRESET_VALUES: dict[ExtractionPreset, tuple[int, int]] = {
46
+ ExtractionPreset.QUICK_CHAT: (5, 3),
47
+ ExtractionPreset.STANDARD: (DEFAULT_WINDOW_SIZE, DEFAULT_STRIDE_SIZE),
48
+ ExtractionPreset.LONG_FORM: (25, 10),
49
+ ExtractionPreset.HIGH_VOLUME: (15, 8),
50
+ }
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+ # Field migration maps (old stored JSON name → new Python attr name)
55
+ # ---------------------------------------------------------------------------
56
+ _CONFIG_FIELD_MIGRATION: dict[str, str] = {
57
+ "batch_size": "window_size",
58
+ "batch_interval": "stride_size",
59
+ "extraction_window_size": "window_size",
60
+ "extraction_window_stride": "stride_size",
61
+ }
62
+
63
+ _AGGREGATOR_FIELD_MIGRATION: dict[str, str] = {
64
+ "min_feedback_threshold": "min_cluster_size",
65
+ "refresh_count": "reaggregation_trigger_count",
66
+ "similarity_threshold": "clustering_similarity",
67
+ }
68
+
69
+ _EXTRACTOR_OVERRIDE_MIGRATION: dict[str, str] = {
70
+ "batch_size_override": "window_size_override",
71
+ "batch_interval_override": "stride_size_override",
72
+ "extraction_window_size_override": "window_size_override",
73
+ "extraction_window_stride_override": "stride_size_override",
74
+ }
75
+
76
+ _PROFILE_CONFIG_FIELD_MIGRATION: dict[str, str] = {
77
+ "profile_content_definition_prompt": "extraction_definition_prompt",
78
+ }
79
+
80
+ _PLAYBOOK_CONFIG_FIELD_MIGRATION: dict[str, str] = {
81
+ "feedback_definition_prompt": "extraction_definition_prompt",
82
+ "playbook_definition_prompt": "extraction_definition_prompt",
83
+ "feedback_aggregator_config": "aggregation_config",
84
+ "playbook_aggregator_config": "aggregation_config",
85
+ "playbook_name": "extractor_name",
86
+ "feedback_name": "extractor_name",
87
+ }
88
+
89
+
90
+ def _migrate_dict(data: Any, mapping: dict[str, str]) -> Any:
91
+ """Rename old field names to new ones in a raw dict before Pydantic validates.
92
+
93
+ Creates a shallow copy to avoid mutating the caller's dict.
94
+ """
95
+ if isinstance(data, dict):
96
+ data = dict(data)
97
+ for old, new in mapping.items():
98
+ if old in data and new not in data:
99
+ data[new] = data.pop(old)
100
+ return data
101
+
102
+
103
+ # Retired list-valued config fields and the singular field that replaced them.
104
+ # The first configured entry wins when an old list contains multiple items.
105
+ _LEGACY_SINGLE_CONFIG_FIELDS: tuple[tuple[str, str], ...] = (
106
+ ("profile_extractor_configs", "profile_extractor_config"),
107
+ ("user_playbook_extractor_configs", "user_playbook_extractor_config"),
108
+ ("playbook_configs", "user_playbook_extractor_config"),
109
+ ("agent_feedback_configs", "user_playbook_extractor_config"),
110
+ ("agent_success_configs", "agent_success_config"),
111
+ )
112
+
113
+
114
+ def _first_config_entry(value: Any) -> Any:
115
+ if isinstance(value, list):
116
+ return value[0] if value else None
117
+ return value
118
+
119
+
120
+ def normalize_legacy_config_shape(data: dict[str, Any]) -> dict[str, Any]:
121
+ """Map retired list-valued config fields onto current singular fields.
122
+
123
+ This is a stored-data upgrade path applied at storage load boundaries: any
124
+ config persisted before the single-extractor refactor still carries list
125
+ keys (e.g. ``agent_success_configs``) that ``Config`` would otherwise drop
126
+ as unknown fields, silently losing the user's customization. Legacy keys are
127
+ removed from the returned payload and the first configured entry wins.
128
+
129
+ Returns a shallow copy; the caller's dict is not mutated.
130
+ """
131
+ normalized = dict(data)
132
+ for legacy_field, current_field in _LEGACY_SINGLE_CONFIG_FIELDS:
133
+ if legacy_field not in normalized:
134
+ continue
135
+ if current_field not in normalized:
136
+ normalized[current_field] = _first_config_entry(normalized[legacy_field])
137
+ del normalized[legacy_field]
138
+ return normalized
139
+
140
+
141
+ class _ExtractorWindowOverrideCompatMixin:
142
+ @property
143
+ def batch_size_override(self) -> int | None:
144
+ """Deprecated alias for window_size_override."""
145
+ return self.window_size_override # type: ignore[attr-defined]
146
+
147
+ @batch_size_override.setter
148
+ def batch_size_override(self, value: int | None) -> None:
149
+ self.window_size_override = value # type: ignore[attr-defined]
150
+
151
+ @property
152
+ def batch_interval_override(self) -> int | None:
153
+ """Deprecated alias for stride_size_override."""
154
+ return self.stride_size_override # type: ignore[attr-defined]
155
+
156
+ @batch_interval_override.setter
157
+ def batch_interval_override(self, value: int | None) -> None:
158
+ self.stride_size_override = value # type: ignore[attr-defined]
159
+
160
+
161
+ class SearchMode(StrEnum):
162
+ """Search mode for hybrid search functionality.
163
+
164
+ Controls how search queries are processed:
165
+ - VECTOR: Pure vector similarity search using embeddings
166
+ - FTS: Pure full-text search using PostgreSQL tsvector
167
+ - HYBRID: Combined search using Reciprocal Rank Fusion (RRF)
168
+ """
169
+
170
+ VECTOR = "vector"
171
+ FTS = "fts"
172
+ HYBRID = "hybrid"
173
+
174
+
175
+ @dataclass
176
+ class SearchOptions:
177
+ """Engine-level search parameters that are pre-computed or not part of the API request."""
178
+
179
+ query_embedding: list[float] | None = field(default=None)
180
+ search_mode: SearchMode = field(default=SearchMode.HYBRID)
181
+ fresh: bool = field(default=False)
182
+ rrf_k: int = field(default=60)
183
+ vector_weight: float = field(default=1.0)
184
+ fts_weight: float = field(default=1.0)
185
+
186
+
187
+ class StorageConfigTest(IntEnum):
188
+ UNKNOWN = 0
189
+ INCOMPLETE = 1
190
+ FAILED = 2
191
+ SUCCEEDED = 3
192
+
193
+
194
+ class StorageConfigSQLite(BaseModel):
195
+ """SQLite storage configuration."""
196
+
197
+ db_path: str | None = None # None = use LOCAL_STORAGE_PATH env var default
198
+
199
+
200
+ class StorageConfigSupabase(BaseModel):
201
+ model_config = ConfigDict(populate_by_name=True, serialize_by_alias=True)
202
+
203
+ url: NonEmptyStr
204
+ key: NonEmptyStr
205
+ db_url: NonEmptyStr
206
+ schema_name: str | None = Field(default=None, alias="schema")
207
+ read_url: NonEmptyStr | None = None
208
+ read_key: NonEmptyStr | None = None
209
+
210
+
211
+ class StorageConfigPostgres(BaseModel):
212
+ model_config = ConfigDict(populate_by_name=True, serialize_by_alias=True)
213
+
214
+ storage_type: Literal["postgres"] = Field(default="postgres", alias="type")
215
+ db_url: NonEmptyStr
216
+ schema_name: str | None = Field(default=None, alias="schema")
217
+ pool_size: int = Field(default=10, ge=1)
218
+ # Seconds a query waits for a free pooled connection before failing. Bounds the
219
+ # back-pressure applied when concurrent queries exceed pool_size.
220
+ pool_acquire_timeout: float = Field(default=30.0, gt=0)
221
+ read_db_url: NonEmptyStr | None = None
222
+ read_pool_size: int | None = Field(default=None, ge=1)
223
+ read_pool_acquire_timeout: float | None = Field(default=None, gt=0)
224
+
225
+
226
+ class StorageConfigManagedSupabase(BaseModel):
227
+ """Redacted API response for platform-managed Supabase storage."""
228
+
229
+ managed_by: Literal["platform"]
230
+ schema_present: bool = True
231
+
232
+
233
+ StorageConfig = (
234
+ StorageConfigSQLite
235
+ | StorageConfigSupabase
236
+ | StorageConfigPostgres
237
+ | StorageConfigManagedSupabase
238
+ | None
239
+ )
240
+
241
+
242
+ class AzureOpenAIConfig(BaseModel):
243
+ """Azure OpenAI specific configuration."""
244
+
245
+ api_key: NonEmptyStr
246
+ endpoint: SafeHttpUrl # e.g., "https://your-resource.openai.azure.com/"
247
+ api_version: str = "2024-02-15-preview"
248
+ deployment_name: str | None = None # Optional, can be specified per request
249
+
250
+
251
+ class OpenAIConfig(BaseModel):
252
+ """OpenAI API configuration (direct or Azure)."""
253
+
254
+ api_key: str | None = None # Direct OpenAI API key
255
+ azure_config: AzureOpenAIConfig | None = None # Azure OpenAI configuration
256
+
257
+ @model_validator(mode="after")
258
+ def check_at_least_one_auth(self) -> Self:
259
+ """Validate that at least one of api_key or azure_config is provided."""
260
+ if self.api_key is not None and not self.api_key.strip():
261
+ self.api_key = None
262
+ if not self.api_key and not self.azure_config:
263
+ raise ValueError(
264
+ "At least one of 'api_key' or 'azure_config' must be provided"
265
+ )
266
+ return self
267
+
268
+
269
+ class AnthropicConfig(BaseModel):
270
+ """Anthropic API configuration."""
271
+
272
+ api_key: NonEmptyStr
273
+
274
+
275
+ class OpenRouterConfig(BaseModel):
276
+ """OpenRouter API configuration."""
277
+
278
+ api_key: NonEmptyStr
279
+
280
+
281
+ class GeminiConfig(BaseModel):
282
+ """Google Gemini API configuration."""
283
+
284
+ api_key: NonEmptyStr
285
+
286
+
287
+ class MiniMaxConfig(BaseModel):
288
+ """MiniMax API configuration."""
289
+
290
+ api_key: NonEmptyStr
291
+
292
+
293
+ class DeepSeekConfig(BaseModel):
294
+ """DeepSeek API configuration."""
295
+
296
+ api_key: NonEmptyStr
297
+
298
+
299
+ class DashScopeConfig(BaseModel):
300
+ """Alibaba DashScope (Qwen) API configuration."""
301
+
302
+ api_key: NonEmptyStr
303
+ api_base: str | None = None # None = default; set for intl vs China endpoint
304
+
305
+
306
+ class ZAIConfig(BaseModel):
307
+ """Zhipu AI (GLM) API configuration."""
308
+
309
+ api_key: NonEmptyStr
310
+
311
+
312
+ class MoonshotConfig(BaseModel):
313
+ """Moonshot (Kimi) API configuration."""
314
+
315
+ api_key: NonEmptyStr
316
+
317
+
318
+ class XAIConfig(BaseModel):
319
+ """xAI (Grok) API configuration."""
320
+
321
+ api_key: NonEmptyStr
322
+
323
+
324
+ class CustomEndpointConfig(BaseModel):
325
+ """Custom OpenAI-compatible endpoint configuration.
326
+
327
+ Args:
328
+ model (str): Model name to use (e.g., 'openai/mistral', 'mistral'). Passed as-is to LiteLLM.
329
+ api_key (str): API key for the custom endpoint.
330
+ api_base (SafeHttpUrl): Base URL of the custom endpoint (e.g., 'http://localhost:8000/v1').
331
+ Validated against SSRF: always blocks cloud metadata endpoints;
332
+ blocks private IPs when REFLEXIO_BLOCK_PRIVATE_URLS=true.
333
+ """
334
+
335
+ model: NonEmptyStr
336
+ api_key: NonEmptyStr
337
+ api_base: SafeHttpUrl
338
+
339
+
340
+ class APIKeyConfig(BaseModel):
341
+ """
342
+ API key configuration for LLM providers.
343
+
344
+ Supports OpenAI (direct and Azure), Anthropic, OpenRouter, Google Gemini, MiniMax,
345
+ DeepSeek, DashScope (Qwen), Zhipu AI (GLM), Moonshot (Kimi), xAI (Grok), and custom
346
+ OpenAI-compatible endpoints. When custom_endpoint is configured with non-empty fields,
347
+ it takes priority over all other providers for LLM completion calls (but not embeddings).
348
+ """
349
+
350
+ custom_endpoint: CustomEndpointConfig | None = None
351
+ openai: OpenAIConfig | None = None
352
+ anthropic: AnthropicConfig | None = None
353
+ openrouter: OpenRouterConfig | None = None
354
+ gemini: GeminiConfig | None = None
355
+ minimax: MiniMaxConfig | None = None
356
+ deepseek: DeepSeekConfig | None = None
357
+ dashscope: DashScopeConfig | None = None
358
+ zai: ZAIConfig | None = None
359
+ moonshot: MoonshotConfig | None = None
360
+ xai: XAIConfig | None = None
361
+
362
+
363
+ class DeduplicationConfig(BaseModel):
364
+ """Configuration for playbook deduplication search parameters.
365
+
366
+ Controls the hybrid search behavior when looking for existing playbooks
367
+ to deduplicate against.
368
+
369
+ Args:
370
+ search_threshold: Minimum similarity score for search results (0.0-1.0).
371
+ search_top_k: Maximum number of existing playbooks to retrieve per new playbook.
372
+ max_unified_content_chars: Soft cap on a unified playbook's content length.
373
+ """
374
+
375
+ search_threshold: float = Field(
376
+ default=0.4,
377
+ ge=0.0,
378
+ le=1.0,
379
+ description="Minimum similarity score for deduplication search results.",
380
+ )
381
+ search_top_k: int = Field(
382
+ default=5,
383
+ ge=1,
384
+ description="Maximum number of existing playbooks to retrieve per new playbook.",
385
+ )
386
+ max_unified_content_chars: int = Field(
387
+ default=1200,
388
+ gt=0,
389
+ description=(
390
+ "Soft cap on a unified playbook's content length; unify that would "
391
+ "exceed it should prefer differentiate/keep-separate. Enforced as a "
392
+ "warning-only backstop in the consolidator apply path."
393
+ ),
394
+ )
395
+
396
+
397
+ SINGLETON_PROFILE_EXTRACTOR_NAME = "profile"
398
+ SINGLETON_USER_PLAYBOOK_NAME = "playbook"
399
+ SINGLETON_AGENT_SUCCESS_EVALUATION_NAME = "agent_success"
400
+
401
+
402
+ class ProfileExtractorConfig(_ExtractorWindowOverrideCompatMixin, BaseModel):
403
+ # Deprecated: kept for back-compat with stored configs. Extraction is singleton
404
+ # (one profile extractor per org), so the name is accepted but ignored.
405
+ extractor_name: NonEmptyStr | None = None
406
+ extraction_definition_prompt: SanitizedNonEmptyStr
407
+ context_prompt: str | None = None
408
+ metadata_definition_prompt: str | None = None
409
+ should_extract_profile_prompt_override: str | None = None
410
+ request_sources_enabled: list[str] | None = (
411
+ None # default enabled for all sources, if set, only extract profiles from the enabled request sources
412
+ )
413
+ manual_trigger: bool = False # require manual triggering (rerun) to run extraction and skip auto extraction if set to True
414
+ window_size_override: int | None = Field(default=None, gt=0)
415
+ stride_size_override: int | None = Field(default=None, gt=0)
416
+
417
+ @model_validator(mode="before")
418
+ @classmethod
419
+ def _migrate_field_names(cls, data: Any) -> Any:
420
+ data = _migrate_dict(data, _PROFILE_CONFIG_FIELD_MIGRATION)
421
+ return _migrate_dict(data, _EXTRACTOR_OVERRIDE_MIGRATION)
422
+
423
+
424
+ class PlaybookAggregatorConfig(BaseModel):
425
+ min_cluster_size: int = Field(default=2, ge=1)
426
+ reaggregation_trigger_count: int = Field(default=2, ge=1)
427
+ clustering_similarity: float = Field(
428
+ default=0.3,
429
+ ge=0.0,
430
+ le=1.0,
431
+ description=(
432
+ "Cosine similarity threshold for clustering. Higher = tighter clusters. "
433
+ "Default 0.3 is a compromise that works for both cloud embeddings "
434
+ "(OpenAI text-embedding-3-*, Gemini) and the local zero-padded "
435
+ "MiniLM-L6-v2 embedder. Cloud embeddings typically tolerate 0.4-0.6; "
436
+ "the local embedder's 384-dim vectors zero-padded to 512 produce "
437
+ "lower cosine similarities and need ~0.15-0.3 to cluster at all."
438
+ ),
439
+ )
440
+ direction_overlap_threshold: float = Field(
441
+ default=0.6,
442
+ ge=0.0,
443
+ le=1.0,
444
+ description="Token overlap threshold for grouping playbooks by direction.",
445
+ )
446
+
447
+ @model_validator(mode="before")
448
+ @classmethod
449
+ def _migrate_field_names(cls, data: Any) -> Any:
450
+ return _migrate_dict(data, _AGGREGATOR_FIELD_MIGRATION)
451
+
452
+
453
+ class UserPlaybookExtractorConfig(_ExtractorWindowOverrideCompatMixin, BaseModel):
454
+ # Deprecated: kept for back-compat with stored configs. Extraction is singleton
455
+ # (one playbook extractor per org), so the name is accepted but ignored.
456
+ extractor_name: NonEmptyStr | None = None
457
+ extraction_definition_prompt: SanitizedNonEmptyStr
458
+ context_prompt: str | None = None
459
+ metadata_definition_prompt: str | None = None
460
+ aggregation_config: PlaybookAggregatorConfig | None = None
461
+ deduplication_config: DeduplicationConfig | None = None
462
+ request_sources_enabled: list[str] | None = (
463
+ None # default enabled for all sources, if set, only extract user playbooks from the enabled request sources
464
+ )
465
+ window_size_override: int | None = Field(default=None, gt=0)
466
+ stride_size_override: int | None = Field(default=None, gt=0)
467
+
468
+ @model_validator(mode="before")
469
+ @classmethod
470
+ def _migrate_field_names(cls, data: Any) -> Any:
471
+ data = _migrate_dict(data, _PLAYBOOK_CONFIG_FIELD_MIGRATION)
472
+ return _migrate_dict(data, _EXTRACTOR_OVERRIDE_MIGRATION)
473
+
474
+
475
+ # Backward-compatible alias (deprecated — use UserPlaybookExtractorConfig)
476
+ PlaybookConfig = UserPlaybookExtractorConfig
477
+
478
+
479
+ class ToolUseConfig(BaseModel):
480
+ tool_name: NonEmptyStr
481
+ tool_description: NonEmptyStr
482
+
483
+
484
+ # define what success looks like for agent
485
+ class AgentSuccessConfig(_ExtractorWindowOverrideCompatMixin, BaseModel):
486
+ # Deprecated: kept for back-compat with stored configs. Evaluation is singleton
487
+ # (one agent-success evaluator per org), so the name is accepted but ignored.
488
+ evaluation_name: NonEmptyStr | None = None
489
+ success_definition_prompt: SanitizedNonEmptyStr
490
+ metadata_definition_prompt: str | None = None
491
+ sampling_rate: float = Field(
492
+ default=1.0, ge=0.0, le=1.0
493
+ ) # fraction of window of interactions to be sampled for success evaluation
494
+ window_size_override: int | None = Field(default=None, gt=0)
495
+ stride_size_override: int | None = Field(default=None, gt=0)
496
+
497
+ @model_validator(mode="before")
498
+ @classmethod
499
+ def _migrate_field_names(cls, data: Any) -> Any:
500
+ return _migrate_dict(data, _EXTRACTOR_OVERRIDE_MIGRATION)
501
+
502
+
503
+ class ReflectionConfig(BaseModel):
504
+ """Configuration for the sliding-window reflection step.
505
+
506
+ Reflection runs inside ``GenerationService.run`` as its own
507
+ sliding-window step (window = global ``window_size``, stride = global
508
+ ``stride_size``, bookmark via ``OperationStateManager``). When
509
+ the gate opens and at least one Assistant interaction in the window
510
+ cites a current user playbook / user profile row, the LLM is asked
511
+ whether any cited rows should be replaced. When ``enabled`` is
512
+ False the step short-circuits.
513
+
514
+ Args:
515
+ enabled (bool): Master switch. When False, no LLM call is made.
516
+ model (str | None): Optional model name override. Falls back to
517
+ ``LLMConfig.generation_model_name`` and then the site
518
+ default for ``ModelRole.GENERATION`` when None.
519
+ post_horizon_size (int): Minimum interactions after a citation before
520
+ reflection judges it with full confidence. Citations near the recent
521
+ edge of the window with fewer than this many follow-up turns get a
522
+ 'last_chance' judgment with the prompt biased toward no_change.
523
+ Set to 0 to disable the filter (legacy behavior).
524
+ max_revisions_per_pass (int): Cap on the number of revision decisions
525
+ applied in a single reflection pass (regularization). Once the cap
526
+ is hit, remaining revision-intent decisions are skipped and counted
527
+ in ``ReflectionResult.capped_count``.
528
+ """
529
+
530
+ enabled: bool = True
531
+ model: str | None = None
532
+ post_horizon_size: int = Field(
533
+ default=3,
534
+ description=(
535
+ "Minimum interactions after a citation before reflection judges "
536
+ "it with full confidence. Citations near the recent edge of the "
537
+ "window with fewer than this many follow-up turns get a "
538
+ "'last_chance' judgment with the prompt biased toward no_change. "
539
+ "Set to 0 to disable the filter (legacy behavior)."
540
+ ),
541
+ ge=0,
542
+ )
543
+ max_revisions_per_pass: int = Field(
544
+ default=8,
545
+ gt=0,
546
+ description=(
547
+ "Cap on revision decisions applied per reflection pass "
548
+ "(regularization; excess are skipped)."
549
+ ),
550
+ )
551
+
552
+
553
+ class RetrievalFloorConfig(BaseModel):
554
+ """Read-path relevance floor: drop search results below a per-arm cross-encoder score.
555
+
556
+ Floors are RAW cross-encoder logits (ms-marco-MiniLM), not probabilities. On this
557
+ corpus strongly relevant items score roughly 0..-3, weak/marginal items -3..-5,
558
+ and clear junk -6..-11. A default of -3 keeps strong matches while dropping the
559
+ weak tail that drives false-positive citations. Calibrate per arm on real data.
560
+ """
561
+
562
+ enabled: bool = True
563
+ pool_size: int = Field(
564
+ default=30,
565
+ gt=0,
566
+ description="Candidates fetched per arm before flooring + cap to top_k.",
567
+ )
568
+ profile_floor: float = -3.0
569
+ user_playbook_floor: float = -3.0
570
+ agent_playbook_floor: float = -3.0
571
+
572
+
573
+ class PlaybookOptimizerConfig(BaseModel):
574
+ """Configuration for GEPA-backed playbook content optimization.
575
+
576
+ The optimizer is opt-in (``enabled=False`` by default) and requires
577
+ *exactly one* assistant backend to actually do anything. The two
578
+ backends are mutually exclusive — see ``check_single_assistant_backend``
579
+ below.
580
+ """
581
+
582
+ # --- gating ------------------------------------------------------------
583
+ enabled: bool = False
584
+ optimize_agent_playbooks: bool = False
585
+ optimize_user_playbooks: bool = False
586
+ auto_update_pending_agent_playbooks: bool = True
587
+ auto_update_user_playbooks: bool = False
588
+
589
+ # --- GEPA budget -------------------------------------------------------
590
+ max_metric_calls: int = Field(default=20, gt=0)
591
+ max_turns: int = Field(default=4, gt=0)
592
+ early_stop_score: float = Field(default=0.9, ge=0.0, le=1.0)
593
+ reflection_minibatch_size: int = Field(default=2, gt=0)
594
+ max_validation_windows: int = Field(default=2, gt=0)
595
+ min_commit_windows: int = Field(default=2, gt=0)
596
+ min_commit_score: float = Field(default=0.75, ge=0.0, le=1.0)
597
+ min_commit_likert: int = Field(default=4, ge=1, le=5)
598
+ use_merge: bool = True
599
+ max_merge_invocations: int = Field(default=5, ge=0)
600
+ reflection_model: str | None = None
601
+
602
+ # --- assistant backend: webhook ---------------------------------------
603
+ webhook_url: str | None = None
604
+ webhook_auth_header: str | None = None
605
+ # The webhook_* timeout/retry/backoff fields apply to BOTH backends —
606
+ # the prefix is preserved purely to avoid a config-schema migration.
607
+ webhook_timeout_seconds: int = Field(default=60, gt=0)
608
+ webhook_max_retries: int = Field(default=3, ge=0)
609
+ webhook_backoff_base_seconds: float = Field(default=1.0, ge=0.0)
610
+
611
+ # --- assistant backend: local script ----------------------------------
612
+ # Absolute path to the executable. The optimizer spawns
613
+ # [assistant_script_path, *assistant_script_args] per turn, hands the
614
+ # rollout payload over stdin, and reads {"content": "..."} from stdout.
615
+ # See playbook_optimizer/assistant_webhook.py::LocalScriptAssistant.
616
+ assistant_script_path: str | None = None
617
+ assistant_script_args: list[str] = Field(default_factory=list)
618
+
619
+ # --- scheduler --------------------------------------------------------
620
+ scheduler_jitter_seconds: float = Field(default=1.0, ge=0.0)
621
+ cooldown_after_aborts_seconds: int = Field(default=3600, ge=0)
622
+ abort_cooldown_threshold: int = Field(default=2, ge=1)
623
+
624
+ @model_validator(mode="after")
625
+ def check_single_assistant_backend(self) -> Self:
626
+ # Two backends configured at once would create ambiguous behavior
627
+ # (which one wins?), so reject at load time rather than picking one
628
+ # silently in _create_assistant.
629
+ if self.webhook_url and self.assistant_script_path:
630
+ raise ValueError(
631
+ "Configure only one playbook optimizer assistant backend: "
632
+ "webhook_url or assistant_script_path"
633
+ )
634
+ return self
635
+
636
+
637
+ @dataclass(frozen=True)
638
+ class EffectivePendingToolCallConfig:
639
+ """Resolved pending-tool-call settings after applying tool overrides."""
640
+
641
+ max_pending_followups_per_scope: int
642
+ pending_ttl_seconds: int
643
+ dedup_cache_seconds: int
644
+ prior_answer_valid_seconds: int
645
+ similarity_threshold: float
646
+
647
+
648
+ class PendingToolCallToolOverrideConfig(BaseModel):
649
+ """Optional per-tool pending-call limits."""
650
+
651
+ max_pending_followups_per_scope: int | None = Field(default=None, gt=0)
652
+ pending_ttl_seconds: int | None = Field(default=None, gt=0)
653
+ dedup_cache_seconds: int | None = Field(default=None, gt=0)
654
+ prior_answer_valid_seconds: int | None = Field(default=None, gt=0)
655
+ similarity_threshold: float | None = Field(default=None, ge=0.0, le=1.0)
656
+
657
+
658
+ class PendingToolCallConfig(BaseModel):
659
+ """Configuration for non-blocking pending tool calls."""
660
+
661
+ enabled: bool = False
662
+ max_pending_followups_per_scope: int = Field(default=10, gt=0)
663
+ pending_ttl_seconds: int = Field(default=86_400, gt=0)
664
+ dedup_cache_seconds: int = Field(default=300, gt=0)
665
+ prior_answer_valid_seconds: int = Field(default=2_592_000, gt=0)
666
+ similarity_threshold: float = Field(default=0.0, ge=0.0, le=1.0)
667
+ resume_poll_interval_seconds: float = Field(default=5.0, gt=0)
668
+ resume_claim_ttl_seconds: int = Field(default=600, gt=0)
669
+ max_resume_attempts: int = Field(default=3, ge=0)
670
+ max_finalization_attempts: int = Field(default=3, ge=0)
671
+ hmac_secrets: list[str] = Field(default_factory=list)
672
+ tool_overrides: dict[str, PendingToolCallToolOverrideConfig] = Field(
673
+ default_factory=dict
674
+ )
675
+
676
+ def for_tool(self, tool_name: str) -> EffectivePendingToolCallConfig:
677
+ """Return base settings with an optional exact tool-name override."""
678
+ override = self.tool_overrides.get(tool_name)
679
+
680
+ def _value(name: str) -> Any:
681
+ if override is not None:
682
+ override_value = getattr(override, name)
683
+ if override_value is not None:
684
+ return override_value
685
+ return getattr(self, name)
686
+
687
+ return EffectivePendingToolCallConfig(
688
+ max_pending_followups_per_scope=_value("max_pending_followups_per_scope"),
689
+ pending_ttl_seconds=_value("pending_ttl_seconds"),
690
+ dedup_cache_seconds=_value("dedup_cache_seconds"),
691
+ prior_answer_valid_seconds=_value("prior_answer_valid_seconds"),
692
+ similarity_threshold=_value("similarity_threshold"),
693
+ )
694
+
695
+
696
+ class LLMConfig(BaseModel):
697
+ """
698
+ LLM model configuration overrides.
699
+
700
+ These settings override the default model names from llm_model_setting.json site variable.
701
+ If a field is None, the default from site variable is used.
702
+ """
703
+
704
+ should_run_model_name: str | None = None # Model for "should run extraction" checks
705
+ generation_model_name: str | None = (
706
+ None # Model for generation and evaluation tasks
707
+ )
708
+ embedding_model_name: str | None = None # Model for embedding generation
709
+ pre_retrieval_model_name: str | None = (
710
+ None # Model for pre-retrieval query reformulation
711
+ )
712
+
713
+
714
+ def _default_profile_extractor_config() -> ProfileExtractorConfig:
715
+ return ProfileExtractorConfig(
716
+ extraction_definition_prompt=(
717
+ "Extract key information about the user and their working "
718
+ "environment: name, role, preferences, and stable facts the "
719
+ "agent needs to know to serve the user correctly — including "
720
+ "data/schema details (table names, column types, units, join "
721
+ "paths), metric definitions the user enforces, and tool "
722
+ "quirks or workarounds the user relies on. Do NOT extract "
723
+ "behavioral rules for the agent (those belong in the "
724
+ "playbook extractor)."
725
+ ),
726
+ )
727
+
728
+
729
+ def _default_user_playbook_extractor_config() -> UserPlaybookExtractorConfig:
730
+ return UserPlaybookExtractorConfig(
731
+ 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.",
732
+ )
733
+
734
+
735
+ class Config(BaseModel):
736
+ # define where user configuration is stored at
737
+ storage_config: StorageConfig
738
+ storage_config_test: StorageConfigTest | None = StorageConfigTest.UNKNOWN
739
+ # define agent working environment, tool can use and action space
740
+ agent_context_prompt: str | None = None
741
+ # tools agent can use (shared across success evaluation and playbook extraction)
742
+ tool_can_use: list[ToolUseConfig] | None = None
743
+ # user level memory
744
+ profile_extractor_config: ProfileExtractorConfig | None = Field(
745
+ default_factory=_default_profile_extractor_config
746
+ )
747
+ # user playbook extraction
748
+ user_playbook_extractor_config: UserPlaybookExtractorConfig | None = Field(
749
+ default_factory=_default_user_playbook_extractor_config
750
+ )
751
+ # agent level success
752
+ agent_success_config: AgentSuccessConfig | None = None
753
+ # extraction preset — selects bundled window_size/stride_size values
754
+ extraction_preset: ExtractionPreset | None = None
755
+ # extraction parameters
756
+ window_size: int = Field(default=DEFAULT_WINDOW_SIZE, gt=0)
757
+ stride_size: int = Field(default=DEFAULT_STRIDE_SIZE, gt=0)
758
+ # API key configuration for LLM providers
759
+ api_key_config: APIKeyConfig | None = None
760
+ # LLM model configuration overrides
761
+ llm_config: LLMConfig | None = None
762
+ # Post-publish reflection service configuration
763
+ reflection_config: ReflectionConfig = Field(default_factory=ReflectionConfig)
764
+ # Read-path relevance floor (per-arm cross-encoder score cutoff)
765
+ retrieval_floor: RetrievalFloorConfig = Field(default_factory=RetrievalFloorConfig)
766
+ # Optional GEPA-backed playbook content optimizer
767
+ playbook_optimizer_config: PlaybookOptimizerConfig = Field(
768
+ default_factory=PlaybookOptimizerConfig
769
+ )
770
+ # Optional non-blocking async information tools for classic extraction.
771
+ pending_tool_call_config: PendingToolCallConfig = Field(
772
+ default_factory=PendingToolCallConfig
773
+ )
774
+ # Skip the LLM pre-extraction eligibility check (always run extraction)
775
+ skip_should_run_check: bool = False
776
+ # Enable storage-time document expansion for improved FTS recall
777
+ enable_document_expansion: bool = False
778
+ # Whether this org has opted into shadow-mode runs. Drives /healthz/eval
779
+ # liveness derivation and the /api/get_evaluation_overview hero state
780
+ # machine. When True, each publish optionally schedules a parallel
781
+ # "without Reflexio" generation for side-by-side comparison.
782
+ shadow_mode_enabled: bool = False
783
+ eval_sample_n_per_stratum: int = Field(
784
+ default=200,
785
+ gt=0,
786
+ description=(
787
+ "F3: stratified-sample cap per (day × group) stratum in the regen "
788
+ "pipeline. Strata with fewer items are kept whole. Predictable cost "
789
+ "regardless of traffic volume."
790
+ ),
791
+ )
792
+ eval_concurrency_limit: int = Field(
793
+ default=10,
794
+ gt=0,
795
+ description=(
796
+ "F3: max simultaneous LLM judge calls in flight per regen job, "
797
+ "enforced via a ThreadPoolExecutor. Bound to respect provider "
798
+ "rate limits."
799
+ ),
800
+ )
801
+ shadow_comparison_judge_prompt_version: NonEmptyStr = Field(
802
+ default="v1.0.0",
803
+ description=(
804
+ "F1: pinned judge prompt version for per-turn shadow comparison. "
805
+ "Verdicts are stored with the version that produced them; the "
806
+ "dashboard filters to this org's current pinned version so a "
807
+ "future rubric bump doesn't silently mix epochs into the headline."
808
+ ),
809
+ )
810
+
811
+ @model_validator(mode="before")
812
+ @classmethod
813
+ def _migrate_field_names(cls, data: Any) -> Any:
814
+ """Rename old field names from stored JSON to current names.
815
+
816
+ Also strips None values for fields that have non-optional defaults,
817
+ so rows missing these columns fall back to defaults instead of
818
+ failing validation.
819
+ """
820
+ data = _migrate_dict(data, _CONFIG_FIELD_MIGRATION)
821
+ if isinstance(data, dict):
822
+ for key in (
823
+ "window_size",
824
+ "stride_size",
825
+ "reflection_config",
826
+ "playbook_optimizer_config",
827
+ "pending_tool_call_config",
828
+ "retrieval_floor",
829
+ ):
830
+ if key in data and data[key] is None:
831
+ del data[key]
832
+ return data
833
+
834
+ @model_validator(mode="after")
835
+ def apply_extraction_preset(self) -> Self:
836
+ """Apply preset values when window_size/stride_size are at defaults.
837
+
838
+ If a preset is selected but the user also explicitly set window_size or
839
+ stride_size, the explicit values win (checked via model_fields_set).
840
+ """
841
+ if self.extraction_preset is None:
842
+ return self
843
+
844
+ preset_values = _PRESET_VALUES.get(self.extraction_preset)
845
+ if preset_values is None:
846
+ return self
847
+
848
+ preset_window_size, preset_stride_size = preset_values
849
+ if "window_size" not in self.model_fields_set:
850
+ self.window_size = preset_window_size
851
+ if "stride_size" not in self.model_fields_set:
852
+ self.stride_size = preset_stride_size
853
+
854
+ return self
855
+
856
+ @model_validator(mode="after")
857
+ def check_stride_size_le_window_size(self) -> Self:
858
+ """Validate that stride_size <= window_size."""
859
+ if self.stride_size > self.window_size:
860
+ raise ValueError("stride_size must be <= window_size")
861
+ return self
862
+
863
+ @model_validator(mode="after")
864
+ def check_pending_tool_calls_storage_backend(self) -> Self:
865
+ """Pending tool calls require a database-backed storage backend.
866
+
867
+ ``storage_config is None`` is allowed: in enterprise deployments storage
868
+ is configured centrally (via ``REFLEXIO_STORAGE``) and the per-org config
869
+ blob carries ``None`` rather than a concrete backend. The only removed
870
+ non-database backend (``disk``) is no longer representable as a
871
+ ``StorageConfig``, so a ``None`` here always denotes a deployment-managed
872
+ database backend (sqlite/supabase/postgres).
873
+ """
874
+ if (
875
+ self.pending_tool_call_config.enabled
876
+ and self.storage_config is not None
877
+ and not isinstance(
878
+ self.storage_config,
879
+ (
880
+ StorageConfigSQLite,
881
+ StorageConfigSupabase,
882
+ StorageConfigPostgres,
883
+ StorageConfigManagedSupabase,
884
+ ),
885
+ )
886
+ ):
887
+ raise ValueError(
888
+ "pending_tool_call_config.enabled requires sqlite, supabase, or postgres storage"
889
+ )
890
+ return self
891
+
892
+ @property
893
+ def batch_size(self) -> int:
894
+ """Deprecated alias for window_size."""
895
+ return self.window_size
896
+
897
+ @batch_size.setter
898
+ def batch_size(self, value: int) -> None:
899
+ self.window_size = value
900
+
901
+ @property
902
+ def batch_interval(self) -> int:
903
+ """Deprecated alias for stride_size."""
904
+ return self.stride_size
905
+
906
+ @batch_interval.setter
907
+ def batch_interval(self, value: int) -> None:
908
+ self.stride_size = value