Spaces:
Sleeping
Sleeping
File size: 11,274 Bytes
a65bf0f b5df639 afbe1a5 3654c3e 6bb8fa6 869ee30 9456eeb 869ee30 3654c3e 869ee30 9456eeb 508414d afbe1a5 869ee30 2635dd8 a65bf0f afbe1a5 6817933 47c7302 a65bf0f 47c7302 a65bf0f 234ca11 3654c3e 234ca11 a65bf0f 234ca11 47c7302 234ca11 afbe1a5 a65bf0f 2e8dda8 234ca11 2e8dda8 234ca11 2e8dda8 afbe1a5 869ee30 3654c3e afbe1a5 869ee30 6bb8fa6 869ee30 afbe1a5 869ee30 6bb8fa6 869ee30 9456eeb 869ee30 9456eeb afbe1a5 9456eeb 869ee30 6bb8fa6 9456eeb 869ee30 6bb8fa6 9456eeb 3654c3e 869ee30 9456eeb 869ee30 6bb8fa6 3654c3e 6bb8fa6 869ee30 6bb8fa6 869ee30 6bb8fa6 869ee30 3654c3e 6bb8fa6 869ee30 9456eeb 869ee30 9456eeb 6bb8fa6 869ee30 9456eeb 3654c3e 6bb8fa6 9456eeb 3654c3e 6bb8fa6 9456eeb 869ee30 9456eeb 869ee30 6bb8fa6 869ee30 9456eeb 869ee30 9456eeb 869ee30 d7ba497 afbe1a5 b5df639 6bb8fa6 68ced7a b5df639 869ee30 234ca11 3af6719 68ced7a b5df639 508414d 6bb8fa6 b5df639 437b092 afbe1a5 869ee30 a65bf0f b5df639 6bb8fa6 2635dd8 869ee30 b5df639 869ee30 b5df639 869ee30 6bb8fa6 869ee30 9456eeb 869ee30 3654c3e 869ee30 6bb8fa6 3af6719 3654c3e b5df639 869ee30 b5df639 869ee30 3654c3e afbe1a5 6bb8fa6 afbe1a5 a65bf0f b5df639 3229cca b5df639 869ee30 6bb8fa6 a65bf0f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
#--- START OF FILE app (23).py ---
import streamlit as st
import time
import os
import tempfile # Para criar diretórios temporários seguros
# --- IMPORTS GROQ ---
from groq import Groq
# --- IMPORTS LANGCHAIN / RAG ---
from langchain_community.document_loaders import TextLoader
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain_groq import ChatGroq
# --------------------------
# 1. Título da Página e Configuração de Layout
st.set_page_config(page_title="Iza - Assistente Groq RAG", layout="wide")
# --- CSS CORRIGIDO E ATUALIZADO (REMOÇÃO DO AVATAR E ESPAÇO) ---
st.markdown("""
<style>
/* NOVO: Oculta o primeiro filho dentro do container de mensagem (que é o avatar/ícone) */
[data-testid^="chat-message-"] > div:first-child {
display: none !important;
width: 0px !important;
height: 0px !important;
min-width: 0px !important;
}
/* Garante que o container de avatar (o elemento com o testid específico) também não ocupe espaço */
[data-testid="stChatAvatar"] {
display: none !important;
width: 0px !important;
height: 0px !important;
min-width: 0px !important;
}
/* Remove o espaçamento extra para alinhar o texto à esquerda e remove o gap */
[data-testid="stChatMessage"] {
padding-left: 0px;
padding-right: 0px;
gap: 0.0rem !important;
}
/* Mantém a justificação do texto e garante a largura total para o conteúdo da mensagem */
[data-testid="stChatMessageContent"] {
text-align: justify;
width: 100%;
}
/* Regras de Tabela (Mantidas para garantir a legibilidade) */
table {
width: 100% !important;
table-layout: fixed;
}
th, td {
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
}
</style>
""", unsafe_allow_html=True)
st.title("Iza - Assistente com Groq RAG 🚀")
st.caption("Um chatbot com memória, upload de arquivos, LangChain RAG e controle de velocidade.")
# 3. Configuração do Cliente Groq
print("DEBUG: Inicializando clientes Groq.") # Log de depuração
client = Groq()
groq_llm = ChatGroq(model_name="mixtral-8x7b-32768", temperature=0.7)
# 2. Barra Lateral e Lógica de Upload/Processamento RAG
with st.sidebar:
st.header("Opções")
if 'retriever' not in st.session_state:
st.session_state.retriever = None
st.session_state.retriever_source = None
print("DEBUG: Estado inicial: retriever=None.") # Log de depuração
uploaded_file = st.file_uploader(
"Anexe um arquivo para pesquisa RAG (opcional)",
type=["txt", "md", "pdf"],
help="O arquivo será processado e a IA poderá responder perguntas sobre seu conteúdo."
)
# Lógica de Processamento do Arquivo
if uploaded_file:
# Apenas processa se o arquivo for novo
if st.session_state.retriever_source != uploaded_file.name:
file_path = f"./temp_file_{uploaded_file.name.replace('/', '_')}"
try:
# --- USO DO DIRETÓRIO TEMPORÁRIO ---
st.info("Passo 1/5: Salvando arquivo temporariamente. Aguarde...")
print(f"DEBUG: Tentando salvar arquivo: {uploaded_file.name}") # Log de depuração
with tempfile.NamedTemporaryFile(delete=False, suffix=f"_{uploaded_file.name}") as tmp_file:
tmp_file.write(uploaded_file.read())
file_path = tmp_file.name
print(f"DEBUG: Arquivo salvo em: {file_path}") # Log de depuração
# 2. CONFIGURAÇÃO RAG (Processo de Embedding)
with st.spinner(f"Processando '{uploaded_file.name}' com LangChain..."):
# Carregamento do Documento
if uploaded_file.type == 'application/pdf':
loader = PyPDFLoader(file_path)
elif uploaded_file.type in ['text/markdown', 'text/plain']:
loader = TextLoader(file_path)
else:
raise ValueError("Tipo de arquivo não suportado após o upload.")
documents = loader.load()
st.info(f"Passo 2/5: Carregamento concluído. Documentos carregados: {len(documents)}.")
# Fragmentação do Texto
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
texts = text_splitter.split_documents(documents)
st.info(f"Passo 3/5: Texto fragmentado em {len(texts)} pedaços.")
# HuggingFace Embeddings (Roda na CPU)
st.info("Passo 4/5: Criando Embeddings (vetores) com HuggingFace. Isso pode levar alguns segundos.")
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
# Criar o Vector Store (FAISS)
st.info("Passo 5/5: Criando o Vector Store (FAISS) para busca rápida.")
vectorstore = FAISS.from_documents(texts, embeddings)
# Armazenar na sessão
st.session_state.retriever = vectorstore.as_retriever()
st.session_state.retriever_source = uploaded_file.name
st.success(f"Arquivo '{uploaded_file.name}' processado! Pergunte sobre ele.")
print("DEBUG: Processo RAG concluído e retriever armazenado.") # Log de depuração
except Exception as e:
# Tratamento de erro 403 e outros
if "403" in str(e):
st.error("Erro no upload (403 Forbidden). O servidor de deploy está rejeitando a requisição.")
print(f"ERRO CRÍTICO (403): {e}") # Log de depuração
else:
st.error(f"Erro ao processar o arquivo: {e}")
print(f"ERRO DE PROCESSAMENTO: {e}") # Log de depuração
st.session_state.retriever = None
st.session_state.retriever_source = None
finally:
# Garante que o arquivo temporário seja removido
if os.path.exists(file_path):
os.remove(file_path)
print(f"DEBUG: Arquivo temporário removido: {file_path}") # Log de depuração
else:
st.info(f"O arquivo '{st.session_state.retriever_source}' já foi processado e está ativo.")
elif st.session_state.retriever_source is not None:
st.warning("O arquivo processado foi removido. A IA voltará a usar pesquisa web.")
st.session_state.retriever = None
st.session_state.retriever_source = None
# 4. Inicialização do Histórico da Conversa
if "messages" not in st.session_state:
print("DEBUG: Inicializando histórico de mensagens.") # Log de depuração
system_prompt = (
"Você é um assistente de pesquisa avançado chamado Iza. "
"Se houver um documento anexo, use-o como primeira fonte de conhecimento. "
"Caso contrário, use as ferramentas 'visit_website' ou 'web_search' para obter informações. "
"Sua tarefa é fornecer um resumo completo, bem estruturado e detalhado em markdown. "
"IMPORTANTE: Ao criar tabelas, elas devem ter no máximo 3 colunas, e de preferência apenas 2, "
"para garantir a legibilidade em todas as telas."
)
st.session_state.messages = [{"role": "system", "content": system_prompt}]
# 5. Exibição das Mensagens Anteriores (Log de quantas mensagens)
if len(st.session_state.messages) > 1:
print(f"DEBUG: Exibindo {len(st.session_state.messages) - 1} mensagens anteriores.") # Log de depuração
for message in st.session_state.messages:
if message["role"] != "system":
with st.chat_message(message["role"]):
st.markdown(message["content"])
# 6. Lógica de Interação do Chat
if prompt := st.chat_input("Pergunte algo sobre o documento ou faça uma pesquisa na web..."):
# 6a. Adiciona a mensagem do usuário e exibe
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
print(f"DEBUG: Prompt do usuário: '{prompt}'") # Log de depuração
# 6b. Obtém a resposta do assistente (RAG ou Streaming com Tool Use)
with st.chat_message("assistant"):
placeholder = st.empty()
full_response = ""
try:
# --- LÓGICA DE DECISÃO RAG vs GROQ DIRETO ---
if st.session_state.retriever is not None:
# RAG: Caso haja um arquivo anexado.
print("DEBUG: Modo RAG (Documento anexado) ativado.") # Log de depuração
qa_chain = RetrievalQA.from_chain_type(
llm=groq_llm,
chain_type="stuff",
retriever=st.session_state.retriever,
return_source_documents=False
)
# Resposta RAG
with st.spinner("Buscando no documento e gerando resposta..."):
result = qa_chain.invoke({"query": prompt})
full_response = result['result']
else:
# GROQ DIRETO: Caso NÃO haja arquivo (usa Tool Use para pesquisa web).
print("DEBUG: Modo Groq Direto (Web Search) ativado.") # Log de depuração
stream = client.chat.completions.create(
model="groq/compound",
messages=[
{"role": m["role"], "content": m["content"]}
for m in st.session_state.messages
],
temperature=0.7,
max_tokens=4096,
stream=True,
compound_custom={"tools": {"enabled_tools": ["web_search", "visit_website"]}}
)
# Streaming da resposta
for chunk in stream:
full_response += chunk.choices[0].delta.content or ""
placeholder.markdown(full_response + "▌")
time.sleep(0.005)
# Exibe a resposta completa
placeholder.markdown(full_response)
print("DEBUG: Resposta completa enviada ao chat.") # Log de depuração
# 6c. Adiciona a resposta completa ao histórico
st.session_state.messages.append({"role": "assistant", "content": full_response})
except Exception as e:
st.error(f"Ocorreu um erro na interação: {e}")
print(f"ERRO DE INTERAÇÃO: {e}") # Log de depuração
# --- END OF FILE app (23).py --- |