Declarative quality gate loops for AI-assisted development
Project description
pyqual
AI Cost Tracking
- ๐ค LLM usage: $1.0500 (7 commits)
- ๐ค Human dev: ~$500 (5.0h @ $100/h, 30min dedup)
Generated on 2026-03-29 using openrouter/qwen/qwen3-coder-next
Declarative quality gate loops for AI-assisted development.
One YAML file. One command. Pipeline iterates until your code meets quality thresholds.
pip install pyqual
pyqual init
pyqual run
The problem
You use Copilot, Claude, GPT. They generate code. But nobody checks if that code meets your quality standards before it hits code review. And nobody automatically iterates if it doesn't.
pyqual closes that gap: define metrics โ run tools โ check gates โ if fail, LLM fixes โ re-check โ repeat until pass.
How it works
pyqual.yaml defines everything:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ metrics: โ
โ cc_max: 15 โ quality gates โ
โ vallm_pass_min: 90 โ
โ coverage_min: 80 โ
โ โ
โ stages: โ
โ - analyze (code2llm) โ
โ - validate (vallm) โ
โ - fix (llx/aider, when: fail) โ
โ - test (pytest) โ
โ โ
โ loop: โ
โ max_iterations: 3 โ
โ on_fail: report โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
pyqual run:
Iteration 1 โ analyze โ validate โ fix โ test โ check gates
โ
โโโ PASS โโโดโโ FAIL โโโ
โ โ
Done โ
Iteration 2...
pyqual.yaml
pipeline:
name: quality-loop
metrics:
cc_max: 15 # cyclomatic complexity per function
vallm_pass_min: 90 # vallm validation pass rate (%)
coverage_min: 80 # test coverage (%)
stages:
- name: analyze
run: code2llm ./ -f toon,evolution
- name: validate
run: vallm batch ./ --recursive --errors-json > .pyqual/errors.json
- name: fix
run: echo "Connect your LLM fixer here"
when: metrics_fail # only runs if gates fail
- name: test
run: pytest --cov --cov-report=json:.pyqual/coverage.json
loop:
max_iterations: 3
on_fail: report # report | create_ticket | block
CLI
pyqual init # create pyqual.yaml
pyqual run # execute full loop
pyqual run --dry-run # preview without executing
pyqual gates # check gates without running stages
pyqual status # show current metrics
Python API
from pyqual import Pipeline, PyqualConfig
config = PyqualConfig.load("pyqual.yaml")
pipeline = Pipeline(config, workdir="./my-project")
result = pipeline.run()
if result.final_passed:
print(f"All gates passed in {result.iteration_count} iterations")
else:
print("Gates not met โ check result.iterations for details")
LLM Integration
pyqual includes built-in LLM support via liteLLM. Configure via .env:
OPENROUTER_API_KEY=sk-or-v1-...
LLM_MODEL=openrouter/qwen/qwen3-coder-next
Use in your code:
from pyqual import get_llm
llm = get_llm() # Auto-loads config from .env
# Simple completion
response = llm.complete("Explain Python decorators")
print(response.content)
# Fix code issues
response = llm.fix_code(
code="def foo(x): return x + 1", # missing type hints
error="Function lacks type annotations"
)
print(response.content)
# Access cost info
print(f"Cost: ${response.cost:.4f}")
See examples/llm_fix/ for complete examples.
Metric sources
pyqual automatically collects metrics from:
| Source | Metrics | How |
|---|---|---|
analysis_toon.yaml |
cc (CCฬ), critical |
Regex parse from code2llm output |
validation_toon.yaml |
vallm_pass |
Pass rate from vallm batch |
.pyqual/errors.json |
error_count |
Count of vallm errors |
.pyqual/coverage.json |
coverage |
pytest-cov JSON report |
Custom metrics: extend GateSet._collect_metrics() or add your own collector.
Gate operators
metrics:
cc_max: 15 # cc โค 15
coverage_min: 80 # coverage โฅ 80
critical_max: 0 # critical โค 0
error_count_max: 5 # error_count โค 5
vallm_pass_min: 90 # vallm_pass โฅ 90
Suffixes: _max โ โค, _min โ โฅ, _lt โ <, _gt โ >, _eq โ =
Integration with ecosystem
pyqual is intentionally small (~800 lines). It orchestrates, not implements:
- code2llm does analysis โ pyqual reads the
.toonoutput - vallm does validation โ pyqual reads pass rates
- llx does LLM routing โ pyqual calls it as a stage
- planfile manages tickets โ pyqual creates tickets on gate failure
- costs tracks spending โ pyqual can gate on budget
- algitex can import pyqual as a dependency for its
gocommand
Examples
See examples/ directory for real-world configurations:
Project setups:
python-packageโ Standard Python package (src-layout)python-flatโ Simple project without src/monorepoโ Multiple packages in one repository
CI/CD:
github-actionsโ CI/CD with GitHub Actionsgitlab-ciโ CI/CD with GitLab CI
Python API usage:
basicโ Using Pipeline and GateSet from Pythonllm_fixโ LLM integration for auto-fixing codecustom_gatesโ Custom quality gates and metrics
Why not add this to algitex?
algitex has 29,448 lines, CCฬ=3.6, 64 critical issues, vallm pass 42.8%. Adding more features makes it worse. pyqual does one thing well: declarative quality gate loops. algitex imports pyqual. Both improve.
License
Licensed under Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyqual-0.1.14.tar.gz.
File metadata
- Download URL: pyqual-0.1.14.tar.gz
- Upload date:
- Size: 155.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0622a0831b4b44920f3b046df2bc326f9b91623963d3cdd9c913760cfe66eb83
|
|
| MD5 |
d59c0b74b4411b94e2789f27a13ebb44
|
|
| BLAKE2b-256 |
40489681edc30dfde4fe6912cf2ad1835e0b096b185e9971c103cffa884b056b
|
File details
Details for the file pyqual-0.1.14-py3-none-any.whl.
File metadata
- Download URL: pyqual-0.1.14-py3-none-any.whl
- Upload date:
- Size: 24.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bd180fae8529bc2f2823b7f3056a4299bed926f5e1b480fa10f614d566f4d3f
|
|
| MD5 |
1bb20badd0add95724e8510b46cdd9be
|
|
| BLAKE2b-256 |
84d1c58d41f95628f97a0f6428740f2cdde84f6d1a6b7617f7de76aa65eafe31
|