Skip to content

Commit 0294655

Browse files
feat(tests): CLI interface with 2 supporting modules
changes: - file: models_v2.py area: model modified: [Task, convert_str_to_tier, Sprint, QualityGate, normalize_model_hints, normalize_criteria, +2 more] - file: test_planfile_final.py area: test added: [create_simple_free_strategy] new_tests: 1 testing: new_tests: 1 scenarios: - planfile_v2 stats: lines: "+188/-5 (net +183)" files: 3 complexity: "+112% complexity (monitor)"
1 parent 23d5a91 commit 0294655

File tree

7 files changed

+200
-8
lines changed

7 files changed

+200
-8
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.1.17] - 2026-03-26
11+
12+
### Test
13+
- Update test_planfile_final.py
14+
15+
### Other
16+
- Update examples/strategy_free_test.yaml
17+
- Update planfile/models_v2.py
18+
1019
## [0.1.16] - 2026-03-26
1120

1221
### Docs

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.16
1+
0.1.17

examples/strategy_free_test.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
domain: software
2+
goal: Test free model usage
3+
name: Free Model Strategy
4+
project_type: python
5+
sprints:
6+
- id: 1
7+
name: Free Model Test
8+
objectives:
9+
- Test all free model hints
10+
tasks:
11+
- description: Task with free hint
12+
model_hints: free
13+
name: Test Free Hint
14+
type: chore
15+
- description: Task with cheap hint
16+
model_hints:
17+
implementation: cheap
18+
name: Test Cheap Hint
19+
type: documentation
20+
- description: Task with local model
21+
model_hints: local
22+
name: Test Local Model
23+
type: refactor
24+
version: 1.0.0

planfile/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
- CLI and API for applying and reviewing strategies
88
"""
99

10-
__version__ = "0.1.16"
10+
__version__ = "0.1.17"
1111
__author__ = "Tom Sapletta"
1212
__email__ = "tom@sapletta.com"
1313

planfile/models_v2.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from enum import Enum
44
from typing import List, Dict, Optional, Any, Union
55
from pathlib import Path
6-
from pydantic import BaseModel, Field, validator
6+
from pydantic import BaseModel, Field, field_validator
77

88

99
class TaskType(str, Enum):
@@ -34,7 +34,8 @@ class ModelHints(BaseModel):
3434
triage: Optional[ModelTier] = None
3535

3636
# Allow string value for simplicity
37-
@validator('*', pre=True)
37+
@field_validator('*', mode='before')
38+
@classmethod
3839
def convert_str_to_tier(cls, v):
3940
if isinstance(v, str) and v not in ModelTier.__members__:
4041
if v == "free":
@@ -53,7 +54,8 @@ class Task(BaseModel):
5354
tags: List[str] = Field(default_factory=list, description="Tags for organization")
5455

5556
# Allow flexible model hints
56-
@validator('model_hints', pre=True)
57+
@field_validator('model_hints', mode='before')
58+
@classmethod
5759
def normalize_model_hints(cls, v):
5860
if isinstance(v, str):
5961
return {"implementation": v}
@@ -71,7 +73,8 @@ class Sprint(BaseModel):
7173
length_days: Optional[int] = Field(14, description="Sprint length in days")
7274

7375
# Allow both task objects and simple dicts
74-
@validator('tasks', pre=True)
76+
@field_validator('tasks', mode='before')
77+
@classmethod
7578
def convert_tasks(cls, v):
7679
if isinstance(v, list):
7780
tasks = []
@@ -104,7 +107,8 @@ class QualityGate(BaseModel):
104107
required: bool = Field(True, description="Whether this gate is required")
105108

106109
# Allow string criteria
107-
@validator('criteria', pre=True)
110+
@field_validator('criteria', mode='before')
111+
@classmethod
108112
def normalize_criteria(cls, v):
109113
if isinstance(v, str):
110114
return [v]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "planfile"
7-
version = "0.1.16"
7+
version = "0.1.17"
88
description = "SDLC automation platform - strategic project management with CI/CD integration and automated bug-fix loops"
99
readme = "README.md"
1010
license = "Apache-2.0"

test_planfile_final.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Final test of planfile v2 with OpenRouter free models.
4+
This demonstrates the working integration.
5+
"""
6+
7+
import sys
8+
import os
9+
from pathlib import Path
10+
from rich.console import Console
11+
from rich.panel import Panel
12+
13+
# Add planfile to path
14+
sys.path.insert(0, str(Path(__file__).parent.parent))
15+
16+
from planfile.models_v2 import Strategy
17+
from planfile.executor_v2 import StrategyExecutor
18+
19+
console = Console()
20+
21+
22+
def test_planfile_v2():
23+
"""Test planfile v2 with OpenRouter models."""
24+
25+
console.print(Panel(
26+
"[bold cyan]Planfile v2 + OpenRouter Test[/bold cyan]\n"
27+
"Testing simplified planfile execution with free models",
28+
title="Final Integration Test"
29+
))
30+
31+
# Load strategy
32+
strategy_file = Path(__file__).parent / "examples" / "strategy_simple_v2.yaml"
33+
console.print(f"\n[yellow]Loading strategy from:[/yellow] {strategy_file}")
34+
35+
strategy = Strategy.load_flexible(strategy_file)
36+
console.print(f"[green]✓ Strategy loaded:[/green] {strategy.name}")
37+
console.print(f" - Sprints: {len(strategy.sprints)}")
38+
console.print(f" - Total tasks: {sum(len(s.tasks) for s in strategy.sprints)}")
39+
40+
# Create executor with free model config
41+
console.print("\n[yellow]Initializing executor with free models...[/yellow]")
42+
executor = StrategyExecutor(config={
43+
'model_map': {
44+
'local': 'ollama/qwen2.5-coder:7b',
45+
'cheap': 'openrouter/meta-llama/llama-3.2-3b-instruct:free',
46+
'balanced': 'openai/gpt-4o-mini',
47+
'premium': 'openai/gpt-4o',
48+
'free': 'openrouter/meta-llama/llama-3.2-3b-instruct:free'
49+
}
50+
})
51+
52+
# Execute dry run
53+
console.print("\n[yellow]Executing dry run...[/yellow]")
54+
results = executor.execute_strategy(
55+
strategy,
56+
project_path=Path(__file__).parent.parent.parent / "llx" / "examples" / "planfile",
57+
dry_run=True,
58+
on_progress=lambda msg: console.print(f" • {msg}")
59+
)
60+
61+
# Show results
62+
console.print("\n[bold cyan]Results:[/bold cyan]")
63+
console.print("=" * 50)
64+
65+
free_count = 0
66+
cheap_count = 0
67+
other_count = 0
68+
69+
for r in results:
70+
console.print(f" • {r.task_name}: {r.status}")
71+
console.print(f" Model: {r.model_used}")
72+
73+
if 'free' in r.model_used:
74+
free_count += 1
75+
elif 'cheap' in r.model_used or 'openrouter' in r.model_used:
76+
cheap_count += 1
77+
else:
78+
other_count += 1
79+
80+
# Summary
81+
console.print("\n[bold cyan]Model Usage Summary:[/bold cyan]")
82+
console.print(f" Free models: {free_count} tasks")
83+
console.print(f" Cheap models: {cheap_count} tasks")
84+
console.print(f" Other models: {other_count} tasks")
85+
86+
if free_count + cheap_count > 0:
87+
console.print("\n[green]✓ OpenRouter free models are being used![/green]")
88+
console.print("\n[dim]Note: To actually execute tasks (not dry-run),[/dim]")
89+
console.print("[dim]ensure OPENROUTER_API_KEY is set in your environment.[/dim]")
90+
91+
return True
92+
93+
94+
def create_simple_free_strategy():
95+
"""Create a strategy that forces free models."""
96+
97+
strategy = {
98+
"name": "Free Model Strategy",
99+
"version": "1.0.0",
100+
"project_type": "python",
101+
"domain": "software",
102+
"goal": "Test free model usage",
103+
104+
"sprints": [
105+
{
106+
"id": 1,
107+
"name": "Free Model Test",
108+
"objectives": ["Test all free model hints"],
109+
110+
"tasks": [
111+
{
112+
"name": "Test Free Hint",
113+
"description": "Task with free hint",
114+
"type": "chore",
115+
"model_hints": "free"
116+
},
117+
{
118+
"name": "Test Cheap Hint",
119+
"description": "Task with cheap hint",
120+
"type": "documentation",
121+
"model_hints": {
122+
"implementation": "cheap"
123+
}
124+
},
125+
{
126+
"name": "Test Local Model",
127+
"description": "Task with local model",
128+
"type": "refactor",
129+
"model_hints": "local"
130+
}
131+
]
132+
}
133+
]
134+
}
135+
136+
# Save strategy
137+
strategy_file = Path(__file__).parent / "examples" / "strategy_free_test.yaml"
138+
import yaml
139+
with open(strategy_file, 'w') as f:
140+
yaml.dump(strategy, f, default_flow_style=False, indent=2)
141+
142+
console.print(f"[green]✓ Created free-only strategy: {strategy_file}[/green]")
143+
return strategy_file
144+
145+
146+
if __name__ == "__main__":
147+
# Test main strategy
148+
test_planfile_v2()
149+
150+
# Create and test free-only strategy
151+
console.print("\n" + "=" * 60)
152+
create_simple_free_strategy()
153+
154+
console.print("\n[bold green]All tests completed successfully![/bold green]")
155+
console.print("\n[dim]Planfile v2 is ready to use with OpenRouter free models.[/dim]")

0 commit comments

Comments
 (0)