fix: resolve CI linting and type errors
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
"""Prompt model and management."""
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -10,6 +12,8 @@ from pydantic import BaseModel, Field, field_validator
|
|||||||
|
|
||||||
|
|
||||||
class VariableType(str, Enum):
|
class VariableType(str, Enum):
|
||||||
|
"""Supported variable types."""
|
||||||
|
|
||||||
STRING = "string"
|
STRING = "string"
|
||||||
INTEGER = "integer"
|
INTEGER = "integer"
|
||||||
FLOAT = "float"
|
FLOAT = "float"
|
||||||
@@ -18,6 +22,8 @@ class VariableType(str, Enum):
|
|||||||
|
|
||||||
|
|
||||||
class PromptVariable(BaseModel):
|
class PromptVariable(BaseModel):
|
||||||
|
"""Definition of a template variable."""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
type: VariableType = VariableType.STRING
|
type: VariableType = VariableType.STRING
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
@@ -25,8 +31,17 @@ class PromptVariable(BaseModel):
|
|||||||
default: Optional[Any] = None
|
default: Optional[Any] = None
|
||||||
choices: Optional[List[str]] = None
|
choices: Optional[List[str]] = None
|
||||||
|
|
||||||
|
@field_validator('choices')
|
||||||
|
@classmethod
|
||||||
|
def validate_choices(cls, v, info):
|
||||||
|
if v is not None and info.data.get('type') != VariableType.CHOICE:
|
||||||
|
raise ValueError("choices only valid for CHOICE type")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
class ValidationRule(BaseModel):
|
class ValidationRule(BaseModel):
|
||||||
|
"""Validation rule for prompt output."""
|
||||||
|
|
||||||
type: str
|
type: str
|
||||||
pattern: Optional[str] = None
|
pattern: Optional[str] = None
|
||||||
json_schema: Optional[Dict[str, Any]] = None
|
json_schema: Optional[Dict[str, Any]] = None
|
||||||
@@ -34,6 +49,8 @@ class ValidationRule(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class Prompt(BaseModel):
|
class Prompt(BaseModel):
|
||||||
|
"""Prompt model with metadata and template."""
|
||||||
|
|
||||||
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
||||||
name: str
|
name: str
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
@@ -55,12 +72,20 @@ class Prompt(BaseModel):
|
|||||||
return hashlib.md5(content.encode()).hexdigest()
|
return hashlib.md5(content.encode()).hexdigest()
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
def to_dict(self, exclude_none: bool = False) -> Dict[str, Any]:
|
||||||
|
"""Export prompt to dictionary."""
|
||||||
|
data = super().model_dump(exclude_none=exclude_none)
|
||||||
|
data['created_at'] = self.created_at.isoformat()
|
||||||
|
data['updated_at'] = self.updated_at.isoformat()
|
||||||
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_yaml(cls, yaml_content: str) -> "Prompt":
|
def from_yaml(cls, yaml_content: str) -> "Prompt":
|
||||||
|
"""Parse prompt from YAML with front matter."""
|
||||||
content = yaml_content.strip()
|
content = yaml_content.strip()
|
||||||
|
|
||||||
if not content.startswith('---'):
|
if not content.startswith('---'):
|
||||||
metadata = {}
|
metadata: Dict[str, Any] = {}
|
||||||
prompt_content = content
|
prompt_content = content
|
||||||
else:
|
else:
|
||||||
parts = content[4:].split('\n---', 1)
|
parts = content[4:].split('\n---', 1)
|
||||||
@@ -80,6 +105,7 @@ class Prompt(BaseModel):
|
|||||||
return cls(**data)
|
return cls(**data)
|
||||||
|
|
||||||
def to_yaml(self) -> str:
|
def to_yaml(self) -> str:
|
||||||
|
"""Export prompt to YAML front matter format."""
|
||||||
def var_to_dict(v):
|
def var_to_dict(v):
|
||||||
d = v.model_dump()
|
d = v.model_dump()
|
||||||
d['type'] = v.type.value
|
d['type'] = v.type.value
|
||||||
@@ -101,6 +127,14 @@ class Prompt(BaseModel):
|
|||||||
return f"---\n{yaml_str}---\n{self.content}"
|
return f"---\n{yaml_str}---\n{self.content}"
|
||||||
|
|
||||||
def save(self, prompts_dir: Path) -> Path:
|
def save(self, prompts_dir: Path) -> Path:
|
||||||
|
"""Save prompt to file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompts_dir: Directory to save prompt in.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to saved file.
|
||||||
|
"""
|
||||||
prompts_dir.mkdir(parents=True, exist_ok=True)
|
prompts_dir.mkdir(parents=True, exist_ok=True)
|
||||||
filename = self.name.lower().replace(' ', '_').replace('/', '_') + '.yaml'
|
filename = self.name.lower().replace(' ', '_').replace('/', '_') + '.yaml'
|
||||||
filepath = prompts_dir / filename
|
filepath = prompts_dir / filename
|
||||||
@@ -110,13 +144,15 @@ class Prompt(BaseModel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, filepath: Path) -> "Prompt":
|
def load(cls, filepath: Path) -> "Prompt":
|
||||||
|
"""Load prompt from file."""
|
||||||
with open(filepath, 'r') as f:
|
with open(filepath, 'r') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
return cls.from_yaml(content)
|
return cls.from_yaml(content)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, prompts_dir: Path) -> List["Prompt"]:
|
def list(cls, prompts_dir: Path) -> List["Prompt"]:
|
||||||
prompts = []
|
"""List all prompts in directory."""
|
||||||
|
prompts: List["Prompt"] = []
|
||||||
if not prompts_dir.exists():
|
if not prompts_dir.exists():
|
||||||
return prompts
|
return prompts
|
||||||
for filepath in prompts_dir.glob('*.yaml'):
|
for filepath in prompts_dir.glob('*.yaml'):
|
||||||
@@ -124,4 +160,4 @@ class Prompt(BaseModel):
|
|||||||
prompts.append(cls.load(filepath))
|
prompts.append(cls.load(filepath))
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
return sorted(prompts, key=lambda p: p.name)
|
return sorted(prompts, key=lambda p: p.name)
|
||||||
|
|||||||
Reference in New Issue
Block a user