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,128 @@
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
+ 功能概述:
19
+ - 统一导出各类数据源规格类(Excel、CSV、JSON、SQL)
20
+ - 提供延迟导入和旧版名称兼容支持
21
+ - 通过 __getattr__ 实现按需加载,避免循环导入并减少启动时间
22
+
23
+ 架构设计:
24
+ - 基础类(DataSourceSpec、FileSourceSpec)直接导入
25
+ - 具体实现类(ExcelSourceSpec 等)通过 __getattr__ 延迟导入
26
+ - 旧版名称(ExcelSpec 等)映射到新版类,保持向后兼容
27
+ - __all__ 显式声明公开接口,控制 from module import * 的行为
28
+
29
+ 输入示例:
30
+ from app.shared.core.data_source.specs import DataSourceSpec, ExcelSourceSpec
31
+ from app.shared.core.data_source.specs import create_spec # 工厂函数
32
+
33
+ # 旧版名称仍可正常使用(兼容支持)
34
+ from app.shared.core.data_source.specs import ExcelSpec
35
+
36
+ 输出示例:
37
+ # create_spec 根据 type 字段自动创建对应实例
38
+ spec = create_spec({"type": "csv", "path": "data.csv"})
39
+ # 返回 CSVSourceSpec 实例
40
+ """
41
+
42
+ from .base import (
43
+ DataSourceSpec,
44
+ create_spec,
45
+ get_spec_class,
46
+ register_source_spec,
47
+ )
48
+ from .file_base import FileSourceSpec
49
+
50
+
51
+ # 延迟导入具体实现,避免循环依赖
52
+ # 当用户首次访问这些名称时,才会执行实际导入
53
+ def __getattr__(name: str) -> type[DataSourceSpec]:
54
+ """
55
+ @methoddesc 延迟导入具体数据源规格类
56
+
57
+ 当代码访问 app.shared.core.data_source.specs.ExcelSourceSpec 等属性时,
58
+ 此函数会被调用,执行实际的导入操作。
59
+
60
+ 延迟导入的好处:
61
+ 1. 避免循环依赖(具体子类可能引用其他模块)
62
+ 2. 减少启动时间(只有实际使用的类才会被加载)
63
+ 3. 内存优化(未使用的类不会占用内存)
64
+
65
+ Args:
66
+ name: 要访问的属性名称(如 "ExcelSourceSpec")
67
+
68
+ Returns:
69
+ 对应的数据源规格类
70
+
71
+ Raises:
72
+ AttributeError: 如果名称不是已知的规格类或旧版别名
73
+ """
74
+ if name == "ExcelSourceSpec":
75
+ from .excel_source import ExcelSourceSpec
76
+
77
+ return ExcelSourceSpec
78
+ elif name == "CSVSourceSpec":
79
+ from .csv_source import CSVSourceSpec
80
+
81
+ return CSVSourceSpec
82
+ elif name == "JSONSourceSpec":
83
+ from .json_source import JSONSourceSpec
84
+
85
+ return JSONSourceSpec
86
+ elif name == "SQLSourceSpec":
87
+ from .sql_source import SQLSourceSpec
88
+
89
+ return SQLSourceSpec
90
+ elif name in ("ExcelSpec", "CSVSpec", "JSONSpec"):
91
+ # 旧版名称映射到新类,保持向后兼容
92
+ # 这样使用旧名称的代码无需修改即可继续工作
93
+ if name == "ExcelSpec":
94
+ from .excel_source import ExcelSourceSpec
95
+
96
+ return ExcelSourceSpec
97
+ elif name == "CSVSpec":
98
+ from .csv_source import CSVSourceSpec
99
+
100
+ return CSVSourceSpec
101
+ elif name == "JSONSpec":
102
+ from .json_source import JSONSourceSpec
103
+
104
+ return JSONSourceSpec
105
+ # 如果名称不匹配任何已知类,抛出标准 AttributeError
106
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
107
+
108
+
109
+ # 显式声明模块的公开接口
110
+ # 控制 from app.shared.core.data_source.specs import * 时导入哪些名称
111
+ __all__ = [
112
+ # 基础规格
113
+ "DataSourceSpec", # 所有数据源的抽象基类
114
+ "FileSourceSpec", # 文件数据源的抽象基类
115
+ # 旧版规格(兼容)
116
+ "ExcelSpec",
117
+ "CSVSpec",
118
+ "JSONSpec",
119
+ # 新版规格(延迟加载)
120
+ "ExcelSourceSpec", # Excel 数据源规格
121
+ "CSVSourceSpec", # CSV 数据源规格
122
+ "JSONSourceSpec", # JSON 数据源规格
123
+ "SQLSourceSpec", # SQL 数据源规格
124
+ # 注册函数
125
+ "register_source_spec", # 装饰器:注册数据源规格类
126
+ "get_spec_class", # 根据类型获取规格类
127
+ "create_spec", # 工厂函数:根据字典创建规格实例
128
+ ]
@@ -0,0 +1,258 @@
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 数据源规范基类模块
18
+
19
+ 功能概述:
20
+ - 提供所有数据源配置的抽象基类 DataSourceSpec
21
+ - 使用 Pydantic 进行配置验证和类型约束
22
+ - 支持多态反序列化(通过 discriminator 字段)
23
+ - 提供缓存、超时等通用配置字段
24
+
25
+ 架构设计:
26
+ - 抽象基类模式(ABC + BaseModel)
27
+ - 子类必须设置 source_type 类属性并通过装饰器注册
28
+ - model_validator 自动校验 type 与 source_type 一致性
29
+ - 与 DataSourceLoader 形成规格-加载器配对关系
30
+
31
+ 输入示例:
32
+ @register_source_spec
33
+ class JSONSourceSpec(FileSourceSpec):
34
+ source_type: ClassVar[str] = "json"
35
+ type: str = "json"
36
+ path: str = "data.json"
37
+
38
+ 输出示例:
39
+ spec = DataSourceSpec.model_validate({"type": "json", "path": "data.json"})
40
+ # 自动反序列化为 JSONSourceSpec 实例
41
+ key = spec.get_connection_key() # 返回连接标识(用于缓存)
42
+ """
43
+
44
+ from __future__ import annotations
45
+
46
+ import builtins
47
+ from abc import ABC, abstractmethod
48
+ from typing import Any, ClassVar, Self
49
+
50
+ from pydantic import BaseModel, Field, model_validator
51
+
52
+
53
+ class DataSourceSpec(BaseModel, ABC):
54
+ """
55
+ @classdesc 数据源规范抽象基类
56
+
57
+ 所有数据源配置(文件、数据库、API等)的基类。
58
+ 子类必须设置 source_type 类属性,并通过装饰器注册。
59
+
60
+ 属性说明:
61
+ source_type: 类级别的类型标识,用于反序列化时识别类型
62
+ type: 实例级别的类型字段,必须与 source_type 保持一致
63
+ name: 数据源的显示名称(可选)
64
+ description: 数据源的描述信息(可选)
65
+ cache_enabled: 是否启用数据缓存,默认 True
66
+ cache_ttl: 缓存有效期(秒),默认 300 秒(5分钟)
67
+ timeout_seconds: 数据加载超时时间(秒),默认 30 秒
68
+
69
+ 示例:
70
+ @register_source_spec
71
+ class JSONSourceSpec(FileSourceSpec):
72
+ source_type: ClassVar[str] = "json"
73
+ type: str = "json"
74
+ # ... 其他字段
75
+ """
76
+
77
+ # 类级别的类型标识,用于反序列化时识别类型
78
+ source_type: ClassVar[str] = "abstract"
79
+
80
+ # 实例级别的类型字段
81
+ type: str = Field(..., description="数据源类型标识")
82
+
83
+ # 通用字段(所有数据源都有)
84
+ name: str | None = Field(None, description="数据源显示名称")
85
+ description: str | None = Field(None, description="数据源描述")
86
+
87
+ # 缓存和性能配置
88
+ cache_enabled: bool = Field(True, description="是否启用缓存")
89
+ cache_ttl: int = Field(300, ge=0, description="缓存有效期(秒)")
90
+
91
+ # 超时配置
92
+ timeout_seconds: int = Field(30, ge=1, description="加载超时时间(秒)")
93
+
94
+ @model_validator(mode="after")
95
+ def validate_type_match(self) -> Self:
96
+ """
97
+ @methoddesc 验证实例类型与声明的 type 字段一致
98
+
99
+ 这是为了防止配置文件中声明的类型与实际类不匹配。
100
+ 例如:配置写 type="excel",但实际解析成 CSVSourceSpec 就会报错。
101
+
102
+ Raises:
103
+ ValueError: 当 self.type 与 self.source_type 不一致时抛出
104
+
105
+ Returns:
106
+ 验证通过后的实例自身(遵循 Pydantic 验证器约定)
107
+ """
108
+ if self.type != self.source_type:
109
+ raise ValueError(f"类型不匹配: 声明为 '{self.type}',但类定义为 '{self.source_type}'")
110
+ return self
111
+
112
+ @abstractmethod
113
+ def get_connection_key(self) -> str:
114
+ """
115
+ @methoddesc 获取连接标识符(用于缓存键)
116
+
117
+ 每个数据源实例应有唯一的连接标识。
118
+ 例如:
119
+ - 文件:绝对路径
120
+ - 数据库:connection_url + table
121
+
122
+ Returns:
123
+ 连接标识字符串,用于在缓存中唯一标识该数据源
124
+ """
125
+ pass
126
+
127
+ @abstractmethod
128
+ def get_loader_class(self) -> builtins.type[DataSourceLoader]:
129
+ """
130
+ @methoddesc 获取对应的加载器类
131
+
132
+ 子类需要返回对应的数据加载器类(如 CSVLoader、ExcelLoader 等)。
133
+ 采用延迟导入(在方法内部 import)以避免循环依赖。
134
+
135
+ Returns:
136
+ DataSourceLoader 的子类,负责实际的数据读取工作
137
+ """
138
+ pass
139
+
140
+ def get_discriminator_value(self) -> str:
141
+ """
142
+ @methoddesc 获取类型标识符(用于加载器注册表)
143
+
144
+ 默认返回 source_type,子类可以覆盖。
145
+ 该值用于在反序列化时识别应该创建哪个子类的实例。
146
+
147
+ Returns:
148
+ 类型标识字符串(如 "csv"、"excel"、"sql")
149
+ """
150
+ return self.source_type
151
+
152
+ def to_display_dict(self) -> dict[str, Any]:
153
+ """
154
+ @methoddesc 转换为显示用的字典(去除敏感信息)
155
+
156
+ 用于前端展示或日志记录,避免暴露密码等敏感字段。
157
+ 子类应通过 super().to_display_dict() 复用基类字段。
158
+
159
+ Returns:
160
+ 包含安全字段的字典,至少包含 type、name、source_type
161
+ """
162
+ return {
163
+ "type": self.type,
164
+ "name": self.name,
165
+ "source_type": self.source_type,
166
+ }
167
+
168
+
169
+ # 用于 Pydantic 多态反序列化的类型映射
170
+ # 键是数据源类型标识(如 "csv"),值是对应的 Spec 类
171
+ # 通过 @register_source_spec 装饰器自动填充
172
+ SOURCE_SPEC_REGISTRY: dict[str, type[DataSourceSpec]] = {}
173
+
174
+
175
+ def register_source_spec(spec_class: type[DataSourceSpec]) -> type[DataSourceSpec]:
176
+ """
177
+ @methoddesc 装饰器:注册数据源规范类
178
+
179
+ 用法:
180
+ @register_source_spec
181
+ class JSONSourceSpec(FileSourceSpec):
182
+ source_type = "json"
183
+ ...
184
+
185
+ 注册后,该类型会被加入 SOURCE_SPEC_REGISTRY,
186
+ 从而支持通过 create_spec() 根据 type 字段自动创建对应实例。
187
+
188
+ Args:
189
+ spec_class: 数据源规范类,必须定义 source_type 类属性
190
+
191
+ Returns:
192
+ 原类(装饰器模式,不改变类本身)
193
+
194
+ Raises:
195
+ ValueError: 如果类没有 source_type 属性,或 source_type 为 "abstract"
196
+ """
197
+ # 检查是否定义了 source_type 类属性
198
+ if not hasattr(spec_class, "source_type"):
199
+ raise ValueError(f"{spec_class.__name__} 必须定义 source_type 类属性")
200
+
201
+ # 禁止将抽象基类注册到注册表
202
+ if spec_class.source_type == "abstract":
203
+ raise ValueError(f"{spec_class.__name__} 的 source_type 不能为 'abstract'")
204
+
205
+ # 将类注册到全局字典中,键为 source_type 的值
206
+ SOURCE_SPEC_REGISTRY[spec_class.source_type] = spec_class
207
+ return spec_class
208
+
209
+
210
+ def get_spec_class(source_type: str) -> type[DataSourceSpec] | None:
211
+ """
212
+ @methoddesc 根据类型标识获取对应的 Spec 类
213
+
214
+ 用于在反序列化时查找对应的类,或判断某种类型是否被支持。
215
+
216
+ Args:
217
+ source_type: 数据源类型标识(如 "excel", "json", "csv", "sql")
218
+
219
+ Returns:
220
+ Spec 类,或 None(如果该类型未注册)
221
+ """
222
+ return SOURCE_SPEC_REGISTRY.get(source_type)
223
+
224
+
225
+ def create_spec(source_dict: dict[str, Any]) -> DataSourceSpec:
226
+ """
227
+ @methoddesc 根据字典创建对应的 Spec 实例
228
+
229
+ 这是多态反序列化的入口:根据字典中的 "type" 字段,
230
+ 自动查找并实例化对应的 Spec 子类。
231
+
232
+ Args:
233
+ source_dict: 包含 type 字段的配置字典,其他字段取决于具体子类
234
+
235
+ Returns:
236
+ DataSourceSpec 子类的实例
237
+
238
+ Raises:
239
+ ValueError: 缺少 type 字段,或 type 对应的类型未注册
240
+ """
241
+ # 从字典中提取类型标识
242
+ source_type = source_dict.get("type")
243
+ if not source_type:
244
+ raise ValueError("配置字典必须包含 'type' 字段")
245
+
246
+ # 查找对应的数据源规格类
247
+ spec_class = get_spec_class(source_type)
248
+ if not spec_class:
249
+ raise ValueError(f"未知的数据源类型: '{source_type}'。支持的类型: {list(SOURCE_SPEC_REGISTRY.keys())}")
250
+
251
+ # 使用 Pydantic 模型解析字典,自动进行类型校验和转换
252
+ return spec_class(**source_dict)
253
+
254
+
255
+ # 延迟导入,避免循环依赖
256
+ # DataSourceLoader 在加载时会引用 DataSourceSpec,
257
+ # 如果在这里顶层导入会导致循环导入问题
258
+ from ..loaders.base import DataSourceLoader
@@ -0,0 +1,137 @@
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 CSV 数据源规范模块
18
+
19
+ 功能概述:
20
+ - 定义 CSV 文件的数据源规格类
21
+ - 支持自定义分隔符、引号字符、转义字符
22
+ - 提供自动编码检测和编码回退机制
23
+ - 支持坏行处理策略(error/warn/skip)
24
+
25
+ 架构设计:
26
+ - 继承自 FileSourceSpec,复用文件通用配置
27
+ - 通过 @register_source_spec 装饰器自动注册到规格注册表
28
+ - 与 CSVLoader 配对使用,实现 CSV 数据加载
29
+ - Pydantic Field 提供默认值和校验约束
30
+
31
+ 输入示例:
32
+ spec = CSVSourceSpec(
33
+ path="data/users.csv",
34
+ delimiter=",",
35
+ quotechar='"',
36
+ encoding="utf-8",
37
+ encoding_detection=True,
38
+ on_bad_lines="error"
39
+ )
40
+
41
+ 输出示例:
42
+ loader_class = spec.get_loader_class() # 返回 CSVLoader
43
+ display = spec.to_display_dict()
44
+ # {"path": "data/users.csv", "delimiter": ",", "encoding": "utf-8", ...}
45
+ """
46
+
47
+ from __future__ import annotations
48
+
49
+ import builtins
50
+ from typing import TYPE_CHECKING, Any, ClassVar, Literal
51
+
52
+ if TYPE_CHECKING:
53
+ from ..loaders.base import DataSourceLoader
54
+
55
+ from pydantic import Field
56
+
57
+ from .base import register_source_spec
58
+ from .file_base import FileSourceSpec
59
+
60
+
61
+ @register_source_spec
62
+ class CSVSourceSpec(FileSourceSpec):
63
+ """
64
+ @classdesc CSV 数据源配置
65
+
66
+ 支持标准 CSV(逗号分隔值)文件,提供自动编码检测功能。
67
+ CSV 是纯文本格式,每行是一条记录,字段之间用分隔符隔开。
68
+
69
+ 特有配置:
70
+ delimiter: 字段分隔符(默认逗号),也可以是分号、制表符等
71
+ quotechar: 引号字符(默认双引号),用于包裹包含分隔符的字段
72
+ escapechar: 转义字符,用于转义引号等特殊字符
73
+ encoding_detection: 是否自动检测编码(通过 chardet 等库)
74
+ fallback_encodings: 编码检测失败时依次尝试的编码列表
75
+ on_bad_lines: 遇到格式错误的行时的处理方式
76
+
77
+ 示例:
78
+ ```yaml
79
+ source:
80
+ type: csv
81
+ path: data/users.csv
82
+ delimiter: ","
83
+ encoding: utf-8
84
+ ```
85
+ """
86
+
87
+ source_type: ClassVar[str] = "csv"
88
+ type: str = "csv"
89
+
90
+ # CSV 特有配置
91
+ delimiter: str = Field(",", min_length=1, max_length=1, description="字段分隔符(如逗号、分号、制表符)")
92
+ quotechar: str = Field('"', min_length=1, max_length=1, description="引号字符(用于包裹包含特殊字符的字段)")
93
+ escapechar: str | None = Field(None, description="转义字符(用于转义引号等)")
94
+
95
+ # 编码处理
96
+ encoding_detection: bool = Field(True, description="是否自动检测编码(检测失败时使用 fallback_encodings)")
97
+ fallback_encodings: list[str] = Field(
98
+ default_factory=lambda: ["utf-8", "gbk", "latin1"],
99
+ description="编码检测失败时尝试的编码列表(按优先级排序)",
100
+ )
101
+
102
+ # 错误处理
103
+ # 默认 error(校验工具红线:字段数超表头的坏行静默跳过=漏报,用户在"缺行"
104
+ # 的数据上看到全绿;warn/skip 是显式 opt-in 的容忍行为)
105
+ on_bad_lines: Literal["error", "warn", "skip"] = Field(
106
+ "error", description="遇到坏行的处理方式:error-报错(默认,fail-closed), warn-警告并跳过, skip-静默跳过"
107
+ )
108
+
109
+ def get_loader_class(self) -> builtins.type[DataSourceLoader]:
110
+ """
111
+ @methoddesc 获取 CSV 数据加载器类
112
+
113
+ 延迟导入 CSVLoader,避免循环依赖。
114
+ CSVLoader 负责调用 pandas.read_csv 读取数据。
115
+
116
+ Returns:
117
+ CSVLoader 类
118
+ """
119
+ from ..loaders.csv_loader import CSVLoader
120
+
121
+ return CSVLoader
122
+
123
+ def to_display_dict(self) -> dict[str, Any]:
124
+ """
125
+ @methoddesc 转换为显示用的字典
126
+
127
+ 在基类的基础上增加 CSV 特有的展示字段,
128
+ 使用 repr() 显示分隔符以便区分空格等特殊字符。
129
+
130
+ Returns:
131
+ 包含 delimiter、encoding 等 CSV 特有字段的字典
132
+ """
133
+ return {
134
+ **super().to_display_dict(),
135
+ "delimiter": repr(self.delimiter), # 使用 repr 显示特殊字符
136
+ "encoding": self.encoding,
137
+ }
@@ -0,0 +1,141 @@
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 Excel 数据源规范模块
18
+
19
+ 功能概述:
20
+ - 定义 Excel 文件的数据源规格类
21
+ - 支持 .xlsx 和 .xls 格式
22
+ - 提供工作表名称/索引选择和读取引擎配置
23
+ - 支持数据类型自动推断开关
24
+
25
+ 架构设计:
26
+ - 继承自 FileSourceSpec,复用文件通用配置
27
+ - 通过 @register_source_spec 装饰器自动注册到规格注册表
28
+ - 与 ExcelLoader 配对使用,实现 Excel 数据加载
29
+ - 连接键包含文件路径和工作表信息,确保唯一性
30
+
31
+ 输入示例:
32
+ spec = ExcelSourceSpec(
33
+ path="data/users.xlsx",
34
+ sheet="Sheet1",
35
+ sheet_index=0,
36
+ engine="openpyxl",
37
+ dtype_inference=True
38
+ )
39
+
40
+ 输出示例:
41
+ key = spec.get_connection_key() # "absolute_path#Sheet1"
42
+ loader_class = spec.get_loader_class() # 返回 ExcelLoader
43
+ display = spec.to_display_dict()
44
+ # {"path": "data/users.xlsx", "sheet": "Sheet1", "engine": "openpyxl", ...}
45
+ """
46
+
47
+ from __future__ import annotations
48
+
49
+ import builtins
50
+ from typing import TYPE_CHECKING, Any, ClassVar, Literal
51
+
52
+ if TYPE_CHECKING:
53
+ from ..loaders.base import DataSourceLoader
54
+
55
+ from pydantic import Field
56
+
57
+ from .base import register_source_spec
58
+ from .file_base import FileSourceSpec
59
+
60
+
61
+ @register_source_spec
62
+ class ExcelSourceSpec(FileSourceSpec):
63
+ """
64
+ @classdesc Excel 数据源配置
65
+
66
+ 支持 .xlsx(Excel 2007+)和 .xls(Excel 97-2003)格式的 Excel 文件。
67
+ Excel 文件可以包含多个工作表(sheet),需要指定读取哪个工作表。
68
+
69
+ 特有配置:
70
+ sheet: 工作表名称(如 "Sheet1"),默认 None(使用 sheet_index)
71
+ sheet_index: 工作表索引(从0开始),当 sheet 未指定时使用
72
+ engine: 读取引擎,openpyxl 支持 .xlsx,xlrd 支持 .xls
73
+ dtype_inference: 是否自动推断数据类型(如将数字字符串转为数值)
74
+
75
+ 示例:
76
+ ```yaml
77
+ source:
78
+ type: excel
79
+ path: data/users.xlsx
80
+ sheet: Sheet1
81
+ header_row: 0
82
+ ```
83
+ """
84
+
85
+ source_type: ClassVar[str] = "excel"
86
+ type: str = "excel"
87
+
88
+ # Excel 特有配置
89
+ sheet: str | None = Field(None, description="工作表名称(优先使用);如果为 None,则使用 sheet_index")
90
+ sheet_index: int = Field(0, ge=0, description="工作表索引(从0开始),当 sheet 未指定时使用")
91
+ engine: Literal["openpyxl", "xlrd"] = Field(
92
+ "openpyxl", description="读取引擎:openpyxl(支持 .xlsx)或 xlrd(支持 .xls)"
93
+ )
94
+
95
+ # 数据类型推断
96
+ dtype_inference: bool = Field(True, description="是否自动推断数据类型(如 '123' 转为整数 123)")
97
+
98
+ def get_connection_key(self) -> str:
99
+ """
100
+ @methoddesc 获取连接标识符
101
+
102
+ Excel 的连接键由文件路径和工作表信息组合而成,
103
+ 确保同一个文件的不同工作表被视为不同的数据源。
104
+
105
+ Returns:
106
+ "文件绝对路径#工作表名称" 格式的字符串
107
+ """
108
+ base_key = super().get_connection_key() # 获取文件绝对路径
109
+ # 如果指定了 sheet 名称则使用,否则使用 sheet_index
110
+ sheet = self.sheet or f"sheet_{self.sheet_index}"
111
+ return f"{base_key}#{sheet}"
112
+
113
+ def get_loader_class(self) -> builtins.type[DataSourceLoader]:
114
+ """
115
+ @methoddesc 获取 Excel 数据加载器类
116
+
117
+ 延迟导入 ExcelLoader,避免循环依赖。
118
+ ExcelLoader 负责调用 pandas.read_excel 读取数据。
119
+
120
+ Returns:
121
+ ExcelLoader 类
122
+ """
123
+ from ..loaders.excel_loader import ExcelLoader
124
+
125
+ return ExcelLoader
126
+
127
+ def to_display_dict(self) -> dict[str, Any]:
128
+ """
129
+ @methoddesc 转换为显示用的字典
130
+
131
+ 在基类的基础上增加 Excel 特有的展示字段。
132
+ 如果未指定 sheet 名称,则显示为 "sheet_索引" 形式。
133
+
134
+ Returns:
135
+ 包含 sheet、engine 等 Excel 特有字段的字典
136
+ """
137
+ return {
138
+ **super().to_display_dict(),
139
+ "sheet": self.sheet or f"sheet_{self.sheet_index}",
140
+ "engine": self.engine,
141
+ }