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,411 @@
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
+ - 提供项目全量配置的读写接口,支持导出、导入与 AI 生成配置写入
21
+ - 支持 YAML 格式导出
22
+ - 提供配置对比能力(ConfigDiffService)
23
+
24
+ 架构设计:
25
+ - 读取时通过 compute_manifest_coverage 扫描目录补充缺失引用
26
+ - 写入时采用合并策略:保留现有 manifest 中未覆盖的引用
27
+ - 支持 schema ID 自动迁移(根据 source.path 生成标准 ID)
28
+
29
+ 输入示例:
30
+ GET /config/full
31
+ PUT /config/full (body: FullConfigV2Request)
32
+ POST /config/compare (body: FullConfigV2Request)
33
+
34
+ 输出示例:
35
+ dict: 包含 manifest、effective_manifest、schemas、constraints、regex_nodes 等
36
+ ConfigDiffResult: 配置差异详情
37
+ """
38
+
39
+ import logging
40
+ import os
41
+ from datetime import UTC
42
+ from pathlib import Path
43
+ from typing import Any
44
+
45
+ from fastapi import APIRouter, Depends
46
+
47
+ from app.api.dependencies import get_project_config_path
48
+ from app.shared.core.io.yaml import read_yaml
49
+ from app.shared.core.project.loader.loader_parts.config_inspector import inspect_config
50
+ from app.shared.core.project.loader.types import LoadingError
51
+ from app.shared.core.project.manifest.coverage import compute_manifest_coverage, coverage_to_api_dict
52
+ from app.shared.core.project.manual_data.types import ManualDataFileV2
53
+ from app.shared.core.project.regex.types import RegexNodeFileV2
54
+ from app.shared.core.project.template.reader import load_template
55
+ from app.shared.core.project.transform.types import TransformFileV2
56
+ from app.shared.services.diff.config_diff import ConfigDiffResult, ConfigDiffService
57
+
58
+ from .base import (
59
+ ConstraintFileV2,
60
+ ConstraintRefV2,
61
+ FullConfigV2Request,
62
+ RegexNodeRefV2,
63
+ SchemaRefV2,
64
+ StandardResponse,
65
+ TableSchemaFileV2,
66
+ )
67
+ from .full_config_writer import write_v2_full_config
68
+ from .helpers import _resolve_project_path
69
+ from .manifest import get_v2_manifest
70
+
71
+ logger = logging.getLogger(__name__)
72
+
73
+ router = APIRouter(prefix="", tags=["Project-FullConfig"])
74
+
75
+
76
+ @router.get(
77
+ "/config/full",
78
+ response_model=dict[str, Any],
79
+ summary="读取 V2 全量配置",
80
+ responses={
81
+ 404: {"description": "Manifest 不存在"},
82
+ 500: {"description": "服务器内部错误"},
83
+ },
84
+ )
85
+ def get_v2_full_config(
86
+ config_path: str = Depends(get_project_config_path),
87
+ inspect: bool = False,
88
+ ) -> dict[str, Any]:
89
+ """
90
+ 读取 V2 全量配置:manifest + 所有 schemas/constraints/regex_nodes 内容。
91
+
92
+ 使用场景:
93
+ - 前端导出整个项目配置
94
+ - AI 配置生成前获取现有配置
95
+ - 项目备份/迁移
96
+
97
+ 副作用:
98
+ - 如果 manifest 中引用的文件不存在,记录警告但继续处理
99
+
100
+ 查询参数:
101
+ - inspect: 是否执行配置文件格式自检(默认 false,避免重复调用)
102
+
103
+ 数据流(分三步):
104
+ Step 1: 获取/补充 manifest
105
+ - 读取 manifest(优先)
106
+ - 扫描目录补充缺失的引用
107
+
108
+ Step 2: 读取所有资源文件
109
+ - 遍历 manifest 中的引用
110
+ - 读取每个 schema/constraint/regex 文件
111
+
112
+ Step 3: 返回聚合结果
113
+ - 组合 manifest + schemas + constraints + regex_nodes
114
+
115
+ 参数:
116
+ config_path: 项目配置根目录(通过 Depends 注入)
117
+ inspect: 是否执行配置自检(通过查询参数注入)
118
+
119
+ 返回:
120
+ dict: 包含 manifest、schemas、constraints、regex_nodes 的字典
121
+ """
122
+ manifest = get_v2_manifest(config_path)
123
+ coverage_model = compute_manifest_coverage(config_path, manifest)
124
+ coverage = coverage_to_api_dict(coverage_model)
125
+ manifest_modified = bool(
126
+ coverage_model.unlisted_schemas or coverage_model.unlisted_constraints or coverage_model.unlisted_regex_nodes
127
+ )
128
+
129
+ effective_manifest = manifest.model_copy(
130
+ update={
131
+ "schemas": [
132
+ *manifest.schemas,
133
+ *[SchemaRefV2(id=r.id, path=r.path) for r in coverage_model.unlisted_schemas],
134
+ ],
135
+ "constraints": [
136
+ *manifest.constraints,
137
+ *[ConstraintRefV2(id=r.id, path=r.path) for r in coverage_model.unlisted_constraints],
138
+ ],
139
+ "regex_nodes": [
140
+ *manifest.regex_nodes,
141
+ *[RegexNodeRefV2(id=r.id, path=r.path) for r in coverage_model.unlisted_regex_nodes],
142
+ ],
143
+ }
144
+ )
145
+
146
+ # 读取所有 Schema 文件
147
+ schemas: dict[str, Any] = {}
148
+ schema_objects: dict[str, TableSchemaFileV2] = {} # 保留对象用于自检
149
+ schema_errors: dict[str, str] = {}
150
+ for ref in effective_manifest.schemas:
151
+ try:
152
+ schema_path = _resolve_project_path(config_path, ref.path)
153
+ except ValueError as e:
154
+ schema_errors[ref.id] = str(e)
155
+ continue
156
+ if os.path.isfile(schema_path):
157
+ try:
158
+ schema_obj = TableSchemaFileV2.model_validate(read_yaml(Path(schema_path)))
159
+ # 同 id 冲突时仅保留首个,记录 warning 日志。
160
+ # 注意:不写入 schema_errors —— schema_errors 语义是「YAML 解析失败」,
161
+ # 用于资源树标记 + toast 提示;ID 冲突应由 inspect 的 SchemaIdDuplicate
162
+ # blocker 统一上报(避免同一问题触发 toast + 徽章双重提示)。
163
+ if ref.id in schemas:
164
+ logger.warning(
165
+ f"[get_v2_full_config] Schema ID 冲突: '{ref.id}' 已存在,"
166
+ f"文件 {ref.path} 的数据未被加载(已存在: {schemas[ref.id].get('source', {}).get('path', '?')})"
167
+ )
168
+ else:
169
+ schema_objects[ref.id] = schema_obj
170
+ schemas[ref.id] = schema_obj.model_dump(exclude_none=True)
171
+ except Exception as e:
172
+ error_msg = str(e)
173
+ logger.error(f"[get_v2_full_config] 解析 Schema 文件失败: {schema_path}, 错误: {error_msg}")
174
+ schema_errors[ref.id] = error_msg
175
+ else:
176
+ logger.warning(f"[get_v2_full_config] Schema 文件不存在: {schema_path}")
177
+ schema_errors[ref.id] = f"Schema 文件不存在: {ref.path}"
178
+
179
+ # 读取所有 Constraint 文件
180
+ constraints: dict[str, Any] = {}
181
+ constraint_objects: dict[str, ConstraintFileV2] = {} # 保留对象用于自检
182
+ for constraint_ref in effective_manifest.constraints:
183
+ try:
184
+ constraint_path = _resolve_project_path(config_path, constraint_ref.path)
185
+ except ValueError as e:
186
+ logger.error(f"[get_v2_full_config] 非法 Constraint 路径: {constraint_ref.path}, 错误: {e}")
187
+ continue
188
+ if os.path.isfile(constraint_path):
189
+ try:
190
+ constraint_obj = ConstraintFileV2.model_validate(read_yaml(Path(constraint_path)))
191
+ constraint_objects[constraint_ref.id] = constraint_obj
192
+ constraints[constraint_ref.id] = constraint_obj.model_dump(exclude_none=True)
193
+ except Exception as e:
194
+ logger.error(f"[get_v2_full_config] 解析 Constraint 文件失败: {constraint_path}, 错误: {e}")
195
+ else:
196
+ logger.warning(f"[get_v2_full_config] Constraint 文件不存在: {constraint_path}")
197
+
198
+ # 扫描正则表达式注册表目录(patterns/)
199
+ # 这些目录存储可复用的正则表达式模式定义,供节点引用
200
+ regex_registries: dict[str, Any] = {}
201
+ patterns_dir = os.path.join(config_path, manifest.patterns_dir or "patterns")
202
+
203
+ if os.path.isdir(patterns_dir):
204
+ for filename in os.listdir(patterns_dir):
205
+ if filename.endswith(".yaml"):
206
+ pattern_id = filename[:-5]
207
+ file_path = os.path.join(patterns_dir, filename)
208
+ try:
209
+ raw = read_yaml(Path(file_path))
210
+ regex_registries[f"patterns/{pattern_id}"] = {
211
+ "id": pattern_id,
212
+ "registry": "patterns",
213
+ "definition": raw,
214
+ }
215
+ except Exception as e:
216
+ logger.warning(f"[get_v2_full_config] 解析正则注册表失败: {file_path}, 错误: {e}")
217
+
218
+ # 读取正则表达式节点
219
+ # 优先从 manifest.regex_nodes 读取,否则扫描 regex/ 目录
220
+ regex_nodes: dict[str, Any] = {}
221
+ regex_objects: dict[str, RegexNodeFileV2] = {} # 保留对象用于自检
222
+
223
+ for regex_ref in effective_manifest.regex_nodes:
224
+ # B-reliability: 单条坏引用(手改 YAML/AI 写入/旧项目迁移)不得让整个接口 500
225
+ # (前端 bootstrapProjectPaths 会失败,项目打不开)。与 schemas/constraints 的容错对齐。
226
+ try:
227
+ abs_path = _resolve_project_path(config_path, regex_ref.path)
228
+ except ValueError as e:
229
+ logger.warning(
230
+ f"[get_v2_full_config] Regex 引用路径非法,已跳过: id={regex_ref.id}, path={regex_ref.path}, 错误: {e}"
231
+ )
232
+ continue
233
+ if os.path.isfile(abs_path):
234
+ try:
235
+ regex_obj = RegexNodeFileV2.model_validate(read_yaml(Path(abs_path)))
236
+ regex_objects[regex_ref.id] = regex_obj
237
+ regex_nodes[regex_ref.id] = regex_obj.model_dump(exclude_none=True)
238
+ except Exception as e:
239
+ logger.error(f"[get_v2_full_config] 解析 Regex 文件失败: {abs_path}, 错误: {e}")
240
+ else:
241
+ logger.warning(f"[get_v2_full_config] Regex 文件不存在: {abs_path}")
242
+
243
+ # 读取 Transform 节点
244
+ transforms: dict[str, Any] = {}
245
+ transform_objects: dict[str, TransformFileV2] = {} # 保留对象用于自检
246
+
247
+ for transform_ref in effective_manifest.transforms or []:
248
+ # 同 Regex: 单条坏引用记日志跳过,不阻断整个接口(见上)
249
+ try:
250
+ abs_path = _resolve_project_path(config_path, transform_ref.path)
251
+ except ValueError as e:
252
+ logger.warning(
253
+ f"[get_v2_full_config] Transform 引用路径非法,已跳过: id={transform_ref.id}, path={transform_ref.path}, 错误: {e}"
254
+ )
255
+ continue
256
+ if os.path.isfile(abs_path):
257
+ try:
258
+ transform_obj = TransformFileV2.model_validate(read_yaml(Path(abs_path)))
259
+ transform_objects[transform_ref.id] = transform_obj
260
+ transforms[transform_ref.id] = transform_obj.model_dump(exclude_none=True)
261
+ except Exception as e:
262
+ logger.error(f"[get_v2_full_config] 解析 Transform 文件失败: {abs_path}, 错误: {e}")
263
+ else:
264
+ logger.warning(f"[get_v2_full_config] Transform 文件不存在: {abs_path}")
265
+
266
+ # 读取 ManualData 节点
267
+ manual_data: dict[str, Any] = {}
268
+ manual_data_objects: dict[str, ManualDataFileV2] = {} # 保留对象用于自检
269
+
270
+ for manual_data_ref in effective_manifest.manual_data or []:
271
+ # 同 Regex: 单条坏引用记日志跳过,不阻断整个接口(见上)
272
+ try:
273
+ abs_path = _resolve_project_path(config_path, manual_data_ref.path)
274
+ except ValueError as e:
275
+ logger.warning(
276
+ f"[get_v2_full_config] ManualData 引用路径非法,已跳过: id={manual_data_ref.id}, path={manual_data_ref.path}, 错误: {e}"
277
+ )
278
+ continue
279
+ if os.path.isfile(abs_path):
280
+ try:
281
+ manual_data_obj = ManualDataFileV2.model_validate(read_yaml(Path(abs_path)))
282
+ manual_data_objects[manual_data_ref.id] = manual_data_obj
283
+ manual_data[manual_data_ref.id] = manual_data_obj.model_dump(exclude_none=True)
284
+ except Exception as e:
285
+ logger.error(f"[get_v2_full_config] 解析 ManualData 文件失败: {abs_path}, 错误: {e}")
286
+ else:
287
+ logger.warning(f"[get_v2_full_config] ManualData 文件不存在: {abs_path}")
288
+
289
+ # 读取 Template 定义
290
+ # 前端资源树/检查器此前只能拿到模板 id(name 断链,节点标题显示 id),
291
+ # 补齐内容字典对齐 schemas/regex_nodes 等资源模式
292
+ templates: dict[str, Any] = {}
293
+ for template_ref in effective_manifest.templates or []:
294
+ # 同 Regex: 单条坏引用记日志跳过,不阻断整个接口(见上)
295
+ try:
296
+ abs_path = _resolve_project_path(config_path, template_ref.path)
297
+ except ValueError as e:
298
+ logger.warning(
299
+ f"[get_v2_full_config] Template 引用路径非法,已跳过: id={template_ref.id}, path={template_ref.path}, 错误: {e}"
300
+ )
301
+ continue
302
+ if os.path.isfile(abs_path):
303
+ try:
304
+ template_obj = load_template(Path(abs_path))
305
+ templates[template_ref.id] = template_obj.model_dump(exclude_none=True)
306
+ except Exception as e:
307
+ logger.error(f"[get_v2_full_config] 解析 Template 文件失败: {abs_path}, 错误: {e}")
308
+ else:
309
+ logger.warning(f"[get_v2_full_config] Template 文件不存在: {abs_path}")
310
+
311
+ result = {
312
+ "manifest": manifest.model_dump(exclude_none=True),
313
+ "effective_manifest": effective_manifest.model_dump(exclude_none=True),
314
+ "schemas": schemas,
315
+ "constraints": constraints,
316
+ "regex_registries": regex_registries,
317
+ "regex_nodes": regex_nodes,
318
+ "transforms": transforms,
319
+ "manual_data": manual_data,
320
+ "templates": templates,
321
+ "coverage": coverage,
322
+ "manifest_modified": manifest_modified,
323
+ "schema_errors": schema_errors,
324
+ }
325
+
326
+ # 仅当 inspect=true 时执行配置文件格式自检
327
+ if inspect:
328
+ from datetime import datetime
329
+
330
+ inspection_warnings: list[str] = []
331
+ inspection_errors: list[LoadingError] = []
332
+ inspect_config(
333
+ Path(config_path) / "project.precis.yaml",
334
+ effective_manifest,
335
+ schema_objects,
336
+ constraint_objects,
337
+ regex_objects,
338
+ transform_objects, # ⭐ 传入 transform 对象用于自检
339
+ manual_data_objects,
340
+ inspection_warnings,
341
+ inspection_errors,
342
+ )
343
+ result["inspection"] = {
344
+ "inspected_at": datetime.now(UTC).isoformat(),
345
+ "errors": [e.to_dict() for e in inspection_errors],
346
+ }
347
+
348
+ return result
349
+
350
+
351
+ @router.put(
352
+ "/config/full",
353
+ response_model=StandardResponse,
354
+ summary="写入 V2 全量配置",
355
+ responses={
356
+ 400: {"description": "请求参数错误"},
357
+ 404: {"description": "Manifest 不存在"},
358
+ 500: {"description": "服务器内部错误"},
359
+ },
360
+ )
361
+ def put_v2_full_config(
362
+ payload: FullConfigV2Request, config_path: str = Depends(get_project_config_path)
363
+ ) -> dict[str, str]:
364
+ """
365
+ @methoddesc 写入 V2 全量配置
366
+
367
+ 业务用途:
368
+ - 由前端"保存项目"或"导入配置"等流程调用
369
+ - 委托给 write_v2_full_config 执行实际写入,包含 manifest、所有 schemas/constraints/regex_nodes
370
+
371
+ 参数:
372
+ payload: 完整 V2 配置请求体
373
+ config_path: 项目配置路径(依赖注入)
374
+
375
+ 返回:
376
+ StandardResponse: 写入操作的统一响应
377
+ """
378
+ return write_v2_full_config(payload, config_path)
379
+
380
+
381
+ @router.post(
382
+ "/config/compare",
383
+ response_model=ConfigDiffResult,
384
+ summary="对比 V2 全量配置差异",
385
+ responses={
386
+ 400: {"description": "请求参数错误"},
387
+ 404: {"description": "Manifest 不存在"},
388
+ 500: {"description": "服务器内部错误"},
389
+ },
390
+ )
391
+ def compare_v2_full_config(
392
+ payload: FullConfigV2Request, config_path: str = Depends(get_project_config_path)
393
+ ) -> ConfigDiffResult:
394
+ """
395
+ 对比 V2 全量配置差异。
396
+
397
+ 使用场景:
398
+ - AI 生成完整项目配置后,与当前配置对比,供用户确认
399
+ - 项目导入前,预览变更内容
400
+
401
+ 参数:
402
+ payload: 包含 manifest 和所有资源配置的新配置
403
+ config_path: 项目配置根目录(通过 Depends 注入)
404
+
405
+ 返回:
406
+ ConfigDiffResult: 配置差异详情
407
+ """
408
+ current_config = get_v2_full_config(config_path)
409
+ # 将 Pydantic 模型转为 dict,与 get_v2_full_config 返回结构对齐
410
+ new_config = payload.model_dump(exclude_none=True)
411
+ return ConfigDiffService.compare(current_config, new_config)