claude-smart 0.2.45 → 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 +5 -1
- package/plugin/scripts/backend-service.sh +22 -2
- package/plugin/scripts/ensure-plugin-root.sh +4 -4
- package/plugin/scripts/hook_entry.sh +1 -1
- 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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Request CRUD methods for SQLite storage."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import sqlite3
|
|
5
4
|
from typing import Any
|
|
6
5
|
|
|
7
6
|
from reflexio.models.api_schema.internal_schema import (
|
|
8
7
|
RequestInteractionDataModel,
|
|
9
8
|
SessionDescriptor,
|
|
9
|
+
SessionFirstRequest,
|
|
10
10
|
)
|
|
11
11
|
from reflexio.models.api_schema.service_schemas import (
|
|
12
12
|
Request,
|
|
@@ -15,6 +15,7 @@ from reflexio.models.api_schema.service_schemas import (
|
|
|
15
15
|
from ._base import (
|
|
16
16
|
SQLiteStorageBase,
|
|
17
17
|
_epoch_to_iso,
|
|
18
|
+
_iso_to_epoch,
|
|
18
19
|
_row_to_interaction,
|
|
19
20
|
_row_to_request,
|
|
20
21
|
)
|
|
@@ -39,7 +40,7 @@ class RequestMixin:
|
|
|
39
40
|
created_at_iso = _epoch_to_iso(request.created_at)
|
|
40
41
|
self._execute(
|
|
41
42
|
"""INSERT OR REPLACE INTO requests
|
|
42
|
-
(request_id, user_id, created_at, source, agent_version, session_id,
|
|
43
|
+
(request_id, user_id, created_at, source, agent_version, session_id, evaluation_only)
|
|
43
44
|
VALUES (?,?,?,?,?,?,?)""",
|
|
44
45
|
(
|
|
45
46
|
request.request_id,
|
|
@@ -48,7 +49,7 @@ class RequestMixin:
|
|
|
48
49
|
request.source,
|
|
49
50
|
request.agent_version,
|
|
50
51
|
request.session_id,
|
|
51
|
-
|
|
52
|
+
1 if request.evaluation_only else 0,
|
|
52
53
|
),
|
|
53
54
|
)
|
|
54
55
|
|
|
@@ -261,3 +262,76 @@ class RequestMixin:
|
|
|
261
262
|
)
|
|
262
263
|
for r in rows
|
|
263
264
|
]
|
|
265
|
+
|
|
266
|
+
@SQLiteStorageBase.handle_exceptions
|
|
267
|
+
def get_first_requests_by_session_ids(
|
|
268
|
+
self, session_ids: list[str]
|
|
269
|
+
) -> dict[str, SessionFirstRequest]:
|
|
270
|
+
if not session_ids:
|
|
271
|
+
return {}
|
|
272
|
+
out: dict[str, SessionFirstRequest] = {}
|
|
273
|
+
ids = sorted(set(session_ids))
|
|
274
|
+
chunk_size = 500
|
|
275
|
+
for i in range(0, len(ids), chunk_size):
|
|
276
|
+
chunk = ids[i : i + chunk_size]
|
|
277
|
+
ph = ",".join("?" for _ in chunk)
|
|
278
|
+
rows = self._fetchall(
|
|
279
|
+
f"""SELECT session_id, user_id, source, created_at
|
|
280
|
+
FROM (
|
|
281
|
+
SELECT session_id, user_id, source, created_at,
|
|
282
|
+
ROW_NUMBER() OVER (
|
|
283
|
+
PARTITION BY session_id
|
|
284
|
+
ORDER BY created_at ASC, request_id ASC
|
|
285
|
+
) AS rn
|
|
286
|
+
FROM requests
|
|
287
|
+
WHERE session_id IN ({ph})
|
|
288
|
+
)
|
|
289
|
+
WHERE rn = 1""", # noqa: S608
|
|
290
|
+
chunk,
|
|
291
|
+
)
|
|
292
|
+
for row in rows:
|
|
293
|
+
session_id = row["session_id"]
|
|
294
|
+
out[session_id] = SessionFirstRequest(
|
|
295
|
+
session_id=session_id,
|
|
296
|
+
user_id=row["user_id"],
|
|
297
|
+
source=row["source"] or "",
|
|
298
|
+
created_at=_iso_to_epoch(row["created_at"]),
|
|
299
|
+
)
|
|
300
|
+
return out
|
|
301
|
+
|
|
302
|
+
@SQLiteStorageBase.handle_exceptions
|
|
303
|
+
def get_first_requests_by_user_session_pairs(
|
|
304
|
+
self, pairs: list[tuple[str, str]]
|
|
305
|
+
) -> dict[tuple[str, str], SessionFirstRequest]:
|
|
306
|
+
if not pairs:
|
|
307
|
+
return {}
|
|
308
|
+
out: dict[tuple[str, str], SessionFirstRequest] = {}
|
|
309
|
+
pair_list = sorted(set(pairs))
|
|
310
|
+
chunk_size = 300
|
|
311
|
+
for i in range(0, len(pair_list), chunk_size):
|
|
312
|
+
chunk = pair_list[i : i + chunk_size]
|
|
313
|
+
values = ",".join("(?, ?)" for _ in chunk)
|
|
314
|
+
params = [value for pair in chunk for value in pair]
|
|
315
|
+
rows = self._fetchall(
|
|
316
|
+
f"""SELECT session_id, user_id, source, created_at
|
|
317
|
+
FROM (
|
|
318
|
+
SELECT session_id, user_id, source, created_at,
|
|
319
|
+
ROW_NUMBER() OVER (
|
|
320
|
+
PARTITION BY user_id, session_id
|
|
321
|
+
ORDER BY created_at ASC, request_id ASC
|
|
322
|
+
) AS rn
|
|
323
|
+
FROM requests
|
|
324
|
+
WHERE (user_id, session_id) IN ({values})
|
|
325
|
+
)
|
|
326
|
+
WHERE rn = 1""", # noqa: S608
|
|
327
|
+
params,
|
|
328
|
+
)
|
|
329
|
+
for row in rows:
|
|
330
|
+
key = (row["user_id"], row["session_id"])
|
|
331
|
+
out[key] = SessionFirstRequest(
|
|
332
|
+
session_id=row["session_id"],
|
|
333
|
+
user_id=row["user_id"],
|
|
334
|
+
source=row["source"] or "",
|
|
335
|
+
created_at=_iso_to_epoch(row["created_at"]),
|
|
336
|
+
)
|
|
337
|
+
return out
|
package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py
CHANGED
|
@@ -13,13 +13,6 @@ from reflexio.models.api_schema.eval_overview_schema import (
|
|
|
13
13
|
|
|
14
14
|
from ._base import SQLiteStorageBase, _epoch_to_iso
|
|
15
15
|
|
|
16
|
-
# Maximum epoch seconds that ``datetime.fromtimestamp`` can represent (year
|
|
17
|
-
# 9999-12-31). Callers passing sentinel "open" upper bounds like
|
|
18
|
-
# ``sys.maxsize`` or ``10**12`` would otherwise overflow ``_epoch_to_iso``;
|
|
19
|
-
# clamping to this value yields the same query semantics (≥ everything) with
|
|
20
|
-
# a valid ISO string.
|
|
21
|
-
_MAX_SAFE_EPOCH_TS = 253_402_300_799 # 9999-12-31T23:59:59Z
|
|
22
|
-
|
|
23
16
|
|
|
24
17
|
def _parse_dt(value: str) -> datetime:
|
|
25
18
|
"""
|
|
@@ -164,8 +157,8 @@ class ShadowVerdictsMixin:
|
|
|
164
157
|
list[ShadowComparisonVerdict]: Verdicts in chronological order
|
|
165
158
|
(ascending ``created_at``).
|
|
166
159
|
"""
|
|
167
|
-
from_iso = _epoch_to_iso(
|
|
168
|
-
to_iso = _epoch_to_iso(
|
|
160
|
+
from_iso = _epoch_to_iso(from_ts)
|
|
161
|
+
to_iso = _epoch_to_iso(to_ts)
|
|
169
162
|
rows = self._fetchall(
|
|
170
163
|
"""SELECT * FROM shadow_comparison_verdicts
|
|
171
164
|
WHERE created_at >= ? AND created_at <= ?
|
|
@@ -175,6 +168,27 @@ class ShadowVerdictsMixin:
|
|
|
175
168
|
)
|
|
176
169
|
return [_row_to_verdict(r) for r in rows]
|
|
177
170
|
|
|
171
|
+
@SQLiteStorageBase.handle_exceptions
|
|
172
|
+
def get_recent_shadow_comparison_verdicts(
|
|
173
|
+
self,
|
|
174
|
+
from_ts: int,
|
|
175
|
+
to_ts: int,
|
|
176
|
+
judge_prompt_version: str,
|
|
177
|
+
limit: int,
|
|
178
|
+
) -> list[ShadowComparisonVerdict]:
|
|
179
|
+
from_iso = _epoch_to_iso(from_ts)
|
|
180
|
+
to_iso = _epoch_to_iso(to_ts)
|
|
181
|
+
safe_limit = max(0, limit)
|
|
182
|
+
rows = self._fetchall(
|
|
183
|
+
"""SELECT * FROM shadow_comparison_verdicts
|
|
184
|
+
WHERE created_at >= ? AND created_at <= ?
|
|
185
|
+
AND judge_prompt_version = ?
|
|
186
|
+
ORDER BY created_at DESC
|
|
187
|
+
LIMIT ?""",
|
|
188
|
+
(from_iso, to_iso, judge_prompt_version, safe_limit),
|
|
189
|
+
)
|
|
190
|
+
return [_row_to_verdict(r) for r in rows]
|
|
191
|
+
|
|
178
192
|
@SQLiteStorageBase.handle_exceptions
|
|
179
193
|
def delete_shadow_comparison_verdicts_by_session(self, session_id: str) -> int:
|
|
180
194
|
"""
|
package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py
CHANGED
|
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
import logging
|
|
12
12
|
import sqlite3
|
|
13
13
|
from dataclasses import dataclass
|
|
14
|
-
from datetime import datetime
|
|
14
|
+
from datetime import UTC, datetime
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
17
|
from reflexio.models.api_schema.stall_state_schema import StallReason
|
|
@@ -163,10 +163,13 @@ def _parse_ts(raw: str | None) -> datetime | None:
|
|
|
163
163
|
if raw is None:
|
|
164
164
|
return None
|
|
165
165
|
try:
|
|
166
|
-
|
|
166
|
+
parsed = datetime.fromisoformat(raw)
|
|
167
167
|
except ValueError:
|
|
168
168
|
_LOGGER.warning("Malformed timestamp in stall_state: %r", raw)
|
|
169
169
|
return None
|
|
170
|
+
if parsed.tzinfo is None:
|
|
171
|
+
parsed = parsed.replace(tzinfo=UTC)
|
|
172
|
+
return parsed
|
|
170
173
|
|
|
171
174
|
|
|
172
175
|
class SQLiteStallStateMixin:
|
|
@@ -23,10 +23,12 @@ from ._agent_run import (
|
|
|
23
23
|
)
|
|
24
24
|
from ._base import BaseStorageCore, matches_status_filter
|
|
25
25
|
from ._extras import ExtrasMixin
|
|
26
|
+
from ._lineage import EntityType, LineageEventMixin
|
|
26
27
|
from ._operations import OperationMixin
|
|
27
28
|
from ._playbook import PlaybookMixin
|
|
28
29
|
from ._profiles import ProfileMixin
|
|
29
30
|
from ._requests import RequestMixin
|
|
31
|
+
from ._retrieval_log import RetrievalLogMixin
|
|
30
32
|
from ._shadow_verdicts import ShadowVerdictsMixin
|
|
31
33
|
from ._share_links import ShareLinkMixin
|
|
32
34
|
from ._stall_state import StallStateMixin
|
|
@@ -37,6 +39,8 @@ class BaseStorage(
|
|
|
37
39
|
ProfileMixin,
|
|
38
40
|
RequestMixin,
|
|
39
41
|
PlaybookMixin,
|
|
42
|
+
RetrievalLogMixin,
|
|
43
|
+
LineageEventMixin,
|
|
40
44
|
OperationMixin,
|
|
41
45
|
ExtrasMixin,
|
|
42
46
|
ShareLinkMixin,
|
|
@@ -46,6 +50,65 @@ class BaseStorage(
|
|
|
46
50
|
):
|
|
47
51
|
"""Base class for storage."""
|
|
48
52
|
|
|
53
|
+
def _is_lineage_tombstone(self, entity_type: EntityType, entity_id: str) -> bool:
|
|
54
|
+
"""Return True when the entity row is a tombstone (merged_into or superseded_by set).
|
|
55
|
+
|
|
56
|
+
Uses the portable ``include_tombstones=True`` getters so the check
|
|
57
|
+
works for every backend without backend-specific SQL.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
entity_type (EntityType): ``"profile"`` or ``"user_playbook"``.
|
|
61
|
+
entity_id (str): The entity's primary key as a string.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
bool: True if the row has ``merged_into`` or ``superseded_by`` set.
|
|
65
|
+
"""
|
|
66
|
+
if entity_type == "profile":
|
|
67
|
+
row = self.get_profile_by_id(entity_id, include_tombstones=True)
|
|
68
|
+
if row is None:
|
|
69
|
+
return False
|
|
70
|
+
return bool(row.merged_into or row.superseded_by)
|
|
71
|
+
if entity_type == "user_playbook":
|
|
72
|
+
row = self.get_user_playbook_by_id(int(entity_id), include_tombstones=True)
|
|
73
|
+
if row is None:
|
|
74
|
+
return False
|
|
75
|
+
return bool(row.merged_into or row.superseded_by)
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def _partition_purge_vs_delete(
|
|
79
|
+
self, entity_type: EntityType, ids: list[str]
|
|
80
|
+
) -> tuple[list[str], list[str]]:
|
|
81
|
+
"""Split entity ids into purge-eligible and hard-delete sets.
|
|
82
|
+
|
|
83
|
+
An entity is purge-eligible when it is a tombstone (``merged_into`` or
|
|
84
|
+
``superseded_by`` is set) **or** is pointed to by another row
|
|
85
|
+
(``has_inbound_lineage_refs`` returns True). All other ids go to the
|
|
86
|
+
hard-delete set.
|
|
87
|
+
|
|
88
|
+
Uses only portable storage reads so this helper works for every
|
|
89
|
+
backend (SQLite, Supabase, Postgres). Callers that need
|
|
90
|
+
backend-specific efficiency may override ``clear_user_data`` instead.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
entity_type (EntityType): ``"profile"`` or ``"user_playbook"``.
|
|
94
|
+
ids (list[str]): Entity primary keys as strings.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
tuple[list[str], list[str]]: ``(purge_ids, delete_ids)`` where
|
|
98
|
+
``purge_ids`` are content-purge candidates and ``delete_ids``
|
|
99
|
+
are safe to hard-delete.
|
|
100
|
+
"""
|
|
101
|
+
purge_ids: list[str] = []
|
|
102
|
+
delete_ids: list[str] = []
|
|
103
|
+
for eid in ids:
|
|
104
|
+
if self._is_lineage_tombstone(
|
|
105
|
+
entity_type, eid
|
|
106
|
+
) or self.has_inbound_lineage_refs(entity_type=entity_type, entity_id=eid):
|
|
107
|
+
purge_ids.append(eid)
|
|
108
|
+
else:
|
|
109
|
+
delete_ids.append(eid)
|
|
110
|
+
return purge_ids, delete_ids
|
|
111
|
+
|
|
49
112
|
def clear_user_data(self, user_id: str) -> dict[str, int]:
|
|
50
113
|
"""Delete all rows scoped to a single ``user_id``.
|
|
51
114
|
|
|
@@ -57,38 +120,72 @@ class BaseStorage(
|
|
|
57
120
|
parallel tasks without one task's clear-all nuking another
|
|
58
121
|
in-flight task's rows.
|
|
59
122
|
|
|
123
|
+
**Lineage-aware erasure:** rows that are tombstones (``merged_into``
|
|
124
|
+
or ``superseded_by`` is set) *or* are pointed to by another row
|
|
125
|
+
(``has_inbound_lineage_refs`` returns True) are content-purged
|
|
126
|
+
(skeleton kept, body blanked) rather than hard-deleted. Standalone
|
|
127
|
+
rows with no lineage involvement are hard-deleted.
|
|
128
|
+
|
|
60
129
|
The default implementation composes existing per-user / by-ids
|
|
61
130
|
primitives so any backend that implements those (sqlite, supabase,
|
|
62
131
|
postgres, ...) gets correct behaviour for free.
|
|
63
132
|
Subclasses MAY override for atomic / transactional efficiency.
|
|
64
133
|
|
|
134
|
+
**Atomicity note (default path only):** this default implementation is
|
|
135
|
+
NOT wrapped in a single transaction. Each ``purge_content`` call commits
|
|
136
|
+
independently, so a crash mid-erasure may leave some purge-eligible rows
|
|
137
|
+
with PII intact until ``clear_user_data`` is re-invoked. The call is
|
|
138
|
+
idempotent — re-invoking it is safe and will finish the erasure.
|
|
139
|
+
|
|
65
140
|
Args:
|
|
66
141
|
user_id (str): The user id whose rows should be deleted.
|
|
67
142
|
|
|
68
143
|
Returns:
|
|
69
|
-
dict[str, int]: Per-entity
|
|
70
|
-
``
|
|
71
|
-
``
|
|
72
|
-
|
|
144
|
+
dict[str, int]: Per-entity counts with keys ``interactions``,
|
|
145
|
+
``user_playbooks``, ``profiles``, ``requests``,
|
|
146
|
+
``purged_profiles``, and ``purged_user_playbooks``.
|
|
147
|
+
``profiles`` and ``user_playbooks`` reflect hard-deleted
|
|
148
|
+
counts; purged rows are counted separately.
|
|
73
149
|
"""
|
|
74
150
|
interaction_count = len(self.get_user_interaction(user_id))
|
|
75
151
|
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
|
|
152
|
+
# All statuses a user's row can have — including tombstones (SUPERSEDED,
|
|
153
|
+
# MERGED). Erasure MUST reach every row the user owns regardless of
|
|
154
|
+
# status; the old filter excluded tombstones, leaving them in the DB
|
|
155
|
+
# after clear_user_data (GDPR regression).
|
|
156
|
+
_all_statuses: list[Status | None] = [
|
|
157
|
+
None, # CURRENT
|
|
158
|
+
Status.ARCHIVED,
|
|
159
|
+
Status.PENDING,
|
|
160
|
+
Status.ARCHIVE_IN_PROGRESS,
|
|
161
|
+
Status.SUPERSEDED,
|
|
162
|
+
Status.MERGED,
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
# Snapshot user_playbook ids for the user and partition into
|
|
166
|
+
# purge vs delete sets.
|
|
167
|
+
raw_upb_ids = [
|
|
168
|
+
str(up.user_playbook_id)
|
|
81
169
|
for up in self.get_user_playbooks(
|
|
82
170
|
user_id=user_id,
|
|
83
171
|
limit=1_000_000,
|
|
84
|
-
status_filter=
|
|
172
|
+
status_filter=_all_statuses,
|
|
85
173
|
)
|
|
86
174
|
if up.user_playbook_id is not None
|
|
87
175
|
]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
176
|
+
purge_upb_ids, delete_upb_ids = self._partition_purge_vs_delete(
|
|
177
|
+
"user_playbook", raw_upb_ids
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Snapshot profile ids for the user and partition into
|
|
181
|
+
# purge vs delete sets.
|
|
182
|
+
raw_profile_ids = [
|
|
183
|
+
p.profile_id
|
|
184
|
+
for p in self.get_user_profile(user_id, status_filter=_all_statuses)
|
|
185
|
+
if p.profile_id is not None
|
|
186
|
+
]
|
|
187
|
+
purge_profile_ids, delete_profile_ids = self._partition_purge_vs_delete(
|
|
188
|
+
"profile", raw_profile_ids
|
|
92
189
|
)
|
|
93
190
|
|
|
94
191
|
# Snapshot request ids for the user so we can both count and
|
|
@@ -103,30 +200,43 @@ class BaseStorage(
|
|
|
103
200
|
]
|
|
104
201
|
|
|
105
202
|
# Delete in dependency-safe order: interactions first (they
|
|
106
|
-
# reference requests), then user playbooks
|
|
107
|
-
# requests
|
|
203
|
+
# reference requests), then user playbooks, then profiles, then
|
|
204
|
+
# requests. Only the delete-sets are hard-deleted; purge-sets are
|
|
205
|
+
# content-purged below.
|
|
108
206
|
self.delete_all_interactions_for_user(user_id)
|
|
109
207
|
deleted_user_playbooks = (
|
|
110
|
-
self.delete_user_playbooks_by_ids(
|
|
111
|
-
if
|
|
208
|
+
self.delete_user_playbooks_by_ids([int(i) for i in delete_upb_ids])
|
|
209
|
+
if delete_upb_ids
|
|
112
210
|
else 0
|
|
113
211
|
)
|
|
114
|
-
|
|
212
|
+
deleted_profiles = (
|
|
213
|
+
self.delete_profiles_by_ids(delete_profile_ids) if delete_profile_ids else 0
|
|
214
|
+
)
|
|
115
215
|
deleted_requests = (
|
|
116
216
|
self.delete_requests_by_ids(request_ids) if request_ids else 0
|
|
117
217
|
)
|
|
118
218
|
|
|
219
|
+
# Content-purge the purge-eligible rows after the hard-deletes.
|
|
220
|
+
for pid in purge_profile_ids:
|
|
221
|
+
self.purge_content(entity_type="profile", entity_id=pid)
|
|
222
|
+
for upid in purge_upb_ids:
|
|
223
|
+
self.purge_content(entity_type="user_playbook", entity_id=upid)
|
|
224
|
+
|
|
119
225
|
return {
|
|
120
226
|
"interactions": interaction_count,
|
|
121
227
|
"user_playbooks": deleted_user_playbooks,
|
|
122
|
-
"profiles":
|
|
228
|
+
"profiles": deleted_profiles,
|
|
123
229
|
"requests": deleted_requests,
|
|
230
|
+
"purged_profiles": len(purge_profile_ids),
|
|
231
|
+
"purged_user_playbooks": len(purge_upb_ids),
|
|
124
232
|
}
|
|
125
233
|
|
|
126
234
|
|
|
127
235
|
__all__ = [
|
|
128
236
|
"AgentBinding",
|
|
129
237
|
"AgentRunMixin",
|
|
238
|
+
"EntityType",
|
|
239
|
+
"LineageEventMixin",
|
|
130
240
|
"AgentRunRecord",
|
|
131
241
|
"AgentRunStatus",
|
|
132
242
|
"BaseStorage",
|
|
@@ -135,6 +245,7 @@ __all__ = [
|
|
|
135
245
|
"PendingToolCallStatus",
|
|
136
246
|
"PendingToolCallUpsertResult",
|
|
137
247
|
"PlaybookMixin",
|
|
248
|
+
"RetrievalLogMixin",
|
|
138
249
|
"PriorAnswerMatch",
|
|
139
250
|
"RunToolDependencyKind",
|
|
140
251
|
"RunToolDependencyRecord",
|
|
@@ -11,6 +11,8 @@ from datetime import datetime
|
|
|
11
11
|
from enum import StrEnum
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
|
+
from reflexio.models.api_schema.pending_tool_call_schema import PendingToolCallStatus
|
|
15
|
+
|
|
14
16
|
_WHITESPACE_RE = re.compile(r"\s+")
|
|
15
17
|
|
|
16
18
|
|
|
@@ -28,14 +30,6 @@ class AgentRunStatus(StrEnum):
|
|
|
28
30
|
CANCELLED = "cancelled"
|
|
29
31
|
|
|
30
32
|
|
|
31
|
-
class PendingToolCallStatus(StrEnum):
|
|
32
|
-
PENDING = "pending"
|
|
33
|
-
RESOLVED = "resolved"
|
|
34
|
-
EXPIRED = "expired"
|
|
35
|
-
SUPERSEDED = "superseded"
|
|
36
|
-
CANCELLED = "cancelled"
|
|
37
|
-
|
|
38
|
-
|
|
39
33
|
NOT_APPLICABLE_ANSWER = "User does not have information about this question."
|
|
40
34
|
|
|
41
35
|
|
|
@@ -40,6 +40,11 @@ class BaseStorageCore(ABC): # noqa: B024
|
|
|
40
40
|
# Capability flag — backends that implement `_get_embedding` set this to True.
|
|
41
41
|
supports_embedding: ClassVar[bool] = False
|
|
42
42
|
|
|
43
|
+
# Capability flag — backends that can serve every unified-search arm in a
|
|
44
|
+
# single database round trip expose a `unified_hybrid_search` method and
|
|
45
|
+
# set this to True (see reflexio.server.services.unified_search_service).
|
|
46
|
+
supports_unified_hybrid_search: ClassVar[bool] = False
|
|
47
|
+
|
|
43
48
|
def __init__(self, org_id: str, base_dir: str | None = None) -> None:
|
|
44
49
|
self.org_id = org_id
|
|
45
50
|
if base_dir is None:
|
|
@@ -6,14 +6,13 @@ from reflexio.models.api_schema.braintrust_schema import (
|
|
|
6
6
|
)
|
|
7
7
|
from reflexio.models.api_schema.domain import (
|
|
8
8
|
Interaction,
|
|
9
|
-
PlaybookAggregationChangeLog,
|
|
10
|
-
ProfileChangeLog,
|
|
11
9
|
)
|
|
10
|
+
from reflexio.models.api_schema.internal_schema import SessionCitation
|
|
12
11
|
from reflexio.models.api_schema.retriever_schema import PlaybookApplicationStat
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class ExtrasMixin:
|
|
16
|
-
"""Mixin for dashboard, profile change logs,
|
|
15
|
+
"""Mixin for dashboard, profile change logs, and misc methods."""
|
|
17
16
|
|
|
18
17
|
# ==============================
|
|
19
18
|
# Dashboard methods
|
|
@@ -72,56 +71,6 @@ class ExtrasMixin:
|
|
|
72
71
|
"""
|
|
73
72
|
raise NotImplementedError
|
|
74
73
|
|
|
75
|
-
# ==============================
|
|
76
|
-
# Profile Change Log methods
|
|
77
|
-
# ==============================
|
|
78
|
-
|
|
79
|
-
@abstractmethod
|
|
80
|
-
def add_profile_change_log(self, profile_change_log: ProfileChangeLog) -> None:
|
|
81
|
-
"""Add a profile change log entry."""
|
|
82
|
-
raise NotImplementedError
|
|
83
|
-
|
|
84
|
-
@abstractmethod
|
|
85
|
-
def get_profile_change_logs(self, limit: int = 100) -> list[ProfileChangeLog]:
|
|
86
|
-
"""Get profile change logs for an organization."""
|
|
87
|
-
raise NotImplementedError
|
|
88
|
-
|
|
89
|
-
@abstractmethod
|
|
90
|
-
def delete_profile_change_log_for_user(self, user_id: str) -> None:
|
|
91
|
-
"""Delete all profile change logs for a user."""
|
|
92
|
-
raise NotImplementedError
|
|
93
|
-
|
|
94
|
-
@abstractmethod
|
|
95
|
-
def delete_all_profile_change_logs(self) -> None:
|
|
96
|
-
"""Delete all profile change logs."""
|
|
97
|
-
raise NotImplementedError
|
|
98
|
-
|
|
99
|
-
# ==============================
|
|
100
|
-
# Playbook Aggregation Change Log methods
|
|
101
|
-
# ==============================
|
|
102
|
-
|
|
103
|
-
@abstractmethod
|
|
104
|
-
def add_playbook_aggregation_change_log(
|
|
105
|
-
self, change_log: PlaybookAggregationChangeLog
|
|
106
|
-
) -> None:
|
|
107
|
-
"""Add a playbook aggregation change log entry."""
|
|
108
|
-
raise NotImplementedError
|
|
109
|
-
|
|
110
|
-
@abstractmethod
|
|
111
|
-
def get_playbook_aggregation_change_logs(
|
|
112
|
-
self,
|
|
113
|
-
playbook_name: str,
|
|
114
|
-
agent_version: str,
|
|
115
|
-
limit: int = 100,
|
|
116
|
-
) -> list[PlaybookAggregationChangeLog]:
|
|
117
|
-
"""Get playbook aggregation change logs filtered by playbook_name and agent_version."""
|
|
118
|
-
raise NotImplementedError
|
|
119
|
-
|
|
120
|
-
@abstractmethod
|
|
121
|
-
def delete_all_playbook_aggregation_change_logs(self) -> None:
|
|
122
|
-
"""Delete all playbook aggregation change logs."""
|
|
123
|
-
raise NotImplementedError
|
|
124
|
-
|
|
125
74
|
# ==============================
|
|
126
75
|
# Misc methods
|
|
127
76
|
# ==============================
|
|
@@ -171,6 +120,40 @@ class ExtrasMixin:
|
|
|
171
120
|
"""
|
|
172
121
|
return []
|
|
173
122
|
|
|
123
|
+
def get_citations_by_session_ids(
|
|
124
|
+
self,
|
|
125
|
+
session_ids: list[str],
|
|
126
|
+
) -> list[SessionCitation]:
|
|
127
|
+
"""Return rule/profile citations for the requested sessions.
|
|
128
|
+
|
|
129
|
+
Default implementation uses ``get_interactions_by_session`` so legacy
|
|
130
|
+
backends keep working. SQL backends should override with a bulk
|
|
131
|
+
request/interactions join.
|
|
132
|
+
"""
|
|
133
|
+
out: list[SessionCitation] = []
|
|
134
|
+
for session_id in set(session_ids):
|
|
135
|
+
for interaction in self.get_interactions_by_session(session_id):
|
|
136
|
+
for cite in getattr(interaction, "citations", []) or []:
|
|
137
|
+
if isinstance(cite, dict):
|
|
138
|
+
kind = cite.get("kind")
|
|
139
|
+
real_id = cite.get("real_id")
|
|
140
|
+
title = cite.get("title") or ""
|
|
141
|
+
else:
|
|
142
|
+
kind = getattr(cite, "kind", None)
|
|
143
|
+
real_id = getattr(cite, "real_id", None)
|
|
144
|
+
title = getattr(cite, "title", "") or ""
|
|
145
|
+
if kind and real_id:
|
|
146
|
+
out.append(
|
|
147
|
+
SessionCitation(
|
|
148
|
+
user_id="",
|
|
149
|
+
session_id=session_id,
|
|
150
|
+
kind=str(kind),
|
|
151
|
+
real_id=str(real_id),
|
|
152
|
+
title=str(title),
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
return out
|
|
156
|
+
|
|
174
157
|
# ==============================
|
|
175
158
|
# Braintrust connector (default no-ops; backends override)
|
|
176
159
|
# ==============================
|