Spaces:
Sleeping
Sleeping
File size: 10,439 Bytes
256e811 | 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 | # app.py β GraphRAG Portfolio Agent | GPT-4o-mini + Neo4j
import streamlit as st
import os
st.set_page_config(
page_title="GraphRAG β Portfolio Agent",
page_icon="π€",
layout="wide",
initial_sidebar_state="expanded"
)
# ββ SESSION STATE βββββββββββββββββββββββββββββββββββββββββββββ
for k, v in {
'messages': [], 'neo4j': None, 'neo4j_ok': False,
'agent': None, 'schema_ok': False,
'openai_key': '', 'graph_stats': None,
}.items():
if k not in st.session_state:
st.session_state[k] = v
# ββ HELPERS βββββββββββββββββββββββββββββββββββββββββββββββββββ
def get_neo4j_config():
cfg = {}
try:
s = st.secrets
if 'NEO4J_URI' in s:
return {'uri': s['NEO4J_URI'], 'username': s['NEO4J_USERNAME'],
'password': s['NEO4J_PASSWORD'],
'database': s.get('NEO4J_DATABASE', 'neo4j')}
if 'neo4j' in s:
n = s['neo4j']
return {'uri': n.get('uri',''), 'username': n.get('username',''),
'password': n.get('password',''), 'database': n.get('database','neo4j')}
except Exception:
pass
return {'uri': os.getenv('NEO4J_URI',''), 'username': os.getenv('NEO4J_USERNAME',''),
'password': os.getenv('NEO4J_PASSWORD',''), 'database': os.getenv('NEO4J_DATABASE','neo4j')}
def get_openai_key():
try:
if 'OPENAI_API_KEY' in st.secrets:
return st.secrets['OPENAI_API_KEY']
except Exception:
pass
return os.getenv('OPENAI_API_KEY', st.session_state.openai_key)
def conectar_neo4j():
if st.session_state.neo4j is not None:
return st.session_state.neo4j
try:
from neo4j import GraphDatabase
cfg = get_neo4j_config()
if not all([cfg['uri'], cfg['username'], cfg['password']]):
return None
driver = GraphDatabase.driver(cfg['uri'], auth=(cfg['username'], cfg['password']))
with driver.session(database=cfg['database']) as s:
s.run('RETURN 1')
st.session_state.neo4j = (driver, cfg['database'])
st.session_state.neo4j_ok = True
return st.session_state.neo4j
except Exception as e:
st.session_state.neo4j_ok = False
return None
def criar_agente(openai_key):
try:
from graph_agent import GraphRAGAgent
conn = st.session_state.neo4j
if conn is None:
return None
driver, database = conn
return GraphRAGAgent(openai_key, driver, database)
except Exception as e:
st.error(f"Erro ao criar agente: {e}")
return None
# ββ SIDEBAR βββββββββββββββββββββββββββββββββββββββββββββββββββ
with st.sidebar:
st.title("π€ GraphRAG Agent")
st.caption("GPT-4o-mini Β· Neo4j Β· Cypher")
st.divider()
# Neo4j status
conn = conectar_neo4j()
if st.session_state.neo4j_ok:
st.success("ποΈ Neo4j Conectado")
else:
st.error("Neo4j offline")
st.info("Configure NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD nos secrets do HF Space.")
st.divider()
# OpenAI Key
st.markdown("#### π OpenAI API Key")
key_input = st.text_input(
"Chave", type="password",
value=st.session_state.openai_key,
placeholder="sk-...",
help="Ou configure OPENAI_API_KEY nos secrets do HF"
)
if key_input:
st.session_state.openai_key = key_input
openai_key = get_openai_key()
if openai_key:
st.success("β
OpenAI Key configurada")
else:
st.warning("OpenAI Key nΓ£o configurada")
st.divider()
# Popular grafo
st.markdown("#### 𧬠Base de Conhecimento")
if st.session_state.neo4j_ok:
if st.button("π₯ Popular Grafo Neo4j", use_container_width=True, type="primary"):
try:
from graph_knowledge import popular_neo4j, verificar_schema
driver, database = st.session_state.neo4j
with st.spinner("Populando Neo4j com projetos GNN..."):
n, erros = popular_neo4j(driver, database)
nos, arestas = verificar_schema(driver, database)
st.session_state.graph_stats = {'nos': nos, 'arestas': arestas}
st.session_state.schema_ok = True
st.success(f"β
{n} statements executados!")
if erros:
st.warning(f"{len(erros)} avisos (normal em re-execuΓ§Γ΅es)")
except Exception as e:
st.error(f"Erro: {e}")
if st.session_state.graph_stats:
st.markdown("**NΓ³s no grafo:**")
for item in st.session_state.graph_stats['nos']:
col1, col2 = st.columns([3,1])
col1.caption(item['tipo'])
col2.markdown(f"**{item['total']}**")
total_ar = sum(a['total'] for a in st.session_state.graph_stats['arestas'])
st.caption(f"Total de arestas: **{total_ar}**")
else:
st.info("Conecte o Neo4j primeiro")
st.divider()
if st.button("ποΈ Limpar Chat", use_container_width=True):
st.session_state.messages = []
st.rerun()
# ββ MAIN ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
st.title("π€ Portfolio GraphRAG Agent")
st.markdown("Pergunte qualquer coisa sobre os **5 projetos de GNN** β o agente consulta o Neo4j com Cypher gerado pelo GPT e responde.")
st.divider()
# Alertas de configuraΓ§Γ£o
if not get_openai_key():
st.warning("β¬
οΈ Configure sua OpenAI API Key na sidebar para usar o agente.")
if not st.session_state.neo4j_ok:
st.error("Neo4j nΓ£o conectado. Configure as credenciais na sidebar.")
if st.session_state.neo4j_ok and not st.session_state.schema_ok:
st.info("β¬
οΈ Clique em **Popular Grafo Neo4j** na sidebar para carregar a base de conhecimento.")
# ββ COMO FUNCIONA βββββββββββββββββββββββββββββββββββββββββββββ
if not st.session_state.messages:
with st.expander("βΉοΈ Como funciona o GraphRAG?", expanded=True):
c1, c2, c3 = st.columns(3)
with c1:
st.markdown("**1. VocΓͺ pergunta**")
st.markdown("Em linguagem natural, sobre qualquer projeto, tecnologia ou conceito do portfΓ³lio.")
with c2:
st.markdown("**2. GPT gera Cypher**")
st.markdown("O GPT-4o-mini analisa o schema do grafo e gera uma query Cypher para o Neo4j.")
with c3:
st.markdown("**3. Neo4j responde**")
st.markdown("A query Γ© executada, o contexto Γ© passado de volta ao GPT que formula a resposta final.")
st.markdown("### π‘ SugestΓ΅es de perguntas")
sugestoes = [
"Quais projetos usam PyTorch Geometric?",
"Qual projeto tem maior AUC?",
"Me explique o DOMINANT",
"Qual a diferenΓ§a entre HetGNN e GraphSAGE?",
"Quais papers sΓ£o referenciados?",
"Que conceitos o TGN implementa?",
"Qual projeto usa dado real?",
"Como funciona Inductive Learning?",
]
cols = st.columns(4)
for i, sug in enumerate(sugestoes):
with cols[i % 4]:
if st.button(sug, key=f"sug_{i}", use_container_width=True):
st.session_state.pending_question = sug
st.rerun()
# ββ CHAT HISTORY βββββββββββββββββββββββββββββββββββββββββββββ
for msg in st.session_state.messages:
with st.chat_message(msg["role"], avatar="π§" if msg["role"]=="user" else "π€"):
if msg["role"] == "assistant" and msg.get("cypher"):
with st.expander(f"π Cypher gerado ({msg.get('n_resultados', 0)} resultados)", expanded=False):
st.code(msg["cypher"], language="cypher")
st.markdown(msg["content"])
# ββ INPUT βββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Processa pergunta pendente (de botΓ£o de sugestΓ£o)
pergunta_pendente = st.session_state.pop("pending_question", None)
pergunta = st.chat_input("Pergunte sobre os projetos de GNN...") or pergunta_pendente
if pergunta and get_openai_key() and st.session_state.neo4j_ok:
# Mostra mensagem do usuΓ‘rio
with st.chat_message("user", avatar="π§"):
st.markdown(pergunta)
st.session_state.messages.append({"role": "user", "content": pergunta})
# Cria agente e responde
agent = criar_agente(get_openai_key())
if agent:
with st.chat_message("assistant", avatar="π€"):
with st.spinner("Consultando grafo Neo4j..."):
try:
resultado = agent.responder(pergunta)
with st.expander(
f"π Cypher gerado ({len(resultado['resultados'])} resultados)",
expanded=False
):
st.code(resultado["cypher"], language="cypher")
st.markdown(resultado["resposta"])
st.session_state.messages.append({
"role": "assistant",
"content": resultado["resposta"],
"cypher": resultado["cypher"],
"n_resultados": len(resultado["resultados"]),
})
except Exception as e:
msg_erro = f"Erro ao processar: {e}"
st.error(msg_erro)
st.session_state.messages.append({
"role": "assistant", "content": msg_erro})
else:
st.error("NΓ£o foi possΓvel criar o agente. Verifique as configuraΓ§Γ΅es.")
elif pergunta and not get_openai_key():
st.warning("Configure a OpenAI API Key na sidebar.")
elif pergunta and not st.session_state.neo4j_ok:
st.error("Neo4j nΓ£o conectado.") |