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,81 @@
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 DateFormat 转换运行器
18
+
19
+ 功能概述:
20
+ - 将日期字符串从一种格式转换为另一种格式
21
+ - 支持解析失败时的容错处理
22
+
23
+ 参数:
24
+ input_format: 输入日期格式(默认 "%Y-%m-%d")
25
+ output_format: 输出日期格式(默认 "%Y/%m/%d")
26
+ errors: 错误处理策略("coerce" 转为 NaT, "raise" 抛出异常, "ignore" 保留原值)
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
36
+
37
+
38
+ class DateFormatRunner(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
+ input_format = params.get("input_format", "%Y-%m-%d")
65
+ output_format = params.get("output_format", "%Y/%m/%d")
66
+ errors = params.get("errors", "coerce")
67
+
68
+ if input_column not in df.columns:
69
+ raise ValueError(f"输入列不存在: {input_column}")
70
+
71
+ if not output_columns:
72
+ raise ValueError("DateFormat 需要至少一个 output_columns")
73
+
74
+ output_col = output_columns[0]
75
+
76
+ parsed = pd.to_datetime(df[input_column], format=input_format, errors=errors)
77
+ formatted = parsed.dt.strftime(output_format)
78
+ formatted = formatted.where(parsed.notna(), None)
79
+ df[output_col] = formatted
80
+
81
+ return df
@@ -0,0 +1,77 @@
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 Digits 转换运行器
18
+
19
+ 功能概述:
20
+ - 将字符串的每个字符拆分为逗号分隔的数字字符串
21
+ - 保持行数不变,输出单列字符串(如 "1,1,0,1,0,1")
22
+
23
+ 参数:
24
+ 无
25
+
26
+ 输入输出:
27
+ 输入: "11010119900307889"(字符串)
28
+ 输出: "1,1,0,1,0,1,1,9,9,0,0,3,0,7,8,8,9"(逗号分隔)
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ from typing import Any
34
+
35
+ import pandas as pd
36
+
37
+ from .base import TransformRunner, stringify_preserve_null
38
+
39
+
40
+ class DigitsRunner(TransformRunner):
41
+ """@classdesc 逐位分解转换运行器"""
42
+
43
+ def execute(
44
+ self,
45
+ df: pd.DataFrame,
46
+ input_column: str,
47
+ params: dict[str, Any],
48
+ output_columns: list[str],
49
+ ) -> pd.DataFrame:
50
+ """
51
+ @methoddesc 执行 数字提取转换
52
+
53
+ 业务用途:
54
+ - TransformRunner 协议的标准入口,由 transform 节点调用
55
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
56
+
57
+ 参数:
58
+ df: 源 DataFrame
59
+ input_column: 输入列名
60
+ params: 转换参数字典
61
+ output_columns: 目标输出列名列表
62
+
63
+ 返回:
64
+ 转换后的 DataFrame
65
+ """
66
+ if input_column not in df.columns:
67
+ raise ValueError(f"输入列不存在: {input_column}")
68
+
69
+ output_col = output_columns[0] if output_columns else "digits"
70
+
71
+ # 将每个字符用逗号连接,保持行数不变
72
+ # 空值透传为 None,不产生 "nan" 拆位
73
+ df[output_col] = stringify_preserve_null(df[input_column]).apply(
74
+ lambda s: None if s is None else ",".join(list(s))
75
+ )
76
+
77
+ 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 DropDuplicates 转换运行器
18
+
19
+ 功能概述:
20
+ - 对 DataFrame 进行去重
21
+ - 支持指定子集列(逗号分隔)或全列去重
22
+ - 支持保留策略:first(保留第一条)、last(保留最后一条)、false(全部删除)
23
+
24
+ 参数:
25
+ subset: 去重列名,支持逗号分隔字符串或列表,留空则基于全部列去重
26
+ keep: 保留策略 ("first"|"last"|"false")
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 DropDuplicatesRunner(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
+ keep = params.get("keep", "first")
70
+ subset_raw = params.get("subset", "")
71
+
72
+ # 解析 subset:兼容逗号分隔字符串与列表两种格式
73
+ # 前端 tags 产出的是数组,旧配置/手写 YAML 可能是逗号分隔字符串
74
+ subset = None
75
+ if subset_raw:
76
+ if isinstance(subset_raw, list):
77
+ parsed = [str(col).strip() for col in subset_raw if str(col).strip()]
78
+ else:
79
+ parsed = [col.strip() for col in str(subset_raw).split(",") if col.strip()]
80
+ # §1.16: 拼错列名报配置错误——原实现静默剔除后走全列去重,
81
+ # 去重结果比预期多、下游统计偏大且无提示。空 subset(未配置)保持全列去重。
82
+ missing = [col for col in parsed if col not in df.columns]
83
+ if missing:
84
+ raise ValueError(f"subset 列不存在: {missing}(表列: {list(df.columns)})")
85
+ if parsed:
86
+ subset = parsed
87
+
88
+ # pandas 的 keep 参数:keep=False 表示全部删除
89
+ keep_param: str | bool = keep
90
+ if keep == "false":
91
+ keep_param = False
92
+
93
+ result = df.drop_duplicates(subset=subset, keep=keep_param).reset_index(drop=True)
94
+ return result
@@ -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 FillNA 转换运行器
18
+
19
+ 功能概述:
20
+ - 填充空值(NaN/None)
21
+ - 支持多种填充策略:固定值、前向填充、后向填充、均值、中位数
22
+
23
+ 参数:
24
+ strategy: 填充策略 ("value"|"ffill"|"bfill"|"mean"|"median")
25
+ value: 当 strategy=="value" 时的填充值
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from typing import Any
31
+
32
+ import pandas as pd
33
+
34
+ from .base import TransformRunner
35
+
36
+
37
+ class FillNARunner(TransformRunner):
38
+ """@classdesc 空值填充转换运行器"""
39
+
40
+ def execute(
41
+ self,
42
+ df: pd.DataFrame,
43
+ input_column: str,
44
+ params: dict[str, Any],
45
+ output_columns: list[str],
46
+ ) -> pd.DataFrame:
47
+ """
48
+ @methoddesc 执行 空值填充转换
49
+
50
+ 业务用途:
51
+ - TransformRunner 协议的标准入口,由 transform 节点调用
52
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
53
+
54
+ 参数:
55
+ df: 源 DataFrame
56
+ input_column: 输入列名
57
+ params: 转换参数字典
58
+ output_columns: 目标输出列名列表
59
+
60
+ 返回:
61
+ 转换后的 DataFrame
62
+ """
63
+ strategy = params.get("strategy", "value")
64
+
65
+ if input_column not in df.columns:
66
+ raise ValueError(f"输入列不存在: {input_column}")
67
+
68
+ if not output_columns:
69
+ raise ValueError("FillNA 需要至少一个 output_columns")
70
+
71
+ output_col = output_columns[0]
72
+ series = df[input_column].copy()
73
+
74
+ if strategy == "value":
75
+ fill_value = params.get("value", "")
76
+ series = series.fillna(fill_value)
77
+ elif strategy == "ffill":
78
+ series = series.ffill()
79
+ elif strategy == "bfill":
80
+ series = series.bfill()
81
+ elif strategy in ("mean", "median"):
82
+ # §1.6: 只填真正的空值,非空非数值值保留原样——原实现整列 coerce 后 fillna,
83
+ # "abc" 这类非空脏值被顺带清洗成均值(用户没要求清洗,原始信息无声丢失)。
84
+ # 均值只对"能转数值的非空值"计算;填充后输出列为 object(混合类型)是预期。
85
+ numeric_series = pd.to_numeric(series, errors="coerce")
86
+ stat = numeric_series.mean() if strategy == "mean" else numeric_series.median()
87
+ filled = series.copy()
88
+ filled[series.isna()] = stat
89
+ series = filled
90
+ else:
91
+ raise ValueError(f"不支持的 FillNA 策略: {strategy}")
92
+
93
+ df[output_col] = series
94
+ return df
@@ -0,0 +1,97 @@
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 FilterRows 转换运行器
18
+
19
+ 功能概述:
20
+ - 按条件过滤行,返回满足条件的行子集
21
+ - 支持多条件组合(AND/OR 逻辑)
22
+ - 支持 eq/ne/gt/gte/lt/lte/contains/startswith/endswith/in/not_in/is_null/is_not_null 操作符
23
+
24
+ 参数:
25
+ conditions: 条件列表 [{column, op, value}]
26
+ logic: 条件组合逻辑 ("and"|"or")
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, evaluate_condition
41
+
42
+
43
+ class FilterRowsRunner(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
+ conditions = params.get("conditions", [])
70
+ # logic 归一大小写不敏感("OR"/"And" 均合法),未知值 fail-fast——不再静默按 AND 组合
71
+ raw_logic = params.get("logic", "and")
72
+ logic = str(raw_logic).lower()
73
+ if logic not in ("and", "or"):
74
+ raise ValueError(f"未知的 logic '{raw_logic}',支持的值为: and, or")
75
+
76
+ if not conditions:
77
+ # 无条件时保留所有行
78
+ return df.reset_index(drop=True)
79
+
80
+ # 逐个评估条件,生成布尔掩码
81
+ masks = []
82
+ for cond in conditions:
83
+ mask = evaluate_condition(df, cond)
84
+ masks.append(mask)
85
+
86
+ # 组合条件
87
+ if logic == "or":
88
+ combined = masks[0]
89
+ for m in masks[1:]:
90
+ combined = combined | m
91
+ else:
92
+ combined = masks[0]
93
+ for m in masks[1:]:
94
+ combined = combined & m
95
+
96
+ result = df.loc[combined].reset_index(drop=True)
97
+ return result
@@ -0,0 +1,78 @@
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 Lookup 转换运行器
18
+
19
+ 功能概述:
20
+ - 根据映射表精确替换列值
21
+ - 支持默认值回退
22
+
23
+ 参数:
24
+ mapping: 字典映射 {旧值: 新值}
25
+ default: 未匹配时的默认值(默认 None)
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from typing import Any
31
+
32
+ import pandas as pd
33
+
34
+ from .base import TransformRunner
35
+
36
+
37
+ class LookupRunner(TransformRunner):
38
+ """@classdesc 字典映射转换运行器"""
39
+
40
+ def execute(
41
+ self,
42
+ df: pd.DataFrame,
43
+ input_column: str,
44
+ params: dict[str, Any],
45
+ output_columns: list[str],
46
+ ) -> pd.DataFrame:
47
+ """
48
+ @methoddesc 执行 字典查找/映射转换
49
+
50
+ 业务用途:
51
+ - TransformRunner 协议的标准入口,由 transform 节点调用
52
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
53
+
54
+ 参数:
55
+ df: 源 DataFrame
56
+ input_column: 输入列名
57
+ params: 转换参数字典
58
+ output_columns: 目标输出列名列表
59
+
60
+ 返回:
61
+ 转换后的 DataFrame
62
+ """
63
+ mapping = params.get("mapping", {})
64
+ default = params.get("default", None)
65
+
66
+ if input_column not in df.columns:
67
+ raise ValueError(f"输入列不存在: {input_column}")
68
+
69
+ if not output_columns:
70
+ raise ValueError("Lookup 需要至少一个 output_columns")
71
+
72
+ output_col = output_columns[0]
73
+
74
+ df[output_col] = df[input_column].map(mapping)
75
+ if default is not None:
76
+ df[output_col] = df[output_col].fillna(default)
77
+
78
+ return df
@@ -0,0 +1,70 @@
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 LowerCase 转换运行器
18
+
19
+ 功能概述:
20
+ - 将字符串转换为小写
21
+
22
+ 参数:
23
+ 无
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ from typing import Any
29
+
30
+ import pandas as pd
31
+
32
+ from .base import TransformRunner, stringify_preserve_null
33
+
34
+
35
+ class LowerCaseRunner(TransformRunner):
36
+ """@classdesc 字符串转小写转换运行器"""
37
+
38
+ def execute(
39
+ self,
40
+ df: pd.DataFrame,
41
+ input_column: str,
42
+ params: dict[str, Any],
43
+ output_columns: list[str],
44
+ ) -> pd.DataFrame:
45
+ """
46
+ @methoddesc 执行 转小写转换
47
+
48
+ 业务用途:
49
+ - TransformRunner 协议的标准入口,由 transform 节点调用
50
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
51
+
52
+ 参数:
53
+ df: 源 DataFrame
54
+ input_column: 输入列名
55
+ params: 转换参数字典
56
+ output_columns: 目标输出列名列表
57
+
58
+ 返回:
59
+ 转换后的 DataFrame
60
+ """
61
+ if input_column not in df.columns:
62
+ raise ValueError(f"输入列不存在: {input_column}")
63
+
64
+ if not output_columns:
65
+ raise ValueError("LowerCase 需要至少一个 output_columns")
66
+
67
+ output_col = output_columns[0]
68
+ df[output_col] = stringify_preserve_null(df[input_column]).str.lower()
69
+
70
+ return df
@@ -0,0 +1,90 @@
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 MapValue 转换运行器
18
+
19
+ 功能概述:
20
+ - 以上游值作为索引,查表映射到对应元素
21
+ - 索引越界或无法解析时保留原值
22
+
23
+ 参数:
24
+ mapping: 映射数组(如 ["1","0","X","9","8","7","6","5","4","3","2"])
25
+
26
+ 输入输出:
27
+ 输入: 2
28
+ 输出: "X"(mapping[2])
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ from typing import Any
34
+
35
+ import pandas as pd
36
+
37
+ from .base import TransformRunner
38
+
39
+
40
+ class MapValueRunner(TransformRunner):
41
+ """@classdesc 查表映射转换运行器"""
42
+
43
+ def execute(
44
+ self,
45
+ df: pd.DataFrame,
46
+ input_column: str,
47
+ params: dict[str, Any],
48
+ output_columns: list[str],
49
+ ) -> pd.DataFrame:
50
+ """
51
+ @methoddesc 执行 值映射转换
52
+
53
+ 业务用途:
54
+ - TransformRunner 协议的标准入口,由 transform 节点调用
55
+ - 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
56
+
57
+ 参数:
58
+ df: 源 DataFrame
59
+ input_column: 输入列名
60
+ params: 转换参数字典
61
+ output_columns: 目标输出列名列表
62
+
63
+ 返回:
64
+ 转换后的 DataFrame
65
+ """
66
+ mapping = params.get("mapping", [])
67
+ if not mapping:
68
+ raise ValueError("MapValue 需要 mapping 参数")
69
+
70
+ if input_column not in df.columns:
71
+ raise ValueError(f"输入列不存在: {input_column}")
72
+
73
+ output_col = output_columns[0] if output_columns else "mapped"
74
+
75
+ mapping_arr = list(mapping)
76
+ numeric_idx = pd.to_numeric(df[input_column], errors="coerce")
77
+ # §1.24: 非整数索引报错——原实现 int(1.9) 向下截断当 1 查表,
78
+ # 用户预期"非整数索引无效"却拿到 mapping[1] 的值,错得毫无迹象
79
+ non_integer_mask = numeric_idx.notna() & (numeric_idx != numeric_idx.round())
80
+ if non_integer_mask.any():
81
+ first_idx = numeric_idx.index[non_integer_mask][0]
82
+ raise ValueError(f"map_value 索引 '{df[input_column][first_idx]}'(第 {first_idx} 行)不是整数,无法查表")
83
+ in_range = numeric_idx.notna() & (numeric_idx >= 0) & (numeric_idx < len(mapping_arr))
84
+ idx_int = numeric_idx.where(in_range)
85
+ mapped = idx_int.apply(
86
+ lambda x: mapping_arr[int(x)] if pd.notna(x) and 0 <= int(x) < len(mapping_arr) else None
87
+ )
88
+ df[output_col] = mapped.where(in_range, df[input_column])
89
+
90
+ return df