1f4e7bea6b
- Created schemas/requests.py with validated models: - GenerateRequest - IngestRequest - ConfigRequest - Validates: concept length, repo format, numeric ranges - Prevents invalid input from reaching handlers This addresses input validation concern.
108 lines
3.1 KiB
Python
108 lines
3.1 KiB
Python
"""Input validation for Opus API requests.
|
|
|
|
Uses Pydantic for robust request validation.
|
|
"""
|
|
|
|
from pydantic import BaseModel, Field, validator
|
|
from typing import Optional, Literal
|
|
|
|
|
|
class GenerateRequest(BaseModel):
|
|
"""Request to generate a book."""
|
|
|
|
concept: str = Field(..., min_length=3, max_length=500)
|
|
repo: Optional[str] = None
|
|
|
|
# Framework options
|
|
framework: str = Field(default="snowflake")
|
|
genre: str = Field(default="fiction")
|
|
book_type: Literal["fiction", "nonfiction"] = Field(default="fiction")
|
|
|
|
# Nonfiction options
|
|
purpose: Optional[Literal["learn", "understand", "transform", "decide", "reference", "inspire"]] = None
|
|
category: Optional[str] = None
|
|
|
|
# Generation options
|
|
words: int = Field(default=5000, ge=100, le=200000)
|
|
chapters: int = Field(default=3, ge=1, le=100)
|
|
tone: str = Field(default="literary")
|
|
|
|
# Orchestration options
|
|
use_crewai: bool = False
|
|
use_autogen: bool = True
|
|
|
|
# Checkpointing
|
|
thread_id: Optional[str] = None
|
|
resume: bool = False
|
|
|
|
@validator("concept")
|
|
def concept_not_empty(cls, v):
|
|
if not v or not v.strip():
|
|
raise ValueError("Concept cannot be empty")
|
|
return v.strip()
|
|
|
|
@validator("repo")
|
|
def validate_repo(cls, v):
|
|
if v and not cls._is_valid_repo(v):
|
|
raise ValueError("Invalid repository format. Use 'owner/repo'")
|
|
return v
|
|
|
|
@staticmethod
|
|
def _is_valid_repo(repo: str) -> bool:
|
|
return "/" in repo and len(repo.split("/")) == 2
|
|
|
|
class Config:
|
|
schema_extra = {
|
|
"example": {
|
|
"concept": "A robot who dreams of being human",
|
|
"genre": "sci-fi",
|
|
"book_type": "fiction",
|
|
"words": 5000
|
|
}
|
|
}
|
|
|
|
|
|
class IngestRequest(BaseModel):
|
|
"""Request to ingest content."""
|
|
|
|
source_type: Literal["github", "s3", "local", "url"] = Field(...)
|
|
repo: Optional[str] = None
|
|
bucket: Optional[str] = None
|
|
path: Optional[str] = None
|
|
url: Optional[str] = None
|
|
|
|
@validator("source_type")
|
|
def validate_source(cls, v, values):
|
|
required = {
|
|
"github": "repo",
|
|
"s3": "bucket",
|
|
"local": "path",
|
|
"url": "url",
|
|
}
|
|
if required.get(v) and not values.get(required[v]):
|
|
raise ValueError(f"{required[v]} required for {v} source")
|
|
return v
|
|
|
|
class Config:
|
|
schema_extra = {
|
|
"example": {
|
|
"source_type": "github",
|
|
"repo": "owner/repo"
|
|
}
|
|
}
|
|
|
|
|
|
class ConfigRequest(BaseModel):
|
|
"""Request to update config."""
|
|
|
|
provider: Optional[Literal["openai", "anthropic", "minimax"]] = None
|
|
model: Optional[str] = None
|
|
temperature: Optional[float] = Field(default=0.7, ge=0.0, le=2.0)
|
|
max_tokens: Optional[int] = Field(default=4000, ge=100, le=100000)
|
|
|
|
@validator("temperature")
|
|
def validate_temperature(cls, v):
|
|
if v < 0 or v > 2:
|
|
raise ValueError("Temperature must be between 0 and 2")
|
|
return v
|