caarleexx commited on
Commit
869ee30
·
verified ·
1 Parent(s): 6817933

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -32
app.py CHANGED
@@ -1,15 +1,27 @@
1
  #--- START OF FILE app (23).py ---
2
 
3
  import streamlit as st
4
- from groq import Groq
5
  import time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  # 1. Título da Página e Configuração de Layout
8
- st.set_page_config(page_title="Iza - Assistente Groq", layout="wide")
9
 
10
  # --- CSS CORRIGIDO E ATUALIZADO (REMOÇÃO DO AVATAR E ESPAÇO) ---
11
- # Este CSS oculta os avatares (usando seletores mais robustos), remove o espaço
12
- # que ocupavam e justifica o texto da conversa.
13
  st.markdown("""
14
  <style>
15
  /* NOVO: Oculta o primeiro filho dentro do container de mensagem (que é o avatar/ícone) */
@@ -54,53 +66,137 @@ st.markdown("""
54
  </style>
55
  """, unsafe_allow_html=True)
56
 
57
- st.title("Iza - Assistente com Groq 🚀")
58
- st.caption("Um chatbot com memória, upload de arquivos e controle de velocidade.")
59
 
60
- # 2. Barra Lateral para Opções e Upload de Arquivos
 
 
 
 
 
 
61
  with st.sidebar:
62
  st.header("Opções")
63
- uploaded_file = st.file_uploader("Anexe um arquivo (opcional)", type=None)
 
 
 
 
 
 
 
 
64
  if uploaded_file:
65
- st.success(f"Arquivo '{uploaded_file.name}' carregado!")
66
- # A lógica para usar o arquivo seria adicionada aqui.
 
 
 
 
 
 
 
 
67
 
68
- # 3. Configuração do Cliente Groq
69
- # Assume que a chave GROQ_API_KEY está configurada no ambiente ou no .streamlit/secrets.toml
70
- client = Groq()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  # 4. Inicialização do Histórico da Conversa
73
  if "messages" not in st.session_state:
74
  system_prompt = (
75
  "Você é um assistente de pesquisa avançado chamado Iza. "
76
- "Sua tarefa é usar as ferramentas 'visit_website' ou 'web_search' para obter informações sobre um site "
77
- "e fornecer um resumo completo, bem estruturado e detalhado em markdown. "
 
78
  "IMPORTANTE: Ao criar tabelas, elas devem ter no máximo 3 colunas, e de preferência apenas 2, "
79
  "para garantir a legibilidade em todas as telas."
80
  )
81
  st.session_state.messages = [{"role": "system", "content": system_prompt}]
82
 
83
  # 5. Exibição das Mensagens Anteriores
84
- # O 'role' é mantido para fins de estrutura, mas o avatar está oculto pelo CSS
85
  for message in st.session_state.messages:
86
  if message["role"] != "system":
87
  with st.chat_message(message["role"]):
88
  st.markdown(message["content"])
89
 
90
  # 6. Lógica de Interação do Chat
91
- if prompt := st.chat_input("Pergunte algo sobre um site..."):
92
  # 6a. Adiciona a mensagem do usuário e exibe
93
  st.session_state.messages.append({"role": "user", "content": prompt})
94
  with st.chat_message("user"):
95
  st.markdown(prompt)
96
 
97
- # 6b. Obtém a resposta do assistente (Streaming)
98
  with st.chat_message("assistant"):
 
 
 
99
  try:
100
- def stream_groq_response():
101
- # Chama a API Groq com o histórico completo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  stream = client.chat.completions.create(
103
- model="groq/compound",
104
  messages=[
105
  {"role": m["role"], "content": m["content"]}
106
  for m in st.session_state.messages
@@ -110,23 +206,20 @@ if prompt := st.chat_input("Pergunte algo sobre um site..."):
110
  stream=True,
111
  compound_custom={"tools": {"enabled_tools": ["web_search", "visit_website"]}}
112
  )
 
 
113
  for chunk in stream:
114
- yield chunk.choices[0].delta.content or ""
115
-
116
- placeholder = st.empty()
117
- full_response = ""
118
- for token in stream_groq_response():
119
- full_response += token
120
- # Exibe a resposta com um cursor de digitação '▌'
121
- placeholder.markdown(full_response + "▌")
122
- # Atraso mínimo para o efeito de digitação (opcional)
123
- time.sleep(0.005)
124
  placeholder.markdown(full_response)
125
 
126
  # 6c. Adiciona a resposta completa ao histórico
127
  st.session_state.messages.append({"role": "assistant", "content": full_response})
128
 
129
  except Exception as e:
130
- st.error(f"Ocorreu um erro ao contatar a API da Groq: {e}")
131
 
132
  # --- END OF FILE app (23).py ---
 
1
  #--- START OF FILE app (23).py ---
2
 
3
  import streamlit as st
 
4
  import time
5
+ import os # Importado para manipulação de arquivos temporários
6
+
7
+ # --- IMPORTS GROQ ---
8
+ from groq import Groq
9
+
10
+ # --- IMPORTS LANGCHAIN / RAG ---
11
+ from langchain_community.document_loaders import TextLoader
12
+ from langchain_community.document_loaders import PyPDFLoader # Para carregar PDFs
13
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
14
+ from langchain_huggingface import HuggingFaceEmbeddings # Embeddings que rodam na CPU
15
+ from langchain_community.vectorstores import FAISS
16
+ from langchain.chains import RetrievalQA
17
+ from langchain_groq import ChatGroq
18
+ # O LangChain precisa de uma chave de API para o modelo de embeddings (se usar OpenAI),
19
+ # mas o HuggingFaceEmbeddings é local/gratuito.
20
 
21
  # 1. Título da Página e Configuração de Layout
22
+ st.set_page_config(page_title="Iza - Assistente Groq RAG", layout="wide")
23
 
24
  # --- CSS CORRIGIDO E ATUALIZADO (REMOÇÃO DO AVATAR E ESPAÇO) ---
 
 
25
  st.markdown("""
26
  <style>
27
  /* NOVO: Oculta o primeiro filho dentro do container de mensagem (que é o avatar/ícone) */
 
66
  </style>
67
  """, unsafe_allow_html=True)
68
 
69
+ st.title("Iza - Assistente com Groq RAG 🚀")
70
+ st.caption("Um chatbot com memória, upload de arquivos e controle de velocidade. Integração com LangChain RAG.")
71
 
72
+ # 3. Configuração do Cliente Groq
73
+ # Cliente Groq padrão para chamadas com Tool Use
74
+ client = Groq()
75
+ # Cliente Groq para uso dentro do LangChain
76
+ groq_llm = ChatGroq(model_name="mixtral-8x7b-32768", temperature=0.7)
77
+
78
+ # 2. Barra Lateral e Lógica de Upload/Processamento RAG
79
  with st.sidebar:
80
  st.header("Opções")
81
+
82
+ # Adicionando uma variável de sessão para rastrear o arquivo processado
83
+ if 'retriever' not in st.session_state:
84
+ st.session_state.retriever = None
85
+ st.session_state.retriever_source = None
86
+
87
+ uploaded_file = st.file_uploader("Anexe um arquivo (.txt, .md, .pdf)", type=["txt", "md", "pdf"])
88
+
89
+ # Processamento do Arquivo
90
  if uploaded_file:
91
+ # Apenas processa se o arquivo for novo ou o retriever ainda não existir
92
+ if st.session_state.retriever_source != uploaded_file.name:
93
+
94
+ # 1. SALVAR/LER ARQUIVO TEMPORARIAMENTE
95
+ bytes_data = uploaded_file.read()
96
+ # Cria um caminho de arquivo temporário (importa o 'os' para remover depois, se necessário)
97
+ file_path = f"./temp_file_{uploaded_file.name.replace('/', '_')}"
98
+ try:
99
+ with open(file_path, "wb") as f:
100
+ f.write(bytes_data)
101
 
102
+ # 2. CONFIGURAÇÃO RAG
103
+ with st.spinner(f"Processando '{uploaded_file.name}' para pesquisa..."):
104
+
105
+ # Carregamento do Documento
106
+ if uploaded_file.type == 'application/pdf':
107
+ loader = PyPDFLoader(file_path)
108
+ elif uploaded_file.type in ['text/markdown', 'text/plain']:
109
+ loader = TextLoader(file_path)
110
+ else:
111
+ st.error("Tipo de arquivo não suportado após o upload.")
112
+ st.session_state.retriever = None
113
+ st.session_state.retriever_source = None
114
+
115
+ documents = loader.load()
116
+
117
+ # Fragmentação do Texto
118
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
119
+ texts = text_splitter.split_documents(documents)
120
+
121
+ # HuggingFace Embeddings (Roda na CPU)
122
+ embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
123
+
124
+ # Criar o Vector Store (FAISS)
125
+ vectorstore = FAISS.from_documents(texts, embeddings)
126
+
127
+ # Criar o Retriever e armazenar na sessão
128
+ st.session_state.retriever = vectorstore.as_retriever()
129
+ st.session_state.retriever_source = uploaded_file.name
130
+ st.success(f"Arquivo '{uploaded_file.name}' processado e pronto para pesquisa!")
131
+
132
+ except Exception as e:
133
+ st.error(f"Erro ao processar o arquivo com LangChain: {e}")
134
+ st.session_state.retriever = None
135
+ st.session_state.retriever_source = None
136
+ finally:
137
+ # Tenta remover o arquivo temporário
138
+ if os.path.exists(file_path):
139
+ os.remove(file_path)
140
+
141
+ else:
142
+ # Se o arquivo já foi processado e está na sessão
143
+ st.success(f"Arquivo '{st.session_state.retriever_source}' carregado e pronto para pesquisa!")
144
+
145
+ elif st.session_state.retriever_source is not None:
146
+ # Limpa se o widget do uploader estiver vazio mas o retriever estiver ativo
147
+ st.session_state.retriever = None
148
+ st.session_state.retriever_source = None
149
 
150
  # 4. Inicialização do Histórico da Conversa
151
  if "messages" not in st.session_state:
152
  system_prompt = (
153
  "Você é um assistente de pesquisa avançado chamado Iza. "
154
+ "Se houver um documento anexo, use-o como primeira fonte de conhecimento. "
155
+ "Caso contrário, use as ferramentas 'visit_website' ou 'web_search' para obter informações. "
156
+ "Sua tarefa é fornecer um resumo completo, bem estruturado e detalhado em markdown. "
157
  "IMPORTANTE: Ao criar tabelas, elas devem ter no máximo 3 colunas, e de preferência apenas 2, "
158
  "para garantir a legibilidade em todas as telas."
159
  )
160
  st.session_state.messages = [{"role": "system", "content": system_prompt}]
161
 
162
  # 5. Exibição das Mensagens Anteriores
 
163
  for message in st.session_state.messages:
164
  if message["role"] != "system":
165
  with st.chat_message(message["role"]):
166
  st.markdown(message["content"])
167
 
168
  # 6. Lógica de Interação do Chat
169
+ if prompt := st.chat_input("Pergunte algo sobre o documento ou faça uma pesquisa na web..."):
170
  # 6a. Adiciona a mensagem do usuário e exibe
171
  st.session_state.messages.append({"role": "user", "content": prompt})
172
  with st.chat_message("user"):
173
  st.markdown(prompt)
174
 
175
+ # 6b. Obtém a resposta do assistente (RAG ou Streaming com Tool Use)
176
  with st.chat_message("assistant"):
177
+ placeholder = st.empty()
178
+ full_response = ""
179
+
180
  try:
181
+ # --- LÓGICA DE DECISÃO RAG vs GROQ DIRETO ---
182
+ if st.session_state.retriever is not None:
183
+ # RAG: Caso haja um arquivo anexado.
184
+ qa_chain = RetrievalQA.from_chain_type(
185
+ llm=groq_llm,
186
+ chain_type="stuff",
187
+ retriever=st.session_state.retriever,
188
+ return_source_documents=False # Opcional: mude para True para ver as fontes
189
+ )
190
+
191
+ # OBS: A resposta RAG geralmente não faz streaming de forma simples.
192
+ with st.spinner("Buscando no documento e gerando resposta..."):
193
+ result = qa_chain.invoke({"query": prompt})
194
+ full_response = result['result']
195
+
196
+ else:
197
+ # GROQ DIRETO: Caso NÃO haja arquivo (usa Tool Use para pesquisa web).
198
  stream = client.chat.completions.create(
199
+ model="groq/compound", # Modelo com Compound para Tool Use
200
  messages=[
201
  {"role": m["role"], "content": m["content"]}
202
  for m in st.session_state.messages
 
206
  stream=True,
207
  compound_custom={"tools": {"enabled_tools": ["web_search", "visit_website"]}}
208
  )
209
+
210
+ # Streaming da resposta
211
  for chunk in stream:
212
+ full_response += chunk.choices[0].delta.content or ""
213
+ placeholder.markdown(full_response + "▌")
214
+ time.sleep(0.005)
215
+
216
+ # Exibe a resposta completa (do RAG ou do Streaming)
 
 
 
 
 
217
  placeholder.markdown(full_response)
218
 
219
  # 6c. Adiciona a resposta completa ao histórico
220
  st.session_state.messages.append({"role": "assistant", "content": full_response})
221
 
222
  except Exception as e:
223
+ st.error(f"Ocorreu um erro na interação: {e}")
224
 
225
  # --- END OF FILE app (23).py ---