dfernandezl12 commited on
Commit
8652599
·
verified ·
1 Parent(s): aa87f15

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +192 -0
main.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from unittest import result
2
+
3
+ from fastapi import FastAPI, File, UploadFile, HTTPException
4
+ from google import genai
5
+ from httpcore import request
6
+ from google.genai import types
7
+ #from dotenv import load_dotenv
8
+ from pydantic import BaseModel
9
+ from fastapi.staticfiles import StaticFiles
10
+ from fastapi.responses import FileResponse
11
+ from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM
12
+ import cv2
13
+ from paddleocr import PaddleOCR
14
+ import numpy as np
15
+ import time
16
+ import os
17
+
18
+ os.environ["OMP_NUM_THREADS"] = "1"
19
+
20
+ MODEL_ID = "models/gemini-2.5-flash"
21
+ MODEL_FALLBACK = "models/gemini-3.1-flash-lite-preview"
22
+ tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-small")
23
+ model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-small")
24
+ modelo_local = pipeline("text2text-generation", model=model, tokenizer=tokenizer)
25
+
26
+ #load_dotenv()
27
+
28
+ api_key = os.environ.get("GEMINI_API_KEY")
29
+
30
+ client = genai.Client(api_key=api_key)
31
+
32
+
33
+ config = types.GenerateContentConfig(
34
+ temperature=0,
35
+ )
36
+
37
+ class ChatRequest(BaseModel):
38
+ pregunta: str
39
+ contexto: str = ""
40
+ historial: list[str] = []
41
+
42
+ class ChatResponse(BaseModel):
43
+ respuesta: str
44
+ historial: list[str] = []
45
+
46
+ class OCRResponse(BaseModel):
47
+ texto: str
48
+
49
+ app = FastAPI(title="OCR API", version="1.0.0")
50
+ ocr = PaddleOCR(use_angle_cls=True, lang="es")
51
+
52
+ try:
53
+ local_model = pipeline("text2text-generation", model="google/flan-t5-small")
54
+ except Exception as e:
55
+ print(f"Aviso: No se pudo cargar el modelo local: {e}")
56
+ local_model = None
57
+
58
+ app.mount("/static", StaticFiles(directory="static", html=True), name="static")
59
+
60
+ @app.get("/")
61
+ def home():
62
+ return FileResponse("static/index.html")
63
+
64
+
65
+
66
+ @app.post("/api/chat", response_model=ChatResponse)
67
+ def prompt(request: ChatRequest):
68
+ max_reintentos = 2
69
+ segundos_espera = 1.5
70
+ e = "Error desconocido"
71
+
72
+ try:
73
+ pregunta = request.pregunta
74
+ historial = request.historial
75
+ texto_ocr = request.contexto # <--- Tomamos el texto enviado por el frontend
76
+
77
+ texto_prompt = f"""
78
+ Eres un asistente que responde preguntas usando SOLO información del documento.
79
+
80
+ REGLAS IMPORTANTES:
81
+ - No copies el documento completo.
82
+ - No repitas texto largo del documento.
83
+ - Extrae SOLO la información necesaria.
84
+ - Si la respuesta no está en el documento, di: "No aparece en el documento".
85
+ - Responde de forma breve y directa.
86
+
87
+ DOCUMENTO:
88
+ \"\"\"{texto_ocr}\"\"\" <--- Cambiado: ahora usa el OCR
89
+
90
+ HISTORIAL:
91
+ {historial}
92
+
93
+ PREGUNTA:
94
+ {pregunta}
95
+
96
+ RESPUESTA:
97
+ """
98
+
99
+ for intento in range(max_reintentos):
100
+ try:
101
+ response = client.models.generate_content(
102
+ model=MODEL_ID,
103
+ contents=texto_prompt,
104
+ config=config
105
+ )
106
+ return ChatResponse(respuesta=response.text)
107
+ except Exception as ex:
108
+ e = ex
109
+ print(f"Intento {intento+1} fallido: {e}")
110
+ time.sleep(segundos_espera)
111
+
112
+ try:
113
+ response = client.models.generate_content(
114
+ model=MODEL_FALLBACK,
115
+ contents=texto_prompt,
116
+ config=config
117
+ )
118
+ return ChatResponse(respuesta=response.text)
119
+ except Exception as e_fallback:
120
+ print(f"Fallido: {e_fallback}")
121
+ try:
122
+ # Simplificamos el prompt para el modelo local pequeño
123
+ res_local = modelo_local(f"question: {pregunta} context: {texto_ocr}", max_new_tokens=50)
124
+ return ChatResponse(respuesta=res_local[0]['generated_text'])
125
+ except Exception as e_final:
126
+ raise HTTPException(status_code=500, detail="Error en todos los modelos (incluyendo local)")
127
+
128
+ except Exception as e:
129
+ if isinstance(e, HTTPException): raise e
130
+ raise HTTPException(status_code=500, detail=str(e))
131
+
132
+ except Exception as e:
133
+ if isinstance(e, HTTPException): raise e
134
+ raise HTTPException(status_code=500, detail=str(e))
135
+
136
+
137
+ def extraer_texto(resultado_ocr):
138
+ textos = []
139
+
140
+ # PaddleOCR devuelve una lista de páginas.
141
+ # Validamos que el resultado no sea None y que la primera página tenga contenido.
142
+ if resultado_ocr and resultado_ocr[0] is not None:
143
+ for linea in resultado_ocr[0]:
144
+ # linea[1][0] es donde reside el texto detectado
145
+ textos.append(linea[1][0])
146
+
147
+ return " ".join(textos)
148
+
149
+
150
+ def preprocesar_imagen(image_bytes):
151
+ # Convertir bytes a imagen
152
+ nparr = np.frombuffer(image_bytes, np.uint8)
153
+ img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
154
+
155
+ if img is None:
156
+ raise ValueError("No se pudo decodificar la imagen")
157
+
158
+ # PaddleOCR maneja internamente el binarizado,
159
+ # es mejor enviarle la imagen limpia o solo en gris.
160
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
161
+ return gray
162
+
163
+ @app.post("/api/ocr", response_model=OCRResponse)
164
+ async def ocr_image(file: UploadFile = File(...)):
165
+ if file.content_type not in {"image/png", "image/jpeg", "image/jpg", "image/webp"}:
166
+ raise HTTPException(status_code=400, detail="Formato no soportado")
167
+
168
+ try:
169
+ # Leemos los bytes del archivo
170
+ contenido = await file.read()
171
+
172
+ # Preprocesamos y ejecutamos OCR
173
+ img = preprocesar_imagen(contenido)
174
+
175
+ # cls=True activa la clasificación de ángulo si se configuró en la instancia
176
+ result = ocr.ocr(img, cls=True)
177
+
178
+ texto_extraido = extraer_texto(result)
179
+
180
+ if not texto_extraido.strip():
181
+ return OCRResponse(texto="No se detectó texto en la imagen.")
182
+
183
+ return OCRResponse(texto=texto_extraido)
184
+
185
+ except ValueError as e:
186
+ raise HTTPException(status_code=400, detail=str(e))
187
+ except Exception as e:
188
+ raise HTTPException(status_code=500, detail=f"Error OCR: {str(e)}")
189
+
190
+
191
+
192
+