Imágenes OpenAI y Claude

This commit is contained in:
lansan69
2026-03-31 03:54:46 -06:00
parent fc25a47f04
commit bc513ad2c4
11 changed files with 75 additions and 22 deletions

View File

@@ -21,7 +21,7 @@ from app.services.image.evaluations_adapters import evaluate_image_with_provider
# Inicializar el router de FastAPI para este módulo # Inicializar el router de FastAPI para este módulo
image_router_analysis = APIRouter() image_router_analysis = APIRouter()
@image_router_analysis.post("/evaluations/", response_model=StandardImageAnalysisResult) @image_router_analysis.post("/evaluations", response_model=StandardImageAnalysisResult)
async def evaluate_image(image_request: ImageRequestFile = Depends()) -> StandardImageAnalysisResult: async def evaluate_image(image_request: ImageRequestFile = Depends()) -> StandardImageAnalysisResult:
""" """
Endpoint para analizar imágenes usando una rúbrica de evaluación infiriendo el proveedor de IA Endpoint para analizar imágenes usando una rúbrica de evaluación infiriendo el proveedor de IA

View File

@@ -22,5 +22,6 @@ class Settings:
# Proveedores de Imagen # Proveedores de Imagen
# --------------------------------------------------------------- # ---------------------------------------------------------------
CLARIFAI_API_KEY = os.getenv("CLARIFAI_API_KEY", "") CLARIFAI_API_KEY = os.getenv("CLARIFAI_API_KEY", "")
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "")
settings = Settings() settings = Settings()

View File

@@ -49,4 +49,12 @@ class StandardImageAnalysisResult(BaseModel):
model_used: str = Field(..., description="Modelo específico utilizado para el análisis (ej. 'vision-1')") model_used: str = Field(..., description="Modelo específico utilizado para el análisis (ej. 'vision-1')")
score: float = Field(None, description="Puntuación general asignada a la imagen después del análisis") score: float = Field(None, description="Puntuación general asignada a la imagen después del análisis")
feedback: str = Field(None, description="Comentarios o retroalimentación generada por el modelo de IA sobre la imagen") feedback: str = Field(None, description="Comentarios o retroalimentación generada por el modelo de IA sobre la imagen")
detailed_criteria: List[ImageEvaluationCriteria] = Field(None, description="Lista de criterios de evaluación detallados con sus respectivas puntuaciones y descripciones") detailed_criteria: List[ImageEvaluationCriteria] = Field(None, description="Lista de criterios de evaluación detallados con sus respectivas puntuaciones y descripciones")
class StandardImageAnalysis(BaseModel):
"""Modelo que representa el resultado estándar de un análisis de imágenes para Qualidot."""
score: float = Field(None, description="Puntuación general asignada a la imagen después del análisis")
feedback: str = Field(None, description="Comentarios o retroalimentación generada por el modelo de IA sobre la imagen")
detailed_criteria: List[ImageEvaluationCriteria] = Field(None, description="Lista de criterios de evaluación detallados con sus respectivas puntuaciones y descripciones")

View File

@@ -9,6 +9,8 @@ Propósito:
""" """
import json import json
import mimetypes
import mimetypes
import tempfile import tempfile
import os import os
from fastapi import HTTPException from fastapi import HTTPException
@@ -21,6 +23,7 @@ from app.schemas.image_standard import ImageRequestFile, StandardImageAnalysisRe
from app.core import config from app.core import config
from app.utilities.image_utilities import json_to_rubric, encode_image_from_bytes from app.utilities.image_utilities import json_to_rubric, encode_image_from_bytes
from app.services.image.prompt_builder import build_image_evaluation_prompt from app.services.image.prompt_builder import build_image_evaluation_prompt
from anthropic import Anthropic
# Función de adaptador principal que infiere el proveedor y llama al adaptador específico # Función de adaptador principal que infiere el proveedor y llama al adaptador específico
async def evaluate_image_with_provider(image_request: ImageRequestFile) -> StandardImageAnalysisResult: async def evaluate_image_with_provider(image_request: ImageRequestFile) -> StandardImageAnalysisResult:
@@ -33,7 +36,7 @@ async def evaluate_image_with_provider(image_request: ImageRequestFile) -> Stand
rubric_dict = json.loads(content) rubric_dict = json.loads(content)
rubric = json_to_rubric(rubric_dict) rubric = json_to_rubric(rubric_dict)
prompt = build_image_evaluation_prompt(rubric) prompt = build_image_evaluation_prompt(rubric)
match provider: match provider:
case "openai": case "openai":
return await evaluate_with_openai(image_request, prompt) return await evaluate_with_openai(image_request, prompt)
@@ -74,20 +77,19 @@ async def evaluate_with_openai(image_request: ImageRequestFile, prompt: str) ->
) )
resultado = json.loads(response.choices[0].message.content) resultado = json.loads(response.choices[0].message.content)
return StandardImageAnalysisResult(**resultado) return StandardImageAnalysisResult(
status="success",
original_filename=image_request.file.filename,
provider_used="OpenAI",
model_used=image_request.model,
**resultado
)
except Exception as e: except Exception as e:
# Capturamos cualquier error de OpenAI o de lectura de archivos # Capturamos cualquier error de OpenAI o de lectura de archivos
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=f"Error evaluando la imagen: {str(e)}" detail=f"Error evaluando la imagen: {str(e)}"
) )
# PASOS A SEGUIR PARA IMPLEMENTAR LA LÓGICA DE EVALUACIÓN CON OPENAI:
# 1. Validar la imagen de entrada (tamaño, formato, etc.)
# 2. Configurar el cliente de OpenAI con la clave API
# 3. Llamar a la API de OpenAI para evaluar la imagen
# 4. Convertir la respuesta de OpenAI al formato estándar de evaluación de imágenes de Qualidot
# 5. Manejar errores y excepciones adecuadamente
raise NotImplementedError("La función evaluate_with_openai aún no está implementada.")
async def evaluate_with_clarifai(image_request: ImageRequestFile, rubric: ImageEvaluationRubric, prompt: str) -> StandardImageAnalysisResult: async def evaluate_with_clarifai(image_request: ImageRequestFile, rubric: ImageEvaluationRubric, prompt: str) -> StandardImageAnalysisResult:
""" """
@@ -150,13 +152,54 @@ async def evaluate_with_clarifai(image_request: ImageRequestFile, rubric: ImageE
async def evaluate_with_claude(image_request: ImageRequestFile, prompt: str) -> StandardImageAnalysisResult: async def evaluate_with_claude(image_request: ImageRequestFile, prompt: str) -> StandardImageAnalysisResult:
""" """
Función de adaptador para evaluar imágenes usando Claude. Función de adaptador para evaluar imágenes usando Claude.
(Plantilla para futuras implementaciones)
""" """
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
image_bytes = await image_request.file.read()
base64_image = encode_image_from_bytes(image_bytes)
media_type = image_request.file.content_type
if media_type not in ["image/jpeg", "image/png", "image/gif", "image/webp"]:
raise ValueError(f"Tipo de imagen no soportado por Anthropic: {media_type}")
try:
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": media_type,
"data": base64_image
},
},
{"type": "text", "text": prompt}
],
}
]
response = client.messages.create(
model=image_request.model,
max_tokens=1024,
messages=messages,
)
json_string = response.content[0].text
parsed_data = json.loads(json_string)
return StandardImageAnalysisResult(
status="success",
original_filename=image_request.file.filename,
provider_used="Claude",
model_used=image_request.model,
**parsed_data
)
# PASOS A SEGUIR PARA IMPLEMENTAR LA LÓGICA DE EVALUACIÓN CON CLAUDE: except Exception as e:
# 1. Validar la imagen de entrada (tamaño, formato, etc.) raise HTTPException(
# 2. Configurar el cliente de Claude con la clave API status_code=500,
# 3. Llamar a la API de Claude para evaluar la imagen detail=f"Error evaluando la imagen: {str(e)}"
# 4. Convertir la respuesta de Claude al formato estándar de evaluación de imágenes de Qualidot )
# 5. Manejar errores y excepciones adecuadamente
raise NotImplementedError("La función evaluate_with_claude aún no está implementada.")

View File

@@ -8,7 +8,7 @@ procesamiento y análisis.
import json import json
from app.schemas.image_standard import StandardImageAnalysisResult, ImageEvaluationRubric from app.schemas.image_standard import StandardImageAnalysis, ImageEvaluationRubric
def build_image_evaluation_prompt(rubric: ImageEvaluationRubric) -> str: def build_image_evaluation_prompt(rubric: ImageEvaluationRubric) -> str:
""" """
@@ -20,7 +20,7 @@ def build_image_evaluation_prompt(rubric: ImageEvaluationRubric) -> str:
rubric_json = rubric.model_dump_json(exclude_none=True, indent=2) rubric_json = rubric.model_dump_json(exclude_none=True, indent=2)
# 2. Extraemos el esquema dinámico de salida basado en Pydantic # 2. Extraemos el esquema dinámico de salida basado en Pydantic
expected_output_schema = json.dumps(StandardImageAnalysisResult.model_json_schema(), indent=2) expected_output_schema = json.dumps(StandardImageAnalysis.model_json_schema(), indent=2)
# 3. Obtenemos el path de especialización # 3. Obtenemos el path de especialización
# Si por alguna razón viene vacío, le damos un rol genérico por defecto # Si por alguna razón viene vacío, le damos un rol genérico por defecto

View File

@@ -25,4 +25,5 @@ numpy
openai openai
langchain langchain
langchain-openai langchain-openai
assemblyai assemblyai
anthropic