Skip to main content

workflowval

Model-agnostic CLI agent that reads an OpenAPI spec, infers multi-step workflows, writes and runs pytest tests against a live API, and triages every failure as a test bug, an API bug or spec drift.

Status: all six commands work: test, plan, run, drift, inspect and report.

Start here

All the project's documents are in the docs folder.

  • Requirements: what workflowval was built to do.
  • Conclusions: how it measured up, what helped and what didn't.
  • Model comparison: two models on the same benchmark, side by side.
  • Drift study: the same tests against three implementations of one API.
  • Build notes: decisions in M0, then changes and known limits in M1, M2 and M3.

Quickstart

It needs Python 3.11 or later.

pip install workflowval

Then, from the folder where you want the tests written:

export OPENROUTER_API_KEY=...
export WORKFLOWVAL_MODEL=openrouter/<vendor>/<model>
workflowval inspect openapi.json
workflowval test openapi.json --base-url http://localhost:8000 --max-cost-usd 2
workflowval run tests/wf

This writes workflows/<id>.json, tests/wf/test_<id>.py and results.json in the current folder, plus report.md, junit.xml, bug reports and spec patches in reports/. If the API needs a login, pass --auth auth.json to both test and run, and set the env vars that file names. In a login config, a field like user.email nests the login body. See benchmarks/targets.md for the demo targets.

How it works

workflowval test runs these steps, and saves its place after each one:

  1. Plan. The Planner reads the spec and any docs, and writes one workflow file per multi-step scenario, each with the result it expects.
  2. Review the plan. The Reviewer looks for what the plan missed, as a user of the API would, and the Planner adds workflows.
  3. Write tests. Code, not a model, renders each workflow into a pytest file, so this step is free and repeatable.
  4. Run and label. The tests run against the live API. Only a result that doesn't match what the workflow expected goes to the Reviewer, which labels it TEST_BUG, API_BUG, SPEC_DRIFT, CONFLICT or UNRESOLVED.
  5. Fix test bugs only. The Planner corrects a wrong workflow and the Writer corrects test code. A real failure is never changed to make it pass. It stays as a labeled expected failure.
  6. Review the results. The Reviewer sees the confirmed failures and asks for new workflows, including ones that get past a known bug.
  7. Report. Bug reports, spec patches, report.md and junit.xml go in reports/.
Part Where
Commands src/workflowval/cli.py, pipeline.py
Agent loop, budgets and stop rules src/workflowval/loop/
Model adapter, the only code that imports a provider library src/workflowval/models/
Tools the roles may call, limited per role src/workflowval/tools/
Data formats src/workflowval/schemas/
Spec reading and response checks src/workflowval/spec/
Test rendering src/workflowval/gen/
Role prompts src/workflowval/prompts/

What needs a model

Needs no model or key Needs a model and its key
workflowval inspect workflowval plan
workflowval run tests/wf workflowval test
Plain pytest tests/wf workflowval drift
workflowval report Labeling and fixing failures
This repo's own lint and tests uv run pytest -m live, and the benchmark

So you can generate tests once, commit them, and re-run them with no model cost. A failure already reviewed as real counts as expected, so it doesn't fail the run. Any other failure makes run exit 1, but nothing labels it; run workflowval test to get the diagnosis. run and pytest still need a reachable API and, for a login API, its credentials.

Reading the results

Result Meaning Fails the run?
PASSED The test passed No
EXPECTED It hit the failure already reviewed as real, and only that failure No
LOOKS FIXED A test marked as a known issue passed No
MISMATCH A response breaks the spec, though the test may pass No, except in drift
UNRESOLVED Fix attempts ran out, or the failure has no verdict Yes

Benchmark

Scored against a seeded-bug app with 10 planted defects and one spec-docs conflict, for 3 rounds on one low-cost model. See docs/m3-notes.md for what the numbers mean, and benchmarks/seeded/README.md for who wrote the app, which affects how independent this score is.

Metric Median (range) Target
Seeded bugs detected, of 10 10 (9 to 10) 8 or more
Label accuracy on detected bugs, % 70 (67 to 90) 80 or more
Bugs a workflow could catch, of 10 10 (10 to 10) 10
False API_BUG on the clean build 1 (1 to 1) 1 or fewer
Tests green on the clean build, % 98 (96 to 98) 90 or more
CONFLICT reports 0 (0 to 0) 1
Cost per round, USD 0.31 (0.30 to 0.35) reported only

Label accuracy and the conflict report still miss their targets on this model. A second model met them on the medians, at about 28 times the cost and with less steady detection: see docs/model-comparison.md.

Known limits

  • Results vary by model and run, since models choose the workflows and labels. Three rounds is a small sample.
  • Labels are the weak spot. About a quarter of detected bugs got the wrong label on both models tested. Check a label before acting on it.
  • A test stops at its first failure. A bug in an early step can hide later bugs from every test that goes through that step. This is deliberate: the failure stays a result, and getting further is the job of another test.
  • Finding a conflict isn't reliable on the low-cost model, which wrote no CONFLICT reports in the benchmark.
  • The fix loop can be inconsistent. On the same kind of edge case, it may rewrite what some tests expect and not others. Rewrites that change what a test checks are printed as CHANGED, so review them.
  • Edge cases get missed, and a 204 can raise a false alarm. See docs/drift-study.md.
  • Generated tests aren't sandboxed. They run on your machine with your permissions, so use only specs and docs you trust.
  • The cost cap can go slightly over, since it judges by the dearest call so far.
  • A run resumes only after being killed. One that stopped on a model error, a step limit or a cost limit doesn't pick up where it stopped. Running it again starts a new run, which reuses a finished plan for the same spec.
  • The benchmark isn't fully independent, because of who wrote the seeded app. See benchmarks/seeded/README.md.

Safety and CI

workflowval test openapi.json --base-url https://staging.example.com --allow-remote --read-only
workflowval run tests/wf --ci --ci-max-api-bugs 2

--read-only sends no writes. A non-local host needs --allow-remote, then --yes, --ci or --read-only. Generated tests refuse any other host. --ci fails on anything unresolved, or on bugs above the limits. A killed workflowval test picks up at its last step when run again. See docs/m3-notes.md.

--max-cost-usd 2 stops a run before a model call could pass that cost, judging by the dearest call so far, so a much larger call can still go slightly over. A call with no known price also stops the run, since the limit can't be kept. Temporary provider errors, such as rate limits, are retried up to 5 times.

Secrets are hidden in everything saved or sent to a model: known values, auth headers and fields with names like token. A hidden value isn't checked against the spec. --no-redact keeps recorded response bodies exact, and works only with a local API, since those bodies may hold real secrets. Everything else stays hidden.

Develop

This needs uv.

git clone https://github.com/rkvf01/workflowval.git && cd workflowval
uv sync
uv run ruff check . && uv run ruff format --check .
uv run mypy --strict src
uv run pytest --cov=workflowval.loop --cov=workflowval.spec --cov=workflowval.triage --cov-fail-under=80
uv run workflowval --help

Unit tests use FakeModel and block network sockets, so they need no API key.

Models and API keys

Live runs go through LiteLLM. Pick a model with --model provider/name or WORKFLOWVAL_MODEL, and override per role with --role-model writer=provider/name. Provider keys come from environment variables only, for example OPENROUTER_API_KEY. Local Ollama models (ollama/<name>) need no key.

To check the adapter against a real provider:

export OPENROUTER_API_KEY=...
WORKFLOWVAL_LIVE_MODEL=openrouter/<vendor>/<model> uv run pytest -m live

Stats

Every run writes model calls, tool calls and cost, per workflow and per role, to .workflowval/stats.json, and adds a line to .workflowval/stats_history.jsonl. After 5 usable runs, the run summary suggests a --max-steps value based on that history. It never changes the limit by itself. See docs/m0-review.md.

Cost is the figure the provider returns with each reply, so tracking it adds no extra calls. To turn it off, use --no-cost-tracking or set WORKFLOWVAL_COST_TRACKING=off.

License

MIT

Metadata

Release files for workflowval 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for workflowval 0.1.0
File Size Uploaded Provenance Actions
workflowval-0.1.0.tar.gz 70.1 kB Without provenance Details for workflowval-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for workflowval 0.1.0
File Interpreter ABI Platform Actions
workflowval-0.1.0-py3-none-any.whl Python 3 none any Details for workflowval-0.1.0-py3-none-any.whl

Total release size: 163.0 kB

Release files / workflowval-0.1.0.tar.gz

Download URL workflowval-0.1.0.tar.gz
Size 70.1 kB
Tags Source
SHA-256 checksum
How to use checksums
06367f05ee3791b65514cd12fb18cc4ad1f603de87af156dd71b73b559862b9b
BLAKE2b-256 checksum
How to use checksums
2a3813e3b6857618638377fd1cf03d90e8780c10d294f21865efbb3fccee6b28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / workflowval-0.1.0-py3-none-any.whl

Download URL workflowval-0.1.0-py3-none-any.whl
Size 92.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ef02d756fce4d6ff0f36520e92eef5fb3cdc175f626b48cc3ba44c043e930f2e
BLAKE2b-256 checksum
How to use checksums
3adb3e589bdd44366d9e8ac64e1ebc0a54766ddf67e88f91a5befcca4db45fab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for workflowval 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page