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,281 @@
|
|
|
1
|
+
"""Orchestrates the Braintrust connector: connect, select-projects, sync, status.
|
|
2
|
+
|
|
3
|
+
The service is stateless across requests; it loads the connection from
|
|
4
|
+
storage on each call. API keys are encrypted in storage and decrypted
|
|
5
|
+
only when needed for a Braintrust HTTP call.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
from reflexio.models.api_schema.braintrust_schema import (
|
|
16
|
+
BraintrustConnection,
|
|
17
|
+
BraintrustProjectSummary,
|
|
18
|
+
BraintrustStatusResponse,
|
|
19
|
+
BraintrustWorkspaceSummary,
|
|
20
|
+
ConnectBraintrustRequest,
|
|
21
|
+
ConnectBraintrustResponse,
|
|
22
|
+
ImportedScore,
|
|
23
|
+
SelectProjectsRequest,
|
|
24
|
+
SelectProjectsResponse,
|
|
25
|
+
SyncBraintrustResponse,
|
|
26
|
+
)
|
|
27
|
+
from reflexio.server.services.braintrust._encryption import decrypt, encrypt
|
|
28
|
+
from reflexio.server.services.braintrust.client import (
|
|
29
|
+
DEFAULT_BASE_URL,
|
|
30
|
+
BraintrustAuthError,
|
|
31
|
+
BraintrustClient,
|
|
32
|
+
BraintrustHTTPError,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _default_client_factory(api_key: str) -> BraintrustClient:
|
|
37
|
+
"""Default factory that honors `BRAINTRUST_BASE_URL` env override.
|
|
38
|
+
|
|
39
|
+
Set the env var to point at a staging or mock Braintrust instance
|
|
40
|
+
(useful for dev / cron testing without hitting the real API).
|
|
41
|
+
"""
|
|
42
|
+
import os
|
|
43
|
+
|
|
44
|
+
base_url = os.environ.get("BRAINTRUST_BASE_URL", "").strip() or DEFAULT_BASE_URL
|
|
45
|
+
return BraintrustClient(api_key, base_url=base_url)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
_METADATA_SESSION_KEY = "reflexio_session_id"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class BraintrustConnectorService:
|
|
56
|
+
"""Top-level orchestrator for the Braintrust connector.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
storage: BaseStorage-like; uses save/get/delete_braintrust_connection
|
|
60
|
+
and save_imported_scores. Default no-op storage methods keep
|
|
61
|
+
this workable until per-backend implementations land.
|
|
62
|
+
org_id (str): The Reflexio org for which the connector operates.
|
|
63
|
+
client_factory (Callable[[str], BraintrustClient]): Factory that
|
|
64
|
+
takes an API key and returns a BraintrustClient. Injectable so
|
|
65
|
+
tests can pass mocks.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
storage: object
|
|
69
|
+
org_id: str
|
|
70
|
+
client_factory: Callable[[str], BraintrustClient] = field(
|
|
71
|
+
default=_default_client_factory
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# ------------------------------------------------------------------
|
|
75
|
+
# Public API
|
|
76
|
+
# ------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def connect(self, request: ConnectBraintrustRequest) -> ConnectBraintrustResponse:
|
|
79
|
+
"""Step 1: validate the API key and return the workspace/project tree.
|
|
80
|
+
|
|
81
|
+
Persists nothing — the caller chooses which projects to sync and
|
|
82
|
+
then calls `select_projects` with the same key.
|
|
83
|
+
"""
|
|
84
|
+
client = self.client_factory(request.api_key)
|
|
85
|
+
try:
|
|
86
|
+
if not client.validate_key():
|
|
87
|
+
return ConnectBraintrustResponse(
|
|
88
|
+
success=False, msg="Braintrust rejected the API key."
|
|
89
|
+
)
|
|
90
|
+
workspaces = self._fetch_workspace_tree(client)
|
|
91
|
+
except BraintrustAuthError:
|
|
92
|
+
return ConnectBraintrustResponse(
|
|
93
|
+
success=False, msg="Braintrust rejected the API key."
|
|
94
|
+
)
|
|
95
|
+
except BraintrustHTTPError as e:
|
|
96
|
+
return ConnectBraintrustResponse(
|
|
97
|
+
success=False, msg=f"Braintrust HTTP error: {e}"
|
|
98
|
+
)
|
|
99
|
+
finally:
|
|
100
|
+
client.close()
|
|
101
|
+
|
|
102
|
+
return ConnectBraintrustResponse(success=True, workspaces=workspaces, msg="")
|
|
103
|
+
|
|
104
|
+
def select_projects(self, request: SelectProjectsRequest) -> SelectProjectsResponse:
|
|
105
|
+
"""Step 2: persist the connection with the selected projects.
|
|
106
|
+
|
|
107
|
+
Stores the API key encrypted. Overwrites any existing connection
|
|
108
|
+
for the org.
|
|
109
|
+
"""
|
|
110
|
+
connection = BraintrustConnection(
|
|
111
|
+
org_id=self.org_id,
|
|
112
|
+
api_key_enc=encrypt(request.api_key),
|
|
113
|
+
workspace_id=request.workspace_id,
|
|
114
|
+
workspace_name=request.workspace_name,
|
|
115
|
+
project_ids=request.project_ids,
|
|
116
|
+
last_sync_ts=None,
|
|
117
|
+
last_error=None,
|
|
118
|
+
)
|
|
119
|
+
self._save_connection(connection)
|
|
120
|
+
return SelectProjectsResponse(success=True, msg="Connected.")
|
|
121
|
+
|
|
122
|
+
def status(self) -> BraintrustStatusResponse:
|
|
123
|
+
"""Return whether the org is connected + sync state. Never echoes the key."""
|
|
124
|
+
connection = self._load_connection()
|
|
125
|
+
if connection is None:
|
|
126
|
+
return BraintrustStatusResponse(connected=False)
|
|
127
|
+
return BraintrustStatusResponse(
|
|
128
|
+
connected=True,
|
|
129
|
+
workspace_id=connection.workspace_id,
|
|
130
|
+
workspace_name=connection.workspace_name,
|
|
131
|
+
project_count=len(connection.project_ids),
|
|
132
|
+
last_sync_ts=connection.last_sync_ts,
|
|
133
|
+
last_error=connection.last_error,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def disconnect(self) -> None:
|
|
137
|
+
"""Delete the persisted connection."""
|
|
138
|
+
self.storage.delete_braintrust_connection(self.org_id) # type: ignore[attr-defined]
|
|
139
|
+
|
|
140
|
+
def sync_once(self, backfill_days: int = 90) -> SyncBraintrustResponse:
|
|
141
|
+
"""One-shot sync: walk projects → experiments → spans → write scores.
|
|
142
|
+
|
|
143
|
+
Updates `last_sync_ts` and `last_error` on the persisted connection.
|
|
144
|
+
Idempotent at the storage layer (per-backend overrides should
|
|
145
|
+
upsert by (source, source_run_id, scorer_name)).
|
|
146
|
+
"""
|
|
147
|
+
connection = self._load_connection()
|
|
148
|
+
if connection is None:
|
|
149
|
+
return SyncBraintrustResponse(
|
|
150
|
+
success=False, msg="Not connected to Braintrust."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
api_key = decrypt(connection.api_key_enc)
|
|
155
|
+
except Exception as e: # noqa: BLE001
|
|
156
|
+
return SyncBraintrustResponse(
|
|
157
|
+
success=False, msg=f"Failed to decrypt API key: {e}"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
since_ts = connection.last_sync_ts
|
|
161
|
+
if since_ts is None:
|
|
162
|
+
since_ts = max(0, int(time.time()) - backfill_days * 24 * 60 * 60)
|
|
163
|
+
|
|
164
|
+
all_scores: list[ImportedScore] = []
|
|
165
|
+
client = self.client_factory(api_key)
|
|
166
|
+
try:
|
|
167
|
+
for project_id in connection.project_ids:
|
|
168
|
+
experiments = client.list_experiments(project_id, since_ts=since_ts)
|
|
169
|
+
for exp in experiments:
|
|
170
|
+
spans = client.list_spans(exp["id"])
|
|
171
|
+
all_scores.extend(_scores_from_spans(spans, self.org_id))
|
|
172
|
+
except BraintrustAuthError:
|
|
173
|
+
self._persist_sync_outcome(connection, error="API key invalid.")
|
|
174
|
+
return SyncBraintrustResponse(
|
|
175
|
+
success=False, msg="API key invalid; halting sync."
|
|
176
|
+
)
|
|
177
|
+
except BraintrustHTTPError as e:
|
|
178
|
+
self._persist_sync_outcome(connection, error=str(e))
|
|
179
|
+
return SyncBraintrustResponse(success=False, msg=str(e))
|
|
180
|
+
finally:
|
|
181
|
+
client.close()
|
|
182
|
+
|
|
183
|
+
self.storage.save_imported_scores(all_scores) # type: ignore[attr-defined]
|
|
184
|
+
self._persist_sync_outcome(connection, error=None)
|
|
185
|
+
return SyncBraintrustResponse(
|
|
186
|
+
success=True, scored_count=len(all_scores), msg=""
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# ------------------------------------------------------------------
|
|
190
|
+
# Internals
|
|
191
|
+
# ------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
def _fetch_workspace_tree(
|
|
194
|
+
self, client: BraintrustClient
|
|
195
|
+
) -> list[BraintrustWorkspaceSummary]:
|
|
196
|
+
"""Pull workspaces + their projects in one shot for the connect UX."""
|
|
197
|
+
result: list[BraintrustWorkspaceSummary] = []
|
|
198
|
+
for ws in client.list_organizations():
|
|
199
|
+
workspace_id = str(ws.get("id", ""))
|
|
200
|
+
workspace_name = str(ws.get("name", ""))
|
|
201
|
+
if not workspace_id:
|
|
202
|
+
continue
|
|
203
|
+
projects_raw = client.list_projects(workspace_id)
|
|
204
|
+
projects = [
|
|
205
|
+
BraintrustProjectSummary(
|
|
206
|
+
project_id=str(p.get("id", "")),
|
|
207
|
+
project_name=str(p.get("name", "")),
|
|
208
|
+
)
|
|
209
|
+
for p in projects_raw
|
|
210
|
+
if p.get("id")
|
|
211
|
+
]
|
|
212
|
+
result.append(
|
|
213
|
+
BraintrustWorkspaceSummary(
|
|
214
|
+
workspace_id=workspace_id,
|
|
215
|
+
workspace_name=workspace_name,
|
|
216
|
+
projects=projects,
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
return result
|
|
220
|
+
|
|
221
|
+
def _load_connection(self) -> BraintrustConnection | None:
|
|
222
|
+
return self.storage.get_braintrust_connection(self.org_id) # type: ignore[attr-defined]
|
|
223
|
+
|
|
224
|
+
def _save_connection(self, connection: BraintrustConnection) -> None:
|
|
225
|
+
self.storage.save_braintrust_connection(connection) # type: ignore[attr-defined]
|
|
226
|
+
|
|
227
|
+
def _persist_sync_outcome(
|
|
228
|
+
self, connection: BraintrustConnection, *, error: str | None
|
|
229
|
+
) -> None:
|
|
230
|
+
update: dict[str, int | str | None] = {"last_error": error}
|
|
231
|
+
if error is None:
|
|
232
|
+
update["last_sync_ts"] = int(time.time())
|
|
233
|
+
updated = connection.model_copy(update=update)
|
|
234
|
+
self._save_connection(updated)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _scores_from_spans(
|
|
238
|
+
spans: list[dict],
|
|
239
|
+
org_id: str,
|
|
240
|
+
) -> list[ImportedScore]:
|
|
241
|
+
"""Extract ImportedScore rows from a list of Braintrust spans.
|
|
242
|
+
|
|
243
|
+
A single span may carry multiple scorers — one row per scorer.
|
|
244
|
+
`session_id` is pulled from `span.metadata.reflexio_session_id` when
|
|
245
|
+
present (the progressive-matching upgrade path).
|
|
246
|
+
"""
|
|
247
|
+
out: list[ImportedScore] = []
|
|
248
|
+
for span in spans:
|
|
249
|
+
span_id = str(span.get("id", ""))
|
|
250
|
+
if not span_id:
|
|
251
|
+
continue
|
|
252
|
+
ts_raw = span.get("created_at", 0)
|
|
253
|
+
try:
|
|
254
|
+
ts = int(ts_raw)
|
|
255
|
+
except (TypeError, ValueError):
|
|
256
|
+
ts = 0
|
|
257
|
+
metadata = span.get("metadata") or {}
|
|
258
|
+
session_id: str | None = None
|
|
259
|
+
if isinstance(metadata, dict):
|
|
260
|
+
v = metadata.get(_METADATA_SESSION_KEY)
|
|
261
|
+
if isinstance(v, str) and v:
|
|
262
|
+
session_id = v
|
|
263
|
+
scores = span.get("scores") or {}
|
|
264
|
+
if not isinstance(scores, dict):
|
|
265
|
+
continue
|
|
266
|
+
for scorer_name, value in scores.items():
|
|
267
|
+
try:
|
|
268
|
+
fvalue = float(value)
|
|
269
|
+
except (TypeError, ValueError):
|
|
270
|
+
continue
|
|
271
|
+
out.append(
|
|
272
|
+
ImportedScore(
|
|
273
|
+
org_id=org_id,
|
|
274
|
+
source_run_id=span_id,
|
|
275
|
+
session_id=session_id,
|
|
276
|
+
scorer_name=str(scorer_name),
|
|
277
|
+
value=fvalue,
|
|
278
|
+
ts=ts,
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
return out
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any, ClassVar
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from reflexio.models.config_schema import Config, StorageConfig, StorageConfigTest
|
|
11
|
+
from reflexio.server.services.configurator.config_storage import ConfigStorage
|
|
12
|
+
from reflexio.server.services.storage.error import StorageError
|
|
13
|
+
from reflexio.server.services.storage.storage_base import BaseStorage
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
_CONFIG_NAME_ALIASES = {
|
|
18
|
+
"batch_size": "window_size",
|
|
19
|
+
"batch_interval": "stride_size",
|
|
20
|
+
"extraction_window_size": "window_size",
|
|
21
|
+
"extraction_window_stride": "stride_size",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BaseConfigurator(ABC):
|
|
26
|
+
"""Abstract base for organization configurators.
|
|
27
|
+
|
|
28
|
+
Subclasses must:
|
|
29
|
+
- Set ``_STORAGE_FACTORIES`` and ``_STORAGE_READINESS_CHECKS`` class variables.
|
|
30
|
+
- Implement ``_select_config_storage`` to choose the config persistence backend.
|
|
31
|
+
|
|
32
|
+
All shared configuration and storage-dispatch logic lives here so that OS
|
|
33
|
+
and enterprise implementations only override what differs.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
_STORAGE_FACTORIES: ClassVar[dict[type[StorageConfig], Callable[..., BaseStorage]]]
|
|
37
|
+
_STORAGE_READINESS_CHECKS: ClassVar[
|
|
38
|
+
dict[type[StorageConfig], Callable[[Any], bool]]
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
org_id: str,
|
|
44
|
+
base_dir: str | None = None,
|
|
45
|
+
config: Config | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
self.org_id = org_id
|
|
48
|
+
self.base_dir = base_dir
|
|
49
|
+
|
|
50
|
+
self.config_storage = self._select_config_storage(org_id, base_dir)
|
|
51
|
+
if not config:
|
|
52
|
+
self.config = self.config_storage.load_config()
|
|
53
|
+
else:
|
|
54
|
+
self.config = config
|
|
55
|
+
|
|
56
|
+
if not self.config:
|
|
57
|
+
raise ValueError(f"Failed to load configuration for organization {org_id}")
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def _select_config_storage(
|
|
61
|
+
self, org_id: str, base_dir: str | None
|
|
62
|
+
) -> ConfigStorage:
|
|
63
|
+
"""Select and return the appropriate ConfigStorage backend.
|
|
64
|
+
|
|
65
|
+
This is the primary extension point: OS uses LocalJson only, while
|
|
66
|
+
enterprise uses a multi-tier priority system.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
# ==========================
|
|
70
|
+
# Configuration
|
|
71
|
+
# ==========================
|
|
72
|
+
|
|
73
|
+
def get_config(self) -> Config:
|
|
74
|
+
return self.config
|
|
75
|
+
|
|
76
|
+
def get_config_for_response(self) -> dict[str, Any]:
|
|
77
|
+
"""Return config serialized for API responses."""
|
|
78
|
+
return self.config.model_dump(mode="json")
|
|
79
|
+
|
|
80
|
+
def normalize_config_payload(self, config: dict[str, Any]) -> dict[str, Any]:
|
|
81
|
+
"""Normalize raw API config payloads before Pydantic validation."""
|
|
82
|
+
return config
|
|
83
|
+
|
|
84
|
+
def get_agent_context(self) -> str:
|
|
85
|
+
context = self.get_config().agent_context_prompt
|
|
86
|
+
if not context:
|
|
87
|
+
return ""
|
|
88
|
+
return context.strip()
|
|
89
|
+
|
|
90
|
+
def set_config(self, config: Config) -> None:
|
|
91
|
+
self.config_storage.save_config(config=config)
|
|
92
|
+
self.config = config
|
|
93
|
+
|
|
94
|
+
def set_config_by_name(
|
|
95
|
+
self,
|
|
96
|
+
config_name: str,
|
|
97
|
+
config_value: str | int | float | bool | list | dict | BaseModel | None,
|
|
98
|
+
) -> None:
|
|
99
|
+
original_config_name = config_name
|
|
100
|
+
config_name = _CONFIG_NAME_ALIASES.get(config_name, config_name)
|
|
101
|
+
if config_name not in type(self.config).model_fields:
|
|
102
|
+
raise ValueError(f"Invalid config name: {config_name}")
|
|
103
|
+
|
|
104
|
+
# Aliased plural fields (e.g. ``profile_extractor_configs``) map to
|
|
105
|
+
# singular Optional fields on the model. Collapse the list to its
|
|
106
|
+
# first element, or ``None`` if empty, before assignment.
|
|
107
|
+
resolved_value: str | int | float | bool | list | dict | BaseModel | None = (
|
|
108
|
+
config_value
|
|
109
|
+
)
|
|
110
|
+
if (
|
|
111
|
+
original_config_name in _CONFIG_NAME_ALIASES
|
|
112
|
+
and config_name
|
|
113
|
+
in {"profile_extractor_config", "user_playbook_extractor_config"}
|
|
114
|
+
and isinstance(config_value, list)
|
|
115
|
+
):
|
|
116
|
+
resolved_value = config_value[0] if config_value else None
|
|
117
|
+
|
|
118
|
+
setattr(self.config, config_name, resolved_value)
|
|
119
|
+
self.set_config(config=self.config)
|
|
120
|
+
|
|
121
|
+
# ==========================
|
|
122
|
+
# Storage
|
|
123
|
+
# ==========================
|
|
124
|
+
|
|
125
|
+
def get_current_storage_configuration(self) -> StorageConfig:
|
|
126
|
+
"""Return the currently configured storage config."""
|
|
127
|
+
return self.get_config().storage_config
|
|
128
|
+
|
|
129
|
+
def create_storage(self, storage_config: StorageConfig) -> BaseStorage | None:
|
|
130
|
+
"""Create a storage from the ``_STORAGE_FACTORIES`` registry.
|
|
131
|
+
|
|
132
|
+
Returns None when *storage_config* is None (enterprise cloud mode
|
|
133
|
+
where storage is not yet configured).
|
|
134
|
+
"""
|
|
135
|
+
if storage_config is None:
|
|
136
|
+
return None
|
|
137
|
+
factory = self._STORAGE_FACTORIES.get(type(storage_config))
|
|
138
|
+
if factory is None:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
f"No storage factory registered for {type(storage_config).__name__}"
|
|
141
|
+
)
|
|
142
|
+
return factory(self, storage_config)
|
|
143
|
+
|
|
144
|
+
def is_storage_configured(self) -> bool:
|
|
145
|
+
"""Check whether a valid, non-failed storage option is configured."""
|
|
146
|
+
if not self.is_storage_config_ready_to_test(
|
|
147
|
+
storage_config=self.get_current_storage_configuration(),
|
|
148
|
+
):
|
|
149
|
+
return False
|
|
150
|
+
return self.get_config().storage_config_test != StorageConfigTest.FAILED
|
|
151
|
+
|
|
152
|
+
def is_storage_config_ready_to_test(self, storage_config: StorageConfig) -> bool:
|
|
153
|
+
"""Check whether *storage_config* is fully filled in and ready for a test connection."""
|
|
154
|
+
check = self._STORAGE_READINESS_CHECKS.get(type(storage_config))
|
|
155
|
+
return check(storage_config) if check else False
|
|
156
|
+
|
|
157
|
+
def test_and_init_storage_config(
|
|
158
|
+
self, storage_config: StorageConfig
|
|
159
|
+
) -> tuple[bool, str]:
|
|
160
|
+
"""Test whether *storage_config* is valid and initialise the storage.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
tuple[bool, str]: (success, message)
|
|
164
|
+
"""
|
|
165
|
+
if not self.is_storage_config_ready_to_test(storage_config=storage_config):
|
|
166
|
+
return False, "Storage configuration is not ready to test"
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
storage = self.create_storage(storage_config=storage_config)
|
|
170
|
+
if storage is not None:
|
|
171
|
+
storage.migrate()
|
|
172
|
+
return True, "Storage initialized successfully"
|
|
173
|
+
return False, "Failed to create storage"
|
|
174
|
+
except StorageError as e:
|
|
175
|
+
logger.error("Storage initialization failed: %s", e.message)
|
|
176
|
+
return False, e.message
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.error("Storage initialization failed: %s", e)
|
|
179
|
+
return False, str(e)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from reflexio.models.config_schema import Config
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ConfigStorage(ABC):
|
|
8
|
+
"""
|
|
9
|
+
Abstract base class for configuration storage operations.
|
|
10
|
+
Defines the interface for saving/loading configuration to/from persistent storage.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, org_id: str):
|
|
14
|
+
self.org_id: str = org_id
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def get_default_config(self) -> Config:
|
|
18
|
+
"""
|
|
19
|
+
Returns a default configuration that is uninitialized.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Config: Default configuration object
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def load_config(self) -> Config:
|
|
27
|
+
"""
|
|
28
|
+
Loads the current configuration of the organization. If the organization does
|
|
29
|
+
not exist, or if no config exists, or if the current saved config is no longer valid,
|
|
30
|
+
this routine creates a default one but will not update the persistent storage.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Config: Loaded configuration object
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def save_config(self, config: Config) -> None:
|
|
38
|
+
"""
|
|
39
|
+
Saves the configuration to the persistent storage.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
config (Config): Configuration object to save
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def get_version(self) -> tuple[str, Any] | None:
|
|
46
|
+
"""Cheap probe of the persisted config version, used by the per-org cache.
|
|
47
|
+
|
|
48
|
+
Backends that can cheaply detect out-of-band mutations should
|
|
49
|
+
override this. The Reflexio cache stamps the returned tuple at
|
|
50
|
+
load time and re-probes on every cache hit; if the value
|
|
51
|
+
changes, the cached instance is evicted and rebuilt with fresh
|
|
52
|
+
configuration.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
tuple[str, Any] | None: A ``(kind, value)`` tuple where
|
|
56
|
+
``kind`` is a short string identifying the probe type
|
|
57
|
+
(e.g. ``"file"`` for mtime, ``"db"`` for a row version).
|
|
58
|
+
Returns ``None`` when probing is unsupported or fails — the
|
|
59
|
+
cache treats ``None`` as "still fresh", deferring to the
|
|
60
|
+
TTL safety net.
|
|
61
|
+
"""
|
|
62
|
+
return None
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from reflexio.models.config_schema import (
|
|
9
|
+
StorageConfig,
|
|
10
|
+
StorageConfigSQLite,
|
|
11
|
+
)
|
|
12
|
+
from reflexio.server.services.configurator.base_configurator import BaseConfigurator
|
|
13
|
+
from reflexio.server.services.configurator.config_storage import ConfigStorage
|
|
14
|
+
from reflexio.server.services.configurator.local_file_config_storage import (
|
|
15
|
+
LocalFileConfigStorage,
|
|
16
|
+
)
|
|
17
|
+
from reflexio.server.services.storage.sqlite_storage import SQLiteStorage
|
|
18
|
+
from reflexio.server.services.storage.storage_base import BaseStorage
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Storage factory functions — one per StorageConfig type
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _create_sqlite_storage(
|
|
29
|
+
configurator: BaseConfigurator, config: StorageConfigSQLite
|
|
30
|
+
) -> BaseStorage:
|
|
31
|
+
logger.info("Using SQLite storage for org %s", configurator.org_id)
|
|
32
|
+
full_config = configurator.get_config()
|
|
33
|
+
api_key_config = full_config.api_key_config if full_config else None
|
|
34
|
+
llm_config = full_config.llm_config if full_config else None
|
|
35
|
+
enable_document_expansion = (
|
|
36
|
+
full_config.enable_document_expansion if full_config else False
|
|
37
|
+
)
|
|
38
|
+
# When base_dir is explicitly provided (e.g. tests with temp dirs)
|
|
39
|
+
# and no db_path is configured, use base_dir for the SQLite DB
|
|
40
|
+
# so the storage is isolated from the shared default database.
|
|
41
|
+
db_path = config.db_path
|
|
42
|
+
if db_path is None and configurator.base_dir:
|
|
43
|
+
db_path = str(Path(configurator.base_dir) / "reflexio.db")
|
|
44
|
+
return SQLiteStorage(
|
|
45
|
+
org_id=configurator.org_id,
|
|
46
|
+
db_path=db_path,
|
|
47
|
+
api_key_config=api_key_config,
|
|
48
|
+
llm_config=llm_config,
|
|
49
|
+
enable_document_expansion=enable_document_expansion,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class DefaultConfigurator(BaseConfigurator):
|
|
54
|
+
"""OS configurator with LocalJson config storage and SQLite data storage."""
|
|
55
|
+
|
|
56
|
+
_STORAGE_FACTORIES: dict[type[StorageConfig], Callable[..., BaseStorage]] = {
|
|
57
|
+
StorageConfigSQLite: _create_sqlite_storage,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_STORAGE_READINESS_CHECKS: dict[type[StorageConfig], Callable[[Any], bool]] = {
|
|
61
|
+
StorageConfigSQLite: lambda _: True, # db_path defaults via env var if None
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def _select_config_storage(
|
|
65
|
+
self, org_id: str, base_dir: str | None
|
|
66
|
+
) -> ConfigStorage:
|
|
67
|
+
if base_dir:
|
|
68
|
+
return LocalFileConfigStorage(org_id=org_id, base_dir=base_dir)
|
|
69
|
+
return LocalFileConfigStorage(org_id=org_id)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# Configurator class registry — allows enterprise to swap in its own class
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
_configurator_class: type[BaseConfigurator] = DefaultConfigurator
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def set_configurator_class(cls: type[BaseConfigurator]) -> None:
|
|
80
|
+
"""Register a configurator class to be used by RequestContext."""
|
|
81
|
+
global _configurator_class # noqa: PLW0603
|
|
82
|
+
_configurator_class = cls
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_configurator_class() -> type[BaseConfigurator]:
|
|
86
|
+
"""Return the currently registered configurator class."""
|
|
87
|
+
return _configurator_class
|