97 lines
4.1 KiB
Python
97 lines
4.1 KiB
Python
"""
|
|
Modelos Pydantic para schemas personalizables.
|
|
Permite definir schemas dinámicos desde el frontend para extracción de datos.
|
|
"""
|
|
from pydantic import BaseModel, Field, field_validator
|
|
from typing import List, Optional
|
|
from enum import Enum
|
|
from datetime import datetime
|
|
|
|
|
|
class FieldType(str, Enum):
|
|
"""Tipos de campos soportados para extracción"""
|
|
STRING = "string"
|
|
INTEGER = "integer"
|
|
FLOAT = "float"
|
|
BOOLEAN = "boolean"
|
|
ARRAY_STRING = "array_string"
|
|
ARRAY_INTEGER = "array_integer"
|
|
ARRAY_FLOAT = "array_float"
|
|
DATE = "date"
|
|
|
|
|
|
class SchemaField(BaseModel):
|
|
"""Definición de un campo del schema"""
|
|
name: str = Field(..., description="Nombre del campo (snake_case)", min_length=1)
|
|
type: FieldType = Field(..., description="Tipo de dato del campo")
|
|
description: str = Field(..., description="Descripción clara para el LLM sobre qué extraer", min_length=1)
|
|
required: bool = Field(default=False, description="¿Es obligatorio extraer este campo?")
|
|
|
|
# Validaciones opcionales
|
|
min_value: Optional[float] = Field(None, description="Valor mínimo (para integer/float)")
|
|
max_value: Optional[float] = Field(None, description="Valor máximo (para integer/float)")
|
|
pattern: Optional[str] = Field(None, description="Patrón regex para validar strings")
|
|
|
|
@field_validator('name')
|
|
@classmethod
|
|
def validate_field_name(cls, v: str) -> str:
|
|
"""Valida que el nombre del campo sea snake_case válido"""
|
|
if not v.replace('_', '').isalnum():
|
|
raise ValueError("El nombre del campo debe ser snake_case alfanumérico")
|
|
if v[0].isdigit():
|
|
raise ValueError("El nombre del campo no puede empezar con número")
|
|
return v.lower()
|
|
|
|
@field_validator('min_value', 'max_value')
|
|
@classmethod
|
|
def validate_numeric_constraints(cls, v: Optional[float], info) -> Optional[float]:
|
|
"""Valida que min/max solo se usen con tipos numéricos"""
|
|
if v is not None:
|
|
field_type = info.data.get('type')
|
|
if field_type not in [FieldType.INTEGER, FieldType.FLOAT, FieldType.ARRAY_INTEGER, FieldType.ARRAY_FLOAT]:
|
|
raise ValueError(f"min_value/max_value solo aplican a campos numéricos, no a {field_type}")
|
|
return v
|
|
|
|
|
|
class CustomSchema(BaseModel):
|
|
"""Schema personalizable por el usuario para extracción de datos"""
|
|
schema_id: Optional[str] = Field(None, description="ID único del schema (generado automáticamente si no se provee)")
|
|
schema_name: str = Field(..., description="Nombre descriptivo del schema", min_length=1, max_length=100)
|
|
description: str = Field(..., description="Descripción de qué extrae este schema", min_length=1, max_length=500)
|
|
fields: List[SchemaField] = Field(..., description="Lista de campos a extraer", min_items=1, max_items=200)
|
|
|
|
# Metadata
|
|
created_at: Optional[str] = Field(None, description="Timestamp de creación ISO")
|
|
updated_at: Optional[str] = Field(None, description="Timestamp de última actualización ISO")
|
|
tema: Optional[str] = Field(None, description="Tema asociado (si es específico de un tema)")
|
|
is_global: bool = Field(default=False, description="¿Disponible para todos los temas?")
|
|
|
|
@field_validator('fields')
|
|
@classmethod
|
|
def validate_unique_field_names(cls, v: List[SchemaField]) -> List[SchemaField]:
|
|
"""Valida que no haya nombres de campos duplicados"""
|
|
field_names = [field.name for field in v]
|
|
if len(field_names) != len(set(field_names)):
|
|
raise ValueError("Los nombres de campos deben ser únicos en el schema")
|
|
return v
|
|
|
|
@field_validator('schema_name')
|
|
@classmethod
|
|
def validate_schema_name(cls, v: str) -> str:
|
|
"""Limpia y valida el nombre del schema"""
|
|
return v.strip()
|
|
|
|
|
|
class SchemaListResponse(BaseModel):
|
|
"""Response para listar schemas"""
|
|
schemas: List[CustomSchema]
|
|
total: int
|
|
|
|
|
|
class SchemaValidationResponse(BaseModel):
|
|
"""Response para validación de schema"""
|
|
valid: bool
|
|
message: str
|
|
json_schema: Optional[dict] = None
|
|
errors: Optional[List[str]] = None
|