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.
- package/.claude-plugin/marketplace.json +17 -0
- package/README.md +1 -1
- package/bin/claude-smart.js +86 -48
- package/package.json +10 -3
- package/plugin/.claude-plugin/plugin.json +9 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +2 -2
- package/plugin/dashboard/next.config.ts +9 -1
- package/plugin/pyproject.toml +2 -2
- package/plugin/scripts/_lib.sh +91 -0
- package/plugin/scripts/backend-service.sh +46 -15
- package/plugin/scripts/cli.sh +29 -1
- package/plugin/scripts/codex-hook.js +72 -4
- package/plugin/scripts/dashboard-build.sh +1 -0
- package/plugin/scripts/dashboard-service.sh +1 -0
- package/plugin/scripts/ensure-plugin-root.sh +7 -14
- package/plugin/scripts/hook_entry.sh +1 -0
- package/plugin/scripts/smart-install.sh +18 -2
- package/plugin/src/claude_smart/cli.py +72 -38
- package/plugin/src/claude_smart/context_format.py +11 -12
- package/plugin/src/claude_smart/cs_cite.py +26 -12
- package/plugin/src/claude_smart/ids.py +13 -5
- package/plugin/uv.lock +1 -1
- package/plugin/vendor/reflexio/.env.example +53 -0
- package/plugin/vendor/reflexio/LICENSE +201 -0
- package/plugin/vendor/reflexio/README.md +338 -0
- package/plugin/vendor/reflexio/pyproject.toml +271 -0
- package/plugin/vendor/reflexio/reflexio/README.md +184 -0
- package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
- package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
- package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
- package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
- package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
- package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
- package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
- package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
- package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
- package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
- package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
- package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
- package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
- package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
- package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
- package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
- package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
- package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
- package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
- package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
- package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +540 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
- package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +110 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1595 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +255 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +177 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +699 -0
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
- package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
- package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
- package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified search service that searches across all entity types in parallel.
|
|
3
|
+
|
|
4
|
+
Executes in two phases:
|
|
5
|
+
Phase A: Query reformulation + embedding generation (sequential)
|
|
6
|
+
Phase B: Entity searches across profiles, agent playbooks, user playbooks (parallel)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import contextvars
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
from collections import OrderedDict
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
|
19
|
+
from concurrent.futures import TimeoutError as FuturesTimeoutError
|
|
20
|
+
from typing import TYPE_CHECKING, Any
|
|
21
|
+
|
|
22
|
+
from reflexio.models.api_schema.retriever_schema import (
|
|
23
|
+
ConversationTurn,
|
|
24
|
+
SearchAgentPlaybookRequest,
|
|
25
|
+
SearchUserPlaybookRequest,
|
|
26
|
+
SearchUserProfileRequest,
|
|
27
|
+
UnifiedSearchRequest,
|
|
28
|
+
UnifiedSearchResponse,
|
|
29
|
+
)
|
|
30
|
+
from reflexio.models.api_schema.service_schemas import (
|
|
31
|
+
AgentPlaybook,
|
|
32
|
+
PlaybookStatus,
|
|
33
|
+
UserPlaybook,
|
|
34
|
+
UserProfile,
|
|
35
|
+
)
|
|
36
|
+
from reflexio.models.config_schema import (
|
|
37
|
+
RetrievalFloorConfig,
|
|
38
|
+
SearchMode,
|
|
39
|
+
SearchOptions,
|
|
40
|
+
)
|
|
41
|
+
from reflexio.server.llm.litellm_client import LiteLLMClient
|
|
42
|
+
from reflexio.server.prompt.prompt_manager import PromptManager
|
|
43
|
+
from reflexio.server.services.pre_retrieval import QueryReformulator
|
|
44
|
+
from reflexio.server.services.retrieval.relevance_floor import apply_relevance_floor
|
|
45
|
+
from reflexio.server.services.storage.storage_base import BaseStorage
|
|
46
|
+
from reflexio.server.tracing import profile_step, set_span_data
|
|
47
|
+
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from reflexio.server.api_endpoints.request_context import RequestContext
|
|
50
|
+
|
|
51
|
+
logger = logging.getLogger(__name__)
|
|
52
|
+
_DEFAULT_ENTITY_TYPES = frozenset({"profiles", "agent_playbooks", "user_playbooks"})
|
|
53
|
+
# Statuses returned for agent_playbooks when the caller does not pass an
|
|
54
|
+
# explicit ``agent_playbook_status_filter``. Excludes REJECTED so that a
|
|
55
|
+
# rejection in the dashboard immediately suppresses the playbook from search
|
|
56
|
+
# results — every consumer benefits without opting in. Callers that genuinely
|
|
57
|
+
# want REJECTED items (e.g. admin views) must pass the full list explicitly.
|
|
58
|
+
_DEFAULT_AGENT_PLAYBOOK_STATUSES: tuple[PlaybookStatus, ...] = (
|
|
59
|
+
PlaybookStatus.APPROVED,
|
|
60
|
+
PlaybookStatus.PENDING,
|
|
61
|
+
)
|
|
62
|
+
_SEARCH_FANOUT_MAX_WORKERS = max(
|
|
63
|
+
1, int(os.getenv("REFLEXIO_SEARCH_FANOUT_WORKERS", "16") or "16")
|
|
64
|
+
)
|
|
65
|
+
_SEARCH_FANOUT_EXECUTOR = ThreadPoolExecutor(
|
|
66
|
+
max_workers=_SEARCH_FANOUT_MAX_WORKERS,
|
|
67
|
+
thread_name_prefix="reflexio-search",
|
|
68
|
+
)
|
|
69
|
+
_EMBEDDING_CACHE_TTL_SECONDS = max(
|
|
70
|
+
0, int(os.getenv("REFLEXIO_QUERY_EMBEDDING_CACHE_TTL_SECONDS", "300") or "300")
|
|
71
|
+
)
|
|
72
|
+
_EMBEDDING_CACHE_MAX_SIZE = max(
|
|
73
|
+
1, int(os.getenv("REFLEXIO_QUERY_EMBEDDING_CACHE_MAX_SIZE", "1024") or "1024")
|
|
74
|
+
)
|
|
75
|
+
_embedding_cache_lock = threading.Lock()
|
|
76
|
+
_embedding_cache: OrderedDict[tuple[str, int, str, str], tuple[float, list[float]]] = (
|
|
77
|
+
OrderedDict()
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def run_unified_search(
|
|
82
|
+
request: UnifiedSearchRequest,
|
|
83
|
+
org_id: str,
|
|
84
|
+
storage: BaseStorage,
|
|
85
|
+
llm_client: LiteLLMClient,
|
|
86
|
+
prompt_manager: PromptManager,
|
|
87
|
+
pre_retrieval_model_name: str | None = None,
|
|
88
|
+
retrieval_floor: RetrievalFloorConfig | None = None,
|
|
89
|
+
) -> UnifiedSearchResponse:
|
|
90
|
+
"""
|
|
91
|
+
Search across all entity types (profiles, agent playbooks, user playbooks) in parallel.
|
|
92
|
+
|
|
93
|
+
Phase A runs query reformulation and embedding generation sequentially.
|
|
94
|
+
Phase B runs all entity searches in parallel using the results from Phase A.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
request (UnifiedSearchRequest): The unified search request
|
|
98
|
+
org_id (str): Organization ID (used for feature flag checks)
|
|
99
|
+
storage: Storage instance (BaseStorage implementation)
|
|
100
|
+
llm_client (LiteLLMClient): Shared LLM client instance
|
|
101
|
+
prompt_manager (PromptManager): Prompt manager for query reformulator
|
|
102
|
+
pre_retrieval_model_name (str, optional): Model name override for query reformulation.
|
|
103
|
+
Caller should resolve this from config and/or site vars.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
UnifiedSearchResponse: Combined results from all entity types
|
|
107
|
+
"""
|
|
108
|
+
if not request.query:
|
|
109
|
+
return UnifiedSearchResponse(success=True, msg="No query provided")
|
|
110
|
+
|
|
111
|
+
top_k = request.top_k if request.top_k is not None else 5
|
|
112
|
+
threshold = request.threshold if request.threshold is not None else 0.3
|
|
113
|
+
|
|
114
|
+
floor_cfg = retrieval_floor or RetrievalFloorConfig()
|
|
115
|
+
floor_on = floor_cfg.enabled
|
|
116
|
+
fetch_k = max(top_k, floor_cfg.pool_size) if floor_on else top_k
|
|
117
|
+
|
|
118
|
+
# --- Phase A: query reformulation + embedding generation ---
|
|
119
|
+
reformulated_query, embedding = _run_phase_a(
|
|
120
|
+
query=request.query,
|
|
121
|
+
storage=storage,
|
|
122
|
+
llm_client=llm_client,
|
|
123
|
+
prompt_manager=prompt_manager,
|
|
124
|
+
supports_embedding=storage.supports_embedding,
|
|
125
|
+
conversation_history=request.conversation_history,
|
|
126
|
+
enable_reformulation=bool(request.enable_reformulation),
|
|
127
|
+
pre_retrieval_model_name=pre_retrieval_model_name,
|
|
128
|
+
search_mode=request.search_mode,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# --- Phase B: parallel searches across all entity types ---
|
|
132
|
+
profiles, agent_playbooks, user_playbooks = _run_phase_b(
|
|
133
|
+
request=request,
|
|
134
|
+
org_id=org_id,
|
|
135
|
+
storage=storage,
|
|
136
|
+
embedding=embedding,
|
|
137
|
+
query=reformulated_query,
|
|
138
|
+
top_k=fetch_k,
|
|
139
|
+
threshold=threshold,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if profiles is None:
|
|
143
|
+
return UnifiedSearchResponse(success=False, msg="Search failed")
|
|
144
|
+
|
|
145
|
+
if floor_on:
|
|
146
|
+
profiles, agent_playbooks, user_playbooks = _apply_floors(
|
|
147
|
+
query=reformulated_query,
|
|
148
|
+
profiles=profiles,
|
|
149
|
+
agent_playbooks=agent_playbooks, # type: ignore[arg-type]
|
|
150
|
+
user_playbooks=user_playbooks, # type: ignore[arg-type]
|
|
151
|
+
top_k=top_k,
|
|
152
|
+
cfg=floor_cfg,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return UnifiedSearchResponse(
|
|
156
|
+
success=True,
|
|
157
|
+
profiles=profiles,
|
|
158
|
+
agent_playbooks=agent_playbooks, # type: ignore[reportArgumentType]
|
|
159
|
+
user_playbooks=user_playbooks, # type: ignore[reportArgumentType]
|
|
160
|
+
reformulated_query=reformulated_query
|
|
161
|
+
if reformulated_query != request.query
|
|
162
|
+
else None,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _run_phase_a(
|
|
167
|
+
query: str,
|
|
168
|
+
storage: BaseStorage,
|
|
169
|
+
llm_client: LiteLLMClient,
|
|
170
|
+
prompt_manager: PromptManager,
|
|
171
|
+
supports_embedding: bool = True,
|
|
172
|
+
conversation_history: list[ConversationTurn] | None = None,
|
|
173
|
+
enable_reformulation: bool = False,
|
|
174
|
+
pre_retrieval_model_name: str | None = None,
|
|
175
|
+
search_mode: SearchMode = SearchMode.HYBRID,
|
|
176
|
+
) -> tuple[str, list[float] | None]:
|
|
177
|
+
"""Run query reformulation and embedding generation sequentially.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
query (str): The original search query
|
|
181
|
+
storage (BaseStorage): Storage instance
|
|
182
|
+
llm_client (LiteLLMClient): Shared LLM client instance
|
|
183
|
+
prompt_manager (PromptManager): Prompt manager instance
|
|
184
|
+
supports_embedding (bool): Whether the storage backend supports embedding generation.
|
|
185
|
+
When False, skips embedding and returns None (local/self-host storage).
|
|
186
|
+
conversation_history (list, optional): Prior conversation turns for context-aware query reformulation
|
|
187
|
+
enable_reformulation (bool): Whether query reformulation is enabled for this request
|
|
188
|
+
pre_retrieval_model_name (str, optional): Model name override for query reformulation
|
|
189
|
+
search_mode (SearchMode): Search mode; FTS-only mode skips embedding generation entirely
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
tuple[str, Optional[list[float]]]: (standalone_query, embedding_vector) — embedding is None when unsupported or on failure
|
|
193
|
+
"""
|
|
194
|
+
reformulator = QueryReformulator(
|
|
195
|
+
llm_client=llm_client,
|
|
196
|
+
prompt_manager=prompt_manager,
|
|
197
|
+
model_name=pre_retrieval_model_name,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Query reformulation (rewrite() handles all exceptions internally)
|
|
201
|
+
with profile_step(
|
|
202
|
+
"search.reformulate",
|
|
203
|
+
enabled=enable_reformulation,
|
|
204
|
+
has_conversation_history=bool(conversation_history),
|
|
205
|
+
):
|
|
206
|
+
if enable_reformulation:
|
|
207
|
+
result = reformulator.rewrite(query, conversation_history)
|
|
208
|
+
standalone_query = result.standalone_query
|
|
209
|
+
else:
|
|
210
|
+
standalone_query = query
|
|
211
|
+
|
|
212
|
+
# Embedding generation (uses reformulated query for semantic accuracy).
|
|
213
|
+
# FTS-only search has no use for an embedding, so skip the call entirely.
|
|
214
|
+
embedding = None
|
|
215
|
+
if supports_embedding and search_mode != SearchMode.FTS:
|
|
216
|
+
with profile_step(
|
|
217
|
+
"search.embedding",
|
|
218
|
+
backend=_storage_backend_name(storage),
|
|
219
|
+
purpose="query",
|
|
220
|
+
) as span:
|
|
221
|
+
try:
|
|
222
|
+
embedding = _get_cached_query_embedding(storage, standalone_query)
|
|
223
|
+
span.set_data("embedding_generated", embedding is not None)
|
|
224
|
+
except Exception as e:
|
|
225
|
+
span.set_data("embedding_generated", False)
|
|
226
|
+
logger.error("Embedding generation failed: %s", e)
|
|
227
|
+
|
|
228
|
+
return standalone_query, embedding
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _run_phase_b(
|
|
232
|
+
request: UnifiedSearchRequest,
|
|
233
|
+
org_id: str, # noqa: ARG001
|
|
234
|
+
storage: BaseStorage,
|
|
235
|
+
embedding: list[float] | None,
|
|
236
|
+
query: str,
|
|
237
|
+
top_k: int,
|
|
238
|
+
threshold: float,
|
|
239
|
+
) -> tuple[
|
|
240
|
+
list[UserProfile] | None,
|
|
241
|
+
list[AgentPlaybook] | None,
|
|
242
|
+
list[UserPlaybook] | None,
|
|
243
|
+
]:
|
|
244
|
+
"""Run parallel searches across all entity types by delegating to storage methods.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
request (UnifiedSearchRequest): The search request (for filters)
|
|
248
|
+
org_id (str): Organization ID
|
|
249
|
+
storage (BaseStorage): Storage instance
|
|
250
|
+
embedding (Optional[list[float]]): Pre-computed query embedding, or None for text-only search
|
|
251
|
+
query (str): Query string (possibly rewritten) for FTS
|
|
252
|
+
top_k (int): Maximum results per entity type
|
|
253
|
+
threshold (float): Minimum match threshold
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
tuple: (profiles, agent_playbooks, user_playbooks) — all None on timeout/failure
|
|
257
|
+
"""
|
|
258
|
+
options = SearchOptions(query_embedding=embedding, search_mode=request.search_mode)
|
|
259
|
+
|
|
260
|
+
entity_types = set(request.entity_types or _DEFAULT_ENTITY_TYPES)
|
|
261
|
+
allowed_agent_statuses = request.agent_playbook_status_filter
|
|
262
|
+
try:
|
|
263
|
+
with profile_step(
|
|
264
|
+
"search.phase_b",
|
|
265
|
+
backend=_storage_backend_name(storage),
|
|
266
|
+
entity_types=sorted(entity_types),
|
|
267
|
+
top_k=top_k,
|
|
268
|
+
) as span:
|
|
269
|
+
profiles_future = (
|
|
270
|
+
_submit_with_current_context(
|
|
271
|
+
_SEARCH_FANOUT_EXECUTOR,
|
|
272
|
+
_search_profiles_via_storage,
|
|
273
|
+
storage,
|
|
274
|
+
query,
|
|
275
|
+
top_k,
|
|
276
|
+
threshold,
|
|
277
|
+
request.user_id,
|
|
278
|
+
embedding,
|
|
279
|
+
request.search_mode,
|
|
280
|
+
)
|
|
281
|
+
if "profiles" in entity_types
|
|
282
|
+
else None
|
|
283
|
+
)
|
|
284
|
+
agent_playbooks_future = (
|
|
285
|
+
_submit_with_current_context(
|
|
286
|
+
_SEARCH_FANOUT_EXECUTOR,
|
|
287
|
+
_search_agent_playbooks_via_storage,
|
|
288
|
+
storage,
|
|
289
|
+
query,
|
|
290
|
+
top_k,
|
|
291
|
+
threshold,
|
|
292
|
+
request.agent_version,
|
|
293
|
+
request.playbook_name,
|
|
294
|
+
allowed_agent_statuses,
|
|
295
|
+
options,
|
|
296
|
+
)
|
|
297
|
+
if "agent_playbooks" in entity_types
|
|
298
|
+
else None
|
|
299
|
+
)
|
|
300
|
+
if "user_playbooks" in entity_types:
|
|
301
|
+
rf_request = SearchUserPlaybookRequest(
|
|
302
|
+
query=query,
|
|
303
|
+
user_id=request.user_id,
|
|
304
|
+
agent_version=request.agent_version,
|
|
305
|
+
playbook_name=request.playbook_name,
|
|
306
|
+
status_filter=None,
|
|
307
|
+
threshold=threshold,
|
|
308
|
+
top_k=top_k,
|
|
309
|
+
search_mode=request.search_mode,
|
|
310
|
+
)
|
|
311
|
+
user_playbooks_future = _submit_with_current_context(
|
|
312
|
+
_SEARCH_FANOUT_EXECUTOR,
|
|
313
|
+
_search_user_playbooks_via_storage,
|
|
314
|
+
storage,
|
|
315
|
+
rf_request,
|
|
316
|
+
options,
|
|
317
|
+
)
|
|
318
|
+
else:
|
|
319
|
+
user_playbooks_future = None
|
|
320
|
+
|
|
321
|
+
profiles = profiles_future.result(timeout=30) if profiles_future else []
|
|
322
|
+
agent_playbooks = (
|
|
323
|
+
agent_playbooks_future.result(timeout=30)
|
|
324
|
+
if agent_playbooks_future
|
|
325
|
+
else []
|
|
326
|
+
)
|
|
327
|
+
user_playbooks = (
|
|
328
|
+
user_playbooks_future.result(timeout=30)
|
|
329
|
+
if user_playbooks_future
|
|
330
|
+
else []
|
|
331
|
+
)
|
|
332
|
+
set_span_data(
|
|
333
|
+
span,
|
|
334
|
+
{
|
|
335
|
+
"profiles_count": len(profiles),
|
|
336
|
+
"agent_playbooks_count": len(agent_playbooks),
|
|
337
|
+
"user_playbooks_count": len(user_playbooks),
|
|
338
|
+
},
|
|
339
|
+
)
|
|
340
|
+
except FuturesTimeoutError:
|
|
341
|
+
logger.error("Unified search timed out")
|
|
342
|
+
return None, None, None
|
|
343
|
+
except Exception as e:
|
|
344
|
+
logger.error("Unified search failed: %s", e)
|
|
345
|
+
return None, None, None
|
|
346
|
+
|
|
347
|
+
return profiles, agent_playbooks, user_playbooks
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _apply_floors(
|
|
351
|
+
query: str,
|
|
352
|
+
profiles: list[UserProfile],
|
|
353
|
+
agent_playbooks: list[AgentPlaybook],
|
|
354
|
+
user_playbooks: list[UserPlaybook],
|
|
355
|
+
top_k: int,
|
|
356
|
+
cfg: RetrievalFloorConfig,
|
|
357
|
+
) -> tuple[list[UserProfile], list[AgentPlaybook], list[UserPlaybook]]:
|
|
358
|
+
"""Apply the per-arm relevance floor to each entity arm in parallel."""
|
|
359
|
+
with ThreadPoolExecutor(max_workers=3) as ex:
|
|
360
|
+
f_profiles = ex.submit(
|
|
361
|
+
apply_relevance_floor,
|
|
362
|
+
query,
|
|
363
|
+
profiles,
|
|
364
|
+
cfg.profile_floor,
|
|
365
|
+
top_k,
|
|
366
|
+
arm="profiles",
|
|
367
|
+
)
|
|
368
|
+
f_agent = ex.submit(
|
|
369
|
+
apply_relevance_floor,
|
|
370
|
+
query,
|
|
371
|
+
agent_playbooks,
|
|
372
|
+
cfg.agent_playbook_floor,
|
|
373
|
+
top_k,
|
|
374
|
+
arm="agent_playbooks",
|
|
375
|
+
)
|
|
376
|
+
f_user = ex.submit(
|
|
377
|
+
apply_relevance_floor,
|
|
378
|
+
query,
|
|
379
|
+
user_playbooks,
|
|
380
|
+
cfg.user_playbook_floor,
|
|
381
|
+
top_k,
|
|
382
|
+
arm="user_playbooks",
|
|
383
|
+
)
|
|
384
|
+
return f_profiles.result(), f_agent.result(), f_user.result()
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _get_cached_query_embedding(
|
|
388
|
+
storage: BaseStorage,
|
|
389
|
+
query: str,
|
|
390
|
+
) -> list[float]:
|
|
391
|
+
"""Return a cached query embedding when available."""
|
|
392
|
+
model_name = str(getattr(storage, "embedding_model_name", "unknown"))
|
|
393
|
+
dimensions = int(getattr(storage, "embedding_dimensions", 0) or 0)
|
|
394
|
+
normalized_query = " ".join(query.casefold().split())
|
|
395
|
+
key = (model_name, dimensions, normalized_query, "query")
|
|
396
|
+
now = time.monotonic()
|
|
397
|
+
if _EMBEDDING_CACHE_TTL_SECONDS > 0:
|
|
398
|
+
with _embedding_cache_lock:
|
|
399
|
+
cached = _embedding_cache.get(key)
|
|
400
|
+
if cached is not None:
|
|
401
|
+
created_at, value = cached
|
|
402
|
+
if now - created_at <= _EMBEDDING_CACHE_TTL_SECONDS:
|
|
403
|
+
_embedding_cache.move_to_end(key)
|
|
404
|
+
return list(value)
|
|
405
|
+
del _embedding_cache[key]
|
|
406
|
+
|
|
407
|
+
embedding = storage._get_embedding(query, purpose="query") # type: ignore[reportAttributeAccessIssue]
|
|
408
|
+
if _EMBEDDING_CACHE_TTL_SECONDS > 0 and embedding:
|
|
409
|
+
with _embedding_cache_lock:
|
|
410
|
+
_embedding_cache[key] = (now, list(embedding))
|
|
411
|
+
_embedding_cache.move_to_end(key)
|
|
412
|
+
while len(_embedding_cache) > _EMBEDDING_CACHE_MAX_SIZE:
|
|
413
|
+
_embedding_cache.popitem(last=False)
|
|
414
|
+
return embedding
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _search_agent_playbooks_via_storage(
|
|
418
|
+
storage: BaseStorage,
|
|
419
|
+
query: str,
|
|
420
|
+
top_k: int,
|
|
421
|
+
threshold: float,
|
|
422
|
+
agent_version: str | None,
|
|
423
|
+
playbook_name: str | None,
|
|
424
|
+
allowed_statuses: list[PlaybookStatus] | None,
|
|
425
|
+
options: SearchOptions,
|
|
426
|
+
) -> list[AgentPlaybook]:
|
|
427
|
+
"""Search agent playbooks, restricted to one or more approval statuses.
|
|
428
|
+
|
|
429
|
+
When ``allowed_statuses`` is None or empty, falls back to
|
|
430
|
+
``_DEFAULT_AGENT_PLAYBOOK_STATUSES`` (APPROVED + PENDING). Callers that
|
|
431
|
+
genuinely want REJECTED playbooks must opt in by passing the full list.
|
|
432
|
+
"""
|
|
433
|
+
with profile_step(
|
|
434
|
+
"search.branch.agent_playbooks",
|
|
435
|
+
backend=_storage_backend_name(storage),
|
|
436
|
+
top_k=top_k,
|
|
437
|
+
) as span:
|
|
438
|
+
statuses = (
|
|
439
|
+
list(allowed_statuses)
|
|
440
|
+
if allowed_statuses
|
|
441
|
+
else list(_DEFAULT_AGENT_PLAYBOOK_STATUSES)
|
|
442
|
+
)
|
|
443
|
+
request = SearchAgentPlaybookRequest(
|
|
444
|
+
query=query,
|
|
445
|
+
agent_version=agent_version,
|
|
446
|
+
playbook_name=playbook_name,
|
|
447
|
+
status_filter=[None],
|
|
448
|
+
playbook_status_filter=statuses,
|
|
449
|
+
threshold=threshold,
|
|
450
|
+
top_k=top_k,
|
|
451
|
+
search_mode=options.search_mode,
|
|
452
|
+
)
|
|
453
|
+
results: list[AgentPlaybook] = []
|
|
454
|
+
seen_ids: set[str] = set()
|
|
455
|
+
for playbook in storage.search_agent_playbooks(request, options):
|
|
456
|
+
playbook_id = str(getattr(playbook, "agent_playbook_id", ""))
|
|
457
|
+
if playbook_id and playbook_id not in seen_ids:
|
|
458
|
+
seen_ids.add(playbook_id)
|
|
459
|
+
results.append(playbook)
|
|
460
|
+
if len(results) >= top_k:
|
|
461
|
+
break
|
|
462
|
+
span.set_data("result_count", len(results))
|
|
463
|
+
return results
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def _search_profiles_via_storage(
|
|
467
|
+
storage: BaseStorage,
|
|
468
|
+
query: str,
|
|
469
|
+
top_k: int,
|
|
470
|
+
threshold: float,
|
|
471
|
+
user_id: str | None,
|
|
472
|
+
embedding: list[float] | None,
|
|
473
|
+
search_mode: SearchMode,
|
|
474
|
+
) -> list[UserProfile]:
|
|
475
|
+
"""Search profiles via storage.search_user_profile, returning [] on error or missing user_id.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
storage (BaseStorage): Storage instance
|
|
479
|
+
query (str): Search query text
|
|
480
|
+
top_k (int): Maximum results
|
|
481
|
+
threshold (float): Minimum match threshold
|
|
482
|
+
user_id (Optional[str]): User ID filter (required for profile search)
|
|
483
|
+
embedding (Optional[list[float]]): Pre-computed query embedding, or None for text-only search
|
|
484
|
+
search_mode (SearchMode): Search mode (hybrid/vector/fts)
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
list[UserProfile]: Matching profiles, or [] on error/missing user_id
|
|
488
|
+
"""
|
|
489
|
+
with profile_step(
|
|
490
|
+
"search.branch.profiles",
|
|
491
|
+
backend=_storage_backend_name(storage),
|
|
492
|
+
top_k=top_k,
|
|
493
|
+
) as span:
|
|
494
|
+
if not user_id:
|
|
495
|
+
span.set_data("result_count", 0)
|
|
496
|
+
return []
|
|
497
|
+
try:
|
|
498
|
+
profiles = storage.search_user_profile(
|
|
499
|
+
SearchUserProfileRequest(
|
|
500
|
+
user_id=user_id,
|
|
501
|
+
query=query,
|
|
502
|
+
top_k=top_k,
|
|
503
|
+
threshold=threshold,
|
|
504
|
+
search_mode=search_mode,
|
|
505
|
+
),
|
|
506
|
+
status_filter=[None],
|
|
507
|
+
query_embedding=embedding,
|
|
508
|
+
)
|
|
509
|
+
span.set_data("result_count", len(profiles))
|
|
510
|
+
return profiles
|
|
511
|
+
except Exception as e:
|
|
512
|
+
span.set_data("result_count", 0)
|
|
513
|
+
logger.error("Profile search failed: %s", e)
|
|
514
|
+
return []
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def _search_user_playbooks_via_storage(
|
|
518
|
+
storage: BaseStorage,
|
|
519
|
+
request: SearchUserPlaybookRequest,
|
|
520
|
+
options: SearchOptions,
|
|
521
|
+
) -> list[UserPlaybook]:
|
|
522
|
+
with profile_step(
|
|
523
|
+
"search.branch.user_playbooks",
|
|
524
|
+
backend=_storage_backend_name(storage),
|
|
525
|
+
top_k=request.top_k,
|
|
526
|
+
) as span:
|
|
527
|
+
user_playbooks = storage.search_user_playbooks(request, options)
|
|
528
|
+
span.set_data("result_count", len(user_playbooks))
|
|
529
|
+
return user_playbooks
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def _submit_with_current_context(
|
|
533
|
+
executor: ThreadPoolExecutor,
|
|
534
|
+
fn: Callable[..., object],
|
|
535
|
+
*args: object,
|
|
536
|
+
) -> Future[Any]:
|
|
537
|
+
context = contextvars.copy_context()
|
|
538
|
+
return executor.submit(context.run, fn, *args)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def _storage_backend_name(storage: BaseStorage) -> str:
|
|
542
|
+
class_name = storage.__class__.__name__.lower()
|
|
543
|
+
if "postgres" in class_name:
|
|
544
|
+
return "postgres"
|
|
545
|
+
if "supabase" in class_name:
|
|
546
|
+
return "supabase"
|
|
547
|
+
return class_name
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
class UnifiedSearchService:
|
|
551
|
+
"""Class handle for the classic unified search pipeline.
|
|
552
|
+
|
|
553
|
+
Wraps :func:`run_unified_search` so the dispatcher factory can return an
|
|
554
|
+
object whose ``__class__.__name__`` can be inspected uniformly alongside
|
|
555
|
+
the agentic search service (Phase 4).
|
|
556
|
+
|
|
557
|
+
Args:
|
|
558
|
+
llm_client (LiteLLMClient): Configured LLM client.
|
|
559
|
+
request_context (RequestContext): Current request context.
|
|
560
|
+
"""
|
|
561
|
+
|
|
562
|
+
def __init__(
|
|
563
|
+
self,
|
|
564
|
+
llm_client: LiteLLMClient,
|
|
565
|
+
request_context: RequestContext,
|
|
566
|
+
) -> None:
|
|
567
|
+
self.llm_client = llm_client
|
|
568
|
+
self.request_context = request_context
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Site Variables
|
|
2
|
+
Description: Global configuration manager for site-wide variables and per-org feature flags
|
|
3
|
+
|
|
4
|
+
> Part of the [Reflexio Server](../README.md).
|
|
5
|
+
|
|
6
|
+
## Main Entry Points
|
|
7
|
+
|
|
8
|
+
- **Manager**: `site_var_manager.py` - `SiteVarManager` (singleton)
|
|
9
|
+
- **Feature Flags**: `feature_flags.py` - Per-org feature gating helpers
|
|
10
|
+
- **Sources**: `site_var_sources/` - JSON/TXT config files
|
|
11
|
+
|
|
12
|
+
## Purpose
|
|
13
|
+
|
|
14
|
+
1. **Global settings** - Model names, embedding models
|
|
15
|
+
2. **Feature flags** - Per-org feature gating (global enable or per-org allowlist)
|
|
16
|
+
3. **Dual storage** - File-based with optional Redis caching
|
|
17
|
+
4. **Auto-fallback** - Redis → File system graceful degradation
|
|
18
|
+
|
|
19
|
+
## Feature Flags
|
|
20
|
+
|
|
21
|
+
**File**: `feature_flags.py`
|
|
22
|
+
**Config**: `site_var_sources/feature_flags.json`
|
|
23
|
+
|
|
24
|
+
Per-org feature gating with fail-open defaults. Each flag supports global enable or per-org allowlist.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from reflexio.server.site_var.feature_flags import is_feature_enabled, get_all_feature_flags
|
|
28
|
+
|
|
29
|
+
is_feature_enabled(org_id, "deduplicator") # Single flag check
|
|
30
|
+
get_all_feature_flags(org_id) # All flags as dict[str, bool]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Config format** (`feature_flags.json`):
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"deduplicator": {
|
|
37
|
+
"enabled": true,
|
|
38
|
+
"enabled_org_ids": []
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Resolution logic**: Enabled if `enabled=True` (global) OR `org_id` in `enabled_org_ids`. Unknown flags default to enabled (fail-open).
|
|
44
|
+
|
|
45
|
+
**Current flags**: `invitation_only` (global, gates registration), `deduplicator` (gates playbook deduplication).
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from reflexio.server.site_var.site_var_manager import SiteVarManager
|
|
51
|
+
|
|
52
|
+
manager = SiteVarManager()
|
|
53
|
+
config = manager.get_site_var("app_config") # Returns dict or string
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## File Structure
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
site_var/
|
|
60
|
+
├── site_var_manager.py # SiteVarManager (singleton)
|
|
61
|
+
├── feature_flags.py # Per-org feature flag helpers
|
|
62
|
+
└── site_var_sources/
|
|
63
|
+
├── app_config.json # JSON → parsed dict
|
|
64
|
+
├── model_config.json
|
|
65
|
+
└── feature_flags.json # Feature flag config (per-flag enable + org allowlist)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Architecture Pattern
|
|
69
|
+
|
|
70
|
+
- **JSON priority**: `.json` files take precedence over `.txt`
|
|
71
|
+
- **Variable name**: Filename without extension
|
|
72
|
+
- **Redis optional**: Enable via `SiteVarManager(enable_redis=True)`
|
|
73
|
+
- **Feature flags**: Loaded via SiteVarManager, resolved per-org at request time
|
|
74
|
+
|
|
75
|
+
## See Also
|
|
76
|
+
|
|
77
|
+
- [Server README](../README.md) -- FastAPI backend component overview
|