File size: 14,417 Bytes
a30f7c6
 
 
 
6831f04
a30f7c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d5aa9a
a30f7c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# ╔════════════════════════════════════════════════════════════════════════════╗
# β•‘             PIPELINE v35: GROQ ONLY + DEBUG LOGS (FIX)                    β•‘
# β•‘             Usando apenas Groq API (sem Gemini)                           β•‘
# β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

import os
import json
import re
import time
from datetime import datetime
import gradio as gr
from groq import Groq

# ==================== 1. CONFIGURAÇÃO ====================
groq_key = os.getenv("GROQ_API_KEY", "SUA_GROQ_KEY_AQUUI")
groq_client = Groq(api_key=groq_key)

ARQUIVO_CONFIG = "protocolo.json"
ARQUIVO_HELP = "help.md"
DELAY_ENTRE_AGENTES = 5  # 3 minutos

print("πŸš€ App inicializada - GROQ ONLY")
print(f"  βœ… Groq: {'OK - API configurada' if groq_key != 'SUA_GROQ_KEY_AQUUI' else '⚠️ usando key placeholder'}")

# ==================== 2. UTILIDADES ====================
def carregar_protocolo():
    try:
        with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f:
            return f.read()
    except Exception as e:
        print(f"❌ Erro carregar_protocolo: {e}")
        return "[]"

def salvar_protocolo(conteudo):
    try:
        json.loads(conteudo)
        with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
            f.write(conteudo)
        print(f"πŸ’Ύ Protocolo salvo: {len(json.loads(conteudo))} agentes")
        return "βœ… Protocolo salvo com sucesso"
    except Exception as e:
        print(f"❌ Erro salvar_protocolo: {e}")
        return f"❌ Erro JSON: {str(e)}"

def carregar_help():
    try:
        with open(ARQUIVO_HELP, "r", encoding="utf-8") as f:
            return f.read()
    except:
        return "# Help nΓ£o encontrado\n\nCrie um arquivo help.md na raiz do projeto."

def ler_anexo(arquivo):
    if arquivo is None:
        return ""
    try:
        with open(arquivo.name, "r", encoding="utf-8") as f:
            conteudo = f.read()
            print(f"πŸ“Ž Anexo lido: {os.path.basename(arquivo.name)} ({len(conteudo)} chars)")
            return f"\n\n[ANEXO: {os.path.basename(arquivo.name)}]\n{conteudo}\n[FIM ANEXO]\n"
    except Exception as e:
        print(f"❌ Erro ler_anexo {arquivo.name}: {e}")
        return ""

def verificar_stop(texto):
    if not texto:
        return False
    stop_detectado = bool(re.search(r'\bstop\b', str(texto), re.IGNORECASE))
    print(f"πŸ›‘ STOP detectado? {stop_detectado} em '{str(texto)[:100]}...'")
    return stop_detectado

# ==================== 3. ENGINE DE EXECUÇÃO (GROQ) ====================
def executar_no(timeline, config):
    print(f"\nπŸ”₯ === EXECUTANDO {config['nome']} ===")
    modelo = config.get('modelo', 'meta-llama/llama-4-maverick-17b-128e-instruct')
    print(f"   Modelo Groq: {modelo}")
    
    try:
        inicio = time.time()
        
        # Converte timeline para formato messages do Groq
        messages = []
        
        # System message com missΓ£o do agente
        messages.append({
            "role": "system",
            "content": f"AGENTE: {config['nome']}\nMISSÃO: {config['missao']}"
        })
        
        # Adiciona timeline como contexto
        for msg in timeline:
            role = msg.get('role')
            if role in ['user', 'assistant']:
                content = msg.get('content', '')
                # Se content for dict/list, serializa
                if isinstance(content, (dict, list)):
                    content = json.dumps(content, ensure_ascii=False)
                messages.append({
                    "role": role,
                    "content": str(content)
                })
        
        print(f"πŸ“€ Groq messages: {len(messages)} mensagens")
        print(f"   System: {messages[0]['content'][:200]}...")
        if len(messages) > 1:
            print(f"   Last user: {messages[-1]['content'][:200]}...")
        
        # Chama Groq API com streaming
        completion = groq_client.chat.completions.create(
            model=modelo,
            messages=messages,
            temperature=1,
            max_completion_tokens=4096,
            top_p=1,
            stream=True,
            stop=None
        )
        
        # Coleta resposta em streaming
        out_raw = ""
        for chunk in completion:
            content_chunk = chunk.choices[0].delta.content or ""
            out_raw += content_chunk
        
        tempo_exec = time.time() - inicio
        
        print(f"πŸ“₯ OUTPUT GROQ ({len(out_raw)} chars, {tempo_exec:.2f}s):")
        print(out_raw[:1000])
        print("..." if len(out_raw) > 1000 else "")
        
        # Parse JSON se necessΓ‘rio
        content = out_raw
        if config.get('tipo_saida') == 'json':
            try:
                # Remove markdown code blocks se presentes
                cleaned = out_raw.strip()
                 
                content = json.loads(cleaned)
                print("βœ… JSON parseado com sucesso")
            except Exception as parse_e:
                print(f"⚠️ Erro parse JSON: {parse_e}")
                print(f"   Primeiros 200 chars: {cleaned[:200]}")
                content = out_raw
        
        print(f"⏱️ Tempo total: {tempo_exec:.2f}s")
        
        return {
            "role": "assistant",
            "agent": config['nome'],
            "content": content,
            "raw": out_raw,
            "tempo": tempo_exec
        }, True
        
    except Exception as e:
        print(f"πŸ’₯ ERRO GROQ: {str(e)}")
        import traceback
        traceback.print_exc()
        return {
            "role": "system",
            "error": str(e),
            "agent": config['nome']
        }, False

# ==================== 4. ORQUESTRADOR ====================
def orquestrador(texto, anexos_list, history, json_config, contexto_objetivo):
    print("\n" + "="*80)
    print("🎬 INICIANDO ORQUESTRADOR - NOVA EXECUÇÃO")
    print(f"πŸ“ Input texto: '{texto[:200]}...' ({len(texto)} chars)")
    print(f"πŸ“Ž Anexos: {len(anexos_list)} arquivos")
    
    if not texto.strip():
        print("⚠️ Texto vazio, abortando")
        yield history, []
        return
    
    history = history + [{"role": "user", "content": texto}]
    
    try:
        protocolo = json.loads(json_config)
        print(f"πŸ”— Protocolo: {len(protocolo)} agentes")
        for i, ag in enumerate(protocolo):
            print(f"   {i+1}. {ag['nome']} ({ag.get('modelo', 'default')})")
    except Exception as e:
        print(f"πŸ’₯ Erro JSON config: {e}")
        history.append({"role": "assistant", "content": f"❌ Erro no JSON de Configuração: {str(e)}"})
        yield history, []
        return
    
    history.append({"role": "assistant", "content": ""})
    
    # Contexto inicial
    contexto_inicial = ""
    if contexto_objetivo and contexto_objetivo.strip():
        contexto_inicial += f"[OBJETIVO DO MODELO]\n{contexto_objetivo.strip()}\n[FIM OBJETIVO]\n\n"
        print(f"🎯 Objetivo adicionado: {len(contexto_objetivo)} chars")
    
    if anexos_list:
        for anexo in anexos_list:
            anexo_conteudo = ler_anexo(anexo)
            if anexo_conteudo:
                contexto_inicial += anexo_conteudo
    
    full_input = f"{contexto_inicial}{texto}".strip()
    timeline = [{"role": "user", "content": full_input}]
    print(f"🌐 Timeline inicial: {len(full_input)} chars")
    
    audit_data = []
    
    # Loop pelos agentes
    for idx, cfg in enumerate(protocolo):
        print(f"\n{'='*50}")
        print(f"πŸš€ FASE {idx+1}/{len(protocolo)}: {cfg['nome']}")
        
        history[-1]["content"] = f"⏳ Chamando agente: **{cfg['nome']}**... Aguarde."
        yield history, audit_data
        
        print(f"πŸ’€ Delay entre agentes: {DELAY_ENTRE_AGENTES}s")
        time.sleep(DELAY_ENTRE_AGENTES)
        
        history[-1]["content"] = f"⏳ Agente **{cfg['nome']}** processando..."
        yield history, audit_data
        
        res, sucesso = executar_no(timeline, cfg)
        timeline.append(res)
        
        audit_entry = {
            "step": idx + 1,
            "agent": cfg['nome'],
            "model": cfg.get('modelo', 'default'),
            "type": cfg.get('tipo_saida', 'texto'),
            "response_preview": res.get('content' , ''),
            "raw_len": len(res.get('raw', '')),
            "tempo": round(res.get('tempo', 0), 2),
            "sucesso": sucesso,
            "timestamp": datetime.now().strftime('%H:%M:%S')
        }
        audit_data.append(audit_entry)
        print(f"πŸ“‹ Audit #{audit_entry['step']} salvo")
        
        conteudo_resposta = res.get('content', '')
        if verificar_stop(conteudo_resposta):
            print("πŸ›‘ STOP detectado - encerrando pipeline")
            history[-1]["content"] = str(conteudo_resposta) if isinstance(conteudo_resposta, str) else json.dumps(conteudo_resposta, ensure_ascii=False, indent=2)
            yield history, audit_data
            return
        
        # Se for ΓΊltimo agente ou tipo texto, exibe resposta
        if idx == len(protocolo) - 1 or cfg.get('tipo_saida') == 'texto':
            texto_final = str(conteudo_resposta) if isinstance(conteudo_resposta, str) else json.dumps(conteudo_resposta, ensure_ascii=False, indent=2)
            print(f"✨ Exibindo resposta final: {len(texto_final)} chars")
            
            # Typewriter effect
            for i in range(0, len(texto_final), 5):
                history[-1]["content"] = texto_final[:i+5]
                yield history, audit_data
                time.sleep(0.05)
            
            history[-1]["content"] = texto_final
            yield history, audit_data
    
    print("🏁 Pipeline concluída com sucesso")
    print("="*80)

# ==================== 5. UI ====================
def ui_clean():
    config_init = carregar_protocolo()
    help_init = carregar_help()
    
    with gr.Blocks(title="AI Forensics - Groq") as app:
        anexos_state = gr.State([])
        
        with gr.Tabs():
            with gr.Tab("πŸ’¬ Chat"):
                gr.Markdown("## Investigador AI (v35 - Groq Only)")
                chatbot = gr.Chatbot(label="HistΓ³rico", height=500)
                
                with gr.Row():
                    txt_in = gr.Textbox(show_label=False, placeholder="Digite sua mensagem...", lines=2, scale=9)
                    btn_send = gr.Button("πŸ“€ Enviar", variant="primary", scale=1)
            
            with gr.Tab("πŸ“Ž Anexos & Contexto"):
                gr.Markdown("""
                ## Anexos e Contexto Factual
                Carregue arquivos que serΓ£o enviados **uma vez** antes da primeira operaΓ§Γ£o.
                """)
                objetivo_text = gr.Textbox(
                    label="Objetivo do Modelo",
                    placeholder="Ex: VocΓͺ Γ© um analista forense imparcial...",
                    lines=5
                )
                gr.Markdown("### Anexos")
                anexos_upload = gr.File(
                    file_count="multiple",
                    file_types=[".txt", ".md", ".csv", ".json"]
                )
                anexos_display = gr.Textbox(label="Arquivos Carregados", interactive=False, lines=3)
                
                def atualizar_anexos(files):
                    if not files:
                        return [], "Nenhum arquivo carregado"
                    nomes = [os.path.basename(f.name) for f in files]
                    return files, f"πŸ“Ž {len(files)} arquivo(s): {', '.join(nomes)}"
                
                anexos_upload.change(atualizar_anexos, anexos_upload, [anexos_state, anexos_display])
            
            with gr.Tab("βš™οΈ Protocolo"):
                gr.Markdown("""
                ## EdiΓ§Γ£o do Protocolo de Agentes (Groq)
                
                **Modelos disponΓ­veis:**
                - `meta-llama/llama-4-maverick-17b-128e-instruct` (padrΓ£o)
                - `meta-llama/llama-3.3-70b-versatile`
                - `deepseek-r1-distill-llama-70b`
                - `llama-3.1-70b-versatile`
                
                **Exemplo:**
                ```
                [
                  {
                    "nome": "Alien EscrivΓ£o",
                    "modelo": "meta-llama/llama-4-maverick-17b-128e-instruct",
                    "tipo_saida": "json",
                    "missao": "Analisar BO de forma imparcial..."
                  }
                ]
                ```
                """)
                
                with gr.Row():
                    btn_save_proto = gr.Button("πŸ’Ύ Salvar", variant="primary", size="sm")
                    btn_reload_proto = gr.Button("πŸ”„ Recarregar", size="sm")
                proto_status = gr.Markdown("")
                code_json = gr.Code(value=config_init, language="json", lines=30)
                
                btn_save_proto.click(salvar_protocolo, code_json, proto_status)
                btn_reload_proto.click(lambda: carregar_protocolo(), outputs=code_json)
            
            with gr.Tab("πŸ” Auditoria"):
                gr.Markdown("## Auditoria da Última Execução")
                audit_display = gr.JSON(label="Dados de Auditoria", value=[])
            
            with gr.Tab("❓ Ajuda"):
                help_content = gr.Markdown(help_init)
                btn_reload_help = gr.Button("πŸ”„ Recarregar Help")
                btn_reload_help.click(lambda: carregar_help(), outputs=help_content)
        
        # Triggers
        btn_send.click(
            orquestrador,
            [txt_in, anexos_state, chatbot, code_json, objetivo_text],
            [chatbot, audit_display]
        ).then(lambda: "", outputs=txt_in)
        
        txt_in.submit(
            orquestrador,
            [txt_in, anexos_state, chatbot, code_json, objetivo_text],
            [chatbot, audit_display]
        ).then(lambda: "", outputs=txt_in)
    
    return app

if __name__ == "__main__":
    print("πŸŽ‰ LanΓ§ando app Groq-only...")
    ui_clean().launch(css="footer{display:none!important;}")