claude-smart 0.2.45 → 0.2.47
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 +47 -10
- package/bin/claude-smart.js +535 -23
- 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 +27 -6
- 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/layout.tsx +20 -0
- 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 +232 -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 +251 -0
- package/plugin/opencode/tsconfig.json +13 -0
- package/plugin/pyproject.toml +25 -4
- 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 +17 -9
- package/plugin/src/README.md +1 -1
- package/plugin/src/claude_smart/cli.py +498 -12
- package/plugin/src/claude_smart/context_format.py +9 -9
- package/plugin/src/claude_smart/cs_cite.py +66 -19
- 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 +5 -5
- package/plugin/vendor/reflexio/.env.example +54 -30
- package/plugin/vendor/reflexio/README.md +17 -11
- package/plugin/vendor/reflexio/pyproject.toml +13 -1
- package/plugin/vendor/reflexio/reflexio/README.md +4 -1
- 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 +212 -52
- 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/cli/utils.py +44 -1
- package/plugin/vendor/reflexio/reflexio/client/client.py +154 -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 +246 -10
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +17 -5
- package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +224 -21
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +52 -25
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +11 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
- 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/domain/governance.py +117 -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 +97 -3
- 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 +104 -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 +58 -33
- 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/api.py +899 -103
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +4 -2
- package/plugin/vendor/reflexio/reflexio/server/auth.py +90 -0
- 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/extensions.py +213 -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/model_defaults.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
- 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/rerank/cross_encoder_reranker.py +12 -1
- 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 +278 -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 +28 -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 +47 -16
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
- package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +219 -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 +18 -11
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
- 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} +334 -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_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} +31 -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/recency.py +211 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +99 -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 +31 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -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 +16 -2
- 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 +644 -197
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +106 -119
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +630 -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 +31 -1369
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +756 -210
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +148 -34
- 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/sqlite_storage/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +150 -23
- 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 +40 -57
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
- 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 +4 -677
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +134 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +62 -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/storage/storage_base/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -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 +372 -34
- 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/_auth.py +0 -25
- 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
|
@@ -5,8 +5,9 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
7
7
|
|
|
8
|
-
from reflexio.models.api_schema.domain.entities import Interaction
|
|
8
|
+
from reflexio.models.api_schema.domain.entities import Interaction, UserPlaybook
|
|
9
9
|
from reflexio.models.api_schema.internal_schema import RequestInteractionDataModel
|
|
10
|
+
from reflexio.models.structured_output import StrictStructuredOutput
|
|
10
11
|
from reflexio.server.prompt.prompt_manager import PromptManager
|
|
11
12
|
from reflexio.server.services.playbook.playbook_service_constants import (
|
|
12
13
|
PlaybookServiceConstants,
|
|
@@ -21,6 +22,35 @@ from reflexio.server.services.service_utils import (
|
|
|
21
22
|
|
|
22
23
|
logger = logging.getLogger(__name__)
|
|
23
24
|
|
|
25
|
+
|
|
26
|
+
def _normalized_playbook_key(playbook: UserPlaybook) -> tuple[str, str]:
|
|
27
|
+
"""Return the same-batch duplicate key for a user playbook."""
|
|
28
|
+
return (
|
|
29
|
+
(playbook.trigger or "").strip().casefold(),
|
|
30
|
+
playbook.content.strip().casefold(),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def dedupe_and_drop_empty(playbooks: list[UserPlaybook]) -> list[UserPlaybook]:
|
|
35
|
+
"""Drop empty user playbooks and collapse exact same-batch duplicates.
|
|
36
|
+
|
|
37
|
+
This is intentionally deterministic and local to one extraction batch. The
|
|
38
|
+
LLM-backed consolidator still handles semantic deduplication against stored
|
|
39
|
+
rows when its feature flag is enabled.
|
|
40
|
+
"""
|
|
41
|
+
deduped: list[UserPlaybook] = []
|
|
42
|
+
seen: set[tuple[str, str]] = set()
|
|
43
|
+
for playbook in playbooks:
|
|
44
|
+
if not playbook.content.strip():
|
|
45
|
+
continue
|
|
46
|
+
key = _normalized_playbook_key(playbook)
|
|
47
|
+
if key in seen:
|
|
48
|
+
continue
|
|
49
|
+
seen.add(key)
|
|
50
|
+
deduped.append(playbook)
|
|
51
|
+
return deduped
|
|
52
|
+
|
|
53
|
+
|
|
24
54
|
# ===============================
|
|
25
55
|
# Pydantic classes for playbook_extraction_main prompt output schema
|
|
26
56
|
# ===============================
|
|
@@ -84,7 +114,7 @@ class StructuredPlaybookContent(BaseModel):
|
|
|
84
114
|
return bool(self.content and self.content.strip())
|
|
85
115
|
|
|
86
116
|
|
|
87
|
-
class StructuredPlaybookList(
|
|
117
|
+
class StructuredPlaybookList(StrictStructuredOutput):
|
|
88
118
|
"""
|
|
89
119
|
Wrapper schema for extracting zero or more playbook entries in a single LLM call.
|
|
90
120
|
|
|
@@ -105,7 +135,7 @@ class StructuredPlaybookList(BaseModel):
|
|
|
105
135
|
)
|
|
106
136
|
|
|
107
137
|
|
|
108
|
-
class PlaybookAggregationOutput(
|
|
138
|
+
class PlaybookAggregationOutput(StrictStructuredOutput):
|
|
109
139
|
"""
|
|
110
140
|
Output schema for the playbook_aggregation prompt.
|
|
111
141
|
|
|
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from reflexio.server.api_endpoints.request_context import RequestContext
|
|
10
10
|
from reflexio.server.llm.litellm_client import LiteLLMClient
|
|
11
|
+
from reflexio.server.services.storage.storage_base import BaseStorage
|
|
11
12
|
|
|
12
13
|
from reflexio.models.api_schema.internal_schema import RequestInteractionDataModel
|
|
13
14
|
from reflexio.models.api_schema.service_schemas import (
|
|
@@ -21,13 +22,17 @@ from reflexio.models.api_schema.service_schemas import (
|
|
|
21
22
|
UserPlaybook,
|
|
22
23
|
)
|
|
23
24
|
from reflexio.models.config_schema import PlaybookConfig
|
|
25
|
+
from reflexio.server.extensions import get_service
|
|
24
26
|
from reflexio.server.operation_limiter import run_with_operation_limit
|
|
25
27
|
from reflexio.server.services.base_generation_service import (
|
|
26
28
|
BaseGenerationService,
|
|
27
29
|
StatusChangeOperation,
|
|
28
30
|
)
|
|
29
|
-
from reflexio.server.services.playbook.
|
|
30
|
-
|
|
31
|
+
from reflexio.server.services.playbook.aggregation_prompt_processing import (
|
|
32
|
+
AGGREGATION_PROMPT_PROCESSOR,
|
|
33
|
+
)
|
|
34
|
+
from reflexio.server.services.playbook.components.aggregator import PlaybookAggregator
|
|
35
|
+
from reflexio.server.services.playbook.components.extractor import PlaybookExtractor
|
|
31
36
|
from reflexio.server.services.playbook.playbook_service_constants import (
|
|
32
37
|
PlaybookServiceConstants,
|
|
33
38
|
)
|
|
@@ -45,6 +50,33 @@ from reflexio.server.services.service_utils import (
|
|
|
45
50
|
logger = logging.getLogger(__name__)
|
|
46
51
|
|
|
47
52
|
|
|
53
|
+
def read_user_playbook_as_of_for_learning(
|
|
54
|
+
storage: BaseStorage, *, user_playbook_id: int, served_at: int
|
|
55
|
+
) -> UserPlaybook | None:
|
|
56
|
+
"""Return the exact cited playbook row iff its body is still attributable.
|
|
57
|
+
|
|
58
|
+
This is the strict learning-time read path: it dereferences ONLY the cited
|
|
59
|
+
id, never follows merged/superseded pointers to a current survivor. When the
|
|
60
|
+
exact row body is unavailable or was revised in-place after ``served_at``,
|
|
61
|
+
the row is unattributable and the helper returns ``None``.
|
|
62
|
+
"""
|
|
63
|
+
playbook = storage.get_user_playbook_by_id(
|
|
64
|
+
user_playbook_id, include_tombstones=True
|
|
65
|
+
)
|
|
66
|
+
if playbook is None:
|
|
67
|
+
return None
|
|
68
|
+
if playbook.created_at >= served_at:
|
|
69
|
+
return None
|
|
70
|
+
if not playbook.content.strip():
|
|
71
|
+
return None
|
|
72
|
+
events = storage.get_lineage_events(
|
|
73
|
+
entity_type="user_playbook", entity_id=str(user_playbook_id)
|
|
74
|
+
)
|
|
75
|
+
if any(event.op == "revise" and event.created_at >= served_at for event in events):
|
|
76
|
+
return None
|
|
77
|
+
return playbook
|
|
78
|
+
|
|
79
|
+
|
|
48
80
|
@dataclass
|
|
49
81
|
class PlaybookGenerationServiceConfig:
|
|
50
82
|
"""Runtime configuration for playbook generation service shared across all extractors.
|
|
@@ -84,6 +116,9 @@ class PlaybookGenerationService(
|
|
|
84
116
|
Runs the configured PlaybookExtractor for each generation request.
|
|
85
117
|
"""
|
|
86
118
|
|
|
119
|
+
# Playbook generation produces learnings — opt in to ② Learning billing.
|
|
120
|
+
EMITS_LEARNING_BILLING: bool = True
|
|
121
|
+
|
|
87
122
|
def __init__(
|
|
88
123
|
self,
|
|
89
124
|
llm_client: LiteLLMClient,
|
|
@@ -257,12 +292,19 @@ class PlaybookGenerationService(
|
|
|
257
292
|
|
|
258
293
|
def _finalize_extracted_items(self, all_playbooks: list[UserPlaybook]) -> None:
|
|
259
294
|
"""Deduplicate, persist, and aggregate extracted user playbook items."""
|
|
295
|
+
from reflexio.server.services.playbook.playbook_service_utils import (
|
|
296
|
+
dedupe_and_drop_empty,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
all_playbooks = dedupe_and_drop_empty(all_playbooks)
|
|
300
|
+
|
|
260
301
|
# Deduplicate against existing entries in DB when deduplicator is enabled
|
|
261
302
|
existing_ids_to_delete: list[int] = []
|
|
303
|
+
merge_groups: list[tuple[int, list[int]]] = []
|
|
262
304
|
from reflexio.server.site_var.feature_flags import is_deduplicator_enabled
|
|
263
305
|
|
|
264
306
|
if is_deduplicator_enabled(self.org_id):
|
|
265
|
-
from reflexio.server.services.playbook.
|
|
307
|
+
from reflexio.server.services.playbook.components.consolidator import (
|
|
266
308
|
PlaybookConsolidator,
|
|
267
309
|
)
|
|
268
310
|
|
|
@@ -276,7 +318,11 @@ class PlaybookGenerationService(
|
|
|
276
318
|
llm_client=self.client,
|
|
277
319
|
dedup_config=dedup_config,
|
|
278
320
|
)
|
|
279
|
-
|
|
321
|
+
(
|
|
322
|
+
deduplicated_playbooks,
|
|
323
|
+
existing_ids_to_delete,
|
|
324
|
+
merge_groups,
|
|
325
|
+
) = consolidator.deduplicate(
|
|
280
326
|
[all_playbooks],
|
|
281
327
|
self.service_config.request_id, # type: ignore[reportOptionalMemberAccess]
|
|
282
328
|
self.service_config.agent_version, # type: ignore[reportOptionalMemberAccess]
|
|
@@ -308,21 +354,9 @@ class PlaybookGenerationService(
|
|
|
308
354
|
try:
|
|
309
355
|
self.storage.save_user_playbooks(all_playbooks) # type: ignore[reportOptionalMemberAccess]
|
|
310
356
|
self._enqueue_user_playbook_optimization(all_playbooks)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
try:
|
|
315
|
-
deleted_count = self.storage.delete_user_playbooks_by_ids( # type: ignore[reportOptionalMemberAccess]
|
|
316
|
-
existing_ids_to_delete
|
|
317
|
-
)
|
|
318
|
-
logger.info(
|
|
319
|
-
"Deleted %d superseded existing entries", deleted_count
|
|
320
|
-
)
|
|
321
|
-
except Exception as e:
|
|
322
|
-
logger.error(
|
|
323
|
-
"Failed to delete superseded existing entries: %s",
|
|
324
|
-
str(e),
|
|
325
|
-
)
|
|
357
|
+
self._apply_consolidation_lineage(
|
|
358
|
+
all_playbooks, merge_groups, existing_ids_to_delete
|
|
359
|
+
)
|
|
326
360
|
except Exception as e:
|
|
327
361
|
logger.error(
|
|
328
362
|
"Failed to save %s results for request id: %s due to %s, exception type: %s",
|
|
@@ -331,12 +365,73 @@ class PlaybookGenerationService(
|
|
|
331
365
|
str(e),
|
|
332
366
|
type(e).__name__,
|
|
333
367
|
)
|
|
368
|
+
raise
|
|
334
369
|
|
|
335
370
|
# Trigger playbook aggregation
|
|
336
371
|
if not self.output_pending_status and not self.skip_aggregation:
|
|
337
372
|
logger.info("Trigger playbook aggregation")
|
|
338
373
|
self._trigger_playbook_aggregation()
|
|
339
374
|
|
|
375
|
+
def _apply_consolidation_lineage(
|
|
376
|
+
self,
|
|
377
|
+
saved_playbooks: list[UserPlaybook],
|
|
378
|
+
merge_groups: list[tuple[int, list[int]]],
|
|
379
|
+
existing_ids_to_delete: list[int],
|
|
380
|
+
) -> None:
|
|
381
|
+
"""Materialize consolidation merges as lineage tombstones.
|
|
382
|
+
|
|
383
|
+
Runs AFTER ``save_user_playbooks`` has assigned survivor ids. For each
|
|
384
|
+
``unify`` merge group, routes the sources atomically through
|
|
385
|
+
``storage.merge_records`` so each becomes a MERGED tombstone pointing at
|
|
386
|
+
its survivor with a ``merge`` lineage event (``resolve_current`` then
|
|
387
|
+
follows the pointer). Any archived id NOT covered by a merge group
|
|
388
|
+
(e.g. a ``differentiate`` split source, which has no single survivor) is
|
|
389
|
+
soft-superseded in place so the dead source content stays auditable for
|
|
390
|
+
point-in-time attribution reads.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
saved_playbooks: The just-persisted entries (survivor ids assigned).
|
|
394
|
+
merge_groups: ``(survivor_index, source_existing_ids)`` per merge.
|
|
395
|
+
existing_ids_to_delete: ALL archived ids (merge sources + leftovers).
|
|
396
|
+
"""
|
|
397
|
+
from reflexio.models.api_schema.domain.entities import LineageContext
|
|
398
|
+
|
|
399
|
+
merged_source_ids: set[int] = set()
|
|
400
|
+
for survivor_idx, source_ids in merge_groups:
|
|
401
|
+
survivor_id = saved_playbooks[survivor_idx].user_playbook_id
|
|
402
|
+
merged_source_ids.update(source_ids)
|
|
403
|
+
self.storage.merge_records( # type: ignore[reportOptionalMemberAccess]
|
|
404
|
+
entity_type="user_playbook",
|
|
405
|
+
survivor_id=str(survivor_id),
|
|
406
|
+
source_ids=[str(s) for s in source_ids],
|
|
407
|
+
context=LineageContext(
|
|
408
|
+
op_kind="merge",
|
|
409
|
+
actor="consolidator",
|
|
410
|
+
source_ids=[str(s) for s in source_ids],
|
|
411
|
+
reason="dedup-merge",
|
|
412
|
+
request_id=self.service_config.request_id, # type: ignore[reportOptionalMemberAccess]
|
|
413
|
+
),
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
# Leftover archives not covered by any merge group (e.g. differentiate
|
|
417
|
+
# split sources) have no single survivor, so preserve them as dead-source
|
|
418
|
+
# tombstones instead of hard-deleting them.
|
|
419
|
+
leftover_ids = [
|
|
420
|
+
pid for pid in existing_ids_to_delete if pid not in merged_source_ids
|
|
421
|
+
]
|
|
422
|
+
if leftover_ids:
|
|
423
|
+
try:
|
|
424
|
+
superseded_count = self.storage.supersede_user_playbooks_by_ids( # type: ignore[reportOptionalMemberAccess]
|
|
425
|
+
leftover_ids,
|
|
426
|
+
self.service_config.request_id, # type: ignore[reportOptionalMemberAccess]
|
|
427
|
+
)
|
|
428
|
+
logger.info(
|
|
429
|
+
"Superseded %d split-source existing entries", superseded_count
|
|
430
|
+
)
|
|
431
|
+
except Exception:
|
|
432
|
+
logger.exception("Failed to supersede split-source existing entries")
|
|
433
|
+
raise
|
|
434
|
+
|
|
340
435
|
def _enqueue_user_playbook_optimization(
|
|
341
436
|
self, saved_playbooks: list[UserPlaybook]
|
|
342
437
|
) -> None:
|
|
@@ -446,10 +541,17 @@ class PlaybookGenerationService(
|
|
|
446
541
|
)
|
|
447
542
|
|
|
448
543
|
# Initialize and run aggregator (synchronous)
|
|
544
|
+
aggregation_prompt_processor = get_service(AGGREGATION_PROMPT_PROCESSOR)
|
|
545
|
+
aggregator_kwargs = {}
|
|
546
|
+
if aggregation_prompt_processor is not None:
|
|
547
|
+
aggregator_kwargs["aggregation_prompt_processor"] = (
|
|
548
|
+
aggregation_prompt_processor
|
|
549
|
+
)
|
|
449
550
|
aggregator = PlaybookAggregator(
|
|
450
551
|
llm_client=self.client,
|
|
451
552
|
request_context=self.request_context,
|
|
452
553
|
agent_version=self.service_config.agent_version, # type: ignore[reportOptionalMemberAccess]
|
|
554
|
+
**aggregator_kwargs,
|
|
453
555
|
)
|
|
454
556
|
try:
|
|
455
557
|
run_with_operation_limit(
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# playbook_optimizer
|
|
2
|
+
|
|
3
|
+
GEPA-driven playbook content optimizer.
|
|
4
|
+
|
|
5
|
+
- `optimizer.py` orchestrates one optimization run and persists candidates, evaluations, events, and optional successor playbooks.
|
|
6
|
+
- `scheduler.py` owns deferred optimization scheduling.
|
|
7
|
+
- `models.py` owns optimizer-local data shapes.
|
|
8
|
+
- `judge.py`, `rollout.py`, `gepa_adapter.py`, `assistant_webhook.py`, and `scenario_resolver.py` are mature implementation units and intentionally remain at the package root.
|
|
9
|
+
|
|
10
|
+
Do not introduce a `components/` package without a separate design that proves the dependency direction is clearer after the move.
|
|
@@ -13,6 +13,7 @@ from typing import Any, Literal
|
|
|
13
13
|
from pydantic import BaseModel, Field
|
|
14
14
|
|
|
15
15
|
from reflexio.models.api_schema.domain import Interaction
|
|
16
|
+
from reflexio.models.structured_output import StrictStructuredOutput
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class ChatMessage(BaseModel):
|
|
@@ -58,7 +59,7 @@ class JudgeASI(BaseModel):
|
|
|
58
59
|
recommended_mutation: str | list[str] = ""
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
class JudgeOutput(
|
|
62
|
+
class JudgeOutput(StrictStructuredOutput):
|
|
62
63
|
verdict: Literal["candidate", "incumbent", "tie"]
|
|
63
64
|
score: float = Field(ge=0.0, le=1.0)
|
|
64
65
|
likert: int = Field(ge=1, le=5)
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
5
7
|
from typing import Any, Literal, cast
|
|
6
8
|
|
|
7
9
|
from pydantic import BaseModel
|
|
@@ -14,6 +16,7 @@ from reflexio.models.api_schema.domain import (
|
|
|
14
16
|
PlaybookStatus,
|
|
15
17
|
UserPlaybook,
|
|
16
18
|
)
|
|
19
|
+
from reflexio.models.api_schema.domain.entities import LineageContext
|
|
17
20
|
from reflexio.models.config_schema import PlaybookOptimizerConfig
|
|
18
21
|
from reflexio.server.api_endpoints.request_context import RequestContext
|
|
19
22
|
from reflexio.server.llm.litellm_client import LiteLLMClient
|
|
@@ -28,6 +31,22 @@ from .scenario_resolver import ScenarioResolver
|
|
|
28
31
|
|
|
29
32
|
logger = logging.getLogger(__name__)
|
|
30
33
|
|
|
34
|
+
# Prefix used when constructing the run-scoped lineage request_id from a job id.
|
|
35
|
+
# Shared with tests so the format is defined in one place.
|
|
36
|
+
_OPTIMIZER_RUN_ID_PREFIX = "optjob_"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def optimizer_run_request_id(job_id: int) -> str:
|
|
40
|
+
"""Return the lineage request_id for a playbook optimizer run.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
job_id (int): The ``PlaybookOptimizationJob.job_id`` for this run.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
str: A non-empty, job-scoped request id of the form ``optjob_<job_id>``.
|
|
47
|
+
"""
|
|
48
|
+
return f"{_OPTIMIZER_RUN_ID_PREFIX}{job_id}"
|
|
49
|
+
|
|
31
50
|
|
|
32
51
|
class PlaybookOptimizationTarget(BaseModel):
|
|
33
52
|
"""A single playbook (agent or user) the optimizer should try to improve."""
|
|
@@ -126,6 +145,7 @@ class PlaybookOptimizer:
|
|
|
126
145
|
metadata_json=json.dumps(split_metadata, ensure_ascii=False),
|
|
127
146
|
)
|
|
128
147
|
)
|
|
148
|
+
run_request_id = optimizer_run_request_id(job.job_id)
|
|
129
149
|
logger.info(
|
|
130
150
|
"event=playbook_optimization_start job_id=%d candidate_id=none "
|
|
131
151
|
"target_kind=%s target_id=%d "
|
|
@@ -248,7 +268,9 @@ class PlaybookOptimizer:
|
|
|
248
268
|
)
|
|
249
269
|
return "completed"
|
|
250
270
|
|
|
251
|
-
successor_id = self._commit_if_allowed(
|
|
271
|
+
successor_id = self._commit_if_allowed(
|
|
272
|
+
target, incumbent, best_content, config, run_request_id
|
|
273
|
+
)
|
|
252
274
|
logger.info(
|
|
253
275
|
"event=playbook_optimization_committed job_id=%d candidate_id=%d "
|
|
254
276
|
"successor_target_id=%s best_score=%.3f",
|
|
@@ -331,6 +353,13 @@ class PlaybookOptimizer:
|
|
|
331
353
|
backoff_base_s=config.webhook_backoff_base_seconds,
|
|
332
354
|
)
|
|
333
355
|
if config.assistant_script_path:
|
|
356
|
+
if not _is_executable_file(config.assistant_script_path):
|
|
357
|
+
logger.warning(
|
|
358
|
+
"Skipping playbook optimization: assistant_script_path is not "
|
|
359
|
+
"an executable file path=%s",
|
|
360
|
+
config.assistant_script_path,
|
|
361
|
+
)
|
|
362
|
+
return None
|
|
334
363
|
return LocalScriptAssistant(
|
|
335
364
|
script_path=config.assistant_script_path,
|
|
336
365
|
script_args=config.assistant_script_args,
|
|
@@ -415,6 +444,7 @@ class PlaybookOptimizer:
|
|
|
415
444
|
incumbent: AgentPlaybook,
|
|
416
445
|
best_content: str,
|
|
417
446
|
config: PlaybookOptimizerConfig,
|
|
447
|
+
run_request_id: str,
|
|
418
448
|
) -> int | None:
|
|
419
449
|
# The optimize() entrypoint already gates on _can_adopt_winner before
|
|
420
450
|
# creating the job, but check again so this stays correct if called
|
|
@@ -432,49 +462,39 @@ class PlaybookOptimizer:
|
|
|
432
462
|
or current.playbook_status != PlaybookStatus.PENDING
|
|
433
463
|
):
|
|
434
464
|
return None
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
465
|
+
metadata = _append_optimizer_metadata(
|
|
466
|
+
incumbent.playbook_metadata, target.target_id
|
|
467
|
+
)
|
|
468
|
+
successor_id = _supersede_agent_playbook(
|
|
469
|
+
self.storage,
|
|
470
|
+
incumbent,
|
|
471
|
+
best_content,
|
|
472
|
+
"playbook_optimizer",
|
|
473
|
+
playbook_metadata=metadata,
|
|
474
|
+
request_id=run_request_id,
|
|
446
475
|
)
|
|
447
|
-
|
|
448
|
-
if saved and saved[0].agent_playbook_id:
|
|
476
|
+
if successor_id is not None:
|
|
449
477
|
self.storage.set_source_windows_for_agent_playbook(
|
|
450
|
-
|
|
478
|
+
successor_id, source_windows
|
|
451
479
|
)
|
|
452
|
-
|
|
453
|
-
return None
|
|
480
|
+
return successor_id
|
|
454
481
|
current_user = self.storage.get_user_playbook_by_id(target.target_id)
|
|
455
482
|
if current_user is None or current_user.status is not None:
|
|
456
483
|
return None
|
|
457
484
|
if current_user.user_id is None:
|
|
458
485
|
return None
|
|
459
|
-
archived = self.storage.archive_user_playbook_by_id(
|
|
460
|
-
current_user.user_id, current_user.user_playbook_id
|
|
461
|
-
)
|
|
462
|
-
if not archived:
|
|
463
|
-
return None
|
|
464
486
|
# The optimizer can legitimately flip framing (positive guidance ->
|
|
465
487
|
# negative anti-pattern or vice versa). Orientation lives entirely in
|
|
466
488
|
# the rule wording, so writing ``best_content`` is sufficient — there
|
|
467
489
|
# is no derived polarity label or separate polarity field to keep in
|
|
468
490
|
# sync.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
491
|
+
return _supersede_user_playbook(
|
|
492
|
+
self.storage,
|
|
493
|
+
current_user,
|
|
494
|
+
best_content,
|
|
495
|
+
"playbook_optimizer",
|
|
496
|
+
request_id=run_request_id,
|
|
475
497
|
)
|
|
476
|
-
self.storage.save_user_playbooks([successor_user])
|
|
477
|
-
return successor_user.user_playbook_id or None
|
|
478
498
|
|
|
479
499
|
|
|
480
500
|
def _agent_like_playbook(playbook: UserPlaybook) -> AgentPlaybook:
|
|
@@ -506,6 +526,129 @@ def _append_optimizer_metadata(existing: str, predecessor_id: int) -> str:
|
|
|
506
526
|
return f"{existing}; {suffix}"
|
|
507
527
|
|
|
508
528
|
|
|
529
|
+
def _supersede_user_playbook(
|
|
530
|
+
storage: Any,
|
|
531
|
+
incumbent: UserPlaybook,
|
|
532
|
+
best_content: str,
|
|
533
|
+
source: str,
|
|
534
|
+
*,
|
|
535
|
+
request_id: str,
|
|
536
|
+
) -> int | None:
|
|
537
|
+
"""Insert a user-playbook successor then atomically supersede the incumbent.
|
|
538
|
+
|
|
539
|
+
Returns the new ``user_playbook_id`` on success, or ``None`` when the
|
|
540
|
+
incumbent is no longer CURRENT (lost race / already superseded).
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
storage: A storage instance implementing ``save_user_playbooks``,
|
|
544
|
+
``supersede_record``, and ``delete_user_playbooks_by_ids``.
|
|
545
|
+
incumbent: The current user playbook to replace.
|
|
546
|
+
best_content: Content for the successor playbook.
|
|
547
|
+
source: Provenance label written to the lineage event actor field.
|
|
548
|
+
request_id: Run-scoped correlation id for the lineage event. Must be
|
|
549
|
+
non-empty; use the job-derived id from the calling optimizer run.
|
|
550
|
+
|
|
551
|
+
Returns:
|
|
552
|
+
int | None: ``user_playbook_id`` of the successor, or ``None`` if the
|
|
553
|
+
incumbent was not CURRENT.
|
|
554
|
+
|
|
555
|
+
Raises:
|
|
556
|
+
ValueError: If ``request_id`` is empty or None.
|
|
557
|
+
"""
|
|
558
|
+
if not request_id:
|
|
559
|
+
raise ValueError(
|
|
560
|
+
"_supersede_user_playbook: request_id must be non-empty (run-correlation id)"
|
|
561
|
+
)
|
|
562
|
+
successor = incumbent.model_copy(
|
|
563
|
+
update={"user_playbook_id": 0, "content": best_content, "status": None}
|
|
564
|
+
)
|
|
565
|
+
storage.save_user_playbooks([successor])
|
|
566
|
+
ctx = LineageContext(
|
|
567
|
+
op_kind="revise",
|
|
568
|
+
actor=source,
|
|
569
|
+
request_id=request_id,
|
|
570
|
+
)
|
|
571
|
+
ok = storage.supersede_record(
|
|
572
|
+
entity_type="user_playbook",
|
|
573
|
+
incumbent_id=str(incumbent.user_playbook_id),
|
|
574
|
+
successor_id=str(successor.user_playbook_id),
|
|
575
|
+
context=ctx,
|
|
576
|
+
)
|
|
577
|
+
if not ok:
|
|
578
|
+
# Lost CAS: remove the never-live successor without auditing it as erasure.
|
|
579
|
+
storage.delete_user_playbooks_by_ids(
|
|
580
|
+
[successor.user_playbook_id], emit_hard_delete=False
|
|
581
|
+
)
|
|
582
|
+
return None
|
|
583
|
+
return successor.user_playbook_id
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _supersede_agent_playbook(
|
|
587
|
+
storage: Any,
|
|
588
|
+
incumbent: AgentPlaybook,
|
|
589
|
+
best_content: str,
|
|
590
|
+
source: str,
|
|
591
|
+
playbook_metadata: str = "",
|
|
592
|
+
*,
|
|
593
|
+
request_id: str,
|
|
594
|
+
) -> int | None:
|
|
595
|
+
"""Insert an agent-playbook successor then atomically supersede the incumbent.
|
|
596
|
+
|
|
597
|
+
Returns the new ``agent_playbook_id`` on success, or ``None`` when the
|
|
598
|
+
incumbent is no longer CURRENT.
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
storage: A storage instance implementing ``save_agent_playbooks``,
|
|
602
|
+
``supersede_record``, and ``delete_agent_playbooks_by_ids``.
|
|
603
|
+
incumbent: The current agent playbook to replace.
|
|
604
|
+
best_content: Content for the successor playbook.
|
|
605
|
+
source: Provenance label written to the lineage event actor field.
|
|
606
|
+
playbook_metadata: Optional metadata string for the successor row.
|
|
607
|
+
request_id: Run-scoped correlation id for the lineage event. Must be
|
|
608
|
+
non-empty; use the job-derived id from the calling optimizer run.
|
|
609
|
+
|
|
610
|
+
Returns:
|
|
611
|
+
int | None: ``agent_playbook_id`` of the successor, or ``None`` if the
|
|
612
|
+
incumbent was not CURRENT.
|
|
613
|
+
|
|
614
|
+
Raises:
|
|
615
|
+
ValueError: If ``request_id`` is empty or None.
|
|
616
|
+
"""
|
|
617
|
+
if not request_id:
|
|
618
|
+
raise ValueError(
|
|
619
|
+
"_supersede_agent_playbook: request_id must be non-empty (run-correlation id)"
|
|
620
|
+
)
|
|
621
|
+
successor = incumbent.model_copy(
|
|
622
|
+
update={
|
|
623
|
+
"agent_playbook_id": 0,
|
|
624
|
+
"content": best_content,
|
|
625
|
+
"status": None,
|
|
626
|
+
"playbook_status": PlaybookStatus.PENDING,
|
|
627
|
+
"playbook_metadata": playbook_metadata,
|
|
628
|
+
}
|
|
629
|
+
)
|
|
630
|
+
saved = storage.save_agent_playbooks([successor])
|
|
631
|
+
if not saved or not saved[0].agent_playbook_id:
|
|
632
|
+
return None
|
|
633
|
+
successor_id = saved[0].agent_playbook_id
|
|
634
|
+
ctx = LineageContext(
|
|
635
|
+
op_kind="revise",
|
|
636
|
+
actor=source,
|
|
637
|
+
request_id=request_id,
|
|
638
|
+
)
|
|
639
|
+
ok = storage.supersede_record(
|
|
640
|
+
entity_type="agent_playbook",
|
|
641
|
+
incumbent_id=str(incumbent.agent_playbook_id),
|
|
642
|
+
successor_id=str(successor_id),
|
|
643
|
+
context=ctx,
|
|
644
|
+
)
|
|
645
|
+
if not ok:
|
|
646
|
+
# Lost CAS: remove the never-live successor without auditing it as erasure.
|
|
647
|
+
storage.delete_agent_playbooks_by_ids([successor_id], emit_hard_delete=False)
|
|
648
|
+
return None
|
|
649
|
+
return successor_id
|
|
650
|
+
|
|
651
|
+
|
|
509
652
|
class _GEPAStorageCallback:
|
|
510
653
|
def __init__(self, storage: Any, job_id: int) -> None:
|
|
511
654
|
self.storage = storage
|
|
@@ -627,6 +770,11 @@ def _split_metadata(
|
|
|
627
770
|
}
|
|
628
771
|
|
|
629
772
|
|
|
773
|
+
def _is_executable_file(path: str) -> bool:
|
|
774
|
+
candidate = Path(path)
|
|
775
|
+
return candidate.is_file() and os.access(candidate, os.X_OK)
|
|
776
|
+
|
|
777
|
+
|
|
630
778
|
def _result_metadata(result: Any, split_metadata: dict[str, Any]) -> dict[str, Any]:
|
|
631
779
|
metadata = result.to_dict()
|
|
632
780
|
if not isinstance(metadata, dict):
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# pre_retrieval
|
|
2
|
+
|
|
3
|
+
Compact search-preparation capability.
|
|
4
|
+
|
|
5
|
+
- `__init__.py` is the public import surface for query reformulation and document expansion.
|
|
6
|
+
- `_query_reformulator.py` rewrites user queries and can run a caller-provided search function.
|
|
7
|
+
- `_document_expander.py` enriches stored documents with related terms before indexing.
|
|
8
|
+
|
|
9
|
+
This package intentionally does not use `components/`: both implementation files are already focused, and storage adapters import the package-level public surface.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Profile service components."""
|
|
2
|
+
|
|
3
|
+
from reflexio.server.services.profile.components.consolidator import (
|
|
4
|
+
ProfileConsolidator,
|
|
5
|
+
ProfileDeduplicationOutput,
|
|
6
|
+
ProfileDeletionDirective,
|
|
7
|
+
ProfileDuplicateGroup,
|
|
8
|
+
)
|
|
9
|
+
from reflexio.server.services.profile.components.extractor import ProfileExtractor
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"ProfileConsolidator",
|
|
13
|
+
"ProfileDeduplicationOutput",
|
|
14
|
+
"ProfileDeletionDirective",
|
|
15
|
+
"ProfileDuplicateGroup",
|
|
16
|
+
"ProfileExtractor",
|
|
17
|
+
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Profile
|
|
2
|
+
Profile consolidation service that merges duplicate profiles from multiple extractors
|
|
3
3
|
and against existing profiles in the database using hybrid search and LLM.
|
|
4
4
|
"""
|
|
5
5
|
|
|
@@ -13,6 +13,7 @@ from pydantic import BaseModel, ConfigDict, Field
|
|
|
13
13
|
|
|
14
14
|
from reflexio.models.api_schema.retriever_schema import SearchUserProfileRequest
|
|
15
15
|
from reflexio.models.api_schema.service_schemas import Status, UserProfile
|
|
16
|
+
from reflexio.models.structured_output import StrictStructuredOutput
|
|
16
17
|
from reflexio.server.api_endpoints.request_context import RequestContext
|
|
17
18
|
from reflexio.server.llm.litellm_client import LiteLLMClient
|
|
18
19
|
from reflexio.server.services.deduplication_utils import (
|
|
@@ -130,7 +131,7 @@ class ProfileDeletionDirective(BaseModel):
|
|
|
130
131
|
)
|
|
131
132
|
|
|
132
133
|
|
|
133
|
-
class ProfileDeduplicationOutput(
|
|
134
|
+
class ProfileDeduplicationOutput(StrictStructuredOutput):
|
|
134
135
|
"""
|
|
135
136
|
Output schema for profile deduplication with NEW/EXISTING format.
|
|
136
137
|
|
|
@@ -165,9 +166,9 @@ class ProfileDeduplicationOutput(BaseModel):
|
|
|
165
166
|
)
|
|
166
167
|
|
|
167
168
|
|
|
168
|
-
class
|
|
169
|
+
class ProfileConsolidator(BaseDeduplicator):
|
|
169
170
|
"""
|
|
170
|
-
|
|
171
|
+
Consolidates new profiles against each other and against existing profiles
|
|
171
172
|
in the database using hybrid search (vector + FTS) and LLM-based merging.
|
|
172
173
|
|
|
173
174
|
Follows the same pattern as PlaybookConsolidator.
|
|
@@ -182,12 +183,12 @@ class ProfileDeduplicator(BaseDeduplicator):
|
|
|
182
183
|
output_pending_status: bool = False,
|
|
183
184
|
):
|
|
184
185
|
"""
|
|
185
|
-
Initialize the profile
|
|
186
|
+
Initialize the profile consolidator.
|
|
186
187
|
|
|
187
188
|
Args:
|
|
188
189
|
request_context: Request context with storage and prompt manager
|
|
189
190
|
llm_client: Unified LLM client for LLM calls
|
|
190
|
-
output_pending_status: When True (rerun preview mode), the
|
|
191
|
+
output_pending_status: When True (rerun preview mode), the consolidator
|
|
191
192
|
searches against existing PENDING profiles instead of CURRENT
|
|
192
193
|
profiles. This makes rerun idempotent against pending state and
|
|
193
194
|
leaves the user's current profile set untouched. When False
|