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,403 @@
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 配置校验工具
17
+
18
+ Agent 可调用的工具:对生成的配置做快速抽样校验,返回命中率和问题列表。
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import json
24
+ import logging
25
+ import os
26
+ import re
27
+ from typing import Any
28
+
29
+ import pandas as pd
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class ConfigValidateTool:
35
+ """
36
+ @classdesc 配置校验工具
37
+
38
+ 对生成的约束规则进行快速抽样验证。
39
+ """
40
+
41
+ NAME = "validate_config"
42
+
43
+ def __init__(self, file_paths: list[str], profiling_data: list[dict], sample_size: int = 1000):
44
+ """
45
+ @methoddesc 初始化工具
46
+
47
+ 参数:
48
+ file_paths: 数据文件路径列表
49
+ profiling_data: 数据画像
50
+ sample_size: 校验采样行数
51
+ """
52
+ self.file_paths = file_paths
53
+ self.profiling_data = profiling_data
54
+ self.sample_size = sample_size
55
+ self._dataframes: dict[str, pd.DataFrame] = {}
56
+
57
+ def get_definition(self) -> dict[str, Any]:
58
+ """返回 OpenAI tool 定义。"""
59
+ return {
60
+ "type": "function",
61
+ "function": {
62
+ "name": self.NAME,
63
+ "description": "对生成的配置进行快速抽样校验,返回每个约束的命中情况。",
64
+ "parameters": {
65
+ "type": "object",
66
+ "properties": {
67
+ "config": {
68
+ "type": "object",
69
+ "description": "待校验的配置,包含 schemas、constraints、regex_nodes",
70
+ },
71
+ "sample_size": {
72
+ "type": "integer",
73
+ "description": "校验采样行数,覆盖默认值",
74
+ },
75
+ },
76
+ "required": ["config"],
77
+ },
78
+ },
79
+ }
80
+
81
+ def _load_dataframe(self, file_path: str) -> pd.DataFrame | None:
82
+ """加载数据文件为 DataFrame(带缓存)。"""
83
+ if file_path in self._dataframes:
84
+ return self._dataframes[file_path]
85
+
86
+ if not file_path or not os.path.exists(file_path):
87
+ return None
88
+
89
+ ext = os.path.splitext(file_path)[1].lower()
90
+ try:
91
+ if ext in [".xlsx", ".xls"]:
92
+ df = pd.read_excel(file_path, nrows=self.sample_size)
93
+ elif ext == ".csv":
94
+ df = pd.read_csv(file_path, nrows=self.sample_size, encoding="utf-8")
95
+ elif ext == ".json":
96
+ with open(file_path, encoding="utf-8") as f:
97
+ data = json.load(f)
98
+ if isinstance(data, list):
99
+ df = pd.json_normalize(data)
100
+ else:
101
+ df = pd.json_normalize([data])
102
+ df = df.head(self.sample_size)
103
+ elif ext == ".jsonl":
104
+ records = []
105
+ with open(file_path, encoding="utf-8") as f:
106
+ for i, line in enumerate(f):
107
+ if i >= self.sample_size:
108
+ break
109
+ line = line.strip()
110
+ if line:
111
+ records.append(json.loads(line))
112
+ df = pd.json_normalize(records)
113
+ else:
114
+ return None
115
+ self._dataframes[file_path] = df
116
+ return df
117
+ except Exception as e:
118
+ logger.warning(f"加载文件失败 {file_path}: {e}")
119
+ return None
120
+
121
+ def _find_file_path(self, table_id: str, schemas: dict[str, Any]) -> str | None:
122
+ """根据 table_id 找到对应的数据文件路径。"""
123
+ schema = schemas.get(table_id)
124
+ if not schema:
125
+ return None
126
+ source = schema.get("source", {})
127
+ path = source.get("path", "")
128
+ # 尝试匹配绝对路径或相对路径
129
+ for fp in self.file_paths:
130
+ if fp.endswith(path) or path.endswith(os.path.basename(fp)):
131
+ return fp
132
+ return path if os.path.exists(path) else None
133
+
134
+ def _get_column(self, df: pd.DataFrame, column_id: str) -> pd.Series:
135
+ """获取列,支持 column_id 和 name 映射。"""
136
+ if column_id in df.columns:
137
+ return df[column_id]
138
+ # 尝试大小写不敏感匹配
139
+ for col in df.columns:
140
+ if str(col).lower() == column_id.lower():
141
+ return df[col]
142
+ return pd.Series(dtype=object)
143
+
144
+ def run(self, arguments: dict[str, Any]) -> dict[str, Any]:
145
+ """
146
+ @methoddesc 执行校验
147
+
148
+ 参数:
149
+ arguments: tool 参数
150
+
151
+ 返回:
152
+ {"success": bool, "total_rules": int, "passed": int, "issues": [...]}
153
+ """
154
+ config = arguments.get("config", {})
155
+ sample_size = arguments.get("sample_size", self.sample_size)
156
+ if sample_size != self.sample_size:
157
+ self.sample_size = sample_size
158
+ self._dataframes.clear()
159
+
160
+ constraints = config.get("constraints", {})
161
+ if isinstance(constraints, list):
162
+ constraints = {c.get("id", f"c_{i}"): c for i, c in enumerate(constraints)}
163
+ schemas = config.get("schemas", {})
164
+ if isinstance(schemas, list):
165
+ schemas = {s.get("id", f"s_{i}"): s for i, s in enumerate(schemas)}
166
+
167
+ if not constraints:
168
+ return {"success": True, "total_rules": 0, "passed": 0, "issues": []}
169
+
170
+ total = 0
171
+ passed = 0
172
+ issues: list[dict[str, Any]] = []
173
+
174
+ for cid, cdef in constraints.items():
175
+ total += 1
176
+ ctype = cdef.get("type", "")
177
+ refs = cdef.get("refs", {})
178
+ params = cdef.get("params", {})
179
+ table_id = refs.get("table_id", "")
180
+ file_path = self._find_file_path(table_id, schemas)
181
+ if not file_path:
182
+ issues.append(
183
+ {
184
+ "rule_id": cid,
185
+ "type": ctype,
186
+ "severity": "warning",
187
+ "message": f"找不到 table_id={table_id} 对应的数据文件",
188
+ }
189
+ )
190
+ continue
191
+
192
+ df = self._load_dataframe(file_path)
193
+ if df is None:
194
+ issues.append(
195
+ {
196
+ "rule_id": cid,
197
+ "type": ctype,
198
+ "severity": "warning",
199
+ "message": f"无法加载文件 {file_path}",
200
+ }
201
+ )
202
+ continue
203
+
204
+ result = self._validate_constraint(df, ctype, refs, params, cid)
205
+ if result.get("passed"):
206
+ passed += 1
207
+ else:
208
+ issues.append(result.get("issue", {}))
209
+
210
+ # 同时校验 regex_nodes
211
+ regex_nodes = config.get("regex_nodes", {})
212
+ if isinstance(regex_nodes, list):
213
+ regex_nodes = {r.get("id", f"r_{i}"): r for i, r in enumerate(regex_nodes)}
214
+ for rid, rdef in regex_nodes.items():
215
+ total += 1
216
+ source_ref = rdef.get("source_ref", {})
217
+ table_id = source_ref.get("table_id", "")
218
+ column_id = source_ref.get("column_id", "")
219
+ file_path = self._find_file_path(table_id, schemas)
220
+ if not file_path:
221
+ issues.append(
222
+ {
223
+ "rule_id": rid,
224
+ "type": "Regex",
225
+ "severity": "warning",
226
+ "message": "找不到 regex 对应的数据文件",
227
+ }
228
+ )
229
+ continue
230
+ df = self._load_dataframe(file_path)
231
+ if df is None:
232
+ continue
233
+ col = self._get_column(df, column_id)
234
+ pattern = rdef.get("pattern", "")
235
+ match_mode = rdef.get("match_mode", "full")
236
+ try:
237
+ regex = re.compile(pattern)
238
+ total_values = len(col.dropna())
239
+ if total_values == 0:
240
+ continue
241
+ if match_mode == "full":
242
+ matched = col.dropna().astype(str).str.fullmatch(regex).sum()
243
+ else:
244
+ matched = col.dropna().astype(str).str.contains(regex).sum()
245
+ match_rate = float(matched) / float(total_values)
246
+ if match_rate < 0.5:
247
+ issues.append(
248
+ {
249
+ "rule_id": rid,
250
+ "type": "Regex",
251
+ "severity": "error",
252
+ "message": f"正则匹配率仅 {match_rate:.1%},可能过于严格或模式错误",
253
+ "match_rate": match_rate,
254
+ "column": column_id,
255
+ }
256
+ )
257
+ else:
258
+ passed += 1
259
+ except re.error as e:
260
+ issues.append(
261
+ {
262
+ "rule_id": rid,
263
+ "type": "Regex",
264
+ "severity": "error",
265
+ "message": f"正则表达式无效: {e}",
266
+ }
267
+ )
268
+
269
+ return {
270
+ "success": True,
271
+ "total_rules": total,
272
+ "passed": passed,
273
+ "failed": total - passed,
274
+ "issues": issues,
275
+ }
276
+
277
+ def _validate_constraint(
278
+ self,
279
+ df: pd.DataFrame,
280
+ ctype: str,
281
+ refs: dict[str, Any],
282
+ params: dict[str, Any],
283
+ rule_id: str,
284
+ ) -> dict[str, Any]:
285
+ """校验单个约束。"""
286
+ column_id = refs.get("column_id", "")
287
+ col = self._get_column(df, column_id)
288
+
289
+ if ctype == "NotNull":
290
+ null_count = int(col.isna().sum()) + int((col.astype(str).str.strip() == "").sum())
291
+ total = len(col)
292
+ if total == 0:
293
+ return {"passed": True}
294
+ null_rate = null_count / total
295
+ if null_rate > 0.05:
296
+ return {
297
+ "passed": False,
298
+ "issue": {
299
+ "rule_id": rule_id,
300
+ "type": ctype,
301
+ "severity": "error",
302
+ "message": f"空值比例 {null_rate:.1%},不适合 NotNull 约束",
303
+ "null_rate": null_rate,
304
+ "column": column_id,
305
+ },
306
+ }
307
+ return {"passed": True}
308
+
309
+ if ctype == "Unique":
310
+ column_ids = refs.get("column_ids", [column_id])
311
+ cols = [self._get_column(df, c) for c in column_ids]
312
+ combined = pd.concat(cols, axis=1).dropna()
313
+ if len(combined) == 0:
314
+ return {"passed": True}
315
+ dup_rate = 1 - float(len(combined.drop_duplicates())) / float(len(combined))
316
+ if dup_rate > 0.05:
317
+ return {
318
+ "passed": False,
319
+ "issue": {
320
+ "rule_id": rule_id,
321
+ "type": ctype,
322
+ "severity": "error",
323
+ "message": f"重复率 {dup_rate:.1%},不适合 Unique 约束",
324
+ "dup_rate": dup_rate,
325
+ "columns": column_ids,
326
+ },
327
+ }
328
+ return {"passed": True}
329
+
330
+ if ctype == "AllowedValues":
331
+ allowed = params.get("allowed_values", [])
332
+ if not allowed:
333
+ return {"passed": True}
334
+ total = len(col.dropna())
335
+ if total == 0:
336
+ return {"passed": True}
337
+ invalid = col.dropna().apply(lambda x: x not in allowed)
338
+ invalid_rate = float(invalid.sum()) / float(total)
339
+ if invalid_rate > 0.05:
340
+ # 收集不在白名单中的高频值。
341
+ # 注意:invalid 是 col.dropna() 子集上的布尔掩码,索引与 col 不对齐,
342
+ # 直接 col[invalid] 会引发 IndexError/错位;必须用同样 dropna 后的序列索引
343
+ invalid_values = col.dropna()[invalid].value_counts().head(5).to_dict()
344
+ return {
345
+ "passed": False,
346
+ "issue": {
347
+ "rule_id": rule_id,
348
+ "type": ctype,
349
+ "severity": "error",
350
+ "message": f"{invalid_rate:.1%} 的值不在允许列表中",
351
+ "invalid_rate": invalid_rate,
352
+ "invalid_values": invalid_values,
353
+ "column": column_id,
354
+ },
355
+ }
356
+ return {"passed": True}
357
+
358
+ if ctype == "Range":
359
+ min_val = params.get("min")
360
+ max_val = params.get("max")
361
+ numeric = pd.to_numeric(col, errors="coerce").dropna()
362
+ if len(numeric) == 0:
363
+ return {"passed": True}
364
+ violations = 0
365
+ if min_val is not None:
366
+ violations += int((numeric < min_val).sum())
367
+ if max_val is not None:
368
+ violations += int((numeric > max_val).sum())
369
+ violation_rate = violations / len(numeric)
370
+ if violation_rate > 0.05:
371
+ return {
372
+ "passed": False,
373
+ "issue": {
374
+ "rule_id": rule_id,
375
+ "type": ctype,
376
+ "severity": "error",
377
+ "message": f"越界比例 {violation_rate:.1%},请调整 Range 范围",
378
+ "violation_rate": violation_rate,
379
+ "column": column_id,
380
+ },
381
+ }
382
+ return {"passed": True}
383
+
384
+ if ctype == "ForeignKey":
385
+ # 外键需要目标表数据,简化处理:仅检查 from_column 非空率
386
+ from_col = self._get_column(df, refs.get("from_column_id", column_id))
387
+ null_count = int(from_col.isna().sum())
388
+ total = len(from_col)
389
+ if total > 0 and null_count / total > 0.1:
390
+ return {
391
+ "passed": False,
392
+ "issue": {
393
+ "rule_id": rule_id,
394
+ "type": ctype,
395
+ "severity": "warning",
396
+ "message": f"外键列空值比例 {(null_count / total):.1%},可能影响引用完整性",
397
+ "column": refs.get("from_column_id", column_id),
398
+ },
399
+ }
400
+ return {"passed": True}
401
+
402
+ # 其他类型默认通过
403
+ return {"passed": True}
@@ -0,0 +1,136 @@
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 结果合并工具
17
+
18
+ Agent 可调用的工具:合并多个分块生成的配置,去重并标记冲突。
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ from typing import Any
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class MergeResultsTool:
30
+ """
31
+ @classdesc 结果合并工具
32
+
33
+ 合并多个局部配置为一个完整配置。
34
+ """
35
+
36
+ NAME = "merge_results"
37
+
38
+ def get_definition(self) -> dict[str, Any]:
39
+ """返回 OpenAI tool 定义。"""
40
+ return {
41
+ "type": "function",
42
+ "function": {
43
+ "name": self.NAME,
44
+ "description": "合并多个分块生成的配置,去重并检测冲突。",
45
+ "parameters": {
46
+ "type": "object",
47
+ "properties": {
48
+ "configs": {
49
+ "type": "array",
50
+ "items": {"type": "object"},
51
+ "description": "待合并的局部配置列表",
52
+ },
53
+ },
54
+ "required": ["configs"],
55
+ },
56
+ },
57
+ }
58
+
59
+ def run(self, arguments: dict[str, Any]) -> dict[str, Any]:
60
+ """
61
+ @methoddesc 执行合并
62
+
63
+ 参数:
64
+ arguments: tool 参数
65
+
66
+ 返回:
67
+ {"success": bool, "config": {...}, "warnings": [...], "conflicts": [...]}
68
+ """
69
+ configs = arguments.get("configs", [])
70
+ if not configs:
71
+ return {"success": True, "config": {}, "warnings": [], "conflicts": []}
72
+
73
+ merged: dict[str, Any] = {"schemas": {}, "constraints": {}, "regex_nodes": {}}
74
+ warnings: list[str] = []
75
+ conflicts: list[dict[str, Any]] = []
76
+
77
+ # 合并 schemas
78
+ for cfg in configs:
79
+ for sid, schema in cfg.get("schemas", {}).items():
80
+ if sid in merged["schemas"]:
81
+ # 合并列,按 name 去重
82
+ existing_cols = {c.get("name"): c for c in merged["schemas"][sid].get("columns", [])}
83
+ for col in schema.get("columns", []):
84
+ existing_cols[col.get("name")] = col
85
+ merged["schemas"][sid]["columns"] = list(existing_cols.values())
86
+ else:
87
+ merged["schemas"][sid] = schema
88
+
89
+ # 合并 constraints,按 (table_id, column_id, type) 去重
90
+ seen_constraints: set[tuple[str, str, str]] = set()
91
+ for cfg in configs:
92
+ for cid, cdef in cfg.get("constraints", {}).items():
93
+ ctype = cdef.get("type", "")
94
+ refs = cdef.get("refs", {})
95
+ table_id = refs.get("table_id", "")
96
+ column_id = refs.get("column_id", "")
97
+ column_ids = tuple(refs.get("column_ids", []))
98
+ constraint_key: tuple[str, str, str] = (table_id, column_id or "_".join(column_ids), ctype)
99
+ if constraint_key in seen_constraints:
100
+ conflicts.append(
101
+ {
102
+ "rule_id": cid,
103
+ "type": ctype,
104
+ "reason": "重复约束",
105
+ "key": constraint_key,
106
+ }
107
+ )
108
+ continue
109
+ seen_constraints.add(constraint_key)
110
+ merged["constraints"][cid] = cdef
111
+
112
+ # 合并 regex_nodes,按 pattern + source_ref 去重
113
+ seen_regex: set[str] = set()
114
+ for cfg in configs:
115
+ for rid, rdef in cfg.get("regex_nodes", {}).items():
116
+ source_ref = rdef.get("source_ref", {})
117
+ regex_key = (
118
+ f"{rdef.get('pattern', '')}|{source_ref.get('table_id', '')}|{source_ref.get('column_id', '')}"
119
+ )
120
+ if regex_key in seen_regex:
121
+ conflicts.append(
122
+ {
123
+ "rule_id": rid,
124
+ "type": "Regex",
125
+ "reason": "重复正则",
126
+ "key": regex_key,
127
+ }
128
+ )
129
+ continue
130
+ seen_regex.add(regex_key)
131
+ merged["regex_nodes"][rid] = rdef
132
+
133
+ if conflicts:
134
+ warnings.append(f"合并时发现 {len(conflicts)} 处重复,已自动去重")
135
+
136
+ return {"success": True, "config": merged, "warnings": warnings, "conflicts": conflicts}
@@ -0,0 +1,79 @@
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 分块规划工具
17
+
18
+ Agent 可调用的工具:根据数据画像输出分块执行计划。
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from typing import Any
24
+
25
+ from app.shared.services.ai.agent.planner import build_chunk_plan, plan_to_dict
26
+
27
+
28
+ class PlanChunksTool:
29
+ """
30
+ @classdesc 分块规划工具
31
+
32
+ 根据数据画像自动决定分块策略。
33
+ """
34
+
35
+ NAME = "plan_chunks"
36
+
37
+ def __init__(self, profiling_data: list[dict], file_paths: list[str], chunk_max_columns: int, chunk_max_files: int):
38
+ """
39
+ @methoddesc 初始化工具
40
+
41
+ 参数:
42
+ profiling_data: 数据画像
43
+ file_paths: 文件路径列表
44
+ chunk_max_columns: 每个 chunk 最大列数
45
+ chunk_max_files: 每个 chunk 最大文件数
46
+ """
47
+ self.profiling_data = profiling_data
48
+ self.file_paths = file_paths
49
+ self.chunk_max_columns = chunk_max_columns
50
+ self.chunk_max_files = chunk_max_files
51
+
52
+ def get_definition(self) -> dict[str, Any]:
53
+ """返回 OpenAI tool 定义。"""
54
+ return {
55
+ "type": "function",
56
+ "function": {
57
+ "name": self.NAME,
58
+ "description": "根据数据画像生成分块执行计划,用于大数据量场景。",
59
+ "parameters": {
60
+ "type": "object",
61
+ "properties": {},
62
+ },
63
+ },
64
+ }
65
+
66
+ def run(self, arguments: dict[str, Any]) -> dict[str, Any]:
67
+ """
68
+ @methoddesc 执行分块规划
69
+
70
+ 返回:
71
+ {"success": bool, "plan": {...}}
72
+ """
73
+ plan = build_chunk_plan(
74
+ self.profiling_data,
75
+ self.file_paths,
76
+ max_columns_per_chunk=self.chunk_max_columns,
77
+ max_files_per_chunk=self.chunk_max_files,
78
+ )
79
+ return {"success": True, "plan": plan_to_dict(plan)}