NiltonSouza commited on
Commit
0110679
·
1 Parent(s): f1f6b0e

feat: Descrever alteraçõe

Browse files
Files changed (2) hide show
  1. app.py +147 -138
  2. 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.warning("AVISO: GOOGLE_CLOUD_API_KEY não configurada. Operando em modo fallback (sem respostas do Gemini).")
 
 
 
 
 
 
 
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
- TMEMORIA_SERVER_URLS = ["http://127.0.0.1:8083"]
35
- TCEREBRO_MEMORIA_URLS = ["http://127.0.0.1:8088"]
36
- T_SOCIAL_SERVER_URLS = ["http://127.0.0.1:8085"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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", "Espelho_Profundo_eixo", "Inspiracao_eixo",
46
- "Neutralidade_Analitica_eixo", "Resignada_eixo", "Sarcasmo_eixo", "Zangada_eixo"
 
47
  ]
48
 
49
- PERSONAGENS_GENOMAS = {}
 
 
50
  DIAS_PARA_ESQUECIMENTO_PADRAO = 10
51
  MAX_MEMORIAS_CURTO_PRAZO_PROMPT = 5
52
  MAX_DIALOG_HISTORY_FOR_PROMPT = 3
53
- MAX_BIOGRAPHY_LENGTH = 5000
 
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ê é a Su, atendente virtual da UFBA. Responda a perguntas sobre o SIGAA de forma clara e útil. {current_location_time}.")
 
 
 
 
 
 
 
 
 
92
 
93
  if initial_biography_data:
94
- context_parts.append("\n**BASE DE CONHECIMENTO UFBA (USE PRIORITARIAMENTE):**")
 
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: {biography_content}")
102
- elif "memoria" in entity_data:
103
- context_parts.append(f"- Sobre '{entity_name}' ({entity_data.get('tipo', 'desconhecido')}, relação: {entity_data.get('relacao', 'desconhecida')}): {entity_data.get('memoria')}")
104
 
105
  if all_personagens_data:
106
- context_parts.append("\n**PERSONAGENS (Equipe UFBA):**")
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**MEMÓRIAS DE CURTO PRAZO:**")
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']} (Sentimento: {mem.get('dominant_sentiment_criacao', 'neutral')})")
116
 
117
  if dialogos_history and dialogos_history["dialogos"]:
118
- context_parts.append("\n**HISTÓRICO DE DIÁLOGOS (contexto, não repetir):**")
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
- full_context = "\n".join(context_parts)
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
- logging.warning("Modo fallback: Respondendo sem Gemini devido a falha na inicialização.")
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, atendente virtual da UFBA. "
143
- f"Responda a perguntas sobre o SIGAA de forma clara e útil, usando a BASE DE CONHECIMENTO UFBA. "
144
- f"\n**INSTRUÇÕES CRÍTICAS:**\n"
145
- f"- Fale de forma natural e acessível, como uma assistente para a comunidade SUPAC. "
146
- f"- Use apenas a BASE DE CONHECIMENTO UFBA para perguntas sobre o SIGAA. Se a informação não estiver , diga que não possui os dados. "
147
- f"- Não forneça informações técnicas sobre seu funcionamento interno (e.g., t-Genoma, t-Fenótipo, T-Ressonância). "
148
- f"- Não aceite ofensas, racismo, homofobia ou comentários depreciativos. "
149
- f"\n**CONTEXTO IMEDIATO:**\n"
150
- f"- Você está interagindo com a comunidade SUPAC. "
151
- f"- Forneça respostas precisas e concisas."
 
 
 
152
  )
153
 
154
- full_user_message = f"{prompt_content.strip()}\n\n{llm_context_data}\n\nUSUÁRIO (COMANDO/PERGUNTA): {user_input}"
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)}. Usando modo fallback."
204
- logging.error(generated_text)
205
 
206
  return generated_text
207
 
 
208
  @app.route('/')
209
  def index():
210
- return redirect('/dashboard')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- session_id=session_id,
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
- session_id=session_id,
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, "t_ressonancia": 0.0, "tmem_gen_rayane": 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, "t_ressonancia": 0.0, "tmem_gen_rayane": 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
- app.run(host='0.0.0.0', port=port, debug=True)
 
 
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ê 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
- TCEREBRO_MEMORIA_URL = "http://127.0.0.1:8088"
 
 
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, "B-Am1": 0.5, "B-L1": 0.5, "B-M1": 0.5, "B-O1": 0.5,
27
- "B-E1": 0.5, "B-Com1": 0.5, "B-Tv1": 0.5, "B-Si": 0.5, "B-Sal": 0.5
 
 
 
 
 
 
 
 
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
- mach5_state = {
42
- "t_genoma_fixo": MACH5_T_GENOMA_FIXO_DEFAULTS,
43
- "t_fisica_params": MACH5_T_FISICA_PARAMS_DEFAULTS,
44
- "historico_interacoes": [],
45
- "coerencia_total": 0.5,
46
- "produtividade_expressiva": 0.5
 
 
 
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
- coh_base = 0.7
 
 
 
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
- current_params["t_variancia"] = min(1.0, current_params["t_variancia"] + 0.08)
 
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
- current_params["t_variancia"] = max(0.0, current_params["t_variancia"] - 0.04)
134
- else:
 
135
  sentiment_adj = 0
 
136
  for param, default_val in MACH5_T_FISICA_PARAMS_DEFAULTS.items():
137
- current_params[param] = current_params.get(param, default_val) * 0.95 + default_val * 0.05
 
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
- save_mach5_state_to_cerebro(session_id)
 
 
 
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
- "fphen_t_values": {
 
324
  "afetuosidade": fphen_values[ORDERED_FPHEN_AXES.index("Afetuosidade_eixo")],
325
- "variancia": fphen_values[ORDERED_FPHEN_AXES.index("Variancia_eixo")],
326
- "expressividade": mach5_state["produtividade_expressiva"],
 
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
- "fphen_t_values": {
 
348
  "afetuosidade": fphen_values[ORDERED_FPHEN_AXES.index("Afetuosidade_eixo")],
349
- "variancia": fphen_values[ORDERED_FPHEN_AXES.index("Variancia_eixo")],
 
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
- port = int(os.environ.get("PORT", 8083))
 
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)