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,112 @@
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 MathExpr 转换运行器
18
+
19
+ 功能概述:
20
+ - 使用 simpleeval 沙箱计算数学表达式(B-sec5: 替代原 pandas.DataFrame.eval,消除注入面)
21
+ - 支持引用现有列作为变量
22
+
23
+ 参数:
24
+ expression: 数学表达式字符串,如 "@col_a + @col_b * 2" 或 "col_a + col_b * 2"
25
+ (支持 @列名 语法,会自动转换为列名引用)
26
+ output_type: 输出类型(int, float, 默认保持原样)
27
+
28
+ 安全说明:
29
+ - B-sec5: 原实现用 pandas.DataFrame.eval,其 @语法可访问局部变量/可调用对象,
30
+ 在不同 pandas 版本/引擎下存在逃逸面;表达式来自用户写入的 transforms/*.yaml,
31
+ 构成存储型代码注入风险。现改用 simpleeval(与 Scripted 约束同一沙箱),
32
+ 仅允许 SAFE_FUNCTIONS 白名单内的纯函数,禁用属性访问与 dunder。
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import re
38
+ from typing import Any
39
+
40
+ import pandas as pd
41
+
42
+ from app.shared.domain.eval_sandbox import make_sandbox_evaluator
43
+
44
+ from .base import TransformRunner
45
+
46
+
47
+ class MathExprRunner(TransformRunner):
48
+ """@classdesc 数学表达式转换运行器"""
49
+
50
+ def execute(
51
+ self,
52
+ df: pd.DataFrame,
53
+ input_column: str,
54
+ params: dict[str, Any],
55
+ output_columns: list[str],
56
+ ) -> pd.DataFrame:
57
+ """
58
+ @methoddesc 执行 数学表达式转换
59
+
60
+ 业务用途:
61
+ - TransformRunner 协议的标准入口,由 transform 节点调用
62
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
63
+
64
+ 参数:
65
+ df: 源 DataFrame
66
+ input_column: 输入列名
67
+ params: 转换参数字典
68
+ output_columns: 目标输出列名列表
69
+
70
+ 返回:
71
+ 转换后的 DataFrame
72
+ """
73
+ expression = params.get("expression", "")
74
+ output_type = params.get("output_type", None)
75
+
76
+ if not expression:
77
+ raise ValueError("MathExpr 需要 expression 参数")
78
+
79
+ if not output_columns:
80
+ raise ValueError("MathExpr 需要至少一个 output_columns")
81
+
82
+ output_col = output_columns[0]
83
+
84
+ # 支持 @列名 语法:simpleeval 的变量绑定即列名字面量,故去除 @ 前缀。
85
+ eval_expression = re.sub(r"@(\w+)", r"\1", expression)
86
+
87
+ # B-sec5: 逐行用 simpleeval 沙箱求值(替代 df.eval 的注入面)。
88
+ # names 注入该行所有列值(col_name -> cell_value),表达式如 "a + b" 直接引用列名。
89
+ results: list[Any] = []
90
+ # to_dict("records") 比 iterrows 快,且每行独立绑定避免跨行状态泄漏。
91
+ for record in df.to_dict("records"):
92
+ try:
93
+ value = make_sandbox_evaluator(names=record).eval(eval_expression)
94
+ except Exception as e:
95
+ raise ValueError(f"数学表达式计算失败: {expression}, 错误: {e}")
96
+ results.append(value)
97
+
98
+ result_series = pd.Series(results, index=df.index)
99
+
100
+ if output_type == "int":
101
+ try:
102
+ df[output_col] = pd.to_numeric(result_series, errors="coerce").astype("Int64")
103
+ except (TypeError, ValueError):
104
+ # 小数结果(如 7 / 2)无法安全转可空整数,保留 float 结果;
105
+ # 原实现此处抛未捕获 TypeError,一行小数即炸整条转换。
106
+ df[output_col] = pd.to_numeric(result_series, errors="coerce")
107
+ elif output_type == "float":
108
+ df[output_col] = pd.to_numeric(result_series, errors="coerce")
109
+ else:
110
+ df[output_col] = result_series
111
+
112
+ return df
@@ -0,0 +1,82 @@
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 Modulo 转换运行器
18
+
19
+ 功能概述:
20
+ - 对数值列取模运算
21
+
22
+ 参数:
23
+ divisor: 除数(模数),默认 1
24
+
25
+ 输入输出:
26
+ 输入: 266
27
+ 输出: 2 (266 % 11)
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from typing import Any
33
+
34
+ import pandas as pd
35
+
36
+ from .base import TransformRunner
37
+
38
+
39
+ class ModuloRunner(TransformRunner):
40
+ """@classdesc 取模转换运行器"""
41
+
42
+ def execute(
43
+ self,
44
+ df: pd.DataFrame,
45
+ input_column: str,
46
+ params: dict[str, Any],
47
+ output_columns: list[str],
48
+ ) -> pd.DataFrame:
49
+ """
50
+ @methoddesc 执行 取模转换
51
+
52
+ 业务用途:
53
+ - TransformRunner 协议的标准入口,由 transform 节点调用
54
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
55
+
56
+ 参数:
57
+ df: 源 DataFrame
58
+ input_column: 输入列名
59
+ params: 转换参数字典
60
+ output_columns: 目标输出列名列表
61
+
62
+ 返回:
63
+ 转换后的 DataFrame
64
+ """
65
+ divisor = params.get("divisor", 1)
66
+ if divisor == 0:
67
+ raise ValueError("Modulo 的 divisor 不能为 0")
68
+
69
+ if input_column not in df.columns:
70
+ raise ValueError(f"输入列不存在: {input_column}")
71
+
72
+ output_col = output_columns[0] if output_columns else "modulo_result"
73
+
74
+ df[output_col] = pd.to_numeric(df[input_column], errors="coerce") % divisor
75
+ try:
76
+ df[output_col] = df[output_col].astype("Int64")
77
+ except (TypeError, ValueError):
78
+ # 小数余数(如 2.5 % 2 = 0.5)无法安全转可空整数,保留 float 结果;
79
+ # 原实现此处抛未捕获 TypeError,一行小数即炸整条转换。
80
+ pass
81
+
82
+ return df
@@ -0,0 +1,103 @@
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 RegexExtract 转换运行器
18
+
19
+ 功能概述:
20
+ - 使用正则表达式从字符串中提取捕获组
21
+ - 输出多个新列(每个捕获组一列)
22
+
23
+ 参数:
24
+ pattern: 正则表达式模式(必须包含捕获组)
25
+ flags: 正则标志(如 "i" 表示忽略大小写)
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import re
31
+ from typing import Any
32
+
33
+ import pandas as pd
34
+
35
+ from .base import TransformRunner, stringify_preserve_null
36
+
37
+
38
+ class RegexExtractRunner(TransformRunner):
39
+ """@classdesc 正则提取转换运行器"""
40
+
41
+ def execute(
42
+ self,
43
+ df: pd.DataFrame,
44
+ input_column: str,
45
+ params: dict[str, Any],
46
+ output_columns: list[str],
47
+ ) -> pd.DataFrame:
48
+ """
49
+ @methoddesc 执行 正则提取转换
50
+
51
+ 业务用途:
52
+ - TransformRunner 协议的标准入口,由 transform 节点调用
53
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
54
+
55
+ 参数:
56
+ df: 源 DataFrame
57
+ input_column: 输入列名
58
+ params: 转换参数字典
59
+ output_columns: 目标输出列名列表
60
+
61
+ 返回:
62
+ 转换后的 DataFrame
63
+ """
64
+ pattern = params.get("pattern", "")
65
+ flags_str = params.get("flags", "")
66
+
67
+ if not pattern:
68
+ raise ValueError("RegexExtract 需要 pattern 参数")
69
+
70
+ if input_column not in df.columns:
71
+ raise ValueError(f"输入列不存在: {input_column}")
72
+
73
+ # flags 整词匹配(与 regex 约束/reader 同一套 token 语义),不得子串包含
74
+ _flag_short_map = {"i": re.IGNORECASE, "m": re.MULTILINE, "s": re.DOTALL}
75
+ flag_values = 0
76
+ for tok in [t for t in re.split(r"[,\s]+", str(flags_str).strip()) if t]:
77
+ if all(ch in _flag_short_map for ch in tok.lower()):
78
+ for ch in tok.lower():
79
+ flag_values |= _flag_short_map[ch]
80
+
81
+ compiled = re.compile(pattern, flag_values)
82
+
83
+ def _extract(text: str | None) -> list[str | None]:
84
+ if text is None:
85
+ # 空值不参与提取(原 astype(str) 把空值字符串化为 "nan" 参与匹配)
86
+ return [None] * len(output_columns)
87
+ m = compiled.search(text)
88
+ if not m:
89
+ return [None] * len(output_columns)
90
+ groups = m.groups()
91
+ result: list[str | None] = []
92
+ for i in range(len(output_columns)):
93
+ if i < len(groups):
94
+ result.append(groups[i])
95
+ else:
96
+ result.append(None)
97
+ return result
98
+
99
+ extracted = stringify_preserve_null(df[input_column]).apply(_extract)
100
+ for i, col_name in enumerate(output_columns):
101
+ df[col_name] = extracted.apply(lambda x: x[i] if x else None)
102
+
103
+ return df
@@ -0,0 +1,95 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview Transform 运行器注册表
18
+
19
+ 功能概述:
20
+ - 将 TransformFile.type 映射到对应的 TransformRunner 类
21
+ - 提供统一的 create_runner(type_name) 工厂函数
22
+
23
+ 使用示例:
24
+ runner = create_runner("StringSplit")
25
+ df = runner.execute(df, "name", {"delimiter": " "}, ["first_name", "last_name"])
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from .aggregate import AggregateRunner
31
+ from .base import TransformRunner
32
+ from .cast_type import CastTypeRunner
33
+ from .concat import ConcatRunner
34
+ from .conditional_assign import ConditionalAssignRunner
35
+ from .date_format import DateFormatRunner
36
+ from .digits import DigitsRunner
37
+ from .drop_duplicates import DropDuplicatesRunner
38
+ from .fill_na import FillNARunner
39
+ from .filter_rows import FilterRowsRunner
40
+ from .lookup import LookupRunner
41
+ from .lower_case import LowerCaseRunner
42
+ from .map_value import MapValueRunner
43
+ from .math_expr import MathExprRunner
44
+ from .modulo import ModuloRunner
45
+ from .regex_extract import RegexExtractRunner
46
+ from .replace import ReplaceRunner
47
+ from .sort_rows import SortRowsRunner
48
+ from .string_split import StringSplitRunner
49
+ from .strip import StripRunner
50
+ from .substring import SubstringRunner
51
+ from .upper_case import UpperCaseRunner
52
+ from .weighted_sum import WeightedSumRunner
53
+
54
+ TRANSFORM_REGISTRY: dict[str, type[TransformRunner]] = {
55
+ "StringSplit": StringSplitRunner,
56
+ "RegexExtract": RegexExtractRunner,
57
+ "MathExpr": MathExprRunner,
58
+ "DateFormat": DateFormatRunner,
59
+ "Lookup": LookupRunner,
60
+ "Strip": StripRunner,
61
+ "UpperCase": UpperCaseRunner,
62
+ "LowerCase": LowerCaseRunner,
63
+ "Replace": ReplaceRunner,
64
+ "FillNA": FillNARunner,
65
+ "CastType": CastTypeRunner,
66
+ "Concat": ConcatRunner,
67
+ "Substring": SubstringRunner,
68
+ "ConditionalAssign": ConditionalAssignRunner,
69
+ "Digits": DigitsRunner,
70
+ "WeightedSum": WeightedSumRunner,
71
+ "Modulo": ModuloRunner,
72
+ "MapValue": MapValueRunner,
73
+ "FilterRows": FilterRowsRunner,
74
+ "DropDuplicates": DropDuplicatesRunner,
75
+ "Aggregate": AggregateRunner,
76
+ "SortRows": SortRowsRunner,
77
+ }
78
+
79
+
80
+ def create_runner(type_name: str) -> TransformRunner:
81
+ """@methoddesc 根据类型名称创建 TransformRunner 实例
82
+
83
+ 参数:
84
+ type_name: Transform 类型名称(如 "StringSplit")
85
+
86
+ 返回:
87
+ TransformRunner 实例
88
+
89
+ 异常:
90
+ ValueError: 类型名称未注册
91
+ """
92
+ runner_class = TRANSFORM_REGISTRY.get(type_name)
93
+ if not runner_class:
94
+ raise ValueError(f"未注册的 Transform 类型: {type_name},支持类型: {list(TRANSFORM_REGISTRY.keys())}")
95
+ return runner_class()
@@ -0,0 +1,88 @@
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 Replace 转换运行器
18
+
19
+ 功能概述:
20
+ - 将字符串中的指定内容替换为新内容
21
+ - 支持控制替换次数
22
+
23
+ 参数:
24
+ old: 要被替换的内容(必填)
25
+ new: 替换后的新内容(默认空字符串)
26
+ count: 最大替换次数(默认 -1,替换所有)
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from typing import Any
32
+
33
+ import pandas as pd
34
+
35
+ from .base import TransformRunner, stringify_preserve_null
36
+
37
+
38
+ class ReplaceRunner(TransformRunner):
39
+ """@classdesc 字符串替换转换运行器"""
40
+
41
+ def execute(
42
+ self,
43
+ df: pd.DataFrame,
44
+ input_column: str,
45
+ params: dict[str, Any],
46
+ output_columns: list[str],
47
+ ) -> pd.DataFrame:
48
+ """
49
+ @methoddesc 执行 字符串替换转换
50
+
51
+ 业务用途:
52
+ - TransformRunner 协议的标准入口,由 transform 节点调用
53
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
54
+
55
+ 参数:
56
+ df: 源 DataFrame
57
+ input_column: 输入列名
58
+ params: 转换参数字典
59
+ output_columns: 目标输出列名列表
60
+
61
+ 返回:
62
+ 转换后的 DataFrame
63
+ """
64
+ old = params.get("old")
65
+ new = params.get("new", "")
66
+ count = params.get("count", -1)
67
+
68
+ # §1.17: old 缺省/显式空串报配置错误——原实现 "" 兜底触发 str.replace("", "X")
69
+ # 的逐字符插入,输出列变 "XaXbXcX" 垃圾数据。new 缺省 "" 是合法删除语义,保持。
70
+ if old is None or old == "":
71
+ raise ValueError("Replace 转换缺少必填参数 old(被替换内容不能为空)")
72
+
73
+ if input_column not in df.columns:
74
+ raise ValueError(f"输入列不存在: {input_column}")
75
+
76
+ if not output_columns:
77
+ raise ValueError("Replace 需要至少一个 output_columns")
78
+
79
+ output_col = output_columns[0]
80
+ # 空值透传为 None,替换不作用于空值
81
+ series = stringify_preserve_null(df[input_column])
82
+
83
+ if count == -1:
84
+ df[output_col] = series.str.replace(old, new, regex=False)
85
+ else:
86
+ df[output_col] = series.str.replace(old, new, n=count, regex=False)
87
+
88
+ return df
@@ -0,0 +1,94 @@
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 SortRows 转换运行器
18
+
19
+ 功能概述:
20
+ - 对 DataFrame 按指定列和方向排序
21
+ - 支持多列排序
22
+
23
+ 参数:
24
+ sort_by: 排序规则列表 [{column, order}]
25
+ - column: 排序列名
26
+ - order: 排序方向 ("asc"|"desc")
27
+
28
+ 说明:
29
+ - input_column 被忽略(操作整行而非单列)
30
+ - output_columns 被忽略(不产生新列,保留所有原始列)
31
+ - 输出 DataFrame 索引重置为连续 0-based
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ from typing import Any
37
+
38
+ import pandas as pd
39
+
40
+ from .base import TransformRunner
41
+
42
+
43
+ class SortRowsRunner(TransformRunner):
44
+ """@classdesc 排序转换运行器"""
45
+
46
+ def execute(
47
+ self,
48
+ df: pd.DataFrame,
49
+ input_column: str,
50
+ params: dict[str, Any],
51
+ output_columns: list[str],
52
+ ) -> pd.DataFrame:
53
+ """
54
+ @methoddesc 执行 排序转换
55
+
56
+ 业务用途:
57
+ - TransformRunner 协议的标准入口,由 transform 节点调用
58
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
59
+
60
+ 参数:
61
+ df: 源 DataFrame
62
+ input_column: 输入列名
63
+ params: 转换参数字典
64
+ output_columns: 目标输出列名列表
65
+
66
+ 返回:
67
+ 转换后的 DataFrame
68
+ """
69
+ sort_by = params.get("sort_by", [])
70
+
71
+ if not sort_by:
72
+ # 无排序规则时保持原序
73
+ return df.reset_index(drop=True)
74
+
75
+ # 解析排序规则
76
+ columns = []
77
+ ascending = []
78
+ for rule in sort_by:
79
+ col = rule.get("column", "")
80
+ if col not in df.columns:
81
+ raise ValueError(f"排序列不存在: {col}")
82
+ columns.append(col)
83
+ # order 归一大小写不敏感("ASC"/"Desc" 均合法),未知值 fail-fast——不再静默按降序
84
+ raw_order = rule.get("order", "asc")
85
+ order = str(raw_order).lower()
86
+ if order == "asc":
87
+ ascending.append(True)
88
+ elif order == "desc":
89
+ ascending.append(False)
90
+ else:
91
+ raise ValueError(f"未知的排序方向 '{raw_order}',支持的值为: asc, desc")
92
+
93
+ result = df.sort_values(by=columns, ascending=ascending).reset_index(drop=True)
94
+ return result
@@ -0,0 +1,84 @@
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 StringSplit 转换运行器
18
+
19
+ 功能概述:
20
+ - 按分隔符或固定位置切割字符串
21
+ - 输出多个新列
22
+
23
+ 参数:
24
+ delimiter: 分隔符字符串(默认 " ")
25
+ maxsplit: 最大分割次数(默认 -1,不限制)
26
+ expand: 是否展开为多列(默认 True)
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from typing import Any
32
+
33
+ import pandas as pd
34
+
35
+ from .base import TransformRunner, stringify_preserve_null
36
+
37
+
38
+ class StringSplitRunner(TransformRunner):
39
+ """@classdesc 字符串切割转换运行器"""
40
+
41
+ def execute(
42
+ self,
43
+ df: pd.DataFrame,
44
+ input_column: str,
45
+ params: dict[str, Any],
46
+ output_columns: list[str],
47
+ ) -> pd.DataFrame:
48
+ """
49
+ @methoddesc 执行 字符串分割转换
50
+
51
+ 业务用途:
52
+ - TransformRunner 协议的标准入口,由 transform 节点调用
53
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
54
+
55
+ 参数:
56
+ df: 源 DataFrame
57
+ input_column: 输入列名
58
+ params: 转换参数字典
59
+ output_columns: 目标输出列名列表
60
+
61
+ 返回:
62
+ 转换后的 DataFrame
63
+ """
64
+ delimiter = params.get("delimiter", " ")
65
+ maxsplit = params.get("maxsplit", -1)
66
+
67
+ if input_column not in df.columns:
68
+ raise ValueError(f"输入列不存在: {input_column}")
69
+
70
+ # 空值透传为 None:原 astype(str) 把空值字符串化为 "nan" 参与切分
71
+ series = stringify_preserve_null(df[input_column])
72
+
73
+ if maxsplit == -1:
74
+ split_df = series.str.split(delimiter, expand=True)
75
+ else:
76
+ split_df = series.str.split(delimiter, n=maxsplit, expand=True)
77
+
78
+ for i, col_name in enumerate(output_columns):
79
+ if i < split_df.shape[1]:
80
+ df[col_name] = split_df[i]
81
+ else:
82
+ df[col_name] = None
83
+
84
+ return df