pavoz 0.3.0__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.
- pavoz-0.3.0/.github/social-preview.png +0 -0
- pavoz-0.3.0/.github/workflows/publish.yml +28 -0
- pavoz-0.3.0/.github/workflows/test.yml +32 -0
- pavoz-0.3.0/.gitignore +12 -0
- pavoz-0.3.0/CHANGELOG.md +428 -0
- pavoz-0.3.0/CLAUDE.md +84 -0
- pavoz-0.3.0/CONTRIBUTING.md +49 -0
- pavoz-0.3.0/LICENSE +21 -0
- pavoz-0.3.0/PKG-INFO +235 -0
- pavoz-0.3.0/README.cn.md +278 -0
- pavoz-0.3.0/README.md +209 -0
- pavoz-0.3.0/RELEASING.md +36 -0
- pavoz-0.3.0/ROADMAP.md +223 -0
- pavoz-0.3.0/dags/__init__.py +1 -0
- pavoz-0.3.0/dags/demo.py +36 -0
- pavoz-0.3.0/dags/research_dag.py +257 -0
- pavoz-0.3.0/docs/cn/api.md +309 -0
- pavoz-0.3.0/docs/cn/architecture.md +208 -0
- pavoz-0.3.0/docs/cn/quickstart.md +125 -0
- pavoz-0.3.0/docs/cn/use-cases/ai-writer.md +162 -0
- pavoz-0.3.0/docs/design/extension-plan-2026-09-13.md +223 -0
- pavoz-0.3.0/docs/design/handoff-upgrade-plan-v2-2026-09-13.md +199 -0
- pavoz-0.3.0/docs/design/node-input-edit-fork.md +72 -0
- pavoz-0.3.0/docs/design/pr-reply-ai-research-2026-09-10.md +31 -0
- pavoz-0.3.0/docs/en/api.md +336 -0
- pavoz-0.3.0/docs/en/architecture.md +212 -0
- pavoz-0.3.0/docs/en/quickstart.md +125 -0
- pavoz-0.3.0/docs/en/use-cases/ai-writer.md +152 -0
- pavoz-0.3.0/docs/pavoz-upgrade-plan-2026-09-12.md +392 -0
- pavoz-0.3.0/docs/replay-params.md +167 -0
- pavoz-0.3.0/docs/review-pavoz-upgrade-plan-2026-09-13.md +207 -0
- pavoz-0.3.0/docs/social-preview.svg +92 -0
- pavoz-0.3.0/docs/storage.md +157 -0
- pavoz-0.3.0/examples/01_model_switch.sh +36 -0
- pavoz-0.3.0/examples/02_prompt_patch.py +41 -0
- pavoz-0.3.0/examples/03_compare_diff.py +45 -0
- pavoz-0.3.0/examples/04_deep_merge.py +45 -0
- pavoz-0.3.0/examples/05_yaml_file.sh +30 -0
- pavoz-0.3.0/examples/README.md +42 -0
- pavoz-0.3.0/examples/agent_loop.py +171 -0
- pavoz-0.3.0/examples/llm_pipeline.py +65 -0
- pavoz-0.3.0/examples/resume_after_crash.py +52 -0
- pavoz-0.3.0/llms.txt +32 -0
- pavoz-0.3.0/pavoz/__init__.py +66 -0
- pavoz-0.3.0/pavoz/__main__.py +6 -0
- pavoz-0.3.0/pavoz/_id.py +16 -0
- pavoz-0.3.0/pavoz/checkpoint.py +340 -0
- pavoz-0.3.0/pavoz/cli.py +367 -0
- pavoz-0.3.0/pavoz/dag.py +186 -0
- pavoz-0.3.0/pavoz/runtime.py +727 -0
- pavoz-0.3.0/pavoz/state.py +425 -0
- pavoz-0.3.0/pavoz/storage.py +92 -0
- pavoz-0.3.0/pavoz/storage_loader.py +75 -0
- pavoz-0.3.0/pavoz/testing.py +123 -0
- pavoz-0.3.0/pavoz/types.py +65 -0
- pavoz-0.3.0/pyproject.toml +54 -0
- pavoz-0.3.0/research/__init__.py +1 -0
- pavoz-0.3.0/research/llm_router.py +87 -0
- pavoz-0.3.0/research/pavoz_stub.py +28 -0
- pavoz-0.3.0/research/state.py +59 -0
- pavoz-0.3.0/tests/test_api_docs_coverage.py +23 -0
- pavoz-0.3.0/tests/test_cancel.py +189 -0
- pavoz-0.3.0/tests/test_checkpoint.py +120 -0
- pavoz-0.3.0/tests/test_checkpoint_size.py +122 -0
- pavoz-0.3.0/tests/test_cli_fork_set.py +232 -0
- pavoz-0.3.0/tests/test_cli_replay.py +132 -0
- pavoz-0.3.0/tests/test_dag.py +114 -0
- pavoz-0.3.0/tests/test_deltas.py +95 -0
- pavoz-0.3.0/tests/test_events.py +88 -0
- pavoz-0.3.0/tests/test_examples_agent_loop.py +39 -0
- pavoz-0.3.0/tests/test_fork.py +174 -0
- pavoz-0.3.0/tests/test_fork_overrides.py +95 -0
- pavoz-0.3.0/tests/test_id_model.py +286 -0
- pavoz-0.3.0/tests/test_prune_error_progress.py +203 -0
- pavoz-0.3.0/tests/test_replay.py +232 -0
- pavoz-0.3.0/tests/test_retry_jitter.py +30 -0
- pavoz-0.3.0/tests/test_runtime.py +195 -0
- pavoz-0.3.0/tests/test_stage_ts.py +73 -0
- pavoz-0.3.0/tests/test_state.py +58 -0
- pavoz-0.3.0/tests/test_state_apply.py +341 -0
- pavoz-0.3.0/tests/test_state_stats.py +45 -0
- pavoz-0.3.0/tests/test_storage_loader.py +74 -0
- pavoz-0.3.0/tests/test_testpipe.py +148 -0
- pavoz-0.3.0/tests/test_version_sync.py +21 -0
- pavoz-0.3.0/tools/check_doc_links.py +51 -0
- pavoz-0.3.0/tools/check_doc_style.py +65 -0
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# PyPI Trusted Publishing (#33): tag v* 时自动发版.
|
|
4
|
+
# 前提 (一次性): repo public + PyPI 建 pavoz 项目 +
|
|
5
|
+
# GitHub repo Settings → Environments → New environment "pypi" +
|
|
6
|
+
# PyPI 项目 Settings → Publishing → 添加 GitHub 仓库 liyong-labs/pavoz 信任.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # Trusted Publishing 必需
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
publish:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
environment: pypi
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
- name: Build wheel + sdist
|
|
24
|
+
run: |
|
|
25
|
+
pip install --upgrade build
|
|
26
|
+
python -m build
|
|
27
|
+
- name: Publish to PyPI
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- name: Install
|
|
20
|
+
run: |
|
|
21
|
+
pip install -e ".[dev]"
|
|
22
|
+
- name: Lint (pyflakes)
|
|
23
|
+
run: |
|
|
24
|
+
pip install pyflakes
|
|
25
|
+
python -m pyflakes pavoz tests examples tools
|
|
26
|
+
- name: Doc links + style
|
|
27
|
+
run: |
|
|
28
|
+
python tools/check_doc_links.py
|
|
29
|
+
python tools/check_doc_style.py
|
|
30
|
+
- name: Test
|
|
31
|
+
run: |
|
|
32
|
+
python -m pytest tests/ -q
|
pavoz-0.3.0/.gitignore
ADDED
pavoz-0.3.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本项目遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.3.0] — 2026-09-14
|
|
8
|
+
|
|
9
|
+
### Added (文档工程 — 2026-09-13)
|
|
10
|
+
|
|
11
|
+
- **`llms.txt`**: 仓库根的 agent 自读入口 — 这是什么 / 从哪读起 / 不可变规则 / 提交前跑什么命令。
|
|
12
|
+
- **`tools/check_doc_links.py` + CI 步骤**: markdown 相对链接必须可解析 (上线即抓出 3 处死链,
|
|
13
|
+
含 `docs/storage.md` 与 `docs/en/*.md` 的错路径)。
|
|
14
|
+
- **`tools/check_doc_style.py` + CI 步骤**: 只做高精度风格检查 — AI 禁用词、README 装饰性版本号、
|
|
15
|
+
中文句尾半角句号; 过程类文档 (CLAUDE/CHANGELOG/ROADMAP) 与 `docs/design/` 显式豁免, 误报挡 PR 比漏报贵。
|
|
16
|
+
- **`tests/test_api_docs_coverage.py`**: 断言 `pavoz.__all__` 每个名字都出现在
|
|
17
|
+
`docs/{cn,en}/api.md` — 防"文档悄悄落后代码"。
|
|
18
|
+
|
|
19
|
+
### Changed (文档工程 — 2026-09-13)
|
|
20
|
+
|
|
21
|
+
- **API 参考补齐滞后项** (`docs/cn/api.md` + `docs/en/api.md`): 补上已 ship 但未写进参考的能力 —
|
|
22
|
+
`Runtime(on_event=...)` / `cancel_check`(含事件表)、`Runtime.fork_run`、
|
|
23
|
+
`ctx.set_progress` / `cancelled` / `on_event`、`RunResult.error_class` / `stage_timings`、
|
|
24
|
+
`CheckpointStore.prune`、参数覆盖四个函数 (`parse_set_args` / `parse_set_file` /
|
|
25
|
+
`merge_overrides` / `apply_overrides`) 与 `__version__`。
|
|
26
|
+
- **CONTRIBUTING 对齐 CI**: 质量门槛改为 CI 实际执行的两条命令 (原写 `ruff check` 与
|
|
27
|
+
"38+ tests" 均与现状不符); 修正 3 处指向不存在文件的文档链接。
|
|
28
|
+
- **CI lint 范围加入 `examples/` 与 `tools/`**。
|
|
29
|
+
|
|
30
|
+
### Changed (扩展面文档 + 示例升级 — 2026-09-13)
|
|
31
|
+
|
|
32
|
+
- **架构文档新增《扩展面》一节** (`docs/cn/architecture.md` + `docs/en/architecture.md`):
|
|
33
|
+
五个扩展点 (stage 函数 / 生命周期事件 / `ctx.call` / `StorageBackend` / 取消) + 事件表 +
|
|
34
|
+
装饰器注意事项 (`functools.wraps`) + 版本策略。**核心 API 无改动。**
|
|
35
|
+
- **`examples/agent_loop.py` 升级**: 保留原内联循环写法, 新增 `quality_gate` 装饰器版本
|
|
36
|
+
(多 lens 评分 / `min` 聚合 / 阈值 / 分数写 `on_event`), 并加回归测试 (`tests/test_examples_agent_loop.py`)
|
|
37
|
+
防止示例腐化。
|
|
38
|
+
- **pyflakes 范围加入 `examples/`**; 顺带修掉 `tests/` 中一处未使用 import (存量 lint 失败)。
|
|
39
|
+
- 修正 `pavoz/dag.py` docstring 中过期的 stage 签名 (`async def s_x(req, ctx)` → `async def s_x(ctx)`,
|
|
40
|
+
实际调用见 `runtime.py` 的 ctx-only 约定)。
|
|
41
|
+
|
|
42
|
+
### Rejected (2026-09-13 评审决议 — 见 `docs/design/extension-plan-2026-09-13.md`)
|
|
43
|
+
|
|
44
|
+
- 核心不新增全局 hook 注册表: `Runtime(on_event=...)` 已提供同等观察能力, 且注入式优于进程级全局状态。
|
|
45
|
+
- 核心不新增 `ctx._meta`: `Ctx` 在每次 attempt 重建, 不能承载 run 级记账 (计数会被重试绕过)。
|
|
46
|
+
- 下游质量门 / 契约校验以**独立扩展包**形式提供 (pavoz-extensions), 核心保持零依赖、零改动。
|
|
47
|
+
|
|
48
|
+
### Added (consumer PR: ai-research R1/R2/R3 — 2026-09-10, 见 docs/design/pr-reply-ai-research-2026-09-10.md)
|
|
49
|
+
|
|
50
|
+
- **R2 `RunResult.error_class`**: 原始异常类名 (失败时保留, cancelled/done 为 None)。
|
|
51
|
+
未知业务异常 (如 PipelineError) 保真原始类名; `error` 字符串格式不变 (API 冻结)。
|
|
52
|
+
用途: 错误分诊 (业务失败→调参重跑 vs 代码 bug→找人)。
|
|
53
|
+
- **R1 `CheckpointStore.prune(task_id, *, keep_last, dry_run)` + `pavoz prune` CLI**:
|
|
54
|
+
按保留数修剪旧 run checkpoint。排序 = max(stage_ts); **latest 指针指向的 run
|
|
55
|
+
无条件保留**; `--dry-run` 列将删 run + 近似字节。不做 max_runs_per_task 自动修剪
|
|
56
|
+
(v0.4 adapters REVERSED 先例: 不替客户决定保留策略)。
|
|
57
|
+
- **R3 `Ctx.set_progress(fraction, note=None)`**: stage 内进度心跳 → `stage_progress`
|
|
58
|
+
生命周期事件 (第 5 种)。best-effort 瞬态信号: 不落 checkpoint, 不碰 deadline/重试,
|
|
59
|
+
同 fraction 引擎内去抖, 未启用 on_event 时 no-op。
|
|
60
|
+
|
|
61
|
+
### 2026-09-10 之前 (已在 0.3.0-rc1 tag 之后, 未入版本)
|
|
62
|
+
|
|
63
|
+
- ai-write 需求 PR (530f4c2): CLI storage 解耦 `PAVOZ_STORAGE_SPEC` + fork-run `--dry-run` 预演。
|
|
64
|
+
|
|
65
|
+
## [0.3.0-rc1] — 2026-09-09
|
|
66
|
+
|
|
67
|
+
### Added (fork-run 通用 param override — Declarative stage-level debug)
|
|
68
|
+
|
|
69
|
+
**4 个新 CLI 标志** (`pavoz fork-run`):
|
|
70
|
+
- `--set KEY=VALUE` (可重复, 点分路径 KEY, 类型自动推断)
|
|
71
|
+
- `--set-file PATH` (JSON/YAML 文件, 1MB 上限, `yaml.safe_load`)
|
|
72
|
+
- `--compare-with RUN_ID` (跑完后输出 state leaf diff)
|
|
73
|
+
- `--dry-run` flag (仅解析 + 显示, 不执行 stage)
|
|
74
|
+
|
|
75
|
+
**新模块级函数** (`parse_set_args` / `parse_set_file` / `merge_overrides` /
|
|
76
|
+
`apply_overrides` 顶层也导出; `_state_diff` 是 `pavoz.state` 内部 helper,
|
|
77
|
+
仅 compare-with 内部使用, 不在顶层导出面):
|
|
78
|
+
- `parse_set_args(items)` — `--set` 列表解析
|
|
79
|
+
- `parse_set_file(path)` — JSON/YAML 文件解析 (1MB + safe_load + dunder 校验)
|
|
80
|
+
- `merge_overrides(*sources)` — 多源合并 (按优先级)
|
|
81
|
+
- `apply_overrides(base, *patches)` — 接受 dot-path 或 nested dict, 不 mutate base
|
|
82
|
+
|
|
83
|
+
**CLI storage 解耦 (R1)**: `PAVOZ_STORAGE_SPEC` env (`pkg.module:Class`) +
|
|
84
|
+
`PAVOZ_STORAGE_KWARGS` (JSON, `{task_id}` 插值) — CLI 全子命令可操作任意
|
|
85
|
+
StorageBackend (如 consumer 的 MinIO adapter); 不设时现 FileStorage 行为不变。
|
|
86
|
+
|
|
87
|
+
**dry-run 预演 (R2)**: `fork-run --dry-run` 从 "显示解析结果" 升级为
|
|
88
|
+
"预演 apply 效果" — 基于 `rebuild_state_before(stage)` 的执行前 state 做
|
|
89
|
+
leaf 级 diff (`preview.diff_keys_count` / `diff_sample` / `would_rerun`),
|
|
90
|
+
试错不再需要真跑。
|
|
91
|
+
|
|
92
|
+
**Priority merge**: `--set > --set-file > --overrides > --input` (后写覆盖前写;
|
|
93
|
+
`--overrides` > `--input` 沿袭 v0.8 组合使用时的既有胜者)
|
|
94
|
+
|
|
95
|
+
**Safety**:
|
|
96
|
+
- 拒绝 dunder key (`__proto__`, `__class__` 等) — 防 Python object injection
|
|
97
|
+
- 路径深度 ≤ 5 限制
|
|
98
|
+
- YAML 用 `safe_load` (不 `yaml.load`) — 防任意代码执行
|
|
99
|
+
- 文件大小 ≤ 1MB 限制
|
|
100
|
+
|
|
101
|
+
**新 examples** (`examples/`):
|
|
102
|
+
- `01_model_switch.sh` — 4-model composer A/B (ai-research 场景)
|
|
103
|
+
- `02_prompt_patch.py` — 改 prompt 不重跑前序 stage
|
|
104
|
+
- `03_compare_diff.py` — 多 run state diff 对比
|
|
105
|
+
- `04_deep_merge.py` — dot-path 兄弟键保留演示
|
|
106
|
+
- `05_yaml_file.sh` — YAML 文件批改 (dry-run first)
|
|
107
|
+
|
|
108
|
+
**新 tests**:
|
|
109
|
+
- `tests/test_state_apply.py` (54 case) — state.py utilities
|
|
110
|
+
- `tests/test_fork_overrides.py` (6 case) — 深合并语义
|
|
111
|
+
- `tests/test_cli_fork_set.py` (8 case) — CLI 直接调用
|
|
112
|
+
|
|
113
|
+
**新 docs**:
|
|
114
|
+
- `docs/replay-params.md` (完整指南)
|
|
115
|
+
|
|
116
|
+
### Fixed
|
|
117
|
+
|
|
118
|
+
- `fork_run` overrides 应用从顶层浅替换 (`state.update`) 改为深合并
|
|
119
|
+
(`apply_overrides`) — 嵌套 patch 不再抹掉兄弟键 (如 `--set llm.model=x`
|
|
120
|
+
不再丢 `llm.temperature`). 顶层标量覆盖行为不变.
|
|
121
|
+
|
|
122
|
+
### Compatibility
|
|
123
|
+
|
|
124
|
+
| 0.2.0 | 0.3.0 |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `--overrides '{"k":"v"}'` | ✅ unchanged |
|
|
127
|
+
| `--input edited.json` | ✅ unchanged |
|
|
128
|
+
| `Runtime.fork_run(overrides=dict)` | ✅ 签名不变 (嵌套 dict 语义见 Fixed) |
|
|
129
|
+
| `replay --patch patch.py` | ✅ unchanged |
|
|
130
|
+
|
|
131
|
+
### Breaking changes
|
|
132
|
+
|
|
133
|
+
无 (behavior fix 见 Fixed; 顶层标量覆盖不变).
|
|
134
|
+
|
|
135
|
+
### Stdlib
|
|
136
|
+
|
|
137
|
+
保持 zero runtime deps. YAML 是 `[yaml]` optional extra.
|
|
138
|
+
|
|
139
|
+
### Motivation
|
|
140
|
+
|
|
141
|
+
ai-research (consumer) 做 composer 4-model A/B 测试时, 当前痛点:
|
|
142
|
+
- 直接 DB INSERT 绕 API (侵入性)
|
|
143
|
+
- writer_chain 列存 model id (ai_writer-specific)
|
|
144
|
+
- 4 task 并发无 isolation (race condition)
|
|
145
|
+
|
|
146
|
+
pavoz v0.6 已有 `fork_run(overrides=dict)` (LangGraph fork 范式), 但 CLI UX 差
|
|
147
|
+
(JSON string 嵌套痛苦). 本 PR 加 declarative 层, 让 1 行命令 = 切 model 重跑
|
|
148
|
+
stage + diff 输出, 把 2 小时 hack 缩到 15 分钟.
|
|
149
|
+
|
|
150
|
+
## [0.2.0] — 2026-09-08
|
|
151
|
+
|
|
152
|
+
### Changed (stageflow → pavoz 品牌重命名)
|
|
153
|
+
|
|
154
|
+
- 包名: `stageflow` → **`pavoz`** (PyPI 包 + import 路径 + CLI 命令全统一)
|
|
155
|
+
- 仓库: `ebziw/stageflow` → **`liyong-labs/pavoz`** (含 pyproject URLs / docs / CI)
|
|
156
|
+
- 内部目录: `stageflow/` → `pavoz/` (12 文件 git mv)
|
|
157
|
+
- Author: `ebziw` → `liyong-labs`
|
|
158
|
+
|
|
159
|
+
### Added (协作式取消 + 生命周期事件 — 0.8.0 → 0.2.0 之间)
|
|
160
|
+
|
|
161
|
+
- 协作式取消: `Runtime(cancel_check=...)` — stage 间/重试间检查点拦截, `RunResult.status`
|
|
162
|
+
新增 `"cancelled"`; 已完成 stage 照常落 checkpoint, `resume=True` 无缝续跑.
|
|
163
|
+
stage 内长循环用 `ctx.cancelled()` 轮询自行退出 (循环留业务层).
|
|
164
|
+
cancel_check 异常视为未取消 (fail-open).
|
|
165
|
+
- 生命周期事件钩子: `Runtime(on_event=...)` — run_start / stage_start / stage_end /
|
|
166
|
+
stage_retry / run_end 五种结构化事件, observer 异常隔离. `RunResult.stage_timings`
|
|
167
|
+
记录每 stage 墙钟耗时 (含 retry 退避). 单 stage 重放 (run_stage) 不发事件.
|
|
168
|
+
- 重试退避 full-jitter: `uniform(0, min(2^(attempt-1), 30))` — 多 task 同步重试防雷群.
|
|
169
|
+
|
|
170
|
+
## [0.8.0] — 2026-09-06
|
|
171
|
+
|
|
172
|
+
### Added
|
|
173
|
+
|
|
174
|
+
- 协作式取消: `Runtime(cancel_check=...)` — stage 间/重试间检查点拦截, `RunResult.status`
|
|
175
|
+
新增 `"cancelled"`; 已完成 stage 照常落 checkpoint, `resume=True` 无缝续跑.
|
|
176
|
+
stage 内长循环用 `ctx.cancelled()` 轮询自行退出 (循环留业务层).
|
|
177
|
+
cancel_check 异常视为未取消 (fail-open).
|
|
178
|
+
- 生命周期事件钩子: `Runtime(on_event=...)` — run_start / stage_start / stage_end /
|
|
179
|
+
stage_retry / run_end 五种结构化事件, observer 异常隔离. `RunResult.stage_timings`
|
|
180
|
+
记录每 stage 墙钟耗时 (含 retry 退避). 单 stage 重放 (run_stage) 不发事件.
|
|
181
|
+
- 重试退避 full-jitter: `uniform(0, min(2^(attempt-1), 30))` — 多 task 同步重试防雷群.
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
## [0.8.0] — 2026-09-06
|
|
185
|
+
|
|
186
|
+
### Changed (checkpoint 格式: state 不落盘 — 旧格式 cp 兼容读但不再写)
|
|
187
|
+
|
|
188
|
+
- `Checkpoint.state` 字段 → property (从 `initial_state` + `stage_deltas` 惰性
|
|
189
|
+
rebuild) — 落盘 2x 冗余消除. 实测 ai_writer 真实 cp 2.82MB → 2.05MB (-28%),
|
|
190
|
+
rebuild 0ms 等价. 旧存量 cp (带 state) 照常读 (rebuild 覆盖 state 键).
|
|
191
|
+
- 2026-09-06 user 拍板: 发布前向前看, 不做向后兼容 — 序列化层不再写 state,
|
|
192
|
+
无兼容分支
|
|
193
|
+
- `fork_overrides` 新字段: fork_run 的 overrides 持久化 (曾靠全量 state 落盘),
|
|
194
|
+
随每次 checkpoint save 携带 — fork 分支的 overrides 活到分支终点
|
|
195
|
+
|
|
196
|
+
### Fixed
|
|
197
|
+
|
|
198
|
+
- fork resume 丢 overrides: fork_cp 保存后 resume 续跑, 每轮 save 的新 cp 若
|
|
199
|
+
不带 overrides → 分支输入丢失 (从 from_stage 重跑用旧输入)
|
|
200
|
+
- run_stage/replay_from 的 legacy-cp 检查依赖 cp.state truthy — state 变
|
|
201
|
+
property 后恒有值, 检查短路失效 → 防静默空输入重放的防护恢复 (只看 deltas)
|
|
202
|
+
|
|
203
|
+
### Chore
|
|
204
|
+
|
|
205
|
+
- 审计清理 (project-doctor): cli `importlib.sys.modules` → `sys.modules`,
|
|
206
|
+
Ctx.caller sync-lambda default (await dict 隐患) → 单 async `_noop_caller`,
|
|
207
|
+
删死 API `validate_state`, ruff UP034, `.gitignore` + data/
|
|
208
|
+
|
|
209
|
+
## [0.7.0] — 2026-09-06
|
|
210
|
+
|
|
211
|
+
### Added
|
|
212
|
+
|
|
213
|
+
- `Checkpoint.stage_ts`: stage 完成 epoch 时刻 (每 node 落盘, resume/fork 保留前缀) —
|
|
214
|
+
恢复侧内容过期 (TTL) gate 的判定依据 (ai_writer CP 迁移 Step 1 用)
|
|
215
|
+
|
|
216
|
+
## [0.6.0] — 2026-09-06
|
|
217
|
+
|
|
218
|
+
### Added (time-travel fork: 任意历史节点取输入 → 改 → 装回)
|
|
219
|
+
|
|
220
|
+
- `Runtime.fork_run(dag, task_id, *, from_stage, overrides=None, run_id=None)`:
|
|
221
|
+
LangGraph fork 范式 — truncate 到 from_stage 前, initial + 保留 deltas +
|
|
222
|
+
overrides 重建 state, 新 run_id 落盘且 latest 指向 fork, 原 run cp 不动,
|
|
223
|
+
委托 `run(resume=True)` 从 from_stage 续跑 (前序 stage 结果复用不重跑)
|
|
224
|
+
- `Checkpoint.state_stats()`: state 体积观测 (key top N + 构成), CLI `state --stats`
|
|
225
|
+
- CLI: `export-input --task-id X --stage Y` (打印 stage 执行前输入 JSON, 可编辑)
|
|
226
|
+
+ `fork-run ... --input file | --overrides JSON` (装回续跑)
|
|
227
|
+
- 失败/中断的 stage 也可 fork (依赖已全完成) — 修输入重跑失败点
|
|
228
|
+
|
|
229
|
+
## [0.5.1] — 2026-09-05
|
|
230
|
+
|
|
231
|
+
### Added (M2 stage replay + M3 single-stage replay)
|
|
232
|
+
|
|
233
|
+
- **Checkpoint 存每 stage 原始 return** (`stage_deltas`, 按完成序) + run 的
|
|
234
|
+
`initial_state` (M2) — 两者合并可精确重建任意 stage 执行前 state
|
|
235
|
+
(cp.state 是最终合并态, 不能当 stage 输入逆向拆)
|
|
236
|
+
- `Checkpoint.rebuild_state()` / `rebuild_state_before(stage_name)`: 重建
|
|
237
|
+
已完成 stage merge 后的 state / 某 stage 执行前的 state (链式覆盖由
|
|
238
|
+
完成序天然处理)
|
|
239
|
+
- `TestPipe.replay_from(cp, dag)`: 已完成 stage 用历史 delta 全 mock —
|
|
240
|
+
真实 run 的 cp 直接当回归 fixture (改 stage 实现后对比终态);
|
|
241
|
+
hash mismatch / v0.5.0 旧 cp → 明确报错
|
|
242
|
+
- `Runtime.run_stage(dag, task_id, stage_name)`: 从 cp 重建输入单 stage
|
|
243
|
+
重放 (调 prompt/参数秒级迭代) — 不落 cp 不动 latest 指针 (A6/A7 防
|
|
244
|
+
replay 污染 resume 目标); 失败/中断的 stage (依赖已全完成) 也可重放
|
|
245
|
+
- CLI `replay <dag.py> --task-id X --stage Y [--patch P.py]`: 单 stage 重放;
|
|
246
|
+
patch 文件约定 `patch(dag) -> None` 替换 stage fn (async patch 拒绝 —
|
|
247
|
+
防静默 no-op)
|
|
248
|
+
- CLI run 恒落盘 (R5 fix)
|
|
249
|
+
|
|
250
|
+
### Changed
|
|
251
|
+
|
|
252
|
+
- CLI `run` 不再只在 `--resume` 时 attach CheckpointStore — 单跑也写
|
|
253
|
+
`~/.pavoz/data` (`PAVOZ_STORAGE` 可覆盖)。行为: 单次 run 即
|
|
254
|
+
落盘, replay/trace/state 对 CLI 产物直接可用
|
|
255
|
+
|
|
256
|
+
### Compatibility
|
|
257
|
+
|
|
258
|
+
- Checkpoint 新字段 (`initial_state` / `stage_deltas`) `from_dict` `.get`
|
|
259
|
+
默认 `{}` — v0.5.0 旧 cp 可读可 resume
|
|
260
|
+
- v0.5.0 旧 cp (无 stage_deltas) 上重放 (CLI replay / run_stage /
|
|
261
|
+
TestPipe.replay_from) → 明确报错提示重跑一次, 不静默错配
|
|
262
|
+
|
|
263
|
+
### Internal
|
|
264
|
+
|
|
265
|
+
- 81 tests, ruff clean, 零运行时依赖 (stdlib only)
|
|
266
|
+
|
|
267
|
+
## [0.5.0] — 2026-09-05
|
|
268
|
+
|
|
269
|
+
### Added (industry-standard ID model)
|
|
270
|
+
|
|
271
|
+
- `task_id` 参数可选: 省略 → pavoz 自动生成 UUID4。跨 retry/resume 稳定 —
|
|
272
|
+
幂等键 (Temporal WorkflowId / DBOS workflow_id 模式)。入口校验: 非空、
|
|
273
|
+
≤128、字符集 `[A-Za-z0-9_.-]` (禁 `/`, 防 storage key 路径注入)
|
|
274
|
+
- `run_id`: 每次 `Runtime.run()` 自动生成 UUID4, 返回在 `RunResult.run_id`。
|
|
275
|
+
同 task 多次 run 互不覆盖 (original / resume / 重跑)
|
|
276
|
+
- `Ctx.run_id` + `Ctx.attempt` (1-based, stage retry 时 +1) 自动注入
|
|
277
|
+
- `ctx.call` 底层 caller 签名 3 参 → 4 参 (新增 `CallMeta`) —
|
|
278
|
+
caller 落 trace/llm_calls 时可直接关联 task/run/stage/attempt (A1)
|
|
279
|
+
- `CheckpointStore.list_runs(task_id)` + `load_latest(task_id)` (指针文件) helpers
|
|
280
|
+
|
|
281
|
+
### Changed (BREAKING)
|
|
282
|
+
|
|
283
|
+
- Checkpoint 存储 key: `runs/{task_id}/{run_id}/checkpoint` (was
|
|
284
|
+
`runs/{task_id}/checkpoint`) + 指针文件 `runs/{task_id}/latest` =
|
|
285
|
+
`{"run_id": ...}` (uuid4 字典序 ≠ 时间序, 不靠排序判 "最新")
|
|
286
|
+
- `RunResult` / `Checkpoint` 新增必填 `run_id` 字段 (was absent)
|
|
287
|
+
- `CheckpointStore.load(task_id, run_id)` 2 参 (was 1 参); "最新 run" 用
|
|
288
|
+
`load_latest(task_id)`
|
|
289
|
+
- `CheckpointStore.delete(task_id, run_id)` 2 参 (was 1 参 task 级); 指针文件不随
|
|
290
|
+
删除更新 (删的恰是最新 run → 后续 load_latest 返 None, 属可接受边界)
|
|
291
|
+
- `CheckpointStore.load_compatible(task_id, run_id, dag)` 3 参 (was 2 参);
|
|
292
|
+
`run_id=""` = 加载最新
|
|
293
|
+
- `Runtime(caller=...)` 自定义 caller 需接第 4 参 `CallMeta` (默认 no-op 已同步)
|
|
294
|
+
- `Runtime.run()` 移除 `deadline` kwarg (was per-run 绝对超时, 优先于
|
|
295
|
+
`default_timeout`) — run 级超时现在只走 `Runtime.default_timeout`
|
|
296
|
+
- `Runtime.run()` `resume` 默认值 True → False (默认起新 run_id; 续跑需显式
|
|
297
|
+
`resume=True`)
|
|
298
|
+
- resume 语义收紧: `resume=True` 无 checkpoint → RuntimeError; run 已全部完成
|
|
299
|
+
(done guard) → RuntimeError 防静默 no-op。重跑 = `resume=False` 起新 run_id
|
|
300
|
+
- run 完成后 checkpoint **不再自动删除** (v0.4.1 前会清) — done guard 取代
|
|
301
|
+
|
|
302
|
+
### Rationale
|
|
303
|
+
|
|
304
|
+
Adopts Temporal WorkflowId/RunId, DBOS workflow_id idempotency key, and
|
|
305
|
+
Airflow `dag_id + task_id + run_id + try_number` composite key patterns.
|
|
306
|
+
Industry-standard, no caller-invented ID schemes.
|
|
307
|
+
|
|
308
|
+
### Migration (hard cut)
|
|
309
|
+
|
|
310
|
+
No legacy loader. Old `runs/{task_id}/checkpoint` keys become orphaned
|
|
311
|
+
on disk — caller can clean up with their storage backend.
|
|
312
|
+
旧数据 (无 run_id 字段) 同样不可加载。
|
|
313
|
+
|
|
314
|
+
### Internal
|
|
315
|
+
|
|
316
|
+
- 60 tests (52 core + 8 storage_loader), ruff clean, 零运行时依赖 (stdlib only)
|
|
317
|
+
- CLI: resume 语义错误 (无 cp / 已全部完成) → 友好消息 + exit 1, 不炸 traceback
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
## [0.1.1] — 2026-09-05
|
|
321
|
+
|
|
322
|
+
### Added
|
|
323
|
+
|
|
324
|
+
- **链式覆盖 (chain overwrite)**: 下游 stage 可以覆盖**传递上游 producer** 写的
|
|
325
|
+
state key。数据流水线 (如 `search → filter → compress` 逐级演进同一产物) 是
|
|
326
|
+
通用模式, 不再被 one-producer-per-key 误判为平行冲突。
|
|
327
|
+
- `DAG.reachable(upstream, downstream)`: 传递依赖判定
|
|
328
|
+
- Runtime 追踪 `state key → producer stage`, checkpoint 持久化 `producers`
|
|
329
|
+
- 平行 producer (无依赖链) 冲突仍抛 `StateConflictError` (v1 语义保留)
|
|
330
|
+
- 旧 checkpoint (无 producers 字段) resume → 宽松模式 (视同单链), 向后兼容
|
|
331
|
+
|
|
332
|
+
### Changed
|
|
333
|
+
|
|
334
|
+
- `merge_state(prev, delta, stage_name, overwrite_keys=None)`: 新增可选参数
|
|
335
|
+
- `Checkpoint`: 新增 `producers` 字段 (序列化向后兼容, 缺省 `{}`)
|
|
336
|
+
|
|
337
|
+
## [0.1.0] — 2026-09-05
|
|
338
|
+
|
|
339
|
+
### Added
|
|
340
|
+
|
|
341
|
+
首个可用版本 (micro/in-process workflow engine):
|
|
342
|
+
|
|
343
|
+
- **DAG DSL**: `@dag.stage(depends_on, retries, timeout)` 静态声明, Kahn 拓扑
|
|
344
|
+
排序, Tarjan SCC 环检测 (含自环), validate 后冻结
|
|
345
|
+
- **Runtime**: 顺序执行 + per-node 重试 (指数退避) + absolute deadline 传播 +
|
|
346
|
+
state merge + 每 node checkpoint
|
|
347
|
+
- **State 契约**: json-serializable 类型白名单 (str/int/float/bool/None/
|
|
348
|
+
list/dict), `ctx.state` 为只读深拷贝 (写即 raise), `return dict` 是唯一写
|
|
349
|
+
路径, 平行 producer 冲突 → `StateConflictError`
|
|
350
|
+
- **异常契约**: `StageError` (业务失败, 不重试) / `RetryableError` (退避重试,
|
|
351
|
+
耗尽 fail) / `FatalError` (程序 bug, 立即 fail)
|
|
352
|
+
- **Checkpoint**: 每 node 落盘, `workflow_hash` (stage 结构指纹) mismatch →
|
|
353
|
+
拒 resume (只改函数体不影响 hash), run 完成自动清理
|
|
354
|
+
- **ctx.call(kind, op, params)**: 对外调用统一入口 (caller 由业务注入),
|
|
355
|
+
默认 no-op echo (demo/TestPipe 可用)
|
|
356
|
+
- **TestPipe**: 第一天可用的回归 fixture — mock stage 输出跑全图
|
|
357
|
+
- **StorageBackend Protocol + FileStorage**: core 零依赖, 业务可注入
|
|
358
|
+
DB/MinIO adapter
|
|
359
|
+
- **TaskTrigger Protocol**: 业务 task 表边界抽象
|
|
360
|
+
- **CLI**: `run / trace / state / inspect` (v0.1 面)
|
|
361
|
+
- 38 tests, ruff clean, 零运行时依赖 (stdlib only)
|
|
362
|
+
|
|
363
|
+
## [0.1.2] — 2026-09-05
|
|
364
|
+
|
|
365
|
+
### Removed (ponytail cleanup)
|
|
366
|
+
|
|
367
|
+
- `trigger.py` (`TaskTrigger` / `TaskRef` — zero callers)
|
|
368
|
+
- `Ctx.log` / `Ctx.remaining_seconds` (zero callers)
|
|
369
|
+
- `Stage.metadata` field + `@dag.stage` `**metadata` (zero callers)
|
|
370
|
+
- `DAG.entrypoints` / `DAG.depth_of` (zero callers)
|
|
371
|
+
- CLI `inspect` subcommand (空壳 — "v0.1 无 call 级 trace" 提示)
|
|
372
|
+
|
|
373
|
+
### Internal
|
|
374
|
+
|
|
375
|
+
- API 冻结面 zero change: `@dag.stage` / `Runtime.run` / `Ctx` / `State` / 异常 / Checkpoint 全部不动
|
|
376
|
+
- 38 tests 全绿, ruff 0 violation
|
|
377
|
+
|
|
378
|
+
## [0.4.0] — 2026-09-05
|
|
379
|
+
|
|
380
|
+
### Added (pavoz.contrib.storage)
|
|
381
|
+
|
|
382
|
+
- **PostgresStorage** (psycopg3 + JSONB, upsert via ON CONFLICT)
|
|
383
|
+
- **MySQLStorage** (PyMySQL + LONGTEXT, upsert via ON DUPLICATE KEY)
|
|
384
|
+
- **RedisStorage** (redis-py, prefix-scoped, SCAN-based list_keys)
|
|
385
|
+
- **MinioStorage** (boto3, **delete/get 容忍 NoSuchKey/404** 对齐 ai_writer `sf_storage.py`)
|
|
386
|
+
- **SqliteStorage** (stdlib sqlite3, 单文件 DB)
|
|
387
|
+
- 公共 helpers: `_validate_key` (路径安全) + `_encode_payload` / `_decode_payload` (UTF-8 JSON)
|
|
388
|
+
|
|
389
|
+
### Internal
|
|
390
|
+
|
|
391
|
+
- core 12 模块 + `StorageBackend` Protocol 零变化 (API 冻结); 0 行 core 功能修改 (fix1 仅同步 manifest 版本号)
|
|
392
|
+
- contrib 是独立 subpackage (`pavoz.contrib.storage`); core 不引入任何 driver
|
|
393
|
+
- pyproject extras 分组: `[postgres]` / `[mysql]` / `[redis]` / `[minio]` / `[sqlite]` (空) / `[contrib]` (4 个) / `[all]`
|
|
394
|
+
- 缺 driver 时 adapter 模块顶 try/except ImportError + 安装提示
|
|
395
|
+
|
|
396
|
+
### Tests
|
|
397
|
+
|
|
398
|
+
- 38 core tests + 44 contrib tests (10 标 `@pytest.mark.integration` 需 docker)
|
|
399
|
+
- MinioStorage 用 moto[s3] mock; RedisStorage 用 fakeredis mock (无 docker 依赖)
|
|
400
|
+
|
|
401
|
+
## [0.4.1] — 2026-09-05
|
|
402
|
+
|
|
403
|
+
### Removed (reversal of v0.4 contrib approach)
|
|
404
|
+
|
|
405
|
+
- `pavoz.contrib.storage` 5 个 vendor adapter (SqliteStorage / PostgresStorage / MySQLStorage / RedisStorage / MinioStorage) — over-engineering, user 自管 deps + 自写 adapter 更合适
|
|
406
|
+
- `pavoz.contrib` subpackage 完全移除
|
|
407
|
+
- pyproject.toml extras (`postgres` / `mysql` / `redis` / `minio` / `sqlite` / `contrib` / `all`) 移除 — 用户自己装自己要的
|
|
408
|
+
- 相关 tests (tests/contrib/) 移除
|
|
409
|
+
|
|
410
|
+
### Added
|
|
411
|
+
|
|
412
|
+
- `pavoz.storage_loader.load_storage(spec: str, **kwargs) -> StorageBackend` — 按 `"pkg.module:ClassName"` 字符串 importlib 加载
|
|
413
|
+
- `pavoz.PavozStorageError` — 友好错误 (module 未装 / class 名错 / kwargs 错 / 类型错 5 类)
|
|
414
|
+
- 用户可自己写 adapter, pavoz 不带 driver, 通过 `pip install` 自管 deps
|
|
415
|
+
- ai_writer 兼容: `load_storage("backend.integration.sf_storage.MinioStorage", ...)` 直接可用
|
|
416
|
+
|
|
417
|
+
### Internal
|
|
418
|
+
|
|
419
|
+
- core 仍 stdlib-only + 零第三方依赖
|
|
420
|
+
- 0 行 core 修改 (除新增 storage_loader.py + 2 行 export)
|
|
421
|
+
- reversal 是真实设计演化: v0.4 contrib 是反向教材, git history 保留以做记录
|
|
422
|
+
|
|
423
|
+
## [0.1.3] — 2026-09-05
|
|
424
|
+
|
|
425
|
+
### Fixed
|
|
426
|
+
|
|
427
|
+
- `pyproject.toml` version + `pavoz/__init__.py` `__version__` 与 tag 同步 (0.1.1 → 0.1.3)。final-review B1: 之前 `pip install -e .` / `import pavoz; pavoz.__version__` 返 0.1.1,与 v0.1.2 tag 不符。
|
|
428
|
+
- 不 amend v0.1.2 (keep git history honest) — 走新 patch tag v0.1.3
|
pavoz-0.3.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# CLAUDE.md — pavoz
|
|
2
|
+
|
|
3
|
+
> 本文件只放**铁律 + 指针**. 架构细节在 `docs/design/`, 阶段路线在 `ROADMAP.md`, 时序决策日志在 `docs/work-note/`, **使用文档**:
|
|
4
|
+
> - **30 秒上手**: `docs/QUICKSTART.md` (摘要 + 5 行示例 + gotchas)
|
|
5
|
+
> - **完整 doc**: `docs/handoff/usage.md` (10 节, 含端到端示例 + 决策树)
|
|
6
|
+
> - **架构**: `docs/design/architecture.md` (核心理念 / 模块 / 边界)
|
|
7
|
+
> - **AI 写作指南**: `docs/handoff/ai-writer-style-handoff.md` (供 LLM agent 看)
|
|
8
|
+
|
|
9
|
+
## 定位 (2026-09-05 user 拍板, 永久生效)
|
|
10
|
+
|
|
11
|
+
**pavoz = 独立通用 Python 工作流编排库, 准备公开发布的开源项目.**
|
|
12
|
+
|
|
13
|
+
**核心原则**: pavoz 是**通用**项目, ai_writer 仅是第 1 个 use case (不是专属用户, 不是设计中心).
|
|
14
|
+
|
|
15
|
+
**含义**:
|
|
16
|
+
- ❌ pavoz **不能**为 ai_writer 业务定制任何 feature (research-specific / writing-specific / 公众号-specific)
|
|
17
|
+
- ❌ pavoz **不能**假设 stage 名是 `s_plan / s_search / s_compose` — 这些是 ai_writer 的
|
|
18
|
+
- ❌ pavoz **不能**假设 storage 是 MinIO / PG / 业务 DB — 这些是 caller 的
|
|
19
|
+
- ❌ pavoz **不能**硬编码 ai_writer 的配置 / schema / 模型路由表
|
|
20
|
+
- ✅ pavoz **只暴露**通用原语 (DAG / Runtime / Ctx / State / Checkpoint / Protocol)
|
|
21
|
+
- ✅ ai_writer 通过 `StorageBackend` / `CallRecorder` / `TaskTrigger` Protocol **注入**自己的实现
|
|
22
|
+
- ✅ 任何其他项目 (RAG / 数据 pipeline / ETL / ML training / agent workflow) 都应**同等**使用 pavoz
|
|
23
|
+
|
|
24
|
+
**为什么**: pavoz 不绑业务 = 不会被业务拖死 (Uber Piper 反面教材, 8 年自研 EOL 2026 回流 Airflow). 这是 pavoz 的最大资产.
|
|
25
|
+
|
|
26
|
+
**开发纪律**:
|
|
27
|
+
- 改 pavoz 前先问: "这个改动对 ai_writer 之外的项目也有用吗?"
|
|
28
|
+
- 如果答案是否, 改 ai_writer 而不是 pavoz
|
|
29
|
+
- 如果答案是是, 加进 pavoz 但**通用化** (不能含 ai_writer 名字/术语/路径)
|
|
30
|
+
- docs 不能写 "pavoz 是 ai_writer 的子项目 / 配套工具 / 由 ai_writer 维护"
|
|
31
|
+
- examples/ 目录可以有 ai_writer 例子, 但必须有 ≥2 个其他 use case 平衡
|
|
32
|
+
|
|
33
|
+
**踩过的坑 (避免再犯)**:
|
|
34
|
+
- iter 5 v0.4 contrib storage adapters — 5 个 adapter (Sqlite/Postgres/MySQL/Redis/MinIO) vendor 进 pavoz, user 拍板反转 (c7f760b → 984ebfb). 反转理由: pavoz 不该替客户决定需要哪些 DB/S3 client. 教训: vendor adapter 是 over-engineering, user 自管 deps + 自写 adapter 更合适.
|
|
35
|
+
|
|
36
|
+
## 核心文档索引
|
|
37
|
+
|
|
38
|
+
- **架构**: `docs/design/architecture.md` — 5 原语 / Protocol 设计 / 边界
|
|
39
|
+
- **5 原语**: `docs/design/dag.md` / `runtime.md` / `state.md` / `checkpoint.md` / `protocols.md`
|
|
40
|
+
- **API 参考**: `docs/api.md`
|
|
41
|
+
- **路线**: `ROADMAP.md` (v0.2 freeze + 后续候选)
|
|
42
|
+
- **决策日志**: `docs/work-note/` (时序, 跨 session 沉淀)
|
|
43
|
+
- **变更历史**: `CHANGELOG.md`
|
|
44
|
+
|
|
45
|
+
## 项目结构
|
|
46
|
+
|
|
47
|
+
- 源码: `pavoz/` (git 跟踪)
|
|
48
|
+
- 部署: N/A — pavoz 是 library, 跟 caller 一起 ship
|
|
49
|
+
- 集成测试: 由 caller 项目承接 (library 无独立部署)
|
|
50
|
+
|
|
51
|
+
## 开发铁律
|
|
52
|
+
|
|
53
|
+
1. **stdlib only** — `pyproject.toml` 永远 `dependencies = []`. 0 第三方运行时依赖. (调研结论: zeroflow 同形竞品对齐)
|
|
54
|
+
2. **protocol 优先** — 跨项目差异走 Protocol (StorageBackend / CallRecorder / TaskTrigger), 不写默认实现
|
|
55
|
+
3. **YAGNI 严格** — 不加 dynamic DAG / subgraph / scheduler / UI / distributed / cron. (Uber Piper 反例)
|
|
56
|
+
4. **workflow_hash** — checkpoint 存 DAG 结构指纹, resume 时 mismatch 拒续跑
|
|
57
|
+
5. **YAGNI 上限** — M1 (state 观测) / M2 (replay fixture) / M3 (replay --prompt-patch) 是 v0.2 锁定范围. 不加 M4+
|
|
58
|
+
|
|
59
|
+
## 测试铁律
|
|
60
|
+
|
|
61
|
+
- 单测覆盖率 ≥ 80% (重点: DAG parser / state merge / retry_budget decrement)
|
|
62
|
+
- TestPipe fixture v0.1 第一天就有 (justpipe 启示)
|
|
63
|
+
- pytest markers: `unit` / `integration` / `slow` (integration 默认 skip)
|
|
64
|
+
- ruff 0 violations
|
|
65
|
+
- pyright strict 0 errors
|
|
66
|
+
|
|
67
|
+
## 发布铁律
|
|
68
|
+
|
|
69
|
+
- 语义化版本 (semver)
|
|
70
|
+
- v0.2 API freeze (见 ROADMAP.md)
|
|
71
|
+
- tag 必须 local + remote 一致
|
|
72
|
+
- CHANGELOG 每版本必写
|
|
73
|
+
- **push 永远由 user 决定**, AI 不主动 push
|
|
74
|
+
|
|
75
|
+
## 不做的事 (YAGNI 永久)
|
|
76
|
+
|
|
77
|
+
- ❌ 不引入重依赖 (Prefect / Airflow / Dagster 都是 100+ deps 的反面教材)
|
|
78
|
+
- ❌ 不做可视化 UI / Web server / scheduler (M4+ 候选, 不主动)
|
|
79
|
+
- ❌ 不做 conditional DAG (depends_on + retries 足够, 业务 while 在 stage 内)
|
|
80
|
+
- ❌ 不做 parallel stage (asyncio.gather 业务自管)
|
|
81
|
+
- ❌ 不做 distributed runtime (in-process 是核心定位)
|
|
82
|
+
- ❌ 不做 HITL (业务侧自己实现, 不进 pavoz)
|
|
83
|
+
- ❌ 不做 sub-DAG / nested DAG (循环在业务 super-node 内)
|
|
84
|
+
- ❌ 不做 plugin/entry_points 强制注册 (config string 足够, 借鉴 Kedro catalog)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 贡献指南
|
|
2
|
+
|
|
3
|
+
pavoz 是通用流程编排库 — 贡献前请先读 [`docs/cn/architecture.md`](docs/cn/architecture.md)
|
|
4
|
+
(英文: [`docs/en/architecture.md`](docs/en/architecture.md)), 理解三个不变量:
|
|
5
|
+
|
|
6
|
+
1. **core 零依赖**: `pavoz/` 不 import 任何第三方库 (stdlib only)。
|
|
7
|
+
存储/外部调用/任务表全部走 Protocol, 由业务侧注入。
|
|
8
|
+
2. **循环留在业务层**: 框架只有 DAG + per-node retries + checkpoint 三种能力。
|
|
9
|
+
审计循环、收敛 gate 等是 stage 函数内的普通 Python — 不要给框架加
|
|
10
|
+
`retry_budget` / `escalation` 之类的流程原语。
|
|
11
|
+
3. **API 最小化**: 每个新字段/装饰器参数必须有真实 use case, 不做"未来可能用"
|
|
12
|
+
的预留。
|
|
13
|
+
|
|
14
|
+
## 开发环境
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git clone git@github.com:liyong-labs/pavoz.git
|
|
18
|
+
cd pavoz
|
|
19
|
+
python -m venv .venv && source .venv/bin/activate
|
|
20
|
+
pip install -e ".[dev]"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 质量门槛 (PR 前必须全过 — 与 CI 同款命令)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
python -m pytest # 全部测试
|
|
27
|
+
python -m pyflakes pavoz tests examples # lint
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- 新行为必须有对应测试 (`tests/test_*.py`); 示例 (`examples/`) 有回归测试防腐化
|
|
31
|
+
- 改 merge/checkpoint 语义时, 向后兼容 (旧 checkpoint 能 resume) 是硬要求
|
|
32
|
+
|
|
33
|
+
## 提交规范
|
|
34
|
+
|
|
35
|
+
- 中文或英文 message 均可, 前缀 `feat|fix|docs|refactor|test|chore`
|
|
36
|
+
- 破坏性变更在 message 标注 `BREAKING` 并更新 CHANGELOG
|
|
37
|
+
- 语义版本: 兼容性行为变更 = minor, 破坏性 = major
|
|
38
|
+
|
|
39
|
+
## 文档
|
|
40
|
+
|
|
41
|
+
- 公开 API 变更同步 `docs/cn/api.md` + `docs/en/api.md`
|
|
42
|
+
- 定位/能力边界变更同步 `docs/cn/architecture.md` + `docs/en/architecture.md`
|
|
43
|
+
- 新版本条目追加 `CHANGELOG.md`
|
|
44
|
+
- 业务接入示例归 `docs/cn/use-cases/` + `docs/en/use-cases/` (参考实现, 不进主文档)
|
|
45
|
+
|
|
46
|
+
## Issue / PR
|
|
47
|
+
|
|
48
|
+
- Bug report 请附: 最小复现 DAG + 期望 vs 实际
|
|
49
|
+
- Feature request 请说明 use case (无真实 use case 的 feature 会被拒)
|
pavoz-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ebziw
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|