Files
slahiriandClaude Opus 4.5 d11328ff90 feat: add STRUCTURED prompt style with category-segregated output
- Add STRUCTURED style that outputs each category on separate lines
- Fix '3d' token stripping - only strip booru patterns (1girl, 2boys)
- Increase max_tokens_per_category to 50 (was 5)
- Increase validator limits to preserve more information
- Preserve newlines throughout validation pipeline
- Disable truncation and duplicate removal by default

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 19:05:47 +05:30

149 lines
4.7 KiB
Python

"""Base classes for prompt validation (Phase 5).
The validator checks assembled prompts for quality issues:
- Length constraints
- Duplicate detection
- Conflict detection
- Missing required content
- Quality scoring
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional, Any, Tuple
from ..assembler.base import AssembledPrompt
class ValidationSeverity(Enum):
"""Severity level for validation issues."""
INFO = "info" # Informational, no action needed
WARNING = "warning" # Potential issue, consider fixing
ERROR = "error" # Must be fixed before use
class ValidationCategory(Enum):
"""Category of validation issue."""
LENGTH = "length" # Prompt too long/short
DUPLICATE = "duplicate" # Repeated content
CONFLICT = "conflict" # Contradictory terms
MISSING = "missing" # Required content missing
QUALITY = "quality" # General quality issue
GRAMMAR = "grammar" # Grammar/syntax issue
@dataclass
class ValidationIssue:
"""A single validation issue found in the prompt."""
category: ValidationCategory
severity: ValidationSeverity
message: str
location: Optional[str] = None # Where in prompt (section, position)
suggestion: Optional[str] = None # How to fix
tokens: List[str] = field(default_factory=list) # Related tokens
@dataclass
class ValidationResult:
"""Result from prompt validation."""
is_valid: bool # No errors (warnings OK)
prompt: str # The validated (possibly corrected) prompt
original_prompt: str # Original before corrections
issues: List[ValidationIssue] = field(default_factory=list)
corrections_made: int = 0
quality_score: float = 1.0 # 0.0 to 1.0
metadata: Dict[str, Any] = field(default_factory=dict)
@property
def error_count(self) -> int:
return sum(1 for i in self.issues if i.severity == ValidationSeverity.ERROR)
@property
def warning_count(self) -> int:
return sum(1 for i in self.issues if i.severity == ValidationSeverity.WARNING)
@property
def info_count(self) -> int:
return sum(1 for i in self.issues if i.severity == ValidationSeverity.INFO)
def get_issues_by_category(self, category: ValidationCategory) -> List[ValidationIssue]:
return [i for i in self.issues if i.category == category]
def get_summary(self) -> str:
"""Get human-readable validation summary."""
if self.is_valid and not self.issues:
return "Valid prompt, no issues found"
parts = []
if self.error_count:
parts.append(f"{self.error_count} error(s)")
if self.warning_count:
parts.append(f"{self.warning_count} warning(s)")
if self.info_count:
parts.append(f"{self.info_count} info")
if self.corrections_made:
parts.append(f"{self.corrections_made} auto-corrected")
status = "Invalid" if not self.is_valid else "Valid with issues"
return f"{status}: {', '.join(parts)}"
@dataclass
class ValidatorConfig:
"""Configuration for prompt validation."""
# Length constraints
max_tokens: int = 500 # High limit to preserve information
max_words: int = 500 # High limit to preserve information
min_words: int = 5 # Minimum for meaningful prompt
# Duplicate detection
detect_duplicates: bool = True
remove_duplicates: bool = False # Disabled by default to preserve info
# Conflict detection
detect_conflicts: bool = True
# Required categories (warning if missing)
required_categories: List[str] = field(default_factory=lambda: ["subject"])
# Auto-correction
auto_correct: bool = True
truncate_if_too_long: bool = False # Disabled by default to preserve info
class BaseValidator(ABC):
"""Base class for prompt validators."""
def __init__(self, config: Optional[ValidatorConfig] = None):
self.config = config or ValidatorConfig()
@abstractmethod
def validate(self, prompt: AssembledPrompt) -> ValidationResult:
"""Validate an assembled prompt.
Args:
prompt: AssembledPrompt from the assembler
Returns:
ValidationResult with issues and corrected prompt
"""
pass
def validate_text(self, text: str) -> ValidationResult:
"""Validate a raw text prompt.
Args:
text: Raw prompt text
Returns:
ValidationResult with issues
"""
# Create a minimal AssembledPrompt for validation
from ..assembler.base import AssembledPrompt, PromptStyle
prompt = AssembledPrompt(
prompt=text,
style=PromptStyle.NATURAL,
)
return self.validate(prompt)