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,330 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Embedding cache + deterministic fake embedder for retrieval latency benchmarks.
|
|
3
|
+
|
|
4
|
+
This module controls the cost of embedding generation during a benchmark run.
|
|
5
|
+
Retrieval latency measurement should not be dominated by embedder API calls,
|
|
6
|
+
but query-side vectors must still be realistic so the vector-search path
|
|
7
|
+
computes meaningful cosine similarities.
|
|
8
|
+
|
|
9
|
+
Two helpers are provided:
|
|
10
|
+
|
|
11
|
+
1. :class:`QueryEmbedCache` — on-disk cache of real embeddings for the fixed
|
|
12
|
+
query set. Populated once via :class:`LiteLLMClient`, then reused on every
|
|
13
|
+
subsequent run. First-time population requires a configured embedding API
|
|
14
|
+
key (typically ``OPENAI_API_KEY``).
|
|
15
|
+
2. :func:`make_fake_document_embedder` — deterministic pseudo-random vector
|
|
16
|
+
generator used during corpus seeding. Document vector *content* does not
|
|
17
|
+
affect query-time latency (only row count does), so fake vectors are a
|
|
18
|
+
sound optimization for a latency benchmark.
|
|
19
|
+
|
|
20
|
+
Both helpers expose a :func:`patch_storage_get_embedding` style context
|
|
21
|
+
manager that swaps out the storage instance's bound ``_get_embedding`` method
|
|
22
|
+
for the duration of a ``with`` block.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import hashlib
|
|
28
|
+
import json
|
|
29
|
+
import logging
|
|
30
|
+
import struct
|
|
31
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
32
|
+
from contextlib import contextmanager
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
from typing import Any, Literal
|
|
35
|
+
|
|
36
|
+
from reflexio.models.config_schema import EMBEDDING_DIMENSIONS
|
|
37
|
+
from reflexio.server.llm.litellm_client import LiteLLMClient, LiteLLMConfig
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
# Track the storage-level canonical dimension so fake document vectors
|
|
42
|
+
# always fit the vec0 column width. Query cache entries are populated
|
|
43
|
+
# through LiteLLMClient at this same dimension for consistency.
|
|
44
|
+
DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small"
|
|
45
|
+
DEFAULT_EMBEDDING_DIMS = EMBEDDING_DIMENSIONS
|
|
46
|
+
|
|
47
|
+
_CACHE_DIR = Path.home() / ".cache" / "reflexio-benchmarks"
|
|
48
|
+
_DOCUMENT_PREFIX = "search_document: "
|
|
49
|
+
_QUERY_PREFIX = "search_query: "
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
EmbeddingPurpose = Literal["document", "query"]
|
|
53
|
+
|
|
54
|
+
EmbeddingFn = Callable[[str, EmbeddingPurpose], list[float]]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _cache_path(model: str) -> Path:
|
|
58
|
+
"""
|
|
59
|
+
Return the on-disk cache path for embeddings of a given model.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
model (str): Embedding model identifier (e.g. text-embedding-3-small).
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Path: JSON cache file path. Parent directory is not created here.
|
|
66
|
+
"""
|
|
67
|
+
safe = model.replace("/", "_")
|
|
68
|
+
return _CACHE_DIR / f"embeddings-{safe}.json"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _hash_key(text: str) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Return a stable short hash key for a full (prefixed) embedding input.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
text (str): The full text that will be passed to the embedding model.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
str: Hex digest suitable for use as a JSON dict key.
|
|
80
|
+
"""
|
|
81
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class QueryEmbedCache:
|
|
85
|
+
"""
|
|
86
|
+
On-disk cache of real embedding vectors for the benchmark query set.
|
|
87
|
+
|
|
88
|
+
The cache file is a flat JSON dict mapping SHA256(prefixed_text) → vector.
|
|
89
|
+
Population happens lazily via :meth:`ensure` and only touches the network
|
|
90
|
+
when the cache is missing an entry.
|
|
91
|
+
|
|
92
|
+
Attributes:
|
|
93
|
+
model (str): Embedding model used to populate the cache.
|
|
94
|
+
dimensions (int): Expected vector dimensionality.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(
|
|
98
|
+
self,
|
|
99
|
+
model: str = DEFAULT_EMBEDDING_MODEL,
|
|
100
|
+
dimensions: int = DEFAULT_EMBEDDING_DIMS,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Initialize the cache, loading any existing on-disk entries.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
model (str): Embedding model name. Must match whatever the live
|
|
107
|
+
storage backend is configured with.
|
|
108
|
+
dimensions (int): Expected embedding dimensionality. Must match
|
|
109
|
+
the storage vector column width.
|
|
110
|
+
"""
|
|
111
|
+
self.model = model
|
|
112
|
+
self.dimensions = dimensions
|
|
113
|
+
self._path = _cache_path(model)
|
|
114
|
+
self._data: dict[str, list[float]] = {}
|
|
115
|
+
if self._path.exists():
|
|
116
|
+
try:
|
|
117
|
+
self._data = json.loads(self._path.read_text())
|
|
118
|
+
except (OSError, json.JSONDecodeError) as err:
|
|
119
|
+
logger.warning(
|
|
120
|
+
"Failed to load embed cache at %s: %s — starting fresh",
|
|
121
|
+
self._path,
|
|
122
|
+
err,
|
|
123
|
+
)
|
|
124
|
+
self._data = {}
|
|
125
|
+
|
|
126
|
+
def ensure(self, queries: Iterable[str]) -> None:
|
|
127
|
+
"""
|
|
128
|
+
Make sure every query has a real embedding in the cache.
|
|
129
|
+
|
|
130
|
+
Populates missing entries via the live embedding API in a single
|
|
131
|
+
batched call. Writes the cache to disk at the end of population so
|
|
132
|
+
subsequent runs skip the network entirely.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
queries (Iterable[str]): Raw query strings (no prefix). The
|
|
136
|
+
``search_query:`` prefix is applied internally.
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
RuntimeError: If the embedder cannot be reached and the cache is
|
|
140
|
+
not already populated for every query.
|
|
141
|
+
"""
|
|
142
|
+
missing: list[str] = []
|
|
143
|
+
missing_prefixed: list[str] = []
|
|
144
|
+
for q in queries:
|
|
145
|
+
prefixed = _QUERY_PREFIX + q
|
|
146
|
+
if _hash_key(prefixed) not in self._data:
|
|
147
|
+
missing.append(q)
|
|
148
|
+
missing_prefixed.append(prefixed)
|
|
149
|
+
if not missing:
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
logger.info(
|
|
153
|
+
"Embed cache miss for %d queries (model=%s) — fetching",
|
|
154
|
+
len(missing),
|
|
155
|
+
self.model,
|
|
156
|
+
)
|
|
157
|
+
client = LiteLLMClient(LiteLLMConfig(model=self.model))
|
|
158
|
+
try:
|
|
159
|
+
vectors = client.get_embeddings(
|
|
160
|
+
missing_prefixed, self.model, self.dimensions
|
|
161
|
+
)
|
|
162
|
+
except Exception as err: # noqa: BLE001
|
|
163
|
+
raise RuntimeError(
|
|
164
|
+
f"Failed to populate query embed cache for model {self.model}: "
|
|
165
|
+
f"{err}. Set OPENAI_API_KEY (or the appropriate provider key) "
|
|
166
|
+
"and re-run."
|
|
167
|
+
) from err
|
|
168
|
+
|
|
169
|
+
for prefixed, vec in zip(missing_prefixed, vectors, strict=True):
|
|
170
|
+
if len(vec) != self.dimensions:
|
|
171
|
+
raise RuntimeError(
|
|
172
|
+
f"Embed cache got vector of length {len(vec)}, expected "
|
|
173
|
+
f"{self.dimensions} for model {self.model}"
|
|
174
|
+
)
|
|
175
|
+
self._data[_hash_key(prefixed)] = list(vec)
|
|
176
|
+
|
|
177
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
self._path.write_text(json.dumps(self._data))
|
|
179
|
+
|
|
180
|
+
def lookup(self, text: str, purpose: EmbeddingPurpose) -> list[float]:
|
|
181
|
+
"""
|
|
182
|
+
Return a cached embedding for the given raw text.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
text (str): Raw text (no prefix).
|
|
186
|
+
purpose (EmbeddingPurpose): ``"document"`` or ``"query"``.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
list[float]: The cached embedding vector.
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
KeyError: If the text is not in the cache.
|
|
193
|
+
"""
|
|
194
|
+
prefix = _DOCUMENT_PREFIX if purpose == "document" else _QUERY_PREFIX
|
|
195
|
+
key = _hash_key(prefix + text)
|
|
196
|
+
try:
|
|
197
|
+
return self._data[key]
|
|
198
|
+
except KeyError as err:
|
|
199
|
+
raise KeyError(
|
|
200
|
+
f"Query embed cache miss for text={text!r} purpose={purpose}. "
|
|
201
|
+
"Did you forget to call QueryEmbedCache.ensure() for the query set?"
|
|
202
|
+
) from err
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _deterministic_vector(text: str, dimensions: int) -> list[float]:
|
|
206
|
+
"""
|
|
207
|
+
Generate a deterministic pseudo-random unit vector from text.
|
|
208
|
+
|
|
209
|
+
Uses SHA256 as a cheap keyed PRNG, converts the digest stream into
|
|
210
|
+
little-endian float32 values, then normalizes to unit length. The
|
|
211
|
+
output is stable across runs and machines.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
text (str): Input text (the hash seed).
|
|
215
|
+
dimensions (int): Desired vector length.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
list[float]: Unit-length vector of ``dimensions`` floats.
|
|
219
|
+
"""
|
|
220
|
+
# Derive 4*dimensions pseudo-random bytes from chained SHA256 of
|
|
221
|
+
# (text, counter). This is not cryptographic — it just needs to be
|
|
222
|
+
# stable and unbiased enough to exercise cosine similarity paths.
|
|
223
|
+
need = dimensions * 4
|
|
224
|
+
buf = bytearray()
|
|
225
|
+
counter = 0
|
|
226
|
+
while len(buf) < need:
|
|
227
|
+
h = hashlib.sha256(f"{text}:{counter}".encode()).digest()
|
|
228
|
+
buf.extend(h)
|
|
229
|
+
counter += 1
|
|
230
|
+
floats = [
|
|
231
|
+
struct.unpack("<i", bytes(buf[i * 4 : (i + 1) * 4]))[0] / 2**31
|
|
232
|
+
for i in range(dimensions)
|
|
233
|
+
]
|
|
234
|
+
norm = sum(x * x for x in floats) ** 0.5
|
|
235
|
+
if norm == 0.0:
|
|
236
|
+
return [0.0] * dimensions
|
|
237
|
+
return [x / norm for x in floats]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def make_fake_document_embedder(
|
|
241
|
+
dimensions: int = DEFAULT_EMBEDDING_DIMS,
|
|
242
|
+
) -> Callable[[str, EmbeddingPurpose], list[float]]:
|
|
243
|
+
"""
|
|
244
|
+
Build a fake ``_get_embedding`` replacement for corpus seeding.
|
|
245
|
+
|
|
246
|
+
Returns deterministic hash-derived vectors so seeding is fast (no API
|
|
247
|
+
calls) while preserving vector column width and cosine-similarity
|
|
248
|
+
determinism across runs. Only valid for seeding — query-time retrieval
|
|
249
|
+
should use a real :class:`QueryEmbedCache` so BM25/vector ranking is
|
|
250
|
+
meaningful.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
dimensions (int): Vector length to produce. Must match the storage
|
|
254
|
+
backend's vector column width.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Callable: A bound-method-compatible ``(text, purpose) → vector``
|
|
258
|
+
function ready to patch onto a storage instance.
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
def fake(text: str, purpose: EmbeddingPurpose = "document") -> list[float]:
|
|
262
|
+
# Include purpose in the seed so doc and query versions of the same
|
|
263
|
+
# text get different vectors — mirrors how the real embedder treats
|
|
264
|
+
# the search_document/search_query prefixes as distinct inputs.
|
|
265
|
+
return _deterministic_vector(f"{purpose}:{text}", dimensions)
|
|
266
|
+
|
|
267
|
+
return fake
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def make_cached_query_embedder(
|
|
271
|
+
cache: QueryEmbedCache,
|
|
272
|
+
fallback_dims: int = DEFAULT_EMBEDDING_DIMS,
|
|
273
|
+
) -> Callable[[str, EmbeddingPurpose], list[float]]:
|
|
274
|
+
"""
|
|
275
|
+
Build a ``_get_embedding`` replacement backed by :class:`QueryEmbedCache`.
|
|
276
|
+
|
|
277
|
+
Document lookups fall back to fake deterministic vectors (seeding already
|
|
278
|
+
happened; any ``purpose='document'`` call during the timed loop would be
|
|
279
|
+
an oversight but must not crash). Query lookups raise if the cache is
|
|
280
|
+
empty — that indicates :meth:`QueryEmbedCache.ensure` was skipped.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
cache (QueryEmbedCache): Populated cache for the benchmark query set.
|
|
284
|
+
fallback_dims (int): Vector width for any document-side lookups that
|
|
285
|
+
leak into the timed loop.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Callable: A bound-method-compatible ``(text, purpose) → vector`` fn.
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
def cached(text: str, purpose: EmbeddingPurpose = "document") -> list[float]:
|
|
292
|
+
if purpose == "query":
|
|
293
|
+
return cache.lookup(text, "query")
|
|
294
|
+
return _deterministic_vector(f"document:{text}", fallback_dims)
|
|
295
|
+
|
|
296
|
+
return cached
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@contextmanager
|
|
300
|
+
def patch_storage_get_embedding(
|
|
301
|
+
storage: Any,
|
|
302
|
+
replacement: EmbeddingFn,
|
|
303
|
+
) -> Iterator[None]:
|
|
304
|
+
"""
|
|
305
|
+
Temporarily replace a storage instance's ``_get_embedding`` bound method.
|
|
306
|
+
|
|
307
|
+
Used to swap in either the fake seeding embedder or the cached query
|
|
308
|
+
embedder for the duration of a benchmark phase.
|
|
309
|
+
|
|
310
|
+
``storage`` is typed ``Any`` because ``_get_embedding`` is declared on the
|
|
311
|
+
sqlite storage mixins rather than on ``BaseStorage`` itself — a Protocol
|
|
312
|
+
that required the attribute would reject real storage instances.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
storage (Any): A BaseStorage-like instance exposing a
|
|
316
|
+
``_get_embedding`` method. Writing to ``storage._get_embedding``
|
|
317
|
+
goes into the instance dict, so subsequent lookups resolve to the
|
|
318
|
+
plain replacement function (no auto-binding).
|
|
319
|
+
replacement (EmbeddingFn): The ``(text, purpose) → vector`` fn to bind.
|
|
320
|
+
|
|
321
|
+
Yields:
|
|
322
|
+
None: During the ``with`` block, ``storage._get_embedding`` routes to
|
|
323
|
+
``replacement``. On exit, the original bound method is restored.
|
|
324
|
+
"""
|
|
325
|
+
original = storage._get_embedding
|
|
326
|
+
storage._get_embedding = replacement
|
|
327
|
+
try:
|
|
328
|
+
yield
|
|
329
|
+
finally:
|
|
330
|
+
storage._get_embedding = original
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Statistics and markdown rendering for retrieval latency benchmark results.
|
|
3
|
+
|
|
4
|
+
Input: a flat list of trial rows produced by :mod:`bench`, each with
|
|
5
|
+
``{backend, retrieval_type, layer, n, trial, elapsed_ms}``.
|
|
6
|
+
|
|
7
|
+
Output: markdown tables grouped by retrieval type, optional diff column
|
|
8
|
+
against a committed baseline file.
|
|
9
|
+
|
|
10
|
+
All quantile math uses :mod:`statistics` to avoid pulling in numpy.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import statistics
|
|
17
|
+
from dataclasses import asdict, dataclass, field
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from reflexio.benchmarks.retrieval_latency.scenarios import QUERIES
|
|
22
|
+
|
|
23
|
+
# Flag a cell when p95 has grown by more than this factor since the baseline.
|
|
24
|
+
REGRESSION_THRESHOLD = 1.20
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class CellKey:
|
|
29
|
+
"""
|
|
30
|
+
Immutable key identifying one (backend, retrieval_type, layer, n) cell.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
backend (str): Storage backend name, e.g. ``"sqlite"``.
|
|
34
|
+
retrieval_type (str): One of ``profile``, ``user_playbook``,
|
|
35
|
+
``agent_playbook``, ``unified``.
|
|
36
|
+
layer (str): ``service`` or ``http``.
|
|
37
|
+
n (int): Corpus size for this cell.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
backend: str
|
|
41
|
+
retrieval_type: str
|
|
42
|
+
layer: str
|
|
43
|
+
n: int
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class CellStats:
|
|
48
|
+
"""
|
|
49
|
+
Aggregated latency stats for one benchmark cell.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
trials (int): Number of timed samples summarized.
|
|
53
|
+
mean_ms (float): Arithmetic mean latency in milliseconds.
|
|
54
|
+
p50_ms (float): Median latency.
|
|
55
|
+
p95_ms (float): 95th percentile.
|
|
56
|
+
p99_ms (float): 99th percentile.
|
|
57
|
+
max_ms (float): Slowest sample observed.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
trials: int
|
|
61
|
+
mean_ms: float
|
|
62
|
+
p50_ms: float
|
|
63
|
+
p95_ms: float
|
|
64
|
+
p99_ms: float
|
|
65
|
+
max_ms: float
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class RunResults:
|
|
70
|
+
"""
|
|
71
|
+
Full results of a benchmark run, aggregated and raw.
|
|
72
|
+
|
|
73
|
+
Attributes:
|
|
74
|
+
config (dict): Top-level metadata (CLI args, git sha, timestamp).
|
|
75
|
+
raw (list[dict]): All trial rows, unmodified.
|
|
76
|
+
stats (dict[str, CellStats]): Aggregated stats keyed by
|
|
77
|
+
``backend|retrieval_type|layer|n``.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
config: dict[str, Any]
|
|
81
|
+
raw: list[dict[str, Any]] = field(default_factory=list)
|
|
82
|
+
stats: dict[str, CellStats] = field(default_factory=dict)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _quantile(sorted_samples: list[float], q: float) -> float:
|
|
86
|
+
"""
|
|
87
|
+
Return the ``q``-th quantile of an already-sorted sample list.
|
|
88
|
+
|
|
89
|
+
Uses the linear interpolation method so small N (e.g. 10 samples for
|
|
90
|
+
the smoke test) still gives a stable number.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
sorted_samples (list[float]): Sorted list of samples.
|
|
94
|
+
q (float): Target quantile in ``[0, 1]``.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
float: Interpolated quantile value.
|
|
98
|
+
"""
|
|
99
|
+
if not sorted_samples:
|
|
100
|
+
return 0.0
|
|
101
|
+
if len(sorted_samples) == 1:
|
|
102
|
+
return sorted_samples[0]
|
|
103
|
+
pos = q * (len(sorted_samples) - 1)
|
|
104
|
+
lo = int(pos)
|
|
105
|
+
hi = min(lo + 1, len(sorted_samples) - 1)
|
|
106
|
+
frac = pos - lo
|
|
107
|
+
return sorted_samples[lo] * (1 - frac) + sorted_samples[hi] * frac
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def compute_stats(samples: list[float]) -> CellStats:
|
|
111
|
+
"""
|
|
112
|
+
Compute mean / p50 / p95 / p99 / max for one cell's samples.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
samples (list[float]): Latency samples in milliseconds.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
CellStats: Aggregated stats. Empty input returns zeros.
|
|
119
|
+
"""
|
|
120
|
+
if not samples:
|
|
121
|
+
return CellStats(
|
|
122
|
+
trials=0, mean_ms=0.0, p50_ms=0.0, p95_ms=0.0, p99_ms=0.0, max_ms=0.0
|
|
123
|
+
)
|
|
124
|
+
sorted_s = sorted(samples)
|
|
125
|
+
return CellStats(
|
|
126
|
+
trials=len(samples),
|
|
127
|
+
mean_ms=statistics.fmean(sorted_s),
|
|
128
|
+
p50_ms=_quantile(sorted_s, 0.50),
|
|
129
|
+
p95_ms=_quantile(sorted_s, 0.95),
|
|
130
|
+
p99_ms=_quantile(sorted_s, 0.99),
|
|
131
|
+
max_ms=sorted_s[-1],
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def cell_key_str(k: CellKey) -> str:
|
|
136
|
+
"""
|
|
137
|
+
Serialize a :class:`CellKey` into a flat string for JSON keys.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
k (CellKey): The cell identifier.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
str: ``backend|retrieval_type|layer|n`` form.
|
|
144
|
+
"""
|
|
145
|
+
return f"{k.backend}|{k.retrieval_type}|{k.layer}|{k.n}"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def aggregate(raw: list[dict[str, Any]]) -> dict[str, CellStats]:
|
|
149
|
+
"""
|
|
150
|
+
Bucket raw trial rows by cell and compute aggregated stats per cell.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
raw (list[dict]): Trial rows as emitted by :mod:`bench`.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
dict[str, CellStats]: Mapping from flat cell key to stats.
|
|
157
|
+
"""
|
|
158
|
+
buckets: dict[str, list[float]] = {}
|
|
159
|
+
for row in raw:
|
|
160
|
+
k = CellKey(
|
|
161
|
+
backend=row["backend"],
|
|
162
|
+
retrieval_type=row["retrieval_type"],
|
|
163
|
+
layer=row["layer"],
|
|
164
|
+
n=row["n"],
|
|
165
|
+
)
|
|
166
|
+
buckets.setdefault(cell_key_str(k), []).append(row["elapsed_ms"])
|
|
167
|
+
return {key: compute_stats(samples) for key, samples in buckets.items()}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def write_results_json(results: RunResults, path: Path) -> None:
|
|
171
|
+
"""
|
|
172
|
+
Persist aggregated + raw results to disk as JSON.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
results (RunResults): Fully populated run results.
|
|
176
|
+
path (Path): Output file path. Parent directory is created.
|
|
177
|
+
"""
|
|
178
|
+
payload = {
|
|
179
|
+
"config": results.config,
|
|
180
|
+
"stats": {k: asdict(v) for k, v in results.stats.items()},
|
|
181
|
+
"raw": results.raw,
|
|
182
|
+
}
|
|
183
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
184
|
+
path.write_text(json.dumps(payload, indent=2))
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def load_baseline(path: Path) -> dict[str, CellStats]:
|
|
188
|
+
"""
|
|
189
|
+
Load aggregated baseline stats from a previously-written results file.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
path (Path): Baseline results.json file.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
dict[str, CellStats]: Stats indexed by flat cell key. Empty dict
|
|
196
|
+
if the file is missing or malformed.
|
|
197
|
+
"""
|
|
198
|
+
if not path.exists():
|
|
199
|
+
return {}
|
|
200
|
+
data = json.loads(path.read_text())
|
|
201
|
+
return {
|
|
202
|
+
k: CellStats(**v)
|
|
203
|
+
for k, v in data.get("stats", {}).items() # type: ignore[arg-type]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _fmt_cell(stats: CellStats) -> str:
|
|
208
|
+
"""
|
|
209
|
+
Render a single cell's stats as ``p50 / p95 (mean)`` ms.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
stats (CellStats): The cell's aggregated stats.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
str: Human-readable one-line summary, or ``"—"`` for empty cells.
|
|
216
|
+
"""
|
|
217
|
+
if stats.trials == 0:
|
|
218
|
+
return "—"
|
|
219
|
+
return f"{stats.p50_ms:.1f} / {stats.p95_ms:.1f} ({stats.mean_ms:.1f})"
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _diff_cell(current: CellStats, baseline: CellStats | None) -> str:
|
|
223
|
+
"""
|
|
224
|
+
Render a baseline-diff cell showing ΔP95 and a regression marker.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
current (CellStats): This run's stats for the cell.
|
|
228
|
+
baseline (CellStats | None): Prior run's stats, if any.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
str: ``+42%`` or ``-8%`` with a ``⚠`` marker if current p95 is
|
|
232
|
+
above the regression threshold.
|
|
233
|
+
"""
|
|
234
|
+
if baseline is None or baseline.p95_ms == 0 or current.trials == 0:
|
|
235
|
+
return "—"
|
|
236
|
+
ratio = current.p95_ms / baseline.p95_ms
|
|
237
|
+
delta = (ratio - 1.0) * 100
|
|
238
|
+
marker = " ⚠" if ratio >= REGRESSION_THRESHOLD else ""
|
|
239
|
+
return f"{delta:+.0f}%{marker}"
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def render_markdown(
|
|
243
|
+
results: RunResults, baseline: dict[str, CellStats] | None = None
|
|
244
|
+
) -> str:
|
|
245
|
+
"""
|
|
246
|
+
Render the full run results as a markdown report.
|
|
247
|
+
|
|
248
|
+
One table per retrieval type, rows grouped by (backend, layer, N),
|
|
249
|
+
with an optional ΔP95 column against the committed baseline.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
results (RunResults): Fully populated run results.
|
|
253
|
+
baseline (dict[str, CellStats] | None): Optional baseline stats.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
str: Markdown report suitable for writing to ``report.md``.
|
|
257
|
+
"""
|
|
258
|
+
retrieval_types = sorted({row["retrieval_type"] for row in results.raw})
|
|
259
|
+
backends = sorted({row["backend"] for row in results.raw})
|
|
260
|
+
layers = sorted({row["layer"] for row in results.raw})
|
|
261
|
+
sizes = sorted({row["n"] for row in results.raw})
|
|
262
|
+
|
|
263
|
+
lines: list[str] = ["# Retrieval latency benchmark", ""]
|
|
264
|
+
lines.append(f"- Trials per cell: {results.config.get('trials', '?')}")
|
|
265
|
+
lines.append(f"- Warmup per cell: {results.config.get('warmup', '?')}")
|
|
266
|
+
lines.append(f"- Query set: {len(QUERIES)} fixed queries (see scenarios.py)")
|
|
267
|
+
lines.append("- Cell format: `p50 / p95 (mean)` in ms")
|
|
268
|
+
if baseline:
|
|
269
|
+
lines.append(f"- Regression threshold: p95 ≥ {REGRESSION_THRESHOLD:.0%}")
|
|
270
|
+
lines.append("")
|
|
271
|
+
|
|
272
|
+
for rt in retrieval_types:
|
|
273
|
+
lines.append(f"## {rt}")
|
|
274
|
+
lines.append("")
|
|
275
|
+
header = ["backend", "layer", *[f"N={n}" for n in sizes]]
|
|
276
|
+
if baseline:
|
|
277
|
+
header.append("ΔP95")
|
|
278
|
+
lines.append("| " + " | ".join(header) + " |")
|
|
279
|
+
lines.append("|" + "|".join(["---"] * len(header)) + "|")
|
|
280
|
+
|
|
281
|
+
for backend in backends:
|
|
282
|
+
for layer in layers:
|
|
283
|
+
row_cells: list[str] = [backend, layer]
|
|
284
|
+
latest_stats: CellStats | None = None
|
|
285
|
+
for n in sizes:
|
|
286
|
+
key = cell_key_str(CellKey(backend, rt, layer, n))
|
|
287
|
+
stats = results.stats.get(
|
|
288
|
+
key,
|
|
289
|
+
CellStats(0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
|
290
|
+
)
|
|
291
|
+
row_cells.append(_fmt_cell(stats))
|
|
292
|
+
if stats.trials > 0:
|
|
293
|
+
latest_stats = stats
|
|
294
|
+
if baseline and latest_stats is not None:
|
|
295
|
+
# Diff against the largest-N cell we have for this row.
|
|
296
|
+
largest_n = max(
|
|
297
|
+
(
|
|
298
|
+
n
|
|
299
|
+
for n in sizes
|
|
300
|
+
if cell_key_str(CellKey(backend, rt, layer, n))
|
|
301
|
+
in results.stats
|
|
302
|
+
),
|
|
303
|
+
default=None,
|
|
304
|
+
)
|
|
305
|
+
base_key = (
|
|
306
|
+
cell_key_str(CellKey(backend, rt, layer, largest_n))
|
|
307
|
+
if largest_n is not None
|
|
308
|
+
else None
|
|
309
|
+
)
|
|
310
|
+
base_stats = baseline.get(base_key) if base_key else None
|
|
311
|
+
row_cells.append(_diff_cell(latest_stats, base_stats))
|
|
312
|
+
elif baseline:
|
|
313
|
+
row_cells.append("—")
|
|
314
|
+
lines.append("| " + " | ".join(row_cells) + " |")
|
|
315
|
+
lines.append("")
|
|
316
|
+
|
|
317
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Retrieval latency benchmark
|
|
2
|
+
|
|
3
|
+
- Trials per cell: 30
|
|
4
|
+
- Warmup per cell: 3
|
|
5
|
+
- Query set: 20 fixed queries (see scenarios.py)
|
|
6
|
+
- Cell format: `p50 / p95 (mean)` in ms
|
|
7
|
+
|
|
8
|
+
## agent_playbook
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
| backend | layer | N=100 | N=1000 |
|
|
12
|
+
| ------- | ------- | ------------------ | ------------------ |
|
|
13
|
+
| sqlite | http | 18.8 / 39.1 (21.4) | 19.2 / 21.3 (19.7) |
|
|
14
|
+
| sqlite | service | 15.2 / 16.9 (15.5) | 16.9 / 21.6 (17.6) |
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## profile
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
| backend | layer | N=100 | N=1000 |
|
|
21
|
+
| ------- | ------- | --------------- | ------------------ |
|
|
22
|
+
| sqlite | http | 4.1 / 5.6 (4.4) | 20.7 / 25.6 (21.2) |
|
|
23
|
+
| sqlite | service | 1.8 / 2.2 (2.0) | 17.4 / 21.2 (17.9) |
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## unified
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
| backend | layer | N=100 | N=1000 |
|
|
30
|
+
| ------- | ------- | ------------------ | ------------------ |
|
|
31
|
+
| sqlite | http | 24.7 / 30.3 (25.4) | 58.8 / 71.8 (59.9) |
|
|
32
|
+
| sqlite | service | 20.1 / 21.5 (20.1) | 57.5 / 72.7 (58.3) |
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## user_playbook
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
| backend | layer | N=100 | N=1000 |
|
|
39
|
+
| ------- | ------- | --------------- | ------------------ |
|
|
40
|
+
| sqlite | http | 3.9 / 4.8 (4.2) | 19.5 / 22.0 (19.8) |
|
|
41
|
+
| sqlite | service | 1.6 / 2.0 (1.7) | 17.1 / 17.8 (17.1) |
|
|
42
|
+
|
|
43
|
+
|