AgenteHelpN8n / app.py
Jeice's picture
Retirado: **Status:** {'✅ Sistema Pronto' if sucesso else '❌ ' + mensagem}
8362a0d verified
raw
history blame
10.5 kB
"""
🤖 N8n Assistant - Versão Corrigida (SEM ERRO DE IMAGEM)
Chatbot inteligente para dúvidas sobre n8n - Compatível com Hugging Face Spaces
CORREÇÃO APLICADA:
- Removido componente Image problemático que causava erro 404
- Mantida toda funcionalidade do sistema
"""
import os
import yaml
import json
import logging
import time
from typing import Optional, Tuple
import gradio as gr
# Configurar logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Importações com tratamento de erro
try:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from huggingface_hub import snapshot_download
logger.info("✅ Bibliotecas importadas com sucesso")
except ImportError as e:
logger.error(f"❌ Erro ao importar bibliotecas: {e}")
raise
class N8nAssistant:
"""Assistente N8n simplificado e funcional"""
def __init__(self):
self.index = None
self.query_engine = None
self.docs_dir = None
self.inicializado = False
def setup_openai(self) -> bool:
"""Configurar OpenAI"""
try:
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
logger.error("❌ OPENAI_API_KEY não encontrada")
return False
os.environ["OPENAI_API_KEY"] = api_key
logger.info("✅ OpenAI configurada")
return True
except Exception as e:
logger.error(f"❌ Erro OpenAI: {e}")
return False
def extrair_conteudo_arquivos(self, pasta: str) -> str:
"""Extrair conteúdo dos arquivos"""
texto_final = ""
if not os.path.exists(pasta):
logger.error(f"❌ Pasta não encontrada: {pasta}")
return ""
for root, dirs, files in os.walk(pasta):
for file in files:
caminho_arquivo = os.path.join(root, file)
try:
if file.endswith(('.yml', '.yaml')):
with open(caminho_arquivo, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
texto = yaml.dump(data, allow_unicode=True)
texto_final += f"\n\n### Arquivo: {file}\n{texto}"
elif file.endswith('.json'):
with open(caminho_arquivo, 'r', encoding='utf-8') as f:
data = json.load(f)
texto = json.dumps(data, indent=2, ensure_ascii=False)
texto_final += f"\n\n### Arquivo: {file}\n{texto}"
elif file.endswith(('.md', '.txt')):
with open(caminho_arquivo, 'r', encoding='utf-8') as f:
texto = f.read()
texto_final += f"\n\n### Arquivo: {file}\n{texto}"
except Exception as e:
logger.warning(f"⚠️ Erro ao ler {file}: {e}")
continue
return texto_final
def gerar_documentacao(self, pasta_origem: str) -> bool:
"""Gerar arquivo de documentação"""
try:
texto = self.extrair_conteudo_arquivos(pasta_origem)
if not texto.strip():
logger.warning("⚠️ Nenhum conteúdo encontrado")
return False
with open("documentacao.txt", 'w', encoding='utf-8') as f:
f.write(texto)
logger.info("✅ Documentação gerada")
return True
except Exception as e:
logger.error(f"❌ Erro ao gerar documentação: {e}")
return False
def baixar_docs(self) -> bool:
"""Baixar documentação do HF"""
try:
logger.info("📥 Baixando documentação...")
self.docs_dir = snapshot_download(
repo_id="Jeice/n8n-docs-v2",
repo_type="dataset"
)
logger.info("✅ Download concluído")
return True
except Exception as e:
logger.error(f"❌ Erro no download: {e}")
return False
def criar_index(self) -> bool:
"""Criar índice vetorial"""
try:
# Carregar documentos
if not os.path.exists("documentacao.txt"):
logger.error("❌ documentacao.txt não encontrado")
return False
documents = SimpleDirectoryReader(input_files=["documentacao.txt"]).load_data()
if not documents:
logger.error("❌ Nenhum documento carregado")
return False
# Configurar LLM
Settings.llm = OpenAI(
model="gpt-3.5-turbo",
temperature=0.1,
system_prompt=(
"Você é um assistente especialista em n8n. "
"Responda sempre em português do Brasil, de forma clara e objetiva, "
"baseado exclusivamente na documentação fornecida. "
"Se não souber, diga que não há informações suficientes."
)
)
Settings.embed_model = OpenAIEmbedding()
# Criar índice
logger.info("🧠 Criando índice...")
self.index = VectorStoreIndex.from_documents(documents)
self.query_engine = self.index.as_query_engine()
logger.info("✅ Índice criado")
return True
except Exception as e:
logger.error(f"❌ Erro ao criar índice: {e}")
return False
def inicializar(self) -> Tuple[bool, str]:
"""Inicializar sistema completo"""
try:
# 1. Configurar OpenAI
if not self.setup_openai():
return False, "Erro na configuração OpenAI"
# 2. Baixar docs
if not self.baixar_docs():
return False, "Erro no download da documentação"
# 3. Gerar documentação
if not self.gerar_documentacao(self.docs_dir):
return False, "Erro ao processar documentação"
# 4. Criar índice
if not self.criar_index():
return False, "Erro ao criar índice"
self.inicializado = True
return True, "Sistema inicializado com sucesso"
except Exception as e:
logger.error(f"❌ Erro na inicialização: {e}")
return False, f"Erro: {str(e)}"
def responder(self, pergunta: str) -> str:
"""Responder pergunta"""
if not pergunta or not pergunta.strip():
return "⚠️ Por favor, digite uma pergunta."
if not self.inicializado or not self.query_engine:
return "❌ Sistema não inicializado. Recarregue a página."
try:
logger.info(f"🤔 Pergunta: {pergunta[:50]}...")
response = self.query_engine.query(pergunta)
return str(response)
except Exception as e:
logger.error(f"❌ Erro ao responder: {e}")
return f"❌ Erro ao processar pergunta: {str(e)}"
# Inicializar sistema
logger.info("🚀 Inicializando N8n Assistant...")
assistant = N8nAssistant()
sucesso, mensagem = assistant.inicializar()
if sucesso:
logger.info(f"✅ {mensagem}")
else:
logger.error(f"❌ {mensagem}")
def processar_pergunta(pergunta: str) -> str:
"""Wrapper para Gradio"""
if not sucesso:
return f"❌ Sistema não inicializado: {mensagem}"
return assistant.responder(pergunta)
# Interface Gradio - SEM COMPONENTE IMAGE PROBLEMÁTICO
with gr.Blocks(theme=gr.themes.Soft(), title="N8n Assistant") as demo:
# Cabeçalho
gr.Markdown(
f"""
# 🤖 N8n Assistant
Assistente inteligente para dúvidas sobre **n8n** baseado na documentação oficial.
"""
#**Status:** {'✅ Sistema Pronto' if sucesso else '❌ ' + mensagem}
)
# Layout principal - SEM IMAGEM
with gr.Row():
with gr.Column(scale=1):
# REMOVIDO: componente gr.Image que causava erro 404
gr.Markdown("### 🤖 N8n Bot")
with gr.Column(scale=4):
gr.Markdown("## Como posso ajudar você com o n8n?")
with gr.Row():
with gr.Column(scale=3):
input_box = gr.Textbox(
label="Sua pergunta",
placeholder="Ex: Como criar um workflow no n8n?",
lines=3
)
with gr.Row():
enviar_btn = gr.Button("🚀 Perguntar", variant="primary")
limpar_btn = gr.Button("🧹 Limpar")
with gr.Column(scale=4):
output_box = gr.Textbox(
label="Resposta",
placeholder="Sua resposta aparecerá aqui...",
lines=12
)
# Exemplos
with gr.Accordion("💡 Exemplos de Perguntas", open=False):
gr.Markdown(
"""
- Como criar um workflow no n8n?
- Para que serve o node HTTP Request?
- Como integrar n8n com Google Sheets?
- Como configurar webhooks no n8n?
- Quais são as melhores práticas para workflows?
- Como debugar erros nos nodes?
- Como usar condições nos workflows?
- Quais nodes usar para automação de email?
"""
)
# Eventos
enviar_btn.click(
fn=processar_pergunta,
inputs=input_box,
outputs=output_box
)
limpar_btn.click(
lambda: ("", ""),
None,
[input_box, output_box]
)
input_box.submit(
fn=processar_pergunta,
inputs=input_box,
outputs=output_box
)
# Lançar aplicação
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
show_error=True
)