roundb commited on
Commit
6034b74
·
verified ·
1 Parent(s): 36c6f1c

Upload 5 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ vectorstore_faiss/index.faiss filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ RAG Chatbot – Gradio + FAISS + NVIDIA NIM
4
+ Layout com cards automáticos usando examples do ChatInterface
5
+ """
6
+
7
+ import os
8
+ import glob
9
+ from typing import List
10
+
11
+ import gradio as gr
12
+ import pandas as pd
13
+ from openai import OpenAI
14
+
15
+ from langchain_core.documents import Document
16
+ from langchain_community.vectorstores import FAISS
17
+ from langchain_huggingface import HuggingFaceEmbeddings
18
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
19
+
20
+
21
+ # =========================
22
+ # CONFIG
23
+ # =========================
24
+ DATA_DIR = os.getenv("DATA_DIR", "data")
25
+
26
+ EMB_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
27
+ CHUNK_SIZE = 900
28
+ CHUNK_OVERLAP = 150
29
+ TOP_K = 6
30
+ MAX_CONTEXT_CHARS = 4500
31
+
32
+ NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY", "")
33
+ NVIDIA_BASE_URL = "https://integrate.api.nvidia.com/v1"
34
+ NVIDIA_MODEL = "meta/llama-3.3-70b-instruct"
35
+
36
+ client = OpenAI(base_url=NVIDIA_BASE_URL, api_key=NVIDIA_API_KEY) if NVIDIA_API_KEY else None
37
+
38
+ SYSTEM_PROMPT = """Você é um assistente que responde perguntas com base em documentos.
39
+ Responda SOMENTE com base no CONTEXTO recuperado.
40
+ Se não houver evidência suficiente, diga claramente.
41
+ Seja objetivo.
42
+ """
43
+
44
+
45
+ # =========================
46
+ # READ FILES
47
+ # =========================
48
+ SUPPORTED_EXT = {".pdf", ".docx", ".xlsx", ".xls", ".csv", ".txt"}
49
+
50
+ def list_files(data_dir: str) -> List[str]:
51
+ files = []
52
+ for ext in SUPPORTED_EXT:
53
+ files.extend(glob.glob(os.path.join(data_dir, f"**/*{ext}"), recursive=True))
54
+ return sorted(set(files))
55
+
56
+
57
+ def read_txt(path):
58
+ try:
59
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
60
+ return f.read()
61
+ except:
62
+ return ""
63
+
64
+
65
+ def read_csv(path):
66
+ try:
67
+ df = pd.read_csv(path)
68
+ return df.head(1000).to_csv(index=False)
69
+ except:
70
+ return ""
71
+
72
+
73
+ def read_docx(path):
74
+ from docx import Document as DocxDocument
75
+ doc = DocxDocument(path)
76
+ return "\n".join([p.text for p in doc.paragraphs if p.text.strip()])
77
+
78
+
79
+ def read_pdf(path):
80
+ from pypdf import PdfReader
81
+ reader = PdfReader(path)
82
+ return "\n".join([p.extract_text() or "" for p in reader.pages])
83
+
84
+
85
+ # =========================
86
+ # BUILD VECTOR DATABASE
87
+ # =========================
88
+ def build_vectordb():
89
+ files = list_files(DATA_DIR)
90
+ if not files:
91
+ raise FileNotFoundError("Nenhum arquivo encontrado na pasta data/")
92
+
93
+ docs = []
94
+ splitter = RecursiveCharacterTextSplitter(
95
+ chunk_size=CHUNK_SIZE,
96
+ chunk_overlap=CHUNK_OVERLAP
97
+ )
98
+
99
+ for path in files:
100
+ ext = os.path.splitext(path)[1].lower()
101
+ text = ""
102
+
103
+ if ext == ".txt":
104
+ text = read_txt(path)
105
+ elif ext == ".csv":
106
+ text = read_csv(path)
107
+ elif ext in [".xlsx", ".xls"]:
108
+ text = read_csv(path)
109
+ elif ext == ".docx":
110
+ text = read_docx(path)
111
+ elif ext == ".pdf":
112
+ text = read_pdf(path)
113
+
114
+ for chunk in splitter.split_text(text):
115
+ docs.append(Document(page_content=chunk, metadata={"source": path}))
116
+
117
+ embedding = HuggingFaceEmbeddings(model_name=EMB_MODEL)
118
+ db = FAISS.from_documents(docs, embedding)
119
+ return db
120
+
121
+
122
+ vectordb = build_vectordb()
123
+
124
+
125
+ # =========================
126
+ # SUGGESTIONS (CARDS)
127
+ # =========================
128
+ SUGGESTIONS = [
129
+ "Resuma os principais pontos do documento.",
130
+ "Quais procedimentos são descritos?",
131
+ "Liste requisitos ou obrigações mencionadas.",
132
+ "Explique os termos técnicos utilizados.",
133
+ "Há prazos ou datas importantes?",
134
+ "Existe checklist operacional?",
135
+ "Quais seções são mais relevantes?",
136
+ "Há diferenças entre versões?"
137
+ ]
138
+
139
+
140
+ # =========================
141
+ # RAG FUNCTION
142
+ # =========================
143
+ def format_context(docs):
144
+ context = "\n\n".join([d.page_content for d in docs])
145
+ if len(context) > MAX_CONTEXT_CHARS:
146
+ context = context[:MAX_CONTEXT_CHARS]
147
+ return context
148
+
149
+
150
+ def chat_rag_nvidia(message, history):
151
+ if not client:
152
+ return "❌ Configure NVIDIA_API_KEY."
153
+
154
+ retrieved = vectordb.similarity_search(message, k=TOP_K)
155
+ context = format_context(retrieved)
156
+
157
+ messages = [
158
+ {"role": "system", "content": SYSTEM_PROMPT},
159
+ {"role": "user", "content": f"CONTEXTO:\n{context}\n\nPERGUNTA:\n{message}"}
160
+ ]
161
+
162
+ completion = client.chat.completions.create(
163
+ model=NVIDIA_MODEL,
164
+ messages=messages,
165
+ temperature=0.3,
166
+ max_tokens=800,
167
+ )
168
+
169
+ return completion.choices[0].message.content
170
+
171
+
172
+ # =========================
173
+ # UI (USANDO EXAMPLES NATIVOS)
174
+ # =========================
175
+ with gr.Blocks(title="Document RAG Assistant") as demo:
176
+
177
+ gr.Markdown("""
178
+ ## 📚 SOGETREL
179
+ Faça perguntas sobre os documentos indexados.
180
+ """)
181
+
182
+ gr.ChatInterface(
183
+ fn=chat_rag_nvidia,
184
+ examples=SUGGESTIONS, # ← Aqui são gerados os cards automaticamente
185
+ title="Assistant",
186
+ description="Pergunte algo sobre os documentos."
187
+ )
188
+
189
+ if __name__ == "__main__":
190
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio
2
+ openai
3
+ langchain
4
+ langchain-community
5
+ langchain-huggingface
6
+ faiss-cpu
7
+ sentence-transformers
vectorstore_faiss/_fingerprint.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "fingerprint": "53f693c5016866ca",
3
+ "files": [
4
+ "ATELIER_SOGETREL_26-06_25.pdf",
5
+ "CAFF BE-CPA ORANGE.xls",
6
+ "CHELEM + ARTICLES.xlsx",
7
+ "COML 14 DISSIM POI2.docx",
8
+ "CPDET - avenant OI 2025 du 1er avril 2025.pdf",
9
+ "CPFOR - Catalogue Lot 2- 3 - avenant DELTA du 1er juin 2024 avec marques de révision 1.pdf",
10
+ "CQ RACCO.docx",
11
+ "CRIAR DEVIS DEFAC RIP MEGALIS.docx",
12
+ "CRIAÇÃO PAR.docx",
13
+ "CUIVRE.docx",
14
+ "DESSAT ARM.docx",
15
+ "IPON 13-11-2025.docx",
16
+ "IPON DESSAT.docx",
17
+ "MEMO KA.pdf",
18
+ "MOD OP_DT.DICT.pdf",
19
+ "MOD'OP_COML .docx",
20
+ "OPTI BLOCAGE IMB.docx",
21
+ "PAR.docx",
22
+ "PEC_UIO.docx",
23
+ "PMV-1090104-QPR500974-2505606.pdf",
24
+ "PROCESS-DISSIMULATION_10_12_25.pdf",
25
+ "RAMI.docx",
26
+ "THDB_Modop_Resacode.pdf",
27
+ "TPSWAN.docx",
28
+ "[Fascicules et catalogues] CPFOR - Catalogue Lot 2-3 - avenant OI AVRIL 2025.pdf",
29
+ "récap géoref 2 (1).docx"
30
+ ]
31
+ }
vectorstore_faiss/index.faiss ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4addf064543f8d0208ba01c59f2e3b00b949814be043f0dfc9cae4078ce87e8a
3
+ size 4432941
vectorstore_faiss/index.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:27b6bebb4ff59b75699437d47a1d221b383116af7b30c39845356b815c5a2f04
3
+ size 2661417