precis-cli 0.1.6__tar.gz → 0.1.8__tar.gz
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.
- {precis_cli-0.1.6/precis_cli.egg-info → precis_cli-0.1.8}/PKG-INFO +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/dependencies.py +42 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/full_validation.py +6 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/validation.py +6 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/hardware.py +5 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/providers.py +8 -3
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/utils.py +8 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/core/data_sources.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/core/reporting.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/preview/header_row.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/helpers.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/projects/open.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/projects/scan.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/validation/common.py +3 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/services/full_validation_response_builder.py +3 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shared_services/config_ops.py +2 -2
- precis_cli-0.1.8/app/cli/shell/commands/ai/agent_interaction.py +210 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/chat.py +2 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/executor.py +12 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/provider.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/validate.py +46 -6
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/io/yaml.py +3 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/registry.py +4 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporters/__init__.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/allowed_values.py +6 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/charset.py +15 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/composite.py +4 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/conditional.py +31 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/date_logic.py +80 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/foreign_key.py +12 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/not_null.py +6 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/range.py +20 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/regex.py +14 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/scripted.py +12 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/unique.py +8 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/chat_agent_runner.py +10 -7
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/chat_orchestrator.py +14 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/_constraint_validator.py +62 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/action_handlers.py +33 -16
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/registry.py +4 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/chat/chat_system_prompt.py +16 -7
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/config/models.py +2 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/config/presets.py +6 -4
- precis_cli-0.1.8/app/shared/services/llm/constraints/constraint_builder.py +421 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/constraints/inline_batch.py +53 -22
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/config_builder.py +40 -3
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/prompt_builder.py +2 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/providers/__init__.py +3 -1
- precis_cli-0.1.8/app/shared/services/llm/providers/fake.py +184 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/providers/registry.py +3 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/yaml_io.py +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/service.py +29 -9
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/validators/adapter.py +32 -11
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/validators/base.py +8 -4
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/validators/composite.py +8 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/validators/date_logic.py +48 -6
- {precis_cli-0.1.6 → precis_cli-0.1.8/precis_cli.egg-info}/PKG-INFO +1 -1
- {precis_cli-0.1.6 → precis_cli-0.1.8}/precis_cli.egg-info/SOURCES.txt +2 -4
- {precis_cli-0.1.6 → precis_cli-0.1.8}/pyproject.toml +1 -1
- precis_cli-0.1.6/app/cli_main.py +0 -52
- precis_cli-0.1.6/app/shared/core/manifest_schema/types.py +0 -99
- precis_cli-0.1.6/app/shared/services/ai/types.py +0 -213
- precis_cli-0.1.6/app/shared/services/llm/constraints/constraint_builder.py +0 -177
- precis_cli-0.1.6/app/shared/services/llm/models.py +0 -104
- {precis_cli-0.1.6 → precis_cli-0.1.8}/MANIFEST.in +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/PYPI_README.md +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/README.md +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/main.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/middleware/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/middleware/exception_handler.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/middleware/request_logging.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/middleware/token_auth.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/connection_rules.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/files.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/project.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/projects.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/schema.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/v2_responses.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/models/workspace.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/chat.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/generate.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/jobs.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/migrate.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/models.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/ollama.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/router.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/ai/stream.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/core/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/core/connection_rules.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/core/regex.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/files/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/files/ops.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/files/transfer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/preview/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/preview/content_mode.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/preview/models.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/preview/path_mode.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/preview/router.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/constraint.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/full_config.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/full_config_writer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/inspection_fix.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/manifest.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/models.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/pattern.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/regex.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/scanner.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/schema.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/schema_helpers.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/settings.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/template.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/validation.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/view.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/project/workspaces.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/projects/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/projects/create.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/validation/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/validation/content_mode.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/validation/history.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/validation/inline_mode.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/validation/path_mode.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/routers/validation/router.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/services/io_error_messages.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/api/services/preview_service.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/__main__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shared_services/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shared_services/generation_ops.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shared_services/project_ops.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/delete.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/diff.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/display.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/executor_utils.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/generate.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/interaction.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/migrate.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/resolver.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/status.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/switch.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/ai/utils.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/check.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/edit.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/get.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/init.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/inspect.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/list.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/set.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/config/show.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/exit.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/help.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/infer_schema.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/open.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/project.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/commands/system.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/completer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/config_storage.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/exceptions.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/formatter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/interactive_menu.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/main.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/shell/parser.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/cli/start.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/mcp_server.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/app_version.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/config/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/config/server.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/converter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/csv_loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/excel_loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/extractor.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/json_loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/registry.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/sql_loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/strategies/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/strategies/array_parser.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/strategies/lines_parser.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/loaders/strategies/object_parser.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/schema_info.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/specs/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/specs/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/specs/csv_source.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/specs/excel_source.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/specs/file_base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/specs/json_source.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/data_source/specs/sql_source.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/encoding.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/io/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/manifest_schema/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/manifest_schema/version.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/patterns/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/patterns/loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/patterns/writer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/composite.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/conditional.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/foreign_key.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/registry.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/scripted.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/single_column.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/builders/unique.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/factory.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/reader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/types/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/types/constraint_file.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/types/refs.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/constraint/writer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/config_inspector.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/embedded_constraints.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/file_loaders.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/inspection_ids.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/inspector_helpers.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/inspector_id_checks.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/loading_error_messages.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/main.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/path_validation.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/loader_parts/runtime.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/loader/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/coverage.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/reader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/constants.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/data_source.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/info.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/manifest.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/refs.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/settings.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/settings_file_processing.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/settings_script_security.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/settings_validation.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/types_parts/template.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manifest/writer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manual_data/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/manual_data/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/regex/reader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/regex/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/regex/writer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/reader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/column.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/column_utils.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/constraint.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/schema_id.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/source.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/source_options.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/types_parts/table.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema/writer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/schema_ref_check.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/template/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/template/expander.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/template/reader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/template/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/transform/reader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/transform/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/project/transform/writer.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/pydantic_messages.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporters/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporters/dingtalk_app_reporter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporters/email_reporter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporters/feishu_app_reporter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporters/local_file_reporter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/reporter/reporters/wecom_app_reporter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/utils/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/utils/path_utils.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/core/utils/regex_extract.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/condition_registry.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/constraints/key_normalization.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_engine.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/composite.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/expression.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/extracted.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/json_types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/scalars.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/data_types_parts/sequence.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/dataset_schema.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/eval_sandbox.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/expression_system.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/regex_flags.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/schema/builder.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/schema/models.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/aggregate.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/cast_type.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/concat.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/conditional_assign.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/date_format.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/digits.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/drop_duplicates.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/fill_na.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/filter_rows.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/lookup.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/lower_case.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/map_value.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/math_expr.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/modulo.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/regex_extract.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/registry.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/replace.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/sort_rows.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/string_split.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/strip.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/substring.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/upper_case.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/transforms/weighted_sum.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/domain/validation_constraints.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/apply_actions.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/ask_user.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/read_canvas.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/read_project.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/read_table.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/schemas.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/chat_tools/validate_table.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/executor.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/memory.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/planner.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tool_registry.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tools/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tools/config_generate.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tools/config_refine.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tools/config_validate.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tools/merge_results.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tools/plan_chunks.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/tools/script_parse.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/agent/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/failure_messages.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/job_storage.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/migrate_service.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/streaming/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/streaming/event_journal.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/streaming/orchestrator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/streaming/pending_interaction_store.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/streaming/sse_response.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/streaming/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/ai/utils.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/diff/config_diff.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/hardware.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/_canvas_validator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/_regex_validator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/_schema_validator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/_settings_validator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/_transform_validator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/action_parser.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/action_processor.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/action_validator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/diff_compute.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/regex_handlers.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/schema_handlers.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/settings_handlers.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/specs.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/transform_handlers.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/actions/validation_types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/cache/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/cache/response_cache.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/chat/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/chat/response_parser.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/config/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/config/crypto.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/config/loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/config_generator.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/constraints/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/constraints/constraint_deletion.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/constraints/constraint_id.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/constraints/frontend_instructions.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/discovery/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/discovery/scanner.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/agent_wiring.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/errors.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/existing_config.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/profiler.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/response_parser.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/generation/service.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/providers/base.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/providers/cached_provider.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/providers/ollama.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/providers/openai.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/schema_resolver.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/suggestion_utils.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/llm/validate_executor.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/preview/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/preview/loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/preview/path_validation.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/project_loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/schema_inference.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/schema_runtime_builder.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/chunked_loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/dag/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/dag/builder.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/dag/executor.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/dag/sorter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/data_loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/engine.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/executor.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/extractors.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/history.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/json_payload.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/loader.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/memory_monitor.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/postprocess.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/progress.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/report_export.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/resolver.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/types.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/shared/services/validation/validators/__init__.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/app/start_server.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/precis_cli.egg-info/dependency_links.txt +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/precis_cli.egg-info/entry_points.txt +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/precis_cli.egg-info/requires.txt +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/precis_cli.egg-info/top_level.txt +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/setup.cfg +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/tests/test_local_file_reporter.py +0 -0
- {precis_cli-0.1.6 → precis_cli-0.1.8}/tests/test_schema_builder.py +0 -0
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
# backend/app/api/dependencies.py
|
|
38
38
|
import os
|
|
39
|
+
from urllib.parse import quote, unquote
|
|
39
40
|
|
|
40
41
|
from fastapi import Depends, Header, HTTPException
|
|
41
42
|
|
|
@@ -65,6 +66,44 @@ class ProjectStore:
|
|
|
65
66
|
self.project_path = project_path
|
|
66
67
|
|
|
67
68
|
|
|
69
|
+
def _decode_header_path(value: str) -> str:
|
|
70
|
+
"""还原 HTTP header 携带的非 ASCII 路径。
|
|
71
|
+
|
|
72
|
+
两种线上契约并存,按特征识别、互不干扰:
|
|
73
|
+
|
|
74
|
+
1. GUI 契约(axios/fetch):前端在出口处对路径做 encodeURIComponent(纯
|
|
75
|
+
ASCII 百分号转义)。浏览器 XHR/fetch 的 header 值只接受 ByteString
|
|
76
|
+
(ISO-8859-1),中文原值会被 XHR 直接拒绝;axios 1.18 的
|
|
77
|
+
toByteStringHeaderValue 还会把 latin1 之外的字符静默删除——不转义的
|
|
78
|
+
中文路径会以"删字后的错误路径"上线且无从在服务端还原。判定是否为
|
|
79
|
+
规范转义串用 round-trip 校验(quote(unquote(v)) == v),字面上含 % 的
|
|
80
|
+
合法路径通常不会被误解码(例外:字面 "%XX" 恰为合法 UTF-8 转义序列
|
|
81
|
+
的路径会被还原,随后走校验链报错而非指向任意目录)。round-trip 的
|
|
82
|
+
转义集必须与 encodeURIComponent 的不转义集对齐:JS 保留
|
|
83
|
+
`!'()*` 五字符原样上线,校验端 quote 的 safe 集须包含它们,否则含
|
|
84
|
+
括号/感叹号的中文路径(如 `D:/项目(备份)`)契约判定必败,被契约 2
|
|
85
|
+
原样放行后以百分号串走校验链 400/404。
|
|
86
|
+
2. TUI/脚本契约(reqwest FromBytes、curl 等):UTF-8 原始字节按 obs-text
|
|
87
|
+
上线。ASGI 服务器把 header 字节按 latin-1 解码成乱码,latin-1 与字节流
|
|
88
|
+
双射,编码回字节再按 UTF-8 解码即可无损还原;非 UTF-8 字节保持原值,
|
|
89
|
+
交由后续校验链正常报错。
|
|
90
|
+
"""
|
|
91
|
+
# 契约 1:规范百分号转义(GUI)。safe 集对齐 encodeURIComponent 的保留集
|
|
92
|
+
# (`!'()*`),Python quote 恒不转义字母数字与 `_.-~`,两者已一致
|
|
93
|
+
if "%" in value:
|
|
94
|
+
try:
|
|
95
|
+
decoded = unquote(value, errors="strict")
|
|
96
|
+
except UnicodeDecodeError:
|
|
97
|
+
decoded = None
|
|
98
|
+
if decoded is not None and decoded != value and quote(decoded, safe="!'()*") == value:
|
|
99
|
+
return decoded
|
|
100
|
+
# 契约 2:UTF-8 原始字节(TUI/脚本)
|
|
101
|
+
try:
|
|
102
|
+
return value.encode("latin-1").decode("utf-8")
|
|
103
|
+
except (UnicodeEncodeError, UnicodeDecodeError):
|
|
104
|
+
return value
|
|
105
|
+
|
|
106
|
+
|
|
68
107
|
async def get_project_config_path(
|
|
69
108
|
x_project_config_path: str = Header(..., description="项目根目录的绝对路径(须含 project.precis.yaml)"),
|
|
70
109
|
) -> str:
|
|
@@ -121,6 +160,9 @@ def _validate_project_root(raw_path: str | None) -> str:
|
|
|
121
160
|
"""
|
|
122
161
|
if not raw_path:
|
|
123
162
|
raise HTTPException(status_code=400, detail="X-Project-Config-Path header 不能为空。")
|
|
163
|
+
# 还原 header 编码乱码(见 _decode_header_path):中文路径经 UTF-8 上线、
|
|
164
|
+
# latin-1 解码后 isdir 必失败,必须先还原再做后续校验
|
|
165
|
+
raw_path = _decode_header_path(raw_path)
|
|
124
166
|
# 步骤1:先校验原始输入是否为绝对路径
|
|
125
167
|
# 在 Windows 下,os.path.abspath 会把相对路径(如 "../project")解析成绝对路径,
|
|
126
168
|
# 导致后续 isabs 检查无法拒绝相对路径。因此必须在规范化之前先做判断。
|
|
@@ -438,6 +438,12 @@ class FullValidationErrorItem(BaseModel):
|
|
|
438
438
|
source_sheet: str | None = Field(
|
|
439
439
|
default=None, description="配置文件内定义的 Excel Sheet 名(可选)"
|
|
440
440
|
) # schema 配置中 source.sheet 定义的 Excel 工作表名称,可选
|
|
441
|
+
error_code: str | None = Field(
|
|
442
|
+
default=None, description="稳定错误码(UPPER_SNAKE),供前端映射 i18n key 按当前语言渲染"
|
|
443
|
+
) # 与 message 同时给出:message 为后端默认语言的兜底文案
|
|
444
|
+
error_params: dict | None = Field(
|
|
445
|
+
default=None, description="错误码对应的插值参数(JSON 标量),如 {column: 'Total'}"
|
|
446
|
+
) # 供前端 t(key, params) 插值
|
|
441
447
|
|
|
442
448
|
|
|
443
449
|
class ValidationPassedItem(BaseModel):
|
|
@@ -257,6 +257,12 @@ class ValidationErrorRow(BaseModel):
|
|
|
257
257
|
error_message: str | None = Field(
|
|
258
258
|
None, description="错误信息"
|
|
259
259
|
) # 校验失败的具体错误描述,如"值不在允许列表中",可选
|
|
260
|
+
error_code: str | None = Field(
|
|
261
|
+
None, description="稳定错误码(UPPER_SNAKE),供前端映射 i18n key 按当前语言渲染"
|
|
262
|
+
) # 与 error_message 同时给出:message 为后端默认语言的兜底文案
|
|
263
|
+
error_params: dict | None = Field(
|
|
264
|
+
None, description="错误码对应的插值参数(JSON 标量),如 {column: 'Total', row: 1}"
|
|
265
|
+
) # 供前端 t(key, params) 插值
|
|
260
266
|
|
|
261
267
|
|
|
262
268
|
class ValidationResult(BaseModel):
|
|
@@ -117,7 +117,11 @@ def diagnose_hardware() -> HardwareDiagnoseResponse:
|
|
|
117
117
|
import subprocess
|
|
118
118
|
|
|
119
119
|
result = subprocess.run(
|
|
120
|
-
["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader"],
|
|
120
|
+
["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader"],
|
|
121
|
+
capture_output=True,
|
|
122
|
+
text=True,
|
|
123
|
+
encoding="utf-8",
|
|
124
|
+
errors="replace", # 中文 Windows 下 text=True 默认按 GBK 解码会乱码/抛错
|
|
121
125
|
)
|
|
122
126
|
if result.returncode == 0:
|
|
123
127
|
for line in result.stdout.strip().split("\n"):
|
|
@@ -58,8 +58,10 @@ def _is_configured(p: AIProvider) -> bool:
|
|
|
58
58
|
"""判断 Provider 是否已配置。
|
|
59
59
|
|
|
60
60
|
loader.load() 已完成 env-var 覆盖,直接检查 api_key 即可。
|
|
61
|
-
本地 Provider(Ollama)不需要 API Key。
|
|
61
|
+
本地 Provider(Ollama)不需要 API Key;fake 是确定性测试 Provider,永不依赖 key。
|
|
62
62
|
"""
|
|
63
|
+
if p.type == ProviderType.FAKE:
|
|
64
|
+
return True
|
|
63
65
|
if p.deployment == DeploymentType.LOCAL:
|
|
64
66
|
return bool(p.base_url)
|
|
65
67
|
return bool(p.api_key and p.api_key.strip())
|
|
@@ -141,7 +143,7 @@ providers:
|
|
|
141
143
|
type: openai
|
|
142
144
|
base_url: https://api.deepseek.com
|
|
143
145
|
api_key: ${DEEPSEEK_API_KEY}
|
|
144
|
-
model: deepseek-
|
|
146
|
+
model: deepseek-flash
|
|
145
147
|
|
|
146
148
|
# 本地 Ollama(无需 API Key)
|
|
147
149
|
- id: ollama-local
|
|
@@ -391,7 +393,10 @@ async def create_provider(req: CreateProviderRequest) -> ProviderResponse:
|
|
|
391
393
|
try:
|
|
392
394
|
provider_type = ProviderType(req.type)
|
|
393
395
|
except ValueError:
|
|
394
|
-
raise HTTPException(
|
|
396
|
+
raise HTTPException(
|
|
397
|
+
400,
|
|
398
|
+
detail=f"不支持的 AI 服务类型「{req.type}」,目前支持 openai、ollama 两种接入方式(fake 仅用于测试演练)",
|
|
399
|
+
)
|
|
395
400
|
|
|
396
401
|
_validate_base_url(req.base_url) # SSRF 防护
|
|
397
402
|
|
|
@@ -138,12 +138,19 @@ def expand_paths(
|
|
|
138
138
|
|
|
139
139
|
数据文件类型:.xlsx, .xls, .csv, .json, .jsonl
|
|
140
140
|
"""
|
|
141
|
+
# 还原 header 线上编码(GUI 契约 percent-encode / TUI 原始 UTF-8 字节,见
|
|
142
|
+
# app.api.dependencies._decode_header_path):前端出口统一转义后,未解码的
|
|
143
|
+
# 百分号串作 base 解析相对路径会全 miss,中文项目下静默返回空列表
|
|
144
|
+
from app.api.dependencies import _decode_header_path
|
|
145
|
+
|
|
146
|
+
base_path = _decode_header_path(x_project_config_path) if x_project_config_path else None
|
|
147
|
+
|
|
141
148
|
result = []
|
|
142
149
|
seen = set()
|
|
143
150
|
|
|
144
151
|
for raw_path in paths:
|
|
145
152
|
# 1. 解析为绝对路径
|
|
146
|
-
path = _resolve_path(raw_path,
|
|
153
|
+
path = _resolve_path(raw_path, base_path)
|
|
147
154
|
|
|
148
155
|
# 2. 如果是目录,递归展开
|
|
149
156
|
if os.path.isdir(path):
|
|
@@ -86,7 +86,7 @@ def load_workspace_config(project_root: str) -> dict[str, Any]:
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
try:
|
|
89
|
-
with open(config_path, encoding="utf-8") as f:
|
|
89
|
+
with open(config_path, encoding="utf-8-sig") as f:
|
|
90
90
|
return yaml.safe_load(f) or {}
|
|
91
91
|
except Exception as e:
|
|
92
92
|
raise HTTPException(
|
|
@@ -223,7 +223,7 @@ def get_reporting_config(store: ProjectStore = Depends(get_project_store)) -> Re
|
|
|
223
223
|
)
|
|
224
224
|
try:
|
|
225
225
|
# 读取并解析 YAML 配置文件
|
|
226
|
-
with open(config_path, encoding="utf-8") as f:
|
|
226
|
+
with open(config_path, encoding="utf-8-sig") as f:
|
|
227
227
|
config_data = yaml.safe_load(f)
|
|
228
228
|
return ReportingConfig(**config_data)
|
|
229
229
|
except Exception as e:
|
|
@@ -106,7 +106,7 @@ def handle_header_row_changed(
|
|
|
106
106
|
|
|
107
107
|
if os.path.exists(schema_path):
|
|
108
108
|
try:
|
|
109
|
-
with open(schema_path, encoding="utf-8") as f:
|
|
109
|
+
with open(schema_path, encoding="utf-8-sig") as f:
|
|
110
110
|
schema_content = yaml.safe_load(f)
|
|
111
111
|
|
|
112
112
|
if "header_row" not in schema_content:
|
|
@@ -36,7 +36,7 @@ def _get_project_name(project_path: str) -> str:
|
|
|
36
36
|
"""从 project.precis.yaml 中读取项目名称,失败时返回目录名。"""
|
|
37
37
|
manifest_path = os.path.join(project_path, "project.precis.yaml")
|
|
38
38
|
try:
|
|
39
|
-
with open(manifest_path, encoding="utf-8") as f:
|
|
39
|
+
with open(manifest_path, encoding="utf-8-sig") as f:
|
|
40
40
|
manifest = yaml.safe_load(f)
|
|
41
41
|
if isinstance(manifest, dict):
|
|
42
42
|
project = manifest.get("project", {})
|
|
@@ -69,7 +69,7 @@ def scan_projects(
|
|
|
69
69
|
def _parse_project(project_path: str, manifest_path: str) -> ProjectInfo | None:
|
|
70
70
|
"""解析单个项目的 manifest 文件获取元信息。"""
|
|
71
71
|
try:
|
|
72
|
-
with open(manifest_path, encoding="utf-8") as f:
|
|
72
|
+
with open(manifest_path, encoding="utf-8-sig") as f:
|
|
73
73
|
manifest = yaml.safe_load(f)
|
|
74
74
|
except Exception:
|
|
75
75
|
return None
|
|
@@ -189,11 +189,14 @@ def execute_dataframe_validation(
|
|
|
189
189
|
)
|
|
190
190
|
|
|
191
191
|
# 将原始错误行列表转换为标准化的 ValidationErrorRow 对象列表
|
|
192
|
+
# error_code/error_params 透传给前端,供按当前语言渲染错误文案
|
|
192
193
|
error_rows = [
|
|
193
194
|
ValidationErrorRow(
|
|
194
195
|
row_index=err.get("row_index", 0),
|
|
195
196
|
cell_value=str(err.get("cell_value", "")),
|
|
196
197
|
error_message=err.get("error_message"),
|
|
198
|
+
error_code=err.get("error_code"),
|
|
199
|
+
error_params=err.get("error_params"),
|
|
197
200
|
)
|
|
198
201
|
for err in result.error_rows
|
|
199
202
|
]
|
|
@@ -150,6 +150,9 @@ class FullValidationResponseBuilder:
|
|
|
150
150
|
source_path=err.get("source_path"),
|
|
151
151
|
source_file=err.get("source_file"),
|
|
152
152
|
source_sheet=err.get("source_sheet"),
|
|
153
|
+
# i18n:稳定错误码 + 插值参数透传,message 保留兜底文案
|
|
154
|
+
error_code=err.get("error_code"),
|
|
155
|
+
error_params=err.get("error_params"),
|
|
153
156
|
)
|
|
154
157
|
)
|
|
155
158
|
|
|
@@ -371,7 +371,7 @@ def set_config_value_in_file(project_path: str, filename: str, key_path: str, va
|
|
|
371
371
|
yaml_parser.preserve_quotes = True
|
|
372
372
|
|
|
373
373
|
try:
|
|
374
|
-
with open(config_path, encoding="utf-8") as f:
|
|
374
|
+
with open(config_path, encoding="utf-8-sig") as f:
|
|
375
375
|
data = yaml_parser.load(f)
|
|
376
376
|
except RuamelYAMLError as e:
|
|
377
377
|
return False, f"YAML 解析失败: {e}"
|
|
@@ -482,7 +482,7 @@ def load_config_content(project_path: str, filename: str) -> dict | str:
|
|
|
482
482
|
if not config_path:
|
|
483
483
|
return f"配置文件不存在: {filename}"
|
|
484
484
|
try:
|
|
485
|
-
with open(config_path, encoding="utf-8") as f:
|
|
485
|
+
with open(config_path, encoding="utf-8-sig") as f:
|
|
486
486
|
content = yaml.safe_load(f)
|
|
487
487
|
if content:
|
|
488
488
|
return content if isinstance(content, dict) else str(content)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""@fileoverview Agent 模式终端交互适配器(CLI)
|
|
17
|
+
|
|
18
|
+
为 CLI `ai chat`(默认 agent 模式)提供 apply_actions 两阶段确认与 ask_user
|
|
19
|
+
交互问答的终端实现,解开非流式环境的 fail-closed 写盘/提问闸门。
|
|
20
|
+
|
|
21
|
+
机制与 GUI 流式路径同源:回调在事件循环协程内触发(on_apply_pending /
|
|
22
|
+
on_user_input_requested),此处打印摘要后在独立 daemon 线程阻塞读终端输入,
|
|
23
|
+
经 pending interaction store 线程安全 resolve 唤醒挂起的 await_decision() /
|
|
24
|
+
await_response()。超时(5 分钟)由 controller 兜底自动 reject/skip。
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import asyncio
|
|
30
|
+
import logging
|
|
31
|
+
import threading
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
from app.cli.shell.commands.ai.executor_utils import SpinnerController
|
|
35
|
+
from app.cli.shell.formatter import Formatter
|
|
36
|
+
from app.shared.services.ai.agent.chat_tools.apply_actions import ApplyCallbacks
|
|
37
|
+
from app.shared.services.ai.agent.chat_tools.ask_user import AskCallbacks
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
# 终端 diff 展示上限:超长 diff 只显示前 N 行,避免刷屏(完整内容在 dry-run 文件里)
|
|
42
|
+
_MAX_DIFF_LINES = 30
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def build_agent_interaction(spinner: SpinnerController | None) -> tuple[ApplyCallbacks, AskCallbacks]:
|
|
46
|
+
"""构建 CLI 终端的 apply/ask 回调集合。
|
|
47
|
+
|
|
48
|
+
参数:
|
|
49
|
+
spinner: CLI 交互模式的 spinner 控制器(None 表示无动画,静默模式)
|
|
50
|
+
|
|
51
|
+
返回:
|
|
52
|
+
(ApplyCallbacks, AskCallbacks),注入 ChatOptions 后经 ChatAgentRunner 生效
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def on_apply_pending(payload: dict[str, Any]) -> None:
|
|
56
|
+
# 回调在事件循环协程内同步触发,捕获运行中的 loop 供工作线程 resolve
|
|
57
|
+
loop = asyncio.get_running_loop()
|
|
58
|
+
apply_id = payload.get("apply_id", "")
|
|
59
|
+
if spinner:
|
|
60
|
+
spinner.pause()
|
|
61
|
+
_print_apply_summary(payload)
|
|
62
|
+
|
|
63
|
+
def _worker() -> None:
|
|
64
|
+
decision = _read_apply_decision()
|
|
65
|
+
try:
|
|
66
|
+
future = asyncio.run_coroutine_threadsafe(_resolve_apply(apply_id, decision), loop)
|
|
67
|
+
future.result(timeout=10)
|
|
68
|
+
except Exception:
|
|
69
|
+
logger.exception("[agent-confirm] 终端确认 resolve 失败: apply_id=%s", apply_id)
|
|
70
|
+
finally:
|
|
71
|
+
if spinner:
|
|
72
|
+
spinner.resume()
|
|
73
|
+
|
|
74
|
+
threading.Thread(target=_worker, daemon=True, name="agent-apply-confirm").start()
|
|
75
|
+
|
|
76
|
+
def on_user_input_requested(payload: dict[str, Any]) -> None:
|
|
77
|
+
loop = asyncio.get_running_loop()
|
|
78
|
+
ask_id = payload.get("ask_id", "")
|
|
79
|
+
if spinner:
|
|
80
|
+
spinner.pause()
|
|
81
|
+
_print_ask_question(payload)
|
|
82
|
+
|
|
83
|
+
def _worker() -> None:
|
|
84
|
+
response = _read_ask_response(payload)
|
|
85
|
+
try:
|
|
86
|
+
future = asyncio.run_coroutine_threadsafe(_resolve_ask(ask_id, response), loop)
|
|
87
|
+
future.result(timeout=10)
|
|
88
|
+
except Exception:
|
|
89
|
+
logger.exception("[agent-ask] 终端问答 resolve 失败: ask_id=%s", ask_id)
|
|
90
|
+
finally:
|
|
91
|
+
if spinner:
|
|
92
|
+
spinner.resume()
|
|
93
|
+
|
|
94
|
+
threading.Thread(target=_worker, daemon=True, name="agent-ask-input").start()
|
|
95
|
+
|
|
96
|
+
apply_callbacks = ApplyCallbacks(on_apply_pending=on_apply_pending)
|
|
97
|
+
ask_callbacks = AskCallbacks(on_user_input_requested=on_user_input_requested)
|
|
98
|
+
return apply_callbacks, ask_callbacks
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
async def _resolve_apply(apply_id: str, decision: str) -> None:
|
|
102
|
+
"""按 apply_id 从全局 store 取 controller 并写入决策(幂等)。"""
|
|
103
|
+
# 延迟导入与 apply_actions._run_two_phase 一致(避免 streaming 包初始化链循环导入)
|
|
104
|
+
from app.shared.services.ai.streaming.pending_interaction_store import (
|
|
105
|
+
ConfirmController,
|
|
106
|
+
get_global_pending_interaction_store,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
controller = get_global_pending_interaction_store().get(apply_id)
|
|
110
|
+
if isinstance(controller, ConfirmController):
|
|
111
|
+
await controller.resolve(decision)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def _resolve_ask(ask_id: str, response: dict[str, Any]) -> None:
|
|
115
|
+
from app.shared.services.ai.streaming.pending_interaction_store import (
|
|
116
|
+
InteractionController,
|
|
117
|
+
get_global_pending_interaction_store,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
controller = get_global_pending_interaction_store().get(ask_id)
|
|
121
|
+
if isinstance(controller, InteractionController):
|
|
122
|
+
await controller.resolve(response)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _print_apply_summary(payload: dict[str, Any]) -> None:
|
|
126
|
+
"""打印写盘计划摘要:文件清单 + 状态 + 截断 diff + 汇总行。"""
|
|
127
|
+
files = payload.get("files") or []
|
|
128
|
+
print(Formatter.warning(f"\nAI 请求写入 {len(files)} 个文件:"))
|
|
129
|
+
for f in files:
|
|
130
|
+
status = f.get("status", "?")
|
|
131
|
+
path = f.get("path", "?")
|
|
132
|
+
print(f" [{status}] {path}")
|
|
133
|
+
diff = (f.get("diff") or "").strip()
|
|
134
|
+
if diff:
|
|
135
|
+
diff_lines = diff.splitlines()
|
|
136
|
+
shown = diff_lines[:_MAX_DIFF_LINES]
|
|
137
|
+
for line in shown:
|
|
138
|
+
print(f" {line}")
|
|
139
|
+
if len(diff_lines) > _MAX_DIFF_LINES:
|
|
140
|
+
print(f" ...(共 {len(diff_lines)} 行,已截断)")
|
|
141
|
+
summary = payload.get("summary")
|
|
142
|
+
if summary:
|
|
143
|
+
print(Formatter.info(f" 摘要: {summary}"))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _read_apply_decision() -> str:
|
|
147
|
+
"""读取 y/n 决策;空输入与非 y 输入一律 reject(写盘默认保守)。"""
|
|
148
|
+
try:
|
|
149
|
+
raw = input(Formatter.warning("确认写入以上变更?[y/N]: ")).strip().lower()
|
|
150
|
+
except EOFError:
|
|
151
|
+
return "reject"
|
|
152
|
+
return "confirm" if raw in ("y", "yes", "是") else "reject"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _print_ask_question(payload: dict[str, Any]) -> None:
|
|
156
|
+
print(Formatter.warning(f"\nAI 提问: {payload.get('prompt', '')}"))
|
|
157
|
+
options = payload.get("options")
|
|
158
|
+
if isinstance(options, list) and options:
|
|
159
|
+
for idx, opt in enumerate(options, start=1):
|
|
160
|
+
print(f" {idx}. {opt}")
|
|
161
|
+
if payload.get("optional"):
|
|
162
|
+
print(Formatter.info(" (直接回车跳过此问题)"))
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _read_ask_response(payload: dict[str, Any]) -> dict[str, Any]:
|
|
166
|
+
"""按 question_type 读取终端回答,构造 AskResponseBody 形态。
|
|
167
|
+
|
|
168
|
+
形态与前端 AskUserCard 一致:{answer: str|num|bool|str[]} / {skipped: true, reason}。
|
|
169
|
+
"""
|
|
170
|
+
question_type = payload.get("question_type", "free_text")
|
|
171
|
+
optional = bool(payload.get("optional"))
|
|
172
|
+
try:
|
|
173
|
+
if question_type == "confirm":
|
|
174
|
+
raw = input("[y/N]: ").strip().lower()
|
|
175
|
+
return {"answer": raw in ("y", "yes", "是")}
|
|
176
|
+
if question_type == "choice":
|
|
177
|
+
options = payload.get("options") or []
|
|
178
|
+
raw = input(("(多选,逗号分隔) " if payload.get("multiple") else "") + "选项编号: ").strip()
|
|
179
|
+
if not raw and optional:
|
|
180
|
+
return {"skipped": True, "reason": "user_skipped"}
|
|
181
|
+
indexes = [part.strip() for part in raw.split(",") if part.strip()]
|
|
182
|
+
picked: list[str] = []
|
|
183
|
+
for part in indexes:
|
|
184
|
+
if part.isdigit() and 1 <= int(part) <= len(options):
|
|
185
|
+
picked.append(str(options[int(part) - 1]))
|
|
186
|
+
else:
|
|
187
|
+
picked.append(part)
|
|
188
|
+
if payload.get("multiple"):
|
|
189
|
+
return {"answer": picked}
|
|
190
|
+
return {"answer": picked[0] if picked else ""}
|
|
191
|
+
if question_type == "value":
|
|
192
|
+
value_type = payload.get("value_type", "string")
|
|
193
|
+
raw = input(f"输入值({value_type}): ").strip()
|
|
194
|
+
if not raw and optional:
|
|
195
|
+
return {"skipped": True, "reason": "user_skipped"}
|
|
196
|
+
if value_type in ("integer", "float"):
|
|
197
|
+
try:
|
|
198
|
+
return {"answer": float(raw) if value_type == "float" else int(raw)}
|
|
199
|
+
except ValueError:
|
|
200
|
+
return {"answer": raw}
|
|
201
|
+
if value_type == "boolean":
|
|
202
|
+
return {"answer": raw.lower() in ("y", "yes", "true", "1", "是")}
|
|
203
|
+
return {"answer": raw}
|
|
204
|
+
# free_text
|
|
205
|
+
raw = input("回答: ").strip()
|
|
206
|
+
if not raw and optional:
|
|
207
|
+
return {"skipped": True, "reason": "user_skipped"}
|
|
208
|
+
return {"answer": raw}
|
|
209
|
+
except EOFError:
|
|
210
|
+
return {"skipped": True, "reason": "eof"}
|
|
@@ -275,6 +275,8 @@ AI 助手帮助
|
|
|
275
275
|
- ForeignKey: 外键约束 (如: "关联到 orders 表的 user_id 列")
|
|
276
276
|
- Conditional: 条件约束 (如: "如果 type 等于 A 则值为 B")
|
|
277
277
|
- DateLogic: 日期逻辑约束 (如: "日期晚于 2023-01-01")
|
|
278
|
+
- Charset: 字符集约束 (如: "该列只能包含中文")
|
|
279
|
+
- Composite: 复合约束 (如: "该列必须非空且唯一")
|
|
278
280
|
"""
|
|
279
281
|
print(Formatter.info(help_text))
|
|
280
282
|
|
|
@@ -125,6 +125,15 @@ def execute_ai_chat(
|
|
|
125
125
|
context_window = resolve_context_window(provider_config)
|
|
126
126
|
max_history_tokens, _output_budget = compute_token_budgets(context_window)
|
|
127
127
|
|
|
128
|
+
# Agent 模式终端交互:注入 apply/ask 回调解开 fail-closed 写盘/提问闸门
|
|
129
|
+
# (非交互或 legacy JSON 路径不需要——legacy 走 confirm_callback 确认门)
|
|
130
|
+
agent_apply_callbacks = None
|
|
131
|
+
agent_ask_callbacks = None
|
|
132
|
+
if interactive and agent_mode:
|
|
133
|
+
from app.cli.shell.commands.ai.agent_interaction import build_agent_interaction
|
|
134
|
+
|
|
135
|
+
agent_apply_callbacks, agent_ask_callbacks = build_agent_interaction(spinner)
|
|
136
|
+
|
|
128
137
|
# 配置对话选项
|
|
129
138
|
options = ChatOptions(
|
|
130
139
|
history=history or [],
|
|
@@ -137,6 +146,9 @@ def execute_ai_chat(
|
|
|
137
146
|
agent_mode=agent_mode,
|
|
138
147
|
max_agent_iterations=5,
|
|
139
148
|
canvas_nodes=[],
|
|
149
|
+
apply_callbacks=agent_apply_callbacks,
|
|
150
|
+
ask_callbacks=agent_ask_callbacks,
|
|
151
|
+
dry_run_enabled=agent_apply_callbacks is not None,
|
|
140
152
|
)
|
|
141
153
|
|
|
142
154
|
try:
|
|
@@ -111,6 +111,39 @@ def _parse_standalone_args(args: list[str]) -> dict:
|
|
|
111
111
|
return result
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
def _extract_settings(project_config: dict | None) -> tuple[dict, dict]:
|
|
115
|
+
"""从清单 dump 中提取 (validation, script_security) 设置。
|
|
116
|
+
|
|
117
|
+
兼容两种形态:标准清单 dump(settings 嵌套在 "settings" 键下,open 命令
|
|
118
|
+
的 project_config 即此形态)与直接以 settings 内容为顶层的形态。
|
|
119
|
+
旧实现读顶层 "validation"/"script_security" 在标准形态下恒 miss。
|
|
120
|
+
"""
|
|
121
|
+
if not project_config:
|
|
122
|
+
return {}, {}
|
|
123
|
+
settings = project_config.get("settings")
|
|
124
|
+
if isinstance(settings, dict):
|
|
125
|
+
return settings.get("validation") or {}, settings.get("script_security") or {}
|
|
126
|
+
return project_config.get("validation") or {}, project_config.get("script_security") or {}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _load_manifest_settings(manifest_path: str) -> tuple[dict, dict]:
|
|
130
|
+
"""轻量读取清单 YAML 的 settings 段,供 standalone 模式使用。
|
|
131
|
+
|
|
132
|
+
不做完整模型加载(executor 内部自会加载并报告真正的解析错误);
|
|
133
|
+
文件不可读/非 dict 结构时静默回退默认设置。
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
import yaml
|
|
137
|
+
|
|
138
|
+
with open(manifest_path, encoding="utf-8") as f:
|
|
139
|
+
data = yaml.safe_load(f) or {}
|
|
140
|
+
except Exception:
|
|
141
|
+
return {}, {}
|
|
142
|
+
if not isinstance(data, dict):
|
|
143
|
+
return {}, {}
|
|
144
|
+
return _extract_settings(data)
|
|
145
|
+
|
|
146
|
+
|
|
114
147
|
def _find_dangling_option(args: list[str]) -> str | None:
|
|
115
148
|
"""检测缺值的 standalone 选项(尾部悬挂或值位置是下一个选项)。
|
|
116
149
|
|
|
@@ -236,9 +269,9 @@ class ValidateCommand(Command):
|
|
|
236
269
|
manifest_path = os.path.join(project_path, "project.precis.yaml")
|
|
237
270
|
data_dir = project_path
|
|
238
271
|
|
|
239
|
-
#
|
|
240
|
-
|
|
241
|
-
script_security = context.project_config
|
|
272
|
+
# 从项目配置中读取设置(settings 嵌套在清单 dump 的 "settings" 键下;
|
|
273
|
+
# 旧实现读顶层 "validation" 恒 miss,REPL 模式的 timeout/error_handling 实际从未生效)
|
|
274
|
+
validation_settings, script_security = _extract_settings(context.project_config)
|
|
242
275
|
|
|
243
276
|
return self._run_validation(
|
|
244
277
|
manifest_path,
|
|
@@ -255,7 +288,8 @@ class ValidateCommand(Command):
|
|
|
255
288
|
"""Standalone 模式执行校验。
|
|
256
289
|
|
|
257
290
|
直接从命令行参数获取路径,不依赖项目上下文。
|
|
258
|
-
|
|
291
|
+
读取清单内 settings(validation/script_security),缺省回退默认值
|
|
292
|
+
(timeout=30 / continue / 安全沙箱)。
|
|
259
293
|
|
|
260
294
|
Args:
|
|
261
295
|
parsed: 解析后的参数字典,包含 manifest/data_directory/table/format/report
|
|
@@ -294,8 +328,14 @@ class ValidateCommand(Command):
|
|
|
294
328
|
|
|
295
329
|
table_name = parsed["table"]
|
|
296
330
|
|
|
297
|
-
# standalone
|
|
298
|
-
|
|
331
|
+
# standalone 模式同样读取清单内的 settings(插件/CI 场景改 YAML 的
|
|
332
|
+
# timeout/error_handling 由此生效;旧实现传空 settings 导致配置被忽略,
|
|
333
|
+
# 与 SKILL.md "调整超时/错误处理" 的指引矛盾)。清单不可读时退回默认值,
|
|
334
|
+
# 真正的解析错误由 executor 加载阶段报告。
|
|
335
|
+
validation_settings, script_security = _load_manifest_settings(manifest_path)
|
|
336
|
+
return self._run_validation(
|
|
337
|
+
manifest_path, data_dir, table_name, validation_settings, script_security, output_format, parsed["report"]
|
|
338
|
+
)
|
|
299
339
|
|
|
300
340
|
def _run_validation(
|
|
301
341
|
self,
|
|
@@ -162,7 +162,9 @@ def read_yaml(path: Path) -> dict[str, Any]:
|
|
|
162
162
|
:raises FileNotFoundError: 当指定路径的文件不存在时抛出
|
|
163
163
|
:raises yaml.YAMLError: 当 YAML 格式错误时抛出
|
|
164
164
|
"""
|
|
165
|
-
|
|
165
|
+
# utf-8-sig:兼容记事本等编辑器保存的"UTF-8 带 BOM"(BOM 若混入会被
|
|
166
|
+
# yaml 误认Control Character抛错);无 BOM 时解码行为与 utf-8 完全一致
|
|
167
|
+
with open(path, encoding="utf-8-sig") as f:
|
|
166
168
|
return cast(dict[str, Any], yaml.safe_load(f))
|
|
167
169
|
|
|
168
170
|
|