diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..372538d --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,24 @@ +{ + "permissions": { + "allow": [ + "Bash(git status*)", + "Bash(git diff*)", + "Bash(git log*)", + "Bash(git add*)", + "Bash(git commit*)", + "Bash(git push*)", + "Bash(git branch*)", + "Bash(git checkout*)", + "Bash(git stash*)", + "Bash(git remote*)", + "Bash(ruff check*)", + "Bash(ruff format*)", + "Bash(pytest*)", + "Bash(python -m pytest*)", + "Bash(pip install*)", + "Bash(pip list*)", + "Bash(soup *)", + "Bash(python -m soup_cli*)" + ] + } +} diff --git a/soup_cli/config/schema.py b/soup_cli/config/schema.py index 8259f5d..157d558 100644 --- a/soup_cli/config/schema.py +++ b/soup_cli/config/schema.py @@ -1,6 +1,6 @@ """Pydantic schemas for soup.yaml config — single source of truth.""" -from typing import Literal, Optional +from typing import List, Literal, Optional, Union from pydantic import BaseModel, Field @@ -9,7 +9,7 @@ class LoraConfig(BaseModel): r: int = Field(default=64, description="LoRA rank") alpha: int = Field(default=16, description="LoRA alpha") dropout: float = Field(default=0.05, description="LoRA dropout") - target_modules: str | list[str] = Field( + target_modules: Union[str, List[str]] = Field( default="auto", description="Target modules for LoRA. 'auto' = let peft decide.", ) @@ -28,7 +28,7 @@ class DataConfig(BaseModel): class TrainingConfig(BaseModel): epochs: int = Field(default=3, ge=1, description="Number of training epochs") lr: float = Field(default=2e-5, gt=0, description="Learning rate") - batch_size: int | Literal["auto"] = Field( + batch_size: Union[int, Literal["auto"]] = Field( default="auto", description="Batch size. 'auto' = find max that fits in memory.", ) diff --git a/soup_cli/data/validator.py b/soup_cli/data/validator.py index 1d322b8..688525f 100644 --- a/soup_cli/data/validator.py +++ b/soup_cli/data/validator.py @@ -1,9 +1,11 @@ """Dataset validation and statistics.""" +from typing import Optional + from soup_cli.data.formats import FORMAT_SIGNATURES -def validate_and_stats(data: list[dict], expected_format: str | None = None) -> dict: +def validate_and_stats(data: list[dict], expected_format: Optional[str] = None) -> dict: """Compute stats and validate dataset.""" if not data: return { diff --git a/soup_cli/monitoring/display.py b/soup_cli/monitoring/display.py index af78b65..b48e947 100644 --- a/soup_cli/monitoring/display.py +++ b/soup_cli/monitoring/display.py @@ -1,5 +1,7 @@ """Rich live training dashboard in the terminal.""" +from typing import Optional + from rich.console import Console from rich.live import Live from rich.panel import Panel @@ -23,7 +25,7 @@ class TrainingDisplay: self.grad_norm = 0.0 self.gpu_mem = "" self.speed = 0.0 - self._live: Live | None = None + self._live: Optional[Live] = None def start(self, total_steps: int): """Start the live display."""