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,264 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview 文件内容预览路由模块
18
+
19
+ 功能概述:
20
+ - 提供基于文件路径和文件上传的数据预览接口
21
+ - 支持 Excel(.xlsx/.xls)和 CSV 文件的内容解析与预览
22
+ - 支持工作表切换预览
23
+ - 使用临时文件存储上传内容,处理完成后自动清理
24
+ - 限制返回的最大行数和列数,防止内存溢出
25
+
26
+ 架构设计:
27
+ - 基于 FastAPI APIRouter 组织路由
28
+ - 具体预览逻辑委托给 preview_service.py
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import logging
34
+ import os
35
+
36
+ from fastapi import File, Form, HTTPException, UploadFile
37
+
38
+ from app.api.routers.preview.models import FilePreviewRequest, FilePreviewResponse, SheetSwitchRequest
39
+ from app.api.services.preview_service import (
40
+ preview_from_content,
41
+ preview_from_path,
42
+ )
43
+ from app.shared.services.preview.path_validation import validate_file_access
44
+
45
+ from .router import router
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+
50
+ @router.post(
51
+ "/file",
52
+ response_model=FilePreviewResponse,
53
+ summary="基于文件路径预览文件内容",
54
+ responses={
55
+ 500: {"description": "预览文件时发生错误"},
56
+ },
57
+ )
58
+ def preview_file(request: FilePreviewRequest) -> FilePreviewResponse:
59
+ """基于文件路径预览文件内容
60
+
61
+ 根据请求中的文件路径读取 Excel 或 CSV 文件,
62
+ 返回指定行数和列数的预览数据。
63
+ """
64
+ logger.info("=" * 50)
65
+ logger.info(f"[PREVIEW] 收到文件预览请求: {request.file_path}")
66
+ logger.info(f"[PREVIEW] max_rows: {request.max_rows}, max_cols: {request.max_cols}")
67
+ logger.info("=" * 50)
68
+
69
+ # B-sec: 路径校验(与姊妹端点 /file/path、/switch-sheet 对齐)。
70
+ # 此前该端点连 `..`/相对路径都不拦截,是约束最弱的文件读取口。
71
+ request.file_path = validate_file_access(request.file_path)
72
+
73
+ try:
74
+ data, file_type, total_rows, sheets, current_sheet = preview_from_path(
75
+ request.file_path,
76
+ request.max_rows,
77
+ request.max_cols,
78
+ )
79
+ return FilePreviewResponse(
80
+ success=True,
81
+ data=data,
82
+ file_type=file_type,
83
+ file_name=os.path.basename(request.file_path),
84
+ total_rows=total_rows,
85
+ total_cols=len(data[0]) if data else 0,
86
+ sheets=sheets,
87
+ current_sheet=current_sheet,
88
+ )
89
+ except HTTPException:
90
+ raise
91
+ except Exception as e:
92
+ logger.error(f"[PREVIEW] 未知错误: {e}")
93
+ return FilePreviewResponse(
94
+ success=False,
95
+ data=None,
96
+ file_type="unknown",
97
+ file_name="",
98
+ error=f"预览文件时发生错误: {e}",
99
+ )
100
+
101
+
102
+ @router.post(
103
+ "/file/content",
104
+ response_model=FilePreviewResponse,
105
+ summary="基于文件上传预览文件内容",
106
+ responses={
107
+ 500: {"description": "预览文件时发生错误"},
108
+ },
109
+ )
110
+ async def preview_file_content(
111
+ file: UploadFile = File(...),
112
+ max_rows: int = Form(65535),
113
+ max_cols: int = Form(65535),
114
+ ) -> FilePreviewResponse:
115
+ """
116
+ @methoddesc 基于文件上传预览文件内容
117
+
118
+ 业务用途:
119
+ - 接收前端通过 multipart 上传的文件流
120
+ - 内部读取字节流并复用 preview_from_content 服务进行解析
121
+
122
+ 参数:
123
+ file: 上传的文件对象
124
+ max_rows: 最大返回行数,默认 65535
125
+ max_cols: 最大返回列数,默认 65535
126
+
127
+ 返回:
128
+ FilePreviewResponse: 包含解析数据、文件类型、Sheet 列表的响应
129
+ """
130
+ logger.info("=" * 50)
131
+ logger.info(f"[PREVIEW] 收到文件内容预览请求: {file.filename}")
132
+ logger.info(f"[PREVIEW] max_rows: {max_rows}, max_cols: {max_cols}")
133
+ logger.info("=" * 50)
134
+
135
+ try:
136
+ file_name = file.filename or "unknown"
137
+ file_ext = os.path.splitext(file_name)[1].lower()
138
+ content = await file.read()
139
+
140
+ data, file_type, total_rows, sheets, current_sheet = preview_from_content(content, file_ext, max_rows, max_cols)
141
+ return FilePreviewResponse(
142
+ success=True,
143
+ data=data,
144
+ file_type=file_type,
145
+ file_name=file_name,
146
+ total_rows=total_rows,
147
+ total_cols=len(data[0]) if data else 0,
148
+ sheets=sheets,
149
+ current_sheet=current_sheet,
150
+ )
151
+ except HTTPException:
152
+ raise
153
+ except Exception as e:
154
+ logger.error(f"[PREVIEW] 未知错误: {e}")
155
+ return FilePreviewResponse(
156
+ success=False,
157
+ data=None,
158
+ file_type="unknown",
159
+ file_name=file.filename or "",
160
+ error=f"预览文件时发生错误: {e}",
161
+ )
162
+
163
+
164
+ @router.post(
165
+ "/switch-sheet",
166
+ response_model=FilePreviewResponse,
167
+ summary="切换 Excel 工作表预览",
168
+ responses={
169
+ 500: {"description": "切换工作表时发生错误"},
170
+ },
171
+ )
172
+ def switch_sheet(request: SheetSwitchRequest) -> FilePreviewResponse:
173
+ """切换 Excel 工作表预览"""
174
+ file_path = request.file_path
175
+
176
+ validate_file_access(file_path)
177
+
178
+ try:
179
+ data, file_type, total_rows, sheets, current_sheet = preview_from_path(
180
+ file_path,
181
+ request.max_rows,
182
+ request.max_cols,
183
+ sheet_name=request.sheet_name,
184
+ )
185
+ return FilePreviewResponse(
186
+ success=True,
187
+ data=data,
188
+ file_type=file_type,
189
+ file_name=os.path.basename(request.file_path),
190
+ total_rows=total_rows,
191
+ total_cols=len(data[0]) if data else 0,
192
+ sheets=sheets,
193
+ current_sheet=current_sheet,
194
+ )
195
+ except HTTPException:
196
+ raise
197
+ except Exception as e:
198
+ return FilePreviewResponse(
199
+ success=False,
200
+ data=None,
201
+ file_type="excel",
202
+ file_name="",
203
+ error=f"切换工作表时发生错误: {e}",
204
+ )
205
+
206
+
207
+ @router.post(
208
+ "/switch-sheet/content",
209
+ response_model=FilePreviewResponse,
210
+ summary="基于文件上传切换 Excel 工作表预览",
211
+ responses={
212
+ 500: {"description": "切换工作表时发生错误"},
213
+ },
214
+ )
215
+ async def switch_sheet_content(
216
+ file: UploadFile = File(...),
217
+ sheet_name: str = Form(...),
218
+ max_rows: int = Form(65535),
219
+ max_cols: int = Form(65535),
220
+ ) -> FilePreviewResponse:
221
+ """
222
+ @methoddesc 基于文件上传切换 Excel 工作表预览
223
+
224
+ 业务用途:
225
+ - 接收前端上传的多 Sheet Excel 文件,并切换到指定 sheet 进行预览
226
+ - 与 switch_sheet_by_path 互补:本接口处理"用户拖入即上传"的场景
227
+
228
+ 参数:
229
+ file: 上传的文件对象(multipart/form-data)
230
+ sheet_name: 目标工作表名
231
+ max_rows: 最大返回行数,默认 65535
232
+ max_cols: 最大返回列数,默认 65535
233
+
234
+ 返回:
235
+ FilePreviewResponse: 包含新工作表预览数据的响应
236
+ """
237
+ try:
238
+ file_name = file.filename or "unknown"
239
+ file_ext = os.path.splitext(file_name)[1].lower()
240
+ content = await file.read()
241
+
242
+ data, file_type, total_rows, sheets, current_sheet = preview_from_content(
243
+ content, file_ext, max_rows, max_cols, sheet_name=sheet_name
244
+ )
245
+ return FilePreviewResponse(
246
+ success=True,
247
+ data=data,
248
+ file_type=file_type,
249
+ file_name=file_name,
250
+ total_rows=total_rows,
251
+ total_cols=len(data[0]) if data else 0,
252
+ sheets=sheets,
253
+ current_sheet=current_sheet,
254
+ )
255
+ except HTTPException:
256
+ raise
257
+ except Exception as e:
258
+ return FilePreviewResponse(
259
+ success=False,
260
+ data=None,
261
+ file_type="excel",
262
+ file_name=file.filename or "",
263
+ error=f"切换工作表时发生错误: {e}",
264
+ )
@@ -0,0 +1,154 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview 表头行变更处理路由模块
18
+
19
+ 功能概述:
20
+ - 接收并处理表头行变更事件
21
+ - 将新的表头行信息持久化到对应的 Schema YAML 文件
22
+ - 记录变更时间戳和节点关联信息
23
+
24
+ 架构设计:
25
+ - 基于 FastAPI APIRouter 组织路由
26
+ - 直接操作本地 YAML Schema 配置文件
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import logging
32
+ import os
33
+ from datetime import datetime
34
+ from pathlib import Path
35
+
36
+ import yaml
37
+ from fastapi import Depends
38
+
39
+ from app.api.dependencies import get_project_config_path
40
+ from app.api.models import HeaderRowChangedRequest, HeaderRowChangedResponse
41
+ from app.shared.core.io.yaml import write_yaml_atomic
42
+
43
+ from .router import router
44
+
45
+ logger = logging.getLogger(__name__)
46
+
47
+ _SAFE_SCHEMA_NAME_CHARS = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.")
48
+
49
+
50
+ def _sanitize_schema_name(name: str) -> str:
51
+ cleaned = "".join(c for c in name if c in _SAFE_SCHEMA_NAME_CHARS)
52
+ if not cleaned or cleaned != name:
53
+ raise ValueError(f"非法的 schema_name: {name!r}")
54
+ return cleaned
55
+
56
+
57
+ @router.post(
58
+ "/header-row-changed",
59
+ response_model=HeaderRowChangedResponse,
60
+ summary="处理表头行变更请求",
61
+ responses={
62
+ 500: {"description": "处理表头行变更时发生错误"},
63
+ },
64
+ )
65
+ def handle_header_row_changed(
66
+ request: HeaderRowChangedRequest,
67
+ config_path: str = Depends(get_project_config_path),
68
+ ) -> HeaderRowChangedResponse:
69
+ """
70
+ 处理表头行变更请求。
71
+
72
+ 接收前端传来的表头行变更事件,将新的表头行索引和关联信息
73
+ 持久化到对应的 Schema YAML 文件中。
74
+
75
+ 参数:
76
+ request: 表头行变更请求,包含 node_id、header_row、schema_name 等信息
77
+
78
+ 返回:
79
+ HeaderRowChangedResponse: 变更处理结果,包含成功状态和消息
80
+
81
+ 业务逻辑:
82
+ 1. 提取请求中的 node_id、header_row、schema_name 等字段
83
+ 2. 如果指定了 schema_name,尝试更新对应的 Schema YAML 文件
84
+ 3. 在 Schema 文件中记录 header_row.index、node_id 和 updated_at
85
+ 4. 如果提供了 row_data,一并保存
86
+ 5. 返回操作结果
87
+ """
88
+ try:
89
+ node_id = request.node_id
90
+ header_row = request.header_row
91
+ _old_header_row = request.old_header_row
92
+ row_data = request.row_data
93
+ schema_name = request.schema_name
94
+
95
+ if schema_name:
96
+ try:
97
+ safe_name = _sanitize_schema_name(schema_name)
98
+ except ValueError:
99
+ return HeaderRowChangedResponse(
100
+ success=False,
101
+ message="非法的 schema_name",
102
+ schema_name=schema_name,
103
+ updated_at=datetime.now().isoformat(),
104
+ )
105
+ schema_path = Path(config_path) / "schemas" / f"{safe_name}.yaml"
106
+
107
+ if os.path.exists(schema_path):
108
+ try:
109
+ with open(schema_path, encoding="utf-8") as f:
110
+ schema_content = yaml.safe_load(f)
111
+
112
+ if "header_row" not in schema_content:
113
+ schema_content["header_row"] = {}
114
+
115
+ schema_content["header_row"]["index"] = header_row
116
+ schema_content["header_row"]["node_id"] = node_id
117
+ schema_content["header_row"]["updated_at"] = datetime.now().isoformat()
118
+
119
+ if row_data:
120
+ schema_content["header_row"]["data"] = row_data
121
+
122
+ write_yaml_atomic(schema_path, schema_content)
123
+
124
+ return HeaderRowChangedResponse(
125
+ success=True,
126
+ message=f"表头行已更新到第{header_row}行",
127
+ schema_name=schema_name,
128
+ updated_at=datetime.now().isoformat(),
129
+ )
130
+
131
+ except Exception as e:
132
+ logger.warning("更新Schema文件失败: %s", e)
133
+ return HeaderRowChangedResponse(
134
+ success=False,
135
+ message="更新Schema文件失败",
136
+ schema_name=schema_name,
137
+ updated_at=datetime.now().isoformat(),
138
+ )
139
+
140
+ return HeaderRowChangedResponse(
141
+ success=True,
142
+ message=f"表头行变更事件已接收,node_id: {node_id}, header_row: {header_row}",
143
+ schema_name=schema_name or "",
144
+ updated_at=datetime.now().isoformat(),
145
+ )
146
+
147
+ except Exception as e:
148
+ logger.warning("处理表头行变更时发生错误: %s", e)
149
+ return HeaderRowChangedResponse(
150
+ success=False,
151
+ message="处理表头行变更时发生错误",
152
+ schema_name=request.schema_name or "",
153
+ updated_at=datetime.now().isoformat(),
154
+ )
@@ -0,0 +1,81 @@
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
+ - 定义文件预览相关的 Pydantic 数据模型
21
+ - 包括路径预览、内容预览、工作表切换等请求和响应结构
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import Any
27
+
28
+ from pydantic import BaseModel, Field
29
+
30
+
31
+ class FilePreviewRequest(BaseModel):
32
+ """文件预览请求(基于上传内容模式)"""
33
+
34
+ file_path: str = Field(..., description="文件路径(经 validate_file_access 校验后由后端读取,参与文件 IO)")
35
+ max_rows: int = Field(default=65535, description="最大返回行数")
36
+ max_cols: int = Field(default=65535, description="最大返回列数")
37
+
38
+
39
+ class FilePreviewResponse(BaseModel):
40
+ """文件预览响应"""
41
+
42
+ success: bool = Field(..., description="是否成功解析")
43
+ data: list[list[str]] | None = Field(default=None, description="表格化预览数据")
44
+ file_type: str = Field(..., description="文件类型标识,如 xlsx/csv/json")
45
+ file_name: str = Field(..., description="原始文件名")
46
+ total_rows: int | None = Field(default=None, description="文件总行数")
47
+ total_cols: int | None = Field(default=None, description="文件总列数")
48
+ sheets: list[str] | None = Field(default=None, description="Excel 工作表列表")
49
+ current_sheet: str | None = Field(default=None, description="当前预览的工作表名")
50
+ error: str | None = Field(default=None, description="错误信息")
51
+ # JSON 原始数据(用于树状显示)
52
+ raw_data: list[Any] | None = Field(default=None, description="JSON 原始数据(用于树状显示)")
53
+ # JSON 类型推断(字段名 → 推断类型)
54
+ type_inference: dict[str, str] | None = Field(default=None, description="JSON 字段类型推断结果")
55
+ # JSON 结构统计
56
+ field_count: int | None = Field(default=None, description="JSON 顶层字段数量")
57
+ nest_depth: int | None = Field(default=None, description="JSON 嵌套深度")
58
+
59
+
60
+ class SheetSwitchRequest(BaseModel):
61
+ """Excel 工作表切换请求"""
62
+
63
+ file_path: str = Field(..., description="文件路径或原始名称")
64
+ sheet_name: str = Field(..., description="目标工作表名")
65
+ max_rows: int = Field(default=100, description="最大返回行数")
66
+ max_cols: int = Field(default=50, description="最大返回列数")
67
+
68
+
69
+ class FilePathPreviewRequest(BaseModel):
70
+ """基于文件路径的预览请求"""
71
+
72
+ file_path: str = Field(..., description="文件绝对路径")
73
+ max_rows: int = Field(default=65535, description="最大返回行数")
74
+ max_cols: int = Field(default=65535, description="最大返回列数")
75
+ sheet_name: str | None = Field(default=None, description="Excel 工作表名")
76
+ # JSON 特有参数
77
+ json_path: str | None = Field(default=None, description="JSONPath 提取路径,如 $.data.items")
78
+ json_format: str | None = Field(
79
+ default=None, description="JSON 格式:array / lines / object(前端直接透传,auto 已废弃)"
80
+ )
81
+ record_path: str | None = Field(default=None, description="Record path,用于 pd.json_normalize")