ToM / ai_studio_code.py
caarleexx's picture
Upload ai_studio_code.py
6d1a39a verified
import json
import os
import base64
import re
import warnings
from datetime import datetime
from typing import Dict, List, Tuple
import gradio as gr
import google.generativeai as genai
# ============================================================================
# CONFIGURAÇÃO
# ============================================================================
# Filtra avisos do gRPC para limpar o console
warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")
# API KEY
API_KEY = os.getenv("GOOGLE_API_KEY", "")
if not API_KEY:
print("❌ ERRO CRÍTICO: GOOGLE_API_KEY não encontrada nas variáveis de ambiente.")
print("Defina com: export GOOGLE_API_KEY='sua_chave'")
# Não paramos o script, mas vai dar erro na chamada
else:
print(f"✅ API Key carregada (termina em ...{API_KEY[-4:]})")
genai.configure(api_key=API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash")
TITLE = "# 🛠️ Anise v10.2 DEBUG MODE\n**Console mostra SAÍDA BRUTA**"
# ============================================================================
# SISTEMA DE LOGS & DEBUG
# ============================================================================
def debug_print(titulo: str, conteudo: any):
"""Imprime no console com formatação visível para debug"""
print(f"\n{'='*60}")
print(f"🐛 DEBUG: {titulo}")
print(f"{'-'*60}")
if isinstance(conteudo, (dict, list)):
print(json.dumps(conteudo, indent=2, ensure_ascii=False))
else:
print(str(conteudo))
print(f"{'='*60}\n")
# ============================================================================
# HELPERS (SEM TRY/EXCEPT PARA DEBUG)
# ============================================================================
def processar_anexo(arquivo) -> Tuple[str, str]:
if arquivo is None:
return "", "nenhum"
caminho = str(arquivo)
print(f"📂 Processando arquivo: {caminho}")
if caminho.lower().endswith('.pdf'):
import PyPDF2 # Se falhar aqui, queremos ver o erro de importação
with open(caminho, 'rb') as f:
leitor = PyPDF2.PdfReader(f)
texto = "".join(pagina.extract_text() + "\n" for pagina in leitor.pages[:5])
print(f"📄 PDF extraído: {len(texto)} caracteres")
return texto[:5000], "pdf"
elif any(caminho.lower().endswith(ext) for ext in ['.png','.jpg','.jpeg','.gif','.webp']):
with open(caminho, 'rb') as f:
encoded = base64.b64encode(f.read()).decode()
print(f"🖼️ Imagem codificada: {len(encoded)} bytes")
return encoded, "imagem"
return "", "nao_suportado"
def limpar_json_raw(texto: str) -> str:
"""Limpa markdown ```json ... ``` para tentar parsear"""
texto = re.sub(r'^```json\s*', '', texto, flags=re.MULTILINE)
texto = re.sub(r'^```\s*', '', texto, flags=re.MULTILINE)
texto = re.sub(r'```$', '', texto, flags=re.MULTILINE)
return texto.strip()
def chamar_gemini_json(prompt_base: str, etapa: str, temperatura=0.2, max_tokens=2000) -> Dict:
"""
Chama o Gemini e imprime o RAW OUTPUT.
Se falhar o JSON, explode o erro para análise.
"""
full_prompt = f"""{prompt_base}
---
**INSTRUÇÃO DE SISTEMA OBRIGATÓRIA:**
1. Responda APENAS com um JSON válido.
2. NÃO use blocos de código markdown (```json).
3. NÃO escreva texto antes ou depois do JSON.
"""
# 1. Chamada API
print(f"📡 Enviando requisição para {etapa}...")
response = model.generate_content(
full_prompt,
generation_config=genai.types.GenerationConfig(
temperature=temperatura,
max_output_tokens=max_tokens,
# response_mime_type="application/json" # Opcional: força modo JSON estrito do 1.5
)
)
raw_text = response.text
# 2. PRINT DA SAÍDA BRUTA (O que você pediu)
print(f"\n🛑 SAÍDA BRUTA GEMINI [{etapa}]:")
print(f">>> INICIO RAW <<<\n{raw_text}\n>>> FIM RAW <<<")
# 3. Tentativa de Parse (sem try/except silencioso)
texto_limpo = limpar_json_raw(raw_text)
if not texto_limpo:
raise ValueError(f"[{etapa}] Gemini retornou texto vazio!")
try:
dados_json = json.loads(texto_limpo)
return dados_json
except json.JSONDecodeError as e:
print(f"❌ FALHA NO PARSE JSON [{etapa}]")
print(f"Erro: {e}")
print("Tentando corrigir manualmente strings...")
# Fallback simples se for apenas texto solto (opcional)
raise e # Relança o erro para parar o script e ver o traceback
def historico_compacto(historico: List) -> str:
if not historico: return "Nenhum."
return "\n".join([f"{m['role']}: {str(m['content'])[:100]}..." for m in historico[-4:]])
def criar_dna() -> Dict:
return {"historico": [], "meta": {"turnos": 0}}
# ============================================================================
# PIPELINE - PASSOS (COM LOGS EXPLÍCITOS)
# ============================================================================
def passo_0_aluno(pergunta: str, historico: List) -> Dict:
prompt = f"""ETAPA: P0-INTENÇÃO
HISTÓRICO: {historico_compacto(historico)}
PERGUNTA: {pergunta}
Analise a intenção do usuário.
JSON: {{"relacao": "continua|nova", "intent": "resumo"}}"""
return chamar_gemini_json(prompt, "P0")
def passo_1_triagem(pergunta: str, p0: Dict) -> Dict:
prompt = f"""ETAPA: P1-TRIAGEM
P0: {json.dumps(p0)}
PERGUNTA: {pergunta}
Classifique a pergunta.
JSON: {{"tipo": "factual|analitica", "complexidade": "alta|baixa"}}"""
return chamar_gemini_json(prompt, "P1")
def passo_x1_lacunas(pergunta: str, p1: Dict) -> Dict:
prompt = f"""ETAPA: X1-LACUNAS
P1: {json.dumps(p1)}
PERGUNTA: {pergunta}
Quais perguntas o assistente deve fazer a si mesmo para responder isso perfeitamente?
JSON: {{"perguntas_internas": ["pergunta1", "pergunta2"]}}"""
return chamar_gemini_json(prompt, "X1")
def passo_x2_resolver(x1: Dict, historico: List) -> Dict:
perguntas = x1.get("perguntas_internas", [])
if not perguntas: return {"respostas": []}
prompt = f"""ETAPA: X2-RESOLUÇÃO
PERGUNTAS: {json.dumps(perguntas)}
CONTEXTO: {historico_compacto(historico)}
Responda as perguntas internas.
JSON: {{"respostas": [{{"p": "pergunta", "r": "resposta", "confianca": "alta|baixa"}}]}}"""
return chamar_gemini_json(prompt, "X2")
def passo_2_cenarios(pergunta: str, x2: Dict) -> Dict:
prompt = f"""ETAPA: P2-CENÁRIOS
DADOS X2: {json.dumps(x2)}
PERGUNTA ORIGINAL: {pergunta}
Crie cenários de resposta ou decida parar se faltar informação crítica.
JSON: {{"decisao": "continuar|parar", "cenarios": ["C1: ...", "C2: ..."], "motivo_parada": "..."}}"""
return chamar_gemini_json(prompt, "P2")
def passo_7_sintese(p2: Dict, pergunta: str) -> Dict:
prompt = f"""ETAPA: P7-FINAL
CENÁRIOS: {json.dumps(p2)}
PERGUNTA: {pergunta}
Escreva a resposta final para o usuário.
JSON: {{"resposta_final": "texto aqui"}}"""
return chamar_gemini_json(prompt, "P7", temperatura=0.7)
# ============================================================================
# ORQUESTADOR (SEM REDE DE SEGURANÇA)
# ============================================================================
def processar_pipeline(pergunta: str, historico: List, arquivo_anexo=None, dna=None) -> Tuple[str, List, Dict]:
# Debug inicial
debug_print("INICIO PIPELINE", f"Pergunta: {pergunta}\nAnexo: {arquivo_anexo}")
if dna is None: dna = criar_dna()
# 1. Processar Anexo
conteudo_anexo, tipo_anexo = processar_anexo(arquivo_anexo)
# 2. Montar Prompt com Contexto Visual/Documental
if tipo_anexo == "pdf":
prompt_final = f"CONTEXTO DO PDF:\n{conteudo_anexo}\n---\nPERGUNTA: {pergunta}"
elif tipo_anexo == "imagem":
prompt_final = f"[IMAGEM BASE64: {conteudo_anexo}]\nAnalise a imagem.\nPERGUNTA: {pergunta}"
else:
prompt_final = pergunta
# 3. Execução Linear (Se falhar, o erro aparece no console)
p0 = passo_0_aluno(prompt_final, historico)
debug_print("P0 Resultado", p0)
p1 = passo_1_triagem(prompt_final, p0)
debug_print("P1 Resultado", p1)
x1 = passo_x1_lacunas(prompt_final, p1)
debug_print("X1 Resultado", x1)
x2 = passo_x2_resolver(x1, historico)
debug_print("X2 Resultado", x2)
p2 = passo_2_cenarios(prompt_final, x2)
debug_print("P2 Resultado", p2)
# Lógica de Parada
if p2.get("decisao") == "parar":
resposta = f"⚠️ Não consigo responder com certeza.\nMotivo: {p2.get('motivo_parada')}"
else:
p7 = passo_7_sintese(p2, prompt_final)
debug_print("P7 Resultado", p7)
resposta = p7.get("resposta_final", "Erro na síntese P7")
# Atualiza Histórico
novo_hist = historico + [
{"role": "user", "content": pergunta},
{"role": "assistant", "content": resposta}
]
dna["historico"].append({"turn": dna["meta"]["turnos"], "p": pergunta, "r": resposta[:20]})
dna["meta"]["turnos"] += 1
return resposta, novo_hist, dna
# ============================================================================
# INTERFACE
# ============================================================================
def chat_interface(msg, hist, anexo, dna_json):
# Converte string JSON do DNA de volta para dict
dna = json.loads(dna_json) if dna_json else {}
if hist is None: hist = []
# Chama o pipeline (Erros vão aparecer no console do servidor)
resp, novo_hist, dna_new = processar_pipeline(msg, hist, anexo, dna)
return novo_hist, "", json.dumps(dna_new, indent=2), None
if __name__ == "__main__":
with gr.Blocks(title="Anise Debug", theme=gr.themes.Base()) as demo:
gr.Markdown(TITLE)
with gr.Row():
chat = gr.Chatbot(height=500, type="messages", label="Chat")
dna_box = gr.Code(label="DNA (JSON State)", language="json")
with gr.Row():
txt_in = gr.Textbox(label="Pergunta", scale=2)
file_in = gr.File(label="Anexo")
btn = gr.Button("Enviar", variant="primary")
btn.click(chat_interface, [txt_in, chat, file_in, dna_box], [chat, txt_in, dna_box, file_in])
txt_in.submit(chat_interface, [txt_in, chat, file_in, dna_box], [chat, txt_in, dna_box, file_in])
print("🚀 Servidor Iniciado! Verifique este console para logs.")
demo.launch(server_name="0.0.0.0", server_port=7860)