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,222 @@
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 校验报告导出模块(P2-3 --report)
17
+
18
+ 功能概述:
19
+ - 把 validate 的 JSON 契约 payload 导出为人类可分享的报告文件
20
+ - HTML:自包含单文件(内联 CSS,无外部资源),违规明细表
21
+ - Excel:openpyxl(backend 现有依赖),每张表一个 sheet,违规行标红
22
+
23
+ 设计说明:
24
+ - 输入直接复用 `_build_json_payload` 的契约结构,保证
25
+ "stdout JSON / 报告文件" 两产物内容一致(验收口径)
26
+ - 纯函数 + 显式路径参数,无隐藏 I/O;HTML 值经 html.escape 防注入
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import html
32
+ from pathlib import Path
33
+ from typing import Any
34
+
35
+ # 违规明细表的列头(HTML 与 Excel 共用口径)
36
+ _REPORT_COLUMNS = ("表", "列", "行号", "值", "约束类型", "约束文件", "错误消息")
37
+
38
+
39
+ def _error_row(entry: dict[str, Any]) -> list[str]:
40
+ """把契约错误条目转为报告行(None 统一展示为空串)。"""
41
+ message = entry.get("error_message") or ""
42
+ suggestion = entry.get("suggestion")
43
+ if suggestion:
44
+ # 修复建议拼进消息单元格,避免两种导出格式各加一列
45
+ message = f"{message}(建议:{suggestion})"
46
+ return [
47
+ entry.get("table") or "",
48
+ entry.get("column") or "",
49
+ "" if entry.get("row_index") is None else str(entry["row_index"]),
50
+ "" if entry.get("cell_value") is None else str(entry["cell_value"]),
51
+ entry.get("constraint_type") or "",
52
+ entry.get("constraint_file") or "",
53
+ message,
54
+ ]
55
+
56
+
57
+ def _summary_line(payload: dict[str, Any]) -> str:
58
+ """一句话概览:结论 + 检查数 + 耗时。"""
59
+ summary = payload.get("summary", {})
60
+ verdict = "通过" if payload.get("is_valid") else "发现违规"
61
+ return (
62
+ f"校验{verdict}:约束检查 {summary.get('constraints_total', 0)} 项,"
63
+ f"通过 {summary.get('constraints_passed', 0)} / 失败 {summary.get('constraints_failed', 0)},"
64
+ f"耗时 {payload.get('duration_ms', 0)} ms"
65
+ )
66
+
67
+
68
+ def export_html_report(payload: dict[str, Any], output_path: str | Path) -> Path:
69
+ """导出自包含 HTML 报告。
70
+
71
+ Args:
72
+ payload: validate JSON 契约 payload(_build_json_payload 输出)
73
+ output_path: 报告文件路径(.html)
74
+
75
+ Returns:
76
+ 写入的文件路径
77
+
78
+ Raises:
79
+ OSError: 写文件失败
80
+ """
81
+ rows = payload.get("errors", [])
82
+ table_headers = "".join(f"<th>{html.escape(col)}</th>" for col in _REPORT_COLUMNS)
83
+ table_rows: list[str] = []
84
+ for index, entry in enumerate(rows, 1):
85
+ cells = "".join(f"<td>{html.escape(cell)}</td>" for cell in _error_row(entry))
86
+ row_class = "fail" if not payload.get("is_valid") else ""
87
+ table_rows.append(f'<tr class="{row_class}"><td class="idx">{index}</td>{cells}</tr>')
88
+ table_html = "".join(table_rows) if table_rows else '<tr><td colspan="8" class="empty">无违规记录</td></tr>'
89
+
90
+ tables_info = (
91
+ "、".join(
92
+ f"{t.get('name') or '?'}({t.get('rows') if t.get('rows') is not None else '?'} 行)"
93
+ for t in payload.get("tables", [])
94
+ )
95
+ or "无"
96
+ )
97
+
98
+ document = f"""<!DOCTYPE html>
99
+ <html lang="zh-CN">
100
+ <head>
101
+ <meta charset="utf-8">
102
+ <title>Precis 校验报告</title>
103
+ <style>
104
+ body {{ font-family: "Segoe UI", "Microsoft YaHei", sans-serif; margin: 2rem; color: #1f2937; }}
105
+ h1 {{ font-size: 1.4rem; }}
106
+ .meta {{ color: #6b7280; margin-bottom: 1rem; }}
107
+ .verdict {{ display: inline-block; padding: 0.2rem 0.8rem; border-radius: 999px; font-weight: 600; }}
108
+ .verdict.pass {{ background: #d1fae5; color: #065f46; }}
109
+ .verdict.fail {{ background: #fee2e2; color: #991b1b; }}
110
+ table {{ border-collapse: collapse; width: 100%; font-size: 0.9rem; }}
111
+ th, td {{ border: 1px solid #d1d5db; padding: 0.4rem 0.6rem; text-align: left; vertical-align: top; }}
112
+ th {{ background: #f3f4f6; }}
113
+ tr.fail td {{ background: #fef2f2; }}
114
+ td.idx {{ color: #9ca3af; width: 2.5rem; }}
115
+ td.empty {{ text-align: center; color: #6b7280; padding: 2rem; }}
116
+ </style>
117
+ </head>
118
+ <body>
119
+ <h1>Precis 数据校验报告</h1>
120
+ <p class="meta">
121
+ <span class="verdict {"pass" if payload.get("is_valid") else "fail"}">{"通过" if payload.get("is_valid") else "发现违规"}</span>
122
+ &nbsp;{_summary_line(payload)}
123
+ <br>数据表:{html.escape(tables_info)}
124
+ </p>
125
+ <table>
126
+ <thead><tr><th>#</th>{table_headers}</tr></thead>
127
+ <tbody>{table_html}</tbody>
128
+ </table>
129
+ </body>
130
+ </html>
131
+ """
132
+ path = Path(output_path)
133
+ path.parent.mkdir(parents=True, exist_ok=True)
134
+ path.write_text(document, encoding="utf-8")
135
+ return path
136
+
137
+
138
+ def export_excel_report(payload: dict[str, Any], output_path: str | Path) -> Path:
139
+ """导出 Excel 报告(每张表一个 sheet,违规行标红)。
140
+
141
+ Sheet 组织:每张数据表一个 sheet(列出该表违规明细);无表归属的错误
142
+ 进入"汇总" sheet;另有"概览" sheet 记录结论与统计。
143
+
144
+ Args:
145
+ payload: validate JSON 契约 payload(_build_json_payload 输出)
146
+ output_path: 报告文件路径(.xlsx)
147
+
148
+ Returns:
149
+ 写入的文件路径
150
+
151
+ Raises:
152
+ OSError: 写文件失败
153
+ """
154
+ from openpyxl import Workbook
155
+ from openpyxl.styles import Alignment, Font, PatternFill
156
+
157
+ red_fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
158
+ header_font = Font(bold=True)
159
+
160
+ workbook = Workbook()
161
+ # 概览 sheet
162
+ overview = workbook.active
163
+ overview.title = "概览"
164
+ overview.append(["结论", "通过" if payload.get("is_valid") else "发现违规"])
165
+ for key, label in (
166
+ ("constraints_total", "约束检查总数"),
167
+ ("constraints_passed", "通过数"),
168
+ ("constraints_failed", "失败数"),
169
+ ):
170
+ overview.append([label, payload.get("summary", {}).get(key, 0)])
171
+ overview.append(["耗时(ms)", payload.get("duration_ms", 0)])
172
+ overview.append(["违规条数", len(payload.get("errors", []))])
173
+ overview.append(["数据表", ", ".join(str(t.get("name")) for t in payload.get("tables", []))])
174
+ for cell in overview[1]:
175
+ cell.font = header_font
176
+
177
+ # 按表分组违规条目(table 为空/None 的归入"汇总" sheet)
178
+ errors_by_table: dict[str, list[dict[str, Any]]] = {}
179
+ for entry in payload.get("errors", []):
180
+ key = entry.get("table") or "汇总"
181
+ errors_by_table.setdefault(key, []).append(entry)
182
+
183
+ sheet_names = {t.get("name") for t in payload.get("tables", []) if t.get("name")}
184
+ for name in sorted(sheet_names | set(errors_by_table.keys())):
185
+ # Excel sheet 名长度上限 31 字符,超长截断
186
+ sheet = workbook.create_sheet(title=name[:31])
187
+ sheet.append(list(_REPORT_COLUMNS))
188
+ for cell in sheet[1]:
189
+ cell.font = header_font
190
+ cell.alignment = Alignment(horizontal="center")
191
+ for entry in errors_by_table.get(name, []):
192
+ sheet.append(_error_row(entry))
193
+ for cell in sheet[sheet.max_row]:
194
+ cell.fill = red_fill
195
+ if not errors_by_table.get(name):
196
+ sheet.append(["(该表无违规)"])
197
+
198
+ path = Path(output_path)
199
+ path.parent.mkdir(parents=True, exist_ok=True)
200
+ workbook.save(path)
201
+ return path
202
+
203
+
204
+ def export_report(payload: dict[str, Any], output_path: str | Path) -> Path:
205
+ """按扩展名分派报告导出(.html/.htm → HTML;.xlsx → Excel)。
206
+
207
+ Args:
208
+ payload: validate JSON 契约 payload
209
+ output_path: 报告文件路径
210
+
211
+ Returns:
212
+ 写入的文件路径
213
+
214
+ Raises:
215
+ ValueError: 扩展名不支持
216
+ """
217
+ suffix = Path(output_path).suffix.lower()
218
+ if suffix in (".html", ".htm"):
219
+ return export_html_report(payload, output_path)
220
+ if suffix == ".xlsx":
221
+ return export_excel_report(payload, output_path)
222
+ raise ValueError(f"不支持的报告格式: {suffix}(支持 .html / .xlsx)")
@@ -0,0 +1,180 @@
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
+ - 解析 manifest 中配置的第一个数据源目录(绝对/相对路径)
21
+ - 根据 Schema source 定义解析文件路径
22
+ - 自动发现匹配表名的数据文件(按扩展名优先级搜索)
23
+ - 支持 Excel 多 sheet 场景的路径解析
24
+
25
+ 架构设计:
26
+ - 单一职责模式: 只做路径解析,不涉及数据加载或校验
27
+ - 从 ValidationExecutor 中提取,独立为可复用组件
28
+
29
+ 输入示例:
30
+ resolver = DataSourceResolver(project_root, manifest, schema_by_id)
31
+ data_dir = resolver.resolve_first_data_source()
32
+ file_path, sheet = resolver.resolve_source_path(data_dir, schema_file)
33
+
34
+ 输出示例:
35
+ data_dir = "/path/to/data"
36
+ file_path = "/path/to/data/users.xlsx"
37
+ sheet = "Sheet1"
38
+ """
39
+
40
+ import logging
41
+ import os
42
+
43
+ from app.shared.core.project.manifest.types import ProjectManifestV2
44
+ from app.shared.core.project.schema.types import TableSchemaFile
45
+ from app.shared.core.utils.path_utils import normalize_to_posix
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+
50
+ class DataSourceResolver:
51
+ """
52
+ @classdesc 数据源路径解析器
53
+
54
+ 负责:
55
+ - 解析 manifest 中配置的第一个数据源目录
56
+ - 根据 Schema source 定义解析文件路径(绝对/相对)
57
+ - 自动发现匹配表名的数据文件(按扩展名优先级搜索)
58
+
59
+ 设计决策:
60
+ - 从 ValidationExecutor 中提取,单一职责:只做路径解析,不涉及数据加载或校验
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ project_root: str,
66
+ manifest: ProjectManifestV2,
67
+ schema_by_id: dict[str, TableSchemaFile],
68
+ ):
69
+ self.project_root = project_root
70
+ self.manifest = manifest
71
+ self._schema_by_id = schema_by_id
72
+
73
+ def resolve_first_data_source(self) -> str | None:
74
+ """
75
+ @methoddesc 解析 manifest 中配置的第一个数据源目录
76
+
77
+ 返回第一个数据源对应的绝对路径。支持绝对路径和相对路径两种模式。
78
+
79
+ 返回:
80
+ 数据源目录的绝对路径,未配置或不存在则返回 None
81
+ """
82
+ if not self.manifest.data_sources:
83
+ return None
84
+
85
+ ds = self.manifest.data_sources[0]
86
+
87
+ if ds.mode == "absolute":
88
+ # 绝对路径模式:直接使用配置的路径
89
+ if os.path.isdir(ds.path):
90
+ return os.path.normpath(ds.path)
91
+ else:
92
+ logger.warning(f"[DataSourceResolver] 数据源目录不存在(绝对路径): {ds.path}")
93
+ return None
94
+ else:
95
+ # 相对路径模式:基于项目根目录拼接
96
+ relative_path = os.path.join(self.project_root, ds.path)
97
+ if os.path.isdir(relative_path):
98
+ return os.path.normpath(relative_path)
99
+ else:
100
+ logger.warning(f"[DataSourceResolver] 数据源目录不存在(相对路径): {relative_path}")
101
+ return None
102
+
103
+ def resolve_source_path(self, data_directory: str, schema_file: TableSchemaFile) -> tuple[str | None, str | None]:
104
+ """
105
+ @methoddesc 根据 Schema 定义解析数据源文件路径
106
+
107
+ 按照以下优先级解析文件路径:
108
+ 1. Schema 中显式配置的 source 路径(绝对文件或相对文件)
109
+ 2. 自动发现:在数据目录中按表名搜索匹配的数据文件
110
+
111
+ 参数:
112
+ data_directory: 数据文件搜索目录
113
+ schema_file: 表 Schema 定义文件
114
+
115
+ 返回:
116
+ (文件路径, Sheet 名称) 元组,未找到则返回 (None, None)
117
+ """
118
+ # 提取 Sheet 名称
119
+ sheet_name = schema_file.sheet
120
+ if not sheet_name and schema_file.source:
121
+ sheet_name = schema_file.source.sheet
122
+
123
+ # 优先级 1:Schema 显式配置了 source 路径
124
+ if schema_file.source:
125
+ src = schema_file.source
126
+ if src.mode == "absolute_file":
127
+ return src.path, sheet_name
128
+
129
+ src_path = src.path
130
+ src_path_abs = os.path.normpath(os.path.join(self.project_root, src_path))
131
+
132
+ if os.path.isfile(src_path_abs):
133
+ return src_path_abs, sheet_name
134
+
135
+ # 处理路径中多余的项目目录前缀(如 data/users.xlsx 中的 data 部分)
136
+ data_dir_basename = os.path.basename(self.project_root)
137
+ src_path_parts = normalize_to_posix(src_path).split("/")
138
+ if len(src_path_parts) > 1 and src_path_parts[0] == data_dir_basename:
139
+ src_path_adjusted = "/".join(src_path_parts[1:])
140
+ adjusted_path = os.path.normpath(os.path.join(self.project_root, src_path_adjusted))
141
+ if os.path.isfile(adjusted_path):
142
+ return adjusted_path, sheet_name
143
+
144
+ final_path = os.path.normpath(os.path.join(self.project_root, src_path))
145
+ return final_path, sheet_name
146
+
147
+ # 优先级 2:自动发现数据文件
148
+ table_name = schema_file.name or schema_file.id
149
+
150
+ # 如果指定了 Sheet 名称,说明是 Excel 文件,只搜索 Excel 扩展名
151
+ if sheet_name:
152
+ extensions = [".xlsx", ".xls"]
153
+ else:
154
+ extensions = [".xlsx", ".xls", ".csv", ".json", ".jsonl"]
155
+
156
+ # 确定搜索目录,优先使用 manifest 中配置的数据源目录
157
+ search_directory = data_directory
158
+ first_data_source = self.resolve_first_data_source()
159
+ if first_data_source:
160
+ search_directory = first_data_source
161
+
162
+ # 先在搜索目录根目录中查找
163
+ for ext in extensions:
164
+ file_path = os.path.join(search_directory, f"{table_name}{ext}")
165
+ if os.path.isfile(file_path):
166
+ return file_path, sheet_name
167
+
168
+ # 递归搜索子目录,跳过常见非数据目录
169
+ for root, dirs, files in os.walk(search_directory):
170
+ dirs[:] = [
171
+ d for d in dirs if not d.startswith(".") and d not in ["node_modules", "__pycache__", ".git", ".venv"]
172
+ ]
173
+
174
+ for ext in extensions:
175
+ file_name = f"{table_name}{ext}"
176
+ if file_name in files:
177
+ file_path = os.path.join(root, file_name)
178
+ return file_path, sheet_name
179
+
180
+ return None, None