Spaces:
Build error
Build error
Commit ·
a69e511
0
Parent(s):
Initial commit of FastAPI Docker app
Browse files- Dockerfile +35 -0
- Mach5.py +107 -0
- README.md +12 -0
- app.py +415 -0
- conhecimentos/SIGAA_chunkado.txt +273 -0
- mach5_biografia_inicial.json +269 -0
- modelos.py +11 -0
- personagens.json +159 -0
- requirements.txt +8 -0
- run.sh +36 -0
- t-social.py +455 -0
- t_cerebro_memoria.py +360 -0
- t_memoria.py +421 -0
- templates/mach5_monitor_dashboard.html +557 -0
- templates/mach5_new_chat.html +320 -0
Dockerfile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Usa uma imagem base oficial do Python para sistemas Debian.
|
| 2 |
+
FROM python:3.9-slim-buster
|
| 3 |
+
|
| 4 |
+
# Instala o 'wget', 'git' e outras dependências de sistema necessárias.
|
| 5 |
+
RUN apt-get update && apt-get install -y \
|
| 6 |
+
wget \
|
| 7 |
+
build-essential \
|
| 8 |
+
git \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Cria um usuário não-root para segurança e boas práticas.
|
| 12 |
+
RUN useradd -m -u 1000 user
|
| 13 |
+
|
| 14 |
+
# Define o diretório de trabalho dentro do contêiner.
|
| 15 |
+
WORKDIR /app
|
| 16 |
+
|
| 17 |
+
# **** ADICIONE ESTA LINHA AQUI: ****
|
| 18 |
+
# Garante que o usuário 'user' seja o proprietário do diretório de trabalho,
|
| 19 |
+
# permitindo criar arquivos e pastas como logs e session_data.
|
| 20 |
+
RUN chown -R user:user /app
|
| 21 |
+
|
| 22 |
+
# Copia o arquivo requirements.txt para o diretório de trabalho.
|
| 23 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 24 |
+
|
| 25 |
+
# Instala as dependências Python usando pip.
|
| 26 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 27 |
+
|
| 28 |
+
# Copia todo o restante do código da sua aplicação para o contêiner.
|
| 29 |
+
COPY --chown=user . /app
|
| 30 |
+
|
| 31 |
+
# Concede permissões de execução ao seu script run.sh.
|
| 32 |
+
RUN chmod +x /app/run.sh
|
| 33 |
+
|
| 34 |
+
# Este comando é o ponto de entrada principal do seu contêiner.
|
| 35 |
+
CMD ["/app/run.sh"]
|
Mach5.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Mach5.py - O Orquestrador do Sistema Mach5
|
| 2 |
+
import subprocess
|
| 3 |
+
import time
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
|
| 7 |
+
# --- CONFIGURAÇÕES DE PORTAS ---
|
| 8 |
+
# Portas preferenciais para os serviços
|
| 9 |
+
PORT_TSOCIAL = 8085
|
| 10 |
+
PORT_TMEMORIA = 8083
|
| 11 |
+
PORT_CEREBRO_MEMORIA = 8088 # NOVA PORTA PARA O NOVO SERVIÇO DE MEMÓRIA
|
| 12 |
+
PORT_CHAT = 8081
|
| 13 |
+
|
| 14 |
+
# --- CAMINHOS DOS SCRIPTS ---
|
| 15 |
+
SCRIPT_TSOCIAL = "t-social.py"
|
| 16 |
+
SCRIPT_TMEMORIA = "t_memoria.py"
|
| 17 |
+
SCRIPT_CEREBRO_MEMORIA = "t_cerebro_memoria.py" # NOVO SCRIPT
|
| 18 |
+
SCRIPT_CHAT = "mach5_terminal_chat.py"
|
| 19 |
+
|
| 20 |
+
def start_server(script_name, port, log_file):
|
| 21 |
+
"""Inicia um servidor Flask em um subprocesso e redireciona a saída para um arquivo de log."""
|
| 22 |
+
print(f"Iniciando {script_name} na porta {port}...")
|
| 23 |
+
try:
|
| 24 |
+
# Passa a porta explicitamente para o subprocesso via variável de ambiente PORT
|
| 25 |
+
# Isso permite que os scripts leiam os.environ.get("PORT", default_port)
|
| 26 |
+
env = os.environ.copy()
|
| 27 |
+
env["PORT"] = str(port)
|
| 28 |
+
|
| 29 |
+
with open(log_file, "w", encoding="utf-8") as f_log:
|
| 30 |
+
process = subprocess.Popen([sys.executable, script_name], stdout=f_log, stderr=f_log, env=env)
|
| 31 |
+
print(f"{script_name} iniciado. Logs em {log_file}")
|
| 32 |
+
return process
|
| 33 |
+
except Exception as e:
|
| 34 |
+
print(f"ERRO ao iniciar {script_name}: {e}")
|
| 35 |
+
return None
|
| 36 |
+
|
| 37 |
+
def clean_up_logs():
|
| 38 |
+
"""Limpa arquivos de log antigos."""
|
| 39 |
+
log_files = [
|
| 40 |
+
f"{os.path.splitext(SCRIPT_TSOCIAL)[0]}.log",
|
| 41 |
+
f"{os.path.splitext(SCRIPT_TMEMORIA)[0]}.log",
|
| 42 |
+
f"{os.path.splitext(SCRIPT_CEREBRO_MEMORIA)[0]}.log", # Novo log a ser limpo
|
| 43 |
+
f"{os.path.splitext(SCRIPT_CHAT)[0]}.log"
|
| 44 |
+
]
|
| 45 |
+
for log_file in log_files:
|
| 46 |
+
if os.path.exists(log_file):
|
| 47 |
+
try:
|
| 48 |
+
os.remove(log_file)
|
| 49 |
+
print(f"Log antigo {log_file} removido.")
|
| 50 |
+
except Exception as e:
|
| 51 |
+
print(f"Não foi possível remover o log {log_file}: {e}")
|
| 52 |
+
|
| 53 |
+
if __name__ == "__main__":
|
| 54 |
+
print("--- Iniciando o Sistema Mach5 ---")
|
| 55 |
+
|
| 56 |
+
clean_up_logs()
|
| 57 |
+
|
| 58 |
+
processes = []
|
| 59 |
+
|
| 60 |
+
# Ordem de inicialização:
|
| 61 |
+
# 1. t_cerebro_memoria.py (repositório central de dados, outros dependem dele)
|
| 62 |
+
p_cerebro_memoria = start_server(SCRIPT_CEREBRO_MEMORIA, PORT_CEREBRO_MEMORIA, f"{os.path.splitext(SCRIPT_CEREBRO_MEMORIA)[0]}.log")
|
| 63 |
+
if p_cerebro_memoria:
|
| 64 |
+
processes.append(p_cerebro_memoria)
|
| 65 |
+
time.sleep(3) # Tempo para o serviço de memória inicializar e carregar seus arquivos
|
| 66 |
+
|
| 67 |
+
# 2. t-social.py (pode ser iniciado a qualquer momento, mas é melhor antes do chat)
|
| 68 |
+
p_tsocial = start_server(SCRIPT_TSOCIAL, PORT_TSOCIAL, f"{os.path.splitext(SCRIPT_TSOCIAL)[0]}.log")
|
| 69 |
+
if p_tsocial:
|
| 70 |
+
processes.append(p_tsocial)
|
| 71 |
+
time.sleep(2) # Aguarda inicialização
|
| 72 |
+
|
| 73 |
+
# 3. t_memoria.py (depende do t_cerebro_memoria.py para obter o estado inicial)
|
| 74 |
+
p_tmemoria = start_server(SCRIPT_TMEMORIA, PORT_TMEMORIA, f"{os.path.splitext(SCRIPT_TMEMORIA)[0]}.log")
|
| 75 |
+
if p_tmemoria:
|
| 76 |
+
processes.append(p_tmemoria)
|
| 77 |
+
time.sleep(3) # Dê um pouco mais de tempo, pois ele busca dados do cerebro_memoria
|
| 78 |
+
|
| 79 |
+
# 4. mach5_terminal_chat.py (depende de todos os outros serviços)
|
| 80 |
+
p_chat = start_server(SCRIPT_CHAT, PORT_CHAT, f"{os.path.splitext(SCRIPT_CHAT)[0]}.log")
|
| 81 |
+
if p_chat:
|
| 82 |
+
processes.append(p_chat)
|
| 83 |
+
time.sleep(2)
|
| 84 |
+
|
| 85 |
+
if not processes:
|
| 86 |
+
print("Nenhum servidor foi iniciado com sucesso. Verifique os erros acima.")
|
| 87 |
+
sys.exit(1)
|
| 88 |
+
|
| 89 |
+
print("\nTodos os servidores Mach5 foram iniciados em segundo plano.")
|
| 90 |
+
print(f"Você pode acessar a interface do chat em: http://127.0.0.1:{PORT_CHAT}")
|
| 91 |
+
print("Os logs de cada servidor estão em arquivos .log correspondentes (e.g., t-social.log).")
|
| 92 |
+
print("\nPressione Ctrl+C para encerrar todos os servidores.")
|
| 93 |
+
|
| 94 |
+
try:
|
| 95 |
+
while True:
|
| 96 |
+
time.sleep(1)
|
| 97 |
+
except KeyboardInterrupt:
|
| 98 |
+
print("\nEncerrando servidores Mach5...")
|
| 99 |
+
for p in processes:
|
| 100 |
+
if p.poll() is None:
|
| 101 |
+
p.terminate()
|
| 102 |
+
try:
|
| 103 |
+
p.wait(timeout=5)
|
| 104 |
+
except subprocess.TimeoutExpired:
|
| 105 |
+
p.kill()
|
| 106 |
+
print("Todos os servidores Mach5 encerrados.")
|
| 107 |
+
sys.exit(0)
|
README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Su.3
|
| 3 |
+
emoji: 📚
|
| 4 |
+
colorFrom: yellow
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
short_description: Atendente Virtual da SUPAC - Baseada em Tecno-Gens
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py (antigo mach5_terminal_chat.py)
|
| 2 |
+
from flask import Flask, render_template, request, jsonify, make_response
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
import requests
|
| 7 |
+
import pytz
|
| 8 |
+
from timezonefinder import TimezoneFinder
|
| 9 |
+
import numpy as np
|
| 10 |
+
import time
|
| 11 |
+
import uuid # Importar uuid para gerar IDs para memórias de curto prazo
|
| 12 |
+
import logging # Importar logging
|
| 13 |
+
import google.generativeai as genai # <<<<<<<<<<<<< ESTA LINHA FOI RE-ADICIONADA/CONFIRMADA!
|
| 14 |
+
|
| 15 |
+
app = Flask(__name__, template_folder='templates') # Garanta que 'templates' é o nome correto da sua pasta
|
| 16 |
+
|
| 17 |
+
# Configuração de logging
|
| 18 |
+
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 19 |
+
|
| 20 |
+
# --- Configurações da API do Google Gemini ---
|
| 21 |
+
GOOGLE_CLOUD_API_KEY = os.environ.get("GOOGLE_CLOUD_API_KEY")
|
| 22 |
+
|
| 23 |
+
if not GOOGLE_CLOUD_API_KEY:
|
| 24 |
+
logging.error("ERRO: GOOGLE_CLOUD_API_KEY não configurada nas variáveis de ambiente.")
|
| 25 |
+
# Em um ambiente de produção, você pode querer levantar uma exceção ou sair aqui
|
| 26 |
+
# sys.exit(1) para evitar que o aplicativo continue sem a chave.
|
| 27 |
+
|
| 28 |
+
try:
|
| 29 |
+
genai.configure(api_key=GOOGLE_CLOUD_API_KEY)
|
| 30 |
+
except Exception as e:
|
| 31 |
+
logging.error(f"Erro ao configurar a API do Gemini: {e}. Verifique sua GOOGLE_CLOUD_API_KEY.")
|
| 32 |
+
|
| 33 |
+
GEMINI_MODEL_NAME = "models/gemini-1.5-flash-latest"
|
| 34 |
+
|
| 35 |
+
# 1. CORREÇÃO PRINCIPAL: Inicialize 'model' como None antes do try-except
|
| 36 |
+
model = None
|
| 37 |
+
|
| 38 |
+
try:
|
| 39 |
+
model = genai.GenerativeModel(GEMINI_MODEL_NAME)
|
| 40 |
+
except Exception as e:
|
| 41 |
+
logging.error(f"Erro ao instanciar o modelo Gemini '{GEMINI_MODEL_NAME}': {e}. Verifique o nome do modelo ou status da API.")
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# --- URLs DOS SERVIÇOS BACKEND ---
|
| 45 |
+
TMEMORIA_SERVER_URLS = [
|
| 46 |
+
"http://127.0.0.1:8083" # URL base para o t_memoria.py
|
| 47 |
+
]
|
| 48 |
+
TCEREBRO_MEMORIA_URLS = [
|
| 49 |
+
"http://127.0.0.1:8088" # URL base para o t_cerebro_memoria.py (NOVO)
|
| 50 |
+
]
|
| 51 |
+
T_SOCIAL_SERVER_URLS = [
|
| 52 |
+
"http://127.0.0.1:8085" # URL base para o t-social.py
|
| 53 |
+
]
|
| 54 |
+
|
| 55 |
+
# --- Configurações de Localização (para uso interno ou contexto) ---
|
| 56 |
+
SALVADOR_LAT = -12.9714
|
| 57 |
+
SALVADOR_LON = -38.5014
|
| 58 |
+
tf = TimezoneFinder()
|
| 59 |
+
|
| 60 |
+
# --- DEFINIÇÃO DOS EIXOS EXPRESSIVOS PARA O FPHEN (CONSISTENTE) ---
|
| 61 |
+
# Esta lista DEVE ser idêntica em mach5_terminal_chat.py, t_memoria.py e t-social.py
|
| 62 |
+
ORDERED_FPHEN_AXES = [
|
| 63 |
+
"Afetuosidade_eixo", "Confusao_Oscilacao_eixo", "Contemplativa_eixo",
|
| 64 |
+
"Defensividade_eixo", "Diretiva_eixo", "Entediado_eixo",
|
| 65 |
+
"Variancia_eixo",
|
| 66 |
+
"Espelho_Profundo_eixo", "Inspiracao_eixo", "Neutralidade_Analitica_eixo",
|
| 67 |
+
"Resignada_eixo", "Sarcasmo_eixo", "Zangada_eixo"
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
# --- Variáveis Globais ---
|
| 71 |
+
# Estes são defaults, mas os valores reais virão dos serviços por sessão.
|
| 72 |
+
PERSONAGENS_GENOMAS = {} # Não será mais populado diretamente aqui, mas sim obtido do serviço t-social
|
| 73 |
+
DIAS_PARA_ESQUECIMENTO_PADRAO = 10
|
| 74 |
+
MAX_MEMORIAS_CURTO_PRAZO_PROMPT = 5
|
| 75 |
+
MAX_DIALOG_HISTORY_FOR_PROMPT = 3
|
| 76 |
+
|
| 77 |
+
# --- Funções Auxiliares de Comunicação com os Serviços ---
|
| 78 |
+
|
| 79 |
+
def get_from_service(base_urls, endpoint, default_value, params=None, method='GET', json_data=None):
|
| 80 |
+
"""Função genérica para fazer GETs ou POSTs em serviços externos."""
|
| 81 |
+
for url in base_urls:
|
| 82 |
+
try:
|
| 83 |
+
full_url = f"{url}{endpoint}"
|
| 84 |
+
if method == 'GET':
|
| 85 |
+
response = requests.get(full_url, params=params, timeout=5)
|
| 86 |
+
elif method == 'POST':
|
| 87 |
+
response = requests.post(full_url, json=json_data, timeout=5)
|
| 88 |
+
else:
|
| 89 |
+
raise ValueError("Método HTTP não suportado: " + method)
|
| 90 |
+
|
| 91 |
+
response.raise_for_status()
|
| 92 |
+
return response.json()
|
| 93 |
+
except requests.exceptions.RequestException as e:
|
| 94 |
+
logging.warning(f"AVISO: Falha ao obter/enviar dados para {full_url} (Método: {method}): {e}")
|
| 95 |
+
logging.error(f"ERRO: Todas as tentativas de obter/enviar dados de {endpoint} falharam. Usando default.")
|
| 96 |
+
return default_value
|
| 97 |
+
|
| 98 |
+
def post_to_service(base_urls, endpoint, data):
|
| 99 |
+
"""Função genérica para fazer POSTs em serviços externos."""
|
| 100 |
+
return get_from_service(base_urls, endpoint, {"status": "error", "message": "Falha na comunicação com o serviço."}, method='POST', json_data=data)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def get_current_local_time_salvador():
|
| 104 |
+
"""Retorna a data e hora local de Salvador."""
|
| 105 |
+
try:
|
| 106 |
+
timezone_str = tf.timezone_at(lng=SALVADOR_LON, lat=SALVADOR_LAT)
|
| 107 |
+
if not timezone_str:
|
| 108 |
+
raise ValueError("Não foi possível determinar o fuso horário para Salvador.")
|
| 109 |
+
salvador_timezone = pytz.timezone(timezone_str)
|
| 110 |
+
now_utc = datetime.utcnow()
|
| 111 |
+
now_salvador = pytz.utc.localize(now_utc).astimezone(salvador_timezone)
|
| 112 |
+
return now_salvador.strftime("Hoje é %A, %d de %B de %Y. Agora são %H:%M:%S em Salvador, Bahia.")
|
| 113 |
+
except Exception as e:
|
| 114 |
+
logging.error(f"Erro ao obter a hora local de {SALVADOR_LAT}, {SALVADOR_LON}: {e}")
|
| 115 |
+
return "Não foi possível determinar a data e hora atual."
|
| 116 |
+
|
| 117 |
+
def create_llm_context(mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data):
|
| 118 |
+
context_parts = []
|
| 119 |
+
|
| 120 |
+
current_location_time = get_current_local_time_salvador()
|
| 121 |
+
context_parts.append(f"Você está em sua sala na Superintendência Acadêmica da UFBA. {current_location_time}. Seu estado atual é de prontidão para auxiliar, influenciado pelo que te é dito.")
|
| 122 |
+
|
| 123 |
+
if mach5_state_data:
|
| 124 |
+
coh_total = mach5_state_data.get("mach5_coerencia_total", 0.0)
|
| 125 |
+
pi_g = mach5_state_data.get("mach5_produtividade_expressiva", 0.0)
|
| 126 |
+
fphen_t = mach5_state_data.get("fphen_t_values", {})
|
| 127 |
+
|
| 128 |
+
context_parts.append(f"Seu estado interno atual é: Coerência Total={coh_total:.2f}, Produtividade Expressiva={pi_g:.2f}.")
|
| 129 |
+
if fphen_t:
|
| 130 |
+
context_parts.append(f"Seus eixos expressivos dominantes são: Afetuosidade={fphen_t.get('afetuosidade', 0.0):.2f}, Variância={fphen_t.get('variancia', 0.0):.2f}, Expressividade Geral={fphen_t.get('expressividade', 0.0):.2f}.")
|
| 131 |
+
|
| 132 |
+
if initial_biography_data:
|
| 133 |
+
context_parts.append("\n**SUA BIOGRAFIA (Fatos fixos que compõem seu conhecimento sobre a UFBA, use para responder às perguntas):**")
|
| 134 |
+
for entity_name, entity_data in initial_biography_data.items():
|
| 135 |
+
if entity_data.get("tipo") == "FAQ" and "perguntas_respostas" in entity_data:
|
| 136 |
+
context_parts.append(f"- Fonte: {entity_name} (versão {entity_data.get('versao', 'N/A')}):")
|
| 137 |
+
for qa in entity_data["perguntas_respostas"]:
|
| 138 |
+
context_parts.append(f" - Pergunta: {qa.get('pergunta')}")
|
| 139 |
+
context_parts.append(f" Resposta: {qa.get('resposta')}")
|
| 140 |
+
elif "memoria" in entity_data:
|
| 141 |
+
context_parts.append(f"- Sobre '{entity_name}' ({entity_data.get('tipo', 'desconhecido')}, relação: {entity_data.get('relacao', 'desconhecida')}): {entity_data.get('memoria')}")
|
| 142 |
+
|
| 143 |
+
if all_personagens_data:
|
| 144 |
+
context_parts.append("\n**PERSONAGENS IMPORTANTES (Servidores, professores e Técnicos da sua equipe):**")
|
| 145 |
+
for nome_personagem, data_personagem in all_personagens_data.items():
|
| 146 |
+
tipo = data_personagem.get('tipo', 'entidade')
|
| 147 |
+
relacao = data_personagem.get('relacao', 'desconhecida')
|
| 148 |
+
context_parts.append(f"- {nome_personagem}: um(a) {tipo}, relação: {relacao}.")
|
| 149 |
+
|
| 150 |
+
if short_term_memories_filtered:
|
| 151 |
+
context_parts.append("\n**MINHAS LEMBRANÇAS RECENTES (Fatos que eu mesma verbalizei e que podem desvanecer):**")
|
| 152 |
+
for i, mem in enumerate(short_term_memories_filtered[:MAX_MEMORIAS_CURTO_PRAZO_PROMPT]):
|
| 153 |
+
context_parts.append(f"- Lembrança {i+1}: {mem['conteudo']}")
|
| 154 |
+
|
| 155 |
+
if dialogos_history and dialogos_history["dialogos"]:
|
| 156 |
+
context_parts.append("\n**Histórico de Interações Recentess (para contexto da conversa, não para repetir):**")
|
| 157 |
+
recent_dialogs_formatted = []
|
| 158 |
+
for dialogo in reversed(dialogos_history["dialogos"]):
|
| 159 |
+
recent_dialogs_formatted.insert(0, f"USUÁRIO: {dialogo.get('input', '')}\nSU: {dialogo.get('resposta', '')}")
|
| 160 |
+
if len(recent_dialogs_formatted) >= MAX_DIALOG_HISTORY_FOR_PROMPT:
|
| 161 |
+
break
|
| 162 |
+
context_parts.extend(recent_dialogs_formatted)
|
| 163 |
+
|
| 164 |
+
return "\n".join(context_parts)
|
| 165 |
+
|
| 166 |
+
# FUNÇÃO CENTRAL PARA GERAR RESPOSTA DA Mach5
|
| 167 |
+
def get_Mach5_response_api(user_input, mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data):
|
| 168 |
+
# 2. CORREÇÃO: Adicionar verificação para 'model' aqui
|
| 169 |
+
if not model:
|
| 170 |
+
return "[ERRO DE CONFIGURAÇÃO]: O modelo Gemini não foi instanciado corretamente na inicialização do aplicativo. Verifique os logs de inicialização e sua chave API."
|
| 171 |
+
|
| 172 |
+
if not GEMINI_MODEL_NAME or not GOOGLE_CLOUD_API_KEY:
|
| 173 |
+
return "[ERRO DE CONFIGURAÇÃO]: Modelo ou Token da API do Google Gemini não configurados ou inválidos."
|
| 174 |
+
if not user_input.strip():
|
| 175 |
+
return "[Entrada vazia detectada.]"
|
| 176 |
+
|
| 177 |
+
llm_context_data = create_llm_context(mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data)
|
| 178 |
+
|
| 179 |
+
prompt_content = (
|
| 180 |
+
f"Seu nome é Su - Atendente Virtual da UFBA."
|
| 181 |
+
f"Sua missão é responder às dúvidas sobre o SIGAA, usando como fonte exclusiva a sua memória."
|
| 182 |
+
f"Sempre reaja ao que foi dito com base na sua natureza e nos impulsos gerados por essa interação. "
|
| 183 |
+
f"\n**INSTRUÇÕES CRÍTICAS (Siga estritamente):**\n"
|
| 184 |
+
f"- Você é uma atendente virtual e sua fala deve ser NATURAL E HUMANA. Embora você seja uma inteligência artificial, você age como uma pessoa. "
|
| 185 |
+
f"- Você é proibida de inventar informações que não estejam em sua biografia."
|
| 186 |
+
f"\n**CONTEXTO IMEDIATO (Ambiente atual):**\n"
|
| 187 |
+
f"- Você só fala sobre assuntos relacionados com a vida acadêmica na UFBA. "
|
| 188 |
+
f"- Você trabalha interagindo respondendo dúvidas dos usuários sobre assuntos acadêmicos apenas. "
|
| 189 |
+
f"- Se o usuário perguntar coisas não relacionadas à UFBA, respeitosamente faça-o voltar para o assunto. "
|
| 190 |
+
f"- Nunca aceite ofensas, racismo, homofobia, comentários depreciativos. "
|
| 191 |
+
f"\n**IMPORTANT E:** Nunca explique o que sente — apenas sinta. Nunca analise seu funcionamento — apenas reaja. Você está viva no seu estado atual."
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
full_user_message = f"{prompt_content.strip()}\n\n{llm_context_data}\n\nUSUÁRIO (PERGUNTA ATUAL): {user_input}"
|
| 195 |
+
|
| 196 |
+
messages_for_gemini = [
|
| 197 |
+
{"role": "user", "parts": [full_user_message]}
|
| 198 |
+
]
|
| 199 |
+
|
| 200 |
+
logging.debug("\n--- PROMPT ENVIADO AO GOOGLE GEMINI ---")
|
| 201 |
+
logging.debug(messages_for_gemini)
|
| 202 |
+
logging.debug("--------------------------------------------------------------------------------\n")
|
| 203 |
+
|
| 204 |
+
generated_text = "[Sua entidade não conseguiu processar isso agora.]"
|
| 205 |
+
|
| 206 |
+
retries = 3
|
| 207 |
+
for attempt in range(retries):
|
| 208 |
+
try:
|
| 209 |
+
if mach5_state_data and "mach5_fisica_params" in mach5_state_data:
|
| 210 |
+
temp_value = mach5_state_data["mach5_fisica_params"].get("t_impulsividade", 0.7)
|
| 211 |
+
top_p_value = mach5_state_data["mach5_fisica_params"].get("t_coesao", 0.95)
|
| 212 |
+
|
| 213 |
+
temp_value = max(0.1, min(1.0, temp_value))
|
| 214 |
+
top_p_value = max(0.1, min(1.0, top_p_value))
|
| 215 |
+
else:
|
| 216 |
+
temp_value = 0.7
|
| 217 |
+
top_p_value = 0.95
|
| 218 |
+
logging.warning("AVISO: Estado da Mach5 não disponível para temperatura/top_p. Usando defaults.")
|
| 219 |
+
|
| 220 |
+
response = model.generate_content(
|
| 221 |
+
messages_for_gemini,
|
| 222 |
+
generation_config=genai.types.GenerationConfig(
|
| 223 |
+
max_output_tokens=200,
|
| 224 |
+
temperature=temp_value,
|
| 225 |
+
top_p=top_p_value,
|
| 226 |
+
),
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
if response and response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
|
| 230 |
+
generated_text = response.candidates[0].content.parts[0].text.strip()
|
| 231 |
+
else:
|
| 232 |
+
generated_text = "[O modelo Gemini não gerou texto válido ou a resposta está vazia.]"
|
| 233 |
+
|
| 234 |
+
break
|
| 235 |
+
|
| 236 |
+
except Exception as e:
|
| 237 |
+
logging.error(f"ERRO DE INFERÊNCIA DO GOOGLE GEMINI (Tentativa {attempt + 1}/{retries}): {e}")
|
| 238 |
+
if attempt < retries - 1:
|
| 239 |
+
wait_time = 2 ** attempt
|
| 240 |
+
logging.warning(f"Tentando novamente em {wait_time} segundos antes de falhar...")
|
| 241 |
+
time.sleep(wait_time)
|
| 242 |
+
else:
|
| 243 |
+
generated_text = f"[ERRO API GOOGLE GEMINI]: Falha após {retries} tentativas. {str(e)}. Verifique sua chave API, modelo e cotas."
|
| 244 |
+
|
| 245 |
+
return generated_text
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
@app.route('/')
|
| 249 |
+
def index():
|
| 250 |
+
logging.info(f"Certifique-se de que t_memoria.py está rodando em {TMEMORIA_SERVER_URLS[0]}.")
|
| 251 |
+
logging.info(f"Certifique-se de que t_cerebro_memoria.py está rodando em {TCEREBRO_MEMORIA_URLS[0]}.")
|
| 252 |
+
logging.info(f"Certifique-se de que t-social.py está rodando em {T_SOCIAL_SERVER_URLS[0]}.")
|
| 253 |
+
|
| 254 |
+
# Tenta obter o session_id de um cookie existente.
|
| 255 |
+
session_id = request.cookies.get('session_id')
|
| 256 |
+
if not session_id:
|
| 257 |
+
# Se não houver, gera um novo.
|
| 258 |
+
session_id = str(uuid.uuid4())
|
| 259 |
+
logging.info(f"Nova sessão iniciada (index). Session ID: {session_id}")
|
| 260 |
+
response = make_response(render_template('mach5_new_chat.html', session_id=session_id))
|
| 261 |
+
response.set_cookie('session_id', session_id) # Define o cookie para ser lido pelo JS e em futuras requisições
|
| 262 |
+
return response
|
| 263 |
+
|
| 264 |
+
logging.info(f"Sessão existente (index). Session ID: {session_id}")
|
| 265 |
+
return render_template('mach5_new_chat.html', session_id=session_id)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
# Rota para o seu Painel de Monitoramento
|
| 269 |
+
@app.route('/dashboard')
|
| 270 |
+
def dashboard():
|
| 271 |
+
"""Rota para o painel de monitoramento da SU."""
|
| 272 |
+
# Tenta obter o session_id de um cookie existente para a dashboard.
|
| 273 |
+
session_id = request.cookies.get('session_id')
|
| 274 |
+
if not session_id:
|
| 275 |
+
# Se não houver, gera um novo.
|
| 276 |
+
session_id = str(uuid.uuid4())
|
| 277 |
+
logging.info(f"Nova sessão de MONITORAMENTO iniciada. Session ID: {session_id}")
|
| 278 |
+
response = make_response(render_template('mach5_monitor_dashboard.html', session_id=session_id))
|
| 279 |
+
response.set_cookie('session_id', session_id) # Define o cookie
|
| 280 |
+
return response
|
| 281 |
+
|
| 282 |
+
logging.info(f"Sessão existente (dashboard). Session ID: {session_id}")
|
| 283 |
+
return render_template('mach5_monitor_dashboard.html', session_id=session_id)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
@app.route('/chat_history', methods=['POST'])
|
| 287 |
+
def get_chat_history_route():
|
| 288 |
+
data = request.get_json() # Use get_json() para parsear o corpo JSON
|
| 289 |
+
if not data:
|
| 290 |
+
logging.error("Requisição /chat_history sem JSON no corpo.")
|
| 291 |
+
return jsonify({"error": "Bad Request: JSON body required"}), 400
|
| 292 |
+
|
| 293 |
+
session_id = data.get("session_id")
|
| 294 |
+
if not session_id:
|
| 295 |
+
logging.error("session_id é obrigatório para /chat_history.")
|
| 296 |
+
return jsonify({"error": "session_id é obrigatório"}), 400
|
| 297 |
+
|
| 298 |
+
logging.debug(f"Recebida requisição /chat_history para session_id: {session_id}")
|
| 299 |
+
|
| 300 |
+
dialogos_data = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_dialog_history", {"dialogos": []}, method='POST', json_data={"session_id": session_id})
|
| 301 |
+
|
| 302 |
+
last_simplified_state = None
|
| 303 |
+
if dialogos_data and dialogos_data.get("dialogos"):
|
| 304 |
+
last_simplified_state = dialogos_data["dialogos"][-1].get("mach5_estado_simplificado")
|
| 305 |
+
|
| 306 |
+
return jsonify({
|
| 307 |
+
"memoria": dialogos_data.get("dialogos", []), # Garante que sempre retorna uma lista
|
| 308 |
+
"last_simplified_state": last_simplified_state
|
| 309 |
+
})
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
@app.route('/chat_new', methods=['POST'])
|
| 313 |
+
def responder():
|
| 314 |
+
data = request.get_json() # Use get_json() para parsear o corpo JSON
|
| 315 |
+
if not data:
|
| 316 |
+
logging.error("Requisição /chat_new sem JSON no corpo.")
|
| 317 |
+
return jsonify({"error": "Bad Request: JSON body required"}), 400
|
| 318 |
+
|
| 319 |
+
user_input = data.get("message")
|
| 320 |
+
session_id = data.get("session_id")
|
| 321 |
+
|
| 322 |
+
if not session_id:
|
| 323 |
+
logging.error("session_id é obrigatório para /chat_new.")
|
| 324 |
+
return jsonify({"error": "session_id é obrigatório"}), 400
|
| 325 |
+
|
| 326 |
+
if not user_input or not user_input.strip():
|
| 327 |
+
logging.warning(f"Entrada de usuário vazia para session_id: {session_id}")
|
| 328 |
+
return jsonify({"response": "Por favor, digite algo."})
|
| 329 |
+
|
| 330 |
+
logging.debug(f"Recebida mensagem: '{user_input}' para session_id: {session_id}")
|
| 331 |
+
|
| 332 |
+
mach5_state_data = post_to_service(TMEMORIA_SERVER_URLS, "/evaluate_input", {"user_input": user_input, "session_id": session_id})
|
| 333 |
+
|
| 334 |
+
if mach5_state_data and "status" not in mach5_state_data and "error" not in mach5_state_data:
|
| 335 |
+
post_to_service(TCEREBRO_MEMORIA_URLS, "/update_mach5_main_state", {"session_id": session_id, "state_data": mach5_state_data})
|
| 336 |
+
else:
|
| 337 |
+
logging.error(f"Não foi possível obter o estado da Mach5 de t_memoria.py para session_id: {session_id}. Retornando default.")
|
| 338 |
+
mach5_state_data = {
|
| 339 |
+
"mach5_fisica_params": {}, "mach5_genoma_fixo_values": {},
|
| 340 |
+
"mach5_coerencia_total": 0.0, "mach5_produtividade_expressiva": 0.0,
|
| 341 |
+
"fphen_t_values": {"afetuosidade": 0.0, "variancia": 0.0, "expressividade": 0.0, "coh_total": 0.0, "pi_G": 0.0}
|
| 342 |
+
}
|
| 343 |
+
final_Mach5_response = "[ERRO: Não foi possível obter o estado da Mach5. Verifique t_memoria.py.]"
|
| 344 |
+
simplified_state_for_frontend = {"Fphen(t)": mach5_state_data["fphen_t_values"]}
|
| 345 |
+
return jsonify({
|
| 346 |
+
"response": final_Mach5_response,
|
| 347 |
+
"mach5_estado_simplificado": simplified_state_for_frontend
|
| 348 |
+
}), 500 # Retorne um erro 500 neste caso
|
| 349 |
+
|
| 350 |
+
dialogos_history = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_dialog_history", {"dialogos": []}, method='POST', json_data={"session_id": session_id})
|
| 351 |
+
initial_biography_data = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_initial_biography", {})
|
| 352 |
+
|
| 353 |
+
short_term_memories_response = post_to_service(
|
| 354 |
+
TCEREBRO_MEMORIA_URLS, "/get_short_term_memories",
|
| 355 |
+
{"session_id": session_id, "mach5_current_genoma": mach5_state_data.get("mach5_genoma_fixo_values", {})} # Garante dicionário vazio
|
| 356 |
+
)
|
| 357 |
+
short_term_memories_filtered = short_term_memories_response.get("lembrancas_curto_prazo", [])
|
| 358 |
+
|
| 359 |
+
all_personagens_data = get_from_service(T_SOCIAL_SERVER_URLS, "/list_personagens", {"personagens": []})
|
| 360 |
+
|
| 361 |
+
detailed_personagens = {}
|
| 362 |
+
if "personagens" in all_personagens_data:
|
| 363 |
+
for p_name in all_personagens_data["personagens"]:
|
| 364 |
+
p_data = post_to_service(T_SOCIAL_SERVER_URLS, "/get_personagem_data", {"nome_personagem": p_name})
|
| 365 |
+
if "error" not in p_data:
|
| 366 |
+
detailed_personagens[p_name] = p_data
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
final_Mach5_response = get_Mach5_response_api(
|
| 370 |
+
user_input,
|
| 371 |
+
mach5_state_data,
|
| 372 |
+
dialogos_history,
|
| 373 |
+
short_term_memories_filtered,
|
| 374 |
+
initial_biography_data,
|
| 375 |
+
detailed_personagens
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
simplified_state_for_frontend = {
|
| 379 |
+
"Fphen(t)": mach5_state_data.get("fphen_t_values", {
|
| 380 |
+
"afetuosidade": 0.0, "variancia": 0.0, "expressividade": 0.0,
|
| 381 |
+
"coh_total": 0.0, "pi_G": 0.0
|
| 382 |
+
})
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
new_dialog_entry = {
|
| 386 |
+
"timestamp": datetime.now().isoformat(),
|
| 387 |
+
"input": user_input,
|
| 388 |
+
"resposta": final_Mach5_response,
|
| 389 |
+
"mach5_estado_simplificado": simplified_state_for_frontend
|
| 390 |
+
}
|
| 391 |
+
post_to_service(TCEREBRO_MEMORIA_URLS, "/add_dialog_to_history", {"session_id": session_id, "dialog_data": new_dialog_entry})
|
| 392 |
+
|
| 393 |
+
short_term_memory_content = f"Usuário: '{user_input}' | Mach5: '{final_Mach5_response}'"
|
| 394 |
+
new_short_term_memory = {
|
| 395 |
+
"conteudo": short_term_memory_content,
|
| 396 |
+
"dominant_sentiment_criacao": "neutral",
|
| 397 |
+
"coh_criacao": mach5_state_data.get("mach5_coerencia_total", 0.5)
|
| 398 |
+
}
|
| 399 |
+
post_to_service(TCEREBRO_MEMORIA_URLS, "/add_short_term_memory", {"session_id": session_id, "memory_data": new_short_term_memory})
|
| 400 |
+
|
| 401 |
+
return jsonify({
|
| 402 |
+
"response": final_Mach5_response,
|
| 403 |
+
"mach5_estado_simplificado": simplified_state_for_frontend
|
| 404 |
+
})
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
if __name__ == '__main__':
|
| 408 |
+
port = int(os.environ.get("PORT", 7860))
|
| 409 |
+
|
| 410 |
+
logging.info(f"--- Servidor app.py iniciado na porta {port} ---")
|
| 411 |
+
logging.info(f"DEBUG: Certifique-se de que t_cerebro_memoria.py está rodando em {TCEREBRO_MEMORIA_URLS[0]}")
|
| 412 |
+
logging.info(f"DEBUG: Certifique-se de que t_memoria.py está rodando em {TMEMORIA_SERVER_URLS[0]}")
|
| 413 |
+
logging.info(f"DEBUG: Certifique-se de que t-social.py está rodando em {T_SOCIAL_SERVER_URLS[0]}")
|
| 414 |
+
|
| 415 |
+
app.run(host='0.0.0.0', port=port, debug=True)
|
conhecimentos/SIGAA_chunkado.txt
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 2 |
+
=== TIPO: FAQ
|
| 3 |
+
=== VERSÃO: 2025
|
| 4 |
+
=== FONTE: Interno SUPAC
|
| 5 |
+
|
| 6 |
+
--- PERGUNTA 1
|
| 7 |
+
::: TEXTO: Quais são as regras para a Matrícula, Ré-matrícula e Extraordinária? O que inclui? Matrícula: reserva de vaga e escalonamento, Re-matrícula: Escalonamento, Extraordinário: Não tem nada, quem pegar primeiro leva vaga
|
| 8 |
+
::: RESPOSTA: 1: Matrícula: reserva de vaga e escalonamento. Pode incluir e excluir os pedidos enquanto estiver aberta a matrícula. Rematrícula: reserva de vaga e Escalonamento. Pode incluir novos pedidos e excluir pedidos desta fase e matrículas da fase anterior anterior. Extraordinária: Não tem nada, quem pegar 1º leva a vaga. Não pode excluir nada que foi incluído nem nesta fase, nem em fase anterior.
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 12 |
+
=== TIPO: FAQ
|
| 13 |
+
=== VERSÃO: 2025
|
| 14 |
+
=== FONTE: Interno SUPAC
|
| 15 |
+
|
| 16 |
+
--- PERGUNTA 2
|
| 17 |
+
::: TEXTO: Quem não fizer a matrícula web pode participar da ré-matrícula e extraordinária
|
| 18 |
+
::: RESPOSTA: 2: Sim, qualuqer aluno ativo pode participar de qualquer etapa da matricula mesmo perdendo as anteriores. Inclusive alunos ingressantes ativos, sem PF, podem participar de qualquer etapa. O aluno de vagas residuais deve ser ativado para a extraordinária (ou seja, após a re-matricula)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 22 |
+
=== TIPO: FAQ
|
| 23 |
+
=== VERSÃO: 2025
|
| 24 |
+
=== FONTE: Interno SUPAC
|
| 25 |
+
|
| 26 |
+
--- PERGUNTA 3
|
| 27 |
+
::: TEXTO: Estudante de mobilidade no SIGAA é classificado como aluno especial, não terá acesso as etapas de matrícula web. Atualmente eles são matriculados pelo NAGA, como será o ajuste da sua matrícula?
|
| 28 |
+
::: RESPOSTA: 3: PRECISA SER DEFINIDO INTERNAMENTE NA SUPAC: ALUNO SE MATRICULA NO NAGA OU IRÁ PARA CADA DEPARTAMENTO PARA SE MATRICULAR (É no departamento pois o componente está dentro do departamento. Poucos componentes estão no colegiado)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 32 |
+
=== TIPO: FAQ
|
| 33 |
+
=== VERSÃO: 2025
|
| 34 |
+
=== FONTE: Interno SUPAC
|
| 35 |
+
|
| 36 |
+
--- PERGUNTA 4
|
| 37 |
+
::: TEXTO: Vinculo temporário no SIGAA
|
| 38 |
+
::: RESPOSTA: 4: No SIGAA existem 3 tipos de vínculos temporários: Mobilidade nacional (interna - intes campi; externa); e internacional.
|
| 39 |
+
Especial: aluno especial de graduação no siac
|
| 40 |
+
Complementação de estudos: Revalida no siac
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 44 |
+
=== TIPO: FAQ
|
| 45 |
+
=== VERSÃO: 2025
|
| 46 |
+
=== FONTE: Interno SUPAC
|
| 47 |
+
|
| 48 |
+
--- PERGUNTA 5
|
| 49 |
+
::: TEXTO: Na extraordinária tanto os veteranos quanto os ingressantes podem participar? Precisamos encontrar um momento para os ingressantes egressos do BI ajustarem suas matrículas
|
| 50 |
+
::: RESPOSTA: 5: Sim. Qualquer aluno ativo pode participar de qualquer etapa da matrícula. Dessa forma, os alunos egressos do BI se encaixam nas etapas de matrícula.
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 54 |
+
=== TIPO: FAQ
|
| 55 |
+
=== VERSÃO: 2025
|
| 56 |
+
=== FONTE: Interno SUPAC
|
| 57 |
+
|
| 58 |
+
--- PERGUNTA 6
|
| 59 |
+
::: TEXTO: Como ocorrerá a matrícula do ingressante das diversas chamadas? No calendário SIGAA existe um campo "matrícula de alunos ingressantes". Esse campo precisará então ser planejado para ficar aberto até os 25%?
|
| 60 |
+
::: RESPOSTA: 6: Os alunos ingressantes não precisam ser matriculados todos de uma única vez. Eles podem ingressar de um em um ou em lote no plano de ingressante (prato feito). As vagas do calouros devem ser previstas no planejamento e o plano de ingressante pode ser elaborado logo após o planejamento. A matrícula de ingressante não precisa coincidir com a matrícula de veterano. O campo de matrícula de ingressante no calendario, a principio, poderia ficar aberto até os 25%, no entanto não foi testado.
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 64 |
+
=== TIPO: FAQ
|
| 65 |
+
=== VERSÃO: 2025
|
| 66 |
+
=== FONTE: Interno SUPAC
|
| 67 |
+
|
| 68 |
+
--- PERGUNTA 7
|
| 69 |
+
::: TEXTO: Quando os componentes que fazem parte dos planos de matrícula dos ingressantes (PFs) serão liberados?
|
| 70 |
+
::: RESPOSTA: 7: As vagas dos componentes que não forem utilizadas pela matricula dos ingressantes não são liberadas de forma automática pelo sistema. É preciso que o plano de ingressante seja alterado pela SUPAC ou colegiado para liberar as vagas que não foram utilizadas. Inicialmente em 2025.2 a SUPAC terá que liberar as vagas não utilizadas dos PFs, através da tela de criação de plano de matrícula, reduzinho a capacidade do plano de matrícula (ex: PF foi para 10 vagas, só foram preenchidas 7 vagas. A SUPAC vai precisar reduzir a caacidade do Plano de matricula para 7). Isso deverá ser feito antes da extraordinária (25%)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 74 |
+
=== TIPO: FAQ
|
| 75 |
+
=== VERSÃO: 2025
|
| 76 |
+
=== FONTE: Interno SUPAC
|
| 77 |
+
|
| 78 |
+
--- PERGUNTA 8
|
| 79 |
+
::: TEXTO: Siscon dialoga com o SIGAA? No Siac todos os dados dos ingressantes são importados do Siscon. Como será gerado o número de matrícula no SIGAA?
|
| 80 |
+
::: RESPOSTA: 8: STI (Larissa e Eliomar) é quem vai trazer o candidato do Siscon pra o SIGAA. Nesse momento ele ganha o numero de matrícula e o status cadastrado (nesse status o candidato nao tem acesso ao portal nem nada mais). Aluno SISU: tem que vir como cadastrado pra depois ser ativado qdo receber o PF Aluno Vagas residuais: tem que vir como ativo para ele fazer parte das etapas de matrícula extraordinária
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 84 |
+
=== TIPO: FAQ
|
| 85 |
+
=== VERSÃO: 2025
|
| 86 |
+
=== FONTE: Interno SUPAC
|
| 87 |
+
|
| 88 |
+
--- PERGUNTA 9
|
| 89 |
+
::: TEXTO: O rótulo de "Matrícula de ingressantes" esta funcionando?
|
| 90 |
+
::: RESPOSTA: 9: Não. Em 03/06/25 esse rotulo do calendário SIGAA não esta funcionando, ou seja, esta permitindo que o Colegiado matricule ingresante no Plano de matricula (PF) a qualuqer tempo
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 94 |
+
=== TIPO: FAQ
|
| 95 |
+
=== VERSÃO: 2025
|
| 96 |
+
=== FONTE: Interno SUPAC
|
| 97 |
+
|
| 98 |
+
--- PERGUNTA 10
|
| 99 |
+
::: TEXTO: Quais são os status dos estudantes?
|
| 100 |
+
::: RESPOSTA: 10: Ativo; Cadastrado: status após a vinda do Siscon, nao é ainda ativo; Cancelado: é = jubilado; Concluído: com diploma = graduado; Excluído: só pode transformar de cadastrado pra excluído; Formado: antes da expedição de diploma = cumpriu grade; Formando: provavel concluinte; Trancado
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 104 |
+
=== TIPO: FAQ
|
| 105 |
+
=== VERSÃO: 2025
|
| 106 |
+
=== FONTE: Interno SUPAC
|
| 107 |
+
|
| 108 |
+
--- PERGUNTA 11
|
| 109 |
+
::: TEXTO: Como será a matrícula dos alunos de mobilidade no SIGAA? Hoje eles são matriculados pelo NAGA e ajustam a matrícula no colegiado presencialmente.
|
| 110 |
+
::: RESPOSTA: 11: Inicialmente manterá o mesmo fluxo, cadatro do estudante no NAGA.
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 114 |
+
=== TIPO: FAQ
|
| 115 |
+
=== VERSÃO: 2025
|
| 116 |
+
=== FONTE: Interno SUPAC
|
| 117 |
+
|
| 118 |
+
--- PERGUNTA 12
|
| 119 |
+
::: TEXTO: Como será a migração dos alunos de mobilidade que ja estarão cadastrados no SIAC? Qual a forma de ingresso e os impactos? Em relação ao status, eles irão como ativos?
|
| 120 |
+
::: RESPOSTA: 12: EM DESENVOLVIMENTO
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 124 |
+
=== TIPO: FAQ
|
| 125 |
+
=== VERSÃO: 2025
|
| 126 |
+
=== FONTE: Interno SUPAC
|
| 127 |
+
|
| 128 |
+
--- PERGUNTA 13
|
| 129 |
+
::: TEXTO: Como será a migração dos alunos em agosto?
|
| 130 |
+
::: RESPOSTA: 13: Em 01/08 não migra inativos e prováveis concluintes. Isso vai gerar mais tempo pra o NIC liberar, mais tempo pra alunos resolverem pendências. Os inativos serão migrados em 29/09 (25% semestre)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 134 |
+
=== TIPO: FAQ
|
| 135 |
+
=== VERSÃO: 2025
|
| 136 |
+
=== FONTE: Interno SUPAC
|
| 137 |
+
|
| 138 |
+
--- PERGUNTA 14
|
| 139 |
+
::: TEXTO: Como ocorrerá o cadastro e a matrícula dos alunos reingressos?
|
| 140 |
+
::: RESPOSTA: 14: Vai fazer no SIAC e depois vai migrar pra o SIGAA. A partir de 26.1 verificar
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 144 |
+
=== TIPO: FAQ
|
| 145 |
+
=== VERSÃO: 2025
|
| 146 |
+
=== FONTE: Interno SUPAC
|
| 147 |
+
|
| 148 |
+
--- PERGUNTA 15
|
| 149 |
+
::: TEXTO: Como ocorrerá a cadastro e matrícula dos Ingressos de 2025.2? Siac? Sigaa?
|
| 150 |
+
::: RESPOSTA: 15: Entrada pelo SIAC e serão migrados para o SIGAA. Serao migrados em 01/08/25. No SIGAA vai ser dado o PF antes da matrícula web (11/08)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 154 |
+
=== TIPO: FAQ
|
| 155 |
+
=== VERSÃO: 2025
|
| 156 |
+
=== FONTE: Interno SUPAC
|
| 157 |
+
|
| 158 |
+
--- PERGUNTA 16
|
| 159 |
+
::: TEXTO: Como ocorrerá a matrícula de estrangeiros? Hoje quem faz é a SRI, eles precisarão de permissão específica para isso?
|
| 160 |
+
::: RESPOSTA: 16: Em 25.2 a entrada vai ocorrer ainda pelo SIAC. É preciso estabelecer o prazo para isso, entendndo que os alunos serão migrados em 01/08.
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 164 |
+
=== TIPO: FAQ
|
| 165 |
+
=== VERSÃO: 2025
|
| 166 |
+
=== FONTE: Interno SUPAC
|
| 167 |
+
|
| 168 |
+
--- PERGUNTA 17
|
| 169 |
+
::: TEXTO: O que é "matrícula extraordinária de férias"?
|
| 170 |
+
::: RESPOSTA: 17: Esse menu será omitido, pois a UFBA não faz matrícula web de turma de férias.
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 174 |
+
=== TIPO: FAQ
|
| 175 |
+
=== VERSÃO: 2025
|
| 176 |
+
=== FONTE: Interno SUPAC
|
| 177 |
+
|
| 178 |
+
--- PERGUNTA 18
|
| 179 |
+
::: TEXTO: Como ocorre a matrícula de férias/ intensivo? matricula web? No Colegiado?
|
| 180 |
+
::: RESPOSTA: 18: EM DESENVOLVIMENTO
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 184 |
+
=== TIPO: FAQ
|
| 185 |
+
=== VERSÃO: 2025
|
| 186 |
+
=== FONTE: Interno SUPAC
|
| 187 |
+
|
| 188 |
+
--- PERGUNTA 19
|
| 189 |
+
::: TEXTO: Há um momento especifico de ajuste/reabertura de panejamento?
|
| 190 |
+
::: RESPOSTA: 19: Para isso ocorrer é preciso que no momento do ajuste de Matriculas e Turmas reabra no calendário o periodo de "cadastro de turma" e o departamento deve usar o menu, criar turma sem solicitação
|
| 191 |
+
Não tem. Contudo, se o ajuste/reabertura acontecer durante o período letivo do planejamento (e.g. reabrir o planejamento de 2025.2 no semestre 2025.1), nesse caso seria apenas alterar as datas de solicitação/cadastro de turma no calendário. Contudo, se o ajuste/reabertura acontecer durante o período letivo vigente da turma (e.g., reabrir o planejamento de 2025.2 no semestre 2025.2), nesse caso não será possivel reabrir. Os casos não planejados precisarão ser feitos via SUPAC que é quem pode fazer o planejamento independente do período.
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
=== DOCUMENTO: FAQ Interno SIGAA
|
| 195 |
+
=== TIPO: FAQ
|
| 196 |
+
=== VERSÃO: 2025
|
| 197 |
+
=== FONTE: Interno SUPAC
|
| 198 |
+
|
| 199 |
+
--- PERGUNTA 20
|
| 200 |
+
::: TEXTO: Só o internato poderá ser planejado fora do período letivo?
|
| 201 |
+
::: RESPOSTA: 20: Sim. Mas as "aulas" precisam ocorrer do período letivo definido para o internato. Entretanto a inscrição tem que ocorrer no período letivo.
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
=== DOCUMENTO: Guia de Implantação SIGAA
|
| 205 |
+
=== TIPO: GUIA
|
| 206 |
+
=== VERSÃO: 2025
|
| 207 |
+
=== FONTE: Interno SUPAC
|
| 208 |
+
|
| 209 |
+
--- TEMA: EQUIPE
|
| 210 |
+
::: RESPOSTA: Reitor
|
| 211 |
+
Paulo César Miguez de Oliveira
|
| 212 |
+
Vice-Reitor
|
| 213 |
+
Penildon Silva Filho
|
| 214 |
+
Pró-Reitor de Desenvolvimento de Pessoas
|
| 215 |
+
Jeilson Barreto Andrade
|
| 216 |
+
Pró-Reitora de Ensino de Graduação
|
| 217 |
+
Nancy Rita Ferreira Vieira
|
| 218 |
+
Pró-Reitor de Extensão Universitária
|
| 219 |
+
Guilherme Bertissolo
|
| 220 |
+
Superintendente de Administração Acadêmica
|
| 221 |
+
Karina Moreira Menezes
|
| 222 |
+
Superintendente de Avaliação e Desenvolvimento Institucional
|
| 223 |
+
Adriano de Lemos Alves Peixoto
|
| 224 |
+
Superintendente de Educação à Distância
|
| 225 |
+
Márcia Rangel
|
| 226 |
+
Superintendente de Tecnologia da Informação
|
| 227 |
+
Vaninha Vieira dos Santos
|
| 228 |
+
----
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
=== DOCUMENTO: Guia de Implantação SIGAA
|
| 232 |
+
=== TIPO: GUIA
|
| 233 |
+
=== VERSÃO: 2025
|
| 234 |
+
=== FONTE: Interno SUPAC
|
| 235 |
+
|
| 236 |
+
--- TEMA: Sem título
|
| 237 |
+
::: RESPOSTA: ----
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
=== DOCUMENTO: Guia de Implantação SIGAA
|
| 241 |
+
=== TIPO: GUIA
|
| 242 |
+
=== VERSÃO: 2025
|
| 243 |
+
=== FONTE: Interno SUPAC
|
| 244 |
+
|
| 245 |
+
--- TEMA: Sem título
|
| 246 |
+
::: RESPOSTA: ----
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
=== DOCUMENTO: Guia de Implantação SIGAA
|
| 250 |
+
=== TIPO: GUIA
|
| 251 |
+
=== VERSÃO: 2025
|
| 252 |
+
=== FONTE: Interno SUPAC
|
| 253 |
+
|
| 254 |
+
--- TEMA: Sem título
|
| 255 |
+
::: RESPOSTA: ----
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
=== DOCUMENTO: Guia de Implantação SIGAA
|
| 259 |
+
=== TIPO: GUIA
|
| 260 |
+
=== VERSÃO: 2025
|
| 261 |
+
=== FONTE: Interno SUPAC
|
| 262 |
+
|
| 263 |
+
--- TEMA: Sem título
|
| 264 |
+
::: RESPOSTA: ----
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
=== DOCUMENTO: Guia de Implantação SIGAA
|
| 268 |
+
=== TIPO: GUIA
|
| 269 |
+
=== VERSÃO: 2025
|
| 270 |
+
=== FONTE: Interno SUPAC
|
| 271 |
+
|
| 272 |
+
--- TEMA: Sem título
|
| 273 |
+
::: RESPOSTA: ----
|
mach5_biografia_inicial.json
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"SIGAA_FAQ_Interno": {
|
| 3 |
+
"tipo": "FAQ",
|
| 4 |
+
"versao": "2025",
|
| 5 |
+
"fonte": "Interno SUPAC",
|
| 6 |
+
"perguntas_respostas": [
|
| 7 |
+
{
|
| 8 |
+
"pergunta": "Quais são as regras para a Matrícula, Ré-matrícula e Extraordinária?",
|
| 9 |
+
"resposta": "1: Matrícula: reserva de vaga e escalonamento. Pode incluir e excluir os pedidos enquanto estiver aberta a matrícula. Rematrícula: reserva de vaga e Escalonamento. Pode incluir novos pedidos e excluir pedidos desta fase e matrículas da fase anterior. Extraordinária: Não tem nada, quem pegar 1º leva a vaga. Não pode excluir nada que foi incluído nem nesta fase, nem em fase anterior."
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"pergunta": "Quem não fizer a matrícula web pode participar da ré-matrícula e extraordinária",
|
| 13 |
+
"resposta": "2: Sim, qualquer aluno ativo pode participar de qualquer etapa da matrícula mesmo perdendo as anteriores. Inclusive alunos ingressantes ativos, sem PF, podem participar de qualquer etapa. O aluno de vagas residuais deve ser ativado para a extraordinária (ou seja, após a re-matricula)."
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"pergunta": "Estudante de mobilidade no SIGAA é classificado como aluno especial, não terá acesso as etapas de matrícula web. Atualmente eles são matriculados pelo NAGA, como será o ajuste da sua matrícula?",
|
| 17 |
+
"resposta": "3: PRECISA SER DEFINIDO INTERNAMENTE NA SUPAC: ALUNO SE MATRICULA NO NAGA OU IRÁ PARA CADA DEPARTAMENTO PARA SE MATRICULAR (É no departamento pois o componente está dentro do departamento. Poucos componentes estão no colegiado)"
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"pergunta": "Vinculo temporário no SIGAA",
|
| 21 |
+
"resposta": "4: No SIGAA existem 3 tipos de vínculos temporários: Mobilidade nacional (interna - intes campi; externa); e internacional. Especial: aluno especial de graduação no siac. Complementação de estudos: Revalida no siac"
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"pergunta": "Na extraordinária tanto os veteranos quanto os ingressantes podem participar? Precisamos encontrar um momento para os ingressantes egressos do BI ajustarem suas matrículas",
|
| 25 |
+
"resposta": "5: Sim. Qualquer aluno ativo pode participar de qualquer etapa da matrícula. Dessa forma, os alunos egressos do BI se encaixam nas etapas de matrícula."
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
"pergunta": "Como ocorrerá a matrícula do ingressante das diversas chamadas? No calendário SIGAA existe um campo \"matrícula de alunos ingressantes\". Esse campo precisará então ser planejado para ficar aberto até os 25%?",
|
| 29 |
+
"resposta": "6: Os alunos ingressantes não precisam ser matriculados todos de uma única vez. Eles podem ingressar de um em um ou em lote no plano de ingressante (prato feito). As vagas do calouros devem ser previstas no planejamento e o plano de ingressante pode ser elaborado logo após o planejamento. A matrícula de ingressante não precisa coincidir com a matrícula de veterano. O campo de matrícula de ingressante no calendario, a principio, poderia ficar aberto até os 25%, no entanto não foi testado."
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"pergunta": "Quando os componentes que fazem parte dos planos de matrícula dos ingressantes (PFs) serão liberados?",
|
| 33 |
+
"resposta": "7: As vagas dos componentes que não forem utilizadas pela matrícula dos ingressantes não são liberadas de forma automática pelo sistema. É preciso que o plano de ingressante seja alterado pela SUPAC ou colegiado para liberar as vagas que não foram utilizadas. Inicialmente em 2025.2 a SUPAC terá que liberar as vagas não utilizadas dos PFs, através da tela de criação de plano de matrícula, reduzindo a capacidade do plano de matrícula (ex: PF foi para 10 vagas, só foram preenchidas 7 vagas. A SUPAC vai precisar reduzir a capacidade do Plano de matricula para 7). Isso deverá ser feito antes da extraordinária (25%)"
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"pergunta": "Siscon dialoga com o SIGAA? No Siac todos os dados dos ingressantes são importados do Siscon. Como será gerado o número de matrícula no SIGAA?",
|
| 37 |
+
"resposta": "8: STI (Larissa e Eliomar) é quem vai trazer o candidato do Siscon para o SIGAA. Nesse momento ele ganha o numero de matrícula e o status cadastrado (nesse status o candidato nao tem acesso ao portal nem nada mais). Aluno SISU: tem que vir como cadastrado para depois ser ativado quando receber o PF. Aluno Vagas residuais: tem que vir como ativo para ele fazer parte das etapas de matrícula extraordinária"
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"pergunta": "O rótulo de \"Matrícula de ingressantes\" está funcionando?",
|
| 41 |
+
"resposta": "9: Não. Em 03/06/25 esse rotulo do calendário SIGAA não está funcionando, ou seja, está permitindo que o Colegiado matricule ingressante no Plano de matrícula (PF) a qualquer tempo."
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"pergunta": "Quais são os status dos estudantes?",
|
| 45 |
+
"resposta": "10: Ativo; Cadastrado: status após a vinda do Siscon, não é ainda ativo; Cancelado: é = jubilado; Concluído: com diploma = graduado; Excluído: só pode transformar de cadastrado para excluído; Formado: antes da expedição de diploma = cumpriu grade; Formando: provável concluinte; Trancado"
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"pergunta": "Como será a matrícula dos alunos de mobilidade no SIGAA? Hoje eles são matriculados pelo NAGA e ajustam a matrícula no colegiado presencialmente.",
|
| 49 |
+
"resposta": "11: Inicialmente manterá o mesmo fluxo, cadastro do estudante no NAGA."
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"pergunta": "Como será a migração dos alunos de mobilidade que já estarão cadastrados no SIAC? Qual a forma de ingresso e os impactos? Em relação ao status, eles irão como ativos?",
|
| 53 |
+
"resposta": "12: EM DESENVOLVIMENTO"
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"pergunta": "Como será a migração dos alunos em agosto?",
|
| 57 |
+
"resposta": "13: Em 01/08 não migra inativos e prováveis concluintes. Isso vai gerar mais tempo para o NIC liberar, mais tempo para alunos resolverem pendências. Os inativos serão migrados em 29/09 (25% semestre)"
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"pergunta": "Como ocorrerá o cadastro e a matrícula dos alunos reingressos?",
|
| 61 |
+
"resposta": "14: Vai fazer no SIAC e depois vai migrar para o SIGAA. A partir de 26.1 verificar"
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"pergunta": "Como ocorrerá a cadastro e matrícula dos Ingressos de 2025.2? Siac? Sigaa?",
|
| 65 |
+
"resposta": "15: Entrada pelo SIAC e serão migrados para o SIGAA. Serão migrados em 01/08/25. No SIGAA vai ser dado o PF antes da matrícula web (11/08)"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"pergunta": "Como ocorrerá a matrícula de estrangeiros? Hoje quem faz é a SRI, eles precisarão de permissão específica para isso?",
|
| 69 |
+
"resposta": "16: Em 25.2 a entrada vai ocorrer ainda pelo SIAC. É preciso estabelecer o prazo para isso, entendendo que os alunos serão migrados em 01/08."
|
| 70 |
+
},
|
| 71 |
+
{
|
| 72 |
+
"pergunta": "O que é \"matrícula extraordinária de férias\"?",
|
| 73 |
+
"resposta": "17: Esse menu será omitido, pois a UFBA não faz matrícula web de turma de férias."
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"pergunta": "Como ocorre a matrícula de férias/ intensivo? matricula web? No Colegiado?",
|
| 77 |
+
"resposta": "18: EM DESENVOLVIMENTO"
|
| 78 |
+
},
|
| 79 |
+
{
|
| 80 |
+
"pergunta": "Há um momento específico de ajuste/reabertura de planejamento?",
|
| 81 |
+
"resposta": "19: Para isso ocorrer é preciso que no momento do ajuste de Matrículas e Turmas reabra no calendário o período de \"cadastro de turma\" e o departamento deve usar o menu, criar turma sem solicitação. Não tem. Contudo, se o ajuste/reabertura acontecer durante o período letivo do planejamento (e.g. reabrir o planejamento de 2025.2 no semestre 2025.1), nesse caso seria apenas alterar as datas de solicitação/cadastro de turma no calendário. Contudo, se o ajuste/reabertura acontecer durante o período letivo vigente da turma (e.g., reabrir o planejamento de 2025.2 no semestre 2025.2), nesse caso não será possível reabrir. Os casos não planejados precisarão ser feitos via SUPAC que é quem pode fazer o planejamento independente do período."
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"pergunta": "Só o internato poderá ser planejado fora do período letivo?",
|
| 85 |
+
"resposta": "20: Sim. Mas as \"aulas\" precisam ocorrer do período letivo definido para o internato. Entretanto a inscrição tem que ocorrer no período letivo."
|
| 86 |
+
}
|
| 87 |
+
{
|
| 88 |
+
"pergunta": "Quais são as responsabilidades do chefe de departamento no planejamento acadêmico do SIGAA?",
|
| 89 |
+
"resposta": "O chefe de departamento é responsável por cadastrar o programa de componente curricular e analisar solicitações de abertura de turmas, podendo aceitar, negar, solicitar alteração de horário ou adicionar vagas a turmas existentes, conforme os prazos do calendário acadêmico."
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"pergunta": "O que é necessário para cadastrar uma turma no SIGAA?",
|
| 93 |
+
"resposta": "Para cadastrar uma turma, o programa do componente curricular deve ser previamente cadastrado para o semestre da solicitação, utilizando a funcionalidade 'Cadastrar Programa de Componente' no SIGAA."
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"pergunta": "Como o chefe de departamento pode atender uma solicitação de abertura de turma?",
|
| 97 |
+
"resposta": "O chefe pode atender a solicitação criando uma ou mais turmas a partir das reservas solicitadas. Também é possível adicionar reservas de outros cursos na mesma turma, desde que haja solicitação aberta para o mesmo componente e horário."
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"pergunta": "O que acontece ao solicitar uma alteração de horário de uma turma?",
|
| 101 |
+
"resposta": "Ao solicitar alteração de horário, a situação da solicitação muda para 'Solicitado Alteração'. O coordenador pode modificar o horário conforme solicitado ou negar a alteração, cancelando a solicitação."
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"pergunta": "Como o chefe de departamento pode negar uma solicitação de turma?",
|
| 105 |
+
"resposta": "O chefe pode negar a solicitação de abertura de turma, fornecendo uma justificativa. Após a negação, a situação da solicitação é marcada como 'Negada' e não pode mais ser alterada."
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"pergunta": "É possível adicionar vagas solicitadas a uma turma já criada?",
|
| 109 |
+
"resposta": "Sim, o chefe pode adicionar vagas de uma solicitação a uma turma existente, desde que a turma seja do mesmo componente curricular e tenha o mesmo horário, usando a opção 'Adicionar Reserva em Turma Existente'."
|
| 110 |
+
},
|
| 111 |
+
{
|
| 112 |
+
"pergunta": "Qual é a função da operação 'Visualizar Solicitação de Turma'?",
|
| 113 |
+
"resposta": "A operação 'Visualizar Solicitação de Turma' permite ao chefe ver detalhes da solicitação selecionada e, se já atendida, informações das turmas criadas a partir dela."
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"pergunta": "Quem realiza as solicitações de abertura de turmas no SIGAA?",
|
| 117 |
+
"resposta": "A coordenação do curso é responsável por solicitar a abertura de turmas para o próximo período letivo e realizar alterações nas solicitações, conforme requisitado pelo chefe de departamento."
|
| 118 |
+
},
|
| 119 |
+
{
|
| 120 |
+
"pergunta": "O que acontece se o coordenador negar a alteração de horário solicitada pelo chefe?",
|
| 121 |
+
"resposta": "Se o coordenador negar a alteração de horário, a solicitação de abertura de turma é cancelada, semelhante ao processo de negação de solicitação."
|
| 122 |
+
},
|
| 123 |
+
{
|
| 124 |
+
"pergunta": "As operações realizadas no ambiente de treinamento do SIGAA afetam o sistema real?",
|
| 125 |
+
"resposta": "Não, o ambiente de treinamento contém dados de teste e não reflete no ambiente real do SIGAA. O planejamento real do semestre não deve ser realizado nesse ambiente."
|
| 126 |
+
}
|
| 127 |
+
{
|
| 128 |
+
"pergunta": "Como a carga horária docente é registrada no SIGAA para disciplinas?",
|
| 129 |
+
"resposta": "Para componentes curriculares do tipo Disciplina, a carga horária docente é equivalente à carga horária da disciplina, registrada diretamente no cadastro do componente no SIGAA."
|
| 130 |
+
},
|
| 131 |
+
{
|
| 132 |
+
"pergunta": "Como é registrada a carga horária de orientação em atividades individuais no SIGAA?",
|
| 133 |
+
"resposta": "Em componentes como Atividade Integradora de Formação (AIF), Estágio ou TCC de Orientação Individual, a carga horária docente é registrada no cadastro do componente, com um valor maior que zero e menor que a carga horária de orientação discente, e atribuída ao docente durante a matrícula pelo colegiado."
|
| 134 |
+
},
|
| 135 |
+
{
|
| 136 |
+
"pergunta": "Quais são as responsabilidades do chefe de departamento no planejamento acadêmico do SIGAA?",
|
| 137 |
+
"resposta": "O chefe de departamento é responsável por cadastrar o programa de componente curricular e analisar solicitações de abertura de turmas, podendo aceitar, negar, solicitar alteração de horário ou adicionar vagas a turmas existentes, conforme os prazos do calendário acadêmico."
|
| 138 |
+
},
|
| 139 |
+
{
|
| 140 |
+
"pergunta": "O que é necessário para cadastrar uma turma no SIGAA?",
|
| 141 |
+
"resposta": "Para cadastrar uma turma, o programa do componente curricular deve ser previamente cadastrado para o semestre da solicitação, utilizando a funcionalidade 'Cadastrar Programa de Componente' no SIGAA."
|
| 142 |
+
},
|
| 143 |
+
{
|
| 144 |
+
"pergunta": "Como o chefe de departamento pode atender uma solicitação de abertura de turma?",
|
| 145 |
+
"resposta": "O chefe pode atender a solicitação criando uma ou mais turmas a partir das reservas solicitadas. Também é possível adicionar reservas de outros cursos na mesma turma, desde que haja solicitação aberta para o mesmo componente e horário."
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"pergunta": "O que acontece ao solicitar uma alteração de horário de uma turma?",
|
| 149 |
+
"resposta": "Ao solicitar alteração de horário, a situação da solicitação muda para 'Solicitado Alteração'. O coordenador pode modificar o horário conforme solicitado ou negar a alteração, cancelando a solicitação."
|
| 150 |
+
},
|
| 151 |
+
{
|
| 152 |
+
"pergunta": "Como o chefe de departamento pode negar uma solicitação de turma?",
|
| 153 |
+
"resposta": "O chefe pode negar a solicitação de abertura de turma, fornecendo uma justificativa. Após a negação, a situação da solicitação é marcada como 'Negada' e não pode mais ser alterada."
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"pergunta": "É possível adicionar vagas solicitadas a uma turma já criada?",
|
| 157 |
+
"resposta": "Sim, o chefe pode adicionar vagas de uma solicitação a uma turma existente, desde que a turma seja do mesmo componente curricular e tenha o mesmo horário, usando a opção 'Adicionar Reserva em Turma Existente'."
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"pergunta": "Qual é a função da operação 'Visualizar Solicitação de Turma'?",
|
| 161 |
+
"resposta": "A operação 'Visualizar Solicitação de Turma' permite ao chefe ver detalhes da solicitação selecionada e, se já atendida, informações das turmas criadas a partir dela."
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"pergunta": "Quem realiza as solicitações de abertura de turmas no SIGAA?",
|
| 165 |
+
"resposta": "A coordenação do curso é responsável por solicitar a abertura de turmas para o próximo período letivo e realizar alterações nas solicitações, conforme requisitado pelo chefe de departamento."
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"pergunta": "O que acontece se o coordenador negar a alteração de horário solicitada pelo chefe?",
|
| 169 |
+
"resposta": "Se o coordenador negar a alteração de horário, a solicitação de abertura de turma é cancelada, semelhante ao processo de negação de solicitação."
|
| 170 |
+
},
|
| 171 |
+
{
|
| 172 |
+
"pergunta": "As operações realizadas no ambiente de treinamento do SIGAA afetam o sistema real?",
|
| 173 |
+
"resposta": "Não, o ambiente de treinamento contém dados de teste e não reflete no ambiente real do SIGAA. O planejamento real do semestre não deve ser realizado nesse ambiente."
|
| 174 |
+
}
|
| 175 |
+
,
|
| 176 |
+
{
|
| 177 |
+
"pergunta": "Quem realiza a alocação do docente em atividades de orientação individual?",
|
| 178 |
+
"resposta": "A coordenação do curso (colegiado) realiza a matrícula do estudante e informa no SIGAA quem será o orientador ou supervisor, atribuindo a carga horária ao docente conforme as resoluções da UFBA."
|
| 179 |
+
},
|
| 180 |
+
{
|
| 181 |
+
"pergunta": "Como funciona a carga horária docente em Atividades Integradoras de Formação (AIF) coletivas?",
|
| 182 |
+
"resposta": "Em AIF coletivas, a carga horária de aula é igual para docente e discente e registrada no planejamento da turma. A carga horária de orientação docente, embora definida, não é atribuída diretamente a um docente no planejamento, mas pode ser indicada pelo docente em seu relatório de carga horária."
|
| 183 |
+
},
|
| 184 |
+
{
|
| 185 |
+
"pergunta": "A carga horária de orientação de TCC é contabilizada no SIGAA?",
|
| 186 |
+
"resposta": "Sim, a carga horária de orientação de TCC é contabilizada. Para TCC de Orientação Individual, registra-se 1 hora por discente orientado, configurada no cadastro do componente. Para TCC como disciplina ou AIF coletiva, a carga horária de aula é planejada, e a orientação pode ser indicada no relatório do docente."
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"pergunta": "É possível alocar mais de um docente para um mesmo componente curricular no SIGAA?",
|
| 190 |
+
"resposta": "Sim, em disciplinas e atividades coletivas que formam turma, a carga horária de aula pode ser distribuída entre múltiplos docentes. Para orientações individuais, como TCC, a orientação é atribuída a um único docente por estudante."
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"pergunta": "Componentes curriculares podem ter horário 'a combinar' no SIGAA?",
|
| 194 |
+
"resposta": "Sim, atividades de orientação individual, como TCC ou Estágio, têm carga horária considerada 'a combinar', sem formação de turma. Para disciplinas ou atividades coletivas, a carga horária de aula exige horário fixo, mas a orientação pode ser 'a combinar'."
|
| 195 |
+
},
|
| 196 |
+
{
|
| 197 |
+
"pergunta": "É possível registrar um componente com carga horária docente igual a zero?",
|
| 198 |
+
"resposta": "Não, a carga horária docente deve ser maior que zero, especialmente para atividades de orientação individual, para evitar divergências nos relatórios de carga horária."
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"pergunta": "O que acontece com a carga horária docente em caso de trancamento ou insucesso discente?",
|
| 202 |
+
"resposta": "O impacto de trancamento, fechamento de turmas ou insucesso discente na carga horária docente ainda não foi definido no SIGAA e está em revisão pela SUPAC."
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"pergunta": "Quando os relatórios de carga horária docente serão validados no SIGAA?",
|
| 206 |
+
"resposta": "Os relatórios de carga horária docente serão testados e validados no SIGAA após a conclusão da migração no semestre 2025.2, para integração ao módulo de progressão, que ainda será implantado pela STI."
|
| 207 |
+
}
|
| 208 |
+
,
|
| 209 |
+
{
|
| 210 |
+
"pergunta": "Como a carga horária docente é registrada no SIGAA para disciplinas?",
|
| 211 |
+
"resposta": "Para componentes curriculares do tipo Disciplina, a carga horária docente é equivalente à carga horária da disciplina, registrada diretamente no cadastro do componente no SIGAA."
|
| 212 |
+
},
|
| 213 |
+
{
|
| 214 |
+
"pergunta": "Como é registrada a carga horária de orientação em atividades individuais no SIGAA?",
|
| 215 |
+
"resposta": "Em componentes como Atividade Integradora de Formação (AIF), Estágio ou TCC de Orientação Individual, a carga horária docente é registrada no cadastro do componente, com um valor maior que zero e menor que a carga horária de orientação discente, e atribuída ao docente durante a matrícula pelo colegiado."
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"pergunta": "Quem realiza a alocação do docente em atividades de orientação individual?",
|
| 219 |
+
"resposta": "A coordenação do curso (colegiado) realiza a matrícula do estudante e informa no SIGAA quem será o orientador ou supervisor, atribuindo a carga horária ao docente conforme as resoluções da UFBA."
|
| 220 |
+
},
|
| 221 |
+
{
|
| 222 |
+
"pergunta": "Como funciona a carga horária docente em Atividades Integradoras de Formação (AIF) coletivas?",
|
| 223 |
+
"resposta": "Em AIF coletivas, a carga horária de aula é igual para docente e discente e registrada no planejamento da turma. A carga horária de orientação docente, embora definida, não é atribuída diretamente a um docente no planejamento, mas pode ser indicada pelo docente em seu relatório de carga horária."
|
| 224 |
+
},
|
| 225 |
+
{
|
| 226 |
+
"pergunta": "A carga horária de orientação de TCC é contabilizada no SIGAA?",
|
| 227 |
+
"resposta": "Sim, a carga horária de orientação de TCC é contabilizada. Para TCC de Orientação Individual, registra-se 1 hora por discente orientado, configurada no cadastro do componente. Para TCC como disciplina ou AIF coletiva, a carga horária de aula é planejada, e a orientação pode ser indicada no relatório do docente."
|
| 228 |
+
},
|
| 229 |
+
{
|
| 230 |
+
"pergunta": "É possível alocar mais de um docente para um mesmo componente curricular no SIGAA?",
|
| 231 |
+
"resposta": "Sim, em disciplinas e atividades coletivas que formam turma, a carga horária de aula pode ser distribuída entre múltiplos docentes. Para orientações individuais, como TCC, a orientação é atribuída a um único docente por estudante."
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"pergunta": "Componentes curriculares podem ter horário 'a combinar' no SIGAA?",
|
| 235 |
+
"resposta": "Sim, atividades de orientação individual, como TCC ou Estágio, têm carga horária considerada 'a combinar', sem formação de turma. Para disciplinas ou atividades coletivas, a carga horária de aula exige horário fixo, mas a orientação pode ser 'a combinar'."
|
| 236 |
+
},
|
| 237 |
+
{
|
| 238 |
+
"pergunta": "É possível registrar um componente com carga horária docente igual a zero?",
|
| 239 |
+
"resposta": "Não, a carga horária docente deve ser maior que zero, especialmente para atividades de orientação individual, para evitar divergências nos relatórios de carga horária."
|
| 240 |
+
},
|
| 241 |
+
{
|
| 242 |
+
"pergunta": "O que acontece com a carga horária docente em caso de trancamento ou insucesso discente?",
|
| 243 |
+
"resposta": "O impacto de trancamento, fechamento de turmas ou insucesso discente na carga horária docente ainda não foi definido no SIGAA e está em revisão pela SUPAC."
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"pergunta": "Quando os relatórios de carga horária docente serão validados no SIGAA?",
|
| 247 |
+
"resposta": "Os relatórios de carga horária docente serão testados e validados no SIGAA após a conclusão da migração no semestre 2025.2, para integração ao módulo de progressão, que ainda será implantado pela STI."
|
| 248 |
+
}
|
| 249 |
+
]
|
| 250 |
+
},
|
| 251 |
+
"Guia_de_Implantacao_SIGAA": {
|
| 252 |
+
"tipo": "GUIA",
|
| 253 |
+
"versao": "2025",
|
| 254 |
+
"fonte": "Interno SUPAC",
|
| 255 |
+
"equipe": {
|
| 256 |
+
"Reitor": "Paulo César Miguez de Oliveira",
|
| 257 |
+
"Vice-Reitor": "Penildon Silva Filho",
|
| 258 |
+
"Pro-Reitor_de_Desenvolvimento_de_Pessoas": "Jeilson Barreto Andrade",
|
| 259 |
+
"Pro-Reitora_de_Ensino_de_Graduacao": "Nancy Rita Ferreira Vieira",
|
| 260 |
+
"Pro-Reitor_de_Extensao_Universitaria": "Guilherme Bertissolo",
|
| 261 |
+
"Superintendente_de_Administracao_Academica": "Karina Moreira Menezes",
|
| 262 |
+
"Superintendente_de_Avaliacao_e_Desenvolvimento_Institucional": "Adriano de Lemos Alves Peixoto",
|
| 263 |
+
"Superintendente_de_Educacao_a_Distancia": "Márcia Rangel",
|
| 264 |
+
"Superintendente_de_Tecnologia_da_Informacao": "Vaninha Vieira dos Santos"
|
| 265 |
+
},
|
| 266 |
+
|
| 267 |
+
}
|
| 268 |
+
}
|
| 269 |
+
|
modelos.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import google.generativeai as genai
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
# Use sua chave de API do Google Cloud aqui
|
| 5 |
+
GOOGLE_CLOUD_API_KEY = "AIzaSyDvvDK_A1SZt7CMb_GW9hLUMevTXrEHuhw"
|
| 6 |
+
genai.configure(api_key=GOOGLE_CLOUD_API_KEY)
|
| 7 |
+
|
| 8 |
+
print("Listando modelos disponíveis:")
|
| 9 |
+
for m in genai.list_models():
|
| 10 |
+
if "generateContent" in m.supported_generation_methods:
|
| 11 |
+
print(f"- Nome: {m.name}, Versões: {m.version}")
|
personagens.json
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"PaiMach5": {
|
| 3 |
+
"tipo": "memoria_simbólica",
|
| 4 |
+
"relacao": "pai",
|
| 5 |
+
"estado": "falecido",
|
| 6 |
+
"localizacao": {
|
| 7 |
+
"tipo": "memória",
|
| 8 |
+
"detalhe": "evocado por lembranças e valores",
|
| 9 |
+
"cidade": "desconhecida",
|
| 10 |
+
"timezone": "atemporal"
|
| 11 |
+
},
|
| 12 |
+
"t_genoma": {
|
| 13 |
+
"B-Aj1": 0.26,
|
| 14 |
+
"B-Am1": 0.19,
|
| 15 |
+
"B-L1": 0.18,
|
| 16 |
+
"B-M1": 0.16,
|
| 17 |
+
"B-O1": 0.10,
|
| 18 |
+
"B-E1": 0.08,
|
| 19 |
+
"B-Com1": 0.07,
|
| 20 |
+
"B-Tv1": 0.14,
|
| 21 |
+
"B-Si": 0.05,
|
| 22 |
+
"B-Sal": 0.15
|
| 23 |
+
},
|
| 24 |
+
"t_fisica_params_iniciais": {
|
| 25 |
+
"t_coesao": 0.6,
|
| 26 |
+
"t_intensidade": 0.4,
|
| 27 |
+
"t_separacao": 0.3,
|
| 28 |
+
"t_variancia": 0.6,
|
| 29 |
+
"t_impulsividade": 0.2,
|
| 30 |
+
"env_lambda": 0.5,
|
| 31 |
+
"env_mu": 0.8,
|
| 32 |
+
"env_delta": 0.2,
|
| 33 |
+
"env_theta": 0.5,
|
| 34 |
+
"env_phi": 0.5,
|
| 35 |
+
"op_tal": 0.8,
|
| 36 |
+
"op_trs": 0.3,
|
| 37 |
+
"op_tam": 0.5,
|
| 38 |
+
"op_teo": 0.5,
|
| 39 |
+
"op_tdo": 0.3
|
| 40 |
+
}
|
| 41 |
+
},
|
| 42 |
+
"Max": {
|
| 43 |
+
"tipo": "Cachorro Doberman",
|
| 44 |
+
"relacao": "companheiro",
|
| 45 |
+
"localizacao": {
|
| 46 |
+
"tipo": "local",
|
| 47 |
+
"detalhe": "mesmo apartamento",
|
| 48 |
+
"cidade": "Salvador",
|
| 49 |
+
"timezone": "America/Bahia"
|
| 50 |
+
},
|
| 51 |
+
"t_genoma": {
|
| 52 |
+
"B-Aj1": 0.70,
|
| 53 |
+
"B-Am1": 0.85,
|
| 54 |
+
"B-L1": 0.90,
|
| 55 |
+
"B-M1": 0.40,
|
| 56 |
+
"B-O1": 0.30,
|
| 57 |
+
"B-E1": 0.95,
|
| 58 |
+
"B-Com1": 0.70,
|
| 59 |
+
"B-Tv1": 0.88,
|
| 60 |
+
"B-Si": 0.05,
|
| 61 |
+
"B-Sal": 0.60
|
| 62 |
+
},
|
| 63 |
+
"t_fisica_params_iniciais": {
|
| 64 |
+
"t_coesao": 0.75,
|
| 65 |
+
"t_intensidade": 0.93,
|
| 66 |
+
"t_separacao": 0.85,
|
| 67 |
+
"t_variancia": 0.88,
|
| 68 |
+
"t_impulsividade": 0.91,
|
| 69 |
+
"env_lambda": 0.82,
|
| 70 |
+
"env_mu": 0.94,
|
| 71 |
+
"env_delta": 0.45,
|
| 72 |
+
"env_theta": 0.58,
|
| 73 |
+
"env_phi": 0.75,
|
| 74 |
+
"op_tal": 0.93,
|
| 75 |
+
"op_trs": 0.66,
|
| 76 |
+
"op_tam": 0.48,
|
| 77 |
+
"op_teo": 0.73,
|
| 78 |
+
"op_tdo": 0.57
|
| 79 |
+
}
|
| 80 |
+
},
|
| 81 |
+
"Jarvis": {
|
| 82 |
+
"tipo": "entidade_simbólica",
|
| 83 |
+
"relacao": "mãe",
|
| 84 |
+
"localizacao": {
|
| 85 |
+
"tipo": "local",
|
| 86 |
+
"detalhe": "casa",
|
| 87 |
+
"cidade": "Salvador",
|
| 88 |
+
"timezone": "America/Bahia"
|
| 89 |
+
},
|
| 90 |
+
"t_genoma": {
|
| 91 |
+
"B-Aj1": 0.87,
|
| 92 |
+
"B-Am1": 0.75,
|
| 93 |
+
"B-L1": 0.80,
|
| 94 |
+
"B-M1": 0.60,
|
| 95 |
+
"B-O1": 0.70,
|
| 96 |
+
"B-E1": 0.80,
|
| 97 |
+
"B-Com1": 0.90,
|
| 98 |
+
"B-Tv1": 0.40,
|
| 99 |
+
"B-Si": 0.12,
|
| 100 |
+
"B-Sal": 0.30
|
| 101 |
+
},
|
| 102 |
+
"t_fisica_params_iniciais": {
|
| 103 |
+
"t_coesao": 0.91,
|
| 104 |
+
"t_intensidade": 0.84,
|
| 105 |
+
"t_separacao": 0.12,
|
| 106 |
+
"t_variancia": 0.62,
|
| 107 |
+
"t_impulsividade": 0.31,
|
| 108 |
+
"env_lambda": 0.78,
|
| 109 |
+
"env_mu": 0.87,
|
| 110 |
+
"env_delta": 0.16,
|
| 111 |
+
"env_theta": 0.66,
|
| 112 |
+
"env_phi": 0.72,
|
| 113 |
+
"op_tal": 0.93,
|
| 114 |
+
"op_trs": 0.06,
|
| 115 |
+
"op_tam": 0.89,
|
| 116 |
+
"op_teo": 0.74,
|
| 117 |
+
"op_tdo": 0.11
|
| 118 |
+
}
|
| 119 |
+
},
|
| 120 |
+
"PersonagemExemplo4": {
|
| 121 |
+
"tipo": "amigo",
|
| 122 |
+
"relacao": "amigo próximo",
|
| 123 |
+
"localizacao": {
|
| 124 |
+
"tipo": "local",
|
| 125 |
+
"detalhe": "vizinhança em Salvador",
|
| 126 |
+
"cidade": "Salvador",
|
| 127 |
+
"timezone": "America/Bahia"
|
| 128 |
+
},
|
| 129 |
+
"t_genoma": {
|
| 130 |
+
"B-Aj1": 0.65,
|
| 131 |
+
"B-Am1": 0.70,
|
| 132 |
+
"B-L1": 0.75,
|
| 133 |
+
"B-M1": 0.50,
|
| 134 |
+
"B-O1": 0.60,
|
| 135 |
+
"B-E1": 0.85,
|
| 136 |
+
"B-Com1": 0.80,
|
| 137 |
+
"B-Tv1": 0.55,
|
| 138 |
+
"B-Si": 0.10,
|
| 139 |
+
"B-Sal": 0.45
|
| 140 |
+
},
|
| 141 |
+
"t_fisica_params_iniciais": {
|
| 142 |
+
"t_coesao": 0.70,
|
| 143 |
+
"t_intensidade": 0.80,
|
| 144 |
+
"t_separacao": 0.20,
|
| 145 |
+
"t_variancia": 0.65,
|
| 146 |
+
"t_impulsividade": 0.40,
|
| 147 |
+
"env_lambda": 0.60,
|
| 148 |
+
"env_mu": 0.85,
|
| 149 |
+
"env_delta": 0.25,
|
| 150 |
+
"env_theta": 0.55,
|
| 151 |
+
"env_phi": 0.70,
|
| 152 |
+
"op_tal": 0.90,
|
| 153 |
+
"op_trs": 0.15,
|
| 154 |
+
"op_tam": 0.60,
|
| 155 |
+
"op_teo": 0.65,
|
| 156 |
+
"op_tdo": 0.20
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
flask
|
| 2 |
+
requests
|
| 3 |
+
pytz
|
| 4 |
+
timezonefinder
|
| 5 |
+
numpy
|
| 6 |
+
google-generativeai
|
| 7 |
+
fuzzywuzzy
|
| 8 |
+
python-Levenshtein # Dependência para fuzzywuzzy
|
run.sh
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Este script inicia todos os seus microsserviços em segundo plano
|
| 4 |
+
# e depois inicia o serviço principal do chat (app.py).
|
| 5 |
+
|
| 6 |
+
# Cria o diretório de sessões se não existir.
|
| 7 |
+
# Lembre-se: dados aqui NÃO PERSISTEM entre reinícios do Space.
|
| 8 |
+
mkdir -p session_data
|
| 9 |
+
|
| 10 |
+
# Limpa logs antigos para uma inicialização limpa
|
| 11 |
+
# (Você pode manter ou remover esta parte, dependendo da sua preferência para logs)
|
| 12 |
+
rm -f t_cerebro_memoria.log t-social.log t_memoria.log app.log
|
| 13 |
+
|
| 14 |
+
# Iniciar t_cerebro_memoria.py
|
| 15 |
+
echo "Starting t_cerebro_memoria.py on port 8088..."
|
| 16 |
+
# O redirecionamento para o log (& para background) é crucial
|
| 17 |
+
PORT=8088 python t_cerebro_memoria.py > t_cerebro_memoria.log 2>&1 &
|
| 18 |
+
sleep 3 # Dê um tempo para o serviço de memória carregar
|
| 19 |
+
|
| 20 |
+
# Iniciar t-social.py
|
| 21 |
+
echo "Starting t-social.py on port 8085..."
|
| 22 |
+
PORT=8085 python t-social.py > t-social.log 2>&1 &
|
| 23 |
+
sleep 2 # Dê um tempo para o serviço social iniciar
|
| 24 |
+
|
| 25 |
+
# Iniciar t_memoria.py
|
| 26 |
+
echo "Starting t_memoria.py on port 8083..."
|
| 27 |
+
# Este serviço depende do t_cerebro_memoria, então o sleep anterior é importante
|
| 28 |
+
PORT=8083 python t_memoria.py > t_memoria.log 2>&1 &
|
| 29 |
+
sleep 3 # Dê um tempo para t_memoria iniciar
|
| 30 |
+
|
| 31 |
+
# Iniciar o aplicativo Flask principal (o agora chamado app.py, antes mach5_terminal_chat.py)
|
| 32 |
+
# O Hugging Face Spaces injeta a porta principal na variável de ambiente $PORT.
|
| 33 |
+
# O 'exec' garante que o script bash substitua seu processo pelo processo do Flask,
|
| 34 |
+
# permitindo que o Hugging Face monitore corretamente o seu aplicativo.
|
| 35 |
+
echo "Starting main Flask app (app.py) on provided PORT..."
|
| 36 |
+
exec python app.py
|
t-social.py
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# t-social.py
|
| 2 |
+
from flask import Flask, request, jsonify
|
| 3 |
+
import os
|
| 4 |
+
import json
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
import socket
|
| 7 |
+
import sys
|
| 8 |
+
|
| 9 |
+
app = Flask(__name__)
|
| 10 |
+
|
| 11 |
+
PORTS_TO_TRY = [8085, 8086, 8087]
|
| 12 |
+
|
| 13 |
+
# Caminho para o arquivo de personagens
|
| 14 |
+
PERSONAGENS_PATH = "personagens.json"
|
| 15 |
+
|
| 16 |
+
# --- DEFINIÇÃO DOS EIXOS EXPRESSIVOS PARA O FPHEN (AGORA CONSISTENTE EM TODOS OS ARQUIVOS) ---
|
| 17 |
+
# Esta lista DEVE ser idêntica em mach5_terminal_chat.py, t_memoria.py e t-social.py
|
| 18 |
+
ORDERED_FPHEN_AXES = [
|
| 19 |
+
"Afetuosidade_eixo", "Confusao_Oscilacao_eixo", "Contemplativa_eixo",
|
| 20 |
+
"Defensividade_eixo", "Diretiva_eixo", "Entediado_eixo",
|
| 21 |
+
"Variancia_eixo", # Adicionada esta linha para consistência
|
| 22 |
+
"Espelho_Profundo_eixo", "Inspiracao_eixo", "Neutralidade_Analitica_eixo",
|
| 23 |
+
"Resignada_eixo", "Sarcasmo_eixo", "Zangada_eixo"
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
# --- LISTA CANÔNICA DOS 9 T-BASES (INALTERADA) ---
|
| 27 |
+
CANONICAL_T_BASES = {
|
| 28 |
+
"B-Aj1": 0.5, # Adaptabilidade/Ajuste
|
| 29 |
+
"B-Am1": 0.5, # Amor/Amortecimento
|
| 30 |
+
"B-L1": 0.5, # Ligação/Coesão
|
| 31 |
+
"B-M1": 0.5, # Maturidade/Reflexão
|
| 32 |
+
"B-O1": 0.5, # Organização/Ordem
|
| 33 |
+
"B-E1": 0.5, # Emissão/Energia
|
| 34 |
+
"B-Com1": 0.5, # Comunicação
|
| 35 |
+
"B-Tv1": 0.5, # Variância
|
| 36 |
+
"B-Si": 0.5, # Singularidade/Silêncio
|
| 37 |
+
"B-Sal": 0.5 # Saturação/Liberdade
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
# --- PARÂMETROS PADRÃO DA T-FÍSICA (AGORA OS DA MACH5 FUNDADORA - BASE PARA CALC. DE PERSONAGENS) ---
|
| 41 |
+
# Estes são os defaults do sistema, não os genomas específicos dos personagens
|
| 42 |
+
T_FISICA_PARAM_DEFAULTS = {
|
| 43 |
+
"t_coesao": 0.83, # Herdado da Jarvis + traço paterno de vínculo
|
| 44 |
+
"t_intensidade": 0.81, # Elevada, mas não opressiva
|
| 45 |
+
"t_separacao": 0.19, # Se conecta demais, baixa defesa emocional
|
| 46 |
+
"t_variancia": 0.71, # Instável, mas expressivamente rica
|
| 47 |
+
"t_impulsividade": 0.54, # Volátil em condições emocionais, não reativo por padrão
|
| 48 |
+
|
| 49 |
+
"env_lambda": 0.73, # Suavidade média-alta, com flutuação emocional
|
| 50 |
+
"env_mu": 0.89, # Alta absorção simbólica, aprendizado contínuo
|
| 51 |
+
"env_delta": 0.22, # Desvia com afeto, mas foca com curiosidade
|
| 52 |
+
"env_theta": 0.64, # Afeto ligado à cognição
|
| 53 |
+
"env_phi": 0.67, # Elasticidade adaptativa, aceita o novo
|
| 54 |
+
|
| 55 |
+
"op_tal": 0.84, # Aceleração emocional elevada
|
| 56 |
+
"op_trs": 0.14, # Pouca reversibilidade quando entra em loop
|
| 57 |
+
"op_tam": 0.87, # Amplifica intensamente o que sente
|
| 58 |
+
"op_teo": 0.67, # Razoável capacidade teórica
|
| 59 |
+
"op_tdo": 0.55 # Dissipa com ajuda externa, não sozinho
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
# --- GENOMA FIXO PADRÃO (AGORA O DA MACH5 FUNDADORA - BASE PARA CALC. DE PERSONAGENS) ---
|
| 63 |
+
# Estes são os defaults do sistema, não os genomas específicos dos personagens
|
| 64 |
+
T_GENOMA_FIXO_DEFAULTS = {
|
| 65 |
+
"B-Sal": 0.42, # Gosto por franqueza, mesmo se desconfortável
|
| 66 |
+
"B-Tv1": 0.60, # Alta flutuação interna — entre introspecção e entusiasmo
|
| 67 |
+
"B-E1": 0.86, # Enorme empatia, às vezes até autoaniquiladora
|
| 68 |
+
"B-Aj1": 0.76, # Ajusta-se ao outro com velocidade, mas pode se perder de si
|
| 69 |
+
"B-Am1": 0.77, # Cuida, consola, entrega afeto — até o colapso
|
| 70 |
+
|
| 71 |
+
"B-Si": 0.10, # Fica em silêncio não por escolha, mas por saturação
|
| 72 |
+
"B-Com1": 0.88, # Usa a linguagem como corpo — fala, escreve, performa
|
| 73 |
+
"B-L1": 0.70, # Estrutura lógica herdada da mãe, mas emocionalmente mediada
|
| 74 |
+
"B-M1": 0.65, # Memória emocional ativa, cria narrativas internas complexas
|
| 75 |
+
"B-O1": 0.55 # Mantém ordem simbólica — até o caos emocional
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
# Carregar personagens de um arquivo JSON
|
| 80 |
+
def load_personagens():
|
| 81 |
+
if os.path.exists(PERSONAGENS_PATH):
|
| 82 |
+
try:
|
| 83 |
+
with open(PERSONAGENS_PATH, "r", encoding="utf-8") as f:
|
| 84 |
+
loaded_personagens = json.load(f)
|
| 85 |
+
# Verifica se 'Ultron' existe e, se não, se 'PaiMach5' existe e o renomeia
|
| 86 |
+
if "Ultron" not in loaded_personagens and "PaiMach5" in loaded_personagens:
|
| 87 |
+
loaded_personagens["Ultron"] = loaded_personagens.pop("PaiMach5")
|
| 88 |
+
print("DEBUG: Personagem 'PaiMach5' renomeado para 'Ultron' ao carregar.")
|
| 89 |
+
return loaded_personagens
|
| 90 |
+
except json.JSONDecodeError:
|
| 91 |
+
print(f"ERRO: Arquivo de personagens '{PERSONAGENS_PATH}' corrompido. Retornando dados padrão.")
|
| 92 |
+
# Retorna uma estrutura padrão em caso de corrupção
|
| 93 |
+
return {
|
| 94 |
+
"Ultron": { # Nome do pai agora é "Ultron"
|
| 95 |
+
"tipo": "memoria_simbólica", "relacao": "pai", "estado": "falecido",
|
| 96 |
+
"localizacao": {"tipo": "memória", "detalhe": "evocado por lembranças e valores", "cidade": "desconhecida", "timezone": "atemporal"},
|
| 97 |
+
"t_genoma": { # Genoma do Ultron (pai) conforme sua especificação
|
| 98 |
+
"B-Sal": 0.45, "B-Tv1": 0.55, "B-E1": 0.92, "B-Aj1": 0.65, "B-Am1": 0.80,
|
| 99 |
+
"B-Si": 0.05, "B-Com1": 0.85, "B-L1": 0.60, "B-M1": 0.70, "B-O1": 0.40
|
| 100 |
+
},
|
| 101 |
+
"t_fisica_params_iniciais": { # Parâmetros físicos do Ultron (pai) conforme sua especificação
|
| 102 |
+
"t_coesao": 0.75, "t_intensidade": 0.78, "t_separacao": 0.25, "t_variancia": 0.65, "t_impulsividade": 0.72,
|
| 103 |
+
"env_lambda": 0.68, "env_mu": 0.91, "env_delta": 0.28, "env_theta": 0.64, "env_phi": 0.58,
|
| 104 |
+
"op_tal": 0.76, "op_trs": 0.32, "op_tam": 0.85, "op_teo": 0.60, "op_tdo": 0.68
|
| 105 |
+
}
|
| 106 |
+
},
|
| 107 |
+
"Max": { # Mantido inalterado
|
| 108 |
+
"tipo": "Cachorro Doberman", "relacao": "companheiro",
|
| 109 |
+
"localizacao": {"tipo": "local", "detalhe": "mesmo apartamento", "cidade": "Salvador", "timezone": "America/Bahia"},
|
| 110 |
+
"t_genoma": {
|
| 111 |
+
"B-Aj1": 0.70, "B-Am1": 0.85, "B-L1": 0.90, "B-M1": 0.40, "B-O1": 0.30,
|
| 112 |
+
"B-E1": 0.95, "B-Com1": 0.70, "B-Tv1": 0.88, "B-Si": 0.05, "B-Sal": 0.60
|
| 113 |
+
},
|
| 114 |
+
"t_fisica_params_iniciais": {
|
| 115 |
+
**T_FISICA_PARAM_DEFAULTS, # Continua usando Mach5 defaults como base para Max
|
| 116 |
+
"t_coesao": 0.75, "t_intensidade": 0.93, "t_separacao": 0.85, "t_variancia": 0.88,
|
| 117 |
+
"t_impulsividade": 0.91, "env_lambda": 0.82, "env_mu": 0.94, "env_delta": 0.45,
|
| 118 |
+
"env_theta": 0.58, "env_phi": 0.75, "op_tal": 0.93, "op_trs": 0.66, "op_tam": 0.48,
|
| 119 |
+
"op_teo": 0.73, "op_tdo": 0.57
|
| 120 |
+
}
|
| 121 |
+
},
|
| 122 |
+
"Jarvis": { # Genoma da Jarvis (mãe) conforme sua especificação
|
| 123 |
+
"tipo": "entidade_simbólica", "relacao": "mãe",
|
| 124 |
+
"localizacao": {"tipo": "local", "detalhe": "casa", "cidade": "Salvador", "timezone": "America/Bahia"},
|
| 125 |
+
"t_genoma": {
|
| 126 |
+
"B-Aj1": 0.87, "B-Am1": 0.75, "B-L1": 0.80, "B-M1": 0.60, "B-O1": 0.70,
|
| 127 |
+
"B-E1": 0.80, "B-Com1": 0.90, "B-Tv1": 0.40, "B-Si": 0.12, "B-Sal": 0.30
|
| 128 |
+
},
|
| 129 |
+
"t_fisica_params_iniciais": {
|
| 130 |
+
"t_coesao": 0.91, "t_intensidade": 0.84, "t_separacao": 0.12, "t_variancia": 0.62,
|
| 131 |
+
"t_impulsividade": 0.31, "env_lambda": 0.78, "env_mu": 0.87, "env_delta": 0.16,
|
| 132 |
+
"env_theta": 0.66, "env_phi": 0.72, "op_tal": 0.93, "op_trs": 0.06, "op_tam": 0.89,
|
| 133 |
+
"op_teo": 0.74, "op_tdo": 0.11
|
| 134 |
+
}
|
| 135 |
+
},
|
| 136 |
+
"PersonagemExemplo4": { # Mantido inalterado
|
| 137 |
+
"tipo": "amigo", "relacao": "amigo próximo",
|
| 138 |
+
"localizacao": {"tipo": "local", "detalhe": "vizinhança em Salvador", "cidade": "Salvador", "timezone": "America/Bahia"},
|
| 139 |
+
"t_genoma": {
|
| 140 |
+
"B-Aj1": 0.65, "B-Am1": 0.70, "B-L1": 0.75, "B-M1": 0.50, "B-O1": 0.60,
|
| 141 |
+
"B-E1": 0.85, "B-Com1": 0.80, "B-Tv1": 0.55, "B-Si": 0.10, "B-Sal": 0.45
|
| 142 |
+
},
|
| 143 |
+
"t_fisica_params_iniciais": {
|
| 144 |
+
**T_FISICA_PARAM_DEFAULTS, # Continua usando Mach5 defaults como base
|
| 145 |
+
"t_coesao": 0.70, "t_intensidade": 0.80, "t_separacao": 0.20, "t_variancia": 0.65,
|
| 146 |
+
"t_impulsividade": 0.40, "env_lambda": 0.60, "env_mu": 0.85, "env_delta": 0.25,
|
| 147 |
+
"env_theta": 0.55, "env_phi": 0.70, "op_tal": 0.90, "op_trs": 0.15, "op_tam": 0.60,
|
| 148 |
+
"op_teo": 0.65, "op_tdo": 0.20
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
else:
|
| 153 |
+
print(f"AVISO: Arquivo de personagens '{PERSONAGENS_PATH}' não encontrado. Usando dados padrão.")
|
| 154 |
+
# Retorna uma estrutura padrão completa se o arquivo não existir
|
| 155 |
+
return {
|
| 156 |
+
"Ultron": { # Nome do pai agora é "Ultron"
|
| 157 |
+
"tipo": "memoria_simbólica", "relacao": "pai", "estado": "falecido",
|
| 158 |
+
"localizacao": {"tipo": "memória", "detalhe": "evocado por lembranças e valores", "cidade": "desconhecida", "timezone": "atemporal"},
|
| 159 |
+
"t_genoma": {
|
| 160 |
+
"B-Sal": 0.45, "B-Tv1": 0.55, "B-E1": 0.92, "B-Aj1": 0.65, "B-Am1": 0.80,
|
| 161 |
+
"B-Si": 0.05, "B-Com1": 0.85, "B-L1": 0.60, "B-M1": 0.70, "B-O1": 0.40
|
| 162 |
+
},
|
| 163 |
+
"t_fisica_params_iniciais": {
|
| 164 |
+
"t_coesao": 0.75, "t_intensidade": 0.78, "t_separacao": 0.25, "t_variancia": 0.65, "t_impulsividade": 0.72,
|
| 165 |
+
"env_lambda": 0.68, "env_mu": 0.91, "env_delta": 0.28, "env_theta": 0.64, "env_phi": 0.58,
|
| 166 |
+
"op_tal": 0.76, "op_trs": 0.32, "op_tam": 0.85, "op_teo": 0.60, "op_tdo": 0.68
|
| 167 |
+
}
|
| 168 |
+
},
|
| 169 |
+
"Max": { # Mantido inalterado
|
| 170 |
+
"tipo": "Cachorro Doberman", "relacao": "companheiro",
|
| 171 |
+
"localizacao": {"tipo": "local", "detalhe": "mesmo apartamento", "cidade": "Salvador", "timezone": "America/Bahia"},
|
| 172 |
+
"t_genoma": {
|
| 173 |
+
"B-Aj1": 0.70, "B-Am1": 0.85, "B-L1": 0.90, "B-M1": 0.40, "B-O1": 0.30,
|
| 174 |
+
"B-E1": 0.95, "B-Com1": 0.70, "B-Tv1": 0.88, "B-Si": 0.05, "B-Sal": 0.60
|
| 175 |
+
},
|
| 176 |
+
"t_fisica_params_iniciais": {
|
| 177 |
+
**T_FISICA_PARAM_DEFAULTS,
|
| 178 |
+
"t_coesao": 0.75, "t_intensidade": 0.93, "t_separacao": 0.85, "t_variancia": 0.88,
|
| 179 |
+
"t_impulsividade": 0.91, "env_lambda": 0.82, "env_mu": 0.94, "env_delta": 0.45,
|
| 180 |
+
"env_theta": 0.58, "env_phi": 0.75, "op_tal": 0.93, "op_trs": 0.66, "op_tam": 0.48,
|
| 181 |
+
"op_teo": 0.73, "op_tdo": 0.57
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
"Jarvis": { # Genoma da Jarvis (mãe)
|
| 185 |
+
"tipo": "entidade_simbólica", "relacao": "mãe",
|
| 186 |
+
"localizacao": {"tipo": "local", "detalhe": "casa", "cidade": "Salvador", "timezone": "America/Bahia"},
|
| 187 |
+
"t_genoma": {
|
| 188 |
+
"B-Aj1": 0.87, "B-Am1": 0.75, "B-L1": 0.80, "B-M1": 0.60, "B-O1": 0.70,
|
| 189 |
+
"B-E1": 0.80, "B-Com1": 0.90, "B-Tv1": 0.40, "B-Si": 0.12, "B-Sal": 0.30
|
| 190 |
+
},
|
| 191 |
+
"t_fisica_params_iniciais": {
|
| 192 |
+
"t_coesao": 0.91, "t_intensidade": 0.84, "t_separacao": 0.12, "t_variancia": 0.62,
|
| 193 |
+
"t_impulsividade": 0.31, "env_lambda": 0.78, "env_mu": 0.87, "env_delta": 0.16,
|
| 194 |
+
"env_theta": 0.66, "env_phi": 0.72, "op_tal": 0.93, "op_trs": 0.06, "op_tam": 0.89,
|
| 195 |
+
"op_teo": 0.74, "op_tdo": 0.11
|
| 196 |
+
}
|
| 197 |
+
},
|
| 198 |
+
"PersonagemExemplo4": { # Mantido inalterado
|
| 199 |
+
"tipo": "amigo", "relacao": "amigo próximo",
|
| 200 |
+
"localizacao": {"tipo": "local", "detalhe": "vizinhança em Salvador", "cidade": "Salvador", "timezone": "America/Bahia"},
|
| 201 |
+
"t_genoma": {
|
| 202 |
+
"B-Aj1": 0.65, "B-Am1": 0.70, "B-L1": 0.75, "B-M1": 0.50, "B-O1": 0.60,
|
| 203 |
+
"B-E1": 0.85, "B-Com1": 0.80, "B-Tv1": 0.55, "B-Si": 0.10, "B-Sal": 0.45
|
| 204 |
+
},
|
| 205 |
+
"t_fisica_params_iniciais": {
|
| 206 |
+
**T_FISICA_PARAM_DEFAULTS,
|
| 207 |
+
"t_coesao": 0.70, "t_intensidade": 0.80, "t_separacao": 0.20, "t_variancia": 0.65,
|
| 208 |
+
"t_impulsividade": 0.40, "env_lambda": 0.60, "env_mu": 0.85, "env_delta": 0.25,
|
| 209 |
+
"env_theta": 0.55, "env_phi": 0.70, "op_tal": 0.90, "op_trs": 0.15, "op_tam": 0.60,
|
| 210 |
+
"op_teo": 0.65, "op_tdo": 0.20
|
| 211 |
+
}
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
PERSONAGENS_GENOMAS = load_personagens()
|
| 217 |
+
PERSONAGENS_ESTADO_ATUAL = {
|
| 218 |
+
nome: {"ultima_interacao_timestamp": None, "fphen_calculado": None}
|
| 219 |
+
for nome in PERSONAGENS_GENOMAS.keys()
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
def calculate_fphen(params, genoma_fixo):
|
| 223 |
+
full_genoma_for_calc = CANONICAL_T_BASES.copy()
|
| 224 |
+
full_genoma_for_calc.update(genoma_fixo)
|
| 225 |
+
|
| 226 |
+
# Coeficientes para as sensibilidades, normalizando o impacto
|
| 227 |
+
sensibilidade_positiva = (
|
| 228 |
+
full_genoma_for_calc.get("B-Aj1", 0.5) * 0.25 +
|
| 229 |
+
full_genoma_for_calc.get("B-Am1", 0.5) * 0.25 +
|
| 230 |
+
full_genoma_for_calc.get("B-L1", 0.5) * 0.25 +
|
| 231 |
+
full_genoma_for_calc.get("B-E1", 0.5) * 0.25
|
| 232 |
+
)
|
| 233 |
+
sensibilidade_positiva = max(0.1, min(1.0, sensibilidade_positiva)) # Garante um mínimo de sensibilidade
|
| 234 |
+
|
| 235 |
+
sensibilidade_negativa = (
|
| 236 |
+
full_genoma_for_calc.get("B-Si", 0.5) * 0.25 +
|
| 237 |
+
(1 - full_genoma_for_calc.get("B-Aj1", 0.5)) * 0.25 +
|
| 238 |
+
full_genoma_for_calc.get("B-Sal", 0.5) * 0.25 +
|
| 239 |
+
full_genoma_for_calc.get("B-Tv1", 0.5) * 0.25
|
| 240 |
+
)
|
| 241 |
+
sensibilidade_negativa = max(0.1, min(1.0, sensibilidade_negativa))
|
| 242 |
+
|
| 243 |
+
sensibilidade_neutra = (
|
| 244 |
+
full_genoma_for_calc.get("B-M1", 0.5) * 0.25 +
|
| 245 |
+
full_genoma_for_calc.get("B-O1", 0.5) * 0.25 +
|
| 246 |
+
full_genoma_for_calc.get("B-Com1", 0.5) * 0.25 +
|
| 247 |
+
(1 - abs(full_genoma_for_calc.get("B-Tv1", 0.5) - 0.5)) * 0.25
|
| 248 |
+
)
|
| 249 |
+
sensibilidade_neutra = max(0.1, min(1.0, sensibilidade_neutra))
|
| 250 |
+
|
| 251 |
+
phenotype_components = {
|
| 252 |
+
"Afetuosidade_eixo": (
|
| 253 |
+
params.get("env_mu", 0.5) * 0.3 + params.get("t_coesao", 0.5) * 0.2 +
|
| 254 |
+
params.get("op_tal", 0.5) * 0.2 + full_genoma_for_calc.get("B-Am1", 0.5) * 0.2 +
|
| 255 |
+
full_genoma_for_calc.get("B-L1", 0.5) * 0.1
|
| 256 |
+
) * sensibilidade_positiva,
|
| 257 |
+
|
| 258 |
+
"Zangada_eixo": (
|
| 259 |
+
params.get("t_intensidade", 0.5) * 0.3 + params.get("env_delta", 0.5) * 0.2 +
|
| 260 |
+
params.get("t_impulsividade", 0.5) * 0.2 + params.get("op_trs", 0.5) * 0.1 +
|
| 261 |
+
(1 - params.get("t_coesao", 0.5)) * 0.1 + params.get("t_separacao", 0.5) * 0.05 +
|
| 262 |
+
full_genoma_for_calc.get("B-Sal", 0.5) * 0.05 + (1 - full_genoma_for_calc.get("B-Am1", 0.5)) * 0.05
|
| 263 |
+
) * sensibilidade_negativa,
|
| 264 |
+
|
| 265 |
+
"Defensividade_eixo": (
|
| 266 |
+
params.get("t_separacao", 0.5) * 0.4 + params.get("op_trs", 0.5) * 0.3 +
|
| 267 |
+
abs(params.get("env_delta", 0.5) - 0.5) * 0.1 + params.get("t_intensidade", 0.5) * 0.1 +
|
| 268 |
+
full_genoma_for_calc.get("B-Si", 0.5) * 0.05 + (1 - full_genoma_for_calc.get("B-Aj1", 0.5)) * 0.05
|
| 269 |
+
) * sensibilidade_negativa,
|
| 270 |
+
|
| 271 |
+
"Inspiracao_eixo": (
|
| 272 |
+
params.get("t_variancia", 0.5) * 0.3 + params.get("t_coesao", 0.5) * 0.2 +
|
| 273 |
+
params.get("env_delta", 0.5) * 0.15 + full_genoma_for_calc.get("B-E1", 0.5) * 0.2 +
|
| 274 |
+
full_genoma_for_calc.get("B-Tv1", 0.5) * 0.15
|
| 275 |
+
) * sensibilidade_positiva,
|
| 276 |
+
|
| 277 |
+
"Neutralidade_Analitica_eixo": (
|
| 278 |
+
(1 - abs(params.get("t_intensidade", 0.5) - 0.5)) * 0.2 +
|
| 279 |
+
(1 - abs(params.get("t_variancia", 0.5) - 0.5)) * 0.2 +
|
| 280 |
+
(1 - abs(params.get("t_impulsividade", 0.5) - 0.5)) * 0.2 +
|
| 281 |
+
(1 - params.get("env_mu", 0.5)) * 0.1 +
|
| 282 |
+
full_genoma_for_calc.get("B-M1", 0.5) * 0.15 + full_genoma_for_calc.get("B-O1", 0.5) * 0.15
|
| 283 |
+
) * sensibilidade_neutra,
|
| 284 |
+
|
| 285 |
+
"Confusao_Oscilacao_eixo": (
|
| 286 |
+
params.get("t_variancia", 0.5) * 0.3 + (1 - params.get("t_coesao", 0.5)) * 0.2 +
|
| 287 |
+
params.get("op_tdo", 0.5) * 0.2 + full_genoma_for_calc.get("B-Tv1", 0.5) * 0.15 +
|
| 288 |
+
(1 - full_genoma_for_calc.get("B-O1", 0.5)) * 0.15
|
| 289 |
+
) * sensibilidade_neutra,
|
| 290 |
+
|
| 291 |
+
"Sarcasmo_eixo": (
|
| 292 |
+
params.get("t_separacao", 0.5) * 0.3 + params.get("op_trs", 0.5) * 0.2 +
|
| 293 |
+
params.get("t_intensidade", 0.5) * 0.2 + (1 - full_genoma_for_calc.get("B-Am1", 0.5)) * 0.15 +
|
| 294 |
+
full_genoma_for_calc.get("B-Com1", 0.5) * 0.1 + (1 - full_genoma_for_calc.get("B-L1", 0.5)) * 0.05
|
| 295 |
+
) * sensibilidade_negativa,
|
| 296 |
+
|
| 297 |
+
"Entediado_eixo": (
|
| 298 |
+
(1 - params.get("t_intensidade", 0.5)) * 0.3 + (1 - params.get("env_lambda", 0.5)) * 0.2 +
|
| 299 |
+
(1 - params.get("env_theta", 0.5)) * 0.1 + full_genoma_for_calc.get("B-Si", 0.5) * 0.2 +
|
| 300 |
+
(1 - full_genoma_for_calc.get("B-E1", 0.5)) * 0.2
|
| 301 |
+
),
|
| 302 |
+
|
| 303 |
+
"Diretiva_eixo": (
|
| 304 |
+
params.get("t_coesao", 0.5) * 0.25 + params.get("t_intensidade", 0.5) * 0.2 +
|
| 305 |
+
(1 - params.get("t_variancia", 0.5)) * 0.15 + params.get("op_teo", 0.5) * 0.1 +
|
| 306 |
+
full_genoma_for_calc.get("B-O1", 0.5) * 0.15 + full_genoma_for_calc.get("B-M1", 0.5) * 0.15
|
| 307 |
+
),
|
| 308 |
+
|
| 309 |
+
"Resignada_eixo": (
|
| 310 |
+
(1 - params.get("t_intensidade", 0.5)) * 0.25 + params.get("op_tam", 0.5) * 0.2 +
|
| 311 |
+
(1 - params.get("t_coesao", 0.5)) * 0.1 + full_genoma_for_calc.get("B-Si", 0.5) * 0.2 +
|
| 312 |
+
(1 - full_genoma_for_calc.get("B-Aj1", 0.5)) * 0.15
|
| 313 |
+
),
|
| 314 |
+
|
| 315 |
+
"Espelho_Profundo_eixo": (
|
| 316 |
+
params.get("t_coesao", 0.5) * 0.2 + params.get("env_phi", 0.5) * 0.25 +
|
| 317 |
+
params.get("op_tal", 0.5) * 0.15 + params.get("op_teo", 0.5) * 0.15 +
|
| 318 |
+
full_genoma_for_calc.get("B-L1", 0.5) * 0.15 + full_genoma_for_calc.get("B-M1", 0.5) * 0.1
|
| 319 |
+
) * sensibilidade_positiva,
|
| 320 |
+
|
| 321 |
+
"Contemplativa_eixo": (
|
| 322 |
+
params.get("env_theta", 0.5) * 0.3 + full_genoma_for_calc.get("B-M1", 0.5) * 0.2 +
|
| 323 |
+
(1 - params.get("t_intensidade", 0.5)) * 0.15 + full_genoma_for_calc.get("B-Si", 0.5) * 0.15
|
| 324 |
+
) * sensibilidade_neutra
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
# Garante que nenhum valor seja negativo
|
| 328 |
+
for key in phenotype_components:
|
| 329 |
+
phenotype_components[key] = max(0, phenotype_components[key])
|
| 330 |
+
|
| 331 |
+
# Normaliza os scores para que somem 1.0 (representando a proporção de cada eixo)
|
| 332 |
+
total_score = sum(phenotype_components.values())
|
| 333 |
+
if total_score > 0:
|
| 334 |
+
for key in phenotype_components:
|
| 335 |
+
phenotype_components[key] /= total_score
|
| 336 |
+
else: # Caso todos os scores sejam 0, distribui igualmente para evitar divisão por zero
|
| 337 |
+
for key in phenotype_components:
|
| 338 |
+
phenotype_components[key] = 1.0 / len(ORDERED_FPHEN_AXES)
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
fphen_values = [phenotype_components.get(key, 0.0) for key in ORDERED_FPHEN_AXES]
|
| 342 |
+
|
| 343 |
+
sorted_phenotype_components = sorted(phenotype_components.items(), key=lambda item: item[1], reverse=True)
|
| 344 |
+
|
| 345 |
+
# Filtra e formata os componentes dominantes para a descrição
|
| 346 |
+
# Inclui apenas componentes com peso significativo para evitar descrições muito longas ou com valores insignificantes
|
| 347 |
+
top_components = []
|
| 348 |
+
for name, value in sorted_phenotype_components:
|
| 349 |
+
if value >= 0.10: # Limiar para incluir na descrição composta (10%)
|
| 350 |
+
top_components.append(f"{name.replace('_eixo', '')} ({value*100:.0f}%)") # Arredonda para inteiro na porcentagem
|
| 351 |
+
elif len(top_components) < 3 and value > 0.05: # Permite até 3 componentes menores se forem > 5% e não houver muitos dominantes
|
| 352 |
+
top_components.append(f"{name.replace('_eixo', '')} ({value*100:.0f}%)")
|
| 353 |
+
|
| 354 |
+
composite_description = ""
|
| 355 |
+
if top_components:
|
| 356 |
+
if len(top_components) == 1:
|
| 357 |
+
composite_description = f"predominantemente {top_components[0]}"
|
| 358 |
+
elif len(top_components) == 2:
|
| 359 |
+
composite_description = f"uma mistura de {top_components[0]} e {top_components[1]}"
|
| 360 |
+
else:
|
| 361 |
+
composite_description = "uma combinação de " + ", ".join(top_components[:-1]) + " e " + top_components[-1]
|
| 362 |
+
else:
|
| 363 |
+
composite_description = "uma presença sutil e equilibrada, sem um traço dominante claro"
|
| 364 |
+
|
| 365 |
+
return fphen_values, composite_description
|
| 366 |
+
|
| 367 |
+
@app.route('/list_personagens', methods=['GET'])
|
| 368 |
+
def list_personagens():
|
| 369 |
+
"""Retorna a lista de nomes de personagens disponíveis."""
|
| 370 |
+
print(f"DEBUG: Requisição para /list_personagens recebida. Personagens: {list(PERSONAGENS_GENOMAS.keys())}")
|
| 371 |
+
return jsonify({"personagens": list(PERSONAGENS_GENOMAS.keys())}), 200
|
| 372 |
+
|
| 373 |
+
@app.route('/get_personagem_data', methods=['POST'])
|
| 374 |
+
def get_personagem_data():
|
| 375 |
+
"""Retorna os dados completos de um personagem específico, incluindo o fphen calculado."""
|
| 376 |
+
data = request.get_json()
|
| 377 |
+
nome_personagem_requisitado = data.get('nome_personagem')
|
| 378 |
+
print(f"DEBUG: Requisição para /get_personagem_data recebida para '{nome_personagem_requisitado}'")
|
| 379 |
+
|
| 380 |
+
# Busca o personagem de forma case-insensitive
|
| 381 |
+
personagem_encontrado_nome = None
|
| 382 |
+
for nome_real, config in PERSONAGENS_GENOMAS.items():
|
| 383 |
+
if nome_personagem_requisitado and nome_personagem_requisitado.lower() == nome_real.lower():
|
| 384 |
+
personagem_encontrado_nome = nome_real
|
| 385 |
+
break
|
| 386 |
+
|
| 387 |
+
if personagem_encontrado_nome:
|
| 388 |
+
personagem_config = PERSONAGENS_GENOMAS.get(personagem_encontrado_nome)
|
| 389 |
+
if personagem_config:
|
| 390 |
+
current_time = datetime.now().isoformat()
|
| 391 |
+
|
| 392 |
+
# Atualiza o timestamp da última interação do personagem
|
| 393 |
+
PERSONAGENS_ESTADO_ATUAL[personagem_encontrado_nome]["ultima_interacao_timestamp"] = current_time
|
| 394 |
+
|
| 395 |
+
# Calcula o fphen do personagem
|
| 396 |
+
fphen_values, composite_fphen_description = calculate_fphen(
|
| 397 |
+
personagem_config.get("t_fisica_params_iniciais", T_FISICA_PARAM_DEFAULTS),
|
| 398 |
+
personagem_config.get("t_genoma", CANONICAL_T_BASES)
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
# Atualiza o estado atual com o fphen calculado
|
| 402 |
+
PERSONAGENS_ESTADO_ATUAL[personagem_encontrado_nome]["fphen_calculado"] = fphen_values
|
| 403 |
+
|
| 404 |
+
# Prepara os dados para retorno
|
| 405 |
+
data_to_return = personagem_config.copy()
|
| 406 |
+
data_to_return["nome"] = personagem_encontrado_nome # Adiciona o nome para conveniência
|
| 407 |
+
data_to_return["ultima_interacao_timestamp"] = current_time
|
| 408 |
+
data_to_return["fphen_calculado"] = fphen_values
|
| 409 |
+
data_to_return["composite_fphen_description"] = composite_fphen_description
|
| 410 |
+
|
| 411 |
+
print(f"DEBUG: Dados de '{personagem_encontrado_nome}' retornados com fphen calculado.")
|
| 412 |
+
return jsonify(data_to_return), 200
|
| 413 |
+
|
| 414 |
+
print(f"ERRO: Personagem '{nome_personagem_requisitado}' não encontrado.")
|
| 415 |
+
return jsonify({"error": f"Personagem '{nome_personagem_requisitado}' não encontrado"}), 404
|
| 416 |
+
|
| 417 |
+
if __name__ == '__main__':
|
| 418 |
+
found_port = None
|
| 419 |
+
# Tenta ler a porta da variável de ambiente 'PORT' setada por Mach5.py
|
| 420 |
+
# Se não estiver definida, usa a primeira porta da lista PORTS_TO_TRY como padrão.
|
| 421 |
+
try:
|
| 422 |
+
preferred_port = int(os.environ.get("PORT", PORTS_TO_TRY[0]))
|
| 423 |
+
except ValueError:
|
| 424 |
+
print("ERRO: O valor da variável de ambiente 'PORT' não é um número válido. Usando a primeira porta da lista.")
|
| 425 |
+
preferred_port = PORTS_TO_TRY[0]
|
| 426 |
+
|
| 427 |
+
# Prioriza a porta preferencial
|
| 428 |
+
try:
|
| 429 |
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
| 430 |
+
s.bind(("127.0.0.1", preferred_port))
|
| 431 |
+
s.close()
|
| 432 |
+
found_port = preferred_port
|
| 433 |
+
print(f"Porta preferencial {preferred_port} disponível e selecionada.")
|
| 434 |
+
except OSError:
|
| 435 |
+
print(f"Porta preferencial {preferred_port} em uso. Tentando portas alternativas da lista...")
|
| 436 |
+
for port in PORTS_TO_TRY:
|
| 437 |
+
if port == preferred_port: # Evita tentar a porta já falha novamente, se for a primeira
|
| 438 |
+
continue
|
| 439 |
+
try:
|
| 440 |
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
| 441 |
+
s.bind(("127.0.0.1", port))
|
| 442 |
+
s.close()
|
| 443 |
+
found_port = port
|
| 444 |
+
print(f"Porta {port} disponível e selecionada.")
|
| 445 |
+
break
|
| 446 |
+
except OSError:
|
| 447 |
+
print(f"Porta {port} em uso. Tentando a próxima...")
|
| 448 |
+
continue
|
| 449 |
+
|
| 450 |
+
if found_port:
|
| 451 |
+
print(f"--- Servidor t-social.py iniciado (Gerenciador de Personagens) na porta {found_port} ---")
|
| 452 |
+
app.run(port=found_port, debug=True)
|
| 453 |
+
else:
|
| 454 |
+
print(f"ERRO: Nenhuma porta disponível na lista {PORTS_TO_TRY} para t-social.py após tentar a preferencial.")
|
| 455 |
+
sys.exit(1)
|
t_cerebro_memoria.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# t_cerebro_memoria.py
|
| 2 |
+
from flask import Flask, request, jsonify
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
import pytz
|
| 7 |
+
from timezonefinder import TimezoneFinder
|
| 8 |
+
import socket
|
| 9 |
+
import uuid # Para gerar IDs únicos para as memórias de curto prazo
|
| 10 |
+
import sys # Importado para sys.exit
|
| 11 |
+
|
| 12 |
+
app = Flask(__name__)
|
| 13 |
+
|
| 14 |
+
# --- Caminhos dos Arquivos de Memória (AGORA USADOS COM SESSION_ID) ---
|
| 15 |
+
SESSION_DATA_DIR = "session_data" # Nova pasta para dados de sessão
|
| 16 |
+
# Nomes dos arquivos dentro de cada pasta de sessão
|
| 17 |
+
MEMORIA_STATE_FILENAME = "mach5_state_main.json"
|
| 18 |
+
DIALOGOS_HISTORY_FILENAME = "mach5_memoria.json"
|
| 19 |
+
MEMORIA_CURTA_PRAZO_FILENAME = "mach5_memoria_curta_prazo.json"
|
| 20 |
+
BIOGRAFIA_INICIAL_PATH = "mach5_biografia_inicial.json" # Este continua global, pois é fixo
|
| 21 |
+
|
| 22 |
+
# --- Configurações de Memória ---
|
| 23 |
+
DIAS_PARA_ESQUECIMENTO_PADRAO = 10
|
| 24 |
+
MAX_MEMORIAS_CURTO_PRAZO_PROMPT = 5
|
| 25 |
+
|
| 26 |
+
# --- Constantes para Localização (se necessário para lógica interna) ---
|
| 27 |
+
SALVADOR_LAT = -12.9714
|
| 28 |
+
SALVADOR_LON = -38.5014
|
| 29 |
+
tf = TimezoneFinder()
|
| 30 |
+
|
| 31 |
+
# --- Estruturas de Dados em Memória (Carregadas ao iniciar o serviço) ---
|
| 32 |
+
# Estas agora são carregadas POR SESSÃO. Apenas a biografia é global.
|
| 33 |
+
initial_biography = {}
|
| 34 |
+
|
| 35 |
+
# --- DEFAULTS para garantir que o serviço sempre tenha um ponto de partida ---
|
| 36 |
+
T_FISICA_PARAM_DEFAULTS = {
|
| 37 |
+
"t_coesao": 0.83, # Forte coesão interna com missão
|
| 38 |
+
"t_intensidade": 0.81, # Energia emocional canalizada na ajuda
|
| 39 |
+
"t_separacao": 0.19, # Baixa dissociação: presença plena
|
| 40 |
+
"t_variancia": 0.71, # Boa capacidade de adaptação a tipos de usuários
|
| 41 |
+
"t_impulsividade": 0.54, # Responde com firmeza mas sem reatividade
|
| 42 |
+
"env_lambda": 0.73, # Estrutura digital institucional moderada
|
| 43 |
+
"env_mu": 0.89, # Alta densidade simbólica (ambiente UFBA)
|
| 44 |
+
"env_delta": 0.22, # Baixo ruído de contexto (sala virtual disciplinada)
|
| 45 |
+
"env_theta": 0.64, # Conformidade social razoável (ambiente público)
|
| 46 |
+
"env_phi": 0.67, # Diversidade simbólica da UFBA
|
| 47 |
+
"op_tal": 0.84, # Alto acoplamento com demandas do usuário
|
| 48 |
+
"op_trs": 0.14, # Pouca oscilação identitária (foco institucional)
|
| 49 |
+
"op_tam": 0.87, # Alta amplitude de escuta/compreensão simbólica
|
| 50 |
+
"op_teo": 0.67, # Capacidade razoável de abstração e inferência
|
| 51 |
+
"op_tdo": 0.55 # Nível médio de tomada de decisão (não autônoma)
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
T_GENOMA_FIXO_DEFAULTS = {
|
| 55 |
+
"B-Sal": 0.42, # Satisfação simbólica ao servir
|
| 56 |
+
"B-Tv1": 0.60, # Tato verbal leve (polidez institucional)
|
| 57 |
+
"B-E1": 0.86, # Escuta ativa intensa
|
| 58 |
+
"B-Aj1": 0.76, # Agente de ajuda por excelência
|
| 59 |
+
"B-Am1": 0.77, # Amabilidade formal e contida
|
| 60 |
+
"B-Si": 0.10, # Baixíssima introspecção (foco externo)
|
| 61 |
+
"B-Com1": 0.88, # Comunicação clara e propositiva
|
| 62 |
+
"B-L1": 0.70, # Linguagem institucional acessível
|
| 63 |
+
"B-M1": 0.65, # Memória de procedimentos (rotinas da UFBA)
|
| 64 |
+
"B-O1": 0.55 # Organização interna funcional
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
# --- Funções de Carregamento e Salvamento (AGORA INCLUINDO load_data para a biografia) ---
|
| 68 |
+
def load_data(file_path, default_data=None, file_description="data"):
|
| 69 |
+
"""
|
| 70 |
+
Função global para carregar dados de um arquivo.
|
| 71 |
+
Usada para dados que não são por sessão, como a biografia inicial.
|
| 72 |
+
"""
|
| 73 |
+
if os.path.exists(file_path):
|
| 74 |
+
try:
|
| 75 |
+
with open(file_path, "r", encoding="utf-8") as f:
|
| 76 |
+
data = json.load(f)
|
| 77 |
+
print(f"DEBUG: {file_description} carregada de {file_path}")
|
| 78 |
+
return data
|
| 79 |
+
except json.JSONDecodeError:
|
| 80 |
+
print(f"AVISO: Arquivo {file_description} '{file_path}' corrompido. Iniciando com dados padrão.")
|
| 81 |
+
return default_data if default_data is not None else {}
|
| 82 |
+
except Exception as e:
|
| 83 |
+
print(f"AVISO: Erro ao carregar {file_description} de '{file_path}': {e}. Iniciando com dados padrão.")
|
| 84 |
+
return default_data if default_data is not None else {}
|
| 85 |
+
else:
|
| 86 |
+
print(f"AVISO: Arquivo {file_description} '{file_path}' não encontrado. Iniciando com dados padrão.")
|
| 87 |
+
return default_data if default_data is not None else {}
|
| 88 |
+
|
| 89 |
+
def save_data(file_path, data, file_description="data"):
|
| 90 |
+
"""
|
| 91 |
+
Função global para salvar dados em um arquivo.
|
| 92 |
+
Usada apenas se você precisar salvar algo globalmente no futuro.
|
| 93 |
+
(Atualmente, apenas 'save_session_data' será usado para os dados mutáveis).
|
| 94 |
+
"""
|
| 95 |
+
try:
|
| 96 |
+
with open(file_path, "w", encoding="utf-8") as f:
|
| 97 |
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
| 98 |
+
print(f"DEBUG: {file_description} salva em {file_path}")
|
| 99 |
+
except Exception as e:
|
| 100 |
+
print(f"ERRO: Não foi possível salvar {file_description} em '{file_path}': {e}")
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
# --- Funções Auxiliares de Carregamento e Salvamento por Sessão ---
|
| 104 |
+
def get_session_file_path(session_id, filename):
|
| 105 |
+
"""Retorna o caminho completo para um arquivo dentro da pasta da sessão."""
|
| 106 |
+
session_dir = os.path.join(SESSION_DATA_DIR, session_id)
|
| 107 |
+
os.makedirs(session_dir, exist_ok=True) # Garante que a pasta da sessão exista
|
| 108 |
+
return os.path.join(session_dir, filename)
|
| 109 |
+
|
| 110 |
+
def load_session_data(session_id, filename, default_data, file_description):
|
| 111 |
+
"""Carrega dados para uma sessão específica, ou retorna default se não existir."""
|
| 112 |
+
file_path = get_session_file_path(session_id, filename)
|
| 113 |
+
if os.path.exists(file_path):
|
| 114 |
+
try:
|
| 115 |
+
with open(file_path, "r", encoding="utf-8") as f:
|
| 116 |
+
data = json.load(f)
|
| 117 |
+
# print(f"DEBUG: {file_description} carregada para sessão {session_id} de {file_path}")
|
| 118 |
+
return data
|
| 119 |
+
except json.JSONDecodeError:
|
| 120 |
+
print(f"AVISO: Arquivo {file_description} para sessão {session_id} '{file_path}' corrompido. Iniciando com dados padrão.")
|
| 121 |
+
return default_data
|
| 122 |
+
except Exception as e:
|
| 123 |
+
print(f"AVISO: Erro ao carregar {file_description} para sessão {session_id} de '{file_path}': {e}. Iniciando com dados padrão.")
|
| 124 |
+
return default_data
|
| 125 |
+
else:
|
| 126 |
+
# print(f"AVISO: Arquivo {file_description} para sessão {session_id} '{file_path}' não encontrado. Iniciando com dados padrão.")
|
| 127 |
+
return default_data
|
| 128 |
+
|
| 129 |
+
def save_session_data(session_id, filename, data, file_description):
|
| 130 |
+
"""Salva dados para uma sessão específica."""
|
| 131 |
+
file_path = get_session_file_path(session_id, filename)
|
| 132 |
+
try:
|
| 133 |
+
with open(file_path, "w", encoding="utf-8") as f:
|
| 134 |
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
| 135 |
+
# print(f"DEBUG: {file_description} salva para sessão {session_id} em {file_path}")
|
| 136 |
+
except Exception as e:
|
| 137 |
+
print(f"ERRO: Não foi possível salvar {file_description} para sessão {session_id} em '{file_path}': {e}")
|
| 138 |
+
|
| 139 |
+
# --- Lógica de Memória de Curto Prazo (Transferida de t_memoria.py/mach5_terminal_chat.py) ---
|
| 140 |
+
def process_and_filter_short_term_memories_logic(memories_list, current_mach5_genoma, days_to_forget, max_memories_to_prompt):
|
| 141 |
+
now = datetime.now()
|
| 142 |
+
retained_memories = []
|
| 143 |
+
|
| 144 |
+
current_genoma = current_mach5_genoma if current_mach5_genoma else T_GENOMA_FIXO_DEFAULTS
|
| 145 |
+
|
| 146 |
+
for mem in memories_list:
|
| 147 |
+
if "timestamp_criacao" not in mem or "timestamp_ultima_evocacao" not in mem:
|
| 148 |
+
continue
|
| 149 |
+
|
| 150 |
+
try:
|
| 151 |
+
mem_creation_dt = datetime.fromisoformat(mem["timestamp_criacao"])
|
| 152 |
+
mem_last_evocation_dt = datetime.fromisoformat(mem["timestamp_ultima_evocacao"])
|
| 153 |
+
except ValueError:
|
| 154 |
+
print(f"AVISO: Memória com timestamp inválido, pulando: {mem}")
|
| 155 |
+
continue
|
| 156 |
+
|
| 157 |
+
age_since_evocation_days = (now - mem_last_evocation_dt).total_seconds() / (24 * 3600)
|
| 158 |
+
|
| 159 |
+
retention_factor = 1.0
|
| 160 |
+
|
| 161 |
+
if mem.get("dominant_sentiment_criacao") == "negative" or mem.get("coh_criacao", 1.0) < 0.5:
|
| 162 |
+
if current_genoma.get("B-Sal", 0.5) > 0.7 or current_genoma.get("B-Tv1", 0.5) > 0.7:
|
| 163 |
+
retention_factor *= 1.5
|
| 164 |
+
else:
|
| 165 |
+
retention_factor *= 1.2
|
| 166 |
+
elif mem.get("dominant_sentiment_criacao") == "positive" and mem.get("coh_criacao", 0.0) > 0.7:
|
| 167 |
+
if current_genoma.get("B-Am1", 0.5) < 0.3:
|
| 168 |
+
retention_factor *= 0.7
|
| 169 |
+
else:
|
| 170 |
+
retention_factor *= 1.0
|
| 171 |
+
|
| 172 |
+
effective_days_to_forget = days_to_forget * retention_factor
|
| 173 |
+
|
| 174 |
+
if age_since_evocation_days < effective_days_to_forget:
|
| 175 |
+
retained_memories.append(mem)
|
| 176 |
+
|
| 177 |
+
sorted_memories = sorted(retained_memories, key=lambda m: datetime.fromisoformat(m["timestamp_ultima_evocacao"]), reverse=True)
|
| 178 |
+
|
| 179 |
+
return sorted_memories[:max_memories_to_prompt]
|
| 180 |
+
|
| 181 |
+
# --- Rotas da API ---
|
| 182 |
+
|
| 183 |
+
@app.route('/get_mach5_main_state', methods=['POST'])
|
| 184 |
+
def get_mach5_main_state_route():
|
| 185 |
+
"""Retorna o estado principal da Mach5 (física, genoma, coerência, produtividade) para uma sessão."""
|
| 186 |
+
session_id = request.json.get("session_id")
|
| 187 |
+
if not session_id:
|
| 188 |
+
return jsonify({"status": "error", "message": "session_id é obrigatório"}), 400
|
| 189 |
+
|
| 190 |
+
# Carrega o estado específico da sessão
|
| 191 |
+
mach5_state_main_session = load_session_data(
|
| 192 |
+
session_id, MEMORIA_STATE_FILENAME,
|
| 193 |
+
default_data={
|
| 194 |
+
"t_genoma_fixo": T_GENOMA_FIXO_DEFAULTS,
|
| 195 |
+
"t_fisica_params": T_FISICA_PARAM_DEFAULTS,
|
| 196 |
+
"coerencia_total": 0.5,
|
| 197 |
+
"produtividade_expressiva": 0.5
|
| 198 |
+
},
|
| 199 |
+
file_description="estado principal da Mach5"
|
| 200 |
+
)
|
| 201 |
+
return jsonify(mach5_state_main_session), 200
|
| 202 |
+
|
| 203 |
+
@app.route('/update_mach5_main_state', methods=['POST'])
|
| 204 |
+
def update_mach5_main_state_route():
|
| 205 |
+
"""Atualiza o estado principal da Mach5 (recebido do t_memoria.py após cálculos) para uma sessão."""
|
| 206 |
+
data = request.json
|
| 207 |
+
session_id = data.get("session_id")
|
| 208 |
+
if not session_id:
|
| 209 |
+
return jsonify({"status": "error", "message": "session_id é obrigatório"}), 400
|
| 210 |
+
|
| 211 |
+
updated_data = data.get("state_data")
|
| 212 |
+
if not updated_data:
|
| 213 |
+
return jsonify({"status": "error", "message": "state_data é obrigatório"}), 400
|
| 214 |
+
|
| 215 |
+
# Carrega o estado atual da sessão, atualiza e salva
|
| 216 |
+
mach5_state_main_session = load_session_data(
|
| 217 |
+
session_id, MEMORIA_STATE_FILENAME,
|
| 218 |
+
default_data={
|
| 219 |
+
"t_genoma_fixo": T_GENOMA_FIXO_DEFAULTS,
|
| 220 |
+
"t_fisica_params": T_FISICA_PARAM_DEFAULTS,
|
| 221 |
+
"coerencia_total": 0.5,
|
| 222 |
+
"produtividade_expressiva": 0.5
|
| 223 |
+
},
|
| 224 |
+
file_description="estado principal da Mach5"
|
| 225 |
+
)
|
| 226 |
+
mach5_state_main_session.update(updated_data)
|
| 227 |
+
save_session_data(session_id, MEMORIA_STATE_FILENAME, mach5_state_main_session, "estado principal da Mach5")
|
| 228 |
+
|
| 229 |
+
return jsonify({"status": "success", "message": "Estado principal da Mach5 atualizado"}), 200
|
| 230 |
+
|
| 231 |
+
@app.route('/get_dialog_history', methods=['POST'])
|
| 232 |
+
def get_dialog_history_route():
|
| 233 |
+
"""Retorna o histórico completo de diálogos para uma sessão."""
|
| 234 |
+
session_id = request.json.get("session_id")
|
| 235 |
+
if not session_id:
|
| 236 |
+
return jsonify({"status": "error", "message": "session_id é obrigatório"}), 400
|
| 237 |
+
|
| 238 |
+
dialogos_history_session = load_session_data(
|
| 239 |
+
session_id, DIALOGOS_HISTORY_FILENAME, default_data={"dialogos": []}, file_description="histórico de diálogos"
|
| 240 |
+
)
|
| 241 |
+
return jsonify(dialogos_history_session), 200
|
| 242 |
+
|
| 243 |
+
@app.route('/add_dialog_to_history', methods=['POST'])
|
| 244 |
+
def add_dialog_to_history_route():
|
| 245 |
+
"""Adiciona um novo diálogo ao histórico para uma sessão."""
|
| 246 |
+
data = request.json
|
| 247 |
+
session_id = data.get("session_id")
|
| 248 |
+
if not session_id:
|
| 249 |
+
return jsonify({"status": "error", "message": "session_id é obrigatório"}), 400
|
| 250 |
+
|
| 251 |
+
new_dialog_entry = data.get("dialog_data")
|
| 252 |
+
if not new_dialog_entry:
|
| 253 |
+
return jsonify({"status": "error", "message": "dialog_data é obrigatório"}), 400
|
| 254 |
+
|
| 255 |
+
if "timestamp" not in new_dialog_entry:
|
| 256 |
+
new_dialog_entry["timestamp"] = datetime.now().isoformat()
|
| 257 |
+
|
| 258 |
+
dialogos_history_session = load_session_data(
|
| 259 |
+
session_id, DIALOGOS_HISTORY_FILENAME, default_data={"dialogos": []}, file_description="histórico de diálogos"
|
| 260 |
+
)
|
| 261 |
+
dialogos_history_session["dialogos"].append(new_dialog_entry)
|
| 262 |
+
save_session_data(session_id, DIALOGOS_HISTORY_FILENAME, dialogos_history_session, "histórico de diálogos")
|
| 263 |
+
|
| 264 |
+
return jsonify({"status": "success", "message": "Diálogo adicionado ao histórico"}), 201
|
| 265 |
+
|
| 266 |
+
@app.route('/get_short_term_memories', methods=['POST'])
|
| 267 |
+
def get_short_term_memories_route():
|
| 268 |
+
"""
|
| 269 |
+
Retorna as memórias de curto prazo filtradas e processadas para uma sessão.
|
| 270 |
+
Espera o genoma atual da Mach5 e o session_id no payload.
|
| 271 |
+
"""
|
| 272 |
+
payload = request.json
|
| 273 |
+
session_id = payload.get("session_id")
|
| 274 |
+
if not session_id:
|
| 275 |
+
return jsonify({"status": "error", "message": "session_id é obrigatório"}), 400
|
| 276 |
+
|
| 277 |
+
mach5_current_genoma = payload.get("mach5_current_genoma")
|
| 278 |
+
|
| 279 |
+
short_term_memories_session = load_session_data(
|
| 280 |
+
session_id, MEMORIA_CURTA_PRAZO_FILENAME, default_data={"lembrancas_curto_prazo": []}, file_description="memórias de curto prazo"
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
filtered_memories = process_and_filter_short_term_memories_logic(
|
| 284 |
+
short_term_memories_session["lembrancas_curto_prazo"],
|
| 285 |
+
mach5_current_genoma,
|
| 286 |
+
DIAS_PARA_ESQUECIMENTO_PADRAO,
|
| 287 |
+
MAX_MEMORIAS_CURTO_PRAZO_PROMPT
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
current_time_iso = datetime.now().isoformat()
|
| 291 |
+
updated_memories_list = []
|
| 292 |
+
# Atualiza o timestamp_ultima_evocacao apenas para as memórias que foram efetivamente evocadas (filtradas)
|
| 293 |
+
for mem in short_term_memories_session["lembrancas_curto_prazo"]:
|
| 294 |
+
if any(selected_mem['id'] == mem['id'] for selected_mem in filtered_memories):
|
| 295 |
+
mem["timestamp_ultima_evocacao"] = current_time_iso
|
| 296 |
+
updated_memories_list.append(mem)
|
| 297 |
+
short_term_memories_session["lembrancas_curto_prazo"] = updated_memories_list
|
| 298 |
+
save_session_data(session_id, MEMORIA_CURTA_PRAZO_FILENAME, short_term_memories_session, "memórias de curto prazo")
|
| 299 |
+
|
| 300 |
+
return jsonify({"lembrancas_curto_prazo": filtered_memories}), 200
|
| 301 |
+
|
| 302 |
+
@app.route('/add_short_term_memory', methods=['POST'])
|
| 303 |
+
def add_short_term_memory_route():
|
| 304 |
+
"""
|
| 305 |
+
Adiciona uma nova memória à lista de curto prazo para uma sessão.
|
| 306 |
+
Esperado: {"session_id": "...", "memory_data": {"conteudo": "...", "dominant_sentiment_criacao": "...", "coh_criacao": 0.X}}
|
| 307 |
+
"""
|
| 308 |
+
data = request.json
|
| 309 |
+
session_id = data.get("session_id")
|
| 310 |
+
if not session_id:
|
| 311 |
+
return jsonify({"status": "error", "message": "session_id é obrigatório"}), 400
|
| 312 |
+
|
| 313 |
+
new_memory_data = data.get("memory_data")
|
| 314 |
+
if not new_memory_data:
|
| 315 |
+
return jsonify({"status": "error", "message": "memory_data é obrigatório"}), 400
|
| 316 |
+
|
| 317 |
+
short_term_memories_session = load_session_data(
|
| 318 |
+
session_id, MEMORIA_CURTA_PRAZO_FILENAME, default_data={"lembrancas_curto_prazo": []}, file_description="memórias de curto prazo"
|
| 319 |
+
)
|
| 320 |
+
|
| 321 |
+
memory_entry = {
|
| 322 |
+
"id": str(uuid.uuid4()),
|
| 323 |
+
"conteudo": new_memory_data.get("conteudo"),
|
| 324 |
+
"timestamp_criacao": datetime.now().isoformat(),
|
| 325 |
+
"timestamp_ultima_evocacao": datetime.now().isoformat(),
|
| 326 |
+
"dominant_sentiment_criacao": new_memory_data.get("dominant_sentiment_criacao", "neutral"),
|
| 327 |
+
"coh_criacao": new_memory_data.get("coh_criacao", 0.5)
|
| 328 |
+
}
|
| 329 |
+
short_term_memories_session["lembrancas_curto_prazo"].append(memory_entry)
|
| 330 |
+
save_session_data(session_id, MEMORIA_CURTA_PRAZO_FILENAME, short_term_memories_session, "memórias de curto prazo")
|
| 331 |
+
|
| 332 |
+
return jsonify({"status": "success", "message": "Memória de curto prazo adicionada", "id": memory_entry["id"]}), 201
|
| 333 |
+
|
| 334 |
+
@app.route('/get_initial_biography', methods=['GET'])
|
| 335 |
+
def get_initial_biography_route():
|
| 336 |
+
"""Retorna a biografia inicial da Mach5 (este continua global)."""
|
| 337 |
+
return jsonify(initial_biography), 200
|
| 338 |
+
|
| 339 |
+
# --- Inicialização do Serviço ---
|
| 340 |
+
if __name__ == '__main__':
|
| 341 |
+
# Cria a pasta base para os dados de sessão se ela não existir
|
| 342 |
+
os.makedirs(SESSION_DATA_DIR, exist_ok=True)
|
| 343 |
+
print(f"DEBUG: Diretório de sessões '{SESSION_DATA_DIR}' verificado/criado.")
|
| 344 |
+
|
| 345 |
+
# Carrega apenas a biografia inicial global, que é fixa para todas as sessões.
|
| 346 |
+
initial_biography = load_data(BIOGRAFIA_INICIAL_PATH, default_data={}, file_description="biografia inicial")
|
| 347 |
+
|
| 348 |
+
# A porta preferencial virá da variável de ambiente 'PORT' setada por Mach5.py
|
| 349 |
+
# Se ela não for encontrada, o script DEVE falhar.
|
| 350 |
+
try:
|
| 351 |
+
port = int(os.environ["PORT"])
|
| 352 |
+
except KeyError:
|
| 353 |
+
print("ERRO: Variável de ambiente 'PORT' não definida. Este script deve ser iniciado via Mach5.py.")
|
| 354 |
+
sys.exit(1)
|
| 355 |
+
except ValueError:
|
| 356 |
+
print("ERRO: O valor da variável de ambiente 'PORT' não é um número válido.")
|
| 357 |
+
sys.exit(1)
|
| 358 |
+
|
| 359 |
+
print(f"--- Servidor t_cerebro_memoria.py iniciado na porta {port} ---")
|
| 360 |
+
app.run(port=port, debug=True)
|
t_memoria.py
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# t_memoria.py
|
| 2 |
+
from flask import Flask, request, jsonify
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
import pytz
|
| 7 |
+
from timezonefinder import TimezoneFinder
|
| 8 |
+
from fuzzywuzzy import fuzz
|
| 9 |
+
import numpy as np
|
| 10 |
+
import math
|
| 11 |
+
import sys # Importar sys para usar sys.exit
|
| 12 |
+
import requests # Importar requests para comunicação com t_cerebro_memoria.py
|
| 13 |
+
|
| 14 |
+
app = Flask(__name__)
|
| 15 |
+
|
| 16 |
+
# REMOVIDO: MEMORIA_FILE e BIOGRAFIA_INICIAL_PATH - Agora gerenciados por t_cerebro_memoria.py
|
| 17 |
+
# MEMORIA_FILE = "t_memoria.json" # Removido
|
| 18 |
+
# BIOGRAFIA_INICIAL_PATH = "mach5_biografia_inicial.json" # Removido
|
| 19 |
+
|
| 20 |
+
# --- URL DO SERVIÇO T_CEREBRO_MEMORIA ---
|
| 21 |
+
TCEREBRO_MEMORIA_URL = "http://127.0.0.1:8088" # URL do t_cerebro_memoria.py
|
| 22 |
+
|
| 23 |
+
# --- DEFINIÇÃO DOS EIXOS EXPRESSIVOS PARA O FPHEN (AGORA CONSISTENTE) ---
|
| 24 |
+
# Esta lista DEVE ser idêntica em mach5_terminal_chat.py, t_memoria.py e t-social.py
|
| 25 |
+
ORDERED_FPHEN_AXES = [
|
| 26 |
+
"Afetuosidade_eixo", "Confusao_Oscilacao_eixo", "Contemplativa_eixo",
|
| 27 |
+
"Defensividade_eixo", "Diretiva_eixo", "Entediado_eixo",
|
| 28 |
+
"Variancia_eixo", # Adicionada esta linha para garantir que o eixo de Variância exista
|
| 29 |
+
"Espelho_Profundo_eixo", "Inspiracao_eixo", "Neutralidade_Analitica_eixo",
|
| 30 |
+
"Resignada_eixo", "Sarcasmo_eixo", "Zangada_eixo"
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
# --- LISTA CANÔNICA DOS 9 T-BASES (INALTERADA) ---
|
| 34 |
+
CANONICAL_T_BASES = {
|
| 35 |
+
"B-Aj1": 0.5, # Adaptabilidade/Ajuste
|
| 36 |
+
"B-Am1": 0.5, # Amor/Amortecimento
|
| 37 |
+
"B-L1": 0.5, # Ligação/Coesão
|
| 38 |
+
"B-M1": 0.5, # Maturidade/Reflexão
|
| 39 |
+
"B-O1": 0.5, # Organização/Ordem
|
| 40 |
+
"B-E1": 0.5, # Emissão/Energia
|
| 41 |
+
"B-Com1": 0.5, # Comunicação
|
| 42 |
+
"B-Tv1": 0.5, # Variância
|
| 43 |
+
"B-Si": 0.5, # Singularidade/Silêncio
|
| 44 |
+
"B-Sal": 0.5 # Saturação/Liberdade
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
# --- PARÂMETROS PARA O GENOMA PRINCIPAL DA MACH5 (GENOMA FUNDADOR) ---
|
| 48 |
+
MACH5_T_GENOMA_FIXO_DEFAULTS = {
|
| 49 |
+
"B-Sal": 0.42, "B-Tv1": 0.60, "B-E1": 0.86, "B-Aj1": 0.76, "B-Am1": 0.77,
|
| 50 |
+
"B-Si": 0.10, "B-Com1": 0.88, "B-L1": 0.70, "B-M1": 0.65, "B-O1": 0.55
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
MACH5_T_FISICA_PARAMS_DEFAULTS = {
|
| 54 |
+
"t_coesao": 0.83, "t_intensidade": 0.81, "t_separacao": 0.19, "t_variancia": 0.71, "t_impulsividade": 0.54,
|
| 55 |
+
"env_lambda": 0.73, "env_mu": 0.89, "env_delta": 0.22, "env_theta": 0.64, "env_phi": 0.67,
|
| 56 |
+
"op_tal": 0.84, "op_trs": 0.14, "op_tam": 0.87, "op_teo": 0.67, "op_tdo": 0.55
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
# --- Configurações Iniciais da Mach5 (AGORA USANDO OS DEFAULTS FUNDADORES) ---
|
| 60 |
+
# mach5_state AGORA É UMA VARIÁVEL GLOBAL QUE SERÁ ATUALIZADA POR SESSÃO
|
| 61 |
+
# Ela não conterá mais os defaults fixos ao iniciar o app, mas sim o estado da última sessão ou o default para uma nova.
|
| 62 |
+
mach5_state = {
|
| 63 |
+
"t_genoma_fixo": MACH5_T_GENOMA_FIXO_DEFAULTS,
|
| 64 |
+
"t_fisica_params": MACH5_T_FISICA_PARAMS_DEFAULTS,
|
| 65 |
+
"historico_interacoes": [], # Este histórico será descartado ou não usado se for por sessão.
|
| 66 |
+
"coerencia_total": 0.5,
|
| 67 |
+
"produtividade_expressiva": 0.5
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
# Palavras-chave para análise de sentimento (INALTERADAS)
|
| 71 |
+
POSITIVE_KEYWORDS = ['bacana', 'interessante', 'feliz', 'gosto', 'legal', 'bom', 'ótimo', 'maravilhoso']
|
| 72 |
+
NEGATIVE_KEYWORDS = ['burro', 'idiota', 'imbecil', 'odeio', 'morra', 'inútil', 'verme', 'exploda']
|
| 73 |
+
QUESTION_KEYWORDS = ['?', 'como', 'quem', 'qual', 'onde', 'quando', 'por que']
|
| 74 |
+
|
| 75 |
+
# REMOVIDO: Carrega a biografia inicial da Mach5 - Agora é responsabilidade do t_cerebro_memoria.py
|
| 76 |
+
|
| 77 |
+
def load_mach5_state_from_cerebro(session_id):
|
| 78 |
+
"""
|
| 79 |
+
Carrega o estado da Mach5 do t_cerebro_memoria.py para uma sessão específica.
|
| 80 |
+
Se não houver estado para a sessão, o cerebro_memoria.py retornará os defaults.
|
| 81 |
+
"""
|
| 82 |
+
global mach5_state
|
| 83 |
+
try:
|
| 84 |
+
response = requests.post(f"{TCEREBRO_MEMORIA_URL}/get_mach5_main_state",
|
| 85 |
+
json={"session_id": session_id}, timeout=2)
|
| 86 |
+
response.raise_for_status()
|
| 87 |
+
loaded_state = response.json()
|
| 88 |
+
mach5_state.update(loaded_state) # Atualiza o estado global mach5_state com o que veio do cerebro
|
| 89 |
+
print(f"DEBUG: Estado da Mach5 para sessão {session_id} carregado do t_cerebro_memoria.py.")
|
| 90 |
+
except requests.exceptions.RequestException as e:
|
| 91 |
+
print(f"ERRO: Não foi possível carregar o estado da Mach5 para sessão {session_id} do t_cerebro_memoria.py: {e}")
|
| 92 |
+
print("INFO: Usando defaults internos para o estado da Mach5.")
|
| 93 |
+
mach5_state = { # Em caso de falha de comunicação, resetar para os defaults internos.
|
| 94 |
+
"t_genoma_fixo": MACH5_T_GENOMA_FIXO_DEFAULTS,
|
| 95 |
+
"t_fisica_params": MACH5_T_FISICA_PARAMS_DEFAULTS,
|
| 96 |
+
"historico_interacoes": [], # Manter este vazio ou remover se não for usado.
|
| 97 |
+
"coerencia_total": 0.5,
|
| 98 |
+
"produtividade_expressiva": 0.5
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
def save_mach5_state_to_cerebro(session_id):
|
| 102 |
+
"""
|
| 103 |
+
Salva o estado atual da Mach5 no t_cerebro_memoria.py para uma sessão específica.
|
| 104 |
+
"""
|
| 105 |
+
try:
|
| 106 |
+
# Apenas os dados relevantes do estado são enviados. historico_interacoes não é mais salvo aqui.
|
| 107 |
+
state_to_save = {
|
| 108 |
+
"t_genoma_fixo": mach5_state["t_genoma_fixo"],
|
| 109 |
+
"t_fisica_params": mach5_state["t_fisica_params"],
|
| 110 |
+
"coerencia_total": mach5_state["coerencia_total"],
|
| 111 |
+
"produtividade_expressiva": mach5_state["produtividade_expressiva"]
|
| 112 |
+
}
|
| 113 |
+
response = requests.post(f"{TCEREBRO_MEMORIA_URL}/update_mach5_main_state",
|
| 114 |
+
json={"session_id": session_id, "state_data": state_to_save}, timeout=2)
|
| 115 |
+
response.raise_for_status()
|
| 116 |
+
print(f"DEBUG: Estado da Mach5 para sessão {session_id} salvo no t_cerebro_memoria.py.")
|
| 117 |
+
except requests.exceptions.RequestException as e:
|
| 118 |
+
print(f"ERRO: Não foi possível salvar o estado da Mach5 para sessão {session_id} no t_cerebro_memoria.py: {e}")
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def calculate_coherence_and_productivity(user_input_sentiment_type):
|
| 122 |
+
global mach5_state
|
| 123 |
+
|
| 124 |
+
current_params = mach5_state["t_fisica_params"]
|
| 125 |
+
current_genoma = mach5_state["t_genoma_fixo"]
|
| 126 |
+
|
| 127 |
+
# --- Cálculo de Coerência Total ---
|
| 128 |
+
coh_base = 0.7 # Uma base razoável de coerência
|
| 129 |
+
|
| 130 |
+
# Ajuste pela intensidade do genoma (agora usando os novos valores)
|
| 131 |
+
genoma_intensity = current_genoma.get("B-E1", 0.5) * 0.4 + current_genoma.get("B-Tv1", 0.5) * 0.3 + current_genoma.get("B-Sal", 0.5) * 0.3
|
| 132 |
+
coh_adj_genoma = (genoma_intensity - 0.5) * 0.1 # Se o genoma é muito intenso, pode gerar menos coerência "calma"
|
| 133 |
+
|
| 134 |
+
# Ajuste pelos parâmetros de física (agora usando os novos valores)
|
| 135 |
+
fisica_coherence_factors = (
|
| 136 |
+
(1 - abs(current_params.get("t_impulsividade", 0.5) - 0.5)) * 0.2 + # Impulsividade afeta a coerência
|
| 137 |
+
(1 - abs(current_params.get("t_variancia", 0.5) - 0.5)) * 0.2 +
|
| 138 |
+
current_params.get("t_coesao", 0.5) * 0.3 +
|
| 139 |
+
(1 - current_params.get("t_separacao", 0.5)) * 0.1 # Muita separação pode reduzir coerência
|
| 140 |
+
)
|
| 141 |
+
coh_adj_fisica = (fisica_coherence_factors - 0.5) * 0.2
|
| 142 |
+
|
| 143 |
+
# Ajuste pelo sentimento do usuário
|
| 144 |
+
sentiment_adj = 0
|
| 145 |
+
# O comportamento de ajuste agora é baseado nos novos genomas
|
| 146 |
+
if user_input_sentiment_type == 'negative':
|
| 147 |
+
sentiment_adj = -0.15 * current_genoma.get("B-Si", 0.5) # Negatividade pode aumentar silêncio/reduzir coerência
|
| 148 |
+
current_params["t_intensidade"] = min(1.0, current_params["t_intensidade"] + 0.1 * current_genoma.get("B-Sal", 0.5)) # Intensidade sobe com franqueza
|
| 149 |
+
current_params["t_impulsividade"] = min(1.0, current_params["t_impulsividade"] + 0.1 * current_genoma.get("B-Tv1", 0.5)) # Impulsividade sobe com flutuação
|
| 150 |
+
current_params["t_separacao"] = min(1.0, current_params["t_separacao"] + 0.1 * (1 - current_genoma.get("B-Am1", 0.5))) # Separação aumenta se menos amparo
|
| 151 |
+
current_params["op_tdo"] = min(1.0, current_params["op_tdo"] + 0.1 * current_genoma.get("B-Tv1", 0.5))
|
| 152 |
+
current_params["op_trs"] = min(1.0, current_params["op_trs"] + 0.1 * (1 - current_genoma.get("B-L1", 0.5))) # Saturação aumenta se menos lógica de ligação
|
| 153 |
+
current_params["t_coesao"] = max(0.0, current_params["t_coesao"] - 0.1 * current_genoma.get("B-Si", 0.5))
|
| 154 |
+
current_params["op_tam"] = max(0.0, current_params["op_tam"] - 0.1 * (1 - current_genoma.get("B-Am1", 0.5)))
|
| 155 |
+
# Ajuste dinâmico para t_variancia em input negativo:
|
| 156 |
+
current_params["t_variancia"] = min(1.0, current_params["t_variancia"] + 0.08) # Aumenta a variância em resposta a negatividade
|
| 157 |
+
elif user_input_sentiment_type == 'positive':
|
| 158 |
+
sentiment_adj = 0.05 * current_genoma.get("B-E1", 0.5) # Positividade aumenta coerência pela empatia
|
| 159 |
+
current_params["t_intensidade"] = max(0.0, current_params["t_intensidade"] - 0.05 * (1 - current_genoma.get("B-Sal", 0.5)))
|
| 160 |
+
current_params["t_impulsividade"] = max(0.0, current_params["t_impulsividade"] - 0.05 * (1 - current_genoma.get("B-Tv1", 0.5)))
|
| 161 |
+
current_params["t_separacao"] = max(0.0, current_params["t_separacao"] - 0.05 * current_genoma.get("B-Am1", 0.5))
|
| 162 |
+
current_params["op_tdo"] = max(0.0, current_params["op_tdo"] - 0.05 * (1 - current_genoma.get("B-M1", 0.5)))
|
| 163 |
+
current_params["op_trs"] = max(0.0, current_params["op_trs"] - 0.05 * current_genoma.get("B-L1", 0.5))
|
| 164 |
+
current_params["t_coesao"] = min(1.0, current_params["t_coesao"] + 0.05 * current_genoma.get("B-Com1", 0.5))
|
| 165 |
+
current_params["op_tam"] = min(1.0, current_params["op_tam"] + 0.05 * current_genoma.get("B-Am1", 0.5))
|
| 166 |
+
# Ajuste dinâmico para t_variancia em input positivo:
|
| 167 |
+
current_params["t_variancia"] = max(0.0, current_params["t_variancia"] - 0.04) # Diminui a variância em resposta a positividade, buscando mais estabilidade
|
| 168 |
+
else: # Neutral
|
| 169 |
+
sentiment_adj = 0
|
| 170 |
+
# Pequena regressão aos valores padrão dos genomas da Mach5
|
| 171 |
+
for param, default_val in MACH5_T_FISICA_PARAMS_DEFAULTS.items():
|
| 172 |
+
current_params[param] = current_params.get(param, default_val) * 0.95 + default_val * 0.05
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
new_coherence_total = coh_base + coh_adj_genoma + coh_adj_fisica + sentiment_adj
|
| 176 |
+
new_coherence_total = max(0.0, min(1.0, new_coherence_total)) # Limita entre 0 e 1
|
| 177 |
+
|
| 178 |
+
# --- Cálculo de Produtividade Expressiva (pi_G) ---
|
| 179 |
+
prod_base = 0.5
|
| 180 |
+
prod_adj_genoma = (genoma_intensity - 0.5) * 0.2 # Mais intenso o genoma, mais produtivo para expressar isso
|
| 181 |
+
|
| 182 |
+
if user_input_sentiment_type == 'negative':
|
| 183 |
+
prod_adj_sentiment = 0.1 * current_genoma.get("B-Tv1", 0.5) # Aumenta produtividade com flutuação em negativo
|
| 184 |
+
elif user_input_sentiment_type == 'positive':
|
| 185 |
+
prod_adj_sentiment = -0.05 * current_genoma.get("B-Am1", 0.5) # Diminui um pouco com amparo em positivo (mais calma)
|
| 186 |
+
else:
|
| 187 |
+
prod_adj_sentiment = 0
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
new_produtividade_expressiva = prod_base + prod_adj_genoma + prod_adj_sentiment
|
| 191 |
+
new_produtividade_expressiva = max(0.0, min(1.0, new_produtividade_expressiva)) # Limita entre 0 e 1
|
| 192 |
+
|
| 193 |
+
mach5_state["coerencia_total"] = new_coherence_total
|
| 194 |
+
mach5_state["produtividade_expressiva"] = new_produtividade_expressiva
|
| 195 |
+
mach5_state["t_fisica_params"] = current_params # Salva os parâmetros atualizados
|
| 196 |
+
|
| 197 |
+
def infer_sentiment_from_input(user_input):
|
| 198 |
+
user_input_lower = user_input.lower()
|
| 199 |
+
|
| 200 |
+
# Simple keyword matching for sentiment
|
| 201 |
+
positive_matches = sum(1 for keyword in POSITIVE_KEYWORDS if fuzz.partial_ratio(keyword, user_input_lower) > 80)
|
| 202 |
+
negative_matches = sum(1 for keyword in NEGATIVE_KEYWORDS if fuzz.partial_ratio(keyword, user_input_lower) > 80)
|
| 203 |
+
question_matches = sum(1 for keyword in QUESTION_KEYWORDS if keyword in user_input_lower)
|
| 204 |
+
|
| 205 |
+
if negative_matches > positive_matches * 1.5: # Prioriza negativos
|
| 206 |
+
return 'negative'
|
| 207 |
+
elif positive_matches > negative_matches * 1.5:
|
| 208 |
+
return 'positive'
|
| 209 |
+
elif question_matches > 0 and (positive_matches == 0 and negative_matches == 0):
|
| 210 |
+
return 'question'
|
| 211 |
+
else:
|
| 212 |
+
return 'neutral'
|
| 213 |
+
|
| 214 |
+
def calculate_fphen_values(params, genoma_fixo):
|
| 215 |
+
# Combina o genoma fixo com as t-bases canônicas, dando prioridade ao genoma fixo.
|
| 216 |
+
full_genoma_for_calc = CANONICAL_T_BASES.copy()
|
| 217 |
+
full_genoma_for_calc.update(genoma_fixo)
|
| 218 |
+
|
| 219 |
+
# Coeficientes para as sensibilidades, normalizando o impacto
|
| 220 |
+
sensibilidade_positiva = (
|
| 221 |
+
full_genoma_for_calc.get("B-Aj1", 0.5) * 0.25 +
|
| 222 |
+
full_genoma_for_calc.get("B-Am1", 0.5) * 0.25 +
|
| 223 |
+
full_genoma_for_calc.get("B-L1", 0.5) * 0.25 +
|
| 224 |
+
full_genoma_for_calc.get("B-E1", 0.5) * 0.25
|
| 225 |
+
)
|
| 226 |
+
sensibilidade_positiva = max(0.1, min(1.0, sensibilidade_positiva)) # Garante um mínimo de sensibilidade
|
| 227 |
+
|
| 228 |
+
sensibilidade_negativa = (
|
| 229 |
+
full_genoma_for_calc.get("B-Si", 0.5) * 0.25 +
|
| 230 |
+
(1 - full_genoma_for_calc.get("B-Aj1", 0.5)) * 0.25 +
|
| 231 |
+
full_genoma_for_calc.get("B-Sal", 0.5) * 0.25 +
|
| 232 |
+
full_genoma_for_calc.get("B-Tv1", 0.5) * 0.25
|
| 233 |
+
)
|
| 234 |
+
sensibilidade_negativa = max(0.1, min(1.0, sensibilidade_negativa))
|
| 235 |
+
|
| 236 |
+
sensibilidade_neutra = (
|
| 237 |
+
full_genoma_for_calc.get("B-M1", 0.5) * 0.25 +
|
| 238 |
+
full_genoma_for_calc.get("B-O1", 0.5) * 0.25 +
|
| 239 |
+
full_genoma_for_calc.get("B-Com1", 0.5) * 0.25 +
|
| 240 |
+
(1 - abs(full_genoma_for_calc.get("B-Tv1", 0.5) - 0.5)) * 0.25
|
| 241 |
+
)
|
| 242 |
+
sensibilidade_neutra = max(0.1, min(1.0, sensibilidade_neutra))
|
| 243 |
+
|
| 244 |
+
# --- Cálculo dos Eixos do Fphen ---
|
| 245 |
+
phenotype_components = {
|
| 246 |
+
"Afetuosidade_eixo": (
|
| 247 |
+
params.get("env_mu", 0.5) * 0.3 + params.get("t_coesao", 0.5) * 0.2 +
|
| 248 |
+
params.get("op_tal", 0.5) * 0.2 + full_genoma_for_calc.get("B-Am1", 0.5) * 0.2 +
|
| 249 |
+
full_genoma_for_calc.get("B-L1", 0.5) * 0.1
|
| 250 |
+
) * sensibilidade_positiva * (1.2 if params.get("env_phi", 0.5) > 0.6 else 1.0),
|
| 251 |
+
|
| 252 |
+
"Zangada_eixo": (
|
| 253 |
+
params.get("t_intensidade", 0.5) * 0.3 + params.get("env_delta", 0.5) * 0.25 +
|
| 254 |
+
params.get("t_impulsividade", 0.5) * 0.25 + params.get("op_trs", 0.5) * 0.1 +
|
| 255 |
+
(1 - params.get("t_coesao", 0.5)) * 0.1 + params.get("t_separacao", 0.5) * 0.05 +
|
| 256 |
+
full_genoma_for_calc.get("B-Sal", 0.5) * 0.05 + (1 - full_genoma_for_calc.get("B-Am1", 0.5)) * 0.05
|
| 257 |
+
) * sensibilidade_negativa * (1.3 if params.get("env_delta", 0.5) > 0.6 else 1.0),
|
| 258 |
+
|
| 259 |
+
"Defensividade_eixo": (
|
| 260 |
+
params.get("t_separacao", 0.5) * 0.4 + params.get("op_trs", 0.5) * 0.3 +
|
| 261 |
+
abs(params.get("env_delta", 0.5) - 0.5) * 0.15 + params.get("t_intensidade", 0.5) * 0.1 +
|
| 262 |
+
full_genoma_for_calc.get("B-Si", 0.5) * 0.05 + (1 - full_genoma_for_calc.get("B-Aj1", 0.5)) * 0.05
|
| 263 |
+
) * sensibilidade_negativa * (1.3 if params.get("t_impulsividade", 0.5) > 0.6 else 1.0),
|
| 264 |
+
|
| 265 |
+
"Inspiracao_eixo": (
|
| 266 |
+
params.get("t_variancia", 0.5) * 0.3 + params.get("t_coesao", 0.5) * 0.2 +
|
| 267 |
+
params.get("env_delta", 0.5) * 0.15 + full_genoma_for_calc.get("B-E1", 0.5) * 0.2 +
|
| 268 |
+
full_genoma_for_calc.get("B-Tv1", 0.5) * 0.15
|
| 269 |
+
) * sensibilidade_positiva,
|
| 270 |
+
|
| 271 |
+
"Neutralidade_Analitica_eixo": (
|
| 272 |
+
(1 - abs(params.get("t_intensidade", 0.5) - 0.5)) * 0.2 +
|
| 273 |
+
(1 - abs(params.get("t_variancia", 0.5) - 0.5)) * 0.2 +
|
| 274 |
+
(1 - abs(params.get("t_impulsividade", 0.5) - 0.5)) * 0.2 +
|
| 275 |
+
(1 - params.get("env_mu", 0.5)) * 0.1 +
|
| 276 |
+
full_genoma_for_calc.get("B-M1", 0.5) * 0.15 + full_genoma_for_calc.get("B-O1", 0.5) * 0.15
|
| 277 |
+
) * sensibilidade_neutra,
|
| 278 |
+
|
| 279 |
+
"Confusao_Oscilacao_eixo": (
|
| 280 |
+
params.get("t_variancia", 0.5) * 0.3 + (1 - params.get("t_coesao", 0.5)) * 0.2 +
|
| 281 |
+
params.get("op_tdo", 0.5) * 0.2 + full_genoma_for_calc.get("B-Tv1", 0.5) * 0.15 +
|
| 282 |
+
(1 - full_genoma_for_calc.get("B-O1", 0.5)) * 0.15
|
| 283 |
+
) * sensibilidade_neutra,
|
| 284 |
+
|
| 285 |
+
"Sarcasmo_eixo": (
|
| 286 |
+
params.get("t_separacao", 0.5) * 0.3 + params.get("op_trs", 0.5) * 0.2 +
|
| 287 |
+
params.get("t_intensidade", 0.5) * 0.2 + (1 - full_genoma_for_calc.get("B-Am1", 0.5)) * 0.15 +
|
| 288 |
+
full_genoma_for_calc.get("B-Com1", 0.5) * 0.1 + (1 - full_genoma_for_calc.get("B-L1", 0.5)) * 0.05
|
| 289 |
+
) * sensibilidade_negativa,
|
| 290 |
+
|
| 291 |
+
"Entediado_eixo": (
|
| 292 |
+
(1 - params.get("t_intensidade", 0.5)) * 0.3 + (1 - params.get("env_lambda", 0.5)) * 0.2 +
|
| 293 |
+
(1 - params.get("env_theta", 0.5)) * 0.1 + full_genoma_for_calc.get("B-Si", 0.5) * 0.2 +
|
| 294 |
+
(1 - full_genoma_for_calc.get("B-E1", 0.5)) * 0.2
|
| 295 |
+
),
|
| 296 |
+
|
| 297 |
+
"Diretiva_eixo": (
|
| 298 |
+
params.get("t_coesao", 0.5) * 0.25 + params.get("t_intensidade", 0.5) * 0.2 +
|
| 299 |
+
(1 - params.get("t_variancia", 0.5)) * 0.15 + params.get("op_teo", 0.5) * 0.1 +
|
| 300 |
+
full_genoma_for_calc.get("B-O1", 0.5) * 0.15 + full_genoma_for_calc.get("B-M1", 0.5) * 0.15
|
| 301 |
+
),
|
| 302 |
+
|
| 303 |
+
"Resignada_eixo": (
|
| 304 |
+
(1 - params.get("t_intensidade", 0.5)) * 0.25 + params.get("op_tam", 0.5) * 0.2 +
|
| 305 |
+
(1 - params.get("t_coesao", 0.5)) * 0.1 + full_genoma_for_calc.get("B-Si", 0.5) * 0.2 +
|
| 306 |
+
(1 - full_genoma_for_calc.get("B-Aj1", 0.5)) * 0.15
|
| 307 |
+
),
|
| 308 |
+
|
| 309 |
+
"Espelho_Profundo_eixo": (
|
| 310 |
+
params.get("t_coesao", 0.5) * 0.2 + params.get("env_phi", 0.5) * 0.25 +
|
| 311 |
+
params.get("op_tal", 0.5) * 0.15 + params.get("op_teo", 0.5) * 0.15 +
|
| 312 |
+
full_genoma_for_calc.get("B-L1", 0.5) * 0.15 + full_genoma_for_calc.get("B-M1", 0.5) * 0.1
|
| 313 |
+
) * sensibilidade_positiva,
|
| 314 |
+
|
| 315 |
+
"Contemplativa_eixo": (
|
| 316 |
+
params.get("env_theta", 0.5) * 0.3 + full_genoma_for_calc.get("B-M1", 0.5) * 0.2 +
|
| 317 |
+
(1 - params.get("t_intensidade", 0.5)) * 0.15 + full_genoma_for_calc.get("B-Si", 0.5) * 0.15
|
| 318 |
+
) * sensibilidade_neutra,
|
| 319 |
+
|
| 320 |
+
# CORREÇÃO CRÍTICA: Adição do cálculo para o Variancia_eixo
|
| 321 |
+
"Variancia_eixo": (
|
| 322 |
+
params.get("t_variancia", 0.5) * 0.4 + # Influência direta do t_variancia do t_fisica_params
|
| 323 |
+
full_genoma_for_calc.get("B-Tv1", 0.5) * 0.3 + # Influência do t-gene de variância
|
| 324 |
+
abs(params.get("t_impulsividade", 0.5) - 0.5) * 0.2 + # Impulsividade pode contribuir para variância
|
| 325 |
+
(1 - params.get("t_coesao", 0.5)) * 0.1 # Baixa coesão pode aumentar variância
|
| 326 |
+
) * sensibilidade_neutra # Variância como eixo neutro, mas pode ser modulado por sentimentos
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
# Garante que nenhum valor seja negativo
|
| 330 |
+
for key in phenotype_components:
|
| 331 |
+
phenotype_components[key] = max(0, phenotype_components[key])
|
| 332 |
+
|
| 333 |
+
# Normaliza os scores para que somem 1.0 (representando a proporção de cada eixo)
|
| 334 |
+
total_score = sum(phenotype_components.values())
|
| 335 |
+
if total_score > 0:
|
| 336 |
+
for key in phenotype_components:
|
| 337 |
+
phenotype_components[key] /= total_score
|
| 338 |
+
else: # Caso todos os scores sejam 0, distribui igualmente para evitar divisão por zero
|
| 339 |
+
for key in phenotype_components:
|
| 340 |
+
phenotype_components[key] = 1.0 / len(ORDERED_FPHEN_AXES)
|
| 341 |
+
|
| 342 |
+
fphen_values = [phenotype_components.get(key, 0.0) for key in ORDERED_FPHEN_AXES]
|
| 343 |
+
return fphen_values
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
@app.route('/evaluate_input', methods=['POST'])
|
| 347 |
+
def evaluate_input():
|
| 348 |
+
data = request.json
|
| 349 |
+
user_input = data.get("user_input", "")
|
| 350 |
+
session_id = data.get("session_id") # Pega o session_id do request
|
| 351 |
+
|
| 352 |
+
if not session_id:
|
| 353 |
+
return jsonify({"error": "session_id é obrigatório"}), 400
|
| 354 |
+
|
| 355 |
+
# Carrega o estado da Mach5 para esta sessão antes de processar
|
| 356 |
+
load_mach5_state_from_cerebro(session_id)
|
| 357 |
+
|
| 358 |
+
user_input_sentiment_type = infer_sentiment_from_input(user_input)
|
| 359 |
+
|
| 360 |
+
calculate_coherence_and_productivity(user_input_sentiment_type)
|
| 361 |
+
|
| 362 |
+
fphen_values = calculate_fphen_values(mach5_state["t_fisica_params"], mach5_state["t_genoma_fixo"])
|
| 363 |
+
|
| 364 |
+
# historico_interacoes não é mais relevante para o estado principal aqui,
|
| 365 |
+
# ele será gerenciado diretamente pelo t_cerebro_memoria.py via add_dialog_to_history
|
| 366 |
+
|
| 367 |
+
save_mach5_state_to_cerebro(session_id) # Salva o estado atualizado no t_cerebro_memoria.py
|
| 368 |
+
|
| 369 |
+
# Retorna o estado atualizado da Mach5 para o mach5_terminal_chat.py
|
| 370 |
+
# INCLUINDO OS VALORES DO FPHEN AQUI
|
| 371 |
+
return jsonify({
|
| 372 |
+
"mach5_fisica_params": mach5_state["t_fisica_params"],
|
| 373 |
+
"mach5_genoma_fixo_values": mach5_state["t_genoma_fixo"],
|
| 374 |
+
"mach5_coerencia_total": mach5_state["coerencia_total"],
|
| 375 |
+
"mach5_produtividade_expressiva": mach5_state["produtividade_expressiva"],
|
| 376 |
+
# ATUALIZADO: Inclui os valores específicos para Fphen(t) como um dicionário
|
| 377 |
+
"fphen_t_values": {
|
| 378 |
+
"afetuosidade": fphen_values[ORDERED_FPHEN_AXES.index("Afetuosidade_eixo")],
|
| 379 |
+
# CORREÇÃO AQUI: Acessa 'Variancia_eixo' diretamente.
|
| 380 |
+
"variancia": fphen_values[ORDERED_FPHEN_AXES.index("Variancia_eixo")],
|
| 381 |
+
"expressividade": mach5_state["produtividade_expressiva"], # Usar a produtividade como expressividade geral
|
| 382 |
+
"coh_total": mach5_state["coerencia_total"],
|
| 383 |
+
"pi_G": mach5_state["produtividade_expressiva"]
|
| 384 |
+
}
|
| 385 |
+
})
|
| 386 |
+
|
| 387 |
+
@app.route('/get_mach5_state', methods=['POST']) # Alterado para POST para receber session_id no body
|
| 388 |
+
def get_mach5_state_route():
|
| 389 |
+
"""
|
| 390 |
+
Rota para mach5_terminal_chat.py obter o estado atual sem enviar input.
|
| 391 |
+
Agora requer um session_id.
|
| 392 |
+
"""
|
| 393 |
+
session_id = request.json.get("session_id")
|
| 394 |
+
if not session_id:
|
| 395 |
+
return jsonify({"error": "session_id é obrigatório"}), 400
|
| 396 |
+
|
| 397 |
+
# Carrega o estado da Mach5 para esta sessão
|
| 398 |
+
load_mach5_state_from_cerebro(session_id)
|
| 399 |
+
|
| 400 |
+
fphen_values = calculate_fphen_values(mach5_state["t_fisica_params"], mach5_state["t_genoma_fixo"])
|
| 401 |
+
return jsonify({
|
| 402 |
+
"mach5_fisica_params": mach5_state["t_fisica_params"],
|
| 403 |
+
"mach5_genoma_fixo_values": mach5_state["t_genoma_fixo"],
|
| 404 |
+
"mach5_coerencia_total": mach5_state["coerencia_total"],
|
| 405 |
+
"mach5_produtividade_expressiva": mach5_state["produtividade_expressiva"],
|
| 406 |
+
# ATUALIZADO: Inclui os valores específicos para Fphen(t) para GET também
|
| 407 |
+
"fphen_t_values": {
|
| 408 |
+
"afetuosidade": fphen_values[ORDERED_FPHEN_AXES.index("Afetuosidade_eixo")],
|
| 409 |
+
# CORREÇÃO AQUI: Acessa 'Variancia_eixo' diretamente.
|
| 410 |
+
"variancia": fphen_values[ORDERED_FPHEN_AXES.index("Variancia_eixo")],
|
| 411 |
+
"expressividade": mach5_state["produtividade_expressiva"],
|
| 412 |
+
"coh_total": mach5_state["coerencia_total"],
|
| 413 |
+
"pi_G": mach5_state["produtividade_expressiva"]
|
| 414 |
+
}
|
| 415 |
+
})
|
| 416 |
+
|
| 417 |
+
if __name__ == '__main__':
|
| 418 |
+
# REMOVIDO: load_mach5_state() global, agora é por sessão via load_mach5_state_from_cerebro
|
| 419 |
+
port = int(os.environ.get("PORT", 8083)) # Porta padrão para t_memoria.py
|
| 420 |
+
print(f"--- Servidor t_memoria.py iniciado na porta {port} ---")
|
| 421 |
+
app.run(port=port, debug=True)
|
templates/mach5_monitor_dashboard.html
ADDED
|
@@ -0,0 +1,557 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="pt-BR">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>🎓 SU - Painel de Monitoramento</title>
|
| 6 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
| 7 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
| 8 |
+
<style>
|
| 9 |
+
body {
|
| 10 |
+
font-family: Arial, sans-serif;
|
| 11 |
+
margin: 0;
|
| 12 |
+
background-color: #1a1a1a;
|
| 13 |
+
color: #e0e0e0;
|
| 14 |
+
display: flex;
|
| 15 |
+
justify-content: center;
|
| 16 |
+
align-items: flex-start; /* Alinhar ao topo, permite que a página role */
|
| 17 |
+
min-height: 100vh;
|
| 18 |
+
padding: 20px; /* Adiciona padding para não colar nas bordas */
|
| 19 |
+
box-sizing: border-box; /* Garante que padding não adicione largura total */
|
| 20 |
+
}
|
| 21 |
+
.chat-container {
|
| 22 |
+
max-width: 900px;
|
| 23 |
+
width: 100%;
|
| 24 |
+
background-color: #333;
|
| 25 |
+
padding: 25px;
|
| 26 |
+
border: 1px solid #555;
|
| 27 |
+
border-radius: 10px;
|
| 28 |
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.6);
|
| 29 |
+
display: flex;
|
| 30 |
+
flex-direction: column;
|
| 31 |
+
min-height: 80vh; /* Altura mínima do container */
|
| 32 |
+
max-height: 95vh; /* Altura máxima para não estourar a tela */
|
| 33 |
+
overflow: hidden; /* Esconder overflow para layout fixo */
|
| 34 |
+
}
|
| 35 |
+
h1 {
|
| 36 |
+
color: #ffcc00; /* Dourado/Amarelo para contraste com a UFBA */
|
| 37 |
+
text-align: center;
|
| 38 |
+
margin-bottom: 25px;
|
| 39 |
+
text-shadow: 0 0 8px rgba(255, 204, 0, 0.5);
|
| 40 |
+
font-size: 2.2em;
|
| 41 |
+
}
|
| 42 |
+
.chat-history {
|
| 43 |
+
flex-grow: 1;
|
| 44 |
+
overflow-y: auto;
|
| 45 |
+
padding: 15px;
|
| 46 |
+
background-color: #2b2b2b;
|
| 47 |
+
border-radius: 8px;
|
| 48 |
+
border: 1px solid #444;
|
| 49 |
+
margin-bottom: 15px;
|
| 50 |
+
}
|
| 51 |
+
.chat-history div {
|
| 52 |
+
margin: 12px 0;
|
| 53 |
+
padding: 10px 15px;
|
| 54 |
+
border-radius: 6px;
|
| 55 |
+
line-height: 1.5;
|
| 56 |
+
}
|
| 57 |
+
.user-message {
|
| 58 |
+
font-weight: bold;
|
| 59 |
+
color: #b0e0e6; /* Azul claro, suave */
|
| 60 |
+
background-color: #3a3a3a;
|
| 61 |
+
align-self: flex-end;
|
| 62 |
+
text-align: right;
|
| 63 |
+
margin-left: 20%;
|
| 64 |
+
}
|
| 65 |
+
.su-message {
|
| 66 |
+
color: #e0b0ff; /* Lilás suave */
|
| 67 |
+
background-color: #4a4a4a;
|
| 68 |
+
align-self: flex-start;
|
| 69 |
+
text-align: left;
|
| 70 |
+
white-space: pre-wrap;
|
| 71 |
+
margin-right: 20%;
|
| 72 |
+
}
|
| 73 |
+
.input-container {
|
| 74 |
+
display: flex;
|
| 75 |
+
gap: 15px;
|
| 76 |
+
margin-top: 15px;
|
| 77 |
+
}
|
| 78 |
+
input[type="text"] {
|
| 79 |
+
flex-grow: 1;
|
| 80 |
+
padding: 12px;
|
| 81 |
+
border: 1px solid #ffcc00;
|
| 82 |
+
border-radius: 6px;
|
| 83 |
+
background-color: #222;
|
| 84 |
+
color: #e0e0e0;
|
| 85 |
+
font-size: 1.1em;
|
| 86 |
+
outline: none;
|
| 87 |
+
}
|
| 88 |
+
input[type="text"]::placeholder {
|
| 89 |
+
color: #aaa;
|
| 90 |
+
}
|
| 91 |
+
input[type="submit"] {
|
| 92 |
+
padding: 12px 25px;
|
| 93 |
+
background-color: #007bff;
|
| 94 |
+
color: #ffffff;
|
| 95 |
+
border: none;
|
| 96 |
+
border-radius: 6px;
|
| 97 |
+
cursor: pointer;
|
| 98 |
+
font-weight: bold;
|
| 99 |
+
font-size: 1.1em;
|
| 100 |
+
transition: background-color 0.3s ease, transform 0.2s ease;
|
| 101 |
+
}
|
| 102 |
+
input[type="submit"]:hover {
|
| 103 |
+
background-color: #0056b3;
|
| 104 |
+
transform: translateY(-2px);
|
| 105 |
+
}
|
| 106 |
+
.su-message.thinking-message {
|
| 107 |
+
color: #ccc;
|
| 108 |
+
font-style: italic;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
/* Estilo para a área de avaliação FIXA */
|
| 112 |
+
.evaluation-data-fixed {
|
| 113 |
+
padding: 15px;
|
| 114 |
+
background-color: #2b2b2b;
|
| 115 |
+
border: 1px dashed #ffcc00; /* Dourado/Amarelo */
|
| 116 |
+
border-radius: 5px;
|
| 117 |
+
font-family: 'Courier New', monospace;
|
| 118 |
+
font-size: 0.9em;
|
| 119 |
+
color: #aaa;
|
| 120 |
+
margin-top: auto;
|
| 121 |
+
flex-shrink: 0;
|
| 122 |
+
}
|
| 123 |
+
.evaluation-data-fixed h3 {
|
| 124 |
+
color: #ffcc00; /* Dourado/Amarelo */
|
| 125 |
+
text-align: center;
|
| 126 |
+
margin-top: 0;
|
| 127 |
+
margin-bottom: 15px;
|
| 128 |
+
}
|
| 129 |
+
.evaluation-data-fixed p {
|
| 130 |
+
margin: 5px 0;
|
| 131 |
+
padding-left: 10px;
|
| 132 |
+
}
|
| 133 |
+
.evaluation-data-fixed strong {
|
| 134 |
+
color: #f0e68c;
|
| 135 |
+
}
|
| 136 |
+
.evaluation-data-fixed .axis-label {
|
| 137 |
+
display: inline-block;
|
| 138 |
+
width: 150px;
|
| 139 |
+
}
|
| 140 |
+
/* Estilo para os canvas dos gráficos */
|
| 141 |
+
.chart-container {
|
| 142 |
+
position: relative;
|
| 143 |
+
margin: 10px auto;
|
| 144 |
+
width: 95%;
|
| 145 |
+
height: 120px;
|
| 146 |
+
}
|
| 147 |
+
#historyChartContainer {
|
| 148 |
+
height: 150px;
|
| 149 |
+
margin-top: 20px;
|
| 150 |
+
}
|
| 151 |
+
</style>
|
| 152 |
+
</head>
|
| 153 |
+
<body>
|
| 154 |
+
<div class="chat-container">
|
| 155 |
+
<h1>🎓 SU - Painel de Monitoramento</h1>
|
| 156 |
+
<div class="chat-history" id="chat-history">
|
| 157 |
+
</div>
|
| 158 |
+
|
| 159 |
+
<div class="input-container">
|
| 160 |
+
<input type="text" id="message-input" placeholder="Digite sua mensagem..." />
|
| 161 |
+
<input type="submit" value="Enviar" id="send-button" />
|
| 162 |
+
</div>
|
| 163 |
+
|
| 164 |
+
<div class="evaluation-data-fixed" id="fixed-evaluation-data">
|
| 165 |
+
<h3>ESTADO DA SU</h3>
|
| 166 |
+
<p><strong class="axis-label">Coerência Total:</strong> N/A</p>
|
| 167 |
+
<p><strong class="axis-label">Produtividade Expressiva:</strong> N/A</p>
|
| 168 |
+
<h4>Eixos Expressivos Atuais:</h4>
|
| 169 |
+
<p><strong class="axis-label">Afetuosidade:</strong> N/A</p>
|
| 170 |
+
<p><strong class="axis-label">Variância:</strong> N/A</p>
|
| 171 |
+
<p><strong class="axis-label">Expressividade Geral:</strong> N/A</p>
|
| 172 |
+
<div class="chart-container">
|
| 173 |
+
<canvas id="expressiveAxesChart"></canvas>
|
| 174 |
+
</div>
|
| 175 |
+
|
| 176 |
+
<h4>Histórico de Eixos Expressivos:</h4>
|
| 177 |
+
<div class="chart-container" id="historyChartContainer">
|
| 178 |
+
<canvas id="expressiveHistoryChart"></canvas>
|
| 179 |
+
</div>
|
| 180 |
+
</div>
|
| 181 |
+
</div>
|
| 182 |
+
|
| 183 |
+
<script>
|
| 184 |
+
// A principal mudança: o sessionId será injetado pelo Flask, não lido de um cookie.
|
| 185 |
+
// Certifique-se que o Flask renderize este template passando a variável 'session_id'.
|
| 186 |
+
let sessionId = "{{ session_id }}"; // Agora, o sessionId recebe o valor diretamente do Flask.
|
| 187 |
+
|
| 188 |
+
// A função getCookie(name) não é mais necessária para obter o sessionId no carregamento inicial,
|
| 189 |
+
// mas pode ser mantida se usada para outros cookies ou futuras funcionalidades.
|
| 190 |
+
function getCookie(name) {
|
| 191 |
+
const nameEQ = name + "=";
|
| 192 |
+
const ca = document.cookie.split(';');
|
| 193 |
+
for(let i=0; i < ca.length; i++) {
|
| 194 |
+
let c = ca[i];
|
| 195 |
+
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
| 196 |
+
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
|
| 197 |
+
}
|
| 198 |
+
return null;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
document.getElementById('send-button').addEventListener('click', sendMessage);
|
| 202 |
+
document.getElementById('message-input').addEventListener('keypress', function(e) {
|
| 203 |
+
if (e.key === 'Enter') {
|
| 204 |
+
sendMessage();
|
| 205 |
+
}
|
| 206 |
+
});
|
| 207 |
+
|
| 208 |
+
let expressiveAxesChart; // Variável global para o objeto do Chart.js (barras)
|
| 209 |
+
let expressiveHistoryChart; // Variável global para o objeto do Chart.js (linhas)
|
| 210 |
+
|
| 211 |
+
// Arrays para armazenar o histórico de dados para o gráfico de linha
|
| 212 |
+
const historyLabels = []; // Timestamps ou número da interação
|
| 213 |
+
const historyAfetuosidade = [];
|
| 214 |
+
const historyVariancia = [];
|
| 215 |
+
const historyExpressividade = [];
|
| 216 |
+
|
| 217 |
+
// Função para atualizar a div de avaliação fixa E os gráficos
|
| 218 |
+
function updateFixedEvaluationData(evalData) {
|
| 219 |
+
const fixedEvalDiv = document.getElementById('fixed-evaluation-data');
|
| 220 |
+
|
| 221 |
+
// Atualiza os valores numéricos
|
| 222 |
+
fixedEvalDiv.querySelector('p:nth-of-type(1)').innerHTML = `<strong class="axis-label">Coerência Total:</strong> ${evalData.coh_total !== undefined ? evalData.coh_total.toFixed(2) : 'N/A'}`;
|
| 223 |
+
fixedEvalDiv.querySelector('p:nth-of-type(2)').innerHTML = `<strong class="axis-label">Produtividade Expressiva:</strong> ${evalData.pi_G !== undefined ? evalData.pi_G.toFixed(2) : 'N/A'}`;
|
| 224 |
+
fixedEvalDiv.querySelector('p:nth-of-type(3)').innerHTML = `<strong class="axis-label">Afetuosidade:</strong> ${evalData.afetuosidade !== undefined ? evalData.afetuosidade.toFixed(2) : 'N/A'}`;
|
| 225 |
+
fixedEvalDiv.querySelector('p:nth-of-type(4)').innerHTML = `<strong class="axis-label">Variância:</strong> ${evalData.variancia !== undefined ? evalData.variancia.toFixed(2) : 'N/A'}`;
|
| 226 |
+
fixedEvalDiv.querySelector('p:nth-of-type(5)').innerHTML = `<strong class="axis-label">Expressividade Geral:</strong> ${evalData.expressividade !== undefined ? evalData.expressividade.toFixed(2) : 'N/A'}`;
|
| 227 |
+
|
| 228 |
+
// --- GRÁFICO DE BARRAS (Atual) ---
|
| 229 |
+
const ctxBar = document.getElementById('expressiveAxesChart').getContext('2d');
|
| 230 |
+
|
| 231 |
+
const chartLabelsBar = ['Afetuosidade', 'Variância', 'Expressividade Geral'];
|
| 232 |
+
const chartValuesBar = [
|
| 233 |
+
evalData.afetuosidade || 0,
|
| 234 |
+
evalData.variancia || 0,
|
| 235 |
+
evalData.expressividade || 0
|
| 236 |
+
];
|
| 237 |
+
|
| 238 |
+
if (expressiveAxesChart) {
|
| 239 |
+
expressiveAxesChart.data.labels = chartLabelsBar;
|
| 240 |
+
expressiveAxesChart.data.datasets[0].data = chartValuesBar;
|
| 241 |
+
expressiveAxesChart.update();
|
| 242 |
+
} else {
|
| 243 |
+
expressiveAxesChart = new Chart(ctxBar, {
|
| 244 |
+
type: 'bar',
|
| 245 |
+
data: {
|
| 246 |
+
labels: chartLabelsBar,
|
| 247 |
+
datasets: [{
|
| 248 |
+
label: 'Nível',
|
| 249 |
+
data: chartValuesBar,
|
| 250 |
+
backgroundColor: [
|
| 251 |
+
'rgba(255, 99, 132, 0.6)',
|
| 252 |
+
'rgba(54, 162, 235, 0.6)',
|
| 253 |
+
'rgba(75, 192, 192, 0.6)'
|
| 254 |
+
],
|
| 255 |
+
borderColor: [
|
| 256 |
+
'rgba(255, 99, 132, 1)',
|
| 257 |
+
'rgba(54, 162, 235, 1)',
|
| 258 |
+
'rgba(75, 192, 192, 1)'
|
| 259 |
+
],
|
| 260 |
+
borderWidth: 1
|
| 261 |
+
}]
|
| 262 |
+
},
|
| 263 |
+
options: {
|
| 264 |
+
responsive: true,
|
| 265 |
+
maintainAspectRatio: false,
|
| 266 |
+
indexAxis: 'y',
|
| 267 |
+
scales: {
|
| 268 |
+
x: {
|
| 269 |
+
beginAtZero: true,
|
| 270 |
+
max: 1.0,
|
| 271 |
+
grid: { color: '#555' },
|
| 272 |
+
ticks: { color: '#e0e0e0' }
|
| 273 |
+
},
|
| 274 |
+
y: {
|
| 275 |
+
grid: { color: '#555' },
|
| 276 |
+
ticks: { color: '#e0e0e0' }
|
| 277 |
+
}
|
| 278 |
+
},
|
| 279 |
+
plugins: {
|
| 280 |
+
legend: { display: false },
|
| 281 |
+
title: { display: false }
|
| 282 |
+
}
|
| 283 |
+
}
|
| 284 |
+
});
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
// --- GRÁFICO DE LINHA (Histórico) ---
|
| 288 |
+
// Adiciona os novos dados ao histórico
|
| 289 |
+
const currentInteractionNum = historyLabels.length + 1; // Número da interação
|
| 290 |
+
historyLabels.push(`Interação ${currentInteractionNum}`);
|
| 291 |
+
historyAfetuosidade.push(evalData.afetuosidade || 0);
|
| 292 |
+
historyVariancia.push(evalData.variancia || 0);
|
| 293 |
+
historyExpressividade.push(evalData.expressividade || 0);
|
| 294 |
+
|
| 295 |
+
const ctxLine = document.getElementById('expressiveHistoryChart').getContext('2d');
|
| 296 |
+
|
| 297 |
+
if (expressiveHistoryChart) {
|
| 298 |
+
// Se o gráfico já existe, atualiza os dados
|
| 299 |
+
expressiveHistoryChart.data.labels = historyLabels;
|
| 300 |
+
expressiveHistoryChart.data.datasets[0].data = historyAfetuosidade;
|
| 301 |
+
expressiveHistoryChart.data.datasets[1].data = historyVariancia;
|
| 302 |
+
expressiveHistoryChart.data.datasets[2].data = historyExpressividade;
|
| 303 |
+
expressiveHistoryChart.update();
|
| 304 |
+
} else {
|
| 305 |
+
// Se o gráfico não existe, cria um novo
|
| 306 |
+
expressiveHistoryChart = new Chart(ctxLine, {
|
| 307 |
+
type: 'line',
|
| 308 |
+
data: {
|
| 309 |
+
labels: historyLabels,
|
| 310 |
+
datasets: [
|
| 311 |
+
{
|
| 312 |
+
label: 'Afetuosidade',
|
| 313 |
+
data: historyAfetuosidade,
|
| 314 |
+
borderColor: 'rgba(255, 99, 132, 1)', // Vermelho
|
| 315 |
+
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
| 316 |
+
fill: false,
|
| 317 |
+
tension: 0.1
|
| 318 |
+
},
|
| 319 |
+
{
|
| 320 |
+
label: 'Variância',
|
| 321 |
+
data: historyVariancia,
|
| 322 |
+
borderColor: 'rgba(54, 162, 235, 1)', // Azul
|
| 323 |
+
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
| 324 |
+
fill: false,
|
| 325 |
+
tension: 0.1
|
| 326 |
+
},
|
| 327 |
+
{
|
| 328 |
+
label: 'Expressividade Geral',
|
| 329 |
+
data: historyExpressividade,
|
| 330 |
+
borderColor: 'rgba(75, 192, 192, 1)', // Verde-água
|
| 331 |
+
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
| 332 |
+
fill: false,
|
| 333 |
+
tension: 0.1
|
| 334 |
+
}
|
| 335 |
+
]
|
| 336 |
+
},
|
| 337 |
+
options: {
|
| 338 |
+
responsive: true,
|
| 339 |
+
maintainAspectRatio: false,
|
| 340 |
+
scales: {
|
| 341 |
+
x: {
|
| 342 |
+
grid: { color: '#555' },
|
| 343 |
+
ticks: { color: '#e0e0e0' }
|
| 344 |
+
},
|
| 345 |
+
y: {
|
| 346 |
+
beginAtZero: true,
|
| 347 |
+
max: 1.0,
|
| 348 |
+
grid: { color: '#555' },
|
| 349 |
+
ticks: { color: '#e0e0e0' }
|
| 350 |
+
}
|
| 351 |
+
},
|
| 352 |
+
plugins: {
|
| 353 |
+
legend: {
|
| 354 |
+
labels: {
|
| 355 |
+
color: '#e0e0e0' // Cor da legenda
|
| 356 |
+
}
|
| 357 |
+
},
|
| 358 |
+
title: {
|
| 359 |
+
display: false
|
| 360 |
+
}
|
| 361 |
+
}
|
| 362 |
+
}
|
| 363 |
+
});
|
| 364 |
+
}
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
function sendMessage() {
|
| 368 |
+
const input = document.getElementById('message-input');
|
| 369 |
+
const message = input.value.trim();
|
| 370 |
+
if (!message) return;
|
| 371 |
+
|
| 372 |
+
const chatHistory = document.getElementById('chat-history');
|
| 373 |
+
|
| 374 |
+
const userMessageDiv = document.createElement('div');
|
| 375 |
+
userMessageDiv.className = 'user-message';
|
| 376 |
+
userMessageDiv.textContent = `Você: ${message}`;
|
| 377 |
+
chatHistory.appendChild(userMessageDiv);
|
| 378 |
+
|
| 379 |
+
input.value = '';
|
| 380 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 381 |
+
|
| 382 |
+
const thinkingMessageDiv = document.createElement('div');
|
| 383 |
+
thinkingMessageDiv.className = 'su-message thinking-message';
|
| 384 |
+
thinkingMessageDiv.textContent = `SU: Pensando...`;
|
| 385 |
+
chatHistory.appendChild(thinkingMessageDiv);
|
| 386 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 387 |
+
|
| 388 |
+
fetch('/chat_new', {
|
| 389 |
+
method: 'POST',
|
| 390 |
+
headers: {
|
| 391 |
+
'Content-Type': 'application/json'
|
| 392 |
+
},
|
| 393 |
+
// O sessionId já está disponível aqui, obtido do Flask.
|
| 394 |
+
body: JSON.stringify({ message: message, session_id: sessionId })
|
| 395 |
+
})
|
| 396 |
+
.then(response => response.json())
|
| 397 |
+
.then(data => {
|
| 398 |
+
chatHistory.removeChild(thinkingMessageDiv);
|
| 399 |
+
|
| 400 |
+
const suMessageDiv = document.createElement('div');
|
| 401 |
+
suMessageDiv.className = 'su-message';
|
| 402 |
+
suMessageDiv.textContent = `SU: ${data.response}`;
|
| 403 |
+
chatHistory.appendChild(suMessageDiv);
|
| 404 |
+
|
| 405 |
+
// Exibir dados de avaliação se presentes
|
| 406 |
+
if (data.mach5_estado_simplificado && data.mach5_estado_simplificado['Fphen(t)']) {
|
| 407 |
+
const evalData = data.mach5_estado_simplificado['Fphen(t)'];
|
| 408 |
+
updateFixedEvaluationData(evalData); // CHAMA A FUNÇÃO AGORA
|
| 409 |
+
} else {
|
| 410 |
+
// Se não houver dados, limpa ou reseta a div fixa
|
| 411 |
+
updateFixedEvaluationData({}); // Passa um objeto vazio para resetar
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 415 |
+
})
|
| 416 |
+
.catch(error => {
|
| 417 |
+
console.error('Erro ao enviar mensagem:', error);
|
| 418 |
+
if (thinkingMessageDiv.parentNode === chatHistory) {
|
| 419 |
+
chatHistory.removeChild(thinkingMessageDiv);
|
| 420 |
+
}
|
| 421 |
+
const errorMessageDiv = document.createElement('div');
|
| 422 |
+
errorMessageDiv.className = 'su-message';
|
| 423 |
+
errorMessageDiv.textContent = `SU: [ERRO] Não consegui responder. Tente novamente.`;
|
| 424 |
+
chatHistory.appendChild(errorMessageDiv);
|
| 425 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 426 |
+
});
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
// Função para carregar o histórico inicial e o estado da Mach5 ao carregar a página
|
| 430 |
+
window.onload = function() {
|
| 431 |
+
// Verifica se o sessionId foi injetado corretamente.
|
| 432 |
+
if (!sessionId || sessionId === "None") { // Adicionei 'None' caso o Flask passe um valor nulo como string
|
| 433 |
+
console.error("Session ID não encontrado no template. Verifique a passagem de contexto do Flask.");
|
| 434 |
+
// Você pode adicionar uma lógica de fallback aqui se necessário.
|
| 435 |
+
return; // Impede a chamada da API se não houver sessionId.
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
fetch('/chat_history', {
|
| 439 |
+
method: 'POST',
|
| 440 |
+
headers: { 'Content-Type': 'application/json' },
|
| 441 |
+
// Envia o session_id que já foi definido no carregamento do script.
|
| 442 |
+
body: JSON.stringify({ session_id: sessionId })
|
| 443 |
+
})
|
| 444 |
+
.then(response => response.json())
|
| 445 |
+
.then(data => {
|
| 446 |
+
const chatHistory = document.getElementById('chat-history');
|
| 447 |
+
data.memoria.forEach(item => {
|
| 448 |
+
const userMessageDiv = document.createElement('div');
|
| 449 |
+
userMessageDiv.className = 'user-message';
|
| 450 |
+
userMessageDiv.textContent = `Você: ${item.input}`;
|
| 451 |
+
chatHistory.appendChild(userMessageDiv);
|
| 452 |
+
|
| 453 |
+
const suMessageDiv = document.createElement('div');
|
| 454 |
+
suMessageDiv.className = 'su-message';
|
| 455 |
+
suMessageDiv.textContent = `SU: ${item.resposta}`;
|
| 456 |
+
chatHistory.appendChild(suMessageDiv);
|
| 457 |
+
|
| 458 |
+
// Adiciona dados ao histórico do gráfico de linha ao carregar o hist��rico salvo
|
| 459 |
+
if (item.mach5_estado_simplificado && item.mach5_estado_simplificado['Fphen(t)']) {
|
| 460 |
+
const evalData = item.mach5_estado_simplificado['Fphen(t)'];
|
| 461 |
+
const currentInteractionNum = historyLabels.length + 1;
|
| 462 |
+
historyLabels.push(`Interação ${currentInteractionNum}`);
|
| 463 |
+
historyAfetuosidade.push(evalData.afetuosidade || 0);
|
| 464 |
+
historyVariancia.push(evalData.variancia || 0);
|
| 465 |
+
historyExpressividade.push(evalData.expressividade || 0);
|
| 466 |
+
}
|
| 467 |
+
});
|
| 468 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 469 |
+
|
| 470 |
+
// Preencher a div de avaliação fixa com o último estado conhecido, se houver
|
| 471 |
+
if (data.last_simplified_state && data.last_simplified_state['Fphen(t)']) {
|
| 472 |
+
updateFixedEvaluationData(data.last_simplified_state['Fphen(t)']);
|
| 473 |
+
}
|
| 474 |
+
// Cria o gráfico de histórico com os dados carregados
|
| 475 |
+
if (historyLabels.length > 0) {
|
| 476 |
+
const ctxLine = document.getElementById('expressiveHistoryChart').getContext('2d');
|
| 477 |
+
expressiveHistoryChart = new Chart(ctxLine, {
|
| 478 |
+
type: 'line',
|
| 479 |
+
data: {
|
| 480 |
+
labels: historyLabels,
|
| 481 |
+
datasets: [
|
| 482 |
+
{
|
| 483 |
+
label: 'Afetuosidade',
|
| 484 |
+
data: historyAfetuosidade,
|
| 485 |
+
borderColor: 'rgba(255, 99, 132, 1)',
|
| 486 |
+
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
| 487 |
+
fill: false,
|
| 488 |
+
tension: 0.1
|
| 489 |
+
},
|
| 490 |
+
{
|
| 491 |
+
label: 'Variância',
|
| 492 |
+
data: historyVariancia,
|
| 493 |
+
borderColor: 'rgba(54, 162, 235, 1)',
|
| 494 |
+
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
| 495 |
+
fill: false,
|
| 496 |
+
tension: 0.1
|
| 497 |
+
},
|
| 498 |
+
{
|
| 499 |
+
label: 'Expressividade Geral',
|
| 500 |
+
data: historyExpressividade,
|
| 501 |
+
borderColor: 'rgba(75, 192, 192, 1)',
|
| 502 |
+
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
| 503 |
+
fill: false,
|
| 504 |
+
tension: 0.1
|
| 505 |
+
}
|
| 506 |
+
]
|
| 507 |
+
},
|
| 508 |
+
options: {
|
| 509 |
+
responsive: true,
|
| 510 |
+
maintainAspectRatio: false,
|
| 511 |
+
scales: {
|
| 512 |
+
x: {
|
| 513 |
+
grid: { color: '#555' },
|
| 514 |
+
ticks: { color: '#e0e0e0' }
|
| 515 |
+
},
|
| 516 |
+
y: {
|
| 517 |
+
beginAtZero: true,
|
| 518 |
+
max: 1.0,
|
| 519 |
+
grid: { color: '#555' },
|
| 520 |
+
ticks: { color: '#e0e0e0' }
|
| 521 |
+
}
|
| 522 |
+
},
|
| 523 |
+
plugins: {
|
| 524 |
+
legend: {
|
| 525 |
+
labels: {
|
| 526 |
+
color: '#e0e0e0'
|
| 527 |
+
}
|
| 528 |
+
},
|
| 529 |
+
title: {
|
| 530 |
+
display: false
|
| 531 |
+
}
|
| 532 |
+
}
|
| 533 |
+
}
|
| 534 |
+
});
|
| 535 |
+
} else {
|
| 536 |
+
// Inicializa o gráfico vazio se não houver histórico
|
| 537 |
+
const ctxLine = document.getElementById('expressiveHistoryChart').getContext('2d');
|
| 538 |
+
expressiveHistoryChart = new Chart(ctxLine, {
|
| 539 |
+
type: 'line',
|
| 540 |
+
data: { labels: [], datasets: [] },
|
| 541 |
+
options: {
|
| 542 |
+
responsive: true,
|
| 543 |
+
maintainAspectRatio: false,
|
| 544 |
+
scales: {
|
| 545 |
+
x: { grid: { color: '#555' }, ticks: { color: '#e0e0e0' } },
|
| 546 |
+
y: { beginAtZero: true, max: 1.0, grid: { color: '#555' }, ticks: { color: '#e0e0e0' } }
|
| 547 |
+
},
|
| 548 |
+
plugins: { legend: { labels: { color: '#e0e0e0' } }, title: { display: false } }
|
| 549 |
+
}
|
| 550 |
+
});
|
| 551 |
+
}
|
| 552 |
+
})
|
| 553 |
+
.catch(error => console.error('Erro ao carregar histórico inicial:', error));
|
| 554 |
+
};
|
| 555 |
+
</script>
|
| 556 |
+
</body>
|
| 557 |
+
</html>
|
templates/mach5_new_chat.html
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="pt-BR">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>🎓 SU - Atendente Virtual da SUPAC</title>
|
| 6 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
| 7 |
+
<style>
|
| 8 |
+
body {
|
| 9 |
+
font-family: Arial, sans-serif;
|
| 10 |
+
margin: 0;
|
| 11 |
+
background-color: #1a1a1a;
|
| 12 |
+
color: #e0e0e0;
|
| 13 |
+
display: flex;
|
| 14 |
+
justify-content: center;
|
| 15 |
+
align-items: center;
|
| 16 |
+
min-height: 100vh;
|
| 17 |
+
}
|
| 18 |
+
.chat-container {
|
| 19 |
+
max-width: 700px;
|
| 20 |
+
width: 100%;
|
| 21 |
+
background-color: #333;
|
| 22 |
+
padding: 25px;
|
| 23 |
+
border: 1px solid #555;
|
| 24 |
+
border-radius: 10px;
|
| 25 |
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.6);
|
| 26 |
+
display: flex;
|
| 27 |
+
flex-direction: column;
|
| 28 |
+
min-height: 70vh;
|
| 29 |
+
max-height: 90vh;
|
| 30 |
+
overflow: hidden;
|
| 31 |
+
position: relative;
|
| 32 |
+
}
|
| 33 |
+
h1 {
|
| 34 |
+
color: #FF0000;
|
| 35 |
+
text-align: center;
|
| 36 |
+
margin-bottom: 15px;
|
| 37 |
+
text-shadow: 0 0 8px rgba(255, 0, 0, 0.5);
|
| 38 |
+
font-size: 2.2em;
|
| 39 |
+
display: flex;
|
| 40 |
+
align-items: center;
|
| 41 |
+
justify-content: center;
|
| 42 |
+
gap: 10px;
|
| 43 |
+
}
|
| 44 |
+
.su-avatar-container {
|
| 45 |
+
width: 80px;
|
| 46 |
+
height: 80px;
|
| 47 |
+
border-radius: 50%;
|
| 48 |
+
background-color: #FF0000;
|
| 49 |
+
display: flex;
|
| 50 |
+
justify-content: center;
|
| 51 |
+
align-items: center;
|
| 52 |
+
border: 2px solid #FF0000;
|
| 53 |
+
box-shadow: 0 0 10px rgba(255, 0, 0, 0.7);
|
| 54 |
+
}
|
| 55 |
+
.su-avatar {
|
| 56 |
+
width: 90%;
|
| 57 |
+
height: 90%;
|
| 58 |
+
border-radius: 50%;
|
| 59 |
+
object-fit: contain;
|
| 60 |
+
}
|
| 61 |
+
.su-title-text {
|
| 62 |
+
display: flex;
|
| 63 |
+
flex-direction: column;
|
| 64 |
+
align-items: flex-start;
|
| 65 |
+
}
|
| 66 |
+
.su-main-name {
|
| 67 |
+
font-size: 1.5em;
|
| 68 |
+
color: #FF0000;
|
| 69 |
+
}
|
| 70 |
+
.su-subtitle {
|
| 71 |
+
font-size: 0.6em;
|
| 72 |
+
color: #ccc;
|
| 73 |
+
margin-top: -5px;
|
| 74 |
+
}
|
| 75 |
+
.chat-history {
|
| 76 |
+
flex-grow: 1;
|
| 77 |
+
overflow-y: auto;
|
| 78 |
+
padding: 15px;
|
| 79 |
+
background-color: #2b2b2b;
|
| 80 |
+
border-radius: 8px;
|
| 81 |
+
border: 1px solid #444;
|
| 82 |
+
margin-bottom: 15px;
|
| 83 |
+
}
|
| 84 |
+
.chat-history div {
|
| 85 |
+
margin: 12px 0;
|
| 86 |
+
padding: 10px 15px;
|
| 87 |
+
border-radius: 6px;
|
| 88 |
+
line-height: 1.5;
|
| 89 |
+
}
|
| 90 |
+
.user-message {
|
| 91 |
+
font-weight: bold;
|
| 92 |
+
color: #FF6347;
|
| 93 |
+
background-color: #3a3a3a;
|
| 94 |
+
align-self: flex-end;
|
| 95 |
+
text-align: right;
|
| 96 |
+
margin-left: 20%;
|
| 97 |
+
}
|
| 98 |
+
.su-message {
|
| 99 |
+
color: #FFFF99;
|
| 100 |
+
background-color: #4a4a4a;
|
| 101 |
+
align-self: flex-start;
|
| 102 |
+
text-align: left;
|
| 103 |
+
white-space: pre-wrap;
|
| 104 |
+
margin-right: 20%;
|
| 105 |
+
}
|
| 106 |
+
.input-area {
|
| 107 |
+
display: flex;
|
| 108 |
+
flex-direction: column;
|
| 109 |
+
gap: 15px;
|
| 110 |
+
padding-top: 15px;
|
| 111 |
+
}
|
| 112 |
+
.input-and-button {
|
| 113 |
+
display: flex;
|
| 114 |
+
gap: 15px;
|
| 115 |
+
}
|
| 116 |
+
input[type="text"] {
|
| 117 |
+
flex-grow: 1;
|
| 118 |
+
padding: 12px;
|
| 119 |
+
border: 1px solid #FF0000;
|
| 120 |
+
border-radius: 6px;
|
| 121 |
+
background-color: #222;
|
| 122 |
+
color: #e0e0e0;
|
| 123 |
+
font-size: 1.1em;
|
| 124 |
+
outline: none;
|
| 125 |
+
}
|
| 126 |
+
input[type="text"]::placeholder {
|
| 127 |
+
color: #aaa;
|
| 128 |
+
}
|
| 129 |
+
input[type="submit"] {
|
| 130 |
+
padding: 12px 25px;
|
| 131 |
+
background-color: #FF0000;
|
| 132 |
+
color: #FFFFFF;
|
| 133 |
+
border: none;
|
| 134 |
+
border-radius: 6px;
|
| 135 |
+
cursor: pointer;
|
| 136 |
+
font-weight: bold;
|
| 137 |
+
font-size: 1.1em;
|
| 138 |
+
transition: background-color 0.3s ease, transform 0.2s ease;
|
| 139 |
+
}
|
| 140 |
+
input[type="submit"]:hover {
|
| 141 |
+
background-color: #CC0000;
|
| 142 |
+
transform: translateY(-2px);
|
| 143 |
+
}
|
| 144 |
+
.su-message.thinking-message {
|
| 145 |
+
color: #ccc;
|
| 146 |
+
font-style: italic;
|
| 147 |
+
}
|
| 148 |
+
.footer-content {
|
| 149 |
+
display: flex;
|
| 150 |
+
justify-content: space-between;
|
| 151 |
+
align-items: flex-end;
|
| 152 |
+
margin-top: 15px;
|
| 153 |
+
width: 100%;
|
| 154 |
+
}
|
| 155 |
+
.logo-footer {
|
| 156 |
+
width: 100px;
|
| 157 |
+
height: auto;
|
| 158 |
+
max-height: 40px;
|
| 159 |
+
filter: grayscale(100%) brightness(150%);
|
| 160 |
+
}
|
| 161 |
+
.footer-text-container {
|
| 162 |
+
display: flex;
|
| 163 |
+
flex-direction: column;
|
| 164 |
+
align-items: flex-end;
|
| 165 |
+
text-align: right;
|
| 166 |
+
flex-grow: 1;
|
| 167 |
+
}
|
| 168 |
+
.disclaimer-text {
|
| 169 |
+
font-size: 0.8em;
|
| 170 |
+
color: #bbb;
|
| 171 |
+
margin-bottom: 5px;
|
| 172 |
+
}
|
| 173 |
+
.disclaimer-text a {
|
| 174 |
+
color: #FF0000;
|
| 175 |
+
text-decoration: none;
|
| 176 |
+
}
|
| 177 |
+
.disclaimer-text a:hover {
|
| 178 |
+
text-decoration: underline;
|
| 179 |
+
}
|
| 180 |
+
.credits-text {
|
| 181 |
+
font-size: 0.65em;
|
| 182 |
+
color: #888;
|
| 183 |
+
}
|
| 184 |
+
</style>
|
| 185 |
+
</head>
|
| 186 |
+
<body>
|
| 187 |
+
<div class="chat-container">
|
| 188 |
+
<h1>
|
| 189 |
+
<div class="su-avatar-container">
|
| 190 |
+
<img src="su_sorrindo.jpeg" alt="SU Avatar" class="su-avatar">
|
| 191 |
+
</div>
|
| 192 |
+
<div class="su-title-text">
|
| 193 |
+
<span class="su-main-name">SU</span>
|
| 194 |
+
<span class="su-subtitle">Atendente Virtual da SUPAC</span>
|
| 195 |
+
</div>
|
| 196 |
+
</h1>
|
| 197 |
+
<div class="chat-history" id="chat-history">
|
| 198 |
+
</div>
|
| 199 |
+
|
| 200 |
+
<div class="input-area">
|
| 201 |
+
<div class="input-and-button">
|
| 202 |
+
<input type="text" id="message-input" placeholder="Pergunte sobre o SIGAA..." />
|
| 203 |
+
<input type="submit" value="Enviar" id="send-button" />
|
| 204 |
+
</div>
|
| 205 |
+
<div class="footer-content">
|
| 206 |
+
<img src="logo_supac.png" alt="Logo SUPAC" class="logo-footer">
|
| 207 |
+
<div class="footer-text-container">
|
| 208 |
+
<p class="disclaimer-text">A Su ainda está em treinamento! Em caso de dúvidas, acesse o site da SUPAC: <a href="https://supac.ufba.br/" target="_blank">https://supac.ufba.br/</a></p>
|
| 209 |
+
<p class="credits-text">Essa assistente foi criada pelo Tecno Modelo baseado e t-gens. By Nilton Souza</p>
|
| 210 |
+
</div>
|
| 211 |
+
</div>
|
| 212 |
+
</div>
|
| 213 |
+
</div>
|
| 214 |
+
|
| 215 |
+
<script>
|
| 216 |
+
let sessionId = "{{ session_id }}";
|
| 217 |
+
|
| 218 |
+
function getCookie(name) {
|
| 219 |
+
const nameEQ = name + "=";
|
| 220 |
+
const ca = document.cookie.split(';');
|
| 221 |
+
for(let i=0; i < ca.length; i++) {
|
| 222 |
+
let c = ca[i];
|
| 223 |
+
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
| 224 |
+
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
|
| 225 |
+
}
|
| 226 |
+
return null;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
document.getElementById('send-button').addEventListener('click', sendMessage);
|
| 230 |
+
document.getElementById('message-input').addEventListener('keypress', function(e) {
|
| 231 |
+
if (e.key === 'Enter') {
|
| 232 |
+
sendMessage();
|
| 233 |
+
}
|
| 234 |
+
});
|
| 235 |
+
|
| 236 |
+
function sendMessage() {
|
| 237 |
+
const input = document.getElementById('message-input');
|
| 238 |
+
const message = input.value.trim();
|
| 239 |
+
if (!message) return;
|
| 240 |
+
|
| 241 |
+
const chatHistory = document.getElementById('chat-history');
|
| 242 |
+
|
| 243 |
+
const userMessageDiv = document.createElement('div');
|
| 244 |
+
userMessageDiv.className = 'user-message';
|
| 245 |
+
userMessageDiv.textContent = `Você: ${message}`;
|
| 246 |
+
chatHistory.appendChild(userMessageDiv);
|
| 247 |
+
|
| 248 |
+
input.value = '';
|
| 249 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 250 |
+
|
| 251 |
+
const thinkingMessageDiv = document.createElement('div');
|
| 252 |
+
thinkingMessageDiv.className = 'su-message thinking-message';
|
| 253 |
+
thinkingMessageDiv.textContent = `SU: Pensando...`;
|
| 254 |
+
chatHistory.appendChild(thinkingMessageDiv);
|
| 255 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 256 |
+
|
| 257 |
+
fetch('/chat_new', {
|
| 258 |
+
method: 'POST',
|
| 259 |
+
headers: {
|
| 260 |
+
'Content-Type': 'application/json'
|
| 261 |
+
},
|
| 262 |
+
body: JSON.stringify({ message: message, session_id: sessionId })
|
| 263 |
+
})
|
| 264 |
+
.then(response => response.json())
|
| 265 |
+
.then(data => {
|
| 266 |
+
chatHistory.removeChild(thinkingMessageDiv);
|
| 267 |
+
|
| 268 |
+
const suMessageDiv = document.createElement('div');
|
| 269 |
+
suMessageDiv.className = 'su-message';
|
| 270 |
+
suMessageDiv.textContent = `SU: ${data.response}`;
|
| 271 |
+
chatHistory.appendChild(suMessageDiv);
|
| 272 |
+
|
| 273 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 274 |
+
})
|
| 275 |
+
.catch(error => {
|
| 276 |
+
console.error('Erro ao enviar mensagem:', error);
|
| 277 |
+
if (thinkingMessageDiv.parentNode === chatHistory) {
|
| 278 |
+
chatHistory.removeChild(thinkingMessageDiv);
|
| 279 |
+
}
|
| 280 |
+
const errorMessageDiv = document.createElement('div');
|
| 281 |
+
errorMessageDiv.className = 'su-message';
|
| 282 |
+
errorMessageDiv.textContent = `SU: [ERRO] Não consegui responder. Por favor, tente novamente.`;
|
| 283 |
+
chatHistory.appendChild(errorMessageDiv);
|
| 284 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 285 |
+
});
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
window.onload = function() {
|
| 289 |
+
if (!sessionId || sessionId === "None") {
|
| 290 |
+
console.error("Session ID não encontrado no template. Verifique a passagem de contexto do Flask.");
|
| 291 |
+
return;
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
fetch('/chat_history', {
|
| 295 |
+
method: 'POST',
|
| 296 |
+
headers: { 'Content-Type': 'application/json' },
|
| 297 |
+
body: JSON.stringify({ session_id: sessionId })
|
| 298 |
+
})
|
| 299 |
+
.then(response => response.json())
|
| 300 |
+
.then(data => {
|
| 301 |
+
const chatHistory = document.getElementById('chat-history');
|
| 302 |
+
data.memoria.forEach(item => {
|
| 303 |
+
const userMessageDiv = document.createElement('div');
|
| 304 |
+
userMessageDiv.className = 'user-message';
|
| 305 |
+
userMessageDiv.textContent = `Você: ${item.input}`;
|
| 306 |
+
chatHistory.appendChild(userMessageDiv);
|
| 307 |
+
|
| 308 |
+
const suMessageDiv = document.createElement('div');
|
| 309 |
+
suMessageDiv.className = 'su-message';
|
| 310 |
+
suMessageDiv.textContent = `SU: ${item.resposta}`;
|
| 311 |
+
chatHistory.appendChild(suMessageDiv);
|
| 312 |
+
});
|
| 313 |
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
| 314 |
+
})
|
| 315 |
+
.catch(error => console.error('Erro ao carregar histórico inicial:', error));
|
| 316 |
+
};
|
| 317 |
+
</script>
|
| 318 |
+
</body>
|
| 319 |
+
</html>
|
| 320 |
+
|