panner-ai 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.
- panner_ai-0.1.0/.github/workflows/ci.yml +34 -0
- panner_ai-0.1.0/.github/workflows/panner-ai.yml +59 -0
- panner_ai-0.1.0/.github/workflows/publish.yml +83 -0
- panner_ai-0.1.0/.gitignore +12 -0
- panner_ai-0.1.0/CHANGELOG.md +134 -0
- panner_ai-0.1.0/CONTRIBUTING.md +313 -0
- panner_ai-0.1.0/Dockerfile +6 -0
- panner_ai-0.1.0/PKG-INFO +292 -0
- panner_ai-0.1.0/README.md +259 -0
- panner_ai-0.1.0/RENAME_SUMMARY.md +107 -0
- panner_ai-0.1.0/WORKFLOW.md +512 -0
- panner_ai-0.1.0/action.yml +23 -0
- panner_ai-0.1.0/demo/suite-valid.yaml +20 -0
- panner_ai-0.1.0/docs/ARCHITECTURE.md +388 -0
- panner_ai-0.1.0/docs/adr/001-architecture.md +138 -0
- panner_ai-0.1.0/pyproject.toml +70 -0
- panner_ai-0.1.0/src/panner_ai/__init__.py +21 -0
- panner_ai-0.1.0/src/panner_ai/baseline/__init__.py +7 -0
- panner_ai-0.1.0/src/panner_ai/baseline/tracker.py +113 -0
- panner_ai-0.1.0/src/panner_ai/cli.py +122 -0
- panner_ai-0.1.0/src/panner_ai/config/__init__.py +1 -0
- panner_ai-0.1.0/src/panner_ai/config/parser.py +94 -0
- panner_ai-0.1.0/src/panner_ai/core/__init__.py +1 -0
- panner_ai-0.1.0/src/panner_ai/core/evaluators.py +116 -0
- panner_ai-0.1.0/src/panner_ai/core/pipeline.py +56 -0
- panner_ai-0.1.0/src/panner_ai/domain/__init__.py +1 -0
- panner_ai-0.1.0/src/panner_ai/domain/types.py +66 -0
- panner_ai-0.1.0/src/panner_ai/evaluators/__init__.py +7 -0
- panner_ai-0.1.0/src/panner_ai/evaluators/llm_judge.py +54 -0
- panner_ai-0.1.0/src/panner_ai/executor/__init__.py +15 -0
- panner_ai-0.1.0/src/panner_ai/executor/executor.py +200 -0
- panner_ai-0.1.0/src/panner_ai/infrastructure/__init__.py +1 -0
- panner_ai-0.1.0/src/panner_ai/infrastructure/llm.py +135 -0
- panner_ai-0.1.0/src/panner_ai/infrastructure/reporters/__init__.py +1 -0
- panner_ai-0.1.0/src/panner_ai/reporters/__init__.py +14 -0
- panner_ai-0.1.0/src/panner_ai/reporters/base.py +33 -0
- panner_ai-0.1.0/src/panner_ai/reporters/json.py +43 -0
- panner_ai-0.1.0/src/panner_ai/reporters/junit.py +65 -0
- panner_ai-0.1.0/src/panner_ai/reporters/terminal.py +82 -0
- panner_ai-0.1.0/tests/suites/regression.yaml +73 -0
- panner_ai-0.1.0/tests/suites/smoke.yaml +21 -0
- panner_ai-0.1.0/tests/test_executor.py +125 -0
- panner_ai-0.1.0/tests/test_functional_core.py +208 -0
- panner_ai-0.1.0/tests/test_llm_judge.py +340 -0
- panner_ai-0.1.0/tests/test_parser.py +236 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v4
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: pip install -e ".[dev]"
|
|
26
|
+
|
|
27
|
+
- name: Lint with ruff
|
|
28
|
+
run: ruff check src tests
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: pytest tests -v --tb=short
|
|
32
|
+
|
|
33
|
+
- name: Build
|
|
34
|
+
run: pip install build && python -m build
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Panner AI Test Suite
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-and-validate:
|
|
11
|
+
name: Lint & Validate
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install -e .[dev]
|
|
25
|
+
|
|
26
|
+
- name: Lint with Ruff
|
|
27
|
+
run: |
|
|
28
|
+
ruff check src tests
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
name: Unit Tests
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
strategy:
|
|
34
|
+
matrix:
|
|
35
|
+
python-version: ["3.11", "3.12"]
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
|
|
39
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
40
|
+
uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: ${{ matrix.python-version }}
|
|
43
|
+
|
|
44
|
+
- name: Install dependencies
|
|
45
|
+
run: |
|
|
46
|
+
python -m pip install --upgrade pip
|
|
47
|
+
pip install -e .[dev]
|
|
48
|
+
|
|
49
|
+
- name: Run unit tests
|
|
50
|
+
run: |
|
|
51
|
+
pytest tests/ -v --cov=src/panner-ai --cov-report=xml
|
|
52
|
+
|
|
53
|
+
- name: Upload coverage
|
|
54
|
+
if: always()
|
|
55
|
+
uses: actions/upload-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: coverage-${{ matrix.python-version }}
|
|
58
|
+
path: coverage.xml
|
|
59
|
+
retention-days: 30
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build Distribution
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install build dependencies
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade pip
|
|
26
|
+
pip install build
|
|
27
|
+
|
|
28
|
+
- name: Build distribution
|
|
29
|
+
run: |
|
|
30
|
+
python -m build
|
|
31
|
+
|
|
32
|
+
- name: Store distribution packages
|
|
33
|
+
uses: actions/upload-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: python-package-distributions
|
|
36
|
+
path: dist/
|
|
37
|
+
|
|
38
|
+
publish-to-pypi:
|
|
39
|
+
name: Publish to PyPI
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment:
|
|
43
|
+
name: pypi
|
|
44
|
+
url: https://pypi.org/project/panner-ai/
|
|
45
|
+
permissions:
|
|
46
|
+
id-token: write
|
|
47
|
+
steps:
|
|
48
|
+
- name: Download distribution packages
|
|
49
|
+
uses: actions/download-artifact@v4
|
|
50
|
+
with:
|
|
51
|
+
name: python-package-distributions
|
|
52
|
+
path: dist/
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
56
|
+
with:
|
|
57
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
58
|
+
|
|
59
|
+
github-release:
|
|
60
|
+
name: Sign & Create GitHub Release
|
|
61
|
+
needs: publish-to-pypi
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
permissions:
|
|
64
|
+
contents: write
|
|
65
|
+
id-token: write
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
|
|
69
|
+
- name: Download distribution packages
|
|
70
|
+
uses: actions/download-artifact@v4
|
|
71
|
+
with:
|
|
72
|
+
name: python-package-distributions
|
|
73
|
+
path: dist/
|
|
74
|
+
|
|
75
|
+
- name: Create GitHub Release
|
|
76
|
+
env:
|
|
77
|
+
GH_TOKEN: ${{ github.token }}
|
|
78
|
+
run: |
|
|
79
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
80
|
+
gh release create "$GITHUB_REF" \
|
|
81
|
+
--title "Release $VERSION" \
|
|
82
|
+
--generate-notes \
|
|
83
|
+
dist/*
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Panner AI are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-08-31
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **M1.1: Config Parser** — YAML configuration parsing with Pydantic v2 frozen models
|
|
12
|
+
- `parse_suite()` loads and validates SuiteConfig, TestCaseSpec, AssertionSpec
|
|
13
|
+
- Comprehensive error messages for malformed YAML
|
|
14
|
+
- Support for inline YAML body definitions
|
|
15
|
+
|
|
16
|
+
- **M1.2: HTTP Transport Layer** — Async HTTP test execution with concurrency control
|
|
17
|
+
- `TestExecutor` class with httpx async client
|
|
18
|
+
- Semaphore-based concurrency (default: 5 workers, configurable)
|
|
19
|
+
- Baseline loading and automatic regression detection flag
|
|
20
|
+
- Comprehensive exception handling for network failures
|
|
21
|
+
|
|
22
|
+
- **M1.3: Evaluator Pipeline** — Pure functional evaluators for test assertions
|
|
23
|
+
- `evaluate_status_code()` — HTTP response code matching
|
|
24
|
+
- `evaluate_latency()` — Response time threshold validation (milliseconds)
|
|
25
|
+
- `evaluate_json_schema()` — Response body JSON schema validation (Pydantic)
|
|
26
|
+
- `evaluate_regex()` — Response body regex pattern matching (with flags)
|
|
27
|
+
- Pipeline orchestration: `evaluate_test_case()`, `aggregate_suite()`
|
|
28
|
+
- Determinism verification via 100-iteration tests
|
|
29
|
+
|
|
30
|
+
- **M1.4: LLM-as-Judge** — Semantic correctness evaluation via Claude/GPT-4
|
|
31
|
+
- `evaluate_llm_judge()` — Structured JSON response parsing (0.0–1.0 score)
|
|
32
|
+
- LiteLLM abstraction for vendor independence (OpenAI, Anthropic, others)
|
|
33
|
+
- Environment variable configuration (LLM_MODEL, OPENAI_API_KEY, ANTHROPIC_API_KEY)
|
|
34
|
+
- Cost tracking: Token usage + estimated spend per model
|
|
35
|
+
- Graceful fallback: Default score 0.5 on JSON parse error
|
|
36
|
+
- 15 comprehensive mocked tests (zero real API calls in CI)
|
|
37
|
+
|
|
38
|
+
- **M1.5: Baseline Tracking & Regression Detection** — Historical test score tracking
|
|
39
|
+
- `BaselineTracker` class with `compare()` and `update()` methods
|
|
40
|
+
- Regression detection logic: delta < -0.1 (10% drop threshold)
|
|
41
|
+
- Git commit SHA tracking via subprocess `git rev-parse HEAD`
|
|
42
|
+
- ISO 8601 UTC timestamp recording
|
|
43
|
+
- Baseline persistence to `baseline.json` (git-versioned for trend analysis)
|
|
44
|
+
- Version-aware datetime.UTC import for Python 3.11+ compatibility
|
|
45
|
+
|
|
46
|
+
- **M1.6: CLI & Multi-Format Reporters** — Command-line interface and report generation
|
|
47
|
+
- `panner-ai run` CLI command with Typer framework
|
|
48
|
+
- Reporter pattern: Abstract base class + 3 concrete implementations
|
|
49
|
+
- `TerminalReporter` — ANSI colored output via Rich library (tables, summary stats)
|
|
50
|
+
- `JUnitReporter` — JUnit XML format for GitHub Actions and Jenkins integration
|
|
51
|
+
- `JSONReporter` — JSON telemetry export for analytics and dashboards
|
|
52
|
+
- Multi-reporter dispatch: `--reporter terminal,junit,json` (comma-separated)
|
|
53
|
+
- Smart output defaults: Terminal → stdout, JUnit/JSON → files with sensible names
|
|
54
|
+
- Exit codes: 0 (all pass, no regressions), 1 (failures or regression detected)
|
|
55
|
+
- Configuration options: `--config`, `--reporter`, `--output`, `--baseline-file`
|
|
56
|
+
|
|
57
|
+
- **M1.7: GitHub Actions CI/CD Integration** — Automated testing in GitHub workflows
|
|
58
|
+
- `.github/workflows/panner-ai.yml` workflow with 2-stage pipeline
|
|
59
|
+
- `smoke-tests` job: Fast baseline validation (2 tests, <1 minute, gates regression)
|
|
60
|
+
- `regression-tests` job: Full suite (8 tests, ~5 minutes, comprehensive coverage)
|
|
61
|
+
- Artifact upload: 90-day retention for test reports and telemetry
|
|
62
|
+
- Test reporting: JUnit XML parsed as GitHub PR checks + annotations
|
|
63
|
+
- Exit codes propagated: Blocks PR merge on failures/regressions (exit 1)
|
|
64
|
+
- Pre-configured test suites:
|
|
65
|
+
- `tests/suites/smoke.yaml` — Health check + root endpoint
|
|
66
|
+
- `tests/suites/regression.yaml` — CRUD operations, latency, error handling
|
|
67
|
+
|
|
68
|
+
- **M1.8: Documentation & Release** — Comprehensive user and developer documentation
|
|
69
|
+
- `README.md` — Project overview, quick start, architecture, usage examples
|
|
70
|
+
- `CONTRIBUTING.md` — Setup, code style, testing, PR workflow, release process
|
|
71
|
+
- `CHANGELOG.md` — This file (release history)
|
|
72
|
+
- `docs/ARCHITECTURE.md` — Deep-dive system design, data flow, extension points
|
|
73
|
+
- PyPI metadata: description, readme, homepage, repository, keywords
|
|
74
|
+
- GitHub milestone tracking: `M21-status.md` updated with completion status
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **Hotfix: cli.py Syntax Errors** — Fixed literal backslash-n escape sequences
|
|
79
|
+
- Replaced `except Exception as e:\n ` (literal) with proper newline characters\n - Updated `pyproject.toml` to suppress BLE001 linting rule (Phase 1 error propagation)\n - Removed unused `ReporterConfig` import from `reporters/json.py`
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- Moved `SuiteReport` to canonical location in `executor.executor` (eliminated duplication)
|
|
84
|
+
- Ruff configuration: Added `BLE001` to `extend-ignore` list with rationale
|
|
85
|
+
|
|
86
|
+
### Dependencies
|
|
87
|
+
|
|
88
|
+
- **typer** 0.12+ — CLI framework
|
|
89
|
+
- **rich** 13+ — Terminal output formatting (ANSI colors, tables)
|
|
90
|
+
- **httpx** 0.24+ — Async HTTP client
|
|
91
|
+
- **pydantic** 2.0+ — Data validation and serialization
|
|
92
|
+
- **litellm** 1.0+ — Vendor-agnostic LLM interface
|
|
93
|
+
- **pytest** 7.0+ — Testing framework (dev dependency)
|
|
94
|
+
- **pytest-cov** 4.0+ — Code coverage reporting (dev dependency)
|
|
95
|
+
|
|
96
|
+
### Architecture
|
|
97
|
+
|
|
98
|
+
- **Functional Core + Imperative Shell (ADR-001)**: Pure evaluators (core) separated from CLI/I/O (shell)
|
|
99
|
+
- **Zero Code Duplication**: Single SuiteReport model, reusable evaluator functions, abstract reporter pattern
|
|
100
|
+
- **Concurrency Control**: Semaphore-based async dispatch (5 workers default)
|
|
101
|
+
- **Immutable Data Models**: Pydantic frozen=True for all core types
|
|
102
|
+
- **Error Propagation**: Phase 1 — no exception handling in core; CLI propagates via exit codes
|
|
103
|
+
- **Git Integration**: Baseline versioning with commit SHA + timestamp
|
|
104
|
+
|
|
105
|
+
### Test Coverage
|
|
106
|
+
|
|
107
|
+
- **Total Tests**: 48+ test methods across all milestones
|
|
108
|
+
- **Coverage Target**: ≥80% (enforced by CI/CD)
|
|
109
|
+
- **Mocking Strategy**: All external services mocked (LLM, HTTP) — zero real API calls in CI
|
|
110
|
+
- **Determinism Verification**: 100-iteration tests for all evaluators
|
|
111
|
+
|
|
112
|
+
### Known Limitations (Phase 1)
|
|
113
|
+
|
|
114
|
+
- No conditional test skipping (Phase 2 feature)
|
|
115
|
+
- No advanced report filtering (Phase 2 feature)
|
|
116
|
+
- Baseline regression threshold hardcoded to -0.1 (Phase 2: configurable)
|
|
117
|
+
- No authentication support in HTTP executor (Phase 2: OAuth2, API keys)
|
|
118
|
+
- Limited custom assertion types (Phase 2: user-defined evaluators via plugins)
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Roadmap (Phase 2+)
|
|
123
|
+
|
|
124
|
+
- [ ] Authentication support (OAuth2, API keys, JWT)
|
|
125
|
+
- [ ] Conditional test skipping (environment-based, tag-based)
|
|
126
|
+
- [ ] Advanced reporting (HTML, Markdown, Slack integration)
|
|
127
|
+
- [ ] Plugin system for custom evaluators
|
|
128
|
+
- [ ] Configurable baseline thresholds
|
|
129
|
+
- [ ] Performance profiling integration
|
|
130
|
+
- [ ] Load testing mode (variable concurrency, ramp-up)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
**For detailed architecture and contribution guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).**
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Contributing to Panner AI
|
|
2
|
+
|
|
3
|
+
Thank you for contributing! This guide explains how to develop, test, and submit changes to Panner AI.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### Clone and install in dev mode
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/CraftedWithIntent/panner-ai.git
|
|
11
|
+
cd panner-ai
|
|
12
|
+
python -m venv venv
|
|
13
|
+
source venv/bin/activate # on Windows: venv\Scripts\activate
|
|
14
|
+
pip install -e .[dev]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Verify setup
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pytest tests/ -v
|
|
21
|
+
python -m py_compile src/panner-ai/**/*.py
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
Panner AI follows **Functional Core + Imperative Shell** (ADR-001):
|
|
27
|
+
|
|
28
|
+
- **Functional Core**: Pure evaluators (regex, latency, JSON schema, LLM judge)
|
|
29
|
+
- **Imperative Shell**: CLI (Typer), HTTP executor (asyncio), reporters (file I/O)
|
|
30
|
+
|
|
31
|
+
### Code Organization
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
src/panner-ai/
|
|
35
|
+
├── config/ # M1.1: Config parsing (Pydantic models)
|
|
36
|
+
├── executor/ # M1.2: HTTP dispatcher (asyncio, semaphore)
|
|
37
|
+
├── evaluators/ # M1.3–M1.4: Pure evaluation functions
|
|
38
|
+
├── baseline/ # M1.5: Regression tracking, git versioning
|
|
39
|
+
├── reporters/ # M1.6: Terminal, JUnit XML, JSON output
|
|
40
|
+
├── cli.py # M1.7: Typer CLI entry point
|
|
41
|
+
└── domain/ # Shared types (assertion specs, enums)
|
|
42
|
+
|
|
43
|
+
tests/
|
|
44
|
+
├── test_parser.py
|
|
45
|
+
├── test_executor.py
|
|
46
|
+
├── test_evaluators.py
|
|
47
|
+
├── test_llm_judge.py
|
|
48
|
+
├── test_baseline.py
|
|
49
|
+
├── test_reporters.py
|
|
50
|
+
├── test_cli.py
|
|
51
|
+
└── suites/ # M1.7: YAML test configurations (smoke, regression)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Code Style
|
|
55
|
+
|
|
56
|
+
### Linting & Formatting
|
|
57
|
+
|
|
58
|
+
Panner AI uses **Ruff** for all style enforcement:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
ruff check src tests # Check only
|
|
62
|
+
ruff check --fix src tests # Auto-fix
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Ruff Configuration
|
|
66
|
+
|
|
67
|
+
```toml
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 100
|
|
70
|
+
target-version = "py311"
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint]
|
|
73
|
+
extend-ignore = [
|
|
74
|
+
"BLE001", # Blind exception catches (Phase 1: CLI error propagation via exit codes)
|
|
75
|
+
"UP017", # datetime.UTC not available in Python 3.11 (project minimum is 3.11+)
|
|
76
|
+
]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Type Hints
|
|
80
|
+
|
|
81
|
+
- All functions must have parameter + return type hints
|
|
82
|
+
- Use `from typing import ...` for generic types
|
|
83
|
+
- Frozen Pydantic models for immutability: `class MyModel(BaseModel): model_config = ConfigDict(frozen=True)`
|
|
84
|
+
|
|
85
|
+
### Imports
|
|
86
|
+
|
|
87
|
+
- Group: stdlib, third-party, local (in that order)
|
|
88
|
+
- Alphabetical within each group
|
|
89
|
+
- Ruff auto-sorts on `--fix`
|
|
90
|
+
|
|
91
|
+
### Docstrings
|
|
92
|
+
|
|
93
|
+
- Use triple-quoted docstrings for all public functions, classes, modules
|
|
94
|
+
- Format: Google-style (Args, Returns, Raises, Example)
|
|
95
|
+
- Required for: CLI commands, evaluators, reporters, public APIs
|
|
96
|
+
|
|
97
|
+
## Testing
|
|
98
|
+
|
|
99
|
+
### Run tests
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# All tests
|
|
103
|
+
pytest tests/ -v
|
|
104
|
+
|
|
105
|
+
# Specific test file
|
|
106
|
+
pytest tests/test_parser.py -v
|
|
107
|
+
|
|
108
|
+
# With coverage
|
|
109
|
+
pytest tests/ --cov=src/panner-ai --cov-report=term-missing
|
|
110
|
+
|
|
111
|
+
# With markers
|
|
112
|
+
pytest tests/ -m "not slow" -v
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Coverage Requirements
|
|
116
|
+
|
|
117
|
+
- Minimum: **80%**
|
|
118
|
+
- Target: **90%+**
|
|
119
|
+
- Enforced by CI/CD
|
|
120
|
+
|
|
121
|
+
### Writing Tests
|
|
122
|
+
|
|
123
|
+
**Test file naming:** `test_<module>.py`
|
|
124
|
+
|
|
125
|
+
**Mocking external services:**
|
|
126
|
+
```python
|
|
127
|
+
from unittest.mock import patch, MagicMock
|
|
128
|
+
import pytest
|
|
129
|
+
|
|
130
|
+
@patch("panner-ai.evaluators.llm_judge.litellm.completion")
|
|
131
|
+
def test_llm_judge_mocked(mock_llm):
|
|
132
|
+
mock_llm.return_value = {"choices": [{"message": {"content": '{"score": 0.9}'}}]}
|
|
133
|
+
# Test assertion
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Fixtures:**
|
|
137
|
+
```python
|
|
138
|
+
@pytest.fixture
|
|
139
|
+
def sample_suite_config():
|
|
140
|
+
return SuiteConfig(
|
|
141
|
+
name="test",
|
|
142
|
+
test_cases=[
|
|
143
|
+
TestCaseSpec(
|
|
144
|
+
name="health",
|
|
145
|
+
endpoint="http://localhost:8000/health",
|
|
146
|
+
method="GET",
|
|
147
|
+
assertions=[AssertionSpec(type=AssertionType.STATUS_CODE, expected=200)],
|
|
148
|
+
)
|
|
149
|
+
],
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Adding New Evaluators
|
|
154
|
+
|
|
155
|
+
1. **Create evaluator function** in `src/panner-ai/evaluators/<name>.py`:
|
|
156
|
+
```python
|
|
157
|
+
def evaluate_my_check(response: httpx.Response, config: AssertionSpec) -> bool:
|
|
158
|
+
"""Evaluate custom assertion."""
|
|
159
|
+
# Pure function, no side effects
|
|
160
|
+
return True # or False
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
2. **Register in pipeline** (`src/panner-ai/core/pipeline.py`):
|
|
164
|
+
```python
|
|
165
|
+
EVALUATORS = {
|
|
166
|
+
AssertionType.STATUS_CODE: evaluate_status_code,
|
|
167
|
+
# ... existing evaluators ...
|
|
168
|
+
AssertionType.MY_CHECK: evaluate_my_check,
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
3. **Add test** in `tests/test_evaluators.py`:
|
|
173
|
+
```python
|
|
174
|
+
def test_my_check_success():
|
|
175
|
+
response = MagicMock(spec=httpx.Response)
|
|
176
|
+
# Set up response mock
|
|
177
|
+
result = evaluate_my_check(response, AssertionSpec(...))
|
|
178
|
+
assert result is True
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
4. **Update docs**:
|
|
182
|
+
- Add to README.md assertion types table
|
|
183
|
+
- Update docs/domain/suite-schema.md
|
|
184
|
+
- Add example YAML in tests/suites/
|
|
185
|
+
|
|
186
|
+
## PR Workflow
|
|
187
|
+
|
|
188
|
+
### Before You Start
|
|
189
|
+
|
|
190
|
+
1. **Check for open PRs:** `gh pr list --state open`
|
|
191
|
+
2. **Verify main clean:** `git log main --oneline | head -1`
|
|
192
|
+
3. **Search codebase** for existing implementations (zero duplication policy):
|
|
193
|
+
```bash
|
|
194
|
+
rg "def evaluate_" src/panner-ai/evaluators/
|
|
195
|
+
find src/panner-ai -name "*.py" -exec grep -l "class.*Reporter" {} +
|
|
196
|
+
```
|
|
197
|
+
4. **Update issue label:** `status:backlog` → `status:in-progress` (if applicable)
|
|
198
|
+
5. **Create feature branch:** `git checkout -b feature/ISSUE-description`
|
|
199
|
+
|
|
200
|
+
### During Development
|
|
201
|
+
|
|
202
|
+
- Keep scope small: **Max 5 files per PR** (excludes lockfiles, .sln, .csproj, generated files)
|
|
203
|
+
- Build frequently: `dotnet build FlowLedger.sln /p:TreatWarningsAsErrors=true` (or Python equivalent)
|
|
204
|
+
- Run tests: `pytest tests/ --cov=src/panner-ai`
|
|
205
|
+
- Update CHANGELOG.md with your changes
|
|
206
|
+
|
|
207
|
+
### Submitting PR
|
|
208
|
+
|
|
209
|
+
1. **Commit message format:**
|
|
210
|
+
```
|
|
211
|
+
feat: Brief description (M1.X: Component if applicable)
|
|
212
|
+
|
|
213
|
+
Longer explanation of what changed and why.
|
|
214
|
+
Include test coverage summary.
|
|
215
|
+
Fixes #ISSUE_NUMBER.
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
2. **Create PR via CLI:**
|
|
219
|
+
```bash
|
|
220
|
+
git push origin feature/ISSUE-description
|
|
221
|
+
gh pr create --title "feat: M1.X: Brief description" \
|
|
222
|
+
--body "Detailed description, testing notes, architecture decisions"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
3. **Wait for CI:** All checks must pass (ruff, pytest, type checking)
|
|
226
|
+
|
|
227
|
+
4. **Address feedback:** Push fixes to same branch (auto-updates PR)
|
|
228
|
+
|
|
229
|
+
5. **Merge:** Author squashes + merges (never fast-forward)
|
|
230
|
+
```bash
|
|
231
|
+
gh pr merge <PR_NUMBER> --squash
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
6. **Delete branch** after merge:
|
|
235
|
+
```bash
|
|
236
|
+
git branch -d feature/ISSUE-description
|
|
237
|
+
git push origin --delete feature/ISSUE-description
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Release Process
|
|
241
|
+
|
|
242
|
+
### Version Bumping
|
|
243
|
+
|
|
244
|
+
Panner AI uses semantic versioning: **MAJOR.MINOR.PATCH**
|
|
245
|
+
|
|
246
|
+
- **MAJOR:** Breaking API changes
|
|
247
|
+
- **MINOR:** New features (backward compatible)
|
|
248
|
+
- **PATCH:** Bug fixes
|
|
249
|
+
|
|
250
|
+
### Release Checklist
|
|
251
|
+
|
|
252
|
+
1. **Update version** in `pyproject.toml`:
|
|
253
|
+
```toml
|
|
254
|
+
[project]
|
|
255
|
+
version = "0.2.0"
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
2. **Update CHANGELOG.md** with release notes
|
|
259
|
+
|
|
260
|
+
3. **Tag commit:**
|
|
261
|
+
```bash
|
|
262
|
+
git tag v0.2.0
|
|
263
|
+
git push origin v0.2.0
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
4. **Build and publish to PyPI:**
|
|
267
|
+
```bash
|
|
268
|
+
pip install build twine
|
|
269
|
+
python -m build
|
|
270
|
+
twine upload dist/panner-ai-0.2.0-py3-none-any.whl
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Troubleshooting
|
|
274
|
+
|
|
275
|
+
### "SyntaxError: unexpected character after line continuation character"
|
|
276
|
+
|
|
277
|
+
**Cause:** Literal `\n` escape sequence in exception handlers (tool escaping issue)
|
|
278
|
+
|
|
279
|
+
**Fix:** Use Python string methods to avoid escape sequences:
|
|
280
|
+
```python
|
|
281
|
+
# DO NOT: write literal \n
|
|
282
|
+
# DO: Use chr() or f-strings
|
|
283
|
+
code = "except Exception as e:" + chr(10) + " pass"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### "ruff: BLE001 Do not catch blind exception"
|
|
287
|
+
|
|
288
|
+
**Rationale:** Phase 1 design uses broad exception handlers for CLI error propagation. Suppressed in config.
|
|
289
|
+
|
|
290
|
+
**If adding new exception handler:** Document why in code comment.
|
|
291
|
+
|
|
292
|
+
### Test failures on Python 3.12+
|
|
293
|
+
|
|
294
|
+
**Check:** `datetime.UTC` vs. `datetime.timezone.utc` compatibility
|
|
295
|
+
|
|
296
|
+
**Solution:** Use version-aware import:
|
|
297
|
+
```python
|
|
298
|
+
try:
|
|
299
|
+
from datetime import UTC
|
|
300
|
+
except ImportError:
|
|
301
|
+
from datetime import timezone
|
|
302
|
+
UTC = timezone.utc
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Questions?
|
|
306
|
+
|
|
307
|
+
- Open a GitHub Issue: [Issues](https://github.com/CraftedWithIntent/panner-ai/issues)
|
|
308
|
+
- Start a Discussion: [Discussions](https://github.com/CraftedWithIntent/panner-ai/discussions)
|
|
309
|
+
- Review architecture: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
**Thank you for making Panner AI better!**
|