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,123 @@
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
+ - 写入 *.regex.yaml 文件
21
+ - 优化字段顺序,提升 YAML 可读性
22
+
23
+ 架构设计:
24
+ - 唯一入口: 封装 Pydantic 模型到 YAML 文件的转换逻辑
25
+ - 字段排序: 按重要程度分组(核心 / 可选 / 内部)
26
+ - 一致性: 与 schema/writer.py 保持一致的排序策略
27
+
28
+ 输入示例:
29
+ node = RegexNodeFile(
30
+ version=2,
31
+ id="email",
32
+ name="邮箱校验",
33
+ pattern="^[\\w\\.-]+@[\\w\\.-]+\\.\\w+$",
34
+ match_mode="full",
35
+ enabled=True,
36
+ )
37
+
38
+ 输出示例:
39
+ save_regex_node(node, "regex/email.regex.yaml")
40
+ """
41
+
42
+ from __future__ import annotations
43
+
44
+ from pathlib import Path
45
+ from typing import TYPE_CHECKING
46
+
47
+ from app.shared.core.io.yaml import write_yaml_atomic
48
+
49
+ from .types import RegexNodeFile
50
+
51
+ if TYPE_CHECKING:
52
+ from .types import RegexNodeFile
53
+
54
+
55
+ def save_regex_node(regex_node: RegexNodeFile, regex_path: str | Path) -> None:
56
+ """
57
+ @methoddesc 保存正则节点到 YAML 文件。
58
+
59
+ 字段顺序优化(按重要程度分组):
60
+ L1(核心):name, description, pattern/uses_pattern
61
+ L2(可选):enabled, match_mode
62
+ L3(内部):version, id, _internal
63
+
64
+ 处理流程:
65
+ 1. 将 RegexNodeFile 对象序列化为字典(model_dump)
66
+ 2. 按重要程度重新组织字段顺序(其余字段追加在末尾,确保不丢失)
67
+ 3. 写入 YAML 文件
68
+
69
+ 设计说明:
70
+ 历史版本曾用手动白名单挑字段,导致新增字段(如 source_ref、input_from_node、
71
+ rules 等)被静默丢弃。现改为「全量保留 + 顺序优化」:先 dump 全部字段,
72
+ 再按指定顺序重排,未在顺序表中的字段自动追加到末尾,从根本上避免丢字段。
73
+
74
+ :param regex_node: RegexNodeFile 对象,包含正则节点的完整配置
75
+ :param regex_path: 保存路径,可以是相对路径或绝对路径
76
+ :raises IOError: 文件写入失败时抛出
77
+ """
78
+ # 步骤1:将 Pydantic 模型序列化为字典(包含全部字段)
79
+ data = regex_node.model_dump()
80
+
81
+ # 步骤2:按重要程度重排字段顺序
82
+ # 核心字段在前,其余字段追加其后,保证不丢失任何字段
83
+ ordered_keys = [
84
+ # L1 - 核心字段(用户最常编辑)
85
+ "name",
86
+ "description",
87
+ "pattern",
88
+ "uses_pattern",
89
+ "pattern_overrides",
90
+ # L2 - 可选配置(功能开关和模式)
91
+ "enabled",
92
+ "match_mode",
93
+ "case_sensitive",
94
+ "flags",
95
+ # 数据流与上游绑定(重建画布连线必需)
96
+ "source_ref",
97
+ "source_column_name",
98
+ "input_from_node",
99
+ "input_column",
100
+ # Extract 模式专用
101
+ "capture_groups",
102
+ "output_columns",
103
+ # 前端扩展结构
104
+ "parameters",
105
+ "rules",
106
+ # L3 - 内部字段(开发者/系统使用)
107
+ "version",
108
+ "id",
109
+ "_internal",
110
+ ]
111
+
112
+ ordered: dict[str, object] = {}
113
+ # 先按指定顺序写入已知字段
114
+ for key in ordered_keys:
115
+ if key in data:
116
+ ordered[key] = data[key]
117
+ # 再追加未在顺序表中的字段(未来新增字段自动保留,防止再次丢字段)
118
+ for key, value in data.items():
119
+ if key not in ordered:
120
+ ordered[key] = value
121
+
122
+ # 步骤3:调用底层 YAML 写入工具
123
+ write_yaml_atomic(Path(regex_path), ordered)
@@ -0,0 +1,170 @@
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.yaml 文件读取表结构配置
21
+ - 返回 core 层的 TableSchemaFile 配置对象
22
+ - 运行时 TableSchema 对象的构建已迁移至 services.schema_runtime_builder,
23
+ 以避免 core 层反向依赖 domain 层。
24
+
25
+ 架构设计:
26
+ - 单一职责: 仅负责 Schema 文件读取和基础配置解析
27
+ - 依赖注入: 使用 YAML 读取工具进行文件解析
28
+ - 类型安全: 使用 Pydantic 模型验证确保配置有效
29
+
30
+ 输入示例:
31
+ # users.schema.yaml
32
+ version: 2
33
+ id: users
34
+ name: users
35
+ source:
36
+ mode: relative_file
37
+ path: data/users.xlsx
38
+ columns:
39
+ - id: user_id
40
+ name: user_id
41
+ type: string
42
+
43
+ 输出示例:
44
+ schema_file = load_schema("schemas/users.schema.yaml")
45
+ # schema_file 是 TableSchemaFile 对象,可交由 services 层转换为运行时对象
46
+ """
47
+
48
+ from __future__ import annotations
49
+
50
+ from pathlib import Path
51
+
52
+ from pydantic import ValidationError
53
+
54
+ from app.shared.core.io.yaml import read_yaml
55
+
56
+ from .types import TableSchemaFile
57
+
58
+
59
+ def load_schema(schema_path: str | Path) -> TableSchemaFile:
60
+ """
61
+ @methoddesc 从 YAML 文件加载表结构配置。
62
+
63
+ ============================================================================
64
+ 配置文件示例 (本函数处理的配置文件长这样)
65
+ ============================================================================
66
+ 本函数处理以下格式的 *.schema.yaml 文件:
67
+
68
+ ```yaml
69
+ # ============================================================
70
+ # 表结构配置文件 (*.schema.yaml)
71
+ # ============================================================
72
+
73
+ version: 2
74
+
75
+ id: users
76
+
77
+ name: users
78
+
79
+ source:
80
+ mode: relative_file
81
+ path: data/users.xlsx
82
+ sheet: Sheet1
83
+ header_row: 0
84
+
85
+ columns:
86
+ - id: user_id
87
+ name: user_id
88
+ type: string
89
+ primary_key: true
90
+ expand: false
91
+ - id: email
92
+ name: email
93
+ type: string
94
+ primary_key: false
95
+
96
+ constraints:
97
+ - id: email_notnull
98
+ type: NotNull
99
+ column: email
100
+ enabled: true
101
+
102
+ script_checks: []
103
+ ```
104
+
105
+ ============================================================================
106
+ 业务场景 (什么情况下会调用这个函数)
107
+ ============================================================================
108
+ - 场景1: 加载表结构进行数据校验
109
+ 系统需要读取表结构配置来获取列定义和数据源信息。
110
+
111
+ - 场景2: 构建运行时数据模型
112
+ 需要将配置文件转换为运行时对象供校验引擎使用。
113
+ 运行时转换请使用 app.shared.services.schema_runtime_builder。
114
+
115
+ ============================================================================
116
+ 数据流 (输入如何变成输出)
117
+ ============================================================================
118
+ 输入参数:
119
+ - schema_path: str | Path,schema 文件路径
120
+ 示例值: "schemas/users.schema.yaml"
121
+
122
+ 处理步骤:
123
+ 路径标准化 -> YAML 解析 -> Pydantic 验证 -> TableSchemaFile 对象
124
+
125
+ 最终输出: TableSchemaFile 对象
126
+ 示例:
127
+ schema = load_schema("schemas/users.schema.yaml")
128
+ print(schema.name) # 输出: users
129
+ print(len(schema.columns)) # 输出: 2
130
+
131
+ ============================================================================
132
+ 异常处理
133
+ ============================================================================
134
+ | 异常类型 | 触发条件 | 处理方式 |
135
+ |---------|---------|---------|
136
+ | ValueError | 文件不存在 | 检查文件路径 |
137
+ | ValueError | YAML 格式错误 | 验证 YAML 文件 |
138
+ | ValueError | 验证失败 | 检查配置格式 |
139
+
140
+ :param schema_path: schema 文件路径
141
+ :return: TableSchemaFile 对象
142
+ :raises ValueError: 文件不存在或格式错误
143
+ """
144
+ path = Path(schema_path)
145
+ raw = read_yaml(path)
146
+
147
+ try:
148
+ return TableSchemaFile.model_validate(raw)
149
+ except ValidationError as e:
150
+ raise ValueError(f"schema 校验失败: {path}\n{e}") from e
151
+
152
+
153
+ def schema_column_name_by_id(schema: TableSchemaFile) -> dict[str, str]:
154
+ """
155
+ @methoddesc 构建列 ID 到列名的映射。
156
+
157
+ :param schema: TableSchemaFile 对象
158
+ :return: Dict[str, str],列 ID -> 列名 映射
159
+ """
160
+ return {c.id: c.name for c in schema.columns if c.id is not None}
161
+
162
+
163
+ def schema_column_id_by_name(schema: TableSchemaFile) -> dict[str, str]:
164
+ """
165
+ @methoddesc 构建列名到列 ID 的映射。
166
+
167
+ :param schema: TableSchemaFile 对象
168
+ :return: Dict[str, str],列名 -> 列 ID 映射
169
+ """
170
+ return {c.name: c.id for c in schema.columns if c.id is not None}
@@ -0,0 +1,47 @@
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 Schema 类型统一导出模块
17
+
18
+ 功能概述:
19
+ - 聚合导出 Schema 相关核心类型(列定义、表结构、数据源等)
20
+ - 提供 V2 版本别名及 source 标准化工具函数
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from app.shared.core.project.schema.types_parts.column import ColumnSpec, ExtractedSpec
26
+ from app.shared.core.project.schema.types_parts.constraint import ConstraintItem
27
+ from app.shared.core.project.schema.types_parts.schema_id import normalize_source_key
28
+ from app.shared.core.project.schema.types_parts.source import SourceSpec
29
+ from app.shared.core.project.schema.types_parts.table import TableSchemaFile
30
+
31
+ SourceSpecV2 = SourceSpec
32
+ ColumnSpecV2 = ColumnSpec
33
+ TableSchemaFileV2 = TableSchemaFile
34
+ ConstraintItemV2 = ConstraintItem
35
+
36
+ __all__ = [
37
+ "SourceSpec",
38
+ "ExtractedSpec",
39
+ "ColumnSpec",
40
+ "ConstraintItem",
41
+ "TableSchemaFile",
42
+ "SourceSpecV2",
43
+ "ColumnSpecV2",
44
+ "TableSchemaFileV2",
45
+ "ConstraintItemV2",
46
+ "normalize_source_key",
47
+ ]
@@ -0,0 +1,36 @@
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 Schema 子类型聚合导出模块
17
+
18
+ 功能概述:
19
+ - 集中导出 Schema 各组成部分的类型定义与工具函数
20
+ - 提供列定义、表结构、数据源、约束项及 source 标准化工具
21
+ """
22
+
23
+ from app.shared.core.project.schema.types_parts.column import ColumnSpec, ExtractedSpec
24
+ from app.shared.core.project.schema.types_parts.constraint import ConstraintItem
25
+ from app.shared.core.project.schema.types_parts.schema_id import normalize_source_key
26
+ from app.shared.core.project.schema.types_parts.source import SourceSpec
27
+ from app.shared.core.project.schema.types_parts.table import TableSchemaFile
28
+
29
+ __all__ = [
30
+ "ColumnSpec",
31
+ "ExtractedSpec",
32
+ "ConstraintItem",
33
+ "SourceSpec",
34
+ "TableSchemaFile",
35
+ "normalize_source_key",
36
+ ]
@@ -0,0 +1,174 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview 列类型定义模块
18
+
19
+ 功能概述:
20
+ - 定义 Schema 中的列结构 (ColumnSpec)
21
+ - 支持提取列类型 (ExtractedSpec) 用于正则提取
22
+
23
+ 架构设计:
24
+ - 列 ID 自动生成: 如果未指定 id,默认使用 name
25
+ - 类型灵活: type 字段可以是字符串或字典
26
+
27
+ 输入示例 (schema.yaml):
28
+ columns:
29
+ - id: col_1
30
+ name: id
31
+ type: integer
32
+ primary_key: true
33
+ - id: col_2
34
+ name: email
35
+ type: string
36
+ - id: col_3
37
+ name: username
38
+ type:
39
+ name: Extracted
40
+ source_column: email
41
+ extract_key: username
42
+ result_type: string
43
+
44
+ 输出示例:
45
+ ColumnSpec(
46
+ id="col_1",
47
+ name="id",
48
+ type="integer",
49
+ primary_key=True,
50
+ expand=False
51
+ )
52
+ """
53
+
54
+ from __future__ import annotations
55
+
56
+ from typing import Any, Literal
57
+
58
+ from pydantic import BaseModel, Field, model_validator
59
+
60
+
61
+ class ExtractedSpec(BaseModel):
62
+ """@classdesc 提取列规范
63
+
64
+ 用于定义从源列中提取数据的规则。
65
+ 常用于正则提取创建派生列。
66
+
67
+ 字段说明:
68
+ - name: 固定为 "Extracted"
69
+ - source_column: 源列名
70
+ - extract_key: 正则表达式中的命名捕获组
71
+ - result_type: 提取结果的数据类型
72
+
73
+ 示例:
74
+ # 从 email 列中提取用户名
75
+ name: Extracted
76
+ source_column: email
77
+ extract_key: username
78
+ result_type: string
79
+ """
80
+
81
+ name: Literal["Extracted"] = "Extracted"
82
+ source_column: str = Field(..., description="源列名(从中提取数据)")
83
+ extract_key: str = Field(..., description="提取键(正则命名捕获组)")
84
+ result_type: str | None = Field(None, description="提取结果的数据类型")
85
+
86
+
87
+ class ColumnSpec(BaseModel):
88
+ """
89
+ @classdesc 列规范
90
+
91
+ 定义表中的一列。
92
+
93
+ 字段说明:
94
+ - id: 列的唯一标识符 (可选,默认使用 name)
95
+ - name: 列名 (用于显示和校验)
96
+ - type: 数据类型
97
+ - 简单类型: "integer", "string", "boolean" 等
98
+ - 复杂类型: ExtractedSpec 字典
99
+ - primary_key: 是否为主键
100
+ - expand: 是否展开 (用于数组/对象类型)
101
+
102
+ 输入示例 (schema.yaml):
103
+ columns:
104
+ - id: col_1
105
+ name: id
106
+ type: integer
107
+ primary_key: true
108
+ - id: col_2
109
+ name: email
110
+ type: string
111
+ - id: col_3
112
+ name: username
113
+ type:
114
+ name: Extracted
115
+ source_column: email
116
+ extract_key: username
117
+ result_type: string
118
+
119
+ 输出示例:
120
+ ColumnSpec(
121
+ id="col_1",
122
+ name="id",
123
+ type="integer",
124
+ primary_key=True,
125
+ expand=False
126
+ )
127
+ """
128
+
129
+ id: str | None = Field(None, description="列 ID(稳定标识,缺失时自动使用 name)")
130
+ name: str = Field(..., description="列名(展示/校验使用)")
131
+ type: Any = Field(..., description="数据类型配置:支持字符串或字典(与现有 TYPE_REGISTRY 兼容)")
132
+ primary_key: bool = Field(False, description="是否主键")
133
+ nullable: bool = Field(True, description="是否允许为空")
134
+ expand: bool = Field(False, description="是否展开(与现有 schema 模型保持一致)")
135
+ json_path: str | None = Field(None, description="JSON 特有路径映射")
136
+ children: list[ColumnSpec] | None = Field(None, description="JSON 嵌套子列(树形结构)")
137
+
138
+ @model_validator(mode="before")
139
+ @classmethod
140
+ def _generate_id_from_name(cls, data: Any) -> Any:
141
+ """@methoddesc 当未提供列 ID 时,自动使用列名作为 ID
142
+
143
+ 处理逻辑:
144
+ 1. 检查输入数据是否为字典类型(Pydantic 验证器接收原始数据)
145
+ 2. 如果是字典,检查是否同时满足以下两个条件:
146
+ - 包含 "name" 字段(有列名可用)
147
+ - 不包含 "id" 字段(用户未手动指定 ID)
148
+ 3. 满足条件时,将 name 的值复制给 id,实现 ID 自动补全
149
+ 4. 返回处理后的数据(无论是否修改),供后续验证流程继续处理
150
+
151
+ 为什么需要这个验证器?
152
+ - 简化配置:用户只需写 name,不必重复写 id
153
+ - 向后兼容:旧配置中 id 和 name 相同的场景无需修改
154
+ - 一致性保证:确保每列都有稳定的 ID 用于内部引用
155
+
156
+ 示例:
157
+ 输入: {"name": "email", "type": "string"}
158
+ 处理: 自动补充 id -> {"name": "email", "type": "string", "id": "email"}
159
+ 输出: {"name": "email", "type": "string", "id": "email"}
160
+
161
+ 输入: {"id": "col_1", "name": "email", "type": "string"}
162
+ 处理: 已有 id,不修改 -> {"id": "col_1", "name": "email", "type": "string"}
163
+ 输出: {"id": "col_1", "name": "email", "type": "string"}
164
+ """
165
+ # 只有字典类型的原始数据才需要处理
166
+ # Pydantic 验证器可能接收到 dict、已实例化的模型对象或其他类型
167
+ if isinstance(data, dict):
168
+ # 条件判断:有 name 但没有 id 时才自动补全
169
+ # 使用 "in" 操作符检查键是否存在(即使值为 None 也算存在)
170
+ if "name" in data and "id" not in data:
171
+ # 将 name 的值赋给 id,实现自动 ID 生成
172
+ data["id"] = data["name"]
173
+ # 返回处理后的数据对象,供 Pydantic 继续后续验证流程
174
+ return data
@@ -0,0 +1,72 @@
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
+ 提供递归处理 ColumnSpec(含嵌套 children)的工具函数。
20
+ 平面 schema(无 children)行为等价于原顶层遍历,向后兼容。
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from collections.abc import Iterator
26
+
27
+ from app.shared.core.project.schema.types_parts.column import ColumnSpec
28
+
29
+
30
+ def iter_all_columns(columns: list[ColumnSpec] | None) -> Iterator[ColumnSpec]:
31
+ """递归遍历列(含嵌套 children),深度优先遍历父节点本身。
32
+
33
+ 对无 children 的平面列,等价于直接遍历顶层(行为不变)。
34
+
35
+ :param columns: 顶层列列表,可为 None
36
+ :yield: 每个列(父 + 所有子孙),深度优先
37
+ """
38
+ for col in columns or []:
39
+ yield col
40
+ if col.children:
41
+ yield from iter_all_columns(col.children)
42
+
43
+
44
+ def build_column_id_to_name_map(columns: list[ColumnSpec] | None) -> dict[str, str]:
45
+ """递归构建 column_id -> column_name 映射。
46
+
47
+ 嵌套子列用「父.子」全限定名(与 json_normalize 展平后的点分列一致),
48
+ 使约束能直接 df['profile.name'] 取值。
49
+ 平面列(无父)映射为自身名。
50
+
51
+ :param columns: 顶层列列表
52
+ :return: {column_id: 全限定列名},跳过 id 为 None 的列
53
+ """
54
+ result: dict[str, str] = {}
55
+ _collect_qualified_names(columns or [], "", result)
56
+ return result
57
+
58
+
59
+ def _collect_qualified_names(columns: list[ColumnSpec], parent_path: str, result: dict[str, str]) -> None:
60
+ """递归收集 column_id -> 全限定列名。
61
+
62
+ :param columns: 当前层级的列列表
63
+ :param parent_path: 父级全限定路径(空串表示顶层)
64
+ :param result: 累积结果的字典
65
+ """
66
+ prefix = f"{parent_path}." if parent_path else ""
67
+ for col in columns:
68
+ qualified_name = f"{prefix}{col.name}"
69
+ if col.id is not None:
70
+ result[col.id] = qualified_name
71
+ if col.children:
72
+ _collect_qualified_names(col.children, qualified_name, result)