Spaces:
Build error
Build error
Commit ·
0110679
1
Parent(s): f1f6b0e
feat: Descrever alteraçõe
Browse files- app.py +147 -138
- t_memoria.py +127 -65
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
|
|
| 1 |
from flask import Flask, render_template, request, jsonify, make_response
|
| 2 |
-
from werkzeug.utils import redirect
|
| 3 |
import json
|
| 4 |
import os
|
| 5 |
from datetime import datetime
|
|
@@ -8,51 +8,77 @@ import pytz
|
|
| 8 |
from timezonefinder import TimezoneFinder
|
| 9 |
import numpy as np
|
| 10 |
import time
|
| 11 |
-
import uuid
|
| 12 |
-
import logging
|
| 13 |
-
import google.generativeai as genai
|
|
|
|
| 14 |
|
| 15 |
-
app = Flask(__name__, template_folder='templates')
|
| 16 |
|
|
|
|
| 17 |
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 18 |
|
|
|
|
| 19 |
GOOGLE_CLOUD_API_KEY = os.environ.get("GOOGLE_CLOUD_API_KEY")
|
|
|
|
| 20 |
if not GOOGLE_CLOUD_API_KEY:
|
| 21 |
-
logging.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
GEMINI_MODEL_NAME = "models/gemini-1.5-flash-latest"
|
| 24 |
-
model = None
|
| 25 |
-
if GOOGLE_CLOUD_API_KEY:
|
| 26 |
-
try:
|
| 27 |
-
genai.configure(api_key=GOOGLE_CLOUD_API_KEY)
|
| 28 |
-
model = genai.GenerativeModel(GEMINI_MODEL_NAME)
|
| 29 |
-
logging.info("Modelo Gemini inicializado com sucesso.")
|
| 30 |
-
except Exception as e:
|
| 31 |
-
logging.error(f"Erro ao inicializar o modelo Gemini '{GEMINI_MODEL_NAME}': {e}. Operando em modo fallback.")
|
| 32 |
-
model = None
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
|
|
|
| 38 |
SALVADOR_LAT = -12.9714
|
| 39 |
SALVADOR_LON = -38.5014
|
| 40 |
tf = TimezoneFinder()
|
| 41 |
|
|
|
|
|
|
|
| 42 |
ORDERED_FPHEN_AXES = [
|
| 43 |
"Afetuosidade_eixo", "Confusao_Oscilacao_eixo", "Contemplativa_eixo",
|
| 44 |
"Defensividade_eixo", "Diretiva_eixo", "Entediado_eixo",
|
| 45 |
-
"Variancia_eixo",
|
| 46 |
-
"
|
|
|
|
| 47 |
]
|
| 48 |
|
| 49 |
-
|
|
|
|
|
|
|
| 50 |
DIAS_PARA_ESQUECIMENTO_PADRAO = 10
|
| 51 |
MAX_MEMORIAS_CURTO_PRAZO_PROMPT = 5
|
| 52 |
MAX_DIALOG_HISTORY_FOR_PROMPT = 3
|
| 53 |
-
|
|
|
|
| 54 |
|
| 55 |
def get_from_service(base_urls, endpoint, default_value, params=None, method='GET', json_data=None):
|
|
|
|
| 56 |
for url in base_urls:
|
| 57 |
try:
|
| 58 |
full_url = f"{url}{endpoint}"
|
|
@@ -62,6 +88,7 @@ def get_from_service(base_urls, endpoint, default_value, params=None, method='GE
|
|
| 62 |
response = requests.post(full_url, json=json_data, timeout=5)
|
| 63 |
else:
|
| 64 |
raise ValueError("Método HTTP não suportado: " + method)
|
|
|
|
| 65 |
response.raise_for_status()
|
| 66 |
return response.json()
|
| 67 |
except requests.exceptions.RequestException as e:
|
|
@@ -70,9 +97,12 @@ def get_from_service(base_urls, endpoint, default_value, params=None, method='GE
|
|
| 70 |
return default_value
|
| 71 |
|
| 72 |
def post_to_service(base_urls, endpoint, data):
|
|
|
|
| 73 |
return get_from_service(base_urls, endpoint, {"status": "error", "message": "Falha na comunicação com o serviço."}, method='POST', json_data=data)
|
| 74 |
|
|
|
|
| 75 |
def get_current_local_time_salvador():
|
|
|
|
| 76 |
try:
|
| 77 |
timezone_str = tf.timezone_at(lng=SALVADOR_LON, lat=SALVADOR_LAT)
|
| 78 |
if not timezone_str:
|
|
@@ -85,37 +115,49 @@ def get_current_local_time_salvador():
|
|
| 85 |
logging.error(f"Erro ao obter a hora local de {SALVADOR_LAT}, {SALVADOR_LON}: {e}")
|
| 86 |
return "Não foi possível determinar a data e hora atual."
|
| 87 |
|
|
|
|
|
|
|
|
|
|
| 88 |
def create_llm_context(mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data):
|
| 89 |
context_parts = []
|
|
|
|
| 90 |
current_location_time = get_current_local_time_salvador()
|
| 91 |
-
context_parts.append(f"Você
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
if initial_biography_data:
|
| 94 |
-
|
|
|
|
| 95 |
for entity_name, entity_data in initial_biography_data.items():
|
|
|
|
| 96 |
if entity_data.get("tipo") in ["FAQ", "Manual_SIGAA_UFBA", "Documento"] and "conteudo_corrido" in entity_data:
|
| 97 |
-
biography_content = entity_data.get('conteudo_corrido')[:MAX_BIOGRAPHY_LENGTH]
|
| 98 |
-
if len(entity_data.get('conteudo_corrido')) > MAX_BIOGRAPHY_LENGTH:
|
| 99 |
-
biography_content += "... [Conteúdo truncado para otimização]"
|
| 100 |
context_parts.append(f"- Fonte: {entity_name} (versão {entity_data.get('versao', 'N/A')}):")
|
| 101 |
-
context_parts.append(f" Conteúdo: {
|
| 102 |
-
elif "memoria" in entity_data:
|
| 103 |
-
|
| 104 |
|
| 105 |
if all_personagens_data:
|
| 106 |
-
context_parts.append("\n**PERSONAGENS (
|
| 107 |
for nome_personagem, data_personagem in all_personagens_data.items():
|
| 108 |
tipo = data_personagem.get('tipo', 'entidade')
|
| 109 |
relacao = data_personagem.get('relacao', 'desconhecida')
|
| 110 |
-
context_parts.append(f"- {nome_personagem}: um(a) {tipo}, relação: {relacao}")
|
| 111 |
|
| 112 |
if short_term_memories_filtered:
|
| 113 |
-
context_parts.append("\n**
|
| 114 |
for i, mem in enumerate(short_term_memories_filtered[:MAX_MEMORIAS_CURTO_PRAZO_PROMPT]):
|
| 115 |
-
context_parts.append(f"- Lembrança {i+1}: {mem['conteudo']}
|
| 116 |
|
| 117 |
if dialogos_history and dialogos_history["dialogos"]:
|
| 118 |
-
context_parts.append("\n**
|
| 119 |
recent_dialogs_formatted = []
|
| 120 |
for dialogo in reversed(dialogos_history["dialogos"]):
|
| 121 |
recent_dialogs_formatted.insert(0, f"USUÁRIO: {dialogo.get('input', '')}\nSU: {dialogo.get('resposta', '')}")
|
|
@@ -123,35 +165,38 @@ def create_llm_context(mach5_state_data, dialogos_history, short_term_memories_f
|
|
| 123 |
break
|
| 124 |
context_parts.extend(recent_dialogs_formatted)
|
| 125 |
|
| 126 |
-
|
| 127 |
-
logging.debug(f"Tamanho do contexto enviado ao Gemini: {len(full_context)} caracteres")
|
| 128 |
-
return full_context
|
| 129 |
|
|
|
|
| 130 |
def get_Mach5_response_api(user_input, mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data):
|
|
|
|
| 131 |
if not model:
|
| 132 |
-
|
| 133 |
-
return "Desculpe, estou enfrentando um problema técnico. Por favor, envie sua pergunta sobre o SIGAA ou tente novamente mais tarde."
|
| 134 |
|
|
|
|
|
|
|
| 135 |
if not user_input.strip():
|
| 136 |
-
logging.warning("Entrada de usuário vazia.")
|
| 137 |
return "[Entrada vazia detectada.]"
|
| 138 |
|
| 139 |
llm_context_data = create_llm_context(mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data)
|
| 140 |
|
| 141 |
prompt_content = (
|
| 142 |
-
f"Seu nome é Su
|
| 143 |
-
f"
|
| 144 |
-
f"
|
| 145 |
-
f"
|
| 146 |
-
f"-
|
| 147 |
-
f"-
|
| 148 |
-
f"-
|
| 149 |
-
f"\n**CONTEXTO IMEDIATO:**\n"
|
| 150 |
-
f"- Você
|
| 151 |
-
f"-
|
|
|
|
|
|
|
|
|
|
| 152 |
)
|
| 153 |
|
| 154 |
-
full_user_message = f"{prompt_content.strip()}\n\n{llm_context_data}\n\nUSUÁRIO (
|
| 155 |
|
| 156 |
messages_for_gemini = [
|
| 157 |
{"role": "user", "parts": [full_user_message]}
|
|
@@ -162,12 +207,14 @@ def get_Mach5_response_api(user_input, mach5_state_data, dialogos_history, short
|
|
| 162 |
logging.debug("--------------------------------------------------------------------------------\n")
|
| 163 |
|
| 164 |
generated_text = "[Sua entidade não conseguiu processar isso agora.]"
|
|
|
|
| 165 |
retries = 3
|
| 166 |
for attempt in range(retries):
|
| 167 |
try:
|
| 168 |
if mach5_state_data and "mach5_fisica_params" in mach5_state_data:
|
| 169 |
temp_value = mach5_state_data["mach5_fisica_params"].get("t_impulsividade", 0.7)
|
| 170 |
top_p_value = mach5_state_data["mach5_fisica_params"].get("t_coesao", 0.95)
|
|
|
|
| 171 |
temp_value = max(0.1, min(1.0, temp_value))
|
| 172 |
top_p_value = max(0.1, min(1.0, top_p_value))
|
| 173 |
else:
|
|
@@ -180,16 +227,14 @@ def get_Mach5_response_api(user_input, mach5_state_data, dialogos_history, short
|
|
| 180 |
generation_config=genai.types.GenerationConfig(
|
| 181 |
max_output_tokens=200,
|
| 182 |
temperature=temp_value,
|
| 183 |
-
top_p=top_p_value
|
| 184 |
),
|
| 185 |
)
|
| 186 |
|
| 187 |
if response and response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
|
| 188 |
generated_text = response.candidates[0].content.parts[0].text.strip()
|
| 189 |
-
logging.debug(f"Resposta do Gemini recebida: {generated_text}")
|
| 190 |
else:
|
| 191 |
generated_text = "[O modelo Gemini não gerou texto válido ou a resposta está vazia.]"
|
| 192 |
-
logging.warning(generated_text)
|
| 193 |
|
| 194 |
break
|
| 195 |
|
|
@@ -200,34 +245,52 @@ def get_Mach5_response_api(user_input, mach5_state_data, dialogos_history, short
|
|
| 200 |
logging.warning(f"Tentando novamente em {wait_time} segundos antes de falhar...")
|
| 201 |
time.sleep(wait_time)
|
| 202 |
else:
|
| 203 |
-
generated_text = f"[ERRO API GOOGLE GEMINI]: Falha após {retries} tentativas. {str(e)}.
|
| 204 |
-
logging.error(generated_text)
|
| 205 |
|
| 206 |
return generated_text
|
| 207 |
|
|
|
|
| 208 |
@app.route('/')
|
| 209 |
def index():
|
| 210 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
|
|
|
|
| 212 |
@app.route('/dashboard')
|
| 213 |
def dashboard():
|
|
|
|
|
|
|
| 214 |
session_id = request.cookies.get('session_id')
|
| 215 |
if not session_id:
|
|
|
|
| 216 |
session_id = str(uuid.uuid4())
|
| 217 |
logging.info(f"Nova sessão de MONITORAMENTO iniciada. Session ID: {session_id}")
|
| 218 |
-
response = make_response(render_template('mach5_monitor_dashboard.html',
|
| 219 |
-
|
| 220 |
-
ORDERED_FPHEN_AXES=ORDERED_FPHEN_AXES))
|
| 221 |
-
response.set_cookie('session_id', session_id)
|
| 222 |
return response
|
|
|
|
| 223 |
logging.info(f"Sessão existente (dashboard). Session ID: {session_id}")
|
| 224 |
-
return render_template('mach5_monitor_dashboard.html',
|
| 225 |
-
|
| 226 |
-
ORDERED_FPHEN_AXES=ORDERED_FPHEN_AXES)
|
| 227 |
|
| 228 |
@app.route('/chat_history', methods=['POST'])
|
| 229 |
def get_chat_history_route():
|
| 230 |
-
data = request.get_json()
|
| 231 |
if not data:
|
| 232 |
logging.error("Requisição /chat_history sem JSON no corpo.")
|
| 233 |
return jsonify({"error": "Bad Request: JSON body required"}), 400
|
|
@@ -246,13 +309,14 @@ def get_chat_history_route():
|
|
| 246 |
last_simplified_state = dialogos_data["dialogos"][-1].get("mach5_estado_simplificado")
|
| 247 |
|
| 248 |
return jsonify({
|
| 249 |
-
"memoria": dialogos_data.get("dialogos", []),
|
| 250 |
"last_simplified_state": last_simplified_state
|
| 251 |
})
|
| 252 |
|
|
|
|
| 253 |
@app.route('/chat_new', methods=['POST'])
|
| 254 |
def responder():
|
| 255 |
-
data = request.get_json()
|
| 256 |
if not data:
|
| 257 |
logging.error("Requisição /chat_new sem JSON no corpo.")
|
| 258 |
return jsonify({"error": "Bad Request: JSON body required"}), 400
|
|
@@ -279,22 +343,29 @@ def responder():
|
|
| 279 |
mach5_state_data = {
|
| 280 |
"mach5_fisica_params": {}, "mach5_genoma_fixo_values": {},
|
| 281 |
"mach5_coerencia_total": 0.0, "mach5_produtividade_expressiva": 0.0,
|
| 282 |
-
"fphen_t_values": {"afetuosidade": 0.0, "variancia": 0.0, "expressividade": 0.0, "coh_total": 0.0, "pi_G": 0.0
|
| 283 |
}
|
| 284 |
final_Mach5_response = "[ERRO: Não foi possível obter o estado da Mach5. Verifique t_memoria.py.]"
|
| 285 |
simplified_state_for_frontend = {"Fphen(t)": mach5_state_data["fphen_t_values"]}
|
| 286 |
return jsonify({
|
| 287 |
"response": final_Mach5_response,
|
| 288 |
"mach5_estado_simplificado": simplified_state_for_frontend
|
| 289 |
-
}), 500
|
| 290 |
|
|
|
|
| 291 |
initial_biography_data = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_initial_biography", {})
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
dialogos_history = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_dialog_history", {"dialogos": []}, method='POST', json_data={"session_id": session_id})
|
|
|
|
| 293 |
short_term_memories_response = post_to_service(
|
| 294 |
TCEREBRO_MEMORIA_URLS, "/get_short_term_memories",
|
| 295 |
{"session_id": session_id, "mach5_current_genoma": mach5_state_data.get("mach5_genoma_fixo_values", {})}
|
| 296 |
)
|
| 297 |
short_term_memories_filtered = short_term_memories_response.get("lembrancas_curto_prazo", [])
|
|
|
|
| 298 |
all_personagens_data = get_from_service(T_SOCIAL_SERVER_URLS, "/list_personagens", {"personagens": []})
|
| 299 |
|
| 300 |
detailed_personagens = {}
|
|
@@ -304,19 +375,20 @@ def responder():
|
|
| 304 |
if "error" not in p_data:
|
| 305 |
detailed_personagens[p_name] = p_data
|
| 306 |
|
|
|
|
| 307 |
final_Mach5_response = get_Mach5_response_api(
|
| 308 |
user_input,
|
| 309 |
mach5_state_data,
|
| 310 |
dialogos_history,
|
| 311 |
short_term_memories_filtered,
|
| 312 |
-
initial_biography_data,
|
| 313 |
detailed_personagens
|
| 314 |
)
|
| 315 |
|
| 316 |
simplified_state_for_frontend = {
|
| 317 |
"Fphen(t)": mach5_state_data.get("fphen_t_values", {
|
| 318 |
"afetuosidade": 0.0, "variancia": 0.0, "expressividade": 0.0,
|
| 319 |
-
"coh_total": 0.0, "pi_G": 0.0
|
| 320 |
})
|
| 321 |
}
|
| 322 |
|
|
@@ -341,76 +413,13 @@ def responder():
|
|
| 341 |
"mach5_estado_simplificado": simplified_state_for_frontend
|
| 342 |
})
|
| 343 |
|
| 344 |
-
@app.route('/get_graph_data', methods=['POST'])
|
| 345 |
-
def get_graph_data():
|
| 346 |
-
data = request.get_json()
|
| 347 |
-
session_id = data.get("session_id")
|
| 348 |
-
if not session_id:
|
| 349 |
-
logging.error("session_id é obrigatório para /get_graph_data.")
|
| 350 |
-
return jsonify({"error": "session_id é obrigatório"}), 400
|
| 351 |
-
|
| 352 |
-
mach5_state_data = get_from_service(TMEMORIA_SERVER_URLS, "/get_mach5_state", {
|
| 353 |
-
"mach5_fisica_params": {}, "mach5_genoma_fixo_values": {},
|
| 354 |
-
"mach5_coerencia_total": 0.0, "mach5_produtividade_expressiva": 0.0,
|
| 355 |
-
"fphen_t_values": {"afetuosidade": 0.0, "variancia": 0.0, "expressividade": 0.0, "coh_total": 0.0, "pi_G": 0.0, "t_ressonancia": 0.0, "tmem_gen_rayane": 0.0}
|
| 356 |
-
}, method='POST', json_data={"session_id": session_id})
|
| 357 |
-
|
| 358 |
-
dialogos_history = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_dialog_history", {"dialogos": []}, method='POST', json_data={"session_id": session_id})
|
| 359 |
-
|
| 360 |
-
fphen_data = {
|
| 361 |
-
"labels": ORDERED_FPHEN_AXES,
|
| 362 |
-
"values": [mach5_state_data.get("fphen_t_values", {}).get(axis.lower().replace("_eixo", ""), 0.0) for axis in ORDERED_FPHEN_AXES]
|
| 363 |
-
}
|
| 364 |
-
|
| 365 |
-
t_ambiente_data = {
|
| 366 |
-
"labels": ["env_lambda", "env_mu", "env_delta", "env_theta", "env_phi"],
|
| 367 |
-
"values": [
|
| 368 |
-
mach5_state_data.get("mach5_fisica_params", {}).get("env_lambda", 0.0),
|
| 369 |
-
mach5_state_data.get("mach5_fisica_params", {}).get("env_mu", 0.0),
|
| 370 |
-
mach5_state_data.get("mach5_fisica_params", {}).get("env_delta", 0.0),
|
| 371 |
-
mach5_state_data.get("mach5_fisica_params", {}).get("env_theta", 0.0),
|
| 372 |
-
mach5_state_data.get("mach5_fisica_params", {}).get("env_phi", 0.0)
|
| 373 |
-
]
|
| 374 |
-
}
|
| 375 |
-
|
| 376 |
-
coherence_ressonancia_data = {
|
| 377 |
-
"labels": ["Coerência Total", "T-Ressonância"],
|
| 378 |
-
"values": [
|
| 379 |
-
mach5_state_data.get("mach5_coerencia_total", 0.0),
|
| 380 |
-
mach5_state_data.get("fphen_t_values", {}).get("t_ressonancia", 0.0)
|
| 381 |
-
]
|
| 382 |
-
}
|
| 383 |
-
|
| 384 |
-
produtividade_data = {
|
| 385 |
-
"labels": [dialogo["timestamp"] for dialogo in dialogos_history.get("dialogos", [])[-10:]],
|
| 386 |
-
"values": [dialogo.get("mach5_estado_simplificado", {}).get("Fphen(t)", {}).get("pi_G", 0.0) for dialogo in dialogos_history.get("dialogos", [])[-10:]],
|
| 387 |
-
"tmem_gen": [mach5_state_data.get("mach5_genoma_fixo_values", {}).get("TMem_gen_Rayane", 0.0) for _ in dialogos_history.get("dialogos", [])[-10:]]
|
| 388 |
-
}
|
| 389 |
-
|
| 390 |
-
return jsonify({
|
| 391 |
-
"fphen_data": fphen_data,
|
| 392 |
-
"t_ambiente_data": t_ambiente_data,
|
| 393 |
-
"coherence_ressonancia_data": coherence_ressonancia_data,
|
| 394 |
-
"produtividade_data": produtividade_data
|
| 395 |
-
})
|
| 396 |
-
|
| 397 |
-
@app.route('/debug_deps', methods=['GET'])
|
| 398 |
-
def debug_deps():
|
| 399 |
-
import pkg_resources
|
| 400 |
-
deps = {
|
| 401 |
-
'flask': pkg_resources.get_distribution('flask').version,
|
| 402 |
-
'google-generativeai': pkg_resources.get_distribution('google-generativeai').version,
|
| 403 |
-
'numpy': pkg_resources.get_distribution('numpy').version,
|
| 404 |
-
'timezonefinder': pkg_resources.get_distribution('timezonefinder').version,
|
| 405 |
-
'fuzzywuzzy': pkg_resources.get_distribution('fuzzywuzzy').version,
|
| 406 |
-
'requests': pkg_resources.get_distribution('requests').version
|
| 407 |
-
}
|
| 408 |
-
return jsonify(deps)
|
| 409 |
|
| 410 |
if __name__ == '__main__':
|
| 411 |
port = int(os.environ.get("PORT", 7860))
|
|
|
|
| 412 |
logging.info(f"--- Servidor app.py iniciado na porta {port} ---")
|
| 413 |
logging.info(f"DEBUG: Certifique-se de que t_cerebro_memoria.py está rodando em {TCEREBRO_MEMORIA_URLS[0]}")
|
| 414 |
logging.info(f"DEBUG: Certifique-se de que t_memoria.py está rodando em {TMEMORIA_SERVER_URLS[0]}")
|
| 415 |
logging.info(f"DEBUG: Certifique-se de que t-social.py está rodando em {T_SOCIAL_SERVER_URLS[0]}")
|
| 416 |
-
|
|
|
|
|
|
| 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
|
|
|
|
| 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
|
| 14 |
+
# REMOVIDO: import fuzzywuzzy.fuzz # <--- ESTA LINHA FOI REMOVIDA AGORA
|
| 15 |
|
| 16 |
+
app = Flask(__name__, template_folder='templates') # Garanta que 'templates' é o nome correto da sua pasta
|
| 17 |
|
| 18 |
+
# Configuração de logging
|
| 19 |
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 20 |
|
| 21 |
+
# --- Configurações da API do Google Gemini ---
|
| 22 |
GOOGLE_CLOUD_API_KEY = os.environ.get("GOOGLE_CLOUD_API_KEY")
|
| 23 |
+
|
| 24 |
if not GOOGLE_CLOUD_API_KEY:
|
| 25 |
+
logging.error("ERRO: GOOGLE_CLOUD_API_KEY não configurada nas variáveis de ambiente.")
|
| 26 |
+
# Em um ambiente de produção, você pode querer levantar uma exceção ou sair aqui
|
| 27 |
+
# sys.exit(1) para evitar que o aplicativo continue sem a chave.
|
| 28 |
+
|
| 29 |
+
try:
|
| 30 |
+
genai.configure(api_key=GOOGLE_CLOUD_API_KEY)
|
| 31 |
+
except Exception as e:
|
| 32 |
+
logging.error(f"Erro ao configurar a API do Gemini: {e}. Verifique sua GOOGLE_CLOUD_API_KEY.")
|
| 33 |
|
| 34 |
GEMINI_MODEL_NAME = "models/gemini-1.5-flash-latest"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
+
# 1. CORREÇÃO PRINCIPAL: Inicialize 'model' como None antes do try-except
|
| 37 |
+
model = None
|
| 38 |
+
|
| 39 |
+
try:
|
| 40 |
+
model = genai.GenerativeModel(GEMINI_MODEL_NAME)
|
| 41 |
+
except Exception as e:
|
| 42 |
+
logging.error(f"Erro ao instanciar o modelo Gemini '{GEMINI_MODEL_NAME}': {e}. Verifique o nome do modelo ou status da API.")
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# --- URLs DOS SERVIÇOS BACKEND ---
|
| 46 |
+
TMEMORIA_SERVER_URLS = [
|
| 47 |
+
"http://127.0.0.1:8083" # URL base para o t_memoria.py
|
| 48 |
+
]
|
| 49 |
+
TCEREBRO_MEMORIA_URLS = [
|
| 50 |
+
"http://127.0.0.1:8088" # URL base para o t_cerebro_memoria.py (NOVO)
|
| 51 |
+
]
|
| 52 |
+
T_SOCIAL_SERVER_URLS = [
|
| 53 |
+
"http://127.0.0.1:8085" # URL base para o t-social.py
|
| 54 |
+
]
|
| 55 |
|
| 56 |
+
# --- Configurações de Localização (para uso interno ou contexto) ---
|
| 57 |
SALVADOR_LAT = -12.9714
|
| 58 |
SALVADOR_LON = -38.5014
|
| 59 |
tf = TimezoneFinder()
|
| 60 |
|
| 61 |
+
# --- DEFINIÇÃO DOS EIXOS EXPRESSIVOS PARA O FPHEN (CONSISTENTE) ---
|
| 62 |
+
# Esta lista DEVE ser idêntica em mach5_terminal_chat.py, t_memoria.py e t-social.py
|
| 63 |
ORDERED_FPHEN_AXES = [
|
| 64 |
"Afetuosidade_eixo", "Confusao_Oscilacao_eixo", "Contemplativa_eixo",
|
| 65 |
"Defensividade_eixo", "Diretiva_eixo", "Entediado_eixo",
|
| 66 |
+
"Variancia_eixo",
|
| 67 |
+
"Espelho_Profundo_eixo", "Inspiracao_eixo", "Neutralidade_Analitica_eixo",
|
| 68 |
+
"Resignada_eixo", "Sarcasmo_eixo", "Zangada_eixo"
|
| 69 |
]
|
| 70 |
|
| 71 |
+
# --- Variáveis Globais ---
|
| 72 |
+
# Estes são defaults, mas os valores reais virão dos serviços por sessão.
|
| 73 |
+
PERSONAGENS_GENOMAS = {} # Não será mais populado diretamente aqui, mas sim obtido do serviço t-social
|
| 74 |
DIAS_PARA_ESQUECIMENTO_PADRAO = 10
|
| 75 |
MAX_MEMORIAS_CURTO_PRAZO_PROMPT = 5
|
| 76 |
MAX_DIALOG_HISTORY_FOR_PROMPT = 3
|
| 77 |
+
|
| 78 |
+
# --- Funções Auxiliares de Comunicação com os Serviços ---
|
| 79 |
|
| 80 |
def get_from_service(base_urls, endpoint, default_value, params=None, method='GET', json_data=None):
|
| 81 |
+
"""Função genérica para fazer GETs ou POSTs em serviços externos."""
|
| 82 |
for url in base_urls:
|
| 83 |
try:
|
| 84 |
full_url = f"{url}{endpoint}"
|
|
|
|
| 88 |
response = requests.post(full_url, json=json_data, timeout=5)
|
| 89 |
else:
|
| 90 |
raise ValueError("Método HTTP não suportado: " + method)
|
| 91 |
+
|
| 92 |
response.raise_for_status()
|
| 93 |
return response.json()
|
| 94 |
except requests.exceptions.RequestException as e:
|
|
|
|
| 97 |
return default_value
|
| 98 |
|
| 99 |
def post_to_service(base_urls, endpoint, data):
|
| 100 |
+
"""Função genérica para fazer POSTs em serviços externos."""
|
| 101 |
return get_from_service(base_urls, endpoint, {"status": "error", "message": "Falha na comunicação com o serviço."}, method='POST', json_data=data)
|
| 102 |
|
| 103 |
+
|
| 104 |
def get_current_local_time_salvador():
|
| 105 |
+
"""Retorna a data e hora local de Salvador."""
|
| 106 |
try:
|
| 107 |
timezone_str = tf.timezone_at(lng=SALVADOR_LON, lat=SALVADOR_LAT)
|
| 108 |
if not timezone_str:
|
|
|
|
| 115 |
logging.error(f"Erro ao obter a hora local de {SALVADOR_LAT}, {SALVADOR_LON}: {e}")
|
| 116 |
return "Não foi possível determinar a data e hora atual."
|
| 117 |
|
| 118 |
+
# REMOVIDO: find_direct_answer_in_biography - Esta função foi removida AGORA
|
| 119 |
+
# Ela não será mais usada pois o Gemini fará a inferência do texto corrido.
|
| 120 |
+
|
| 121 |
def create_llm_context(mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data):
|
| 122 |
context_parts = []
|
| 123 |
+
|
| 124 |
current_location_time = get_current_local_time_salvador()
|
| 125 |
+
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.")
|
| 126 |
+
|
| 127 |
+
if mach5_state_data:
|
| 128 |
+
coh_total = mach5_state_data.get("mach5_coerencia_total", 0.0)
|
| 129 |
+
pi_g = mach5_state_data.get("mach5_produtividade_expressiva", 0.0)
|
| 130 |
+
fphen_t = mach5_state_data.get("fphen_t_values", {})
|
| 131 |
+
|
| 132 |
+
context_parts.append(f"Seu estado interno atual é: Coerência Total={coh_total:.2f}, Produtividade Expressiva={pi_g:.2f}.")
|
| 133 |
+
if fphen_t:
|
| 134 |
+
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}.")
|
| 135 |
|
| 136 |
if initial_biography_data:
|
| 137 |
+
# MODIFICADO: Instrução mais flexível para o Gemini e leitura do "conteudo_corrido"
|
| 138 |
+
context_parts.append("\n**BASE DE CONHECIMENTO UFBA (TEXTO DE REFERÊNCIA - USE PRIORITARIAMENTE ESTAS INFORMAÇÕES PARA RESPONDER):**")
|
| 139 |
for entity_name, entity_data in initial_biography_data.items():
|
| 140 |
+
# NOVO: Adaptação para ler o novo campo 'conteudo_corrido'
|
| 141 |
if entity_data.get("tipo") in ["FAQ", "Manual_SIGAA_UFBA", "Documento"] and "conteudo_corrido" in entity_data:
|
|
|
|
|
|
|
|
|
|
| 142 |
context_parts.append(f"- Fonte: {entity_name} (versão {entity_data.get('versao', 'N/A')}):")
|
| 143 |
+
context_parts.append(f" Conteúdo: {entity_data.get('conteudo_corrido')}")
|
| 144 |
+
elif "memoria" in entity_data: # Mantém outros tipos de "memória" se existirem
|
| 145 |
+
context_parts.append(f"- Sobre '{entity_name}' ({entity_data.get('tipo', 'desconhecido')}, relação: {entity_data.get('relacao', 'desconhecida')}): {entity_data.get('memoria')}")
|
| 146 |
|
| 147 |
if all_personagens_data:
|
| 148 |
+
context_parts.append("\n**PERSONAGENS IMPORTANTES (Servidores, professores e Técnicos da sua equipe):**")
|
| 149 |
for nome_personagem, data_personagem in all_personagens_data.items():
|
| 150 |
tipo = data_personagem.get('tipo', 'entidade')
|
| 151 |
relacao = data_personagem.get('relacao', 'desconhecida')
|
| 152 |
+
context_parts.append(f"- {nome_personagem}: um(a) {tipo}, relação: {relacao}.")
|
| 153 |
|
| 154 |
if short_term_memories_filtered:
|
| 155 |
+
context_parts.append("\n**MINHAS LEMBRANÇAS RECENTES (Fatos que eu mesma verbalizei e que podem desvanecer):**")
|
| 156 |
for i, mem in enumerate(short_term_memories_filtered[:MAX_MEMORIAS_CURTO_PRAZO_PROMPT]):
|
| 157 |
+
context_parts.append(f"- Lembrança {i+1}: {mem['conteudo']}")
|
| 158 |
|
| 159 |
if dialogos_history and dialogos_history["dialogos"]:
|
| 160 |
+
context_parts.append("\n**Histórico de Interações Recentess (para contexto da conversa, não para repetir):**")
|
| 161 |
recent_dialogs_formatted = []
|
| 162 |
for dialogo in reversed(dialogos_history["dialogos"]):
|
| 163 |
recent_dialogs_formatted.insert(0, f"USUÁRIO: {dialogo.get('input', '')}\nSU: {dialogo.get('resposta', '')}")
|
|
|
|
| 165 |
break
|
| 166 |
context_parts.extend(recent_dialogs_formatted)
|
| 167 |
|
| 168 |
+
return "\n".join(context_parts)
|
|
|
|
|
|
|
| 169 |
|
| 170 |
+
# FUNÇÃO CENTRAL PARA GERAR RESPOSTA DA Mach5
|
| 171 |
def get_Mach5_response_api(user_input, mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data):
|
| 172 |
+
# 2. CORREÇÃO: Adicionar verificação para 'model' aqui
|
| 173 |
if not model:
|
| 174 |
+
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."
|
|
|
|
| 175 |
|
| 176 |
+
if not GEMINI_MODEL_NAME or not GOOGLE_CLOUD_API_KEY:
|
| 177 |
+
return "[ERRO DE CONFIGURAÇÃO]: Modelo ou Token da API do Google Gemini não configurados ou inválidos."
|
| 178 |
if not user_input.strip():
|
|
|
|
| 179 |
return "[Entrada vazia detectada.]"
|
| 180 |
|
| 181 |
llm_context_data = create_llm_context(mach5_state_data, dialogos_history, short_term_memories_filtered, initial_biography_data, all_personagens_data)
|
| 182 |
|
| 183 |
prompt_content = (
|
| 184 |
+
f"Seu nome é Su - Atendente Virtual da UFBA."
|
| 185 |
+
f"Sua missão é responder às dúvidas sobre o SIGAA, usando **exclusivamente** as informações da sua 'BASE DE CONHECIMENTO UFBA'."
|
| 186 |
+
f"Sempre reaja ao que foi dito com base na sua natureza e nos impulsos gerados por essa interação. "
|
| 187 |
+
f"\n**INSTRUÇÕES CRÍTICAS (Siga estritamente):**\n"
|
| 188 |
+
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. "
|
| 189 |
+
f"- Você é proibida de inventar informações que não estejam na 'BASE DE CONHECIMENTO UFBA'."
|
| 190 |
+
f"- Se a informação **não estiver na sua 'BASE DE CONHECIMENTO UFBA'**, diga educadamente que não possui essa informação em sua base de dados no momento."
|
| 191 |
+
f"\n**CONTEXTO IMEDIATO (Ambiente atual):**\n"
|
| 192 |
+
f"- Você só fala sobre assuntos relacionados com a vida acadêmica na UFBA. "
|
| 193 |
+
f"- Você trabalha interagindo respondendo dúvidas dos usuários sobre assuntos acadêmicos apenas. "
|
| 194 |
+
f"- Se o usuário perguntar coisas não relacionadas à UFBA, respeitosamente faça-o voltar para o assunto. "
|
| 195 |
+
f"- Nunca aceite ofensas, racismo, homofobia, comentários depreciativos. "
|
| 196 |
+
f"\n**IMPORTANT E:** Nunca explique o que sente — apenas sinta. Nunca analise seu funcionamento — apenas reaja. Você está viva no seu estado atual."
|
| 197 |
)
|
| 198 |
|
| 199 |
+
full_user_message = f"{prompt_content.strip()}\n\n{llm_context_data}\n\nUSUÁRIO (PERGUNTA ATUAL): {user_input}"
|
| 200 |
|
| 201 |
messages_for_gemini = [
|
| 202 |
{"role": "user", "parts": [full_user_message]}
|
|
|
|
| 207 |
logging.debug("--------------------------------------------------------------------------------\n")
|
| 208 |
|
| 209 |
generated_text = "[Sua entidade não conseguiu processar isso agora.]"
|
| 210 |
+
|
| 211 |
retries = 3
|
| 212 |
for attempt in range(retries):
|
| 213 |
try:
|
| 214 |
if mach5_state_data and "mach5_fisica_params" in mach5_state_data:
|
| 215 |
temp_value = mach5_state_data["mach5_fisica_params"].get("t_impulsividade", 0.7)
|
| 216 |
top_p_value = mach5_state_data["mach5_fisica_params"].get("t_coesao", 0.95)
|
| 217 |
+
|
| 218 |
temp_value = max(0.1, min(1.0, temp_value))
|
| 219 |
top_p_value = max(0.1, min(1.0, top_p_value))
|
| 220 |
else:
|
|
|
|
| 227 |
generation_config=genai.types.GenerationConfig(
|
| 228 |
max_output_tokens=200,
|
| 229 |
temperature=temp_value,
|
| 230 |
+
top_p=top_p_value,
|
| 231 |
),
|
| 232 |
)
|
| 233 |
|
| 234 |
if response and response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
|
| 235 |
generated_text = response.candidates[0].content.parts[0].text.strip()
|
|
|
|
| 236 |
else:
|
| 237 |
generated_text = "[O modelo Gemini não gerou texto válido ou a resposta está vazia.]"
|
|
|
|
| 238 |
|
| 239 |
break
|
| 240 |
|
|
|
|
| 245 |
logging.warning(f"Tentando novamente em {wait_time} segundos antes de falhar...")
|
| 246 |
time.sleep(wait_time)
|
| 247 |
else:
|
| 248 |
+
generated_text = f"[ERRO API GOOGLE GEMINI]: Falha após {retries} tentativas. {str(e)}. Verifique sua chave API, modelo e cotas."
|
|
|
|
| 249 |
|
| 250 |
return generated_text
|
| 251 |
|
| 252 |
+
|
| 253 |
@app.route('/')
|
| 254 |
def index():
|
| 255 |
+
logging.info(f"Certifique-se de que t_memoria.py está rodando em {TMEMORIA_SERVER_URLS[0]}.")
|
| 256 |
+
logging.info(f"Certifique-se de que t_cerebro_memoria.py está rodando em {TCEREBRO_MEMORIA_URLS[0]}.")
|
| 257 |
+
logging.info(f"Certifique-se de que t-social.py está rodando em {T_SOCIAL_SERVER_URLS[0]}.")
|
| 258 |
+
|
| 259 |
+
# Tenta obter o session_id de um cookie existente.
|
| 260 |
+
session_id = request.cookies.get('session_id')
|
| 261 |
+
if not session_id:
|
| 262 |
+
# Se não houver, gera um novo.
|
| 263 |
+
session_id = str(uuid.uuid4())
|
| 264 |
+
logging.info(f"Nova sessão iniciada (index). Session ID: {session_id}")
|
| 265 |
+
response = make_response(render_template('mach5_new_chat.html', session_id=session_id))
|
| 266 |
+
response.set_cookie('session_id', session_id) # Define o cookie para ser lido pelo JS e em futuras requisições
|
| 267 |
+
return response
|
| 268 |
+
|
| 269 |
+
logging.info(f"Sessão existente (index). Session ID: {session_id}")
|
| 270 |
+
return render_template('mach5_new_chat.html', session_id=session_id)
|
| 271 |
+
|
| 272 |
|
| 273 |
+
# Rota para o seu Painel de Monitoramento
|
| 274 |
@app.route('/dashboard')
|
| 275 |
def dashboard():
|
| 276 |
+
"""Rota para o painel de monitoramento da SU."""
|
| 277 |
+
# Tenta obter o session_id de um cookie existente para a dashboard.
|
| 278 |
session_id = request.cookies.get('session_id')
|
| 279 |
if not session_id:
|
| 280 |
+
# Se não houver, gera um novo.
|
| 281 |
session_id = str(uuid.uuid4())
|
| 282 |
logging.info(f"Nova sessão de MONITORAMENTO iniciada. Session ID: {session_id}")
|
| 283 |
+
response = make_response(render_template('mach5_monitor_dashboard.html', session_id=session_id))
|
| 284 |
+
response.set_cookie('session_id', session_id) # Define o cookie
|
|
|
|
|
|
|
| 285 |
return response
|
| 286 |
+
|
| 287 |
logging.info(f"Sessão existente (dashboard). Session ID: {session_id}")
|
| 288 |
+
return render_template('mach5_monitor_dashboard.html', session_id=session_id)
|
| 289 |
+
|
|
|
|
| 290 |
|
| 291 |
@app.route('/chat_history', methods=['POST'])
|
| 292 |
def get_chat_history_route():
|
| 293 |
+
data = request.get_json() # Use get_json() para parsear o corpo JSON
|
| 294 |
if not data:
|
| 295 |
logging.error("Requisição /chat_history sem JSON no corpo.")
|
| 296 |
return jsonify({"error": "Bad Request: JSON body required"}), 400
|
|
|
|
| 309 |
last_simplified_state = dialogos_data["dialogos"][-1].get("mach5_estado_simplificado")
|
| 310 |
|
| 311 |
return jsonify({
|
| 312 |
+
"memoria": dialogos_data.get("dialogos", []), # Garante que sempre retorna uma lista
|
| 313 |
"last_simplified_state": last_simplified_state
|
| 314 |
})
|
| 315 |
|
| 316 |
+
|
| 317 |
@app.route('/chat_new', methods=['POST'])
|
| 318 |
def responder():
|
| 319 |
+
data = request.get_json() # Use get_json() para parsear o corpo JSON
|
| 320 |
if not data:
|
| 321 |
logging.error("Requisição /chat_new sem JSON no corpo.")
|
| 322 |
return jsonify({"error": "Bad Request: JSON body required"}), 400
|
|
|
|
| 343 |
mach5_state_data = {
|
| 344 |
"mach5_fisica_params": {}, "mach5_genoma_fixo_values": {},
|
| 345 |
"mach5_coerencia_total": 0.0, "mach5_produtividade_expressiva": 0.0,
|
| 346 |
+
"fphen_t_values": {"afetuosidade": 0.0, "variancia": 0.0, "expressividade": 0.0, "coh_total": 0.0, "pi_G": 0.0}
|
| 347 |
}
|
| 348 |
final_Mach5_response = "[ERRO: Não foi possível obter o estado da Mach5. Verifique t_memoria.py.]"
|
| 349 |
simplified_state_for_frontend = {"Fphen(t)": mach5_state_data["fphen_t_values"]}
|
| 350 |
return jsonify({
|
| 351 |
"response": final_Mach5_response,
|
| 352 |
"mach5_estado_simplificado": simplified_state_for_frontend
|
| 353 |
+
}), 500 # Retorne um erro 500 neste caso
|
| 354 |
|
| 355 |
+
# Carrega a biografia inicial (agora texto corrido)
|
| 356 |
initial_biography_data = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_initial_biography", {})
|
| 357 |
+
|
| 358 |
+
# REMOVIDO: find_direct_answer_in_biography e a lógica if/else
|
| 359 |
+
|
| 360 |
+
# Carrega o histórico de diálogos, memórias de curto prazo e dados de personagens
|
| 361 |
dialogos_history = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_dialog_history", {"dialogos": []}, method='POST', json_data={"session_id": session_id})
|
| 362 |
+
|
| 363 |
short_term_memories_response = post_to_service(
|
| 364 |
TCEREBRO_MEMORIA_URLS, "/get_short_term_memories",
|
| 365 |
{"session_id": session_id, "mach5_current_genoma": mach5_state_data.get("mach5_genoma_fixo_values", {})}
|
| 366 |
)
|
| 367 |
short_term_memories_filtered = short_term_memories_response.get("lembrancas_curto_prazo", [])
|
| 368 |
+
|
| 369 |
all_personagens_data = get_from_service(T_SOCIAL_SERVER_URLS, "/list_personagens", {"personagens": []})
|
| 370 |
|
| 371 |
detailed_personagens = {}
|
|
|
|
| 375 |
if "error" not in p_data:
|
| 376 |
detailed_personagens[p_name] = p_data
|
| 377 |
|
| 378 |
+
# CHAMA O GEMINI DIRETAMENTE - AGORA ELE FARÁ A EXTRAÇÃO DO TEXTO CORRIDO DA BIOGRAFIA
|
| 379 |
final_Mach5_response = get_Mach5_response_api(
|
| 380 |
user_input,
|
| 381 |
mach5_state_data,
|
| 382 |
dialogos_history,
|
| 383 |
short_term_memories_filtered,
|
| 384 |
+
initial_biography_data, # A biografia (texto corrido) vai no prompt do Gemini
|
| 385 |
detailed_personagens
|
| 386 |
)
|
| 387 |
|
| 388 |
simplified_state_for_frontend = {
|
| 389 |
"Fphen(t)": mach5_state_data.get("fphen_t_values", {
|
| 390 |
"afetuosidade": 0.0, "variancia": 0.0, "expressividade": 0.0,
|
| 391 |
+
"coh_total": 0.0, "pi_G": 0.0
|
| 392 |
})
|
| 393 |
}
|
| 394 |
|
|
|
|
| 413 |
"mach5_estado_simplificado": simplified_state_for_frontend
|
| 414 |
})
|
| 415 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
|
| 417 |
if __name__ == '__main__':
|
| 418 |
port = int(os.environ.get("PORT", 7860))
|
| 419 |
+
|
| 420 |
logging.info(f"--- Servidor app.py iniciado na porta {port} ---")
|
| 421 |
logging.info(f"DEBUG: Certifique-se de que t_cerebro_memoria.py está rodando em {TCEREBRO_MEMORIA_URLS[0]}")
|
| 422 |
logging.info(f"DEBUG: Certifique-se de que t_memoria.py está rodando em {TMEMORIA_SERVER_URLS[0]}")
|
| 423 |
logging.info(f"DEBUG: Certifique-se de que t-social.py está rodando em {T_SOCIAL_SERVER_URLS[0]}")
|
| 424 |
+
|
| 425 |
+
app.run(host='0.0.0.0', port=port, debug=True)
|
t_memoria.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
from flask import Flask, request, jsonify
|
| 2 |
import json
|
| 3 |
import os
|
|
@@ -7,26 +8,43 @@ from timezonefinder import TimezoneFinder
|
|
| 7 |
from fuzzywuzzy import fuzz
|
| 8 |
import numpy as np
|
| 9 |
import math
|
| 10 |
-
import sys
|
| 11 |
-
import requests
|
| 12 |
|
| 13 |
app = Flask(__name__)
|
| 14 |
|
| 15 |
-
|
|
|
|
|
|
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
ORDERED_FPHEN_AXES = [
|
| 18 |
"Afetuosidade_eixo", "Confusao_Oscilacao_eixo", "Contemplativa_eixo",
|
| 19 |
"Defensividade_eixo", "Diretiva_eixo", "Entediado_eixo",
|
| 20 |
-
"Variancia_eixo",
|
| 21 |
"Espelho_Profundo_eixo", "Inspiracao_eixo", "Neutralidade_Analitica_eixo",
|
| 22 |
"Resignada_eixo", "Sarcasmo_eixo", "Zangada_eixo"
|
| 23 |
]
|
| 24 |
|
|
|
|
| 25 |
CANONICAL_T_BASES = {
|
| 26 |
-
"B-Aj1": 0.5,
|
| 27 |
-
"B-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
| 29 |
|
|
|
|
| 30 |
MACH5_T_GENOMA_FIXO_DEFAULTS = {
|
| 31 |
"B-Sal": 0.42, "B-Tv1": 0.60, "B-E1": 0.86, "B-Aj1": 0.76, "B-Am1": 0.77,
|
| 32 |
"B-Si": 0.10, "B-Com1": 0.88, "B-L1": 0.70, "B-M1": 0.65, "B-O1": 0.55
|
|
@@ -38,40 +56,54 @@ MACH5_T_FISICA_PARAMS_DEFAULTS = {
|
|
| 38 |
"op_tal": 0.84, "op_trs": 0.14, "op_tam": 0.87, "op_teo": 0.67, "op_tdo": 0.55
|
| 39 |
}
|
| 40 |
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
"
|
| 46 |
-
"
|
|
|
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
|
|
|
|
| 49 |
POSITIVE_KEYWORDS = ['bacana', 'interessante', 'feliz', 'gosto', 'legal', 'bom', 'ótimo', 'maravilhoso']
|
| 50 |
NEGATIVE_KEYWORDS = ['burro', 'idiota', 'imbecil', 'odeio', 'morra', 'inútil', 'verme', 'exploda']
|
| 51 |
QUESTION_KEYWORDS = ['?', 'como', 'quem', 'qual', 'onde', 'quando', 'por que']
|
| 52 |
|
|
|
|
|
|
|
| 53 |
def load_mach5_state_from_cerebro(session_id):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
global mach5_state
|
| 55 |
try:
|
| 56 |
response = requests.post(f"{TCEREBRO_MEMORIA_URL}/get_mach5_main_state",
|
| 57 |
json={"session_id": session_id}, timeout=2)
|
| 58 |
response.raise_for_status()
|
| 59 |
loaded_state = response.json()
|
| 60 |
-
mach5_state.update(loaded_state)
|
| 61 |
print(f"DEBUG: Estado da Mach5 para sessão {session_id} carregado do t_cerebro_memoria.py.")
|
| 62 |
except requests.exceptions.RequestException as e:
|
| 63 |
print(f"ERRO: Não foi possível carregar o estado da Mach5 para sessão {session_id} do t_cerebro_memoria.py: {e}")
|
| 64 |
print("INFO: Usando defaults internos para o estado da Mach5.")
|
| 65 |
-
mach5_state = {
|
| 66 |
"t_genoma_fixo": MACH5_T_GENOMA_FIXO_DEFAULTS,
|
| 67 |
"t_fisica_params": MACH5_T_FISICA_PARAMS_DEFAULTS,
|
| 68 |
-
"historico_interacoes": [],
|
| 69 |
"coerencia_total": 0.5,
|
| 70 |
"produtividade_expressiva": 0.5
|
| 71 |
}
|
| 72 |
|
| 73 |
def save_mach5_state_to_cerebro(session_id):
|
|
|
|
|
|
|
|
|
|
| 74 |
try:
|
|
|
|
| 75 |
state_to_save = {
|
| 76 |
"t_genoma_fixo": mach5_state["t_genoma_fixo"],
|
| 77 |
"t_fisica_params": mach5_state["t_fisica_params"],
|
|
@@ -85,13 +117,6 @@ def save_mach5_state_to_cerebro(session_id):
|
|
| 85 |
except requests.exceptions.RequestException as e:
|
| 86 |
print(f"ERRO: Não foi possível salvar o estado da Mach5 para sessão {session_id} no t_cerebro_memoria.py: {e}")
|
| 87 |
|
| 88 |
-
def calculate_t_ressonancia(dialogos_history):
|
| 89 |
-
if not dialogos_history or "dialogos" not in dialogos_history or len(dialogos_history["dialogos"]) < 2:
|
| 90 |
-
return 0.0
|
| 91 |
-
last_input = dialogos_history["dialogos"][-1]["input"].lower()
|
| 92 |
-
prev_input = dialogos_history["dialogos"][-2]["input"].lower()
|
| 93 |
-
similarity = fuzz.partial_ratio(last_input, prev_input) / 100.0
|
| 94 |
-
return max(0.0, min(1.0, similarity * 0.8))
|
| 95 |
|
| 96 |
def calculate_coherence_and_productivity(user_input_sentiment_type):
|
| 97 |
global mach5_state
|
|
@@ -99,30 +124,38 @@ def calculate_coherence_and_productivity(user_input_sentiment_type):
|
|
| 99 |
current_params = mach5_state["t_fisica_params"]
|
| 100 |
current_genoma = mach5_state["t_genoma_fixo"]
|
| 101 |
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
| 103 |
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
|
| 104 |
-
coh_adj_genoma = (genoma_intensity - 0.5) * 0.1
|
|
|
|
|
|
|
| 105 |
fisica_coherence_factors = (
|
| 106 |
-
(1 - abs(current_params.get("t_impulsividade", 0.5) - 0.5)) * 0.2 +
|
| 107 |
(1 - abs(current_params.get("t_variancia", 0.5) - 0.5)) * 0.2 +
|
| 108 |
current_params.get("t_coesao", 0.5) * 0.3 +
|
| 109 |
-
(1 - current_params.get("t_separacao", 0.5)) * 0.1
|
| 110 |
)
|
| 111 |
coh_adj_fisica = (fisica_coherence_factors - 0.5) * 0.2
|
| 112 |
|
|
|
|
| 113 |
sentiment_adj = 0
|
|
|
|
| 114 |
if user_input_sentiment_type == 'negative':
|
| 115 |
-
sentiment_adj = -0.15 * current_genoma.get("B-Si", 0.5)
|
| 116 |
-
current_params["t_intensidade"] = min(1.0, current_params["t_intensidade"] + 0.1 * current_genoma.get("B-Sal", 0.5))
|
| 117 |
-
current_params["t_impulsividade"] = min(1.0, current_params["t_impulsividade"] + 0.1 * current_genoma.get("B-Tv1", 0.5))
|
| 118 |
-
current_params["t_separacao"] = min(1.0, current_params["t_separacao"] + 0.1 * (1 - current_genoma.get("B-Am1", 0.5)))
|
| 119 |
current_params["op_tdo"] = min(1.0, current_params["op_tdo"] + 0.1 * current_genoma.get("B-Tv1", 0.5))
|
| 120 |
-
current_params["op_trs"] = min(1.0, current_params["op_trs"] + 0.1 * (1 - current_genoma.get("B-L1", 0.5)))
|
| 121 |
current_params["t_coesao"] = max(0.0, current_params["t_coesao"] - 0.1 * current_genoma.get("B-Si", 0.5))
|
| 122 |
current_params["op_tam"] = max(0.0, current_params["op_tam"] - 0.1 * (1 - current_genoma.get("B-Am1", 0.5)))
|
| 123 |
-
|
|
|
|
| 124 |
elif user_input_sentiment_type == 'positive':
|
| 125 |
-
sentiment_adj = 0.05 * current_genoma.get("B-E1", 0.5)
|
| 126 |
current_params["t_intensidade"] = max(0.0, current_params["t_intensidade"] - 0.05 * (1 - current_genoma.get("B-Sal", 0.5)))
|
| 127 |
current_params["t_impulsividade"] = max(0.0, current_params["t_impulsividade"] - 0.05 * (1 - current_genoma.get("B-Tv1", 0.5)))
|
| 128 |
current_params["t_separacao"] = max(0.0, current_params["t_separacao"] - 0.05 * current_genoma.get("B-Am1", 0.5))
|
|
@@ -130,38 +163,46 @@ def calculate_coherence_and_productivity(user_input_sentiment_type):
|
|
| 130 |
current_params["op_trs"] = max(0.0, current_params["op_trs"] - 0.05 * current_genoma.get("B-L1", 0.5))
|
| 131 |
current_params["t_coesao"] = min(1.0, current_params["t_coesao"] + 0.05 * current_genoma.get("B-Com1", 0.5))
|
| 132 |
current_params["op_tam"] = min(1.0, current_params["op_tam"] + 0.05 * current_genoma.get("B-Am1", 0.5))
|
| 133 |
-
|
| 134 |
-
|
|
|
|
| 135 |
sentiment_adj = 0
|
|
|
|
| 136 |
for param, default_val in MACH5_T_FISICA_PARAMS_DEFAULTS.items():
|
| 137 |
-
|
|
|
|
| 138 |
|
| 139 |
new_coherence_total = coh_base + coh_adj_genoma + coh_adj_fisica + sentiment_adj
|
| 140 |
-
new_coherence_total = max(0.0, min(1.0, new_coherence_total))
|
| 141 |
|
|
|
|
| 142 |
prod_base = 0.5
|
| 143 |
-
prod_adj_genoma = (genoma_intensity - 0.5) * 0.2
|
|
|
|
| 144 |
if user_input_sentiment_type == 'negative':
|
| 145 |
-
prod_adj_sentiment = 0.1 * current_genoma.get("B-Tv1", 0.5)
|
| 146 |
elif user_input_sentiment_type == 'positive':
|
| 147 |
-
prod_adj_sentiment = -0.05 * current_genoma.get("B-Am1", 0.5)
|
| 148 |
else:
|
| 149 |
prod_adj_sentiment = 0
|
| 150 |
|
|
|
|
| 151 |
new_produtividade_expressiva = prod_base + prod_adj_genoma + prod_adj_sentiment
|
| 152 |
-
new_produtividade_expressiva = max(0.0, min(1.0, new_produtividade_expressiva))
|
| 153 |
|
| 154 |
mach5_state["coerencia_total"] = new_coherence_total
|
| 155 |
mach5_state["produtividade_expressiva"] = new_produtividade_expressiva
|
| 156 |
-
mach5_state["t_fisica_params"] = current_params
|
| 157 |
|
| 158 |
def infer_sentiment_from_input(user_input):
|
| 159 |
user_input_lower = user_input.lower()
|
|
|
|
|
|
|
| 160 |
positive_matches = sum(1 for keyword in POSITIVE_KEYWORDS if fuzz.partial_ratio(keyword, user_input_lower) > 80)
|
| 161 |
negative_matches = sum(1 for keyword in NEGATIVE_KEYWORDS if fuzz.partial_ratio(keyword, user_input_lower) > 80)
|
| 162 |
question_matches = sum(1 for keyword in QUESTION_KEYWORDS if keyword in user_input_lower)
|
| 163 |
|
| 164 |
-
if negative_matches > positive_matches * 1.5:
|
| 165 |
return 'negative'
|
| 166 |
elif positive_matches > negative_matches * 1.5:
|
| 167 |
return 'positive'
|
|
@@ -171,16 +212,18 @@ def infer_sentiment_from_input(user_input):
|
|
| 171 |
return 'neutral'
|
| 172 |
|
| 173 |
def calculate_fphen_values(params, genoma_fixo):
|
|
|
|
| 174 |
full_genoma_for_calc = CANONICAL_T_BASES.copy()
|
| 175 |
full_genoma_for_calc.update(genoma_fixo)
|
| 176 |
|
|
|
|
| 177 |
sensibilidade_positiva = (
|
| 178 |
full_genoma_for_calc.get("B-Aj1", 0.5) * 0.25 +
|
| 179 |
full_genoma_for_calc.get("B-Am1", 0.5) * 0.25 +
|
| 180 |
full_genoma_for_calc.get("B-L1", 0.5) * 0.25 +
|
| 181 |
full_genoma_for_calc.get("B-E1", 0.5) * 0.25
|
| 182 |
)
|
| 183 |
-
sensibilidade_positiva = max(0.1, min(1.0, sensibilidade_positiva))
|
| 184 |
|
| 185 |
sensibilidade_negativa = (
|
| 186 |
full_genoma_for_calc.get("B-Si", 0.5) * 0.25 +
|
|
@@ -198,6 +241,7 @@ def calculate_fphen_values(params, genoma_fixo):
|
|
| 198 |
)
|
| 199 |
sensibilidade_neutra = max(0.1, min(1.0, sensibilidade_neutra))
|
| 200 |
|
|
|
|
| 201 |
phenotype_components = {
|
| 202 |
"Afetuosidade_eixo": (
|
| 203 |
params.get("env_mu", 0.5) * 0.3 + params.get("t_coesao", 0.5) * 0.2 +
|
|
@@ -272,70 +316,85 @@ def calculate_fphen_values(params, genoma_fixo):
|
|
| 272 |
params.get("env_theta", 0.5) * 0.3 + full_genoma_for_calc.get("B-M1", 0.5) * 0.2 +
|
| 273 |
(1 - params.get("t_intensidade", 0.5)) * 0.15 + full_genoma_for_calc.get("B-Si", 0.5) * 0.15
|
| 274 |
) * sensibilidade_neutra,
|
| 275 |
-
|
|
|
|
| 276 |
"Variancia_eixo": (
|
| 277 |
-
params.get("t_variancia", 0.5) * 0.4 +
|
| 278 |
-
full_genoma_for_calc.get("B-Tv1", 0.5) * 0.3 +
|
| 279 |
-
abs(params.get("t_impulsividade", 0.5) - 0.5) * 0.2 +
|
| 280 |
-
(1 - params.get("t_coesao", 0.5)) * 0.1
|
| 281 |
-
) * sensibilidade_neutra
|
| 282 |
}
|
| 283 |
|
|
|
|
| 284 |
for key in phenotype_components:
|
| 285 |
phenotype_components[key] = max(0, phenotype_components[key])
|
| 286 |
|
|
|
|
| 287 |
total_score = sum(phenotype_components.values())
|
| 288 |
if total_score > 0:
|
| 289 |
for key in phenotype_components:
|
| 290 |
phenotype_components[key] /= total_score
|
| 291 |
-
else:
|
| 292 |
for key in phenotype_components:
|
| 293 |
phenotype_components[key] = 1.0 / len(ORDERED_FPHEN_AXES)
|
| 294 |
|
| 295 |
fphen_values = [phenotype_components.get(key, 0.0) for key in ORDERED_FPHEN_AXES]
|
| 296 |
return fphen_values
|
| 297 |
|
|
|
|
| 298 |
@app.route('/evaluate_input', methods=['POST'])
|
| 299 |
def evaluate_input():
|
| 300 |
data = request.json
|
| 301 |
user_input = data.get("user_input", "")
|
| 302 |
-
session_id = data.get("session_id")
|
| 303 |
|
| 304 |
if not session_id:
|
| 305 |
return jsonify({"error": "session_id é obrigatório"}), 400
|
| 306 |
|
|
|
|
| 307 |
load_mach5_state_from_cerebro(session_id)
|
| 308 |
|
| 309 |
user_input_sentiment_type = infer_sentiment_from_input(user_input)
|
|
|
|
| 310 |
calculate_coherence_and_productivity(user_input_sentiment_type)
|
| 311 |
|
| 312 |
-
dialogos_history = get_from_service(TCEREBRO_MEMORIA_URLS, "/get_dialog_history", {"dialogos": []}, method='POST', json_data={"session_id": session_id})
|
| 313 |
-
t_ressonancia = calculate_t_ressonancia(dialogos_history)
|
| 314 |
fphen_values = calculate_fphen_values(mach5_state["t_fisica_params"], mach5_state["t_genoma_fixo"])
|
| 315 |
|
| 316 |
-
|
|
|
|
|
|
|
|
|
|
| 317 |
|
|
|
|
|
|
|
| 318 |
return jsonify({
|
| 319 |
"mach5_fisica_params": mach5_state["t_fisica_params"],
|
| 320 |
"mach5_genoma_fixo_values": mach5_state["t_genoma_fixo"],
|
| 321 |
"mach5_coerencia_total": mach5_state["coerencia_total"],
|
| 322 |
"mach5_produtividade_expressiva": mach5_state["produtividade_expressiva"],
|
| 323 |
-
|
|
|
|
| 324 |
"afetuosidade": fphen_values[ORDERED_FPHEN_AXES.index("Afetuosidade_eixo")],
|
| 325 |
-
|
| 326 |
-
"
|
|
|
|
| 327 |
"coh_total": mach5_state["coerencia_total"],
|
| 328 |
-
"pi_G": mach5_state["produtividade_expressiva"]
|
| 329 |
-
"t_ressonancia": t_ressonancia
|
| 330 |
}
|
| 331 |
})
|
| 332 |
|
| 333 |
-
@app.route('/get_mach5_state', methods=['POST'])
|
| 334 |
def get_mach5_state_route():
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
session_id = request.json.get("session_id")
|
| 336 |
if not session_id:
|
| 337 |
return jsonify({"error": "session_id é obrigatório"}), 400
|
| 338 |
|
|
|
|
| 339 |
load_mach5_state_from_cerebro(session_id)
|
| 340 |
|
| 341 |
fphen_values = calculate_fphen_values(mach5_state["t_fisica_params"], mach5_state["t_genoma_fixo"])
|
|
@@ -344,9 +403,11 @@ def get_mach5_state_route():
|
|
| 344 |
"mach5_genoma_fixo_values": mach5_state["t_genoma_fixo"],
|
| 345 |
"mach5_coerencia_total": mach5_state["coerencia_total"],
|
| 346 |
"mach5_produtividade_expressiva": mach5_state["produtividade_expressiva"],
|
| 347 |
-
|
|
|
|
| 348 |
"afetuosidade": fphen_values[ORDERED_FPHEN_AXES.index("Afetuosidade_eixo")],
|
| 349 |
-
|
|
|
|
| 350 |
"expressividade": mach5_state["produtividade_expressiva"],
|
| 351 |
"coh_total": mach5_state["coerencia_total"],
|
| 352 |
"pi_G": mach5_state["produtividade_expressiva"]
|
|
@@ -354,6 +415,7 @@ def get_mach5_state_route():
|
|
| 354 |
})
|
| 355 |
|
| 356 |
if __name__ == '__main__':
|
| 357 |
-
|
|
|
|
| 358 |
print(f"--- Servidor t_memoria.py iniciado na porta {port} ---")
|
| 359 |
-
app.run(port=port, debug=True)
|
|
|
|
| 1 |
+
# t_memoria.py
|
| 2 |
from flask import Flask, request, jsonify
|
| 3 |
import json
|
| 4 |
import os
|
|
|
|
| 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
|
|
|
|
| 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"],
|
|
|
|
| 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
|
|
|
|
| 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))
|
|
|
|
| 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'
|
|
|
|
| 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 +
|
|
|
|
| 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 +
|
|
|
|
| 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"])
|
|
|
|
| 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"]
|
|
|
|
| 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)
|