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.
Potentially problematic release.
This version of precis-cli might be problematic. Click here for more details.
- 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,237 @@
|
|
|
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 系统硬件信息采集模块
|
|
17
|
+
|
|
18
|
+
功能概述:
|
|
19
|
+
- 采集操作系统、CPU、内存、磁盘、GPU 等硬件信息
|
|
20
|
+
- 提供 HardwareSnapshot 数据类用于性能优化和错误报告
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
import platform
|
|
28
|
+
import shutil
|
|
29
|
+
import subprocess
|
|
30
|
+
import sys
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class HardwareSnapshot:
|
|
38
|
+
"""
|
|
39
|
+
@classdesc 硬件快照数据类
|
|
40
|
+
|
|
41
|
+
使用 frozen=True 确保实例不可变,线程安全。
|
|
42
|
+
|
|
43
|
+
字段说明:
|
|
44
|
+
- os_name: 操作系统名称(如 'Windows', 'Linux')
|
|
45
|
+
- os_version: 操作系统版本号
|
|
46
|
+
- arch: 系统架构(如 'x86_64', 'AMD64')
|
|
47
|
+
- cpu_cores: CPU 逻辑核心数
|
|
48
|
+
- memory_total_bytes: 物理内存总量(字节)
|
|
49
|
+
- disk_free_bytes: 磁盘可用空间(字节)
|
|
50
|
+
- has_nvidia_gpu: 是否存在 NVIDIA GPU
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
os_name: str
|
|
54
|
+
os_version: str
|
|
55
|
+
arch: str
|
|
56
|
+
cpu_cores: int
|
|
57
|
+
memory_total_bytes: int
|
|
58
|
+
disk_free_bytes: int
|
|
59
|
+
has_nvidia_gpu: bool
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _cpu_cores() -> int:
|
|
63
|
+
"""
|
|
64
|
+
@methoddesc 获取 CPU 逻辑核心数
|
|
65
|
+
|
|
66
|
+
实现逻辑:
|
|
67
|
+
1. 尝试使用 os.cpu_count() 获取核心数
|
|
68
|
+
2. 异常处理:若获取失败,返回默认值 1
|
|
69
|
+
|
|
70
|
+
返回:
|
|
71
|
+
CPU 逻辑核心数,至少为 1
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
v = int(os.cpu_count() or 0)
|
|
75
|
+
except Exception:
|
|
76
|
+
logger.debug("获取 CPU 核心数失败", exc_info=True)
|
|
77
|
+
v = 0
|
|
78
|
+
return v if v > 0 else 1
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _memory_total_bytes() -> int:
|
|
82
|
+
"""
|
|
83
|
+
@methoddesc 获取系统物理内存总量
|
|
84
|
+
|
|
85
|
+
平台实现:
|
|
86
|
+
- Windows: 使用 ctypes 调用 kernel32.GlobalMemoryStatusEx
|
|
87
|
+
- Linux/Unix: 使用 os.sysconf 获取页大小和物理页数
|
|
88
|
+
|
|
89
|
+
返回:
|
|
90
|
+
物理内存总量(字节),获取失败返回 0
|
|
91
|
+
"""
|
|
92
|
+
# Windows 平台实现
|
|
93
|
+
if os.name == "nt":
|
|
94
|
+
try:
|
|
95
|
+
import ctypes
|
|
96
|
+
|
|
97
|
+
# 定义 Windows MEMORYSTATUSEX 结构体
|
|
98
|
+
class _MEMORYSTATUSEX(ctypes.Structure):
|
|
99
|
+
_fields_ = [
|
|
100
|
+
("dwLength", ctypes.c_ulong), # 结构体大小
|
|
101
|
+
("dwMemoryLoad", ctypes.c_ulong), # 内存使用率
|
|
102
|
+
("ullTotalPhys", ctypes.c_ulonglong), # 物理内存总量
|
|
103
|
+
("ullAvailPhys", ctypes.c_ulonglong), # 物理内存可用量
|
|
104
|
+
("ullTotalPageFile", ctypes.c_ulonglong), # 页面文件总量
|
|
105
|
+
("ullAvailPageFile", ctypes.c_ulonglong), # 页面文件可用量
|
|
106
|
+
("ullTotalVirtual", ctypes.c_ulonglong), # 虚拟内存总量
|
|
107
|
+
("ullAvailVirtual", ctypes.c_ulonglong), # 虚拟内存可用量
|
|
108
|
+
("sullAvailExtendedVirtual", ctypes.c_ulonglong), # 扩展虚拟内存
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
stat = _MEMORYSTATUSEX()
|
|
112
|
+
stat.dwLength = ctypes.sizeof(_MEMORYSTATUSEX)
|
|
113
|
+
# 调用 Windows API 获取内存状态
|
|
114
|
+
# windll 是 Windows 专属属性,非 Windows 平台 typeshed 无此定义
|
|
115
|
+
if ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)) == 0: # type: ignore[attr-defined]
|
|
116
|
+
return 0
|
|
117
|
+
return int(stat.ullTotalPhys)
|
|
118
|
+
except Exception:
|
|
119
|
+
logger.debug("Windows 平台获取内存总量失败", exc_info=True)
|
|
120
|
+
return 0
|
|
121
|
+
|
|
122
|
+
# Linux/Unix 平台实现
|
|
123
|
+
if hasattr(os, "sysconf"):
|
|
124
|
+
try:
|
|
125
|
+
# 获取系统页大小(字节)
|
|
126
|
+
page = int(os.sysconf("SC_PAGE_SIZE"))
|
|
127
|
+
# 获取物理内存页数
|
|
128
|
+
pages = int(os.sysconf("SC_PHYS_PAGES"))
|
|
129
|
+
total = page * pages
|
|
130
|
+
return int(total) if total > 0 else 0
|
|
131
|
+
except Exception:
|
|
132
|
+
logger.debug("Linux/Unix 平台获取内存总量失败", exc_info=True)
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _disk_free_bytes(path: str | None = None) -> int:
|
|
139
|
+
r"""
|
|
140
|
+
@methoddesc 获取指定路径所在磁盘的可用空间
|
|
141
|
+
|
|
142
|
+
路径解析逻辑:
|
|
143
|
+
- 若指定 path 参数:使用该路径
|
|
144
|
+
- 若未指定:
|
|
145
|
+
- Windows: 使用系统驱动器(默认 C:\)
|
|
146
|
+
- Linux/Unix: 使用用户主目录
|
|
147
|
+
|
|
148
|
+
参数:
|
|
149
|
+
path: 可选的磁盘路径
|
|
150
|
+
|
|
151
|
+
返回:
|
|
152
|
+
可用空间(字节),获取失败返回 0
|
|
153
|
+
"""
|
|
154
|
+
p = path
|
|
155
|
+
if not p:
|
|
156
|
+
if os.name == "nt":
|
|
157
|
+
# Windows: 使用系统驱动器
|
|
158
|
+
p = os.environ.get("SystemDrive", "C:") + "\\"
|
|
159
|
+
else:
|
|
160
|
+
# Linux/Unix: 使用用户主目录
|
|
161
|
+
p = os.path.expanduser("~")
|
|
162
|
+
try:
|
|
163
|
+
usage = shutil.disk_usage(p)
|
|
164
|
+
return int(usage.free)
|
|
165
|
+
except Exception:
|
|
166
|
+
logger.debug(f"获取磁盘可用空间失败: path={p}", exc_info=True)
|
|
167
|
+
return 0
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _has_nvidia_gpu(timeout_seconds: float = 1.2) -> bool:
|
|
171
|
+
"""
|
|
172
|
+
@methoddesc 检测系统是否装有 NVIDIA GPU
|
|
173
|
+
|
|
174
|
+
实现机制:
|
|
175
|
+
通过执行 nvidia-smi -L 命令检测 GPU 存在性。
|
|
176
|
+
使用较短的超时时间(默认 1.2 秒)避免长时间阻塞。
|
|
177
|
+
|
|
178
|
+
参数:
|
|
179
|
+
timeout_seconds: 命令执行超时时间(秒)
|
|
180
|
+
|
|
181
|
+
返回:
|
|
182
|
+
存在 NVIDIA GPU 返回 True,否则返回 False
|
|
183
|
+
"""
|
|
184
|
+
try:
|
|
185
|
+
# 执行 nvidia-smi -L 命令检测GPU
|
|
186
|
+
res = subprocess.run(
|
|
187
|
+
["nvidia-smi", "-L"],
|
|
188
|
+
stdout=subprocess.DEVNULL,
|
|
189
|
+
stderr=subprocess.DEVNULL,
|
|
190
|
+
timeout=timeout_seconds,
|
|
191
|
+
check=False,
|
|
192
|
+
)
|
|
193
|
+
# 返回码为0表示命令成功执行,即存在GPU
|
|
194
|
+
return res.returncode == 0
|
|
195
|
+
except Exception:
|
|
196
|
+
logger.debug("NVIDIA GPU 检测失败", exc_info=True)
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def snapshot() -> HardwareSnapshot:
|
|
201
|
+
"""
|
|
202
|
+
@methoddesc 获取当前系统的硬件信息快照
|
|
203
|
+
|
|
204
|
+
采集的信息:
|
|
205
|
+
1. 操作系统信息:
|
|
206
|
+
- os_name: platform.system() 或 os.name
|
|
207
|
+
- os_version: platform.version() 或 platform.release()
|
|
208
|
+
2. 系统架构:platform.machine() 或 platform.architecture()
|
|
209
|
+
3. CPU 核心数:_cpu_cores()
|
|
210
|
+
4. 内存总量:_memory_total_bytes()
|
|
211
|
+
5. 磁盘可用空间:_disk_free_bytes()
|
|
212
|
+
6. GPU 检测:_has_nvidia_gpu()
|
|
213
|
+
|
|
214
|
+
返回:
|
|
215
|
+
HardwareSnapshot 实例,包含所有硬件信息
|
|
216
|
+
"""
|
|
217
|
+
# 采集操作系统信息
|
|
218
|
+
os_name = platform.system() or os.name
|
|
219
|
+
os_version = platform.version() or platform.release() or ""
|
|
220
|
+
arch = platform.machine() or platform.architecture()[0] or sys.platform
|
|
221
|
+
|
|
222
|
+
# 采集各项硬件指标
|
|
223
|
+
cpu_cores = _cpu_cores()
|
|
224
|
+
memory_total = _memory_total_bytes()
|
|
225
|
+
disk_free = _disk_free_bytes()
|
|
226
|
+
has_gpu = _has_nvidia_gpu()
|
|
227
|
+
|
|
228
|
+
# 构建并返回硬件快照
|
|
229
|
+
return HardwareSnapshot(
|
|
230
|
+
os_name=os_name,
|
|
231
|
+
os_version=os_version,
|
|
232
|
+
arch=arch,
|
|
233
|
+
cpu_cores=cpu_cores,
|
|
234
|
+
memory_total_bytes=memory_total,
|
|
235
|
+
disk_free_bytes=disk_free,
|
|
236
|
+
has_nvidia_gpu=has_gpu,
|
|
237
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
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 LLM 服务模块聚合导出
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 聚合导出 AI Provider 配置、Provider 实现、服务发现、配置生成等子模块
|
|
21
|
+
- 为上层业务提供统一的 LLM 服务入口
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- 通过 __init__.py 聚合子模块公共接口,降低外部导入复杂度
|
|
25
|
+
- 遵循显式导出原则,使用 __all__ 控制公开 API
|
|
26
|
+
- 各子模块职责分离:config 负责配置,providers 负责调用,discovery 负责发现,generation 负责生成
|
|
27
|
+
|
|
28
|
+
输入示例:
|
|
29
|
+
from app.shared.services.llm import AIConfig, BaseProvider, ConfigGenerationService
|
|
30
|
+
|
|
31
|
+
输出示例:
|
|
32
|
+
AIConfig 实例可用于配置管理
|
|
33
|
+
BaseProvider 子类可用于执行对话
|
|
34
|
+
ConfigGenerationService 可用于生成项目配置
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from .config import AIConfig, AIProvider, DeploymentType, ProviderType, loader
|
|
38
|
+
from .discovery import DiscoveredService, scanner
|
|
39
|
+
from .generation import ConfigGenerationService, GenerationOptions, ProfilingOptions
|
|
40
|
+
from .providers import BaseProvider, ChatMessage, ChatRequest, ChatResponse, create
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
# 配置
|
|
44
|
+
"AIConfig",
|
|
45
|
+
"AIProvider",
|
|
46
|
+
"ProviderType",
|
|
47
|
+
"DeploymentType",
|
|
48
|
+
"loader",
|
|
49
|
+
# Provider
|
|
50
|
+
"BaseProvider",
|
|
51
|
+
"ChatMessage",
|
|
52
|
+
"ChatRequest",
|
|
53
|
+
"ChatResponse",
|
|
54
|
+
"create",
|
|
55
|
+
# 发现
|
|
56
|
+
"scanner",
|
|
57
|
+
"DiscoveredService",
|
|
58
|
+
# 配置生成
|
|
59
|
+
"ConfigGenerationService",
|
|
60
|
+
"GenerationOptions",
|
|
61
|
+
"ProfilingOptions",
|
|
62
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
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 AI 动作解析与执行子包
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- registry: 动作类型与约束别名的单一事实源(无依赖叶子模块)
|
|
21
|
+
- action_parser: 动作类型映射 + 向后兼容导出
|
|
22
|
+
- action_processor: process_actions() 批量处理
|
|
23
|
+
- action_handlers: update_yaml_config() + 协调函数
|
|
24
|
+
- action_validator: ActionValidator 预验证器
|
|
25
|
+
|
|
26
|
+
架构设计:
|
|
27
|
+
- 管道模式: 解析 → 验证 → 执行
|
|
28
|
+
- 批量优化: 内联约束按 schema 分组合并 IO
|
|
29
|
+
|
|
30
|
+
导入说明:
|
|
31
|
+
本 __init__.py 故意不做包级 re-export。原因:action_handlers 依赖
|
|
32
|
+
constraints.constraint_builder/frontend_instructions,而这些模块从 registry 派生常量——
|
|
33
|
+
若在此处包级 import 会形成循环(actions/__init__ → action_handlers →
|
|
34
|
+
constraint_builder → actions 包),导致 test_constraint_builder_behavior.py 等独立
|
|
35
|
+
运行时 ImportError。
|
|
36
|
+
|
|
37
|
+
调用方请直接 import 具体子模块,例如:
|
|
38
|
+
from app.shared.services.llm.actions.action_processor import process_actions
|
|
39
|
+
from app.shared.services.llm.actions.action_handlers import update_yaml_config
|
|
40
|
+
from app.shared.services.llm.actions.action_parser import CONSTRAINT_TYPE_MAP
|
|
41
|
+
from app.shared.services.llm.actions.registry import CONSTRAINT_TYPE_ALIASES
|
|
42
|
+
"""
|
|
@@ -0,0 +1,147 @@
|
|
|
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 ADD_TO_CANVAS 动作验证器
|
|
18
|
+
|
|
19
|
+
验证 AI 生成 ADD_TO_CANVAS 动作的合法性:
|
|
20
|
+
- canvasSpec.resourceKind 必须是 schema/regex/constraint/transform 之一
|
|
21
|
+
- resourceId 或 resourceName 至少有一个
|
|
22
|
+
- 目标资源在项目配置中必须真实存在(避免把不存在的资源"显示"到画布)
|
|
23
|
+
|
|
24
|
+
与其它验证器不同:本验证器是纯读校验(ADD_TO_CANVAS 不写盘),
|
|
25
|
+
但仍接入预验证链以拦截"显示不存在的资源"这类语义错误,把错误回灌给 LLM 自我修正。
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Any
|
|
32
|
+
|
|
33
|
+
import yaml
|
|
34
|
+
|
|
35
|
+
from app.shared.services.llm.actions.registry import CANVAS_RESOURCE_KINDS
|
|
36
|
+
from app.shared.services.llm.actions.validation_types import ValidationError
|
|
37
|
+
|
|
38
|
+
# 支持的资源类型从注册表派生(单一事实源,与前端 ProjectResourceKind 对齐)
|
|
39
|
+
VALID_CANVAS_RESOURCE_KINDS = CANVAS_RESOURCE_KINDS
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _list_existing_resource_ids(workspace_path: str, kind: str) -> set[str]:
|
|
43
|
+
"""扫描项目配置目录,返回指定 kind 的已存在资源 ID 集合。
|
|
44
|
+
|
|
45
|
+
用于校验 ADD_TO_CANVAS 的目标资源是否真实存在。
|
|
46
|
+
扫描逻辑与 get_project_overview / 各 handler 的文件定位保持一致。
|
|
47
|
+
"""
|
|
48
|
+
root = Path(workspace_path)
|
|
49
|
+
ids: set[str] = set()
|
|
50
|
+
|
|
51
|
+
dir_map = {
|
|
52
|
+
"schema": ("schemas", "*.schema.yaml"),
|
|
53
|
+
"regex": ("regex", "*.regex.yaml"),
|
|
54
|
+
"constraint": ("constraints", "*.constraint.yaml"),
|
|
55
|
+
"transform": ("transforms", "*.transform.yaml"),
|
|
56
|
+
}
|
|
57
|
+
if kind not in dir_map:
|
|
58
|
+
return ids
|
|
59
|
+
|
|
60
|
+
subdir, pattern = dir_map[kind]
|
|
61
|
+
target_dir = root / subdir
|
|
62
|
+
if not target_dir.exists():
|
|
63
|
+
return ids
|
|
64
|
+
|
|
65
|
+
for f in target_dir.glob(pattern):
|
|
66
|
+
try:
|
|
67
|
+
with open(f, encoding="utf-8") as fh:
|
|
68
|
+
data = yaml.safe_load(fh) or {}
|
|
69
|
+
rid = data.get("id") or data.get("name")
|
|
70
|
+
if rid:
|
|
71
|
+
ids.add(str(rid))
|
|
72
|
+
# 同时收录 name 作为兜底匹配键(resourceName 常用 name)
|
|
73
|
+
name = data.get("name")
|
|
74
|
+
if name and str(name) != str(rid):
|
|
75
|
+
ids.add(str(name))
|
|
76
|
+
except Exception:
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
return ids
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def validate_canvas_action(action: dict[str, Any], index: int, workspace_path: str) -> list[ValidationError]:
|
|
83
|
+
"""验证 ADD_TO_CANVAS 操作
|
|
84
|
+
|
|
85
|
+
检查 canvasSpec 的 resourceKind 合法性、resourceId/resourceName 存在性,
|
|
86
|
+
以及目标资源在项目配置中是否真实存在。
|
|
87
|
+
"""
|
|
88
|
+
errors: list[ValidationError] = []
|
|
89
|
+
action_type = action.get("actionType", "")
|
|
90
|
+
spec = action.get("canvasSpec", {})
|
|
91
|
+
|
|
92
|
+
if not isinstance(spec, dict):
|
|
93
|
+
errors.append(
|
|
94
|
+
ValidationError(
|
|
95
|
+
action_index=index,
|
|
96
|
+
action_type=action_type,
|
|
97
|
+
error_type="missing_canvas_spec",
|
|
98
|
+
message="ADD_TO_CANVAS 需要 canvasSpec 字段",
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
return errors
|
|
102
|
+
|
|
103
|
+
kind = spec.get("resourceKind", "")
|
|
104
|
+
resource_id = spec.get("resourceId", "")
|
|
105
|
+
resource_name = spec.get("resourceName") or spec.get("name", "")
|
|
106
|
+
|
|
107
|
+
# 1. resourceKind 必须合法
|
|
108
|
+
if kind not in VALID_CANVAS_RESOURCE_KINDS:
|
|
109
|
+
errors.append(
|
|
110
|
+
ValidationError(
|
|
111
|
+
action_index=index,
|
|
112
|
+
action_type=action_type,
|
|
113
|
+
error_type="invalid_resource_kind",
|
|
114
|
+
message=f"不支持的资源类型: '{kind}'",
|
|
115
|
+
suggestion=f"可用类型: {', '.join(sorted(VALID_CANVAS_RESOURCE_KINDS))}",
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
return errors
|
|
119
|
+
|
|
120
|
+
# 2. resourceId 或 resourceName 至少有一个
|
|
121
|
+
if not resource_id and not resource_name:
|
|
122
|
+
errors.append(
|
|
123
|
+
ValidationError(
|
|
124
|
+
action_index=index,
|
|
125
|
+
action_type=action_type,
|
|
126
|
+
error_type="missing_resource_identifier",
|
|
127
|
+
message="ADD_TO_CANVAS 需要指定 resourceId 或 resourceName",
|
|
128
|
+
suggestion="可用 read_project 查询资源 ID/名称",
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
return errors
|
|
132
|
+
|
|
133
|
+
# 3. 目标资源必须真实存在(避免"显示不存在的资源")
|
|
134
|
+
existing_ids = _list_existing_resource_ids(workspace_path, kind)
|
|
135
|
+
target_keys = {str(resource_id), str(resource_name)} - {""}
|
|
136
|
+
if existing_ids and not (target_keys & existing_ids):
|
|
137
|
+
errors.append(
|
|
138
|
+
ValidationError(
|
|
139
|
+
action_index=index,
|
|
140
|
+
action_type=action_type,
|
|
141
|
+
error_type="resource_not_found",
|
|
142
|
+
message=f"资源不存在: {kind} '{resource_id or resource_name}'",
|
|
143
|
+
suggestion="该资源未在项目配置中找到,请先创建(如用 ADD_SCHEMA)或检查名称拼写",
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return errors
|