diff --git a/backend/app/main.py b/backend/app/main.py index 8d351b3..f80731b 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -204,7 +204,7 @@ def send_completed_inspection_to_n8n(inspection, db): # No lanzamos excepción para no interrumpir el flujo normal -BACKEND_VERSION = "1.0.83" +BACKEND_VERSION = "1.0.84" app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION) # S3/MinIO configuration @@ -2582,7 +2582,8 @@ Responde SOLO en formato JSON válido (sin markdown, sin ```json): "status": "ok", "observations": "Describe lo que observas en la imagen en relación a la tarea solicitada", "recommendation": "Acción sugerida basada en lo observado", - "confidence": 0.85 + "confidence": 0.85, + "context_match": true }} VALORES DE STATUS: @@ -2590,7 +2591,13 @@ VALORES DE STATUS: - "minor": Presenta observaciones menores o advertencias - "critical": Presenta problemas graves o no cumple con lo esperado -IMPORTANTE: Si la tarea requiere verificar funcionamiento (algo encendido, prendido, activo) pero la imagen muestra el componente apagado o en reposo, usa status "critical" e indica en "recommendation" que se necesita una foto con el componente funcionando o un video.""" +VALOR DE CONTEXT_MATCH: +- true: La imagen SÍ corresponde al contexto de la pregunta/tarea +- false: La imagen NO corresponde (ej: pregunta sobre luces pero muestra motor) + +IMPORTANTE: +- Si la imagen NO corresponde al contexto de la pregunta, establece context_match=false y en observations indica qué se esperaba ver vs qué se muestra +- Si la tarea requiere verificar funcionamiento (algo encendido, prendido, activo) pero la imagen muestra el componente apagado o en reposo, usa status "critical" y context_match=false, indica en "recommendation" que se necesita una foto con el componente funcionando o un video.""" user_message = f"Pregunta de inspección: {question_obj.text}\n\nAnaliza esta imagen según la tarea especificada." else: @@ -2611,15 +2618,16 @@ Sé directo y enfócate solo en lo que la pregunta solicita. Considera el kilometraje y características del vehículo para contextualizar tu análisis. VALIDACIÓN DE IMAGEN: -- Si la imagen NO corresponde al contexto de la pregunta, indica en "recommendation" que deben cambiar la foto -- Si la imagen es borrosa o no permite análisis, indica en "recommendation" que tomen otra foto más clara +- Si la imagen NO corresponde al contexto de la pregunta, establece context_match=false y explica en observations qué se esperaba vs qué se muestra +- Si la imagen es borrosa o no permite análisis, establece context_match=false e indica en recommendation que tomen otra foto más clara Responde SOLO en formato JSON válido (sin markdown, sin ```json): {{ "status": "ok", "observations": "Respuesta técnica específica a: {question_text}", "recommendation": "Acción técnica recomendada o mensaje si la foto no es apropiada", - "confidence": 0.85 + "confidence": 0.85, + "context_match": true }} NOTA IMPORTANTE sobre el campo "status": @@ -2627,6 +2635,10 @@ NOTA IMPORTANTE sobre el campo "status": - Usa "minor" si hay problemas leves que requieren atención pero no son críticos - Usa "critical" si hay problemas graves que requieren reparación inmediata +VALOR DE CONTEXT_MATCH: +- true: La imagen SÍ corresponde y es apropiada para responder la pregunta +- false: La imagen NO corresponde al contexto de la pregunta (ej: pregunta sobre luces pero imagen muestra motor) + RECUERDA: - Responde SOLO lo que la pregunta pide - No des información genérica del vehículo @@ -2647,16 +2659,20 @@ Analiza la imagen y proporciona: 2. Nivel de criticidad (ok/minor/critical) 3. Observaciones técnicas breves 4. Recomendación de acción +5. Si la imagen corresponde al contexto automotriz Responde SOLO en formato JSON válido (sin markdown, sin ```json): {{ "status": "ok", "observations": "descripción técnica del componente", "recommendation": "acción sugerida", - "confidence": 0.85 + "confidence": 0.85, + "context_match": true }} -NOTA: "status" debe ser "ok" (bueno), "minor" (problemas leves) o "critical" (problemas graves).""" +NOTA: +- "status" debe ser "ok" (bueno), "minor" (problemas leves) o "critical" (problemas graves) +- "context_match" debe ser true si la imagen muestra un componente vehicular relevante, false si no corresponde.""" user_message = "Analiza este componente del vehículo para la inspección general." print(f"\n🤖 PROMPT ENVIADO AL AI:") diff --git a/frontend/package.json b/frontend/package.json index 140a7d4..83691a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "checklist-frontend", "private": true, - "version": "1.0.83", + "version": "1.0.84", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index eb03df6..3f43205 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4444,6 +4444,57 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl if (analyses.length > 0) { console.log(`✅ Análisis IA guardado (${analyses.length} análisis)`) console.log(`📝 Las observaciones quedan para que el mecánico las escriba manualmente`) + + // Verificar si alguna imagen no corresponde al contexto + const invalidImages = [] + analyses.forEach((analysis, idx) => { + if (analysis.analysis) { + // Verificar si la IA indica que la imagen no corresponde + const obs = analysis.analysis.observations?.toLowerCase() || '' + const isInvalid = + obs.includes('no corresponde') || + obs.includes('no coincide') || + obs.includes('imagen incorrecta') || + obs.includes('no es relevante') || + obs.includes('no relacionad') || + analysis.analysis.context_match === false + + if (isInvalid) { + invalidImages.push({ + index: idx + 1, + fileName: analysis.fileName, + reason: analysis.analysis.observations + }) + } + } + }) + + // Mostrar advertencia si hay imágenes que no corresponden + if (invalidImages.length > 0) { + let warningMsg = '⚠️ ATENCIÓN: Se detectaron imágenes que podrían NO corresponder al contexto:\n\n' + invalidImages.forEach(img => { + warningMsg += `📸 Imagen ${img.index}: ${img.reason}\n\n` + }) + warningMsg += '¿Deseas reemplazar estas imágenes?\n\n' + warningMsg += 'Presiona OK para eliminar las imágenes incorrectas y cargar nuevas.\n' + warningMsg += 'Presiona Cancelar si estás seguro de que las imágenes son correctas.' + + if (confirm(warningMsg)) { + // Eliminar las imágenes que no corresponden + const validPhotos = files.filter((_, idx) => !invalidImages.some(inv => inv.index === idx + 1)) + setAnswers(prev => ({ + ...prev, + [questionId]: { + ...(prev[questionId] || { value: '', observations: '', photos: [] }), + photos: validPhotos, + aiAnalysis: undefined, + documentsLoaded: false // Resetear para que vuelva a cargar + } + })) + alert('📸 Por favor carga nuevas imágenes que correspondan a: ' + question.text) + return // No mostrar el popup de éxito + } + } } else { console.log('ℹ️ No se generaron análisis IA, pero documentos procesados') }