workflowval 0.1.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.
Files changed (59) hide show
  1. workflowval-0.1.0/.gitignore +15 -0
  2. workflowval-0.1.0/LICENSE +21 -0
  3. workflowval-0.1.0/PKG-INFO +215 -0
  4. workflowval-0.1.0/README.md +193 -0
  5. workflowval-0.1.0/pyproject.toml +88 -0
  6. workflowval-0.1.0/src/workflowval/__init__.py +3 -0
  7. workflowval-0.1.0/src/workflowval/cli.py +584 -0
  8. workflowval-0.1.0/src/workflowval/gen/__init__.py +194 -0
  9. workflowval-0.1.0/src/workflowval/gen/source.py +109 -0
  10. workflowval-0.1.0/src/workflowval/gen/style.md +17 -0
  11. workflowval-0.1.0/src/workflowval/gen/templates/.gitkeep +0 -0
  12. workflowval-0.1.0/src/workflowval/gen/templates/conftest.py.tmpl +332 -0
  13. workflowval-0.1.0/src/workflowval/loop/__init__.py +55 -0
  14. workflowval-0.1.0/src/workflowval/loop/runner.py +488 -0
  15. workflowval-0.1.0/src/workflowval/loop/stats.py +178 -0
  16. workflowval-0.1.0/src/workflowval/loop/trace.py +29 -0
  17. workflowval-0.1.0/src/workflowval/models/__init__.py +19 -0
  18. workflowval-0.1.0/src/workflowval/models/base.py +68 -0
  19. workflowval-0.1.0/src/workflowval/models/fake.py +55 -0
  20. workflowval-0.1.0/src/workflowval/models/litellm_adapter.py +153 -0
  21. workflowval-0.1.0/src/workflowval/pipeline.py +906 -0
  22. workflowval-0.1.0/src/workflowval/planner/__init__.py +62 -0
  23. workflowval-0.1.0/src/workflowval/prompts/__init__.py +34 -0
  24. workflowval-0.1.0/src/workflowval/prompts/planner.md +37 -0
  25. workflowval-0.1.0/src/workflowval/prompts/reviewer.md +30 -0
  26. workflowval-0.1.0/src/workflowval/prompts/task_answer_review.md +14 -0
  27. workflowval-0.1.0/src/workflowval/prompts/task_finding.md +17 -0
  28. workflowval-0.1.0/src/workflowval/prompts/task_fix_test.md +6 -0
  29. workflowval-0.1.0/src/workflowval/prompts/task_fix_workflow.md +7 -0
  30. workflowval-0.1.0/src/workflowval/prompts/task_patch.md +9 -0
  31. workflowval-0.1.0/src/workflowval/prompts/task_plan.md +13 -0
  32. workflowval-0.1.0/src/workflowval/prompts/task_plan_gaps.md +6 -0
  33. workflowval-0.1.0/src/workflowval/prompts/task_report.md +9 -0
  34. workflowval-0.1.0/src/workflowval/prompts/task_review_plan.md +17 -0
  35. workflowval-0.1.0/src/workflowval/prompts/task_review_results.md +19 -0
  36. workflowval-0.1.0/src/workflowval/prompts/task_triage.md +9 -0
  37. workflowval-0.1.0/src/workflowval/prompts/writer.md +11 -0
  38. workflowval-0.1.0/src/workflowval/py.typed +0 -0
  39. workflowval-0.1.0/src/workflowval/redact.py +97 -0
  40. workflowval-0.1.0/src/workflowval/report/__init__.py +162 -0
  41. workflowval-0.1.0/src/workflowval/report/curl.py +75 -0
  42. workflowval-0.1.0/src/workflowval/schemas/__init__.py +78 -0
  43. workflowval-0.1.0/src/workflowval/schemas/_base.py +43 -0
  44. workflowval-0.1.0/src/workflowval/schemas/auth.py +66 -0
  45. workflowval-0.1.0/src/workflowval/schemas/labels.py +31 -0
  46. workflowval-0.1.0/src/workflowval/schemas/results.py +222 -0
  47. workflowval-0.1.0/src/workflowval/schemas/review.py +94 -0
  48. workflowval-0.1.0/src/workflowval/schemas/state.py +32 -0
  49. workflowval-0.1.0/src/workflowval/schemas/stats.py +36 -0
  50. workflowval-0.1.0/src/workflowval/schemas/workflow.py +148 -0
  51. workflowval-0.1.0/src/workflowval/spec/__init__.py +38 -0
  52. workflowval-0.1.0/src/workflowval/spec/graph.py +196 -0
  53. workflowval-0.1.0/src/workflowval/spec/loader.py +102 -0
  54. workflowval-0.1.0/src/workflowval/spec/validation.py +98 -0
  55. workflowval-0.1.0/src/workflowval/testrun.py +159 -0
  56. workflowval-0.1.0/src/workflowval/tools/__init__.py +6 -0
  57. workflowval-0.1.0/src/workflowval/tools/base.py +49 -0
  58. workflowval-0.1.0/src/workflowval/tools/registry.py +637 -0
  59. workflowval-0.1.0/src/workflowval/triage/__init__.py +199 -0
@@ -0,0 +1,15 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .mypy_cache/
5
+ .ruff_cache/
6
+ .pytest_cache/
7
+ .coverage
8
+ coverage.xml
9
+ dist/
10
+ build/
11
+ .workflowval/
12
+ .env
13
+ .DS_Store
14
+ .benchmark/
15
+ reports/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 workflowval contributors
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.
@@ -0,0 +1,215 @@
1
+ Metadata-Version: 2.5
2
+ Name: workflowval
3
+ Version: 0.1.0
4
+ Summary: Model-agnostic CLI agent that infers multi-step API workflows from an OpenAPI spec, tests them with pytest and triages every failure.
5
+ Project-URL: Homepage, https://github.com/rkvf01/workflowval
6
+ Project-URL: Documentation, https://github.com/rkvf01/workflowval/tree/main/docs
7
+ Project-URL: Issues, https://github.com/rkvf01/workflowval/issues
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Requires-Python: >=3.11
11
+ Requires-Dist: httpx>=0.27
12
+ Requires-Dist: jsonpatch>=1.41
13
+ Requires-Dist: jsonpointer>=3.8
14
+ Requires-Dist: litellm[mcp]>=1.83
15
+ Requires-Dist: openapi-core>=0.19
16
+ Requires-Dist: openapi-spec-validator>=0.7
17
+ Requires-Dist: pydantic>=2.7
18
+ Requires-Dist: pytest>=8
19
+ Requires-Dist: ruff>=0.6
20
+ Requires-Dist: typer>=0.12
21
+ Description-Content-Type: text/markdown
22
+
23
+ # workflowval
24
+
25
+ Model-agnostic CLI agent that reads an OpenAPI spec, infers multi-step workflows, writes and runs
26
+ pytest tests against a live API, and triages every failure as a test bug, an API bug or spec drift.
27
+
28
+ **Status:** all six commands work: `test`, `plan`, `run`, `drift`, `inspect` and `report`.
29
+
30
+ ## Start here
31
+
32
+ All the project's documents are in the [docs folder](https://github.com/rkvf01/workflowval/tree/main/docs).
33
+
34
+ - **[Requirements](https://github.com/rkvf01/workflowval/blob/main/docs/requirements.md):** what workflowval was built to do.
35
+ - **[Conclusions](https://github.com/rkvf01/workflowval/blob/main/docs/conclusions.md):** how it measured up, what helped and what didn't.
36
+ - **[Model comparison](https://github.com/rkvf01/workflowval/blob/main/docs/model-comparison.md):** two models on the same benchmark, side by side.
37
+ - **[Drift study](https://github.com/rkvf01/workflowval/blob/main/docs/drift-study.md):** the same tests against three implementations of one API.
38
+ - **Build notes:** decisions in [M0](https://github.com/rkvf01/workflowval/blob/main/docs/m0-review.md), then changes and known limits in [M1](https://github.com/rkvf01/workflowval/blob/main/docs/m1-notes.md), [M2](https://github.com/rkvf01/workflowval/blob/main/docs/m2-notes.md) and [M3](https://github.com/rkvf01/workflowval/blob/main/docs/m3-notes.md).
39
+
40
+ ## Quickstart
41
+
42
+ It needs Python 3.11 or later.
43
+
44
+ ```
45
+ pip install workflowval
46
+ ```
47
+
48
+ Then, from the folder where you want the tests written:
49
+
50
+ ```
51
+ export OPENROUTER_API_KEY=...
52
+ export WORKFLOWVAL_MODEL=openrouter/<vendor>/<model>
53
+ workflowval inspect openapi.json
54
+ workflowval test openapi.json --base-url http://localhost:8000 --max-cost-usd 2
55
+ workflowval run tests/wf
56
+ ```
57
+
58
+ This writes `workflows/<id>.json`, `tests/wf/test_<id>.py` and `results.json` in the current folder,
59
+ plus `report.md`, `junit.xml`, bug reports and spec patches in `reports/`.
60
+ If the API needs a login, pass `--auth auth.json` to both `test` and `run`, and set the env vars that
61
+ file names. In a login config, a field like `user.email` nests the login body. See `benchmarks/targets.md` for
62
+ the demo targets.
63
+
64
+ ## How it works
65
+
66
+ `workflowval test` runs these steps, and saves its place after each one:
67
+
68
+ 1. **Plan.** The Planner reads the spec and any docs, and writes one workflow file per multi-step scenario,
69
+ each with the result it expects.
70
+ 2. **Review the plan.** The Reviewer looks for what the plan missed, as a user of the API would, and the
71
+ Planner adds workflows.
72
+ 3. **Write tests.** Code, not a model, renders each workflow into a pytest file, so this step is free and
73
+ repeatable.
74
+ 4. **Run and label.** The tests run against the live API. Only a result that doesn't match what the workflow
75
+ expected goes to the Reviewer, which labels it `TEST_BUG`, `API_BUG`, `SPEC_DRIFT`, `CONFLICT` or
76
+ `UNRESOLVED`.
77
+ 5. **Fix test bugs only.** The Planner corrects a wrong workflow and the Writer corrects test code. A real
78
+ failure is never changed to make it pass. It stays as a labeled expected failure.
79
+ 6. **Review the results.** The Reviewer sees the confirmed failures and asks for new workflows, including
80
+ ones that get past a known bug.
81
+ 7. **Report.** Bug reports, spec patches, `report.md` and `junit.xml` go in `reports/`.
82
+
83
+ | Part | Where |
84
+ | --- | --- |
85
+ | Commands | `src/workflowval/cli.py`, `pipeline.py` |
86
+ | Agent loop, budgets and stop rules | `src/workflowval/loop/` |
87
+ | Model adapter, the only code that imports a provider library | `src/workflowval/models/` |
88
+ | Tools the roles may call, limited per role | `src/workflowval/tools/` |
89
+ | Data formats | `src/workflowval/schemas/` |
90
+ | Spec reading and response checks | `src/workflowval/spec/` |
91
+ | Test rendering | `src/workflowval/gen/` |
92
+ | Role prompts | `src/workflowval/prompts/` |
93
+
94
+ ## What needs a model
95
+
96
+ | Needs no model or key | Needs a model and its key |
97
+ | --- | --- |
98
+ | `workflowval inspect` | `workflowval plan` |
99
+ | `workflowval run tests/wf` | `workflowval test` |
100
+ | Plain `pytest tests/wf` | `workflowval drift` |
101
+ | `workflowval report` | Labeling and fixing failures |
102
+ | This repo's own lint and tests | `uv run pytest -m live`, and the benchmark |
103
+
104
+ So you can generate tests once, commit them, and re-run them with no model cost. A failure already
105
+ reviewed as real counts as expected, so it doesn't fail the run. Any other failure makes `run` exit 1,
106
+ but nothing labels it; run `workflowval test` to get the diagnosis. `run` and `pytest` still need a
107
+ reachable API and, for a login API, its credentials.
108
+
109
+ ## Reading the results
110
+
111
+ | Result | Meaning | Fails the run? |
112
+ | --- | --- | --- |
113
+ | `PASSED` | The test passed | No |
114
+ | `EXPECTED` | It hit the failure already reviewed as real, and only that failure | No |
115
+ | `LOOKS FIXED` | A test marked as a known issue passed | No |
116
+ | `MISMATCH` | A response breaks the spec, though the test may pass | No, except in `drift` |
117
+ | `UNRESOLVED` | Fix attempts ran out, or the failure has no verdict | Yes |
118
+
119
+ ## Benchmark
120
+
121
+ Scored against a seeded-bug app with 10 planted defects and one spec-docs conflict, for 3 rounds on one low-cost
122
+ model. See `docs/m3-notes.md` for what the numbers mean, and `benchmarks/seeded/README.md` for who wrote the app,
123
+ which affects how independent this score is.
124
+
125
+ | Metric | Median (range) | Target |
126
+ | --- | --- | --- |
127
+ | Seeded bugs detected, of 10 | 10 (9 to 10) | 8 or more |
128
+ | Label accuracy on detected bugs, % | 70 (67 to 90) | 80 or more |
129
+ | Bugs a workflow could catch, of 10 | 10 (10 to 10) | 10 |
130
+ | False API_BUG on the clean build | 1 (1 to 1) | 1 or fewer |
131
+ | Tests green on the clean build, % | 98 (96 to 98) | 90 or more |
132
+ | CONFLICT reports | 0 (0 to 0) | 1 |
133
+ | Cost per round, USD | 0.31 (0.30 to 0.35) | reported only |
134
+
135
+ Label accuracy and the conflict report still miss their targets on this model. A second model met them on
136
+ the medians, at about 28 times the cost and with less steady detection: see `docs/model-comparison.md`.
137
+
138
+ ## Known limits
139
+
140
+ - **Results vary by model and run,** since models choose the workflows and labels. Three rounds is a small sample.
141
+ - **Labels are the weak spot.** About a quarter of detected bugs got the wrong label on both models tested. Check
142
+ a label before acting on it.
143
+ - **A test stops at its first failure.** A bug in an early step can hide later bugs from every test that goes
144
+ through that step. This is deliberate: the failure stays a result, and getting further is the job of another test.
145
+ - **Finding a conflict isn't reliable** on the low-cost model, which wrote no CONFLICT reports in the benchmark.
146
+ - **The fix loop can be inconsistent.** On the same kind of edge case, it may rewrite what some tests expect and not
147
+ others. Rewrites that change what a test checks are printed as `CHANGED`, so review them.
148
+ - **Edge cases get missed,** and a `204` can raise a false alarm. See `docs/drift-study.md`.
149
+ - **Generated tests aren't sandboxed.** They run on your machine with your permissions, so use only specs and docs
150
+ you trust.
151
+ - **The cost cap can go slightly over,** since it judges by the dearest call so far.
152
+ - **A run resumes only after being killed.** One that stopped on a model error, a step limit or a cost limit doesn't
153
+ pick up where it stopped. Running it again starts a new run, which reuses a finished plan for the same spec.
154
+ - **The benchmark isn't fully independent,** because of who wrote the seeded app. See `benchmarks/seeded/README.md`.
155
+
156
+ ## Safety and CI
157
+
158
+ ```
159
+ workflowval test openapi.json --base-url https://staging.example.com --allow-remote --read-only
160
+ workflowval run tests/wf --ci --ci-max-api-bugs 2
161
+ ```
162
+
163
+ `--read-only` sends no writes. A non-local host needs `--allow-remote`, then `--yes`, `--ci` or `--read-only`.
164
+ Generated tests refuse any other host. `--ci` fails on anything unresolved, or on bugs above the limits. A killed
165
+ `workflowval test` picks up at its last step when run again. See `docs/m3-notes.md`.
166
+
167
+ `--max-cost-usd 2` stops a run before a model call could pass that cost, judging by the dearest call so far, so a
168
+ much larger call can still go slightly over. A call with no known price also stops the run, since the limit
169
+ can't be kept. Temporary provider errors, such as rate limits, are retried up to 5 times.
170
+
171
+ Secrets are hidden in everything saved or sent to a model: known values, auth headers and fields with names like
172
+ `token`. A hidden value isn't checked against the spec. `--no-redact` keeps recorded response bodies exact, and works
173
+ only with a local API, since those bodies may hold real secrets. Everything else stays hidden.
174
+
175
+ ## Develop
176
+
177
+ This needs [uv](https://docs.astral.sh/uv/).
178
+
179
+ ```
180
+ git clone https://github.com/rkvf01/workflowval.git && cd workflowval
181
+ uv sync
182
+ uv run ruff check . && uv run ruff format --check .
183
+ uv run mypy --strict src
184
+ uv run pytest --cov=workflowval.loop --cov=workflowval.spec --cov=workflowval.triage --cov-fail-under=80
185
+ uv run workflowval --help
186
+ ```
187
+
188
+ Unit tests use `FakeModel` and block network sockets, so they need no API key.
189
+
190
+ ## Models and API keys
191
+
192
+ Live runs go through LiteLLM. Pick a model with `--model provider/name` or `WORKFLOWVAL_MODEL`, and
193
+ override per role with `--role-model writer=provider/name`. Provider keys come from environment
194
+ variables only, for example `OPENROUTER_API_KEY`. Local Ollama models (`ollama/<name>`) need no key.
195
+
196
+ To check the adapter against a real provider:
197
+
198
+ ```
199
+ export OPENROUTER_API_KEY=...
200
+ WORKFLOWVAL_LIVE_MODEL=openrouter/<vendor>/<model> uv run pytest -m live
201
+ ```
202
+
203
+ ## Stats
204
+
205
+ Every run writes model calls, tool calls and cost, per workflow and per role, to
206
+ `.workflowval/stats.json`, and adds a line to `.workflowval/stats_history.jsonl`. After 5 usable runs,
207
+ the run summary suggests a `--max-steps` value based on that history. It never changes the limit by
208
+ itself. See `docs/m0-review.md`.
209
+
210
+ Cost is the figure the provider returns with each reply, so tracking it adds no extra calls. To turn
211
+ it off, use `--no-cost-tracking` or set `WORKFLOWVAL_COST_TRACKING=off`.
212
+
213
+ ## License
214
+
215
+ MIT
@@ -0,0 +1,193 @@
1
+ # workflowval
2
+
3
+ Model-agnostic CLI agent that reads an OpenAPI spec, infers multi-step workflows, writes and runs
4
+ pytest tests against a live API, and triages every failure as a test bug, an API bug or spec drift.
5
+
6
+ **Status:** all six commands work: `test`, `plan`, `run`, `drift`, `inspect` and `report`.
7
+
8
+ ## Start here
9
+
10
+ All the project's documents are in the [docs folder](https://github.com/rkvf01/workflowval/tree/main/docs).
11
+
12
+ - **[Requirements](https://github.com/rkvf01/workflowval/blob/main/docs/requirements.md):** what workflowval was built to do.
13
+ - **[Conclusions](https://github.com/rkvf01/workflowval/blob/main/docs/conclusions.md):** how it measured up, what helped and what didn't.
14
+ - **[Model comparison](https://github.com/rkvf01/workflowval/blob/main/docs/model-comparison.md):** two models on the same benchmark, side by side.
15
+ - **[Drift study](https://github.com/rkvf01/workflowval/blob/main/docs/drift-study.md):** the same tests against three implementations of one API.
16
+ - **Build notes:** decisions in [M0](https://github.com/rkvf01/workflowval/blob/main/docs/m0-review.md), then changes and known limits in [M1](https://github.com/rkvf01/workflowval/blob/main/docs/m1-notes.md), [M2](https://github.com/rkvf01/workflowval/blob/main/docs/m2-notes.md) and [M3](https://github.com/rkvf01/workflowval/blob/main/docs/m3-notes.md).
17
+
18
+ ## Quickstart
19
+
20
+ It needs Python 3.11 or later.
21
+
22
+ ```
23
+ pip install workflowval
24
+ ```
25
+
26
+ Then, from the folder where you want the tests written:
27
+
28
+ ```
29
+ export OPENROUTER_API_KEY=...
30
+ export WORKFLOWVAL_MODEL=openrouter/<vendor>/<model>
31
+ workflowval inspect openapi.json
32
+ workflowval test openapi.json --base-url http://localhost:8000 --max-cost-usd 2
33
+ workflowval run tests/wf
34
+ ```
35
+
36
+ This writes `workflows/<id>.json`, `tests/wf/test_<id>.py` and `results.json` in the current folder,
37
+ plus `report.md`, `junit.xml`, bug reports and spec patches in `reports/`.
38
+ If the API needs a login, pass `--auth auth.json` to both `test` and `run`, and set the env vars that
39
+ file names. In a login config, a field like `user.email` nests the login body. See `benchmarks/targets.md` for
40
+ the demo targets.
41
+
42
+ ## How it works
43
+
44
+ `workflowval test` runs these steps, and saves its place after each one:
45
+
46
+ 1. **Plan.** The Planner reads the spec and any docs, and writes one workflow file per multi-step scenario,
47
+ each with the result it expects.
48
+ 2. **Review the plan.** The Reviewer looks for what the plan missed, as a user of the API would, and the
49
+ Planner adds workflows.
50
+ 3. **Write tests.** Code, not a model, renders each workflow into a pytest file, so this step is free and
51
+ repeatable.
52
+ 4. **Run and label.** The tests run against the live API. Only a result that doesn't match what the workflow
53
+ expected goes to the Reviewer, which labels it `TEST_BUG`, `API_BUG`, `SPEC_DRIFT`, `CONFLICT` or
54
+ `UNRESOLVED`.
55
+ 5. **Fix test bugs only.** The Planner corrects a wrong workflow and the Writer corrects test code. A real
56
+ failure is never changed to make it pass. It stays as a labeled expected failure.
57
+ 6. **Review the results.** The Reviewer sees the confirmed failures and asks for new workflows, including
58
+ ones that get past a known bug.
59
+ 7. **Report.** Bug reports, spec patches, `report.md` and `junit.xml` go in `reports/`.
60
+
61
+ | Part | Where |
62
+ | --- | --- |
63
+ | Commands | `src/workflowval/cli.py`, `pipeline.py` |
64
+ | Agent loop, budgets and stop rules | `src/workflowval/loop/` |
65
+ | Model adapter, the only code that imports a provider library | `src/workflowval/models/` |
66
+ | Tools the roles may call, limited per role | `src/workflowval/tools/` |
67
+ | Data formats | `src/workflowval/schemas/` |
68
+ | Spec reading and response checks | `src/workflowval/spec/` |
69
+ | Test rendering | `src/workflowval/gen/` |
70
+ | Role prompts | `src/workflowval/prompts/` |
71
+
72
+ ## What needs a model
73
+
74
+ | Needs no model or key | Needs a model and its key |
75
+ | --- | --- |
76
+ | `workflowval inspect` | `workflowval plan` |
77
+ | `workflowval run tests/wf` | `workflowval test` |
78
+ | Plain `pytest tests/wf` | `workflowval drift` |
79
+ | `workflowval report` | Labeling and fixing failures |
80
+ | This repo's own lint and tests | `uv run pytest -m live`, and the benchmark |
81
+
82
+ So you can generate tests once, commit them, and re-run them with no model cost. A failure already
83
+ reviewed as real counts as expected, so it doesn't fail the run. Any other failure makes `run` exit 1,
84
+ but nothing labels it; run `workflowval test` to get the diagnosis. `run` and `pytest` still need a
85
+ reachable API and, for a login API, its credentials.
86
+
87
+ ## Reading the results
88
+
89
+ | Result | Meaning | Fails the run? |
90
+ | --- | --- | --- |
91
+ | `PASSED` | The test passed | No |
92
+ | `EXPECTED` | It hit the failure already reviewed as real, and only that failure | No |
93
+ | `LOOKS FIXED` | A test marked as a known issue passed | No |
94
+ | `MISMATCH` | A response breaks the spec, though the test may pass | No, except in `drift` |
95
+ | `UNRESOLVED` | Fix attempts ran out, or the failure has no verdict | Yes |
96
+
97
+ ## Benchmark
98
+
99
+ Scored against a seeded-bug app with 10 planted defects and one spec-docs conflict, for 3 rounds on one low-cost
100
+ model. See `docs/m3-notes.md` for what the numbers mean, and `benchmarks/seeded/README.md` for who wrote the app,
101
+ which affects how independent this score is.
102
+
103
+ | Metric | Median (range) | Target |
104
+ | --- | --- | --- |
105
+ | Seeded bugs detected, of 10 | 10 (9 to 10) | 8 or more |
106
+ | Label accuracy on detected bugs, % | 70 (67 to 90) | 80 or more |
107
+ | Bugs a workflow could catch, of 10 | 10 (10 to 10) | 10 |
108
+ | False API_BUG on the clean build | 1 (1 to 1) | 1 or fewer |
109
+ | Tests green on the clean build, % | 98 (96 to 98) | 90 or more |
110
+ | CONFLICT reports | 0 (0 to 0) | 1 |
111
+ | Cost per round, USD | 0.31 (0.30 to 0.35) | reported only |
112
+
113
+ Label accuracy and the conflict report still miss their targets on this model. A second model met them on
114
+ the medians, at about 28 times the cost and with less steady detection: see `docs/model-comparison.md`.
115
+
116
+ ## Known limits
117
+
118
+ - **Results vary by model and run,** since models choose the workflows and labels. Three rounds is a small sample.
119
+ - **Labels are the weak spot.** About a quarter of detected bugs got the wrong label on both models tested. Check
120
+ a label before acting on it.
121
+ - **A test stops at its first failure.** A bug in an early step can hide later bugs from every test that goes
122
+ through that step. This is deliberate: the failure stays a result, and getting further is the job of another test.
123
+ - **Finding a conflict isn't reliable** on the low-cost model, which wrote no CONFLICT reports in the benchmark.
124
+ - **The fix loop can be inconsistent.** On the same kind of edge case, it may rewrite what some tests expect and not
125
+ others. Rewrites that change what a test checks are printed as `CHANGED`, so review them.
126
+ - **Edge cases get missed,** and a `204` can raise a false alarm. See `docs/drift-study.md`.
127
+ - **Generated tests aren't sandboxed.** They run on your machine with your permissions, so use only specs and docs
128
+ you trust.
129
+ - **The cost cap can go slightly over,** since it judges by the dearest call so far.
130
+ - **A run resumes only after being killed.** One that stopped on a model error, a step limit or a cost limit doesn't
131
+ pick up where it stopped. Running it again starts a new run, which reuses a finished plan for the same spec.
132
+ - **The benchmark isn't fully independent,** because of who wrote the seeded app. See `benchmarks/seeded/README.md`.
133
+
134
+ ## Safety and CI
135
+
136
+ ```
137
+ workflowval test openapi.json --base-url https://staging.example.com --allow-remote --read-only
138
+ workflowval run tests/wf --ci --ci-max-api-bugs 2
139
+ ```
140
+
141
+ `--read-only` sends no writes. A non-local host needs `--allow-remote`, then `--yes`, `--ci` or `--read-only`.
142
+ Generated tests refuse any other host. `--ci` fails on anything unresolved, or on bugs above the limits. A killed
143
+ `workflowval test` picks up at its last step when run again. See `docs/m3-notes.md`.
144
+
145
+ `--max-cost-usd 2` stops a run before a model call could pass that cost, judging by the dearest call so far, so a
146
+ much larger call can still go slightly over. A call with no known price also stops the run, since the limit
147
+ can't be kept. Temporary provider errors, such as rate limits, are retried up to 5 times.
148
+
149
+ Secrets are hidden in everything saved or sent to a model: known values, auth headers and fields with names like
150
+ `token`. A hidden value isn't checked against the spec. `--no-redact` keeps recorded response bodies exact, and works
151
+ only with a local API, since those bodies may hold real secrets. Everything else stays hidden.
152
+
153
+ ## Develop
154
+
155
+ This needs [uv](https://docs.astral.sh/uv/).
156
+
157
+ ```
158
+ git clone https://github.com/rkvf01/workflowval.git && cd workflowval
159
+ uv sync
160
+ uv run ruff check . && uv run ruff format --check .
161
+ uv run mypy --strict src
162
+ uv run pytest --cov=workflowval.loop --cov=workflowval.spec --cov=workflowval.triage --cov-fail-under=80
163
+ uv run workflowval --help
164
+ ```
165
+
166
+ Unit tests use `FakeModel` and block network sockets, so they need no API key.
167
+
168
+ ## Models and API keys
169
+
170
+ Live runs go through LiteLLM. Pick a model with `--model provider/name` or `WORKFLOWVAL_MODEL`, and
171
+ override per role with `--role-model writer=provider/name`. Provider keys come from environment
172
+ variables only, for example `OPENROUTER_API_KEY`. Local Ollama models (`ollama/<name>`) need no key.
173
+
174
+ To check the adapter against a real provider:
175
+
176
+ ```
177
+ export OPENROUTER_API_KEY=...
178
+ WORKFLOWVAL_LIVE_MODEL=openrouter/<vendor>/<model> uv run pytest -m live
179
+ ```
180
+
181
+ ## Stats
182
+
183
+ Every run writes model calls, tool calls and cost, per workflow and per role, to
184
+ `.workflowval/stats.json`, and adds a line to `.workflowval/stats_history.jsonl`. After 5 usable runs,
185
+ the run summary suggests a `--max-steps` value based on that history. It never changes the limit by
186
+ itself. See `docs/m0-review.md`.
187
+
188
+ Cost is the figure the provider returns with each reply, so tracking it adds no extra calls. To turn
189
+ it off, use `--no-cost-tracking` or set `WORKFLOWVAL_COST_TRACKING=off`.
190
+
191
+ ## License
192
+
193
+ MIT
@@ -0,0 +1,88 @@
1
+ [project]
2
+ name = "workflowval"
3
+ version = "0.1.0"
4
+ description = "Model-agnostic CLI agent that infers multi-step API workflows from an OpenAPI spec, tests them with pytest and triages every failure."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ dependencies = [
10
+ "httpx>=0.27",
11
+ "jsonpatch>=1.41",
12
+ "jsonpointer>=3.8",
13
+ "litellm[mcp]>=1.83", # Tool calls import mcp, which is only in this extra.
14
+ "openapi-core>=0.19",
15
+ "openapi-spec-validator>=0.7",
16
+ "pydantic>=2.7",
17
+ "pytest>=8",
18
+ "ruff>=0.6",
19
+ "typer>=0.12",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/rkvf01/workflowval"
24
+ Documentation = "https://github.com/rkvf01/workflowval/tree/main/docs"
25
+ Issues = "https://github.com/rkvf01/workflowval/issues"
26
+
27
+ [project.scripts]
28
+ workflowval = "workflowval.cli:app"
29
+
30
+ [dependency-groups]
31
+ dev = [
32
+ "fastapi>=0.115",
33
+ "mypy>=1.10",
34
+ "pytest-cov>=5",
35
+ "pytest-socket>=0.7",
36
+ "uvicorn>=0.30",
37
+ ]
38
+
39
+ [build-system]
40
+ requires = ["hatchling"]
41
+ build-backend = "hatchling.build"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/workflowval"]
45
+
46
+ # A list of what ships, so nothing else in the folder can end up in the package.
47
+ [tool.hatch.build.targets.sdist]
48
+ only-include = ["src", "README.md", "LICENSE", "pyproject.toml"]
49
+
50
+ [tool.ruff]
51
+ line-length = 100
52
+ target-version = "py311"
53
+ src = ["src", "tests"]
54
+
55
+ [tool.ruff.lint]
56
+ select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "S"]
57
+
58
+ [tool.ruff.lint.per-file-ignores]
59
+ "tests/**" = ["S101", "S105", "S106"]
60
+ "src/workflowval/gen/templates/**" = ["S101"]
61
+
62
+ [tool.mypy]
63
+ strict = true
64
+ python_version = "3.11"
65
+ mypy_path = "src"
66
+ plugins = ["pydantic.mypy"]
67
+
68
+ [[tool.mypy.overrides]]
69
+ module = ["litellm", "litellm.*"]
70
+ ignore_missing_imports = true
71
+ follow_imports = "skip"
72
+
73
+ [[tool.mypy.overrides]]
74
+ module = ["jsonpatch", "jsonpointer"]
75
+ ignore_missing_imports = true
76
+
77
+ [tool.pytest.ini_options]
78
+ testpaths = ["tests"]
79
+ addopts = "-ra --disable-socket --allow-unix-socket -m 'not live'"
80
+ markers = ["live: calls a real model provider; needs an API key and is never run in CI"]
81
+
82
+ [tool.coverage.run]
83
+ branch = true
84
+ source = ["workflowval"]
85
+
86
+ [tool.coverage.report]
87
+ show_missing = true
88
+ exclude_also = ["if TYPE_CHECKING:", "\\.\\.\\.$"]
@@ -0,0 +1,3 @@
1
+ """workflowval: API workflow tests from an OpenAPI spec."""
2
+
3
+ __version__ = "0.1.0"