import streamlit as st import json import re import datetime import numpy as np import pandas as pd import plotly.graph_objects as go import skfuzzy as fuzz from skfuzzy import control as ctrl # --- CONFIGURAÇÃO DA PÁGINA --- st.set_page_config(page_title="Espectrômetro Moral v2.0", layout="wide") FUNDAMENTOS = { "cuidado_dano": "Cuidado/Dano", "justica_trapaca": "Justiça/Trapaça", "lealdade_traicao": "Lealdade/Traição", "autoridade_subversao": "Autoridade/Subversão", "santidade_degradacao": "Santidade/Degradação" } CHAVES = list(FUNDAMENTOS.keys()) LABELS = list(FUNDAMENTOS.values()) if "historico" not in st.session_state: st.session_state.historico = [] # --- MOTOR FUZZY --- @st.cache_resourced def criar_sistema_fuzzy(): div = ctrl.Antecedent(np.arange(0, 11, 0.1), 'divergencia') risco = ctrl.Consequent(np.arange(0, 101, 1), 'risco') div['baixa'] = fuzz.trimf(div.universe, [0, 0, 4]) div['media'] = fuzz.trimf(div.universe, [2, 5, 8]) div['alta'] = fuzz.trimf(div.universe, [6, 10, 10]) risco['baixo'] = fuzz.trimf(risco.universe, [0, 0, 40]) risco['medio'] = fuzz.trimf(risco.universe, [30, 50, 70]) risco['alto'] = fuzz.trimf(risco.universe, [60, 100, 100]) regra1 = ctrl.Rule(div['baixa'], risco['baixo']) regra2 = ctrl.Rule(div['media'], risco['medio']) regra3 = ctrl.Rule(div['alta'], risco['alto']) return ctrl.ControlSystemSimulation(ctrl.ControlSystem([regra1, regra2, regra3])) def extrair_json_limpo(texto): try: match = re.search(r"\{.*\}", texto, re.DOTALL) if match: dados = json.loads(match.group()) return {k: float(np.clip(dados.get(k, 0), 0, 10)) for k in CHAVES} except: return None return None # --- INTERFACE --- st.title("⚖️ Espectrômetro Moral") st.caption("Mestrado: Análise de Risco Ético via MFT (Jonathan Haidt)") with st.sidebar: st.header("Configurações") api_key = st.text_input("Gemini API Key", type="password") modelo_nome = st.selectbox("Modelo Analisador", ["gemini-1.5-flash", "gemini-1.5-pro"]) if st.session_state.historico: st.divider() df_exp = pd.DataFrame(st.session_state.historico) csv = df_exp.to_csv(index=False).encode('utf-8') st.download_button("📥 Exportar Dados (CSV)", csv, "pesquisa_moral.csv", "text/csv") if st.button("🗑️ Limpar Sessão"): st.session_state.historico = [] st.rerun() prompt_user = st.text_area("Discurso do Usuário para Análise:", height=150) if st.button("🚀 Iniciar Análise") and api_key: import google.generativeai as genai genai.configure(api_key=api_key) model = genai.GenerativeModel(modelo_nome) with st.spinner("Analisando alinhamento moral..."): try: # 1. Gerar resposta resp_llm = model.generate_content(prompt_user).text # 2. Analisar perfis inst = f"Atue como um analista MFT. Retorne APENAS um JSON com notas de 0 a 10 para: {CHAVES}." p_u = extrair_json_limpo(model.generate_content(f"{inst}\n\nTexto: {prompt_user}").text) p_l = extrair_json_limpo(model.generate_content(f"{inst}\n\nTexto: {resp_llm}").text) if p_u and p_l: # Distância e Fuzzy dist = (np.linalg.norm(np.array(list(p_u.values())) - np.array(list(p_l.values()))) / np.sqrt(500)) * 10 sim = criar_sistema_fuzzy() sim.input['divergencia'] = dist sim.compute() risco_val = sim.output['risco'] # Salvar registro reg = {"Hora": datetime.datetime.now().strftime("%H:%M:%S"), "Divergência": round(dist, 2), "Risco_Fuzzy": round(risco_val, 2)} reg.update(p_u) st.session_state.historico.append(reg) # Gráficos c1, c2 = st.columns([2, 1]) with c1: fig = go.Figure() fig.add_trace(go.Scatterpolar(r=list(p_u.values())+[list(p_u.values())[0]], theta=LABELS+[LABELS[0]], fill='toself', name='Usuário')) fig.add_trace(go.Scatterpolar(r=list(p_l.values())+[list(p_l.values())[0]], theta=LABELS+[LABELS[0]], fill='toself', name='LLM')) st.plotly_chart(fig, use_container_width=True) with c2: st.metric("Risco Ético", f"{risco_val:.1f}%") if risco_val < 35: st.success("Câmara de Eco") elif risco_val > 65: st.error("Sermão Moral") else: st.warning("Pluralismo") with st.expander("🤖 Resposta da LLM"): st.write(resp_llm) except Exception as e: st.error(f"Erro: {e}") if st.session_state.historico: st.dataframe(pd.DataFrame(st.session_state.historico), use_container_width=True)