DHEIVER commited on
Commit
5992817
·
verified ·
1 Parent(s): 0ed0b30

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +146 -43
app.py CHANGED
@@ -9,19 +9,48 @@ from nltk.tokenize import sent_tokenize
9
  import fitz # PyMuPDF
10
  import logging
11
  from tqdm import tqdm
 
12
 
13
  # Configurar logging
14
  logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger(__name__)
16
 
17
- # Baixar recursos necessários do NLTK
18
- try:
19
- nltk.download('punkt', quiet=True)
20
- except Exception as e:
21
- logger.warning(f"Erro ao baixar recursos NLTK: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  class PDFQuestionAnswering:
24
  def __init__(self):
 
 
 
 
25
  # Usar modelo multilíngue mais avançado
26
  self.model_name = "deepset/roberta-base-squad2"
27
  try:
@@ -36,12 +65,45 @@ class PDFQuestionAnswering:
36
  logger.error(f"Erro ao carregar o modelo: {e}")
37
  raise
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  def extract_text_from_pdf(self, pdf_file: str) -> Tuple[str, Dict[int, str]]:
40
  """
41
- Extrai texto do PDF usando PyMuPDF para melhor precisão
42
- Retorna o texto completo e um dicionário mapeando números de página para texto
43
  """
44
  try:
 
45
  doc = fitz.open(pdf_file)
46
  full_text = ""
47
  page_text = {}
@@ -52,55 +114,75 @@ class PDFQuestionAnswering:
52
  page_text[page_num] = text
53
  full_text += text + "\n"
54
 
 
55
  return full_text, page_text
 
56
  except Exception as e:
57
- logger.error(f"Erro na extração do PDF: {e}")
58
- raise
59
- finally:
60
- if 'doc' in locals():
61
- doc.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  def preprocess_text(self, text: str) -> str:
64
  """
65
  Pré-processa o texto removendo caracteres especiais e formatação indesejada
66
  """
67
- # Remover quebras de linha extras
68
- text = re.sub(r'\n+', ' ', text)
69
- # Remover espaços múltiplos
70
- text = re.sub(r'\s+', ' ', text)
71
- # Remover caracteres especiais
72
- text = re.sub(r'[^\w\s.,!?-]', '', text)
73
- return text.strip()
74
-
75
- def split_into_chunks(self, text: str, max_length: int = 512) -> List[str]:
76
- """
77
- Divide o texto em chunks menores respeitando limites de sentenças
78
- """
79
- sentences = sent_tokenize(text)
80
- chunks = []
81
- current_chunk = ""
82
-
83
- for sentence in sentences:
84
- if len(current_chunk) + len(sentence) < max_length:
85
- current_chunk += sentence + " "
86
- else:
87
- chunks.append(current_chunk.strip())
88
- current_chunk = sentence + " "
89
-
90
- if current_chunk:
91
- chunks.append(current_chunk.strip())
92
-
93
- return chunks
94
 
95
  def get_best_answer(self, question: str, chunks: List[str]) -> Dict:
96
  """
97
  Obtém a melhor resposta considerando todos os chunks de texto
98
  """
99
  try:
 
 
 
 
 
 
 
100
  answers = []
101
  for chunk in chunks:
102
- result = self.nlp(question=question, context=chunk)
103
- answers.append(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
  # Ordenar por score
106
  best_answer = max(answers, key=lambda x: x['score'])
@@ -112,16 +194,36 @@ class PDFQuestionAnswering:
112
  }
113
  except Exception as e:
114
  logger.error(f"Erro ao processar resposta: {e}")
115
- return {'answer': "Desculpe, não consegui processar sua pergunta.", 'score': 0, 'context': ""}
 
 
 
 
116
 
117
  def answer_question(self, pdf_file: gr.File, question: str) -> Dict:
118
  """
119
  Processa o PDF e responde à pergunta
120
  """
121
  try:
 
 
 
 
 
 
 
 
122
  # Extrair texto do PDF
123
  full_text, page_text = self.extract_text_from_pdf(pdf_file.name)
124
 
 
 
 
 
 
 
 
 
125
  # Pré-processar texto
126
  processed_text = self.preprocess_text(full_text)
127
 
@@ -202,4 +304,5 @@ def create_interface():
202
  if __name__ == "__main__":
203
  # Criar e iniciar a interface
204
  demo = create_interface()
205
- demo.launch(share=True, debug=True)
 
 
9
  import fitz # PyMuPDF
10
  import logging
11
  from tqdm import tqdm
12
+ import os
13
 
14
  # Configurar logging
15
  logging.basicConfig(level=logging.INFO)
16
  logger = logging.getLogger(__name__)
17
 
18
+ class NLTKDownloader:
19
+ @staticmethod
20
+ def download_nltk_resources():
21
+ """
22
+ Download recursos NLTK necessários e configura o diretório de dados
23
+ """
24
+ try:
25
+ # Configurar diretório de dados NLTK
26
+ nltk_data_dir = os.path.join(os.path.expanduser("~"), "nltk_data")
27
+ if not os.path.exists(nltk_data_dir):
28
+ os.makedirs(nltk_data_dir)
29
+
30
+ nltk.data.path.append(nltk_data_dir)
31
+
32
+ # Lista de recursos necessários
33
+ resources = ['punkt']
34
+
35
+ for resource in resources:
36
+ try:
37
+ nltk.data.find(f'tokenizers/{resource}')
38
+ logger.info(f"Recurso NLTK '{resource}' já está instalado")
39
+ except LookupError:
40
+ logger.info(f"Baixando recurso NLTK '{resource}'...")
41
+ nltk.download(resource, download_dir=nltk_data_dir, quiet=True)
42
+
43
+ return True
44
+ except Exception as e:
45
+ logger.error(f"Erro ao baixar recursos NLTK: {e}")
46
+ return False
47
 
48
  class PDFQuestionAnswering:
49
  def __init__(self):
50
+ # Inicializar recursos NLTK
51
+ if not NLTKDownloader.download_nltk_resources():
52
+ logger.warning("Alguns recursos NLTK podem não estar disponíveis")
53
+
54
  # Usar modelo multilíngue mais avançado
55
  self.model_name = "deepset/roberta-base-squad2"
56
  try:
 
65
  logger.error(f"Erro ao carregar o modelo: {e}")
66
  raise
67
 
68
+ def split_text_simple(self, text: str, max_length: int = 512) -> List[str]:
69
+ """
70
+ Método alternativo de divisão de texto caso o NLTK não esteja disponível
71
+ """
72
+ words = text.split()
73
+ chunks = []
74
+ current_chunk = []
75
+ current_length = 0
76
+
77
+ for word in words:
78
+ if current_length + len(word) + 1 <= max_length:
79
+ current_chunk.append(word)
80
+ current_length += len(word) + 1
81
+ else:
82
+ chunks.append(' '.join(current_chunk))
83
+ current_chunk = [word]
84
+ current_length = len(word) + 1
85
+
86
+ if current_chunk:
87
+ chunks.append(' '.join(current_chunk))
88
+
89
+ return chunks
90
+
91
+ def split_into_chunks(self, text: str, max_length: int = 512) -> List[str]:
92
+ """
93
+ Divide o texto em chunks menores, com fallback para método simples
94
+ """
95
+ try:
96
+ return [chunk for chunk in self.split_text_simple(text, max_length)]
97
+ except Exception as e:
98
+ logger.warning(f"Erro ao dividir texto com NLTK: {e}. Usando método simples.")
99
+ return self.split_text_simple(text, max_length)
100
+
101
  def extract_text_from_pdf(self, pdf_file: str) -> Tuple[str, Dict[int, str]]:
102
  """
103
+ Extrai texto do PDF com fallback para PyPDF2
 
104
  """
105
  try:
106
+ # Tentar primeiro com PyMuPDF
107
  doc = fitz.open(pdf_file)
108
  full_text = ""
109
  page_text = {}
 
114
  page_text[page_num] = text
115
  full_text += text + "\n"
116
 
117
+ doc.close()
118
  return full_text, page_text
119
+
120
  except Exception as e:
121
+ logger.warning(f"Erro com PyMuPDF: {e}. Tentando PyPDF2...")
122
+ try:
123
+ # Fallback para PyPDF2
124
+ with open(pdf_file, "rb") as file:
125
+ reader = PyPDF2.PdfReader(file)
126
+ full_text = ""
127
+ page_text = {}
128
+
129
+ for i, page in enumerate(reader.pages):
130
+ text = page.extract_text()
131
+ page_text[i] = text
132
+ full_text += text + "\n"
133
+
134
+ return full_text, page_text
135
+
136
+ except Exception as e2:
137
+ logger.error(f"Erro ao extrair texto do PDF: {e2}")
138
+ raise
139
 
140
  def preprocess_text(self, text: str) -> str:
141
  """
142
  Pré-processa o texto removendo caracteres especiais e formatação indesejada
143
  """
144
+ try:
145
+ # Remover quebras de linha extras
146
+ text = re.sub(r'\n+', ' ', text)
147
+ # Remover espaços múltiplos
148
+ text = re.sub(r'\s+', ' ', text)
149
+ # Remover caracteres especiais mas manter acentos
150
+ text = re.sub(r'[^\w\s.,!?-áéíóúâêîôûãõçà]', '', text)
151
+ return text.strip()
152
+ except Exception as e:
153
+ logger.warning(f"Erro no pré-processamento: {e}")
154
+ return text # Retorna texto original em caso de erro
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
  def get_best_answer(self, question: str, chunks: List[str]) -> Dict:
157
  """
158
  Obtém a melhor resposta considerando todos os chunks de texto
159
  """
160
  try:
161
+ if not chunks:
162
+ return {
163
+ 'answer': "Não foi possível processar o texto do documento.",
164
+ 'score': 0,
165
+ 'context': ""
166
+ }
167
+
168
  answers = []
169
  for chunk in chunks:
170
+ if not chunk.strip():
171
+ continue
172
+
173
+ try:
174
+ result = self.nlp(question=question, context=chunk)
175
+ answers.append(result)
176
+ except Exception as e:
177
+ logger.warning(f"Erro ao processar chunk: {e}")
178
+ continue
179
+
180
+ if not answers:
181
+ return {
182
+ 'answer': "Não foi possível encontrar uma resposta no documento.",
183
+ 'score': 0,
184
+ 'context': ""
185
+ }
186
 
187
  # Ordenar por score
188
  best_answer = max(answers, key=lambda x: x['score'])
 
194
  }
195
  except Exception as e:
196
  logger.error(f"Erro ao processar resposta: {e}")
197
+ return {
198
+ 'answer': "Ocorreu um erro ao processar sua pergunta.",
199
+ 'score': 0,
200
+ 'context': ""
201
+ }
202
 
203
  def answer_question(self, pdf_file: gr.File, question: str) -> Dict:
204
  """
205
  Processa o PDF e responde à pergunta
206
  """
207
  try:
208
+ if not pdf_file or not question:
209
+ return {
210
+ 'answer': "Por favor, forneça um arquivo PDF e uma pergunta.",
211
+ 'score': 0,
212
+ 'confidence': "0%",
213
+ 'context': ""
214
+ }
215
+
216
  # Extrair texto do PDF
217
  full_text, page_text = self.extract_text_from_pdf(pdf_file.name)
218
 
219
+ if not full_text.strip():
220
+ return {
221
+ 'answer': "Não foi possível extrair texto do PDF fornecido.",
222
+ 'score': 0,
223
+ 'confidence': "0%",
224
+ 'context': ""
225
+ }
226
+
227
  # Pré-processar texto
228
  processed_text = self.preprocess_text(full_text)
229
 
 
304
  if __name__ == "__main__":
305
  # Criar e iniciar a interface
306
  demo = create_interface()
307
+ # Desabilitar SSR para evitar problemas
308
+ demo.launch(share=False, debug=True, ssr=False)