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,261 @@
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
+ - 实现 ForeignKeyConstraints 类
21
+ - 验证外键列的值是否在目标表的对应列中存在
22
+ - 确保引用完整性: 子表的值必须在父表中存在
23
+
24
+ 架构设计:
25
+ - 继承基类: 继承自 Constraint 抽象类
26
+ - 引用验证: 检查 from_column 的值是否在 to_column 中存在
27
+ - 值规范化: 自动处理类型转换和空值
28
+
29
+ 输入示例:
30
+ # 约束配置: orders.user_id 必须存在于 users.id 中
31
+ constraint = ForeignKeyConstraints(
32
+ from_table="orders",
33
+ from_column="user_id",
34
+ to_table="users",
35
+ to_column="id"
36
+ )
37
+
38
+ # 数据集输入
39
+ datasets = {
40
+ "users": DataFrame({
41
+ "id": [1, 2, 3],
42
+ "username": ["alice", "bob", "charlie"]
43
+ }),
44
+ "orders": DataFrame({
45
+ "id": [1, 2, 3],
46
+ "user_id": [1, 2, 99] # 99 不在 users.id 中
47
+ })
48
+ }
49
+
50
+ 输出示例:
51
+ # 验证通过
52
+ {"errors": [], "info": {...}}
53
+
54
+ # 验证失败 (第3行 user_id=99 在 users 表中不存在)
55
+ {
56
+ "errors": [
57
+ {
58
+ "error_type": "ForeignKeyViolation",
59
+ "table": "orders",
60
+ "row_index": 2,
61
+ "column": "user_id",
62
+ "value": 99,
63
+ "message": "外键冲突: 值 '99' 在目标表 'users' 的列 'id' 中不存在。"
64
+ }
65
+ ],
66
+ "info": {
67
+ "constraint_type": "ForeignKeyConstraints",
68
+ "table": "orders",
69
+ "description": "外键约束: orders.user_id -> users.id"
70
+ }
71
+ }
72
+
73
+ 原理说明:
74
+ 值规范化处理:
75
+ - 字符串前后空格会被去除
76
+ - "123.0" 会被规范化为 "123"
77
+ - 空字符串、None、NaN 会被规范化为 None (不检查)
78
+ - 布尔值会被转换为 "True"/"False"
79
+ - 浮点数会去掉小数点后的 .0
80
+ """
81
+
82
+ from __future__ import annotations
83
+
84
+ # 1. 标准库导入
85
+ from typing import Any
86
+
87
+ # 2. 第三方库导入
88
+ import pandas as pd
89
+
90
+ # 3. 项目内部导入
91
+ from app.shared.domain.constraints.base import Constraint
92
+ from app.shared.domain.constraints.key_normalization import (
93
+ integral_number_to_str,
94
+ strip_trailing_decimal_zeros,
95
+ )
96
+
97
+
98
+ class ForeignKeyConstraints(Constraint):
99
+ """
100
+ @classdesc 外键约束
101
+
102
+ 验证数据列中的值是否在目标表的列中存在。
103
+ 通过值规范化处理,支持不同数据类型之间的匹配。
104
+ """
105
+
106
+ def __init__(self, from_table: str, from_column: str, to_table: str, to_column: str):
107
+ """
108
+ @methoddesc 初始化外键约束
109
+
110
+ 参数:
111
+ from_table: 子表名(包含外键列的表)
112
+ from_column: 子表中的外键列名
113
+ to_table: 父表名(被引用的表)
114
+ to_column: 父表中被引用的列名
115
+ """
116
+ self.from_table = from_table
117
+ self.from_column = from_column
118
+ self.to_table = to_table
119
+ self.to_column = to_column
120
+
121
+ def _get_description(self) -> str:
122
+ """生成外键约束描述,格式为 "外键约束: from_table.from_column -> to_table.to_column"""
123
+ return f"外键约束: {self.from_table}.{self.from_column} -> {self.to_table}.{self.to_column}"
124
+
125
+ def validate(self, datasets: dict[str, pd.DataFrame], **kwargs: Any) -> dict[str, Any]:
126
+ """
127
+ @methoddesc 执行外键验证
128
+
129
+ 参数:
130
+ datasets: 数据集字典,键为表名,值为 pandas DataFrame
131
+ **kwargs: 额外的关键字参数(本方法不使用)
132
+
133
+ 返回:
134
+ 验证结果字典,包含 errors(错误列表)和 info(约束信息)
135
+
136
+ 逻辑说明:
137
+ 1. 检查 from_table 和 to_table 是否都在数据集中
138
+ 2. 检查 from_column 和 to_column 是否存在于各自表中
139
+ 3. 对目标列(父表)的值进行规范化,生成有效键集合
140
+ 4. 对源列(子表)的值进行规范化,检查是否都在有效键集合中
141
+ 5. 为不存在的键生成错误记录
142
+ """
143
+ errors: list[dict[str, Any]] = []
144
+
145
+ def _normalize_fk_value(value: Any) -> Any:
146
+ """
147
+ 规范化外键值,统一不同数据类型的表示形式
148
+
149
+ 规则:
150
+ - None、NaN、空字符串 -> None(跳过检查)
151
+ - 字符串 -> 去除前后空格;"123.0" 转为 "123"
152
+ - 布尔值 -> "True" 或 "False"
153
+ - 整数 -> 字符串形式
154
+ - 浮点数 -> 去掉末尾的 .0 后转字符串
155
+ - 其他类型 -> 转字符串并去除空格
156
+
157
+ 参数:
158
+ value: 原始值
159
+
160
+ 返回:
161
+ 规范化后的值,或 None(表示应跳过检查)
162
+ """
163
+ # 空值统一返回 None,空值不视为外键违规
164
+ if value is None or (isinstance(value, float) and pd.isna(value)) or pd.isna(value):
165
+ return None
166
+
167
+ # 字符串: 去除前后空格,十进制形式去尾随零(与 float/Decimal 侧对称)
168
+ if isinstance(value, str):
169
+ s = value.strip()
170
+ if s == "":
171
+ return None
172
+ # 回归 D4: 支持负号;§1.10: "123.10"→"123.1" 与 float 侧 str(123.10)="123.1" 同构,
173
+ # 消除父 float 123.10 vs 子 str "123.10" 的双侧不对称误报
174
+ return strip_trailing_decimal_zeros(s)
175
+
176
+ # 布尔值转为字符串 "True" 或 "False"
177
+ if isinstance(value, bool):
178
+ return str(value)
179
+
180
+ # 整数转为字符串
181
+ if isinstance(value, int):
182
+ return str(value)
183
+
184
+ # 浮点数: 如果是整数形式(如 123.0),去掉 .0 后转字符串
185
+ if isinstance(value, float):
186
+ integral = integral_number_to_str(value)
187
+ if integral is not None:
188
+ return integral
189
+ return strip_trailing_decimal_zeros(str(value))
190
+
191
+ # 回归 D4: Decimal 是项目一等类型(decimal data_type),应与 float 一致归一。
192
+ # §1.10: 非整 Decimal("123.10") 也走去尾随零,与字符串/float 侧对称。
193
+ try:
194
+ from decimal import Decimal
195
+
196
+ if isinstance(value, Decimal):
197
+ integral = integral_number_to_str(value)
198
+ if integral is not None:
199
+ return integral
200
+ return strip_trailing_decimal_zeros(str(value).strip())
201
+ except (TypeError, ValueError):
202
+ pass
203
+
204
+ # 其他类型: 转字符串并去除空格
205
+ s = str(value).strip()
206
+ return s if s != "" else None
207
+
208
+ # 检查两张表是否都在数据集中
209
+ if self.from_table not in datasets or self.to_table not in datasets:
210
+ errors.append(
211
+ {
212
+ "error_type": "ConstraintConfigError",
213
+ "table": self.from_table,
214
+ "column": self.from_column,
215
+ "message": f"外键约束失败: 表 '{self.from_table}' 或 '{self.to_table}' 不在提供的数据集中。",
216
+ }
217
+ )
218
+ return {"errors": errors, "info": self.get_constraint_info()}
219
+
220
+ from_df = datasets[self.from_table]
221
+ to_df = datasets[self.to_table]
222
+
223
+ # 检查两列是否存在于各自表中
224
+ if self.from_column not in from_df.columns or self.to_column not in to_df.columns:
225
+ errors.append(
226
+ {
227
+ "error_type": "ConstraintConfigError",
228
+ "table": self.from_table,
229
+ "column": self.from_column,
230
+ "message": f"外键约束失败: 列 '{self.from_column}' 或 '{self.to_column}' 在其对应的表中不存在。",
231
+ }
232
+ )
233
+ return {"errors": errors, "info": self.get_constraint_info()}
234
+
235
+ # 对目标列(父表)的值进行规范化,并去除空值,生成有效键集合
236
+ normalized_target_series = to_df[self.to_column].map(_normalize_fk_value).dropna()
237
+ valid_keys = set(normalized_target_series)
238
+
239
+ # 对源列(子表)的值进行规范化
240
+ normalized_keys = from_df[self.from_column].map(_normalize_fk_value)
241
+ # 找出非空且不在有效键集合中的值
242
+ invalid_mask = normalized_keys.notna() & (~normalized_keys.isin(valid_keys))
243
+ invalid_rows = from_df[invalid_mask]
244
+
245
+ # 为每个无效的外键值生成错误记录
246
+ for row_tuple in invalid_rows[[self.from_column]].itertuples(index=True, name=None):
247
+ index = row_tuple[0]
248
+ key_to_check = row_tuple[1]
249
+ row_index = int(index) if index is not None else 0
250
+ errors.append(
251
+ {
252
+ "error_type": "ForeignKeyViolation",
253
+ "table": self.from_table,
254
+ "row_index": row_index,
255
+ "column": self.from_column,
256
+ "value": key_to_check,
257
+ "message": f"外键冲突: 值 '{key_to_check}' 在目标表 '{self.to_table}' 的列 '{self.to_column}' 中不存在。",
258
+ }
259
+ )
260
+
261
+ return {"errors": errors, "info": self.get_constraint_info()}
@@ -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
+ """@fileoverview 约束键值数值归一化(domain 纯逻辑,FK 与 AllowedValues 共用)
17
+
18
+ 口径(2026-09-18 拍板 §1.10/§1.11):
19
+ - 尾随零双侧对称去除:123.10 ≡ 123.1(字符串/float/Decimal 同一语义)
20
+ - Decimal 整数值规整为整数字符串:Decimal("1.0") ≡ 1
21
+ - 去尾随零用字符串操作而非 float 中转,避免 17 位以上键值丢精度
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import re
27
+ from decimal import Decimal
28
+
29
+ # 十进制形式(含小数点)的字符串;科学计数/非数值不匹配,原样返回
30
+ _DECIMAL_STR_RE = re.compile(r"(-?\d+)\.(\d+)")
31
+
32
+
33
+ def strip_trailing_decimal_zeros(s: str) -> str:
34
+ """十进制字符串去尾随零("123.10"→"123.1"、"123.00"→"123"),非十进制形式原样返回。
35
+
36
+ 不做 strip/大小写处理——调用方自行决定前后空白语义。
37
+ """
38
+ m = _DECIMAL_STR_RE.fullmatch(s)
39
+ if not m:
40
+ return s
41
+ frac = m.group(2).rstrip("0")
42
+ if not frac:
43
+ return m.group(1)
44
+ return f"{m.group(1)}.{frac}"
45
+
46
+
47
+ def integral_number_to_str(v: float | Decimal) -> str | None:
48
+ """可整化的 float/Decimal 规整为整数字符串;不可整化返回 None。
49
+
50
+ 判据与 FK 归一(foreign_key.py)一致:v == v.to_integral_value() / float.is_integer()。
51
+ """
52
+ if isinstance(v, Decimal):
53
+ return str(int(v)) if v == v.to_integral_value() else None
54
+ if isinstance(v, float):
55
+ return str(int(v)) if v.is_integer() else None
56
+ return None
@@ -0,0 +1,185 @@
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
+ - 实现 NotNullConstraint 类
21
+ - 验证指定列的值是否为空
22
+
23
+ 架构设计:
24
+ - 继承基类: 继承自 Constraint 抽象类
25
+ - 简单验证: 使用 pandas isnull() 检测空值
26
+
27
+ 输入示例:
28
+ # 约束配置
29
+ constraint = NotNullConstraint(table="users", column="username")
30
+
31
+ # 数据集输入
32
+ datasets = {
33
+ "users": DataFrame({
34
+ "id": [1, 2, 3],
35
+ "username": ["alice", "bob", None], # 第3行为空
36
+ "email": ["a@test.com", "b@test.com", "c@test.com"]
37
+ })
38
+ }
39
+
40
+ 输出示例:
41
+ # 验证通过
42
+ {
43
+ "errors": [],
44
+ "info": {
45
+ "constraint_type": "NotNullConstraint",
46
+ "table": "users",
47
+ "description": "非空约束: users.username"
48
+ }
49
+ }
50
+
51
+ # 验证失败 (第3行 username 为空)
52
+ {
53
+ "errors": [
54
+ {
55
+ "error_type": "NotNullViolation",
56
+ "table": "users",
57
+ "row_index": 2,
58
+ "column": "username",
59
+ "value": None,
60
+ "message": "非空约束冲突: 列 'username' 的值不能为空。"
61
+ }
62
+ ],
63
+ "info": {...}
64
+ }
65
+
66
+ # 配置错误 (列不存在)
67
+ {
68
+ "errors": [
69
+ {
70
+ "error_type": "ConstraintConfigError",
71
+ "table": "users",
72
+ "column": "nonexistent_column",
73
+ "message": "非空约束失败: 列 'nonexistent_column' 在表 'users' 中不存在。"
74
+ }
75
+ ],
76
+ "info": {...}
77
+ }
78
+ """
79
+
80
+ from __future__ import annotations
81
+
82
+ # 1. 标准库导入
83
+ from typing import Any
84
+
85
+ # 2. 第三方库导入
86
+ import pandas as pd
87
+
88
+ # 3. 项目内部导入
89
+ from app.shared.domain.constraints.base import Constraint
90
+
91
+
92
+ class NotNullConstraint(Constraint):
93
+ """
94
+ @classdesc 非空约束
95
+
96
+ 验证指定列中的每个值都不为空。
97
+ 空值包括: None、NaN、以及仅包含空白字符的字符串。
98
+ """
99
+
100
+ def __init__(self, table: str, column: str):
101
+ """
102
+ @methoddesc 初始化非空约束
103
+
104
+ 参数:
105
+ table: 目标表名
106
+ column: 目标列名
107
+ """
108
+ self.table = table
109
+ self.column = column
110
+
111
+ def _get_description(self) -> str:
112
+ """生成非空约束描述"""
113
+ return f"非空约束: {self.table}.{self.column}"
114
+
115
+ def validate(self, datasets: dict[str, pd.DataFrame], **kwargs: Any) -> dict[str, Any]:
116
+ """
117
+ @methoddesc 执行非空验证
118
+
119
+ 参数:
120
+ datasets: 数据集字典,键为表名,值为 pandas DataFrame
121
+ **kwargs: 额外的关键字参数(本方法不使用)
122
+
123
+ 返回:
124
+ 验证结果字典,包含 errors(错误列表)和 info(约束信息)
125
+
126
+ 逻辑说明:
127
+ 1. 检查目标表是否存在于数据集中
128
+ 2. 检查目标列是否存在于目标表中
129
+ 3. 使用 pandas isnull() 检测空值
130
+ 4. 对于字符串类型的列,额外检测仅包含空白字符的值
131
+ 5. 为每个空值行生成错误记录
132
+ """
133
+ errors: list[dict[str, Any]] = []
134
+
135
+ # 检查目标表是否存在
136
+ if self.table not in datasets:
137
+ errors.append(
138
+ {
139
+ "error_type": "ConstraintConfigError",
140
+ "table": self.table,
141
+ "column": self.column,
142
+ "message": f"非空约束失败: 表 '{self.table}' 不在数据集中。",
143
+ }
144
+ )
145
+ return {"errors": errors, "info": self.get_constraint_info()}
146
+
147
+ df = datasets[self.table]
148
+
149
+ # 检查目标列是否存在
150
+ if self.column not in df.columns:
151
+ errors.append(
152
+ {
153
+ "error_type": "ConstraintConfigError",
154
+ "table": self.table,
155
+ "column": self.column,
156
+ "message": f"非空约束失败: 列 '{self.column}' 在表 '{self.table}' 中不存在。",
157
+ }
158
+ )
159
+ return {"errors": errors, "info": self.get_constraint_info()}
160
+
161
+ col = df[self.column]
162
+
163
+ # 使用 pandas isnull() 检测 None、NaN 等空值
164
+ is_null = col.isnull()
165
+
166
+ # 对于 object 类型(通常是字符串),额外检测仅包含空白字符的值
167
+ # 如 " "、"\t\n" 等也应视为空值
168
+ if col.dtype == object:
169
+ is_null = is_null | (col.astype(str).str.strip() == "")
170
+
171
+ # 遍历所有空值行,生成错误记录
172
+ for index in df[is_null].index:
173
+ row_index = int(index) if index is not None else 0
174
+ errors.append(
175
+ {
176
+ "error_type": "NotNullViolation",
177
+ "table": self.table,
178
+ "row_index": row_index,
179
+ "column": self.column,
180
+ "value": None,
181
+ "message": f"非空约束冲突: 列 '{self.column}' 的值不能为空。",
182
+ }
183
+ )
184
+
185
+ return {"errors": errors, "info": self.get_constraint_info()}