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,78 @@
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 Transform 动作验证器
18
+
19
+ 验证 AI 生成 Transform 相关动作(ADD_TRANSFORM/UPDATE_TRANSFORM/DELETE_TRANSFORM)
20
+ 的类型、参数合法性。
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from typing import Any
26
+
27
+ from app.shared.services.llm.actions.registry import TRANSFORM_SUB_TYPES
28
+ from app.shared.services.llm.actions.validation_types import ValidationError
29
+
30
+ # 转换子类型白名单从注册表派生(单一事实源)
31
+ VALID_TRANSFORM_TYPE_NAMES = TRANSFORM_SUB_TYPES
32
+
33
+
34
+ def validate_transform_action(action: dict[str, Any], index: int) -> list[ValidationError]:
35
+ """验证 Transform 操作
36
+
37
+ 检查 transformSpec 中的类型、参数合法性。
38
+ """
39
+ errors = []
40
+ action_type = action.get("actionType", "")
41
+ spec = action.get("transformSpec", {})
42
+
43
+ if action_type == "ADD_TRANSFORM":
44
+ transform_type = spec.get("type", "")
45
+ if not transform_type:
46
+ errors.append(
47
+ ValidationError(
48
+ action_index=index,
49
+ action_type=action_type,
50
+ error_type="missing_transform_type",
51
+ message="Transform 类型不能为空",
52
+ suggestion=f"可选: {', '.join(sorted(VALID_TRANSFORM_TYPE_NAMES))}",
53
+ )
54
+ )
55
+ elif transform_type not in VALID_TRANSFORM_TYPE_NAMES:
56
+ errors.append(
57
+ ValidationError(
58
+ action_index=index,
59
+ action_type=action_type,
60
+ error_type="invalid_transform_type",
61
+ message=f"不支持的 Transform 类型: '{transform_type}'",
62
+ suggestion=f"可选: {', '.join(sorted(VALID_TRANSFORM_TYPE_NAMES))}",
63
+ )
64
+ )
65
+
66
+ if action_type in ("UPDATE_TRANSFORM", "DELETE_TRANSFORM"):
67
+ transform_id = spec.get("transformId") or spec.get("id")
68
+ if not transform_id:
69
+ errors.append(
70
+ ValidationError(
71
+ action_index=index,
72
+ action_type=action_type,
73
+ error_type="missing_transform_id",
74
+ message="更新/删除 Transform 需要指定 transformId",
75
+ )
76
+ )
77
+
78
+ return errors
@@ -0,0 +1,400 @@
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
+ - 处理约束的 YAML 配置更新(添加、更新、删除)
21
+ - 批量处理内联约束(同一 schema 合并为一次文件读写)
22
+ - 执行项目数据校验
23
+ - 生成前端渲染指令
24
+
25
+ 输入示例:
26
+ success, message = update_yaml_config(action, "/workspace")
27
+ results = process_inline_batch(actions, "/workspace")
28
+ result = execute_validate_project("/workspace", table_filter="users")
29
+
30
+ 输出示例:
31
+ (True, "notnull_users_email")
32
+ [{"action": action, "success": True, "message": "..."}]
33
+ {"success": True, "message": "校验通过", "details": {...}}
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import logging
39
+ from pathlib import Path
40
+ from typing import Any
41
+
42
+ import yaml
43
+
44
+ from app.shared.core.project.constraint.types import ConstraintFile
45
+ from app.shared.core.project.constraint.writer import save_constraint
46
+ from app.shared.core.project.manifest.reader import load_manifest
47
+ from app.shared.core.project.manifest.writer import ensure_constraint_ref, save_manifest
48
+ from app.shared.services.llm.actions.regex_handlers import process_regex_action
49
+ from app.shared.services.llm.actions.schema_handlers import process_schema_action
50
+ from app.shared.services.llm.actions.settings_handlers import process_settings_action
51
+ from app.shared.services.llm.actions.transform_handlers import process_transform_action
52
+ from app.shared.services.llm.constraints.constraint_builder import (
53
+ CONSTRAINT_TYPE_MAP,
54
+ _build_constraint_params,
55
+ _build_constraint_refs,
56
+ )
57
+ from app.shared.services.llm.constraints.constraint_deletion import delete_constraint_file
58
+ from app.shared.services.llm.constraints.constraint_id import _generate_constraint_id
59
+ from app.shared.services.llm.constraints.frontend_instructions import generate_frontend_instructions
60
+ from app.shared.services.llm.constraints.inline_batch import (
61
+ _collect_target_schema_id,
62
+ _is_inline_action,
63
+ process_inline_batch,
64
+ )
65
+ from app.shared.services.llm.validate_executor import execute_validate_project
66
+ from app.shared.services.llm.yaml_io import FileLock, YamlUpdateError, atomic_write_yaml
67
+
68
+ logger = logging.getLogger(__name__)
69
+
70
+ # 重新导出子模块函数,保持向后兼容
71
+ __all__ = [
72
+ "update_yaml_config",
73
+ "delete_constraint_file",
74
+ "generate_frontend_instructions",
75
+ "execute_validate_project",
76
+ "_collect_target_schema_id",
77
+ "_is_inline_action",
78
+ "process_inline_batch",
79
+ "process_schema_action",
80
+ "process_regex_action",
81
+ "process_transform_action",
82
+ "process_settings_action",
83
+ ]
84
+
85
+
86
+ def _ensure_manifest_constraint_ref(workspace_path: str, constraint_id: str) -> None:
87
+ """确保 manifest 中包含指定独立约束的引用(镜像 _ensure_manifest_schema_ref)。
88
+
89
+ 失败时抛出异常 —— 避免约束文件已写盘但 manifest 未登记,
90
+ 导致校验引擎永不加载该约束(C1:静默失效)。
91
+ """
92
+ manifest_path = Path(workspace_path) / "project.precis.yaml"
93
+ if not manifest_path.exists():
94
+ return
95
+
96
+ manifest = load_manifest(manifest_path)
97
+ ensure_constraint_ref(manifest, constraint_id)
98
+ save_manifest(manifest, manifest_path)
99
+
100
+
101
+ def _remove_manifest_constraint_ref(workspace_path: str, constraint_id: str) -> None:
102
+ """从 manifest 中移除指定独立约束的引用(镜像 _remove_manifest_schema_ref)。
103
+
104
+ 失败时抛出异常 —— 避免约束文件已删除但 manifest 仍残留引用(dangling ref)。
105
+ """
106
+ manifest_path = Path(workspace_path) / "project.precis.yaml"
107
+ if not manifest_path.exists():
108
+ return
109
+
110
+ manifest = load_manifest(manifest_path)
111
+ manifest.constraints = [c for c in manifest.constraints if c.id != constraint_id]
112
+ save_manifest(manifest, manifest_path)
113
+
114
+
115
+ def _delete_inline_constraint(
116
+ std_type: str,
117
+ workspace_path: str,
118
+ table_name: str,
119
+ target_column: str,
120
+ target_node_id: str,
121
+ target_column_id: str,
122
+ ) -> tuple[bool, str]:
123
+ """从 schema 文件中删除内联约束
124
+
125
+ 定位方式与添加/更新内联约束保持一致:schema 按 id/name 匹配,
126
+ 列按传入 ID 或名称解析,约束项按「同列 + 同类型」匹配(镜像内联分支的既有约束查找)。
127
+
128
+ 参数:
129
+ std_type: 标准化后的约束类型(如 NotNull)
130
+ workspace_path: 项目工作区路径
131
+ table_name: 目标表名
132
+ target_column: 目标列名
133
+ target_node_id: 目标表节点 ID(优先用于匹配 schema 文件)
134
+ target_column_id: 目标列 ID(优先用于匹配列)
135
+
136
+ 返回:
137
+ 元组 (是否成功, 约束ID 或错误信息)
138
+ """
139
+ try:
140
+ schemas_dir = Path(workspace_path) / "schemas"
141
+ target_table_id = target_node_id or table_name
142
+ schema_file = None
143
+
144
+ for sf in schemas_dir.glob("*.yaml"):
145
+ with open(sf, encoding="utf-8") as f:
146
+ sd = yaml.safe_load(f) or {}
147
+ if sd.get("id") == target_table_id or sd.get("name") == table_name:
148
+ schema_file = sf
149
+ break
150
+
151
+ if not schema_file:
152
+ return False, f"未找到 schema 文件: {target_table_id}"
153
+
154
+ with FileLock(str(schema_file)):
155
+ # 重新读取文件(确保在锁保护下读取最新内容)
156
+ with open(schema_file, encoding="utf-8") as f:
157
+ schema_data = yaml.safe_load(f) or {}
158
+
159
+ columns = schema_data.get("columns", [])
160
+ column_id = target_column_id
161
+ if not column_id:
162
+ for col in columns:
163
+ if col.get("name") == target_column:
164
+ column_id = col.get("id")
165
+ break
166
+
167
+ if not column_id:
168
+ return False, f"未找到列: {target_column}"
169
+
170
+ constraints = schema_data.get("constraints", [])
171
+ # 移除「同列 + 同类型」的内联约束项,其余保持不变
172
+ remaining = [c for c in constraints if not (c.get("column") == column_id and c.get("type") == std_type)]
173
+ if len(remaining) == len(constraints):
174
+ return False, f"未找到内联约束: {std_type} on {table_name}.{target_column}"
175
+
176
+ schema_data["constraints"] = remaining
177
+ # 必须全量替换写入:atomic_write_yaml 的 preserve_format 路径按 id
178
+ # 合并列表(只更新/追加、不删除),会把本次删除的约束"复活"
179
+ atomic_write_yaml(schema_file, schema_data, preserve_format=False)
180
+
181
+ constraint_id = _generate_constraint_id(std_type, table_name or "inline", target_column)
182
+ logger.info(f"[updateYamlConfig] 删除内联约束: {constraint_id}")
183
+ return True, f"inline:{constraint_id}"
184
+
185
+ except YamlUpdateError as e:
186
+ error_msg = f"删除内联约束失败: {str(e)}"
187
+ logger.error(f"[updateYamlConfig] {error_msg}")
188
+ return False, error_msg
189
+ except OSError as e:
190
+ error_msg = f"删除内联约束文件操作失败: {str(e)}"
191
+ logger.error(f"[updateYamlConfig] {error_msg}")
192
+ return False, error_msg
193
+ except Exception as e:
194
+ error_msg = f"删除内联约束失败: {str(e)}"
195
+ logger.error(f"[updateYamlConfig] {error_msg}")
196
+ return False, error_msg
197
+
198
+
199
+ def update_yaml_config(action: dict[str, Any], workspace_path: str) -> tuple[bool, str]:
200
+ """
201
+ @methoddesc 更新 YAML 配置文件
202
+
203
+ 将 LLM 返回的约束动作写入到项目的 constraints 目录或 schema 文件的内联约束中。
204
+ 支持添加、更新、删除三种操作类型,以及内联和独立两种约束存储方式。
205
+
206
+ 参数:
207
+ action: 动作字典,包含 actionType 和 constraintSpec
208
+ workspace_path: 工作区路径
209
+
210
+ 返回:
211
+ 元组 (是否成功, 错误信息或约束ID)
212
+
213
+ 示例:
214
+ >>> success, msg = update_yaml_config(
215
+ ... {"actionType": "ADD_CONSTRAINT_NODE", "constraintSpec": {...}},
216
+ ... "/workspace"
217
+ ... )
218
+ """
219
+ action_type = action.get("actionType")
220
+ constraint_spec = action.get("constraintSpec", {})
221
+
222
+ constraint_type = constraint_spec.get("type", "")
223
+ table_name = constraint_spec.get("tableName", "")
224
+ target_column = constraint_spec.get("targetColumn", "")
225
+ target_node_id = constraint_spec.get("targetNodeId", "")
226
+ target_column_id = constraint_spec.get("targetColumnId", "")
227
+ is_inline = constraint_spec.get("isInline", False)
228
+
229
+ # 统一转换类型名
230
+ std_type = CONSTRAINT_TYPE_MAP.get(constraint_type, constraint_type)
231
+
232
+ if not std_type or not target_column:
233
+ error_msg = f"无效的约束规格: constraint_type={constraint_type}, std_type={std_type}, table_name={table_name}, target_column={target_column}"
234
+ logger.error(f"[updateYamlConfig] {error_msg}")
235
+ return False, error_msg
236
+
237
+ # 确定用于文件名的表标识符 (优先使用可读的名称)
238
+ filename_table = table_name or target_node_id or "unknown"
239
+ filename_column = target_column or target_column_id or "unknown"
240
+
241
+ # DELETE 动作必须先于 is_inline 分支处理:删除语义与存储形态无关,
242
+ # 旧实现 isInline=true 时先命中内联"添加"分支,只增不删还误报 success。
243
+ # 内联约束从 schema 文件的 constraints 列表移除,独立约束删除 .constraint.yaml 文件。
244
+ if action_type == "DELETE_CONSTRAINT_NODE":
245
+ if is_inline:
246
+ return _delete_inline_constraint(
247
+ std_type=std_type,
248
+ workspace_path=workspace_path,
249
+ table_name=table_name,
250
+ target_column=target_column,
251
+ target_node_id=target_node_id,
252
+ target_column_id=target_column_id,
253
+ )
254
+ # 删除独立约束文件
255
+ success, message = delete_constraint_file(std_type, filename_table, filename_column, workspace_path)
256
+ # 同步从 manifest 移除引用(避免 dangling ref)
257
+ if success:
258
+ try:
259
+ deleted_constraint_id = _generate_constraint_id(std_type, filename_table or "inline", filename_column)
260
+ _remove_manifest_constraint_ref(workspace_path, deleted_constraint_id)
261
+ except Exception as e:
262
+ # 文件已删成功,manifest 清理失败仅告警(不回滚文件删除)
263
+ logger.warning(f"[updateYamlConfig] 删除 manifest 引用失败(文件已删): {e}")
264
+ return success, message
265
+
266
+ if is_inline:
267
+ logger.info(f"[updateYamlConfig] 内联约束: {std_type} on {filename_table}.{filename_column}")
268
+ constraint_id = _generate_constraint_id(std_type, filename_table or "inline", filename_column)
269
+
270
+ # 将内联约束添加到 schema 文件
271
+ try:
272
+ schemas_dir = Path(workspace_path) / "schemas"
273
+
274
+ # 查找匹配的 schema 文件
275
+ target_table_id = target_node_id or table_name
276
+ schema_file = None
277
+
278
+ for sf in schemas_dir.glob("*.yaml"):
279
+ with open(sf, encoding="utf-8") as f:
280
+ sd = yaml.safe_load(f) or {}
281
+ if sd.get("id") == target_table_id or sd.get("name") == table_name:
282
+ schema_file = sf
283
+ break
284
+
285
+ if not schema_file:
286
+ # 找不到匹配的 schema 文件时必须返回失败,否则会静默成功
287
+ # 并触发前端生成幽灵节点(画布有、文件无)。对齐 process_inline_batch 的行为。
288
+ error_msg = f"未找到 schema 文件: {target_table_id}"
289
+ logger.warning(f"[updateYamlConfig] {error_msg}")
290
+ return False, error_msg
291
+
292
+ # 使用文件锁保护写入操作
293
+ with FileLock(str(schema_file)):
294
+ # 重新读取文件(确保在锁保护下读取最新内容)
295
+ with open(schema_file, encoding="utf-8") as f:
296
+ schema_data = yaml.safe_load(f) or {}
297
+
298
+ columns = schema_data.get("columns", [])
299
+ if "constraints" not in schema_data:
300
+ schema_data["constraints"] = []
301
+
302
+ # 查找目标列的 ID(优先使用传入的 ID,否则按名称查找)
303
+ column_id = target_column_id
304
+ if not column_id:
305
+ for col in columns:
306
+ if col.get("name") == target_column:
307
+ column_id = col.get("id")
308
+ break
309
+
310
+ if not column_id:
311
+ # 目标列不存在时必须返回失败,否则会静默成功(与 process_inline_batch 对齐)。
312
+ error_msg = f"未找到列: {target_column}"
313
+ logger.warning(f"[updateYamlConfig] {error_msg}")
314
+ return False, error_msg
315
+
316
+ # 构建内联约束结构
317
+ inline_constraint = {
318
+ "id": constraint_id,
319
+ "column": column_id,
320
+ "type": std_type,
321
+ }
322
+ params = _build_constraint_params(std_type, constraint_spec)
323
+ if params:
324
+ inline_constraint["params"] = params
325
+
326
+ # 检查是否已存在相同列和类型的约束
327
+ existing_idx = None
328
+ for idx, existing in enumerate(schema_data["constraints"]):
329
+ if existing.get("column") == column_id and existing.get("type") == std_type:
330
+ existing_idx = idx
331
+ break
332
+
333
+ if existing_idx is not None:
334
+ # 更新现有约束
335
+ schema_data["constraints"][existing_idx] = inline_constraint
336
+ logger.info(f"[updateYamlConfig] 更新内联约束: {constraint_id}")
337
+ else:
338
+ # 添加新约束
339
+ schema_data["constraints"].append(inline_constraint)
340
+ logger.info(f"[updateYamlConfig] 添加内联约束: {constraint_id}")
341
+
342
+ # 原子性写入
343
+ atomic_write_yaml(schema_file, schema_data)
344
+ return True, f"inline:{constraint_id}"
345
+
346
+ except YamlUpdateError as e:
347
+ error_msg = f"YAML 更新失败: {str(e)}"
348
+ logger.error(f"[updateYamlConfig] {error_msg}")
349
+ return False, error_msg
350
+ except OSError as e:
351
+ error_msg = f"文件操作失败: {str(e)}"
352
+ logger.error(f"[updateYamlConfig] {error_msg}")
353
+ return False, error_msg
354
+ except Exception as e:
355
+ error_msg = f"保存约束配置失败: {str(e)}"
356
+ logger.error(f"[updateYamlConfig] {error_msg}")
357
+ return False, error_msg
358
+
359
+ else:
360
+ # 添加/更新独立约束文件
361
+ constraint_id = _generate_constraint_id(std_type, filename_table, filename_column)
362
+ constraint_file_path = Path(workspace_path) / "constraints" / f"{constraint_id}.constraint.yaml"
363
+
364
+ try:
365
+ # §2.4: ADD 存在性检查——原实现直写覆盖,同列第二个同类型约束静默顶掉第一个。
366
+ # 已存在时引导走 UPDATE;删除语义(DELETE 分支在前)不受影响。
367
+ if action_type == "ADD_CONSTRAINT_NODE" and constraint_file_path.exists():
368
+ error_msg = f"约束已存在(id={constraint_id}),如需修改请用 UPDATE,如需新建请先删除现有约束。"
369
+ logger.warning(f"[updateYamlConfig] {error_msg}")
370
+ return False, error_msg
371
+
372
+ # 构建约束配置
373
+ constraint_config = ConstraintFile(
374
+ version=2,
375
+ id=constraint_id,
376
+ type=std_type,
377
+ enabled=True,
378
+ description=None,
379
+ refs=_build_constraint_refs(std_type, table_name, target_column, constraint_spec, workspace_path),
380
+ params=_build_constraint_params(std_type, constraint_spec),
381
+ input_from_node=None,
382
+ input_column=None,
383
+ )
384
+
385
+ # 保存约束文件
386
+ save_constraint(constraint_config, str(constraint_file_path))
387
+ # 注册到 manifest(C1 修复:否则约束文件成为孤儿,校验引擎永不加载)
388
+ try:
389
+ _ensure_manifest_constraint_ref(workspace_path, constraint_id)
390
+ except Exception as e:
391
+ logger.error(f"[updateYamlConfig] 登记 manifest 失败,回滚约束文件: {e}")
392
+ constraint_file_path.unlink(missing_ok=True)
393
+ return False, f"更新 manifest 引用失败: {e}"
394
+ logger.info(f"[updateYamlConfig] 成功保存约束: {constraint_id}")
395
+ return True, constraint_id
396
+
397
+ except Exception as e:
398
+ error_msg = f"保存约束配置失败: {str(e)}"
399
+ logger.error(f"[updateYamlConfig] {error_msg}")
400
+ return False, error_msg
@@ -0,0 +1,63 @@
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 Chat 动作解析器模块
18
+
19
+ 功能概述:
20
+ - 解析 LLM 返回的 JSON 动作列表并执行对应的业务操作
21
+ - 支持约束创建、更新、删除,Schema 更新,项目校验等动作
22
+ - 本模块为入口文件,具体的 YAML IO、Schema 解析、约束构建、动作处理
23
+ 已拆分到同目录下的专用子模块中
24
+
25
+ 架构设计:
26
+ - ActionParser 类负责 LLM 响应的解析与验证(见 chat/response_parser.py)
27
+ - process_actions() 为业务入口,协调各子模块完成动作执行(见 actions/action_processor.py)
28
+
29
+ 输入示例:
30
+ actions = [
31
+ {
32
+ "action": "create_constraint",
33
+ "tableName": "users",
34
+ "constraintType": "Unique",
35
+ "columnNames": ["email"]
36
+ }
37
+ ]
38
+ result = process_actions(actions, workspace_path="/path/to/project")
39
+
40
+ 输出示例:
41
+ {
42
+ "success": True,
43
+ "message": "成功处理 1 个动作",
44
+ "details": [{"action": "create_constraint", "status": "success"}]
45
+ }
46
+ """
47
+
48
+ from __future__ import annotations
49
+
50
+ # 重新导出 process_actions,保持向后兼容。
51
+ # 注意:ActionParser 不在此 re-export——它在 response_parser.py 定义,
52
+ # 而 response_parser 现在依赖 actions.registry,re-export 会形成循环导入
53
+ # (response_parser → actions.registry → actions/__init__ → action_parser → response_parser)。
54
+ # 调用方应直接 from app.shared.services.llm.chat.response_parser import ActionParser。
55
+ from app.shared.services.llm.actions.action_processor import process_actions
56
+ from app.shared.services.llm.actions.registry import CONSTRAINT_TYPE_ALIASES as CONSTRAINT_TYPE_MAP
57
+ from app.shared.services.llm.yaml_io import ActionParseError
58
+
59
+ __all__ = [
60
+ "ActionParseError",
61
+ "CONSTRAINT_TYPE_MAP",
62
+ "process_actions",
63
+ ]