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,468 @@
1
+ """Finish-tool runner for resumable classic extraction."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from dataclasses import dataclass, replace
7
+ from datetime import UTC, datetime
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from pydantic import BaseModel
11
+
12
+ from reflexio.models.api_schema.internal_schema import RequestInteractionDataModel
13
+ from reflexio.server.llm.litellm_client import LiteLLMClient
14
+ from reflexio.server.llm.model_defaults import ModelRole
15
+ from reflexio.server.llm.tools import Tool, ToolLoopTrace, ToolRegistry, run_tool_loop
16
+ from reflexio.server.services.extraction.agent_run_records import (
17
+ build_extractor_agent_run_record,
18
+ )
19
+ from reflexio.server.services.extraction.pending_tool_call_dispatch import (
20
+ PendingToolCallToolContext,
21
+ create_ask_human_tool,
22
+ create_attach_pending_info_request_tool,
23
+ )
24
+ from reflexio.server.services.extraction.prior_answer_search import (
25
+ append_prior_knowledge_context,
26
+ )
27
+ from reflexio.server.services.storage.storage_base import (
28
+ AgentRunRecord,
29
+ AgentRunStatus,
30
+ BaseStorage,
31
+ PendingToolCallRecord,
32
+ )
33
+ from reflexio.server.site_var.feature_flags import (
34
+ is_resumable_extraction_agent_enabled as is_resumable_extraction_agent_feature_enabled,
35
+ )
36
+ from reflexio.server.usage_metrics import record_usage_event
37
+
38
+ if TYPE_CHECKING:
39
+ from reflexio.server.api_endpoints.request_context import RequestContext
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+ FINISH_EXTRACTION_TOOL_NAME = "finish_extraction"
44
+ PROFILE_EXTRACTOR_KIND = "profile"
45
+
46
+
47
+ def _record_agent_usage_event(
48
+ *,
49
+ run: AgentRunRecord,
50
+ event_name: str,
51
+ outcome: str | None = None,
52
+ error_kind: str | None = None,
53
+ count_value: int = 1,
54
+ metadata: dict[str, Any] | None = None,
55
+ ) -> None:
56
+ record_usage_event(
57
+ org_id=run.binding.org_id,
58
+ event_name=event_name,
59
+ event_category="extraction_agent",
60
+ user_id=run.binding.user_id,
61
+ request_id=run.binding.request_id,
62
+ pipeline=run.binding.extractor_kind,
63
+ source=run.binding.source,
64
+ agent_version=run.binding.agent_version,
65
+ outcome=outcome,
66
+ error_kind=error_kind,
67
+ count_value=count_value,
68
+ metadata={"run_id": run.id, **(metadata or {})},
69
+ )
70
+
71
+
72
+ @dataclass(slots=True)
73
+ class AgentRunResult:
74
+ run_id: str
75
+ output: BaseModel | None
76
+ pending_tool_call_ids: list[str]
77
+ messages: list[dict[str, Any]]
78
+ trace: ToolLoopTrace
79
+ finished_reason: str
80
+
81
+
82
+ @dataclass(slots=True)
83
+ class _FinishExtractionContext:
84
+ output: BaseModel | None = None
85
+
86
+
87
+ @dataclass(slots=True)
88
+ class _ExtractionAgentToolContext:
89
+ finish_context: _FinishExtractionContext
90
+ extra_tool_context: Any | None = None
91
+
92
+
93
+ def _format_resolved_tool_result(record: PendingToolCallRecord) -> str:
94
+ resolved_at = record.resolved_at.isoformat() if record.resolved_at else "unknown"
95
+ return (
96
+ "Resolved tool result for extraction follow-up\n"
97
+ f"Tool: {record.tool_name}\n"
98
+ f"Question: {record.question_text}\n"
99
+ f"Resolved at: {resolved_at}\n"
100
+ f"Result: {record.result or {}}\n\n"
101
+ "Use this Agent Builder feedback only if it is relevant to the "
102
+ "current extraction window. If it adds or corrects durable profile or "
103
+ "playbook information, include that in finish_extraction."
104
+ )
105
+
106
+
107
+ def append_resolved_tool_result_context(
108
+ messages: list[dict[str, Any]],
109
+ resolved_tool_calls: list[PendingToolCallRecord],
110
+ ) -> list[dict[str, Any]]:
111
+ """Append resolved async tool results as user-role Agent Builder context."""
112
+ ordered = sorted(
113
+ resolved_tool_calls,
114
+ key=lambda record: record.resolved_at or datetime.max.replace(tzinfo=UTC),
115
+ )
116
+ return [
117
+ *messages,
118
+ *[
119
+ {"role": "user", "content": _format_resolved_tool_result(record)}
120
+ for record in ordered
121
+ ],
122
+ ]
123
+
124
+
125
+ def _finish_handler(args: BaseModel, ctx: Any) -> dict[str, Any]:
126
+ ctx = getattr(ctx, "finish_context", ctx)
127
+ if not isinstance(ctx, _FinishExtractionContext):
128
+ raise TypeError(f"Expected _FinishExtractionContext, got {type(ctx).__name__}")
129
+ ctx.output = args
130
+ return {"status": "completed"}
131
+
132
+
133
+ def create_finish_extraction_tool(output_schema: type[BaseModel]) -> Tool:
134
+ return Tool(
135
+ name=FINISH_EXTRACTION_TOOL_NAME,
136
+ args_model=output_schema,
137
+ handler=_finish_handler,
138
+ )
139
+
140
+
141
+ def _pending_tool_call_config(request_context: RequestContext) -> Any | None:
142
+ root_config = request_context.configurator.get_config()
143
+ return (
144
+ getattr(root_config, "pending_tool_call_config", None)
145
+ if root_config is not None
146
+ else None
147
+ )
148
+
149
+
150
+ def pending_tool_calls_enabled(request_context: RequestContext) -> bool:
151
+ """Gate whether pending-info tools are offered.
152
+
153
+ This does NOT gate whether the extraction loop runs — the loop is always
154
+ the extraction path. It only governs whether the resumable human-in-the-loop
155
+ and prior-knowledge tools may be registered alongside ``finish_extraction``.
156
+ """
157
+ pending_config = _pending_tool_call_config(request_context)
158
+ return bool(
159
+ pending_config
160
+ and pending_config.enabled
161
+ and is_resumable_extraction_agent_feature_enabled(request_context.org_id)
162
+ and request_context.storage is not None
163
+ )
164
+
165
+
166
+ def create_pending_info_tools_for_extractor_kind(extractor_kind: str) -> list[Tool]:
167
+ """Return pending-info tools available to a given extractor kind."""
168
+ attach_tool = create_attach_pending_info_request_tool()
169
+ if extractor_kind == PROFILE_EXTRACTOR_KIND:
170
+ return [attach_tool]
171
+ return [create_ask_human_tool(), attach_tool]
172
+
173
+
174
+ def run_resumable_extraction_agent(
175
+ *,
176
+ request_context: RequestContext,
177
+ client: LiteLLMClient,
178
+ extractor_kind: str,
179
+ user_id: str | None,
180
+ request_id: str,
181
+ agent_version: str | None,
182
+ source: str | None,
183
+ request_interaction_data_models: list[RequestInteractionDataModel],
184
+ extractor_config: BaseModel,
185
+ service_config: Any,
186
+ agent_context: str,
187
+ messages: list[dict[str, Any]],
188
+ output_schema: type[BaseModel],
189
+ log_label: str,
190
+ ) -> AgentRunResult:
191
+ """Run and finalize a config-gated classic extraction agent pass."""
192
+ pending_config = _pending_tool_call_config(request_context)
193
+ storage = request_context.storage
194
+ if storage is None:
195
+ raise RuntimeError(f"Resumable {extractor_kind} extraction requires storage")
196
+
197
+ pending_tools_active = pending_tool_calls_enabled(request_context)
198
+
199
+ run = build_extractor_agent_run_record(
200
+ org_id=request_context.org_id,
201
+ extractor_kind=extractor_kind,
202
+ user_id=user_id,
203
+ request_id=request_id,
204
+ agent_version=agent_version,
205
+ source=source,
206
+ request_interaction_data_models=request_interaction_data_models,
207
+ extractor_config=extractor_config,
208
+ service_config=service_config,
209
+ agent_context=agent_context,
210
+ )
211
+ extra_tools: list[Tool] = []
212
+ extra_tool_context = None
213
+ if pending_tools_active and pending_config is not None:
214
+ messages = append_prior_knowledge_context(
215
+ messages=messages,
216
+ storage=storage,
217
+ org_id=request_context.org_id,
218
+ extractor_kind=extractor_kind,
219
+ extractor_config=extractor_config,
220
+ source=source,
221
+ agent_version=agent_version,
222
+ similarity_threshold=pending_config.for_tool(
223
+ "attach_pending_info_request"
224
+ if extractor_kind == PROFILE_EXTRACTOR_KIND
225
+ else "ask_human"
226
+ ).similarity_threshold,
227
+ )
228
+ extra_tool_context = PendingToolCallToolContext(
229
+ storage=storage,
230
+ run_id=run.id,
231
+ org_id=request_context.org_id,
232
+ extractor_kind=extractor_kind,
233
+ user_id=user_id,
234
+ config=pending_config,
235
+ )
236
+ extra_tools.extend(create_pending_info_tools_for_extractor_kind(extractor_kind))
237
+
238
+ return ResumableExtractionAgent(client=client, storage=storage).start(
239
+ run=run,
240
+ messages=messages,
241
+ output_schema=output_schema,
242
+ extra_tools=extra_tools,
243
+ extra_tool_context=extra_tool_context,
244
+ log_label=log_label,
245
+ )
246
+
247
+
248
+ class ResumableExtractionAgent:
249
+ """Run a classic extractor prompt through a durable finish-tool loop."""
250
+
251
+ def __init__(
252
+ self,
253
+ *,
254
+ client: LiteLLMClient,
255
+ storage: BaseStorage,
256
+ max_steps: int = 8,
257
+ model_role: ModelRole = ModelRole.EXTRACTION_AGENT,
258
+ ) -> None:
259
+ self.client = client
260
+ self.storage = storage
261
+ self.max_steps = max_steps
262
+ self.model_role = model_role
263
+
264
+ def start(
265
+ self,
266
+ *,
267
+ run: AgentRunRecord,
268
+ messages: list[dict[str, Any]],
269
+ output_schema: type[BaseModel],
270
+ extra_tools: list[Tool] | None = None,
271
+ extra_tool_context: Any | None = None,
272
+ log_label: str | None = None,
273
+ ) -> AgentRunResult:
274
+ """Create the run row, execute the tool loop, and store completed output."""
275
+ run = replace(
276
+ run,
277
+ max_steps_remaining=(
278
+ self.max_steps
279
+ if run.max_steps_remaining is None
280
+ else min(run.max_steps_remaining, self.max_steps)
281
+ ),
282
+ )
283
+ self.storage.create_agent_run(run)
284
+ logger.info(
285
+ "event=extraction_agent_started org_id=%s user_id=%s extractor_kind=%s "
286
+ "run_id=%s request_id=%s",
287
+ run.binding.org_id,
288
+ run.binding.user_id,
289
+ run.binding.extractor_kind,
290
+ run.id,
291
+ run.binding.request_id,
292
+ )
293
+ _record_agent_usage_event(run=run, event_name="extraction_agent_started")
294
+ return self._run(
295
+ run=run,
296
+ messages=messages,
297
+ output_schema=output_schema,
298
+ extra_tools=extra_tools,
299
+ extra_tool_context=extra_tool_context,
300
+ log_label=log_label,
301
+ )
302
+
303
+ def resume(
304
+ self,
305
+ *,
306
+ run: AgentRunRecord,
307
+ messages: list[dict[str, Any]],
308
+ output_schema: type[BaseModel],
309
+ resolved_tool_calls: list[PendingToolCallRecord],
310
+ extra_tools: list[Tool] | None = None,
311
+ extra_tool_context: Any | None = None,
312
+ log_label: str | None = None,
313
+ ) -> AgentRunResult:
314
+ """Resume a claimed run with resolved async tool results in context."""
315
+ logger.info(
316
+ "event=extraction_agent_resumed org_id=%s user_id=%s extractor_kind=%s "
317
+ "run_id=%s request_id=%s resolved_tool_calls=%d",
318
+ run.binding.org_id,
319
+ run.binding.user_id,
320
+ run.binding.extractor_kind,
321
+ run.id,
322
+ run.binding.request_id,
323
+ len(resolved_tool_calls),
324
+ )
325
+ _record_agent_usage_event(
326
+ run=run,
327
+ event_name="extraction_agent_resumed",
328
+ count_value=len(resolved_tool_calls),
329
+ metadata={"resolved_tool_calls": len(resolved_tool_calls)},
330
+ )
331
+ resumed_messages = append_resolved_tool_result_context(
332
+ messages,
333
+ resolved_tool_calls,
334
+ )
335
+ return self._run(
336
+ run=run,
337
+ messages=resumed_messages,
338
+ output_schema=output_schema,
339
+ extra_tools=extra_tools,
340
+ extra_tool_context=extra_tool_context,
341
+ log_label=log_label,
342
+ )
343
+
344
+ def _run(
345
+ self,
346
+ *,
347
+ run: AgentRunRecord,
348
+ messages: list[dict[str, Any]],
349
+ output_schema: type[BaseModel],
350
+ extra_tools: list[Tool] | None = None,
351
+ extra_tool_context: Any | None = None,
352
+ log_label: str | None = None,
353
+ ) -> AgentRunResult:
354
+ finish_ctx = _FinishExtractionContext()
355
+ ctx: Any = (
356
+ _ExtractionAgentToolContext(
357
+ finish_context=finish_ctx,
358
+ extra_tool_context=extra_tool_context,
359
+ )
360
+ if extra_tool_context is not None
361
+ else finish_ctx
362
+ )
363
+ registry = ToolRegistry(
364
+ [*(extra_tools or []), create_finish_extraction_tool(output_schema)]
365
+ )
366
+ max_steps = self.max_steps
367
+ if run.max_steps_remaining is not None:
368
+ max_steps = min(max_steps, max(0, run.max_steps_remaining))
369
+
370
+ # When only finish_extraction is registered (no human/prior-knowledge
371
+ # tools), force that tool so the degenerate one-call loop is strictly
372
+ # equivalent to the old single-shot response_format extraction —
373
+ # the model cannot return a no-tool turn and yield empty output.
374
+ #
375
+ # When the async-info tools are also registered we cannot force a single
376
+ # tool (the model must be free to pick ask_human vs finish_extraction),
377
+ # but we still require *some* tool call via "required". This prevents a
378
+ # weak tool-caller (e.g. MiniMax) from emitting the answer as plain text
379
+ # with zero tool_calls, which the loop would otherwise treat as a no-op
380
+ # finish and drop the output.
381
+ tool_choice: str | dict[str, Any] = (
382
+ {
383
+ "type": "function",
384
+ "function": {"name": FINISH_EXTRACTION_TOOL_NAME},
385
+ }
386
+ if not extra_tools
387
+ else "required"
388
+ )
389
+
390
+ result = run_tool_loop(
391
+ client=self.client,
392
+ messages=messages,
393
+ registry=registry,
394
+ model_role=self.model_role,
395
+ max_steps=max_steps,
396
+ ctx=ctx,
397
+ finish_tool_name=FINISH_EXTRACTION_TOOL_NAME,
398
+ tool_choice=tool_choice,
399
+ log_label=log_label,
400
+ )
401
+
402
+ committed_output = (
403
+ finish_ctx.output.model_dump() if finish_ctx.output is not None else None
404
+ )
405
+ if result.finished_reason == "finish_tool" and committed_output is not None:
406
+ self.storage.update_agent_run_status(
407
+ run.id,
408
+ AgentRunStatus.AGENT_COMPLETED,
409
+ committed_output=committed_output,
410
+ pending_tool_call_ids=result.pending_tool_call_ids,
411
+ max_steps_remaining=result.max_steps_remaining,
412
+ )
413
+ logger.info(
414
+ "event=extraction_agent_finished org_id=%s user_id=%s "
415
+ "extractor_kind=%s run_id=%s request_id=%s "
416
+ "pending_tool_calls=%d",
417
+ run.binding.org_id,
418
+ run.binding.user_id,
419
+ run.binding.extractor_kind,
420
+ run.id,
421
+ run.binding.request_id,
422
+ len(result.pending_tool_call_ids),
423
+ )
424
+ _record_agent_usage_event(
425
+ run=run,
426
+ event_name="extraction_agent_finished",
427
+ outcome="completed",
428
+ metadata={
429
+ "pending_tool_calls": len(result.pending_tool_call_ids),
430
+ "finished_reason": result.finished_reason,
431
+ },
432
+ )
433
+ else:
434
+ last_error = f"Extraction agent did not finish: {result.finished_reason}"
435
+ self.storage.update_agent_run_status(
436
+ run.id,
437
+ AgentRunStatus.FAILED,
438
+ max_steps_remaining=result.max_steps_remaining,
439
+ last_error=last_error,
440
+ )
441
+ logger.warning(
442
+ "event=extraction_agent_failed org_id=%s user_id=%s "
443
+ "extractor_kind=%s run_id=%s request_id=%s "
444
+ "finished_reason=%s has_output=%s",
445
+ run.binding.org_id,
446
+ run.binding.user_id,
447
+ run.binding.extractor_kind,
448
+ run.id,
449
+ run.binding.request_id,
450
+ result.finished_reason,
451
+ finish_ctx.output is not None,
452
+ )
453
+ _record_agent_usage_event(
454
+ run=run,
455
+ event_name="extraction_agent_failed",
456
+ outcome="failed",
457
+ error_kind=result.finished_reason,
458
+ metadata={"has_output": finish_ctx.output is not None},
459
+ )
460
+
461
+ return AgentRunResult(
462
+ run_id=run.id,
463
+ output=finish_ctx.output,
464
+ pending_tool_call_ids=result.pending_tool_call_ids,
465
+ messages=result.messages,
466
+ trace=result.trace,
467
+ finished_reason=result.finished_reason,
468
+ )
@@ -0,0 +1,171 @@
1
+ """Process-local scheduler for resumable extraction follow-up work.
2
+
3
+ The scheduler is intentionally multi-tenant: each tick it discovers every org
4
+ that has actionable resumable-extraction work (a run ready to resume, a run
5
+ awaiting finalization retry, or a pending tool call due to expire) and drives a
6
+ per-org :class:`ExtractionResumeWorker` for each. Worker claims are org-scoped,
7
+ so a worker only ever resumes runs belonging to the org context it was built
8
+ with — never another tenant's runs.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import threading
15
+ from collections.abc import Callable
16
+ from datetime import UTC, datetime
17
+
18
+ from reflexio.server.api_endpoints.request_context import RequestContext
19
+ from reflexio.server.services.extraction.resumable_agent import (
20
+ pending_tool_calls_enabled,
21
+ )
22
+ from reflexio.server.services.extraction.resume_worker import ExtractionResumeWorker
23
+ from reflexio.server.tracing import sentry_tags
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ _DEFAULT_POLL_INTERVAL_SECONDS = 5.0
28
+
29
+
30
+ class ExtractionResumeScheduler:
31
+ """Small polling wrapper that drives :class:`ExtractionResumeWorker` per org."""
32
+
33
+ def __init__(
34
+ self,
35
+ *,
36
+ request_context_factory: Callable[[str], RequestContext],
37
+ bootstrap_org_id: str,
38
+ max_runs_per_tick: int = 10,
39
+ ) -> None:
40
+ self.request_context_factory = request_context_factory
41
+ self.bootstrap_org_id = bootstrap_org_id
42
+ self.max_runs_per_tick = max_runs_per_tick
43
+ self._stop_event = threading.Event()
44
+ self._thread: threading.Thread | None = None
45
+
46
+ def start(self) -> None:
47
+ if self._thread is not None and self._thread.is_alive():
48
+ return
49
+ self._stop_event.clear()
50
+ self._thread = threading.Thread(
51
+ target=self._run_loop,
52
+ name="reflexio-extraction-resume-scheduler",
53
+ daemon=True,
54
+ )
55
+ self._thread.start()
56
+ logger.info("event=extraction_resume_scheduler_started")
57
+
58
+ def stop(self, *, timeout_seconds: float = 5.0) -> None:
59
+ self._stop_event.set()
60
+ if self._thread is not None:
61
+ self._thread.join(timeout=timeout_seconds)
62
+ self._thread = None
63
+ logger.info("event=extraction_resume_scheduler_stopped")
64
+
65
+ def _discover_org_ids(self, bootstrap_ctx: RequestContext) -> list[str]:
66
+ """Return every org with actionable work, always including the bootstrap org."""
67
+ org_ids: list[str] = []
68
+ storage = getattr(bootstrap_ctx, "storage", None)
69
+ if storage is not None:
70
+ try:
71
+ org_ids = storage.list_resumable_work_org_ids(now=datetime.now(UTC))
72
+ except NotImplementedError:
73
+ org_ids = []
74
+ # Always sweep the bootstrap org so the maintenance loop runs even when
75
+ # the cross-org discovery query is empty or unsupported.
76
+ if bootstrap_ctx.org_id not in org_ids:
77
+ org_ids = [bootstrap_ctx.org_id, *org_ids]
78
+ return org_ids
79
+
80
+ def _expire_pending_tool_calls(self, bootstrap_ctx: RequestContext) -> None:
81
+ storage = getattr(bootstrap_ctx, "storage", None)
82
+ if storage is None:
83
+ return
84
+ # ``expire_pending_tool_calls`` is not org-scoped, so a single call
85
+ # sweeps every tenant's overdue pending rows for this tick.
86
+ try:
87
+ expired = storage.expire_pending_tool_calls(now=datetime.now(UTC))
88
+ except NotImplementedError:
89
+ return
90
+ if expired:
91
+ logger.info("event=pending_tool_calls_expired expired=%d", expired)
92
+
93
+ def _drain_org(self, org_id: str) -> None:
94
+ try:
95
+ ctx = self.request_context_factory(org_id)
96
+ if not pending_tool_calls_enabled(ctx):
97
+ return
98
+ resumed = ExtractionResumeWorker(request_context=ctx).drain(
99
+ max_runs=self.max_runs_per_tick
100
+ )
101
+ if resumed:
102
+ logger.info(
103
+ "event=extraction_resume_scheduler_tick org_id=%s resumed=%d",
104
+ org_id,
105
+ resumed,
106
+ )
107
+ except Exception as exc:
108
+ with sentry_tags(
109
+ subsystem="extraction",
110
+ op="scheduler_org_drain",
111
+ org_id=org_id,
112
+ error_type=type(exc).__name__,
113
+ ):
114
+ logger.exception(
115
+ "event=extraction_resume_scheduler_org_failed org_id=%s",
116
+ org_id,
117
+ )
118
+
119
+ def _run_loop(self) -> None:
120
+ while not self._stop_event.is_set():
121
+ poll_interval = _DEFAULT_POLL_INTERVAL_SECONDS
122
+ try:
123
+ bootstrap_ctx = self.request_context_factory(self.bootstrap_org_id)
124
+ config = bootstrap_ctx.configurator.get_config()
125
+ poll_interval = (
126
+ config.pending_tool_call_config.resume_poll_interval_seconds
127
+ )
128
+ self._expire_pending_tool_calls(bootstrap_ctx)
129
+ for org_id in self._discover_org_ids(bootstrap_ctx):
130
+ if self._stop_event.is_set():
131
+ break
132
+ self._drain_org(org_id)
133
+ except Exception as exc:
134
+ with sentry_tags(
135
+ subsystem="extraction",
136
+ op="scheduler_tick",
137
+ error_type=type(exc).__name__,
138
+ ):
139
+ logger.exception("event=extraction_resume_scheduler_tick_failed")
140
+ self._stop_event.wait(poll_interval)
141
+
142
+
143
+ def maybe_start_resume_scheduler(
144
+ request_context_factory: Callable[[str], RequestContext],
145
+ *,
146
+ bootstrap_org_id: str,
147
+ ) -> ExtractionResumeScheduler | None:
148
+ """Start the scheduler only when the bootstrap-org config enables the feature.
149
+
150
+ Args:
151
+ request_context_factory: Builds an org-scoped :class:`RequestContext`.
152
+ bootstrap_org_id: Org used to read config and to seed cross-org discovery.
153
+ """
154
+ try:
155
+ ctx = request_context_factory(bootstrap_org_id)
156
+ if not pending_tool_calls_enabled(ctx):
157
+ return None
158
+ except Exception as exc:
159
+ logger.warning(
160
+ "event=extraction_resume_scheduler_start_skipped error_type=%s error=%s",
161
+ type(exc).__name__,
162
+ exc,
163
+ )
164
+ return None
165
+
166
+ scheduler = ExtractionResumeScheduler(
167
+ request_context_factory=request_context_factory,
168
+ bootstrap_org_id=bootstrap_org_id,
169
+ )
170
+ scheduler.start()
171
+ return scheduler