Nuevo Tipo de Pregunta: Asistente IA (Chat) 🤖💬

Frontend (v1.0.88)
QuestionTypeEditor.jsx
 Nuevo tipo: ai_assistant con icono 💬
 Configuración completa:
assistant_prompt: Define rol y comportamiento del asistente
context_questions: IDs de preguntas anteriores cuyas fotos usar (o todas)
assistant_instructions: Reglas específicas de diagnóstico
max_messages: Límite de mensajes en el chat
response_length: Corta/Media/Larga
QuestionAnswerInput.jsx
 Mensaje informativo para tipo ai_assistant
 Indica que el chat se abre con botón separado
App.jsx - Modal de Chat IA
 Modal full-screen responsive con:

Header con gradiente purple/blue
Área de mensajes con scroll
Input de texto + botón enviar
Soporte Enter para enviar
Indicador de "pensando..."
Timestamps en mensajes
Confianza de la IA
Límite de mensajes
 Botón "💬 Consultar Asistente IA" al lado de "Cargar Documentos"

 Contador de mensajes en el botón si ya hay historial

 Historial guardado en answers[questionId].chatHistory

 Auto-marca como completada cuando se abre el chat

Backend (v1.0.85)
Endpoint /api/ai/chat-assistant
 Recibe:

Mensaje del usuario
Historial del chat
Fotos de preguntas anteriores con sus análisis
Configuración del asistente
Info del vehículo
 Contexto inteligente:

Información del vehículo (marca, modelo, km, placa)
Análisis previos de fotos (hasta 10 fotos más relevantes)
Instrucciones personalizadas por pregunta
Historial conversacional (últimos 10 mensajes)
 Soporte multi-proveedor:

OpenAI (GPT-4, GPT-3.5)
Google Gemini
 Respuestas adaptables:

Longitud configurable (200/400/800 tokens)
Temperature 0.7 para balance creatividad/precisión
Cómo Funciona
Configuración (Admin):

- Crear pregunta tipo "Asistente IA (Chat)"
- Definir prompt: "Eres experto en sistemas de frenos..."
- Seleccionar preguntas anteriores: "5,8,12" (fotos de frenos)
- Instrucciones: "Si pastillas < 3mm → cambio inmediato"
- Longitud: Media
- Max mensajes: 20

2 Durante Inspección (Mecánico):

- Llega a pregunta tipo AI Assistant
- Ve botón "💬 Consultar Asistente IA"
- Clic → Abre chat full-screen
- La IA YA vio las fotos de preguntas 5, 8, 12
- Mecánico pregunta: "¿Las pastillas están bien?"
- IA responde basado en fotos previas + contexto vehículo
- Conversación natural con historial
- Al cerrar → se guarda automáticamente

3 Ejemplo de Conversación:

Mecánico: "Vi desgaste en las pastillas, ¿qué recomiendas?"

IA: "Basándome en las fotos que analicé:

     Pastilla delantera izquierda: ~2.5mm de espesor
     Pastilla delantera derecha: ~2.8mm

     ⚠️ CRÍTICO: Ambas pastillas están por debajo del
     mínimo de 3mm. Con 45,000 km, es normal este desgaste.

     RECOMENDACIÓN:
     - Cambio INMEDIATO de pastillas delanteras
     - Verificar estado de discos (posible rectificado)
     - Revisar líquido de frenos (última foto muestra nivel bajo)

     ¿Necesitas el código de pieza para este Toyota Corolla 2019?"

Casos de Uso
Diagnóstico de Frenos

assistant_prompt: "Eres especialista en sistemas de frenos.
                   Analiza desgaste, fugas, vibraciones."
context_questions: "10,11,12,13" // Fotos de pastillas, discos, líquido

assistant_prompt: "Experto en motores. Detecta fugas, ruidos anormales,
                   consumo excesivo."
context_questions: "5,6,7,8,9" // Motor, aceite, correa, filtros

assistant_prompt: "Especialista en sistemas eléctricos y electrónicos."
context_questions: "20,21,22" // Batería, luces, tablero
instructions: "Siempre pedir código OBD2 si hay check engine"

Ventajas
 Contextual: La IA ve fotos previas, no pregunta "¿puedes mostrarme?"
 Especializado: Un asistente POR tema (frenos, motor, eléctrico)
 Conversacional: El mecánico puede hacer follow-up questions
 Guiado: Instrucciones específicas por tipo de inspección
 Historial: No repite info, mantiene contexto de la conversación
 Móvil-friendly: Modal responsive, fácil de usar en celular
This commit is contained in:
2025-11-30 23:23:43 -03:00
parent a692948a6f
commit 14a64778b8
6 changed files with 592 additions and 4 deletions

View File

@@ -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.84"
BACKEND_VERSION = "1.0.85"
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
# S3/MinIO configuration
@@ -2849,6 +2849,175 @@ Responde en formato JSON:
}
@app.post("/api/ai/chat-assistant")
async def chat_with_ai_assistant(
request: dict,
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user)
):
"""
Chat conversacional con IA usando contexto de fotos anteriores
El asistente tiene acceso a fotos de preguntas previas para dar mejor contexto
"""
print("\n" + "="*80)
print("🤖 AI CHAT ASSISTANT")
print("="*80)
question_id = request.get('question_id')
inspection_id = request.get('inspection_id')
user_message = request.get('user_message')
chat_history = request.get('chat_history', [])
context_photos = request.get('context_photos', [])
assistant_prompt = request.get('assistant_prompt', '')
assistant_instructions = request.get('assistant_instructions', '')
response_length = request.get('response_length', 'medium')
vehicle_info = request.get('vehicle_info', {})
print(f"📋 Question ID: {question_id}")
print(f"🚗 Inspection ID: {inspection_id}")
print(f"💬 User message: {user_message}")
print(f"📸 Context photos: {len(context_photos)} fotos")
print(f"💭 Chat history: {len(chat_history)} mensajes previos")
# Obtener configuración de IA
ai_config = db.query(models.AIConfiguration).filter(
models.AIConfiguration.is_active == True
).first()
if not ai_config:
return {
"success": False,
"response": "No hay configuración de IA activa. Por favor configura en Settings.",
"confidence": 0
}
try:
# Construir el contexto del vehículo
vehicle_context = f"""
INFORMACIÓN DEL VEHÍCULO:
- Marca: {vehicle_info.get('brand', 'N/A')}
- Modelo: {vehicle_info.get('model', 'N/A')}
- Placa: {vehicle_info.get('plate', 'N/A')}
- Kilometraje: {vehicle_info.get('km', 'N/A')} km
"""
# Construir el contexto de las fotos anteriores
photos_context = ""
if context_photos:
photos_context = f"\n\nFOTOS ANALIZADAS PREVIAMENTE ({len(context_photos)} imágenes):\n"
for idx, photo in enumerate(context_photos[:10], 1): # Limitar a 10 fotos
ai_analysis = photo.get('aiAnalysis', [])
if ai_analysis and len(ai_analysis) > 0:
analysis_text = ai_analysis[0].get('analysis', {})
obs = analysis_text.get('observations', 'Sin análisis')
status = analysis_text.get('status', 'unknown')
photos_context += f"\n{idx}. Pregunta ID {photo.get('questionId')}: Status={status}\n Observaciones: {obs[:200]}...\n"
# Definir la longitud de respuesta
max_tokens_map = {
'short': 200,
'medium': 400,
'long': 800
}
max_tokens = max_tokens_map.get(response_length, 400)
# Construir el system prompt
base_prompt = assistant_prompt or "Eres un experto mecánico automotriz que ayuda a diagnosticar problemas."
system_prompt = f"""{base_prompt}
{vehicle_context}
{photos_context}
INSTRUCCIONES ADICIONALES:
{assistant_instructions if assistant_instructions else "Sé técnico, claro y directo en tus respuestas."}
FORMATO DE RESPUESTA:
- Sé {response_length} en tus respuestas
- Usa lenguaje técnico pero comprensible
- Si ves algo preocupante en las fotos analizadas, menciónalo
- Proporciona recomendaciones específicas cuando sea relevante
- Si no tienes suficiente información, pide más detalles
"""
# Construir el historial de mensajes para la IA
messages = [{"role": "system", "content": system_prompt}]
# Agregar historial previo (últimos 10 mensajes para no saturar)
for msg in chat_history[-10:]:
messages.append({
"role": msg.get('role'),
"content": msg.get('content')
})
# Agregar el mensaje actual del usuario
messages.append({
"role": "user",
"content": user_message
})
print(f"🔧 Enviando a {ai_config.provider} con {len(messages)} mensajes")
# Llamar a la IA según el proveedor
if ai_config.provider == 'openai':
from openai import OpenAI
client = OpenAI(api_key=ai_config.api_key)
response = client.chat.completions.create(
model=ai_config.model or "gpt-4",
messages=messages,
max_tokens=max_tokens,
temperature=0.7
)
ai_response = response.choices[0].message.content
confidence = 0.85 # OpenAI no devuelve confidence directo
elif ai_config.provider == 'gemini':
import google.generativeai as genai
genai.configure(api_key=ai_config.api_key)
model = genai.GenerativeModel(ai_config.model or 'gemini-pro')
# Gemini maneja el chat diferente
# Convertir mensajes al formato de Gemini
chat_content = ""
for msg in messages[1:]: # Skip system message
role_label = "Usuario" if msg['role'] == 'user' else "Asistente"
chat_content += f"\n{role_label}: {msg['content']}\n"
full_prompt = f"{system_prompt}\n\nCONVERSACIÓN:\n{chat_content}\n\nAsistente:"
response = model.generate_content(full_prompt)
ai_response = response.text
confidence = 0.80
else:
raise ValueError(f"Proveedor no soportado: {ai_config.provider}")
print(f"✅ Respuesta generada: {len(ai_response)} caracteres")
return {
"success": True,
"response": ai_response,
"confidence": confidence,
"provider": ai_config.provider,
"model": ai_config.model
}
except Exception as e:
print(f"❌ Error en chat IA: {e}")
import traceback
traceback.print_exc()
return {
"success": False,
"response": f"Error al comunicarse con el asistente: {str(e)}",
"confidence": 0
}
# ============= REPORTS =============
@app.get("/api/reports/dashboard", response_model=schemas.DashboardData)
def get_dashboard_data(

View File

@@ -1,7 +1,7 @@
{
"name": "checklist-frontend",
"private": true,
"version": "1.0.87",
"version": "1.0.88",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,6 +1,6 @@
// Service Worker para PWA con detección de actualizaciones
// IMPORTANTE: Actualizar esta versión cada vez que se despliegue una nueva versión
const CACHE_NAME = 'ayutec-v1.0.87';
const CACHE_NAME = 'ayutec-v1.0.88';
const urlsToCache = [
'/',
'/index.html'

View File

@@ -4054,6 +4054,11 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)
const [aiAnalyzing, setAiAnalyzing] = useState(false)
// AI Assistant Chat
const [showAIChat, setShowAIChat] = useState(false)
const [aiChatMessages, setAiChatMessages] = useState([])
const [aiChatLoading, setAiChatLoading] = useState(false)
// Signature canvas
const mechanicSigRef = useRef(null)
@@ -5031,6 +5036,29 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl
)}
</button>
)}
{/* Botón para abrir chat IA (si es tipo ai_assistant) */}
{currentQuestion.options?.type === 'ai_assistant' && (
<button
type="button"
onClick={() => {
setShowAIChat(true)
// Cargar historial si existe
if (answers[currentQuestion.id]?.chatHistory) {
setAiChatMessages(answers[currentQuestion.id].chatHistory)
}
}}
className="w-full mt-3 px-4 py-3 bg-gradient-to-r from-purple-600 to-blue-600 text-white rounded-lg hover:from-purple-700 hover:to-blue-700 transition flex items-center justify-center gap-2 font-semibold shadow-lg"
>
<span>💬</span>
<span>Consultar Asistente IA</span>
{answers[currentQuestion.id]?.chatHistory?.length > 0 && (
<span className="ml-1 px-2 py-0.5 bg-white/20 rounded-full text-xs">
{answers[currentQuestion.id].chatHistory.length} mensajes
</span>
)}
</button>
)}
</div>
)}
@@ -5171,6 +5199,262 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl
)}
</div>
</div>
{/* Modal de Chat IA Asistente */}
{showAIChat && currentQuestion && (
<AIAssistantChatModal
question={currentQuestion}
inspection={{ id: inspectionId, ...vehicleData }}
allAnswers={answers}
messages={aiChatMessages}
setMessages={setAiChatMessages}
loading={aiChatLoading}
setLoading={setAiChatLoading}
onClose={() => {
setShowAIChat(false)
// Guardar historial del chat en la respuesta
setAnswers(prev => ({
...prev,
[currentQuestion.id]: {
...(prev[currentQuestion.id] || {}),
chatHistory: aiChatMessages,
value: 'chat_completed' // Marcar como respondida
}
}))
saveAnswer(currentQuestion.id)
}}
/>
)}
</div>
)
}
// Componente Modal de Chat IA Asistente
function AIAssistantChatModal({ question, inspection, allAnswers, messages, setMessages, loading, setLoading, onClose }) {
const [inputMessage, setInputMessage] = useState('')
const chatEndRef = useRef(null)
const config = question.options || {}
// Auto-scroll al final
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
// Enviar mensaje al asistente
const sendMessage = async () => {
if (!inputMessage.trim() || loading) return
const userMessage = { role: 'user', content: inputMessage, timestamp: new Date().toISOString() }
setMessages(prev => [...prev, userMessage])
setInputMessage('')
setLoading(true)
try {
const token = localStorage.getItem('token')
const API_URL = import.meta.env.VITE_API_URL || ''
// Recopilar fotos de preguntas anteriores según configuración
const contextPhotos = []
const contextQuestionIds = config.context_questions
? config.context_questions.split(',').map(id => parseInt(id.trim()))
: Object.keys(allAnswers).map(id => parseInt(id))
// Filtrar solo preguntas anteriores a la actual
const previousQuestionIds = contextQuestionIds.filter(id => id < question.id)
previousQuestionIds.forEach(qId => {
const answer = allAnswers[qId]
if (answer?.photos && answer.photos.length > 0) {
answer.photos.forEach(photoUrl => {
contextPhotos.push({
questionId: qId,
url: photoUrl,
aiAnalysis: answer.aiAnalysis
})
})
}
})
// Preparar el payload
const payload = {
question_id: question.id,
inspection_id: inspection.id,
user_message: inputMessage,
chat_history: messages,
context_photos: contextPhotos,
assistant_prompt: config.assistant_prompt || '',
assistant_instructions: config.assistant_instructions || '',
response_length: config.response_length || 'medium',
vehicle_info: {
brand: inspection.vehicle_brand,
model: inspection.vehicle_model,
plate: inspection.vehicle_plate,
km: inspection.vehicle_km
}
}
console.log('📤 Enviando a chat IA:', payload)
const response = await fetch(`${API_URL}/api/ai/chat-assistant`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
if (!response.ok) {
throw new Error('Error en respuesta del servidor')
}
const data = await response.json()
console.log('📥 Respuesta de IA:', data)
const assistantMessage = {
role: 'assistant',
content: data.response,
timestamp: new Date().toISOString(),
confidence: data.confidence
}
setMessages(prev => [...prev, assistantMessage])
} catch (error) {
console.error('Error al enviar mensaje:', error)
const errorMessage = {
role: 'assistant',
content: '❌ Error al comunicarse con el asistente. Por favor intenta nuevamente.',
timestamp: new Date().toISOString(),
isError: true
}
setMessages(prev => [...prev, errorMessage])
} finally {
setLoading(false)
}
}
return (
<div className="fixed inset-0 bg-black/80 flex items-center justify-center z-[100] p-2 sm:p-4">
<div className="bg-white rounded-xl w-full max-w-4xl max-h-[95vh] flex flex-col shadow-2xl">
{/* Header */}
<div className="bg-gradient-to-r from-purple-600 to-blue-600 text-white p-4 sm:p-6 rounded-t-xl">
<div className="flex items-start justify-between gap-3">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-2">
<span className="text-3xl">💬</span>
<h3 className="text-lg sm:text-xl font-bold">Asistente IA</h3>
</div>
<p className="text-sm sm:text-base text-purple-100 line-clamp-2">
{question.text}
</p>
<div className="mt-2 flex flex-wrap gap-2 text-xs">
<span className="px-2 py-1 bg-white/20 rounded">
{Object.values(allAnswers).filter(a => a.photos?.length > 0).length} preguntas con fotos
</span>
<span className="px-2 py-1 bg-white/20 rounded">
{messages.length} mensajes
</span>
</div>
</div>
<button
onClick={onClose}
className="text-white/80 hover:text-white text-3xl flex-shrink-0"
>
×
</button>
</div>
</div>
{/* Chat Messages */}
<div className="flex-1 overflow-y-auto p-4 sm:p-6 space-y-4 bg-gray-50">
{messages.length === 0 && (
<div className="text-center py-12">
<div className="text-6xl mb-4">🤖</div>
<p className="text-gray-600 text-sm sm:text-base mb-2">
¡Hola! Soy tu asistente técnico.
</p>
<p className="text-gray-500 text-xs sm:text-sm">
He analizado las fotos anteriores. ¿En qué puedo ayudarte?
</p>
</div>
)}
{messages.map((msg, idx) => (
<div
key={idx}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[85%] sm:max-w-[75%] rounded-lg p-3 sm:p-4 ${
msg.role === 'user'
? 'bg-blue-600 text-white'
: msg.isError
? 'bg-red-50 border border-red-200 text-red-800'
: 'bg-white border border-gray-200 text-gray-800'
}`}
>
<div className="text-sm sm:text-base whitespace-pre-wrap break-words">
{msg.content}
</div>
<div
className={`text-xs mt-2 ${
msg.role === 'user' ? 'text-blue-100' : 'text-gray-400'
}`}
>
{new Date(msg.timestamp).toLocaleTimeString('es-ES', {
hour: '2-digit',
minute: '2-digit'
})}
{msg.confidence && (
<span className="ml-2"> Confianza: {Math.round(msg.confidence * 100)}%</span>
)}
</div>
</div>
</div>
))}
{loading && (
<div className="flex justify-start">
<div className="bg-white border border-gray-200 rounded-lg p-4">
<div className="flex items-center gap-2 text-gray-600">
<div className="animate-spin h-4 w-4 border-2 border-purple-600 border-t-transparent rounded-full"></div>
<span className="text-sm">El asistente está pensando...</span>
</div>
</div>
</div>
)}
<div ref={chatEndRef} />
</div>
{/* Input */}
<div className="border-t p-3 sm:p-4 bg-white rounded-b-xl">
<div className="flex gap-2">
<input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && !e.shiftKey && sendMessage()}
placeholder="Escribe tu pregunta..."
disabled={loading}
className="flex-1 px-3 sm:px-4 py-2 sm:py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 text-sm sm:text-base disabled:bg-gray-100"
/>
<button
onClick={sendMessage}
disabled={!inputMessage.trim() || loading}
className="px-4 sm:px-6 py-2 sm:py-3 bg-gradient-to-r from-purple-600 to-blue-600 text-white rounded-lg hover:from-purple-700 hover:to-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition text-sm sm:text-base font-semibold"
>
Enviar
</button>
</div>
{config.max_messages && messages.length >= config.max_messages && (
<p className="text-xs text-amber-600 mt-2">
Has alcanzado el límite de {config.max_messages} mensajes
</p>
)}
</div>
</div>
</div>
)
}

View File

@@ -292,6 +292,25 @@ export function QuestionAnswerInput({ question, value, onChange, onSave }) {
)
}
// AI_ASSISTANT (Chat con IA)
if (questionType === 'ai_assistant') {
return (
<div className="p-4 bg-gradient-to-r from-purple-50 to-blue-50 border-2 border-purple-200 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<span className="text-2xl">💬</span>
<h4 className="font-semibold text-purple-900">Asistente IA Disponible</h4>
</div>
<p className="text-sm text-purple-700 mb-2">
Haz clic en el botón "💬 Consultar Asistente" debajo para abrir el chat con IA.
El asistente ha analizado las fotos anteriores y está listo para ayudarte.
</p>
<div className="text-xs text-purple-600 bg-white/50 rounded px-2 py-1">
No requiere respuesta manual - el chat se guarda automáticamente
</div>
</div>
)
}
// Fallback para tipos desconocidos
return (
<div className="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">

View File

@@ -23,7 +23,8 @@ const QUESTION_TYPES = [
{ value: 'number', label: '🔢 Número', icon: '#️⃣' },
{ value: 'date', label: '📅 Fecha', icon: '📆' },
{ value: 'time', label: '🕐 Hora', icon: '⏰' },
{ value: 'photo_only', label: '📸 Solo Fotografía', icon: '📷' }
{ value: 'photo_only', label: '📸 Solo Fotografía', icon: '📷' },
{ value: 'ai_assistant', label: '🤖 Asistente IA (Chat)', icon: '💬' }
]
const STATUS_OPTIONS = [
@@ -512,6 +513,121 @@ export function QuestionTypeEditor({ value, onChange, maxPoints = 1 }) {
</div>
</div>
)}
{/* AI ASSISTANT (CHAT) */}
{config.type === 'ai_assistant' && (
<div className="space-y-4">
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div className="flex items-start gap-3">
<span className="text-3xl">💬</span>
<div>
<h4 className="font-semibold text-blue-900 mb-1">Asistente IA Conversacional</h4>
<p className="text-sm text-blue-700">
El mecánico podrá chatear con IA usando fotos de preguntas anteriores como contexto.
Configura qué preguntas anteriores usar y el comportamiento del asistente.
</p>
</div>
</div>
</div>
{/* Prompt del asistente */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
🎯 Prompt del Asistente (Rol y Comportamiento)
</label>
<textarea
value={config.assistant_prompt || ''}
onChange={(e) => updateConfig({ assistant_prompt: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 font-mono text-sm"
rows="6"
placeholder="Ejemplo: Eres un experto mecánico especializado en sistemas de frenos. Ayuda al mecánico a diagnosticar problemas basándote en las fotos que has visto. Sé directo y técnico. Si ves algo anormal en las fotos, menciónalo proactivamente."
/>
<p className="text-xs text-gray-500 mt-1">
Define cómo debe comportarse el asistente: su rol, tono, especialidad, etc.
</p>
</div>
{/* Preguntas de contexto */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
📸 Preguntas Anteriores para Contexto
</label>
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3 mb-3">
<p className="text-xs text-yellow-800">
<strong>💡 Cómo funciona:</strong> El asistente verá las fotos de las preguntas que selecciones abajo.
Elige preguntas cuyas fotos sean relevantes para el diagnóstico (ej: si es asistente de frenos, selecciona preguntas sobre pastillas, discos, líquido de frenos, etc.)
</p>
</div>
<textarea
value={config.context_questions || ''}
onChange={(e) => updateConfig({ context_questions: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 text-sm"
rows="3"
placeholder="IDs de preguntas separados por comas. Ejemplo: 5,8,12,15
Dejar vacío para usar TODAS las preguntas anteriores con fotos."
/>
<p className="text-xs text-gray-500 mt-1">
Especifica los IDs de preguntas anteriores cuyas fotos debe analizar el asistente, o déjalo vacío para usar todas.
</p>
</div>
{/* Instrucciones adicionales */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
📋 Instrucciones Adicionales (Opcional)
</label>
<textarea
value={config.assistant_instructions || ''}
onChange={(e) => updateConfig({ assistant_instructions: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 text-sm"
rows="4"
placeholder="Instrucciones específicas adicionales.
Ejemplo:
- Si detectas pastillas con menos de 3mm, recomienda cambio inmediato
- Siempre verifica si hay fugas de líquido
- Menciona el código OBD2 si es relevante"
/>
<p className="text-xs text-gray-500 mt-1">
Reglas o criterios específicos que el asistente debe seguir al dar consejos.
</p>
</div>
{/* Configuración de respuestas */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
💬 Mensajes Máximos
</label>
<input
type="number"
min="1"
max="50"
value={config.max_messages || 20}
onChange={(e) => updateConfig({ max_messages: parseInt(e.target.value) })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
/>
<p className="text-xs text-gray-500 mt-1">
Límite de mensajes en el chat
</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
📏 Longitud de Respuesta
</label>
<select
value={config.response_length || 'medium'}
onChange={(e) => updateConfig({ response_length: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
>
<option value="short">Corta (concisa)</option>
<option value="medium">Media (balanceada)</option>
<option value="long">Larga (detallada)</option>
</select>
</div>
</div>
</div>
)}
</div>
</div>
)