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,9 @@
|
|
|
1
|
+
# Backward compatibility — all models are now organized in domain/ and ui/ subpackages.
|
|
2
|
+
# Import from those packages directly for new code:
|
|
3
|
+
# from reflexio.models.api_schema.domain import Interaction, UserProfile, ...
|
|
4
|
+
# from reflexio.models.api_schema.ui import InteractionView, ProfileView, ...
|
|
5
|
+
|
|
6
|
+
# ruff: noqa: I001 — import order is deliberate: domain enums must shadow ui enums
|
|
7
|
+
from .common import * # noqa: F401, F403
|
|
8
|
+
from .ui import * # noqa: F401, F403
|
|
9
|
+
from .domain import * # noqa: F401, F403
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Pydantic models for the stall_state HTTP endpoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
StallReason = Literal["billing_error", "auth_error"]
|
|
11
|
+
"""Canonical stall-reason discriminator.
|
|
12
|
+
|
|
13
|
+
Imported by the storage layer (``sqlite_storage._stall_state``) and the
|
|
14
|
+
LiteLLM provider's stream parser so all three layers share one source of
|
|
15
|
+
truth for the allowed values."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StallStateResponse(BaseModel):
|
|
19
|
+
"""Returned by GET /stall_state."""
|
|
20
|
+
|
|
21
|
+
stalled: bool = Field(..., description="True when learning is currently paused.")
|
|
22
|
+
reason: StallReason | None = None
|
|
23
|
+
stalled_at: datetime | None = None
|
|
24
|
+
reset_estimate: datetime | None = None
|
|
25
|
+
notified_in_cc: bool = False
|
|
26
|
+
error_message: str | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MarkNotifiedResponse(BaseModel):
|
|
30
|
+
"""Returned by POST /stall_state/notified."""
|
|
31
|
+
|
|
32
|
+
notified_in_cc: bool
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Converters from domain models to UI View models.
|
|
2
|
+
|
|
3
|
+
This module bridges the domain layer and UI layer. It imports from both
|
|
4
|
+
domain/entities (input types) and ui/entities (output types).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from ..domain.entities import (
|
|
8
|
+
AgentPlaybook,
|
|
9
|
+
AgentSuccessEvaluationResult,
|
|
10
|
+
Interaction,
|
|
11
|
+
ProfileChangeLog,
|
|
12
|
+
UserPlaybook,
|
|
13
|
+
UserProfile,
|
|
14
|
+
)
|
|
15
|
+
from .entities import (
|
|
16
|
+
AgentPlaybookView,
|
|
17
|
+
EvaluationResultView,
|
|
18
|
+
InteractionView,
|
|
19
|
+
ProfileChangeLogView,
|
|
20
|
+
ProfileView,
|
|
21
|
+
UserPlaybookView,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"to_interaction_view",
|
|
26
|
+
"to_profile_view",
|
|
27
|
+
"to_user_playbook_view",
|
|
28
|
+
"to_agent_playbook_view",
|
|
29
|
+
"to_evaluation_result_view",
|
|
30
|
+
"to_profile_change_log_view",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def to_interaction_view(interaction: Interaction) -> InteractionView:
|
|
35
|
+
"""Convert internal Interaction to user-facing InteractionView.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
interaction (Interaction): Full internal interaction
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
InteractionView: View without embedding and image_encoding
|
|
42
|
+
"""
|
|
43
|
+
return InteractionView(
|
|
44
|
+
interaction_id=interaction.interaction_id,
|
|
45
|
+
user_id=interaction.user_id,
|
|
46
|
+
request_id=interaction.request_id,
|
|
47
|
+
created_at=interaction.created_at,
|
|
48
|
+
role=interaction.role,
|
|
49
|
+
content=interaction.content,
|
|
50
|
+
user_action=interaction.user_action,
|
|
51
|
+
user_action_description=interaction.user_action_description,
|
|
52
|
+
interacted_image_url=interaction.interacted_image_url,
|
|
53
|
+
shadow_content=interaction.shadow_content,
|
|
54
|
+
expert_content=interaction.expert_content,
|
|
55
|
+
tools_used=interaction.tools_used,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def to_profile_view(profile: UserProfile) -> ProfileView:
|
|
60
|
+
"""Convert internal UserProfile to user-facing ProfileView.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
profile (UserProfile): Full internal user profile
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
ProfileView: View without embedding
|
|
67
|
+
"""
|
|
68
|
+
return ProfileView(
|
|
69
|
+
profile_id=profile.profile_id,
|
|
70
|
+
user_id=profile.user_id,
|
|
71
|
+
content=profile.content,
|
|
72
|
+
last_modified_timestamp=profile.last_modified_timestamp,
|
|
73
|
+
generated_from_request_id=profile.generated_from_request_id,
|
|
74
|
+
profile_time_to_live=profile.profile_time_to_live,
|
|
75
|
+
expiration_timestamp=profile.expiration_timestamp,
|
|
76
|
+
custom_features=profile.custom_features,
|
|
77
|
+
source=profile.source,
|
|
78
|
+
status=profile.status,
|
|
79
|
+
extractor_names=profile.extractor_names,
|
|
80
|
+
source_span=profile.source_span,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def to_user_playbook_view(rf: UserPlaybook) -> UserPlaybookView:
|
|
85
|
+
"""Convert internal UserPlaybook to user-facing UserPlaybookView.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
rf (UserPlaybook): Full internal user playbook
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
UserPlaybookView: View without embedding
|
|
92
|
+
"""
|
|
93
|
+
return UserPlaybookView(
|
|
94
|
+
user_playbook_id=rf.user_playbook_id,
|
|
95
|
+
user_id=rf.user_id,
|
|
96
|
+
agent_version=rf.agent_version,
|
|
97
|
+
request_id=rf.request_id,
|
|
98
|
+
playbook_name=rf.playbook_name,
|
|
99
|
+
created_at=rf.created_at,
|
|
100
|
+
content=rf.content,
|
|
101
|
+
trigger=rf.trigger,
|
|
102
|
+
rationale=rf.rationale,
|
|
103
|
+
status=rf.status,
|
|
104
|
+
source=rf.source,
|
|
105
|
+
source_interaction_ids=rf.source_interaction_ids,
|
|
106
|
+
source_span=rf.source_span,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def to_agent_playbook_view(fb: AgentPlaybook) -> AgentPlaybookView:
|
|
111
|
+
"""Convert internal AgentPlaybook to user-facing AgentPlaybookView.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
fb (AgentPlaybook): Full internal agent playbook
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
AgentPlaybookView: View without embedding
|
|
118
|
+
"""
|
|
119
|
+
return AgentPlaybookView(
|
|
120
|
+
agent_playbook_id=fb.agent_playbook_id,
|
|
121
|
+
playbook_name=fb.playbook_name,
|
|
122
|
+
agent_version=fb.agent_version,
|
|
123
|
+
created_at=fb.created_at,
|
|
124
|
+
content=fb.content,
|
|
125
|
+
trigger=fb.trigger,
|
|
126
|
+
rationale=fb.rationale,
|
|
127
|
+
playbook_status=fb.playbook_status,
|
|
128
|
+
playbook_metadata=fb.playbook_metadata,
|
|
129
|
+
status=fb.status,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def to_evaluation_result_view(
|
|
134
|
+
result: AgentSuccessEvaluationResult,
|
|
135
|
+
) -> EvaluationResultView:
|
|
136
|
+
"""Convert internal AgentSuccessEvaluationResult to user-facing EvaluationResultView.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
result (AgentSuccessEvaluationResult): Full internal evaluation result
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
EvaluationResultView: View without embedding
|
|
143
|
+
"""
|
|
144
|
+
return EvaluationResultView(
|
|
145
|
+
result_id=result.result_id,
|
|
146
|
+
agent_version=result.agent_version,
|
|
147
|
+
session_id=result.session_id,
|
|
148
|
+
is_success=result.is_success,
|
|
149
|
+
failure_type=result.failure_type,
|
|
150
|
+
failure_reason=result.failure_reason,
|
|
151
|
+
evaluation_name=result.evaluation_name,
|
|
152
|
+
created_at=result.created_at,
|
|
153
|
+
regular_vs_shadow=result.regular_vs_shadow,
|
|
154
|
+
number_of_correction_per_session=result.number_of_correction_per_session,
|
|
155
|
+
user_turns_to_resolution=result.user_turns_to_resolution,
|
|
156
|
+
is_escalated=result.is_escalated,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def to_profile_change_log_view(log: ProfileChangeLog) -> ProfileChangeLogView:
|
|
161
|
+
"""Convert internal ProfileChangeLog to user-facing ProfileChangeLogView.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
log (ProfileChangeLog): Full internal profile change log
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
ProfileChangeLogView: View with ProfileView lists instead of UserProfile lists
|
|
168
|
+
"""
|
|
169
|
+
return ProfileChangeLogView(
|
|
170
|
+
id=log.id,
|
|
171
|
+
user_id=log.user_id,
|
|
172
|
+
request_id=log.request_id,
|
|
173
|
+
created_at=log.created_at,
|
|
174
|
+
added_profiles=[to_profile_view(p) for p in log.added_profiles],
|
|
175
|
+
removed_profiles=[to_profile_view(p) for p in log.removed_profiles],
|
|
176
|
+
mentioned_profiles=[to_profile_view(p) for p in log.mentioned_profiles],
|
|
177
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field, field_validator
|
|
6
|
+
|
|
7
|
+
from ..common import NEVER_EXPIRES_TIMESTAMP, ToolUsed
|
|
8
|
+
from ..validators import _validate_image_url
|
|
9
|
+
from .enums import (
|
|
10
|
+
PlaybookStatus,
|
|
11
|
+
ProfileTimeToLive,
|
|
12
|
+
RegularVsShadow,
|
|
13
|
+
Status,
|
|
14
|
+
UserActionType,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"InteractionView",
|
|
19
|
+
"ProfileView",
|
|
20
|
+
"UserPlaybookView",
|
|
21
|
+
"AgentPlaybookView",
|
|
22
|
+
"EvaluationResultView",
|
|
23
|
+
"ProfileChangeLogView",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
# ===============================
|
|
27
|
+
# View Models (user-facing, without embeddings)
|
|
28
|
+
# ===============================
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class InteractionView(BaseModel):
|
|
32
|
+
"""User-facing Interaction — excludes embedding and image_encoding."""
|
|
33
|
+
|
|
34
|
+
interaction_id: int = 0
|
|
35
|
+
user_id: str
|
|
36
|
+
request_id: str
|
|
37
|
+
created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
|
|
38
|
+
role: str = "User"
|
|
39
|
+
content: str = ""
|
|
40
|
+
user_action: UserActionType = UserActionType.NONE
|
|
41
|
+
user_action_description: str = ""
|
|
42
|
+
interacted_image_url: str = ""
|
|
43
|
+
shadow_content: str = ""
|
|
44
|
+
expert_content: str = ""
|
|
45
|
+
tools_used: list[ToolUsed] = Field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
@field_validator("interacted_image_url", mode="after")
|
|
48
|
+
@classmethod
|
|
49
|
+
def validate_image_url(cls, v: str) -> str:
|
|
50
|
+
return _validate_image_url(v)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ProfileView(BaseModel):
|
|
54
|
+
"""User-facing UserProfile — excludes embedding."""
|
|
55
|
+
|
|
56
|
+
profile_id: str
|
|
57
|
+
user_id: str
|
|
58
|
+
content: str
|
|
59
|
+
last_modified_timestamp: int
|
|
60
|
+
generated_from_request_id: str
|
|
61
|
+
profile_time_to_live: ProfileTimeToLive = ProfileTimeToLive.INFINITY
|
|
62
|
+
expiration_timestamp: int = NEVER_EXPIRES_TIMESTAMP
|
|
63
|
+
custom_features: dict | None = None
|
|
64
|
+
source: str | None = None
|
|
65
|
+
status: Status | None = None
|
|
66
|
+
extractor_names: list[str] | None = None
|
|
67
|
+
source_span: str | None = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class UserPlaybookView(BaseModel):
|
|
71
|
+
"""User-facing UserPlaybook — excludes embedding."""
|
|
72
|
+
|
|
73
|
+
user_playbook_id: int = 0
|
|
74
|
+
user_id: str | None = None
|
|
75
|
+
agent_version: str
|
|
76
|
+
request_id: str
|
|
77
|
+
playbook_name: str = ""
|
|
78
|
+
created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
|
|
79
|
+
content: str = ""
|
|
80
|
+
trigger: str | None = None
|
|
81
|
+
rationale: str | None = None
|
|
82
|
+
status: Status | None = None
|
|
83
|
+
source: str | None = None
|
|
84
|
+
source_interaction_ids: list[int] = Field(default_factory=list)
|
|
85
|
+
source_span: str | None = None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AgentPlaybookView(BaseModel):
|
|
89
|
+
"""User-facing AgentPlaybook — excludes embedding."""
|
|
90
|
+
|
|
91
|
+
agent_playbook_id: int = 0
|
|
92
|
+
playbook_name: str = ""
|
|
93
|
+
agent_version: str
|
|
94
|
+
created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
|
|
95
|
+
content: str
|
|
96
|
+
trigger: str | None = None
|
|
97
|
+
rationale: str | None = None
|
|
98
|
+
playbook_status: PlaybookStatus = PlaybookStatus.PENDING
|
|
99
|
+
playbook_metadata: str = ""
|
|
100
|
+
status: Status | None = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class EvaluationResultView(BaseModel):
|
|
104
|
+
"""User-facing AgentSuccessEvaluationResult — excludes embedding."""
|
|
105
|
+
|
|
106
|
+
result_id: int = 0
|
|
107
|
+
agent_version: str
|
|
108
|
+
session_id: str
|
|
109
|
+
is_success: bool
|
|
110
|
+
failure_type: str | None = None
|
|
111
|
+
failure_reason: str | None = None
|
|
112
|
+
evaluation_name: str | None = None
|
|
113
|
+
created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
|
|
114
|
+
regular_vs_shadow: RegularVsShadow | None = None
|
|
115
|
+
number_of_correction_per_session: int = 0
|
|
116
|
+
user_turns_to_resolution: int | None = None
|
|
117
|
+
is_escalated: bool = False
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class ProfileChangeLogView(BaseModel):
|
|
121
|
+
"""User-facing ProfileChangeLog — uses ProfileView lists."""
|
|
122
|
+
|
|
123
|
+
id: int
|
|
124
|
+
user_id: str
|
|
125
|
+
request_id: str
|
|
126
|
+
created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
|
|
127
|
+
added_profiles: list[ProfileView]
|
|
128
|
+
removed_profiles: list[ProfileView]
|
|
129
|
+
mentioned_profiles: list[ProfileView]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""UI-layer enums — re-export domain enums to keep type identity shared.
|
|
2
|
+
|
|
3
|
+
Previously this module declared duplicate StrEnum classes with the same
|
|
4
|
+
variants as the domain enums. That broke type identity for pyright — the
|
|
5
|
+
UI enum and the domain enum were seen as distinct types even though their
|
|
6
|
+
values matched. Re-exporting means ``reflexio.models.api_schema.ui.enums.UserActionType``
|
|
7
|
+
and ``reflexio.models.api_schema.domain.enums.UserActionType`` are the same
|
|
8
|
+
class, and converter functions don't need casts.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from reflexio.models.api_schema.domain.enums import (
|
|
12
|
+
PlaybookStatus,
|
|
13
|
+
ProfileTimeToLive,
|
|
14
|
+
RegularVsShadow,
|
|
15
|
+
Status,
|
|
16
|
+
UserActionType,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"PlaybookStatus",
|
|
21
|
+
"ProfileTimeToLive",
|
|
22
|
+
"RegularVsShadow",
|
|
23
|
+
"Status",
|
|
24
|
+
"UserActionType",
|
|
25
|
+
]
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reusable Pydantic v2 validator types for reflexio.models.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
1. **Data Integrity Validators** - NonEmptyStr, EmbeddingVector, numeric constraints
|
|
6
|
+
2. **Security Validators** - SafeHttpUrl (SSRF prevention), SanitizedStr (prompt injection mitigation)
|
|
7
|
+
3. **Mixins** - TimeRangeValidatorMixin for models with start_time/end_time
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from reflexio.models.api_schema.validators import (
|
|
11
|
+
NonEmptyStr, SafeHttpUrl, SanitizedNonEmptyStr, EmbeddingVector, ...
|
|
12
|
+
)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import ipaddress
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
from typing import Annotated, Any
|
|
21
|
+
from urllib.parse import urlparse
|
|
22
|
+
|
|
23
|
+
from pydantic import AfterValidator, HttpUrl
|
|
24
|
+
|
|
25
|
+
# Embedding vector dimensions — must match config_schema.EMBEDDING_DIMENSIONS.
|
|
26
|
+
# Duplicated here to avoid circular imports (config_schema imports from this module).
|
|
27
|
+
EMBEDDING_DIMENSIONS = 512
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# Data Integrity Validators
|
|
32
|
+
# =============================================================================
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _check_non_empty_str(v: str) -> str:
|
|
36
|
+
"""Validate that a string is not empty or whitespace-only after stripping.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
v (str): The string to validate
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
str: The stripped string
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
ValueError: If the string is empty or whitespace-only
|
|
46
|
+
"""
|
|
47
|
+
stripped = v.strip()
|
|
48
|
+
if not stripped:
|
|
49
|
+
raise ValueError("String must not be empty or whitespace-only")
|
|
50
|
+
return stripped
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _check_optional_non_empty_str(v: str | None) -> str | None:
|
|
54
|
+
"""Validate that an optional string, if provided, is not empty or whitespace-only.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
v (Optional[str]): The string to validate, or None
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Optional[str]: The stripped string, or None
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If the string is provided but empty or whitespace-only
|
|
64
|
+
"""
|
|
65
|
+
if v is None:
|
|
66
|
+
return v
|
|
67
|
+
stripped = v.strip()
|
|
68
|
+
if not stripped:
|
|
69
|
+
raise ValueError("String must not be empty or whitespace-only")
|
|
70
|
+
return stripped
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _check_embedding_dimensions(v: list[float]) -> list[float]:
|
|
74
|
+
"""Validate that an embedding vector is either empty or has the correct dimensions.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
v (list[float]): The embedding vector
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
list[float]: The validated embedding vector
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ValueError: If the embedding has wrong dimensions (not empty and not EMBEDDING_DIMENSIONS)
|
|
84
|
+
"""
|
|
85
|
+
if len(v) > 0 and len(v) != EMBEDDING_DIMENSIONS:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"Embedding must be empty or have exactly {EMBEDDING_DIMENSIONS} dimensions, "
|
|
88
|
+
f"got {len(v)}"
|
|
89
|
+
)
|
|
90
|
+
return v
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Reusable Annotated types for data integrity
|
|
94
|
+
NonEmptyStr = Annotated[str, AfterValidator(_check_non_empty_str)]
|
|
95
|
+
"""String that rejects empty/whitespace-only values. Strips leading/trailing whitespace."""
|
|
96
|
+
|
|
97
|
+
OptionalNonEmptyStr = Annotated[
|
|
98
|
+
str | None, AfterValidator(_check_optional_non_empty_str)
|
|
99
|
+
]
|
|
100
|
+
"""Optional string that, if provided, rejects empty/whitespace-only values."""
|
|
101
|
+
|
|
102
|
+
EmbeddingVector = Annotated[list[float], AfterValidator(_check_embedding_dimensions)]
|
|
103
|
+
"""Embedding vector that must be either empty or exactly EMBEDDING_DIMENSIONS (512) floats."""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# =============================================================================
|
|
107
|
+
# Security Validators — SSRF Prevention
|
|
108
|
+
# =============================================================================
|
|
109
|
+
|
|
110
|
+
# Cloud metadata endpoints — ALWAYS blocked (no legitimate use case)
|
|
111
|
+
METADATA_HOSTS = {"metadata.google.internal"}
|
|
112
|
+
METADATA_IPS = {"169.254.169.254", "fd00:ec2::254"} # AWS/GCP/Azure
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _is_strict_mode() -> bool:
|
|
116
|
+
"""Check if strict URL validation is enabled (for production).
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
bool: True if REFLEXIO_BLOCK_PRIVATE_URLS env var is set to true/1/yes
|
|
120
|
+
"""
|
|
121
|
+
return os.environ.get("REFLEXIO_BLOCK_PRIVATE_URLS", "").lower() in (
|
|
122
|
+
"true",
|
|
123
|
+
"1",
|
|
124
|
+
"yes",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _check_safe_url(v: Any) -> Any:
|
|
129
|
+
"""Validate URL is not targeting dangerous resources.
|
|
130
|
+
|
|
131
|
+
Always blocks: cloud metadata endpoints, non-http schemes.
|
|
132
|
+
In strict mode (REFLEXIO_BLOCK_PRIVATE_URLS=true): also blocks private IPs and localhost.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
v: The URL value (HttpUrl or string)
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
The validated URL value
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
ValueError: If the URL targets a dangerous resource
|
|
142
|
+
"""
|
|
143
|
+
url_str = str(v)
|
|
144
|
+
parsed = urlparse(url_str)
|
|
145
|
+
host = (parsed.hostname or "").lower()
|
|
146
|
+
|
|
147
|
+
# ALWAYS block cloud metadata (never legitimate)
|
|
148
|
+
if host in METADATA_HOSTS:
|
|
149
|
+
raise ValueError(f"URL must not target cloud metadata: {host}")
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
ip = ipaddress.ip_address(host)
|
|
153
|
+
if str(ip) in METADATA_IPS:
|
|
154
|
+
raise ValueError(f"URL must not target cloud metadata endpoint: {host}")
|
|
155
|
+
|
|
156
|
+
# In strict mode, also block private/localhost
|
|
157
|
+
if _is_strict_mode() and (
|
|
158
|
+
ip.is_private or ip.is_reserved or ip.is_loopback or ip.is_link_local
|
|
159
|
+
):
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"URL targets private/reserved IP '{host}'. "
|
|
162
|
+
f"If running locally, unset REFLEXIO_BLOCK_PRIVATE_URLS."
|
|
163
|
+
)
|
|
164
|
+
except ValueError as e:
|
|
165
|
+
if "must not target" in str(e) or "targets private" in str(e):
|
|
166
|
+
raise
|
|
167
|
+
# Not an IP (hostname) — check localhost in strict mode
|
|
168
|
+
if _is_strict_mode() and host in ("localhost", "0.0.0.0"): # noqa: S104
|
|
169
|
+
raise ValueError(
|
|
170
|
+
f"URL targets '{host}'. "
|
|
171
|
+
f"If running locally, unset REFLEXIO_BLOCK_PRIVATE_URLS."
|
|
172
|
+
) from None
|
|
173
|
+
|
|
174
|
+
return v
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
SafeHttpUrl = Annotated[HttpUrl, AfterValidator(_check_safe_url)]
|
|
178
|
+
"""HTTP URL that blocks cloud metadata endpoints (always) and private IPs (in strict mode).
|
|
179
|
+
|
|
180
|
+
Use for: api_base, endpoint — anywhere the server fetches a user-provided URL.
|
|
181
|
+
|
|
182
|
+
Local dev: works out of the box (localhost allowed)
|
|
183
|
+
Production: set REFLEXIO_BLOCK_PRIVATE_URLS=true in env/Dockerfile for full protection
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# =============================================================================
|
|
188
|
+
# Security Validators — Prompt Injection Mitigation
|
|
189
|
+
# =============================================================================
|
|
190
|
+
|
|
191
|
+
# Control characters that can manipulate prompt rendering or terminal output.
|
|
192
|
+
# Excludes tab (\x09), newline (\x0a), carriage return (\x0d) which are legitimate.
|
|
193
|
+
_CONTROL_CHAR_PATTERN = re.compile(
|
|
194
|
+
r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]" # C0 control chars except \t \n \r
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _strip_control_chars(v: str) -> str:
|
|
199
|
+
"""Remove invisible control characters that could manipulate prompt rendering.
|
|
200
|
+
|
|
201
|
+
Strips C0 control characters (NULL, bell, backspace, escape sequences, etc.)
|
|
202
|
+
while preserving tabs, newlines, and carriage returns.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
v (str): The string to sanitize
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
str: The sanitized string with control characters removed
|
|
209
|
+
"""
|
|
210
|
+
return _CONTROL_CHAR_PATTERN.sub("", v)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
SanitizedStr = Annotated[str, AfterValidator(_strip_control_chars)]
|
|
214
|
+
"""String with control characters stripped. Use for user content flowing into LLM prompts."""
|
|
215
|
+
|
|
216
|
+
SanitizedNonEmptyStr = Annotated[
|
|
217
|
+
str,
|
|
218
|
+
AfterValidator(_strip_control_chars),
|
|
219
|
+
AfterValidator(_check_non_empty_str),
|
|
220
|
+
]
|
|
221
|
+
"""String that is sanitized (control chars stripped) AND non-empty. Use for config prompts."""
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# =============================================================================
|
|
225
|
+
# Time Range Validator Mixin
|
|
226
|
+
# =============================================================================
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _validate_image_url(v: str) -> str:
|
|
230
|
+
"""SSRF prevention: if URL is provided, must be safe http(s) or data URI.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
v (str): The image URL to validate
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
str: The validated URL
|
|
237
|
+
|
|
238
|
+
Raises:
|
|
239
|
+
ValueError: If the URL scheme is not http, https, or data, or if it targets unsafe resources
|
|
240
|
+
"""
|
|
241
|
+
if not v:
|
|
242
|
+
return v
|
|
243
|
+
parsed = urlparse(v)
|
|
244
|
+
if parsed.scheme not in ("http", "https", "data"):
|
|
245
|
+
raise ValueError(
|
|
246
|
+
f"Image URL scheme must be http, https, or data — got '{parsed.scheme}'"
|
|
247
|
+
)
|
|
248
|
+
if parsed.scheme in ("http", "https"):
|
|
249
|
+
_check_safe_url(v)
|
|
250
|
+
return v
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class TimeRangeValidatorMixin:
|
|
254
|
+
"""Mixin for models with optional start_time/end_time fields.
|
|
255
|
+
|
|
256
|
+
Validates that end_time is after start_time when both are provided.
|
|
257
|
+
Add to models by including in the class bases alongside BaseModel.
|
|
258
|
+
|
|
259
|
+
Usage:
|
|
260
|
+
class MyRequest(TimeRangeValidatorMixin, BaseModel):
|
|
261
|
+
start_time: Optional[datetime] = None
|
|
262
|
+
end_time: Optional[datetime] = None
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
# Note: This is implemented as a classmethod to be called from
|
|
266
|
+
# @model_validator(mode='after') in each model, rather than using
|
|
267
|
+
# __init_subclass__ magic that could conflict with Pydantic's metaclass.
|
|
268
|
+
@staticmethod
|
|
269
|
+
def validate_time_range(start_time: Any, end_time: Any) -> None:
|
|
270
|
+
"""Validate that end_time is after start_time.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
start_time: The start time value
|
|
274
|
+
end_time: The end time value
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
ValueError: If end_time is not after start_time
|
|
278
|
+
"""
|
|
279
|
+
if start_time is not None and end_time is not None and end_time <= start_time:
|
|
280
|
+
raise ValueError("end_time must be after start_time")
|