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,56 @@
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
+ - 对外提供统一的 load_project 加载接口
22
+ - 作为 app.shared.core.project.loader 包的门面(Facade),封装底层实现
23
+
24
+ 架构设计:
25
+ - 门面模式: 将 loader.py(入口函数实现)和 types.py(数据模型)等
26
+ 子模块的细节隐藏在本模块之后,调用方无需关心内部文件结构。
27
+ - 类型重导出: LoadedProject、LoadedRegexNode、LoadingError 等核心类型
28
+ 从底层子模块重新导出,确保调用方只需导入本模块即可获得完整类型支持。
29
+
30
+ 导入路径示例:
31
+ >>> from app.shared.core.project.loader import load_project, LoadedProject
32
+ >>> project = load_project(Path("project.precis.yaml"))
33
+ >>> print(project.schemas)
34
+
35
+ 注意事项:
36
+ - 若需新增对外暴露的类型,应在本模块执行重导出,
37
+ 并在 __all__ 中补充名称以维持显式控制。
38
+ - 禁止在本模块编写具体业务逻辑,所有实现应下沉到 loader.py 或 types.py。
39
+ """
40
+
41
+ # 从 loader 子模块导入项目加载入口函数
42
+ # load_project 是项目加载流程的顶层编排函数,负责协调解析、校验和组装
43
+ from .loader import load_project
44
+
45
+ # 从 types 子模块导入核心数据类型
46
+ # 这些类型定义了加载结果的数据结构,供上层服务和 API 路由使用
47
+ from .types import LoadedProject, LoadedRegexNode, LoadingError
48
+
49
+ # 控制 `from module import *` 时导出的公开名称
50
+ # 显式列出公开 API,防止内部辅助函数或子模块被意外暴露
51
+ __all__ = [
52
+ "LoadedProject",
53
+ "LoadedRegexNode",
54
+ "LoadingError",
55
+ "load_project",
56
+ ]
@@ -0,0 +1,30 @@
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
+ - 从 loader_parts.main 代理导出 load_project 入口函数
21
+ - 保持向后兼容的导入路径
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ # 从主加载逻辑模块导入唯一的入口函数
27
+ from app.shared.core.project.loader.loader_parts.main import load_project
28
+
29
+ # 限定模块的公开接口,仅暴露 load_project
30
+ __all__ = ["load_project"]
@@ -0,0 +1,137 @@
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
+
18
+ 本文件已拆分为 4 个职责清晰的子模块,此处保留 inspect_config 编排入口
19
+ + re-export 全部公开符号,保持 3 个调用方(loader/main.py、full_config.py、CLI)
20
+ 的 import 路径完全兼容。
21
+
22
+ 拆分后的模块结构(按 DAG 依赖层级):
23
+ - inspector_helpers.py — 公共工具(display/actions/columns,Layer 0)
24
+ - inspector_id_checks.py — ID 一致性检查(Layer 1)
25
+ - inspector_reference_checks.py — 引用完整性检查(Layer 1)
26
+ - inspector_uniqueness_checks.py — Schema 唯一性检查(Layer 1,不依赖 helpers)
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import logging
32
+ from pathlib import Path
33
+ from typing import TYPE_CHECKING
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ if TYPE_CHECKING:
38
+ from app.shared.core.project.constraint.types import ConstraintFile
39
+ from app.shared.core.project.manifest.types import ProjectManifest
40
+ from app.shared.core.project.manual_data.types import ManualDataFile
41
+ from app.shared.core.project.regex.types import RegexNodeFile
42
+ from app.shared.core.project.schema.types import TableSchemaFile
43
+ from app.shared.core.project.transform.types import TransformFile
44
+
45
+ # re-export 全部公开符号,保持调用方零改动
46
+ # 向后兼容别名:测试直接 import 了原文件的 _xxx 私有函数,
47
+ # 拆分后这些函数移到了 inspector_helpers 并去掉了 _ 前缀。
48
+ # 此处提供带 _ 前缀的别名保持测试零改动。
49
+ from app.shared.core.project.loader.loader_parts.inspector_helpers import ( # noqa: F401
50
+ default_actions_for_file as _default_actions_for_file,
51
+ )
52
+ from app.shared.core.project.loader.loader_parts.inspector_id_checks import ( # noqa: F401
53
+ inspect_id_consistency,
54
+ )
55
+ from app.shared.core.project.loader.loader_parts.inspector_reference_checks import ( # noqa: F401
56
+ inspect_reference_integrity,
57
+ inspect_regex_reference_integrity,
58
+ )
59
+ from app.shared.core.project.loader.loader_parts.inspector_uniqueness_checks import ( # noqa: F401
60
+ inspect_schema_id_orphan_conflict,
61
+ inspect_source_uniqueness,
62
+ )
63
+
64
+
65
+ def inspect_config(
66
+ manifest_path: Path,
67
+ manifest: ProjectManifest,
68
+ schema_files: dict[str, TableSchemaFile],
69
+ constraint_files: dict[str, ConstraintFile],
70
+ regex_node_files: dict[str, RegexNodeFile],
71
+ transform_files: dict[str, TransformFile],
72
+ manual_data_files: dict[str, ManualDataFile],
73
+ warnings: list[str],
74
+ loading_errors: list, # list[LoadingError],用 list 避免循环 import
75
+ ) -> None:
76
+ """配置文件格式自检主入口。
77
+
78
+ 按 5 个检查维度依次执行,结果就地追加到 warnings / loading_errors:
79
+ 1. ID 跨文件一致性(manifest ID vs 文件内部 ID)
80
+ 2. Schema ID 全局唯一性(基于磁盘扫描,检测重复 ID)
81
+ 3. Schema 数据源唯一性(检测多个 schema 指向同一数据源)
82
+ 4. 约束引用完整性(表/列是否存在)
83
+ 5. 正则引用完整性(表/列是否存在)
84
+ """
85
+ logger.info("[配置自检] 开始检查项目配置: %s", manifest_path.parent.name)
86
+ logger.info(
87
+ "[配置自检] 检查范围: %d schemas, %d constraints, %d regex, %d transforms, %d manual_data",
88
+ len(schema_files),
89
+ len(constraint_files),
90
+ len(regex_node_files),
91
+ len(transform_files),
92
+ len(manual_data_files),
93
+ )
94
+
95
+ errors_before = len(loading_errors)
96
+ warnings_before = len(warnings)
97
+
98
+ inspect_id_consistency(
99
+ manifest,
100
+ schema_files,
101
+ constraint_files,
102
+ regex_node_files,
103
+ transform_files,
104
+ manual_data_files,
105
+ warnings,
106
+ loading_errors,
107
+ )
108
+
109
+ # id → 文件相对路径索引(来自 manifest 引用),供引用/唯一性检查
110
+ # 把问题归属到具体配置文件(前端"按文件"分组依赖 file_path 字段)
111
+ schema_paths = {ref.id: ref.path for ref in manifest.schemas}
112
+ constraint_paths = {ref.id: ref.path for ref in manifest.constraints}
113
+ regex_paths = {ref.id: ref.path for ref in manifest.regex_nodes}
114
+
115
+ # Schema ID 全局唯一性检测(基于磁盘扫描)
116
+ # 同时覆盖 manifest 内文件冲突与孤儿文件冲突,避免重复上报
117
+ inspect_schema_id_orphan_conflict(manifest_path.parent, manifest, schema_files, loading_errors)
118
+
119
+ inspect_source_uniqueness(schema_files, loading_errors, schema_paths)
120
+
121
+ inspect_reference_integrity(
122
+ schema_files, constraint_files, warnings, loading_errors, constraint_paths, schema_paths
123
+ )
124
+
125
+ inspect_regex_reference_integrity(
126
+ regex_node_files, schema_files, warnings, loading_errors, regex_paths, schema_paths
127
+ )
128
+
129
+ errors_found = len(loading_errors) - errors_before
130
+ warnings_found = len(warnings) - warnings_before
131
+
132
+ if errors_found == 0 and warnings_found == 0:
133
+ logger.info("[配置自检] 检查通过,未发现问题")
134
+ else:
135
+ logger.warning("[配置自检] 发现 %d 个问题", errors_found)
136
+ for err in loading_errors[errors_before:]:
137
+ logger.warning("[配置自检] [%s] %s", err.error_type, err.message)
@@ -0,0 +1,224 @@
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
+ - 从 Schema 文件中收集内嵌的约束定义
21
+ - 将 Schema 文件中的 inline 约束转换为独立的 ConstraintFile
22
+
23
+ 架构设计:
24
+ - 扫描所有 schema 的 constraints 字段
25
+ - 转换约束引用: column name -> column id (规范化)
26
+
27
+ 输入示例:
28
+ schema_files = {
29
+ "users": TableSchemaFile(
30
+ id="users",
31
+ name="用户表",
32
+ columns=[
33
+ Column(id="col_1", name="id", type="integer"),
34
+ Column(id="col_2", name="username", type="string"),
35
+ Column(id="col_3", name="email", type="string")
36
+ ],
37
+ constraints=[
38
+ ConstraintItem(
39
+ id="not_null_username",
40
+ type="NotNull",
41
+ column="username", # 列名 (需要转换为 id)
42
+ enabled=True
43
+ ),
44
+ ConstraintItem(
45
+ id="fk_orders",
46
+ type="ForeignKey",
47
+ from_column="user_id",
48
+ to_table="users",
49
+ to_column="id",
50
+ enabled=True
51
+ )
52
+ ]
53
+ )
54
+ }
55
+
56
+ 输出示例:
57
+ constraint_files = {
58
+ "users_not_null_username": ConstraintFile(
59
+ version=2,
60
+ id="users_not_null_username",
61
+ type="NotNull",
62
+ enabled=True,
63
+ refs={"table_id": "users", "column_id": "col_2"},
64
+ params={}
65
+ ),
66
+ "users_fk_orders": ConstraintFile(
67
+ version=2,
68
+ id="users_fk_orders",
69
+ type="ForeignKey",
70
+ enabled=True,
71
+ refs={
72
+ "from_table_id": "users",
73
+ "from_column_id": "user_id", # 保持原名(未找到对应 column)
74
+ "to_table_id": "users",
75
+ "to_column_id": "id"
76
+ },
77
+ params={}
78
+ )
79
+ }
80
+ """
81
+
82
+ from __future__ import annotations
83
+
84
+ from typing import Any, cast
85
+
86
+ from app.shared.core.project.constraint.registry import normalize_constraint_type
87
+ from app.shared.core.project.constraint.types import ConstraintFile
88
+ from app.shared.core.project.schema.types import TableSchemaFile
89
+ from app.shared.core.project.schema.types_parts.column_utils import iter_all_columns
90
+
91
+
92
+ def collect_constraints_from_schemas(
93
+ schema_files: dict[str, TableSchemaFile],
94
+ ) -> dict[str, ConstraintFile]:
95
+ """@methoddesc 从 Schema 文件中收集内嵌约束
96
+
97
+ 遍历所有 Schema,提取其中的 constraints 字段,
98
+ 转换为独立的 ConstraintFile 对象。
99
+
100
+ 核心转换逻辑:
101
+ 1. 约束 ID: "{table_id}_{constraint_id}" 格式,确保全局唯一
102
+ 2. 列引用: column name -> column id (通过列名查找对应 id)
103
+ 3. 外键特殊处理: from_column/to_column 也需要转换
104
+
105
+ 输入示例:
106
+ schema_files = {
107
+ "users": TableSchemaFile(
108
+ id="users",
109
+ constraints=[
110
+ ConstraintItem(
111
+ id="not_null_email",
112
+ type="NotNull",
113
+ column="email", # 列名
114
+ enabled=True,
115
+ params={}
116
+ )
117
+ ]
118
+ )
119
+ }
120
+
121
+ 输出示例:
122
+ constraint_files = {
123
+ "users_not_null_email": ConstraintFile(
124
+ version=2,
125
+ id="users_not_null_email",
126
+ type="NotNull",
127
+ enabled=True,
128
+ refs={"table_id": "users", "column_id": "col_3"}, # email 的 column id
129
+ params={}
130
+ )
131
+ }
132
+
133
+ 原理说明:
134
+ - normalize_constraint_type() 确保约束类型名称标准化 (如 "not null" -> "NotNull")
135
+ - 列名到列 ID 的转换: 遍历 columns 列表,找到 name 匹配的列,取其 id
136
+ - 如果找不到对应的列,则保持原名(向后兼容)
137
+ - ConstraintFile.version 固定为 2 (当前版本)
138
+ """
139
+ # 用于存储转换后的约束文件对象,键为约束全局唯一 ID
140
+ constraint_files: dict[str, ConstraintFile] = {}
141
+ # 遍历所有已加载的 Schema 文件
142
+ for schema in schema_files.values():
143
+ # 如果当前 Schema 没有定义任何内嵌约束,则直接跳过
144
+ if not schema.constraints:
145
+ continue
146
+
147
+ # 遍历该 Schema 中定义的每一条内嵌约束
148
+ for constraint_item in schema.constraints:
149
+ # 将约束类型名称标准化(如 "not null" 转换为 "NotNull")
150
+ constraint_type = normalize_constraint_type(constraint_item.type)
151
+
152
+ # 初始化 refs 字典,至少包含当前表 ID
153
+ refs: dict[str, Any] = {"table_id": schema.id}
154
+ # 如果约束指定了单列名称,则查找对应的列 ID
155
+ if constraint_item.column:
156
+ # 递归遍历列(含嵌套 children)搜索 name 匹配的列,返回其 id
157
+ column_id = next(
158
+ (c.id for c in iter_all_columns(schema.columns) if c.name == constraint_item.column),
159
+ constraint_item.column,
160
+ )
161
+ refs["column_id"] = column_id
162
+ # 如果约束指定了多列名称,则逐个查找对应的列 ID
163
+ elif constraint_item.columns:
164
+ column_ids = []
165
+ for col_name in constraint_item.columns:
166
+ col_id = next((c.id for c in iter_all_columns(schema.columns) if c.name == col_name), col_name)
167
+ column_ids.append(col_id)
168
+ refs["column_ids"] = column_ids
169
+
170
+ # 外键约束需要特殊处理:涉及源列和目标列的映射
171
+ if constraint_type == "ForeignKey":
172
+ # 查找源列名称对应的列 ID(若找不到则保留原名称)
173
+ from_col_id = (
174
+ next(
175
+ (c.id for c in iter_all_columns(schema.columns) if c.name == constraint_item.from_column),
176
+ constraint_item.from_column,
177
+ )
178
+ if constraint_item.from_column
179
+ else None
180
+ )
181
+
182
+ # 对目标列也执行列名 → 列 ID 的转换(在目标 schema 中查找)
183
+ to_col_id = constraint_item.to_column
184
+ if constraint_item.to_table and constraint_item.to_column:
185
+ to_schema = schema_files.get(constraint_item.to_table)
186
+ if to_schema:
187
+ to_col_id = next(
188
+ (c.id for c in iter_all_columns(to_schema.columns) if c.name == constraint_item.to_column),
189
+ constraint_item.to_column,
190
+ )
191
+
192
+ # 外键的 refs 结构包含源表/列和目标表/列
193
+ refs = {
194
+ "from_table_id": schema.id,
195
+ "from_column_id": from_col_id,
196
+ "to_table_id": constraint_item.to_table,
197
+ "to_column_id": to_col_id,
198
+ }
199
+
200
+ # Conditional 约束:将 params 中的字段提取到 refs(前端 embedded 约束无 refs 字段)
201
+ # 提取后从 params 中移除,避免下游同时看到 refs 和 params 中的重复字段
202
+ if constraint_type == "Conditional":
203
+ params = dict(constraint_item.params or {})
204
+ refs["if_logic"] = params.pop("if_logic", "and")
205
+ refs["if_conditions"] = params.pop("if_conditions", []) or []
206
+ refs["then_column_id"] = params.pop("then_column_id", None)
207
+ params.pop("table_id", None)
208
+ else:
209
+ params = constraint_item.params or {}
210
+
211
+ # 构建 ConstraintFile 对象,将内嵌约束转换为独立约束文件格式
212
+ cf = ConstraintFile.model_construct(
213
+ version=2,
214
+ id=f"{schema.id}_{constraint_item.id}", # 使用 "{table_id}_{constraint_id}" 确保全局唯一
215
+ type=cast(Any, constraint_type),
216
+ enabled=constraint_item.enabled,
217
+ refs=refs,
218
+ params=params,
219
+ )
220
+ # 以全局唯一 ID 为键存入字典
221
+ constraint_files[cf.id] = cf
222
+
223
+ # 返回所有收集到的约束文件对象
224
+ return constraint_files
@@ -0,0 +1,58 @@
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
+ - 为项目加载器提供 ManualData 文件的加载接口
21
+ - 其余配置文件(Schema/Constraint/Regex/Transform/Template)由各子模块的 reader 直接加载
22
+
23
+ 架构设计:
24
+ - load_manual_data_file: 加载 ManualData 配置文件(YAML 读取 + 模型校验)
25
+ - 其他类型由调用方直接 import 对应 reader 模块的 load_xxx 函数
26
+
27
+ 异常处理:
28
+ - FileNotFoundError: 文件不存在
29
+ - yaml.YAMLError: YAML 格式错误
30
+ - ValidationError: 数据验证失败
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ from pathlib import Path
36
+
37
+ from app.shared.core.project.manual_data.types import ManualDataFile
38
+
39
+
40
+ def load_manual_data_file(manual_data_path: Path) -> ManualDataFile:
41
+ """@methoddesc 加载 ManualData 配置文件
42
+
43
+ 输入示例:
44
+ manual_data_path = Path("/path/to/project/manual_data/ti1__md1.yaml")
45
+
46
+ 输出示例:
47
+ ManualDataFile(
48
+ id="ti1__md1",
49
+ column_name="age",
50
+ column_data_type="integer",
51
+ rows=[["18"], ["25"], ["65"]],
52
+ )
53
+ """
54
+ import yaml
55
+
56
+ with open(manual_data_path, encoding="utf-8") as f:
57
+ raw = yaml.safe_load(f) or {}
58
+ return ManualDataFile.model_validate(raw)
@@ -0,0 +1,85 @@
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 配置自检问题 ID 集中管理
18
+
19
+ 功能概述:
20
+ - 集中定义所有自检问题(LoadingError.id)的拼接规则
21
+ - 作为前后端约定的单一事实来源,避免 id 格式散落各处导致不一致
22
+ - id 的稳定性直接影响前端"忽略"列表(localStorage)的持久化有效性
23
+
24
+ 设计原则:
25
+ - 纯函数,无副作用
26
+ - 保持现有 id 格式不变(向后兼容已持久化的 ignore 列表)
27
+ - 每个工厂函数对应一种问题类型,命名见文知意
28
+
29
+ id 格式总览(前后端契约,请勿随意变更):
30
+ ID 不一致: id_mismatch_{resource_type}:{manifest_id}:{file_id}
31
+ 重复登记约束: id_mismatch_constraint:{manifest_id}:{file_id}
32
+ 引用表缺失: {error_prefix}:{constraint_id}:{table_id}
33
+ 引用列缺失: {error_prefix}:{constraint_id}:{table_id}:{column_id}
34
+ 正则表缺失: regex_table_missing:{regex_id}:{table_id}
35
+ 正则列缺失: regex_col_missing:{regex_id}:{table_id}:{column_id}
36
+ Schema ID 重复: schema_id_duplicate:{schema_id}
37
+ 数据源重复: schema_source_duplicate:{path}:{sheet}
38
+ """
39
+
40
+ from __future__ import annotations
41
+
42
+
43
+ def id_mismatch(resource_type: str, manifest_id: str, file_id: str) -> str:
44
+ """ID 不一致问题 id(schema/constraint/regex/transform 通用)。"""
45
+ return f"id_mismatch_{resource_type}:{manifest_id}:{file_id}"
46
+
47
+
48
+ def constraint_dup_ref(manifest_id: str, file_id: str) -> str:
49
+ """同一条 constraint 被重复登记问题 id。"""
50
+ return f"id_mismatch_constraint:{manifest_id}:{file_id}"
51
+
52
+
53
+ def ref_table_missing(error_prefix: str, constraint_id: str, table_id: str) -> str:
54
+ """约束引用的表缺失问题 id。
55
+
56
+ Args:
57
+ error_prefix: 检查前缀(如 fk_src_table_missing / ref_table_missing),
58
+ 同时作为 fix_api.body.field 使用
59
+ """
60
+ return f"{error_prefix}:{constraint_id}:{table_id}"
61
+
62
+
63
+ def ref_column_missing(error_prefix: str, constraint_id: str, table_id: str, column_id: str) -> str:
64
+ """约束引用的列缺失问题 id。"""
65
+ return f"{error_prefix}:{constraint_id}:{table_id}:{column_id}"
66
+
67
+
68
+ def regex_table_missing(regex_id: str, table_id: str) -> str:
69
+ """正则节点引用的表缺失问题 id。"""
70
+ return f"regex_table_missing:{regex_id}:{table_id}"
71
+
72
+
73
+ def regex_column_missing(regex_id: str, table_id: str, column_id: str) -> str:
74
+ """正则节点引用的列缺失问题 id。"""
75
+ return f"regex_col_missing:{regex_id}:{table_id}:{column_id}"
76
+
77
+
78
+ def schema_id_duplicate(schema_id: str) -> str:
79
+ """Schema ID 全局重复问题 id。"""
80
+ return f"schema_id_duplicate:{schema_id}"
81
+
82
+
83
+ def schema_source_duplicate(path: str, sheet: str | None) -> str:
84
+ """数据源重复问题 id。"""
85
+ return f"schema_source_duplicate:{path}:{sheet}"