claude-smart 0.2.41 → 0.2.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +17 -0
- package/README.md +1 -1
- package/bin/claude-smart.js +86 -48
- package/package.json +10 -3
- package/plugin/.claude-plugin/plugin.json +9 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +2 -2
- package/plugin/dashboard/next.config.ts +9 -1
- package/plugin/pyproject.toml +2 -2
- package/plugin/scripts/_lib.sh +91 -0
- package/plugin/scripts/backend-service.sh +46 -15
- package/plugin/scripts/cli.sh +29 -1
- package/plugin/scripts/codex-hook.js +72 -4
- package/plugin/scripts/dashboard-build.sh +1 -0
- package/plugin/scripts/dashboard-service.sh +1 -0
- package/plugin/scripts/ensure-plugin-root.sh +7 -14
- package/plugin/scripts/hook_entry.sh +1 -0
- package/plugin/scripts/smart-install.sh +18 -2
- package/plugin/src/claude_smart/cli.py +72 -38
- package/plugin/src/claude_smart/context_format.py +11 -12
- package/plugin/src/claude_smart/cs_cite.py +26 -12
- package/plugin/src/claude_smart/ids.py +13 -5
- package/plugin/uv.lock +1 -1
- package/plugin/vendor/reflexio/.env.example +53 -0
- package/plugin/vendor/reflexio/LICENSE +201 -0
- package/plugin/vendor/reflexio/README.md +338 -0
- package/plugin/vendor/reflexio/pyproject.toml +271 -0
- package/plugin/vendor/reflexio/reflexio/README.md +184 -0
- package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
- package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
- package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
- package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
- package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
- package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
- package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
- package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
- package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
- package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
- package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
- package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
- package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
- package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
- package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
- package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
- package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
- package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
- package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
- package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
- package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +540 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
- package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +110 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1595 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +255 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +177 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +699 -0
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
- package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
- package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
- package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"""Plan-level invariants for the agentic-v2 extraction pipeline.
|
|
2
|
+
|
|
3
|
+
Invariants are pure functions over ``ExtractionCtx``. Hard violations drop
|
|
4
|
+
offending ops from the commit; soft violations are logged and applied.
|
|
5
|
+
See spec §6 for the full catalog and severity policy.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from reflexio.server.services.extraction.plan import (
|
|
13
|
+
CommitResult,
|
|
14
|
+
CreateUserPlaybookOp,
|
|
15
|
+
CreateUserProfileOp,
|
|
16
|
+
DeleteUserPlaybookOp,
|
|
17
|
+
DeleteUserProfileOp,
|
|
18
|
+
ExtractionCtx,
|
|
19
|
+
Violation,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
PLAN_SIZE_CAP = 30
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# --- Hard invariants ---
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def inv_A_search_before_create(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
31
|
+
"""Every CreateOp must be preceded by ≥1 search_* call this run."""
|
|
32
|
+
create_indices = [
|
|
33
|
+
i
|
|
34
|
+
for i, op in enumerate(ctx.plan)
|
|
35
|
+
if isinstance(op, (CreateUserProfileOp, CreateUserPlaybookOp))
|
|
36
|
+
]
|
|
37
|
+
if create_indices and ctx.search_count == 0:
|
|
38
|
+
return [
|
|
39
|
+
Violation(
|
|
40
|
+
code="A",
|
|
41
|
+
severity="hard",
|
|
42
|
+
affected_op_indices=create_indices,
|
|
43
|
+
msg="Plan has create ops but no search was performed this run",
|
|
44
|
+
)
|
|
45
|
+
]
|
|
46
|
+
return []
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def inv_B_delete_known_id(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
50
|
+
"""Every DeleteOp(id) must reference an id in ctx.known_ids.
|
|
51
|
+
|
|
52
|
+
known_ids is populated by search/get/create tool handlers — so deletes
|
|
53
|
+
targeting hallucinated ids (agent never saw them) are rejected.
|
|
54
|
+
"""
|
|
55
|
+
violations: list[Violation] = []
|
|
56
|
+
for i, op in enumerate(ctx.plan):
|
|
57
|
+
if (
|
|
58
|
+
isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp))
|
|
59
|
+
and op.id not in ctx.known_ids
|
|
60
|
+
):
|
|
61
|
+
violations.append(
|
|
62
|
+
Violation(
|
|
63
|
+
code="B",
|
|
64
|
+
severity="hard",
|
|
65
|
+
affected_op_indices=[i],
|
|
66
|
+
msg=f"Delete of unknown id {op.id!r}",
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
return violations
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def inv_D_plan_size_cap(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
73
|
+
"""Plan cannot exceed PLAN_SIZE_CAP ops — guards runaway loops."""
|
|
74
|
+
if len(ctx.plan) > PLAN_SIZE_CAP:
|
|
75
|
+
overflow = list(range(PLAN_SIZE_CAP, len(ctx.plan)))
|
|
76
|
+
return [
|
|
77
|
+
Violation(
|
|
78
|
+
code="D",
|
|
79
|
+
severity="hard",
|
|
80
|
+
affected_op_indices=overflow,
|
|
81
|
+
msg=f"Plan size {len(ctx.plan)} exceeds cap {PLAN_SIZE_CAP}",
|
|
82
|
+
)
|
|
83
|
+
]
|
|
84
|
+
return []
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def inv_F_no_duplicate_deletes(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
88
|
+
"""Same id cannot be deleted twice in one plan."""
|
|
89
|
+
seen: set[str] = set()
|
|
90
|
+
violations: list[Violation] = []
|
|
91
|
+
for i, op in enumerate(ctx.plan):
|
|
92
|
+
if isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp)):
|
|
93
|
+
if op.id in seen:
|
|
94
|
+
violations.append(
|
|
95
|
+
Violation(
|
|
96
|
+
code="F",
|
|
97
|
+
severity="hard",
|
|
98
|
+
affected_op_indices=[i],
|
|
99
|
+
msg=f"Duplicate delete of id {op.id!r}",
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
else:
|
|
103
|
+
seen.add(op.id)
|
|
104
|
+
return violations
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def inv_J_scope_match(_ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
108
|
+
"""User_id scope is primarily enforced at the storage layer (handlers inject
|
|
109
|
+
ctx.user_id). This invariant is a placeholder for future cross-user checks;
|
|
110
|
+
for v1 it is a no-op."""
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
HARD_INVARIANTS = (
|
|
115
|
+
inv_A_search_before_create,
|
|
116
|
+
inv_B_delete_known_id,
|
|
117
|
+
inv_D_plan_size_cap,
|
|
118
|
+
inv_F_no_duplicate_deletes,
|
|
119
|
+
inv_J_scope_match,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# --- Soft invariants ---
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def inv_E_no_duplicate_creates(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
127
|
+
"""Two CreateOps with identical content in one plan = oscillation smell."""
|
|
128
|
+
seen: dict[str, int] = {}
|
|
129
|
+
violations: list[Violation] = []
|
|
130
|
+
for i, op in enumerate(ctx.plan):
|
|
131
|
+
key = None
|
|
132
|
+
if isinstance(op, CreateUserProfileOp):
|
|
133
|
+
key = f"profile::{op.content}"
|
|
134
|
+
elif isinstance(op, CreateUserPlaybookOp):
|
|
135
|
+
key = f"playbook::{op.trigger}::{op.content}"
|
|
136
|
+
if key is None:
|
|
137
|
+
continue
|
|
138
|
+
if key in seen:
|
|
139
|
+
violations.append(
|
|
140
|
+
Violation(
|
|
141
|
+
code="E",
|
|
142
|
+
severity="soft",
|
|
143
|
+
affected_op_indices=[i],
|
|
144
|
+
msg=f"Duplicate create content at op {i}",
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
seen[key] = i
|
|
149
|
+
return violations
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def inv_H_source_span_present(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
153
|
+
"""CreateOps must have non-whitespace source_span.
|
|
154
|
+
|
|
155
|
+
Schema enforces min_length=1, but whitespace-only slips through —
|
|
156
|
+
this is the secondary guard.
|
|
157
|
+
"""
|
|
158
|
+
violations: list[Violation] = []
|
|
159
|
+
for i, op in enumerate(ctx.plan):
|
|
160
|
+
if (
|
|
161
|
+
isinstance(op, (CreateUserProfileOp, CreateUserPlaybookOp))
|
|
162
|
+
and not op.source_span.strip()
|
|
163
|
+
):
|
|
164
|
+
violations.append(
|
|
165
|
+
Violation(
|
|
166
|
+
code="H",
|
|
167
|
+
severity="soft",
|
|
168
|
+
affected_op_indices=[i],
|
|
169
|
+
msg=f"Empty/whitespace source_span on create op {i}",
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
return violations
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def inv_K_deletes_without_creates(ctx: ExtractionCtx) -> list[Violation]: # noqa: N802
|
|
176
|
+
"""Plan with deletes but no creates is unusual — worth logging."""
|
|
177
|
+
has_delete = any(
|
|
178
|
+
isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp)) for op in ctx.plan
|
|
179
|
+
)
|
|
180
|
+
has_create = any(
|
|
181
|
+
isinstance(op, (CreateUserProfileOp, CreateUserPlaybookOp)) for op in ctx.plan
|
|
182
|
+
)
|
|
183
|
+
if has_delete and not has_create:
|
|
184
|
+
indices = [
|
|
185
|
+
i
|
|
186
|
+
for i, op in enumerate(ctx.plan)
|
|
187
|
+
if isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp))
|
|
188
|
+
]
|
|
189
|
+
return [
|
|
190
|
+
Violation(
|
|
191
|
+
code="K",
|
|
192
|
+
severity="soft",
|
|
193
|
+
affected_op_indices=indices,
|
|
194
|
+
msg="Plan contains deletes without any matching creates",
|
|
195
|
+
)
|
|
196
|
+
]
|
|
197
|
+
return []
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
SOFT_INVARIANTS = (
|
|
201
|
+
inv_E_no_duplicate_creates,
|
|
202
|
+
inv_H_source_span_present,
|
|
203
|
+
inv_K_deletes_without_creates,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# --- Oscillation resolver ---
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def resolve_tentative_oscillations(plan: list) -> set[int]:
|
|
211
|
+
"""Return plan indices to drop: create+delete-tentative pairs cancel.
|
|
212
|
+
|
|
213
|
+
When the agent creates an entity (issuing a tentative_id) and later
|
|
214
|
+
deletes that same tentative_id within the same plan, both ops are
|
|
215
|
+
dropped before invariants fire. This is the "oscillated self-correction"
|
|
216
|
+
pattern — the agent changed its mind mid-run.
|
|
217
|
+
|
|
218
|
+
The tentative_id format is ``tentative::<kind>::<plan_index_at_issue_time>``,
|
|
219
|
+
matching ``_next_tentative_id`` in tools.py which uses ``len(ctx.plan)``
|
|
220
|
+
(the plan length BEFORE the op is appended, i.e. the future index of the op).
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
plan: The accumulated list of PlanOp instances from ctx.plan.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Set of plan indices to exclude from apply. Both the create and the
|
|
227
|
+
delete are dropped when a matching pair is found.
|
|
228
|
+
"""
|
|
229
|
+
drop: set[int] = set()
|
|
230
|
+
pending_creates: dict[str, int] = {}
|
|
231
|
+
for i, op in enumerate(plan):
|
|
232
|
+
if isinstance(op, CreateUserProfileOp):
|
|
233
|
+
tentative_id = f"tentative::profile::{i}"
|
|
234
|
+
pending_creates[tentative_id] = i
|
|
235
|
+
elif isinstance(op, CreateUserPlaybookOp):
|
|
236
|
+
tentative_id = f"tentative::user_playbook::{i}"
|
|
237
|
+
pending_creates[tentative_id] = i
|
|
238
|
+
elif isinstance(op, (DeleteUserProfileOp, DeleteUserPlaybookOp)):
|
|
239
|
+
if op.id.startswith("tentative::") and op.id in pending_creates:
|
|
240
|
+
drop.add(pending_creates.pop(op.id))
|
|
241
|
+
drop.add(i)
|
|
242
|
+
return drop
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# --- commit_plan ---
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def commit_plan(
|
|
249
|
+
ctx: ExtractionCtx,
|
|
250
|
+
storage: object,
|
|
251
|
+
*,
|
|
252
|
+
outcome: str, # Literal["finish_tool","max_steps","error"]
|
|
253
|
+
) -> CommitResult:
|
|
254
|
+
"""Run all invariants, then apply surviving ops atomically.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
ctx: Populated ExtractionCtx from the agent loop.
|
|
258
|
+
storage: BaseStorage handle for apply.
|
|
259
|
+
outcome: How the loop terminated.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
CommitResult containing applied ops + all violations (hard + soft).
|
|
263
|
+
"""
|
|
264
|
+
# Error outcome — discard everything, do not apply
|
|
265
|
+
if outcome == "error":
|
|
266
|
+
return CommitResult(applied=[], violations=[], outcome="error")
|
|
267
|
+
|
|
268
|
+
violations: list[Violation] = []
|
|
269
|
+
for check in HARD_INVARIANTS:
|
|
270
|
+
violations.extend(check(ctx))
|
|
271
|
+
for check in SOFT_INVARIANTS:
|
|
272
|
+
violations.extend(check(ctx))
|
|
273
|
+
|
|
274
|
+
dropped: set[int] = set()
|
|
275
|
+
# Oscillation resolver runs first: matching create+delete-tentative pairs
|
|
276
|
+
# cancel before invariants decide what to keep.
|
|
277
|
+
dropped.update(resolve_tentative_oscillations(ctx.plan))
|
|
278
|
+
for v in violations:
|
|
279
|
+
if v.severity == "hard":
|
|
280
|
+
dropped.update(v.affected_op_indices)
|
|
281
|
+
|
|
282
|
+
ops_to_apply = [op for i, op in enumerate(ctx.plan) if i not in dropped]
|
|
283
|
+
|
|
284
|
+
for v in violations:
|
|
285
|
+
logger.info(
|
|
286
|
+
"invariant_violation user_id=%s code=%s severity=%s op_indices=%s msg=%s",
|
|
287
|
+
ctx.user_id,
|
|
288
|
+
v.code,
|
|
289
|
+
v.severity,
|
|
290
|
+
v.affected_op_indices,
|
|
291
|
+
v.msg,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Delegate actual storage writes to the tool-handler module (Task 5 wires this in).
|
|
295
|
+
# Lazy import so Task 3 can land before tools.py exists.
|
|
296
|
+
from reflexio.server.services.extraction.tools import (
|
|
297
|
+
apply_plan_op, # noqa: PLC0415 # type: ignore[import-not-found]
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
for op in ops_to_apply:
|
|
301
|
+
apply_plan_op(op, storage, ctx)
|
|
302
|
+
|
|
303
|
+
return CommitResult(applied=ops_to_apply, violations=violations, outcome=outcome) # type: ignore[arg-type]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Shared extraction outcome types for resumable extraction paths."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class ExtractionOutcome[T]:
|
|
11
|
+
"""Result wrapper returned by extractors that need explicit empty results."""
|
|
12
|
+
|
|
13
|
+
status: Literal["completed", "empty"]
|
|
14
|
+
items: list[T] = field(default_factory=list)
|
|
15
|
+
run_id: str | None = None
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def completed(
|
|
19
|
+
cls, items: list[T], *, run_id: str | None = None
|
|
20
|
+
) -> ExtractionOutcome[T]:
|
|
21
|
+
return cls(status="completed", items=items, run_id=run_id)
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def empty(cls, *, run_id: str | None = None) -> ExtractionOutcome[T]:
|
|
25
|
+
return cls(status="empty", items=[], run_id=run_id)
|
package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Async information tools for resumable extraction runs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import uuid
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from datetime import UTC, datetime, timedelta
|
|
9
|
+
from typing import Annotated, Any
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
from reflexio.models.config_schema import PendingToolCallConfig
|
|
14
|
+
from reflexio.server.llm.tools import (
|
|
15
|
+
AsyncAccepted,
|
|
16
|
+
AsyncInfoTool,
|
|
17
|
+
AsyncRequestSpec,
|
|
18
|
+
Tool,
|
|
19
|
+
)
|
|
20
|
+
from reflexio.server.services.storage.storage_base import (
|
|
21
|
+
BaseStorage,
|
|
22
|
+
PendingToolCallRecord,
|
|
23
|
+
PendingToolCallStatus,
|
|
24
|
+
RunToolDependencyRecord,
|
|
25
|
+
build_pending_tool_call_dedup_key,
|
|
26
|
+
build_scope_hash,
|
|
27
|
+
human_feedback_scope,
|
|
28
|
+
)
|
|
29
|
+
from reflexio.server.usage_metrics import record_usage_event
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AskHumanArgs(BaseModel):
|
|
35
|
+
"""Ask Agent Builder for missing extraction information and continue now."""
|
|
36
|
+
|
|
37
|
+
question: Annotated[str, Field(min_length=1)]
|
|
38
|
+
answer_format: str | None = None
|
|
39
|
+
tags: list[str] = Field(default_factory=list)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AttachPendingInfoRequestArgs(BaseModel):
|
|
43
|
+
"""Attach this run to a relevant pending Prior Knowledge request."""
|
|
44
|
+
|
|
45
|
+
pending_tool_call_id: Annotated[str, Field(min_length=1)]
|
|
46
|
+
why_relevant: str | None = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class PendingToolCallDispatcher:
|
|
50
|
+
"""Dispatch newly-created pending tool calls to an external channel."""
|
|
51
|
+
|
|
52
|
+
def dispatch(self, record: PendingToolCallRecord) -> None:
|
|
53
|
+
raise NotImplementedError
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class NoopPendingToolCallDispatcher(PendingToolCallDispatcher):
|
|
57
|
+
"""Local/test dispatcher that intentionally does nothing."""
|
|
58
|
+
|
|
59
|
+
def dispatch(self, _record: PendingToolCallRecord) -> None:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(slots=True)
|
|
64
|
+
class PendingToolCallToolContext:
|
|
65
|
+
storage: BaseStorage
|
|
66
|
+
run_id: str
|
|
67
|
+
org_id: str
|
|
68
|
+
extractor_kind: str
|
|
69
|
+
user_id: str | None = None
|
|
70
|
+
config: PendingToolCallConfig = field(default_factory=PendingToolCallConfig)
|
|
71
|
+
dispatcher: PendingToolCallDispatcher = field(
|
|
72
|
+
default_factory=NoopPendingToolCallDispatcher
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _build_async_request_spec(
|
|
77
|
+
*,
|
|
78
|
+
args: AskHumanArgs,
|
|
79
|
+
ctx: PendingToolCallToolContext,
|
|
80
|
+
) -> AsyncRequestSpec:
|
|
81
|
+
tool_config = ctx.config.for_tool("ask_human")
|
|
82
|
+
scope = human_feedback_scope(ctx.org_id)
|
|
83
|
+
return AsyncRequestSpec(
|
|
84
|
+
tool_name="ask_human",
|
|
85
|
+
dedup_key=build_pending_tool_call_dedup_key(
|
|
86
|
+
tool_name="ask_human",
|
|
87
|
+
question_text=args.question,
|
|
88
|
+
answer_format=args.answer_format,
|
|
89
|
+
),
|
|
90
|
+
scope=scope,
|
|
91
|
+
question_text=args.question,
|
|
92
|
+
answer_format=args.answer_format,
|
|
93
|
+
args={
|
|
94
|
+
"question": args.question,
|
|
95
|
+
"answer_format": args.answer_format,
|
|
96
|
+
},
|
|
97
|
+
tags=args.tags,
|
|
98
|
+
cache_until_seconds=tool_config.dedup_cache_seconds,
|
|
99
|
+
valid_until_seconds=tool_config.prior_answer_valid_seconds,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _question_embedding(
|
|
104
|
+
storage: BaseStorage,
|
|
105
|
+
question_text: str,
|
|
106
|
+
) -> list[float] | None:
|
|
107
|
+
get_embedding = getattr(storage, "_get_embedding", None)
|
|
108
|
+
if not callable(get_embedding):
|
|
109
|
+
return None
|
|
110
|
+
try:
|
|
111
|
+
embedding = get_embedding(question_text, purpose="document")
|
|
112
|
+
except TypeError:
|
|
113
|
+
embedding = get_embedding(question_text)
|
|
114
|
+
except Exception as exc: # pragma: no cover - backend logs details elsewhere
|
|
115
|
+
logger.warning(
|
|
116
|
+
"event=pending_tool_call_embedding_failed error_type=%s error=%s",
|
|
117
|
+
type(exc).__name__,
|
|
118
|
+
exc,
|
|
119
|
+
)
|
|
120
|
+
return None
|
|
121
|
+
return embedding if isinstance(embedding, list) else None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def handle_ask_human(
|
|
125
|
+
args: AskHumanArgs,
|
|
126
|
+
ctx: PendingToolCallToolContext,
|
|
127
|
+
) -> AsyncAccepted:
|
|
128
|
+
if not ctx.run_id:
|
|
129
|
+
raise ValueError("ask_human requires a durable agent run_id")
|
|
130
|
+
|
|
131
|
+
current = datetime.now(UTC)
|
|
132
|
+
spec = _build_async_request_spec(args=args, ctx=ctx)
|
|
133
|
+
tool_config = ctx.config.for_tool(spec.tool_name)
|
|
134
|
+
# Observational soft cap only: intentionally read OUTSIDE the create/attach
|
|
135
|
+
# transaction. It never blocks or fails extraction (a small count race here
|
|
136
|
+
# would at most over/under-log the warning by one), so it does not need the
|
|
137
|
+
# per-scope transaction boundary that the dedup insert/attach uses.
|
|
138
|
+
unresolved_count = ctx.storage.count_unresolved_followup_dependencies(
|
|
139
|
+
org_id=ctx.org_id,
|
|
140
|
+
extractor_kind=ctx.extractor_kind,
|
|
141
|
+
tool_name=spec.tool_name,
|
|
142
|
+
)
|
|
143
|
+
if unresolved_count >= tool_config.max_pending_followups_per_scope:
|
|
144
|
+
logger.warning(
|
|
145
|
+
"event=pending_followup_soft_cap_exceeded org_id=%s extractor_kind=%s "
|
|
146
|
+
"tool_name=%s current_count=%d soft_cap=%d",
|
|
147
|
+
ctx.org_id,
|
|
148
|
+
ctx.extractor_kind,
|
|
149
|
+
spec.tool_name,
|
|
150
|
+
unresolved_count,
|
|
151
|
+
tool_config.max_pending_followups_per_scope,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
record = PendingToolCallRecord(
|
|
155
|
+
id=f"ptc_{uuid.uuid4().hex}",
|
|
156
|
+
org_id=ctx.org_id,
|
|
157
|
+
user_id=ctx.user_id,
|
|
158
|
+
scope=spec.scope,
|
|
159
|
+
scope_hash=build_scope_hash(spec.scope),
|
|
160
|
+
tool_name=spec.tool_name,
|
|
161
|
+
dedup_key=spec.dedup_key,
|
|
162
|
+
status=PendingToolCallStatus.PENDING,
|
|
163
|
+
question_text=spec.question_text,
|
|
164
|
+
args=spec.args,
|
|
165
|
+
tags=spec.tags,
|
|
166
|
+
answer_format=spec.answer_format,
|
|
167
|
+
embedding=_question_embedding(ctx.storage, spec.question_text),
|
|
168
|
+
expires_at=current + timedelta(seconds=tool_config.pending_ttl_seconds),
|
|
169
|
+
cache_until=current + timedelta(seconds=spec.cache_until_seconds),
|
|
170
|
+
)
|
|
171
|
+
upsert = ctx.storage.create_or_attach_pending_tool_call(
|
|
172
|
+
record=record,
|
|
173
|
+
dependency=RunToolDependencyRecord(
|
|
174
|
+
run_id=ctx.run_id,
|
|
175
|
+
pending_tool_call_id=record.id,
|
|
176
|
+
),
|
|
177
|
+
now=current,
|
|
178
|
+
)
|
|
179
|
+
record_usage_event(
|
|
180
|
+
org_id=ctx.org_id,
|
|
181
|
+
event_name="extraction_agent_async_info_requested",
|
|
182
|
+
event_category="extraction_agent",
|
|
183
|
+
user_id=ctx.user_id,
|
|
184
|
+
pipeline=ctx.extractor_kind,
|
|
185
|
+
outcome="created" if upsert.created else "deduped",
|
|
186
|
+
metadata={
|
|
187
|
+
"run_id": ctx.run_id,
|
|
188
|
+
"pending_tool_call_id": upsert.pending_tool_call.id,
|
|
189
|
+
"tool_name": upsert.pending_tool_call.tool_name,
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
if upsert.created:
|
|
193
|
+
ctx.dispatcher.dispatch(upsert.pending_tool_call)
|
|
194
|
+
logger.info(
|
|
195
|
+
"event=pending_tool_call_created org_id=%s user_id=%s "
|
|
196
|
+
"extractor_kind=%s run_id=%s pending_tool_call_id=%s "
|
|
197
|
+
"tool_name=%s",
|
|
198
|
+
ctx.org_id,
|
|
199
|
+
ctx.user_id,
|
|
200
|
+
ctx.extractor_kind,
|
|
201
|
+
ctx.run_id,
|
|
202
|
+
upsert.pending_tool_call.id,
|
|
203
|
+
upsert.pending_tool_call.tool_name,
|
|
204
|
+
)
|
|
205
|
+
else:
|
|
206
|
+
logger.info(
|
|
207
|
+
"event=extraction_agent_followup_attached org_id=%s user_id=%s "
|
|
208
|
+
"extractor_kind=%s run_id=%s pending_tool_call_id=%s "
|
|
209
|
+
"tool_name=%s deduped=true",
|
|
210
|
+
ctx.org_id,
|
|
211
|
+
ctx.user_id,
|
|
212
|
+
ctx.extractor_kind,
|
|
213
|
+
ctx.run_id,
|
|
214
|
+
upsert.pending_tool_call.id,
|
|
215
|
+
upsert.pending_tool_call.tool_name,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
return AsyncAccepted(
|
|
219
|
+
pending_tool_call_id=upsert.pending_tool_call.id,
|
|
220
|
+
result={
|
|
221
|
+
"status": "request_pending",
|
|
222
|
+
"pending_tool_call_id": upsert.pending_tool_call.id,
|
|
223
|
+
"instruction": (
|
|
224
|
+
"Continue extraction using currently available evidence. "
|
|
225
|
+
"This run will be resumed when the answer arrives."
|
|
226
|
+
),
|
|
227
|
+
},
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def handle_attach_pending_info_request(
|
|
232
|
+
args: AttachPendingInfoRequestArgs,
|
|
233
|
+
ctx: PendingToolCallToolContext,
|
|
234
|
+
) -> dict[str, Any]:
|
|
235
|
+
if not ctx.run_id:
|
|
236
|
+
raise ValueError("attach_pending_info_request requires a durable agent run_id")
|
|
237
|
+
|
|
238
|
+
record = ctx.storage.get_pending_tool_call(args.pending_tool_call_id)
|
|
239
|
+
expected_scope = human_feedback_scope(ctx.org_id)
|
|
240
|
+
current = datetime.now(UTC)
|
|
241
|
+
if (
|
|
242
|
+
record is None
|
|
243
|
+
or record.org_id != ctx.org_id
|
|
244
|
+
or record.tool_name != "ask_human"
|
|
245
|
+
or record.scope_hash != build_scope_hash(expected_scope)
|
|
246
|
+
):
|
|
247
|
+
return {
|
|
248
|
+
"status": "not_found",
|
|
249
|
+
"instruction": (
|
|
250
|
+
"The referenced Prior Knowledge request is unavailable. "
|
|
251
|
+
"Continue extraction, or call ask_human if the information is still needed."
|
|
252
|
+
),
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if record.status == PendingToolCallStatus.RESOLVED:
|
|
256
|
+
if record.valid_until is not None and record.valid_until <= current:
|
|
257
|
+
return {
|
|
258
|
+
"status": "expired",
|
|
259
|
+
"instruction": (
|
|
260
|
+
"The referenced answer is no longer valid. Continue extraction, "
|
|
261
|
+
"or call ask_human if the information is still needed."
|
|
262
|
+
),
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
"status": "already_resolved",
|
|
266
|
+
"pending_tool_call_id": record.id,
|
|
267
|
+
"question": record.question_text,
|
|
268
|
+
"result": record.result or {},
|
|
269
|
+
"instruction": (
|
|
270
|
+
"Use this result only if it is relevant to the current extraction."
|
|
271
|
+
),
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if record.status != PendingToolCallStatus.PENDING or (
|
|
275
|
+
record.expires_at is not None and record.expires_at <= current
|
|
276
|
+
):
|
|
277
|
+
return {
|
|
278
|
+
"status": "unavailable",
|
|
279
|
+
"instruction": (
|
|
280
|
+
"The referenced request is no longer pending. Continue extraction, "
|
|
281
|
+
"or call ask_human if the information is still needed."
|
|
282
|
+
),
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
ctx.storage.attach_run_tool_dependency(
|
|
286
|
+
RunToolDependencyRecord(
|
|
287
|
+
run_id=ctx.run_id,
|
|
288
|
+
pending_tool_call_id=record.id,
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
logger.info(
|
|
292
|
+
"event=extraction_agent_followup_attached org_id=%s user_id=%s "
|
|
293
|
+
"extractor_kind=%s run_id=%s pending_tool_call_id=%s "
|
|
294
|
+
"tool_name=%s",
|
|
295
|
+
ctx.org_id,
|
|
296
|
+
ctx.user_id,
|
|
297
|
+
ctx.extractor_kind,
|
|
298
|
+
ctx.run_id,
|
|
299
|
+
record.id,
|
|
300
|
+
record.tool_name,
|
|
301
|
+
)
|
|
302
|
+
return {
|
|
303
|
+
"status": "attached_for_followup",
|
|
304
|
+
"pending_tool_call_id": record.id,
|
|
305
|
+
"instruction": (
|
|
306
|
+
"Continue extraction using currently available evidence. "
|
|
307
|
+
"This run will be resumed when the pending answer arrives."
|
|
308
|
+
),
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _handle_ask_human_tool(args: BaseModel, ctx: Any) -> AsyncAccepted:
|
|
313
|
+
if not isinstance(args, AskHumanArgs):
|
|
314
|
+
raise TypeError(f"Expected AskHumanArgs, got {type(args).__name__}")
|
|
315
|
+
ctx = getattr(ctx, "extra_tool_context", ctx)
|
|
316
|
+
if not isinstance(ctx, PendingToolCallToolContext):
|
|
317
|
+
raise TypeError(
|
|
318
|
+
f"Expected PendingToolCallToolContext, got {type(ctx).__name__}"
|
|
319
|
+
)
|
|
320
|
+
return handle_ask_human(args, ctx)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _handle_attach_pending_info_request_tool(
|
|
324
|
+
args: BaseModel, ctx: Any
|
|
325
|
+
) -> dict[str, Any]:
|
|
326
|
+
if not isinstance(args, AttachPendingInfoRequestArgs):
|
|
327
|
+
raise TypeError(
|
|
328
|
+
f"Expected AttachPendingInfoRequestArgs, got {type(args).__name__}"
|
|
329
|
+
)
|
|
330
|
+
ctx = getattr(ctx, "extra_tool_context", ctx)
|
|
331
|
+
if not isinstance(ctx, PendingToolCallToolContext):
|
|
332
|
+
raise TypeError(
|
|
333
|
+
f"Expected PendingToolCallToolContext, got {type(ctx).__name__}"
|
|
334
|
+
)
|
|
335
|
+
return handle_attach_pending_info_request(args, ctx)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def create_ask_human_tool() -> AsyncInfoTool:
|
|
339
|
+
return AsyncInfoTool(
|
|
340
|
+
name="ask_human",
|
|
341
|
+
args_model=AskHumanArgs,
|
|
342
|
+
handler=_handle_ask_human_tool,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def create_attach_pending_info_request_tool() -> Tool:
|
|
347
|
+
return Tool(
|
|
348
|
+
name="attach_pending_info_request",
|
|
349
|
+
args_model=AttachPendingInfoRequestArgs,
|
|
350
|
+
handler=_handle_attach_pending_info_request_tool,
|
|
351
|
+
)
|