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,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 项目 API Pydantic 模型定义模块
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 定义 V2 项目视图模型(画布节点坐标和视口)
|
|
21
|
+
- 定义 V2 全量配置请求/响应模型
|
|
22
|
+
- 定义工作区条目和工作区列表模型
|
|
23
|
+
- 提供标准响应模型
|
|
24
|
+
|
|
25
|
+
架构设计:
|
|
26
|
+
- 视图模型与校验语义配置解耦,避免 UI 信息污染业务配置
|
|
27
|
+
- 全量配置模型用于前端导出、AI 生成、备份恢复场景
|
|
28
|
+
- 工作区模型支持多标签页画布的状态持久化
|
|
29
|
+
|
|
30
|
+
输入示例:
|
|
31
|
+
class ProjectViewV2Model(BaseModel):
|
|
32
|
+
version: int = 1
|
|
33
|
+
nodes: dict[str, dict[str, float]] # node_id -> {x, y}
|
|
34
|
+
viewport: Optional[dict[str, float]] # {x, y, zoom}
|
|
35
|
+
|
|
36
|
+
输出示例:
|
|
37
|
+
class FullConfigV2Request(BaseModel):
|
|
38
|
+
manifest: ProjectManifestV2
|
|
39
|
+
schemas: dict[str, TableSchemaFileV2]
|
|
40
|
+
constraints: dict[str, ConstraintFileV2]
|
|
41
|
+
regex_nodes: dict[str, RegexNodeFileV2]
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from pydantic import BaseModel, Field, field_validator
|
|
45
|
+
|
|
46
|
+
from app.shared.core.project.constraint.types import ConstraintFileV2
|
|
47
|
+
from app.shared.core.project.manifest.types import (
|
|
48
|
+
ProjectManifestV2,
|
|
49
|
+
)
|
|
50
|
+
from app.shared.core.project.manual_data.types import ManualDataFileV2
|
|
51
|
+
from app.shared.core.project.regex.types import RegexNodeFileV2
|
|
52
|
+
from app.shared.core.project.schema.types import TableSchemaFileV2
|
|
53
|
+
from app.shared.core.project.transform.types import TransformFileV2
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ProjectViewV2Model(BaseModel):
|
|
57
|
+
"""
|
|
58
|
+
V2 项目视图文件模型(前端画布布局)。
|
|
59
|
+
|
|
60
|
+
设计目标:
|
|
61
|
+
- 将画布布局(节点坐标/视口)与后端校验语义配置解耦
|
|
62
|
+
- 避免把 UI 相关信息写入 project.precis.yaml / schema / constraint / regex 文件
|
|
63
|
+
|
|
64
|
+
数据流:
|
|
65
|
+
- 前端保存画布视图时调用 PUT /project/view
|
|
66
|
+
- 前端加载项目时调用 GET /project/view 获取节点位置
|
|
67
|
+
|
|
68
|
+
字段说明:
|
|
69
|
+
- version: 视图文件版本,用于后续格式迁移
|
|
70
|
+
- nodes: 节点ID到坐标的映射,用于恢复画布布局
|
|
71
|
+
- viewport: 当前视口信息,用于恢复用户查看位置
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
version: int = Field(default=1, description="视图文件版本")
|
|
75
|
+
nodes: dict[str, dict[str, float]] = Field(default_factory=dict, description="节点坐标:node_id -> {x,y}")
|
|
76
|
+
viewport: dict[str, float] | None = Field(default=None, description="视口信息:{x,y,zoom}")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class FullConfigV2Request(BaseModel):
|
|
80
|
+
"""
|
|
81
|
+
V2 全量配置写入请求模型。
|
|
82
|
+
|
|
83
|
+
使用场景:
|
|
84
|
+
- 前端导出整个项目配置时使用
|
|
85
|
+
- AI 生成完整项目配置时使用
|
|
86
|
+
- 项目备份/恢复场景
|
|
87
|
+
|
|
88
|
+
数据完整性保证:
|
|
89
|
+
- manifest: 必填,作为所有资源的索引
|
|
90
|
+
- schemas/constraints/regex_nodes: 可选,用于批量更新资源文件
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
manifest: ProjectManifestV2 = Field(..., description="项目清单")
|
|
94
|
+
schemas: dict[str, TableSchemaFileV2] = Field(default_factory=dict, description="schemas 映射:table_id -> schema")
|
|
95
|
+
constraints: dict[str, ConstraintFileV2] = Field(
|
|
96
|
+
default_factory=dict, description="constraints 映射:constraint_id -> constraint"
|
|
97
|
+
)
|
|
98
|
+
regex_nodes: dict[str, RegexNodeFileV2] = Field(
|
|
99
|
+
default_factory=dict, description="regex_nodes 映射:regex_id -> regex_node"
|
|
100
|
+
)
|
|
101
|
+
transforms: dict[str, TransformFileV2] = Field(
|
|
102
|
+
default_factory=dict, description="transforms 映射:transform_id -> transform"
|
|
103
|
+
)
|
|
104
|
+
manual_data: dict[str, ManualDataFileV2] = Field(
|
|
105
|
+
default_factory=dict, description="manual_data 映射:manual_data_id -> manual_data"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class DisplayNameUpdateRequest(BaseModel):
|
|
110
|
+
"""
|
|
111
|
+
更新资源展示名的请求体模型。
|
|
112
|
+
|
|
113
|
+
设计意图:
|
|
114
|
+
- 允许用户修改资源的显示名称而不改变资源 ID 或文件路径
|
|
115
|
+
- 用于提升用户体验,如给 "customer_table" 改为 "客户信息表"
|
|
116
|
+
|
|
117
|
+
约束:
|
|
118
|
+
- name 不能为空或纯空白(field_validator 拒绝,返回 422)
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
name: str = Field(..., description="新的展示名称")
|
|
122
|
+
|
|
123
|
+
@field_validator("name")
|
|
124
|
+
@classmethod
|
|
125
|
+
def _reject_blank(cls, v: str) -> str:
|
|
126
|
+
if not v or not v.strip():
|
|
127
|
+
raise ValueError("展示名称不能为空或纯空白")
|
|
128
|
+
return v
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class WorkspaceV2Item(BaseModel):
|
|
132
|
+
"""
|
|
133
|
+
V2 工作区条目模型。
|
|
134
|
+
|
|
135
|
+
设计目标:
|
|
136
|
+
- 存储单个工作区的元数据、视图状态和完整画布快照
|
|
137
|
+
- nodes/edges 保存完整画布数据,切换/关闭工作区时立即持久化
|
|
138
|
+
- 重新打开项目时从 workspaces.json 恢复所有工作区的画布状态
|
|
139
|
+
|
|
140
|
+
字段说明:
|
|
141
|
+
- id: 工作区唯一标识
|
|
142
|
+
- title: 工作区显示名称
|
|
143
|
+
- index: 排序索引(删除后不重新编号,新建时取 max+1)
|
|
144
|
+
- createdAt/lastActiveAt: 时间戳
|
|
145
|
+
- visibleNodeIds: 在该工作区中可见的节点 ID 列表
|
|
146
|
+
- viewport: 画布视口状态(位置+缩放)
|
|
147
|
+
- nodes: 画布节点完整数据(JSON 数组)
|
|
148
|
+
- edges: 画布边完整数据(JSON 数组)
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
id: str = Field(..., description="工作区唯一标识")
|
|
152
|
+
title: str = Field(..., description="工作区显示名称")
|
|
153
|
+
index: int = Field(default=0, description="排序索引")
|
|
154
|
+
createdAt: str = Field(default="", description="创建时间 ISO 字符串")
|
|
155
|
+
lastActiveAt: str = Field(default="", description="最后活跃时间 ISO 字符串")
|
|
156
|
+
visibleNodeIds: list[str] = Field(default_factory=list, description="可见节点 ID 列表")
|
|
157
|
+
viewport: dict[str, float] | None = Field(default=None, description="视口信息:{x,y,zoom}")
|
|
158
|
+
nodes: list[dict] = Field(default_factory=list, description="画布节点完整数据")
|
|
159
|
+
edges: list[dict] = Field(default_factory=list, description="画布边完整数据")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class WorkspacesV2Model(BaseModel):
|
|
163
|
+
"""
|
|
164
|
+
V2 工作区配置文件模型。
|
|
165
|
+
|
|
166
|
+
使用场景:
|
|
167
|
+
- 前端多标签页画布的工作区持久化
|
|
168
|
+
- 每个工作区保存完整画布快照(nodes/edges),实现跨会话恢复
|
|
169
|
+
|
|
170
|
+
数据流:
|
|
171
|
+
- 前端切换/关闭工作区时调用 PUT /project/workspaces 保存
|
|
172
|
+
- 前端加载项目时调用 GET /project/workspaces 恢复所有工作区画布状态
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
version: int = Field(default=1, description="工作区文件版本")
|
|
176
|
+
activeWorkspaceId: str | None = Field(default=None, description="当前活跃工作区 ID")
|
|
177
|
+
workspaces: list[WorkspaceV2Item] = Field(default_factory=list, description="工作区列表")
|
|
@@ -0,0 +1,268 @@
|
|
|
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 Pattern CRUD API - 正则表达式模式管理
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 提供 Pattern(可复用正则表达式单元)的创建与存在性检查
|
|
21
|
+
- Pattern 存储在项目的 patterns/ 目录下
|
|
22
|
+
- 支持命名冲突处理(覆盖、建议新名称、取消)
|
|
23
|
+
|
|
24
|
+
架构设计:
|
|
25
|
+
- 依赖 get_v2_manifest 获取 patterns_dir 配置
|
|
26
|
+
- 使用 PatternFile 模型序列化为 YAML 文件
|
|
27
|
+
- 创建请求使用 CreatePatternRequest,响应使用 CreatePatternResponse
|
|
28
|
+
|
|
29
|
+
输入示例:
|
|
30
|
+
POST /pattern (body: CreatePatternRequest)
|
|
31
|
+
GET /pattern/phone_cn/exists
|
|
32
|
+
|
|
33
|
+
输出示例:
|
|
34
|
+
CreatePatternResponse: {message, pattern_path, pattern_name}
|
|
35
|
+
dict: {pattern_name, exists}
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
import logging
|
|
39
|
+
import os
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
from typing import Any
|
|
42
|
+
|
|
43
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
44
|
+
from pydantic import BaseModel, Field
|
|
45
|
+
|
|
46
|
+
from app.api.dependencies import get_project_config_path
|
|
47
|
+
from app.shared.core.io.yaml import read_yaml
|
|
48
|
+
from app.shared.core.patterns.writer import (
|
|
49
|
+
PatternFile,
|
|
50
|
+
check_pattern_exists,
|
|
51
|
+
delete_pattern_file,
|
|
52
|
+
generate_unique_pattern_name,
|
|
53
|
+
save_pattern_file,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
from .manifest import get_v2_manifest
|
|
57
|
+
|
|
58
|
+
logger = logging.getLogger(__name__)
|
|
59
|
+
|
|
60
|
+
router = APIRouter(prefix="", tags=["Project-Pattern"])
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CreatePatternRequest(BaseModel):
|
|
64
|
+
"""创建 Pattern 的请求体。"""
|
|
65
|
+
|
|
66
|
+
name: str = Field(..., description="Pattern 名称")
|
|
67
|
+
regex: str = Field(..., description="正则表达式")
|
|
68
|
+
description: str | None = Field(None, description="描述信息")
|
|
69
|
+
output: dict | None = Field(None, description="输出配置")
|
|
70
|
+
overwrite: bool = Field(False, description="是否覆盖已存在的 Pattern")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class CreatePatternResponse(BaseModel):
|
|
74
|
+
"""创建 Pattern 的响应体。"""
|
|
75
|
+
|
|
76
|
+
message: str
|
|
77
|
+
pattern_path: str
|
|
78
|
+
pattern_name: str
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@router.post(
|
|
82
|
+
"/pattern",
|
|
83
|
+
response_model=CreatePatternResponse,
|
|
84
|
+
summary="创建新的 Pattern 文件",
|
|
85
|
+
responses={
|
|
86
|
+
409: {"description": "Pattern 已存在"},
|
|
87
|
+
500: {"description": "服务器内部错误"},
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
def create_v2_pattern(
|
|
91
|
+
payload: CreatePatternRequest,
|
|
92
|
+
config_path: str = Depends(get_project_config_path),
|
|
93
|
+
) -> CreatePatternResponse:
|
|
94
|
+
"""
|
|
95
|
+
创建新的 Pattern 文件。
|
|
96
|
+
|
|
97
|
+
业务逻辑:
|
|
98
|
+
- 在项目的 patterns/ 目录下创建 Pattern YAML 文件
|
|
99
|
+
- 支持命名冲突处理(覆盖/重命名/报错)
|
|
100
|
+
|
|
101
|
+
参数:
|
|
102
|
+
payload: 创建 Pattern 的请求数据
|
|
103
|
+
config_path: 项目配置根目录(通过 Depends 注入)
|
|
104
|
+
|
|
105
|
+
返回:
|
|
106
|
+
CreatePatternResponse: 创建结果
|
|
107
|
+
"""
|
|
108
|
+
manifest = get_v2_manifest(config_path)
|
|
109
|
+
patterns_dir = os.path.join(config_path, manifest.patterns_dir or "patterns")
|
|
110
|
+
|
|
111
|
+
pattern_exists = check_pattern_exists(patterns_dir, payload.name)
|
|
112
|
+
|
|
113
|
+
if pattern_exists and not payload.overwrite:
|
|
114
|
+
suggested_name = generate_unique_pattern_name(patterns_dir, payload.name)
|
|
115
|
+
raise HTTPException(
|
|
116
|
+
status_code=409,
|
|
117
|
+
detail={
|
|
118
|
+
"error": "pattern_exists",
|
|
119
|
+
"message": f"Pattern '{payload.name}' 已存在",
|
|
120
|
+
"suggested_name": suggested_name,
|
|
121
|
+
"options": ["overwrite", "use_suggested_name", "cancel"],
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if payload.overwrite and pattern_exists:
|
|
126
|
+
logger.info(f"[Pattern API] 覆盖已存在的 Pattern: {payload.name}")
|
|
127
|
+
|
|
128
|
+
pattern_file = PatternFile(
|
|
129
|
+
name=payload.name,
|
|
130
|
+
regex=payload.regex,
|
|
131
|
+
description=payload.description,
|
|
132
|
+
output=payload.output,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
filepath = save_pattern_file(pattern_file, patterns_dir)
|
|
136
|
+
logger.info(f"[Pattern API] Pattern 已创建: {filepath}")
|
|
137
|
+
|
|
138
|
+
return CreatePatternResponse(
|
|
139
|
+
message=f"Pattern '{payload.name}' 已创建",
|
|
140
|
+
pattern_path=filepath,
|
|
141
|
+
pattern_name=payload.name,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@router.get(
|
|
146
|
+
"/pattern/{pattern_name}/exists",
|
|
147
|
+
response_model=dict,
|
|
148
|
+
summary="检查 Pattern 名称是否已存在",
|
|
149
|
+
responses={
|
|
150
|
+
500: {"description": "服务器内部错误"},
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
def check_pattern_name_exists(
|
|
154
|
+
pattern_name: str,
|
|
155
|
+
config_path: str = Depends(get_project_config_path),
|
|
156
|
+
) -> dict[str, Any]:
|
|
157
|
+
"""
|
|
158
|
+
检查指定名称的 Pattern 是否已存在。
|
|
159
|
+
|
|
160
|
+
参数:
|
|
161
|
+
pattern_name: Pattern 名称
|
|
162
|
+
config_path: 项目配置根目录
|
|
163
|
+
|
|
164
|
+
返回:
|
|
165
|
+
包含是否存在信息的字典
|
|
166
|
+
"""
|
|
167
|
+
manifest = get_v2_manifest(config_path)
|
|
168
|
+
patterns_dir = os.path.join(config_path, manifest.patterns_dir or "patterns")
|
|
169
|
+
|
|
170
|
+
exists = check_pattern_exists(patterns_dir, pattern_name)
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
"pattern_name": pattern_name,
|
|
174
|
+
"exists": exists,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@router.get(
|
|
179
|
+
"/pattern",
|
|
180
|
+
response_model=list[dict],
|
|
181
|
+
summary="列出所有 Pattern 规则",
|
|
182
|
+
responses={
|
|
183
|
+
500: {"description": "服务器内部错误"},
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
def list_v2_patterns(
|
|
187
|
+
config_path: str = Depends(get_project_config_path),
|
|
188
|
+
) -> list[dict[str, Any]]:
|
|
189
|
+
"""列出项目 patterns/ 目录下的所有 Pattern。"""
|
|
190
|
+
manifest = get_v2_manifest(config_path)
|
|
191
|
+
patterns_dir = os.path.join(config_path, manifest.patterns_dir or "patterns")
|
|
192
|
+
result: list[dict[str, Any]] = []
|
|
193
|
+
if not os.path.isdir(patterns_dir):
|
|
194
|
+
return result
|
|
195
|
+
for fname in sorted(os.listdir(patterns_dir)):
|
|
196
|
+
if not (fname.endswith(".yaml") or fname.endswith(".yml")):
|
|
197
|
+
continue
|
|
198
|
+
fpath = os.path.join(patterns_dir, fname)
|
|
199
|
+
try:
|
|
200
|
+
data = read_yaml(Path(fpath))
|
|
201
|
+
if isinstance(data, dict) and data.get("name"):
|
|
202
|
+
result.append(data)
|
|
203
|
+
except Exception as e:
|
|
204
|
+
logger.warning(f"[Pattern API] 读取 pattern 文件失败: {fpath}, {e}")
|
|
205
|
+
return result
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@router.put(
|
|
209
|
+
"/pattern/{pattern_name}",
|
|
210
|
+
response_model=CreatePatternResponse,
|
|
211
|
+
summary="更新已存在的 Pattern",
|
|
212
|
+
responses={
|
|
213
|
+
404: {"description": "Pattern 不存在"},
|
|
214
|
+
500: {"description": "服务器内部错误"},
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
def update_v2_pattern(
|
|
218
|
+
pattern_name: str,
|
|
219
|
+
payload: CreatePatternRequest,
|
|
220
|
+
config_path: str = Depends(get_project_config_path),
|
|
221
|
+
) -> CreatePatternResponse:
|
|
222
|
+
"""更新已存在的 Pattern 文件(覆盖写)。"""
|
|
223
|
+
manifest = get_v2_manifest(config_path)
|
|
224
|
+
patterns_dir = os.path.join(config_path, manifest.patterns_dir or "patterns")
|
|
225
|
+
|
|
226
|
+
if not check_pattern_exists(patterns_dir, pattern_name):
|
|
227
|
+
raise HTTPException(status_code=404, detail=f"Pattern '{pattern_name}' 不存在")
|
|
228
|
+
|
|
229
|
+
# 以路径中的名称为准(payload.name 应与 pattern_name 一致)
|
|
230
|
+
pattern_file = PatternFile(
|
|
231
|
+
name=pattern_name,
|
|
232
|
+
regex=payload.regex,
|
|
233
|
+
description=payload.description,
|
|
234
|
+
output=payload.output,
|
|
235
|
+
)
|
|
236
|
+
filepath = save_pattern_file(pattern_file, patterns_dir)
|
|
237
|
+
logger.info(f"[Pattern API] Pattern 已更新: {filepath}")
|
|
238
|
+
|
|
239
|
+
return CreatePatternResponse(
|
|
240
|
+
message=f"Pattern '{pattern_name}' 已更新",
|
|
241
|
+
pattern_path=filepath,
|
|
242
|
+
pattern_name=pattern_name,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@router.delete(
|
|
247
|
+
"/pattern/{pattern_name}",
|
|
248
|
+
response_model=dict,
|
|
249
|
+
summary="删除 Pattern",
|
|
250
|
+
responses={
|
|
251
|
+
404: {"description": "Pattern 不存在"},
|
|
252
|
+
500: {"description": "服务器内部错误"},
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
def delete_v2_pattern(
|
|
256
|
+
pattern_name: str,
|
|
257
|
+
config_path: str = Depends(get_project_config_path),
|
|
258
|
+
) -> dict[str, Any]:
|
|
259
|
+
"""删除指定 Pattern 文件。"""
|
|
260
|
+
manifest = get_v2_manifest(config_path)
|
|
261
|
+
patterns_dir = os.path.join(config_path, manifest.patterns_dir or "patterns")
|
|
262
|
+
|
|
263
|
+
deleted = delete_pattern_file(patterns_dir, pattern_name)
|
|
264
|
+
if not deleted:
|
|
265
|
+
raise HTTPException(status_code=404, detail=f"Pattern '{pattern_name}' 不存在")
|
|
266
|
+
|
|
267
|
+
logger.info(f"[Pattern API] Pattern 已删除: {pattern_name}")
|
|
268
|
+
return {"pattern_name": pattern_name, "deleted": True}
|