Geoeasy commited on
Commit
6596813
·
verified ·
1 Parent(s): f141c0f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -62
app.py CHANGED
@@ -2,12 +2,23 @@ import os
2
  from pathlib import Path
3
  import gradio as gr
4
  import numpy as np
5
- import torch
6
  import faiss
 
 
 
7
 
8
- from sentence_transformers import SentenceTransformer
9
- from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
10
- from huggingface_hub import login
 
 
 
 
 
 
 
 
11
 
12
  # ----------------------------
13
  # Configurações da aplicação
@@ -31,50 +42,30 @@ SUGGESTION_QUESTIONS = [
31
  "Certificações?",
32
  ]
33
 
34
- # Caminhos dos arquivos de índice
35
  INDEX_FILE = "r_docs.index"
36
  CHUNKS_FILE = "r_chunks.npy"
37
  PDF_PATH = "CV-Ronaldo_Menezes_2025_06.pdf"
38
 
39
- # Verificação dos arquivos
40
  if not Path(INDEX_FILE).exists() or not Path(CHUNKS_FILE).exists():
41
- raise FileNotFoundError("Index not found. Por favor, execute: python build_index.py")
 
 
42
 
43
- # Carrega o índice FAISS e os trechos
44
  index = faiss.read_index(INDEX_FILE)
45
  chunks = np.load(CHUNKS_FILE, allow_pickle=True)
46
 
47
- # Modelo de embeddings
48
  embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
49
 
50
- # ----------------------------
51
- # Carregamento do modelo LLM local
52
- # ----------------------------
53
- hf_token = os.getenv("HF_TOKEN")
54
- if hf_token is None:
55
- raise ValueError("Token Hugging Face não encontrado. Defina como segredo 'HF_TOKEN' nos Settings do Space.")
56
-
57
- # Autenticação
58
- login(token=hf_token)
59
-
60
- MODEL_NAME = "microsoft/phi-2"
61
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, token=hf_token)
62
- model = AutoModelForCausalLM.from_pretrained(
63
- MODEL_NAME,
64
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
65
- device_map="auto",
66
- token=hf_token
67
- )
68
- llm_pipeline = pipeline("text-generation", model=model, tokenizer=tokenizer)
69
-
70
- # ----------------------------
71
- # Recuperação de contexto com FAISS
72
- # ----------------------------
73
  def retrieve_context(query: str, k: int = 4) -> str:
74
- q_emb = embedding_model.encode([query], convert_to_numpy=True, show_progress_bar=False)
75
  _, I = index.search(q_emb, k)
76
  return "\n---\n".join(chunks[i] for i in I[0])
77
 
 
78
  dialog_history: list[tuple[str, str]] = []
79
 
80
  # ----------------------------
@@ -85,35 +76,53 @@ def chatbot(user_input, temperature, top_p, max_tokens):
85
  if not user_input:
86
  return dialog_history, ""
87
 
 
88
  context = retrieve_context(user_input)
89
-
90
- prompt = (
91
- f"### System:\nVocê é um assistente especializado em pacotes R. "
92
- f"Use somente o contexto abaixo para responder. Se não souber, diga isso.\n\n"
93
- f"=== Retrieved Context ===\n{context}\n\n"
94
- f"### User:\n{user_input}\n\n### Assistant:\n"
95
- )
96
-
 
 
 
 
 
 
 
 
 
 
97
  try:
98
- result = llm_pipeline(
99
- prompt[:model.config.max_position_embeddings],
100
- max_new_tokens=max_tokens,
101
  temperature=temperature,
102
  top_p=top_p,
103
- do_sample=True
104
- )[0]
105
- assistant_reply = result.get('generated_text') or result.get('text') or "⚠️ Resposta não gerada."
106
- assistant_reply = assistant_reply.split("### Assistant:")[-1].strip()
107
- except Exception as e:
108
- assistant_reply = f"⚠️ Erro local: {e}"
109
-
 
 
 
 
110
  dialog_history.append((user_input, assistant_reply))
111
  return dialog_history, ""
112
 
113
- def clear_all():
 
 
 
114
  global dialog_history
115
  dialog_history = []
116
- return [], "", 0.6, 0.95, 512
117
 
118
  # ----------------------------
119
  # Interface Gradio
@@ -141,25 +150,23 @@ with gr.Blocks(title=APP_TITLE, css=custom_css, theme=gr.themes.Base()) as demo:
141
  gr.Markdown(INTRO)
142
 
143
  with gr.Row():
 
144
  with gr.Column(scale=3):
145
- chatbot_ui = gr.Chatbot(type="tuples", elem_id="chat-window", render_markdown=True)
146
  with gr.Row(elem_id="input-area"):
147
  txt = gr.Textbox(placeholder="Digite sua pergunta…", lines=2, elem_id="user-input")
148
  btn = gr.Button("Enviar", elem_id="send-button")
149
- temp = gr.Slider(0, 1, 0.6, label="Temperatura")
150
- topp = gr.Slider(0, 1, 0.95, label="Top-p")
151
- maxtok = gr.Slider(64, 1024, 512, label="Tokens Máximos")
152
-
153
- btn.click(chatbot, [txt, temp, topp, maxtok], [chatbot_ui, txt])
154
- txt.submit(chatbot, [txt, temp, topp, maxtok], [chatbot_ui, txt])
155
- gr.Button("Limpar").click(clear_all, [], [chatbot_ui, txt, temp, topp, maxtok])
156
 
 
157
  with gr.Column(scale=1, elem_classes="sidebar"):
158
  if Path(PDF_PATH).exists():
159
  gr.Markdown(f"[📄 Baixar CV em PDF](/file={PDF_PATH})")
160
  gr.Markdown("### Sugestões de Perguntas")
161
  for q in SUGGESTION_QUESTIONS:
162
- gr.Button(q).click(lambda q=q: (q, *chatbot(q, 0.6, 0.95, 512)), outputs=[txt, chatbot_ui, txt])
163
  gr.Markdown("---")
164
  gr.Markdown("### Dicas de Exploração do PDF")
165
  gr.Markdown("• Use palavras-chave como 'Process Mining' ou 'GIS' para ir direto à seção relevante.")
@@ -167,3 +174,4 @@ with gr.Blocks(title=APP_TITLE, css=custom_css, theme=gr.themes.Base()) as demo:
167
 
168
  if __name__ == "__main__":
169
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
2
  from pathlib import Path
3
  import gradio as gr
4
  import numpy as np
5
+ from sentence_transformers import SentenceTransformer
6
  import faiss
7
+ from openai import OpenAI, OpenAIError
8
+ from langchain_community.document_loaders import PyPDFLoader
9
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
10
 
11
+ # ----------------------------
12
+ # API Key NVIDIA (defina diretamente aqui)
13
+ # ----------------------------
14
+ NV_API_KEY = "nvapi"
15
+
16
+ # Cliente NVIDIA para chat
17
+ client = OpenAI(
18
+ base_url="https://integrate.api.nvidia.com/v1",
19
+ api_key=NV_API_KEY
20
+ )
21
+ CHAT_MODEL = "meta/llama3-8b-instruct"
22
 
23
  # ----------------------------
24
  # Configurações da aplicação
 
42
  "Certificações?",
43
  ]
44
 
45
+ # Paths for files generated by build_index.py
46
  INDEX_FILE = "r_docs.index"
47
  CHUNKS_FILE = "r_chunks.npy"
48
  PDF_PATH = "CV-Ronaldo_Menezes_2025_06.pdf"
49
 
50
+ # Verificação de índices gerados
51
  if not Path(INDEX_FILE).exists() or not Path(CHUNKS_FILE).exists():
52
+ raise FileNotFoundError(
53
+ "Index not found. Please run first:\n python build_index.py"
54
+ )
55
 
56
+ # Carrega FAISS index e chunks
57
  index = faiss.read_index(INDEX_FILE)
58
  chunks = np.load(CHUNKS_FILE, allow_pickle=True)
59
 
60
+ # Embedding model para seleção de contexto
61
  embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  def retrieve_context(query: str, k: int = 4) -> str:
64
+ q_emb = embedding_model.encode([query], convert_to_numpy=True)
65
  _, I = index.search(q_emb, k)
66
  return "\n---\n".join(chunks[i] for i in I[0])
67
 
68
+ # Histórico de diálogo: tuplas (user, assistant)
69
  dialog_history: list[tuple[str, str]] = []
70
 
71
  # ----------------------------
 
76
  if not user_input:
77
  return dialog_history, ""
78
 
79
+ # Recupera contexto e monta mensagem de sistema
80
  context = retrieve_context(user_input)
81
+ system_msg = {
82
+ "role": "system",
83
+ "content": (
84
+ "You are an assistant specialized in R packages. "
85
+ "Use only the context below to answer. If you don't know, say so.\n\n"
86
+ f"=== Retrieved Context ===\n{context}\n\n"
87
+ )
88
+ }
89
+
90
+ # Constrói lista de mensagens
91
+ messages = [system_msg]
92
+ for u, a in dialog_history:
93
+ messages.append({"role": "user", "content": u})
94
+ messages.append({"role": "assistant", "content": a})
95
+ messages.append({"role": "user", "content": user_input})
96
+
97
+ # Chama a API NVIDIA em streaming
98
+ assistant_reply = ""
99
  try:
100
+ stream = client.chat.completions.create(
101
+ model=CHAT_MODEL,
102
+ messages=messages,
103
  temperature=temperature,
104
  top_p=top_p,
105
+ max_tokens=max_tokens,
106
+ stream=True
107
+ )
108
+ for chunk in stream:
109
+ delta = chunk.choices[0].delta
110
+ if hasattr(delta, "content") and delta.content:
111
+ assistant_reply += delta.content
112
+ except OpenAIError as e:
113
+ assistant_reply = f"⚠️ API Error: {e.__class__.__name__}: {e}"
114
+
115
+ # Atualiza histórico e retorna
116
  dialog_history.append((user_input, assistant_reply))
117
  return dialog_history, ""
118
 
119
+ # ----------------------------
120
+ # Limpa histórico
121
+ # ----------------------------
122
+ def clear_history():
123
  global dialog_history
124
  dialog_history = []
125
+ return [], ""
126
 
127
  # ----------------------------
128
  # Interface Gradio
 
150
  gr.Markdown(INTRO)
151
 
152
  with gr.Row():
153
+ # Coluna principal de chat
154
  with gr.Column(scale=3):
155
+ chatbot_ui = gr.Chatbot(type="tuples", elem_id="chat-window")
156
  with gr.Row(elem_id="input-area"):
157
  txt = gr.Textbox(placeholder="Digite sua pergunta…", lines=2, elem_id="user-input")
158
  btn = gr.Button("Enviar", elem_id="send-button")
159
+ btn.click(chatbot, [txt, gr.Slider(0, 1, 0.6), gr.Slider(0, 1, 0.95), gr.Slider(64, 2048, 512)], [chatbot_ui, txt])
160
+ txt.submit(chatbot, [txt, gr.Slider(0, 1, 0.6), gr.Slider(0, 1, 0.95), gr.Slider(64, 2048, 512)], [chatbot_ui, txt])
161
+ gr.Button("Limpar").click(clear_history, [], [chatbot_ui, txt])
 
 
 
 
162
 
163
+ # Sidebar com PDF e sugestões
164
  with gr.Column(scale=1, elem_classes="sidebar"):
165
  if Path(PDF_PATH).exists():
166
  gr.Markdown(f"[📄 Baixar CV em PDF](/file={PDF_PATH})")
167
  gr.Markdown("### Sugestões de Perguntas")
168
  for q in SUGGESTION_QUESTIONS:
169
+ gr.Button(q).click(lambda q=q: q, inputs=[], outputs=[txt])
170
  gr.Markdown("---")
171
  gr.Markdown("### Dicas de Exploração do PDF")
172
  gr.Markdown("• Use palavras-chave como 'Process Mining' ou 'GIS' para ir direto à seção relevante.")
 
174
 
175
  if __name__ == "__main__":
176
  demo.launch(server_name="0.0.0.0", server_port=7860)
177
+