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,255 @@
|
|
|
1
|
+
"""Local in-process embedder using ``nomic-ai/nomic-embed-text-v1.5``.
|
|
2
|
+
|
|
3
|
+
A higher-quality alternative to the chromadb-bundled MiniLM-L6-v2: 137M
|
|
4
|
+
parameters, 768-dim native, supports Matryoshka representation (64–768
|
|
5
|
+
dimensions without retraining), 8192-token context, Apache-2.0 licensed.
|
|
6
|
+
Performs comparably to OpenAI's ``text-embedding-3-small`` on MTEB
|
|
7
|
+
retrieval at a fraction of the latency cost when run locally on CPU or
|
|
8
|
+
Apple Silicon.
|
|
9
|
+
|
|
10
|
+
Activation
|
|
11
|
+
----------
|
|
12
|
+
|
|
13
|
+
- Set ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1`` in the process environment.
|
|
14
|
+
- Pass model name ``local/nomic-embed-v1.5`` (or ``local/nomic-embed-text-v1.5``)
|
|
15
|
+
to :func:`LiteLLMClient.get_embedding`/``get_embeddings``.
|
|
16
|
+
- Requires the ``sentence-transformers`` pip dependency.
|
|
17
|
+
|
|
18
|
+
Storage compatibility
|
|
19
|
+
---------------------
|
|
20
|
+
|
|
21
|
+
Reflexio's vec0 tables expect 512-dim vectors (``EMBEDDING_DIMENSIONS``).
|
|
22
|
+
Nomic's native 768 dim is reduced via Matryoshka — slice the first 512
|
|
23
|
+
floats, then re-normalize to unit length so cosine similarity remains
|
|
24
|
+
comparable. Quality on retrieval tasks at 512 dim is ~95% of the full
|
|
25
|
+
768 (per Nomic's own evaluation).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import importlib.util
|
|
31
|
+
import logging
|
|
32
|
+
import math
|
|
33
|
+
import os
|
|
34
|
+
import threading
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
_LOGGER = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
_ENV_ENABLE = "CLAUDE_SMART_USE_LOCAL_EMBEDDING"
|
|
40
|
+
_ENV_PROVIDER = "REFLEXIO_EMBEDDING_PROVIDER"
|
|
41
|
+
_ENV_DAEMON = "REFLEXIO_EMBEDDING_DAEMON"
|
|
42
|
+
_MODEL_KEYS = {"local/nomic-embed-v1.5", "local/nomic-embed-text-v1.5"}
|
|
43
|
+
_HF_MODEL_NAME = "nomic-ai/nomic-embed-text-v1.5"
|
|
44
|
+
|
|
45
|
+
# Reflexio's vec0 schema dim. Nomic v1.5 outputs 768 natively; we slice
|
|
46
|
+
# to 512 (Matryoshka) and re-normalize.
|
|
47
|
+
_TARGET_DIM = 512
|
|
48
|
+
# Nomic v1.5 was trained with task-prefixed inputs; "search_document"
|
|
49
|
+
# vs "search_query" prefixes give better asymmetric retrieval. Reflexio's
|
|
50
|
+
# storage layer already passes a "search_document: " / "search_query: "
|
|
51
|
+
# prefix when calling _get_embedding(purpose=...), so we don't add another
|
|
52
|
+
# prefix here — the input arrives correctly tagged.
|
|
53
|
+
# The model has a 8192 token context window; we still cap chars
|
|
54
|
+
# defensively to avoid pathological multi-MB inputs.
|
|
55
|
+
_MAX_CHARS = 32_000
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class NomicEmbedderError(RuntimeError):
|
|
59
|
+
"""Raised when the Nomic embedder is requested but its deps are missing."""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class NomicEmbedder:
|
|
63
|
+
"""Lazily-loaded singleton wrapping a sentence-transformers model.
|
|
64
|
+
|
|
65
|
+
Loading the underlying ``nomic-embed-text-v1.5`` model takes ~5–10 s on
|
|
66
|
+
first call (downloads ~550 MB on cold start, then cached under
|
|
67
|
+
``~/.cache/huggingface/``). After that, embedding latency on CPU is
|
|
68
|
+
~30–60 ms per single text and ~200 ms per batch of 32 (Apple M-series).
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
_instance: NomicEmbedder | None = None
|
|
72
|
+
_lock = threading.Lock()
|
|
73
|
+
|
|
74
|
+
def __init__(self) -> None:
|
|
75
|
+
self._model: Any | None = None
|
|
76
|
+
self._model_lock = threading.Lock()
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def get(cls) -> NomicEmbedder:
|
|
80
|
+
"""Return the process-wide singleton, constructing it on first use."""
|
|
81
|
+
if cls._instance is None:
|
|
82
|
+
with cls._lock:
|
|
83
|
+
if cls._instance is None:
|
|
84
|
+
cls._instance = cls()
|
|
85
|
+
return cls._instance
|
|
86
|
+
|
|
87
|
+
def _load(self) -> Any:
|
|
88
|
+
"""Lazy-import sentence-transformers and load the Nomic model."""
|
|
89
|
+
if self._model is not None:
|
|
90
|
+
return self._model
|
|
91
|
+
with self._model_lock:
|
|
92
|
+
if self._model is not None:
|
|
93
|
+
return self._model
|
|
94
|
+
try:
|
|
95
|
+
from sentence_transformers import (
|
|
96
|
+
SentenceTransformer, # type: ignore[import-not-found]
|
|
97
|
+
)
|
|
98
|
+
except ImportError as exc:
|
|
99
|
+
raise NomicEmbedderError(
|
|
100
|
+
"sentence-transformers is required for the Nomic local "
|
|
101
|
+
"embedder. Install with `uv add sentence-transformers`."
|
|
102
|
+
) from exc
|
|
103
|
+
_LOGGER.info(
|
|
104
|
+
"Loading Nomic embedding model %s — first call may download "
|
|
105
|
+
"~550 MB to ~/.cache/huggingface/",
|
|
106
|
+
_HF_MODEL_NAME,
|
|
107
|
+
)
|
|
108
|
+
# Force CPU device — MPS init has been observed to hang on some
|
|
109
|
+
# Apple Silicon + macOS combos for several minutes during model
|
|
110
|
+
# load. CPU is fast enough for our use case (137M params) and
|
|
111
|
+
# behaves predictably. Set NOMIC_EMBED_DEVICE=mps|cuda|cpu to
|
|
112
|
+
# override.
|
|
113
|
+
device = os.environ.get("NOMIC_EMBED_DEVICE", "cpu")
|
|
114
|
+
self._model = SentenceTransformer(
|
|
115
|
+
_HF_MODEL_NAME,
|
|
116
|
+
trust_remote_code=True, # Nomic v1.5 ships custom code
|
|
117
|
+
device=device,
|
|
118
|
+
)
|
|
119
|
+
_LOGGER.info(
|
|
120
|
+
"Nomic embedder ready (model=%s, target_dim=%d, native_dim=%d)",
|
|
121
|
+
_HF_MODEL_NAME,
|
|
122
|
+
_TARGET_DIM,
|
|
123
|
+
self._model.get_sentence_embedding_dimension(),
|
|
124
|
+
)
|
|
125
|
+
return self._model
|
|
126
|
+
|
|
127
|
+
def embed(self, texts: list[str]) -> list[list[float]]:
|
|
128
|
+
"""Embed a batch of texts, returning ``_TARGET_DIM``-sized unit vectors.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
texts: Inputs to encode. Each is char-truncated to ``_MAX_CHARS``
|
|
132
|
+
as a defensive cap; Nomic itself supports 8192 tokens.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
list[list[float]]: One vector per input, each exactly
|
|
136
|
+
``_TARGET_DIM`` (512) floats and L2-normalised so cosine
|
|
137
|
+
similarity equals dot product.
|
|
138
|
+
"""
|
|
139
|
+
model = self._load()
|
|
140
|
+
safe = [(t or "")[:_MAX_CHARS] for t in texts]
|
|
141
|
+
# show_progress_bar=False so server logs stay clean during ingest
|
|
142
|
+
# batches. convert_to_numpy=True returns a numpy ndarray; we slice
|
|
143
|
+
# and renormalise per-row before converting to plain Python lists.
|
|
144
|
+
raw = model.encode(safe, show_progress_bar=False, convert_to_numpy=True)
|
|
145
|
+
return [_truncate_and_renormalise(vec.tolist()) for vec in raw]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _truncate_and_renormalise(vec: list[float]) -> list[float]:
|
|
149
|
+
"""Slice to ``_TARGET_DIM`` and L2-renormalise for valid Matryoshka use.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
vec (list[float]): Native-dim Nomic embedding (typically 768 floats,
|
|
153
|
+
already L2-unit on the full 768).
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
list[float]: Exactly ``_TARGET_DIM`` floats, L2-normalised in the
|
|
157
|
+
truncated subspace so cosine similarity remains a valid metric.
|
|
158
|
+
Zero-padded if the input is shorter than ``_TARGET_DIM``.
|
|
159
|
+
"""
|
|
160
|
+
if len(vec) >= _TARGET_DIM:
|
|
161
|
+
sliced = vec[:_TARGET_DIM]
|
|
162
|
+
else:
|
|
163
|
+
sliced = vec + [0.0] * (_TARGET_DIM - len(vec))
|
|
164
|
+
norm = math.sqrt(sum(x * x for x in sliced))
|
|
165
|
+
if norm <= 0:
|
|
166
|
+
return sliced
|
|
167
|
+
return [x / norm for x in sliced]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
_REGISTERED = False
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def register_if_enabled() -> bool:
|
|
174
|
+
"""Make the Nomic embedder available when env + deps allow it.
|
|
175
|
+
|
|
176
|
+
Idempotent. Returns ``True`` when the embedder is usable after this
|
|
177
|
+
call. Routing happens via prefix-match on the model name in
|
|
178
|
+
``LiteLLMClient.get_embedding(s)``.
|
|
179
|
+
|
|
180
|
+
Eagerly pre-warms the model in a daemon thread so the first request
|
|
181
|
+
doesn't pay the ~30 s cold-start cost. The thread is fire-and-forget;
|
|
182
|
+
callers either land mid-load (and block briefly) or after-load (and
|
|
183
|
+
proceed immediately).
|
|
184
|
+
"""
|
|
185
|
+
global _REGISTERED
|
|
186
|
+
if _REGISTERED:
|
|
187
|
+
return True
|
|
188
|
+
if os.environ.get(_ENV_ENABLE) != "1":
|
|
189
|
+
return False
|
|
190
|
+
provider = os.environ.get(_ENV_PROVIDER, "").strip().lower()
|
|
191
|
+
if provider in {"local_service", "internal_service", "off"}:
|
|
192
|
+
_LOGGER.info(
|
|
193
|
+
"Nomic in-process prewarm skipped because %s=%s",
|
|
194
|
+
_ENV_PROVIDER,
|
|
195
|
+
provider,
|
|
196
|
+
)
|
|
197
|
+
return False
|
|
198
|
+
if not provider and os.environ.get(_ENV_DAEMON) != "1":
|
|
199
|
+
_LOGGER.info(
|
|
200
|
+
"Nomic in-process prewarm skipped; %s=1 now defaults to the "
|
|
201
|
+
"shared embedding service.",
|
|
202
|
+
_ENV_ENABLE,
|
|
203
|
+
)
|
|
204
|
+
return False
|
|
205
|
+
if importlib.util.find_spec("sentence_transformers") is None:
|
|
206
|
+
_LOGGER.warning(
|
|
207
|
+
"%s=1 set but `sentence-transformers` not installed; the Nomic "
|
|
208
|
+
"local embedder will not be available.",
|
|
209
|
+
_ENV_ENABLE,
|
|
210
|
+
)
|
|
211
|
+
return False
|
|
212
|
+
_REGISTERED = True
|
|
213
|
+
_LOGGER.info(
|
|
214
|
+
"Nomic local embedding provider enabled (models=%s)", sorted(_MODEL_KEYS)
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def _prewarm() -> None:
|
|
218
|
+
"""Background load + dummy inference so the first real request is fast."""
|
|
219
|
+
try:
|
|
220
|
+
embedder = NomicEmbedder.get()
|
|
221
|
+
embedder.embed(["warmup"])
|
|
222
|
+
_LOGGER.info("Nomic embedder pre-warmed")
|
|
223
|
+
except Exception: # noqa: BLE001
|
|
224
|
+
_LOGGER.exception(
|
|
225
|
+
"Nomic embedder pre-warm failed; first call will pay the cost"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
threading.Thread(target=_prewarm, daemon=True, name="nomic-prewarm").start()
|
|
229
|
+
return True
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def is_enabled() -> bool:
|
|
233
|
+
"""Return True after a successful :func:`register_if_enabled`."""
|
|
234
|
+
return _REGISTERED
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def is_nomic_model(model: str) -> bool:
|
|
238
|
+
"""Predicate used by ``LiteLLMClient`` to route by model name.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
model (str): The embedding model name passed by the caller.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
bool: True when the model resolves to the Nomic provider.
|
|
245
|
+
"""
|
|
246
|
+
return model in _MODEL_KEYS
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
__all__ = [
|
|
250
|
+
"NomicEmbedder",
|
|
251
|
+
"NomicEmbedderError",
|
|
252
|
+
"is_enabled",
|
|
253
|
+
"is_nomic_model",
|
|
254
|
+
"register_if_enabled",
|
|
255
|
+
]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Reranking helpers — local cross-encoder + LLM relevance judge."""
|
|
2
|
+
|
|
3
|
+
from reflexio.server.llm.rerank.cross_encoder_reranker import prewarm, score_pairs
|
|
4
|
+
from reflexio.server.llm.rerank.llm_reranker import score_pairs_llm
|
|
5
|
+
|
|
6
|
+
__all__ = ["prewarm", "score_pairs", "score_pairs_llm"]
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Local cross-encoder reranker for ``(query, document)`` pairs.
|
|
2
|
+
|
|
3
|
+
Wraps ``cross-encoder/ms-marco-MiniLM-L-6-v2`` (~25M params) from
|
|
4
|
+
``sentence-transformers``. The model is lazy-loaded on first call and
|
|
5
|
+
held as a process-wide singleton — load takes ~3 s but only happens
|
|
6
|
+
once per server start. Scoring K=30 pairs takes ~50 ms on CPU.
|
|
7
|
+
|
|
8
|
+
Usage
|
|
9
|
+
-----
|
|
10
|
+
|
|
11
|
+
>>> from reflexio.server.llm.rerank import score_pairs
|
|
12
|
+
>>> scores = score_pairs("italian food", ["pasta lover", "weather report"])
|
|
13
|
+
>>> scores[0] > scores[1]
|
|
14
|
+
True
|
|
15
|
+
|
|
16
|
+
The helper is intentionally side-effect free at import time: building
|
|
17
|
+
the singleton happens only when ``score_pairs`` is called, so importing
|
|
18
|
+
this module never triggers a model download.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import threading
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
_LOGGER = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
# HuggingFace identifier for the cross-encoder. Chosen for the
|
|
30
|
+
# size/quality trade-off: 22M parameters, ~50 ms for K=30 on CPU,
|
|
31
|
+
# well-known MS-MARCO benchmark performance.
|
|
32
|
+
_MODEL_NAME = "cross-encoder/ms-marco-MiniLM-L-6-v2"
|
|
33
|
+
|
|
34
|
+
# Singleton state — never accessed directly outside ``_get_model``.
|
|
35
|
+
_MODEL: Any | None = None
|
|
36
|
+
_MODEL_LOCK = threading.Lock()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class CrossEncoderUnavailableError(RuntimeError):
|
|
40
|
+
"""Raised when the cross-encoder model cannot be loaded.
|
|
41
|
+
|
|
42
|
+
The most common cause is ``sentence-transformers`` being absent from
|
|
43
|
+
the runtime environment. Callers should treat this as a soft failure
|
|
44
|
+
(log + skip rerank) rather than a 500.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _import_cross_encoder() -> Any:
|
|
49
|
+
"""Robustly import ``sentence_transformers.CrossEncoder``.
|
|
50
|
+
|
|
51
|
+
The ``sentence_transformers`` package is loaded both by the Nomic
|
|
52
|
+
local-embedding pre-warm thread (kicked off by ``LiteLLMClient.__init__``
|
|
53
|
+
when ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1``) and by this reranker. When
|
|
54
|
+
those concurrent imports race, one thread can see a half-loaded
|
|
55
|
+
``sentence_transformers`` module in ``sys.modules`` whose ``CrossEncoder``
|
|
56
|
+
attribute was never bound — Python's import machinery hands back the
|
|
57
|
+
partial module without re-running ``__init__``. This helper detects that
|
|
58
|
+
case, drops the stale entry, and re-imports cleanly.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Any: The ``CrossEncoder`` class.
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
CrossEncoderUnavailableError: When the package genuinely isn't
|
|
65
|
+
installed, or every retry yields a partial module.
|
|
66
|
+
"""
|
|
67
|
+
import sys
|
|
68
|
+
|
|
69
|
+
for _attempt in range(2):
|
|
70
|
+
try:
|
|
71
|
+
from sentence_transformers import CrossEncoder
|
|
72
|
+
except ImportError:
|
|
73
|
+
# Partial import — drop the stale entry and try once more.
|
|
74
|
+
sys.modules.pop("sentence_transformers", None)
|
|
75
|
+
continue
|
|
76
|
+
return CrossEncoder
|
|
77
|
+
try:
|
|
78
|
+
from sentence_transformers import (
|
|
79
|
+
CrossEncoder, # noqa: F401 — final attempt for the error path
|
|
80
|
+
)
|
|
81
|
+
except ImportError as e:
|
|
82
|
+
raise CrossEncoderUnavailableError(
|
|
83
|
+
"sentence-transformers is not installed; cannot use the "
|
|
84
|
+
"cross-encoder reranker"
|
|
85
|
+
) from e
|
|
86
|
+
return CrossEncoder
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _get_model() -> Any:
|
|
90
|
+
"""Return the lazy-loaded cross-encoder singleton.
|
|
91
|
+
|
|
92
|
+
The first caller pays the load cost (~3 s, weights cached under
|
|
93
|
+
``~/.cache/huggingface/`` after first download). Subsequent callers
|
|
94
|
+
get the warm instance immediately.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Any: A ``sentence_transformers.CrossEncoder`` instance.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
CrossEncoderUnavailableError: If ``sentence-transformers`` is not
|
|
101
|
+
importable, or if the underlying model fails to load.
|
|
102
|
+
"""
|
|
103
|
+
global _MODEL # noqa: PLW0603 — singleton-pattern intentional
|
|
104
|
+
if _MODEL is not None:
|
|
105
|
+
return _MODEL
|
|
106
|
+
with _MODEL_LOCK:
|
|
107
|
+
if _MODEL is not None:
|
|
108
|
+
return _MODEL
|
|
109
|
+
cross_encoder_cls = _import_cross_encoder()
|
|
110
|
+
try:
|
|
111
|
+
_MODEL = cross_encoder_cls(_MODEL_NAME)
|
|
112
|
+
except Exception as e: # noqa: BLE001 — surface as a typed failure
|
|
113
|
+
raise CrossEncoderUnavailableError(
|
|
114
|
+
f"Failed to load cross-encoder model {_MODEL_NAME!r}: {e}"
|
|
115
|
+
) from e
|
|
116
|
+
_LOGGER.info("Loaded cross-encoder model %s", _MODEL_NAME)
|
|
117
|
+
return _MODEL
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def score_pairs(query: str, docs: list[str]) -> list[float]:
|
|
121
|
+
"""Score ``(query, doc)`` pairs with the cross-encoder.
|
|
122
|
+
|
|
123
|
+
Higher score means more relevant. Scores are not bounded to a fixed
|
|
124
|
+
range — they are raw model logits — so callers should treat them as
|
|
125
|
+
opaque relative-ranking signal, not as probabilities.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
query (str): The reranking query.
|
|
129
|
+
docs (list[str]): Documents to score against ``query``.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
list[float]: One score per document, in the same order as
|
|
133
|
+
``docs``. Empty list when ``docs`` is empty.
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
CrossEncoderUnavailableError: If the cross-encoder cannot be
|
|
137
|
+
loaded (re-raised from :func:`_get_model`).
|
|
138
|
+
"""
|
|
139
|
+
if not docs:
|
|
140
|
+
return []
|
|
141
|
+
model = _get_model()
|
|
142
|
+
pairs = [(query, doc) for doc in docs]
|
|
143
|
+
raw_scores = model.predict(pairs)
|
|
144
|
+
# ``predict`` returns a numpy array; convert to plain Python floats so
|
|
145
|
+
# the caller can serialise the result without numpy as a dependency.
|
|
146
|
+
return [float(s) for s in raw_scores]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def prewarm() -> bool:
|
|
150
|
+
"""Force the cross-encoder model to load at startup.
|
|
151
|
+
|
|
152
|
+
Call once during app startup so the ~3s model load never lands on a user
|
|
153
|
+
query (and never serializes a concurrent burst behind the model lock).
|
|
154
|
+
Never raises — startup must not crash if the cross-encoder is unavailable
|
|
155
|
+
or fails to score the smoke input.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
True if the model loaded and scored a smoke input; False otherwise
|
|
159
|
+
(callers should treat the floor as degraded, not crash).
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
score_pairs("warmup", ["warmup"])
|
|
163
|
+
except CrossEncoderUnavailableError:
|
|
164
|
+
_LOGGER.warning(
|
|
165
|
+
"Cross-encoder unavailable at startup; relevance floor will degrade "
|
|
166
|
+
"to unfiltered results until the model is available."
|
|
167
|
+
)
|
|
168
|
+
return False
|
|
169
|
+
except Exception: # noqa: BLE001 — pre-warm must never crash startup
|
|
170
|
+
_LOGGER.warning(
|
|
171
|
+
"Cross-encoder pre-warm failed unexpectedly; relevance floor will "
|
|
172
|
+
"degrade to unfiltered results until the model is available.",
|
|
173
|
+
exc_info=True,
|
|
174
|
+
)
|
|
175
|
+
return False
|
|
176
|
+
_LOGGER.info("Cross-encoder pre-warmed.")
|
|
177
|
+
return True
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""LLM-based reranker for ``(query, document)`` pairs.
|
|
2
|
+
|
|
3
|
+
Sends a single LLM call with the query and a numbered list of candidate
|
|
4
|
+
facts, expects a JSON array of relevance scores (0-10) in the same order.
|
|
5
|
+
Fills the gap that cross-encoders can't bridge: world knowledge / brand
|
|
6
|
+
↔ category equivalence (e.g. "Thrive Market" = grocery service).
|
|
7
|
+
|
|
8
|
+
Usage
|
|
9
|
+
-----
|
|
10
|
+
|
|
11
|
+
>>> from reflexio.server.llm.rerank.llm_reranker import score_pairs_llm
|
|
12
|
+
>>> scores = score_pairs_llm(
|
|
13
|
+
... query="grocery store",
|
|
14
|
+
... docs=["Walmart $120 grocery", "Thrive Market organic"],
|
|
15
|
+
... llm_client=client, prompt_manager=pm,
|
|
16
|
+
... )
|
|
17
|
+
>>> # scores is a list[float] of length 2, or None on failure.
|
|
18
|
+
|
|
19
|
+
The helper returns ``None`` on any failure (no LLM client, prompt error,
|
|
20
|
+
LLM call error, malformed JSON, score-count mismatch). Callers should
|
|
21
|
+
fall back to the existing rerank order.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import logging
|
|
28
|
+
import re
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
_LOGGER = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
_PROMPT_ID = "rerank_relevance"
|
|
34
|
+
_LLM_TIMEOUT = 30.0
|
|
35
|
+
_LLM_MAX_RETRIES = 1
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _parse_scores(text: str, expected_n: int) -> list[float] | None:
|
|
39
|
+
"""Extract a JSON array of N numbers from raw LLM output.
|
|
40
|
+
|
|
41
|
+
Tolerates surrounding prose by locating the outermost ``[ ... ]`` span
|
|
42
|
+
via the first ``[`` and last ``]``. Returns ``None`` when the array
|
|
43
|
+
can't be parsed or the count doesn't match ``expected_n``.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
text (str): Raw LLM response.
|
|
47
|
+
expected_n (int): Number of scores expected.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
list[float] | None: Parsed scores, or ``None`` on any parse failure.
|
|
51
|
+
"""
|
|
52
|
+
start = text.find("[")
|
|
53
|
+
end = text.rfind("]")
|
|
54
|
+
if start == -1 or end <= start:
|
|
55
|
+
return None
|
|
56
|
+
try:
|
|
57
|
+
parsed = json.loads(text[start : end + 1])
|
|
58
|
+
except json.JSONDecodeError:
|
|
59
|
+
return None
|
|
60
|
+
if not isinstance(parsed, list) or len(parsed) != expected_n:
|
|
61
|
+
return None
|
|
62
|
+
try:
|
|
63
|
+
return [float(s) for s in parsed]
|
|
64
|
+
except (TypeError, ValueError):
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _format_docs_block(docs: list[str]) -> str:
|
|
69
|
+
"""Render docs as a numbered, single-line-per-fact block.
|
|
70
|
+
|
|
71
|
+
Newlines inside individual docs are replaced with spaces so the LLM
|
|
72
|
+
sees a clean ``i. <fact>`` structure that matches the prompt's
|
|
73
|
+
output rule (one score per numbered fact).
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
docs (list[str]): Candidate documents.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
str: One ``i. <doc>`` per line.
|
|
80
|
+
"""
|
|
81
|
+
flattened = (re.sub(r"\s+", " ", d).strip() for d in docs)
|
|
82
|
+
return "\n".join(f"{i + 1}. {d}" for i, d in enumerate(flattened))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def score_pairs_llm(
|
|
86
|
+
query: str,
|
|
87
|
+
docs: list[str],
|
|
88
|
+
llm_client: Any,
|
|
89
|
+
prompt_manager: Any,
|
|
90
|
+
timeout: float = _LLM_TIMEOUT,
|
|
91
|
+
) -> list[float] | None:
|
|
92
|
+
"""Score ``(query, doc)`` pairs via an LLM relevance-judge prompt.
|
|
93
|
+
|
|
94
|
+
A single LLM call is made for the entire batch; the model returns one
|
|
95
|
+
score per doc. The model is the LiteLLMClient's default — same model
|
|
96
|
+
that drives the search agent — chosen to keep configuration simple.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
query (str): The reranking query.
|
|
100
|
+
docs (list[str]): Documents to score against ``query``.
|
|
101
|
+
llm_client (Any): A ``LiteLLMClient`` (must expose ``generate_response``).
|
|
102
|
+
prompt_manager (Any): A ``PromptManager`` (must expose ``render_prompt``).
|
|
103
|
+
timeout (float): Per-call timeout in seconds.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
list[float] | None: One score per doc on success (same order as
|
|
107
|
+
``docs``); ``None`` on any failure so the caller can fall back.
|
|
108
|
+
"""
|
|
109
|
+
if not docs:
|
|
110
|
+
return []
|
|
111
|
+
if llm_client is None or prompt_manager is None:
|
|
112
|
+
return None
|
|
113
|
+
try:
|
|
114
|
+
prompt = prompt_manager.render_prompt(
|
|
115
|
+
_PROMPT_ID,
|
|
116
|
+
{
|
|
117
|
+
"query": query,
|
|
118
|
+
"docs_block": _format_docs_block(docs),
|
|
119
|
+
"num_docs": str(len(docs)),
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
except Exception as e: # noqa: BLE001 — prompt-render failure must not break search
|
|
123
|
+
_LOGGER.warning("LLM rerank: prompt render failed: %s", e)
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
result = llm_client.generate_response(
|
|
128
|
+
prompt,
|
|
129
|
+
timeout=timeout,
|
|
130
|
+
max_retries=_LLM_MAX_RETRIES,
|
|
131
|
+
temperature=0.0,
|
|
132
|
+
)
|
|
133
|
+
except Exception as e: # noqa: BLE001 — LLM-call failure must not break search
|
|
134
|
+
_LOGGER.warning("LLM rerank: generate_response failed: %s", e)
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
if not isinstance(result, str) or not result.strip():
|
|
138
|
+
_LOGGER.warning(
|
|
139
|
+
"LLM rerank: empty/non-string response (%r)", type(result).__name__
|
|
140
|
+
)
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
scores = _parse_scores(result, expected_n=len(docs))
|
|
144
|
+
if scores is None:
|
|
145
|
+
_LOGGER.warning(
|
|
146
|
+
"LLM rerank: parse failure (n=%d, raw[:200]=%r)", len(docs), result[:200]
|
|
147
|
+
)
|
|
148
|
+
return scores
|