Refactorizacion de logica de analisis de las preguntas con la IA y que acepte y respete las reglas de imagenes

This commit is contained in:
2025-11-26 17:58:15 -03:00
parent 6999416be9
commit 822615d2ba

View File

@@ -3343,7 +3343,14 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
} }
const handlePhotoChange = async (questionId, files) => { const handlePhotoChange = async (questionId, files) => {
const filesArray = Array.from(files) const question = questions.find(q => q.id === questionId)
let filesArray = Array.from(files)
// Validar límite de fotos
if (question.max_photos && filesArray.length > question.max_photos) {
alert(`⚠️ Solo puedes subir hasta ${question.max_photos} foto${question.max_photos > 1 ? 's' : ''} para esta pregunta`)
filesArray = filesArray.slice(0, question.max_photos)
}
// Update photos immediately // Update photos immediately
setAnswers(prev => ({ setAnswers(prev => ({
@@ -3381,9 +3388,12 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
console.log(`📝 Usando prompt personalizado: ${question.ai_prompt.substring(0, 50)}...`) console.log(`📝 Usando prompt personalizado: ${question.ai_prompt.substring(0, 50)}...`)
} }
// Analyze each photo // Analyze each photo sequentially
const analyses = [] const analyses = []
for (const file of files) { for (let i = 0; i < files.length; i++) {
const file = files[i]
console.log(`📸 Analizando foto ${i + 1} de ${files.length}: ${file.name}`)
const formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
formData.append('question_id', question.id.toString()) formData.append('question_id', question.id.toString())
@@ -3392,6 +3402,7 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
console.log(' - question_id:', question.id) console.log(' - question_id:', question.id)
console.log(' - question.text:', question.text) console.log(' - question.text:', question.text)
console.log(' - question.ai_prompt:', question.ai_prompt || 'NO TIENE') console.log(' - question.ai_prompt:', question.ai_prompt || 'NO TIENE')
console.log(' - imagen:', i + 1, 'de', files.length)
// Include inspection_id for vehicle context // Include inspection_id for vehicle context
if (inspectionId) { if (inspectionId) {
@@ -3424,8 +3435,8 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
// Check if AI analysis was successful // Check if AI analysis was successful
if (result.success && result.analysis) { if (result.success && result.analysis) {
analyses.push(result) analyses.push({ ...result, imageIndex: i + 1, fileName: file.name })
console.log('✅ Análisis IA exitoso') console.log('✅ Análisis IA exitoso para imagen', i + 1)
console.log(' - Provider:', result.provider) console.log(' - Provider:', result.provider)
console.log(' - Model:', result.model) console.log(' - Model:', result.model)
console.log(' - Status:', result.analysis.status) console.log(' - Status:', result.analysis.status)
@@ -3435,6 +3446,7 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
// Show user-friendly error // Show user-friendly error
if (result.error && result.error.includes('No AI configuration')) { if (result.error && result.error.includes('No AI configuration')) {
alert('⚙️ Por favor configura tu API key en Configuración primero.') alert('⚙️ Por favor configura tu API key en Configuración primero.')
break // Stop analyzing if no API key
} }
} }
} else { } else {
@@ -3445,65 +3457,90 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
if (analyses.length > 0) { if (analyses.length > 0) {
console.log('✅ Análisis recibidos:', analyses.length) console.log('✅ Análisis recibidos:', analyses.length)
// Process analysis results
const firstResult = analyses[0]
const analysis = firstResult.analysis
console.log('📊 Análisis completo:', analysis)
let suggestedAnswer = null let suggestedAnswer = null
let observationsText = '' let observationsText = ''
let worstStatus = 'ok' // Track the worst status across all images
if (analyses.length === 1) {
// Single image analysis
const firstResult = analyses[0]
const analysis = firstResult.analysis
console.log('📊 Análisis de imagen única:', analysis)
// Check if analysis is an object (structured JSON response) // Check if analysis is an object (structured JSON response)
if (typeof analysis === 'object' && analysis !== null) { if (typeof analysis === 'object' && analysis !== null) {
// Extract structured information // Extract structured information
const status = analysis.status || 'ok' const status = analysis.status || 'ok'
const observations = analysis.observations || '' const observations = analysis.observations || ''
const recommendation = analysis.recommendation || '' const recommendation = analysis.recommendation || ''
const confidence = analysis.confidence || 0.7 const confidence = analysis.confidence || 0.7
// Build observations text // Build observations text
observationsText = `🤖 Análisis IA (${(confidence * 100).toFixed(0)}% confianza):\n${observations}` observationsText = `🤖 Análisis IA (${(confidence * 100).toFixed(0)}% confianza):\n${observations}`
if (recommendation) { if (recommendation) {
observationsText += `\n\n💡 Recomendación: ${recommendation}` observationsText += `\n\n💡 Recomendación: ${recommendation}`
}
// Map status to answer based on question type
if (question.type === 'pass_fail') {
if (status === 'ok') {
suggestedAnswer = 'pass'
} else if (status === 'critical' || status === 'minor') {
suggestedAnswer = 'fail'
}
} else if (question.type === 'good_bad') {
if (status === 'ok') {
suggestedAnswer = 'good'
} else if (status === 'minor') {
suggestedAnswer = 'regular'
} else if (status === 'critical') {
suggestedAnswer = 'bad'
} }
worstStatus = status
} else if (typeof analysis === 'string') {
observationsText = `🤖 Análisis IA:\n${analysis}`
} }
} else if (typeof analysis === 'string') { } else {
// Fallback for plain text responses // Multiple images - summarize all analyses
observationsText = `🤖 Análisis IA:\n${analysis}` console.log('📊 Resumen de', analyses.length, 'análisis:')
const analysisLower = analysis.toLowerCase() observationsText = `🤖 Análisis IA de ${analyses.length} imágenes:\n\n`
if (question.type === 'pass_fail') { const statusPriority = { 'critical': 3, 'minor': 2, 'warning': 2, 'ok': 1 }
if (analysisLower.includes('pasa') || analysisLower.includes('correcto') || analysisLower.includes('bueno') || analysisLower.includes('ok')) { let maxPriority = 0
suggestedAnswer = 'pass'
} else if (analysisLower.includes('falla') || analysisLower.includes('incorrecto') || analysisLower.includes('problema') || analysisLower.includes('critical')) { analyses.forEach((result, index) => {
suggestedAnswer = 'fail' const analysis = result.analysis
} observationsText += `📸 Imagen ${result.imageIndex}:\n`
} else if (question.type === 'good_bad') {
if (analysisLower.includes('bueno') || analysisLower.includes('excelente')) { if (typeof analysis === 'object' && analysis !== null) {
suggestedAnswer = 'good' const status = analysis.status || 'ok'
} else if (analysisLower.includes('regular') || analysisLower.includes('aceptable')) { const observations = analysis.observations || ''
suggestedAnswer = 'regular' const confidence = analysis.confidence || 0.7
} else if (analysisLower.includes('malo') || analysisLower.includes('deficiente')) {
suggestedAnswer = 'bad' observationsText += ` Estado: ${status.toUpperCase()}`
observationsText += ` (${(confidence * 100).toFixed(0)}% confianza)\n`
observationsText += ` ${observations}\n`
// Track worst status
const priority = statusPriority[status] || 1
if (priority > maxPriority) {
maxPriority = priority
worstStatus = status
}
} else if (typeof analysis === 'string') {
observationsText += ` ${analysis}\n`
} }
observationsText += '\n'
})
// Add overall recommendation
observationsText += `📋 Resumen General:\n`
observationsText += ` Estado más crítico detectado: ${worstStatus.toUpperCase()}\n`
}
// Map worst status to answer
if (question.type === 'pass_fail') {
if (worstStatus === 'ok') {
suggestedAnswer = 'pass'
} else if (worstStatus === 'critical' || worstStatus === 'minor') {
suggestedAnswer = 'fail'
}
} else if (question.type === 'good_bad') {
if (worstStatus === 'ok') {
suggestedAnswer = 'good'
} else if (worstStatus === 'minor' || worstStatus === 'warning') {
suggestedAnswer = 'regular'
} else if (worstStatus === 'critical') {
suggestedAnswer = 'bad'
} }
} }
// In FULL mode, auto-fill the answer // In FULL mode, auto-fill the answer
if (checklist.ai_mode === 'full' && suggestedAnswer) { if (checklist.ai_mode === 'full' && suggestedAnswer) {
setAnswers(prev => ({ setAnswers(prev => ({
@@ -3513,7 +3550,7 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
value: suggestedAnswer, value: suggestedAnswer,
observations: observationsText, observations: observationsText,
photos: files, photos: files,
aiAnalysis: firstResult.analysis // Guardar análisis completo de IA aiAnalysis: analyses // Guardar todos los análisis
} }
})) }))
console.log(`🤖 FULL MODE: Respuesta auto-completada con: ${suggestedAnswer}`) console.log(`🤖 FULL MODE: Respuesta auto-completada con: ${suggestedAnswer}`)
@@ -3883,7 +3920,12 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
{currentQuestion.allow_photos && ( {currentQuestion.allow_photos && (
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
Fotografías (opcional) Fotografías *
{currentQuestion.max_photos && (
<span className="ml-2 text-xs text-gray-600">
(máximo {currentQuestion.max_photos} foto{currentQuestion.max_photos > 1 ? 's' : ''})
</span>
)}
{(checklist.ai_mode === 'assisted' || checklist.ai_mode === 'full') && ( {(checklist.ai_mode === 'assisted' || checklist.ai_mode === 'full') && (
<span className="ml-2 text-xs text-blue-600"> <span className="ml-2 text-xs text-blue-600">
🤖 Análisis IA activado 🤖 Análisis IA activado
@@ -3893,10 +3935,11 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
<input <input
type="file" type="file"
accept="image/*" accept="image/*"
multiple multiple={currentQuestion.max_photos > 1}
onChange={(e) => handlePhotoChange(currentQuestion.id, e.target.files)} onChange={(e) => handlePhotoChange(currentQuestion.id, e.target.files)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
disabled={aiAnalyzing} disabled={aiAnalyzing}
required
/> />
{aiAnalyzing && ( {aiAnalyzing && (
@@ -3941,6 +3984,11 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
{currentQuestionIndex < getVisibleQuestions().length - 1 ? ( {currentQuestionIndex < getVisibleQuestions().length - 1 ? (
<button <button
onClick={() => { onClick={() => {
// Validar que se hayan subido fotos si son obligatorias
if (currentQuestion.allow_photos && (!answers[currentQuestion.id]?.photos || answers[currentQuestion.id].photos.length === 0)) {
alert('⚠️ Debes subir al menos una fotografía para esta pregunta')
return
}
saveAnswer(currentQuestion.id) saveAnswer(currentQuestion.id)
goToQuestion(currentQuestionIndex + 1) goToQuestion(currentQuestionIndex + 1)
}} }}
@@ -3951,6 +3999,11 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
) : ( ) : (
<button <button
onClick={() => { onClick={() => {
// Validar que se hayan subido fotos si son obligatorias
if (currentQuestion.allow_photos && (!answers[currentQuestion.id]?.photos || answers[currentQuestion.id].photos.length === 0)) {
alert('⚠️ Debes subir al menos una fotografía para esta pregunta')
return
}
saveAnswer(currentQuestion.id) saveAnswer(currentQuestion.id)
proceedToSignatures() proceedToSignatures()
}} }}