pyattacker 0.3.1__tar.gz → 0.3.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.
- pyattacker-0.3.3/.github/workflows/docs.yml +55 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/.gitignore +2 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/CHANGELOG.md +79 -6
- {pyattacker-0.3.1 → pyattacker-0.3.3}/PKG-INFO +48 -20
- {pyattacker-0.3.1 → pyattacker-0.3.3}/README.md +46 -18
- {pyattacker-0.3.1 → pyattacker-0.3.3}/README.zh-CN.md +35 -12
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/cli.md +25 -13
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/design.md +54 -48
- pyattacker-0.3.3/docs/documentation.md +49 -0
- pyattacker-0.3.3/docs/hooks.py +15 -0
- pyattacker-0.3.3/docs/index.md +48 -0
- pyattacker-0.3.3/docs/overrides/main.html +8 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/reference.md +100 -35
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/releasing.md +3 -2
- pyattacker-0.3.3/docs/suites.md +276 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/tutorial.md +37 -32
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/zh-CN/cli.md +21 -9
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/zh-CN/design.md +39 -28
- pyattacker-0.3.3/docs/zh-CN/documentation.md +48 -0
- pyattacker-0.3.3/docs/zh-CN/index.md +48 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/zh-CN/reference.md +87 -34
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/zh-CN/releasing.md +3 -2
- pyattacker-0.3.3/docs/zh-CN/suites.md +157 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/zh-CN/tutorial.md +34 -28
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/llm_eval/README.md +1 -1
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/llm_eval/README.zh-CN.md +1 -1
- pyattacker-0.3.3/examples/suites/experiment.json +14 -0
- pyattacker-0.3.3/examples/suites/suite.json +12 -0
- pyattacker-0.3.3/mkdocs.yml +97 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/pyproject.toml +13 -2
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/__init__.py +6 -1
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/cli.py +84 -18
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/declarative.py +15 -3
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/errors.py +5 -2
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/export.py +52 -8
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/handoff.py +23 -20
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/monitor.py +18 -3
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/pipeline.py +14 -5
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/reported_metrics.py +1 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/runner.py +97 -21
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/server.py +65 -18
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/store/__init__.py +2 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/store/base.py +31 -6
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/store/memory.py +3 -1
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/store/sqlite.py +46 -10
- pyattacker-0.3.3/src/pyattacker/store/suite.py +1125 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/store/writebehind.py +51 -14
- pyattacker-0.3.3/src/pyattacker/suite.py +340 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/task.py +8 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/helpers.py +9 -0
- pyattacker-0.3.3/tests/test_admission.py +224 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_backends.py +1 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_backward.py +1 -1
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_cli.py +2 -2
- pyattacker-0.3.3/tests/test_dashboard_security.py +64 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_docs_examples.py +1 -1
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_docs_i18n.py +3 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_export.py +2 -2
- pyattacker-0.3.3/tests/test_fact_batches.py +280 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_handoff.py +1 -1
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_reported_metrics.py +2 -2
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_server.py +12 -2
- pyattacker-0.3.3/tests/test_suite.py +822 -0
- pyattacker-0.3.3/tests/test_suite_queries.py +219 -0
- pyattacker-0.3.3/uv.lock +685 -0
- pyattacker-0.3.1/uv.lock +0 -172
- {pyattacker-0.3.1 → pyattacker-0.3.3}/.github/workflows/ci.yml +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/.github/workflows/release.yml +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/LICENSE +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/benchmark.md +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/docs/zh-CN/benchmark.md +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/data.jsonl +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/live_metrics.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/llm_eval/__init__.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/llm_eval/backend.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/llm_eval/demo.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/llm_eval/pipelines.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_package/README.md +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_package/README.zh-CN.md +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_package/pa_demo_plugin/__init__.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_package/pa_demo_plugin/algorithms.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_package/pa_demo_plugin/codecs.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_package/pa_demo_plugin/tasks.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_package/pyproject.toml +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/plugin_tasks.yaml +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/qa_eval.yaml +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/quickstart.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/examples/sharded.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/__main__.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/algorithm.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/artifact.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/backends.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/benchmark/__init__.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/benchmark/clock.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/benchmark/harness.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/benchmark/metrics.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/benchmark/provider.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/benchmark/report.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/benchmark/scenario.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/history.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/merge.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/plugins.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/resource.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/scheduler.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/shard.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/store/visits.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/src/pyattacker/tasks/__init__.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_algorithms.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_artifact.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_benchmark_cli.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_benchmark_clock.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_benchmark_harness.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_benchmark_provider.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_benchmark_report.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_benchmark_scenario.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_declarative.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_docs_facts.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_errors.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_lease_safety.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_m2.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_monitor.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_optional_yaml.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_packaging.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_pipeline.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_plugins.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_resume_cursor.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_resume_identity.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_retry_policy.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_runner.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_scheduler.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_shard.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_store.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_subprocess_lifecycle.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_task_overrides.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_tasks.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_tutorial.py +0 -0
- {pyattacker-0.3.1 → pyattacker-0.3.3}/tests/test_worker_liveness.py +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: docs-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: Build documentation
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
python-version: '3.11'
|
|
26
|
+
cache-suffix: docs
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --locked --group docs
|
|
29
|
+
- name: Build site and check links
|
|
30
|
+
run: uv run --group docs mkdocs build --strict
|
|
31
|
+
- name: Check bilingual documentation and executable examples
|
|
32
|
+
run: uv run pytest tests/test_docs_i18n.py tests/test_docs_examples.py tests/test_tutorial.py
|
|
33
|
+
- name: Upload Pages artifact
|
|
34
|
+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
|
|
35
|
+
uses: actions/upload-pages-artifact@v4
|
|
36
|
+
with:
|
|
37
|
+
path: site
|
|
38
|
+
|
|
39
|
+
deploy:
|
|
40
|
+
name: Deploy documentation
|
|
41
|
+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
permissions:
|
|
45
|
+
pages: write
|
|
46
|
+
id-token: write
|
|
47
|
+
environment:
|
|
48
|
+
name: github-pages
|
|
49
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
50
|
+
steps:
|
|
51
|
+
- name: Configure Pages
|
|
52
|
+
uses: actions/configure-pages@v5
|
|
53
|
+
- name: Deploy Pages
|
|
54
|
+
id: deployment
|
|
55
|
+
uses: actions/deploy-pages@v4
|
|
@@ -4,6 +4,78 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.3.3] — 2026-09-21
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
* **Dashboard HTML injection** (#78): render event and card values as text rather than
|
|
14
|
+
interpolating untrusted values into HTML.
|
|
15
|
+
* **Atomic write-behind batches** (#79): SQLite commits attempts and events in one transaction
|
|
16
|
+
and assigns IDs only after commit. Failed batches remain recoverable; legacy stores stop
|
|
17
|
+
accepting fact writes after an uncertain failure. Suite eviction and shutdown preserve
|
|
18
|
+
failure boundaries and close remaining resources.
|
|
19
|
+
* **Suite query correctness and cost** (#80): apply experiment/run filters before handoff
|
|
20
|
+
limits, support task limits consistently across built-in stores, and select recent events
|
|
21
|
+
using timestamp indexes. Counts and grouped statistics avoid decoding full history while
|
|
22
|
+
preserving historical run outcomes. Read-only older databases remain supported.
|
|
23
|
+
* Loopback HTTP tests bypass environment proxies (#76).
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
* **Bounded pipeline admission** (#81): `max_admitted` limits queued, executing and delayed
|
|
28
|
+
pipelines together, while due retries retain their admission. Configure it through Runner,
|
|
29
|
+
the run configuration or `--max-admitted`; Suite members share one Runner budget. Live
|
|
30
|
+
statistics and persisted run configuration expose the effective limit.
|
|
31
|
+
* **Bilingual documentation website** (#82): tutorials, Python API and CLI references at
|
|
32
|
+
<https://hazer-bjtu.github.io/pyattacker/>, with search, strict link checks and automated
|
|
33
|
+
GitHub Pages publishing. The site tracks main and identifies itself as development documentation.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
* Admission is bounded by default to `4 * concurrency`; a positive `max_admitted` overrides
|
|
38
|
+
this. Long retry backoffs can now pause input consumption. This bounds scheduler state
|
|
39
|
+
counts, not payload bytes or total memory; MemoryStore still retains accumulated data.
|
|
40
|
+
* `tasks(limit=0)` returns an empty list; negative and non-integer task limits are rejected.
|
|
41
|
+
Suite timestamp indexes add disk/write and first-writer migration cost. Exact duration
|
|
42
|
+
percentiles still read and sort matching pipeline durations.
|
|
43
|
+
* English and Chinese export documentation now covers the `results` row type (#77), and
|
|
44
|
+
streaming-memory claims describe admission and store retention separately (#81).
|
|
45
|
+
* Development and documentation lockfile downloads use official PyPI URLs for portable CI
|
|
46
|
+
installation, without changing artifact hashes or adding library runtime dependencies (#82).
|
|
47
|
+
|
|
48
|
+
## [0.3.2] — 2026-09-21
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
* **Handoffs and forward/backward jumps are supported public features.** `Handoff.to`,
|
|
53
|
+
`Handoff.end`, `Handoff.rewind`, `Handoff.retry_all` and their `control` declarations now
|
|
54
|
+
follow the project's regular versioning policy, without a separate experimental tier.
|
|
55
|
+
Code documentation and both language editions use this status consistently, including
|
|
56
|
+
the companion payload-history guide. Explicit declarations, finite backward budgets,
|
|
57
|
+
atomic store capabilities, execution semantics and persisted identities remain unchanged.
|
|
58
|
+
Documentation links use the new headings; legacy anchors preserve published bookmarks.
|
|
59
|
+
Earlier release entries below describe their status at the time of release.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
* **Experiment suites** (#68): reference existing configurations and schedule their inputs
|
|
64
|
+
round-robin through one Runner with shared global concurrency and explicit local pool bindings.
|
|
65
|
+
Stable Suite/member identities preserve original local keys and isolate checkpoints and metrics.
|
|
66
|
+
* Optional `by_experiment` output directories alongside default `combined` storage, with a
|
|
67
|
+
persistent catalog, source-exhaustion state, selective resume and bounded child connections.
|
|
68
|
+
Directory-aware report/watch/serve/export provide cumulative and durable invocation views,
|
|
69
|
+
preserving historical failures, skips and execution timing after resume. Recovery finalizes
|
|
70
|
+
abandoned catalog runs as interrupted, freezing their elapsed time. Unselected member
|
|
71
|
+
databases remain untouched. Pool environment placeholders allow credential rotation;
|
|
72
|
+
reopening validates the persisted artifact backend.
|
|
73
|
+
* `export --rows results`, `--by-experiment`, member-scoped reported metrics, and task context
|
|
74
|
+
experiment/output-directory fields. Split export filenames follow the selected row kind.
|
|
75
|
+
Single-store exports atomically replace completed files; admitted specs repair migrated repeats.
|
|
76
|
+
* English and Chinese Suite guides and a runnable JSON example. Existing standalone path/ID
|
|
77
|
+
semantics remain unchanged; Suite mode explicitly rejects independent process shards/custom stores.
|
|
78
|
+
|
|
7
79
|
## [0.3.1] — 2026-09-19
|
|
8
80
|
|
|
9
81
|
### Added
|
|
@@ -45,7 +117,7 @@ All notable changes to this project are documented here. The format follows
|
|
|
45
117
|
keeps the append-only history, and a store that has committed its first revisit records a `base` → `visits-v1`
|
|
46
118
|
feature level (`store.feature_level()`, `StoreFeatureUnsupported`) so lineage-unaware writers are refused
|
|
47
119
|
rather than silently mutating the wrong occurrence. See
|
|
48
|
-
[reference → advanced: backward traversal](docs/reference.md#
|
|
120
|
+
[reference → advanced: backward traversal](docs/reference.md#backward-traversal-rewind-retry-all-visits).
|
|
49
121
|
|
|
50
122
|
* **Advanced feature: handoffs — a task can skip ahead, on the record (opt-in, experimental).** A task may
|
|
51
123
|
return `Handoff.to(target, value)` to continue at a declared later station, or `Handoff.end(value)` to
|
|
@@ -68,9 +140,9 @@ All notable changes to this project are documented here. The format follows
|
|
|
68
140
|
commit them atomically; both built-in backends can, and a store that cannot is refused up front with a
|
|
69
141
|
`ConfigError` rather than silently writing a non-durable jump. Marked *experimental until 1.0*;
|
|
70
142
|
the forward declaration excludes backward targets; joins and cross-pipeline jumps are not included. See
|
|
71
|
-
[`docs/design.md` §4.8](docs/design.md#48-
|
|
72
|
-
the [API reference](docs/reference.md#
|
|
73
|
-
[tutorial step 15](docs/tutorial.md#step-15--
|
|
143
|
+
[`docs/design.md` §4.8](docs/design.md#48-handoffs--declared-forward-jumps-opt-in),
|
|
144
|
+
the [API reference](docs/reference.md#handoffs-opt-in) and
|
|
145
|
+
[tutorial step 15](docs/tutorial.md#step-15--skipping-stations-handoffs).
|
|
74
146
|
|
|
75
147
|
### Fixed
|
|
76
148
|
|
|
@@ -206,9 +278,9 @@ All notable changes to this project are documented here. The format follows
|
|
|
206
278
|
|
|
207
279
|
* **The backward-traversal guide is merged into the tutorial and the reference.** `docs/backward.md` was a
|
|
208
280
|
seventh document that a reader had to find before they could use the feature; its usage now lives where
|
|
209
|
-
the rest of the API does. [Tutorial step 16](docs/tutorial.md#step-16--
|
|
281
|
+
the rest of the API does. [Tutorial step 16](docs/tutorial.md#step-16--regenerating-with-rewind-and-retry-all)
|
|
210
282
|
keeps the rewind/retry-all walkthrough and a new step 17 builds an optional `HistoryArtifact` payload,
|
|
211
|
-
while [reference → advanced: backward traversal](docs/reference.md#
|
|
283
|
+
while [reference → advanced: backward traversal](docs/reference.md#backward-traversal-rewind-retry-all-visits)
|
|
212
284
|
gains the `Handoff.rewind`/`Handoff.retry_all` signatures, the backward `control` keys, the visit and
|
|
213
285
|
occurrence model, the budget and recovery rules, and the inspection surface; `HistoryArtifact` is
|
|
214
286
|
documented next to the codecs it belongs to, and the store capability and compatibility/backup rules moved
|
|
@@ -580,6 +652,7 @@ hard limit; asyncio tasks only (wrap blocking code with `asyncio.to_thread`); on
|
|
|
580
652
|
balance is statistical; a merged report is a union, not a sum; a pipeline is a linear chain; the HTTP endpoint
|
|
581
653
|
is unauthenticated.
|
|
582
654
|
|
|
655
|
+
[0.3.3]: https://github.com/Hazer-BJTU/pyattacker/releases/tag/v0.3.3
|
|
583
656
|
[0.3.0]: https://github.com/Hazer-BJTU/pyattacker/releases/tag/v0.3.0
|
|
584
657
|
[0.2.0]: https://github.com/Hazer-BJTU/pyattacker/releases/tag/v0.2.0
|
|
585
658
|
[0.1.1]: https://github.com/Hazer-BJTU/pyattacker/releases/tag/v0.1.1
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyattacker
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Artifact-centric, resumable async task orchestration: pipeline / task / resource pool.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Hazer-BJTU/pyattacker
|
|
6
6
|
Project-URL: Repository, https://github.com/Hazer-BJTU/pyattacker
|
|
7
7
|
Project-URL: Issues, https://github.com/Hazer-BJTU/pyattacker/issues
|
|
8
8
|
Project-URL: Changelog, https://github.com/Hazer-BJTU/pyattacker/blob/main/CHANGELOG.md
|
|
9
|
-
Project-URL: Documentation, https://github.
|
|
9
|
+
Project-URL: Documentation, https://hazer-bjtu.github.io/pyattacker/
|
|
10
10
|
Project-URL: Design, https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/design.md
|
|
11
11
|
Author: pyattacker contributors
|
|
12
12
|
License: MIT
|
|
@@ -34,6 +34,8 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
|
|
35
35
|
**English** | [简体中文](README.zh-CN.md)
|
|
36
36
|
|
|
37
|
+
[Documentation](https://hazer-bjtu.github.io/pyattacker/)
|
|
38
|
+
|
|
37
39
|
> Run tens of thousands of independent tasks to completion — resumably, observably, and without
|
|
38
40
|
> reimplementing endpoint pools, retries and "which rows already ran" for the fifth time.
|
|
39
41
|
|
|
@@ -93,7 +95,7 @@ Five concepts, and that is the whole vocabulary:
|
|
|
93
95
|
| Concept | Meaning | In one line |
|
|
94
96
|
|---|---|---|
|
|
95
97
|
| **artifact** | the persisted state of a task | content-addressed, **persisted as soon as it is produced** → checkpoint granularity = task |
|
|
96
|
-
| **task** | the smallest unit of scheduling | a unary `(artifact) -> artifact` function, sync or async — or `-> artifact \| Handoff`, to skip ahead ([
|
|
98
|
+
| **task** | the smallest unit of scheduling | a unary `(artifact) -> artifact` function, sync or async — or `-> artifact \| Handoff`, to skip ahead ([handoffs](#handoffs-opt-in)) |
|
|
97
99
|
| **pipeline** | the unit of completion | `fetch \| ask \| judge \| metrics` chained linearly, semantically independent of each other |
|
|
98
100
|
| **resource** | a leasable external capability | one endpoint / one key; once pooled, it can be published and subscribed to concurrency-safely |
|
|
99
101
|
| **algorithm** | the policy for acquiring resources | `wait`, `backoff`, `least_busy`, `failover`, `sticky`, `quota_aware`, `immediate` — orthogonal to "retry on failure" |
|
|
@@ -119,7 +121,7 @@ async def fetch(row: dict, ctx) -> dict:
|
|
|
119
121
|
|
|
120
122
|
|
|
121
123
|
def dataset_rows():
|
|
122
|
-
"""Your dataset.
|
|
124
|
+
"""Your dataset. `map` streams generators; max_admitted bounds unfinished pipelines."""
|
|
123
125
|
return [{"q": f"question-{i}"} for i in range(4)]
|
|
124
126
|
|
|
125
127
|
|
|
@@ -135,6 +137,11 @@ with Runner(store="runs/qa.db", pools=[pool], concurrency=8) as runner:
|
|
|
135
137
|
report.export_jsonl("runs/qa.jsonl")
|
|
136
138
|
```
|
|
137
139
|
|
|
140
|
+
Streaming input uses `max_admitted` (default: `4 * concurrency`) to bound queued,
|
|
141
|
+
running and retry-delayed pipelines together. This is a count limit, not a byte
|
|
142
|
+
limit; task payloads, source buffers and Store retention also consume memory.
|
|
143
|
+
The default `MemoryStore` retains results as data grows. See [RunConfig](docs/reference.md#runconfig).
|
|
144
|
+
|
|
138
145
|
`ctx.acquire()` falls back to the task's declared `resource`, and `lease.report(ok=...)` feeds the pool's
|
|
139
146
|
health and quota accounting.
|
|
140
147
|
|
|
@@ -202,7 +209,7 @@ runner.run(template.map(rows), resume=True) # or pyattacker resume -c config.y
|
|
|
202
209
|
* Failed pipelines → continue from **the first task that produced no artifact**: **if task C died, only task C
|
|
203
210
|
reruns when task B's checkpoint is durable**;
|
|
204
211
|
* The seed artifact is persisted too → recovery **does not depend on the original dataset file**;
|
|
205
|
-
* A pipeline that **handed off** ([
|
|
212
|
+
* A pipeline that **handed off** ([handoffs](#handoffs-opt-in)) resumes at the station it jumped to,
|
|
206
213
|
with the entry state the ledger recorded — the task that handed off is not re-run;
|
|
207
214
|
* Changed a task's source code (`spec_digest` includes source digests) → treated as a new pipeline, so old
|
|
208
215
|
results are not incorrectly reused. Factory parameters, fanout children, retry/algorithm policies and
|
|
@@ -213,10 +220,13 @@ rerun and old explicit keys conflict. Finish old runs with the old package, then
|
|
|
213
220
|
[resume identity and idempotency reference](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#resume-identity)
|
|
214
221
|
for migration guidance, dynamic functions and external configuration.
|
|
215
222
|
|
|
216
|
-
|
|
223
|
+
<a id="advanced-handoffs-opt-in"></a>
|
|
224
|
+
|
|
225
|
+
## Handoffs (Opt-In)
|
|
226
|
+
|
|
227
|
+
**Handoffs, forward jumps and rewinds are supported control-flow features, enabled explicitly through `control`.**
|
|
217
228
|
|
|
218
|
-
|
|
219
|
-
1.0.** A task can decide that the rest of the chain no longer needs to run, and *say so* instead of inventing a
|
|
229
|
+
A task can decide that the rest of the chain no longer needs to run, and *say so* instead of inventing a
|
|
220
230
|
failure or hiding the branch inside one step. It **returns** a directive — `Handoff.to(target, value)` to
|
|
221
231
|
continue at a declared later station, `Handoff.end(value)` to finish the pipeline right there:
|
|
222
232
|
|
|
@@ -296,16 +306,16 @@ with Runner(store=":memory:", concurrency=4) as runner:
|
|
|
296
306
|
payloads provide explicit snapshots and restoration; ordinary dictionaries remain author-controlled.
|
|
297
307
|
Visits and exact artifact occurrences retain history and make recovery safe. The APIs, budgets and
|
|
298
308
|
recovery boundaries are in
|
|
299
|
-
[reference →
|
|
309
|
+
[reference → backward traversal](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#backward-traversal-rewind-retry-all-visits),
|
|
300
310
|
with working programs in
|
|
301
|
-
[tutorial steps 16–17](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-16--
|
|
311
|
+
[tutorial steps 16–17](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-16--regenerating-with-rewind-and-retry-all).
|
|
302
312
|
|
|
303
|
-
The API is one class ([`Handoff`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#
|
|
313
|
+
The API is one class ([`Handoff`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#handoffs-opt-in)),
|
|
304
314
|
one declaration — `control={"edges": {...}}` for forward jumps, `control.rewind` / `control.retry_all` /
|
|
305
315
|
`control.max_handoffs` for backward traversal — and one optional store capability; a custom store that cannot
|
|
306
316
|
commit a handoff atomically is refused up front instead of writing a jump that would not survive a crash.
|
|
307
|
-
Walkthrough: [tutorial step 15](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-15--
|
|
308
|
-
Model and rules: [design §4.8](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/design.md#48-
|
|
317
|
+
Walkthrough: [tutorial step 15](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-15--skipping-stations-handoffs).
|
|
318
|
+
Model and rules: [design §4.8](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/design.md#48-handoffs--declared-forward-jumps-opt-in).
|
|
309
319
|
|
|
310
320
|
When a pipeline restarts from the seed, previous task rows and chain artifacts are reset atomically with the cursor/watermark;
|
|
311
321
|
previous handoffs stay in its history but no longer act as
|
|
@@ -476,8 +486,8 @@ what they do not say.
|
|
|
476
486
|
|
|
477
487
|
| Document | What is in it |
|
|
478
488
|
|---|---|
|
|
479
|
-
| [`docs/tutorial.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md) | seventeen runnable steps, from "one task" to sharded evaluation and
|
|
480
|
-
| [`docs/reference.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md) | every public class and function: signatures, parameters, examples — including
|
|
489
|
+
| [`docs/tutorial.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md) | seventeen runnable steps, from "one task" to sharded evaluation, handoffs and backward traversal — rewinds, retry-all and payload history included; each one executed by the test suite |
|
|
490
|
+
| [`docs/reference.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md) | every public class and function: signatures, parameters, examples — including backward traversal and `HistoryArtifact` |
|
|
481
491
|
| [`docs/cli.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/cli.md) | every subcommand, every flag, exit codes, config reference |
|
|
482
492
|
| [`docs/design.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/design.md) | conceptual model, the six invariants, the lease contract, data model, tradeoffs |
|
|
483
493
|
| [`docs/benchmark.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/benchmark.md) | the algorithm benchmark: what the scenarios assume, what the metrics mean, how to read the table |
|
|
@@ -510,7 +520,7 @@ These are design decisions, not missing features:
|
|
|
510
520
|
* **Semantic reduction** — accuracy, pass@k, F1 and any cross-pipeline aggregation. Export the artifacts and
|
|
511
521
|
compute it outside, or write a sink pipeline out of the primitives.
|
|
512
522
|
* **DAG orchestration** — a pipeline is a linear chain; branch inside a task with `fanout`. The one
|
|
513
|
-
qualification is the opt-in [handoff](#
|
|
523
|
+
qualification is the opt-in [handoff](#handoffs-opt-in): it changes the traversal of
|
|
514
524
|
the chain along declared edges, never its topology (no joins, no second entry point, no cross-pipeline
|
|
515
525
|
jumps).
|
|
516
526
|
* **A serving gateway** — the only HTTP surface is the read-only debug endpoint above.
|
|
@@ -521,16 +531,30 @@ every known tradeoff with its reason.
|
|
|
521
531
|
|
|
522
532
|
## Status
|
|
523
533
|
|
|
534
|
+
**0.3.3 — reliability, bounded admission and online documentation.** Atomic write-behind batches,
|
|
535
|
+
correct and faster Suite queries, dashboard HTML injection fixes, and a configurable
|
|
536
|
+
`max_admitted` limit (default: `4 * concurrency`). The bilingual documentation site is now available.
|
|
537
|
+
See the [release notes](https://github.com/Hazer-BJTU/pyattacker/blob/main/CHANGELOG.md#033--2026-09-21).
|
|
538
|
+
|
|
539
|
+
**0.3.2 — experiment suites and supported control flow.** Independent experiments can share resource
|
|
540
|
+
pools and global concurrency, with unified run/resume/report commands and optional per-experiment output
|
|
541
|
+
directories. Durable run histories preserve earlier outcomes after resume. Handoffs, forward jumps,
|
|
542
|
+
rewinds and retry-all are supported public features, enabled through explicit `control` declarations.
|
|
543
|
+
See the [Suite guide](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/suites.md) and
|
|
544
|
+
[release notes](https://github.com/Hazer-BJTU/pyattacker/blob/main/CHANGELOG.md#032--2026-09-21).
|
|
545
|
+
|
|
524
546
|
**0.3.1 — application-reported live metrics.** Applications can publish values such as accuracy to the
|
|
525
547
|
read-only monitoring dashboard through `Runner.report_metric()` or `TaskContext.report_metric()`. A completion
|
|
526
548
|
callback receives committed pipeline results; the application owns the calculation. The dashboard and
|
|
527
549
|
`watch` now follow one run consistently by default. See [`examples/live_metrics.py`](examples/live_metrics.py).
|
|
528
550
|
|
|
529
|
-
**0.3.0 —
|
|
530
|
-
[hand off](#
|
|
551
|
+
**0.3.0 — control flow (opt-in), and the documentation in Chinese.** Tasks can now
|
|
552
|
+
[hand off](#handoffs-opt-in): return a `Handoff` to skip declared stations or finish the pipeline
|
|
531
553
|
early, recorded in a durable ledger that recovery resumes from. It is opt-in and inert — a pipeline without a
|
|
532
|
-
`control` block writes no new rows and keeps a byte-identical `spec_digest
|
|
533
|
-
|
|
554
|
+
`control` block writes no new rows and keeps a byte-identical `spec_digest`. Handoffs and backward traversal
|
|
555
|
+
were experimental in 0.3.0 and became supported features in 0.3.2
|
|
556
|
+
(see [0.3.2 release notes](https://github.com/Hazer-BJTU/pyattacker/blob/main/CHANGELOG.md#032--2026-09-21)). Backward traversal adds declared rewind and retry-all with visits
|
|
557
|
+
and optional payload history. The
|
|
534
558
|
whole documentation set also ships in Simplified Chinese ([`README.zh-CN.md`](README.zh-CN.md),
|
|
535
559
|
[`docs/zh-CN/`](https://github.com/Hazer-BJTU/pyattacker/tree/main/docs/zh-CN)), kept in sync by CI. One
|
|
536
560
|
rename to know about when upgrading: `MergedReport.events_total` is now `source_events_total`, because that
|
|
@@ -586,3 +610,7 @@ extracted and executed by `tests/test_tutorial.py`, so documentation that rots f
|
|
|
586
610
|
## License
|
|
587
611
|
|
|
588
612
|
MIT — see [LICENSE](https://github.com/Hazer-BJTU/pyattacker/blob/main/LICENSE).
|
|
613
|
+
|
|
614
|
+
## Experiment suites
|
|
615
|
+
|
|
616
|
+
Combine existing configuration files under one Runner with shared resource pools and total concurrency. Optional per-experiment directories separate checkpoints, results and file artifacts. Suite reports include prior successes across resumes. See the [Suite guide](docs/suites.md) and [runnable configuration](examples/suites/suite.json).
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
**English** | [简体中文](README.zh-CN.md)
|
|
11
11
|
|
|
12
|
+
[Documentation](https://hazer-bjtu.github.io/pyattacker/)
|
|
13
|
+
|
|
12
14
|
> Run tens of thousands of independent tasks to completion — resumably, observably, and without
|
|
13
15
|
> reimplementing endpoint pools, retries and "which rows already ran" for the fifth time.
|
|
14
16
|
|
|
@@ -68,7 +70,7 @@ Five concepts, and that is the whole vocabulary:
|
|
|
68
70
|
| Concept | Meaning | In one line |
|
|
69
71
|
|---|---|---|
|
|
70
72
|
| **artifact** | the persisted state of a task | content-addressed, **persisted as soon as it is produced** → checkpoint granularity = task |
|
|
71
|
-
| **task** | the smallest unit of scheduling | a unary `(artifact) -> artifact` function, sync or async — or `-> artifact \| Handoff`, to skip ahead ([
|
|
73
|
+
| **task** | the smallest unit of scheduling | a unary `(artifact) -> artifact` function, sync or async — or `-> artifact \| Handoff`, to skip ahead ([handoffs](#handoffs-opt-in)) |
|
|
72
74
|
| **pipeline** | the unit of completion | `fetch \| ask \| judge \| metrics` chained linearly, semantically independent of each other |
|
|
73
75
|
| **resource** | a leasable external capability | one endpoint / one key; once pooled, it can be published and subscribed to concurrency-safely |
|
|
74
76
|
| **algorithm** | the policy for acquiring resources | `wait`, `backoff`, `least_busy`, `failover`, `sticky`, `quota_aware`, `immediate` — orthogonal to "retry on failure" |
|
|
@@ -94,7 +96,7 @@ async def fetch(row: dict, ctx) -> dict:
|
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def dataset_rows():
|
|
97
|
-
"""Your dataset.
|
|
99
|
+
"""Your dataset. `map` streams generators; max_admitted bounds unfinished pipelines."""
|
|
98
100
|
return [{"q": f"question-{i}"} for i in range(4)]
|
|
99
101
|
|
|
100
102
|
|
|
@@ -110,6 +112,11 @@ with Runner(store="runs/qa.db", pools=[pool], concurrency=8) as runner:
|
|
|
110
112
|
report.export_jsonl("runs/qa.jsonl")
|
|
111
113
|
```
|
|
112
114
|
|
|
115
|
+
Streaming input uses `max_admitted` (default: `4 * concurrency`) to bound queued,
|
|
116
|
+
running and retry-delayed pipelines together. This is a count limit, not a byte
|
|
117
|
+
limit; task payloads, source buffers and Store retention also consume memory.
|
|
118
|
+
The default `MemoryStore` retains results as data grows. See [RunConfig](docs/reference.md#runconfig).
|
|
119
|
+
|
|
113
120
|
`ctx.acquire()` falls back to the task's declared `resource`, and `lease.report(ok=...)` feeds the pool's
|
|
114
121
|
health and quota accounting.
|
|
115
122
|
|
|
@@ -177,7 +184,7 @@ runner.run(template.map(rows), resume=True) # or pyattacker resume -c config.y
|
|
|
177
184
|
* Failed pipelines → continue from **the first task that produced no artifact**: **if task C died, only task C
|
|
178
185
|
reruns when task B's checkpoint is durable**;
|
|
179
186
|
* The seed artifact is persisted too → recovery **does not depend on the original dataset file**;
|
|
180
|
-
* A pipeline that **handed off** ([
|
|
187
|
+
* A pipeline that **handed off** ([handoffs](#handoffs-opt-in)) resumes at the station it jumped to,
|
|
181
188
|
with the entry state the ledger recorded — the task that handed off is not re-run;
|
|
182
189
|
* Changed a task's source code (`spec_digest` includes source digests) → treated as a new pipeline, so old
|
|
183
190
|
results are not incorrectly reused. Factory parameters, fanout children, retry/algorithm policies and
|
|
@@ -188,10 +195,13 @@ rerun and old explicit keys conflict. Finish old runs with the old package, then
|
|
|
188
195
|
[resume identity and idempotency reference](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#resume-identity)
|
|
189
196
|
for migration guidance, dynamic functions and external configuration.
|
|
190
197
|
|
|
191
|
-
|
|
198
|
+
<a id="advanced-handoffs-opt-in"></a>
|
|
199
|
+
|
|
200
|
+
## Handoffs (Opt-In)
|
|
201
|
+
|
|
202
|
+
**Handoffs, forward jumps and rewinds are supported control-flow features, enabled explicitly through `control`.**
|
|
192
203
|
|
|
193
|
-
|
|
194
|
-
1.0.** A task can decide that the rest of the chain no longer needs to run, and *say so* instead of inventing a
|
|
204
|
+
A task can decide that the rest of the chain no longer needs to run, and *say so* instead of inventing a
|
|
195
205
|
failure or hiding the branch inside one step. It **returns** a directive — `Handoff.to(target, value)` to
|
|
196
206
|
continue at a declared later station, `Handoff.end(value)` to finish the pipeline right there:
|
|
197
207
|
|
|
@@ -271,16 +281,16 @@ with Runner(store=":memory:", concurrency=4) as runner:
|
|
|
271
281
|
payloads provide explicit snapshots and restoration; ordinary dictionaries remain author-controlled.
|
|
272
282
|
Visits and exact artifact occurrences retain history and make recovery safe. The APIs, budgets and
|
|
273
283
|
recovery boundaries are in
|
|
274
|
-
[reference →
|
|
284
|
+
[reference → backward traversal](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#backward-traversal-rewind-retry-all-visits),
|
|
275
285
|
with working programs in
|
|
276
|
-
[tutorial steps 16–17](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-16--
|
|
286
|
+
[tutorial steps 16–17](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-16--regenerating-with-rewind-and-retry-all).
|
|
277
287
|
|
|
278
|
-
The API is one class ([`Handoff`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#
|
|
288
|
+
The API is one class ([`Handoff`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md#handoffs-opt-in)),
|
|
279
289
|
one declaration — `control={"edges": {...}}` for forward jumps, `control.rewind` / `control.retry_all` /
|
|
280
290
|
`control.max_handoffs` for backward traversal — and one optional store capability; a custom store that cannot
|
|
281
291
|
commit a handoff atomically is refused up front instead of writing a jump that would not survive a crash.
|
|
282
|
-
Walkthrough: [tutorial step 15](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-15--
|
|
283
|
-
Model and rules: [design §4.8](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/design.md#48-
|
|
292
|
+
Walkthrough: [tutorial step 15](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md#step-15--skipping-stations-handoffs).
|
|
293
|
+
Model and rules: [design §4.8](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/design.md#48-handoffs--declared-forward-jumps-opt-in).
|
|
284
294
|
|
|
285
295
|
When a pipeline restarts from the seed, previous task rows and chain artifacts are reset atomically with the cursor/watermark;
|
|
286
296
|
previous handoffs stay in its history but no longer act as
|
|
@@ -451,8 +461,8 @@ what they do not say.
|
|
|
451
461
|
|
|
452
462
|
| Document | What is in it |
|
|
453
463
|
|---|---|
|
|
454
|
-
| [`docs/tutorial.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md) | seventeen runnable steps, from "one task" to sharded evaluation and
|
|
455
|
-
| [`docs/reference.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md) | every public class and function: signatures, parameters, examples — including
|
|
464
|
+
| [`docs/tutorial.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/tutorial.md) | seventeen runnable steps, from "one task" to sharded evaluation, handoffs and backward traversal — rewinds, retry-all and payload history included; each one executed by the test suite |
|
|
465
|
+
| [`docs/reference.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/reference.md) | every public class and function: signatures, parameters, examples — including backward traversal and `HistoryArtifact` |
|
|
456
466
|
| [`docs/cli.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/cli.md) | every subcommand, every flag, exit codes, config reference |
|
|
457
467
|
| [`docs/design.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/design.md) | conceptual model, the six invariants, the lease contract, data model, tradeoffs |
|
|
458
468
|
| [`docs/benchmark.md`](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/benchmark.md) | the algorithm benchmark: what the scenarios assume, what the metrics mean, how to read the table |
|
|
@@ -485,7 +495,7 @@ These are design decisions, not missing features:
|
|
|
485
495
|
* **Semantic reduction** — accuracy, pass@k, F1 and any cross-pipeline aggregation. Export the artifacts and
|
|
486
496
|
compute it outside, or write a sink pipeline out of the primitives.
|
|
487
497
|
* **DAG orchestration** — a pipeline is a linear chain; branch inside a task with `fanout`. The one
|
|
488
|
-
qualification is the opt-in [handoff](#
|
|
498
|
+
qualification is the opt-in [handoff](#handoffs-opt-in): it changes the traversal of
|
|
489
499
|
the chain along declared edges, never its topology (no joins, no second entry point, no cross-pipeline
|
|
490
500
|
jumps).
|
|
491
501
|
* **A serving gateway** — the only HTTP surface is the read-only debug endpoint above.
|
|
@@ -496,16 +506,30 @@ every known tradeoff with its reason.
|
|
|
496
506
|
|
|
497
507
|
## Status
|
|
498
508
|
|
|
509
|
+
**0.3.3 — reliability, bounded admission and online documentation.** Atomic write-behind batches,
|
|
510
|
+
correct and faster Suite queries, dashboard HTML injection fixes, and a configurable
|
|
511
|
+
`max_admitted` limit (default: `4 * concurrency`). The bilingual documentation site is now available.
|
|
512
|
+
See the [release notes](https://github.com/Hazer-BJTU/pyattacker/blob/main/CHANGELOG.md#033--2026-09-21).
|
|
513
|
+
|
|
514
|
+
**0.3.2 — experiment suites and supported control flow.** Independent experiments can share resource
|
|
515
|
+
pools and global concurrency, with unified run/resume/report commands and optional per-experiment output
|
|
516
|
+
directories. Durable run histories preserve earlier outcomes after resume. Handoffs, forward jumps,
|
|
517
|
+
rewinds and retry-all are supported public features, enabled through explicit `control` declarations.
|
|
518
|
+
See the [Suite guide](https://github.com/Hazer-BJTU/pyattacker/blob/main/docs/suites.md) and
|
|
519
|
+
[release notes](https://github.com/Hazer-BJTU/pyattacker/blob/main/CHANGELOG.md#032--2026-09-21).
|
|
520
|
+
|
|
499
521
|
**0.3.1 — application-reported live metrics.** Applications can publish values such as accuracy to the
|
|
500
522
|
read-only monitoring dashboard through `Runner.report_metric()` or `TaskContext.report_metric()`. A completion
|
|
501
523
|
callback receives committed pipeline results; the application owns the calculation. The dashboard and
|
|
502
524
|
`watch` now follow one run consistently by default. See [`examples/live_metrics.py`](examples/live_metrics.py).
|
|
503
525
|
|
|
504
|
-
**0.3.0 —
|
|
505
|
-
[hand off](#
|
|
526
|
+
**0.3.0 — control flow (opt-in), and the documentation in Chinese.** Tasks can now
|
|
527
|
+
[hand off](#handoffs-opt-in): return a `Handoff` to skip declared stations or finish the pipeline
|
|
506
528
|
early, recorded in a durable ledger that recovery resumes from. It is opt-in and inert — a pipeline without a
|
|
507
|
-
`control` block writes no new rows and keeps a byte-identical `spec_digest
|
|
508
|
-
|
|
529
|
+
`control` block writes no new rows and keeps a byte-identical `spec_digest`. Handoffs and backward traversal
|
|
530
|
+
were experimental in 0.3.0 and became supported features in 0.3.2
|
|
531
|
+
(see [0.3.2 release notes](https://github.com/Hazer-BJTU/pyattacker/blob/main/CHANGELOG.md#032--2026-09-21)). Backward traversal adds declared rewind and retry-all with visits
|
|
532
|
+
and optional payload history. The
|
|
509
533
|
whole documentation set also ships in Simplified Chinese ([`README.zh-CN.md`](README.zh-CN.md),
|
|
510
534
|
[`docs/zh-CN/`](https://github.com/Hazer-BJTU/pyattacker/tree/main/docs/zh-CN)), kept in sync by CI. One
|
|
511
535
|
rename to know about when upgrading: `MergedReport.events_total` is now `source_events_total`, because that
|
|
@@ -561,3 +585,7 @@ extracted and executed by `tests/test_tutorial.py`, so documentation that rots f
|
|
|
561
585
|
## License
|
|
562
586
|
|
|
563
587
|
MIT — see [LICENSE](https://github.com/Hazer-BJTU/pyattacker/blob/main/LICENSE).
|
|
588
|
+
|
|
589
|
+
## Experiment suites
|
|
590
|
+
|
|
591
|
+
Combine existing configuration files under one Runner with shared resource pools and total concurrency. Optional per-experiment directories separate checkpoints, results and file artifacts. Suite reports include prior successes across resumes. See the [Suite guide](docs/suites.md) and [runnable configuration](examples/suites/suite.json).
|