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,345 @@
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 Constraint CRUD API - 数据约束管理
18
+
19
+ 功能概述:
20
+ - 提供 Constraint(数据约束规则)的增删改查接口
21
+ - 支持两层查找:优先从 manifest 读取引用,兼容扫描目录
22
+ - 包含展示名更新能力
23
+
24
+ 架构设计:
25
+ - 依赖 get_v2_manifest 定位资源路径
26
+ - 文件命名约定:{constraint_id}.constraint.yaml
27
+ - 写入时自动补齐 manifest 引用,保证索引一致性
28
+
29
+ 输入示例:
30
+ GET /constraints/unique_email
31
+ PUT /constraints/unique_email (body: ConstraintFileV2)
32
+
33
+ 输出示例:
34
+ ConstraintFileV2 模型实例或 StandardResponse 操作结果
35
+ """
36
+
37
+ import os
38
+ from pathlib import Path
39
+
40
+ from fastapi import APIRouter, Depends, HTTPException
41
+
42
+ from app.api.dependencies import get_project_config_path
43
+ from app.shared.core.io.yaml import read_yaml, write_yaml_atomic
44
+ from app.shared.core.project.manifest.types import ProjectManifestV2
45
+
46
+ from .base import (
47
+ ConstraintFileV2,
48
+ ConstraintRefV2,
49
+ DisplayNameUpdateRequest,
50
+ StandardResponse,
51
+ _scan_constraint_file,
52
+ _v2_manifest_path,
53
+ )
54
+ from .helpers import _resolve_project_path, project_lock
55
+ from .manifest import get_v2_manifest
56
+
57
+ router = APIRouter(prefix="", tags=["Project-Constraint"])
58
+
59
+
60
+ @router.get(
61
+ "/constraints/{constraint_id}",
62
+ response_model=ConstraintFileV2,
63
+ summary="读取 Constraint",
64
+ responses={
65
+ 404: {"description": "Constraint 不存在"},
66
+ 500: {"description": "服务器内部错误"},
67
+ },
68
+ )
69
+ def get_v2_constraint(constraint_id: str, config_path: str = Depends(get_project_config_path)) -> ConstraintFileV2:
70
+ """
71
+ 读取指定 constraint_id 的 constraint 文件。
72
+
73
+ 查找策略(两层查找,更健壮):
74
+ 1. 从 manifest.constraints 中查找引用(优先)
75
+ 2. 如果未找到,扫描 constraints/ 目录(兼容旧项目)
76
+
77
+ 副作用:
78
+ - 如果文件不存在,返回 404 错误
79
+
80
+ 数据流:
81
+ 1. 调用 get_v2_manifest 获取项目清单
82
+ 2. 在 manifest.constraints 中查找对应引用
83
+ 3. 如果找到,使用引用中的 path 构建文件路径
84
+ 4. 如果未找到,扫描 constraints/ 目录
85
+ 5. 读取并解析 YAML 文件
86
+ 6. 返回 Pydantic 模型
87
+
88
+ 参数:
89
+ constraint_id: Constraint 的唯一标识符
90
+ config_path: 项目配置根目录(通过 Depends 注入)
91
+
92
+ 返回:
93
+ ConstraintFileV2: Constraint 文件内容
94
+ """
95
+ manifest = get_v2_manifest(config_path)
96
+
97
+ # 查找策略优化:
98
+ # 1. 优先从 manifest 中查找所有匹配的引用(可能存在重复 ID 的情况)
99
+ # 2. 遍历所有引用,直到找到一个实际存在的文件
100
+ # 3. 如果 manifest 中没有有效引用,再扫描目录
101
+
102
+ constraint_path = None
103
+
104
+ # 1. 尝试从 manifest 中查找有效文件
105
+ refs = [c for c in manifest.constraints if c.id == constraint_id]
106
+ for ref in refs:
107
+ path = os.path.join(config_path, ref.path)
108
+ if os.path.isfile(path):
109
+ constraint_path = path
110
+ break
111
+
112
+ # 2. 如果 manifest 中未找到有效文件,尝试扫描目录
113
+ if not constraint_path:
114
+ constraints_dir = os.path.join(config_path, "constraints")
115
+ if os.path.isdir(constraints_dir):
116
+ for filename in os.listdir(constraints_dir):
117
+ if filename.lower().endswith(".constraint.yaml"):
118
+ cid = filename[:-16]
119
+ if cid == constraint_id:
120
+ path = os.path.join(constraints_dir, filename)
121
+ if os.path.isfile(path):
122
+ constraint_path = path
123
+ break
124
+
125
+ if not constraint_path:
126
+ # 如果 manifest 中有引用但文件都找不到,抛出更具体的错误
127
+ if refs:
128
+ raise HTTPException(
129
+ status_code=404,
130
+ detail=f"清单中登记了这条约束,但它的配置文件已不存在(ID: {constraint_id}),请删除这条约束后重建",
131
+ )
132
+ raise HTTPException(
133
+ status_code=404, detail=f"未找到这条约束:清单中没有登记,项目目录里也没有对应文件(ID: {constraint_id})"
134
+ )
135
+
136
+ if not os.path.isfile(constraint_path):
137
+ raise HTTPException(status_code=404, detail=f"constraint 文件未找到: {constraint_path}")
138
+
139
+ data = read_yaml(Path(constraint_path))
140
+ return ConstraintFileV2.model_validate(data)
141
+
142
+
143
+ @router.put(
144
+ "/constraints/{constraint_id}",
145
+ response_model=StandardResponse,
146
+ summary="写入 Constraint",
147
+ responses={
148
+ 400: {"description": "请求参数错误"},
149
+ 404: {"description": "Manifest 不存在"},
150
+ 500: {"description": "服务器内部错误"},
151
+ },
152
+ )
153
+ def put_v2_constraint(
154
+ constraint_id: str,
155
+ constraint: ConstraintFileV2,
156
+ config_path: str = Depends(get_project_config_path),
157
+ ) -> dict[str, str]:
158
+ """
159
+ 写入指定 constraint_id 的 constraint 文件。
160
+
161
+ 业务逻辑:
162
+ - 验证 constraint.id 与路径参数 constraint_id 一致
163
+ - 如果 manifest 未引用,自动补齐引用
164
+ - 同时更新 manifest 文件
165
+
166
+ 副作用:
167
+ - 创建或覆盖 constraint 文件
168
+ - 更新 manifest 中的引用列表
169
+
170
+ 数据流:
171
+ 1. 验证 constraint.id 与 constraint_id 一致
172
+ 2. 读取 manifest 文件
173
+ 3. 检查 manifest 是否已有该 constraint 的引用
174
+ 4. 如果没有,添加新的引用(默认路径:constraints/{constraint_id}.constraint.yaml)
175
+ 5. 写入 constraint 文件
176
+ 6. 更新 manifest 文件
177
+
178
+ 参数:
179
+ constraint_id: Constraint 的唯一标识符
180
+ constraint: Constraint 数据
181
+ config_path: 项目配置根目录(通过 Depends 注入)
182
+
183
+ 返回:
184
+ StandardResponse: 操作结果消息
185
+ """
186
+ if constraint.id != constraint_id:
187
+ raise HTTPException(status_code=400, detail="请求中的约束 ID 与地址栏不一致,请刷新页面后重试")
188
+ manifest_path = _v2_manifest_path(config_path)
189
+ if not os.path.isfile(manifest_path):
190
+ raise HTTPException(
191
+ status_code=404,
192
+ detail={
193
+ # §2.1: 结构化错误码统一(与 dependencies.py 的 manifest 缺失出口同构),
194
+ # 前端按 detail.code 识别项目失效并触发自愈
195
+ "code": "PROJECT_NOT_FOUND",
196
+ "message": f"项目配置文件(project.precis.yaml)不存在,请确认项目是否已被移动或删除(配置目录: {manifest_path})",
197
+ "path": str(
198
+ config_path
199
+ ), # §2.1: 项目根目录(前端与 activeProjectPath 同口径比对,勿传 manifest 文件路径)
200
+ },
201
+ )
202
+ manifest = ProjectManifestV2.model_validate(read_yaml(Path(manifest_path)))
203
+
204
+ ref = next((c for c in manifest.constraints if c.id == constraint_id), None)
205
+ if not ref:
206
+ default_rel = f"constraints/{constraint_id}.constraint.yaml"
207
+ manifest.constraints.append(ConstraintRefV2(id=constraint_id, path=default_rel))
208
+ ref_path = default_rel
209
+ else:
210
+ ref_path = ref.path
211
+
212
+ try:
213
+ abs_constraint_path = _resolve_project_path(config_path, ref_path)
214
+ except ValueError:
215
+ raise HTTPException(status_code=400, detail="非法的 Constraint 文件路径")
216
+ with project_lock(config_path):
217
+ write_yaml_atomic(Path(abs_constraint_path), constraint.model_dump(exclude_none=True))
218
+ write_yaml_atomic(Path(manifest_path), manifest.model_dump(exclude_none=True))
219
+ return {"message": f"V2 constraint '{constraint_id}' 已保存。"}
220
+
221
+
222
+ @router.delete(
223
+ "/constraints/{constraint_id}",
224
+ response_model=StandardResponse,
225
+ summary="删除 Constraint",
226
+ responses={
227
+ 400: {"description": "请求参数错误"},
228
+ 404: {"description": "Constraint 或 Manifest 不存在"},
229
+ 500: {"description": "服务器内部错误"},
230
+ },
231
+ )
232
+ def delete_v2_constraint(constraint_id: str, config_path: str = Depends(get_project_config_path)) -> dict[str, str]:
233
+ """
234
+ 删除指定 constraint_id 的 constraint 文件,并从 manifest 中移除引用。
235
+
236
+ 删除策略(两层查找,更健壮):
237
+ 1. 首先从 manifest.constraints 中查找引用(优先)
238
+ 2. 如果未找到,扫描 constraints/ 目录(兼容旧项目)
239
+
240
+ 副作用:
241
+ - 删除对应的 .constraint.yaml 文件
242
+ - 从 manifest.constraints 中移除引用(如果存在)
243
+
244
+ 数据流:
245
+ 1. 读取 manifest 文件
246
+ 2. 在 manifest.constraints 中查找引用
247
+ 3. 如果未找到,扫描 constraints/ 目录
248
+ 4. 删除找到的 constraint 文件
249
+ 5. 从 manifest 中移除引用
250
+ 6. 保存更新后的 manifest
251
+
252
+ 参数:
253
+ constraint_id: Constraint 的唯一标识符
254
+ config_path: 项目配置根目录(通过 Depends 注入)
255
+
256
+ 返回:
257
+ StandardResponse: 操作结果消息
258
+ """
259
+ manifest_path = _v2_manifest_path(config_path)
260
+ if not os.path.isfile(manifest_path):
261
+ raise HTTPException(
262
+ status_code=404,
263
+ detail={
264
+ # §2.1: 结构化错误码统一(与 dependencies.py 的 manifest 缺失出口同构),
265
+ # 前端按 detail.code 识别项目失效并触发自愈
266
+ "code": "PROJECT_NOT_FOUND",
267
+ "message": f"项目配置文件(project.precis.yaml)不存在,请确认项目是否已被移动或删除(配置目录: {manifest_path})",
268
+ "path": str(
269
+ config_path
270
+ ), # §2.1: 项目根目录(前端与 activeProjectPath 同口径比对,勿传 manifest 文件路径)
271
+ },
272
+ )
273
+ manifest = ProjectManifestV2.model_validate(read_yaml(Path(manifest_path)))
274
+
275
+ ref = next((c for c in manifest.constraints if c.id == constraint_id), None)
276
+ if not ref:
277
+ ref = _scan_constraint_file(constraint_id, config_path)
278
+
279
+ if not ref:
280
+ return {"message": f"未找到 constraint '{constraint_id}',无需删除。"}
281
+
282
+ try:
283
+ abs_constraint_path = _resolve_project_path(config_path, ref.path)
284
+ except ValueError:
285
+ raise HTTPException(status_code=400, detail="非法的 Constraint 文件路径")
286
+
287
+ with project_lock(config_path):
288
+ try:
289
+ if os.path.isfile(abs_constraint_path):
290
+ os.remove(abs_constraint_path)
291
+ except Exception as e:
292
+ raise HTTPException(status_code=500, detail=f"删除 constraint 文件失败: {e}")
293
+
294
+ # B-fix: 该引用原本存在于 manifest 中就写回(即使删除后列表为空)。
295
+ # 此前 `if manifest.constraints:` 在删除最后一个引用时为假而跳过写回,
296
+ # manifest 会留下指向已删除文件的悬空引用。
297
+ remaining_constraints = [c for c in manifest.constraints if c.id != constraint_id]
298
+ if len(remaining_constraints) != len(manifest.constraints):
299
+ manifest.constraints = remaining_constraints
300
+ write_yaml_atomic(Path(manifest_path), manifest.model_dump(exclude_none=True))
301
+
302
+ return {"message": f"V2 constraint '{constraint_id}' 已删除。"}
303
+
304
+
305
+ @router.post(
306
+ "/constraints/{constraint_id}/display-name",
307
+ response_model=StandardResponse,
308
+ summary="更新 Constraint 展示名",
309
+ responses={
310
+ 404: {"description": "Constraint 不存在"},
311
+ 500: {"description": "服务器内部错误"},
312
+ },
313
+ )
314
+ def update_v2_constraint_display_name(
315
+ constraint_id: str,
316
+ payload: DisplayNameUpdateRequest,
317
+ config_path: str = Depends(get_project_config_path),
318
+ ) -> dict[str, str]:
319
+ """
320
+ 更新 constraint 的展示名(不改变 constraint_id 与文件路径)。
321
+
322
+ 设计意图:
323
+ - 允许用户给 Constraint 设置友好的显示名称
324
+ - 不改变底层 ID 和文件路径,保持引用完整性
325
+
326
+ 副作用:
327
+ - 会触发两次文件写入(读取-修改-保存)
328
+
329
+ 数据流:
330
+ 1. 调用 get_v2_constraint 获取当前 constraint
331
+ 2. 修改 constraint.description 为新名称
332
+ 3. 调用 put_v2_constraint 保存更改
333
+
334
+ 参数:
335
+ constraint_id: Constraint 的唯一标识符
336
+ payload: 新的展示名称
337
+ config_path: 项目配置根目录(通过 Depends 注入)
338
+
339
+ 返回:
340
+ StandardResponse: 操作结果消息
341
+ """
342
+ constraint = get_v2_constraint(constraint_id, config_path)
343
+ constraint.description = payload.name
344
+ put_v2_constraint(constraint_id, constraint, config_path)
345
+ return {"message": f"V2 constraint '{constraint_id}' 展示名已更新。"}