✅ 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:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user