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,118 @@
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
+ # backend/app/api/models/workspace.py
17
+ """
18
+ @fileoverview 工作区请求/响应模型
19
+
20
+ 功能概述:
21
+ - 定义工作区和数据源相关的 Pydantic 数据模型
22
+ - 包含外部数据源、UI 偏好设置、工作区配置等模型
23
+
24
+ 输入示例:
25
+ WorkspaceConfig(
26
+ version="1.0",
27
+ data_sources=[ExternalDataSource(id="ds1", name="users.xlsx", fileId="...")],
28
+ ui_preferences=UIPreferences(expanded_folders={"schemas": True})
29
+ )
30
+
31
+ 输出示例:
32
+ WorkspaceConfig(
33
+ version="1.0",
34
+ data_sources=[...],
35
+ alias_mappings={"/path/to/file.xlsx": "users"},
36
+ last_updated="2024-01-01T00:00:00Z"
37
+ )
38
+ """
39
+
40
+ from pydantic import BaseModel, Field
41
+
42
+
43
+ class ExternalDataSource(BaseModel):
44
+ """
45
+ 外部数据源模型
46
+
47
+ 描述一个外部数据源(如 Excel、CSV 文件)的元信息。
48
+ 支持两种数据读取模式:
49
+ - IndexedDB 模式:浏览器环境,从 IndexedDB 读取文件内容
50
+ - 本地文件模式:Electron 环境,从本地文件系统读取
51
+
52
+ Attributes:
53
+ id: 数据源唯一标识符
54
+ name: 数据源显示名称(别名)
55
+ fileId: 文件标识(IndexedDB key 或本地文件路径)
56
+ type: 文件类型(如 excel、csv)
57
+ status: 数据源状态(ready/missing/loading)
58
+ addedAt: 添加到系统的时间
59
+ lastUsed: 最后一次使用的时间
60
+ alias: 数据源别名(可选)
61
+ error: 错误信息(如果有)
62
+ sourceMode: 数据读取模式(indexeddb/localfile)
63
+ localPath: 本地文件路径(Electron 模式专用)
64
+ """
65
+
66
+ id: str = Field(..., description="数据源唯一标识符")
67
+ name: str = Field(..., description="数据源显示名称")
68
+ fileId: str = Field(..., description="文件ID (IndexedDB key 或本地路径)")
69
+ type: str = Field(..., description="文件类型 (excel/csv)")
70
+ status: str = Field(default="ready", description="数据源状态 (ready/missing/loading)")
71
+ addedAt: str = Field(default="", description="添加时间")
72
+ lastUsed: str = Field(default="", description="最后使用时间")
73
+ alias: str | None = Field(None, description="数据源别名")
74
+ error: str | None = Field(None, description="错误信息")
75
+ size: int | None = Field(None, description="文件大小(字节)")
76
+ sourceMode: str | None = Field(None, description="数据来源模式 (indexeddb/localfile)")
77
+ localPath: str | None = Field(None, description="本地文件路径(Electron 模式专用)")
78
+ folderPath: str | None = Field(None, description="文件夹路径(用于分组显示)")
79
+
80
+
81
+ class UIPreferences(BaseModel):
82
+ """
83
+ UI 偏好设置模型
84
+
85
+ 存储用户界面的个性化配置选项。
86
+
87
+ Attributes:
88
+ expanded_folders: 文件树中各文件夹的展开/折叠状态
89
+ startup_loading_enabled: Electron 启动时是否显示加载弹窗
90
+ """
91
+
92
+ expanded_folders: dict[str, bool] = Field(
93
+ default={"input-staging": True, "schemas": True, "patterns": False, "constraints": False},
94
+ description="文件夹展开状态",
95
+ )
96
+ startup_loading_enabled: bool = Field(default=True, description="是否在 Electron 启动时显示加载弹窗")
97
+
98
+
99
+ class WorkspaceConfig(BaseModel):
100
+ """
101
+ 工作区配置模型
102
+
103
+ 完整的工作区设置,包含所有持久化的配置信息。
104
+ 通常存储在 workspace.json 或类似的配置文件中。
105
+
106
+ Attributes:
107
+ version: 配置版本号
108
+ data_sources: 数据源列表
109
+ alias_mappings: 文件路径到别名的映射
110
+ ui_preferences: UI 偏好设置
111
+ last_updated: 最后更新时间
112
+ """
113
+
114
+ version: str = Field(default="1.0", description="配置版本")
115
+ data_sources: list[ExternalDataSource] = Field(default=[], description="数据源列表")
116
+ alias_mappings: dict[str, str] = Field(default={}, description="路径到别名的映射")
117
+ ui_preferences: UIPreferences = Field(default=UIPreferences(), description="UI偏好设置")
118
+ last_updated: str = Field(default="", description="最后更新时间")
@@ -0,0 +1,56 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview API 路由聚合入口模块
18
+
19
+ 功能概述:
20
+ - 聚合并导出所有子模块的路由器实例
21
+ - 统一暴露项目、AI、预览、校验、工作空间等路由
22
+
23
+ 说明:
24
+ - 所有路由器(含 AI)均在包导入时直接实例化并注册端点,确保 `app.include_router`
25
+ 在任意环境(Linux/Windows、Py3.12/3.13)下都能确定性地拷贝到全部路由。
26
+ - 此前曾用 `_LazyAIRouter` 代理延迟加载 AI 路由,但其 `__getattr__` 转发在
27
+ CI(Linux) 环境下会导致 stream.py 等子模块的端点不稳定地漏注册(manifest 为
28
+ /ai/jobs/{id}/cancel、/ai/chat/{id}/confirm 等返回 404)。AI 子模块的可选依赖
29
+ (openai/aiohttp/psutil) 均已在各自模块内做懒加载守卫,启动期直接导入安全。
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ from .ai import router as ai_router
35
+ from .core.connection_rules import router as connection_rules_router
36
+ from .core.data_sources import router as data_sources_router
37
+ from .core.regex import router as regex_router
38
+ from .core.reporting import router as reporting_router
39
+ from .files import router as files_router
40
+ from .preview import router as preview_router
41
+ from .project import router as project_router
42
+ from .projects import router as projects_router
43
+ from .validation import router as validation_router
44
+
45
+ __all__ = [
46
+ "project_router",
47
+ "projects_router",
48
+ "files_router",
49
+ "connection_rules_router",
50
+ "ai_router",
51
+ "preview_router",
52
+ "validation_router",
53
+ "data_sources_router",
54
+ "regex_router",
55
+ "reporting_router",
56
+ ]
@@ -0,0 +1,36 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview AI 路由包入口
18
+
19
+ 功能概述:
20
+ - 作为 AI 相关 API 路由的统一出口
21
+ - 从 router 模块导入主路由实例,保持与旧代码的兼容性
22
+ - 导出 models 子模块供外部使用
23
+
24
+ 输入示例:
25
+ from app.api.routers.ai import router
26
+ app.include_router(router, prefix="/ai")
27
+
28
+ 输出示例:
29
+ router: APIRouter 实例,包含所有 AI 相关端点
30
+ models: AI 相关数据模型子模块
31
+ """
32
+
33
+ from . import models
34
+ from .router import router
35
+
36
+ __all__ = ["router", "models"]
@@ -0,0 +1,243 @@
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 AI 聊天 API 路由模块
18
+
19
+ 功能概述:
20
+ - 提供统一的 AI 聊天接口,支持流式和非流式响应
21
+ - 兼容前端 AI Chat 编排器的负载格式
22
+ - 提供 OpenAI 兼容的 /chat/completions 端点
23
+
24
+ 架构设计:
25
+ - 使用统一的 AI Chat 编排器处理业务逻辑
26
+ - 支持通过 X-Project-Config-Path Header 传递项目上下文
27
+ - 流式响应使用 SSE 格式返回
28
+
29
+ 输入示例:
30
+ POST /ai/chat
31
+ {
32
+ "message": "分析这个数据文件",
33
+ "context": {"hasContext": false, "selectedNodes": []},
34
+ "history": []
35
+ }
36
+
37
+ 输出示例:
38
+ {
39
+ "status": "success",
40
+ "reply": "分析结果...",
41
+ "actions": [],
42
+ "frontend_instructions": [],
43
+ "error": null
44
+ }
45
+ """
46
+
47
+ import json
48
+ import logging
49
+ from collections.abc import AsyncIterator
50
+ from typing import Any
51
+
52
+ from fastapi import Header, HTTPException
53
+ from fastapi.responses import StreamingResponse
54
+
55
+ from ....shared.services.ai.chat_orchestrator import execute_ai_chat_unified
56
+ from ....shared.services.ai.failure_messages import describe_ai_failure
57
+ from ....shared.services.llm.config import loader
58
+ from ....shared.services.llm.providers import ChatMessage, ChatRequest, create
59
+ from .models import AgentMeta, AiChatRequest, AiChatResponse, ChatRequestInput
60
+
61
+ # 从 router 模块导入 router 实例(与旧代码保持一致)
62
+ from .router import router
63
+
64
+
65
+ @router.post(
66
+ "/chat",
67
+ response_model=AiChatResponse,
68
+ summary="与前端对齐的 AI 聊天接口",
69
+ responses={
70
+ 400: {"description": "未配置默认 Provider"},
71
+ 404: {"description": "Provider 未找到"},
72
+ 502: {"description": "AI 服务调用失败"},
73
+ },
74
+ )
75
+ async def chat(request: AiChatRequest, x_project_config_path: str | None = Header(None)) -> AiChatResponse:
76
+ """
77
+ 与前端对齐的 AI 聊天接口
78
+
79
+ 接收前端特定的负载格式,并使用统一的 AI Chat 编排器来处理。
80
+ 先从配置中加载默认 Provider,然后将请求转发给编排器执行。
81
+
82
+ Args:
83
+ request: AI 聊天请求,包含消息、上下文和历史记录
84
+ x_project_config_path: 项目配置文件路径(通过 Header 传递,用于编排器上下文)
85
+
86
+ Returns:
87
+ 包含 AI 回复、状态和前端指令的响应对象
88
+ """
89
+ config = loader.load()
90
+
91
+ # 校验项目路径(修复 #10:防止 X-Project-Config-Path 任意目录读写)
92
+ from .utils import validate_project_path
93
+
94
+ safe_project_path = validate_project_path(x_project_config_path)
95
+
96
+ # 获取默认 provider
97
+ provider_id = config.defaults.get("chat")
98
+ if not provider_id:
99
+ raise HTTPException(400, detail="尚未设置默认的 AI 模型,请先在设置中选择一个 AI 模型")
100
+
101
+ # 查找 Provider 配置
102
+ provider_cfg = next((p for p in config.providers if p.id == provider_id), None)
103
+ if not provider_cfg:
104
+ raise HTTPException(404, detail=f"未找到对应的 AI 模型配置,可能已被删除(ID: {provider_id})")
105
+
106
+ # 转换上下文节点(将 Pydantic 模型转为字典)
107
+ context_nodes = [node.model_dump() for node in request.context.selectedNodes]
108
+
109
+ # 转换画布节点快照(供 read_canvas 工具查询画布真实状态)
110
+ canvas_nodes = [node.model_dump() for node in request.context.canvasNodes]
111
+
112
+ # 转换对话历史(将 Pydantic 模型转为简单字典列表)
113
+ history = [{"role": h.role, "content": h.content} for h in (request.history or [])]
114
+
115
+ # 调用统一编排器执行 AI 聊天
116
+ try:
117
+ result = await execute_ai_chat_unified(
118
+ message=request.message,
119
+ project_path=safe_project_path,
120
+ provider=provider_cfg,
121
+ context_nodes=context_nodes,
122
+ history=history,
123
+ agent_mode=request.agent_mode,
124
+ canvas_nodes=canvas_nodes,
125
+ )
126
+ except Exception as exc:
127
+ logging.getLogger(__name__).exception("AI chat failed")
128
+ raise HTTPException(status_code=502, detail=describe_ai_failure(exc))
129
+
130
+ # agent_meta 仅在 agent_mode=true 时填充,旧路径保持 None
131
+ agent_meta = AgentMeta(iterations=result.iterations, tool_steps=result.tool_steps) if request.agent_mode else None
132
+
133
+ # 根据编排器结果组装响应
134
+ if not result.success:
135
+ return AiChatResponse(
136
+ status="error",
137
+ reply=result.reply or "",
138
+ actions=[],
139
+ frontend_instructions=[],
140
+ agent_meta=agent_meta,
141
+ error=result.error,
142
+ )
143
+
144
+ return AiChatResponse(
145
+ status="success",
146
+ reply=result.reply,
147
+ actions=result.actions,
148
+ frontend_instructions=result.frontend_instructions,
149
+ agent_meta=agent_meta,
150
+ error=None,
151
+ )
152
+
153
+
154
+ @router.post(
155
+ "/chat/completions",
156
+ summary="OpenAI 兼容的聊天接口",
157
+ response_model=None,
158
+ responses={
159
+ 400: {"description": "未指定 Provider"},
160
+ 404: {"description": "Provider 未找到"},
161
+ 502: {"description": "AI 服务调用失败"},
162
+ },
163
+ )
164
+ async def chat_completions(request: ChatRequestInput) -> StreamingResponse | dict[str, Any]:
165
+ """
166
+ OpenAI 兼容的聊天接口
167
+
168
+ 与 OpenAI API 格式兼容,便于前端使用标准客户端。
169
+ 支持流式(SSE)和非流式两种响应模式。
170
+
171
+ Args:
172
+ request: OpenAI 兼容格式的聊天请求
173
+
174
+ Returns:
175
+ 流式模式下返回 SSE 响应,非流式返回 JSON 对象
176
+ """
177
+ config = loader.load()
178
+
179
+ provider_id = request.provider_id or config.defaults.get("chat")
180
+ if not provider_id:
181
+ raise HTTPException(400, detail="尚未指定要使用的 AI 模型,请先在设置中选择")
182
+
183
+ provider_cfg = next((p for p in config.providers if p.id == provider_id), None)
184
+ if not provider_cfg:
185
+ raise HTTPException(404, detail=f"未找到对应的 AI 模型配置,可能已被删除(ID: {provider_id})")
186
+
187
+ provider = create(provider_cfg)
188
+ # 构建聊天请求对象
189
+ chat_req = ChatRequest(
190
+ messages=[ChatMessage(role=m.role, content=m.content) for m in request.messages],
191
+ model=request.model or provider_cfg.model,
192
+ stream=request.stream,
193
+ temperature=request.temperature,
194
+ )
195
+
196
+ if request.stream:
197
+ # 流式响应模式:使用 SSE 格式逐块返回
198
+
199
+ async def generate() -> AsyncIterator[str]:
200
+ """
201
+ @methoddesc SSE 流式响应生成器
202
+
203
+ 业务用途:
204
+ - 在流式聊天请求下,构造与 OpenAI 兼容的 SSE 事件流
205
+ - 起始事件携带 assistant 角色标识;中间事件逐块回传 delta.content;末尾发送 [DONE] 终止标记
206
+
207
+ @yields str - 形如 ``data: {json}\\n\\n`` 的 SSE 帧
208
+ """
209
+ # 发送 SSE 起始事件,标识助手角色
210
+ yield '{"id":"chatcmpl-start","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant"}}]}\n\n'
211
+
212
+ try:
213
+ # 逐块获取流式输出,提取 delta 文本(该端点为纯文本兼容流,不处理 tool_calls)
214
+ # 注意: json 已在模块级导入。此前 import json 写在循环体内——若首个 chunk
215
+ # 之前就抛错,except 分支引用的 json 未绑定,会以 NameError 掩盖真实错误。
216
+ async for chunk in provider.chat_stream(chat_req):
217
+ if chunk.type == "delta" and chunk.text:
218
+ # 构建 SSE 数据包,格式与 OpenAI 兼容
219
+ data = {"object": "chat.completion.chunk", "choices": [{"delta": {"content": chunk.text}}]}
220
+ yield f"data: {json.dumps(data)}\n\n"
221
+ except Exception as exc:
222
+ logging.getLogger(__name__).exception("Chat stream failed")
223
+ yield f"data: {json.dumps({'error': str(exc)})}\n\n"
224
+
225
+ # 发送 SSE 结束标记
226
+ yield "data: [DONE]\n\n"
227
+
228
+ return StreamingResponse(generate(), media_type="text/event-stream")
229
+ else:
230
+ # 非流式响应模式:直接返回完整结果
231
+ try:
232
+ resp = await provider.chat(chat_req)
233
+ except Exception as exc:
234
+ logging.getLogger(__name__).exception("Chat completion failed")
235
+ raise HTTPException(status_code=502, detail=describe_ai_failure(exc))
236
+ return {
237
+ "id": "chatcmpl-local",
238
+ "object": "chat.completion",
239
+ "model": resp.model or provider_cfg.model,
240
+ "choices": [
241
+ {"index": 0, "message": {"role": "assistant", "content": resp.content}, "finish_reason": "stop"}
242
+ ],
243
+ }
@@ -0,0 +1,156 @@
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 AI 配置生成 API 路由模块 (V2)
18
+
19
+ 功能概述:
20
+ - 同步生成 V2 项目配置(manifest、schema、constraint、regex)
21
+ - 调用 ConfigGenerationService 分析数据文件并生成配置
22
+ - 支持自定义采样行数、列数、生成选项等参数
23
+
24
+ 架构设计:
25
+ - 依赖 get_project_config_path 获取项目路径
26
+ - 使用 ConfigGenerationService 封装 AI 配置生成逻辑
27
+ - 对生成过程中的各类异常进行分层处理
28
+
29
+ 输入示例:
30
+ POST /ai/config/generate
31
+ {
32
+ "file_paths": ["data/users.xlsx"],
33
+ "project_name": "用户数据项目",
34
+ "project_id": "user-data",
35
+ "options": {
36
+ "sample_rows": 100,
37
+ "generate_schemas": true,
38
+ "generate_constraints": true
39
+ }
40
+ }
41
+
42
+ 输出示例:
43
+ {
44
+ "success": true,
45
+ "yaml_preview": "version: 2...",
46
+ "manifest": {...},
47
+ "schemas": {...},
48
+ "constraints": {...},
49
+ "regex_nodes": {},
50
+ "warnings": [],
51
+ "error": null
52
+ }
53
+ """
54
+
55
+ import logging
56
+
57
+ from fastapi import Depends, HTTPException
58
+
59
+ from app.api.dependencies import get_project_config_path
60
+ from app.shared.services.llm.generation import (
61
+ CancelledError,
62
+ ConfigGenerationService,
63
+ GenerationOptions,
64
+ GenerationParseError,
65
+ ProfilingOptions,
66
+ )
67
+
68
+ from .models import ConfigGenerateRequest, ConfigGenerateResponse
69
+ from .router import router
70
+
71
+
72
+ @router.post(
73
+ "/config/generate",
74
+ response_model=ConfigGenerateResponse,
75
+ summary="同步生成 V2 配置",
76
+ responses={
77
+ 400: {"description": "参数错误"},
78
+ 409: {"description": "生成任务已取消"},
79
+ 422: {"description": "AI 响应解析失败"},
80
+ 500: {"description": "服务器内部错误"},
81
+ },
82
+ )
83
+ async def generate_v2_config(
84
+ payload: ConfigGenerateRequest,
85
+ config_path: str = Depends(get_project_config_path),
86
+ ) -> ConfigGenerateResponse:
87
+ """
88
+ 同步生成 V2 配置
89
+
90
+ 使用配置的 AI Provider 分析数据文件并生成项目配置。
91
+ 包括数据探查(Profiling)和配置生成两个主要阶段。
92
+
93
+ Args:
94
+ payload: 配置生成请求,包含文件路径、项目信息和生成选项
95
+ config_path: 项目配置文件路径(由依赖注入提供)
96
+
97
+ Returns:
98
+ 包含生成结果、YAML 预览和错误信息的响应对象
99
+
100
+ Raises:
101
+ HTTPException: 参数错误(400)、AI解析失败(422)、任务取消(409)、服务器错误(500)
102
+ """
103
+ # 构建数据探查选项(控制采样策略)
104
+ profiling_opts = ProfilingOptions(
105
+ sample_rows=payload.options.sample_rows,
106
+ sample_values_per_column=payload.options.sample_values_per_column,
107
+ max_files=payload.options.max_files,
108
+ max_cell_chars=payload.options.max_cell_chars,
109
+ )
110
+
111
+ # 构建生成选项(控制输出内容)
112
+ gen_opts = GenerationOptions(
113
+ generate_schemas=payload.options.generate_schemas,
114
+ generate_constraints=payload.options.generate_constraints,
115
+ generate_regex_nodes=payload.options.generate_regex_nodes,
116
+ keep_existing=payload.options.keep_existing,
117
+ )
118
+
119
+ # 创建配置生成服务实例(指定使用的 AI Provider)
120
+ service = ConfigGenerationService(provider_id=payload.provider_id)
121
+
122
+ try:
123
+ # 调用服务执行配置生成
124
+ result = await service.generate(
125
+ file_paths=payload.file_paths,
126
+ project_name=payload.project_name,
127
+ project_id=payload.project_id,
128
+ config_path=config_path,
129
+ profiling_options=profiling_opts,
130
+ generation_options=gen_opts,
131
+ )
132
+ except ValueError as e:
133
+ # 参数校验失败(如文件不存在、Provider 未配置等)
134
+ raise HTTPException(status_code=400, detail=f"生成请求无法处理:{e}")
135
+ except GenerationParseError as e:
136
+ # AI 返回的内容无法解析为有效配置
137
+ raise HTTPException(status_code=422, detail=f"AI 响应解析失败: {e}")
138
+ except CancelledError:
139
+ # 生成任务被用户取消
140
+ raise HTTPException(status_code=409, detail="生成任务已取消")
141
+ except Exception:
142
+ # 其他未预期的服务器错误
143
+ logging.getLogger(__name__).exception("Config generation failed")
144
+ raise HTTPException(status_code=500, detail="配置生成失败,请稍后重试")
145
+
146
+ # 组装响应对象
147
+ return ConfigGenerateResponse(
148
+ success=result["success"],
149
+ yaml_preview=result["yaml_preview"],
150
+ manifest=result["manifest"],
151
+ schemas=result["schemas"],
152
+ constraints=result["constraints"],
153
+ regex_nodes=result["regex_nodes"],
154
+ warnings=result.get("warnings", []),
155
+ error=result.get("error"),
156
+ )