claude-smart 0.2.44 → 0.2.46
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 +34 -5
- package/bin/claude-smart.js +295 -5
- package/package.json +22 -3
- package/plugin/.claude-plugin/plugin.json +4 -2
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +22 -2
- package/plugin/commands/clear-all.md +1 -0
- package/plugin/commands/dashboard.md +1 -0
- package/plugin/commands/learn.md +1 -0
- package/plugin/commands/restart.md +1 -0
- package/plugin/commands/show.md +1 -0
- package/plugin/dashboard/app/configure/env/page.tsx +40 -14
- package/plugin/dashboard/app/configure/server/page.tsx +51 -1
- package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
- package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
- package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
- package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
- package/plugin/dashboard/lib/reflexio-client.ts +16 -0
- package/plugin/dashboard/lib/status.ts +10 -3
- package/plugin/dashboard/lib/types.ts +18 -6
- package/plugin/hooks/codex-hooks.json +0 -1
- package/plugin/opencode/assistant-buffer.ts +108 -0
- package/plugin/opencode/dist/assistant-buffer.js +96 -0
- package/plugin/opencode/dist/internal.js +12 -0
- package/plugin/opencode/dist/payload.js +85 -0
- package/plugin/opencode/dist/server.mjs +158 -0
- package/plugin/opencode/internal.ts +18 -0
- package/plugin/opencode/package.json +3 -0
- package/plugin/opencode/payload.ts +90 -0
- package/plugin/opencode/server.mts +174 -0
- package/plugin/opencode/tsconfig.json +13 -0
- package/plugin/pyproject.toml +20 -3
- package/plugin/scripts/_lib.sh +77 -29
- package/plugin/scripts/backend-service.sh +27 -5
- package/plugin/scripts/dashboard-service.sh +7 -5
- package/plugin/scripts/ensure-plugin-root.sh +4 -4
- package/plugin/scripts/hook_entry.sh +3 -3
- package/plugin/scripts/opencode-claude-compat +4 -0
- package/plugin/scripts/opencode-claude-compat.cmd +3 -0
- package/plugin/scripts/opencode-claude-compat.js +225 -0
- package/plugin/scripts/smart-install.sh +10 -8
- package/plugin/src/README.md +1 -1
- package/plugin/src/claude_smart/cli.py +304 -6
- package/plugin/src/claude_smart/env_config.py +12 -4
- package/plugin/src/claude_smart/events/session_start.py +26 -7
- package/plugin/src/claude_smart/events/stop.py +1 -1
- package/plugin/src/claude_smart/ids.py +1 -1
- package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
- package/plugin/src/claude_smart/runtime.py +7 -1
- package/plugin/uv.lock +1 -1
- package/plugin/vendor/reflexio/.env.example +54 -30
- package/plugin/vendor/reflexio/README.md +14 -8
- package/plugin/vendor/reflexio/pyproject.toml +13 -1
- package/plugin/vendor/reflexio/reflexio/README.md +1 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +210 -50
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
- package/plugin/vendor/reflexio/reflexio/client/client.py +57 -13
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +238 -10
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +16 -5
- package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +198 -6
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +29 -20
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +2 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +52 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +88 -7
- package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
- package/plugin/vendor/reflexio/reflexio/server/README.md +45 -32
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +65 -2
- package/plugin/vendor/reflexio/reflexio/server/api.py +734 -85
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +3 -1
- package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
- package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
- package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +266 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +26 -11
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +39 -16
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +193 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +11 -12
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +316 -178
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +84 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +30 -22
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
- package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +83 -6
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +27 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +642 -197
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +61 -104
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +628 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +978 -184
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +519 -128
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +77 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +131 -20
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +36 -53
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +243 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +121 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +60 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +229 -32
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
- package/scripts/setup-claude-smart.sh +8 -3
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
- /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
- /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
|
@@ -17,7 +17,7 @@ from collections import OrderedDict
|
|
|
17
17
|
from collections.abc import Callable
|
|
18
18
|
from concurrent.futures import Future, ThreadPoolExecutor
|
|
19
19
|
from concurrent.futures import TimeoutError as FuturesTimeoutError
|
|
20
|
-
from typing import TYPE_CHECKING, Any
|
|
20
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
21
21
|
|
|
22
22
|
from reflexio.models.api_schema.retriever_schema import (
|
|
23
23
|
ConversationTurn,
|
|
@@ -41,7 +41,7 @@ from reflexio.models.config_schema import (
|
|
|
41
41
|
from reflexio.server.llm.litellm_client import LiteLLMClient
|
|
42
42
|
from reflexio.server.prompt.prompt_manager import PromptManager
|
|
43
43
|
from reflexio.server.services.pre_retrieval import QueryReformulator
|
|
44
|
-
from reflexio.server.services.retrieval.relevance_floor import
|
|
44
|
+
from reflexio.server.services.retrieval.relevance_floor import apply_relevance_floors
|
|
45
45
|
from reflexio.server.services.storage.storage_base import BaseStorage
|
|
46
46
|
from reflexio.server.tracing import profile_step, set_span_data
|
|
47
47
|
|
|
@@ -50,6 +50,7 @@ if TYPE_CHECKING:
|
|
|
50
50
|
|
|
51
51
|
logger = logging.getLogger(__name__)
|
|
52
52
|
_DEFAULT_ENTITY_TYPES = frozenset({"profiles", "agent_playbooks", "user_playbooks"})
|
|
53
|
+
_SOURCE_USER_PLAYBOOK_IDS_KEY = "_source_user_playbook_ids"
|
|
53
54
|
# Statuses returned for agent_playbooks when the caller does not pass an
|
|
54
55
|
# explicit ``agent_playbook_status_filter``. Excludes REJECTED so that a
|
|
55
56
|
# rejection in the dashboard immediately suppresses the playbook from search
|
|
@@ -66,6 +67,7 @@ _SEARCH_FANOUT_EXECUTOR = ThreadPoolExecutor(
|
|
|
66
67
|
max_workers=_SEARCH_FANOUT_MAX_WORKERS,
|
|
67
68
|
thread_name_prefix="reflexio-search",
|
|
68
69
|
)
|
|
70
|
+
_ENV_SINGLE_RPC = "REFLEXIO_UNIFIED_SEARCH_SINGLE_RPC"
|
|
69
71
|
_EMBEDDING_CACHE_TTL_SECONDS = max(
|
|
70
72
|
0, int(os.getenv("REFLEXIO_QUERY_EMBEDDING_CACHE_TTL_SECONDS", "300") or "300")
|
|
71
73
|
)
|
|
@@ -76,6 +78,20 @@ _embedding_cache_lock = threading.Lock()
|
|
|
76
78
|
_embedding_cache: OrderedDict[tuple[str, int, str, str], tuple[float, list[float]]] = (
|
|
77
79
|
OrderedDict()
|
|
78
80
|
)
|
|
81
|
+
RetrievalCaptureHook = Callable[
|
|
82
|
+
[UnifiedSearchRequest, UnifiedSearchResponse, BaseStorage, str], None
|
|
83
|
+
]
|
|
84
|
+
_retrieval_capture_hook: RetrievalCaptureHook | None = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def configure_retrieval_capture_hook(hook: RetrievalCaptureHook | None) -> None:
|
|
88
|
+
"""Register an optional final-response retrieval capture hook.
|
|
89
|
+
|
|
90
|
+
Deployments that capture retrieval logs install the hook; OSS leaves it
|
|
91
|
+
unset so unified search behavior is unchanged by default.
|
|
92
|
+
"""
|
|
93
|
+
global _retrieval_capture_hook
|
|
94
|
+
_retrieval_capture_hook = hook
|
|
79
95
|
|
|
80
96
|
|
|
81
97
|
def run_unified_search(
|
|
@@ -152,7 +168,13 @@ def run_unified_search(
|
|
|
152
168
|
cfg=floor_cfg,
|
|
153
169
|
)
|
|
154
170
|
|
|
155
|
-
|
|
171
|
+
user_playbooks = _suppress_source_user_playbooks(
|
|
172
|
+
storage=storage,
|
|
173
|
+
agent_playbooks=agent_playbooks or [],
|
|
174
|
+
user_playbooks=user_playbooks or [],
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
response = UnifiedSearchResponse(
|
|
156
178
|
success=True,
|
|
157
179
|
profiles=profiles,
|
|
158
180
|
agent_playbooks=agent_playbooks, # type: ignore[reportArgumentType]
|
|
@@ -161,6 +183,32 @@ def run_unified_search(
|
|
|
161
183
|
if reformulated_query != request.query
|
|
162
184
|
else None,
|
|
163
185
|
)
|
|
186
|
+
_maybe_capture_final_response(
|
|
187
|
+
request=request,
|
|
188
|
+
response=response,
|
|
189
|
+
storage=storage,
|
|
190
|
+
org_id=org_id,
|
|
191
|
+
)
|
|
192
|
+
return response
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _maybe_capture_final_response(
|
|
196
|
+
*,
|
|
197
|
+
request: UnifiedSearchRequest,
|
|
198
|
+
response: UnifiedSearchResponse,
|
|
199
|
+
storage: BaseStorage,
|
|
200
|
+
org_id: str,
|
|
201
|
+
) -> None:
|
|
202
|
+
hook = _retrieval_capture_hook
|
|
203
|
+
if hook is None:
|
|
204
|
+
return
|
|
205
|
+
try:
|
|
206
|
+
hook(request, response, storage, org_id)
|
|
207
|
+
except Exception:
|
|
208
|
+
logger.warning(
|
|
209
|
+
"Unified search retrieval capture hook failed",
|
|
210
|
+
exc_info=True,
|
|
211
|
+
)
|
|
164
212
|
|
|
165
213
|
|
|
166
214
|
def _run_phase_a(
|
|
@@ -266,6 +314,33 @@ def _run_phase_b(
|
|
|
266
314
|
entity_types=sorted(entity_types),
|
|
267
315
|
top_k=top_k,
|
|
268
316
|
) as span:
|
|
317
|
+
if (
|
|
318
|
+
getattr(storage, "supports_unified_hybrid_search", False)
|
|
319
|
+
and _unified_single_rpc_enabled()
|
|
320
|
+
):
|
|
321
|
+
combined = _run_phase_b_single_rpc(
|
|
322
|
+
request=request,
|
|
323
|
+
storage=storage,
|
|
324
|
+
embedding=embedding,
|
|
325
|
+
query=query,
|
|
326
|
+
top_k=top_k,
|
|
327
|
+
threshold=threshold,
|
|
328
|
+
entity_types=entity_types,
|
|
329
|
+
allowed_agent_statuses=allowed_agent_statuses,
|
|
330
|
+
)
|
|
331
|
+
if combined is not None:
|
|
332
|
+
profiles, agent_playbooks, user_playbooks = combined
|
|
333
|
+
set_span_data(
|
|
334
|
+
span,
|
|
335
|
+
{
|
|
336
|
+
"single_rpc": True,
|
|
337
|
+
"profiles_count": len(profiles),
|
|
338
|
+
"agent_playbooks_count": len(agent_playbooks),
|
|
339
|
+
"user_playbooks_count": len(user_playbooks),
|
|
340
|
+
},
|
|
341
|
+
)
|
|
342
|
+
return profiles, agent_playbooks, user_playbooks
|
|
343
|
+
span.set_data("single_rpc_fallback", True)
|
|
269
344
|
profiles_future = (
|
|
270
345
|
_submit_with_current_context(
|
|
271
346
|
_SEARCH_FANOUT_EXECUTOR,
|
|
@@ -347,6 +422,86 @@ def _run_phase_b(
|
|
|
347
422
|
return profiles, agent_playbooks, user_playbooks
|
|
348
423
|
|
|
349
424
|
|
|
425
|
+
def _unified_single_rpc_enabled() -> bool:
|
|
426
|
+
"""Kill switch for the combined Phase B RPC (default on)."""
|
|
427
|
+
return os.getenv(_ENV_SINGLE_RPC, "1").strip().lower() not in {"0", "false", "off"}
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _run_phase_b_single_rpc(
|
|
431
|
+
*,
|
|
432
|
+
request: UnifiedSearchRequest,
|
|
433
|
+
storage: BaseStorage,
|
|
434
|
+
embedding: list[float] | None,
|
|
435
|
+
query: str,
|
|
436
|
+
top_k: int,
|
|
437
|
+
threshold: float,
|
|
438
|
+
entity_types: set[str],
|
|
439
|
+
allowed_agent_statuses: list[PlaybookStatus] | None,
|
|
440
|
+
) -> tuple[list[UserProfile], list[AgentPlaybook], list[UserPlaybook]] | None:
|
|
441
|
+
"""Run all Phase B arms through one combined storage round trip.
|
|
442
|
+
|
|
443
|
+
Trades the per-arm round-trip overhead for serialized execution of the
|
|
444
|
+
three queries inside one database session — a win when round-trip
|
|
445
|
+
overhead dominates per-arm query time (toggle via
|
|
446
|
+
``REFLEXIO_UNIFIED_SEARCH_SINGLE_RPC`` to compare).
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
The three result lists, or None when the combined call fails so the
|
|
450
|
+
caller can fall back to the per-arm fan-out (e.g. the SQL function
|
|
451
|
+
is not yet migrated on this deployment). Timeouts propagate like the
|
|
452
|
+
fan-out path so a hung database is not retried.
|
|
453
|
+
"""
|
|
454
|
+
statuses = (
|
|
455
|
+
list(allowed_agent_statuses)
|
|
456
|
+
if allowed_agent_statuses
|
|
457
|
+
else list(_DEFAULT_AGENT_PLAYBOOK_STATUSES)
|
|
458
|
+
)
|
|
459
|
+
# Resolve storage.unified_hybrid_search before submit so missing or stale
|
|
460
|
+
# capability flags can fall back to the fan-out path.
|
|
461
|
+
unified_hybrid_search = getattr(storage, "unified_hybrid_search", None)
|
|
462
|
+
if not callable(unified_hybrid_search):
|
|
463
|
+
return None
|
|
464
|
+
|
|
465
|
+
future = _submit_with_current_context(
|
|
466
|
+
_SEARCH_FANOUT_EXECUTOR,
|
|
467
|
+
unified_hybrid_search,
|
|
468
|
+
query=query,
|
|
469
|
+
query_embedding=embedding,
|
|
470
|
+
top_k=top_k,
|
|
471
|
+
threshold=threshold,
|
|
472
|
+
user_id=request.user_id,
|
|
473
|
+
agent_version=request.agent_version,
|
|
474
|
+
playbook_name=request.playbook_name,
|
|
475
|
+
agent_playbook_statuses=statuses,
|
|
476
|
+
search_mode=request.search_mode,
|
|
477
|
+
include_profiles="profiles" in entity_types and bool(request.user_id),
|
|
478
|
+
include_agent_playbooks="agent_playbooks" in entity_types,
|
|
479
|
+
include_user_playbooks="user_playbooks" in entity_types,
|
|
480
|
+
)
|
|
481
|
+
try:
|
|
482
|
+
profiles, agent_playbooks, user_playbooks = future.result(timeout=30)
|
|
483
|
+
except FuturesTimeoutError:
|
|
484
|
+
raise
|
|
485
|
+
except Exception:
|
|
486
|
+
logger.warning(
|
|
487
|
+
"Unified single-RPC search failed; falling back to per-arm fan-out",
|
|
488
|
+
exc_info=True,
|
|
489
|
+
)
|
|
490
|
+
return None
|
|
491
|
+
|
|
492
|
+
# Mirror _search_agent_playbooks_via_storage: dedupe by id, cap at top_k.
|
|
493
|
+
deduped: list[AgentPlaybook] = []
|
|
494
|
+
seen_ids: set[str] = set()
|
|
495
|
+
for playbook in agent_playbooks:
|
|
496
|
+
playbook_id = str(getattr(playbook, "agent_playbook_id", ""))
|
|
497
|
+
if playbook_id and playbook_id not in seen_ids:
|
|
498
|
+
seen_ids.add(playbook_id)
|
|
499
|
+
deduped.append(playbook)
|
|
500
|
+
if len(deduped) >= top_k:
|
|
501
|
+
break
|
|
502
|
+
return profiles, deduped, user_playbooks
|
|
503
|
+
|
|
504
|
+
|
|
350
505
|
def _apply_floors(
|
|
351
506
|
query: str,
|
|
352
507
|
profiles: list[UserProfile],
|
|
@@ -355,36 +510,78 @@ def _apply_floors(
|
|
|
355
510
|
top_k: int,
|
|
356
511
|
cfg: RetrievalFloorConfig,
|
|
357
512
|
) -> tuple[list[UserProfile], list[AgentPlaybook], list[UserPlaybook]]:
|
|
358
|
-
"""Apply the per-arm relevance floor
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
513
|
+
"""Apply the per-arm relevance floor with one batched cross-encoder call."""
|
|
514
|
+
floored_profiles, floored_agent, floored_user = apply_relevance_floors(
|
|
515
|
+
query,
|
|
516
|
+
[
|
|
517
|
+
("profiles", profiles, cfg.profile_floor),
|
|
518
|
+
("agent_playbooks", agent_playbooks, cfg.agent_playbook_floor),
|
|
519
|
+
("user_playbooks", user_playbooks, cfg.user_playbook_floor),
|
|
520
|
+
],
|
|
521
|
+
top_k,
|
|
522
|
+
)
|
|
523
|
+
return floored_profiles, floored_agent, floored_user
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _suppress_source_user_playbooks(
|
|
527
|
+
*,
|
|
528
|
+
storage: BaseStorage,
|
|
529
|
+
agent_playbooks: list[AgentPlaybook],
|
|
530
|
+
user_playbooks: list[UserPlaybook],
|
|
531
|
+
) -> list[UserPlaybook]:
|
|
532
|
+
"""Drop user playbooks already represented by returned agent playbooks."""
|
|
533
|
+
if not agent_playbooks or not user_playbooks:
|
|
534
|
+
return user_playbooks
|
|
535
|
+
|
|
536
|
+
source_user_playbook_ids: set[int] = set()
|
|
537
|
+
agent_ids_needing_lookup: list[int] = []
|
|
538
|
+
for playbook in agent_playbooks:
|
|
539
|
+
source_ids = getattr(playbook, _SOURCE_USER_PLAYBOOK_IDS_KEY, None)
|
|
540
|
+
if source_ids is None:
|
|
541
|
+
agent_playbook_id = int(getattr(playbook, "agent_playbook_id", 0) or 0)
|
|
542
|
+
if agent_playbook_id:
|
|
543
|
+
agent_ids_needing_lookup.append(agent_playbook_id)
|
|
544
|
+
continue
|
|
545
|
+
source_user_playbook_ids.update(int(source_id) for source_id in source_ids)
|
|
546
|
+
|
|
547
|
+
if agent_ids_needing_lookup:
|
|
548
|
+
lookup = getattr(
|
|
549
|
+
storage, "get_source_user_playbook_ids_for_agent_playbooks", None
|
|
386
550
|
)
|
|
387
|
-
|
|
551
|
+
if callable(lookup):
|
|
552
|
+
try:
|
|
553
|
+
source_ids_by_agent = cast(
|
|
554
|
+
dict[int, list[int]], lookup(agent_ids_needing_lookup)
|
|
555
|
+
)
|
|
556
|
+
except Exception:
|
|
557
|
+
logger.warning(
|
|
558
|
+
"Failed to resolve source user playbooks for unified search suppression",
|
|
559
|
+
exc_info=True,
|
|
560
|
+
)
|
|
561
|
+
else:
|
|
562
|
+
for source_ids in source_ids_by_agent.values():
|
|
563
|
+
source_user_playbook_ids.update(
|
|
564
|
+
int(source_id) for source_id in source_ids
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
if not source_user_playbook_ids:
|
|
568
|
+
return user_playbooks
|
|
569
|
+
|
|
570
|
+
filtered = [
|
|
571
|
+
playbook
|
|
572
|
+
for playbook in user_playbooks
|
|
573
|
+
if int(getattr(playbook, "user_playbook_id", 0) or 0)
|
|
574
|
+
not in source_user_playbook_ids
|
|
575
|
+
]
|
|
576
|
+
suppressed_count = len(user_playbooks) - len(filtered)
|
|
577
|
+
if suppressed_count:
|
|
578
|
+
with profile_step(
|
|
579
|
+
"search.suppress_source_user_playbooks",
|
|
580
|
+
suppressed_count=suppressed_count,
|
|
581
|
+
source_user_playbook_count=len(source_user_playbook_ids),
|
|
582
|
+
):
|
|
583
|
+
pass
|
|
584
|
+
return filtered
|
|
388
585
|
|
|
389
586
|
|
|
390
587
|
def _get_cached_query_embedding(
|
|
@@ -11,7 +11,7 @@ Description: Global configuration manager for site-wide variables and per-org fe
|
|
|
11
11
|
|
|
12
12
|
## Purpose
|
|
13
13
|
|
|
14
|
-
1. **Global settings** - Model names, embedding models
|
|
14
|
+
1. **Global settings** - Model names, embedding models, and retrieval defaults
|
|
15
15
|
2. **Feature flags** - Per-org feature gating (global enable or per-org allowlist)
|
|
16
16
|
3. **Dual storage** - File-based with optional Redis caching
|
|
17
17
|
4. **Auto-fallback** - Redis → File system graceful degradation
|
|
@@ -62,6 +62,7 @@ site_var/
|
|
|
62
62
|
└── site_var_sources/
|
|
63
63
|
├── app_config.json # JSON → parsed dict
|
|
64
64
|
├── model_config.json
|
|
65
|
+
├── search_settings.json # Default retrieval mode and hybrid search weights
|
|
65
66
|
└── feature_flags.json # Feature flag config (per-flag enable + org allowlist)
|
|
66
67
|
```
|
|
67
68
|
|
|
@@ -55,7 +55,7 @@ def is_feature_enabled(org_id: str, feature_name: str) -> bool:
|
|
|
55
55
|
if feature_config.get("enabled", False):
|
|
56
56
|
return True
|
|
57
57
|
|
|
58
|
-
enabled_org_ids = feature_config.get("enabled_org_ids", [])
|
|
58
|
+
enabled_org_ids = feature_config.get("enabled_org_ids", []) or []
|
|
59
59
|
return org_id in enabled_org_ids
|
|
60
60
|
|
|
61
61
|
|
|
@@ -103,6 +103,103 @@ def is_deduplicator_enabled(org_id: str) -> bool:
|
|
|
103
103
|
return is_feature_enabled(org_id, "deduplicator")
|
|
104
104
|
|
|
105
105
|
|
|
106
|
+
def _is_fail_closed_flag_enabled(org_id: str, feature_key: str) -> bool:
|
|
107
|
+
"""
|
|
108
|
+
Shared fail-closed helper: returns False if key absent or config malformed.
|
|
109
|
+
|
|
110
|
+
Unlike is_feature_enabled (fail-open), this function returns False when the
|
|
111
|
+
feature key is absent or when its value is not a dict. This is the safety
|
|
112
|
+
invariant for soft-delete flags — unconfigured or malformed entries must
|
|
113
|
+
never accidentally activate tombstone-producing paths.
|
|
114
|
+
|
|
115
|
+
A feature is enabled if:
|
|
116
|
+
- The feature's "enabled" field is True (globally enabled), OR
|
|
117
|
+
- The org_id is in the feature's "enabled_org_ids" list.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
org_id (str): The organization ID to check
|
|
121
|
+
feature_key (str): The feature flag key in the config dict
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
bool: True only if the feature is explicitly enabled for this org
|
|
125
|
+
"""
|
|
126
|
+
config = _get_feature_flags_config()
|
|
127
|
+
feature_config = config.get(feature_key)
|
|
128
|
+
|
|
129
|
+
if feature_config is None:
|
|
130
|
+
# Key absent — fail-CLOSED
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
if not isinstance(feature_config, dict):
|
|
134
|
+
logger.warning(
|
|
135
|
+
"feature_flags[%s] is not a dict (got %s), defaulting to OFF",
|
|
136
|
+
feature_key,
|
|
137
|
+
type(feature_config).__name__,
|
|
138
|
+
)
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
# Strict bool identity — truthy strings like "false" must not enable (#195).
|
|
142
|
+
enabled = feature_config.get("enabled", False)
|
|
143
|
+
if enabled is True:
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
# Reject non-list values — a string does substring `in` match, not membership (#195).
|
|
147
|
+
org_ids = feature_config.get("enabled_org_ids", [])
|
|
148
|
+
if not isinstance(org_ids, list):
|
|
149
|
+
return False
|
|
150
|
+
return org_id in org_ids
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _is_default_open_flag_enabled(org_id: str, feature_key: str) -> bool:
|
|
154
|
+
"""
|
|
155
|
+
Shared default-open helper for soft-delete flags.
|
|
156
|
+
|
|
157
|
+
Returns True when the feature key is absent from config (default ON), but
|
|
158
|
+
preserves all explicit-disable and per-org override semantics:
|
|
159
|
+
- Key absent or None → True (default ON — GC is also ON by default).
|
|
160
|
+
- Key present but malformed (not a dict) → False with a warning (safe fallback).
|
|
161
|
+
- Key present, enabled=True → True for all orgs.
|
|
162
|
+
- Key present, enabled=False, org in enabled_org_ids → True.
|
|
163
|
+
- Key present, enabled=False, org NOT in enabled_org_ids → False (explicit disable).
|
|
164
|
+
|
|
165
|
+
Strict-bool and strict-list guards from _is_fail_closed_flag_enabled are
|
|
166
|
+
preserved: truthy strings and non-bool ints do NOT enable, and a string
|
|
167
|
+
enabled_org_ids does NOT match via substring (anti-#195).
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
org_id (str): The organization ID to check
|
|
171
|
+
feature_key (str): The feature flag key in the config dict
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
bool: True when the flag is on (including when absent/unconfigured)
|
|
175
|
+
"""
|
|
176
|
+
config = _get_feature_flags_config()
|
|
177
|
+
feature_config = config.get(feature_key)
|
|
178
|
+
|
|
179
|
+
if feature_config is None:
|
|
180
|
+
# Key absent — default OPEN (soft-delete is on by default; GC runs too).
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
if not isinstance(feature_config, dict):
|
|
184
|
+
logger.warning(
|
|
185
|
+
"feature_flags[%s] is not a dict (got %s), defaulting to OFF",
|
|
186
|
+
feature_key,
|
|
187
|
+
type(feature_config).__name__,
|
|
188
|
+
)
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
# Strict bool identity — truthy strings like "false" must not enable (#195).
|
|
192
|
+
enabled = feature_config.get("enabled", False)
|
|
193
|
+
if enabled is True:
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
# Reject non-list values — a string does substring `in` match, not membership (#195).
|
|
197
|
+
org_ids = feature_config.get("enabled_org_ids", [])
|
|
198
|
+
if not isinstance(org_ids, list):
|
|
199
|
+
return False
|
|
200
|
+
return org_id in org_ids
|
|
201
|
+
|
|
202
|
+
|
|
106
203
|
def is_resumable_extraction_agent_enabled(org_id: str) -> bool:
|
|
107
204
|
"""
|
|
108
205
|
Convenience check for whether classic extraction should use the resumable agent.
|
|
@@ -114,3 +211,25 @@ def is_resumable_extraction_agent_enabled(org_id: str) -> bool:
|
|
|
114
211
|
bool: True if the resumable extraction agent is enabled
|
|
115
212
|
"""
|
|
116
213
|
return is_feature_enabled(org_id, "resumable_extraction_agent")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def is_lineage_dual_read_diff_enabled(org_id: str) -> bool:
|
|
217
|
+
"""
|
|
218
|
+
Check if the lineage dual-read diff path is enabled for a given organization.
|
|
219
|
+
|
|
220
|
+
Defaults to DISABLED when the key is absent from config (default-CLOSED). This
|
|
221
|
+
flag gates an experimental read path that compares new and old lineage query
|
|
222
|
+
results side-by-side; it must never activate accidentally on unconfigured
|
|
223
|
+
deployments.
|
|
224
|
+
|
|
225
|
+
A feature is enabled only if:
|
|
226
|
+
- The feature's "enabled" field is True (globally enabled), OR
|
|
227
|
+
- The org_id is in the feature's "enabled_org_ids" list.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
org_id (str): The organization ID to check
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
bool: True only if the feature is explicitly enabled for this org
|
|
234
|
+
"""
|
|
235
|
+
return _is_fail_closed_flag_enabled(org_id, "lineage_dual_read_diff")
|
|
@@ -156,3 +156,33 @@ def sentry_tags(**tags: Any) -> Iterator[None]:
|
|
|
156
156
|
scope_cm.__exit__(None, None, None)
|
|
157
157
|
except Exception as exc: # noqa: BLE001
|
|
158
158
|
logger.warning("Failed to close Sentry scope: %s", exc)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def capture_anomaly(message: str, *, level: str = "warning", **tags: Any) -> None:
|
|
162
|
+
"""Report a non-fatal anomaly to Sentry, if the SDK is installed.
|
|
163
|
+
|
|
164
|
+
For silent-but-noteworthy conditions that return rather than raise — e.g.
|
|
165
|
+
lineage resolution hitting a cycle or the hop cap, or a best-effort
|
|
166
|
+
provenance append failing. No-op when ``sentry-sdk`` is absent (the OS
|
|
167
|
+
package does not depend on it; enterprise deployments pull it in). Never
|
|
168
|
+
raises — instrumentation must not turn a degraded-but-working path into a
|
|
169
|
+
failure.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
message: short, low-cardinality description of the anomaly (the Sentry
|
|
173
|
+
issue title), e.g. ``"lineage.resolve_current.cycle"``.
|
|
174
|
+
level: Sentry severity ("warning", "error", ...). Defaults to "warning".
|
|
175
|
+
**tags: contextual tag name → value pairs (None values are skipped).
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
import sentry_sdk # type: ignore[import-not-found]
|
|
179
|
+
except ImportError:
|
|
180
|
+
return
|
|
181
|
+
try:
|
|
182
|
+
with sentry_sdk.new_scope() as scope:
|
|
183
|
+
for key, value in tags.items():
|
|
184
|
+
if value is not None:
|
|
185
|
+
scope.set_tag(key, str(value))
|
|
186
|
+
sentry_sdk.capture_message(message, level=level)
|
|
187
|
+
except Exception as exc: # noqa: BLE001
|
|
188
|
+
logger.warning("Failed to capture Sentry anomaly %r: %s", message, exc)
|
|
@@ -34,6 +34,12 @@ class UsageEvent:
|
|
|
34
34
|
backend: str | None = None
|
|
35
35
|
outcome: str | None = None
|
|
36
36
|
count_value: int = 1
|
|
37
|
+
prompt_tokens: int | None = None
|
|
38
|
+
completion_tokens: int | None = None
|
|
39
|
+
billing_input_tokens: int | None = None
|
|
40
|
+
platform_llm: bool | None = None
|
|
41
|
+
platform_storage: bool | None = None
|
|
42
|
+
caller_type: str | None = None
|
|
37
43
|
duration_ms: int | None = None
|
|
38
44
|
error_kind: str | None = None
|
|
39
45
|
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
@@ -73,6 +79,12 @@ def record_usage_event(
|
|
|
73
79
|
backend: str | None = None,
|
|
74
80
|
outcome: str | None = None,
|
|
75
81
|
count_value: int = 1,
|
|
82
|
+
prompt_tokens: int | None = None,
|
|
83
|
+
completion_tokens: int | None = None,
|
|
84
|
+
billing_input_tokens: int | None = None,
|
|
85
|
+
platform_llm: bool | None = None,
|
|
86
|
+
platform_storage: bool | None = None,
|
|
87
|
+
caller_type: str | None = None,
|
|
76
88
|
duration_ms: int | None = None,
|
|
77
89
|
error_kind: str | None = None,
|
|
78
90
|
metadata: Mapping[str, Any] | None = None,
|
|
@@ -104,6 +116,12 @@ def record_usage_event(
|
|
|
104
116
|
backend=backend,
|
|
105
117
|
outcome=outcome,
|
|
106
118
|
count_value=count_value,
|
|
119
|
+
prompt_tokens=prompt_tokens,
|
|
120
|
+
completion_tokens=completion_tokens,
|
|
121
|
+
billing_input_tokens=billing_input_tokens,
|
|
122
|
+
platform_llm=platform_llm,
|
|
123
|
+
platform_storage=platform_storage,
|
|
124
|
+
caller_type=caller_type,
|
|
107
125
|
duration_ms=duration_ms,
|
|
108
126
|
error_kind=error_kind,
|
|
109
127
|
metadata=metadata or {},
|