From dba0605cec8c7c3e4c3b38375060bf2201afdfd7 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Fri, 13 Mar 2026 00:15:17 -0400 Subject: [PATCH] feat(scripts): add Pydantic schemas for Script Generator Co-Authored-By: Claude Sonnet 4.6 --- backend/app/schemas/__init__.py | 9 ++ backend/app/schemas/script_template.py | 137 +++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 backend/app/schemas/script_template.py diff --git a/backend/app/schemas/__init__.py b/backend/app/schemas/__init__.py index 1dd46a2e..c761c02f 100644 --- a/backend/app/schemas/__init__.py +++ b/backend/app/schemas/__init__.py @@ -10,6 +10,11 @@ from .ai_builder import ( AIStartResponse, AIScaffoldResponse, AIBranchDetailResponse, AIAssembleResponse, AIQuotaStatusResponse, ) +from .script_template import ( + ScriptCategoryResponse, + ScriptTemplateCreate, ScriptTemplateUpdate, ScriptTemplateListItem, ScriptTemplateDetail, + ScriptGenerateRequest, ScriptGenerateResponse, ScriptGenerationRecord, +) __all__ = [ # User @@ -30,4 +35,8 @@ __all__ = [ "AIStartRequest", "AIScaffoldRequest", "AIBranchDetailRequest", "AIAssembleRequest", "AIStartResponse", "AIScaffoldResponse", "AIBranchDetailResponse", "AIAssembleResponse", "AIQuotaStatusResponse", + # Script Generator + "ScriptCategoryResponse", + "ScriptTemplateCreate", "ScriptTemplateUpdate", "ScriptTemplateListItem", "ScriptTemplateDetail", + "ScriptGenerateRequest", "ScriptGenerateResponse", "ScriptGenerationRecord", ] diff --git a/backend/app/schemas/script_template.py b/backend/app/schemas/script_template.py new file mode 100644 index 00000000..7acb45b4 --- /dev/null +++ b/backend/app/schemas/script_template.py @@ -0,0 +1,137 @@ +from datetime import datetime +from typing import Optional, Any +from uuid import UUID +from pydantic import BaseModel, Field + + +# ── Parameter schema types ────────────────────────────────────────────────── + +class ScriptParameterValidation(BaseModel): + pattern: Optional[str] = None + min_length: Optional[int] = None + max_length: Optional[int] = None + min_value: Optional[float] = None + max_value: Optional[float] = None + +class ScriptParameter(BaseModel): + key: str + label: str + type: str # text | password | select | boolean | multi_text | number | textarea + required: bool = True + placeholder: Optional[str] = None + group: Optional[str] = None + order: int = 0 + help_text: Optional[str] = None + options: Optional[list[dict]] = None # for select type: [{value, label}] + default: Optional[Any] = None + validation: Optional[ScriptParameterValidation] = None + sensitive: bool = False # password fields → redacted in generation record + +class ScriptParametersSchema(BaseModel): + parameters: list[ScriptParameter] + + +# ── Category ──────────────────────────────────────────────────────────────── + +class ScriptCategoryResponse(BaseModel): + id: UUID + name: str + slug: str + description: Optional[str] = None + icon: Optional[str] = None + sort_order: int + template_count: int = 0 + + class Config: + from_attributes = True + + +# ── Template ──────────────────────────────────────────────────────────────── + +class ScriptTemplateCreate(BaseModel): + category_id: UUID + name: str = Field(..., min_length=1, max_length=200) + description: Optional[str] = None + use_case: Optional[str] = None + script_body: str = Field(..., min_length=1) + parameters_schema: dict = Field(default_factory=dict) + default_values: dict = Field(default_factory=dict) + validation_rules: dict = Field(default_factory=dict) + tags: list[str] = Field(default_factory=list) + complexity: str = Field(default="beginner", pattern="^(beginner|intermediate|advanced)$") + estimated_runtime: Optional[str] = None + requires_elevation: bool = False + requires_modules: list[str] = Field(default_factory=list) + +class ScriptTemplateUpdate(BaseModel): + name: Optional[str] = Field(None, min_length=1, max_length=200) + description: Optional[str] = None + use_case: Optional[str] = None + script_body: Optional[str] = None + parameters_schema: Optional[dict] = None + default_values: Optional[dict] = None + validation_rules: Optional[dict] = None + tags: Optional[list[str]] = None + complexity: Optional[str] = Field(None, pattern="^(beginner|intermediate|advanced)$") + estimated_runtime: Optional[str] = None + requires_elevation: Optional[bool] = None + requires_modules: Optional[list[str]] = None + is_active: Optional[bool] = None + +class ScriptTemplateListItem(BaseModel): + id: UUID + category_id: UUID + team_id: Optional[UUID] = None + name: str + slug: str + description: Optional[str] = None + tags: list[str] + complexity: str + estimated_runtime: Optional[str] = None + requires_elevation: bool + requires_modules: list[str] + is_verified: bool + usage_count: int + + class Config: + from_attributes = True + +class ScriptTemplateDetail(ScriptTemplateListItem): + use_case: Optional[str] = None + script_body: str + parameters_schema: dict + default_values: dict + validation_rules: dict + version: int + created_at: datetime + updated_at: datetime + + class Config: + from_attributes = True + + +# ── Generation ─────────────────────────────────────────────────────────────── + +class ScriptGenerateRequest(BaseModel): + template_id: UUID + parameters: dict[str, Any] + session_id: Optional[UUID] = None + +class ScriptGenerateResponse(BaseModel): + id: UUID + script: str + warnings: list[str] = Field(default_factory=list) + metadata: dict + + class Config: + from_attributes = True + +class ScriptGenerationRecord(BaseModel): + id: UUID + template_id: UUID + template_name: str + parameters_used: dict # passwords already redacted + created_at: datetime + + class Config: + from_attributes = True