Landing AI integrado
This commit is contained in:
Binary file not shown.
Binary file not shown.
96
backend/app/models/schema_models.py
Normal file
96
backend/app/models/schema_models.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
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=50)
|
||||
|
||||
# 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
|
||||
Reference in New Issue
Block a user