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 ---