celestialflow 3.2.2__tar.gz → 3.2.3__tar.gz

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 (116) hide show
  1. {celestialflow-3.2.2 → celestialflow-3.2.3}/PKG-INFO +51 -21
  2. {celestialflow-3.2.2 → celestialflow-3.2.3}/README.md +48 -18
  3. {celestialflow-3.2.2 → celestialflow-3.2.3}/pyproject.toml +4 -7
  4. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/graph/core_graph.py +69 -85
  5. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/graph/core_structure.py +22 -16
  6. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/graph/util_analysis.py +3 -6
  7. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/graph/util_serialize.py +6 -9
  8. celestialflow-3.2.3/src/celestialflow/graph/util_types.py +27 -0
  9. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/observability/core_progress.py +1 -1
  10. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/observability/core_report.py +20 -21
  11. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/persistence/core_fail.py +27 -9
  12. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/persistence/core_log.py +15 -18
  13. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/persistence/core_success.py +7 -6
  14. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/persistence/util_jsonl.py +14 -29
  15. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/core_dispatch.py +35 -15
  16. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/core_envelope.py +16 -10
  17. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/core_metrics.py +50 -40
  18. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/core_queue.py +20 -13
  19. celestialflow-3.2.3/src/celestialflow/runtime/util_constant.py +19 -0
  20. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/util_estimators.py +14 -18
  21. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/util_types.py +47 -42
  22. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/stage/core_executor.py +83 -129
  23. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/stage/core_stage.py +41 -10
  24. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/stage/core_stages.py +103 -77
  25. celestialflow-3.2.3/src/celestialflow/stage/util_types.py +6 -0
  26. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/utils/util_benchmark.py +5 -3
  27. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/utils/util_clone.py +22 -15
  28. celestialflow-3.2.3/src/celestialflow/web/config.json +34 -0
  29. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/core_server.py +219 -217
  30. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/routes/pull_routes.py +5 -7
  31. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/routes/push_routes.py +13 -9
  32. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/css/base.css +0 -38
  33. celestialflow-3.2.3/src/celestialflow/web/static/css/dashboard.css +175 -0
  34. celestialflow-3.2.3/src/celestialflow/web/static/css/dashboard_analysis.css +41 -0
  35. celestialflow-3.2.3/src/celestialflow/web/static/css/dashboard_history.css +89 -0
  36. celestialflow-3.2.3/src/celestialflow/web/static/css/dashboard_statuses.css +225 -0
  37. celestialflow-3.2.3/src/celestialflow/web/static/css/dashboard_structure.css +27 -0
  38. celestialflow-3.2.3/src/celestialflow/web/static/css/dashboard_summary.css +136 -0
  39. celestialflow-3.2.3/src/celestialflow/web/static/css/errors.css +364 -0
  40. celestialflow-3.2.3/src/celestialflow/web/static/css/injection_editor.css +223 -0
  41. celestialflow-3.2.3/src/celestialflow/web/static/css/injection_layout.css +119 -0
  42. celestialflow-3.2.3/src/celestialflow/web/static/css/injection_nodes.css +92 -0
  43. celestialflow-3.2.3/src/celestialflow/web/static/css/injection_preview.css +140 -0
  44. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/dashboard_analysis.js +27 -10
  45. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/dashboard_history.js +48 -34
  46. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/dashboard_statuses.js +64 -47
  47. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/dashboard_structure.js +28 -16
  48. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/dashboard_summary.js +8 -7
  49. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/errors.js +48 -25
  50. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/i18n.js +145 -87
  51. celestialflow-3.2.3/src/celestialflow/web/static/js/injection.js +675 -0
  52. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/layout_editor.js +58 -26
  53. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/main.js +56 -14
  54. celestialflow-3.2.3/src/celestialflow/web/static/js/utils.js +177 -0
  55. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/js/web_config.js +269 -118
  56. celestialflow-3.2.3/src/celestialflow/web/static/ts/dashboard_analysis.ts +95 -0
  57. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/dashboard_history.ts +63 -52
  58. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/dashboard_statuses.ts +85 -56
  59. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/dashboard_structure.ts +88 -80
  60. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/dashboard_summary.ts +8 -8
  61. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/errors.ts +74 -34
  62. celestialflow-3.2.3/src/celestialflow/web/static/ts/globals.d.ts +155 -0
  63. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/i18n.ts +173 -91
  64. celestialflow-3.2.3/src/celestialflow/web/static/ts/injection.ts +787 -0
  65. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/layout_editor.ts +70 -37
  66. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/main.ts +135 -89
  67. celestialflow-3.2.3/src/celestialflow/web/static/ts/utils.ts +205 -0
  68. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/ts/web_config.ts +557 -357
  69. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/templates/index.html +192 -215
  70. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/util_error.py +29 -27
  71. celestialflow-3.2.3/src/celestialflow/web/util_models.py +96 -0
  72. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow.egg-info/PKG-INFO +51 -21
  73. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow.egg-info/SOURCES.txt +7 -2
  74. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow.egg-info/requires.txt +1 -1
  75. celestialflow-3.2.2/src/celestialflow/persistence/util_constant.py +0 -12
  76. celestialflow-3.2.2/src/celestialflow/web/config.json +0 -23
  77. celestialflow-3.2.2/src/celestialflow/web/static/css/dashboard.css +0 -71
  78. celestialflow-3.2.2/src/celestialflow/web/static/css/dashboard_analysis.css +0 -39
  79. celestialflow-3.2.2/src/celestialflow/web/static/css/dashboard_history.css +0 -87
  80. celestialflow-3.2.2/src/celestialflow/web/static/css/dashboard_statuses.css +0 -238
  81. celestialflow-3.2.2/src/celestialflow/web/static/css/dashboard_structure.css +0 -25
  82. celestialflow-3.2.2/src/celestialflow/web/static/css/dashboard_summary.css +0 -134
  83. celestialflow-3.2.2/src/celestialflow/web/static/css/errors.css +0 -315
  84. celestialflow-3.2.2/src/celestialflow/web/static/css/injection.css +0 -615
  85. celestialflow-3.2.2/src/celestialflow/web/static/js/injection.js +0 -404
  86. celestialflow-3.2.2/src/celestialflow/web/static/js/utils.js +0 -117
  87. celestialflow-3.2.2/src/celestialflow/web/static/ts/dashboard_analysis.ts +0 -70
  88. celestialflow-3.2.2/src/celestialflow/web/static/ts/globals.d.ts +0 -27
  89. celestialflow-3.2.2/src/celestialflow/web/static/ts/injection.ts +0 -455
  90. celestialflow-3.2.2/src/celestialflow/web/static/ts/utils.ts +0 -132
  91. celestialflow-3.2.2/src/celestialflow/web/util_models.py +0 -72
  92. {celestialflow-3.2.2 → celestialflow-3.2.3}/setup.cfg +0 -0
  93. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/__init__.py +0 -0
  94. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/funnel/__init__.py +0 -0
  95. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/funnel/core_inlet.py +0 -0
  96. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/funnel/core_spout.py +0 -0
  97. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/graph/__init__.py +0 -0
  98. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/observability/__init__.py +0 -0
  99. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/observability/core_observer.py +0 -0
  100. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/persistence/__init__.py +0 -0
  101. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/__init__.py +0 -0
  102. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/util_errors.py +0 -0
  103. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/runtime/util_hash.py +0 -0
  104. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/stage/__init__.py +0 -0
  105. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/utils/__init__.py +0 -0
  106. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/utils/util_collections.py +0 -0
  107. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/utils/util_format.py +0 -0
  108. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/__init__.py +0 -0
  109. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/routes/__init__.py +0 -0
  110. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/css/_colors.css +0 -0
  111. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/static/favicon.ico +0 -0
  112. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/util_cal.py +0 -0
  113. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow/web/util_config.py +0 -0
  114. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow.egg-info/dependency_links.txt +0 -0
  115. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow.egg-info/entry_points.txt +0 -0
  116. {celestialflow-3.2.2 → celestialflow-3.2.3}/src/celestialflow.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: celestialflow
3
- Version: 3.2.2
3
+ Version: 3.2.3
4
4
  Summary: A flexible GRAPH-based task orchestration framework.
5
5
  Author-email: Mr-xiaotian <mingxiaomingtian@gmail.com>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Operating System :: OS Independent
13
13
  Classifier: Framework :: FastAPI
14
14
  Classifier: Topic :: Software Development :: Libraries
15
- Requires-Python: >=3.11
15
+ Requires-Python: >=3.12
16
16
  Description-Content-Type: text/markdown
17
17
  Requires-Dist: tqdm
18
18
  Requires-Dist: fastapi
@@ -21,7 +21,7 @@ Requires-Dist: requests
21
21
  Requires-Dist: networkx
22
22
  Requires-Dist: redis
23
23
  Requires-Dist: jinja2
24
- Requires-Dist: celestialtree>=0.1.2
24
+ Requires-Dist: celestialtree>=0.1.3
25
25
 
26
26
  # CelestialFlow ——一个轻量级、可并行、基于图结构的 Python 任务调度框架
27
27
 
@@ -240,12 +240,12 @@ flowchart TD
240
240
 
241
241
  ## 环境要求(Requirements)
242
242
 
243
- **CelestialFlow** 基于 Python 3.11+,并依赖以下核心组件。
243
+ **CelestialFlow** 基于 Python 3.12+,并依赖以下核心组件。
244
244
  请确保你的环境能够正常安装这些依赖(`pip install celestialflow` 会自动安装)。
245
245
 
246
246
  | 依赖包 | 说明 |
247
247
  | ----------------- | ---- |
248
- | **Python ≥ 3.11** | 运行环境,建议使用 3.11 及以上版本 |
248
+ | **Python ≥ 3.12** | 运行环境,建议使用 3.12 及以上版本 |
249
249
  | **fastapi** | Web 服务接口框架(用于任务可视化与远程控制) |
250
250
  | **uvicorn** | FastAPI 的高性能 ASGI 服务器 |
251
251
  | **requests** | HTTP 客户端库,用于任务状态上报与远程调用 |
@@ -260,31 +260,61 @@ flowchart TD
260
260
  <p align="center">
261
261
  <img src="https://raw.githubusercontent.com/Mr-xiaotian/CelestialFlow/main/img/file_structure.svg" alt="FileStructure" />
262
262
  <br/>
263
- <em>celestial-flow 3.2.2</em>
263
+ <em>celestial-flow 3.2.3</em>
264
264
  </p>
265
265
 
266
266
  (该视图由我的另一个项目[CelestialVault](https://github.com/Mr-xiaotian/CelestialVault)中inst_file.FileTree.print_tree()生成。转换为图片则借助[Carbon](https://carbon.now.sh)。)
267
267
 
268
268
  ## 版本日志(Version Log)
269
- - 3.2.2
269
+ - 3.2.3
270
270
  - feat:
271
- - `core_server` 中添加数据锁, 避免并发访问导致的错误状态
272
- - 优化前端设置面板的显示, 现在只显示全局设置与当前页相关设置
273
- - 在设置面板中添加全局设置中的 "是否自动更新" 选项与错误日志页面中的 "排序方式" 两项
271
+ - **[IMPORTANT]** 前端仪表盘设置面板中新添 `节点等待使用全局估计` 开关, 开启后节点卡片中原 `等待` 量将被替换为 `全局等待`, 并在卡片中显示 `全局剩余时间`
272
+ - 这个功能非常有趣, 开启前后能看到数值大幅波动
273
+ - **[IMPORTANT]** 重写任务注入页面, 现在实用性远远高于之前版本
274
+ - 可以给每个节点单独配置注入任务列表, 在一起发送
275
+ - **[IMPORTANT]** 移除 `TaskExecutor` 中 `get_args` 与 `process_result` 两个方法, 以及 `unpack_task_args` 属性
276
+ - 这是为引入泛型必要的修改
277
+ - `process_result` 功能很简单, 对func输出的result进行再次处理, 但事实上在输入func前对齐进行包装也能达到一样的效果
278
+ - `get_args` 功能更为复杂, 可以直接将前一节点提供的 `result` 转为当前节点所需的 `task` 类型, 非常灵活; 但问题在于太过灵活, 导致使用心智负担很大
279
+ - `unpack_task_args` 就是 `get_args` 带来的一项心智负担, 默认 `get_args` 会把 `task` 进行 `(task, )` 包裹后发送给func, 开启 `unpack_task_args` 后则直接发送
280
+ - **[IMPORTANT]** `graph` 的init参数中添加 `name`, 以与 `executor` `stage` 一致
281
+ - 破坏接口破坏性更新
282
+ - 在日志的 `start_graph` `end_graph` 与web端的 `graph_anaylysis` 中都有显示
283
+ - 彻底移除前后端通信中的 `graph_summary` , 原本残余的 `全局剩余时间` 现在拆为各个节点的 `全局等待` 与 `全局剩余时间`
284
+ - 这里所说的 `全局` 意为根据图论关系, 由上游剩余的任务数估算下游总共能获得多少任务
285
+ - 例如: 图关系 `A -> B`, A已处理任务2, 未处理任务3, B已处理任务4, 未处理任务2. 这意味着A成功的2个任务为B带来总共了6个任务, 那么我们可以据此估计B总功能获取"3/2*6=9"个任务, 因此B的 `等待` 任务数量为2, 但 `全局等待` 任务数量为5
286
+ - 前端中添加部分提示气泡, 鼠标放上去后可以介绍相关信息, 例如本次新加入的节点 `全局等待` 的含义
287
+ - 移除前端中节点卡片的拖拽功能
288
+ - 这个功能是在最早加入web页面时添加的, 当时感觉很帅, 但现在有点玩腻了
289
+ - 前端错误日志页面添加 `任务注入` 按钮, 可以把选定任务直接添加到任务注入页面中节点的代注入任务列表中
290
+ - 在 `TaskSplitter` 中添加 `split_item` 方法, 可自由定义
291
+ - 原本的 `splitter` 非常依赖于 `get_args`, 现在通过 `split_item` 方法稍微弥补其缺失的灵活性
274
292
  - refactor:
275
- - 删除前后端通讯中的 `summary` , 节点的总体预期结束时间由各个节点的 `status` 分别传递, 并由前端计算整体的预期结束时间
276
- - 修改 `structure_graph`(原 `structure_json`) 字段的内容, 现在更为简洁, 避免信息冗余, 同时方便后续拓展
293
+ - **[IMPORTANT]** 引入泛型, 同时强制性要求py版本>=3.12
294
+ - 泛型的引入使得代码更加类型安全, 同时也提高了代码的可读性
295
+ - 3.12版本对泛型的表述非常直观
296
+ - 移除前端代码中所有对 `localStorage` 的使用
297
+ - 在已经有config配置文件的情况下意义不大, 反而会带来困扰
298
+ - 将任务队列的drain操作从graph层移至stage层
299
+ - 之前不能这样做是因为节点的 `stage_mode` 可能为 `process`, 此时在主进程持有的节点非真实运行的节点
300
+ - 算是3.2.0版本带来的持久影响之一
301
+ - 优化 `TaskMetric` 中对锁的使用
302
+ - 移除 `TaskEnvelope` 中的 `change_id()` 方法, 现在默认envelope不可变, 同时 `emit_retry_envelope` 中不再把原有的envelope的id修改后继续提交给worker, 而是直接使用新生成的envelope
303
+ - 移除错误日志中的 `error` `error_repr` `task_repr` 字段
304
+ - 修改前后端通信中任务注入数据的格式, 以方便同时提交多个节点的任务数据
305
+ - 前端代码中开启strict检查
306
+ - 将前端中 `injection.css` 文件拆分成多个文件
307
+ - 修改 `config.json` 的数据格式, 现在按照生效区域进行分类
308
+ - 收紧前端中的数据类型
309
+ - 添加 `ReportTaskGraph` 类型, 专门用于给reporter做类型声明
277
310
  - fix:
278
- - 修复指标折线图中指标选择失效的问题
279
- - 修改 `report.stop` 中_refresh_all的执行顺序, 避免与thread中的刷新冲突
280
- - `graph._finalize_nodes` 中添加对thread未终止的防御性检查
281
- - 修复 `stage` 中 `start_time` 在未定义前被 `report` 调用的问题
282
- - 修复 `TaskRedisTransport._transport` 中使用 `id()` 来计算task_id导致的问题
283
- - 修复部分任务无法被hash导致的panic问题
311
+ - i18n本地化在部分字段上失效的问题
312
+ - 直接点击仪表盘中错误数字跳转到错误日志页后, 设置面板显示的还是仪表盘页面的设置
313
+ - 前端中为各项仪表盘请求添加 `RequestSeq`, 以避免前后发送两个请求, 但因为延迟关系, 先发送的请求的返回覆盖掉后发送请求的返回
314
+ - 修复前端中各项空字段需要在第一次refresh才显示的问题, 体感上会导致"加载"很慢
284
315
  - chore:
285
- - 删除所有的 `type: ignore`
286
- - 好看不少
287
- - 在 `start_*` 函数的doc-string中标注该函数为一次性调用函数
316
+ - 将文档更新翻译为英/日两语
317
+ - 这项操作太耗token了
288
318
 
289
319
  更多过往日志可看:
290
320
 
@@ -215,12 +215,12 @@ flowchart TD
215
215
 
216
216
  ## 环境要求(Requirements)
217
217
 
218
- **CelestialFlow** 基于 Python 3.11+,并依赖以下核心组件。
218
+ **CelestialFlow** 基于 Python 3.12+,并依赖以下核心组件。
219
219
  请确保你的环境能够正常安装这些依赖(`pip install celestialflow` 会自动安装)。
220
220
 
221
221
  | 依赖包 | 说明 |
222
222
  | ----------------- | ---- |
223
- | **Python ≥ 3.11** | 运行环境,建议使用 3.11 及以上版本 |
223
+ | **Python ≥ 3.12** | 运行环境,建议使用 3.12 及以上版本 |
224
224
  | **fastapi** | Web 服务接口框架(用于任务可视化与远程控制) |
225
225
  | **uvicorn** | FastAPI 的高性能 ASGI 服务器 |
226
226
  | **requests** | HTTP 客户端库,用于任务状态上报与远程调用 |
@@ -235,31 +235,61 @@ flowchart TD
235
235
  <p align="center">
236
236
  <img src="https://raw.githubusercontent.com/Mr-xiaotian/CelestialFlow/main/img/file_structure.svg" alt="FileStructure" />
237
237
  <br/>
238
- <em>celestial-flow 3.2.2</em>
238
+ <em>celestial-flow 3.2.3</em>
239
239
  </p>
240
240
 
241
241
  (该视图由我的另一个项目[CelestialVault](https://github.com/Mr-xiaotian/CelestialVault)中inst_file.FileTree.print_tree()生成。转换为图片则借助[Carbon](https://carbon.now.sh)。)
242
242
 
243
243
  ## 版本日志(Version Log)
244
- - 3.2.2
244
+ - 3.2.3
245
245
  - feat:
246
- - `core_server` 中添加数据锁, 避免并发访问导致的错误状态
247
- - 优化前端设置面板的显示, 现在只显示全局设置与当前页相关设置
248
- - 在设置面板中添加全局设置中的 "是否自动更新" 选项与错误日志页面中的 "排序方式" 两项
246
+ - **[IMPORTANT]** 前端仪表盘设置面板中新添 `节点等待使用全局估计` 开关, 开启后节点卡片中原 `等待` 量将被替换为 `全局等待`, 并在卡片中显示 `全局剩余时间`
247
+ - 这个功能非常有趣, 开启前后能看到数值大幅波动
248
+ - **[IMPORTANT]** 重写任务注入页面, 现在实用性远远高于之前版本
249
+ - 可以给每个节点单独配置注入任务列表, 在一起发送
250
+ - **[IMPORTANT]** 移除 `TaskExecutor` 中 `get_args` 与 `process_result` 两个方法, 以及 `unpack_task_args` 属性
251
+ - 这是为引入泛型必要的修改
252
+ - `process_result` 功能很简单, 对func输出的result进行再次处理, 但事实上在输入func前对齐进行包装也能达到一样的效果
253
+ - `get_args` 功能更为复杂, 可以直接将前一节点提供的 `result` 转为当前节点所需的 `task` 类型, 非常灵活; 但问题在于太过灵活, 导致使用心智负担很大
254
+ - `unpack_task_args` 就是 `get_args` 带来的一项心智负担, 默认 `get_args` 会把 `task` 进行 `(task, )` 包裹后发送给func, 开启 `unpack_task_args` 后则直接发送
255
+ - **[IMPORTANT]** `graph` 的init参数中添加 `name`, 以与 `executor` `stage` 一致
256
+ - 破坏接口破坏性更新
257
+ - 在日志的 `start_graph` `end_graph` 与web端的 `graph_anaylysis` 中都有显示
258
+ - 彻底移除前后端通信中的 `graph_summary` , 原本残余的 `全局剩余时间` 现在拆为各个节点的 `全局等待` 与 `全局剩余时间`
259
+ - 这里所说的 `全局` 意为根据图论关系, 由上游剩余的任务数估算下游总共能获得多少任务
260
+ - 例如: 图关系 `A -> B`, A已处理任务2, 未处理任务3, B已处理任务4, 未处理任务2. 这意味着A成功的2个任务为B带来总共了6个任务, 那么我们可以据此估计B总功能获取"3/2*6=9"个任务, 因此B的 `等待` 任务数量为2, 但 `全局等待` 任务数量为5
261
+ - 前端中添加部分提示气泡, 鼠标放上去后可以介绍相关信息, 例如本次新加入的节点 `全局等待` 的含义
262
+ - 移除前端中节点卡片的拖拽功能
263
+ - 这个功能是在最早加入web页面时添加的, 当时感觉很帅, 但现在有点玩腻了
264
+ - 前端错误日志页面添加 `任务注入` 按钮, 可以把选定任务直接添加到任务注入页面中节点的代注入任务列表中
265
+ - 在 `TaskSplitter` 中添加 `split_item` 方法, 可自由定义
266
+ - 原本的 `splitter` 非常依赖于 `get_args`, 现在通过 `split_item` 方法稍微弥补其缺失的灵活性
249
267
  - refactor:
250
- - 删除前后端通讯中的 `summary` , 节点的总体预期结束时间由各个节点的 `status` 分别传递, 并由前端计算整体的预期结束时间
251
- - 修改 `structure_graph`(原 `structure_json`) 字段的内容, 现在更为简洁, 避免信息冗余, 同时方便后续拓展
268
+ - **[IMPORTANT]** 引入泛型, 同时强制性要求py版本>=3.12
269
+ - 泛型的引入使得代码更加类型安全, 同时也提高了代码的可读性
270
+ - 3.12版本对泛型的表述非常直观
271
+ - 移除前端代码中所有对 `localStorage` 的使用
272
+ - 在已经有config配置文件的情况下意义不大, 反而会带来困扰
273
+ - 将任务队列的drain操作从graph层移至stage层
274
+ - 之前不能这样做是因为节点的 `stage_mode` 可能为 `process`, 此时在主进程持有的节点非真实运行的节点
275
+ - 算是3.2.0版本带来的持久影响之一
276
+ - 优化 `TaskMetric` 中对锁的使用
277
+ - 移除 `TaskEnvelope` 中的 `change_id()` 方法, 现在默认envelope不可变, 同时 `emit_retry_envelope` 中不再把原有的envelope的id修改后继续提交给worker, 而是直接使用新生成的envelope
278
+ - 移除错误日志中的 `error` `error_repr` `task_repr` 字段
279
+ - 修改前后端通信中任务注入数据的格式, 以方便同时提交多个节点的任务数据
280
+ - 前端代码中开启strict检查
281
+ - 将前端中 `injection.css` 文件拆分成多个文件
282
+ - 修改 `config.json` 的数据格式, 现在按照生效区域进行分类
283
+ - 收紧前端中的数据类型
284
+ - 添加 `ReportTaskGraph` 类型, 专门用于给reporter做类型声明
252
285
  - fix:
253
- - 修复指标折线图中指标选择失效的问题
254
- - 修改 `report.stop` 中_refresh_all的执行顺序, 避免与thread中的刷新冲突
255
- - `graph._finalize_nodes` 中添加对thread未终止的防御性检查
256
- - 修复 `stage` 中 `start_time` 在未定义前被 `report` 调用的问题
257
- - 修复 `TaskRedisTransport._transport` 中使用 `id()` 来计算task_id导致的问题
258
- - 修复部分任务无法被hash导致的panic问题
286
+ - i18n本地化在部分字段上失效的问题
287
+ - 直接点击仪表盘中错误数字跳转到错误日志页后, 设置面板显示的还是仪表盘页面的设置
288
+ - 前端中为各项仪表盘请求添加 `RequestSeq`, 以避免前后发送两个请求, 但因为延迟关系, 先发送的请求的返回覆盖掉后发送请求的返回
289
+ - 修复前端中各项空字段需要在第一次refresh才显示的问题, 体感上会导致"加载"很慢
259
290
  - chore:
260
- - 删除所有的 `type: ignore`
261
- - 好看不少
262
- - 在 `start_*` 函数的doc-string中标注该函数为一次性调用函数
291
+ - 将文档更新翻译为英/日两语
292
+ - 这项操作太耗token了
263
293
 
264
294
  更多过往日志可看:
265
295
 
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "celestialflow"
7
- version = "3.2.2"
7
+ version = "3.2.3"
8
8
  description = "A flexible GRAPH-based task orchestration framework."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
11
11
  authors = [{ name = "Mr-xiaotian", email = "mingxiaomingtian@gmail.com" }]
12
12
  keywords = ["workflow", "task", "graph", "async", "CelestialFlow"]
13
- requires-python = ">=3.11"
13
+ requires-python = ">=3.12"
14
14
 
15
15
  dependencies = [
16
16
  "tqdm",
@@ -20,7 +20,7 @@ dependencies = [
20
20
  "networkx",
21
21
  "redis",
22
22
  "jinja2",
23
- "celestialtree>=0.1.2",
23
+ "celestialtree>=0.1.3",
24
24
  ]
25
25
 
26
26
  classifiers = [
@@ -79,14 +79,11 @@ include = ["src"]
79
79
  ignore = ["tests", "**/tests/**", "typings", "demo", "bench", "experiments", "examples", "temp"]
80
80
 
81
81
  typeCheckingMode = "strict"
82
- reportExplicitAny = "none"
83
- reportAny = "none"
84
- reportUnannotatedClassAttribute = "none"
85
82
  reportUnusedFunction = "none"
86
83
  reportImplicitOverride = "none"
87
84
 
88
85
  [tool.ruff]
89
- target-version = "py311"
86
+ target-version = "py312"
90
87
  exclude = ["tests", "typings", "demo", "bench", "experiments", "examples", "temp"]
91
88
 
92
89
  [tool.ruff.lint]
@@ -20,24 +20,21 @@ from ..observability import NullTaskReporter, TaskReporter
20
20
  from ..persistence import FailInlet, FailSpout, LogInlet, LogSpout
21
21
  from ..persistence.util_jsonl import load_task_by_error, load_task_by_stage
22
22
  from ..runtime import TaskInQueue, TaskOutQueue
23
+ from ..runtime.util_constant import LEVEL_DICT, STAGE_STYLE
23
24
  from ..runtime.util_errors import (
24
25
  CelestialTreeConnectionError,
25
26
  DuplicateNodeError,
27
+ LogLevelError,
26
28
  RuntimeStateError,
27
29
  ScheduleModeError,
28
- UnconsumedError,
29
30
  )
30
31
  from ..runtime.util_estimators import (
31
32
  calc_elapsed,
32
- calc_global_remain_equal_pred,
33
+ calc_global_pending,
33
34
  calc_remaining,
34
35
  )
35
- from ..runtime.util_types import (
36
- STAGE_STYLE,
37
- CTreeEvent,
38
- TerminationSignal,
39
- )
40
- from ..stage import TaskStage
36
+ from ..runtime.util_types import TerminationSignal
37
+ from ..stage.util_types import AnyTaskStage
41
38
  from ..utils.util_collections import cluster_by_value_sorted
42
39
  from ..utils.util_format import format_avg_time
43
40
  from .util_analysis import (
@@ -62,6 +59,7 @@ class TaskGraph:
62
59
 
63
60
  def __init__(
64
61
  self,
62
+ name: str,
65
63
  schedule_mode: str = "eager",
66
64
  log_level: str = "INFO",
67
65
  ) -> None:
@@ -77,6 +75,7 @@ class TaskGraph:
77
75
  - 完成一次 start_graph() 后,不应复用同一实例再次启动。
78
76
  - 如需重复执行,请重新构建新的 TaskGraph 与节点对象。
79
77
 
78
+ :param name: 任务图名称
80
79
  :param schedule_mode: str, optional, default = 'eager'
81
80
  控制任务图的调度布局模式,支持以下两种策略:
82
81
  - 'eager':
@@ -97,6 +96,7 @@ class TaskGraph:
97
96
  - 'ERROR'
98
97
  - 'CRITICAL'
99
98
  """
99
+ self._set_name(name)
100
100
  self._set_log_level(log_level)
101
101
  self._set_schedule_mode(schedule_mode)
102
102
  self.set_reporter()
@@ -119,7 +119,7 @@ class TaskGraph:
119
119
  # 用于保存所有子线程的引用
120
120
  self.threads: list[threading.Thread] = []
121
121
  # 用于保存每个节点的运行信息
122
- self.stage_dict: dict[str, TaskStage] = {}
122
+ self.stage_dict: dict[str, AnyTaskStage] = {}
123
123
  # 用于保存每个节点的上一次collect_runtime_snapshot()的状态信息
124
124
  self.status_dict: dict[str, dict[str, Any]] = defaultdict(dict)
125
125
  # 用于保存最近一次状态快照对应的统一时间戳
@@ -127,10 +127,12 @@ class TaskGraph:
127
127
  # 用于保存每个节点的输入任务ID集合
128
128
  self.input_ids: dict[str, set[int]] = defaultdict(set)
129
129
  # 用于保存源节点列表(由 _build_analysis 自动计算)
130
- self.source_stages: list[TaskStage] = []
130
+ self.source_stages: list[AnyTaskStage] = []
131
131
  # 用于保存图结构的邻接表
132
132
  self.out_edges: dict[str, list[str]] = defaultdict(list)
133
133
  self.in_edges: dict[str, list[str]] = defaultdict(list)
134
+ # 用于保存任务图启动时间
135
+ self.start_time: float = 0.0
134
136
 
135
137
  def _init_spout(self) -> None:
136
138
  """
@@ -148,7 +150,7 @@ class TaskGraph:
148
150
 
149
151
  # ==== 建图 ====
150
152
 
151
- def set_stages(self, stages: list[TaskStage]) -> None:
153
+ def set_stages(self, stages: list[AnyTaskStage]) -> None:
152
154
  """
153
155
  添加节点到任务图中
154
156
 
@@ -167,7 +169,11 @@ class TaskGraph:
167
169
 
168
170
  stage.set_inlet(fail_queue, log_queue)
169
171
 
170
- def connect(self, from_stages: list[TaskStage], to_stages: list[TaskStage]) -> None:
172
+ def connect(
173
+ self,
174
+ from_stages: list[AnyTaskStage],
175
+ to_stages: list[AnyTaskStage],
176
+ ) -> None:
171
177
  """
172
178
  建立超边连接:from_stages 中的每个节点连接到 to_stages 中的每个节点。
173
179
 
@@ -181,6 +187,14 @@ class TaskGraph:
181
187
 
182
188
  # ==== 配置 ====
183
189
 
190
+ def _set_name(self, name: str) -> None:
191
+ """
192
+ 设置任务图名称
193
+
194
+ :param name: 任务图名称
195
+ """
196
+ self.name = name
197
+
184
198
  def _set_schedule_mode(self, schedule_mode: str) -> None:
185
199
  """
186
200
  设置任务链的执行模式
@@ -202,6 +216,8 @@ class TaskGraph:
202
216
  :param level: 日志级别, 默认为 "INFO"
203
217
  """
204
218
  self.log_level = level.upper()
219
+ if self.log_level not in LEVEL_DICT:
220
+ raise LogLevelError(self.log_level)
205
221
 
206
222
  def set_reporter(
207
223
  self, is_report: bool = False, host: str = "127.0.0.1", port: int = 5000
@@ -283,15 +299,15 @@ class TaskGraph:
283
299
  if not self.in_edges[stage_name]: # 如果没有前驱
284
300
  continue
285
301
 
286
- in_queue: TaskInQueue = current_stage.task_queue
287
- prev_stages: list[TaskStage] = []
302
+ in_queue: TaskInQueue[Any] = current_stage.task_queue
303
+ prev_stages: list[AnyTaskStage] = []
288
304
 
289
305
  # 遍历每个前驱,创建边队列
290
306
  for prev_stage_name in self.in_edges[stage_name]:
291
307
  in_queue.add_source_name(prev_stage_name)
292
308
 
293
309
  prev_stage = self.stage_dict[prev_stage_name]
294
- prev_out_queue: TaskOutQueue = prev_stage.result_queue
310
+ prev_out_queue: TaskOutQueue[Any] = prev_stage.result_queue
295
311
  prev_out_queue.add_queue(in_queue.queue, stage_name)
296
312
  prev_stages.append(prev_stage)
297
313
 
@@ -328,7 +344,7 @@ class TaskGraph:
328
344
  for name, tasks in tasks_dict.items():
329
345
  if name not in self.stage_dict:
330
346
  continue
331
- stage: TaskStage = self.stage_dict[name]
347
+ stage: AnyTaskStage = self.stage_dict[name]
332
348
 
333
349
  for task in tasks:
334
350
  if isinstance(task, TerminationSignal):
@@ -374,13 +390,14 @@ class TaskGraph:
374
390
  RuntimeWarning,
375
391
  stacklevel=2,
376
392
  )
377
- start_time = time.perf_counter()
393
+ _start = time.perf_counter()
394
+ self.start_time = time.time()
378
395
 
379
396
  try:
380
397
  self.fail_spout.start()
381
398
  self.log_spout.start()
382
- self.log_inlet.start_graph(self.get_structure_list())
383
- self.fail_inlet.start_graph(self.get_structure_graph())
399
+ self.log_inlet.start_graph(self.name, self.get_structure_list())
400
+ self.fail_inlet.start_graph(self.name, self.get_structure_graph())
384
401
  self.reporter.start()
385
402
 
386
403
  self.put_stage_queue(init_tasks_dict, put_termination_signal)
@@ -390,7 +407,7 @@ class TaskGraph:
390
407
  self._finalize_nodes()
391
408
 
392
409
  self.reporter.stop()
393
- self.log_inlet.end_graph(time.perf_counter() - start_time)
410
+ self.log_inlet.end_graph(self.name, time.perf_counter() - _start)
394
411
  self.fail_spout.stop()
395
412
  self.log_spout.stop()
396
413
 
@@ -413,7 +430,7 @@ class TaskGraph:
413
430
 
414
431
  threads: list[threading.Thread] = []
415
432
  for stage_name in layer:
416
- stage: TaskStage = self.stage_dict[stage_name]
433
+ stage: AnyTaskStage = self.stage_dict[stage_name]
417
434
  self._execute_stage(stage)
418
435
  if stage.stage_mode == "thread":
419
436
  threads.append(self.threads[-1])
@@ -424,7 +441,7 @@ class TaskGraph:
424
441
 
425
442
  self.log_inlet.end_layer(layer, time.perf_counter() - start_time)
426
443
 
427
- def _execute_stage(self, stage: TaskStage) -> None:
444
+ def _execute_stage(self, stage: AnyTaskStage) -> None:
428
445
  """
429
446
  执行单个节点
430
447
 
@@ -474,33 +491,8 @@ class TaskGraph:
474
491
  stage.mark_stopped()
475
492
 
476
493
  # 收集并持久化每个 stage 中未消费的任务
477
- for stage_name, stage in self.stage_dict.items():
478
- in_queue: TaskInQueue = stage.task_queue
479
-
480
- remaining_sources = in_queue.drain()
481
- stage.metrics.add_error_count(len(remaining_sources))
482
-
483
- # 持久化逻辑
484
- for source in remaining_sources:
485
- task: Any = source.task
486
- task_id: int = source.id
487
- error_id: int = self.ctree_client.emit(
488
- CTreeEvent.TASK_ERROR,
489
- [task_id],
490
- payload=stage.get_summary(),
491
- )
492
-
493
- self.fail_inlet.task_error(
494
- stage_name, error_id, UnconsumedError(), task
495
- )
496
-
497
- self.log_inlet.task_error(
498
- stage.get_func_name(),
499
- stage.get_task_repr(task),
500
- UnconsumedError(),
501
- task_id,
502
- error_id,
503
- )
494
+ for stage in self.stage_dict.values():
495
+ stage.drain_task_queue()
504
496
 
505
497
  self.collect_runtime_snapshot()
506
498
 
@@ -508,10 +500,10 @@ class TaskGraph:
508
500
 
509
501
  def _snapshot_one_stage(
510
502
  self,
511
- stage: TaskStage,
503
+ stage: AnyTaskStage,
512
504
  last_status: dict[str, Any],
513
505
  interval: float,
514
- ) -> tuple[dict[str, Any], tuple[int, int, float, float]]:
506
+ ) -> tuple[dict[str, Any], tuple[int, int]]:
515
507
  """
516
508
  计算单个 stage 的运行时快照
517
509
  :param stage: 节点实例
@@ -547,37 +539,30 @@ class TaskGraph:
547
539
 
548
540
  processed = int(stage_counts["tasks_processed"] or 0)
549
541
  pending = int(stage_counts["tasks_pending"] or 0)
550
- elapsed_f = float(elapsed or 0.0)
551
- remaining_f = float(remaining or 0.0)
552
542
 
553
- return snapshot, (processed, pending, elapsed_f, remaining_f)
543
+ return snapshot, (processed, pending)
554
544
 
555
- def _calc_graph_remain(
545
+ def _calc_graph_pending(
556
546
  self,
557
547
  running_processed_map: dict[str, int],
558
548
  running_pending_map: dict[str, int],
559
- running_elapsed_map: dict[str, float],
560
- running_remaining_map: dict[str, float],
561
- ) -> dict[str, float]:
549
+ ) -> dict[str, int]:
562
550
  """
563
- 根据 DAG/非 DAG 策略计算全局预计剩余时间
551
+ 根据 DAG/非 DAG 策略计算全局预计待处理任务数量
564
552
 
565
553
  :param running_processed_map: 各节点已处理任务数
566
554
  :param running_pending_map: 各节点待处理任务数
567
- :param running_elapsed_map: 各节点已用时间
568
- :param running_remaining_map: 各节点预计剩余时间
569
- :return: 全局预计剩余时间(秒)
555
+ :return: 全局预计待处理任务数量
570
556
  """
571
557
  if not self.is_dag:
572
- return running_remaining_map
558
+ return running_pending_map
573
559
 
574
- total_remaining_map = calc_global_remain_equal_pred(
560
+ total_pending_map = calc_global_pending(
575
561
  self.networkx_graph,
576
562
  running_processed_map,
577
563
  running_pending_map,
578
- running_elapsed_map,
579
564
  )
580
- return total_remaining_map
565
+ return total_pending_map
581
566
 
582
567
  def collect_runtime_snapshot(self) -> None:
583
568
  """
@@ -587,39 +572,36 @@ class TaskGraph:
587
572
  now = time.time()
588
573
  interval = self.reporter.interval
589
574
 
590
- # 为全局预计 remaining 收集
591
- running_elapsed_map: dict[str, float] = {}
575
+ # 为全局预计 tasks_pending 收集数据
592
576
  running_processed_map: dict[str, int] = {}
593
577
  running_pending_map: dict[str, int] = {}
594
- running_remaining_map: dict[str, float] = {}
595
578
 
596
579
  for stage_name, stage in self.stage_dict.items():
597
580
  last_status = self.status_dict.get(stage_name, {})
598
581
 
599
- snapshot, (processed, pending, elapsed, remaining) = (
600
- self._snapshot_one_stage(
601
- stage,
602
- last_status,
603
- interval,
604
- )
582
+ snapshot, (processed, pending) = self._snapshot_one_stage(
583
+ stage,
584
+ last_status,
585
+ interval,
605
586
  )
606
587
 
607
588
  status_dict[stage_name] = snapshot
608
589
 
590
+ # 更新各节点的 processed, pending, remaining 数据
609
591
  running_processed_map[stage_name] = processed
610
592
  running_pending_map[stage_name] = pending
611
- running_elapsed_map[stage_name] = elapsed
612
- running_remaining_map[stage_name] = remaining
613
593
 
614
- total_remaining_map = self._calc_graph_remain(
594
+ total_pending_map = self._calc_graph_pending(
615
595
  running_processed_map,
616
596
  running_pending_map,
617
- running_elapsed_map,
618
- running_remaining_map,
619
597
  )
620
-
621
598
  for stage_name, stage_status in status_dict.items():
622
- stage_status["total_remaining_time"] = total_remaining_map[stage_name]
599
+ stage_status["total_tasks_pending"] = total_pending_map[stage_name]
600
+ stage_status["total_remaining_time"] = calc_remaining(
601
+ stage_status["tasks_processed"],
602
+ stage_status["total_tasks_pending"],
603
+ stage_status["elapsed_time"],
604
+ )
623
605
 
624
606
  self.status_dict = status_dict
625
607
  self.status_timestamp = now
@@ -669,12 +651,14 @@ class TaskGraph:
669
651
  """
670
652
  获取任务图的分析信息
671
653
 
672
- :return: 包含 is_dag, schedule_mode, class_name, layers_dict 的字典
654
+ :return: 包含 name, startTime, is_dag, schedule_mode, class_name, layers_dict 的字典
673
655
  """
674
656
  return {
657
+ "name": self.name,
658
+ "startTime": self.start_time,
659
+ "className": self.__class__.__name__,
675
660
  "isDAG": self.is_dag,
676
661
  "scheduleMode": self.schedule_mode,
677
- "className": self.__class__.__name__,
678
662
  "layersDict": self.layers_dict,
679
663
  }
680
664
 
@@ -728,7 +712,7 @@ class TaskGraph:
728
712
  return ""
729
713
  return format_descendants_forest(descendants, STAGE_STYLE)
730
714
 
731
- def get_source_stages(self) -> list[TaskStage]:
715
+ def get_source_stages(self) -> list[AnyTaskStage]:
732
716
  """
733
717
  获取源节点列表
734
718