Versión 1.0

This commit is contained in:
lansan69
2026-03-31 20:00:04 -06:00
parent bc513ad2c4
commit e882442f8b
30 changed files with 778 additions and 89 deletions

View File

@@ -24,6 +24,12 @@ from app.core import config
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 anthropic import Anthropic
import re
# Importaciones de Clarifai
from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2
# 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:
@@ -91,62 +97,77 @@ async def evaluate_with_openai(image_request: ImageRequestFile, prompt: str) ->
detail=f"Error evaluando la imagen: {str(e)}"
)
async def evaluate_with_clarifai(image_request: ImageRequestFile, rubric: ImageEvaluationRubric, prompt: str) -> StandardImageAnalysisResult:
async def evaluate_with_clarifai(image_request: ImageRequestFile, prompt: str) -> StandardImageAnalysisResult:
"""
Función de adaptador para evaluar imágenes usando Clarifai con un modelo Multimodal.
Función de adaptador para evaluar imágenes usando Clarifai.
"""
try:
# 1. Obtener el token de configuración (PAT)
pat = settings.CLARIFAI_API_KEY
if not pat:
raise ValueError("La clave CLARIFAI_API_KEY no está configurada en el entorno.")
# 2. Obtener la URL del modelo enviada en la petición
model_url = image_request.model
CLARIFAI_API_KEY = settings.CLARIFAI_API_KEY
if not CLARIFAI_API_KEY:
raise HTTPException(
status_code=500,
detail="No se encontró CLARIFAI_API_KEY en las variables de entorno"
)
# Inicializar el modelo de Clarifai
model = Model(url=model_url, pat=pat)
# 3. Leer los bytes de la imagen subida
USER_ID = "openai"
APP_ID = "chat-completion"
try:
image_bytes = await image_request.file.read()
if not image_bytes:
raise ValueError("El archivo de imagen recibido está vacío.")
# 2. Preparamos y ejecutamos la llamada a Clarifai (gRPC)
channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)
metadata = (('authorization', 'Key ' + CLARIFAI_API_KEY),)
userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID, app_id=APP_ID)
# 4. Preparar el input multimodal para Clarifai combinando la imagen y el prompt de evaluación
multimodal_input = Inputs.get_multimodal_input(
input_id="image_evaluation",
image_bytes=image_bytes,
raw_text=prompt
request = service_pb2.PostModelOutputsRequest(
user_app_id=userDataObject,
model_id=image_request.model,
inputs=[
resources_pb2.Input(
data=resources_pb2.Data(
image=resources_pb2.Image(base64=image_bytes),
text=resources_pb2.Text(raw=prompt)
)
)
]
)
# 5. Llamar a la API de Clarifai para evaluar la imagen
predict_response = model.predict([multimodal_input])
# Extraer el texto crudo de la respuesta del modelo
raw_output = predict_response.outputs[0].data.text.raw
# 6. Limpiar la respuesta y convertirla a JSON
# Los LLMs suelen devolver el JSON envuelto en bloques de markdown (```json ... ```)
clean_json = raw_output.replace("```json", "").replace("```", "").strip()
response = stub.PostModelOutputs(request, metadata=metadata)
# Convertir el string limpio a un diccionario de Python
parsed_data = json.loads(clean_json)
if response.status.code != status_code_pb2.SUCCESS:
raise Exception(f"Clarifai Error: {response.status.description}")
# 3. Extraemos la respuesta cruda
raw_output = response.outputs[0].data.text.raw
# 7. Retornar el resultado validado contra tu esquema estándar de Pydantic
return StandardImageAnalysisResult(**parsed_data)
except json.JSONDecodeError as e:
# Error específico si el modelo alucinó texto extra y no devolvió un JSON válido
raise HTTPException(
status_code=500,
detail=f"El modelo de Clarifai no devolvió un JSON válido. Error: {str(e)} | Respuesta cruda: {raw_output if 'raw_output' in locals() else 'N/A'}"
# 4. Limpiamos y parseamos el JSON devuelto
json_match = re.search(r'\{.*\}', raw_output, re.DOTALL)
if json_match:
clean_json = json_match.group(0)
parsed_data = json.loads(clean_json)
else:
# Respaldo en caso de que el modelo devuelva texto plano en lugar de JSON
parsed_data = {"raw_response": raw_output}
# 5. Retornamos el modelo estandarizado
return StandardImageAnalysisResult(
status="success",
original_filename=image_request.file.filename,
provider_used="Clarifai",
model_used=image_request.model,
**parsed_data
)
except Exception as e:
# Captura cualquier otro error (problemas de red, token inválido, URL incorrecta, etc.)
raise HTTPException(
status_code=500,
detail=f"Error interno al evaluar con Clarifai: {str(e)}"
status_code=500,
detail=f"Error evaluando la imagen con Clarifai: {str(e)}"
)
async def evaluate_with_claude(image_request: ImageRequestFile, prompt: str) -> StandardImageAnalysisResult: