diff --git a/backend/app/main.py b/backend/app/main.py index a288e80..0180d23 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1029,6 +1029,7 @@ def delete_ai_configuration( async def analyze_image( file: UploadFile = File(...), question_id: int = None, + custom_prompt: str = None, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user) ): @@ -1061,12 +1062,37 @@ async def analyze_image( try: # Construir prompt dinámico basado en la pregunta específica if question_obj: - # Prompt altamente específico para la pregunta - question_text = question_obj.text - question_type = question_obj.type - section = question_obj.section - - system_prompt = f"""Eres un mecánico experto realizando una inspección vehicular. + # Usar prompt personalizado si está disponible + if custom_prompt: + # Prompt 100% personalizado por el administrador + system_prompt = f"""Eres un mecánico experto realizando una inspección vehicular. + +INSTRUCCIONES ESPECÍFICAS PARA ESTA PREGUNTA: +{custom_prompt} + +PREGUNTA A RESPONDER: "{question_obj.text}" +Sección: {question_obj.section} + +Analiza la imagen siguiendo EXACTAMENTE las instrucciones proporcionadas arriba. + +VALIDACIÓN DE IMAGEN: +- Si la imagen NO corresponde al contexto de la pregunta (por ejemplo, si piden luces pero muestran motor), indica en "recommendation" que deben cambiar la foto +- Si la imagen es borrosa, oscura o no permite análisis, indica en "recommendation" que tomen otra foto más clara + +Responde en formato JSON: +{{ + "status": "ok|minor|critical", + "observations": "Análisis específico según el prompt personalizado", + "recommendation": "Si la imagen no es apropiada, indica 'Por favor tome una foto de [componente correcto]'. Si es apropiada, da recomendación técnica.", + "confidence": 0.0-1.0 +}}""" + else: + # Prompt altamente específico para la pregunta + question_text = question_obj.text + question_type = question_obj.type + section = question_obj.section + + system_prompt = f"""Eres un mecánico experto realizando una inspección vehicular. PREGUNTA ESPECÍFICA A RESPONDER: "{question_text}" Sección: {section} @@ -1074,11 +1100,15 @@ Sección: {section} Analiza la imagen ÚNICAMENTE para responder esta pregunta específica. Sé directo y enfócate solo en lo que la pregunta solicita. +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 + Responde en formato JSON: {{ "status": "ok|minor|critical", "observations": "Respuesta específica a: {question_text}", - "recommendation": "Acción si aplica", + "recommendation": "Si la imagen no es apropiada, indica 'Por favor tome una foto de [componente correcto]'. Si es apropiada, da acción técnica si aplica.", "confidence": 0.0-1.0 }} @@ -1089,7 +1119,7 @@ IMPORTANTE: - Si la pregunta es pass/fail, indica claramente si pasa o falla - Si la pregunta es bueno/regular/malo, indica el estado específico del componente""" - user_message = f"Inspecciona la imagen y responde específicamente: {question_text}" + user_message = f"Inspecciona la imagen y responde específicamente: {question_obj.text}" else: # Fallback para análisis general system_prompt = """Eres un experto mecánico automotriz. Analiza la imagen y proporciona: diff --git a/backend/app/models.py b/backend/app/models.py index 653e7a5..40522eb 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -76,6 +76,9 @@ class Question(Base): parent_question_id = Column(Integer, ForeignKey("questions.id"), nullable=True) show_if_answer = Column(String(50), nullable=True) # Valor que dispara esta pregunta + # AI Analysis + ai_prompt = Column(Text, nullable=True) # Prompt personalizado para análisis de IA de esta pregunta + created_at = Column(DateTime(timezone=True), server_default=func.now()) # Relationships diff --git a/backend/app/schemas.py b/backend/app/schemas.py index c806b18..7693ad5 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -99,6 +99,7 @@ class QuestionBase(BaseModel): requires_comment_on_fail: bool = False parent_question_id: Optional[int] = None show_if_answer: Optional[str] = None + ai_prompt: Optional[str] = None class QuestionCreate(QuestionBase): checklist_id: int diff --git a/backend/migrate_ai_prompt.py b/backend/migrate_ai_prompt.py new file mode 100644 index 0000000..bd717c2 --- /dev/null +++ b/backend/migrate_ai_prompt.py @@ -0,0 +1,32 @@ +""" +Migration: Add ai_prompt column to questions table +Date: 2025-11-21 +Description: Adds ai_prompt TEXT column for custom AI analysis prompts per question +""" + +# SQL Migration Script +sql_statements = [ + # Add ai_prompt column + """ + ALTER TABLE questions + ADD COLUMN ai_prompt TEXT; + """, +] + +# To apply this migration, run these SQL statements in your PostgreSQL database: +if __name__ == "__main__": + print("=" * 80) + print("MIGRATION: Add ai_prompt to questions table") + print("=" * 80) + print("\nExecute the following SQL statements in your PostgreSQL database:\n") + + for i, statement in enumerate(sql_statements, 1): + print(f"-- Statement {i}") + print(statement.strip()) + print() + + print("=" * 80) + print("\nTo verify the migration:") + print("SELECT column_name, data_type FROM information_schema.columns") + print("WHERE table_name = 'questions' AND column_name = 'ai_prompt';") + print("=" * 80) diff --git a/docker-compose.hub.yml b/docker-compose.hub.yml index b583c54..f7b0edd 100644 --- a/docker-compose.hub.yml +++ b/docker-compose.hub.yml @@ -20,7 +20,7 @@ services: retries: 5 backend: - image: dymai/syntria-backend:1.0.12 + image: dymai/syntria-backend:1.0.13 container_name: syntria-backend-prod restart: always depends_on: @@ -38,7 +38,7 @@ services: command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 frontend: - image: dymai/syntria-frontend:1.0.20 + image: dymai/syntria-frontend:1.0.21 container_name: syntria-frontend-prod restart: always depends_on: diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 25fe85d..165eb28 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -910,7 +910,8 @@ function QuestionsManagerModal({ checklist, onClose }) { max_photos: 3, requires_comment_on_fail: false, parent_question_id: null, - show_if_answer: '' + show_if_answer: '', + ai_prompt: '' }) useEffect(() => { @@ -967,7 +968,8 @@ function QuestionsManagerModal({ checklist, onClose }) { max_photos: 3, requires_comment_on_fail: false, parent_question_id: null, - show_if_answer: '' + show_if_answer: '', + ai_prompt: '' }) loadQuestions() } else { @@ -1158,6 +1160,26 @@ function QuestionsManagerModal({ checklist, onClose }) { + {/* AI Prompt */} +