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
|
@@ -1,1378 +1,40 @@
|
|
|
1
1
|
"""Playbook CRUD + search methods for SQLite storage."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import sqlite3
|
|
5
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
6
4
|
from typing import Any
|
|
7
5
|
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
_json_dumps,
|
|
33
|
-
_json_loads,
|
|
34
|
-
_row_to_agent_playbook,
|
|
35
|
-
_row_to_eval_result,
|
|
36
|
-
_row_to_user_playbook,
|
|
37
|
-
_sanitize_fts_query,
|
|
38
|
-
_true_rrf_merge,
|
|
39
|
-
_vector_rank_rows,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _row_to_playbook_optimization_candidate(
|
|
44
|
-
row: sqlite3.Row,
|
|
45
|
-
) -> PlaybookOptimizationCandidate:
|
|
46
|
-
return PlaybookOptimizationCandidate(
|
|
47
|
-
candidate_id=row["candidate_id"],
|
|
48
|
-
job_id=row["job_id"],
|
|
49
|
-
candidate_index=row["candidate_index"],
|
|
50
|
-
content=row["content"],
|
|
51
|
-
parent_candidate_ids=_json_loads(row["parent_candidate_ids"]) or [],
|
|
52
|
-
aggregate_score=row["aggregate_score"],
|
|
53
|
-
is_winner=bool(row["is_winner"]),
|
|
54
|
-
created_at=row["created_at"],
|
|
6
|
+
from ._lineage import _append_event_stmt
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _emit_hard_delete_playbook(
|
|
10
|
+
conn: sqlite3.Connection,
|
|
11
|
+
*,
|
|
12
|
+
org_id: str,
|
|
13
|
+
entity_type: str,
|
|
14
|
+
entity_id: str,
|
|
15
|
+
request_id: str,
|
|
16
|
+
actor: str = "api",
|
|
17
|
+
) -> None:
|
|
18
|
+
"""Emit a single hard_delete lineage event for a playbook entity."""
|
|
19
|
+
_append_event_stmt(
|
|
20
|
+
conn,
|
|
21
|
+
org_id=org_id,
|
|
22
|
+
entity_type=entity_type,
|
|
23
|
+
entity_id=entity_id,
|
|
24
|
+
op="hard_delete",
|
|
25
|
+
prov="wasInvalidatedBy",
|
|
26
|
+
source_ids=[],
|
|
27
|
+
actor=actor,
|
|
28
|
+
request_id=request_id,
|
|
29
|
+
reason="erasure",
|
|
55
30
|
)
|
|
56
31
|
|
|
57
32
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
target_kind=row["target_kind"],
|
|
66
|
-
target_id=row["target_id"],
|
|
67
|
-
scenario_user_playbook_id=row["scenario_user_playbook_id"],
|
|
68
|
-
source_interaction_ids=_json_loads(row["source_interaction_ids"]) or [],
|
|
69
|
-
score=row["score"],
|
|
70
|
-
verdict=row["verdict"],
|
|
71
|
-
likert=row["likert"],
|
|
72
|
-
rationale=row["rationale"],
|
|
73
|
-
asi_json=row["asi_json"],
|
|
74
|
-
incumbent_rollout_json=row["incumbent_rollout_json"],
|
|
75
|
-
candidate_rollout_json=row["candidate_rollout_json"],
|
|
76
|
-
created_at=row["created_at"],
|
|
33
|
+
def _build_tags_sql(alias: str, tags: list[str] | None) -> tuple[str, list[Any]]:
|
|
34
|
+
if not tags:
|
|
35
|
+
return "", []
|
|
36
|
+
placeholders = ",".join("?" for _ in tags)
|
|
37
|
+
return (
|
|
38
|
+
f"EXISTS (SELECT 1 FROM json_each({alias}.tags) WHERE value IN ({placeholders}))",
|
|
39
|
+
list(tags),
|
|
77
40
|
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class PlaybookMixin:
|
|
81
|
-
"""Mixin providing user playbook, agent playbook, and evaluation CRUD + search."""
|
|
82
|
-
|
|
83
|
-
# Type hints for instance attributes/methods provided by SQLiteStorageBase via MRO
|
|
84
|
-
_lock: Any
|
|
85
|
-
conn: sqlite3.Connection
|
|
86
|
-
_execute: Any
|
|
87
|
-
_fetchone: Any
|
|
88
|
-
_fetchall: Any
|
|
89
|
-
_get_embedding: Any
|
|
90
|
-
_should_expand_documents: Any
|
|
91
|
-
_expand_document: Any
|
|
92
|
-
_fts_upsert: Any
|
|
93
|
-
_fts_delete: Any
|
|
94
|
-
_vec_upsert: Any
|
|
95
|
-
_vec_delete: Any
|
|
96
|
-
_has_sqlite_vec: bool
|
|
97
|
-
|
|
98
|
-
# ------------------------------------------------------------------
|
|
99
|
-
# User Playbook methods
|
|
100
|
-
# ------------------------------------------------------------------
|
|
101
|
-
|
|
102
|
-
@SQLiteStorageBase.handle_exceptions
|
|
103
|
-
def save_user_playbooks(self, user_playbooks: list[UserPlaybook]) -> None:
|
|
104
|
-
for up in user_playbooks:
|
|
105
|
-
embedding_text = up.trigger or up.content
|
|
106
|
-
if embedding_text:
|
|
107
|
-
if self._should_expand_documents():
|
|
108
|
-
with ThreadPoolExecutor(max_workers=2) as executor:
|
|
109
|
-
emb_future = executor.submit(
|
|
110
|
-
self._get_embedding, embedding_text
|
|
111
|
-
)
|
|
112
|
-
exp_future = executor.submit(
|
|
113
|
-
self._expand_document, embedding_text
|
|
114
|
-
)
|
|
115
|
-
up.embedding = emb_future.result(timeout=15)
|
|
116
|
-
up.expanded_terms = exp_future.result(timeout=15)
|
|
117
|
-
else:
|
|
118
|
-
up.embedding = self._get_embedding(embedding_text)
|
|
119
|
-
|
|
120
|
-
created_at_iso = _epoch_to_iso(up.created_at)
|
|
121
|
-
with self._lock:
|
|
122
|
-
cur = self.conn.execute(
|
|
123
|
-
"""INSERT INTO user_playbooks
|
|
124
|
-
(user_id, playbook_name, created_at, request_id, agent_version,
|
|
125
|
-
content, trigger, rationale, blocking_issue,
|
|
126
|
-
source_interaction_ids,
|
|
127
|
-
status, source, embedding, expanded_terms,
|
|
128
|
-
source_span, notes, reader_angle)
|
|
129
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
|
130
|
-
(
|
|
131
|
-
up.user_id,
|
|
132
|
-
up.playbook_name,
|
|
133
|
-
created_at_iso,
|
|
134
|
-
up.request_id,
|
|
135
|
-
up.agent_version,
|
|
136
|
-
up.content,
|
|
137
|
-
up.trigger,
|
|
138
|
-
up.rationale,
|
|
139
|
-
json.dumps(up.blocking_issue.model_dump())
|
|
140
|
-
if up.blocking_issue
|
|
141
|
-
else None,
|
|
142
|
-
_json_dumps(up.source_interaction_ids or None),
|
|
143
|
-
up.status.value if up.status else None,
|
|
144
|
-
up.source,
|
|
145
|
-
_json_dumps(up.embedding),
|
|
146
|
-
up.expanded_terms,
|
|
147
|
-
up.source_span,
|
|
148
|
-
up.notes,
|
|
149
|
-
up.reader_angle,
|
|
150
|
-
),
|
|
151
|
-
)
|
|
152
|
-
upid = cur.lastrowid or 0
|
|
153
|
-
up.user_playbook_id = upid
|
|
154
|
-
self.conn.commit()
|
|
155
|
-
|
|
156
|
-
fts_parts = [up.trigger or "", up.content or ""]
|
|
157
|
-
if up.expanded_terms:
|
|
158
|
-
fts_parts.append(up.expanded_terms)
|
|
159
|
-
self._fts_upsert(
|
|
160
|
-
"user_playbooks_fts",
|
|
161
|
-
upid,
|
|
162
|
-
search_text=" ".join(p for p in fts_parts if p) or "",
|
|
163
|
-
)
|
|
164
|
-
if up.embedding:
|
|
165
|
-
self._vec_upsert("user_playbooks_vec", upid, up.embedding)
|
|
166
|
-
|
|
167
|
-
@SQLiteStorageBase.handle_exceptions
|
|
168
|
-
def get_user_playbooks(
|
|
169
|
-
self,
|
|
170
|
-
limit: int = 100,
|
|
171
|
-
user_id: str | None = None,
|
|
172
|
-
playbook_name: str | None = None,
|
|
173
|
-
agent_version: str | None = None,
|
|
174
|
-
status_filter: list[Status | None] | None = None,
|
|
175
|
-
start_time: int | None = None,
|
|
176
|
-
end_time: int | None = None,
|
|
177
|
-
include_embedding: bool = False,
|
|
178
|
-
) -> list[UserPlaybook]:
|
|
179
|
-
sql = "SELECT * FROM user_playbooks WHERE 1=1"
|
|
180
|
-
params: list[Any] = []
|
|
181
|
-
|
|
182
|
-
if user_id is not None:
|
|
183
|
-
sql += " AND user_id = ?"
|
|
184
|
-
params.append(user_id)
|
|
185
|
-
if playbook_name:
|
|
186
|
-
sql += " AND playbook_name = ?"
|
|
187
|
-
params.append(playbook_name)
|
|
188
|
-
if agent_version is not None:
|
|
189
|
-
sql += " AND agent_version = ?"
|
|
190
|
-
params.append(agent_version)
|
|
191
|
-
if start_time is not None:
|
|
192
|
-
sql += " AND created_at >= ?"
|
|
193
|
-
params.append(_epoch_to_iso(start_time))
|
|
194
|
-
if end_time is not None:
|
|
195
|
-
sql += " AND created_at <= ?"
|
|
196
|
-
params.append(_epoch_to_iso(end_time))
|
|
197
|
-
if status_filter is not None:
|
|
198
|
-
frag, sparams = _build_status_sql(status_filter)
|
|
199
|
-
sql += f" AND {frag}"
|
|
200
|
-
params.extend(sparams)
|
|
201
|
-
|
|
202
|
-
sql += " ORDER BY created_at DESC LIMIT ?"
|
|
203
|
-
params.append(limit)
|
|
204
|
-
rows = self._fetchall(sql, params)
|
|
205
|
-
return [
|
|
206
|
-
_row_to_user_playbook(r, include_embedding=include_embedding) for r in rows
|
|
207
|
-
]
|
|
208
|
-
|
|
209
|
-
@SQLiteStorageBase.handle_exceptions
|
|
210
|
-
def count_user_playbooks(
|
|
211
|
-
self,
|
|
212
|
-
user_id: str | None = None,
|
|
213
|
-
playbook_name: str | None = None,
|
|
214
|
-
min_user_playbook_id: int | None = None,
|
|
215
|
-
agent_version: str | None = None,
|
|
216
|
-
status_filter: list[Status | None] | None = None,
|
|
217
|
-
) -> int:
|
|
218
|
-
sql = "SELECT COUNT(*) as cnt FROM user_playbooks WHERE 1=1"
|
|
219
|
-
params: list[Any] = []
|
|
220
|
-
|
|
221
|
-
if user_id is not None:
|
|
222
|
-
sql += " AND user_id = ?"
|
|
223
|
-
params.append(user_id)
|
|
224
|
-
if playbook_name:
|
|
225
|
-
sql += " AND playbook_name = ?"
|
|
226
|
-
params.append(playbook_name)
|
|
227
|
-
if min_user_playbook_id is not None:
|
|
228
|
-
sql += " AND user_playbook_id > ?"
|
|
229
|
-
params.append(min_user_playbook_id)
|
|
230
|
-
if agent_version is not None:
|
|
231
|
-
sql += " AND agent_version = ?"
|
|
232
|
-
params.append(agent_version)
|
|
233
|
-
if status_filter is not None:
|
|
234
|
-
frag, sparams = _build_status_sql(status_filter)
|
|
235
|
-
sql += f" AND {frag}"
|
|
236
|
-
params.extend(sparams)
|
|
237
|
-
|
|
238
|
-
row = self._fetchone(sql, params)
|
|
239
|
-
return row["cnt"] if row else 0
|
|
240
|
-
|
|
241
|
-
@SQLiteStorageBase.handle_exceptions
|
|
242
|
-
def count_user_playbooks_by_session(self, session_id: str) -> int:
|
|
243
|
-
row = self._fetchone(
|
|
244
|
-
"""SELECT COUNT(*) as cnt FROM user_playbooks up
|
|
245
|
-
JOIN requests r ON up.request_id = r.request_id
|
|
246
|
-
WHERE r.session_id = ?""",
|
|
247
|
-
(session_id,),
|
|
248
|
-
)
|
|
249
|
-
return row["cnt"] if row else 0
|
|
250
|
-
|
|
251
|
-
@SQLiteStorageBase.handle_exceptions
|
|
252
|
-
def delete_all_user_playbooks(self) -> None:
|
|
253
|
-
with self._lock:
|
|
254
|
-
self.conn.execute("DELETE FROM user_playbooks_fts")
|
|
255
|
-
self.conn.execute("DELETE FROM user_playbooks")
|
|
256
|
-
self.conn.commit()
|
|
257
|
-
|
|
258
|
-
@SQLiteStorageBase.handle_exceptions
|
|
259
|
-
def delete_user_playbook(self, user_playbook_id: int) -> None:
|
|
260
|
-
self._fts_delete("user_playbooks_fts", user_playbook_id)
|
|
261
|
-
self._vec_delete("user_playbooks_vec", user_playbook_id)
|
|
262
|
-
self._execute(
|
|
263
|
-
"DELETE FROM user_playbooks WHERE user_playbook_id = ?", (user_playbook_id,)
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
@SQLiteStorageBase.handle_exceptions
|
|
267
|
-
def delete_all_user_playbooks_by_playbook_name(
|
|
268
|
-
self, playbook_name: str, agent_version: str | None = None
|
|
269
|
-
) -> None:
|
|
270
|
-
sql = "SELECT user_playbook_id FROM user_playbooks WHERE playbook_name = ?"
|
|
271
|
-
params: list[Any] = [playbook_name]
|
|
272
|
-
if agent_version is not None:
|
|
273
|
-
sql += " AND agent_version = ?"
|
|
274
|
-
params.append(agent_version)
|
|
275
|
-
ids = [r["user_playbook_id"] for r in self._fetchall(sql, params)]
|
|
276
|
-
if ids:
|
|
277
|
-
ph = ",".join("?" for _ in ids)
|
|
278
|
-
with self._lock:
|
|
279
|
-
self.conn.execute(
|
|
280
|
-
f"DELETE FROM user_playbooks_fts WHERE rowid IN ({ph})", ids
|
|
281
|
-
)
|
|
282
|
-
self.conn.commit()
|
|
283
|
-
|
|
284
|
-
del_sql = "DELETE FROM user_playbooks WHERE playbook_name = ?"
|
|
285
|
-
del_params: list[Any] = [playbook_name]
|
|
286
|
-
if agent_version is not None:
|
|
287
|
-
del_sql += " AND agent_version = ?"
|
|
288
|
-
del_params.append(agent_version)
|
|
289
|
-
self._execute(del_sql, del_params)
|
|
290
|
-
|
|
291
|
-
@SQLiteStorageBase.handle_exceptions
|
|
292
|
-
def delete_user_playbooks_by_ids(self, user_playbook_ids: list[int]) -> int:
|
|
293
|
-
if not user_playbook_ids:
|
|
294
|
-
return 0
|
|
295
|
-
ph = ",".join("?" for _ in user_playbook_ids)
|
|
296
|
-
with self._lock:
|
|
297
|
-
self.conn.execute(
|
|
298
|
-
f"DELETE FROM user_playbooks_fts WHERE rowid IN ({ph})",
|
|
299
|
-
user_playbook_ids,
|
|
300
|
-
)
|
|
301
|
-
cur = self.conn.execute(
|
|
302
|
-
f"DELETE FROM user_playbooks WHERE user_playbook_id IN ({ph})",
|
|
303
|
-
user_playbook_ids,
|
|
304
|
-
)
|
|
305
|
-
self.conn.commit()
|
|
306
|
-
return cur.rowcount
|
|
307
|
-
|
|
308
|
-
@SQLiteStorageBase.handle_exceptions
|
|
309
|
-
def update_all_user_playbooks_status(
|
|
310
|
-
self,
|
|
311
|
-
old_status: Status | None,
|
|
312
|
-
new_status: Status | None,
|
|
313
|
-
agent_version: str | None = None,
|
|
314
|
-
playbook_name: str | None = None,
|
|
315
|
-
) -> int:
|
|
316
|
-
new_val = new_status.value if new_status else None
|
|
317
|
-
params: list[Any] = [new_val]
|
|
318
|
-
|
|
319
|
-
if old_status is None or (
|
|
320
|
-
hasattr(old_status, "value") and old_status.value is None
|
|
321
|
-
):
|
|
322
|
-
where = "status IS NULL"
|
|
323
|
-
else:
|
|
324
|
-
where = "status = ?"
|
|
325
|
-
params.append(old_status.value)
|
|
326
|
-
|
|
327
|
-
if agent_version is not None:
|
|
328
|
-
where += " AND agent_version = ?"
|
|
329
|
-
params.append(agent_version)
|
|
330
|
-
if playbook_name is not None:
|
|
331
|
-
where += " AND playbook_name = ?"
|
|
332
|
-
params.append(playbook_name)
|
|
333
|
-
|
|
334
|
-
cur = self._execute(
|
|
335
|
-
f"UPDATE user_playbooks SET status = ? WHERE {where}", params
|
|
336
|
-
)
|
|
337
|
-
return cur.rowcount
|
|
338
|
-
|
|
339
|
-
@SQLiteStorageBase.handle_exceptions
|
|
340
|
-
def delete_all_user_playbooks_by_status(
|
|
341
|
-
self,
|
|
342
|
-
status: Status,
|
|
343
|
-
agent_version: str | None = None,
|
|
344
|
-
playbook_name: str | None = None,
|
|
345
|
-
) -> int:
|
|
346
|
-
where = "status = ?"
|
|
347
|
-
params: list[Any] = [status.value]
|
|
348
|
-
if agent_version is not None:
|
|
349
|
-
where += " AND agent_version = ?"
|
|
350
|
-
params.append(agent_version)
|
|
351
|
-
if playbook_name is not None:
|
|
352
|
-
where += " AND playbook_name = ?"
|
|
353
|
-
params.append(playbook_name)
|
|
354
|
-
|
|
355
|
-
# Clean up FTS
|
|
356
|
-
ids = [
|
|
357
|
-
r["user_playbook_id"]
|
|
358
|
-
for r in self._fetchall(
|
|
359
|
-
f"SELECT user_playbook_id FROM user_playbooks WHERE {where}", params
|
|
360
|
-
)
|
|
361
|
-
]
|
|
362
|
-
if ids:
|
|
363
|
-
ph = ",".join("?" for _ in ids)
|
|
364
|
-
with self._lock:
|
|
365
|
-
self.conn.execute(
|
|
366
|
-
f"DELETE FROM user_playbooks_fts WHERE rowid IN ({ph})", ids
|
|
367
|
-
)
|
|
368
|
-
self.conn.commit()
|
|
369
|
-
|
|
370
|
-
cur = self._execute(f"DELETE FROM user_playbooks WHERE {where}", params)
|
|
371
|
-
return cur.rowcount
|
|
372
|
-
|
|
373
|
-
@SQLiteStorageBase.handle_exceptions
|
|
374
|
-
def get_user_playbooks_by_ids(
|
|
375
|
-
self,
|
|
376
|
-
user_id: str,
|
|
377
|
-
user_playbook_ids: list[int],
|
|
378
|
-
status_filter: list[Status | None] | None = None,
|
|
379
|
-
) -> list[UserPlaybook]:
|
|
380
|
-
if not user_playbook_ids:
|
|
381
|
-
return []
|
|
382
|
-
if status_filter is None:
|
|
383
|
-
status_filter = [None]
|
|
384
|
-
frag, sparams = _build_status_sql(status_filter)
|
|
385
|
-
ph = ",".join("?" for _ in user_playbook_ids)
|
|
386
|
-
sql = (
|
|
387
|
-
f"SELECT * FROM user_playbooks "
|
|
388
|
-
f"WHERE user_id = ? AND user_playbook_id IN ({ph}) AND {frag}"
|
|
389
|
-
)
|
|
390
|
-
params: list[Any] = [user_id, *user_playbook_ids, *sparams]
|
|
391
|
-
return [_row_to_user_playbook(r) for r in self._fetchall(sql, params)]
|
|
392
|
-
|
|
393
|
-
@SQLiteStorageBase.handle_exceptions
|
|
394
|
-
def archive_user_playbook_by_id(self, user_id: str, user_playbook_id: int) -> bool:
|
|
395
|
-
cur = self._execute(
|
|
396
|
-
"UPDATE user_playbooks SET status = ? "
|
|
397
|
-
"WHERE user_playbook_id = ? AND user_id = ? AND status IS NULL",
|
|
398
|
-
(Status.ARCHIVED.value, user_playbook_id, user_id),
|
|
399
|
-
)
|
|
400
|
-
return cur.rowcount > 0
|
|
401
|
-
|
|
402
|
-
@SQLiteStorageBase.handle_exceptions
|
|
403
|
-
def has_user_playbooks_with_status(
|
|
404
|
-
self,
|
|
405
|
-
status: Status | None,
|
|
406
|
-
agent_version: str | None = None,
|
|
407
|
-
playbook_name: str | None = None,
|
|
408
|
-
) -> bool:
|
|
409
|
-
sql = "SELECT 1 FROM user_playbooks WHERE "
|
|
410
|
-
params: list[Any] = []
|
|
411
|
-
|
|
412
|
-
if status is None or (hasattr(status, "value") and status.value is None):
|
|
413
|
-
sql += "status IS NULL"
|
|
414
|
-
else:
|
|
415
|
-
sql += "status = ?"
|
|
416
|
-
params.append(status.value)
|
|
417
|
-
|
|
418
|
-
if agent_version is not None:
|
|
419
|
-
sql += " AND agent_version = ?"
|
|
420
|
-
params.append(agent_version)
|
|
421
|
-
if playbook_name is not None:
|
|
422
|
-
sql += " AND playbook_name = ?"
|
|
423
|
-
params.append(playbook_name)
|
|
424
|
-
|
|
425
|
-
sql += " LIMIT 1"
|
|
426
|
-
row = self._fetchone(sql, params)
|
|
427
|
-
return row is not None
|
|
428
|
-
|
|
429
|
-
@SQLiteStorageBase.handle_exceptions
|
|
430
|
-
def search_user_playbooks( # noqa: C901
|
|
431
|
-
self,
|
|
432
|
-
request: SearchUserPlaybookRequest,
|
|
433
|
-
options: SearchOptions | None = None,
|
|
434
|
-
) -> list[UserPlaybook]:
|
|
435
|
-
query = request.query
|
|
436
|
-
user_id = request.user_id
|
|
437
|
-
agent_version = request.agent_version
|
|
438
|
-
playbook_name = request.playbook_name
|
|
439
|
-
start_time = int(request.start_time.timestamp()) if request.start_time else None
|
|
440
|
-
end_time = int(request.end_time.timestamp()) if request.end_time else None
|
|
441
|
-
status_filter = request.status_filter
|
|
442
|
-
match_count = request.top_k or 10
|
|
443
|
-
query_embedding = options.query_embedding if options else None
|
|
444
|
-
mode = _effective_search_mode(
|
|
445
|
-
request.search_mode, query_embedding, request.query
|
|
446
|
-
)
|
|
447
|
-
rrf_k = options.rrf_k if options else 60
|
|
448
|
-
vector_weight = options.vector_weight if options else 1.0
|
|
449
|
-
fts_weight = options.fts_weight if options else 1.0
|
|
450
|
-
|
|
451
|
-
conditions: list[str] = []
|
|
452
|
-
params: list[Any] = []
|
|
453
|
-
|
|
454
|
-
if user_id:
|
|
455
|
-
conditions.append("up.user_id = ?")
|
|
456
|
-
params.append(user_id)
|
|
457
|
-
if agent_version:
|
|
458
|
-
conditions.append("up.agent_version = ?")
|
|
459
|
-
params.append(agent_version)
|
|
460
|
-
if playbook_name:
|
|
461
|
-
conditions.append("up.playbook_name = ?")
|
|
462
|
-
params.append(playbook_name)
|
|
463
|
-
if start_time:
|
|
464
|
-
conditions.append("up.created_at >= ?")
|
|
465
|
-
params.append(_epoch_to_iso(start_time))
|
|
466
|
-
if end_time:
|
|
467
|
-
conditions.append("up.created_at <= ?")
|
|
468
|
-
params.append(_epoch_to_iso(end_time))
|
|
469
|
-
if status_filter is not None:
|
|
470
|
-
frag, sparams = _build_status_sql(status_filter)
|
|
471
|
-
conditions.append(frag)
|
|
472
|
-
params.extend(sparams)
|
|
473
|
-
|
|
474
|
-
where_extra = (" AND " + " AND ".join(conditions)) if conditions else ""
|
|
475
|
-
overfetch = match_count * 5 if mode != SearchMode.FTS else match_count
|
|
476
|
-
|
|
477
|
-
# Pure vector search: fetch all candidates, rank by cosine similarity
|
|
478
|
-
if mode == SearchMode.VECTOR and query_embedding:
|
|
479
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else ""
|
|
480
|
-
sql = f"""SELECT * FROM user_playbooks up
|
|
481
|
-
{base_where}
|
|
482
|
-
ORDER BY up.created_at DESC"""
|
|
483
|
-
rows = self._fetchall(sql, params)
|
|
484
|
-
rows = _vector_rank_rows(rows, query_embedding, match_count)
|
|
485
|
-
return [_row_to_user_playbook(r) for r in rows]
|
|
486
|
-
|
|
487
|
-
if query:
|
|
488
|
-
fts_query = _sanitize_fts_query(query)
|
|
489
|
-
sql = f"""SELECT up.* FROM user_playbooks up
|
|
490
|
-
JOIN user_playbooks_fts f ON up.user_playbook_id = f.rowid
|
|
491
|
-
WHERE user_playbooks_fts MATCH ?{where_extra}
|
|
492
|
-
ORDER BY bm25(user_playbooks_fts, 1.0)
|
|
493
|
-
LIMIT ?"""
|
|
494
|
-
fts_rows = self._fetchall(sql, [fts_query, *params, overfetch])
|
|
495
|
-
|
|
496
|
-
if mode == SearchMode.HYBRID and query_embedding:
|
|
497
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else ""
|
|
498
|
-
vec_limit = match_count * 10
|
|
499
|
-
vec_sql = f"""SELECT * FROM user_playbooks up
|
|
500
|
-
{base_where}
|
|
501
|
-
ORDER BY up.created_at DESC
|
|
502
|
-
LIMIT ?"""
|
|
503
|
-
vec_candidates = self._fetchall(vec_sql, [*params, vec_limit])
|
|
504
|
-
vec_rows = _vector_rank_rows(vec_candidates, query_embedding, overfetch)
|
|
505
|
-
rows = _true_rrf_merge(
|
|
506
|
-
fts_rows,
|
|
507
|
-
vec_rows,
|
|
508
|
-
"user_playbook_id",
|
|
509
|
-
match_count,
|
|
510
|
-
rrf_k,
|
|
511
|
-
vector_weight,
|
|
512
|
-
fts_weight,
|
|
513
|
-
)
|
|
514
|
-
return [_row_to_user_playbook(r) for r in rows]
|
|
515
|
-
return [_row_to_user_playbook(r) for r in fts_rows[:match_count]]
|
|
516
|
-
|
|
517
|
-
# HYBRID without query text: rank by embedding only
|
|
518
|
-
if query_embedding:
|
|
519
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else ""
|
|
520
|
-
sql = f"""SELECT * FROM user_playbooks up
|
|
521
|
-
{base_where}
|
|
522
|
-
ORDER BY up.created_at DESC"""
|
|
523
|
-
rows = self._fetchall(sql, params)
|
|
524
|
-
rows = _vector_rank_rows(rows, query_embedding, match_count)
|
|
525
|
-
return [_row_to_user_playbook(r) for r in rows]
|
|
526
|
-
|
|
527
|
-
# No query text, no embedding -- recency fallback
|
|
528
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else "WHERE 1=1"
|
|
529
|
-
sql = f"""SELECT * FROM user_playbooks up
|
|
530
|
-
{base_where}
|
|
531
|
-
ORDER BY up.created_at DESC LIMIT ?"""
|
|
532
|
-
params.append(match_count)
|
|
533
|
-
rows = self._fetchall(sql, params)
|
|
534
|
-
return [_row_to_user_playbook(r) for r in rows]
|
|
535
|
-
|
|
536
|
-
@SQLiteStorageBase.handle_exceptions
|
|
537
|
-
def get_user_playbook_by_id(self, user_playbook_id: int) -> UserPlaybook | None:
|
|
538
|
-
row = self._fetchone(
|
|
539
|
-
"SELECT * FROM user_playbooks WHERE user_playbook_id = ?",
|
|
540
|
-
(user_playbook_id,),
|
|
541
|
-
)
|
|
542
|
-
return _row_to_user_playbook(row) if row else None
|
|
543
|
-
|
|
544
|
-
@SQLiteStorageBase.handle_exceptions
|
|
545
|
-
def get_user_playbooks_by_ids_any_user(
|
|
546
|
-
self,
|
|
547
|
-
user_playbook_ids: list[int],
|
|
548
|
-
status_filter: list[Status | None] | None = None,
|
|
549
|
-
) -> list[UserPlaybook]:
|
|
550
|
-
if not user_playbook_ids:
|
|
551
|
-
return []
|
|
552
|
-
ph = ",".join("?" for _ in user_playbook_ids)
|
|
553
|
-
sql = f"SELECT * FROM user_playbooks WHERE user_playbook_id IN ({ph})" # noqa: S608
|
|
554
|
-
params: list[Any] = list(user_playbook_ids)
|
|
555
|
-
if status_filter is not None:
|
|
556
|
-
frag, sparams = _build_status_sql(status_filter)
|
|
557
|
-
sql += f" AND {frag}"
|
|
558
|
-
params.extend(sparams)
|
|
559
|
-
rows = self._fetchall(sql, params)
|
|
560
|
-
by_id = {
|
|
561
|
-
_row_to_user_playbook(row).user_playbook_id: _row_to_user_playbook(row)
|
|
562
|
-
for row in rows
|
|
563
|
-
}
|
|
564
|
-
return [by_id[upid] for upid in user_playbook_ids if upid in by_id]
|
|
565
|
-
|
|
566
|
-
# ------------------------------------------------------------------
|
|
567
|
-
# Agent Playbook methods
|
|
568
|
-
# ------------------------------------------------------------------
|
|
569
|
-
|
|
570
|
-
@SQLiteStorageBase.handle_exceptions
|
|
571
|
-
def save_agent_playbooks(
|
|
572
|
-
self, agent_playbooks: list[AgentPlaybook]
|
|
573
|
-
) -> list[AgentPlaybook]:
|
|
574
|
-
saved: list[AgentPlaybook] = []
|
|
575
|
-
for ap in agent_playbooks:
|
|
576
|
-
embedding_text = ap.trigger or ap.content
|
|
577
|
-
if self._should_expand_documents():
|
|
578
|
-
with ThreadPoolExecutor(max_workers=2) as executor:
|
|
579
|
-
emb_future = executor.submit(self._get_embedding, embedding_text)
|
|
580
|
-
exp_future = executor.submit(self._expand_document, embedding_text)
|
|
581
|
-
ap.embedding = emb_future.result(timeout=15)
|
|
582
|
-
ap.expanded_terms = exp_future.result(timeout=15)
|
|
583
|
-
else:
|
|
584
|
-
ap.embedding = self._get_embedding(embedding_text)
|
|
585
|
-
|
|
586
|
-
created_at_iso = _epoch_to_iso(ap.created_at)
|
|
587
|
-
with self._lock:
|
|
588
|
-
cur = self.conn.execute(
|
|
589
|
-
"""INSERT INTO agent_playbooks
|
|
590
|
-
(playbook_name, created_at, agent_version, content,
|
|
591
|
-
trigger, rationale, blocking_issue,
|
|
592
|
-
playbook_status, playbook_metadata, embedding,
|
|
593
|
-
expanded_terms, status)
|
|
594
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
|
|
595
|
-
(
|
|
596
|
-
ap.playbook_name,
|
|
597
|
-
created_at_iso,
|
|
598
|
-
ap.agent_version,
|
|
599
|
-
ap.content,
|
|
600
|
-
ap.trigger,
|
|
601
|
-
ap.rationale,
|
|
602
|
-
json.dumps(ap.blocking_issue.model_dump())
|
|
603
|
-
if ap.blocking_issue
|
|
604
|
-
else None,
|
|
605
|
-
ap.playbook_status.value
|
|
606
|
-
if isinstance(ap.playbook_status, PlaybookStatus)
|
|
607
|
-
else ap.playbook_status,
|
|
608
|
-
ap.playbook_metadata,
|
|
609
|
-
_json_dumps(ap.embedding),
|
|
610
|
-
ap.expanded_terms,
|
|
611
|
-
ap.status.value if ap.status else None,
|
|
612
|
-
),
|
|
613
|
-
)
|
|
614
|
-
ap.agent_playbook_id = cur.lastrowid or 0
|
|
615
|
-
self.conn.commit()
|
|
616
|
-
|
|
617
|
-
fts_parts = [ap.trigger or "", ap.content or ""]
|
|
618
|
-
if ap.expanded_terms:
|
|
619
|
-
fts_parts.append(ap.expanded_terms)
|
|
620
|
-
self._fts_upsert(
|
|
621
|
-
"agent_playbooks_fts",
|
|
622
|
-
ap.agent_playbook_id,
|
|
623
|
-
search_text=" ".join(p for p in fts_parts if p) or "",
|
|
624
|
-
)
|
|
625
|
-
if ap.embedding:
|
|
626
|
-
self._vec_upsert(
|
|
627
|
-
"agent_playbooks_vec", ap.agent_playbook_id, ap.embedding
|
|
628
|
-
)
|
|
629
|
-
saved.append(ap)
|
|
630
|
-
return saved
|
|
631
|
-
|
|
632
|
-
@SQLiteStorageBase.handle_exceptions
|
|
633
|
-
def get_agent_playbooks(
|
|
634
|
-
self,
|
|
635
|
-
limit: int = 100,
|
|
636
|
-
playbook_name: str | None = None,
|
|
637
|
-
agent_version: str | None = None,
|
|
638
|
-
status_filter: list[Status | None] | None = None,
|
|
639
|
-
playbook_status_filter: list[PlaybookStatus] | None = None,
|
|
640
|
-
) -> list[AgentPlaybook]:
|
|
641
|
-
sql = "SELECT * FROM agent_playbooks WHERE 1=1"
|
|
642
|
-
params: list[Any] = []
|
|
643
|
-
|
|
644
|
-
if playbook_name:
|
|
645
|
-
sql += " AND playbook_name = ?"
|
|
646
|
-
params.append(playbook_name)
|
|
647
|
-
|
|
648
|
-
if agent_version is not None:
|
|
649
|
-
sql += " AND agent_version = ?"
|
|
650
|
-
params.append(agent_version)
|
|
651
|
-
|
|
652
|
-
if status_filter is not None:
|
|
653
|
-
frag, sparams = _build_status_sql(status_filter)
|
|
654
|
-
sql += f" AND {frag}"
|
|
655
|
-
params.extend(sparams)
|
|
656
|
-
else:
|
|
657
|
-
sql += " AND status IS NULL"
|
|
658
|
-
|
|
659
|
-
if playbook_status_filter:
|
|
660
|
-
ph = ",".join("?" for _ in playbook_status_filter)
|
|
661
|
-
sql += f" AND playbook_status IN ({ph})"
|
|
662
|
-
params.extend(ps.value for ps in playbook_status_filter)
|
|
663
|
-
|
|
664
|
-
sql += " ORDER BY created_at DESC LIMIT ?"
|
|
665
|
-
params.append(limit)
|
|
666
|
-
rows = self._fetchall(sql, params)
|
|
667
|
-
return [_row_to_agent_playbook(r) for r in rows]
|
|
668
|
-
|
|
669
|
-
@SQLiteStorageBase.handle_exceptions
|
|
670
|
-
def get_agent_playbook_by_id(self, agent_playbook_id: int) -> AgentPlaybook | None:
|
|
671
|
-
row = self._fetchone(
|
|
672
|
-
"SELECT * FROM agent_playbooks WHERE agent_playbook_id = ?",
|
|
673
|
-
(agent_playbook_id,),
|
|
674
|
-
)
|
|
675
|
-
return _row_to_agent_playbook(row) if row else None
|
|
676
|
-
|
|
677
|
-
@SQLiteStorageBase.handle_exceptions
|
|
678
|
-
def delete_all_agent_playbooks(self) -> None:
|
|
679
|
-
with self._lock:
|
|
680
|
-
self.conn.execute("DELETE FROM agent_playbooks_fts")
|
|
681
|
-
self.conn.execute("DELETE FROM agent_playbooks")
|
|
682
|
-
self.conn.commit()
|
|
683
|
-
|
|
684
|
-
@SQLiteStorageBase.handle_exceptions
|
|
685
|
-
def delete_agent_playbook(self, agent_playbook_id: int) -> None:
|
|
686
|
-
self._fts_delete("agent_playbooks_fts", agent_playbook_id)
|
|
687
|
-
self._vec_delete("agent_playbooks_vec", agent_playbook_id)
|
|
688
|
-
self._execute(
|
|
689
|
-
"DELETE FROM agent_playbooks WHERE agent_playbook_id = ?",
|
|
690
|
-
(agent_playbook_id,),
|
|
691
|
-
)
|
|
692
|
-
|
|
693
|
-
@SQLiteStorageBase.handle_exceptions
|
|
694
|
-
def delete_all_agent_playbooks_by_playbook_name(
|
|
695
|
-
self, playbook_name: str, agent_version: str | None = None
|
|
696
|
-
) -> None:
|
|
697
|
-
sql = "SELECT agent_playbook_id FROM agent_playbooks WHERE playbook_name = ?"
|
|
698
|
-
params: list[Any] = [playbook_name]
|
|
699
|
-
if agent_version is not None:
|
|
700
|
-
sql += " AND agent_version = ?"
|
|
701
|
-
params.append(agent_version)
|
|
702
|
-
ids = [r["agent_playbook_id"] for r in self._fetchall(sql, params)]
|
|
703
|
-
if ids:
|
|
704
|
-
ph = ",".join("?" for _ in ids)
|
|
705
|
-
with self._lock:
|
|
706
|
-
self.conn.execute(
|
|
707
|
-
f"DELETE FROM agent_playbooks_fts WHERE rowid IN ({ph})", ids
|
|
708
|
-
)
|
|
709
|
-
self.conn.commit()
|
|
710
|
-
|
|
711
|
-
del_sql = "DELETE FROM agent_playbooks WHERE playbook_name = ?"
|
|
712
|
-
del_params: list[Any] = [playbook_name]
|
|
713
|
-
if agent_version is not None:
|
|
714
|
-
del_sql += " AND agent_version = ?"
|
|
715
|
-
del_params.append(agent_version)
|
|
716
|
-
self._execute(del_sql, del_params)
|
|
717
|
-
|
|
718
|
-
@SQLiteStorageBase.handle_exceptions
|
|
719
|
-
def delete_agent_playbooks_by_ids(self, agent_playbook_ids: list[int]) -> None:
|
|
720
|
-
if not agent_playbook_ids:
|
|
721
|
-
return
|
|
722
|
-
ph = ",".join("?" for _ in agent_playbook_ids)
|
|
723
|
-
with self._lock:
|
|
724
|
-
self.conn.execute(
|
|
725
|
-
f"DELETE FROM agent_playbooks_fts WHERE rowid IN ({ph})",
|
|
726
|
-
agent_playbook_ids,
|
|
727
|
-
)
|
|
728
|
-
self.conn.execute(
|
|
729
|
-
f"DELETE FROM agent_playbooks WHERE agent_playbook_id IN ({ph})",
|
|
730
|
-
agent_playbook_ids,
|
|
731
|
-
)
|
|
732
|
-
self.conn.commit()
|
|
733
|
-
|
|
734
|
-
@SQLiteStorageBase.handle_exceptions
|
|
735
|
-
def update_agent_playbook_status(
|
|
736
|
-
self, agent_playbook_id: int, playbook_status: PlaybookStatus
|
|
737
|
-
) -> None:
|
|
738
|
-
row = self._fetchone(
|
|
739
|
-
"SELECT agent_playbook_id FROM agent_playbooks WHERE agent_playbook_id = ?",
|
|
740
|
-
(agent_playbook_id,),
|
|
741
|
-
)
|
|
742
|
-
if not row:
|
|
743
|
-
raise ValueError(f"Agent playbook with ID {agent_playbook_id} not found")
|
|
744
|
-
self._execute(
|
|
745
|
-
"UPDATE agent_playbooks SET playbook_status = ? WHERE agent_playbook_id = ?",
|
|
746
|
-
(playbook_status.value, agent_playbook_id),
|
|
747
|
-
)
|
|
748
|
-
|
|
749
|
-
@SQLiteStorageBase.handle_exceptions
|
|
750
|
-
def update_agent_playbook(
|
|
751
|
-
self,
|
|
752
|
-
agent_playbook_id: int,
|
|
753
|
-
playbook_name: str | None = None,
|
|
754
|
-
content: str | None = None,
|
|
755
|
-
trigger: str | None = None,
|
|
756
|
-
rationale: str | None = None,
|
|
757
|
-
blocking_issue: BlockingIssue | None = None,
|
|
758
|
-
playbook_status: PlaybookStatus | None = None,
|
|
759
|
-
) -> None:
|
|
760
|
-
row = self._fetchone(
|
|
761
|
-
"SELECT agent_playbook_id FROM agent_playbooks WHERE agent_playbook_id = ?",
|
|
762
|
-
(agent_playbook_id,),
|
|
763
|
-
)
|
|
764
|
-
if not row:
|
|
765
|
-
raise ValueError(f"Agent playbook with ID {agent_playbook_id} not found")
|
|
766
|
-
updates: list[str] = []
|
|
767
|
-
params: list[Any] = []
|
|
768
|
-
if playbook_name is not None:
|
|
769
|
-
updates.append("playbook_name = ?")
|
|
770
|
-
params.append(playbook_name)
|
|
771
|
-
if content is not None:
|
|
772
|
-
updates.append("content = ?")
|
|
773
|
-
params.append(content)
|
|
774
|
-
if trigger is not None:
|
|
775
|
-
updates.append("trigger = ?")
|
|
776
|
-
params.append(trigger)
|
|
777
|
-
if rationale is not None:
|
|
778
|
-
updates.append("rationale = ?")
|
|
779
|
-
params.append(rationale)
|
|
780
|
-
if blocking_issue is not None:
|
|
781
|
-
updates.append("blocking_issue = ?")
|
|
782
|
-
params.append(json.dumps(blocking_issue.model_dump()))
|
|
783
|
-
if playbook_status is not None:
|
|
784
|
-
updates.append("playbook_status = ?")
|
|
785
|
-
params.append(playbook_status.value)
|
|
786
|
-
if updates:
|
|
787
|
-
params.append(agent_playbook_id)
|
|
788
|
-
self._execute(
|
|
789
|
-
f"UPDATE agent_playbooks SET {', '.join(updates)} WHERE agent_playbook_id = ?",
|
|
790
|
-
tuple(params),
|
|
791
|
-
)
|
|
792
|
-
|
|
793
|
-
@SQLiteStorageBase.handle_exceptions
|
|
794
|
-
def update_user_playbook(
|
|
795
|
-
self,
|
|
796
|
-
user_playbook_id: int,
|
|
797
|
-
playbook_name: str | None = None,
|
|
798
|
-
content: str | None = None,
|
|
799
|
-
trigger: str | None = None,
|
|
800
|
-
rationale: str | None = None,
|
|
801
|
-
blocking_issue: BlockingIssue | None = None,
|
|
802
|
-
) -> None:
|
|
803
|
-
row = self._fetchone(
|
|
804
|
-
"SELECT user_playbook_id FROM user_playbooks WHERE user_playbook_id = ?",
|
|
805
|
-
(user_playbook_id,),
|
|
806
|
-
)
|
|
807
|
-
if not row:
|
|
808
|
-
raise ValueError(f"User playbook with ID {user_playbook_id} not found")
|
|
809
|
-
updates: list[str] = []
|
|
810
|
-
params: list[Any] = []
|
|
811
|
-
if playbook_name is not None:
|
|
812
|
-
updates.append("playbook_name = ?")
|
|
813
|
-
params.append(playbook_name)
|
|
814
|
-
if content is not None:
|
|
815
|
-
updates.append("content = ?")
|
|
816
|
-
params.append(content)
|
|
817
|
-
if trigger is not None:
|
|
818
|
-
updates.append("trigger = ?")
|
|
819
|
-
params.append(trigger)
|
|
820
|
-
if rationale is not None:
|
|
821
|
-
updates.append("rationale = ?")
|
|
822
|
-
params.append(rationale)
|
|
823
|
-
if blocking_issue is not None:
|
|
824
|
-
updates.append("blocking_issue = ?")
|
|
825
|
-
params.append(json.dumps(blocking_issue.model_dump()))
|
|
826
|
-
if updates:
|
|
827
|
-
params.append(user_playbook_id)
|
|
828
|
-
self._execute(
|
|
829
|
-
f"UPDATE user_playbooks SET {', '.join(updates)} WHERE user_playbook_id = ?",
|
|
830
|
-
tuple(params),
|
|
831
|
-
)
|
|
832
|
-
|
|
833
|
-
@SQLiteStorageBase.handle_exceptions
|
|
834
|
-
def archive_agent_playbooks_by_playbook_name(
|
|
835
|
-
self, playbook_name: str, agent_version: str | None = None
|
|
836
|
-
) -> None:
|
|
837
|
-
sql = "UPDATE agent_playbooks SET status = 'archived' WHERE playbook_name = ? AND playbook_status != ?"
|
|
838
|
-
params: list[Any] = [playbook_name, PlaybookStatus.APPROVED.value]
|
|
839
|
-
if agent_version is not None:
|
|
840
|
-
sql += " AND agent_version = ?"
|
|
841
|
-
params.append(agent_version)
|
|
842
|
-
self._execute(sql, params)
|
|
843
|
-
|
|
844
|
-
@SQLiteStorageBase.handle_exceptions
|
|
845
|
-
def archive_agent_playbooks_by_ids(self, agent_playbook_ids: list[int]) -> None:
|
|
846
|
-
if not agent_playbook_ids:
|
|
847
|
-
return
|
|
848
|
-
ph = ",".join("?" for _ in agent_playbook_ids)
|
|
849
|
-
self._execute(
|
|
850
|
-
f"UPDATE agent_playbooks SET status = 'archived' WHERE agent_playbook_id IN ({ph}) AND playbook_status != ?",
|
|
851
|
-
[*agent_playbook_ids, PlaybookStatus.APPROVED.value],
|
|
852
|
-
)
|
|
853
|
-
|
|
854
|
-
@SQLiteStorageBase.handle_exceptions
|
|
855
|
-
def restore_archived_agent_playbooks_by_playbook_name(
|
|
856
|
-
self, playbook_name: str, agent_version: str | None = None
|
|
857
|
-
) -> None:
|
|
858
|
-
sql = "UPDATE agent_playbooks SET status = NULL WHERE playbook_name = ? AND status = 'archived'"
|
|
859
|
-
params: list[Any] = [playbook_name]
|
|
860
|
-
if agent_version is not None:
|
|
861
|
-
sql += " AND agent_version = ?"
|
|
862
|
-
params.append(agent_version)
|
|
863
|
-
self._execute(sql, params)
|
|
864
|
-
|
|
865
|
-
@SQLiteStorageBase.handle_exceptions
|
|
866
|
-
def restore_archived_agent_playbooks_by_ids(
|
|
867
|
-
self, agent_playbook_ids: list[int]
|
|
868
|
-
) -> None:
|
|
869
|
-
if not agent_playbook_ids:
|
|
870
|
-
return
|
|
871
|
-
ph = ",".join("?" for _ in agent_playbook_ids)
|
|
872
|
-
self._execute(
|
|
873
|
-
f"UPDATE agent_playbooks SET status = NULL WHERE agent_playbook_id IN ({ph}) AND status = 'archived'",
|
|
874
|
-
agent_playbook_ids,
|
|
875
|
-
)
|
|
876
|
-
|
|
877
|
-
# ------------------------------------------------------------------
|
|
878
|
-
# Playbook optimizer methods
|
|
879
|
-
# ------------------------------------------------------------------
|
|
880
|
-
|
|
881
|
-
@SQLiteStorageBase.handle_exceptions
|
|
882
|
-
def set_source_user_playbook_ids_for_agent_playbook(
|
|
883
|
-
self, agent_playbook_id: int, user_playbook_ids: list[int]
|
|
884
|
-
) -> None:
|
|
885
|
-
self.set_source_windows_for_agent_playbook(
|
|
886
|
-
agent_playbook_id,
|
|
887
|
-
[
|
|
888
|
-
AgentPlaybookSourceWindow(
|
|
889
|
-
user_playbook_id=upid, source_interaction_ids=[]
|
|
890
|
-
)
|
|
891
|
-
for upid in user_playbook_ids
|
|
892
|
-
],
|
|
893
|
-
)
|
|
894
|
-
|
|
895
|
-
@SQLiteStorageBase.handle_exceptions
|
|
896
|
-
def get_source_user_playbook_ids_for_agent_playbook(
|
|
897
|
-
self, agent_playbook_id: int
|
|
898
|
-
) -> list[int]:
|
|
899
|
-
return [
|
|
900
|
-
window.user_playbook_id
|
|
901
|
-
for window in self.get_source_windows_for_agent_playbook(agent_playbook_id)
|
|
902
|
-
]
|
|
903
|
-
|
|
904
|
-
@SQLiteStorageBase.handle_exceptions
|
|
905
|
-
def set_source_windows_for_agent_playbook(
|
|
906
|
-
self,
|
|
907
|
-
agent_playbook_id: int,
|
|
908
|
-
source_windows: list[AgentPlaybookSourceWindow],
|
|
909
|
-
) -> None:
|
|
910
|
-
by_id: dict[int, list[int]] = {}
|
|
911
|
-
for window in source_windows:
|
|
912
|
-
ids = by_id.setdefault(window.user_playbook_id, [])
|
|
913
|
-
seen = set(ids)
|
|
914
|
-
for source_id in window.source_interaction_ids:
|
|
915
|
-
if source_id not in seen:
|
|
916
|
-
ids.append(source_id)
|
|
917
|
-
seen.add(source_id)
|
|
918
|
-
with self._lock:
|
|
919
|
-
self.conn.execute(
|
|
920
|
-
"DELETE FROM agent_playbook_source_user_playbooks WHERE agent_playbook_id = ?",
|
|
921
|
-
(agent_playbook_id,),
|
|
922
|
-
)
|
|
923
|
-
self.conn.executemany(
|
|
924
|
-
"""INSERT OR IGNORE INTO agent_playbook_source_user_playbooks
|
|
925
|
-
(agent_playbook_id, user_playbook_id, source_interaction_ids)
|
|
926
|
-
VALUES (?, ?, ?)""",
|
|
927
|
-
[
|
|
928
|
-
(
|
|
929
|
-
agent_playbook_id,
|
|
930
|
-
upid,
|
|
931
|
-
_json_dumps(source_interaction_ids) or "[]",
|
|
932
|
-
)
|
|
933
|
-
for upid, source_interaction_ids in by_id.items()
|
|
934
|
-
],
|
|
935
|
-
)
|
|
936
|
-
self.conn.commit()
|
|
937
|
-
|
|
938
|
-
@SQLiteStorageBase.handle_exceptions
|
|
939
|
-
def get_source_windows_for_agent_playbook(
|
|
940
|
-
self, agent_playbook_id: int
|
|
941
|
-
) -> list[AgentPlaybookSourceWindow]:
|
|
942
|
-
rows = self._fetchall(
|
|
943
|
-
"""SELECT user_playbook_id, source_interaction_ids
|
|
944
|
-
FROM agent_playbook_source_user_playbooks
|
|
945
|
-
WHERE agent_playbook_id = ?
|
|
946
|
-
ORDER BY user_playbook_id ASC""",
|
|
947
|
-
(agent_playbook_id,),
|
|
948
|
-
)
|
|
949
|
-
return [
|
|
950
|
-
AgentPlaybookSourceWindow(
|
|
951
|
-
user_playbook_id=int(row["user_playbook_id"]),
|
|
952
|
-
source_interaction_ids=_json_loads(row["source_interaction_ids"]) or [],
|
|
953
|
-
)
|
|
954
|
-
for row in rows
|
|
955
|
-
]
|
|
956
|
-
|
|
957
|
-
@SQLiteStorageBase.handle_exceptions
|
|
958
|
-
def create_playbook_optimization_job(
|
|
959
|
-
self, job: PlaybookOptimizationJob
|
|
960
|
-
) -> PlaybookOptimizationJob:
|
|
961
|
-
with self._lock:
|
|
962
|
-
cur = self.conn.execute(
|
|
963
|
-
"""INSERT INTO playbook_optimization_jobs
|
|
964
|
-
(target_kind, target_id, status, best_candidate_id,
|
|
965
|
-
successor_target_id, decision_reason, metadata_json,
|
|
966
|
-
created_at, updated_at)
|
|
967
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
968
|
-
(
|
|
969
|
-
job.target_kind,
|
|
970
|
-
job.target_id,
|
|
971
|
-
job.status,
|
|
972
|
-
job.best_candidate_id,
|
|
973
|
-
job.successor_target_id,
|
|
974
|
-
job.decision_reason,
|
|
975
|
-
job.metadata_json,
|
|
976
|
-
job.created_at,
|
|
977
|
-
job.updated_at,
|
|
978
|
-
),
|
|
979
|
-
)
|
|
980
|
-
job.job_id = cur.lastrowid or 0
|
|
981
|
-
self.conn.commit()
|
|
982
|
-
return job
|
|
983
|
-
|
|
984
|
-
@SQLiteStorageBase.handle_exceptions
|
|
985
|
-
def update_playbook_optimization_job(
|
|
986
|
-
self,
|
|
987
|
-
job_id: int,
|
|
988
|
-
*,
|
|
989
|
-
status: str | None = None,
|
|
990
|
-
best_candidate_id: int | None = None,
|
|
991
|
-
successor_target_id: int | None = None,
|
|
992
|
-
decision_reason: str | None = None,
|
|
993
|
-
metadata_json: str | None = None,
|
|
994
|
-
) -> None:
|
|
995
|
-
updates: list[str] = ["updated_at = strftime('%s','now')"]
|
|
996
|
-
params: list[Any] = []
|
|
997
|
-
if status is not None:
|
|
998
|
-
updates.append("status = ?")
|
|
999
|
-
params.append(status)
|
|
1000
|
-
if best_candidate_id is not None:
|
|
1001
|
-
updates.append("best_candidate_id = ?")
|
|
1002
|
-
params.append(best_candidate_id)
|
|
1003
|
-
if successor_target_id is not None:
|
|
1004
|
-
updates.append("successor_target_id = ?")
|
|
1005
|
-
params.append(successor_target_id)
|
|
1006
|
-
if decision_reason is not None:
|
|
1007
|
-
updates.append("decision_reason = ?")
|
|
1008
|
-
params.append(decision_reason)
|
|
1009
|
-
if metadata_json is not None:
|
|
1010
|
-
updates.append("metadata_json = ?")
|
|
1011
|
-
params.append(metadata_json)
|
|
1012
|
-
params.append(job_id)
|
|
1013
|
-
self._execute(
|
|
1014
|
-
f"UPDATE playbook_optimization_jobs SET {', '.join(updates)} WHERE job_id = ?", # noqa: S608
|
|
1015
|
-
tuple(params),
|
|
1016
|
-
)
|
|
1017
|
-
|
|
1018
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1019
|
-
def insert_playbook_optimization_candidate(
|
|
1020
|
-
self, candidate: PlaybookOptimizationCandidate
|
|
1021
|
-
) -> PlaybookOptimizationCandidate:
|
|
1022
|
-
with self._lock:
|
|
1023
|
-
cur = self.conn.execute(
|
|
1024
|
-
"""INSERT INTO playbook_optimization_candidates
|
|
1025
|
-
(job_id, candidate_index, content, parent_candidate_ids,
|
|
1026
|
-
aggregate_score, is_winner, created_at)
|
|
1027
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
|
1028
|
-
(
|
|
1029
|
-
candidate.job_id,
|
|
1030
|
-
candidate.candidate_index,
|
|
1031
|
-
candidate.content,
|
|
1032
|
-
_json_dumps(candidate.parent_candidate_ids) or "[]",
|
|
1033
|
-
candidate.aggregate_score,
|
|
1034
|
-
1 if candidate.is_winner else 0,
|
|
1035
|
-
candidate.created_at,
|
|
1036
|
-
),
|
|
1037
|
-
)
|
|
1038
|
-
candidate.candidate_id = cur.lastrowid or 0
|
|
1039
|
-
self.conn.commit()
|
|
1040
|
-
return candidate
|
|
1041
|
-
|
|
1042
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1043
|
-
def list_playbook_optimization_candidates(
|
|
1044
|
-
self, job_id: int
|
|
1045
|
-
) -> list[PlaybookOptimizationCandidate]:
|
|
1046
|
-
rows = self._fetchall(
|
|
1047
|
-
"SELECT * FROM playbook_optimization_candidates WHERE job_id = ? ORDER BY candidate_id ASC",
|
|
1048
|
-
(job_id,),
|
|
1049
|
-
)
|
|
1050
|
-
return [_row_to_playbook_optimization_candidate(row) for row in rows]
|
|
1051
|
-
|
|
1052
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1053
|
-
def update_playbook_optimization_candidate(
|
|
1054
|
-
self,
|
|
1055
|
-
candidate_id: int,
|
|
1056
|
-
*,
|
|
1057
|
-
aggregate_score: float | None = None,
|
|
1058
|
-
is_winner: bool | None = None,
|
|
1059
|
-
) -> None:
|
|
1060
|
-
updates: list[str] = []
|
|
1061
|
-
params: list[Any] = []
|
|
1062
|
-
if aggregate_score is not None:
|
|
1063
|
-
updates.append("aggregate_score = ?")
|
|
1064
|
-
params.append(aggregate_score)
|
|
1065
|
-
if is_winner is not None:
|
|
1066
|
-
updates.append("is_winner = ?")
|
|
1067
|
-
params.append(1 if is_winner else 0)
|
|
1068
|
-
if not updates:
|
|
1069
|
-
return
|
|
1070
|
-
params.append(candidate_id)
|
|
1071
|
-
self._execute(
|
|
1072
|
-
f"UPDATE playbook_optimization_candidates SET {', '.join(updates)} WHERE candidate_id = ?", # noqa: S608
|
|
1073
|
-
tuple(params),
|
|
1074
|
-
)
|
|
1075
|
-
|
|
1076
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1077
|
-
def insert_playbook_optimization_evaluation(
|
|
1078
|
-
self, evaluation: PlaybookOptimizationEvaluation
|
|
1079
|
-
) -> PlaybookOptimizationEvaluation:
|
|
1080
|
-
with self._lock:
|
|
1081
|
-
cur = self.conn.execute(
|
|
1082
|
-
"""INSERT INTO playbook_optimization_evaluations
|
|
1083
|
-
(job_id, candidate_id, target_kind, target_id,
|
|
1084
|
-
scenario_user_playbook_id, source_interaction_ids, score,
|
|
1085
|
-
verdict, likert, rationale, asi_json, incumbent_rollout_json,
|
|
1086
|
-
candidate_rollout_json, created_at)
|
|
1087
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
1088
|
-
(
|
|
1089
|
-
evaluation.job_id,
|
|
1090
|
-
evaluation.candidate_id,
|
|
1091
|
-
evaluation.target_kind,
|
|
1092
|
-
evaluation.target_id,
|
|
1093
|
-
evaluation.scenario_user_playbook_id,
|
|
1094
|
-
_json_dumps(evaluation.source_interaction_ids) or "[]",
|
|
1095
|
-
evaluation.score,
|
|
1096
|
-
evaluation.verdict,
|
|
1097
|
-
evaluation.likert,
|
|
1098
|
-
evaluation.rationale,
|
|
1099
|
-
evaluation.asi_json,
|
|
1100
|
-
evaluation.incumbent_rollout_json,
|
|
1101
|
-
evaluation.candidate_rollout_json,
|
|
1102
|
-
evaluation.created_at,
|
|
1103
|
-
),
|
|
1104
|
-
)
|
|
1105
|
-
evaluation.evaluation_id = cur.lastrowid or 0
|
|
1106
|
-
self.conn.commit()
|
|
1107
|
-
return evaluation
|
|
1108
|
-
|
|
1109
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1110
|
-
def list_playbook_optimization_evaluations(
|
|
1111
|
-
self, job_id: int
|
|
1112
|
-
) -> list[PlaybookOptimizationEvaluation]:
|
|
1113
|
-
rows = self._fetchall(
|
|
1114
|
-
"SELECT * FROM playbook_optimization_evaluations WHERE job_id = ? ORDER BY evaluation_id ASC",
|
|
1115
|
-
(job_id,),
|
|
1116
|
-
)
|
|
1117
|
-
return [_row_to_playbook_optimization_evaluation(row) for row in rows]
|
|
1118
|
-
|
|
1119
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1120
|
-
def insert_playbook_optimization_event(
|
|
1121
|
-
self, event: PlaybookOptimizationEvent
|
|
1122
|
-
) -> PlaybookOptimizationEvent:
|
|
1123
|
-
with self._lock:
|
|
1124
|
-
cur = self.conn.execute(
|
|
1125
|
-
"""INSERT INTO playbook_optimization_events
|
|
1126
|
-
(job_id, event_type, payload_json, created_at)
|
|
1127
|
-
VALUES (?, ?, ?, ?)""",
|
|
1128
|
-
(event.job_id, event.event_type, event.payload_json, event.created_at),
|
|
1129
|
-
)
|
|
1130
|
-
event.event_id = cur.lastrowid or 0
|
|
1131
|
-
self.conn.commit()
|
|
1132
|
-
return event
|
|
1133
|
-
|
|
1134
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1135
|
-
def delete_archived_agent_playbooks_by_playbook_name(
|
|
1136
|
-
self, playbook_name: str, agent_version: str | None = None
|
|
1137
|
-
) -> None:
|
|
1138
|
-
# Get IDs for FTS cleanup
|
|
1139
|
-
sql = "SELECT agent_playbook_id FROM agent_playbooks WHERE playbook_name = ? AND status = 'archived'"
|
|
1140
|
-
params: list[Any] = [playbook_name]
|
|
1141
|
-
if agent_version is not None:
|
|
1142
|
-
sql += " AND agent_version = ?"
|
|
1143
|
-
params.append(agent_version)
|
|
1144
|
-
ids = [r["agent_playbook_id"] for r in self._fetchall(sql, params)]
|
|
1145
|
-
if ids:
|
|
1146
|
-
ph = ",".join("?" for _ in ids)
|
|
1147
|
-
with self._lock:
|
|
1148
|
-
self.conn.execute(
|
|
1149
|
-
f"DELETE FROM agent_playbooks_fts WHERE rowid IN ({ph})", ids
|
|
1150
|
-
)
|
|
1151
|
-
self.conn.commit()
|
|
1152
|
-
|
|
1153
|
-
del_sql = "DELETE FROM agent_playbooks WHERE playbook_name = ? AND status = 'archived'"
|
|
1154
|
-
del_params: list[Any] = [playbook_name]
|
|
1155
|
-
if agent_version is not None:
|
|
1156
|
-
del_sql += " AND agent_version = ?"
|
|
1157
|
-
del_params.append(agent_version)
|
|
1158
|
-
self._execute(del_sql, del_params)
|
|
1159
|
-
|
|
1160
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1161
|
-
def search_agent_playbooks( # noqa: C901
|
|
1162
|
-
self,
|
|
1163
|
-
request: SearchAgentPlaybookRequest,
|
|
1164
|
-
options: SearchOptions | None = None,
|
|
1165
|
-
) -> list[AgentPlaybook]:
|
|
1166
|
-
query = request.query
|
|
1167
|
-
agent_version = request.agent_version
|
|
1168
|
-
playbook_name = request.playbook_name
|
|
1169
|
-
start_time = int(request.start_time.timestamp()) if request.start_time else None
|
|
1170
|
-
end_time = int(request.end_time.timestamp()) if request.end_time else None
|
|
1171
|
-
status_filter = request.status_filter
|
|
1172
|
-
playbook_status_filter = request.playbook_status_filter
|
|
1173
|
-
match_count = request.top_k or 10
|
|
1174
|
-
query_embedding = options.query_embedding if options else None
|
|
1175
|
-
mode = _effective_search_mode(
|
|
1176
|
-
request.search_mode, query_embedding, request.query
|
|
1177
|
-
)
|
|
1178
|
-
rrf_k = options.rrf_k if options else 60
|
|
1179
|
-
vector_weight = options.vector_weight if options else 1.0
|
|
1180
|
-
fts_weight = options.fts_weight if options else 1.0
|
|
1181
|
-
|
|
1182
|
-
conditions: list[str] = []
|
|
1183
|
-
params: list[Any] = []
|
|
1184
|
-
|
|
1185
|
-
if agent_version:
|
|
1186
|
-
conditions.append("ap.agent_version = ?")
|
|
1187
|
-
params.append(agent_version)
|
|
1188
|
-
if playbook_name:
|
|
1189
|
-
conditions.append("ap.playbook_name = ?")
|
|
1190
|
-
params.append(playbook_name)
|
|
1191
|
-
if start_time:
|
|
1192
|
-
conditions.append("ap.created_at >= ?")
|
|
1193
|
-
params.append(_epoch_to_iso(start_time))
|
|
1194
|
-
if end_time:
|
|
1195
|
-
conditions.append("ap.created_at <= ?")
|
|
1196
|
-
params.append(_epoch_to_iso(end_time))
|
|
1197
|
-
if playbook_status_filter is not None:
|
|
1198
|
-
if isinstance(playbook_status_filter, list):
|
|
1199
|
-
if not playbook_status_filter:
|
|
1200
|
-
conditions.append("1=0")
|
|
1201
|
-
else:
|
|
1202
|
-
placeholders = ",".join("?" for _ in playbook_status_filter)
|
|
1203
|
-
conditions.append(f"ap.playbook_status IN ({placeholders})")
|
|
1204
|
-
params.extend(s.value for s in playbook_status_filter)
|
|
1205
|
-
else:
|
|
1206
|
-
conditions.append("ap.playbook_status = ?")
|
|
1207
|
-
params.append(playbook_status_filter.value)
|
|
1208
|
-
if status_filter is not None:
|
|
1209
|
-
frag, sparams = _build_status_sql(status_filter)
|
|
1210
|
-
conditions.append(frag)
|
|
1211
|
-
params.extend(sparams)
|
|
1212
|
-
|
|
1213
|
-
where_extra = (" AND " + " AND ".join(conditions)) if conditions else ""
|
|
1214
|
-
overfetch = match_count * 5 if mode != SearchMode.FTS else match_count
|
|
1215
|
-
|
|
1216
|
-
# Pure vector search: fetch all candidates, rank by cosine similarity
|
|
1217
|
-
if mode == SearchMode.VECTOR and query_embedding:
|
|
1218
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else ""
|
|
1219
|
-
sql = f"""SELECT * FROM agent_playbooks ap
|
|
1220
|
-
{base_where}
|
|
1221
|
-
ORDER BY ap.created_at DESC"""
|
|
1222
|
-
rows = self._fetchall(sql, params)
|
|
1223
|
-
rows = _vector_rank_rows(rows, query_embedding, match_count)
|
|
1224
|
-
return [_row_to_agent_playbook(r) for r in rows]
|
|
1225
|
-
|
|
1226
|
-
if query:
|
|
1227
|
-
fts_query = _sanitize_fts_query(query)
|
|
1228
|
-
sql = f"""SELECT ap.* FROM agent_playbooks ap
|
|
1229
|
-
JOIN agent_playbooks_fts f ON ap.agent_playbook_id = f.rowid
|
|
1230
|
-
WHERE agent_playbooks_fts MATCH ?{where_extra}
|
|
1231
|
-
ORDER BY bm25(agent_playbooks_fts, 1.0)
|
|
1232
|
-
LIMIT ?"""
|
|
1233
|
-
fts_rows = self._fetchall(sql, [fts_query, *params, overfetch])
|
|
1234
|
-
|
|
1235
|
-
if mode == SearchMode.HYBRID and query_embedding:
|
|
1236
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else ""
|
|
1237
|
-
vec_limit = match_count * 10
|
|
1238
|
-
vec_sql = f"""SELECT * FROM agent_playbooks ap
|
|
1239
|
-
{base_where}
|
|
1240
|
-
ORDER BY ap.created_at DESC
|
|
1241
|
-
LIMIT ?"""
|
|
1242
|
-
vec_candidates = self._fetchall(vec_sql, [*params, vec_limit])
|
|
1243
|
-
vec_rows = _vector_rank_rows(vec_candidates, query_embedding, overfetch)
|
|
1244
|
-
rows = _true_rrf_merge(
|
|
1245
|
-
fts_rows,
|
|
1246
|
-
vec_rows,
|
|
1247
|
-
"agent_playbook_id",
|
|
1248
|
-
match_count,
|
|
1249
|
-
rrf_k,
|
|
1250
|
-
vector_weight,
|
|
1251
|
-
fts_weight,
|
|
1252
|
-
)
|
|
1253
|
-
return [_row_to_agent_playbook(r) for r in rows]
|
|
1254
|
-
return [_row_to_agent_playbook(r) for r in fts_rows[:match_count]]
|
|
1255
|
-
|
|
1256
|
-
# HYBRID without query text: rank by embedding only
|
|
1257
|
-
if query_embedding:
|
|
1258
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else ""
|
|
1259
|
-
sql = f"""SELECT * FROM agent_playbooks ap
|
|
1260
|
-
{base_where}
|
|
1261
|
-
ORDER BY ap.created_at DESC"""
|
|
1262
|
-
rows = self._fetchall(sql, params)
|
|
1263
|
-
rows = _vector_rank_rows(rows, query_embedding, match_count)
|
|
1264
|
-
return [_row_to_agent_playbook(r) for r in rows]
|
|
1265
|
-
|
|
1266
|
-
# No query text, no embedding -- recency fallback
|
|
1267
|
-
base_where = "WHERE " + " AND ".join(conditions) if conditions else "WHERE 1=1"
|
|
1268
|
-
sql = f"""SELECT * FROM agent_playbooks ap
|
|
1269
|
-
{base_where}
|
|
1270
|
-
ORDER BY ap.created_at DESC LIMIT ?"""
|
|
1271
|
-
params.append(match_count)
|
|
1272
|
-
rows = self._fetchall(sql, params)
|
|
1273
|
-
return [_row_to_agent_playbook(r) for r in rows]
|
|
1274
|
-
|
|
1275
|
-
# ------------------------------------------------------------------
|
|
1276
|
-
# Agent Success Evaluation methods
|
|
1277
|
-
# ------------------------------------------------------------------
|
|
1278
|
-
|
|
1279
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1280
|
-
def save_agent_success_evaluation_results(
|
|
1281
|
-
self, results: list[AgentSuccessEvaluationResult]
|
|
1282
|
-
) -> None:
|
|
1283
|
-
for result in results:
|
|
1284
|
-
embedding_text = f"{result.failure_type} {result.failure_reason}"
|
|
1285
|
-
if embedding_text.strip():
|
|
1286
|
-
result.embedding = self._get_embedding(embedding_text)
|
|
1287
|
-
else:
|
|
1288
|
-
result.embedding = []
|
|
1289
|
-
|
|
1290
|
-
created_at_iso = _epoch_to_iso(result.created_at)
|
|
1291
|
-
self._execute(
|
|
1292
|
-
"""INSERT INTO agent_success_evaluation_result
|
|
1293
|
-
(session_id, agent_version, evaluation_name, is_success,
|
|
1294
|
-
failure_type, failure_reason, regular_vs_shadow,
|
|
1295
|
-
number_of_correction_per_session, user_turns_to_resolution,
|
|
1296
|
-
is_escalated, embedding, created_at)
|
|
1297
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
|
|
1298
|
-
(
|
|
1299
|
-
result.session_id,
|
|
1300
|
-
result.agent_version,
|
|
1301
|
-
result.evaluation_name,
|
|
1302
|
-
int(result.is_success),
|
|
1303
|
-
result.failure_type,
|
|
1304
|
-
result.failure_reason,
|
|
1305
|
-
result.regular_vs_shadow.value
|
|
1306
|
-
if result.regular_vs_shadow
|
|
1307
|
-
else None,
|
|
1308
|
-
result.number_of_correction_per_session,
|
|
1309
|
-
result.user_turns_to_resolution,
|
|
1310
|
-
int(result.is_escalated),
|
|
1311
|
-
_json_dumps(result.embedding) if result.embedding else None,
|
|
1312
|
-
created_at_iso,
|
|
1313
|
-
),
|
|
1314
|
-
)
|
|
1315
|
-
|
|
1316
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1317
|
-
def get_agent_success_evaluation_results(
|
|
1318
|
-
self, limit: int = 100, agent_version: str | None = None
|
|
1319
|
-
) -> list[AgentSuccessEvaluationResult]:
|
|
1320
|
-
sql = "SELECT * FROM agent_success_evaluation_result"
|
|
1321
|
-
params: list[Any] = []
|
|
1322
|
-
if agent_version is not None:
|
|
1323
|
-
sql += " WHERE agent_version = ?"
|
|
1324
|
-
params.append(agent_version)
|
|
1325
|
-
sql += " ORDER BY created_at DESC LIMIT ?"
|
|
1326
|
-
params.append(limit)
|
|
1327
|
-
rows = self._fetchall(sql, params)
|
|
1328
|
-
return [_row_to_eval_result(r) for r in rows]
|
|
1329
|
-
|
|
1330
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1331
|
-
def delete_all_agent_success_evaluation_results(self) -> None:
|
|
1332
|
-
self._execute("DELETE FROM agent_success_evaluation_result")
|
|
1333
|
-
|
|
1334
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1335
|
-
def delete_agent_success_evaluation_results_for_session(
|
|
1336
|
-
self,
|
|
1337
|
-
session_id: str,
|
|
1338
|
-
evaluation_name: str,
|
|
1339
|
-
agent_version: str,
|
|
1340
|
-
) -> int:
|
|
1341
|
-
"""Delete results scoped to (session_id, evaluation_name, agent_version).
|
|
1342
|
-
|
|
1343
|
-
Args:
|
|
1344
|
-
session_id (str): Session whose results to clear.
|
|
1345
|
-
evaluation_name (str): Which evaluator's results to clear.
|
|
1346
|
-
agent_version (str): Agent version scope.
|
|
1347
|
-
|
|
1348
|
-
Returns:
|
|
1349
|
-
int: Number of rows deleted.
|
|
1350
|
-
"""
|
|
1351
|
-
cur = self._execute(
|
|
1352
|
-
"""DELETE FROM agent_success_evaluation_result
|
|
1353
|
-
WHERE session_id = ? AND evaluation_name = ? AND agent_version = ?""",
|
|
1354
|
-
(session_id, evaluation_name, agent_version),
|
|
1355
|
-
)
|
|
1356
|
-
return cur.rowcount
|
|
1357
|
-
|
|
1358
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1359
|
-
def delete_agent_success_evaluation_results_by_ids(
|
|
1360
|
-
self, result_ids: list[int]
|
|
1361
|
-
) -> int:
|
|
1362
|
-
"""Delete agent success eval result rows by primary key.
|
|
1363
|
-
|
|
1364
|
-
Args:
|
|
1365
|
-
result_ids (list[int]): Primary-key result_ids to delete. An empty
|
|
1366
|
-
list is a no-op that returns 0.
|
|
1367
|
-
|
|
1368
|
-
Returns:
|
|
1369
|
-
int: Number of rows actually deleted (ignores non-existent ids).
|
|
1370
|
-
"""
|
|
1371
|
-
if not result_ids:
|
|
1372
|
-
return 0
|
|
1373
|
-
placeholders = ",".join(["?"] * len(result_ids))
|
|
1374
|
-
cur = self._execute(
|
|
1375
|
-
f"DELETE FROM agent_success_evaluation_result WHERE result_id IN ({placeholders})",
|
|
1376
|
-
list(result_ids),
|
|
1377
|
-
)
|
|
1378
|
-
return cur.rowcount
|