PedroM2626 commited on
Commit
c1b16e4
·
1 Parent(s): 5e680ad

chore: add project configuration, tests, and documentation

Browse files
.env.example ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # IBM Watson Natural Language Understanding
2
+ IBM_WATSON_API_KEY=seu_api_key_aqui
3
+ IBM_WATSON_URL=sua_url_do_servico_aqui
4
+
5
+ # IBM Watsonx AI
6
+ IBM_WATSONX_API_KEY=seu_api_key_aqui_se_diferente
7
+ IBM_WATSONX_PROJECT_ID=seu_id_do_projeto_aqui
.gitignore ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+
24
+ # Environment variables
25
+ .env
26
+
27
+ # IDEs
28
+ .vscode/
29
+ .idea/
30
+
31
+ # OS
32
+ .DS_Store
33
+ Thumbs.db
Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
  # Usar uma imagem base leve de Python
2
- FROM python:3.9-slim
3
 
4
  # Definir variáveis de ambiente para o Python não gerar arquivos .pyc e não usar buffer para logs
5
  ENV PYTHONDONTWRITEBYTECODE=1
@@ -16,8 +16,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
16
  # Copiar o arquivo de dependências
17
  COPY requirements.txt .
18
 
19
- # Instalar as dependências do Python
20
- RUN pip install --no-cache-dir -r requirements.txt
 
21
 
22
  # Copiar o restante do código do projeto
23
  COPY . .
 
1
  # Usar uma imagem base leve de Python
2
+ FROM python:3.10-slim
3
 
4
  # Definir variáveis de ambiente para o Python não gerar arquivos .pyc e não usar buffer para logs
5
  ENV PYTHONDONTWRITEBYTECODE=1
 
16
  # Copiar o arquivo de dependências
17
  COPY requirements.txt .
18
 
19
+ # Atualizar o pip e instalar as dependências do Python
20
+ RUN pip install --no-cache-dir --upgrade pip && \
21
+ pip install --no-cache-dir -r requirements.txt
22
 
23
  # Copiar o restante do código do projeto
24
  COPY . .
README.md CHANGED
@@ -8,4 +8,82 @@ pinned: false
8
  license: mit
9
  ---
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  license: mit
9
  ---
10
 
11
+ # 📑 Watsonx AI - Análise Inteligente de Documentos
12
+
13
+ Este projeto é uma aplicação web construída com **Gradio** que utiliza os serviços da **IBM Watson** e **Watsonx AI** para analisar documentos (PDF, DOCX, TXT), extrair informações relevantes, gerar resumos e permitir um chat inteligente baseado no conteúdo do documento (RAG - Retrieval-Augmented Generation).
14
+
15
+ ## 🚀 Funcionalidades
16
+
17
+ - **Extração de Texto:** Suporte para arquivos PDF, DOCX e TXT.
18
+ - **Análise de NLU:** Geração de resumos automáticos, extração de tópicos-chave e classificação temática usando IBM Watson Natural Language Understanding.
19
+ - **Localizador de Trechos:** Busca inteligente de parágrafos relevantes baseada em termos da pergunta.
20
+ - **Chat Inteligente (RAG):** Diálogo interativo com o modelo **Llama-3-70B** via Watsonx AI, focado exclusivamente no conteúdo do documento enviado.
21
+
22
+ ## 🛠️ Tecnologias Utilizadas
23
+
24
+ - [Gradio](https://gradio.app/) - Interface Web
25
+ - [IBM Watson NLU](https://www.ibm.com/cloud/watson-natural-language-understanding) - Processamento de Linguagem Natural
26
+ - [IBM Watsonx AI](https://www.ibm.com/watsonx) - Modelos de Fundação (Llama-3)
27
+ - [Python 3.10](https://www.python.org/)
28
+ - [Docker](https://www.docker.com/)
29
+
30
+ ## 📋 Pré-requisitos
31
+
32
+ Antes de começar, você precisará de:
33
+
34
+ 1. Uma conta na [IBM Cloud](https://cloud.ibm.com/).
35
+ 2. Instâncias dos serviços **Natural Language Understanding** e **Watsonx.ai**.
36
+ 3. Suas chaves de API e URLs dos serviços.
37
+
38
+ ## ⚙️ Instalação e Configuração
39
+
40
+ ### Localmente
41
+
42
+ 1. Clone o repositório:
43
+ ```bash
44
+ git clone https://huggingface.co/spaces/seu-usuario/Watsonx_AI-Intelligent_Document_Analysis
45
+ cd Watsonx_AI-Intelligent_Document_Analysis
46
+ ```
47
+
48
+ 2. Crie um ambiente virtual e instale as dependências:
49
+ ```bash
50
+ python -m venv venv
51
+ source venv/bin/activate # No Windows: venv\Scripts\activate
52
+ pip install -r requirements.txt
53
+ ```
54
+
55
+ 3. Configure as variáveis de ambiente:
56
+ - Copie o arquivo `.env.example` para `.env`.
57
+ - Preencha com suas credenciais da IBM Cloud.
58
+
59
+ 4. Execute a aplicação:
60
+ ```bash
61
+ python app.py
62
+ ```
63
+
64
+ ### Via Docker
65
+
66
+ 1. Construa a imagem:
67
+ ```bash
68
+ docker build -t watson-doc-analysis .
69
+ ```
70
+
71
+ 2. Execute o container:
72
+ ```bash
73
+ docker run -p 7860:7860 --env-file .env watson-doc-analysis
74
+ ```
75
+
76
+ ## 🧪 Testes
77
+
78
+ Para executar os testes unitários e de integração:
79
+
80
+ ```bash
81
+ pytest
82
+ ```
83
+
84
+ ## 📄 Licença
85
+
86
+ Este projeto está sob a licença MIT. Veja o arquivo [LICENSE](LICENSE) para mais detalhes.
87
+
88
+ ---
89
+ Desenvolvido para demonstração de capacidades de IA com Watsonx e Gradio.
app.py CHANGED
@@ -312,7 +312,7 @@ def criar_interface():
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(label="Resposta da IA (Markdown)")
316
 
317
  # Definição dos eventos
318
  def executar_fluxo_analise(arquivo):
 
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):
requirements.txt CHANGED
@@ -1,6 +1,8 @@
1
- gradio
2
- ibm-watson
3
- python-docx
4
- PyPDF2
5
- python-dotenv
6
- pytest
 
 
 
1
+ gradio>=4.44.1
2
+ huggingface-hub>=0.19.0
3
+ ibm-watson>=7.0.0
4
+ python-docx>=0.8.11
5
+ PyPDF2>=3.0.0
6
+ python-dotenv>=1.0.0
7
+ requests>=2.31.0
8
+ pytest>=7.4.0
tests/test_acceptance.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from unittest.mock import patch, MagicMock
3
+ from app import extrair_texto, processar_texto, chat_inteligente
4
+ import os
5
+
6
+ class TestAcceptance(unittest.TestCase):
7
+ """
8
+ Simula o fluxo completo do usuário:
9
+ 1. Upload de arquivo
10
+ 2. Extração de texto
11
+ 3. Processamento de NLU
12
+ 4. Pergunta via Chat
13
+ """
14
+
15
+ @patch('app.nlu.analyze')
16
+ @patch('app.obter_iam_token')
17
+ @patch('app.requests.post')
18
+ def test_full_user_flow(self, mock_post, mock_token, mock_analyze):
19
+ # 1. Setup
20
+ test_filename = "user_doc.txt"
21
+ content = "Este é um documento de teste sobre Watsonx AI."
22
+ with open(test_filename, "w", encoding="utf-8") as f:
23
+ f.write(content)
24
+
25
+ try:
26
+ # 2. Extração
27
+ texto = extrair_texto(test_filename)
28
+ self.assertEqual(texto, content)
29
+
30
+ # 3. Análise NLU (Mocking)
31
+ mock_analyze.side_effect = [
32
+ MagicMock(get_result=lambda: {'summarization': {'text': 'Resumo'}}),
33
+ MagicMock(get_result=lambda: {'keywords': [{'text': 'watsonx'}]}),
34
+ MagicMock(get_result=lambda: {'categories': [{'label': '/tech'}]})
35
+ ]
36
+ resumo, topicos, classificacao = processar_texto(texto)
37
+ self.assertEqual(resumo, "Resumo")
38
+
39
+ # 4. Chat (Mocking)
40
+ mock_token.return_value = "token123"
41
+ mock_resp = MagicMock()
42
+ mock_resp.status_code = 200
43
+ mock_resp.json.return_value = {'choices': [{'message': {'content': 'Resposta da IA'}}]}
44
+ mock_post.return_value = mock_resp
45
+
46
+ resposta = chat_inteligente("O que é Watsonx?", texto)
47
+ self.assertEqual(resposta, "Resposta da IA")
48
+
49
+ finally:
50
+ if os.path.exists(test_filename):
51
+ os.remove(test_filename)
52
+
53
+ if __name__ == "__main__":
54
+ unittest.main()
tests/test_integration.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from unittest.mock import patch, MagicMock
3
+ from app import processar_texto, chat_inteligente
4
+
5
+ class TestIntegration(unittest.TestCase):
6
+ @patch('app.nlu.analyze')
7
+ def test_processar_texto_mock(self, mock_analyze):
8
+ # Configurar o mock para o Watson NLU
9
+ mock_resumo = {'summarization': {'text': 'Este é um resumo.'}}
10
+ mock_topicos = {'keywords': [{'text': 'ia'}, {'text': 'tecnologia'}]}
11
+ mock_classificacao = {'categories': [{'label': '/technology'}]}
12
+
13
+ mock_analyze.side_effect = [
14
+ MagicMock(get_result=lambda: mock_resumo),
15
+ MagicMock(get_result=lambda: mock_topicos),
16
+ MagicMock(get_result=lambda: mock_classificacao)
17
+ ]
18
+
19
+ resumo, topicos, classificacao = processar_texto("Texto de teste com tamanho suficiente.")
20
+
21
+ self.assertEqual(resumo, "Este é um resumo.")
22
+ self.assertIn("ia", topicos)
23
+ self.assertIn("/technology", classificacao)
24
+
25
+ @patch('app.obter_iam_token')
26
+ @patch('app.requests.post')
27
+ def test_chat_inteligente_mock(self, mock_post, mock_token):
28
+ mock_token.return_value = "fake_token"
29
+
30
+ mock_response = MagicMock()
31
+ mock_response.status_code = 200
32
+ mock_response.json.return_value = {
33
+ 'choices': [{'message': {'content': 'Resposta mockada da IA.'}}]
34
+ }
35
+ mock_post.return_value = mock_response
36
+
37
+ resposta = chat_inteligente("Qual o tema?", "O documento fala sobre IA.")
38
+ self.assertEqual(resposta, "Resposta mockada da IA.")
39
+
40
+ if __name__ == "__main__":
41
+ unittest.main()
tests/test_unit.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from app import normalizar_texto, extrair_texto
3
+ import os
4
+
5
+ class TestUnit(unittest.TestCase):
6
+ def test_normalizar_texto(self):
7
+ self.assertEqual(normalizar_texto("Olá Mundo!"), "ola mundo")
8
+ self.assertEqual(normalizar_texto("Ação e Reação"), "acao e reacao")
9
+ self.assertEqual(normalizar_texto(""), "")
10
+ self.assertEqual(normalizar_texto(None), "")
11
+
12
+ def test_extrair_texto_txt(self):
13
+ test_file = "test.txt"
14
+ with open(test_file, "w", encoding="utf-8") as f:
15
+ f.write("Conteúdo de teste")
16
+
17
+ try:
18
+ texto = extrair_texto(test_file)
19
+ self.assertEqual(texto, "Conteúdo de teste")
20
+ finally:
21
+ if os.path.exists(test_file):
22
+ os.remove(test_file)
23
+
24
+ def test_extrair_texto_invalido(self):
25
+ resultado = extrair_texto("arquivo_inexistente.pdf")
26
+ self.assertTrue(resultado.startswith("Erro ao extrair texto"))
27
+
28
+ if __name__ == "__main__":
29
+ unittest.main()