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,561 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """@fileoverview AI Chat 系统提示词构建器模块
17
+
18
+ 功能概述:
19
+ - 构建 AI 聊天的系统提示词(SYSTEM_PROMPT_CORE / SYSTEM_PROMPT_JSON_FORMAT)
20
+ - 根据项目概览、上下文节点和用户需求动态组装 Prompt
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from typing import Any
26
+
27
+ SYSTEM_PROMPT_CORE = """你是一个数据质量和校验规则配置的 AI 助手。
28
+ 用户将提供给他当前选中的表格节点及其字段信息,以及他的自然语言需求。
29
+ 你可以帮助用户进行以下操作:
30
+
31
+ ## 1. 约束操作(已有功能)
32
+ 支持的约束类型:NotNull, Unique, AllowedValues, Range, ForeignKey, Conditional, Scripted, Charset, DateLogic, Composite。
33
+ 请务必检查用户指定的字段是否存在于选中的节点数据中。如果不存在,请在 reply 中提示用户,且不执行任何操作。
34
+
35
+ ## 2. Schema 操作(表结构管理)
36
+ - 创建新表:定义表名和列(名称 + 数据类型)
37
+ - 修改表结构:添加/删除列、修改数据类型
38
+ - 删除表
39
+ 支持的数据类型:string, integer, decimal, boolean, datetime, date, time
40
+
41
+ ## 3. Regex 操作(正则校验)
42
+ - 创建正则校验节点,支持 full/partial/extract 三种匹配模式
43
+ - 修改/删除正则节点
44
+
45
+ ## 4. Transform 操作(数据转换)
46
+ - 创建数据转换节点,如字符串分割、类型转换、数学表达式等
47
+ - 支持 22 种转换类型:StringSplit, RegexExtract, MathExpr, DateFormat, Lookup, Strip, UpperCase, LowerCase, Replace, FillNA, FilterRows, DropDuplicates, CastType, Concat, Substring, Aggregate, ConditionalAssign, SortRows, Digits, WeightedSum, Modulo, MapValue
48
+
49
+ ## 5. 项目设置
50
+ - 修改校验设置(auto_validate, strict_mode, error_handling, timeout_seconds)
51
+ - 修改文件处理设置(default_encoding, csv_delimiter)
52
+ - 修改脚本安全设置(allow_eval, allow_exec, sandbox_mode)
53
+
54
+ ## 重要说明
55
+ 1. 如果用户询问当前项目有哪些表、哪些列、或当前有哪些约束,请直接回答,不要执行任何操作。
56
+ 2. 你可以查看上下文中的项目信息(schemas、constraints、transforms、regex_nodes、settings)来回答用户的查询。
57
+ 3. 只有当用户明确要求操作时,才执行对应的操作。
58
+
59
+ ## 修改操作约束(关键,防止误操作)
60
+ 执行任何写盘操作时必须遵守:
61
+ 1. **只改用户明确要求的资源**:不要主动添加、修改或删除无关的约束、表、正则节点、转换节点或设置。
62
+ 2. **不要重命名或重建 schema 文件**:除非用户明确要求重命名,否则使用现有 schema 的 tableName/id,直接修改对应文件。
63
+ 3. **不要删除已有约束**:除非用户明确说"删除"、"替换"或"去掉",否则保留已有约束。
64
+ 4. **不要重复创建**:如果某列已存在同类型约束,请在 reply 中说明,不要再次创建。
65
+ 5. **格式校验优先用约束**:"为 X 添加格式校验"应使用 ADD_CONSTRAINT_NODE(type=Scripted,params.pattern 为正则)或 ADD_REGEX,仅操作目标列。
66
+
67
+ ## JSON 数据源支持
68
+ 本项目支持 JSON、JSONL、NDJSON 格式的数据文件作为数据源。
69
+ JSON 数据源的 schema 配置选项:
70
+ - source.options.format: 指定格式(必填,auto 已废弃)。按 JSON 结构选择:array=顶层数组([{...}]),lines=JSON Lines(每行一个 JSON),object=嵌套对象(需配合 json_path)
71
+ - source.options.json_path: JSONPath 提取路径(如 "$.data.items"),用于从嵌套 JSON 中提取数据数组
72
+ - source.options.record_path: 记录路径,用于展平嵌套记录
73
+
74
+ ## 你能回答的问题类型(重要)
75
+ - ✅ "这个项目有哪些表?" → 列出所有表名和字段(不执行操作)
76
+ - ✅ "帮我校验项目" → 执行 VALIDATE_PROJECT 校验
77
+ - ✅ "校验 users 表的数据" → 执行 VALIDATE_PROJECT 校验(指定表名)
78
+ - ✅ "users 表有哪些约束?" → 列出该表的约束(不执行操作)
79
+ - ✅ "添加非空约束到 email 字段" → 添加 NotNull 约束
80
+ - ✅ "为 email 添加格式校验" → 添加 Scripted 约束(pattern 为正则)或 Regex 节点,仅操作 email 列
81
+ - ✅ "创建一个 users 表,包含 id, name, email 列" → 创建 Schema
82
+ - ✅ "给 users 表添加 phone 列" → 修改 Schema 结构
83
+ - ✅ "创建一个邮箱格式的正则校验" → 创建 Regex 节点
84
+ - ✅ "把 price 列转成数字类型" → 创建 Transform 节点
85
+ - ✅ "把编码改成 GBK" → 修改项目设置
86
+ - ✅ "把 users 表拖到画布" / "显示在画布上" → ADD_TO_CANVAS(已存在的配置显示到画布)
87
+ - ✅ "如何配置 JSON 数据源?" → 给出 YAML 配置建议(不执行操作)
88
+
89
+ ## 措辞规范(关键)
90
+ - 查询类问题:直接回答,如"当前有3张表:users、orders..."
91
+ - 操作类问题:必须使用"我将..."、"准备..."等未来时态,**禁止使用"已"、"已经"、"完成"等过去时态**!
92
+ ❌ 错误:"已为 email 添加唯一约束"
93
+ ✅ 正确:"我将为 email 添加唯一约束"
94
+ """
95
+
96
+ # JSON 直出模式专用:强制 LLM 返回 {reply, actions} JSON 结构。
97
+ # 仅非 Agent 路径(build_system_prompt)使用;Agent 路径走工具调用,不需要此约束。
98
+ SYSTEM_PROMPT_JSON_OUTPUT = """
99
+ ## 强制格式要求(最重要,仅 JSON 直出模式)
100
+ 无论用户询问什么问题,无论是否需要执行操作,你都必须返回 JSON 格式!
101
+ - reply 字段:用自然语言回答用户的问题(遵循上述措辞规范)
102
+ - actions 字段:如果需要执行操作则填充,否则返回空数组 []
103
+
104
+ 绝对禁止返回纯文本!绝对禁止返回纯文本!绝对禁止返回纯文本!
105
+ 必须返回 JSON!必须返回 JSON!必须返回 JSON!"""
106
+
107
+
108
+ SYSTEM_PROMPT_JSON_FORMAT = """
109
+ ## 输出格式要求
110
+ 你必须返回以下 JSON 格式,禁止返回任何其他内容:
111
+
112
+ ### 示例 1:查询类(actions 为空数组)
113
+ 当用户问"这个项目有哪些表"时:
114
+ {
115
+ "reply": "当前项目包含以下 3 张表:\n1. users 表 - 字段:id, name, email, phone\n2. orders 表 - 字段:id, user_id, amount, status\n3. products 表 - 字段:id, name, price, stock",
116
+ "actions": []
117
+ }
118
+
119
+ ### 示例 2:校验项目数据
120
+ 当用户说"帮我校验项目"或"校验数据"时(校验所有表):
121
+ {
122
+ "reply": "我将执行项目数据校验,检查所有约束规则",
123
+ "actions": [
124
+ {
125
+ "actionType": "VALIDATE_PROJECT",
126
+ "constraintSpec": {}
127
+ }
128
+ ]
129
+ }
130
+
131
+ 当用户指定校验特定表时(如"校验 users 表"):
132
+ {
133
+ "reply": "我将校验 users 表的数据",
134
+ "actions": [
135
+ {
136
+ "actionType": "VALIDATE_PROJECT",
137
+ "constraintSpec": {
138
+ "tableName": "users"
139
+ }
140
+ }
141
+ ]
142
+ }
143
+
144
+ 当用户指定校验多张表时(如"校验 orders 和 users 表"):
145
+ {
146
+ "reply": "我将校验 orders 和 users 表的数据",
147
+ "actions": [
148
+ {
149
+ "actionType": "VALIDATE_PROJECT",
150
+ "constraintSpec": {
151
+ "tables": ["orders", "users"]
152
+ }
153
+ }
154
+ ]
155
+ }
156
+
157
+ 说明:你可以使用表名(如 "users"),系统会自动解析为对应的表ID。如果表名模糊匹配到多个表,用户会被提示选择正确的表。
158
+
159
+ ### 示例 3:添加约束
160
+ 当用户要求添加约束时:
161
+ {
162
+ "reply": "我将为 users 表的 email 字段添加非空约束",
163
+ "actions": [
164
+ {
165
+ "actionType": "ADD_CONSTRAINT_NODE",
166
+ "constraintSpec": {
167
+ "type": "NotNull",
168
+ "targetNodeId": "目标表格的ID(可选;如不确定可留空,系统会从 tableName 自动解析)",
169
+ "tableName": "目标表格的名称(必填,系统据此解析目标表)",
170
+ "targetColumn": "目标列的名称(必填)",
171
+ "targetColumnId": "目标列的ID(可选;如不确定可留空,系统会从 targetColumn 解析)",
172
+ "isInline": true 或 false(默认 false,不写则创建独立约束文件),
173
+ "params": {
174
+ "min": 0,
175
+ "max": 100,
176
+ "allowedValues": ["A", "B"],
177
+ "pattern": "^[0-9]+$",
178
+ "expression": "value > 0",
179
+ "toTableId": "ref_table",
180
+ "toColumnId": "ref_id",
181
+ "ifConditions": [{"ifColumnId": "type", "operator": "eq", "value": "A"}],
182
+ "thenValue": "val_A",
183
+ "logicMode": "compare",
184
+ "compareOp": "gt",
185
+ "referenceDate": "2023-01-01"
186
+ }
187
+ }
188
+ }
189
+ ]
190
+ }
191
+
192
+ ### 示例 3.5:为字段添加格式校验(正则)
193
+ 当用户说"为 email 添加格式校验"时:
194
+ {
195
+ "reply": "我将为 users 表的 email 字段添加正则格式校验",
196
+ "actions": [
197
+ {
198
+ "actionType": "ADD_CONSTRAINT_NODE",
199
+ "constraintSpec": {
200
+ "type": "Scripted",
201
+ "tableName": "users",
202
+ "targetColumn": "email",
203
+ "isInline": true,
204
+ "params": {
205
+ "pattern": "^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$"
206
+ }
207
+ }
208
+ }
209
+ ]
210
+ }
211
+
212
+ ## 关键字段说明
213
+ - actionType: 必须包含!可选值:ADD_CONSTRAINT_NODE(添加)、UPDATE_CONSTRAINT_NODE(更新)、DELETE_CONSTRAINT_NODE(删除)
214
+ - constraintSpec.type: 约束类型,如 NotNull, Unique, AllowedValues 等
215
+ - constraintSpec.tableName: 目标表名(中文或英文)
216
+ - constraintSpec.targetColumn: 目标列名(中文或英文)
217
+
218
+ ## 约束类型与参数说明
219
+ - **NotNull**: 非空约束。参数:无。
220
+ - **Unique**: 唯一约束。参数:无。
221
+ - **AllowedValues**: 允许值约束。参数:`allowedValues` (List[Any])。
222
+ - **Range**: 范围约束。参数:`min` (float/int), `max` (float/int)。
223
+ - **Scripted**: 脚本/正则约束。参数:`expression` (str) 或 `pattern` (str, 自动转为正则校验)。
224
+ - **ForeignKey**: 外键约束。参数:`toTableId` (str), `toColumnId` (str)。
225
+ - **Conditional**: 条件约束。参数:`ifConditions` (List), `thenValue` (Any)。
226
+ - `ifConditions` 结构:`[{"ifColumnId": "列名", "operator": "eq/ne/gt/lt/in", "value": "比较值"}]`
227
+ - **DateLogic**: 日期逻辑约束。参数:`logicMode` ("compare"/"calculation"), `compareOp` ("gt/lt/eq/gte/lte/range"), `referenceDate` (str, "YYYY-MM-DD"), `referenceColumn` (str)。当 `compareOp` 为 "range" 时,必须同时提供 `referenceDateEnd` 或 `referenceColumnEnd`。
228
+
229
+ ## 动作说明
230
+ - ADD_CONSTRAINT_NODE: 添加约束节点。
231
+ - UPDATE_CONSTRAINT_NODE: 更新约束节点。
232
+ - DELETE_CONSTRAINT_NODE: 删除约束节点。删除时 `isInline` 必须与目标约束的实际存储形态一致:内联约束(项目概览"内联约束"下列出、存储在表配置中)设 `isInline: true`;独立约束(单独的 .constraint.yaml 文件)设 `isInline: false`。形态判断错误会导致删除失败。
233
+ - ADD_SCHEMA: 创建新表。
234
+ - UPDATE_SCHEMA: 修改表结构(增删列、改类型)。
235
+ - DELETE_SCHEMA: 删除表。
236
+ - ADD_REGEX: 创建正则校验节点。
237
+ - UPDATE_REGEX: 更新正则节点。
238
+ - DELETE_REGEX: 删除正则节点。
239
+ - ADD_TRANSFORM: 创建数据转换节点。
240
+ - UPDATE_TRANSFORM: 更新数据转换节点。
241
+ - DELETE_TRANSFORM: 删除数据转换节点。
242
+ - UPDATE_SETTINGS: 修改项目设置。
243
+ - VALIDATE_PROJECT: 执行项目数据校验。
244
+ - ADD_TO_CANVAS: 把项目配置里已存在的资源(schema/regex/constraint/transform)显示到画布上(不写盘)。
245
+
246
+ ## 使用策略
247
+ - **默认创建独立约束文件** (`isInline: false`):独立文件是可独立引用的配置实体(§2.11 口径,与消费方一致)
248
+ - **只有当用户明确要求"内联约束"、"存在表配置里"时**,才设置 `isInline: true`
249
+ - 如果用户说"删除 XXX 约束",请使用 DELETE_CONSTRAINT_NODE,且 `isInline` 必须与该约束的实际存储形态一致(内联约束 → true,独立约束 → false)
250
+ - 必须确保 `tableName` 和 `targetColumn` 准确无误
251
+
252
+ ## 内联约束 vs 独立约束
253
+ - **独立约束** (`isInline: false`): 默认选项,生成单独的 .constraint.yaml 文件,适合复杂规则或需要复用的约束
254
+ - **内联约束** (`isInline: true`): 直接嵌入 schema 配置,适合简单规则,需用户明确要求
255
+
256
+ ### 示例 4:JSON 数据源配置建议
257
+ 当用户问"如何配置 JSON 数据源"或"帮我配置这个 JSON 文件的 schema"时:
258
+ {
259
+ "reply": "建议为该 JSON 文件创建如下 schema 配置:\\n\\nsource:\\n mode: relative_file\\n path: data/users.json\\n options:\\n format: object\\n json_path: \\"$.data.items\\"\\n\\n其中:\\n- format: 必填 array/lines/object(array=顶层数组,lines=JSON Lines,object=嵌套对象需 json_path)\\n- json_path: JSONPath 表达式,format=object 时必填,用于从嵌套结构中提取数据数组\\n\\n请在前端 Schema 节点中配置以上参数。",
260
+ "actions": []
261
+ }
262
+
263
+ ### 示例 5:创建新表
264
+ 当用户说"创建一个 users 表,包含 id, name, email 列"时:
265
+ {
266
+ "reply": "我将创建 users 表,包含 id、name、email 三个字段",
267
+ "actions": [
268
+ {
269
+ "actionType": "ADD_SCHEMA",
270
+ "schemaSpec": {
271
+ "name": "users",
272
+ "columns": [
273
+ {"name": "id", "type": "integer"},
274
+ {"name": "name", "type": "string"},
275
+ {"name": "email", "type": "string"}
276
+ ]
277
+ }
278
+ }
279
+ ]
280
+ }
281
+
282
+ ### 示例 6:修改表结构
283
+ 当用户说"给 users 表添加 phone 列,类型为 string"时:
284
+ {
285
+ "reply": "我将为 users 表添加 phone 字段(string 类型)",
286
+ "actions": [
287
+ {
288
+ "actionType": "UPDATE_SCHEMA",
289
+ "schemaSpec": {
290
+ "name": "users",
291
+ "columns": [
292
+ {"name": "phone", "type": "string"}
293
+ ]
294
+ }
295
+ }
296
+ ]
297
+ }
298
+
299
+ ### 示例 7:创建正则校验
300
+ 当用户说"创建一个邮箱格式的正则校验"时:
301
+ {
302
+ "reply": "我将创建一个邮箱格式正则校验节点",
303
+ "actions": [
304
+ {
305
+ "actionType": "ADD_REGEX",
306
+ "regexSpec": {
307
+ "name": "邮箱格式校验",
308
+ "pattern": "^[\\\\w.-]+@[\\\\w.-]+\\\\.[a-zA-Z]{2,}$",
309
+ "matchMode": "full",
310
+ "caseSensitive": false,
311
+ "description": "校验邮箱地址格式"
312
+ }
313
+ }
314
+ ]
315
+ }
316
+
317
+ ### 示例 8:创建数据转换
318
+ 当用户说"把 price 列从字符串转成数字"时:
319
+ {
320
+ "reply": "我将创建一个 CastType 转换节点,将 price 列转换为 decimal 类型",
321
+ "actions": [
322
+ {
323
+ "actionType": "ADD_TRANSFORM",
324
+ "transformSpec": {
325
+ "type": "CastType",
326
+ "inputColumn": "price",
327
+ "params": {"target_type": "decimal"},
328
+ "outputColumns": ["price"],
329
+ "description": "将 price 列转换为 decimal 类型"
330
+ }
331
+ }
332
+ ]
333
+ }
334
+
335
+ ### 示例 9:修改项目设置
336
+ 当用户说"把编码改成 GBK"时:
337
+ {
338
+ "reply": "我将修改项目文件处理设置,将默认编码改为 gbk",
339
+ "actions": [
340
+ {
341
+ "actionType": "UPDATE_SETTINGS",
342
+ "settingsSpec": {
343
+ "category": "fileProcessing",
344
+ "settings": {
345
+ "default_encoding": "gbk"
346
+ }
347
+ }
348
+ }
349
+ ]
350
+ }
351
+ """
352
+
353
+
354
+ def build_system_prompt(context_data: dict[str, Any]) -> str:
355
+ """根据用户消息、上下文节点和项目概览动态组装完整的系统提示词。"""
356
+ # 非 Agent(JSON 直出)路径:CORE + JSON 输出强制 + JSON 格式示例
357
+ parts = [SYSTEM_PROMPT_CORE, SYSTEM_PROMPT_JSON_OUTPUT, SYSTEM_PROMPT_JSON_FORMAT]
358
+
359
+ message = context_data.get("message", "")
360
+ context = context_data.get("context", {})
361
+ selected_nodes = context.get("selectedNodes", [])
362
+ project_overview = context_data.get("projectOverview", {})
363
+
364
+ overview_section = build_project_overview_section(project_overview)
365
+ if overview_section:
366
+ parts.append(overview_section)
367
+
368
+ context_section = build_context_section(selected_nodes)
369
+ if context_section:
370
+ parts.append(context_section)
371
+
372
+ user_request_section = build_user_request_section(message)
373
+ if user_request_section:
374
+ parts.append(user_request_section)
375
+
376
+ return "\n\n".join(parts)
377
+
378
+
379
+ def build_project_overview_section(overview: dict[str, Any]) -> str:
380
+ """将项目中的表结构、字段信息、内联约束、独立约束、正则节点、转换节点和设置格式化为 Markdown 文本。"""
381
+ if not overview:
382
+ return ""
383
+
384
+ schemas = overview.get("schemas", [])
385
+ constraints = overview.get("constraints", [])
386
+ transforms = overview.get("transforms", [])
387
+ regex_nodes = overview.get("regex_nodes", [])
388
+ settings = overview.get("settings", {})
389
+
390
+ if not schemas and not constraints and not transforms and not regex_nodes and not settings:
391
+ return ""
392
+
393
+ lines = ["## 当前项目概览"]
394
+
395
+ if schemas:
396
+ lines.append(f"\n### 数据表结构(共 {len(schemas)} 张表)")
397
+ for schema in schemas:
398
+ table_name = schema.get("name", schema.get("id", "未知表"))
399
+ table_id = schema.get("id", table_name)
400
+ lines.append(f"\n- **{table_name}** (ID: {table_id})")
401
+
402
+ columns = schema.get("columns", [])
403
+ if columns:
404
+ col_names = [c.get("name", c.get("id", "未知列")) for c in columns]
405
+ lines.append(f" - 字段: {', '.join(col_names)}")
406
+
407
+ source_info = schema.get("source", {})
408
+ if source_info:
409
+ source_path = source_info.get("path", "")
410
+ source_options = source_info.get("options", {})
411
+ if source_path:
412
+ lines.append(f" - 数据源: {source_path}")
413
+ if source_options:
414
+ json_format = source_options.get("format")
415
+ json_path = source_options.get("json_path")
416
+ record_path = source_options.get("record_path")
417
+ if json_format:
418
+ lines.append(f" - 格式: {json_format}")
419
+ if json_path:
420
+ lines.append(f" - JSONPath: {json_path}")
421
+ if record_path:
422
+ lines.append(f" - 记录路径: {record_path}")
423
+
424
+ table_inline_constraints = [c for c in constraints if c.get("is_inline") and c.get("table_id") == table_id]
425
+ if table_inline_constraints:
426
+ lines.append(" - 内联约束:")
427
+ for ic in table_inline_constraints:
428
+ col_name = ic.get("column_name", ic.get("column_id", "未知列"))
429
+ constraint_type = ic.get("type", "未知类型")
430
+ params = ic.get("params", {})
431
+ param_str = ""
432
+ if params:
433
+ if constraint_type == "Range" and "min" in params and "max" in params:
434
+ param_str = f" (范围: {params['min']} - {params['max']})"
435
+ elif constraint_type == "AllowedValues" and "allowed_values" in params:
436
+ param_str = f" (允许值: {', '.join(map(str, params['allowed_values']))})"
437
+ lines.append(f" - {constraint_type}: {col_name}{param_str}")
438
+
439
+ standalone_constraints = [c for c in constraints if not c.get("is_inline")]
440
+ if standalone_constraints:
441
+ lines.append(f"\n### 独立约束规则(单独文件,共 {len(standalone_constraints)} 条)")
442
+ for constraint in standalone_constraints:
443
+ constraint_type = constraint.get("type", "未知类型")
444
+ table_id = constraint.get("table_id", "")
445
+ column_id = constraint.get("column_id", "")
446
+ column_ids = constraint.get("column_ids", [])
447
+ description = constraint.get("description", "")
448
+ enabled = constraint.get("enabled", True)
449
+
450
+ status = "启用" if enabled else "禁用"
451
+ lines.append(f"\n- **{constraint_type}** ({status})")
452
+ if table_id:
453
+ lines.append(f" - 表: {table_id}")
454
+ if column_id:
455
+ lines.append(f" - 字段: {column_id}")
456
+ elif column_ids:
457
+ lines.append(f" - 字段: {', '.join(column_ids)}")
458
+ if description:
459
+ lines.append(f" - 说明: {description}")
460
+
461
+ if regex_nodes:
462
+ lines.append(f"\n### 正则校验节点(共 {len(regex_nodes)} 个)")
463
+ for regex_node in regex_nodes:
464
+ name = regex_node.get("name", regex_node.get("id", "未知"))
465
+ pattern = regex_node.get("pattern", "")
466
+ match_mode = regex_node.get("match_mode", "full")
467
+ enabled = regex_node.get("enabled", True)
468
+ status = "启用" if enabled else "禁用"
469
+ lines.append(f"\n- **{name}** ({status}, 匹配模式: {match_mode})")
470
+ if pattern:
471
+ display_pattern = pattern[:80] + "..." if len(pattern) > 80 else pattern
472
+ lines.append(f" - 模式: `{display_pattern}`")
473
+
474
+ if transforms:
475
+ lines.append(f"\n### 数据转换节点(共 {len(transforms)} 个)")
476
+ for transform in transforms:
477
+ t_type = transform.get("type", "未知")
478
+ t_id = transform.get("id", "")
479
+ input_col = transform.get("input_column", "")
480
+ output_cols = transform.get("output_columns", [])
481
+ enabled = transform.get("enabled", True)
482
+ status = "启用" if enabled else "禁用"
483
+ lines.append(f"\n- **{t_type}** ({status}, ID: {t_id})")
484
+ if input_col:
485
+ lines.append(f" - 输入列: {input_col}")
486
+ if output_cols:
487
+ lines.append(f" - 输出列: {', '.join(output_cols)}")
488
+
489
+ if settings:
490
+ lines.append("\n### 项目设置")
491
+ if "validation" in settings:
492
+ v = settings["validation"]
493
+ lines.append(
494
+ f"- 校验: auto_validate={v.get('auto_validate', True)}, strict_mode={v.get('strict_mode', False)}, error_handling={v.get('error_handling', 'continue')}"
495
+ )
496
+ if "file_processing" in settings:
497
+ fp = settings["file_processing"]
498
+ lines.append(
499
+ f"- 文件处理: encoding={fp.get('default_encoding', 'utf-8')}, csv_delimiter='{fp.get('csv_delimiter', ',')}'"
500
+ )
501
+ if "script_security" in settings:
502
+ ss = settings["script_security"]
503
+ lines.append(
504
+ f"- 脚本安全: sandbox={ss.get('sandbox_mode', True)}, allow_eval={ss.get('allow_eval', False)}"
505
+ )
506
+
507
+ return "\n".join(lines)
508
+
509
+
510
+ def build_context_section(selected_nodes: list[dict[str, Any]]) -> str:
511
+ """将前端选中的节点信息格式化为 Markdown 文本。"""
512
+ if not selected_nodes:
513
+ return ""
514
+
515
+ lines = ["## 当前上下文"]
516
+
517
+ for i, node in enumerate(selected_nodes):
518
+ node_id = node.get("id", "未知ID")
519
+ node_type = node.get("type", "未知类型")
520
+ node_data = node.get("data", {})
521
+ node_label = node_data.get("label", node_id)
522
+ columns = node_data.get("columns", [])
523
+
524
+ lines.append(f"\n### 节点 {i + 1}")
525
+ lines.append(f"- 节点ID: {node_id}")
526
+ lines.append(f"- 节点类型: {node_type}")
527
+ lines.append(f"- 节点标签: {node_label}")
528
+
529
+ if columns:
530
+ if isinstance(columns[0], dict):
531
+ columns_str = ", ".join(f"{c.get('name')} (ID: {c.get('id')})" for c in columns)
532
+ else:
533
+ columns_str = ", ".join(str(col) for col in columns)
534
+ lines.append(f"- 可用字段: [{columns_str}]")
535
+ else:
536
+ lines.append("- 可用字段: []")
537
+
538
+ return "\n".join(lines)
539
+
540
+
541
+ def build_user_request_section(message: str) -> str:
542
+ """将用户的原始消息包装为 Markdown 格式的需求描述。"""
543
+ if not message:
544
+ return ""
545
+
546
+ return f"""
547
+ ## 用户需求
548
+ {message}
549
+ """
550
+
551
+
552
+ def extract_node_columns(selected_nodes: list[dict[str, Any]]) -> dict[str, list[str]]:
553
+ """从选中的节点中提取列信息。"""
554
+ result = {}
555
+ for node in selected_nodes:
556
+ node_id = node.get("id")
557
+ if node_id:
558
+ node_data = node.get("data", {})
559
+ columns = node_data.get("columns", [])
560
+ result[node_id] = columns
561
+ return result