celestialflow 3.1.5__tar.gz → 3.1.6__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 (88) hide show
  1. {celestialflow-3.1.5 → celestialflow-3.1.6}/PKG-INFO +14 -9
  2. {celestialflow-3.1.5 → celestialflow-3.1.6}/README.md +13 -8
  3. {celestialflow-3.1.5 → celestialflow-3.1.6}/pyproject.toml +80 -74
  4. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/__init__.py +17 -17
  5. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/graph/__init__.py +3 -3
  6. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/graph/core_graph.py +38 -26
  7. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/graph/core_structure.py +3 -3
  8. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/graph/util_analysis.py +4 -4
  9. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/graph/util_serialize.py +4 -4
  10. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/observability/__init__.py +1 -1
  11. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/observability/core_report.py +45 -39
  12. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/persistence/core_base.py +7 -6
  13. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/persistence/core_fail.py +5 -2
  14. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/persistence/core_log.py +17 -3
  15. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/persistence/util_jsonl.py +1 -1
  16. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/__init__.py +1 -1
  17. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/core_metrics.py +4 -4
  18. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/core_queue.py +14 -12
  19. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/core_runner.py +39 -21
  20. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/util_errors.py +1 -0
  21. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/util_estimators.py +2 -2
  22. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/util_factories.py +5 -7
  23. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/util_types.py +7 -5
  24. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/stage/__init__.py +3 -3
  25. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/stage/core_executor.py +46 -30
  26. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/stage/core_stage.py +25 -13
  27. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/stage/core_stages.py +21 -12
  28. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/utils/util_benchmark.py +12 -1
  29. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/utils/util_format.py +9 -9
  30. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/config.json +4 -4
  31. celestialflow-3.1.5/src/celestialflow/web/server.py → celestialflow-3.1.6/src/celestialflow/web/core_server.py +86 -64
  32. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/css/_colors.css +23 -18
  33. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/css/base.css +74 -56
  34. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/css/dashboard.css +73 -71
  35. celestialflow-3.1.6/src/celestialflow/web/static/css/errors.css +286 -0
  36. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/css/inject.css +124 -161
  37. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/main.js +4 -4
  38. celestialflow-3.1.6/src/celestialflow/web/static/js/task_analysis.js +59 -0
  39. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_errors.js +62 -45
  40. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_history.js +14 -8
  41. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_injection.js +21 -16
  42. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_statuses.js +6 -14
  43. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_structure.js +9 -0
  44. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_topology.js +20 -20
  45. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/utils.js +79 -15
  46. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/main.ts +4 -4
  47. celestialflow-3.1.6/src/celestialflow/web/static/ts/task_analysis.ts +67 -0
  48. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/task_errors.ts +178 -161
  49. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/task_history.ts +15 -9
  50. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/task_injection.ts +9 -4
  51. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/task_statuses.ts +12 -14
  52. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/task_structure.ts +10 -0
  53. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/utils.ts +92 -14
  54. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/templates/index.html +23 -19
  55. celestialflow-3.1.6/src/celestialflow/web/util_cal.py +4 -0
  56. celestialflow-3.1.6/src/celestialflow/web/util_config.py +23 -0
  57. celestialflow-3.1.6/src/celestialflow/web/util_error.py +53 -0
  58. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow.egg-info/PKG-INFO +14 -9
  59. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow.egg-info/SOURCES.txt +6 -2
  60. celestialflow-3.1.6/src/celestialflow.egg-info/entry_points.txt +2 -0
  61. {celestialflow-3.1.5 → celestialflow-3.1.6}/tests/test_executor.py +1 -1
  62. {celestialflow-3.1.5 → celestialflow-3.1.6}/tests/test_stages.py +23 -19
  63. {celestialflow-3.1.5 → celestialflow-3.1.6}/tests/test_structure.py +16 -13
  64. {celestialflow-3.1.5 → celestialflow-3.1.6}/tests/test_utils.py +234 -237
  65. celestialflow-3.1.5/src/celestialflow/web/static/css/errors.css +0 -284
  66. celestialflow-3.1.5/src/celestialflow/web/static/ts/task_topology.ts +0 -67
  67. celestialflow-3.1.5/src/celestialflow.egg-info/entry_points.txt +0 -2
  68. {celestialflow-3.1.5 → celestialflow-3.1.6}/setup.cfg +0 -0
  69. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/persistence/__init__.py +0 -0
  70. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/persistence/util_constant.py +0 -0
  71. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/core_envelope.py +0 -0
  72. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/core_progress.py +0 -0
  73. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/util_hash.py +0 -0
  74. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/runtime/util_queue.py +0 -0
  75. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/utils/__init__.py +0 -0
  76. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/utils/util_clone.py +0 -0
  77. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/utils/util_collections.py +0 -0
  78. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/utils/util_debug.py +0 -0
  79. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/favicon.ico +0 -0
  80. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_config.js +0 -0
  81. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/task_summary.js +0 -0
  82. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/js/web_config.js +0 -0
  83. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/globals.d.ts +0 -0
  84. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/task_summary.ts +0 -0
  85. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow/web/static/ts/web_config.ts +0 -0
  86. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow.egg-info/dependency_links.txt +0 -0
  87. {celestialflow-3.1.5 → celestialflow-3.1.6}/src/celestialflow.egg-info/requires.txt +0 -0
  88. {celestialflow-3.1.5 → celestialflow-3.1.6}/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.1.5
3
+ Version: 3.1.6
4
4
  Summary: A flexible GRAPH-based task orchestration framework.
5
5
  Author-email: Mr-xiaotian <mingxiaomingtian@gmail.com>
6
6
  License: MIT
@@ -254,22 +254,27 @@ flowchart TD
254
254
  <p align="center">
255
255
  <img src="https://raw.githubusercontent.com/Mr-xiaotian/CelestialFlow/main/img/file_structure.svg" alt="FileStructure" />
256
256
  <br/>
257
- <em>celestial-flow 3.1.5</em>
257
+ <em>celestial-flow 3.1.6</em>
258
258
  </p>
259
259
 
260
260
  (该视图由我的另一个项目[CelestialVault](https://github.com/Mr-xiaotian/CelestialVault)中inst_file.FileTree.print_tree()生成。转换为图片则借助[Carbon](https://carbon.now.sh)。)
261
261
 
262
262
  ## 版本日志(Version Log)
263
- - 3.1.5
263
+ - 3.1.6
264
264
  - feat
265
- - 大幅修改节点状态卡片的色彩视觉设计. 包括: 取消原本的悬浮设计, 改为平面化设计; 让进度条更直接的显示不同任务完成状态的比例; 使用左边框来显示节点是否在运行中. 而非原本的badge;
266
- - 前端的error数据拉取不再每次都全量拉取, 而是只拉取自己当前没有的数据;
265
+ - 前端现在只储存一页的error数据, 有效减少了运行大规模任务时前端内存飙升的问题;
266
+ - 优化任务数显示, 大于1*10^7时显示科学计数法, 否则显示英式计数;
267
+ - 优化小屏模式下表格的显示: 改为用卡片式显示;
268
+ - 在error表格中加入index项;
269
+ - 大幅调整任务颜色分配, 现在重复任务使用黄色系, 等待任务使用灰色系;
270
+ - 将节点卡片中的已消耗时间颜色拟合为下方进度条颜色;
267
271
  - refactor
268
- - 大幅重构前端代码中的色彩管理, 现在色彩的一致性更好;
269
- - 修改前端代码中的数据更新判断, 现在相关判断交给serve.py;
272
+ - 用mypy整理了一遍类型标注;
273
+ - 删除与整合部分css代码;
274
+ - 将字体等适合rem单位的地方全部从px替换为rem, 并且都部分size进行统一;
270
275
  - fix
271
- - 修复节点已运行时间在节点完成后显示为0的问题
272
- - 修复在最新fastapi版本下TemplateResponse参数改变导致的问题
276
+ - 修复总体剩余时间在特殊情况下显示0的问题(这玩意真麻烦);
277
+ - 修复部分小屏下的显示问题, 但折线图不显示的问题不太好解决;
273
278
 
274
279
  ## Star 历史趋势(Star History)
275
280
 
@@ -229,22 +229,27 @@ flowchart TD
229
229
  <p align="center">
230
230
  <img src="https://raw.githubusercontent.com/Mr-xiaotian/CelestialFlow/main/img/file_structure.svg" alt="FileStructure" />
231
231
  <br/>
232
- <em>celestial-flow 3.1.5</em>
232
+ <em>celestial-flow 3.1.6</em>
233
233
  </p>
234
234
 
235
235
  (该视图由我的另一个项目[CelestialVault](https://github.com/Mr-xiaotian/CelestialVault)中inst_file.FileTree.print_tree()生成。转换为图片则借助[Carbon](https://carbon.now.sh)。)
236
236
 
237
237
  ## 版本日志(Version Log)
238
- - 3.1.5
238
+ - 3.1.6
239
239
  - feat
240
- - 大幅修改节点状态卡片的色彩视觉设计. 包括: 取消原本的悬浮设计, 改为平面化设计; 让进度条更直接的显示不同任务完成状态的比例; 使用左边框来显示节点是否在运行中. 而非原本的badge;
241
- - 前端的error数据拉取不再每次都全量拉取, 而是只拉取自己当前没有的数据;
240
+ - 前端现在只储存一页的error数据, 有效减少了运行大规模任务时前端内存飙升的问题;
241
+ - 优化任务数显示, 大于1*10^7时显示科学计数法, 否则显示英式计数;
242
+ - 优化小屏模式下表格的显示: 改为用卡片式显示;
243
+ - 在error表格中加入index项;
244
+ - 大幅调整任务颜色分配, 现在重复任务使用黄色系, 等待任务使用灰色系;
245
+ - 将节点卡片中的已消耗时间颜色拟合为下方进度条颜色;
242
246
  - refactor
243
- - 大幅重构前端代码中的色彩管理, 现在色彩的一致性更好;
244
- - 修改前端代码中的数据更新判断, 现在相关判断交给serve.py;
247
+ - 用mypy整理了一遍类型标注;
248
+ - 删除与整合部分css代码;
249
+ - 将字体等适合rem单位的地方全部从px替换为rem, 并且都部分size进行统一;
245
250
  - fix
246
- - 修复节点已运行时间在节点完成后显示为0的问题
247
- - 修复在最新fastapi版本下TemplateResponse参数改变导致的问题
251
+ - 修复总体剩余时间在特殊情况下显示0的问题(这玩意真麻烦);
252
+ - 修复部分小屏下的显示问题, 但折线图不显示的问题不太好解决;
248
253
 
249
254
  ## Star 历史趋势(Star History)
250
255
 
@@ -1,74 +1,80 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "celestialflow"
7
- version = "3.1.5"
8
- description = "A flexible GRAPH-based task orchestration framework."
9
- readme = "README.md"
10
- license = { text = "MIT" }
11
- authors = [{ name = "Mr-xiaotian", email = "mingxiaomingtian@gmail.com" }]
12
- keywords = ["workflow", "task", "graph", "async", "CelestialFlow"]
13
- requires-python = ">=3.10"
14
-
15
- dependencies = [
16
- "tqdm",
17
- "fastapi",
18
- "uvicorn",
19
- "requests",
20
- "networkx",
21
- "redis",
22
- "jinja2",
23
- "celestialtree>=0.1.2",
24
- ]
25
-
26
- classifiers = [
27
- "Programming Language :: Python :: 3",
28
- "License :: OSI Approved :: MIT License",
29
- "Operating System :: OS Independent",
30
- "Framework :: FastAPI",
31
- "Topic :: Software Development :: Libraries",
32
- ]
33
-
34
- [project.urls]
35
- "Homepage" = "https://github.com/Mr-xiaotian/CelestialFlow"
36
- "Bug Tracker" = "https://github.com/Mr-xiaotian/CelestialFlow/issues"
37
-
38
- [project.scripts]
39
- celestialflow-web = "celestialflow.web.server:main_entry"
40
-
41
- [tool.setuptools]
42
- license-files = []
43
-
44
- [tool.setuptools.package-data]
45
- celestialflow = [
46
- "web/templates/**/*.html",
47
- "web/static/**/*",
48
- "web/config.json"
49
- ]
50
-
51
- [tool.pytest.ini_options]
52
- addopts = "-s"
53
- log_cli = true
54
- log_cli_level = "INFO"
55
- log_cli_format = "%(asctime)s [%(levelname)s] %(message)s"
56
- log_cli_date_format = "%Y-%m-%d %H:%M:%S"
57
-
58
- filterwarnings = [
59
- "ignore::DeprecationWarning",
60
- "ignore::pytest.PytestDeprecationWarning",
61
- ]
62
-
63
- asyncio_default_fixture_loop_scope = "function"
64
-
65
- [dependency-groups]
66
- dev = [
67
- "build>=1.2.2.post1",
68
- "twine>=6.1.0",
69
- "pytest>=8.3.4",
70
- "pytest-asyncio>=0.25.3",
71
- "httpx>=0.28.1",
72
- "python-dotenv>=1.2.2",
73
- "black>=26.1.0",
74
- ]
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "celestialflow"
7
+ version = "3.1.6"
8
+ description = "A flexible GRAPH-based task orchestration framework."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "Mr-xiaotian", email = "mingxiaomingtian@gmail.com" }]
12
+ keywords = ["workflow", "task", "graph", "async", "CelestialFlow"]
13
+ requires-python = ">=3.10"
14
+
15
+ dependencies = [
16
+ "tqdm",
17
+ "fastapi",
18
+ "uvicorn",
19
+ "requests",
20
+ "networkx",
21
+ "redis",
22
+ "jinja2",
23
+ "celestialtree>=0.1.2",
24
+ ]
25
+
26
+ classifiers = [
27
+ "Programming Language :: Python :: 3",
28
+ "License :: OSI Approved :: MIT License",
29
+ "Operating System :: OS Independent",
30
+ "Framework :: FastAPI",
31
+ "Topic :: Software Development :: Libraries",
32
+ ]
33
+
34
+ [project.urls]
35
+ "Homepage" = "https://github.com/Mr-xiaotian/CelestialFlow"
36
+ "Bug Tracker" = "https://github.com/Mr-xiaotian/CelestialFlow/issues"
37
+
38
+ [project.scripts]
39
+ celestialflow-web = "celestialflow.web.core_server:main_entry"
40
+
41
+ [tool.setuptools]
42
+ license-files = []
43
+
44
+ [tool.setuptools.package-data]
45
+ celestialflow = [
46
+ "web/templates/**/*.html",
47
+ "web/static/**/*",
48
+ "web/config.json"
49
+ ]
50
+
51
+ [tool.pytest.ini_options]
52
+ addopts = "-s"
53
+ log_cli = true
54
+ log_cli_level = "INFO"
55
+ log_cli_format = "%(asctime)s [%(levelname)s] %(message)s"
56
+ log_cli_date_format = "%Y-%m-%d %H:%M:%S"
57
+
58
+ filterwarnings = [
59
+ "ignore::DeprecationWarning",
60
+ "ignore::pytest.PytestDeprecationWarning",
61
+ ]
62
+
63
+ asyncio_default_fixture_loop_scope = "function"
64
+
65
+ [dependency-groups]
66
+ dev = [
67
+ "build>=1.2.2.post1",
68
+ "twine>=6.1.0",
69
+ "pytest>=8.3.4",
70
+ "pytest-asyncio>=0.25.3",
71
+ "httpx>=0.28.1",
72
+ "python-dotenv>=1.2.2",
73
+ "dill",
74
+ "mypy>=1.19.1",
75
+ ]
76
+
77
+ [tool.mypy]
78
+ python_version = "3.10"
79
+ no_implicit_optional = false
80
+ disable_error_code = ["import-untyped"]
@@ -1,32 +1,32 @@
1
1
  # __init__.py
2
- from .stage import (
3
- TaskExecutor,
4
- TaskStage,
5
- TaskSplitter,
6
- TaskRedisTransport,
7
- TaskRedisSource,
8
- TaskRedisAck,
9
- TaskRouter,
10
- )
11
2
  from .graph import (
12
- TaskGraph,
13
3
  TaskChain,
14
- TaskLoop,
15
- TaskCross,
16
4
  TaskComplete,
17
- TaskWheel,
5
+ TaskCross,
6
+ TaskGraph,
18
7
  TaskGrid,
8
+ TaskLoop,
9
+ TaskWheel,
19
10
  )
20
11
  from .persistence.util_jsonl import (
21
12
  load_jsonl_logs,
22
- load_task_by_stage,
23
13
  load_task_by_error,
14
+ load_task_by_stage,
24
15
  )
25
- from .runtime.util_types import TerminationSignal
26
16
  from .runtime.util_hash import make_hashable
17
+ from .runtime.util_types import TerminationSignal
18
+ from .stage import (
19
+ TaskExecutor,
20
+ TaskRedisAck,
21
+ TaskRedisSource,
22
+ TaskRedisTransport,
23
+ TaskRouter,
24
+ TaskSplitter,
25
+ TaskStage,
26
+ )
27
+ from .utils.util_benchmark import benchmark_executor, benchmark_graph
27
28
  from .utils.util_format import format_table
28
- from .utils.util_benchmark import benchmark_graph, benchmark_executor
29
- from .web.server import TaskWebServer
29
+ from .web.core_server import TaskWebServer
30
30
 
31
31
  __all__ = [
32
32
  "TaskGraph",
@@ -2,11 +2,11 @@
2
2
  from .core_graph import TaskGraph
3
3
  from .core_structure import (
4
4
  TaskChain,
5
- TaskLoop,
6
- TaskCross,
7
5
  TaskComplete,
8
- TaskWheel,
6
+ TaskCross,
9
7
  TaskGrid,
8
+ TaskLoop,
9
+ TaskWheel,
10
10
  )
11
11
 
12
12
  __all__ = [
@@ -1,40 +1,46 @@
1
1
  # graph/core_graph.py
2
+ import multiprocessing
2
3
  import time
3
4
  import warnings
4
- import multiprocessing
5
5
  from collections import defaultdict, deque
6
+ from pathlib import Path
7
+ from typing import Any
6
8
  from multiprocessing import Queue as MPQueue
7
9
 
8
10
  from celestialtree import (
9
11
  Client as CelestialTreeClient,
12
+ )
13
+ from celestialtree import (
10
14
  NullClient as NullCelestialTreeClient,
15
+ )
16
+ from celestialtree import (
11
17
  format_descendants_forest,
12
18
  format_provenance_forest,
13
19
  )
20
+ from networkx import is_directed_acyclic_graph
14
21
 
15
- from ..runtime import TaskInQueue, TaskOutQueue, TaskEnvelope
22
+ from ..observability import NullTaskReporter, TaskReporter
23
+ from ..persistence import FailListener, FailSinker, LogListener, LogSinker
24
+ from ..persistence.util_jsonl import load_task_by_error, load_task_by_stage
25
+ from ..runtime import TaskEnvelope, TaskInQueue, TaskOutQueue
26
+ from ..runtime.util_errors import UnconsumedError
16
27
  from ..runtime.util_estimators import (
17
28
  calc_elapsed,
18
- calc_remaining,
19
29
  calc_global_remain_equal_pred,
30
+ calc_remaining,
20
31
  )
21
- from ..runtime.util_errors import UnconsumedError
22
32
  from ..runtime.util_types import (
33
+ NULL_PREV_STAGE,
34
+ STAGE_STYLE,
23
35
  StageStatus,
24
36
  TerminationSignal,
25
- STAGE_STYLE,
26
- NULL_PREV_STAGE,
27
37
  )
28
38
  from ..stage import TaskStage
29
- from ..observability import TaskReporter, NullTaskReporter
30
- from ..persistence import FailListener, FailSinker, LogListener, LogSinker
31
- from ..persistence.util_jsonl import load_task_by_stage, load_task_by_error
32
39
  from ..utils.util_collections import cluster_by_value_sorted
33
40
  from ..utils.util_format import format_avg_time
34
41
  from .util_analysis import (
35
- format_networkx_graph,
36
42
  compute_node_levels,
37
- is_directed_acyclic_graph,
43
+ format_networkx_graph,
38
44
  )
39
45
  from .util_serialize import build_structure_graph, format_structure_list_from_graph
40
46
 
@@ -102,15 +108,15 @@ class TaskGraph:
102
108
  # 用于保存所有子进程的引用
103
109
  self.processes: list[multiprocessing.Process] = []
104
110
  # 用于保存每个节点的运行信息
105
- self.stage_runtime_dict: dict[str, dict] = defaultdict(dict)
111
+ self.stage_runtime_dict: dict[str, dict[str, Any]] = defaultdict(dict)
106
112
  # 用于保存每个节点的上一次collect_runtime_snapshot()的状态信息
107
- self.status_dict: dict[str, dict] = defaultdict(dict)
113
+ self.status_dict: dict[str, dict[str, Any]] = defaultdict(dict)
108
114
  # 用于保存任务图的摘要信息
109
115
  self.graph_summary: dict[str, int | float] = {}
110
116
  # 用于保存每个节点的历史状态信息列表(仅保留最近20条)
111
117
  self.stage_history: dict[str, list[dict]] = {}
112
118
  # 用于保存每个节点的输入任务ID集合
113
- self.input_ids: dict[str, set] = defaultdict(set)
119
+ self.input_ids: dict[str, set[int]] = defaultdict(set)
114
120
 
115
121
  def init_listener(self) -> None:
116
122
  """
@@ -164,11 +170,11 @@ class TaskGraph:
164
170
  queue.extend(stage.next_stages)
165
171
 
166
172
  for stage_tag, stage_runtime in self.stage_runtime_dict.items():
167
- stage: TaskStage = stage_runtime["stage"]
173
+ current_stage: TaskStage = stage_runtime["stage"]
168
174
  in_queue: TaskInQueue = stage_runtime["in_queue"]
169
175
 
170
176
  # 遍历每个前驱,创建边队列
171
- for prev_stage in stage.prev_stages:
177
+ for prev_stage in current_stage.prev_stages:
172
178
  prev_stage_tag = prev_stage.get_tag()
173
179
  in_queue.add_source_tag(prev_stage_tag)
174
180
 
@@ -235,6 +241,7 @@ class TaskGraph:
235
241
  self._is_report = is_report
236
242
  self._report_host = host
237
243
  self._report_port = port
244
+ self.reporter: TaskReporter | NullTaskReporter
238
245
  if is_report:
239
246
  self.reporter = TaskReporter(
240
247
  host=host,
@@ -291,7 +298,7 @@ class TaskGraph:
291
298
  :param execution_mode: 节点内部执行模式, 可选值为 'serial' 或 'thread''
292
299
  """
293
300
 
294
- def set_subsequent_stage_mode(stage: TaskStage):
301
+ def set_subsequent_stage_mode(stage: TaskStage) -> None:
295
302
  stage.set_stage_mode(stage_mode)
296
303
  stage.set_execution_mode(execution_mode)
297
304
  visited_stages.add(stage)
@@ -301,7 +308,7 @@ class TaskGraph:
301
308
  continue
302
309
  set_subsequent_stage_mode(next_stage)
303
310
 
304
- visited_stages = set()
311
+ visited_stages: set[TaskStage] = set()
305
312
  for root_stage in self.root_stages:
306
313
  set_subsequent_stage_mode(root_stage)
307
314
  self.init_analysis()
@@ -494,11 +501,11 @@ class TaskGraph:
494
501
 
495
502
  # 收集并持久化每个 stage 中未消费的任务
496
503
  for stage_tag, stage_runtime in self.stage_runtime_dict.items():
497
- stage: TaskStage = stage_runtime["stage"]
504
+ current_stage: TaskStage = stage_runtime["stage"]
498
505
  in_queue: TaskInQueue = stage_runtime["in_queue"]
499
506
 
500
507
  remaining_sources = in_queue.drain()
501
- stage.metrics.add_error_count(len(remaining_sources))
508
+ current_stage.metrics.add_error_count(len(remaining_sources))
502
509
 
503
510
  # 持久化逻辑
504
511
  for source in remaining_sources:
@@ -507,7 +514,7 @@ class TaskGraph:
507
514
  error_id = self.ctree_client.emit(
508
515
  "task.error",
509
516
  [task_id],
510
- payload=stage.get_summary(),
517
+ payload=current_stage.get_summary(),
511
518
  )
512
519
 
513
520
  self.fail_sinker.task_error(
@@ -515,8 +522,8 @@ class TaskGraph:
515
522
  )
516
523
 
517
524
  self.log_sinker.task_error(
518
- stage.get_func_name(),
519
- stage.get_task_repr(task),
525
+ current_stage.get_func_name(),
526
+ current_stage.get_task_repr(task),
520
527
  UnconsumedError(),
521
528
  task_id,
522
529
  error_id,
@@ -644,6 +651,9 @@ class TaskGraph:
644
651
  def get_fail_by_error_dict(self) -> dict:
645
652
  return load_task_by_error(self.fail_listener.jsonl_path)
646
653
 
654
+ def get_total_error_num(self) -> int:
655
+ return self.fail_listener.total_error_num
656
+
647
657
  def get_status_dict(self) -> dict[str, dict]:
648
658
  """
649
659
  获取任务链的状态字典
@@ -660,9 +670,9 @@ class TaskGraph:
660
670
  """获取任务链的历史状态信息字典"""
661
671
  return self.stage_history
662
672
 
663
- def get_graph_topology(self) -> dict:
673
+ def get_graph_analysis(self) -> dict:
664
674
  """
665
- 获取任务图的拓扑信息
675
+ 获取任务图的分析信息
666
676
  """
667
677
  return {
668
678
  "isDAG": self.isDAG,
@@ -693,7 +703,9 @@ class TaskGraph:
693
703
  """
694
704
  获取失败任务的回退路径
695
705
  """
696
- return str(self.fail_listener.jsonl_path.resolve())
706
+ if self.fail_listener.jsonl_path is None:
707
+ return ""
708
+ return str(Path(self.fail_listener.jsonl_path).resolve())
697
709
 
698
710
  def get_stage_input_trace(self, stage_tag: str) -> str:
699
711
  """
@@ -67,7 +67,7 @@ class TaskCross(TaskGraph):
67
67
  stage.set_graph_context(
68
68
  next_stages=next_layer,
69
69
  stage_mode="process",
70
- stage_name=f"Layer{i+1}-{index+1}",
70
+ stage_name=f"Layer{i + 1}-{index + 1}",
71
71
  )
72
72
  super().__init__(
73
73
  root_stages=layers[0], schedule_mode=schedule_mode, log_level=log_level
@@ -112,7 +112,7 @@ class TaskGrid(TaskGraph):
112
112
  nexts.append(grid[i + 1][j]) # down
113
113
  if j + 1 < cols:
114
114
  nexts.append(grid[i][j + 1]) # right
115
- curr.set_graph_context(nexts, "process", f"Grid-{i+1}-{j+1}")
115
+ curr.set_graph_context(nexts, "process", f"Grid-{i + 1}-{j + 1}")
116
116
  super().__init__(
117
117
  root_stages=[grid[0][0]], schedule_mode=schedule_mode, log_level=log_level
118
118
  ) # 起点为左上角
@@ -175,7 +175,7 @@ class TaskWheel(TaskGraph):
175
175
  # 环相连(成闭环)
176
176
  for i, node in enumerate(ring):
177
177
  next_stage = ring[(i + 1) % len(ring)]
178
- node.set_graph_context([next_stage], "process", f"Ring-{i+1}")
178
+ node.set_graph_context([next_stage], "process", f"Ring-{i + 1}")
179
179
  super().__init__(root_stages=[center], log_level=log_level)
180
180
 
181
181
  def start_wheel(
@@ -1,8 +1,8 @@
1
1
  # graph/util_analysis.py
2
- import networkx as nx
3
- from networkx import is_directed_acyclic_graph
4
2
  from typing import Any
5
3
 
4
+ import networkx as nx
5
+
6
6
 
7
7
  # ======== (图论分析) ========
8
8
  def format_networkx_graph(structure_graph: list[dict[str, Any]]) -> nx.DiGraph:
@@ -15,11 +15,11 @@ def format_networkx_graph(structure_graph: list[dict[str, Any]]) -> nx.DiGraph:
15
15
  G = nx.DiGraph()
16
16
 
17
17
  def add_node_and_edges(node: dict[str, Any]):
18
- node_id = f'{node["name"]}[{node["func_name"]}]'
18
+ node_id = f"{node['name']}[{node['func_name']}]"
19
19
  G.add_node(node_id, **{"mode": node.get("stage_mode")})
20
20
 
21
21
  for child in node.get("next_stages", []):
22
- child_id = f'{child["name"]}[{child["func_name"]}]'
22
+ child_id = f"{child['name']}[{child['func_name']}]"
23
23
  G.add_edge(node_id, child_id)
24
24
  # 递归添加子节点
25
25
  add_node_and_edges(child)
@@ -1,5 +1,5 @@
1
1
  # graph/util_serialize.py
2
- from typing import TYPE_CHECKING, Dict, Any, List, Set
2
+ from typing import TYPE_CHECKING, Any, Dict, List, Set
3
3
 
4
4
  if TYPE_CHECKING:
5
5
  from ..stage import TaskStage
@@ -49,7 +49,7 @@ def _build_structure_subgraph(
49
49
  return node
50
50
 
51
51
 
52
- def format_structure_list_from_graph(root_roots: List[Dict] = None) -> List[str]:
52
+ def format_structure_list_from_graph(root_roots: List[Dict] | None = None) -> List[str]:
53
53
  """
54
54
  从多个 JSON 图结构生成格式化任务结构文本列表(带边框)
55
55
 
@@ -64,7 +64,7 @@ def format_structure_list_from_graph(root_roots: List[Dict] = None) -> List[str]
64
64
  S = node.get("stage_mode", "?") # S
65
65
  E = node.get("execution_mode", "?") # E
66
66
 
67
- return f"{N}::{F} " f"(S:{S}, E:{E})" f"{visited_note}"
67
+ return f"{N}::{F} (S:{S}, E:{E}){visited_note}"
68
68
 
69
69
  # 只渲染“子节点”(有父节点)——保证一定画连接符
70
70
  def build_child_lines(node: Dict, prefix: str, is_last: bool) -> List[str]:
@@ -88,7 +88,7 @@ def format_structure_list_from_graph(root_roots: List[Dict] = None) -> List[str]
88
88
  lines.extend(build_child_lines(child, "", i == len(next_stages) - 1))
89
89
  return lines
90
90
 
91
- all_lines = []
91
+ all_lines: list[str] = []
92
92
  for root in root_roots or []:
93
93
  if all_lines:
94
94
  all_lines.append("") # 根之间留空行
@@ -1,5 +1,5 @@
1
1
  # observability/__init__.py
2
- from .core_report import TaskReporter, NullTaskReporter
2
+ from .core_report import NullTaskReporter, TaskReporter
3
3
 
4
4
  __all__ = [
5
5
  "TaskReporter",