claude-smart 0.2.45 → 0.2.47

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 (353) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +47 -10
  3. package/bin/claude-smart.js +535 -23
  4. package/package.json +22 -3
  5. package/plugin/.claude-plugin/plugin.json +4 -2
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +27 -6
  8. package/plugin/commands/clear-all.md +1 -0
  9. package/plugin/commands/dashboard.md +1 -0
  10. package/plugin/commands/learn.md +1 -0
  11. package/plugin/commands/restart.md +1 -0
  12. package/plugin/commands/show.md +1 -0
  13. package/plugin/dashboard/app/configure/env/page.tsx +40 -14
  14. package/plugin/dashboard/app/configure/server/page.tsx +51 -1
  15. package/plugin/dashboard/app/layout.tsx +20 -0
  16. package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
  17. package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
  18. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
  19. package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
  20. package/plugin/dashboard/lib/reflexio-client.ts +16 -0
  21. package/plugin/dashboard/lib/status.ts +10 -3
  22. package/plugin/dashboard/lib/types.ts +18 -6
  23. package/plugin/hooks/codex-hooks.json +0 -1
  24. package/plugin/opencode/assistant-buffer.ts +108 -0
  25. package/plugin/opencode/dist/assistant-buffer.js +96 -0
  26. package/plugin/opencode/dist/internal.js +12 -0
  27. package/plugin/opencode/dist/payload.js +85 -0
  28. package/plugin/opencode/dist/server.mjs +232 -0
  29. package/plugin/opencode/internal.ts +18 -0
  30. package/plugin/opencode/package.json +3 -0
  31. package/plugin/opencode/payload.ts +90 -0
  32. package/plugin/opencode/server.mts +251 -0
  33. package/plugin/opencode/tsconfig.json +13 -0
  34. package/plugin/pyproject.toml +25 -4
  35. package/plugin/scripts/_lib.sh +5 -1
  36. package/plugin/scripts/backend-service.sh +22 -2
  37. package/plugin/scripts/ensure-plugin-root.sh +4 -4
  38. package/plugin/scripts/hook_entry.sh +1 -1
  39. package/plugin/scripts/opencode-claude-compat +4 -0
  40. package/plugin/scripts/opencode-claude-compat.cmd +3 -0
  41. package/plugin/scripts/opencode-claude-compat.js +225 -0
  42. package/plugin/scripts/smart-install.sh +17 -9
  43. package/plugin/src/README.md +1 -1
  44. package/plugin/src/claude_smart/cli.py +498 -12
  45. package/plugin/src/claude_smart/context_format.py +9 -9
  46. package/plugin/src/claude_smart/cs_cite.py +66 -19
  47. package/plugin/src/claude_smart/env_config.py +12 -4
  48. package/plugin/src/claude_smart/events/session_start.py +26 -7
  49. package/plugin/src/claude_smart/events/stop.py +1 -1
  50. package/plugin/src/claude_smart/ids.py +1 -1
  51. package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
  52. package/plugin/src/claude_smart/runtime.py +7 -1
  53. package/plugin/uv.lock +5 -5
  54. package/plugin/vendor/reflexio/.env.example +54 -30
  55. package/plugin/vendor/reflexio/README.md +17 -11
  56. package/plugin/vendor/reflexio/pyproject.toml +13 -1
  57. package/plugin/vendor/reflexio/reflexio/README.md +4 -1
  58. package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
  59. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
  60. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
  63. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
  64. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +212 -52
  65. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
  66. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
  67. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
  68. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
  69. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
  70. package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
  71. package/plugin/vendor/reflexio/reflexio/client/client.py +154 -13
  72. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
  73. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
  74. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
  75. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
  145. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +246 -10
  146. package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
  147. package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
  148. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +17 -5
  149. package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
  150. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +224 -21
  151. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
  152. package/plugin/vendor/reflexio/reflexio/lib/_search.py +52 -25
  153. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +11 -1
  154. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
  155. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
  156. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
  157. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
  158. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
  159. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
  160. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
  161. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +97 -3
  162. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
  163. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
  164. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +104 -7
  165. package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
  166. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
  167. package/plugin/vendor/reflexio/reflexio/server/README.md +58 -33
  168. package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
  169. package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
  170. package/plugin/vendor/reflexio/reflexio/server/api.py +899 -103
  171. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
  172. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +4 -2
  173. package/plugin/vendor/reflexio/reflexio/server/auth.py +90 -0
  174. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
  175. package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
  176. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
  177. package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
  178. package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
  179. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
  180. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
  181. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
  182. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
  183. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
  184. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
  185. package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
  186. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
  187. package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
  188. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
  189. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
  190. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
  191. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
  192. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
  193. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
  194. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
  195. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
  196. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
  197. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
  198. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +278 -0
  199. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
  200. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
  201. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
  202. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
  203. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
  204. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
  205. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
  206. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
  207. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
  208. package/plugin/vendor/reflexio/reflexio/server/services/README.md +28 -11
  209. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
  210. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
  211. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
  212. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
  213. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
  214. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
  215. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
  216. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
  217. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
  218. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
  219. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
  220. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
  221. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
  222. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
  223. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
  224. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
  225. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
  226. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
  227. package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
  228. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
  229. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
  230. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
  231. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +47 -16
  232. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
  233. package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
  234. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
  235. package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
  236. package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
  237. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +219 -0
  238. package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
  239. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
  240. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +18 -11
  241. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
  242. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
  243. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +334 -178
  244. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
  245. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
  246. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
  247. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
  248. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
  249. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
  250. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
  251. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
  252. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
  253. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
  254. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
  255. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +31 -22
  256. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
  257. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
  258. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
  259. package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
  260. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
  261. package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
  262. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
  263. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +99 -6
  264. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
  265. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
  266. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
  267. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +31 -0
  268. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
  269. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
  270. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +16 -2
  271. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
  272. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +644 -197
  273. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +106 -119
  274. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
  275. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +630 -0
  276. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
  277. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +31 -1369
  278. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +756 -210
  279. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +148 -34
  280. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
  281. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
  282. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
  283. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +150 -23
  289. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
  290. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +40 -57
  292. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +4 -677
  296. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +134 -1
  297. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +62 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +372 -34
  311. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
  312. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
  313. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
  314. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
  315. package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
  316. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
  317. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
  318. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
  319. package/scripts/setup-claude-smart.sh +8 -3
  320. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
  321. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
  322. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
  323. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
  324. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
  325. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
  326. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
  327. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
  328. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
  329. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
  330. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
  331. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
  332. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
  333. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
  334. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
  335. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
  336. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
  337. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
  338. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
  339. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
  340. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
  341. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
  342. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
  343. package/plugin/vendor/reflexio/reflexio/server/_auth.py +0 -25
  344. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
  345. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
  346. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
  347. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
  348. /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
  349. /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
  350. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
  351. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
  352. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
  353. /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
@@ -0,0 +1,211 @@
1
+ """Recency adjustment helpers for unified search ranking."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import math
7
+ from collections.abc import Mapping
8
+ from dataclasses import dataclass, replace
9
+ from datetime import UTC, datetime
10
+
11
+ from reflexio.server.env_utils import env_str, env_truthy
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ SECONDS_PER_DAY = 24 * 60 * 60
16
+ PLAYBOOK_HALF_LIFE_SECONDS = 90 * SECONDS_PER_DAY
17
+ _PROFILE_TTL_HALF_LIFE_FRACTION = 0.5
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class ScoredItem[T]:
22
+ item: T
23
+ score: float | None
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class RecencyConfig:
28
+ enabled: bool = True
29
+ max_penalty_frac: float = 0.15
30
+ max_penalty_logit: float = 0.2
31
+ pool_size: int = 20
32
+
33
+ @classmethod
34
+ def from_env(cls, *, env: Mapping[str, str] | None = None) -> RecencyConfig:
35
+ return cls(
36
+ enabled=env_truthy(
37
+ env_str("REFLEXIO_SEARCH_RECENCY_ENABLED", "true", env=env)
38
+ ),
39
+ max_penalty_frac=_float_env(
40
+ "REFLEXIO_SEARCH_RECENCY_MAX_PENALTY_FRAC",
41
+ cls.max_penalty_frac,
42
+ env=env,
43
+ minimum=0.0,
44
+ maximum=1.0,
45
+ ),
46
+ max_penalty_logit=_float_env(
47
+ "REFLEXIO_SEARCH_RECENCY_MAX_PENALTY_LOGIT",
48
+ cls.max_penalty_logit,
49
+ env=env,
50
+ minimum=0.0,
51
+ ),
52
+ pool_size=_int_env(
53
+ "REFLEXIO_SEARCH_RECENCY_POOL_SIZE",
54
+ cls.pool_size,
55
+ env=env,
56
+ minimum=1,
57
+ ),
58
+ )
59
+
60
+ def with_overrides(self, values: Mapping[str, object] | None) -> RecencyConfig:
61
+ if not values:
62
+ return self
63
+ updates: dict[str, object] = {}
64
+ if "recency_enabled" in values:
65
+ updates["enabled"] = _bool_value(values["recency_enabled"], self.enabled)
66
+ if "recency_max_penalty_frac" in values:
67
+ updates["max_penalty_frac"] = _float_value(
68
+ values["recency_max_penalty_frac"],
69
+ self.max_penalty_frac,
70
+ minimum=0.0,
71
+ maximum=1.0,
72
+ )
73
+ if "recency_max_penalty_logit" in values:
74
+ updates["max_penalty_logit"] = _float_value(
75
+ values["recency_max_penalty_logit"],
76
+ self.max_penalty_logit,
77
+ minimum=0.0,
78
+ )
79
+ if "recency_pool_size" in values:
80
+ updates["pool_size"] = _int_value(
81
+ values["recency_pool_size"], self.pool_size, minimum=1
82
+ )
83
+ return replace(self, **updates)
84
+
85
+
86
+ def decay_for_item(
87
+ item: object,
88
+ *,
89
+ entity_type: str,
90
+ now: int | None = None,
91
+ ) -> float:
92
+ """Return a freshness decay in [0, 1], where 1 means no penalty."""
93
+ now = now if now is not None else int(datetime.now(UTC).timestamp())
94
+ if entity_type == "profiles":
95
+ return _profile_decay(item, now=now)
96
+ return _timestamp_decay(
97
+ getattr(item, "created_at", None),
98
+ half_life_seconds=PLAYBOOK_HALF_LIFE_SECONDS,
99
+ now=now,
100
+ )
101
+
102
+
103
+ def decay(age_seconds: float, half_life_seconds: float) -> float:
104
+ if half_life_seconds <= 0:
105
+ return 1.0
106
+ age_seconds = max(0.0, age_seconds)
107
+ return math.exp(-age_seconds / (half_life_seconds / math.log(2)))
108
+
109
+
110
+ def multiplicative_factor(decay_value: float, max_penalty_frac: float) -> float:
111
+ return 1.0 - _clamp(max_penalty_frac, 0.0, 1.0) * (1.0 - _decay(decay_value))
112
+
113
+
114
+ def additive_penalty(decay_value: float, max_penalty_logit: float) -> float:
115
+ return max(0.0, max_penalty_logit) * (1.0 - _decay(decay_value))
116
+
117
+
118
+ def _profile_decay(item: object, *, now: int) -> float:
119
+ modified_at = _int_or_none(getattr(item, "last_modified_timestamp", None))
120
+ expires_at = _int_or_none(getattr(item, "expiration_timestamp", None))
121
+ if modified_at is None or expires_at is None or expires_at <= modified_at:
122
+ return 1.0
123
+ half_life = (expires_at - modified_at) * _PROFILE_TTL_HALF_LIFE_FRACTION
124
+ return decay(now - modified_at, half_life)
125
+
126
+
127
+ def _timestamp_decay(value: object, *, half_life_seconds: int, now: int) -> float:
128
+ timestamp = _int_or_none(value)
129
+ if timestamp is None or timestamp <= 0:
130
+ return 1.0
131
+ return decay(now - timestamp, half_life_seconds)
132
+
133
+
134
+ def _float_env(
135
+ name: str,
136
+ default: float,
137
+ *,
138
+ env: Mapping[str, str] | None,
139
+ minimum: float,
140
+ maximum: float | None = None,
141
+ ) -> float:
142
+ return _float_value(
143
+ env_str(name, str(default), env=env),
144
+ default,
145
+ minimum=minimum,
146
+ maximum=maximum,
147
+ )
148
+
149
+
150
+ def _int_env(
151
+ name: str,
152
+ default: int,
153
+ *,
154
+ env: Mapping[str, str] | None,
155
+ minimum: int,
156
+ ) -> int:
157
+ return _int_value(env_str(name, str(default), env=env), default, minimum=minimum)
158
+
159
+
160
+ def _bool_value(value: object, default: bool) -> bool:
161
+ if isinstance(value, bool):
162
+ return value
163
+ if isinstance(value, str) and value.strip():
164
+ return env_truthy(value)
165
+ return default
166
+
167
+
168
+ def _float_value(
169
+ value: object,
170
+ default: float,
171
+ *,
172
+ minimum: float,
173
+ maximum: float | None = None,
174
+ ) -> float:
175
+ try:
176
+ if not isinstance(value, str | int | float):
177
+ raise TypeError
178
+ parsed = float(value)
179
+ except (TypeError, ValueError):
180
+ logger.warning("Invalid recency float override %r; using %.3f", value, default)
181
+ return default
182
+ parsed = max(minimum, parsed)
183
+ if maximum is not None:
184
+ parsed = min(maximum, parsed)
185
+ return parsed
186
+
187
+
188
+ def _int_value(value: object, default: int, *, minimum: int) -> int:
189
+ try:
190
+ if not isinstance(value, str | int):
191
+ raise TypeError
192
+ parsed = int(value)
193
+ except (TypeError, ValueError):
194
+ logger.warning("Invalid recency integer override %r; using %d", value, default)
195
+ return default
196
+ return max(minimum, parsed)
197
+
198
+
199
+ def _int_or_none(value: object) -> int | None:
200
+ try:
201
+ return int(value) # type: ignore[arg-type]
202
+ except (TypeError, ValueError):
203
+ return None
204
+
205
+
206
+ def _decay(value: float) -> float:
207
+ return _clamp(value, 0.0, 1.0)
208
+
209
+
210
+ def _clamp(value: float, low: float, high: float) -> float:
211
+ return min(high, max(low, value))
@@ -9,7 +9,9 @@ never crashes, never silently returns the full unfiltered list.
9
9
  from __future__ import annotations
10
10
 
11
11
  import logging
12
- from collections.abc import Callable
12
+ from collections.abc import Callable, Sequence
13
+ from dataclasses import dataclass
14
+ from typing import Any
13
15
 
14
16
  from reflexio.server.llm.rerank import score_pairs
15
17
  from reflexio.server.llm.rerank.cross_encoder_reranker import (
@@ -20,6 +22,22 @@ from reflexio.server.tracing import profile_step
20
22
  logger = logging.getLogger(__name__)
21
23
 
22
24
 
25
+ @dataclass(frozen=True)
26
+ class RelevanceFloorResult:
27
+ items: list[Any]
28
+ scores: list[float] | None
29
+
30
+
31
+ def _floor_and_sort[T](
32
+ items: list[T], scores: list[float], floor: float
33
+ ) -> list[tuple[T, float]]:
34
+ """Return ``(item, score)`` pairs scoring >= ``floor``, sorted descending."""
35
+ ranked = sorted(
36
+ zip(items, scores, strict=True), key=lambda pair: pair[1], reverse=True
37
+ )
38
+ return [(item, score) for item, score in ranked if score >= floor]
39
+
40
+
23
41
  def apply_relevance_floor[T](
24
42
  query: str,
25
43
  items: list[T],
@@ -62,11 +80,8 @@ def apply_relevance_floor[T](
62
80
  return items[:top_k]
63
81
  span.set_data("available", True)
64
82
 
65
- ranked = sorted(
66
- zip(items, scores, strict=True), key=lambda pair: pair[1], reverse=True
67
- )
68
- survivors = [item for item, score in ranked if score >= floor]
69
- dropped = len(ranked) - len(survivors)
83
+ survivors = [item for item, _score in _floor_and_sort(items, scores, floor)]
84
+ dropped = len(items) - len(survivors)
70
85
  span.set_data("kept", len(survivors))
71
86
  span.set_data("dropped", dropped)
72
87
  if dropped:
@@ -78,3 +93,81 @@ def apply_relevance_floor[T](
78
93
  floor,
79
94
  )
80
95
  return survivors[:top_k]
96
+
97
+
98
+ def apply_relevance_floors(
99
+ query: str,
100
+ arms: Sequence[tuple[str, list[Any], float]],
101
+ top_k: int,
102
+ *,
103
+ content_of: Callable[[Any], str] = lambda item: item.content,
104
+ ) -> list[RelevanceFloorResult]:
105
+ """Floor every arm with a single cross-encoder batch.
106
+
107
+ CPU cross-encoder inference does not parallelize across threads, so
108
+ scoring all arms in one ``score_pairs`` call beats one call per arm:
109
+ the model runs one batched forward pass and per-call overhead is paid
110
+ once.
111
+
112
+ Args:
113
+ query: The search query.
114
+ arms: ``(arm_name, items, floor)`` per arm; items must expose
115
+ ``.content`` or supply ``content_of``.
116
+ top_k: Max items to return per arm after flooring.
117
+ content_of: Extracts the text to score for an item.
118
+
119
+ Returns:
120
+ One result per arm, in input order. Available reranker results are
121
+ sorted by score descending and left uncapped with the paired raw logits.
122
+ On reranker unavailability, every arm returns the original full item
123
+ pool with a ``None`` score sentinel (logged).
124
+ """
125
+ if not any(items for _, items, _ in arms):
126
+ return [RelevanceFloorResult([], []) for _ in arms]
127
+ arm_names = [name for name, _, _ in arms]
128
+ contents: list[str] = []
129
+ for _, items, _ in arms:
130
+ contents.extend(content_of(item) for item in items)
131
+ with profile_step(
132
+ "search.rerank.relevance_floor",
133
+ arm="all",
134
+ arms=arm_names,
135
+ items=len(contents),
136
+ top_k=top_k,
137
+ ) as span:
138
+ try:
139
+ scores = score_pairs(query, contents)
140
+ except CrossEncoderUnavailableError:
141
+ span.set_data("available", False)
142
+ logger.warning(
143
+ "event=relevance_floor_unavailable arms=%s items=%d (returning unfiltered pool)",
144
+ arm_names,
145
+ len(contents),
146
+ )
147
+ return [RelevanceFloorResult(list(items), None) for _, items, _ in arms]
148
+ span.set_data("available", True)
149
+
150
+ results: list[RelevanceFloorResult] = []
151
+ offset = 0
152
+ for name, items, floor in arms:
153
+ arm_scores = scores[offset : offset + len(items)]
154
+ offset += len(items)
155
+ survivors = _floor_and_sort(items, arm_scores, floor)
156
+ dropped = len(items) - len(survivors)
157
+ span.set_data(f"kept_{name}", len(survivors))
158
+ span.set_data(f"dropped_{name}", dropped)
159
+ if dropped:
160
+ logger.info(
161
+ "event=relevance_floor arm=%s kept=%d dropped=%d floor=%.2f",
162
+ name,
163
+ len(survivors),
164
+ dropped,
165
+ floor,
166
+ )
167
+ results.append(
168
+ RelevanceFloorResult(
169
+ [item for item, _score in survivors],
170
+ [score for _item, score in survivors],
171
+ )
172
+ )
173
+ return results
@@ -3,6 +3,8 @@ Utils for service layer
3
3
  """
4
4
 
5
5
  import ast
6
+ import base64
7
+ import binascii
6
8
  import json
7
9
  import logging
8
10
  import os
@@ -45,6 +47,37 @@ _content_token_encoding: tiktoken.Encoding | None = None
45
47
  _INVALID_MAX_TOKENS_WARNED = False
46
48
 
47
49
 
50
+ def _image_mime_type_from_bytes(image_bytes: bytes) -> str | None:
51
+ if image_bytes.startswith(b"\xff\xd8\xff"):
52
+ return "image/jpeg"
53
+ if image_bytes.startswith(b"\x89PNG\r\n\x1a\n"):
54
+ return "image/png"
55
+ if image_bytes.startswith((b"GIF87a", b"GIF89a")):
56
+ return "image/gif"
57
+ if image_bytes.startswith(b"RIFF") and image_bytes[8:12] == b"WEBP":
58
+ return "image/webp"
59
+ return None
60
+
61
+
62
+ def _image_data_url_from_encoding(image_encoding: str) -> str:
63
+ image_encoding = image_encoding.strip()
64
+ if image_encoding.startswith("data:image/"):
65
+ return image_encoding
66
+
67
+ compact_encoding = "".join(image_encoding.split())
68
+ prefix = compact_encoding[:64]
69
+ prefix += "=" * (-len(prefix) % 4)
70
+ try:
71
+ image_header = base64.b64decode(prefix, validate=True)
72
+ except (binascii.Error, ValueError):
73
+ raise ValueError("image_encoding must be valid base64 image data") from None
74
+
75
+ mime_type = _image_mime_type_from_bytes(image_header)
76
+ if mime_type is None:
77
+ raise ValueError("Unsupported image signature in image_encoding")
78
+ return f"data:{mime_type};base64,{compact_encoding}"
79
+
80
+
48
81
  def _get_content_token_encoding() -> tiktoken.Encoding:
49
82
  """Return a process-cached tiktoken encoder for content slicing."""
50
83
  global _content_token_encoding
@@ -500,7 +533,9 @@ def construct_messages_from_interactions(
500
533
  {
501
534
  "type": "image_url",
502
535
  "image_url": {
503
- "url": f"data:image/jpeg;base64,{last_interaction.image_encoding}"
536
+ "url": _image_data_url_from_encoding(
537
+ last_interaction.image_encoding
538
+ )
504
539
  },
505
540
  }
506
541
  )
@@ -0,0 +1,8 @@
1
+ # shadow_comparison
2
+
3
+ Compact LLM-as-judge capability for Reflexio-vs-shadow response comparison.
4
+
5
+ - `judge.py` owns prompt rendering and the LLM call for one interaction.
6
+ - `outcome.py` owns pure position randomization and Reflexio-relative win/loss/tie derivation.
7
+
8
+ This package intentionally does not use `components/`: the current two-file split already separates LLM orchestration from pure outcome logic.
@@ -15,8 +15,7 @@ from reflexio.models.api_schema.eval_overview_schema import ShadowComparisonVerd
15
15
  class Outcome(StrEnum):
16
16
  """Reflexio-relative outcome derived from a judge's position-randomized verdict.
17
17
 
18
- Mirrors the GroupAssignment pattern from F2's group_aggregation module
19
- StrEnum so values serialize transparently to JSON / SQL.
18
+ Uses StrEnum so values serialize transparently to JSON / SQL.
20
19
  """
21
20
 
22
21
  WIN = "win"
@@ -1,3 +1,6 @@
1
+ from typing import Any
2
+
3
+
1
4
  class StorageError(Exception):
2
5
  """
3
6
  Exception raised for storage errors
@@ -9,3 +12,31 @@ class StorageError(Exception):
9
12
 
10
13
  def __str__(self):
11
14
  return f"StorageError: {self.message}"
15
+
16
+
17
+ class SubjectWriteBarrierError(StorageError):
18
+ """Raised when a write targets a subject with an active erasure barrier."""
19
+
20
+
21
+ def require_non_empty_session_id(value: Any) -> str:
22
+ """Return a stripped, non-empty request ``session_id`` or raise ``StorageError``.
23
+
24
+ ``Request.session_id`` is a required non-empty field. Rows persisted before
25
+ the session-id migration may still carry NULL/blank values; surfacing a
26
+ typed storage error (rather than a raw Pydantic ``ValidationError`` deep in
27
+ a read path) tells the operator to run the latest data migrations.
28
+
29
+ Args:
30
+ value (Any): The raw ``session_id`` value read from storage.
31
+
32
+ Returns:
33
+ str: The stripped, non-empty session id.
34
+
35
+ Raises:
36
+ StorageError: If ``value`` is missing or blank.
37
+ """
38
+ if isinstance(value, str) and value.strip():
39
+ return value.strip()
40
+ raise StorageError(
41
+ "requests.session_id is missing or empty; run the latest data migrations"
42
+ )