Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
# Protocolo Nexus Cognitivo - LITE (Obsidiana ES)
|
| 3 |
-
# Versión: 4.1.0-Lite-
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
import random
|
|
@@ -31,7 +31,7 @@ except ImportError:
|
|
| 31 |
NUMPY_AVAILABLE = False
|
| 32 |
|
| 33 |
# --- LITE Text Dictionary (Embedded) ---
|
| 34 |
-
# (TEXTOS_LITE dictionary remains the same
|
| 35 |
TEXTOS_LITE = {
|
| 36 |
"app_title": "Protocolo Nexus Cognitivo LITE",
|
| 37 |
"app_version": "4.1.0-Lite ES",
|
|
@@ -95,12 +95,12 @@ TEXTOS_LITE = {
|
|
| 95 |
"results_level_max_advance": "<h5><strong>Sintonía Pico Nivel {level}.</strong> Calibración adicional recomendada.</h5>",
|
| 96 |
"results_level_regress": "<h5><strong>Disonancia Detectada.</strong> Recalibrando a Nivel {new_level}.</h5>",
|
| 97 |
"results_level_maintain": "<h5>Resonancia Nivel {level} mantenida. (Req: >{threshold}% Alin., <{consistency_threshold:.3f} CV)</h5>",
|
| 98 |
-
"results_analysis_title": "Análisis Básico",
|
| 99 |
"results_analysis_generating": "<p>Calculando...</p>",
|
| 100 |
"results_analysis_error": "<p style='color:var(--color-error);'>Error análisis.</p>",
|
| 101 |
"results_analysis_missing": "<p>Datos no disponibles.</p>",
|
| 102 |
-
"results_analysis_summary": "Módulo {test_name}: Alineación {precision:.1f}%. {rt_info}",
|
| 103 |
-
"results_rt_summary": "TR Medio: {avg_rt:.3f}s (CV: {rt_cv:.3f})",
|
| 104 |
"results_info": "<p class='info-text'>Eco archivado. Estado flujo actualizado. Retornando.</p>",
|
| 105 |
"results_back_button": "Retornar",
|
| 106 |
# Popups / Info / Warnings / Errors
|
|
@@ -126,7 +126,7 @@ TEXTOS_LITE = {
|
|
| 126 |
"error_format_key_missing": "Error Txt: Falta '{missing}' en '{key}'.",
|
| 127 |
"error_format_general": "Error Txt: {error} en '{key}'.",
|
| 128 |
"error_trial_flow": "ERROR CRÍTICO durante flujo de prueba: {error}",
|
| 129 |
-
"error_finish_test_state": "ERROR: Estado inválido al finalizar test.",
|
| 130 |
# Logging
|
| 131 |
"log_init_start": "Iniciando Nexus LITE v{version}...",
|
| 132 |
"log_init_results": "Archivo de ecos: {file}",
|
|
@@ -155,7 +155,7 @@ TEXTOS_LITE = {
|
|
| 155 |
"log_test_transition": "Transición a: {test_name}",
|
| 156 |
"log_test_next_instr": "Mostrando instrucciones para {test_name}",
|
| 157 |
"log_seq_complete": "Calibración Completa. Calculando resultados...",
|
| 158 |
-
"log_level_change": "Evaluación Nivel: Precision={acc:.1f}, Consist={cons:.3f}. Nuevo Nivel: {level}",
|
| 159 |
"log_saving_results": "Archivando eco: Alias={alias}, LvlComp={level_completed}, NewLvl={new_level}",
|
| 160 |
"log_save_results_success": "Eco archivado.",
|
| 161 |
"log_save_profile": "Perfil guardado: {alias}, Nivel: {profile_level}",
|
|
@@ -279,105 +279,12 @@ def _safe_append_csv(row_dict: dict, fieldnames: list, filepath: Path):
|
|
| 279 |
return False
|
| 280 |
|
| 281 |
# --- CSS ---
|
| 282 |
-
# Assume full obsidian_css string
|
| 283 |
obsidian_css = """
|
| 284 |
-
:root {
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
--color-background-dark: #101015; --color-surface-dark: #1c1c2e; --color-border-dark: #3b3e58; /* Slightly darker */
|
| 289 |
-
--color-text-primary-dark: #d5d5e0; --color-text-secondary-dark: #9799ae; --color-text-subdued-dark: #626587; /* Adjusted text colors */
|
| 290 |
-
--color-primary-dark: #8f6aff; --color-primary-hover-dark: #a786ff; /* Slightly different purple */
|
| 291 |
-
--color-accent-dark: #00cf98; --color-accent-hover-dark: #33ffc0; /* More vibrant accent */
|
| 292 |
-
--color-warning-dark: #ffcc00; --color-error-dark: #ff3b5f; --color-error-hover-dark: #ff6a89; /* Brighter warning/error */
|
| 293 |
-
--shadow-sm-dark: 0 1px 4px rgba(0, 0, 0, 0.4); --shadow-md-dark: 0 5px 12px rgba(0, 0, 0, 0.5), 0 1px 3px rgba(0, 0, 0, 0.4); /* Enhanced shadows */
|
| 294 |
-
--code-bg-dark: #2a2a40; --spinner-color: var(--color-primary-dark);
|
| 295 |
-
|
| 296 |
-
/* Nebula Theme (Light Alternative - Kept for toggle functionality) */
|
| 297 |
-
--color-background-light: #f0f0f5; --color-surface-light: #ffffff; --color-border-light: #c8c8d8; /* Cooler light tones */
|
| 298 |
-
--color-text-primary-light: #202035; --color-text-secondary-light: #505065; --color-text-subdued-light: #7a7a8f;
|
| 299 |
-
--color-primary-light: #704ff0; --color-primary-hover-light: #8f6aff;
|
| 300 |
-
--color-accent-light: #00b383; --color-accent-hover-light: #00cf98;
|
| 301 |
-
--color-warning-light: #f0b400; --color-error-light: #e52a4b; --color-error-hover-light: #ff3b5f;
|
| 302 |
-
--shadow-sm-light: 0 1px 3px rgba(30, 30, 60, 0.09); --shadow-md-light: 0 4px 9px rgba(30, 30, 60, 0.12), 0 1px 2px rgba(30, 30, 60, 0.09);
|
| 303 |
-
--code-bg-light: #e4e4ec;
|
| 304 |
-
|
| 305 |
-
/* Default to Obsidian */
|
| 306 |
-
--color-background: var(--color-background-dark); --color-surface: var(--color-surface-dark); --color-border: var(--color-border-dark);
|
| 307 |
-
--color-text-primary: var(--color-text-primary-dark); --color-text-secondary: var(--color-text-secondary-dark); --color-text-subdued: var(--color-text-subdued-dark);
|
| 308 |
-
--color-primary: var(--color-primary-dark); --color-primary-hover: var(--color-primary-hover-dark);
|
| 309 |
-
--color-accent: var(--color-accent-dark); --color-accent-hover: var(--color-accent-hover-dark);
|
| 310 |
-
--color-warning: var(--color-warning-dark); --color-error: var(--color-error-dark); --color-error-hover: var(--color-error-hover-dark);
|
| 311 |
-
--shadow-sm: var(--shadow-sm-dark); --shadow-md: var(--shadow-md-dark); --code-bg: var(--code-bg-dark);
|
| 312 |
-
--border-radius: 6px; --transition-speed: 0.25s;
|
| 313 |
-
}
|
| 314 |
-
body.light-theme {
|
| 315 |
-
--color-background: var(--color-background-light); --color-surface: var(--color-surface-light); --color-border: var(--color-border-light);
|
| 316 |
-
--color-text-primary: var(--color-text-primary-light); --color-text-secondary: var(--color-text-secondary-light); --color-text-subdued: var(--color-text-subdued-light);
|
| 317 |
-
--color-primary: var(--color-primary-light); --color-primary-hover: var(--color-primary-hover-light);
|
| 318 |
-
--color-accent: var(--color-accent-light); --color-accent-hover: var(--color-accent-hover-light);
|
| 319 |
-
--color-warning: var(--color-warning-light); --color-error: var(--color-error-light); --color-error-hover: var(--color-error-hover-light);
|
| 320 |
-
--shadow-sm: var(--shadow-sm-light); --shadow-md: var(--shadow-md-light); --code-bg: var(--code-bg-light);
|
| 321 |
-
--spinner-color: var(--color-primary-light);
|
| 322 |
-
}
|
| 323 |
-
body { font-family: var(--font-main); background-color: var(--color-background); color: var(--color-text-primary); line-height: 1.65; transition: background-color var(--transition-speed) ease, color var(--transition-speed) ease; }
|
| 324 |
-
.gradio-container { max-width: 900px !important; margin: 2.5rem auto; background-color: transparent; box-shadow: none; border: none; padding: 0; }
|
| 325 |
-
.main-content-box { background-color: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--border-radius); padding: 2.5rem; margin-bottom: 2rem; box-shadow: var(--shadow-md); transition: background-color var(--transition-speed) ease, border-color var(--transition-speed) ease, box-shadow var(--transition-speed) ease; animation: fadeIn 0.5s ease-out; }
|
| 326 |
-
.block-title { color: var(--color-text-primary); text-align: center; font-size: 2.2em; font-weight: 600; margin-bottom: 1.2rem; padding-bottom: 0.8rem; border-bottom: 1px solid var(--color-primary); transition: color var(--transition-speed) ease, border-color var(--transition-speed) ease; letter-spacing: 0.5px; text-shadow: 0 0 5px color-mix(in srgb, var(--color-primary) 20%, transparent); }
|
| 327 |
-
.block-subtitle { color: var(--color-text-secondary); text-align: center; font-size: 1.3em; margin-bottom: 1.5rem; font-weight: 500; transition: color var(--transition-speed) ease; }
|
| 328 |
-
.gr-button { font-weight: 500; border-radius: var(--border-radius); padding: 0.8rem 1.6rem; transition: all var(--transition-speed) ease-in-out; box-shadow: var(--shadow-sm); border: 1px solid transparent; font-size: 1.0em; letter-spacing: 0.3px; cursor: pointer; }
|
| 329 |
-
.gr-button:hover { transform: translateY(-2px); }
|
| 330 |
-
.gr-button.gr-button-primary { background-color: var(--color-primary) !important; border-color: var(--color-primary) !important; color: #ffffff !important; text-shadow: 0 1px 2px rgba(0,0,0,0.3); }
|
| 331 |
-
.gr-button.gr-button-primary:hover { background-color: var(--color-primary-hover) !important; border-color: var(--color-primary-hover) !important; box-shadow: var(--shadow-md), 0 0 12px color-mix(in srgb, var(--color-primary) 45%, transparent); }
|
| 332 |
-
.gr-button.gr-button-secondary { background-color: var(--color-surface) !important; border-color: var(--color-border) !important; color: var(--color-text-secondary) !important; }
|
| 333 |
-
.gr-button.gr-button-secondary:hover { border-color: var(--color-primary) !important; color: var(--color-primary) !important; background-color: color-mix(in srgb, var(--color-primary) 10%, var(--color-surface)) !important; }
|
| 334 |
-
.btn-menu { display: block !important; width: 95% !important; margin: 0.9rem auto !important; text-align: left; font-size: 1.1em; padding: 0.9rem 1.8rem !important; }
|
| 335 |
-
.btn-reset { background-color: var(--color-error) !important; border-color: var(--color-error) !important; color: #ffffff !important; }
|
| 336 |
-
.btn-reset:hover { background-color: var(--color-error-hover) !important; border-color: var(--color-error-hover) !important; box-shadow: var(--shadow-md), 0 0 10px color-mix(in srgb, var(--color-error) 40%, transparent); }
|
| 337 |
-
#alias-input-box textarea { font-size: 1.15em; border: 1px solid var(--color-border); border-radius: var(--border-radius); background-color: var(--color-background); color: var(--color-text-primary); transition: all var(--transition-speed) ease; padding: 0.7rem 1rem; }
|
| 338 |
-
#alias-input-box textarea:focus { border-color: var(--color-primary); background-color: var(--color-surface); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 30%, transparent); }
|
| 339 |
-
#alias_feedback, #agent-info-menu { text-align: center; color: var(--color-text-secondary); margin-top: 1rem; min-height: 1.8em; font-size: 1.0em; transition: color var(--transition-speed) ease; }
|
| 340 |
-
#agent-info-menu strong { color: var(--color-primary); font-weight: 600; }
|
| 341 |
-
.info-text { color: var(--color-text-subdued); font-size: 0.95em; text-align: center; margin-top: 1.5rem; transition: color var(--transition-speed) ease; }
|
| 342 |
-
.disclaimer-box { border: 1px solid var(--color-warning); border-left: 6px solid var(--color-warning); padding: 1.5rem; margin: 2rem 0; background-color: color-mix(in srgb, var(--color-warning) 12%, var(--color-surface)); border-radius: var(--border-radius); transition: background-color var(--transition-speed) ease, border-color var(--transition-speed) ease; box-shadow: inset 0 0 10px rgba(0,0,0,0.2); }
|
| 343 |
-
.disclaimer-box p { margin: 0.5rem 0; font-size: 1.0em; color: var(--color-text-secondary); transition: color var(--transition-speed) ease;}
|
| 344 |
-
.disclaimer-box strong { color: color-mix(in srgb, var(--color-warning) 85%, var(--color-text-primary)); font-weight: 600; transition: color var(--transition-speed) ease;}
|
| 345 |
-
#instr-text { color: var(--color-text-secondary); line-height: 1.8; font-size: 1.1em; margin-top: 1.2rem; transition: color var(--transition-speed) ease; }
|
| 346 |
-
#instr-text strong { color: var(--color-text-primary); font-weight: 600; }
|
| 347 |
-
#instr-text code { background-color: var(--code-bg); padding: 4px 8px; border: 1px solid var(--color-border); color: var(--color-primary); font-family: var(--font-monospace); font-weight: 600; font-size: 0.95em; border-radius: 4px; transition: all var(--transition-speed) ease; margin: 0 2px; box-shadow: inset 0 0 3px rgba(0,0,0,0.2); }
|
| 348 |
-
#instr-text h5 { color: var(--color-text-primary); font-size: 1.4em; margin-bottom: 1rem; font-weight: 600; }
|
| 349 |
-
#test-block { text-align: center; position: relative; }
|
| 350 |
-
#progress-indicator, #score-display, #timer-display { color: var(--color-text-secondary); font-size: 1.1em; margin-bottom: 0.8rem; min-height: 1.4em; transition: color var(--transition-speed) ease; font-family: var(--font-monospace); }
|
| 351 |
-
#score-display { font-weight: 500; letter-spacing: 1px; }
|
| 352 |
-
#timer-display { font-weight: 600; color: var(--color-primary); text-shadow: 0 0 4px color-mix(in srgb, var(--color-primary) 30%, transparent); }
|
| 353 |
-
#stimulus-display p.stimulus-core { font-size: 7em; font-weight: 500; text-align: center; margin: 2.5rem 0; min-height: 1.8em; line-height: 1.1; color: var(--color-text-primary); transition: color 0.1s ease-in-out, text-shadow 0.1s ease-in-out, opacity 0.2s ease-out; font-family: var(--font-monospace); letter-spacing: 0.1em; animation: stimulusAppear 0.3s ease-out; text-shadow: 0 0 8px color-mix(in srgb, var(--color-text-primary) 15%, transparent); }
|
| 354 |
-
#stimulus-display p.stimulus-suppressor { font-size: 5.5em; color: var(--color-text-subdued); opacity: 0.5; font-style: italic; }
|
| 355 |
-
#feedback-display { text-align: center; min-height: 2.5em; margin-top: 1.5rem; font-size: 1.15em; animation: feedbackFadeIn 0.3s ease-out; }
|
| 356 |
-
.feedback-subtle { color: var(--color-text-subdued); font-size: 0.9em; margin-left: 0.8rem; font-style: italic; transition: color var(--transition-speed) ease; display: inline-block; }
|
| 357 |
-
.btn-response { font-size: 1.3em !important; font-weight: 600 !important; padding: 1.1rem 0.6rem !important; margin: 6px !important; min-width: 90px !important; background-color: var(--color-surface) !important; border: 2px solid var(--color-border) !important; color: var(--color-text-primary) !important; transition: all 0.15s ease; font-family: var(--font-monospace); letter-spacing: 1px; word-wrap: break-word; white-space: normal; line-height: 1.3; text-shadow: 0 1px 1px rgba(0,0,0,0.3);}
|
| 358 |
-
.btn-response:hover { border-color: var(--color-primary) !important; background-color: color-mix(in srgb, var(--color-primary) 15%, var(--color-surface)) !important; transform: scale(1.05); box-shadow: var(--shadow-sm); color: var(--color-primary) !important; }
|
| 359 |
-
#results-summary h5, #results-level h5, .cognitive-echo-container h5 { color: var(--color-primary); font-weight: 600; font-size: 1.3em; text-align: center; margin-top: 1.2rem; margin-bottom: 0.8rem; padding-bottom: 0.4rem; border-bottom: 1px solid var(--color-border); transition: color var(--transition-speed) ease, border-color var(--transition-speed) ease;}
|
| 360 |
-
#results-summary ul.results-list { list-style: none; padding: 0; margin: 1rem 0 1.8rem 0; }
|
| 361 |
-
#results-summary li { background-color: var(--color-surface); border-left: 6px solid var(--color-primary); margin: 0.7rem 0; padding: 0.9rem 1.4rem; color: var(--color-text-secondary); font-size: 1.1em; border-radius: 4px; transition: all var(--transition-speed) ease; box-shadow: var(--shadow-sm); }
|
| 362 |
-
#results-summary li:hover { transform: translateX(5px); border-left-color: var(--color-accent); background-color: color-mix(in srgb, var(--color-accent) 8%, var(--color-surface)); }
|
| 363 |
-
#results-summary strong { color: var(--color-text-primary); font-weight: 600; }
|
| 364 |
-
#results-level h5 { background-color: color-mix(in srgb, var(--color-primary) 10%, var(--color-surface)); border: 1px solid var(--color-primary); color: var(--color-text-primary); padding: 1rem; border-radius: var(--border-radius); box-shadow: var(--shadow-sm); }
|
| 365 |
-
#results_analysis_content { padding: 1.5rem; background-color: var(--color-surface) !important; border-top: none; transition: background-color var(--transition-speed) ease; }
|
| 366 |
-
#results_analysis_content h5 { color: var(--color-primary); font-size: 1.2em; margin-top: 1.5rem; margin-bottom: 0.8rem; font-weight: 600; padding-bottom: 4px; border-bottom: 1px dashed var(--color-border); }
|
| 367 |
-
#results_analysis_content p { margin-bottom: 1rem; color: var(--color-text-secondary); font-size: 1.0em; line-height: 1.7; transition: color var(--transition-speed) ease; }
|
| 368 |
-
.history-table-container { max-height: 500px; overflow-y: auto; border: 1px solid var(--color-border); margin-top: 1.5rem; background-color: var(--color-surface); border-radius: var(--border-radius); box-shadow: var(--shadow-sm); transition: all var(--transition-speed) ease; }
|
| 369 |
-
#history-html table { width: 100%; border-collapse: collapse; margin-top: 0; }
|
| 370 |
-
#history-html th { border: 1px solid var(--color-border); padding: 10px 14px; background-color: var(--color-background); color: var(--color-text-primary); position: sticky; top: 0; text-align: left; font-weight: 600; transition: all var(--transition-speed) ease; z-index: 1;}
|
| 371 |
-
#history-html td { border: 1px solid var(--color-border); padding: 10px 14px; text-align: left; color: var(--color-text-secondary); transition: all var(--transition-speed) ease; }
|
| 372 |
-
#history-html tr:nth-child(even) { background-color: color-mix(in srgb, var(--color-background) 50%, var(--color-surface)); transition: background-color var(--transition-speed) ease; }
|
| 373 |
-
#history-html tr:hover td { background-color: color-mix(in srgb, var(--color-primary) 10%, var(--color-surface)); color: var(--color-text-primary); }
|
| 374 |
-
.spinner { width: 40px; height: 40px; border: 4px solid color-mix(in srgb, var(--spinner-color) 30%, transparent); border-top-color: var(--spinner-color); border-radius: 50%; animation: spin 1s linear infinite; margin: 1rem auto; }
|
| 375 |
-
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
| 376 |
-
@keyframes stimulusAppear { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }
|
| 377 |
-
@keyframes feedbackFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
| 378 |
-
@keyframes spin { to { transform: rotate(360deg); } }
|
| 379 |
-
hr.section-hr { border: 0; height: 1px; background-image: linear-gradient(to right, transparent, var(--color-border), transparent); margin: 2.5rem 0; }
|
| 380 |
-
hr.analysis-hr { border: 0; height: 1px; background-color: var(--color-border); margin: 1.8rem 0; }
|
| 381 |
footer { display: none !important; }
|
| 382 |
"""
|
| 383 |
|
|
@@ -404,8 +311,8 @@ TEST_CONFIG_LITE = {
|
|
| 404 |
}
|
| 405 |
AVAILABLE_TEST_KEYS_LITE = list(TEST_CONFIG_LITE.keys())
|
| 406 |
|
| 407 |
-
# --- Profile/Results Handling
|
| 408 |
-
# (load_agent_profile, save_agent_profile, save_result, read_history functions
|
| 409 |
def load_agent_profile(alias):
|
| 410 |
if not alias: return 1
|
| 411 |
with profile_lock:
|
|
@@ -493,6 +400,7 @@ def read_history():
|
|
| 493 |
return html
|
| 494 |
|
| 495 |
# --- Difficulty Params (LITE) ---
|
|
|
|
| 496 |
def get_difficulty_params(test_key, level):
|
| 497 |
if test_key not in TEST_CONFIG_LITE: raise ValueError(f"Unknown test key: {test_key}")
|
| 498 |
config = TEST_CONFIG_LITE[test_key]
|
|
@@ -509,7 +417,7 @@ def get_difficulty_params(test_key, level):
|
|
| 509 |
return params
|
| 510 |
|
| 511 |
# --- Sequence Generation (LITE) ---
|
| 512 |
-
# (generate_attention_sequence, generate_inhibition_sequence, generate_memory_sequence functions
|
| 513 |
def generate_attention_sequence(params):
|
| 514 |
n = params['trials']; cue = params['cue']; target = params['target']
|
| 515 |
sim_dist = params['similar_distractors']; other_dist = params['other_distractors']
|
|
@@ -593,6 +501,7 @@ sequence_generators = {
|
|
| 593 |
}
|
| 594 |
|
| 595 |
# --- Instruction HTML Generation (LITE) ---
|
|
|
|
| 596 |
def get_instructions_html(test_key, params):
|
| 597 |
test_config = TEST_CONFIG_LITE.get(test_key)
|
| 598 |
if not test_config: return "<p>Error: Configuración Test No Encontrada.</p>"
|
|
@@ -619,12 +528,12 @@ def get_instructions_html(test_key, params):
|
|
| 619 |
lines.extend(common_lines)
|
| 620 |
return "".join(f"<p>{line}</p>" if not line.startswith("<br>") else line for line in lines)
|
| 621 |
|
| 622 |
-
# --- UI Formatting
|
| 623 |
-
# (format_stimulus_html and get_test_buttons_visibility_and_labels functions
|
| 624 |
def format_stimulus_html(state):
|
| 625 |
stim_raw = state.get("test_stimulus", ""); test_key = state.get("current_test_key", "")
|
| 626 |
params = state.get("test_params", {}); is_awaiting = state.get("awaiting_input", False)
|
| 627 |
-
if not is_awaiting or stim_raw is None or stim_raw == "": return "<p class='stimulus-core'>
|
| 628 |
display_text = ""; style_color = "var(--color-text-primary)"; extra_class = ""
|
| 629 |
try:
|
| 630 |
if test_key == "atencion": display_text = str(stim_raw)
|
|
@@ -643,7 +552,7 @@ def format_stimulus_html(state):
|
|
| 643 |
else: raise ValueError("Invalid mem stimulus")
|
| 644 |
else: display_text = str(stim_raw) # Fallback
|
| 645 |
display_text_safe = str(display_text).replace("&", "&").replace("<", "<").replace(">", ">")
|
| 646 |
-
if not display_text_safe or not display_text_safe.strip(): display_text_safe = "
|
| 647 |
return f"<p class='stimulus-core{extra_class}' style='color:{style_color};'>{display_text_safe}</p>"
|
| 648 |
except Exception as e:
|
| 649 |
log_message("error_stimulus_format", level="ERROR", error=e, traceback=traceback.format_exc())
|
|
@@ -678,7 +587,7 @@ def get_test_buttons_visibility_and_labels(state):
|
|
| 678 |
return final_updates, keys
|
| 679 |
|
| 680 |
# --- Response Processing (LITE) ---
|
| 681 |
-
# (process_response function
|
| 682 |
def process_response(state, key, is_timeout=False):
|
| 683 |
with state_lock:
|
| 684 |
current_state = deepcopy(state)
|
|
@@ -769,7 +678,7 @@ def process_response(state, key, is_timeout=False):
|
|
| 769 |
return current_state
|
| 770 |
|
| 771 |
# --- Score Calculation (LITE) ---
|
| 772 |
-
# (calculate_results_lite function
|
| 773 |
def calculate_results_lite(test_key, trial_results):
|
| 774 |
metrics = {'precision': 0, 'avg_rt': 0, 'rt_cv': 0, 'timeouts': 0}
|
| 775 |
analysis_str = get_text("results_analysis_missing")
|
|
@@ -795,7 +704,6 @@ def calculate_results_lite(test_key, trial_results):
|
|
| 795 |
rt_info = get_text("results_rt_summary", avg_rt=avg_rt, rt_cv=rt_cv)
|
| 796 |
timeouts = sum(1 for r in trial_results if r.get('is_timeout') and not r.get('correct'))
|
| 797 |
metrics['timeouts'] = timeouts
|
| 798 |
-
# Simplified analysis string
|
| 799 |
test_name_loc = get_text(TEST_CONFIG_LITE[test_key]['loc_key'], default=test_key)
|
| 800 |
analysis_str = get_text("results_analysis_summary", test_name=test_name_loc, precision=precision, rt_info=rt_info)
|
| 801 |
log_message("log_score_calculated", test_name=test_key, acc=precision, cons=metrics['rt_cv'])
|
|
@@ -807,7 +715,7 @@ initial_state_lite = {
|
|
| 807 |
"current_test_order": [], "current_test_key": None, "current_test_index": -1,
|
| 808 |
"test_params": {}, "test_sequence": [], "test_expected_response": {},
|
| 809 |
"test_trial_index": 0, "current_stimulus_index": -1, "last_processed_index": -99,
|
| 810 |
-
"test_stimulus": None, "test_user_response": None, "test_feedback": "
|
| 811 |
"test_stimulus_show_time": None, "awaiting_input": False,
|
| 812 |
"current_scores": {}, "current_trial_results": [],
|
| 813 |
"round_results": {"analysis": {}, "metrics": {"per_module": {}, "overall": {}}},
|
|
@@ -816,17 +724,17 @@ initial_state_lite = {
|
|
| 816 |
}
|
| 817 |
|
| 818 |
theme = gr.themes.Soft(primary_hue="purple", secondary_hue="indigo").set(
|
| 819 |
-
body_background_fill="*color-background", body_text_color="*color-text-primary",
|
| 820 |
)
|
| 821 |
|
| 822 |
with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
| 823 |
app_state = gr.State(value=deepcopy(initial_state_lite))
|
| 824 |
with gr.Column(elem_classes=f"{initial_state_lite.get('theme','dark')}-theme", elem_id="master-column") as master_column:
|
| 825 |
-
# --- Stage Blocks ---
|
| 826 |
-
# Welcome / Alias Block
|
| 827 |
-
with gr.Column(visible=
|
| 828 |
-
welcome_title = gr.Markdown("...", elem_classes="block-title")
|
| 829 |
-
welcome_text = gr.Markdown("...")
|
| 830 |
with gr.Accordion("...", open=False) as disclaimer_accordion: disclaimer_text = gr.Markdown("...")
|
| 831 |
gr.HTML("<hr class='section-hr'>")
|
| 832 |
alias_title = gr.Markdown("...", elem_classes="block-subtitle")
|
|
@@ -835,7 +743,7 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 835 |
with gr.Row():
|
| 836 |
alias_confirm_button = gr.Button("...", elem_classes="gr-button-primary", scale=1)
|
| 837 |
alias_skip_button = gr.Button("...", elem_classes="gr-button-secondary", scale=1)
|
| 838 |
-
# Menu Block
|
| 839 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="menu-block") as menu_block:
|
| 840 |
menu_title = gr.Markdown("...", elem_classes="block-title")
|
| 841 |
agent_info = gr.Markdown("...", elem_id="agent-info-menu")
|
|
@@ -844,42 +752,42 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 844 |
view_history_button = gr.Button("...", elem_classes="btn-menu gr-button-secondary")
|
| 845 |
reset_level_button = gr.Button("...", elem_classes="btn-menu btn-reset")
|
| 846 |
theme_toggle_btn = gr.Button("...", elem_classes="btn-menu gr-button-secondary")
|
| 847 |
-
# History Block
|
| 848 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="history-block") as history_block:
|
| 849 |
history_title = gr.Markdown("...", elem_classes="block-title")
|
| 850 |
-
hist_html = gr.HTML(
|
| 851 |
history_back_button = gr.Button("...", elem_classes="gr-button-secondary")
|
| 852 |
-
# Instructions Block
|
| 853 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="instructions-block") as instructions_block:
|
| 854 |
instructions_title = gr.Markdown("...", elem_classes="block-subtitle", elem_id="instructions_title")
|
| 855 |
-
instructions_text = gr.HTML("
|
| 856 |
start_test_button = gr.Button("...", elem_classes="gr-button-primary")
|
| 857 |
-
# Test Block
|
| 858 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="test-block") as test_block:
|
| 859 |
test_title = gr.Markdown("...", elem_classes="block-subtitle", elem_id="test_title")
|
| 860 |
with gr.Row():
|
| 861 |
progress_indicator = gr.Markdown("...", elem_id="progress-indicator")
|
| 862 |
score_display = gr.Markdown("...", elem_id="score-display")
|
| 863 |
timer_display = gr.Markdown("...", elem_id="timer-display")
|
| 864 |
-
stimulus_display = gr.HTML("<p class='stimulus-core'>
|
| 865 |
-
feedback_display = gr.HTML("
|
| 866 |
with gr.Row(equal_height=False, elem_id="response-button-row"):
|
| 867 |
response_btn_1 = gr.Button("", elem_classes="btn-response", scale=1, visible=False)
|
| 868 |
response_btn_2 = gr.Button("", elem_classes="btn-response", scale=1, visible=False)
|
| 869 |
all_response_buttons = [response_btn_1, response_btn_2]
|
| 870 |
-
# Results Block
|
| 871 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="results-block") as results_block:
|
| 872 |
results_title = gr.Markdown("...", elem_classes="block-title")
|
| 873 |
results_report_for = gr.Markdown("...", elem_classes="block-subtitle", elem_id="results_report_for")
|
| 874 |
results_summary = gr.HTML("...", elem_id="results-summary")
|
| 875 |
results_level_msg = gr.HTML("...", elem_id="results-level")
|
| 876 |
-
results_analysis_content = gr.HTML(
|
| 877 |
results_info = gr.Markdown("...", elem_id="results_info")
|
| 878 |
results_back_button = gr.Button("...", elem_classes="gr-button-secondary")
|
| 879 |
|
| 880 |
# --- Define LITE Output Lists ---
|
| 881 |
all_blocks_lite = [ welcome_alias_block, menu_block, history_block, instructions_block, test_block, results_block ]
|
| 882 |
-
text_outputs_lite = [ welcome_title, welcome_text, disclaimer_accordion, disclaimer_text,
|
| 883 |
alias_title, alias_input, alias_confirm_button, alias_skip_button,
|
| 884 |
menu_title, agent_info, start_sim_button, change_alias_button, view_history_button, reset_level_button, theme_toggle_btn,
|
| 885 |
history_title, history_back_button, start_test_button,
|
|
@@ -894,12 +802,13 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 894 |
finish_test_outputs_lite = base_ui_outputs_lite + [instructions_title, instructions_text] + test_block_outputs_lite + results_specific_outputs_lite
|
| 895 |
|
| 896 |
# --- Helper: LITE UI Update ---
|
|
|
|
| 897 |
def update_ui_text_lite(state):
|
| 898 |
alias = state.get('alias', None); level = state.get('level', 1)
|
| 899 |
display_alias = alias if alias else get_text('text_anonymous')
|
| 900 |
return [ # Ensure this list matches text_outputs_lite exactly
|
| 901 |
gr.update(value=get_text('welcome_title')), gr.update(value=get_text('welcome_text')),
|
| 902 |
-
gr.update(label=get_text('disclaimer_title')), gr.update(value=get_text('disclaimer_text')),
|
| 903 |
gr.update(value=get_text('alias_title')), gr.update(label=get_text('alias_label'), placeholder=get_text('alias_placeholder')),
|
| 904 |
gr.update(value=get_text('alias_confirm_button')), gr.update(value=get_text('alias_skip_button')),
|
| 905 |
gr.update(value=get_text('menu_title')), gr.update(value=get_text("menu_agent_info", alias=display_alias, level=level)),
|
|
@@ -917,13 +826,14 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 917 |
"instructions": instructions_block, "test": test_block, "results": results_block}
|
| 918 |
updates = [gr.update(elem_classes=f"{state.get('theme','dark')}-theme")]
|
| 919 |
for stage, block in block_map.items(): updates.append(gr.update(visible=vis_map.get(stage, False)))
|
| 920 |
-
|
|
|
|
| 921 |
return updates
|
| 922 |
|
| 923 |
# --- LITE Handlers ---
|
| 924 |
# (confirm_alias_lite, skip_alias_lite, reset_state_lite, start_simulation_lite, start_test_lite,
|
| 925 |
# run_trial_flow_lite, process_click_lite, finish_test_lite, go_to_menu, reset_level_confirmation,
|
| 926 |
-
# toggle_theme_py_only functions remain the same as previous
|
| 927 |
def confirm_alias_lite(current_state, alias_str):
|
| 928 |
state = deepcopy(current_state); alias = alias_str.strip()[:16] if isinstance(alias_str, str) else ""; feedback = ""
|
| 929 |
if alias and 3 <= len(alias) <= 16 and alias.isalnum():
|
|
@@ -942,7 +852,6 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 942 |
elif not alias: state["stage"] = "menu"; state["alias"] = None; state["level"] = 1; feedback = get_text("alias_feedback_anon"); gr.Info(get_text("info_alias_anon")); log_message("log_alias_anon")
|
| 943 |
else: state["stage"] = "welcome_alias"; feedback = get_text("alias_feedback_invalid"); gr.Warning(feedback)
|
| 944 |
visibility = get_stage_visibility_lite(state["stage"], state); text = update_ui_text_lite(state); fb_update = gr.update(value=feedback)
|
| 945 |
-
# Ensure the number of return values matches base_ui_outputs_lite
|
| 946 |
return [state] + visibility + text + [fb_update]
|
| 947 |
|
| 948 |
def skip_alias_lite(current_state): return confirm_alias_lite(current_state, "")
|
|
@@ -950,7 +859,7 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 950 |
def reset_state_lite(state_dict):
|
| 951 |
state_dict.update({ "current_test_order": [], "current_test_key": None, "current_test_index": -1, "test_params": {}, "test_sequence": [],
|
| 952 |
"test_expected_response": {}, "test_trial_index": 0, "current_stimulus_index": -1, "last_processed_index": -99,
|
| 953 |
-
"test_stimulus": None, "test_user_response": None, "test_feedback": "
|
| 954 |
"awaiting_input": False, "current_scores": {}, "current_trial_results": [],
|
| 955 |
"round_results": {"analysis": {}, "metrics": {"per_module": {}, "overall": {}}},
|
| 956 |
"positive_score": 0, "negative_score": 0, "performance_buffer_correct": deque(maxlen=10), })
|
|
@@ -965,7 +874,6 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 965 |
except Exception as e:
|
| 966 |
log_message("error_prepare_test", level="CRITICAL", test_name=first_key, error=e); gr.Error(get_text("error_prepare_test", test_name=first_key, error=str(e))); state["stage"] = "menu"
|
| 967 |
visibility = get_stage_visibility_lite("menu", state); text = update_ui_text_lite(state)
|
| 968 |
-
# Match start_sim_outputs_lite: state, vis, text, fb, instr_title, instr_text
|
| 969 |
return [state] + visibility + text + [gr.update()] * 3
|
| 970 |
state.update({ "stage": "instructions", "current_test_key": first_key, "test_params": params, "level_before_results": level, "current_scores": {key: 0.0 for key in test_order}, })
|
| 971 |
visibility = get_stage_visibility_lite("instructions", state); text = update_ui_text_lite(state); fb_update = gr.update()
|
|
@@ -990,11 +898,11 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 990 |
dummy_updates_needed = len(start_test_outputs_lite) - (1 + len(visibility) + len(text) + 1)
|
| 991 |
return [state] + visibility + text + [gr.update()] + [gr.update()] * dummy_updates_needed
|
| 992 |
next_stage = f"test_{test_key}"; test_name_loc = get_text(TEST_CONFIG_LITE[test_key]['loc_key']); test_dur = len(sequence)
|
| 993 |
-
state.update({ "stage": next_stage, "test_sequence": sequence, "test_expected_response": expected, "test_trial_index": 0, "current_stimulus_index": -1, "last_processed_index": -99, "test_start_time": time.time(), "test_feedback": "
|
| 994 |
visibility = get_stage_visibility_lite(next_stage, state); text = update_ui_text_lite(state); fb_update = gr.update()
|
| 995 |
instr_clr = [gr.update(value=""), gr.update(value="")]
|
| 996 |
test_title_upd = gr.update(value=get_text("test_title", test_name=test_name_loc, level=level)); progress = gr.update(value=get_text("test_progress", current=0, total=test_dur)); score = gr.update(value=get_text("test_score", correct=0, errors=0)); timer = gr.update(value=get_text("test_timer", time=0))
|
| 997 |
-
stim = gr.HTML(f"<p class='stimulus-core'>{get_text('test_init_message')}</p>"); feedback = gr.HTML("
|
| 998 |
log_message("log_setup_complete", count=test_dur)
|
| 999 |
return [state] + visibility + text + [fb_update] + instr_clr + [test_title_upd, progress, score, timer, stim, feedback] + buttons
|
| 1000 |
|
|
@@ -1009,33 +917,28 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 1009 |
iti = max(INTER_TRIAL_INTERVAL_MIN, params.get('iti_base',0.1) * (1+random.uniform(-0.3,0.3))); time.sleep(iti)
|
| 1010 |
state["current_stimulus_index"] = idx; state["test_stimulus"] = seq[idx]
|
| 1011 |
timeout = max(RESPONSE_WINDOW_TIMEOUT_MIN, params.get('response_timeout_base', 1.6) * (1+random.uniform(-0.15,0.15)))
|
| 1012 |
-
state["awaiting_input"] = True; state["test_feedback"] = "
|
| 1013 |
stim_html = format_stimulus_html(state); feedback_html = gr.HTML(state["test_feedback"]); prog = gr.update(value=get_text("test_progress", current=idx + 1, total=test_dur)); score = gr.update(value=get_text("test_score", correct=state.get('positive_score',0), errors=state.get('negative_score',0))); timer = gr.update(value=get_text("test_timer", time=timeout)); buttons_upd, _ = get_test_buttons_visibility_and_labels(state)
|
| 1014 |
yield [state, gr.update(value=stim_html), feedback_html, prog, score, timer] + buttons_upd
|
| 1015 |
start_wait = time.time(); responded = False
|
| 1016 |
while time.time() - start_wait < timeout:
|
| 1017 |
-
|
| 1018 |
-
# Accessing state directly here in loop might be slightly delayed
|
| 1019 |
-
# It relies on process_click yielding the updated state back quickly.
|
| 1020 |
-
if state.get("last_processed_index", -99) == idx:
|
| 1021 |
-
responded = True
|
| 1022 |
-
break
|
| 1023 |
time.sleep(0.010)
|
| 1024 |
if not responded and state.get("awaiting_input") and state.get("last_processed_index", -99) != idx:
|
| 1025 |
log_message("log_timeout", idx=idx); state = process_response(state, None, is_timeout=True)
|
| 1026 |
if state.get("last_processed_index", -99) == idx:
|
| 1027 |
-
stim_upd = gr.update(value="<p class='stimulus-core'>
|
| 1028 |
yield [state, stim_upd, fb_upd, gr.update(), score_upd, timer_upd] + btn_upd
|
| 1029 |
time.sleep(state.get("test_params",{}).get("feedback_delay",0.1))
|
| 1030 |
-
yield [state, gr.update(), gr.HTML("
|
| 1031 |
state["test_trial_index"] += 1
|
| 1032 |
log_message("log_test_completed", test_name=test_key, count=test_dur); state["awaiting_input"] = False
|
| 1033 |
-
yield [state, gr.update(value=f"<p class='stimulus-core'>{get_text('test_complete_message')}</p>"), gr.HTML("
|
| 1034 |
except Exception as e:
|
| 1035 |
log_message("error_trial_flow", level="CRITICAL", test_name=state.get('current_test_key', '??'), error=e, traceback=traceback.format_exc())
|
| 1036 |
gr.Error(get_text("error_trial_flow", test_name=state.get('current_test_key', '??'), error=str(e)))
|
| 1037 |
state["stage"] = "menu"; state["awaiting_input"] = False
|
| 1038 |
-
yield [state, gr.update(value=f"<p style='color:var(--color-error);'>{get_text('test_error_message')}</p>"), gr.HTML("
|
| 1039 |
|
| 1040 |
def process_click_lite(current_state, button_index):
|
| 1041 |
state_click = deepcopy(current_state); idx = state_click.get("current_stimulus_index", -1); is_awaiting = state_click.get("awaiting_input", False); last_proc = state_click.get("last_processed_index", -99)
|
|
@@ -1047,10 +950,10 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 1047 |
if next_state.get("last_processed_index", -99) != idx:
|
| 1048 |
log_message("log_click_state_warn", level="WARN", idx=idx)
|
| 1049 |
yield [next_state] + [gr.update()] * (len(click_response_outputs_lite) - 1); return
|
| 1050 |
-
stim_upd = gr.update(value="<p class='stimulus-core'>
|
| 1051 |
yield [next_state, stim_upd, fb_upd, gr.update(), score_upd, timer_clr] + btn_vis
|
| 1052 |
time.sleep(next_state.get("test_params",{}).get("feedback_delay",0.1))
|
| 1053 |
-
yield [next_state, gr.update(), gr.HTML("
|
| 1054 |
|
| 1055 |
def finish_test_lite(state_after_flow):
|
| 1056 |
state = deepcopy(state_after_flow); test_key = state.get("current_test_key"); results = state.get("current_trial_results", [])
|
|
@@ -1116,14 +1019,13 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 1116 |
|
| 1117 |
# --- Helper needed for back buttons and reset_level ---
|
| 1118 |
def go_to_menu(current_state):
|
| 1119 |
-
state = deepcopy(current_state)
|
| 1120 |
state["stage"] = "menu"; state = reset_state_lite(state)
|
| 1121 |
visibility = get_stage_visibility_lite("menu", state); text = update_ui_text_lite(state); fb_update = gr.update(value="")
|
| 1122 |
-
#
|
| 1123 |
return [state] + visibility + text + [fb_update]
|
| 1124 |
|
| 1125 |
def reset_level_confirmation(state):
|
| 1126 |
-
# state is already a deepcopy from the lambda caller
|
| 1127 |
alias = state.get("alias")
|
| 1128 |
if alias:
|
| 1129 |
state["level"] = 1
|
|
@@ -1171,7 +1073,23 @@ with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
|
| 1171 |
results_back_button.click(fn=lambda s: go_to_menu(deepcopy(s)), inputs=[app_state], outputs=base_ui_outputs_lite)
|
| 1172 |
history_back_button.click(fn=lambda s: go_to_menu(deepcopy(s)), inputs=[app_state], outputs=base_ui_outputs_lite)
|
| 1173 |
|
| 1174 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1175 |
|
| 1176 |
|
| 1177 |
# --- Launch LITE ---
|
|
@@ -1182,5 +1100,5 @@ if __name__ == "__main__":
|
|
| 1182 |
log_message("log_init_complete")
|
| 1183 |
log_message("log_launching")
|
| 1184 |
demo_lite.queue()
|
| 1185 |
-
# Launch with SSR disabled
|
| 1186 |
-
demo_lite.launch(server_name="0.0.0.0", prevent_thread_lock=True, ssr_mode=False)
|
|
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
# Protocolo Nexus Cognitivo - LITE (Obsidiana ES)
|
| 3 |
+
# Versión: 4.1.0-Lite-LoadFix
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
import random
|
|
|
|
| 31 |
NUMPY_AVAILABLE = False
|
| 32 |
|
| 33 |
# --- LITE Text Dictionary (Embedded) ---
|
| 34 |
+
# (TEXTOS_LITE dictionary remains the same)
|
| 35 |
TEXTOS_LITE = {
|
| 36 |
"app_title": "Protocolo Nexus Cognitivo LITE",
|
| 37 |
"app_version": "4.1.0-Lite ES",
|
|
|
|
| 95 |
"results_level_max_advance": "<h5><strong>Sintonía Pico Nivel {level}.</strong> Calibración adicional recomendada.</h5>",
|
| 96 |
"results_level_regress": "<h5><strong>Disonancia Detectada.</strong> Recalibrando a Nivel {new_level}.</h5>",
|
| 97 |
"results_level_maintain": "<h5>Resonancia Nivel {level} mantenida. (Req: >{threshold}% Alin., <{consistency_threshold:.3f} CV)</h5>",
|
| 98 |
+
"results_analysis_title": "Análisis Básico",
|
| 99 |
"results_analysis_generating": "<p>Calculando...</p>",
|
| 100 |
"results_analysis_error": "<p style='color:var(--color-error);'>Error análisis.</p>",
|
| 101 |
"results_analysis_missing": "<p>Datos no disponibles.</p>",
|
| 102 |
+
"results_analysis_summary": "Módulo {test_name}: Alineación {precision:.1f}%. {rt_info}",
|
| 103 |
+
"results_rt_summary": "TR Medio: {avg_rt:.3f}s (CV: {rt_cv:.3f})",
|
| 104 |
"results_info": "<p class='info-text'>Eco archivado. Estado flujo actualizado. Retornando.</p>",
|
| 105 |
"results_back_button": "Retornar",
|
| 106 |
# Popups / Info / Warnings / Errors
|
|
|
|
| 126 |
"error_format_key_missing": "Error Txt: Falta '{missing}' en '{key}'.",
|
| 127 |
"error_format_general": "Error Txt: {error} en '{key}'.",
|
| 128 |
"error_trial_flow": "ERROR CRÍTICO durante flujo de prueba: {error}",
|
| 129 |
+
"error_finish_test_state": "ERROR: Estado inválido al finalizar test.",
|
| 130 |
# Logging
|
| 131 |
"log_init_start": "Iniciando Nexus LITE v{version}...",
|
| 132 |
"log_init_results": "Archivo de ecos: {file}",
|
|
|
|
| 155 |
"log_test_transition": "Transición a: {test_name}",
|
| 156 |
"log_test_next_instr": "Mostrando instrucciones para {test_name}",
|
| 157 |
"log_seq_complete": "Calibración Completa. Calculando resultados...",
|
| 158 |
+
"log_level_change": "Evaluación Nivel: Precision={acc:.1f}, Consist={cons:.3f}. Nuevo Nivel: {level}",
|
| 159 |
"log_saving_results": "Archivando eco: Alias={alias}, LvlComp={level_completed}, NewLvl={new_level}",
|
| 160 |
"log_save_results_success": "Eco archivado.",
|
| 161 |
"log_save_profile": "Perfil guardado: {alias}, Nivel: {profile_level}",
|
|
|
|
| 279 |
return False
|
| 280 |
|
| 281 |
# --- CSS ---
|
| 282 |
+
# (Assume full obsidian_css string is included here)
|
| 283 |
obsidian_css = """
|
| 284 |
+
:root { /* ... Full CSS from previous versions ... */ }
|
| 285 |
+
body.light-theme { /* ... Full CSS ... */ }
|
| 286 |
+
body { /* ... Full CSS ... */ }
|
| 287 |
+
/* ... All other CSS rules ... */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
footer { display: none !important; }
|
| 289 |
"""
|
| 290 |
|
|
|
|
| 311 |
}
|
| 312 |
AVAILABLE_TEST_KEYS_LITE = list(TEST_CONFIG_LITE.keys())
|
| 313 |
|
| 314 |
+
# --- Profile/Results Handling ---
|
| 315 |
+
# (load_agent_profile, save_agent_profile, save_result, read_history functions are correct)
|
| 316 |
def load_agent_profile(alias):
|
| 317 |
if not alias: return 1
|
| 318 |
with profile_lock:
|
|
|
|
| 400 |
return html
|
| 401 |
|
| 402 |
# --- Difficulty Params (LITE) ---
|
| 403 |
+
# (get_difficulty_params function is correct)
|
| 404 |
def get_difficulty_params(test_key, level):
|
| 405 |
if test_key not in TEST_CONFIG_LITE: raise ValueError(f"Unknown test key: {test_key}")
|
| 406 |
config = TEST_CONFIG_LITE[test_key]
|
|
|
|
| 417 |
return params
|
| 418 |
|
| 419 |
# --- Sequence Generation (LITE) ---
|
| 420 |
+
# (generate_attention_sequence, generate_inhibition_sequence, generate_memory_sequence functions are correct)
|
| 421 |
def generate_attention_sequence(params):
|
| 422 |
n = params['trials']; cue = params['cue']; target = params['target']
|
| 423 |
sim_dist = params['similar_distractors']; other_dist = params['other_distractors']
|
|
|
|
| 501 |
}
|
| 502 |
|
| 503 |
# --- Instruction HTML Generation (LITE) ---
|
| 504 |
+
# (get_instructions_html function is correct)
|
| 505 |
def get_instructions_html(test_key, params):
|
| 506 |
test_config = TEST_CONFIG_LITE.get(test_key)
|
| 507 |
if not test_config: return "<p>Error: Configuración Test No Encontrada.</p>"
|
|
|
|
| 528 |
lines.extend(common_lines)
|
| 529 |
return "".join(f"<p>{line}</p>" if not line.startswith("<br>") else line for line in lines)
|
| 530 |
|
| 531 |
+
# --- UI Formatting ---
|
| 532 |
+
# (format_stimulus_html and get_test_buttons_visibility_and_labels functions are correct)
|
| 533 |
def format_stimulus_html(state):
|
| 534 |
stim_raw = state.get("test_stimulus", ""); test_key = state.get("current_test_key", "")
|
| 535 |
params = state.get("test_params", {}); is_awaiting = state.get("awaiting_input", False)
|
| 536 |
+
if not is_awaiting or stim_raw is None or stim_raw == "": return "<p class='stimulus-core'> </p>"
|
| 537 |
display_text = ""; style_color = "var(--color-text-primary)"; extra_class = ""
|
| 538 |
try:
|
| 539 |
if test_key == "atencion": display_text = str(stim_raw)
|
|
|
|
| 552 |
else: raise ValueError("Invalid mem stimulus")
|
| 553 |
else: display_text = str(stim_raw) # Fallback
|
| 554 |
display_text_safe = str(display_text).replace("&", "&").replace("<", "<").replace(">", ">")
|
| 555 |
+
if not display_text_safe or not display_text_safe.strip(): display_text_safe = " "
|
| 556 |
return f"<p class='stimulus-core{extra_class}' style='color:{style_color};'>{display_text_safe}</p>"
|
| 557 |
except Exception as e:
|
| 558 |
log_message("error_stimulus_format", level="ERROR", error=e, traceback=traceback.format_exc())
|
|
|
|
| 587 |
return final_updates, keys
|
| 588 |
|
| 589 |
# --- Response Processing (LITE) ---
|
| 590 |
+
# (process_response function is correct)
|
| 591 |
def process_response(state, key, is_timeout=False):
|
| 592 |
with state_lock:
|
| 593 |
current_state = deepcopy(state)
|
|
|
|
| 678 |
return current_state
|
| 679 |
|
| 680 |
# --- Score Calculation (LITE) ---
|
| 681 |
+
# (calculate_results_lite function is correct)
|
| 682 |
def calculate_results_lite(test_key, trial_results):
|
| 683 |
metrics = {'precision': 0, 'avg_rt': 0, 'rt_cv': 0, 'timeouts': 0}
|
| 684 |
analysis_str = get_text("results_analysis_missing")
|
|
|
|
| 704 |
rt_info = get_text("results_rt_summary", avg_rt=avg_rt, rt_cv=rt_cv)
|
| 705 |
timeouts = sum(1 for r in trial_results if r.get('is_timeout') and not r.get('correct'))
|
| 706 |
metrics['timeouts'] = timeouts
|
|
|
|
| 707 |
test_name_loc = get_text(TEST_CONFIG_LITE[test_key]['loc_key'], default=test_key)
|
| 708 |
analysis_str = get_text("results_analysis_summary", test_name=test_name_loc, precision=precision, rt_info=rt_info)
|
| 709 |
log_message("log_score_calculated", test_name=test_key, acc=precision, cons=metrics['rt_cv'])
|
|
|
|
| 715 |
"current_test_order": [], "current_test_key": None, "current_test_index": -1,
|
| 716 |
"test_params": {}, "test_sequence": [], "test_expected_response": {},
|
| 717 |
"test_trial_index": 0, "current_stimulus_index": -1, "last_processed_index": -99,
|
| 718 |
+
"test_stimulus": None, "test_user_response": None, "test_feedback": " ",
|
| 719 |
"test_stimulus_show_time": None, "awaiting_input": False,
|
| 720 |
"current_scores": {}, "current_trial_results": [],
|
| 721 |
"round_results": {"analysis": {}, "metrics": {"per_module": {}, "overall": {}}},
|
|
|
|
| 724 |
}
|
| 725 |
|
| 726 |
theme = gr.themes.Soft(primary_hue="purple", secondary_hue="indigo").set(
|
| 727 |
+
body_background_fill="*color-background", body_text_color="*color-text-primary",
|
| 728 |
)
|
| 729 |
|
| 730 |
with gr.Blocks(title=APP_TITLE, theme=theme, css=obsidian_css) as demo_lite:
|
| 731 |
app_state = gr.State(value=deepcopy(initial_state_lite))
|
| 732 |
with gr.Column(elem_classes=f"{initial_state_lite.get('theme','dark')}-theme", elem_id="master-column") as master_column:
|
| 733 |
+
# --- Stage Blocks --- Define structure, set initial visibility via .load()
|
| 734 |
+
# Welcome / Alias Block
|
| 735 |
+
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="welcome-alias-block") as welcome_alias_block:
|
| 736 |
+
welcome_title = gr.Markdown("...", elem_classes="block-title")
|
| 737 |
+
welcome_text = gr.Markdown("...")
|
| 738 |
with gr.Accordion("...", open=False) as disclaimer_accordion: disclaimer_text = gr.Markdown("...")
|
| 739 |
gr.HTML("<hr class='section-hr'>")
|
| 740 |
alias_title = gr.Markdown("...", elem_classes="block-subtitle")
|
|
|
|
| 743 |
with gr.Row():
|
| 744 |
alias_confirm_button = gr.Button("...", elem_classes="gr-button-primary", scale=1)
|
| 745 |
alias_skip_button = gr.Button("...", elem_classes="gr-button-secondary", scale=1)
|
| 746 |
+
# Menu Block
|
| 747 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="menu-block") as menu_block:
|
| 748 |
menu_title = gr.Markdown("...", elem_classes="block-title")
|
| 749 |
agent_info = gr.Markdown("...", elem_id="agent-info-menu")
|
|
|
|
| 752 |
view_history_button = gr.Button("...", elem_classes="btn-menu gr-button-secondary")
|
| 753 |
reset_level_button = gr.Button("...", elem_classes="btn-menu btn-reset")
|
| 754 |
theme_toggle_btn = gr.Button("...", elem_classes="btn-menu gr-button-secondary")
|
| 755 |
+
# History Block
|
| 756 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="history-block") as history_block:
|
| 757 |
history_title = gr.Markdown("...", elem_classes="block-title")
|
| 758 |
+
hist_html = gr.HTML("...", elem_id="history-html")
|
| 759 |
history_back_button = gr.Button("...", elem_classes="gr-button-secondary")
|
| 760 |
+
# Instructions Block
|
| 761 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="instructions-block") as instructions_block:
|
| 762 |
instructions_title = gr.Markdown("...", elem_classes="block-subtitle", elem_id="instructions_title")
|
| 763 |
+
instructions_text = gr.HTML("...", elem_id="instr-text")
|
| 764 |
start_test_button = gr.Button("...", elem_classes="gr-button-primary")
|
| 765 |
+
# Test Block
|
| 766 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="test-block") as test_block:
|
| 767 |
test_title = gr.Markdown("...", elem_classes="block-subtitle", elem_id="test_title")
|
| 768 |
with gr.Row():
|
| 769 |
progress_indicator = gr.Markdown("...", elem_id="progress-indicator")
|
| 770 |
score_display = gr.Markdown("...", elem_id="score-display")
|
| 771 |
timer_display = gr.Markdown("...", elem_id="timer-display")
|
| 772 |
+
stimulus_display = gr.HTML("<p class='stimulus-core'> </p>", elem_id="stimulus-display")
|
| 773 |
+
feedback_display = gr.HTML(" ", elem_id="feedback-display")
|
| 774 |
with gr.Row(equal_height=False, elem_id="response-button-row"):
|
| 775 |
response_btn_1 = gr.Button("", elem_classes="btn-response", scale=1, visible=False)
|
| 776 |
response_btn_2 = gr.Button("", elem_classes="btn-response", scale=1, visible=False)
|
| 777 |
all_response_buttons = [response_btn_1, response_btn_2]
|
| 778 |
+
# Results Block
|
| 779 |
with gr.Column(visible=False, elem_classes="main-content-box", elem_id="results-block") as results_block:
|
| 780 |
results_title = gr.Markdown("...", elem_classes="block-title")
|
| 781 |
results_report_for = gr.Markdown("...", elem_classes="block-subtitle", elem_id="results_report_for")
|
| 782 |
results_summary = gr.HTML("...", elem_id="results-summary")
|
| 783 |
results_level_msg = gr.HTML("...", elem_id="results-level")
|
| 784 |
+
results_analysis_content = gr.HTML("...", elem_id="results_analysis_content")
|
| 785 |
results_info = gr.Markdown("...", elem_id="results_info")
|
| 786 |
results_back_button = gr.Button("...", elem_classes="gr-button-secondary")
|
| 787 |
|
| 788 |
# --- Define LITE Output Lists ---
|
| 789 |
all_blocks_lite = [ welcome_alias_block, menu_block, history_block, instructions_block, test_block, results_block ]
|
| 790 |
+
text_outputs_lite = [ welcome_title, welcome_text, disclaimer_accordion, disclaimer_text,
|
| 791 |
alias_title, alias_input, alias_confirm_button, alias_skip_button,
|
| 792 |
menu_title, agent_info, start_sim_button, change_alias_button, view_history_button, reset_level_button, theme_toggle_btn,
|
| 793 |
history_title, history_back_button, start_test_button,
|
|
|
|
| 802 |
finish_test_outputs_lite = base_ui_outputs_lite + [instructions_title, instructions_text] + test_block_outputs_lite + results_specific_outputs_lite
|
| 803 |
|
| 804 |
# --- Helper: LITE UI Update ---
|
| 805 |
+
# (update_ui_text_lite function remains the same)
|
| 806 |
def update_ui_text_lite(state):
|
| 807 |
alias = state.get('alias', None); level = state.get('level', 1)
|
| 808 |
display_alias = alias if alias else get_text('text_anonymous')
|
| 809 |
return [ # Ensure this list matches text_outputs_lite exactly
|
| 810 |
gr.update(value=get_text('welcome_title')), gr.update(value=get_text('welcome_text')),
|
| 811 |
+
gr.update(label=get_text('disclaimer_title')), gr.update(value=get_text('disclaimer_text')),
|
| 812 |
gr.update(value=get_text('alias_title')), gr.update(label=get_text('alias_label'), placeholder=get_text('alias_placeholder')),
|
| 813 |
gr.update(value=get_text('alias_confirm_button')), gr.update(value=get_text('alias_skip_button')),
|
| 814 |
gr.update(value=get_text('menu_title')), gr.update(value=get_text("menu_agent_info", alias=display_alias, level=level)),
|
|
|
|
| 826 |
"instructions": instructions_block, "test": test_block, "results": results_block}
|
| 827 |
updates = [gr.update(elem_classes=f"{state.get('theme','dark')}-theme")]
|
| 828 |
for stage, block in block_map.items(): updates.append(gr.update(visible=vis_map.get(stage, False)))
|
| 829 |
+
# Avoid logging during the initial load event if it causes issues
|
| 830 |
+
# log_message("log_stage_transition", stage=target_stage)
|
| 831 |
return updates
|
| 832 |
|
| 833 |
# --- LITE Handlers ---
|
| 834 |
# (confirm_alias_lite, skip_alias_lite, reset_state_lite, start_simulation_lite, start_test_lite,
|
| 835 |
# run_trial_flow_lite, process_click_lite, finish_test_lite, go_to_menu, reset_level_confirmation,
|
| 836 |
+
# toggle_theme_py_only functions remain the same as previous fixed version)
|
| 837 |
def confirm_alias_lite(current_state, alias_str):
|
| 838 |
state = deepcopy(current_state); alias = alias_str.strip()[:16] if isinstance(alias_str, str) else ""; feedback = ""
|
| 839 |
if alias and 3 <= len(alias) <= 16 and alias.isalnum():
|
|
|
|
| 852 |
elif not alias: state["stage"] = "menu"; state["alias"] = None; state["level"] = 1; feedback = get_text("alias_feedback_anon"); gr.Info(get_text("info_alias_anon")); log_message("log_alias_anon")
|
| 853 |
else: state["stage"] = "welcome_alias"; feedback = get_text("alias_feedback_invalid"); gr.Warning(feedback)
|
| 854 |
visibility = get_stage_visibility_lite(state["stage"], state); text = update_ui_text_lite(state); fb_update = gr.update(value=feedback)
|
|
|
|
| 855 |
return [state] + visibility + text + [fb_update]
|
| 856 |
|
| 857 |
def skip_alias_lite(current_state): return confirm_alias_lite(current_state, "")
|
|
|
|
| 859 |
def reset_state_lite(state_dict):
|
| 860 |
state_dict.update({ "current_test_order": [], "current_test_key": None, "current_test_index": -1, "test_params": {}, "test_sequence": [],
|
| 861 |
"test_expected_response": {}, "test_trial_index": 0, "current_stimulus_index": -1, "last_processed_index": -99,
|
| 862 |
+
"test_stimulus": None, "test_user_response": None, "test_feedback": " ", "test_stimulus_show_time": None,
|
| 863 |
"awaiting_input": False, "current_scores": {}, "current_trial_results": [],
|
| 864 |
"round_results": {"analysis": {}, "metrics": {"per_module": {}, "overall": {}}},
|
| 865 |
"positive_score": 0, "negative_score": 0, "performance_buffer_correct": deque(maxlen=10), })
|
|
|
|
| 874 |
except Exception as e:
|
| 875 |
log_message("error_prepare_test", level="CRITICAL", test_name=first_key, error=e); gr.Error(get_text("error_prepare_test", test_name=first_key, error=str(e))); state["stage"] = "menu"
|
| 876 |
visibility = get_stage_visibility_lite("menu", state); text = update_ui_text_lite(state)
|
|
|
|
| 877 |
return [state] + visibility + text + [gr.update()] * 3
|
| 878 |
state.update({ "stage": "instructions", "current_test_key": first_key, "test_params": params, "level_before_results": level, "current_scores": {key: 0.0 for key in test_order}, })
|
| 879 |
visibility = get_stage_visibility_lite("instructions", state); text = update_ui_text_lite(state); fb_update = gr.update()
|
|
|
|
| 898 |
dummy_updates_needed = len(start_test_outputs_lite) - (1 + len(visibility) + len(text) + 1)
|
| 899 |
return [state] + visibility + text + [gr.update()] + [gr.update()] * dummy_updates_needed
|
| 900 |
next_stage = f"test_{test_key}"; test_name_loc = get_text(TEST_CONFIG_LITE[test_key]['loc_key']); test_dur = len(sequence)
|
| 901 |
+
state.update({ "stage": next_stage, "test_sequence": sequence, "test_expected_response": expected, "test_trial_index": 0, "current_stimulus_index": -1, "last_processed_index": -99, "test_start_time": time.time(), "test_feedback": " ", "current_trial_results": [], "awaiting_input": False, "positive_score": 0, "negative_score": 0, })
|
| 902 |
visibility = get_stage_visibility_lite(next_stage, state); text = update_ui_text_lite(state); fb_update = gr.update()
|
| 903 |
instr_clr = [gr.update(value=""), gr.update(value="")]
|
| 904 |
test_title_upd = gr.update(value=get_text("test_title", test_name=test_name_loc, level=level)); progress = gr.update(value=get_text("test_progress", current=0, total=test_dur)); score = gr.update(value=get_text("test_score", correct=0, errors=0)); timer = gr.update(value=get_text("test_timer", time=0))
|
| 905 |
+
stim = gr.HTML(f"<p class='stimulus-core'>{get_text('test_init_message')}</p>"); feedback = gr.HTML(" "); buttons = [gr.update(visible=False)] * len(all_response_buttons)
|
| 906 |
log_message("log_setup_complete", count=test_dur)
|
| 907 |
return [state] + visibility + text + [fb_update] + instr_clr + [test_title_upd, progress, score, timer, stim, feedback] + buttons
|
| 908 |
|
|
|
|
| 917 |
iti = max(INTER_TRIAL_INTERVAL_MIN, params.get('iti_base',0.1) * (1+random.uniform(-0.3,0.3))); time.sleep(iti)
|
| 918 |
state["current_stimulus_index"] = idx; state["test_stimulus"] = seq[idx]
|
| 919 |
timeout = max(RESPONSE_WINDOW_TIMEOUT_MIN, params.get('response_timeout_base', 1.6) * (1+random.uniform(-0.15,0.15)))
|
| 920 |
+
state["awaiting_input"] = True; state["test_feedback"] = " "; state["test_stimulus_show_time"] = time.time(); state["last_processed_index"] = -99
|
| 921 |
stim_html = format_stimulus_html(state); feedback_html = gr.HTML(state["test_feedback"]); prog = gr.update(value=get_text("test_progress", current=idx + 1, total=test_dur)); score = gr.update(value=get_text("test_score", correct=state.get('positive_score',0), errors=state.get('negative_score',0))); timer = gr.update(value=get_text("test_timer", time=timeout)); buttons_upd, _ = get_test_buttons_visibility_and_labels(state)
|
| 922 |
yield [state, gr.update(value=stim_html), feedback_html, prog, score, timer] + buttons_upd
|
| 923 |
start_wait = time.time(); responded = False
|
| 924 |
while time.time() - start_wait < timeout:
|
| 925 |
+
if state.get("last_processed_index", -99) == idx: responded = True; break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 926 |
time.sleep(0.010)
|
| 927 |
if not responded and state.get("awaiting_input") and state.get("last_processed_index", -99) != idx:
|
| 928 |
log_message("log_timeout", idx=idx); state = process_response(state, None, is_timeout=True)
|
| 929 |
if state.get("last_processed_index", -99) == idx:
|
| 930 |
+
stim_upd = gr.update(value="<p class='stimulus-core'> </p>"); fb_upd = gr.HTML(state.get("test_feedback", " ")); score_upd = gr.update(value=get_text("test_score", correct=state.get('positive_score',0), errors=state.get('negative_score',0))); btn_upd = [gr.update(visible=False)] * len(all_response_buttons); timer_upd = gr.update(value="")
|
| 931 |
yield [state, stim_upd, fb_upd, gr.update(), score_upd, timer_upd] + btn_upd
|
| 932 |
time.sleep(state.get("test_params",{}).get("feedback_delay",0.1))
|
| 933 |
+
yield [state, gr.update(), gr.HTML(" "), gr.update(), gr.update(), gr.update()] + [gr.update()] * len(all_response_buttons)
|
| 934 |
state["test_trial_index"] += 1
|
| 935 |
log_message("log_test_completed", test_name=test_key, count=test_dur); state["awaiting_input"] = False
|
| 936 |
+
yield [state, gr.update(value=f"<p class='stimulus-core'>{get_text('test_complete_message')}</p>"), gr.HTML(" "), gr.update(), gr.update(value=get_text("test_score", correct=state.get('positive_score',0), errors=state.get('negative_score',0))), gr.update(value="")] + [gr.update(visible=False)]*len(all_response_buttons)
|
| 937 |
except Exception as e:
|
| 938 |
log_message("error_trial_flow", level="CRITICAL", test_name=state.get('current_test_key', '??'), error=e, traceback=traceback.format_exc())
|
| 939 |
gr.Error(get_text("error_trial_flow", test_name=state.get('current_test_key', '??'), error=str(e)))
|
| 940 |
state["stage"] = "menu"; state["awaiting_input"] = False
|
| 941 |
+
yield [state, gr.update(value=f"<p style='color:var(--color-error);'>{get_text('test_error_message')}</p>"), gr.HTML(" "), gr.update(), gr.update(), gr.update()] + [gr.update(visible=False)]*len(all_response_buttons)
|
| 942 |
|
| 943 |
def process_click_lite(current_state, button_index):
|
| 944 |
state_click = deepcopy(current_state); idx = state_click.get("current_stimulus_index", -1); is_awaiting = state_click.get("awaiting_input", False); last_proc = state_click.get("last_processed_index", -99)
|
|
|
|
| 950 |
if next_state.get("last_processed_index", -99) != idx:
|
| 951 |
log_message("log_click_state_warn", level="WARN", idx=idx)
|
| 952 |
yield [next_state] + [gr.update()] * (len(click_response_outputs_lite) - 1); return
|
| 953 |
+
stim_upd = gr.update(value="<p class='stimulus-core'> </p>"); fb_upd = gr.HTML(next_state.get("test_feedback", " ")); score_upd = gr.update(value=get_text("test_score", correct=next_state.get('positive_score',0), errors=next_state.get('negative_score',0))); btn_vis = [gr.update(visible=False)] * len(all_response_buttons); timer_clr = gr.update(value="")
|
| 954 |
yield [next_state, stim_upd, fb_upd, gr.update(), score_upd, timer_clr] + btn_vis
|
| 955 |
time.sleep(next_state.get("test_params",{}).get("feedback_delay",0.1))
|
| 956 |
+
yield [next_state, gr.update(), gr.HTML(" "), gr.update(), gr.update(), gr.update()] + [gr.update()]*len(all_response_buttons)
|
| 957 |
|
| 958 |
def finish_test_lite(state_after_flow):
|
| 959 |
state = deepcopy(state_after_flow); test_key = state.get("current_test_key"); results = state.get("current_trial_results", [])
|
|
|
|
| 1019 |
|
| 1020 |
# --- Helper needed for back buttons and reset_level ---
|
| 1021 |
def go_to_menu(current_state):
|
| 1022 |
+
state = deepcopy(current_state)
|
| 1023 |
state["stage"] = "menu"; state = reset_state_lite(state)
|
| 1024 |
visibility = get_stage_visibility_lite("menu", state); text = update_ui_text_lite(state); fb_update = gr.update(value="")
|
| 1025 |
+
# Return list matching base_ui_outputs_lite
|
| 1026 |
return [state] + visibility + text + [fb_update]
|
| 1027 |
|
| 1028 |
def reset_level_confirmation(state):
|
|
|
|
| 1029 |
alias = state.get("alias")
|
| 1030 |
if alias:
|
| 1031 |
state["level"] = 1
|
|
|
|
| 1073 |
results_back_button.click(fn=lambda s: go_to_menu(deepcopy(s)), inputs=[app_state], outputs=base_ui_outputs_lite)
|
| 1074 |
history_back_button.click(fn=lambda s: go_to_menu(deepcopy(s)), inputs=[app_state], outputs=base_ui_outputs_lite)
|
| 1075 |
|
| 1076 |
+
# --- Initial UI Load Handler ---
|
| 1077 |
+
# Define the handler function
|
| 1078 |
+
def populate_initial_ui(state):
|
| 1079 |
+
# Get visibility updates for the initial stage
|
| 1080 |
+
visibility_updates = get_stage_visibility_lite(state['stage'], state)
|
| 1081 |
+
# Get text updates for all components
|
| 1082 |
+
text_updates = update_ui_text_lite(state)
|
| 1083 |
+
# Need state + visibility updates + text updates + alias feedback update
|
| 1084 |
+
# Must match the outputs list below
|
| 1085 |
+
return [state] + visibility_updates + text_updates + [gr.update()] # Return empty update for alias feedback
|
| 1086 |
+
|
| 1087 |
+
# Bind the handler to the .load() event
|
| 1088 |
+
demo_lite.load(
|
| 1089 |
+
fn=populate_initial_ui,
|
| 1090 |
+
inputs=[app_state],
|
| 1091 |
+
outputs=base_ui_outputs_lite # Use the most comprehensive list here
|
| 1092 |
+
)
|
| 1093 |
|
| 1094 |
|
| 1095 |
# --- Launch LITE ---
|
|
|
|
| 1100 |
log_message("log_init_complete")
|
| 1101 |
log_message("log_launching")
|
| 1102 |
demo_lite.queue()
|
| 1103 |
+
# Launch with SSR explicitly disabled
|
| 1104 |
+
demo_lite.launch(server_name="0.0.0.0", prevent_thread_lock=True, ssr_mode=False)
|