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.
Files changed (442) hide show
  1. app/__init__.py +16 -0
  2. app/api/__init__.py +32 -0
  3. app/api/dependencies.py +185 -0
  4. app/api/main.py +301 -0
  5. app/api/middleware/__init__.py +34 -0
  6. app/api/middleware/exception_handler.py +126 -0
  7. app/api/middleware/request_logging.py +97 -0
  8. app/api/middleware/token_auth.py +215 -0
  9. app/api/models/__init__.py +151 -0
  10. app/api/models/connection_rules.py +98 -0
  11. app/api/models/files.py +99 -0
  12. app/api/models/full_validation.py +554 -0
  13. app/api/models/project.py +117 -0
  14. app/api/models/projects.py +80 -0
  15. app/api/models/schema.py +121 -0
  16. app/api/models/v2_responses.py +46 -0
  17. app/api/models/validation.py +303 -0
  18. app/api/models/workspace.py +118 -0
  19. app/api/routers/__init__.py +56 -0
  20. app/api/routers/ai/__init__.py +36 -0
  21. app/api/routers/ai/chat.py +243 -0
  22. app/api/routers/ai/generate.py +156 -0
  23. app/api/routers/ai/hardware.py +199 -0
  24. app/api/routers/ai/jobs.py +544 -0
  25. app/api/routers/ai/migrate.py +471 -0
  26. app/api/routers/ai/models.py +352 -0
  27. app/api/routers/ai/ollama.py +108 -0
  28. app/api/routers/ai/providers.py +491 -0
  29. app/api/routers/ai/router.py +44 -0
  30. app/api/routers/ai/stream.py +281 -0
  31. app/api/routers/ai/utils.py +182 -0
  32. app/api/routers/core/__init__.py +34 -0
  33. app/api/routers/core/connection_rules.py +188 -0
  34. app/api/routers/core/data_sources.py +374 -0
  35. app/api/routers/core/regex.py +333 -0
  36. app/api/routers/core/reporting.py +263 -0
  37. app/api/routers/files/__init__.py +24 -0
  38. app/api/routers/files/ops.py +179 -0
  39. app/api/routers/files/transfer.py +101 -0
  40. app/api/routers/preview/__init__.py +35 -0
  41. app/api/routers/preview/content_mode.py +264 -0
  42. app/api/routers/preview/header_row.py +154 -0
  43. app/api/routers/preview/models.py +81 -0
  44. app/api/routers/preview/path_mode.py +440 -0
  45. app/api/routers/preview/router.py +30 -0
  46. app/api/routers/project/__init__.py +61 -0
  47. app/api/routers/project/base.py +111 -0
  48. app/api/routers/project/constraint.py +345 -0
  49. app/api/routers/project/full_config.py +411 -0
  50. app/api/routers/project/full_config_writer.py +327 -0
  51. app/api/routers/project/helpers.py +167 -0
  52. app/api/routers/project/inspection_fix.py +330 -0
  53. app/api/routers/project/manifest.py +605 -0
  54. app/api/routers/project/models.py +177 -0
  55. app/api/routers/project/pattern.py +268 -0
  56. app/api/routers/project/regex.py +358 -0
  57. app/api/routers/project/scanner.py +157 -0
  58. app/api/routers/project/schema.py +567 -0
  59. app/api/routers/project/schema_helpers.py +149 -0
  60. app/api/routers/project/settings.py +399 -0
  61. app/api/routers/project/template.py +325 -0
  62. app/api/routers/project/validation.py +227 -0
  63. app/api/routers/project/view.py +177 -0
  64. app/api/routers/project/workspaces.py +167 -0
  65. app/api/routers/projects/__init__.py +25 -0
  66. app/api/routers/projects/create.py +121 -0
  67. app/api/routers/projects/open.py +103 -0
  68. app/api/routers/projects/scan.py +100 -0
  69. app/api/routers/validation/__init__.py +39 -0
  70. app/api/routers/validation/common.py +263 -0
  71. app/api/routers/validation/content_mode.py +401 -0
  72. app/api/routers/validation/history.py +235 -0
  73. app/api/routers/validation/inline_mode.py +196 -0
  74. app/api/routers/validation/path_mode.py +314 -0
  75. app/api/routers/validation/router.py +51 -0
  76. app/api/services/full_validation_response_builder.py +412 -0
  77. app/api/services/io_error_messages.py +41 -0
  78. app/api/services/preview_service.py +248 -0
  79. app/cli/__init__.py +18 -0
  80. app/cli/__main__.py +30 -0
  81. app/cli/shared_services/__init__.py +42 -0
  82. app/cli/shared_services/config_ops.py +581 -0
  83. app/cli/shared_services/generation_ops.py +160 -0
  84. app/cli/shared_services/project_ops.py +288 -0
  85. app/cli/shell/__init__.py +48 -0
  86. app/cli/shell/commands/__init__.py +63 -0
  87. app/cli/shell/commands/ai/__init__.py +260 -0
  88. app/cli/shell/commands/ai/chat.py +331 -0
  89. app/cli/shell/commands/ai/delete.py +165 -0
  90. app/cli/shell/commands/ai/diff.py +70 -0
  91. app/cli/shell/commands/ai/display.py +428 -0
  92. app/cli/shell/commands/ai/executor.py +225 -0
  93. app/cli/shell/commands/ai/executor_utils.py +271 -0
  94. app/cli/shell/commands/ai/generate.py +285 -0
  95. app/cli/shell/commands/ai/interaction.py +262 -0
  96. app/cli/shell/commands/ai/migrate.py +281 -0
  97. app/cli/shell/commands/ai/resolver.py +224 -0
  98. app/cli/shell/commands/ai/status.py +101 -0
  99. app/cli/shell/commands/ai/switch.py +158 -0
  100. app/cli/shell/commands/ai/utils.py +52 -0
  101. app/cli/shell/commands/base.py +343 -0
  102. app/cli/shell/commands/config/__init__.py +131 -0
  103. app/cli/shell/commands/config/base.py +80 -0
  104. app/cli/shell/commands/config/check.py +246 -0
  105. app/cli/shell/commands/config/edit.py +148 -0
  106. app/cli/shell/commands/config/get.py +111 -0
  107. app/cli/shell/commands/config/init.py +123 -0
  108. app/cli/shell/commands/config/inspect.py +164 -0
  109. app/cli/shell/commands/config/list.py +104 -0
  110. app/cli/shell/commands/config/set.py +119 -0
  111. app/cli/shell/commands/config/show.py +150 -0
  112. app/cli/shell/commands/exit.py +67 -0
  113. app/cli/shell/commands/help.py +104 -0
  114. app/cli/shell/commands/infer_schema.py +156 -0
  115. app/cli/shell/commands/open.py +201 -0
  116. app/cli/shell/commands/project.py +215 -0
  117. app/cli/shell/commands/provider.py +619 -0
  118. app/cli/shell/commands/system.py +170 -0
  119. app/cli/shell/commands/validate.py +455 -0
  120. app/cli/shell/completer.py +231 -0
  121. app/cli/shell/config_storage.py +191 -0
  122. app/cli/shell/exceptions.py +90 -0
  123. app/cli/shell/formatter.py +371 -0
  124. app/cli/shell/interactive_menu.py +348 -0
  125. app/cli/shell/main.py +286 -0
  126. app/cli/shell/parser.py +266 -0
  127. app/cli/start.py +180 -0
  128. app/cli_main.py +52 -0
  129. app/mcp_server.py +311 -0
  130. app/shared/core/__init__.py +18 -0
  131. app/shared/core/app_version.py +59 -0
  132. app/shared/core/config/__init__.py +119 -0
  133. app/shared/core/config/server.py +193 -0
  134. app/shared/core/data_source/__init__.py +106 -0
  135. app/shared/core/data_source/loader.py +348 -0
  136. app/shared/core/data_source/loaders/__init__.py +213 -0
  137. app/shared/core/data_source/loaders/base.py +215 -0
  138. app/shared/core/data_source/loaders/converter.py +337 -0
  139. app/shared/core/data_source/loaders/csv_loader.py +252 -0
  140. app/shared/core/data_source/loaders/excel_loader.py +666 -0
  141. app/shared/core/data_source/loaders/extractor.py +268 -0
  142. app/shared/core/data_source/loaders/json_loader.py +354 -0
  143. app/shared/core/data_source/loaders/registry.py +88 -0
  144. app/shared/core/data_source/loaders/sql_loader.py +313 -0
  145. app/shared/core/data_source/loaders/strategies/__init__.py +100 -0
  146. app/shared/core/data_source/loaders/strategies/array_parser.py +277 -0
  147. app/shared/core/data_source/loaders/strategies/lines_parser.py +355 -0
  148. app/shared/core/data_source/loaders/strategies/object_parser.py +278 -0
  149. app/shared/core/data_source/schema_info.py +50 -0
  150. app/shared/core/data_source/specs/__init__.py +128 -0
  151. app/shared/core/data_source/specs/base.py +258 -0
  152. app/shared/core/data_source/specs/csv_source.py +137 -0
  153. app/shared/core/data_source/specs/excel_source.py +141 -0
  154. app/shared/core/data_source/specs/file_base.py +252 -0
  155. app/shared/core/data_source/specs/json_source.py +226 -0
  156. app/shared/core/data_source/specs/sql_source.py +161 -0
  157. app/shared/core/encoding.py +36 -0
  158. app/shared/core/io/__init__.py +24 -0
  159. app/shared/core/io/yaml.py +379 -0
  160. app/shared/core/manifest_schema/__init__.py +52 -0
  161. app/shared/core/manifest_schema/types.py +99 -0
  162. app/shared/core/manifest_schema/version.py +175 -0
  163. app/shared/core/patterns/__init__.py +24 -0
  164. app/shared/core/patterns/loader.py +134 -0
  165. app/shared/core/patterns/writer.py +244 -0
  166. app/shared/core/project/__init__.py +47 -0
  167. app/shared/core/project/constraint/builders/__init__.py +50 -0
  168. app/shared/core/project/constraint/builders/base.py +100 -0
  169. app/shared/core/project/constraint/builders/composite.py +77 -0
  170. app/shared/core/project/constraint/builders/conditional.py +67 -0
  171. app/shared/core/project/constraint/builders/foreign_key.py +53 -0
  172. app/shared/core/project/constraint/builders/registry.py +64 -0
  173. app/shared/core/project/constraint/builders/scripted.py +51 -0
  174. app/shared/core/project/constraint/builders/single_column.py +86 -0
  175. app/shared/core/project/constraint/builders/unique.py +53 -0
  176. app/shared/core/project/constraint/factory.py +170 -0
  177. app/shared/core/project/constraint/reader.py +214 -0
  178. app/shared/core/project/constraint/registry.py +233 -0
  179. app/shared/core/project/constraint/types/__init__.py +63 -0
  180. app/shared/core/project/constraint/types/constraint_file.py +261 -0
  181. app/shared/core/project/constraint/types/refs.py +460 -0
  182. app/shared/core/project/constraint/types.py +28 -0
  183. app/shared/core/project/constraint/writer.py +181 -0
  184. app/shared/core/project/loader/__init__.py +56 -0
  185. app/shared/core/project/loader/loader.py +30 -0
  186. app/shared/core/project/loader/loader_parts/config_inspector.py +137 -0
  187. app/shared/core/project/loader/loader_parts/embedded_constraints.py +224 -0
  188. app/shared/core/project/loader/loader_parts/file_loaders.py +58 -0
  189. app/shared/core/project/loader/loader_parts/inspection_ids.py +85 -0
  190. app/shared/core/project/loader/loader_parts/inspector_helpers.py +312 -0
  191. app/shared/core/project/loader/loader_parts/inspector_id_checks.py +274 -0
  192. app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +690 -0
  193. app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +286 -0
  194. app/shared/core/project/loader/loader_parts/loading_error_messages.py +298 -0
  195. app/shared/core/project/loader/loader_parts/main.py +432 -0
  196. app/shared/core/project/loader/loader_parts/path_validation.py +117 -0
  197. app/shared/core/project/loader/loader_parts/runtime.py +125 -0
  198. app/shared/core/project/loader/types.py +246 -0
  199. app/shared/core/project/manifest/coverage.py +391 -0
  200. app/shared/core/project/manifest/reader.py +260 -0
  201. app/shared/core/project/manifest/types.py +91 -0
  202. app/shared/core/project/manifest/types_parts/__init__.py +60 -0
  203. app/shared/core/project/manifest/types_parts/constants.py +40 -0
  204. app/shared/core/project/manifest/types_parts/data_source.py +68 -0
  205. app/shared/core/project/manifest/types_parts/info.py +59 -0
  206. app/shared/core/project/manifest/types_parts/manifest.py +294 -0
  207. app/shared/core/project/manifest/types_parts/refs.py +153 -0
  208. app/shared/core/project/manifest/types_parts/settings.py +92 -0
  209. app/shared/core/project/manifest/types_parts/settings_file_processing.py +64 -0
  210. app/shared/core/project/manifest/types_parts/settings_script_security.py +78 -0
  211. app/shared/core/project/manifest/types_parts/settings_validation.py +83 -0
  212. app/shared/core/project/manifest/types_parts/template.py +66 -0
  213. app/shared/core/project/manifest/writer.py +262 -0
  214. app/shared/core/project/manual_data/__init__.py +27 -0
  215. app/shared/core/project/manual_data/types.py +75 -0
  216. app/shared/core/project/regex/reader.py +197 -0
  217. app/shared/core/project/regex/types.py +405 -0
  218. app/shared/core/project/regex/writer.py +123 -0
  219. app/shared/core/project/schema/reader.py +170 -0
  220. app/shared/core/project/schema/types.py +47 -0
  221. app/shared/core/project/schema/types_parts/__init__.py +36 -0
  222. app/shared/core/project/schema/types_parts/column.py +174 -0
  223. app/shared/core/project/schema/types_parts/column_utils.py +72 -0
  224. app/shared/core/project/schema/types_parts/constraint.py +165 -0
  225. app/shared/core/project/schema/types_parts/schema_id.py +66 -0
  226. app/shared/core/project/schema/types_parts/source.py +255 -0
  227. app/shared/core/project/schema/types_parts/source_options.py +347 -0
  228. app/shared/core/project/schema/types_parts/table.py +230 -0
  229. app/shared/core/project/schema/writer.py +139 -0
  230. app/shared/core/project/schema_ref_check.py +95 -0
  231. app/shared/core/project/template/__init__.py +27 -0
  232. app/shared/core/project/template/expander.py +263 -0
  233. app/shared/core/project/template/reader.py +120 -0
  234. app/shared/core/project/template/types.py +114 -0
  235. app/shared/core/project/transform/reader.py +76 -0
  236. app/shared/core/project/transform/types.py +116 -0
  237. app/shared/core/project/transform/writer.py +84 -0
  238. app/shared/core/pydantic_messages.py +59 -0
  239. app/shared/core/reporter/__init__.py +46 -0
  240. app/shared/core/reporter/reporter.py +220 -0
  241. app/shared/core/reporter/reporters/__init__.py +65 -0
  242. app/shared/core/reporter/reporters/base.py +188 -0
  243. app/shared/core/reporter/reporters/dingtalk_app_reporter.py +274 -0
  244. app/shared/core/reporter/reporters/email_reporter.py +271 -0
  245. app/shared/core/reporter/reporters/feishu_app_reporter.py +467 -0
  246. app/shared/core/reporter/reporters/local_file_reporter.py +208 -0
  247. app/shared/core/reporter/reporters/wecom_app_reporter.py +268 -0
  248. app/shared/core/utils/__init__.py +18 -0
  249. app/shared/core/utils/path_utils.py +60 -0
  250. app/shared/core/utils/regex_extract.py +113 -0
  251. app/shared/domain/__init__.py +114 -0
  252. app/shared/domain/constraints/__init__.py +76 -0
  253. app/shared/domain/constraints/allowed_values.py +211 -0
  254. app/shared/domain/constraints/base.py +141 -0
  255. app/shared/domain/constraints/charset.py +337 -0
  256. app/shared/domain/constraints/composite.py +174 -0
  257. app/shared/domain/constraints/condition_registry.py +130 -0
  258. app/shared/domain/constraints/conditional.py +629 -0
  259. app/shared/domain/constraints/date_logic.py +731 -0
  260. app/shared/domain/constraints/foreign_key.py +261 -0
  261. app/shared/domain/constraints/key_normalization.py +56 -0
  262. app/shared/domain/constraints/not_null.py +185 -0
  263. app/shared/domain/constraints/range.py +360 -0
  264. app/shared/domain/constraints/regex.py +218 -0
  265. app/shared/domain/constraints/scripted.py +276 -0
  266. app/shared/domain/constraints/unique.py +233 -0
  267. app/shared/domain/data_engine.py +384 -0
  268. app/shared/domain/data_types.py +113 -0
  269. app/shared/domain/data_types_parts/__init__.py +67 -0
  270. app/shared/domain/data_types_parts/base.py +204 -0
  271. app/shared/domain/data_types_parts/composite.py +250 -0
  272. app/shared/domain/data_types_parts/expression.py +202 -0
  273. app/shared/domain/data_types_parts/extracted.py +106 -0
  274. app/shared/domain/data_types_parts/json_types.py +234 -0
  275. app/shared/domain/data_types_parts/scalars.py +719 -0
  276. app/shared/domain/data_types_parts/sequence.py +129 -0
  277. app/shared/domain/dataset_schema.py +48 -0
  278. app/shared/domain/eval_sandbox.py +63 -0
  279. app/shared/domain/expression_system.py +366 -0
  280. app/shared/domain/regex_flags.py +53 -0
  281. app/shared/domain/schema/builder.py +223 -0
  282. app/shared/domain/schema/models.py +339 -0
  283. app/shared/domain/transforms/__init__.py +28 -0
  284. app/shared/domain/transforms/aggregate.py +141 -0
  285. app/shared/domain/transforms/base.py +171 -0
  286. app/shared/domain/transforms/cast_type.py +120 -0
  287. app/shared/domain/transforms/concat.py +103 -0
  288. app/shared/domain/transforms/conditional_assign.py +114 -0
  289. app/shared/domain/transforms/date_format.py +81 -0
  290. app/shared/domain/transforms/digits.py +77 -0
  291. app/shared/domain/transforms/drop_duplicates.py +94 -0
  292. app/shared/domain/transforms/fill_na.py +94 -0
  293. app/shared/domain/transforms/filter_rows.py +97 -0
  294. app/shared/domain/transforms/lookup.py +78 -0
  295. app/shared/domain/transforms/lower_case.py +70 -0
  296. app/shared/domain/transforms/map_value.py +90 -0
  297. app/shared/domain/transforms/math_expr.py +112 -0
  298. app/shared/domain/transforms/modulo.py +82 -0
  299. app/shared/domain/transforms/regex_extract.py +103 -0
  300. app/shared/domain/transforms/registry.py +95 -0
  301. app/shared/domain/transforms/replace.py +88 -0
  302. app/shared/domain/transforms/sort_rows.py +94 -0
  303. app/shared/domain/transforms/string_split.py +84 -0
  304. app/shared/domain/transforms/strip.py +73 -0
  305. app/shared/domain/transforms/substring.py +92 -0
  306. app/shared/domain/transforms/upper_case.py +70 -0
  307. app/shared/domain/transforms/weighted_sum.py +104 -0
  308. app/shared/domain/validation_constraints.py +69 -0
  309. app/shared/services/__init__.py +62 -0
  310. app/shared/services/ai/__init__.py +48 -0
  311. app/shared/services/ai/agent/__init__.py +40 -0
  312. app/shared/services/ai/agent/chat_tools/__init__.py +47 -0
  313. app/shared/services/ai/agent/chat_tools/apply_actions.py +592 -0
  314. app/shared/services/ai/agent/chat_tools/ask_user.py +237 -0
  315. app/shared/services/ai/agent/chat_tools/read_canvas.py +140 -0
  316. app/shared/services/ai/agent/chat_tools/read_project.py +160 -0
  317. app/shared/services/ai/agent/chat_tools/read_table.py +301 -0
  318. app/shared/services/ai/agent/chat_tools/schemas.py +105 -0
  319. app/shared/services/ai/agent/chat_tools/validate_table.py +131 -0
  320. app/shared/services/ai/agent/executor.py +554 -0
  321. app/shared/services/ai/agent/memory.py +256 -0
  322. app/shared/services/ai/agent/planner.py +282 -0
  323. app/shared/services/ai/agent/tool_registry.py +296 -0
  324. app/shared/services/ai/agent/tools/__init__.py +32 -0
  325. app/shared/services/ai/agent/tools/config_generate.py +133 -0
  326. app/shared/services/ai/agent/tools/config_refine.py +109 -0
  327. app/shared/services/ai/agent/tools/config_validate.py +403 -0
  328. app/shared/services/ai/agent/tools/merge_results.py +136 -0
  329. app/shared/services/ai/agent/tools/plan_chunks.py +79 -0
  330. app/shared/services/ai/agent/tools/script_parse.py +359 -0
  331. app/shared/services/ai/agent/types.py +138 -0
  332. app/shared/services/ai/chat_agent_runner.py +596 -0
  333. app/shared/services/ai/chat_orchestrator.py +725 -0
  334. app/shared/services/ai/failure_messages.py +65 -0
  335. app/shared/services/ai/job_storage.py +274 -0
  336. app/shared/services/ai/migrate_service.py +550 -0
  337. app/shared/services/ai/streaming/__init__.py +65 -0
  338. app/shared/services/ai/streaming/event_journal.py +197 -0
  339. app/shared/services/ai/streaming/orchestrator.py +262 -0
  340. app/shared/services/ai/streaming/pending_interaction_store.py +227 -0
  341. app/shared/services/ai/streaming/sse_response.py +148 -0
  342. app/shared/services/ai/streaming/types.py +73 -0
  343. app/shared/services/ai/types.py +213 -0
  344. app/shared/services/ai/utils.py +322 -0
  345. app/shared/services/diff/config_diff.py +320 -0
  346. app/shared/services/hardware.py +237 -0
  347. app/shared/services/llm/__init__.py +62 -0
  348. app/shared/services/llm/actions/__init__.py +42 -0
  349. app/shared/services/llm/actions/_canvas_validator.py +147 -0
  350. app/shared/services/llm/actions/_constraint_validator.py +401 -0
  351. app/shared/services/llm/actions/_regex_validator.py +85 -0
  352. app/shared/services/llm/actions/_schema_validator.py +82 -0
  353. app/shared/services/llm/actions/_settings_validator.py +76 -0
  354. app/shared/services/llm/actions/_transform_validator.py +78 -0
  355. app/shared/services/llm/actions/action_handlers.py +400 -0
  356. app/shared/services/llm/actions/action_parser.py +63 -0
  357. app/shared/services/llm/actions/action_processor.py +468 -0
  358. app/shared/services/llm/actions/action_validator.py +311 -0
  359. app/shared/services/llm/actions/diff_compute.py +195 -0
  360. app/shared/services/llm/actions/regex_handlers.py +284 -0
  361. app/shared/services/llm/actions/registry.py +336 -0
  362. app/shared/services/llm/actions/schema_handlers.py +335 -0
  363. app/shared/services/llm/actions/settings_handlers.py +180 -0
  364. app/shared/services/llm/actions/specs.py +259 -0
  365. app/shared/services/llm/actions/transform_handlers.py +279 -0
  366. app/shared/services/llm/actions/validation_types.py +147 -0
  367. app/shared/services/llm/cache/__init__.py +18 -0
  368. app/shared/services/llm/cache/response_cache.py +80 -0
  369. app/shared/services/llm/chat/__init__.py +35 -0
  370. app/shared/services/llm/chat/chat_system_prompt.py +561 -0
  371. app/shared/services/llm/chat/response_parser.py +296 -0
  372. app/shared/services/llm/config/__init__.py +45 -0
  373. app/shared/services/llm/config/crypto.py +135 -0
  374. app/shared/services/llm/config/loader.py +258 -0
  375. app/shared/services/llm/config/models.py +202 -0
  376. app/shared/services/llm/config/presets.py +128 -0
  377. app/shared/services/llm/config_generator.py +163 -0
  378. app/shared/services/llm/constraints/__init__.py +41 -0
  379. app/shared/services/llm/constraints/constraint_builder.py +177 -0
  380. app/shared/services/llm/constraints/constraint_deletion.py +84 -0
  381. app/shared/services/llm/constraints/constraint_id.py +174 -0
  382. app/shared/services/llm/constraints/frontend_instructions.py +333 -0
  383. app/shared/services/llm/constraints/inline_batch.py +279 -0
  384. app/shared/services/llm/discovery/__init__.py +35 -0
  385. app/shared/services/llm/discovery/scanner.py +181 -0
  386. app/shared/services/llm/generation/__init__.py +42 -0
  387. app/shared/services/llm/generation/agent_wiring.py +156 -0
  388. app/shared/services/llm/generation/config_builder.py +386 -0
  389. app/shared/services/llm/generation/errors.py +36 -0
  390. app/shared/services/llm/generation/existing_config.py +99 -0
  391. app/shared/services/llm/generation/profiler.py +279 -0
  392. app/shared/services/llm/generation/prompt_builder.py +199 -0
  393. app/shared/services/llm/generation/response_parser.py +144 -0
  394. app/shared/services/llm/generation/service.py +622 -0
  395. app/shared/services/llm/models.py +104 -0
  396. app/shared/services/llm/providers/__init__.py +48 -0
  397. app/shared/services/llm/providers/base.py +310 -0
  398. app/shared/services/llm/providers/cached_provider.py +90 -0
  399. app/shared/services/llm/providers/ollama.py +498 -0
  400. app/shared/services/llm/providers/openai.py +334 -0
  401. app/shared/services/llm/providers/registry.py +108 -0
  402. app/shared/services/llm/schema_resolver.py +141 -0
  403. app/shared/services/llm/suggestion_utils.py +219 -0
  404. app/shared/services/llm/validate_executor.py +163 -0
  405. app/shared/services/llm/yaml_io.py +406 -0
  406. app/shared/services/preview/__init__.py +18 -0
  407. app/shared/services/preview/loader.py +145 -0
  408. app/shared/services/preview/path_validation.py +138 -0
  409. app/shared/services/project_loader.py +57 -0
  410. app/shared/services/schema_inference.py +260 -0
  411. app/shared/services/schema_runtime_builder.py +120 -0
  412. app/shared/services/validation/__init__.py +52 -0
  413. app/shared/services/validation/chunked_loader.py +509 -0
  414. app/shared/services/validation/dag/__init__.py +35 -0
  415. app/shared/services/validation/dag/builder.py +141 -0
  416. app/shared/services/validation/dag/executor.py +225 -0
  417. app/shared/services/validation/dag/sorter.py +77 -0
  418. app/shared/services/validation/data_loader.py +231 -0
  419. app/shared/services/validation/engine.py +446 -0
  420. app/shared/services/validation/executor.py +1030 -0
  421. app/shared/services/validation/extractors.py +266 -0
  422. app/shared/services/validation/history.py +209 -0
  423. app/shared/services/validation/json_payload.py +136 -0
  424. app/shared/services/validation/loader.py +152 -0
  425. app/shared/services/validation/memory_monitor.py +179 -0
  426. app/shared/services/validation/postprocess.py +208 -0
  427. app/shared/services/validation/progress.py +64 -0
  428. app/shared/services/validation/report_export.py +222 -0
  429. app/shared/services/validation/resolver.py +180 -0
  430. app/shared/services/validation/service.py +418 -0
  431. app/shared/services/validation/types.py +238 -0
  432. app/shared/services/validation/validators/__init__.py +36 -0
  433. app/shared/services/validation/validators/adapter.py +182 -0
  434. app/shared/services/validation/validators/base.py +332 -0
  435. app/shared/services/validation/validators/composite.py +233 -0
  436. app/shared/services/validation/validators/date_logic.py +276 -0
  437. app/start_server.py +133 -0
  438. precis_cli-0.1.3.dist-info/METADATA +180 -0
  439. precis_cli-0.1.3.dist-info/RECORD +442 -0
  440. precis_cli-0.1.3.dist-info/WHEEL +5 -0
  441. precis_cli-0.1.3.dist-info/entry_points.txt +4 -0
  442. precis_cli-0.1.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,460 @@
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
+ - 定义 V2 约束规则的数据类型(使用 Pydantic BaseModel)
21
+ - 定义各种约束类型的引用结构(Refs)
22
+ - 提供约束配置文件的数据模型
23
+
24
+ 架构设计:
25
+ - 类型定义层: Constraint 系统的核心数据模型层
26
+ - 分离设计: refs(引用区)定义约束目标,params(参数区)定义约束参数
27
+ - Pydantic 验证: 使用 BaseModel 进行数据验证和序列化
28
+
29
+ 输入示例:
30
+ refs = UniqueRefs(table_id="users", column_ids=["email"])
31
+ constraint = ConstraintFile(
32
+ version=2,
33
+ id="unique_email",
34
+ type="Unique",
35
+ refs=refs,
36
+ params={},
37
+ )
38
+
39
+ 输出示例:
40
+ yaml_data = constraint.model_dump(exclude_none=True)
41
+ """
42
+
43
+ from __future__ import annotations
44
+
45
+ from typing import Any, Literal
46
+
47
+ from pydantic import BaseModel, Field
48
+
49
+
50
+ class UniqueRefs(BaseModel):
51
+ """
52
+ @classdesc 唯一性约束引用结构。
53
+
54
+ ============================================================================
55
+ 配置文件对应关系 (这个类对应哪种配置文件)
56
+ ============================================================================
57
+ 本类对应 *.constraint.yaml 中 Unique 约束的 refs 字段:
58
+
59
+ ```yaml
60
+ # *.constraint.yaml - Unique 约束示例
61
+ refs:
62
+ table_id: users # 目标表的 ID
63
+ column_ids: [email] # 目标列 ID 列表(单列)
64
+ # 或多列组合唯一:
65
+ refs:
66
+ table_id: orders
67
+ column_ids: [user_id, order_date] # 组合唯一
68
+ ```
69
+
70
+ ============================================================================
71
+ 字段映射说明 (YAML 如何变成 Python 对象)
72
+ ============================================================================
73
+ | YAML 字段 | Python 属性 | 数据类型 | 说明 | 示例值 |
74
+ |----------|------------|---------|------|-------|
75
+ | table_id | table_id | str | 目标表的 ID | "users" |
76
+ | column_ids | column_ids | List[str] | 目标列 ID 列表 | ["email"] |
77
+
78
+ ============================================================================
79
+ 业务场景 (这个类在什么地方被使用)
80
+ ============================================================================
81
+ - 场景1: 确保邮箱唯一
82
+ 约束类型为 Unique,refs 指定表和列,系统校验时检查该列是否有重复值。
83
+
84
+ - 场景2: 组合唯一
85
+ 如订单表中 (user_id, order_date) 组合必须唯一。
86
+
87
+ ============================================================================
88
+ 使用示例
89
+ ============================================================================
90
+ 【创建实例】
91
+ ```python
92
+ from app.shared.core.project.constraint.types import UniqueRefs
93
+
94
+ refs = UniqueRefs(
95
+ table_id="users",
96
+ column_ids=["email"]
97
+ )
98
+ ```
99
+ """
100
+
101
+ # 目标表的 ID(对应 schema 文件中的 table.id)
102
+ table_id: str
103
+ # 目标列 ID 列表(对应 schema 文件中的 column.id)
104
+ # 多列组合表示这些列的组合值必须唯一
105
+ column_ids: list[str]
106
+
107
+
108
+ class NotNullRefs(BaseModel):
109
+ """
110
+ @classdesc 非空约束引用结构。
111
+
112
+ ============================================================================
113
+ 配置文件对应关系 (这个类对应哪种配置文件)
114
+ ============================================================================
115
+ 本类对应 *.constraint.yaml 中 NotNull 约束的 refs 字段:
116
+
117
+ ```yaml
118
+ # *.constraint.yaml - NotNull 约束示例
119
+ refs:
120
+ table_id: users # 目标表的 ID
121
+ column_id: email # 目标列 ID
122
+ ```
123
+
124
+ ============================================================================
125
+ 字段映射说明
126
+ ============================================================================
127
+ | YAML 字段 | Python 属性 | 数据类型 | 说明 |
128
+ |----------|------------|---------|------|
129
+ | table_id | table_id | str | 目标表的 ID |
130
+ | column_id | column_id | str | 目标列 ID |
131
+
132
+ ============================================================================
133
+ 业务场景
134
+ ============================================================================
135
+ - 场景1: 确保必填字段有值
136
+ 用户注册时,邮箱和手机号必须填写,不能为空。
137
+
138
+ :param table_id: 目标表的 ID
139
+ :param column_id: 目标列 ID
140
+ """
141
+
142
+ # 目标表的 ID
143
+ table_id: str
144
+ # 目标列 ID
145
+ column_id: str
146
+
147
+
148
+ class AllowedValuesRefs(BaseModel):
149
+ """
150
+ @classdesc 允许值约束引用结构。
151
+
152
+ ============================================================================
153
+ 配置文件对应关系 (这个类对应哪种配置文件)
154
+ ============================================================================
155
+ 本类对应 *.constraint.yaml 中 AllowedValues 约束的 refs 字段:
156
+
157
+ ```yaml
158
+ # *.constraint.yaml - AllowedValues 约束示例
159
+ refs:
160
+ table_id: users # 目标表的 ID
161
+ column_id: gender # 目标列 ID
162
+ params:
163
+ allowed_values: [男, 女, 未知] # 允许值列表(在 params 中)
164
+ ```
165
+
166
+ ============================================================================
167
+ 业务场景
168
+ ============================================================================
169
+ - 场景1: 限制枚举值
170
+ 性别字段只能是"男"、"女"、"未知"。
171
+
172
+ :param table_id: 目标表的 ID
173
+ :param column_id: 目标列 ID
174
+ """
175
+
176
+ # 目标表的 ID
177
+ table_id: str
178
+ # 目标列 ID
179
+ column_id: str
180
+
181
+
182
+ class ForeignKeyRefs(BaseModel):
183
+ """
184
+ @classdesc 外键约束引用结构。
185
+
186
+ ============================================================================
187
+ 配置文件对应关系 (这个类对应哪种配置文件)
188
+ ============================================================================
189
+ 本类对应 *.constraint.yaml 中 ForeignKey 约束的 refs 字段:
190
+
191
+ ```yaml
192
+ # *.constraint.yaml - ForeignKey 约束示例
193
+ refs:
194
+ from_table_id: orders # 源表 ID(包含外键的表)
195
+ from_column_id: user_id # 源列 ID(外键列)
196
+ to_table_id: users # 目标表 ID(被参照的表)
197
+ to_column_id: user_id # 目标列 ID(被参照的列,通常为主键)
198
+ ```
199
+
200
+ ============================================================================
201
+ 业务场景
202
+ ============================================================================
203
+ - 场景1: 订单关联用户
204
+ 订单表中的 user_id 必须在用户表中存在。
205
+
206
+ :param from_table_id: 源表 ID
207
+ :param from_column_id: 源列 ID
208
+ :param to_table_id: 目标表 ID
209
+ :param to_column_id: 目标列 ID
210
+ """
211
+
212
+ # 源表 ID(包含外键的表)
213
+ from_table_id: str
214
+ # 源列 ID(外键列)
215
+ from_column_id: str
216
+ # 目标表 ID(被参照的表)
217
+ to_table_id: str
218
+ # 目标列 ID(被参照的列,通常为主键)
219
+ to_column_id: str
220
+
221
+
222
+ class ConditionalIfCondition(BaseModel):
223
+ """
224
+ @classdesc 条件约束的 IF 条件结构。
225
+
226
+ ============================================================================
227
+ 配置文件对应关系 (这个类对应哪种配置文件)
228
+ ============================================================================
229
+ 本类对应 *.constraint.yaml 中 Conditional 约束的 if 条件:
230
+
231
+ ```yaml
232
+ # *.constraint.yaml - Conditional 约束示例
233
+ refs:
234
+ table_id: users
235
+ then_column_id: status
236
+ if_conditions:
237
+ - if_column_id: age
238
+ operator: ">"
239
+ value: 18
240
+ if_logic: and
241
+ params:
242
+ then_value: "成人"
243
+ ```
244
+
245
+ ============================================================================
246
+ 字段映射说明
247
+ ============================================================================
248
+ | YAML 字段 | Python 属性 | 数据类型 | 说明 |
249
+ |----------|------------|---------|------|
250
+ | if_column_id | if_column_id | str | 条件判断依据的列 ID |
251
+ | operator | operator | str | 比较运算符 |
252
+ | value | value | Any | 单值比较时的值 |
253
+ | values | values | Optional[List[Any]] | 多值比较时的值列表 |
254
+
255
+ ============================================================================
256
+ 业务场景
257
+ ============================================================================
258
+ - 场景1: 条件赋值
259
+ 如果年龄 > 18,则状态为"成人"。
260
+
261
+ :param if_column_id: 条件判断依据的列 ID
262
+ :param operator: 比较运算符
263
+ :param value: 单值比较时的值
264
+ :param values: 多值比较时的值列表
265
+ """
266
+
267
+ # 条件判断依据的列 ID
268
+ if_column_id: str
269
+ # 比较运算符,支持 eq/ne/in/greater_than/less_than 等
270
+ operator: str = Field(..., description="eq/ne/in/greater_than 等")
271
+ # 单值比较时的值
272
+ value: Any = None
273
+ # 多值比较时的值列表(用于 in 运算符)
274
+ values: list[Any] | None = None
275
+
276
+
277
+ class ConditionalRefs(BaseModel):
278
+ """
279
+ @classdesc 条件约束引用结构。
280
+
281
+ ============================================================================
282
+ 配置文件对应关系 (这个类对应哪种配置文件)
283
+ ============================================================================
284
+ 本类对应 *.constraint.yaml 中 Conditional 约束的 refs 字段:
285
+
286
+ ```yaml
287
+ # *.constraint.yaml - Conditional 约束示例
288
+ refs:
289
+ table_id: users
290
+ then_column_id: status
291
+ if_conditions:
292
+ - if_column_id: age
293
+ operator: ">"
294
+ value: 18
295
+ if_logic: and
296
+ ```
297
+
298
+ ============================================================================
299
+ 业务场景
300
+ ============================================================================
301
+ - 场景1: 条件更新
302
+ 当满足某些条件时,对特定列应用约束规则。
303
+
304
+ :param table_id: 目标表 ID
305
+ :param then_column_id: 需要应用约束的列 ID
306
+ :param if_conditions: IF 条件列表
307
+ :param if_logic: 条件逻辑:and/or
308
+ """
309
+
310
+ # 目标表 ID
311
+ table_id: str
312
+ # THEN 部分的列 ID(需要应用约束的列)
313
+ then_column_id: str
314
+ # IF 条件列表(支持多个条件组合)
315
+ if_conditions: list[ConditionalIfCondition] = Field(default_factory=list)
316
+ # 条件逻辑:and(全部满足)/ or(任一满足)
317
+ if_logic: Literal["and", "or"] = "and"
318
+
319
+
320
+ class ScriptedRefs(BaseModel):
321
+ """
322
+ @classdesc 脚本约束引用结构。
323
+
324
+ ============================================================================
325
+ 配置文件对应关系 (这个类对应哪种配置文件)
326
+ ============================================================================
327
+ 本类对应 *.constraint.yaml 中 Scripted 约束的 refs 字段:
328
+
329
+ ```yaml
330
+ # *.constraint.yaml - Scripted 约束示例
331
+ refs:
332
+ table_id: users
333
+ column_id: email
334
+ params:
335
+ expression: "validate_email(value)" # 脚本表达式
336
+ ```
337
+
338
+ ============================================================================
339
+ 业务场景
340
+ ============================================================================
341
+ - 场景1: 自定义验证逻辑
342
+ 使用脚本表达式进行复杂的数据验证。
343
+
344
+ :param table_id: 目标表 ID
345
+ :param column_id: 目标列 ID(可选,若不指定则对整行数据执行脚本)
346
+ """
347
+
348
+ # 目标表 ID
349
+ table_id: str
350
+ # 目标列 ID(可选,若不指定则对整行数据执行脚本)
351
+ column_id: str | None = None
352
+
353
+
354
+ class RangeRefs(BaseModel):
355
+ """
356
+ @classdesc 区间约束引用结构。
357
+
358
+ ============================================================================
359
+ 配置文件对应关系 (这个类对应哪种配置文件)
360
+ ============================================================================
361
+ 本类对应 *.constraint.yaml 中 Range 约束的 refs 字段:
362
+
363
+ ```yaml
364
+ # *.constraint.yaml - Range 约束示例
365
+ refs:
366
+ table_id: products
367
+ column_id: price
368
+ params:
369
+ min: 0
370
+ max: 10000
371
+ ```
372
+
373
+ ============================================================================
374
+ 业务场景
375
+ ============================================================================
376
+ - 场景1: 数值范围校验
377
+ 商品价格必须在 0-10000 之间。
378
+
379
+ :param table_id: 目标表 ID
380
+ :param column_id: 目标列 ID
381
+ """
382
+
383
+ # 目标表 ID
384
+ table_id: str
385
+ # 目标列 ID
386
+ column_id: str
387
+
388
+
389
+ class DateLogicRefs(BaseModel):
390
+ """
391
+ @classdesc 日期逻辑约束引用结构。
392
+
393
+ ============================================================================
394
+ 配置文件对应关系 (这个类对应哪种配置文件)
395
+ ============================================================================
396
+ 本类对应 *.constraint.yaml 中 DateLogic 约束的 refs 字段:
397
+
398
+ ```yaml
399
+ # *.constraint.yaml - DateLogic 约束示例
400
+ refs:
401
+ table_id: users
402
+ column_id: birth_date
403
+ params:
404
+ logic_mode: compare # compare=比较模式, calculation=计算模式
405
+ compare_op: gt # gt/lt/eq/gte/lte/range
406
+ reference_date: "2000-01-01"
407
+ ```
408
+
409
+ ============================================================================
410
+ 业务场景
411
+ ============================================================================
412
+ - 场景1: 日期比较
413
+ 出生日期必须晚于 2000-01-01。
414
+
415
+ - 场景2: 日期范围
416
+ 订单日期必须在 2024-01-01 到 2024-12-31 之间。
417
+ 此时 params 中 compare_op 为 "range",并需要 reference_date_end / reference_column_end。
418
+
419
+ :param table_id: 目标表 ID
420
+ :param column_id: 目标列 ID
421
+ """
422
+
423
+ # 目标表 ID
424
+ table_id: str
425
+ # 目标列 ID
426
+ column_id: str
427
+
428
+
429
+ class CharsetRefs(BaseModel):
430
+ """
431
+ @classdesc 字符集约束引用结构。
432
+
433
+ ============================================================================
434
+ 配置文件对应关系 (这个类对应哪种配置文件)
435
+ ============================================================================
436
+ 本类对应 *.constraint.yaml 中 Charset 约束的 refs 字段:
437
+
438
+ ```yaml
439
+ # *.constraint.yaml - Charset 约束示例
440
+ refs:
441
+ table_id: users
442
+ column_id: name
443
+ params:
444
+ encoding: utf-8
445
+ ```
446
+
447
+ ============================================================================
448
+ 业务场景
449
+ ============================================================================
450
+ - 场景1: 字符集检测
451
+ 检测列数据是否符合指定字符集编码。
452
+
453
+ :param table_id: 目标表 ID
454
+ :param column_id: 目标列 ID
455
+ """
456
+
457
+ # 目标表 ID
458
+ table_id: str
459
+ # 目标列 ID
460
+ column_id: str
@@ -0,0 +1,28 @@
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
+ - 向后兼容的重新导出入口
21
+ - 将 constraint.types 子包的内容暴露到 app.shared.core.project.constraint.types 命名空间
22
+
23
+ 架构设计:
24
+ - 兼容层模式:保持历史导入路径稳定
25
+ - 无逻辑:仅重新导出,不定义新类型
26
+ """
27
+
28
+ from .types import * # noqa: F401,F403
@@ -0,0 +1,181 @@
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.yaml 文件
21
+ - 将程序中的 ConstraintFile 对象持久化为 YAML 格式配置文件
22
+
23
+ 架构设计:
24
+ - 写入层: 负责将配置对象序列化并写入文件系统
25
+ - 简洁输出: 使用 model_dump(exclude_none=True) 排除 None 字段
26
+ - 读写配合: 与 reader.py 配合实现约束配置全生命周期管理
27
+
28
+ 输入示例:
29
+ constraint = ConstraintFile(
30
+ version=2,
31
+ id="not_null_email",
32
+ type="NotNull",
33
+ enabled=True,
34
+ refs={"table_id": "users", "column_id": "email"},
35
+ params={},
36
+ )
37
+
38
+ 输出示例:
39
+ save_constraint(constraint, "constraints/email.constraint.yaml")
40
+ """
41
+
42
+ from __future__ import annotations
43
+
44
+ from pathlib import Path
45
+ from typing import TYPE_CHECKING
46
+
47
+ # 导入底层的 YAML 写入工具
48
+ from app.shared.core.io.yaml import write_yaml_atomic
49
+
50
+ # 导入约束配置的数据模型
51
+ from .types import ConstraintFile
52
+
53
+ if TYPE_CHECKING:
54
+ # 仅在类型检查阶段导入,避免运行时循环依赖
55
+ from .types import ConstraintFile
56
+
57
+
58
+ def save_constraint(constraint: ConstraintFile, constraint_path: str | Path) -> None:
59
+ """
60
+ @methoddesc 保存约束到 YAML 文件。
61
+
62
+ ============================================================================
63
+ 输入数据示例 (这个函数接收什么数据)
64
+ ============================================================================
65
+ 本函数接收 ConstraintFile 对象:
66
+
67
+ ```python
68
+ # 创建 Unique 约束对象
69
+ constraint = ConstraintFile(
70
+ version=2,
71
+ id="unique_user_email",
72
+ type="Unique",
73
+ enabled=True,
74
+ description="用户邮箱必须唯一",
75
+ refs={
76
+ "table_id": "users",
77
+ "column_ids": ["email"]
78
+ },
79
+ params={}
80
+ )
81
+ ```
82
+
83
+ 写入后的 YAML 文件内容:
84
+
85
+ ```yaml
86
+ # 约束规则配置文件 (*.constraint.yaml)
87
+ version: 2
88
+
89
+ id: unique_user_email
90
+
91
+ type: Unique
92
+
93
+ enabled: true
94
+
95
+ description: "用户邮箱必须唯一"
96
+
97
+ refs:
98
+ table_id: users
99
+ column_ids:
100
+ - email
101
+
102
+ params: {}
103
+ ```
104
+
105
+ ============================================================================
106
+ 业务场景 (什么情况下会调用这个函数)
107
+ ============================================================================
108
+ - 场景1: 用户通过 UI 创建约束
109
+ 用户在约束创建页面填写完配置表单后,点击保存按钮。
110
+ 系统将表单数据构建为 ConstraintFile 对象,然后调用 save_constraint
111
+ 将其写入到 constraints/ 目录下的 *.constraint.yaml 文件。
112
+
113
+ - 场景2: 用户通过 UI 编辑约束
114
+ 用户修改现有约束的配置后,点击保存按钮。
115
+ 系统更新 ConstraintFile 对象并写回原文件。
116
+
117
+ - 场景3: 导入约束配置
118
+ 用户导入约束配置文件时,系统将其写入项目目录。
119
+
120
+ ============================================================================
121
+ 数据流 (输入如何变成输出)
122
+ ============================================================================
123
+ 输入参数:
124
+ - constraint: ConstraintFile,约束配置对象
125
+ 示例值: ConstraintFile(id="unique_user_email", type="Unique", ...)
126
+ - constraint_path: str | Path,目标文件路径
127
+ 示例值: "constraints/unique_user_email.constraint.yaml"
128
+
129
+ 处理步骤:
130
+ ┌─────────────────────────────────────────────────────────────┐
131
+ │ Step 1: 路径标准化 │
132
+ ├─────────────────────────────────────────────────────────────┤
133
+ │ 输入: 字符串或 Path 对象 │
134
+ │ 操作: 转换为 Path 对象 │
135
+ │ 输出: Path 对象 │
136
+ └─────────────────────────────────────────────────────────────┘
137
+
138
+ ┌─────────────────────────────────────────────────────────────┐
139
+ │ Step 2: 对象序列化为字典 │
140
+ ├─────────────────────────────────────────────────────────────┤
141
+ │ 输入: ConstraintFile 对象 │
142
+ │ 操作: 使用 model_dump(exclude_none=True) 转换为字典 │
143
+ │ exclude_none=True 排除值为 None 的可选字段 │
144
+ │ 输出: Dict 对象 │
145
+ └─────────────────────────────────────────────────────────────┘
146
+
147
+ ┌─────────────────────────────────────────────────────────────┐
148
+ │ Step 3: 写入 YAML 文件 │
149
+ ├─────────────────────────────────────────────────────────────┤
150
+ │ 输入: 字典对象 │
151
+ │ 操作: 调用 write_yaml 写入文件 │
152
+ │ 输出: (无返回值,文件已写入) │
153
+ └─────────────────────────────────────────────────────────────┘
154
+
155
+ 最终输出: None (无返回值,写入成功后直接返回)
156
+
157
+ ============================================================================
158
+ 异常处理 (可能出什么问题)
159
+ ============================================================================
160
+ | 异常类型 | 触发条件 | 处理方式 |
161
+ |---------|---------|---------|
162
+ | PermissionError | 没有写入权限 | 检查文件/目录权限 |
163
+ | FileNotFoundError | 父目录不存在 | 检查路径有效性 |
164
+
165
+ :param constraint: ConstraintFile 对象,包含完整的约束配置数据
166
+ :param constraint_path: 保存路径,可以是字符串或 Path 对象
167
+ :return: 无返回值(写入文件后直接返回)
168
+ :raises IOError: 文件写入失败时由 write_yaml 抛出
169
+
170
+ 示例:
171
+ constraint = ConstraintFile(
172
+ id="unique_user_email",
173
+ type="Unique",
174
+ refs={"table_id": "users", "column_ids": ["email"]},
175
+ params={}
176
+ )
177
+ save_constraint(constraint, "/project/constraints/unique_user_email.constraint.yaml")
178
+ """
179
+ # 步骤1:将 ConstraintFile 对象转换为字典,exclude_none=True 排除空值字段
180
+ # 步骤2:调用底层 YAML 写入函数将数据持久化到文件(write_yaml_atomic 内部处理字符串/Path 路径)
181
+ write_yaml_atomic(Path(constraint_path), constraint.model_dump(exclude_none=True))