precis-cli 0.1.3__py3-none-any.whl
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.
- app/__init__.py +16 -0
- app/api/__init__.py +32 -0
- app/api/dependencies.py +185 -0
- app/api/main.py +301 -0
- app/api/middleware/__init__.py +34 -0
- app/api/middleware/exception_handler.py +126 -0
- app/api/middleware/request_logging.py +97 -0
- app/api/middleware/token_auth.py +215 -0
- app/api/models/__init__.py +151 -0
- app/api/models/connection_rules.py +98 -0
- app/api/models/files.py +99 -0
- app/api/models/full_validation.py +554 -0
- app/api/models/project.py +117 -0
- app/api/models/projects.py +80 -0
- app/api/models/schema.py +121 -0
- app/api/models/v2_responses.py +46 -0
- app/api/models/validation.py +303 -0
- app/api/models/workspace.py +118 -0
- app/api/routers/__init__.py +56 -0
- app/api/routers/ai/__init__.py +36 -0
- app/api/routers/ai/chat.py +243 -0
- app/api/routers/ai/generate.py +156 -0
- app/api/routers/ai/hardware.py +199 -0
- app/api/routers/ai/jobs.py +544 -0
- app/api/routers/ai/migrate.py +471 -0
- app/api/routers/ai/models.py +352 -0
- app/api/routers/ai/ollama.py +108 -0
- app/api/routers/ai/providers.py +491 -0
- app/api/routers/ai/router.py +44 -0
- app/api/routers/ai/stream.py +281 -0
- app/api/routers/ai/utils.py +182 -0
- app/api/routers/core/__init__.py +34 -0
- app/api/routers/core/connection_rules.py +188 -0
- app/api/routers/core/data_sources.py +374 -0
- app/api/routers/core/regex.py +333 -0
- app/api/routers/core/reporting.py +263 -0
- app/api/routers/files/__init__.py +24 -0
- app/api/routers/files/ops.py +179 -0
- app/api/routers/files/transfer.py +101 -0
- app/api/routers/preview/__init__.py +35 -0
- app/api/routers/preview/content_mode.py +264 -0
- app/api/routers/preview/header_row.py +154 -0
- app/api/routers/preview/models.py +81 -0
- app/api/routers/preview/path_mode.py +440 -0
- app/api/routers/preview/router.py +30 -0
- app/api/routers/project/__init__.py +61 -0
- app/api/routers/project/base.py +111 -0
- app/api/routers/project/constraint.py +345 -0
- app/api/routers/project/full_config.py +411 -0
- app/api/routers/project/full_config_writer.py +327 -0
- app/api/routers/project/helpers.py +167 -0
- app/api/routers/project/inspection_fix.py +330 -0
- app/api/routers/project/manifest.py +605 -0
- app/api/routers/project/models.py +177 -0
- app/api/routers/project/pattern.py +268 -0
- app/api/routers/project/regex.py +358 -0
- app/api/routers/project/scanner.py +157 -0
- app/api/routers/project/schema.py +567 -0
- app/api/routers/project/schema_helpers.py +149 -0
- app/api/routers/project/settings.py +399 -0
- app/api/routers/project/template.py +325 -0
- app/api/routers/project/validation.py +227 -0
- app/api/routers/project/view.py +177 -0
- app/api/routers/project/workspaces.py +167 -0
- app/api/routers/projects/__init__.py +25 -0
- app/api/routers/projects/create.py +121 -0
- app/api/routers/projects/open.py +103 -0
- app/api/routers/projects/scan.py +100 -0
- app/api/routers/validation/__init__.py +39 -0
- app/api/routers/validation/common.py +263 -0
- app/api/routers/validation/content_mode.py +401 -0
- app/api/routers/validation/history.py +235 -0
- app/api/routers/validation/inline_mode.py +196 -0
- app/api/routers/validation/path_mode.py +314 -0
- app/api/routers/validation/router.py +51 -0
- app/api/services/full_validation_response_builder.py +412 -0
- app/api/services/io_error_messages.py +41 -0
- app/api/services/preview_service.py +248 -0
- app/cli/__init__.py +18 -0
- app/cli/__main__.py +30 -0
- app/cli/shared_services/__init__.py +42 -0
- app/cli/shared_services/config_ops.py +581 -0
- app/cli/shared_services/generation_ops.py +160 -0
- app/cli/shared_services/project_ops.py +288 -0
- app/cli/shell/__init__.py +48 -0
- app/cli/shell/commands/__init__.py +63 -0
- app/cli/shell/commands/ai/__init__.py +260 -0
- app/cli/shell/commands/ai/chat.py +331 -0
- app/cli/shell/commands/ai/delete.py +165 -0
- app/cli/shell/commands/ai/diff.py +70 -0
- app/cli/shell/commands/ai/display.py +428 -0
- app/cli/shell/commands/ai/executor.py +225 -0
- app/cli/shell/commands/ai/executor_utils.py +271 -0
- app/cli/shell/commands/ai/generate.py +285 -0
- app/cli/shell/commands/ai/interaction.py +262 -0
- app/cli/shell/commands/ai/migrate.py +281 -0
- app/cli/shell/commands/ai/resolver.py +224 -0
- app/cli/shell/commands/ai/status.py +101 -0
- app/cli/shell/commands/ai/switch.py +158 -0
- app/cli/shell/commands/ai/utils.py +52 -0
- app/cli/shell/commands/base.py +343 -0
- app/cli/shell/commands/config/__init__.py +131 -0
- app/cli/shell/commands/config/base.py +80 -0
- app/cli/shell/commands/config/check.py +246 -0
- app/cli/shell/commands/config/edit.py +148 -0
- app/cli/shell/commands/config/get.py +111 -0
- app/cli/shell/commands/config/init.py +123 -0
- app/cli/shell/commands/config/inspect.py +164 -0
- app/cli/shell/commands/config/list.py +104 -0
- app/cli/shell/commands/config/set.py +119 -0
- app/cli/shell/commands/config/show.py +150 -0
- app/cli/shell/commands/exit.py +67 -0
- app/cli/shell/commands/help.py +104 -0
- app/cli/shell/commands/infer_schema.py +156 -0
- app/cli/shell/commands/open.py +201 -0
- app/cli/shell/commands/project.py +215 -0
- app/cli/shell/commands/provider.py +619 -0
- app/cli/shell/commands/system.py +170 -0
- app/cli/shell/commands/validate.py +455 -0
- app/cli/shell/completer.py +231 -0
- app/cli/shell/config_storage.py +191 -0
- app/cli/shell/exceptions.py +90 -0
- app/cli/shell/formatter.py +371 -0
- app/cli/shell/interactive_menu.py +348 -0
- app/cli/shell/main.py +286 -0
- app/cli/shell/parser.py +266 -0
- app/cli/start.py +180 -0
- app/cli_main.py +52 -0
- app/mcp_server.py +311 -0
- app/shared/core/__init__.py +18 -0
- app/shared/core/app_version.py +59 -0
- app/shared/core/config/__init__.py +119 -0
- app/shared/core/config/server.py +193 -0
- app/shared/core/data_source/__init__.py +106 -0
- app/shared/core/data_source/loader.py +348 -0
- app/shared/core/data_source/loaders/__init__.py +213 -0
- app/shared/core/data_source/loaders/base.py +215 -0
- app/shared/core/data_source/loaders/converter.py +337 -0
- app/shared/core/data_source/loaders/csv_loader.py +252 -0
- app/shared/core/data_source/loaders/excel_loader.py +666 -0
- app/shared/core/data_source/loaders/extractor.py +268 -0
- app/shared/core/data_source/loaders/json_loader.py +354 -0
- app/shared/core/data_source/loaders/registry.py +88 -0
- app/shared/core/data_source/loaders/sql_loader.py +313 -0
- app/shared/core/data_source/loaders/strategies/__init__.py +100 -0
- app/shared/core/data_source/loaders/strategies/array_parser.py +277 -0
- app/shared/core/data_source/loaders/strategies/lines_parser.py +355 -0
- app/shared/core/data_source/loaders/strategies/object_parser.py +278 -0
- app/shared/core/data_source/schema_info.py +50 -0
- app/shared/core/data_source/specs/__init__.py +128 -0
- app/shared/core/data_source/specs/base.py +258 -0
- app/shared/core/data_source/specs/csv_source.py +137 -0
- app/shared/core/data_source/specs/excel_source.py +141 -0
- app/shared/core/data_source/specs/file_base.py +252 -0
- app/shared/core/data_source/specs/json_source.py +226 -0
- app/shared/core/data_source/specs/sql_source.py +161 -0
- app/shared/core/encoding.py +36 -0
- app/shared/core/io/__init__.py +24 -0
- app/shared/core/io/yaml.py +379 -0
- app/shared/core/manifest_schema/__init__.py +52 -0
- app/shared/core/manifest_schema/types.py +99 -0
- app/shared/core/manifest_schema/version.py +175 -0
- app/shared/core/patterns/__init__.py +24 -0
- app/shared/core/patterns/loader.py +134 -0
- app/shared/core/patterns/writer.py +244 -0
- app/shared/core/project/__init__.py +47 -0
- app/shared/core/project/constraint/builders/__init__.py +50 -0
- app/shared/core/project/constraint/builders/base.py +100 -0
- app/shared/core/project/constraint/builders/composite.py +77 -0
- app/shared/core/project/constraint/builders/conditional.py +67 -0
- app/shared/core/project/constraint/builders/foreign_key.py +53 -0
- app/shared/core/project/constraint/builders/registry.py +64 -0
- app/shared/core/project/constraint/builders/scripted.py +51 -0
- app/shared/core/project/constraint/builders/single_column.py +86 -0
- app/shared/core/project/constraint/builders/unique.py +53 -0
- app/shared/core/project/constraint/factory.py +170 -0
- app/shared/core/project/constraint/reader.py +214 -0
- app/shared/core/project/constraint/registry.py +233 -0
- app/shared/core/project/constraint/types/__init__.py +63 -0
- app/shared/core/project/constraint/types/constraint_file.py +261 -0
- app/shared/core/project/constraint/types/refs.py +460 -0
- app/shared/core/project/constraint/types.py +28 -0
- app/shared/core/project/constraint/writer.py +181 -0
- app/shared/core/project/loader/__init__.py +56 -0
- app/shared/core/project/loader/loader.py +30 -0
- app/shared/core/project/loader/loader_parts/config_inspector.py +137 -0
- app/shared/core/project/loader/loader_parts/embedded_constraints.py +224 -0
- app/shared/core/project/loader/loader_parts/file_loaders.py +58 -0
- app/shared/core/project/loader/loader_parts/inspection_ids.py +85 -0
- app/shared/core/project/loader/loader_parts/inspector_helpers.py +312 -0
- app/shared/core/project/loader/loader_parts/inspector_id_checks.py +274 -0
- app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +690 -0
- app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +286 -0
- app/shared/core/project/loader/loader_parts/loading_error_messages.py +298 -0
- app/shared/core/project/loader/loader_parts/main.py +432 -0
- app/shared/core/project/loader/loader_parts/path_validation.py +117 -0
- app/shared/core/project/loader/loader_parts/runtime.py +125 -0
- app/shared/core/project/loader/types.py +246 -0
- app/shared/core/project/manifest/coverage.py +391 -0
- app/shared/core/project/manifest/reader.py +260 -0
- app/shared/core/project/manifest/types.py +91 -0
- app/shared/core/project/manifest/types_parts/__init__.py +60 -0
- app/shared/core/project/manifest/types_parts/constants.py +40 -0
- app/shared/core/project/manifest/types_parts/data_source.py +68 -0
- app/shared/core/project/manifest/types_parts/info.py +59 -0
- app/shared/core/project/manifest/types_parts/manifest.py +294 -0
- app/shared/core/project/manifest/types_parts/refs.py +153 -0
- app/shared/core/project/manifest/types_parts/settings.py +92 -0
- app/shared/core/project/manifest/types_parts/settings_file_processing.py +64 -0
- app/shared/core/project/manifest/types_parts/settings_script_security.py +78 -0
- app/shared/core/project/manifest/types_parts/settings_validation.py +83 -0
- app/shared/core/project/manifest/types_parts/template.py +66 -0
- app/shared/core/project/manifest/writer.py +262 -0
- app/shared/core/project/manual_data/__init__.py +27 -0
- app/shared/core/project/manual_data/types.py +75 -0
- app/shared/core/project/regex/reader.py +197 -0
- app/shared/core/project/regex/types.py +405 -0
- app/shared/core/project/regex/writer.py +123 -0
- app/shared/core/project/schema/reader.py +170 -0
- app/shared/core/project/schema/types.py +47 -0
- app/shared/core/project/schema/types_parts/__init__.py +36 -0
- app/shared/core/project/schema/types_parts/column.py +174 -0
- app/shared/core/project/schema/types_parts/column_utils.py +72 -0
- app/shared/core/project/schema/types_parts/constraint.py +165 -0
- app/shared/core/project/schema/types_parts/schema_id.py +66 -0
- app/shared/core/project/schema/types_parts/source.py +255 -0
- app/shared/core/project/schema/types_parts/source_options.py +347 -0
- app/shared/core/project/schema/types_parts/table.py +230 -0
- app/shared/core/project/schema/writer.py +139 -0
- app/shared/core/project/schema_ref_check.py +95 -0
- app/shared/core/project/template/__init__.py +27 -0
- app/shared/core/project/template/expander.py +263 -0
- app/shared/core/project/template/reader.py +120 -0
- app/shared/core/project/template/types.py +114 -0
- app/shared/core/project/transform/reader.py +76 -0
- app/shared/core/project/transform/types.py +116 -0
- app/shared/core/project/transform/writer.py +84 -0
- app/shared/core/pydantic_messages.py +59 -0
- app/shared/core/reporter/__init__.py +46 -0
- app/shared/core/reporter/reporter.py +220 -0
- app/shared/core/reporter/reporters/__init__.py +65 -0
- app/shared/core/reporter/reporters/base.py +188 -0
- app/shared/core/reporter/reporters/dingtalk_app_reporter.py +274 -0
- app/shared/core/reporter/reporters/email_reporter.py +271 -0
- app/shared/core/reporter/reporters/feishu_app_reporter.py +467 -0
- app/shared/core/reporter/reporters/local_file_reporter.py +208 -0
- app/shared/core/reporter/reporters/wecom_app_reporter.py +268 -0
- app/shared/core/utils/__init__.py +18 -0
- app/shared/core/utils/path_utils.py +60 -0
- app/shared/core/utils/regex_extract.py +113 -0
- app/shared/domain/__init__.py +114 -0
- app/shared/domain/constraints/__init__.py +76 -0
- app/shared/domain/constraints/allowed_values.py +211 -0
- app/shared/domain/constraints/base.py +141 -0
- app/shared/domain/constraints/charset.py +337 -0
- app/shared/domain/constraints/composite.py +174 -0
- app/shared/domain/constraints/condition_registry.py +130 -0
- app/shared/domain/constraints/conditional.py +629 -0
- app/shared/domain/constraints/date_logic.py +731 -0
- app/shared/domain/constraints/foreign_key.py +261 -0
- app/shared/domain/constraints/key_normalization.py +56 -0
- app/shared/domain/constraints/not_null.py +185 -0
- app/shared/domain/constraints/range.py +360 -0
- app/shared/domain/constraints/regex.py +218 -0
- app/shared/domain/constraints/scripted.py +276 -0
- app/shared/domain/constraints/unique.py +233 -0
- app/shared/domain/data_engine.py +384 -0
- app/shared/domain/data_types.py +113 -0
- app/shared/domain/data_types_parts/__init__.py +67 -0
- app/shared/domain/data_types_parts/base.py +204 -0
- app/shared/domain/data_types_parts/composite.py +250 -0
- app/shared/domain/data_types_parts/expression.py +202 -0
- app/shared/domain/data_types_parts/extracted.py +106 -0
- app/shared/domain/data_types_parts/json_types.py +234 -0
- app/shared/domain/data_types_parts/scalars.py +719 -0
- app/shared/domain/data_types_parts/sequence.py +129 -0
- app/shared/domain/dataset_schema.py +48 -0
- app/shared/domain/eval_sandbox.py +63 -0
- app/shared/domain/expression_system.py +366 -0
- app/shared/domain/regex_flags.py +53 -0
- app/shared/domain/schema/builder.py +223 -0
- app/shared/domain/schema/models.py +339 -0
- app/shared/domain/transforms/__init__.py +28 -0
- app/shared/domain/transforms/aggregate.py +141 -0
- app/shared/domain/transforms/base.py +171 -0
- app/shared/domain/transforms/cast_type.py +120 -0
- app/shared/domain/transforms/concat.py +103 -0
- app/shared/domain/transforms/conditional_assign.py +114 -0
- app/shared/domain/transforms/date_format.py +81 -0
- app/shared/domain/transforms/digits.py +77 -0
- app/shared/domain/transforms/drop_duplicates.py +94 -0
- app/shared/domain/transforms/fill_na.py +94 -0
- app/shared/domain/transforms/filter_rows.py +97 -0
- app/shared/domain/transforms/lookup.py +78 -0
- app/shared/domain/transforms/lower_case.py +70 -0
- app/shared/domain/transforms/map_value.py +90 -0
- app/shared/domain/transforms/math_expr.py +112 -0
- app/shared/domain/transforms/modulo.py +82 -0
- app/shared/domain/transforms/regex_extract.py +103 -0
- app/shared/domain/transforms/registry.py +95 -0
- app/shared/domain/transforms/replace.py +88 -0
- app/shared/domain/transforms/sort_rows.py +94 -0
- app/shared/domain/transforms/string_split.py +84 -0
- app/shared/domain/transforms/strip.py +73 -0
- app/shared/domain/transforms/substring.py +92 -0
- app/shared/domain/transforms/upper_case.py +70 -0
- app/shared/domain/transforms/weighted_sum.py +104 -0
- app/shared/domain/validation_constraints.py +69 -0
- app/shared/services/__init__.py +62 -0
- app/shared/services/ai/__init__.py +48 -0
- app/shared/services/ai/agent/__init__.py +40 -0
- app/shared/services/ai/agent/chat_tools/__init__.py +47 -0
- app/shared/services/ai/agent/chat_tools/apply_actions.py +592 -0
- app/shared/services/ai/agent/chat_tools/ask_user.py +237 -0
- app/shared/services/ai/agent/chat_tools/read_canvas.py +140 -0
- app/shared/services/ai/agent/chat_tools/read_project.py +160 -0
- app/shared/services/ai/agent/chat_tools/read_table.py +301 -0
- app/shared/services/ai/agent/chat_tools/schemas.py +105 -0
- app/shared/services/ai/agent/chat_tools/validate_table.py +131 -0
- app/shared/services/ai/agent/executor.py +554 -0
- app/shared/services/ai/agent/memory.py +256 -0
- app/shared/services/ai/agent/planner.py +282 -0
- app/shared/services/ai/agent/tool_registry.py +296 -0
- app/shared/services/ai/agent/tools/__init__.py +32 -0
- app/shared/services/ai/agent/tools/config_generate.py +133 -0
- app/shared/services/ai/agent/tools/config_refine.py +109 -0
- app/shared/services/ai/agent/tools/config_validate.py +403 -0
- app/shared/services/ai/agent/tools/merge_results.py +136 -0
- app/shared/services/ai/agent/tools/plan_chunks.py +79 -0
- app/shared/services/ai/agent/tools/script_parse.py +359 -0
- app/shared/services/ai/agent/types.py +138 -0
- app/shared/services/ai/chat_agent_runner.py +596 -0
- app/shared/services/ai/chat_orchestrator.py +725 -0
- app/shared/services/ai/failure_messages.py +65 -0
- app/shared/services/ai/job_storage.py +274 -0
- app/shared/services/ai/migrate_service.py +550 -0
- app/shared/services/ai/streaming/__init__.py +65 -0
- app/shared/services/ai/streaming/event_journal.py +197 -0
- app/shared/services/ai/streaming/orchestrator.py +262 -0
- app/shared/services/ai/streaming/pending_interaction_store.py +227 -0
- app/shared/services/ai/streaming/sse_response.py +148 -0
- app/shared/services/ai/streaming/types.py +73 -0
- app/shared/services/ai/types.py +213 -0
- app/shared/services/ai/utils.py +322 -0
- app/shared/services/diff/config_diff.py +320 -0
- app/shared/services/hardware.py +237 -0
- app/shared/services/llm/__init__.py +62 -0
- app/shared/services/llm/actions/__init__.py +42 -0
- app/shared/services/llm/actions/_canvas_validator.py +147 -0
- app/shared/services/llm/actions/_constraint_validator.py +401 -0
- app/shared/services/llm/actions/_regex_validator.py +85 -0
- app/shared/services/llm/actions/_schema_validator.py +82 -0
- app/shared/services/llm/actions/_settings_validator.py +76 -0
- app/shared/services/llm/actions/_transform_validator.py +78 -0
- app/shared/services/llm/actions/action_handlers.py +400 -0
- app/shared/services/llm/actions/action_parser.py +63 -0
- app/shared/services/llm/actions/action_processor.py +468 -0
- app/shared/services/llm/actions/action_validator.py +311 -0
- app/shared/services/llm/actions/diff_compute.py +195 -0
- app/shared/services/llm/actions/regex_handlers.py +284 -0
- app/shared/services/llm/actions/registry.py +336 -0
- app/shared/services/llm/actions/schema_handlers.py +335 -0
- app/shared/services/llm/actions/settings_handlers.py +180 -0
- app/shared/services/llm/actions/specs.py +259 -0
- app/shared/services/llm/actions/transform_handlers.py +279 -0
- app/shared/services/llm/actions/validation_types.py +147 -0
- app/shared/services/llm/cache/__init__.py +18 -0
- app/shared/services/llm/cache/response_cache.py +80 -0
- app/shared/services/llm/chat/__init__.py +35 -0
- app/shared/services/llm/chat/chat_system_prompt.py +561 -0
- app/shared/services/llm/chat/response_parser.py +296 -0
- app/shared/services/llm/config/__init__.py +45 -0
- app/shared/services/llm/config/crypto.py +135 -0
- app/shared/services/llm/config/loader.py +258 -0
- app/shared/services/llm/config/models.py +202 -0
- app/shared/services/llm/config/presets.py +128 -0
- app/shared/services/llm/config_generator.py +163 -0
- app/shared/services/llm/constraints/__init__.py +41 -0
- app/shared/services/llm/constraints/constraint_builder.py +177 -0
- app/shared/services/llm/constraints/constraint_deletion.py +84 -0
- app/shared/services/llm/constraints/constraint_id.py +174 -0
- app/shared/services/llm/constraints/frontend_instructions.py +333 -0
- app/shared/services/llm/constraints/inline_batch.py +279 -0
- app/shared/services/llm/discovery/__init__.py +35 -0
- app/shared/services/llm/discovery/scanner.py +181 -0
- app/shared/services/llm/generation/__init__.py +42 -0
- app/shared/services/llm/generation/agent_wiring.py +156 -0
- app/shared/services/llm/generation/config_builder.py +386 -0
- app/shared/services/llm/generation/errors.py +36 -0
- app/shared/services/llm/generation/existing_config.py +99 -0
- app/shared/services/llm/generation/profiler.py +279 -0
- app/shared/services/llm/generation/prompt_builder.py +199 -0
- app/shared/services/llm/generation/response_parser.py +144 -0
- app/shared/services/llm/generation/service.py +622 -0
- app/shared/services/llm/models.py +104 -0
- app/shared/services/llm/providers/__init__.py +48 -0
- app/shared/services/llm/providers/base.py +310 -0
- app/shared/services/llm/providers/cached_provider.py +90 -0
- app/shared/services/llm/providers/ollama.py +498 -0
- app/shared/services/llm/providers/openai.py +334 -0
- app/shared/services/llm/providers/registry.py +108 -0
- app/shared/services/llm/schema_resolver.py +141 -0
- app/shared/services/llm/suggestion_utils.py +219 -0
- app/shared/services/llm/validate_executor.py +163 -0
- app/shared/services/llm/yaml_io.py +406 -0
- app/shared/services/preview/__init__.py +18 -0
- app/shared/services/preview/loader.py +145 -0
- app/shared/services/preview/path_validation.py +138 -0
- app/shared/services/project_loader.py +57 -0
- app/shared/services/schema_inference.py +260 -0
- app/shared/services/schema_runtime_builder.py +120 -0
- app/shared/services/validation/__init__.py +52 -0
- app/shared/services/validation/chunked_loader.py +509 -0
- app/shared/services/validation/dag/__init__.py +35 -0
- app/shared/services/validation/dag/builder.py +141 -0
- app/shared/services/validation/dag/executor.py +225 -0
- app/shared/services/validation/dag/sorter.py +77 -0
- app/shared/services/validation/data_loader.py +231 -0
- app/shared/services/validation/engine.py +446 -0
- app/shared/services/validation/executor.py +1030 -0
- app/shared/services/validation/extractors.py +266 -0
- app/shared/services/validation/history.py +209 -0
- app/shared/services/validation/json_payload.py +136 -0
- app/shared/services/validation/loader.py +152 -0
- app/shared/services/validation/memory_monitor.py +179 -0
- app/shared/services/validation/postprocess.py +208 -0
- app/shared/services/validation/progress.py +64 -0
- app/shared/services/validation/report_export.py +222 -0
- app/shared/services/validation/resolver.py +180 -0
- app/shared/services/validation/service.py +418 -0
- app/shared/services/validation/types.py +238 -0
- app/shared/services/validation/validators/__init__.py +36 -0
- app/shared/services/validation/validators/adapter.py +182 -0
- app/shared/services/validation/validators/base.py +332 -0
- app/shared/services/validation/validators/composite.py +233 -0
- app/shared/services/validation/validators/date_logic.py +276 -0
- app/start_server.py +133 -0
- precis_cli-0.1.3.dist-info/METADATA +180 -0
- precis_cli-0.1.3.dist-info/RECORD +442 -0
- precis_cli-0.1.3.dist-info/WHEEL +5 -0
- precis_cli-0.1.3.dist-info/entry_points.txt +4 -0
- precis_cli-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,163 @@
|
|
|
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 V2 YAML 配置生成器兼容模块
|
|
17
|
+
|
|
18
|
+
功能概述:
|
|
19
|
+
- 提供配置生成服务的向后兼容入口(generate_full_config_v2)
|
|
20
|
+
- 封装新版 ConfigGenerationService,适配旧版参数接口
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import asyncio
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
from app.shared.services.llm.generation import (
|
|
29
|
+
ConfigGenerationService,
|
|
30
|
+
GenerationOptions,
|
|
31
|
+
ProfilingOptions,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# V2 清单文件名常量
|
|
35
|
+
V2_MANIFEST_FILENAME = "project.precis.yaml"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def generate_full_config_v2(
|
|
39
|
+
data_paths: list[str],
|
|
40
|
+
output_dir: str,
|
|
41
|
+
profiling_options: ProfilingOptions | None = None,
|
|
42
|
+
generation_options: GenerationOptions | None = None,
|
|
43
|
+
provider_id: str | None = None,
|
|
44
|
+
) -> dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
@methoddesc 生成完整 V2 项目配置(向后兼容的适配函数)
|
|
47
|
+
|
|
48
|
+
内部使用新版 ConfigGenerationService,将旧版参数名适配为新接口:
|
|
49
|
+
- data_paths -> file_paths
|
|
50
|
+
- output_dir -> config_path
|
|
51
|
+
|
|
52
|
+
参数:
|
|
53
|
+
data_paths: 数据文件路径列表
|
|
54
|
+
output_dir: 配置输出目录
|
|
55
|
+
profiling_options: 数据画像选项,None 则使用默认值
|
|
56
|
+
generation_options: 生成选项,None 则使用默认值
|
|
57
|
+
provider_id: 指定使用的 AI Provider ID
|
|
58
|
+
|
|
59
|
+
返回:
|
|
60
|
+
生成结果字典,包含 manifest、schemas、constraints 等
|
|
61
|
+
"""
|
|
62
|
+
service = ConfigGenerationService(provider_id=provider_id)
|
|
63
|
+
|
|
64
|
+
if profiling_options is None:
|
|
65
|
+
profiling_options = ProfilingOptions()
|
|
66
|
+
if generation_options is None:
|
|
67
|
+
generation_options = GenerationOptions()
|
|
68
|
+
|
|
69
|
+
# 适配旧参数名:data_paths -> file_paths,output_dir -> config_path
|
|
70
|
+
return asyncio.run(
|
|
71
|
+
service.generate(
|
|
72
|
+
file_paths=data_paths,
|
|
73
|
+
project_name="generated_project",
|
|
74
|
+
project_id="generated_project",
|
|
75
|
+
config_path=output_dir,
|
|
76
|
+
profiling_options=profiling_options,
|
|
77
|
+
generation_options=generation_options,
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def expand_data_input_paths(paths: list[str]) -> list[str]:
|
|
83
|
+
"""
|
|
84
|
+
@methoddesc 展开数据输入路径
|
|
85
|
+
|
|
86
|
+
如果传入的是目录,递归遍历其中所有支持的数据文件;
|
|
87
|
+
如果传入的是文件,直接保留。
|
|
88
|
+
|
|
89
|
+
参数:
|
|
90
|
+
paths: 路径列表(文件或目录)
|
|
91
|
+
|
|
92
|
+
返回:
|
|
93
|
+
展开后的文件路径列表
|
|
94
|
+
|
|
95
|
+
示例:
|
|
96
|
+
>>> expand_data_input_paths(["./data"])
|
|
97
|
+
['./data/users.xlsx', './data/orders.csv']
|
|
98
|
+
"""
|
|
99
|
+
import os
|
|
100
|
+
|
|
101
|
+
expanded = []
|
|
102
|
+
for path in paths:
|
|
103
|
+
if os.path.isdir(path):
|
|
104
|
+
# 递归遍历目录,收集所有支持的数据文件
|
|
105
|
+
for root, dirs, files in os.walk(path):
|
|
106
|
+
for file in files:
|
|
107
|
+
if file.endswith((".xlsx", ".csv", ".json", ".yaml", ".yml")):
|
|
108
|
+
expanded.append(os.path.join(root, file))
|
|
109
|
+
elif os.path.isfile(path):
|
|
110
|
+
expanded.append(path)
|
|
111
|
+
return expanded
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def profile_files(file_paths: list[str], options: ProfilingOptions | None = None) -> dict[str, Any]:
|
|
115
|
+
"""
|
|
116
|
+
@methoddesc 分析文件画像
|
|
117
|
+
|
|
118
|
+
收集每个文件的基本信息(大小、扩展名),用于快速了解数据输入。
|
|
119
|
+
|
|
120
|
+
参数:
|
|
121
|
+
file_paths: 文件路径列表
|
|
122
|
+
options: 画像选项,None 则使用默认值
|
|
123
|
+
|
|
124
|
+
返回:
|
|
125
|
+
画像结果字典,包含 files 列表、total_count 和 sample
|
|
126
|
+
"""
|
|
127
|
+
import os
|
|
128
|
+
|
|
129
|
+
if options is None:
|
|
130
|
+
options = ProfilingOptions()
|
|
131
|
+
|
|
132
|
+
profiles = []
|
|
133
|
+
for file_path in file_paths:
|
|
134
|
+
if os.path.exists(file_path):
|
|
135
|
+
stat = os.stat(file_path)
|
|
136
|
+
profiles.append(
|
|
137
|
+
{
|
|
138
|
+
"path": file_path,
|
|
139
|
+
"size": stat.st_size,
|
|
140
|
+
"extension": os.path.splitext(file_path)[1],
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
"files": profiles,
|
|
146
|
+
"total_count": len(profiles),
|
|
147
|
+
"sample": profiles[: options.sample_rows] if profiles else [],
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# 为了向后兼容,保留旧名称
|
|
152
|
+
GenerateOptions = GenerationOptions
|
|
153
|
+
|
|
154
|
+
__all__ = [
|
|
155
|
+
"ConfigGenerationService",
|
|
156
|
+
"GenerationOptions",
|
|
157
|
+
"GenerateOptions", # 别名
|
|
158
|
+
"ProfilingOptions",
|
|
159
|
+
"V2_MANIFEST_FILENAME",
|
|
160
|
+
"generate_full_config_v2",
|
|
161
|
+
"expand_data_input_paths",
|
|
162
|
+
"profile_files",
|
|
163
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
"""
|
|
17
|
+
@fileoverview 约束文件操作子包
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- constraint_builder: 构建约束 refs/params 结构
|
|
21
|
+
- constraint_deletion: 删除独立约束文件
|
|
22
|
+
- constraint_id: 生成语义化约束 ID
|
|
23
|
+
- frontend_instructions: 生成前端渲染指令
|
|
24
|
+
- inline_batch: 批量处理内联约束(减少 IO)
|
|
25
|
+
|
|
26
|
+
架构设计:
|
|
27
|
+
- 工具函数集合: 各模块提供单一职责的纯函数
|
|
28
|
+
- 协作模式: builder + id_gen 被 handlers 和 inline_batch 共享调用
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from app.shared.services.llm.constraints.constraint_builder import CONSTRAINT_TYPE_MAP
|
|
32
|
+
from app.shared.services.llm.constraints.constraint_deletion import delete_constraint_file
|
|
33
|
+
from app.shared.services.llm.constraints.frontend_instructions import generate_frontend_instructions
|
|
34
|
+
from app.shared.services.llm.constraints.inline_batch import process_inline_batch
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"CONSTRAINT_TYPE_MAP",
|
|
38
|
+
"delete_constraint_file",
|
|
39
|
+
"generate_frontend_instructions",
|
|
40
|
+
"process_inline_batch",
|
|
41
|
+
]
|
|
@@ -0,0 +1,177 @@
|
|
|
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
|
+
"""
|
|
17
|
+
@fileoverview 约束结构构建模块
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 根据约束类型构建约束的 refs(引用区)结构
|
|
21
|
+
- 根据约束类型构建约束的 params(参数区)结构
|
|
22
|
+
- 处理类型标准化和名称到 ID 的 Fallback 解析
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
import re
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
from app.shared.services.llm.schema_resolver import _resolve_id_from_name
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
# 约束类型别名映射(大写 → PascalCase 标准名)。
|
|
36
|
+
# 单一事实源是 actions/registry.py 的 CONSTRAINT_TYPE_ALIASES;此处保持同步副本。
|
|
37
|
+
# 历史问题:旧副本缺 CHARSET/COMPOSITE,已补齐。直接 import registry 会形成循环导入
|
|
38
|
+
# (actions/__init__ → action_handlers → constraint_builder → actions 包),故维持副本。
|
|
39
|
+
# 新增约束类型时,两处都必须同步更新。
|
|
40
|
+
CONSTRAINT_TYPE_MAP = {
|
|
41
|
+
"NOT_NULL": "NotNull",
|
|
42
|
+
"UNIQUE": "Unique",
|
|
43
|
+
"ALLOWED_VALUES": "AllowedValues",
|
|
44
|
+
"RANGE": "Range",
|
|
45
|
+
"REGEX": "Scripted",
|
|
46
|
+
"FOREIGN_KEY": "ForeignKey",
|
|
47
|
+
"CONDITIONAL": "Conditional",
|
|
48
|
+
"DATE_LOGIC": "DateLogic",
|
|
49
|
+
"CHARSET": "Charset",
|
|
50
|
+
"COMPOSITE": "Composite",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _build_constraint_refs(
|
|
55
|
+
constraint_type: str, table_name: str, column_name: str, constraint_spec: dict[str, Any], workspace_path: str = ""
|
|
56
|
+
) -> dict[str, Any]:
|
|
57
|
+
"""
|
|
58
|
+
@methoddesc 构建约束的 refs 字段
|
|
59
|
+
|
|
60
|
+
业务用途:
|
|
61
|
+
- 根据约束类型(NotNull/Unique/ForeignKey/...)组装符合 YAML 规范的 refs 字典
|
|
62
|
+
- 必要时根据工作区路径将名称回退解析为 table_id / column_id
|
|
63
|
+
|
|
64
|
+
参数:
|
|
65
|
+
constraint_type: AI 给出的原始约束类型(可能大小写不规范)
|
|
66
|
+
table_name: 表名(用于名称回退)
|
|
67
|
+
column_name: 列名(用于名称回退)
|
|
68
|
+
constraint_spec: AI 输出的 spec 字典
|
|
69
|
+
workspace_path: 工作区路径,启用名称回退
|
|
70
|
+
|
|
71
|
+
返回:
|
|
72
|
+
refs 字典,结构因约束类型而异
|
|
73
|
+
"""
|
|
74
|
+
std_type = CONSTRAINT_TYPE_MAP.get(constraint_type, constraint_type)
|
|
75
|
+
table_id = constraint_spec.get("targetNodeId")
|
|
76
|
+
column_id = constraint_spec.get("targetColumnId")
|
|
77
|
+
|
|
78
|
+
if workspace_path and table_name:
|
|
79
|
+
fallback_table_id, fallback_column_id = _resolve_id_from_name(
|
|
80
|
+
workspace_path, table_name, column_name or column_id
|
|
81
|
+
)
|
|
82
|
+
if fallback_table_id:
|
|
83
|
+
table_id = fallback_table_id
|
|
84
|
+
if fallback_column_id:
|
|
85
|
+
column_id = fallback_column_id
|
|
86
|
+
|
|
87
|
+
if std_type == "NotNull":
|
|
88
|
+
return {"table_id": table_id, "column_id": column_id}
|
|
89
|
+
elif std_type == "Unique":
|
|
90
|
+
return {"table_id": table_id, "column_ids": [column_id]}
|
|
91
|
+
elif std_type == "ForeignKey":
|
|
92
|
+
params = constraint_spec.get("params", {})
|
|
93
|
+
return {
|
|
94
|
+
"from_table_id": table_id,
|
|
95
|
+
"from_column_id": column_id,
|
|
96
|
+
"to_table_id": params.get("toTableId", ""),
|
|
97
|
+
"to_column_id": params.get("toColumnId", ""),
|
|
98
|
+
}
|
|
99
|
+
elif std_type == "Conditional":
|
|
100
|
+
params = constraint_spec.get("params", {})
|
|
101
|
+
then_column_id = constraint_spec.get("targetColumnId", column_name)
|
|
102
|
+
if_conditions = params.get("ifConditions", [])
|
|
103
|
+
formatted_conditions = []
|
|
104
|
+
for cond in if_conditions:
|
|
105
|
+
formatted_conditions.append(
|
|
106
|
+
{
|
|
107
|
+
"if_column_id": cond.get("ifColumnId", ""),
|
|
108
|
+
"operator": cond.get("operator", "eq"),
|
|
109
|
+
"value": cond.get("value"),
|
|
110
|
+
"values": cond.get("values"),
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
return {
|
|
114
|
+
"table_id": table_id,
|
|
115
|
+
"then_column_id": then_column_id,
|
|
116
|
+
"if_conditions": formatted_conditions,
|
|
117
|
+
"if_logic": params.get("ifLogic", "and"),
|
|
118
|
+
}
|
|
119
|
+
else:
|
|
120
|
+
return {"table_id": table_id, "column_id": column_id}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _build_constraint_params(constraint_type: str, constraint_spec: dict[str, Any]) -> dict[str, Any]:
|
|
124
|
+
"""
|
|
125
|
+
@methoddesc 构建约束的 params 字段
|
|
126
|
+
|
|
127
|
+
业务用途:
|
|
128
|
+
- 将 AI 输出的 camelCase 字段(allowedValues, min/max, logicMode 等)映射为 YAML 标准的 snake_case 字段
|
|
129
|
+
- Scripted 约束支持从 pattern 自动生成 expression
|
|
130
|
+
|
|
131
|
+
参数:
|
|
132
|
+
constraint_type: 原始约束类型
|
|
133
|
+
constraint_spec: AI 输出的 spec 字典
|
|
134
|
+
|
|
135
|
+
返回:
|
|
136
|
+
params 字典
|
|
137
|
+
"""
|
|
138
|
+
std_type = CONSTRAINT_TYPE_MAP.get(constraint_type, constraint_type)
|
|
139
|
+
params = constraint_spec.get("params", {})
|
|
140
|
+
|
|
141
|
+
if std_type == "AllowedValues":
|
|
142
|
+
return {"allowed_values": params.get("allowedValues", [])}
|
|
143
|
+
elif std_type == "Range":
|
|
144
|
+
return {"min": params.get("min"), "max": params.get("max")}
|
|
145
|
+
elif std_type == "Scripted":
|
|
146
|
+
pattern = params.get("pattern")
|
|
147
|
+
expression = params.get("expression")
|
|
148
|
+
if pattern and not expression:
|
|
149
|
+
safe_pattern = re.escape(pattern)
|
|
150
|
+
# repr 生成自带引号转义的字符串字面量,pattern 含单/双引号均安全
|
|
151
|
+
expression = f"re.match({safe_pattern!r}, str(value)) is not None"
|
|
152
|
+
if not expression:
|
|
153
|
+
# 空表达式不再兜底为 "True"(恒真约束会静默失效还报成功),
|
|
154
|
+
# 抛出让上层按失败上报且不落盘。三个调用方(update_yaml_config 内联/独立分支、
|
|
155
|
+
# process_inline_batch)均有 except Exception 兜底,会把异常转为该动作的失败结果。
|
|
156
|
+
raise ValueError(
|
|
157
|
+
"Scripted 约束缺少有效参数: 需要 params.expression 或 params.pattern 之一(恒真表达式已被禁止)"
|
|
158
|
+
)
|
|
159
|
+
return {"expression": expression}
|
|
160
|
+
elif std_type == "DateLogic":
|
|
161
|
+
result: dict[str, Any] = {
|
|
162
|
+
"logic_mode": params.get("logicMode", "compare"),
|
|
163
|
+
"compare_op": params.get("compareOp", "gt"),
|
|
164
|
+
}
|
|
165
|
+
if params.get("compareOp") == "range":
|
|
166
|
+
result["reference_date"] = params.get("referenceDate", "")
|
|
167
|
+
result["reference_date_end"] = params.get("referenceDateEnd", "")
|
|
168
|
+
result["reference_column"] = params.get("referenceColumn", "")
|
|
169
|
+
result["reference_column_end"] = params.get("referenceColumnEnd", "")
|
|
170
|
+
else:
|
|
171
|
+
result["reference_date"] = params.get("referenceDate", "")
|
|
172
|
+
result["reference_column"] = params.get("referenceColumn", "")
|
|
173
|
+
return result
|
|
174
|
+
elif std_type == "Conditional":
|
|
175
|
+
return {"then_value": params.get("thenValue")}
|
|
176
|
+
else:
|
|
177
|
+
return {}
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
"""
|
|
17
|
+
@fileoverview 约束文件删除模块
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 根据约束类型、表名和列名生成约束 ID
|
|
21
|
+
- 删除对应的 .constraint.yaml 文件
|
|
22
|
+
- 使用文件锁保护删除操作
|
|
23
|
+
|
|
24
|
+
输入示例:
|
|
25
|
+
success, message = delete_constraint_file("NotNull", "users", "email", "/workspace")
|
|
26
|
+
|
|
27
|
+
输出示例:
|
|
28
|
+
(True, "notnull_users_email")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import logging
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
from app.shared.services.llm.constraints.constraint_id import _generate_constraint_id
|
|
37
|
+
from app.shared.services.llm.yaml_io import FileLock
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def delete_constraint_file(
|
|
43
|
+
constraint_type: str, table_name: str, column_name: str, workspace_path: str
|
|
44
|
+
) -> tuple[bool, str]:
|
|
45
|
+
"""
|
|
46
|
+
@methoddesc 删除独立约束文件
|
|
47
|
+
|
|
48
|
+
根据约束类型、表名和列名生成约束 ID,然后删除对应的 .constraint.yaml 文件。
|
|
49
|
+
使用文件锁保护删除操作,防止并发冲突。
|
|
50
|
+
|
|
51
|
+
参数:
|
|
52
|
+
constraint_type: 约束类型
|
|
53
|
+
table_name: 表名
|
|
54
|
+
column_name: 列名
|
|
55
|
+
workspace_path: 工作区路径
|
|
56
|
+
|
|
57
|
+
返回:
|
|
58
|
+
元组 (是否成功, 约束ID 或错误信息)
|
|
59
|
+
|
|
60
|
+
示例:
|
|
61
|
+
>>> success, msg = delete_constraint_file("NotNull", "users", "email", "/workspace")
|
|
62
|
+
>>> success
|
|
63
|
+
True
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
# 根据类型、表、列生成预期的约束文件 ID
|
|
67
|
+
constraint_id = _generate_constraint_id(constraint_type, table_name, column_name)
|
|
68
|
+
constraint_file_path = Path(workspace_path) / "constraints" / f"{constraint_id}.constraint.yaml"
|
|
69
|
+
|
|
70
|
+
# 使用文件锁保护删除操作,防止并发冲突
|
|
71
|
+
with FileLock(str(constraint_file_path)):
|
|
72
|
+
if constraint_file_path.exists():
|
|
73
|
+
constraint_file_path.unlink()
|
|
74
|
+
logger.info(f"[updateYamlConfig] 成功删除约束文件: {constraint_file_path}")
|
|
75
|
+
return True, constraint_id
|
|
76
|
+
else:
|
|
77
|
+
error_msg = f"约束文件不存在: {constraint_file_path}"
|
|
78
|
+
logger.warning(f"[updateYamlConfig] {error_msg}")
|
|
79
|
+
return False, error_msg
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
error_msg = f"删除约束文件失败: {str(e)}"
|
|
83
|
+
logger.error(f"[updateYamlConfig] {error_msg}")
|
|
84
|
+
return False, error_msg
|
|
@@ -0,0 +1,174 @@
|
|
|
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 约束 ID 生成模块"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import re
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _generate_constraint_id(constraint_type: str, table_name: str, column_name: str) -> str:
|
|
27
|
+
"""
|
|
28
|
+
@methoddesc 生成符合命名规范的约束 ID
|
|
29
|
+
|
|
30
|
+
业务用途:
|
|
31
|
+
- 拼接 {type}_{table}_{column} 形式的 ID
|
|
32
|
+
- 中文表/列名通过 _chinese_to_abbr 转拼音或内建映射
|
|
33
|
+
|
|
34
|
+
参数:
|
|
35
|
+
constraint_type: 约束类型(如 "NotNull" / "Unique")
|
|
36
|
+
table_name: 表名
|
|
37
|
+
column_name: 列名
|
|
38
|
+
|
|
39
|
+
返回:
|
|
40
|
+
标准化后的约束 ID 字符串
|
|
41
|
+
"""
|
|
42
|
+
type_prefix = constraint_type.lower()
|
|
43
|
+
|
|
44
|
+
if re.match(r"^[\u4e00-\u9fff]+$", column_name):
|
|
45
|
+
safe_column = _chinese_to_abbr(column_name) or "col"
|
|
46
|
+
else:
|
|
47
|
+
safe_column = re.sub(r"[^a-zA-Z0-9_]", "_", column_name).strip("_")
|
|
48
|
+
|
|
49
|
+
if re.match(r"^[\u4e00-\u9fff]+$", table_name):
|
|
50
|
+
table_abbr = _chinese_to_abbr(table_name)
|
|
51
|
+
else:
|
|
52
|
+
# §2.4: 不再截断到 10 字符——customers_eu_2024 与 customers_us_2024 截断后
|
|
53
|
+
# 同前缀,同列同类型算出同一 ID,第二个约束静默覆盖第一个
|
|
54
|
+
safe_table = re.sub(r"[^a-zA-Z0-9_]", "_", table_name).strip("_")
|
|
55
|
+
table_abbr = safe_table
|
|
56
|
+
|
|
57
|
+
if table_abbr:
|
|
58
|
+
return f"{type_prefix}_{table_abbr}_{safe_column}"
|
|
59
|
+
else:
|
|
60
|
+
return f"{type_prefix}_{safe_column}"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _chinese_to_abbr(text: str) -> str:
|
|
64
|
+
"""
|
|
65
|
+
@methoddesc 中文文本到拼音/英文缩写
|
|
66
|
+
|
|
67
|
+
业务用途:
|
|
68
|
+
- 内建常用业务词映射(订单→order, 用户→user 等)
|
|
69
|
+
- 不在内建表中的词使用 pypinyin 转换(若可用),否则返回原文本
|
|
70
|
+
|
|
71
|
+
参数:
|
|
72
|
+
text: 中文文本
|
|
73
|
+
|
|
74
|
+
返回:
|
|
75
|
+
拼音或英文缩写
|
|
76
|
+
"""
|
|
77
|
+
mappings = {
|
|
78
|
+
"订单": "order",
|
|
79
|
+
"订单表": "order",
|
|
80
|
+
"用户": "user",
|
|
81
|
+
"用户表": "user",
|
|
82
|
+
"产品": "prod",
|
|
83
|
+
"产品表": "prod",
|
|
84
|
+
"商品": "prod",
|
|
85
|
+
"商品表": "prod",
|
|
86
|
+
"分类": "cat",
|
|
87
|
+
"分类表": "cat",
|
|
88
|
+
"部门": "dept",
|
|
89
|
+
"部门表": "dept",
|
|
90
|
+
"员工": "emp",
|
|
91
|
+
"员工表": "emp",
|
|
92
|
+
"供应商": "supplier",
|
|
93
|
+
"供应商表": "supplier",
|
|
94
|
+
"客户": "customer",
|
|
95
|
+
"客户表": "customer",
|
|
96
|
+
"名称": "name",
|
|
97
|
+
"姓名": "name",
|
|
98
|
+
"邮箱": "email",
|
|
99
|
+
"电子邮件": "email",
|
|
100
|
+
"联系邮箱": "contact_email",
|
|
101
|
+
"电话": "phone",
|
|
102
|
+
"手机": "mobile",
|
|
103
|
+
"地址": "address",
|
|
104
|
+
"编码": "code",
|
|
105
|
+
"编号": "code",
|
|
106
|
+
"类型": "type",
|
|
107
|
+
"状态": "status",
|
|
108
|
+
"描述": "desc",
|
|
109
|
+
"备注": "note",
|
|
110
|
+
"创建时间": "created_at",
|
|
111
|
+
"更新时间": "updated_at",
|
|
112
|
+
"创建日期": "created_date",
|
|
113
|
+
"更新日期": "updated_date",
|
|
114
|
+
"金额": "amount",
|
|
115
|
+
"价格": "price",
|
|
116
|
+
"数量": "qty",
|
|
117
|
+
"总数": "total",
|
|
118
|
+
"用户名": "username",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if text in mappings:
|
|
122
|
+
return mappings[text]
|
|
123
|
+
|
|
124
|
+
# §2.4: 子串匹配删除——"订单" in "订单明细" 会把不同表缩写成同一 ID 前缀,
|
|
125
|
+
# 命中错误映射还引入碰撞面。不在内建表中的词走下方首字母路径
|
|
126
|
+
|
|
127
|
+
if re.match(r"^[\u4e00-\u9fff]+$", text):
|
|
128
|
+
pinyin_map = {
|
|
129
|
+
"供": "g",
|
|
130
|
+
"应": "y",
|
|
131
|
+
"商": "s",
|
|
132
|
+
"表": "b",
|
|
133
|
+
"用": "y",
|
|
134
|
+
"户": "h",
|
|
135
|
+
"订": "d",
|
|
136
|
+
"单": "d",
|
|
137
|
+
"产": "c",
|
|
138
|
+
"品": "p",
|
|
139
|
+
"库": "k",
|
|
140
|
+
"存": "c",
|
|
141
|
+
"交": "j",
|
|
142
|
+
"易": "y",
|
|
143
|
+
"员": "y",
|
|
144
|
+
"工": "g",
|
|
145
|
+
"部": "b",
|
|
146
|
+
"门": "m",
|
|
147
|
+
"名": "m",
|
|
148
|
+
"称": "c",
|
|
149
|
+
"邮": "y",
|
|
150
|
+
"箱": "x",
|
|
151
|
+
"电": "d",
|
|
152
|
+
"话": "h",
|
|
153
|
+
"地": "d",
|
|
154
|
+
"址": "z",
|
|
155
|
+
"编": "b",
|
|
156
|
+
"码": "m",
|
|
157
|
+
"类": "l",
|
|
158
|
+
"型": "x",
|
|
159
|
+
"状": "z",
|
|
160
|
+
"态": "t",
|
|
161
|
+
"描": "m",
|
|
162
|
+
"述": "s",
|
|
163
|
+
"备": "b",
|
|
164
|
+
"注": "z",
|
|
165
|
+
"价": "j",
|
|
166
|
+
"格": "g",
|
|
167
|
+
"数": "s",
|
|
168
|
+
"量": "l",
|
|
169
|
+
}
|
|
170
|
+
# §2.4: 不限前 5 字——长中文名的前 5 字相同仍会碰撞,全名逐字转换
|
|
171
|
+
return "".join([pinyin_map.get(c, c) for c in text])
|
|
172
|
+
else:
|
|
173
|
+
# §2.4: 同理不限前 5 段,全名首字母展开
|
|
174
|
+
return "".join([c[0] for c in text.split("_") if c])
|