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,359 @@
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 旧脚本解析工具
17
+
18
+ Agent 可调用的工具:解析 Python pandas / 自然语言 / Excel 公式等旧检查逻辑,
19
+ 输出结构化的规则意图(RuleIntent)。
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import logging
25
+ import re
26
+ from typing import Any
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ class ScriptParseTool:
32
+ """
33
+ @classdesc 旧脚本解析工具
34
+
35
+ 解析用户已有的数据检查逻辑,转换为规则意图。
36
+ """
37
+
38
+ NAME = "parse_script"
39
+
40
+ def __init__(self, service: Any):
41
+ """
42
+ @methoddesc 初始化工具
43
+
44
+ 参数:
45
+ service: ConfigGenerationService 实例(用于调用 LLM)
46
+ """
47
+ self.service = service
48
+
49
+ def get_definition(self) -> dict[str, Any]:
50
+ """返回 OpenAI tool 定义。"""
51
+ return {
52
+ "type": "function",
53
+ "function": {
54
+ "name": self.NAME,
55
+ "description": "解析旧脚本或自然语言描述,输出结构化的规则意图。",
56
+ "parameters": {
57
+ "type": "object",
58
+ "properties": {
59
+ "script_content": {
60
+ "type": "string",
61
+ "description": "脚本内容或自然语言描述",
62
+ },
63
+ "language": {
64
+ "type": "string",
65
+ "enum": ["python", "natural_language", "excel_formula", "sql"],
66
+ "description": "脚本类型",
67
+ },
68
+ "context": {
69
+ "type": "string",
70
+ "description": "可选上下文,如目标表名、列名",
71
+ },
72
+ },
73
+ "required": ["script_content", "language"],
74
+ },
75
+ },
76
+ }
77
+
78
+ def run(self, arguments: dict[str, Any]) -> dict[str, Any]:
79
+ """
80
+ @methoddesc 执行脚本解析
81
+
82
+ 参数:
83
+ arguments: tool 参数
84
+
85
+ 返回:
86
+ {"success": bool, "intents": [...], "error": str}
87
+ """
88
+ script_content = arguments.get("script_content", "")
89
+ language = arguments.get("language", "natural_language")
90
+ context = arguments.get("context", "")
91
+
92
+ if not script_content.strip():
93
+ return {"success": False, "error": "脚本内容为空"}
94
+
95
+ try:
96
+ if language == "python":
97
+ intents = self._parse_python(script_content, context)
98
+ elif language == "excel_formula":
99
+ intents = self._parse_excel(script_content, context)
100
+ elif language == "sql":
101
+ intents = self._parse_sql(script_content, context)
102
+ else:
103
+ intents = self._parse_natural_language(script_content, context)
104
+
105
+ return {"success": True, "intents": intents}
106
+ except Exception as e:
107
+ logger.exception("脚本解析失败")
108
+ return {"success": False, "error": f"脚本解析失败: {e}"}
109
+
110
+ def _parse_python(self, content: str, context: str) -> list[dict[str, Any]]:
111
+ """解析 Python pandas 代码。"""
112
+ intents: list[dict[str, Any]] = []
113
+
114
+ # 模式:df[df.col < 0]
115
+ range_patterns = [
116
+ (r"df\[(?:df\[['\"](\w+)['\"\]]|df\.(\w+))\s*[<>]=?\s*([^\]]+)\]", "Range"),
117
+ (r"(?:df\[['\"](\w+)['\"\]]|df\.(\w+))\s*[<>]=?\s*([^\n]+)", "Range"),
118
+ ]
119
+ for pattern, ctype in range_patterns:
120
+ for match in re.finditer(pattern, content):
121
+ col = match.group(1) or match.group(2)
122
+ val = match.group(3).strip()
123
+ op = match.group(0)[match.start(3) - match.start(0) : match.start(3) - match.start(0) + 1]
124
+ intent = {
125
+ "type": ctype,
126
+ "column": col,
127
+ "confidence": 0.8,
128
+ "description": f"Python 代码解析: {col} {op} {val}",
129
+ }
130
+ if "<" in op:
131
+ intent["max"] = self._try_parse_number(val)
132
+ if ">" in op:
133
+ intent["min"] = self._try_parse_number(val)
134
+ intents.append(intent)
135
+
136
+ # 模式:df.col.str.match(pattern) 或 df['col'].str.match(pattern)
137
+ regex_patterns = [
138
+ r"df\.\(\w+\)\.str\.(?:match|contains)\(r?['\"]([^'\"]+)['\"]\)",
139
+ r"df\[(['\"])(\w+)\1\]\.str\.(?:match|contains)\(r?['\"]([^'\"]+)['\"]\)",
140
+ r"df\.(\w+)\.str\.(?:match|contains)\(r?['\"]([^'\"]+)['\"]\)",
141
+ r"re\.match\(r?['\"]([^'\"]+)['\"]",
142
+ ]
143
+ for pattern in regex_patterns:
144
+ for match in re.finditer(pattern, content):
145
+ groups = match.groups()
146
+ if len(groups) >= 2 and groups[0] in ("'", '"'):
147
+ # df['col'].str... pattern: groups=(quote, col, pattern)
148
+ col = groups[1]
149
+ pattern_str = groups[2] if len(groups) >= 3 else groups[1]
150
+ elif len(groups) >= 2:
151
+ # df.col.str... pattern: groups=(col, pattern)
152
+ col = groups[0]
153
+ pattern_str = groups[1]
154
+ else:
155
+ col = ""
156
+ pattern_str = groups[0]
157
+ intents.append(
158
+ {
159
+ "type": "Regex",
160
+ "column": col,
161
+ "pattern": pattern_str,
162
+ "confidence": 0.75,
163
+ "description": f"Python 正则解析: {pattern_str}",
164
+ }
165
+ )
166
+
167
+ # 模式:df.col.is_unique
168
+ if re.search(r"\.(\w+)\.is_unique", content):
169
+ for match in re.finditer(r"\.(\w+)\.is_unique", content):
170
+ intents.append(
171
+ {
172
+ "type": "Unique",
173
+ "column": match.group(1),
174
+ "confidence": 0.9,
175
+ "description": f"Python 唯一性检查: {match.group(1)}",
176
+ }
177
+ )
178
+
179
+ # 模式:df.col.isna().sum()
180
+ if re.search(r"\.(\w+)\.isna\(\)", content):
181
+ for match in re.finditer(r"\.(\w+)\.isna\(\)", content):
182
+ intents.append(
183
+ {
184
+ "type": "NotNull",
185
+ "column": match.group(1),
186
+ "confidence": 0.7,
187
+ "description": f"Python 空值检查: {match.group(1)}",
188
+ }
189
+ )
190
+
191
+ # 模式:df.col.isin([...])
192
+ for match in re.finditer(r"\.(\w+)\.isin\(\[(.*?)\]\)", content, re.DOTALL):
193
+ col = match.group(1)
194
+ values_str = match.group(2)
195
+ values = [v.strip().strip("\"'") for v in re.split(r",\s*", values_str) if v.strip()]
196
+ intents.append(
197
+ {
198
+ "type": "AllowedValues",
199
+ "column": col,
200
+ "allowed_values": values,
201
+ "confidence": 0.85,
202
+ "description": f"Python 枚举值检查: {col}",
203
+ }
204
+ )
205
+
206
+ return intents
207
+
208
+ def _parse_excel(self, content: str, context: str) -> list[dict[str, Any]]:
209
+ """解析 Excel 公式/数据验证。"""
210
+ intents: list[dict[str, Any]] = []
211
+
212
+ # 数据验证:列表
213
+ for match in re.finditer(r"allow\s*=\s*['\"]list['\"]", content, re.IGNORECASE):
214
+ intents.append(
215
+ {
216
+ "type": "AllowedValues",
217
+ "confidence": 0.7,
218
+ "description": "Excel 列表验证",
219
+ }
220
+ )
221
+
222
+ # 公式:=AND(A1>0, A1<100)
223
+ range_match = re.search(r"([A-Z]+\d*)\s*[<>]=?\s*(-?\d+(?:\.\d+)?)", content)
224
+ if range_match:
225
+ intents.append(
226
+ {
227
+ "type": "Range",
228
+ "column": range_match.group(1),
229
+ "confidence": 0.7,
230
+ "description": f"Excel 范围验证: {range_match.group(0)}",
231
+ }
232
+ )
233
+
234
+ return intents
235
+
236
+ def _parse_sql(self, content: str, context: str) -> list[dict[str, Any]]:
237
+ """解析 SQL DDL 约束。"""
238
+ intents: list[dict[str, Any]] = []
239
+
240
+ # NOT NULL
241
+ for match in re.finditer(r"(\w+)\s+\S+\s+NOT\s+NULL", content, re.IGNORECASE):
242
+ intents.append(
243
+ {
244
+ "type": "NotNull",
245
+ "column": match.group(1),
246
+ "confidence": 0.9,
247
+ "description": f"SQL NOT NULL: {match.group(1)}",
248
+ }
249
+ )
250
+
251
+ # CHECK (age > 0)
252
+ for match in re.finditer(r"CHECK\s*\(\s*(\w+)\s*([<>]=?)\s*([^\)]+)\)", content, re.IGNORECASE):
253
+ col = match.group(1)
254
+ op = match.group(2)
255
+ val = match.group(3).strip()
256
+ intent = {
257
+ "type": "Range",
258
+ "column": col,
259
+ "confidence": 0.85,
260
+ "description": f"SQL CHECK: {col} {op} {val}",
261
+ }
262
+ if "<" in op:
263
+ intent["max"] = self._try_parse_number(val)
264
+ if ">" in op:
265
+ intent["min"] = self._try_parse_number(val)
266
+ intents.append(intent)
267
+
268
+ # UNIQUE
269
+ for match in re.finditer(r"UNIQUE\s*\(\s*([^\)]+)\s*\)", content, re.IGNORECASE):
270
+ cols = [c.strip() for c in match.group(1).split(",")]
271
+ intents.append(
272
+ {
273
+ "type": "Unique",
274
+ "column": cols[0],
275
+ "column_ids": cols,
276
+ "confidence": 0.9,
277
+ "description": f"SQL UNIQUE: {', '.join(cols)}",
278
+ }
279
+ )
280
+
281
+ return intents
282
+
283
+ def _parse_natural_language(self, content: str, context: str) -> list[dict[str, Any]]:
284
+ """解析自然语言描述(当前为关键词正则匹配,LLM 兜底待实现)。
285
+
286
+ 当前实现基于关键词正则匹配(非空/唯一/范围/枚举等常见表述),
287
+ 置信度固定为 0.6(低于结构化语言的 0.7~0.9)。
288
+ 未来可增强为:正则置信度低时调用 LLM 做语义解析,需将本方法异步化并接入 provider。
289
+ """
290
+ intents: list[dict[str, Any]] = []
291
+
292
+ # 非空
293
+ if re.search(r"非空|不能为空|必须填写", content):
294
+ col_match = re.search(r"([\w\u4e00-\u9fa5]+)\s*(?:列|字段|column)?\s*非空|不能为空", content)
295
+ intents.append(
296
+ {
297
+ "type": "NotNull",
298
+ "column": col_match.group(1) if col_match else "",
299
+ "confidence": 0.6,
300
+ "description": "自然语言:非空约束",
301
+ }
302
+ )
303
+
304
+ # 唯一
305
+ if re.search(r"唯一|不能重复|去重", content):
306
+ col_match = re.search(r"([\w\u4e00-\u9fa5]+)\s*(?:列|字段|column)?\s*唯一|不能重复", content)
307
+ intents.append(
308
+ {
309
+ "type": "Unique",
310
+ "column": col_match.group(1) if col_match else "",
311
+ "confidence": 0.6,
312
+ "description": "自然语言:唯一约束",
313
+ }
314
+ )
315
+
316
+ # 范围(如"年龄在 0-120 之间"、"金额大于 0")
317
+ range_match = re.search(
318
+ r"([\w\u4e00-\u9fa5]+)\s*(?:列|字段)?\s*(?:在|介于)?\s*(\d+(?:\.\d+)?)\s*[-~到至]\s*(\d+(?:\.\d+)?)",
319
+ content,
320
+ )
321
+ if range_match:
322
+ intents.append(
323
+ {
324
+ "type": "Range",
325
+ "column": range_match.group(1),
326
+ "min": float(range_match.group(2)),
327
+ "max": float(range_match.group(3)),
328
+ "confidence": 0.6,
329
+ "description": "自然语言:范围约束",
330
+ }
331
+ )
332
+
333
+ # 枚举(如"状态只能是 A、B、C")
334
+ enum_match = re.search(r"([\w\u4e00-\u9fa5]+)\s*(?:列|字段)?\s*(?:只能|必须)是\s*([^,。,\.]+)", content)
335
+ if enum_match and "、" in enum_match.group(2):
336
+ values = [v.strip() for v in enum_match.group(2).split("、") if v.strip()]
337
+ if len(values) >= 2:
338
+ intents.append(
339
+ {
340
+ "type": "AllowedValues",
341
+ "column": enum_match.group(1),
342
+ "values": values,
343
+ "confidence": 0.55,
344
+ "description": "自然语言:枚举约束",
345
+ }
346
+ )
347
+
348
+ return intents
349
+
350
+ @staticmethod
351
+ def _try_parse_number(text: str) -> int | float | str:
352
+ """尝试解析数字。"""
353
+ text = text.strip()
354
+ try:
355
+ if "." in text:
356
+ return float(text)
357
+ return int(text)
358
+ except ValueError:
359
+ return text
@@ -0,0 +1,138 @@
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 Agent 类型定义模块
17
+
18
+ 定义 Agent 执行过程中使用的核心数据类型。
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from collections.abc import Callable
24
+ from dataclasses import dataclass, field
25
+ from typing import Any
26
+
27
+
28
+ @dataclass
29
+ class ToolCall:
30
+ """单次工具调用请求"""
31
+
32
+ id: str
33
+ name: str
34
+ arguments: dict[str, Any]
35
+
36
+
37
+ @dataclass
38
+ class ToolResult:
39
+ """单次工具调用结果"""
40
+
41
+ call_id: str
42
+ name: str
43
+ success: bool
44
+ observation: str | dict[str, Any]
45
+ error: str | None = None
46
+
47
+
48
+ @dataclass
49
+ class AgentTurn:
50
+ """Agent 单轮执行记录"""
51
+
52
+ turn: int
53
+ content: str | None = None
54
+ tool_calls: list[ToolCall] = field(default_factory=list)
55
+ tool_results: list[ToolResult] = field(default_factory=list)
56
+ checkpoint: dict[str, Any] = field(default_factory=dict)
57
+
58
+
59
+ @dataclass
60
+ class AgentPlan:
61
+ """分块执行计划"""
62
+
63
+ chunks: list[dict[str, Any]] = field(default_factory=list)
64
+ reason: str = ""
65
+
66
+
67
+ @dataclass
68
+ class AgentMetrics:
69
+ """Agent 执行指标"""
70
+
71
+ total_rules: int = 0
72
+ passed_rules: int = 0
73
+ failed_rules: int = 0
74
+ removed_rules: int = 0
75
+ modified_rules: int = 0
76
+ issues: list[dict[str, Any]] = field(default_factory=list)
77
+
78
+ def to_dict(self) -> dict[str, Any]:
79
+ return {
80
+ "total_rules": self.total_rules,
81
+ "passed_rules": self.passed_rules,
82
+ "failed_rules": self.failed_rules,
83
+ "removed_rules": self.removed_rules,
84
+ "modified_rules": self.modified_rules,
85
+ "issues": self.issues,
86
+ }
87
+
88
+
89
+ @dataclass
90
+ class AgentResult:
91
+ """Agent 执行结果"""
92
+
93
+ success: bool
94
+ content: str | None = None
95
+ config: dict[str, Any] | None = None
96
+ turns: list[AgentTurn] = field(default_factory=list)
97
+ metrics: AgentMetrics = field(default_factory=AgentMetrics)
98
+ plan: AgentPlan | None = None
99
+ error: str | None = None
100
+ iterations: int = 0
101
+ # 取消标记:True 表示任务被软取消(区别于失败)。软取消时已落盘的 apply_actions 改动保留。
102
+ cancelled: bool = False
103
+
104
+ def to_dict(self) -> dict[str, Any]:
105
+ return {
106
+ "success": self.success,
107
+ "content": self.content,
108
+ "config": self.config,
109
+ "turns": [self._turn_to_dict(t) for t in self.turns],
110
+ "metrics": self.metrics.to_dict(),
111
+ "plan": {"chunks": self.plan.chunks, "reason": self.plan.reason} if self.plan else None,
112
+ "error": self.error,
113
+ "iterations": self.iterations,
114
+ "cancelled": self.cancelled,
115
+ }
116
+
117
+ @staticmethod
118
+ def _turn_to_dict(turn: AgentTurn) -> dict[str, Any]:
119
+ return {
120
+ "turn": turn.turn,
121
+ "content": turn.content,
122
+ "tool_calls": [{"id": c.id, "name": c.name, "arguments": c.arguments} for c in turn.tool_calls],
123
+ "tool_results": [
124
+ {
125
+ "call_id": r.call_id,
126
+ "name": r.name,
127
+ "success": r.success,
128
+ "observation": r.observation,
129
+ "error": r.error,
130
+ }
131
+ for r in turn.tool_results
132
+ ],
133
+ "checkpoint": turn.checkpoint,
134
+ }
135
+
136
+
137
+ # 工具函数签名:输入 arguments dict,返回 ToolResult
138
+ ToolFunction = Callable[[dict[str, Any]], Any]