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,197 @@
|
|
|
1
|
+
"""Parse the NDJSON stream from ``claude -p --output-format stream-json``
|
|
2
|
+
and classify whether the call ended in a credit/auth stall.
|
|
3
|
+
|
|
4
|
+
Public surface:
|
|
5
|
+
- parse_stream_json(stdout, exit_code, stderr_text) -> ParseResult
|
|
6
|
+
- classify_stall(result) -> "billing_error" | "auth_error" | None
|
|
7
|
+
- parse_reset_estimate(text) -> datetime | None
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import re
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from datetime import UTC, datetime, time, timedelta
|
|
17
|
+
|
|
18
|
+
from reflexio.models.api_schema.stall_state_schema import StallReason
|
|
19
|
+
|
|
20
|
+
_LOGGER = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
_BILLING_CATEGORIES = {"billing_error"}
|
|
23
|
+
_AUTH_CATEGORIES = {"authentication_failed", "oauth_org_not_allowed"}
|
|
24
|
+
|
|
25
|
+
_TEXT_PATTERNS_BILLING = (
|
|
26
|
+
"hit your weekly limit",
|
|
27
|
+
"hit your session limit",
|
|
28
|
+
"credit balance is too low",
|
|
29
|
+
"billing_error",
|
|
30
|
+
)
|
|
31
|
+
_TEXT_PATTERNS_AUTH = (
|
|
32
|
+
"please run /login",
|
|
33
|
+
"oauth token revoked",
|
|
34
|
+
"oauth token has expired",
|
|
35
|
+
"not logged in",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
_RESET_RE = re.compile(
|
|
39
|
+
r"resets\s+(?:(?P<weekday>mon|tue|wed|thu|fri|sat|sun)\w*\s+)?"
|
|
40
|
+
r"(?P<hour>\d{1,2}):(?P<minute>\d{2})\s*(?P<ampm>am|pm)",
|
|
41
|
+
re.IGNORECASE,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class ParseResult:
|
|
47
|
+
"""Aggregated state of one ``claude -p`` invocation.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
success (bool): True iff exit_code==0 AND a terminal result event
|
|
51
|
+
with non-empty text appeared.
|
|
52
|
+
terminal_text (str): The ``result`` field from the terminal event.
|
|
53
|
+
retry_errors (list[str]): All ``error`` strings observed in
|
|
54
|
+
``api_retry`` events, in order.
|
|
55
|
+
stderr_text (str): Raw stderr text from the subprocess.
|
|
56
|
+
raw_lines_parsed (int): NDJSON lines parsed successfully.
|
|
57
|
+
raw_lines_failed (int): NDJSON lines that failed to parse.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
success: bool
|
|
61
|
+
terminal_text: str
|
|
62
|
+
retry_errors: list[str] = field(default_factory=list)
|
|
63
|
+
stderr_text: str = ""
|
|
64
|
+
raw_lines_parsed: int = 0
|
|
65
|
+
raw_lines_failed: int = 0
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def stall_candidate(self) -> str | None:
|
|
69
|
+
"""The last retry error string in a stall category, or None."""
|
|
70
|
+
for err in reversed(self.retry_errors):
|
|
71
|
+
if err in _BILLING_CATEGORIES or err in _AUTH_CATEGORIES:
|
|
72
|
+
return err
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def parse_stream_json(
|
|
77
|
+
stdout: str,
|
|
78
|
+
*,
|
|
79
|
+
exit_code: int,
|
|
80
|
+
stderr_text: str = "",
|
|
81
|
+
) -> ParseResult:
|
|
82
|
+
"""Parse the full NDJSON stream from claude -p.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
stdout (str): Raw subprocess stdout — newline-delimited JSON events.
|
|
86
|
+
exit_code (int): Subprocess exit code.
|
|
87
|
+
stderr_text (str): Raw stderr — used by the text-fallback path.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
ParseResult: Aggregated state. ``success`` requires both a clean
|
|
91
|
+
exit and a parseable terminal ``result`` event.
|
|
92
|
+
"""
|
|
93
|
+
retry_errors: list[str] = []
|
|
94
|
+
terminal_text = ""
|
|
95
|
+
parsed = 0
|
|
96
|
+
failed = 0
|
|
97
|
+
saw_terminal = False
|
|
98
|
+
for line in stdout.splitlines():
|
|
99
|
+
if not line.strip():
|
|
100
|
+
continue
|
|
101
|
+
try:
|
|
102
|
+
event = json.loads(line)
|
|
103
|
+
except json.JSONDecodeError:
|
|
104
|
+
failed += 1
|
|
105
|
+
continue
|
|
106
|
+
parsed += 1
|
|
107
|
+
if not isinstance(event, dict):
|
|
108
|
+
continue
|
|
109
|
+
match event.get("type"), event.get("subtype"):
|
|
110
|
+
case ("system", "api_retry"):
|
|
111
|
+
err = event.get("error")
|
|
112
|
+
if isinstance(err, str):
|
|
113
|
+
retry_errors.append(err)
|
|
114
|
+
case ("result", _) | (_, "result"):
|
|
115
|
+
text = event.get("result")
|
|
116
|
+
if isinstance(text, str):
|
|
117
|
+
terminal_text = text
|
|
118
|
+
saw_terminal = True
|
|
119
|
+
return ParseResult(
|
|
120
|
+
success=(exit_code == 0 and saw_terminal and bool(terminal_text)),
|
|
121
|
+
terminal_text=terminal_text,
|
|
122
|
+
retry_errors=retry_errors,
|
|
123
|
+
stderr_text=stderr_text,
|
|
124
|
+
raw_lines_parsed=parsed,
|
|
125
|
+
raw_lines_failed=failed,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def classify_stall(result: ParseResult) -> StallReason | None:
|
|
130
|
+
"""Decide whether a finished ParseResult represents a stall.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
result (ParseResult): Output of :func:`parse_stream_json`.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
StallReason | None: ``"billing_error"`` or ``"auth_error"`` only
|
|
137
|
+
when the call terminated unsuccessfully AND a stall-class
|
|
138
|
+
signal was observed (event or text fallback).
|
|
139
|
+
"""
|
|
140
|
+
if result.success:
|
|
141
|
+
return None
|
|
142
|
+
candidate = result.stall_candidate
|
|
143
|
+
if candidate in _BILLING_CATEGORIES:
|
|
144
|
+
return "billing_error"
|
|
145
|
+
if candidate in _AUTH_CATEGORIES:
|
|
146
|
+
return "auth_error"
|
|
147
|
+
return _classify_from_text(result.stderr_text + " " + result.terminal_text)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _classify_from_text(text: str) -> StallReason | None:
|
|
151
|
+
"""Belt-and-suspenders: regex the raw error text after NDJSON has been parsed."""
|
|
152
|
+
lower = text.lower()
|
|
153
|
+
if any(p in lower for p in _TEXT_PATTERNS_BILLING):
|
|
154
|
+
_LOGGER.warning("Stall classified via text fallback (billing): %r", text[:200])
|
|
155
|
+
return "billing_error"
|
|
156
|
+
if any(p in lower for p in _TEXT_PATTERNS_AUTH):
|
|
157
|
+
_LOGGER.warning("Stall classified via text fallback (auth): %r", text[:200])
|
|
158
|
+
return "auth_error"
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def parse_reset_estimate(text: str) -> datetime | None:
|
|
163
|
+
"""Best-effort parse of a reset time from Claude Code error text.
|
|
164
|
+
|
|
165
|
+
The hour/minute extracted from the message text is treated as UTC, then
|
|
166
|
+
advanced one day if it has already passed. Claude Code's error messages
|
|
167
|
+
typically express reset times in the user's local timezone, so the
|
|
168
|
+
returned datetime may be off by the user's UTC offset (up to ±24h).
|
|
169
|
+
Callers should treat this as an approximation, not an authoritative
|
|
170
|
+
reset moment.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
text (str): Error text — typically the terminal event message or stderr.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
datetime | None: A UTC-naive-in-spirit but tz-aware datetime that
|
|
177
|
+
best approximates the next reset, or None when no recognizable
|
|
178
|
+
pattern is found.
|
|
179
|
+
"""
|
|
180
|
+
match = _RESET_RE.search(text or "")
|
|
181
|
+
if not match:
|
|
182
|
+
return None
|
|
183
|
+
hour_raw = int(match.group("hour"))
|
|
184
|
+
minute = int(match.group("minute"))
|
|
185
|
+
# The regex permits 1–2 digit hours and minutes; reject out-of-range
|
|
186
|
+
# values so "13:00pm" or "10:75am" parse as None instead of producing
|
|
187
|
+
# silently-wrong times via modulo arithmetic.
|
|
188
|
+
if not 1 <= hour_raw <= 12 or not 0 <= minute <= 59:
|
|
189
|
+
return None
|
|
190
|
+
hour = hour_raw % 12
|
|
191
|
+
if match.group("ampm").lower() == "pm":
|
|
192
|
+
hour += 12
|
|
193
|
+
today = datetime.now(UTC).date()
|
|
194
|
+
candidate = datetime.combine(today, time(hour, minute), tzinfo=UTC)
|
|
195
|
+
if candidate <= datetime.now(UTC):
|
|
196
|
+
candidate += timedelta(days=1)
|
|
197
|
+
return candidate
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""HTTP embedding service provider.
|
|
2
|
+
|
|
3
|
+
This module is the routing boundary between Reflexio's LLM client and an
|
|
4
|
+
OpenAI-compatible embedding service. It lets local Claude Smart installs keep
|
|
5
|
+
one model process alive across backend workers, Claude Code, and Codex, while
|
|
6
|
+
also supporting a horizontally-scaled internal service in cloud deployments.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import time
|
|
14
|
+
from typing import Any, Literal
|
|
15
|
+
|
|
16
|
+
import httpx
|
|
17
|
+
|
|
18
|
+
_LOGGER = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
EmbeddingProviderMode = Literal[
|
|
21
|
+
"cloud", "local_service", "internal_service", "inprocess", "off"
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
_ENV_PROVIDER = "REFLEXIO_EMBEDDING_PROVIDER"
|
|
25
|
+
_ENV_SERVICE_URL = "REFLEXIO_EMBEDDING_SERVICE_URL"
|
|
26
|
+
_ENV_TIMEOUT_MS = "REFLEXIO_EMBEDDING_SERVICE_TIMEOUT_MS"
|
|
27
|
+
_ENV_EMBEDDING_PORT = "EMBEDDING_PORT"
|
|
28
|
+
_ENV_CLAUDE_SMART_LOCAL = "CLAUDE_SMART_USE_LOCAL_EMBEDDING"
|
|
29
|
+
_DEFAULT_LOCAL_PORT = 8072
|
|
30
|
+
_DEFAULT_INTERNAL_SERVICE_TIMEOUT_MS = 2_000
|
|
31
|
+
_DEFAULT_LOCAL_SERVICE_TIMEOUT_MS = 30_000
|
|
32
|
+
_SERVICE_MODES = {"local_service", "internal_service"}
|
|
33
|
+
_VALID_MODES = {"cloud", *_SERVICE_MODES, "inprocess", "off"}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EmbeddingUnavailableError(RuntimeError):
|
|
37
|
+
"""Raised when the configured embedding provider is unavailable."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _local_service_url() -> str:
|
|
41
|
+
port = os.environ.get(_ENV_EMBEDDING_PORT, str(_DEFAULT_LOCAL_PORT))
|
|
42
|
+
return f"http://127.0.0.1:{port}"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def embedding_service_url(mode: EmbeddingProviderMode | None = None) -> str:
|
|
46
|
+
"""Return the configured embedding service URL.
|
|
47
|
+
|
|
48
|
+
``local_service`` defaults to ``127.0.0.1:$EMBEDDING_PORT`` so local
|
|
49
|
+
Claude Smart hosts can share a single machine daemon. ``internal_service``
|
|
50
|
+
requires an explicit URL because it is deployment-specific.
|
|
51
|
+
"""
|
|
52
|
+
resolved = mode or embedding_provider_mode()
|
|
53
|
+
configured = os.environ.get(_ENV_SERVICE_URL)
|
|
54
|
+
if configured:
|
|
55
|
+
return configured.rstrip("/")
|
|
56
|
+
if resolved == "local_service":
|
|
57
|
+
return _local_service_url()
|
|
58
|
+
raise EmbeddingUnavailableError(
|
|
59
|
+
f"{_ENV_SERVICE_URL} is required when {_ENV_PROVIDER}={resolved}"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def embedding_service_timeout_seconds(
|
|
64
|
+
mode: EmbeddingProviderMode | None = None,
|
|
65
|
+
) -> float:
|
|
66
|
+
raw = os.environ.get(_ENV_TIMEOUT_MS)
|
|
67
|
+
if raw is None:
|
|
68
|
+
resolved = mode or embedding_provider_mode()
|
|
69
|
+
if resolved == "local_service":
|
|
70
|
+
return _DEFAULT_LOCAL_SERVICE_TIMEOUT_MS / 1000
|
|
71
|
+
return _DEFAULT_INTERNAL_SERVICE_TIMEOUT_MS / 1000
|
|
72
|
+
try:
|
|
73
|
+
timeout_ms = int(raw)
|
|
74
|
+
except ValueError as exc:
|
|
75
|
+
raise EmbeddingUnavailableError(
|
|
76
|
+
f"{_ENV_TIMEOUT_MS} must be an integer number of milliseconds"
|
|
77
|
+
) from exc
|
|
78
|
+
return max(timeout_ms, 1) / 1000
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _ordered_embeddings_from_response(
|
|
82
|
+
data: Any, expected_count: int
|
|
83
|
+
) -> list[list[float]]:
|
|
84
|
+
if not isinstance(data, list):
|
|
85
|
+
raise ValueError("embedding service response is missing data[]")
|
|
86
|
+
if len(data) != expected_count:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
"embedding service response cardinality mismatch: "
|
|
89
|
+
f"expected {expected_count}, got {len(data)}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
seen: set[int] = set()
|
|
93
|
+
indexed_embeddings: list[tuple[int, list[Any]]] = []
|
|
94
|
+
for item in data:
|
|
95
|
+
if not isinstance(item, dict):
|
|
96
|
+
raise ValueError("embedding service response data[] has invalid item")
|
|
97
|
+
|
|
98
|
+
index = item.get("index")
|
|
99
|
+
if type(index) is not int:
|
|
100
|
+
raise ValueError("embedding service response has invalid index")
|
|
101
|
+
if index in seen:
|
|
102
|
+
raise ValueError(f"embedding service response has duplicate index {index}")
|
|
103
|
+
seen.add(index)
|
|
104
|
+
|
|
105
|
+
embedding = item.get("embedding")
|
|
106
|
+
if not isinstance(embedding, list):
|
|
107
|
+
raise ValueError("embedding service response has invalid embeddings")
|
|
108
|
+
indexed_embeddings.append((index, embedding))
|
|
109
|
+
|
|
110
|
+
expected_indices = set(range(expected_count))
|
|
111
|
+
if seen != expected_indices:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"embedding service response indices mismatch: "
|
|
114
|
+
f"expected {sorted(expected_indices)}, got {sorted(seen)}"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return [
|
|
118
|
+
[float(value) for value in embedding]
|
|
119
|
+
for _, embedding in sorted(indexed_embeddings)
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def embedding_provider_mode(model: str | None = None) -> EmbeddingProviderMode:
|
|
124
|
+
"""Resolve the embedding provider mode for a model.
|
|
125
|
+
|
|
126
|
+
Backwards compatibility: a ``local/*`` model still uses the in-process
|
|
127
|
+
embedder unless the user explicitly chooses a mode or Claude Smart's legacy
|
|
128
|
+
``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1`` flag is present. That legacy flag
|
|
129
|
+
now means "use the shared local service" by default.
|
|
130
|
+
"""
|
|
131
|
+
configured = os.environ.get(_ENV_PROVIDER)
|
|
132
|
+
if configured:
|
|
133
|
+
mode = configured.strip().lower()
|
|
134
|
+
if mode not in _VALID_MODES:
|
|
135
|
+
raise EmbeddingUnavailableError(
|
|
136
|
+
f"Invalid {_ENV_PROVIDER}={configured!r}; expected one of "
|
|
137
|
+
f"{', '.join(sorted(_VALID_MODES))}"
|
|
138
|
+
)
|
|
139
|
+
return mode # type: ignore[return-value]
|
|
140
|
+
|
|
141
|
+
if os.environ.get(_ENV_CLAUDE_SMART_LOCAL) == "1":
|
|
142
|
+
return "local_service"
|
|
143
|
+
|
|
144
|
+
if os.environ.get(_ENV_SERVICE_URL):
|
|
145
|
+
return "internal_service"
|
|
146
|
+
|
|
147
|
+
if model and model.startswith("local/"):
|
|
148
|
+
return "inprocess"
|
|
149
|
+
return "cloud"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def should_use_embedding_service(model: str) -> bool:
|
|
153
|
+
"""Return True when embedding requests should call the HTTP service."""
|
|
154
|
+
return embedding_provider_mode(model) in _SERVICE_MODES
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_service_embeddings(
|
|
158
|
+
texts: list[str],
|
|
159
|
+
*,
|
|
160
|
+
model: str,
|
|
161
|
+
dimensions: int | None = None,
|
|
162
|
+
) -> list[list[float]]:
|
|
163
|
+
"""Call the OpenAI-compatible embedding service.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
texts: Inputs to embed.
|
|
167
|
+
model: Embedding model name.
|
|
168
|
+
dimensions: Optional embedding dimensions.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Embeddings in input order.
|
|
172
|
+
|
|
173
|
+
Raises:
|
|
174
|
+
EmbeddingUnavailableError: If the service cannot be reached or returns
|
|
175
|
+
an invalid response.
|
|
176
|
+
"""
|
|
177
|
+
if not texts:
|
|
178
|
+
return []
|
|
179
|
+
|
|
180
|
+
mode = embedding_provider_mode(model)
|
|
181
|
+
if mode == "off":
|
|
182
|
+
raise EmbeddingUnavailableError("Embedding provider is disabled")
|
|
183
|
+
if mode not in _SERVICE_MODES:
|
|
184
|
+
raise EmbeddingUnavailableError(
|
|
185
|
+
f"Embedding service requested while {_ENV_PROVIDER}={mode}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
url = f"{embedding_service_url(mode)}/v1/embeddings"
|
|
189
|
+
payload: dict[str, Any] = {"model": model, "input": texts}
|
|
190
|
+
if dimensions:
|
|
191
|
+
payload["dimensions"] = dimensions
|
|
192
|
+
|
|
193
|
+
timeout = embedding_service_timeout_seconds(mode)
|
|
194
|
+
last_error: Exception | None = None
|
|
195
|
+
for attempt in range(2):
|
|
196
|
+
try:
|
|
197
|
+
with httpx.Client(timeout=timeout) as client:
|
|
198
|
+
response = client.post(url, json=payload)
|
|
199
|
+
response.raise_for_status()
|
|
200
|
+
body = response.json()
|
|
201
|
+
return _ordered_embeddings_from_response(body.get("data"), len(texts))
|
|
202
|
+
except Exception as exc: # noqa: BLE001 - normalized to typed degradation signal
|
|
203
|
+
last_error = exc
|
|
204
|
+
if attempt == 0:
|
|
205
|
+
time.sleep(0.1)
|
|
206
|
+
|
|
207
|
+
_LOGGER.warning("Embedding service unavailable at %s: %s", url, last_error)
|
|
208
|
+
raise EmbeddingUnavailableError(
|
|
209
|
+
f"Embedding service unavailable at {url}: {last_error}"
|
|
210
|
+
) from last_error
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Local in-process embedder using Chroma's ONNX all-MiniLM-L6-v2.
|
|
2
|
+
|
|
3
|
+
Lets reflexio run without any external embedding API key. Activation is
|
|
4
|
+
opt-in via ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1`` and requires the
|
|
5
|
+
``chromadb`` pip package to be installed (we re-use its packaged ONNX
|
|
6
|
+
model + tokenizer rather than re-bundling them).
|
|
7
|
+
|
|
8
|
+
The model natively produces 384-dim vectors; reflexio's storage schema
|
|
9
|
+
expects 512 dims (``EMBEDDING_DIMENSIONS`` in the vec0 virtual tables).
|
|
10
|
+
We zero-pad each vector to 512 inside this module so the rest of
|
|
11
|
+
reflexio is unchanged. Cosine similarity is preserved on the 384-dim
|
|
12
|
+
subspace — safe as long as *all* embeddings in a given DB come from
|
|
13
|
+
this provider (mixing providers has always required a DB wipe).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import importlib.util
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import threading
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
_LOGGER = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
_ENV_ENABLE = "CLAUDE_SMART_USE_LOCAL_EMBEDDING"
|
|
27
|
+
_MODEL_KEY = "local/minilm-l6-v2"
|
|
28
|
+
|
|
29
|
+
# Reflexio's storage schema (vec0 virtual tables) expects this dimension.
|
|
30
|
+
# MiniLM-L6-v2 natively produces 384; we pad with zeros to _TARGET_DIM.
|
|
31
|
+
_NATIVE_DIM = 384
|
|
32
|
+
_TARGET_DIM = 512
|
|
33
|
+
|
|
34
|
+
# Conservative character budget to stay under MiniLM's 256-token hard cap.
|
|
35
|
+
# ~4 chars/token in English prose; leave headroom so we never raise the
|
|
36
|
+
# ValueError that ONNXMiniLM_L6_V2 throws on over-length input.
|
|
37
|
+
_MAX_CHARS = 800
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class LocalEmbedderError(RuntimeError):
|
|
41
|
+
"""Raised when the local embedder is called without chromadb installed."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class LocalEmbedder:
|
|
45
|
+
"""Lazily-loaded singleton wrapping Chroma's ONNXMiniLM_L6_V2."""
|
|
46
|
+
|
|
47
|
+
_instance: LocalEmbedder | None = None
|
|
48
|
+
_lock = threading.Lock()
|
|
49
|
+
|
|
50
|
+
def __init__(self) -> None:
|
|
51
|
+
self._ef: Any | None = None
|
|
52
|
+
self._ef_lock = threading.Lock()
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def get(cls) -> LocalEmbedder:
|
|
56
|
+
"""Return the process-wide singleton, constructing it on first use."""
|
|
57
|
+
if cls._instance is None:
|
|
58
|
+
with cls._lock:
|
|
59
|
+
if cls._instance is None:
|
|
60
|
+
cls._instance = cls()
|
|
61
|
+
return cls._instance
|
|
62
|
+
|
|
63
|
+
def _load(self) -> Any:
|
|
64
|
+
"""Lazy-import and instantiate the ONNX embedding function."""
|
|
65
|
+
if self._ef is not None:
|
|
66
|
+
return self._ef
|
|
67
|
+
with self._ef_lock:
|
|
68
|
+
if self._ef is not None:
|
|
69
|
+
return self._ef
|
|
70
|
+
try:
|
|
71
|
+
from chromadb.utils.embedding_functions import ( # type: ignore[import-not-found]
|
|
72
|
+
ONNXMiniLM_L6_V2,
|
|
73
|
+
)
|
|
74
|
+
except ImportError as exc:
|
|
75
|
+
raise LocalEmbedderError(
|
|
76
|
+
f"{_ENV_ENABLE}=1 but `chromadb` is not installed. "
|
|
77
|
+
"Install with `uv add chromadb` or `pip install chromadb`."
|
|
78
|
+
) from exc
|
|
79
|
+
self._ef = ONNXMiniLM_L6_V2()
|
|
80
|
+
_LOGGER.info(
|
|
81
|
+
"Initialized local ONNX embedder (model=%s, cache=%s)",
|
|
82
|
+
ONNXMiniLM_L6_V2.MODEL_NAME,
|
|
83
|
+
ONNXMiniLM_L6_V2.DOWNLOAD_PATH,
|
|
84
|
+
)
|
|
85
|
+
return self._ef
|
|
86
|
+
|
|
87
|
+
def embed(self, texts: list[str]) -> list[list[float]]:
|
|
88
|
+
"""Embed a batch of documents, returning 512-dim padded vectors.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
texts: Documents to embed. Each is truncated to ``_MAX_CHARS``
|
|
92
|
+
characters to stay under the 256-token cap of MiniLM-L6-v2.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
list[list[float]]: One vector per input, each exactly
|
|
96
|
+
``_TARGET_DIM`` (512) floats with the last 128 positions
|
|
97
|
+
zero-padded.
|
|
98
|
+
"""
|
|
99
|
+
ef = self._load()
|
|
100
|
+
safe_inputs = [(text or "")[:_MAX_CHARS] for text in texts]
|
|
101
|
+
raw = ef(safe_inputs)
|
|
102
|
+
return [_pad(vec) for vec in raw]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _pad(vec: Any) -> list[float]:
|
|
106
|
+
"""Zero-pad a 384-dim vector to ``_TARGET_DIM`` as a plain list[float]."""
|
|
107
|
+
as_list = list(vec) if not isinstance(vec, list) else vec
|
|
108
|
+
floats = [float(x) for x in as_list]
|
|
109
|
+
if len(floats) == _TARGET_DIM:
|
|
110
|
+
return floats
|
|
111
|
+
if len(floats) > _TARGET_DIM:
|
|
112
|
+
return floats[:_TARGET_DIM]
|
|
113
|
+
return floats + [0.0] * (_TARGET_DIM - len(floats))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
_REGISTERED = False
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def is_chromadb_importable() -> bool:
|
|
120
|
+
"""Return True when the ``chromadb`` package is importable.
|
|
121
|
+
|
|
122
|
+
Independent of :data:`_ENV_ENABLE` (``CLAUDE_SMART_USE_LOCAL_EMBEDDING``).
|
|
123
|
+
Used by callers that want to know whether the local fallback is
|
|
124
|
+
*possible* regardless of whether claude-smart has explicitly opted in.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
bool: True if ``importlib.util.find_spec("chromadb")`` finds the
|
|
128
|
+
package, False otherwise.
|
|
129
|
+
"""
|
|
130
|
+
return importlib.util.find_spec("chromadb") is not None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def register_if_chromadb_available() -> bool:
|
|
134
|
+
"""Make the local embedder available whenever ``chromadb`` imports.
|
|
135
|
+
|
|
136
|
+
This is the env-var-independent companion to
|
|
137
|
+
:func:`register_if_enabled`. Called once from ``litellm_client`` at
|
|
138
|
+
module import so the local-embedder dispatch is wired up regardless
|
|
139
|
+
of whether claude-smart's opt-in env var is set. Idempotent — safe
|
|
140
|
+
to call more than once per process.
|
|
141
|
+
|
|
142
|
+
The actual routing is done by a prefix check in
|
|
143
|
+
``LiteLLMClient.get_embedding(s)``; this function's job is to
|
|
144
|
+
eagerly probe for ``chromadb`` and log a clear message.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
bool: True if the embedder is usable after this call, False
|
|
148
|
+
when ``chromadb`` is not importable.
|
|
149
|
+
"""
|
|
150
|
+
global _REGISTERED
|
|
151
|
+
if _REGISTERED:
|
|
152
|
+
return True
|
|
153
|
+
if not is_chromadb_importable():
|
|
154
|
+
_LOGGER.debug("Local embedder not registered: `chromadb` is not installed.")
|
|
155
|
+
return False
|
|
156
|
+
_REGISTERED = True
|
|
157
|
+
_LOGGER.info("Local embedding provider enabled (model=%s)", _MODEL_KEY)
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def register_if_enabled() -> bool:
|
|
162
|
+
"""Backwards-compatible alias for :func:`register_if_chromadb_available`.
|
|
163
|
+
|
|
164
|
+
Historically gated on ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1``; now
|
|
165
|
+
delegates to the chromadb-only check so the local embedder is also
|
|
166
|
+
available as a silent fallback when no cloud embedder is configured.
|
|
167
|
+
The claude-smart opt-in env var continues to drive
|
|
168
|
+
:func:`is_local_embedder_available` (provider-priority ordering),
|
|
169
|
+
which is the public contract the env var actually owns.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
bool: True if the embedder is usable after this call, False
|
|
173
|
+
otherwise.
|
|
174
|
+
"""
|
|
175
|
+
return register_if_chromadb_available()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def is_enabled() -> bool:
|
|
179
|
+
"""Return True when a previous registration call has succeeded.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
bool: True if the provider is currently registered and usable in
|
|
183
|
+
this process, False otherwise.
|
|
184
|
+
"""
|
|
185
|
+
return _REGISTERED
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def is_local_embedder_available() -> bool:
|
|
189
|
+
"""Return True iff both the env flag is set and ``chromadb`` imports.
|
|
190
|
+
|
|
191
|
+
Unlike :func:`is_enabled`, this does not require
|
|
192
|
+
:func:`register_if_enabled` to have run. It is the predicate
|
|
193
|
+
``model_defaults.detect_available_providers`` uses to decide whether
|
|
194
|
+
to surface ``"local"`` as an option.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
bool: True when ``CLAUDE_SMART_USE_LOCAL_EMBEDDING=1``
|
|
198
|
+
AND ``chromadb`` is importable.
|
|
199
|
+
"""
|
|
200
|
+
if os.environ.get(_ENV_ENABLE) != "1":
|
|
201
|
+
return False
|
|
202
|
+
return is_chromadb_importable()
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
__all__ = [
|
|
206
|
+
"LocalEmbedder",
|
|
207
|
+
"LocalEmbedderError",
|
|
208
|
+
"is_chromadb_importable",
|
|
209
|
+
"is_enabled",
|
|
210
|
+
"is_local_embedder_available",
|
|
211
|
+
"register_if_chromadb_available",
|
|
212
|
+
"register_if_enabled",
|
|
213
|
+
]
|