PedroM2626 commited on
Commit
8f65225
·
1 Parent(s): c1b16e4

refactor: translate Portuguese codebase to English for internationalization

Browse files
Files changed (1) hide show
  1. app.py +187 -187
app.py CHANGED
@@ -10,26 +10,26 @@ import re
10
  import unicodedata
11
  import requests
12
 
13
- def normalizar_texto(texto):
14
- """Remove acentos, caracteres especiais e converte para minúsculas."""
15
- if not texto:
16
  return ""
17
- # Converte para minúsculas e remove espaços extras
18
- texto = texto.lower().strip()
19
- # Remove acentos
20
- texto = "".join(c for c in unicodedata.normalize('NFD', texto) if unicodedata.category(c) != 'Mn')
21
- # Remove pontuação básica para busca (mantém letras e números)
22
- texto = re.sub(r'[^a-z0-9\s]', '', texto)
23
- return texto
24
 
25
- # Carregar variáveis de ambiente
26
  load_dotenv()
27
 
28
- # Inicializar o Natural Language Understanding
29
- API_KEY = os.getenv('IBM_WATSON_API_KEY', 'SUA_CHAVE_API')
30
- SERVICE_URL = os.getenv('IBM_WATSON_URL', 'SUA_URL_SERVICO')
31
- PROJECT_ID = os.getenv('IBM_WATSONX_PROJECT_ID', 'SEU_PROJECT_ID')
32
- WATSONX_API_KEY = os.getenv('IBM_WATSONX_API_KEY', API_KEY) # Usa a chave específica ou a geral como fallback
33
 
34
  authenticator = IAMAuthenticator(API_KEY)
35
  nlu = NaturalLanguageUnderstandingV1(
@@ -38,173 +38,173 @@ nlu = NaturalLanguageUnderstandingV1(
38
  )
39
  nlu.set_service_url(SERVICE_URL)
40
 
41
- # Função para extrair texto de um documento
42
- def extrair_texto(arquivo):
43
- if not arquivo:
44
- return "Nenhum arquivo enviado."
45
 
46
  try:
47
- # Se arquivo for um objeto gr.File, ele tem o atributo .name (caminho temporário)
48
- nome_arquivo = arquivo.name if hasattr(arquivo, 'name') else arquivo
49
 
50
- if nome_arquivo.endswith('.pdf'):
51
- reader = PdfReader(nome_arquivo)
52
- texto = ''
53
  for page in reader.pages:
54
  page_text = page.extract_text()
55
  if page_text:
56
- texto += page_text
57
- return texto
58
- elif nome_arquivo.endswith('.docx'):
59
- doc = Document(nome_arquivo)
60
- texto = ''
61
  for para in doc.paragraphs:
62
- texto += para.text + '\n'
63
- return texto
64
- elif nome_arquivo.endswith('.txt'):
65
- with open(nome_arquivo, 'r', encoding='utf-8') as f:
66
  return f.read()
67
  else:
68
- return "Formato de arquivo não suportado. Use PDF, DOCX ou TXT."
69
  except Exception as e:
70
- return f"Erro ao extrair texto: {str(e)}"
71
 
72
- # Função para processar o texto (Resumo, Tópicos, Classificação)
73
- def processar_texto(texto):
74
- if not texto or len(texto.strip()) < 10:
75
- return "Texto insuficiente para processamento.", "", ""
76
 
77
  try:
78
- # Tenta o resumo automático (pode não estar disponível em todos os planos/regiões)
79
  try:
80
- resumo_res = nlu.analyze(
81
- text=texto,
82
  features={'summarization': {'limit': 1}}
83
  ).get_result()
84
- resumo = resumo_res.get('summarization', {}).get('text', 'Resumo não disponível.')
85
  except Exception:
86
- resumo = "Resumo automático não disponível no seu plano Watson NLU. Exibindo principais conceitos..."
87
 
88
- # Extração de tópicos-chave (keywords)
89
- topicos_res = nlu.analyze(
90
- text=texto,
91
  features={'keywords': {'limit': 10}}
92
  ).get_result()
93
- topicos_lista = [k['text'] for k in topicos_res.get('keywords', [])]
94
- topicos = ", ".join(topicos_lista[:5])
95
 
96
- # Se o resumo falhou, tentamos usar os tópicos para criar uma descrição simples
97
- if "não disponível" in resumo:
98
- resumo = f"O documento aborda temas como: {', '.join(topicos_lista[:3])}."
99
 
100
- # Classificação temática (categories)
101
- classificacao_res = nlu.analyze(
102
- text=texto,
103
  features={'categories': {'limit': 5}}
104
  ).get_result()
105
- classificacao = ", ".join([c['label'] for c in classificacao_res.get('categories', [])])
106
 
107
- return resumo, topicos, classificacao
108
  except Exception as e:
109
- return f"Erro no processamento: {str(e)}", "", ""
110
 
111
- # Função para responder a perguntas sobre o documento (Q&A)
112
- def responder_pergunta(pergunta, texto):
113
- if not pergunta or not texto:
114
- return "Por favor, forneça uma pergunta e garanta que o documento foi analisado primeiro."
115
 
116
  try:
117
- # 1. Extração de termos importantes da pergunta usando NLU (Keywords e Concepts)
118
- termos_busca = []
119
  try:
120
- analise_pergunta = nlu.analyze(
121
- text=pergunta,
122
  features={'keywords': {}, 'concepts': {}}
123
  ).get_result()
124
 
125
- for k in analise_pergunta.get('keywords', []):
126
- termos_busca.append(normalizar_texto(k['text']))
127
- for c in analise_pergunta.get('concepts', []):
128
- termos_busca.append(normalizar_texto(c['text']))
129
  except:
130
- pass # Fallback para extração manual se o NLU falhar na pergunta curta
131
 
132
- # Se o Watson não retornar termos ou falhar, usamos split manual com normalização
133
- if not termos_busca:
134
- termos_busca = normalizar_texto(pergunta).split()
135
 
136
- if not termos_busca:
137
- # Última tentativa: se tudo falhar, usa a pergunta normalizada inteira
138
- termos_busca = [normalizar_texto(pergunta)]
139
 
140
- # 2. Processamento do texto do documento
141
- # Normalizamos o texto completo para a busca
142
- texto_normalizado = normalizar_texto(texto)
143
 
144
- # Dividimos o documento em blocos menores (parágrafos)
145
- blocos_brutos = re.split(r'\n\s*\n', texto)
146
- if len(blocos_brutos) < 2:
147
- blocos_brutos = texto.split('\n')
148
 
149
- paragrafos_validos = []
150
- for bloco in blocos_brutos:
151
- limpo = bloco.strip()
152
- if len(limpo) > 20: # Mantém blocos com conteúdo mínimo
153
- paragrafos_validos.append({
154
- 'original': limpo,
155
- 'normalizado': normalizar_texto(limpo)
156
  })
157
 
158
- # Se ainda houver poucos blocos, tentamos dividir por sentenças
159
- if len(paragrafos_validos) < 3:
160
- sentencas = re.split(r'\.\s+', texto)
161
- paragrafos_validos = []
162
- for s in sentencas:
163
- limpo = s.strip()
164
- if len(limpo) > 20:
165
- paragrafos_validos.append({
166
- 'original': limpo,
167
- 'normalizado': normalizar_texto(limpo)
168
  })
169
 
170
- # 3. Cálculo de relevância (Ranking)
171
- melhor_paragrafo = ""
172
- maior_score = 0
173
 
174
- for item in paragrafos_validos:
175
- p_norm = item['normalizado']
176
  score = 0
177
 
178
- for termo in termos_busca:
179
- if not termo: continue
180
- # Se o termo exato (normalizado) está no parágrafo
181
- if termo in p_norm:
182
  score += 1
183
- # Bônus por palavra inteira para evitar falso-positivos em substrings
184
- if re.search(rf'\b{re.escape(termo)}\b', p_norm):
185
  score += 2
186
 
187
- # Se o score for igual, preferimos o parágrafo mais curto (mais específico)
188
- if score > maior_score:
189
- maior_score = score
190
- melhor_paragrafo = item['original']
191
- elif score == maior_score and score > 0:
192
- if len(item['original']) < len(melhor_paragrafo):
193
- melhor_paragrafo = item['original']
194
 
195
- # 4. Retorno do resultado
196
- if melhor_paragrafo and maior_score > 0:
197
- return f"Com base no documento, encontrei este trecho relevante:\n\n\"{melhor_paragrafo}\""
198
  else:
199
- return "Infelizmente não encontrei uma resposta direta no documento. Tente reformular sua pergunta com outros termos."
200
 
201
  except Exception as e:
202
- return f"Erro ao processar busca inteligente: {str(e)}"
203
 
204
- # --- Funções de Chat Inteligente (RAG com Watsonx AI) ---
205
 
206
- def obter_iam_token():
207
- """Gera um token de acesso IAM usando a API Key do Watsonx."""
208
  url = "https://iam.cloud.ibm.com/identity/token"
209
  headers = {"Content-Type": "application/x-www-form-urlencoded"}
210
  data = f"grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={WATSONX_API_KEY}"
@@ -214,41 +214,41 @@ def obter_iam_token():
214
  if response.status_code == 200:
215
  return response.json().get("access_token")
216
  elif response.status_code == 400:
217
- return f"Erro de Autenticação (400): A API Key fornecida é inválida ou não foi encontrada. Verifique seu arquivo .env."
218
  else:
219
- return f"Erro ao gerar token ({response.status_code}): {response.text}"
220
  except Exception as e:
221
- return f"Erro de conexão ao gerar token: {str(e)}"
222
 
223
- def chat_inteligente(pergunta, texto_documento):
224
- """Realiza um chat inteligente (RAG) usando o modelo Llama-3 no Watsonx AI."""
225
- if not pergunta or not texto_documento:
226
- return "Por favor, analise um documento primeiro e digite uma pergunta."
227
 
228
- token = obter_iam_token()
229
- if token.startswith("Erro"):
230
  return token
231
 
232
  url = "https://us-south.ml.cloud.ibm.com/ml/v1/text/chat?version=2023-05-29"
233
 
234
- # Limitamos o texto do documento para não exceder o limite de tokens do modelo
235
- contexto = texto_documento[:10000] # Aproximadamente 2500 tokens
236
 
237
  body = {
238
  "messages": [
239
  {
240
  "role": "system",
241
  "content": (
242
- "Você é um assistente de IA prestativo e honesto. "
243
- "Sua tarefa é responder perguntas baseando-se EXCLUSIVAMENTE no conteúdo do documento fornecido abaixo. "
244
- "Se a resposta não estiver no texto, diga que não encontrou a informação no documento. "
245
- "Responda sempre em português brasileiro e use formatação Markdown.\n\n"
246
- f"CONTEÚDO DO DOCUMENTO:\n{contexto}"
247
  )
248
  },
249
  {
250
  "role": "user",
251
- "content": pergunta
252
  }
253
  ],
254
  "project_id": PROJECT_ID,
@@ -269,77 +269,77 @@ def chat_inteligente(pergunta, texto_documento):
269
  try:
270
  response = requests.post(url, headers=headers, json=body)
271
  if response.status_code != 200:
272
- return f"Erro na API Watsonx: {response.text}"
273
 
274
  data = response.json()
275
  return data['choices'][0]['message']['content']
276
  except Exception as e:
277
- return f"Erro no processamento do chat: {str(e)}"
278
 
279
- # --- Interface Gradio usando Blocks ---
280
- def criar_interface():
281
- with gr.Blocks(title="Análise Inteligente de Documentos") as demo:
282
- gr.Markdown("# 📑 Watsonx AI - Análise Inteligente de Documentos")
283
- gr.Markdown("Extraia informações, resumos e faça perguntas sobre seus documentos PDF, DOCX ou TXT.")
284
 
285
- with gr.Tab("1. Extração e Análise"):
286
  with gr.Row():
287
  with gr.Column():
288
- arquivo_input = gr.File(label="Upload de Documento")
289
- botao_analisar = gr.Button("Analisar Documento", variant="primary")
290
 
291
  with gr.Column():
292
- texto_extraido = gr.Textbox(label="Texto Extraído", lines=10, interactive=False)
293
 
294
  with gr.Row():
295
- resumo_output = gr.Textbox(label="Resumo Automático")
296
- topicos_output = gr.Textbox(label="Tópicos-Chave")
297
- classificacao_output = gr.Textbox(label="Classificação Temática")
298
 
299
- with gr.Tab("2. Localizador de Trechos (Busca Semântica)"):
300
- gr.Markdown("### 🔍 Encontre trechos específicos no documento")
301
- gr.Markdown("Esta ferramenta localiza os parágrafos mais relevantes que contêm os termos da sua pergunta.")
302
  with gr.Row():
303
- pergunta_input = gr.Textbox(label="O que você procura no texto?", placeholder="Ex: Metas de faturamento")
304
- botao_perguntar = gr.Button("Localizar Trecho", variant="secondary")
305
 
306
- resposta_output = gr.Textbox(label="Trecho mais relevante encontrado", lines=10)
307
 
308
- with gr.Tab("3. Chat Inteligente (RAG)"):
309
- gr.Markdown("### 🤖 Pergunte à Inteligência Artificial")
310
- gr.Markdown("O modelo Llama-3 analisará todo o documento para responder suas perguntas com raciocínio e síntese.")
311
  with gr.Row():
312
- chat_input = gr.Textbox(label="Sua Pergunta para a IA", placeholder="Ex: Qual o tema principal do documento?")
313
- botao_chat = gr.Button("Gerar Resposta com IA", variant="primary")
314
 
315
  chat_output = gr.Markdown()
316
 
317
- # Definição dos eventos
318
- def executar_fluxo_analise(arquivo):
319
- texto = extrair_texto(arquivo)
320
- resumo, topicos, classificacao = processar_texto(texto)
321
- return texto, resumo, topicos, classificacao
322
 
323
- botao_analisar.click(
324
- fn=executar_fluxo_analise,
325
- inputs=[arquivo_input],
326
- outputs=[texto_extraido, resumo_output, topicos_output, classificacao_output]
327
  )
328
 
329
- botao_perguntar.click(
330
- fn=responder_pergunta,
331
- inputs=[pergunta_input, texto_extraido],
332
- outputs=[resposta_output]
333
  )
334
 
335
- botao_chat.click(
336
- fn=chat_inteligente,
337
- inputs=[chat_input, texto_extraido],
338
  outputs=[chat_output]
339
  )
340
 
341
  return demo
342
 
343
  if __name__ == "__main__":
344
- app = criar_interface()
345
  app.launch()
 
10
  import unicodedata
11
  import requests
12
 
13
+ def normalize_text(text):
14
+ """Removes accents, special characters and converts to lowercase."""
15
+ if not text:
16
  return ""
17
+ # Convert to lowercase and remove extra spaces
18
+ text = text.lower().strip()
19
+ # Remove accents
20
+ text = "".join(c for c in unicodedata.normalize('NFD', text) if unicodedata.category(c) != 'Mn')
21
+ # Remove basic punctuation for search (keep letters and numbers)
22
+ text = re.sub(r'[^a-z0-9\s]', '', text)
23
+ return text
24
 
25
+ # Load environment variables
26
  load_dotenv()
27
 
28
+ # Initialize Natural Language Understanding
29
+ API_KEY = os.getenv('IBM_WATSON_API_KEY', 'YOUR_API_KEY')
30
+ SERVICE_URL = os.getenv('IBM_WATSON_URL', 'YOUR_SERVICE_URL')
31
+ PROJECT_ID = os.getenv('IBM_WATSONX_PROJECT_ID', 'YOUR_PROJECT_ID')
32
+ WATSONX_API_KEY = os.getenv('IBM_WATSONX_API_KEY', API_KEY) # Use specific key or general as fallback
33
 
34
  authenticator = IAMAuthenticator(API_KEY)
35
  nlu = NaturalLanguageUnderstandingV1(
 
38
  )
39
  nlu.set_service_url(SERVICE_URL)
40
 
41
+ # Function to extract text from a document
42
+ def extract_text(file):
43
+ if not file:
44
+ return "No file uploaded."
45
 
46
  try:
47
+ # If file is a gr.File object, it has the .name attribute (temporary path)
48
+ file_name = file.name if hasattr(file, 'name') else file
49
 
50
+ if file_name.endswith('.pdf'):
51
+ reader = PdfReader(file_name)
52
+ text = ''
53
  for page in reader.pages:
54
  page_text = page.extract_text()
55
  if page_text:
56
+ text += page_text
57
+ return text
58
+ elif file_name.endswith('.docx'):
59
+ doc = Document(file_name)
60
+ text = ''
61
  for para in doc.paragraphs:
62
+ text += para.text + '\n'
63
+ return text
64
+ elif file_name.endswith('.txt'):
65
+ with open(file_name, 'r', encoding='utf-8') as f:
66
  return f.read()
67
  else:
68
+ return "Unsupported file format. Use PDF, DOCX or TXT."
69
  except Exception as e:
70
+ return f"Error extracting text: {str(e)}"
71
 
72
+ # Function to process text (Summary, Keywords, Classification)
73
+ def process_text(text):
74
+ if not text or len(text.strip()) < 10:
75
+ return "Insufficient text for processing.", "", ""
76
 
77
  try:
78
+ # Try automatic summarization (may not be available in all plans/regions)
79
  try:
80
+ summary_res = nlu.analyze(
81
+ text=text,
82
  features={'summarization': {'limit': 1}}
83
  ).get_result()
84
+ summary = summary_res.get('summarization', {}).get('text', 'Summary not available.')
85
  except Exception:
86
+ summary = "Automatic summarization not available in your Watson NLU plan. Showing main concepts..."
87
 
88
+ # Key topics extraction (keywords)
89
+ topics_res = nlu.analyze(
90
+ text=text,
91
  features={'keywords': {'limit': 10}}
92
  ).get_result()
93
+ topics_list = [k['text'] for k in topics_res.get('keywords', [])]
94
+ topics = ", ".join(topics_list[:5])
95
 
96
+ # If summary failed, we try to use topics to create a simple description
97
+ if "not available" in summary:
98
+ summary = f"The document covers topics such as: {', '.join(topics_list[:3])}."
99
 
100
+ # Thematic classification (categories)
101
+ classification_res = nlu.analyze(
102
+ text=text,
103
  features={'categories': {'limit': 5}}
104
  ).get_result()
105
+ classification = ", ".join([c['label'] for c in classification_res.get('categories', [])])
106
 
107
+ return summary, topics, classification
108
  except Exception as e:
109
+ return f"Processing error: {str(e)}", "", ""
110
 
111
+ # Function to answer questions about the document (Search)
112
+ def answer_question(question, text):
113
+ if not question or not text:
114
+ return "Please provide a question and ensure the document has been analyzed first."
115
 
116
  try:
117
+ # 1. Extraction of important terms from the question using NLU (Keywords and Concepts)
118
+ search_terms = []
119
  try:
120
+ question_analysis = nlu.analyze(
121
+ text=question,
122
  features={'keywords': {}, 'concepts': {}}
123
  ).get_result()
124
 
125
+ for k in question_analysis.get('keywords', []):
126
+ search_terms.append(normalize_text(k['text']))
127
+ for c in question_analysis.get('concepts', []):
128
+ search_terms.append(normalize_text(c['text']))
129
  except:
130
+ pass # Fallback to manual extraction if NLU fails on short question
131
 
132
+ # If Watson doesn't return terms or fails, use manual split with normalization
133
+ if not search_terms:
134
+ search_terms = normalize_text(question).split()
135
 
136
+ if not search_terms:
137
+ # Last attempt: if everything fails, use the entire normalized question
138
+ search_terms = [normalize_text(question)]
139
 
140
+ # 2. Document text processing
141
+ # Normalize full text for search
142
+ normalized_text = normalize_text(text)
143
 
144
+ # Split document into smaller blocks (paragraphs)
145
+ raw_blocks = re.split(r'\n\s*\n', text)
146
+ if len(raw_blocks) < 2:
147
+ raw_blocks = text.split('\n')
148
 
149
+ valid_paragraphs = []
150
+ for block in raw_blocks:
151
+ clean = block.strip()
152
+ if len(clean) > 20: # Keep blocks with minimum content
153
+ valid_paragraphs.append({
154
+ 'original': clean,
155
+ 'normalized': normalize_text(clean)
156
  })
157
 
158
+ # If still few blocks, try to split by sentences
159
+ if len(valid_paragraphs) < 3:
160
+ sentences = re.split(r'\.\s+', text)
161
+ valid_paragraphs = []
162
+ for s in sentences:
163
+ clean = s.strip()
164
+ if len(clean) > 20:
165
+ valid_paragraphs.append({
166
+ 'original': clean,
167
+ 'normalized': normalize_text(clean)
168
  })
169
 
170
+ # 3. Relevance calculation (Ranking)
171
+ best_paragraph = ""
172
+ highest_score = 0
173
 
174
+ for item in valid_paragraphs:
175
+ p_norm = item['normalized']
176
  score = 0
177
 
178
+ for term in search_terms:
179
+ if not term: continue
180
+ # If exact term (normalized) is in paragraph
181
+ if term in p_norm:
182
  score += 1
183
+ # Whole word bonus to avoid false-positives in substrings
184
+ if re.search(rf'\b{re.escape(term)}\b', p_norm):
185
  score += 2
186
 
187
+ # If score is equal, we prefer shorter (more specific) paragraph
188
+ if score > highest_score:
189
+ highest_score = score
190
+ best_paragraph = item['original']
191
+ elif score == highest_score and score > 0:
192
+ if len(item['original']) < len(best_paragraph):
193
+ best_paragraph = item['original']
194
 
195
+ # 4. Result return
196
+ if best_paragraph and highest_score > 0:
197
+ return f"Based on the document, I found this relevant snippet:\n\n\"{best_paragraph}\""
198
  else:
199
+ return "Unfortunately I didn't find a direct answer in the document. Try rephrasing your question with other terms."
200
 
201
  except Exception as e:
202
+ return f"Error processing smart search: {str(e)}"
203
 
204
+ # --- Smart Chat Functions (RAG with Watsonx AI) ---
205
 
206
+ def get_iam_token():
207
+ """Generates an IAM access token using the Watsonx API Key."""
208
  url = "https://iam.cloud.ibm.com/identity/token"
209
  headers = {"Content-Type": "application/x-www-form-urlencoded"}
210
  data = f"grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={WATSONX_API_KEY}"
 
214
  if response.status_code == 200:
215
  return response.json().get("access_token")
216
  elif response.status_code == 400:
217
+ return f"Authentication Error (400): The provided API Key is invalid or not found. Check your .env file."
218
  else:
219
+ return f"Error generating token ({response.status_code}): {response.text}"
220
  except Exception as e:
221
+ return f"Connection error generating token: {str(e)}"
222
 
223
+ def smart_chat(question, document_text):
224
+ """Performs a smart chat (RAG) using the Llama-3 model on Watsonx AI."""
225
+ if not question or not document_text:
226
+ return "Please analyze a document first and type a question."
227
 
228
+ token = get_iam_token()
229
+ if token.startswith("Error"):
230
  return token
231
 
232
  url = "https://us-south.ml.cloud.ibm.com/ml/v1/text/chat?version=2023-05-29"
233
 
234
+ # Limit document text to not exceed model token limit
235
+ context = document_text[:10000] # Approximately 2500 tokens
236
 
237
  body = {
238
  "messages": [
239
  {
240
  "role": "system",
241
  "content": (
242
+ "You are a helpful and honest AI assistant. "
243
+ "Your task is to answer questions based EXCLUSIVELY on the content of the document provided below. "
244
+ "If the answer is not in the text, say you didn't find the information in the document. "
245
+ "Always answer in English and use Markdown formatting.\n\n"
246
+ f"DOCUMENT CONTENT:\n{context}"
247
  )
248
  },
249
  {
250
  "role": "user",
251
+ "content": question
252
  }
253
  ],
254
  "project_id": PROJECT_ID,
 
269
  try:
270
  response = requests.post(url, headers=headers, json=body)
271
  if response.status_code != 200:
272
+ return f"Watsonx API Error: {response.text}"
273
 
274
  data = response.json()
275
  return data['choices'][0]['message']['content']
276
  except Exception as e:
277
+ return f"Chat processing error: {str(e)}"
278
 
279
+ # --- Gradio Interface using Blocks ---
280
+ def create_interface():
281
+ with gr.Blocks(title="Intelligent Document Analysis") as demo:
282
+ gr.Markdown("# 📑 Watsonx AI - Intelligent Document Analysis")
283
+ gr.Markdown("Extract information, summaries and ask questions about your PDF, DOCX or TXT documents.")
284
 
285
+ with gr.Tab("1. Extraction and Analysis"):
286
  with gr.Row():
287
  with gr.Column():
288
+ file_input = gr.File(label="Document Upload")
289
+ analyze_button = gr.Button("Analyze Document", variant="primary")
290
 
291
  with gr.Column():
292
+ extracted_text = gr.Textbox(label="Extracted Text", lines=10, interactive=False)
293
 
294
  with gr.Row():
295
+ summary_output = gr.Textbox(label="Automatic Summary")
296
+ topics_output = gr.Textbox(label="Key Topics")
297
+ classification_output = gr.Textbox(label="Thematic Classification")
298
 
299
+ with gr.Tab("2. Snippet Locator (Semantic Search)"):
300
+ gr.Markdown("### 🔍 Find specific snippets in the document")
301
+ gr.Markdown("This tool locates the most relevant paragraphs containing your search terms.")
302
  with gr.Row():
303
+ question_input = gr.Textbox(label="What are you looking for in the text?", placeholder="Ex: Revenue goals")
304
+ question_button = gr.Button("Locate Snippet", variant="secondary")
305
 
306
+ answer_output = gr.Textbox(label="Most relevant snippet found", lines=10)
307
 
308
+ with gr.Tab("3. Smart Chat (RAG)"):
309
+ gr.Markdown("### 🤖 Ask the Artificial Intelligence")
310
+ gr.Markdown("The Llama-3 model will analyze the entire document to answer your questions with reasoning and synthesis.")
311
  with gr.Row():
312
+ chat_input = gr.Textbox(label="Your Question for IA", placeholder="Ex: What is the main theme of the document?")
313
+ chat_button = gr.Button("Generate IA Response", variant="primary")
314
 
315
  chat_output = gr.Markdown()
316
 
317
+ # Event definitions
318
+ def run_analysis_flow(file):
319
+ text = extract_text(file)
320
+ summary, topics, classification = process_text(text)
321
+ return text, summary, topics, classification
322
 
323
+ analyze_button.click(
324
+ fn=run_analysis_flow,
325
+ inputs=[file_input],
326
+ outputs=[extracted_text, summary_output, topics_output, classification_output]
327
  )
328
 
329
+ question_button.click(
330
+ fn=answer_question,
331
+ inputs=[question_input, extracted_text],
332
+ outputs=[answer_output]
333
  )
334
 
335
+ chat_button.click(
336
+ fn=smart_chat,
337
+ inputs=[chat_input, extracted_text],
338
  outputs=[chat_output]
339
  )
340
 
341
  return demo
342
 
343
  if __name__ == "__main__":
344
+ app = create_interface()
345
  app.launch()