claude-smart 0.2.42 → 0.2.44
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 +3 -3
- package/README.md +1 -1
- package/bin/claude-smart.js +2 -2
- package/package.json +9 -3
- package/plugin/.claude-plugin/plugin.json +9 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +23 -3
- package/plugin/pyproject.toml +3 -3
- package/plugin/scripts/_lib.sh +91 -0
- package/plugin/scripts/backend-service.sh +51 -4
- package/plugin/scripts/cli.sh +3 -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 +1 -0
- package/plugin/scripts/hook_entry.sh +6 -3
- package/plugin/scripts/smart-install.sh +3 -2
- package/plugin/src/README.md +57 -0
- 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 +126 -5
- package/plugin/vendor/reflexio/.env.example +62 -0
- package/plugin/vendor/reflexio/LICENSE +201 -0
- package/plugin/vendor/reflexio/README.md +338 -0
- package/plugin/vendor/reflexio/pyproject.toml +274 -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 +265 -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 +964 -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 +1112 -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 +768 -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 +333 -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 +622 -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 +2868 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +34 -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 +562 -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 +359 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1871 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +140 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +479 -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 +338 -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 +288 -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 +187 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +716 -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_consolidation/v2.3.1.prompt.md +69 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +71 -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 +283 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.2.prompt.md +234 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +244 -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 +137 -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/README.md +58 -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 +336 -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 +1668 -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/embedding_text.py +62 -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 +358 -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 +535 -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 +779 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +94 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +702 -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 +1045 -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 +772 -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 +737 -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 +803 -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 +80 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +756 -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 +1298 -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 +384 -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 +572 -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
|
File without changes
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Recurring 15-minute sync scheduler for the Braintrust connector.
|
|
2
|
+
|
|
3
|
+
Unlike `GroupEvaluationScheduler` (which is event-driven — fires once per
|
|
4
|
+
session after a delay), `BraintrustSyncScheduler` is a fixed-interval
|
|
5
|
+
daemon: every N seconds, walk every connected org and call
|
|
6
|
+
`BraintrustConnectorService.sync_once`.
|
|
7
|
+
|
|
8
|
+
Singleton per process. Started on first call to `get_instance()`. Idempotent
|
|
9
|
+
construction guarded by a class-level lock; the worker thread is a daemon
|
|
10
|
+
so it doesn't block process shutdown.
|
|
11
|
+
|
|
12
|
+
The scheduler discovers connected orgs by asking storage — `BaseStorage`'s
|
|
13
|
+
default no-op returns []; concrete backends override (currently SQLite
|
|
14
|
+
returns the single org it belongs to, via `org_id` attribute).
|
|
15
|
+
|
|
16
|
+
`BRAINTRUST_BASE_URL` env override is honored via the `BraintrustClient`
|
|
17
|
+
factory: when set, all sync calls hit that URL instead of api.braintrust.dev.
|
|
18
|
+
Useful for mocking in dev / staging.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
import threading
|
|
26
|
+
import time
|
|
27
|
+
from collections.abc import Callable
|
|
28
|
+
from dataclasses import dataclass, field
|
|
29
|
+
|
|
30
|
+
from reflexio.server.services.braintrust.client import (
|
|
31
|
+
DEFAULT_BASE_URL,
|
|
32
|
+
BraintrustClient,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
_DEFAULT_INTERVAL_SECONDS = 15 * 60 # 15 min
|
|
39
|
+
_TEST_INTERVAL_SECONDS = 5 # IS_TEST_ENV shortcut for fast tests
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _interval_seconds() -> int:
|
|
43
|
+
"""Pick the recurring interval based on `IS_TEST_ENV`."""
|
|
44
|
+
if os.environ.get("IS_TEST_ENV", "").strip().lower() == "true":
|
|
45
|
+
return _TEST_INTERVAL_SECONDS
|
|
46
|
+
return _DEFAULT_INTERVAL_SECONDS
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _resolve_base_url() -> str:
|
|
50
|
+
"""Honor the `BRAINTRUST_BASE_URL` env override (used in dev/staging)."""
|
|
51
|
+
raw = os.environ.get("BRAINTRUST_BASE_URL", "").strip()
|
|
52
|
+
return raw or DEFAULT_BASE_URL
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def make_client_factory_with_base_url() -> Callable[[str], BraintrustClient]:
|
|
56
|
+
"""Return a client factory that honors `BRAINTRUST_BASE_URL`.
|
|
57
|
+
|
|
58
|
+
Use this anywhere a `client_factory` is needed so the env override
|
|
59
|
+
flows through uniformly (sync scheduler, manual /sync endpoint).
|
|
60
|
+
"""
|
|
61
|
+
base_url = _resolve_base_url()
|
|
62
|
+
|
|
63
|
+
def factory(api_key: str) -> BraintrustClient:
|
|
64
|
+
return BraintrustClient(api_key, base_url=base_url)
|
|
65
|
+
|
|
66
|
+
return factory
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class BraintrustSyncScheduler:
|
|
71
|
+
"""Singleton recurring scheduler for Braintrust sync_once.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
interval_seconds (int): How often to poll. Defaults to 15 min
|
|
75
|
+
(5s under IS_TEST_ENV).
|
|
76
|
+
list_connected_orgs (Callable[[], list[str]]): Discovery hook.
|
|
77
|
+
run_sync_for_org (Callable[[str], None]): Per-org sync action.
|
|
78
|
+
Provided by the caller so the scheduler stays decoupled from
|
|
79
|
+
BraintrustConnectorService construction.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
interval_seconds: int = field(default_factory=_interval_seconds)
|
|
83
|
+
list_connected_orgs: Callable[[], list[str]] = field(default=lambda: [])
|
|
84
|
+
run_sync_for_org: Callable[[str], None] = field(default=lambda _org: None)
|
|
85
|
+
|
|
86
|
+
_instance: BraintrustSyncScheduler | None = field(
|
|
87
|
+
default=None, init=False, repr=False
|
|
88
|
+
)
|
|
89
|
+
_lock: threading.Lock = field(
|
|
90
|
+
default_factory=threading.Lock, init=False, repr=False
|
|
91
|
+
)
|
|
92
|
+
_stop: threading.Event = field(
|
|
93
|
+
default_factory=threading.Event, init=False, repr=False
|
|
94
|
+
)
|
|
95
|
+
_thread: threading.Thread | None = field(default=None, init=False, repr=False)
|
|
96
|
+
|
|
97
|
+
def start(self) -> None:
|
|
98
|
+
"""Spawn the daemon thread (idempotent)."""
|
|
99
|
+
with self._lock:
|
|
100
|
+
if self._thread is not None and self._thread.is_alive():
|
|
101
|
+
return
|
|
102
|
+
self._stop.clear()
|
|
103
|
+
self._thread = threading.Thread(
|
|
104
|
+
target=self._loop, daemon=True, name="braintrust-sync-scheduler"
|
|
105
|
+
)
|
|
106
|
+
self._thread.start()
|
|
107
|
+
logger.info(
|
|
108
|
+
"BraintrustSyncScheduler started (interval=%ss)",
|
|
109
|
+
self.interval_seconds,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def stop(self) -> None:
|
|
113
|
+
"""Signal the daemon to exit. Returns immediately; thread joins lazily."""
|
|
114
|
+
self._stop.set()
|
|
115
|
+
|
|
116
|
+
def _loop(self) -> None:
|
|
117
|
+
"""Main loop: every `interval_seconds`, sync every connected org."""
|
|
118
|
+
while not self._stop.is_set():
|
|
119
|
+
try:
|
|
120
|
+
org_ids = self.list_connected_orgs()
|
|
121
|
+
except Exception: # noqa: BLE001
|
|
122
|
+
logger.exception("BraintrustSyncScheduler: org discovery failed")
|
|
123
|
+
org_ids = []
|
|
124
|
+
for org_id in org_ids:
|
|
125
|
+
if self._stop.is_set():
|
|
126
|
+
break
|
|
127
|
+
try:
|
|
128
|
+
self.run_sync_for_org(org_id)
|
|
129
|
+
except Exception: # noqa: BLE001
|
|
130
|
+
logger.exception(
|
|
131
|
+
"BraintrustSyncScheduler: sync failed for org=%s", org_id
|
|
132
|
+
)
|
|
133
|
+
# Sleep interruptibly so stop() takes effect promptly.
|
|
134
|
+
self._stop.wait(timeout=self.interval_seconds)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# Module-level singleton accessor. Tests reset via `_reset_for_test`.
|
|
138
|
+
_INSTANCE: BraintrustSyncScheduler | None = None
|
|
139
|
+
_GET_LOCK = threading.Lock()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_instance(
|
|
143
|
+
*,
|
|
144
|
+
list_connected_orgs: Callable[[], list[str]] | None = None,
|
|
145
|
+
run_sync_for_org: Callable[[str], None] | None = None,
|
|
146
|
+
) -> BraintrustSyncScheduler:
|
|
147
|
+
"""Get or create the process-wide singleton.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
list_connected_orgs (Callable[[], list[str]] | None): Override
|
|
151
|
+
org discovery (defaults to no-op returning []).
|
|
152
|
+
run_sync_for_org (Callable[[str], None] | None): Override the
|
|
153
|
+
per-org sync action (defaults to no-op).
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
BraintrustSyncScheduler: The shared instance.
|
|
157
|
+
"""
|
|
158
|
+
global _INSTANCE
|
|
159
|
+
with _GET_LOCK:
|
|
160
|
+
if _INSTANCE is None:
|
|
161
|
+
_INSTANCE = BraintrustSyncScheduler(
|
|
162
|
+
list_connected_orgs=list_connected_orgs or (lambda: []),
|
|
163
|
+
run_sync_for_org=run_sync_for_org or (lambda _org: None),
|
|
164
|
+
)
|
|
165
|
+
_INSTANCE.start()
|
|
166
|
+
return _INSTANCE
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _reset_for_test() -> None:
|
|
170
|
+
"""Reset the singleton — for tests only."""
|
|
171
|
+
global _INSTANCE
|
|
172
|
+
with _GET_LOCK:
|
|
173
|
+
if _INSTANCE is not None:
|
|
174
|
+
_INSTANCE.stop()
|
|
175
|
+
if _INSTANCE._thread is not None:
|
|
176
|
+
_INSTANCE._thread.join(timeout=2.0)
|
|
177
|
+
_INSTANCE = None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def trigger_tick_now(scheduler: BraintrustSyncScheduler) -> None:
|
|
181
|
+
"""Force one immediate sync tick — for tests.
|
|
182
|
+
|
|
183
|
+
Tests that don't want to wait for the interval call this to run one
|
|
184
|
+
pass of the loop body synchronously. Mirrors the loop's per-org
|
|
185
|
+
exception handling so a single failing org doesn't abort the tick.
|
|
186
|
+
"""
|
|
187
|
+
org_ids = scheduler.list_connected_orgs()
|
|
188
|
+
for org_id in org_ids:
|
|
189
|
+
try:
|
|
190
|
+
scheduler.run_sync_for_org(org_id)
|
|
191
|
+
except Exception: # noqa: BLE001
|
|
192
|
+
logger.exception("trigger_tick_now: sync failed for org=%s", org_id)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# Tiny no-op `_` reference so unused-import lint doesn't trip on `time`.
|
|
196
|
+
_ = time
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Minimal Fernet encryption helper for the Braintrust connector.
|
|
2
|
+
|
|
3
|
+
Reads `REFLEXIO_FERNET_KEYS` (comma-separated) from the environment. When
|
|
4
|
+
the env var is unset, both `encrypt` and `decrypt` return the value
|
|
5
|
+
unchanged — matching the existing `EncryptManager` behavior in
|
|
6
|
+
`reflexio_ext.utils` and keeping OS dev mode workable without rotation
|
|
7
|
+
infrastructure.
|
|
8
|
+
|
|
9
|
+
In enterprise deployments, the env var IS set and the API key is stored
|
|
10
|
+
ciphertext-only.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from cryptography import fernet
|
|
19
|
+
from cryptography.fernet import Fernet, MultiFernet
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_ENV_KEY = "REFLEXIO_FERNET_KEYS"
|
|
25
|
+
_MULTI: MultiFernet | None = None
|
|
26
|
+
_LOADED = False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _load() -> MultiFernet | None:
|
|
30
|
+
"""Lazy-load the MultiFernet from env on first use."""
|
|
31
|
+
global _MULTI, _LOADED
|
|
32
|
+
if _LOADED:
|
|
33
|
+
return _MULTI
|
|
34
|
+
raw = os.environ.get(_ENV_KEY, "").strip()
|
|
35
|
+
if not raw:
|
|
36
|
+
logger.info(
|
|
37
|
+
"%s is unset — Braintrust connector storing API keys in plaintext "
|
|
38
|
+
"(suitable only for local development).",
|
|
39
|
+
_ENV_KEY,
|
|
40
|
+
)
|
|
41
|
+
_LOADED = True
|
|
42
|
+
return None
|
|
43
|
+
fernets = []
|
|
44
|
+
for k in raw.split(","):
|
|
45
|
+
k = k.strip()
|
|
46
|
+
if not k:
|
|
47
|
+
continue
|
|
48
|
+
try:
|
|
49
|
+
fernets.append(Fernet(k.encode("utf-8")))
|
|
50
|
+
except Exception: # noqa: BLE001
|
|
51
|
+
logger.warning("Discarding invalid Fernet key in %s", _ENV_KEY)
|
|
52
|
+
if not fernets:
|
|
53
|
+
raise RuntimeError(f"{_ENV_KEY} is set but contains no valid Fernet keys")
|
|
54
|
+
_MULTI = MultiFernet(fernets)
|
|
55
|
+
_LOADED = True
|
|
56
|
+
return _MULTI
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def encrypt(value: str) -> str:
|
|
60
|
+
"""Encrypt `value` if Fernet keys are configured; otherwise return as-is.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
value (str): The plaintext (e.g., a Braintrust API key).
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
str: Ciphertext, or `value` unchanged when no keys are configured.
|
|
67
|
+
"""
|
|
68
|
+
m = _load()
|
|
69
|
+
if m is None:
|
|
70
|
+
return value
|
|
71
|
+
return m.encrypt(value.encode("utf-8")).decode("utf-8")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def decrypt(value: str) -> str:
|
|
75
|
+
"""Decrypt `value` if Fernet keys are configured; otherwise return as-is.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
value (str): The (possibly ciphertext) value from storage.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
str: Plaintext.
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
InvalidToken: When the token cannot be decoded by any registered key.
|
|
85
|
+
"""
|
|
86
|
+
m = _load()
|
|
87
|
+
if m is None:
|
|
88
|
+
return value
|
|
89
|
+
try:
|
|
90
|
+
return m.decrypt(value.encode("utf-8")).decode("utf-8")
|
|
91
|
+
except fernet.InvalidToken:
|
|
92
|
+
# Storage may contain plaintext from a pre-encryption deployment.
|
|
93
|
+
# Caller decides whether to surface the issue.
|
|
94
|
+
raise
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _reset_for_test() -> None:
|
|
98
|
+
"""Reset the cached MultiFernet — for tests only."""
|
|
99
|
+
global _MULTI, _LOADED
|
|
100
|
+
_MULTI = None
|
|
101
|
+
_LOADED = False
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""HTTP client for the Braintrust REST API.
|
|
2
|
+
|
|
3
|
+
Pure HTTP wrapper — no storage, no encryption, no orchestration. The
|
|
4
|
+
service layer composes this with storage to build the connector.
|
|
5
|
+
|
|
6
|
+
The exact endpoint paths (`/v1/api_key`, `/v1/organization`, etc.) follow
|
|
7
|
+
the spec §5.1; if Braintrust evolves the API, only this file needs to
|
|
8
|
+
change — call sites use the typed methods.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
import httpx
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
DEFAULT_BASE_URL = "https://api.braintrust.dev"
|
|
22
|
+
DEFAULT_TIMEOUT_SECONDS = 30.0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BraintrustAuthError(RuntimeError):
|
|
26
|
+
"""Raised when the Braintrust API rejects the key (401 / 403)."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class BraintrustHTTPError(RuntimeError):
|
|
30
|
+
"""Raised on non-2xx, non-auth-error responses from Braintrust."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, status_code: int, body: str) -> None:
|
|
33
|
+
super().__init__(f"Braintrust HTTP {status_code}: {body[:200]}")
|
|
34
|
+
self.status_code = status_code
|
|
35
|
+
self.body = body
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class BraintrustClient:
|
|
39
|
+
"""Thin wrapper around the Braintrust REST API.
|
|
40
|
+
|
|
41
|
+
All methods are synchronous; they use a per-client `httpx.Client`
|
|
42
|
+
with a fixed timeout so tests can monkey-patch the client.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
api_key (str): Customer-supplied Braintrust API key.
|
|
46
|
+
base_url (str): Override for testing.
|
|
47
|
+
timeout (float): Per-request timeout in seconds.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
api_key: str,
|
|
53
|
+
*,
|
|
54
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
55
|
+
timeout: float = DEFAULT_TIMEOUT_SECONDS,
|
|
56
|
+
) -> None:
|
|
57
|
+
self.api_key = api_key
|
|
58
|
+
self.base_url = base_url.rstrip("/")
|
|
59
|
+
self._client = httpx.Client(
|
|
60
|
+
timeout=timeout,
|
|
61
|
+
headers={
|
|
62
|
+
"Authorization": f"Bearer {api_key}",
|
|
63
|
+
"Accept": "application/json",
|
|
64
|
+
"User-Agent": "reflexio-braintrust-connector/1.0",
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def close(self) -> None:
|
|
69
|
+
self._client.close()
|
|
70
|
+
|
|
71
|
+
# ------------------------------------------------------------------
|
|
72
|
+
# API methods
|
|
73
|
+
# ------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
def validate_key(self) -> bool:
|
|
76
|
+
"""Return True iff the API key is valid.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
bool: True on 2xx, False on 401/403. Other errors raise.
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
self._get("/v1/api_key")
|
|
83
|
+
except BraintrustAuthError:
|
|
84
|
+
return False
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
def list_organizations(self) -> list[dict[str, Any]]:
|
|
88
|
+
"""List workspaces (Braintrust calls these "organizations").
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
list[dict]: Each item has at least `id` and `name`.
|
|
92
|
+
"""
|
|
93
|
+
return self._unwrap(self._get("/v1/organization"))
|
|
94
|
+
|
|
95
|
+
def list_projects(self, workspace_id: str) -> list[dict[str, Any]]:
|
|
96
|
+
"""List projects in a workspace.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
workspace_id (str): Braintrust organization id.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
list[dict]: Each item has at least `id` and `name`.
|
|
103
|
+
"""
|
|
104
|
+
return self._unwrap(self._get("/v1/project", params={"org_id": workspace_id}))
|
|
105
|
+
|
|
106
|
+
def list_experiments(
|
|
107
|
+
self, project_id: str, since_ts: int | None = None
|
|
108
|
+
) -> list[dict[str, Any]]:
|
|
109
|
+
"""List experiments in a project.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
project_id (str): Braintrust project id.
|
|
113
|
+
since_ts (int | None): Optional unix-epoch lower bound on
|
|
114
|
+
`experiment.created_at`.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
list[dict]: Each item has at least `id`, `name`, `created_at`.
|
|
118
|
+
"""
|
|
119
|
+
params: dict[str, Any] = {"project_id": project_id}
|
|
120
|
+
if since_ts is not None:
|
|
121
|
+
params["since"] = since_ts
|
|
122
|
+
return self._unwrap(self._get("/v1/experiment", params=params))
|
|
123
|
+
|
|
124
|
+
def list_spans(self, experiment_id: str) -> list[dict[str, Any]]:
|
|
125
|
+
"""List spans for an experiment.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
list[dict]: Each item has at least `id`, `metadata`, `scores`,
|
|
129
|
+
`created_at`. Other fields are ignored by the caller.
|
|
130
|
+
"""
|
|
131
|
+
return self._unwrap(
|
|
132
|
+
self._get("/v1/span", params={"experiment_id": experiment_id})
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# ------------------------------------------------------------------
|
|
136
|
+
# internals
|
|
137
|
+
# ------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
def _get(
|
|
140
|
+
self, path: str, *, params: dict[str, Any] | None = None
|
|
141
|
+
) -> httpx.Response:
|
|
142
|
+
url = f"{self.base_url}{path}"
|
|
143
|
+
try:
|
|
144
|
+
response = self._client.get(url, params=params)
|
|
145
|
+
except httpx.RequestError as e:
|
|
146
|
+
raise BraintrustHTTPError(503, str(e)) from e
|
|
147
|
+
if response.status_code in (401, 403):
|
|
148
|
+
raise BraintrustAuthError(
|
|
149
|
+
f"Braintrust rejected the API key (HTTP {response.status_code})"
|
|
150
|
+
)
|
|
151
|
+
if not (200 <= response.status_code < 300):
|
|
152
|
+
raise BraintrustHTTPError(response.status_code, response.text)
|
|
153
|
+
return response
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def _unwrap(response: httpx.Response) -> list[dict[str, Any]]:
|
|
157
|
+
"""Pull the list payload from Braintrust's standard envelope.
|
|
158
|
+
|
|
159
|
+
Braintrust wraps list responses in `{"objects": [...]}`. We
|
|
160
|
+
accept either the wrapped form or a bare list (some endpoints).
|
|
161
|
+
"""
|
|
162
|
+
data = response.json()
|
|
163
|
+
if isinstance(data, dict) and "objects" in data:
|
|
164
|
+
return data["objects"]
|
|
165
|
+
if isinstance(data, list):
|
|
166
|
+
return data
|
|
167
|
+
return []
|