Jairo Gudino commited on
Commit
be6cb23
·
1 Parent(s): bfbf279

Fix critical errors in Gradio app - resolve TypeError and launch issues

Browse files
Files changed (2) hide show
  1. app.py +363 -288
  2. requirements.txt +3 -3
app.py CHANGED
@@ -2,10 +2,10 @@
2
  # Consenso Democrático - App para HuggingFace Spaces
3
  # =========================
4
 
5
- import sys
6
  import os
7
  import io
8
  import gc
 
9
  import time
10
  import warnings
11
  import pandas as pd
@@ -21,23 +21,23 @@ print("=== STARTING APP ===")
21
  print(f"Python version: {sys.version}")
22
  print(f"OPENAI_API_KEY present: {bool(os.getenv('OPENAI_API_KEY'))}")
23
  if os.getenv('OPENAI_API_KEY'):
24
- print(f"API key length: {len(os.getenv('OPENAI_API_KEY'))}")
25
- print(f"API key starts with: {os.getenv('OPENAI_API_KEY')[:10]}...")
 
 
26
  else:
27
  print("API key is None or empty")
28
  print("=== DEBUG END ===")
29
 
30
  # --- Configuración de entorno ---
31
- # IMPORTANTE: En HuggingFace Spaces, las variables de entorno se configuran en la interfaz web
32
- # NO pongas tu API key directamente en el código
33
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
34
  if not OPENAI_API_KEY:
35
  print("WARNING: OPENAI_API_KEY not found!")
36
- # No hacer raise por ahora para poder debuggear
37
  else:
38
  os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
 
39
 
40
- os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
41
  os.environ["USER_AGENT"] = os.getenv("USER_AGENT", "ConsensusApp")
42
  os.environ['CREWAI_DO_NOT_TELEMETRY'] = 'true'
43
  os.environ['CREWAI_SHARE_CREW'] = 'false'
@@ -45,12 +45,12 @@ os.environ['CREWAI_SHARE_CREW'] = 'false'
45
  # Suprimir warnings ruidosos
46
  warnings.filterwarnings("ignore", category=UserWarning, module="crewai")
47
  warnings.filterwarnings("ignore", category=DeprecationWarning)
 
48
 
49
  # =========================
50
  # Datos base y utilidades
51
  # =========================
52
 
53
- # Mapeo de partidos políticos a orientaciones
54
  PARTY_TO_ORIENTATION = {
55
  "FDP": "RIGHT", "jf": "RIGHT", "CVP": "CENTER", "Grüne": "LEFT", "SVP": "RIGHT",
56
  "PdA": "LEFT", "SP": "LEFT", "glp": "CENTER", "JUSO": "LEFT", "BDP": "CENTER",
@@ -59,19 +59,11 @@ PARTY_TO_ORIENTATION = {
59
  "JBDP": "CENTER", "Die": "CENTER", "JFS": "RIGHT", "SD": "RIGHT", "JM": "CENTER"
60
  }
61
 
62
- # Modelos por nivel
63
  LLM_BASE = "gpt-4o-mini"
64
  LLM_SUPERVISOR = "gpt-4o-mini"
65
  LLM_DIRECTOR = "gpt-4o-mini"
66
 
67
  def get_majority_decision_groups(df: pd.DataFrame) -> Dict:
68
- """
69
- Calcula decisiones por mayoría y porcentajes (FAVOR/AGAINST) por:
70
- - language
71
- - party_reply
72
- - orientación (derivada de party_reply via PARTY_TO_ORIENTATION)
73
- - FINAL_MAJORITY global
74
- """
75
  result = {}
76
 
77
  def calc_decision_and_percentages(series):
@@ -89,30 +81,46 @@ def get_majority_decision_groups(df: pd.DataFrame) -> Dict:
89
  return {"decision": decision, "percent_favor": favor_pct, "percent_against": against_pct}
90
 
91
  # Por idioma
92
- for lang, group in df.groupby('language')['label']:
93
- result[lang] = calc_decision_and_percentages(group)
 
 
 
94
 
95
  # Por partido
96
- for party, group in df.groupby('party_reply')['label']:
97
- result[party] = calc_decision_and_percentages(group)
 
 
 
98
 
99
  # Por orientación
100
- df_or = df.copy()
101
- df_or['orientation'] = df_or['party_reply'].map(PARTY_TO_ORIENTATION)
102
- df_or = df_or.dropna(subset=['orientation'])
103
- if not df_or.empty:
104
- for orient, group in df_or.groupby('orientation')['label']:
105
- result[f"{orient}_MAJORITY"] = calc_decision_and_percentages(group)
 
 
 
106
 
107
  # Global
108
- result['FINAL_MAJORITY'] = calc_decision_and_percentages(df['label'])
 
 
 
 
109
  return result
110
 
111
- # Silenciar salidas ruidosas de CrewAI
112
  def _silent_kickoff(crew: Crew):
113
  buf_out, buf_err = io.StringIO(), io.StringIO()
114
  with redirect_stdout(buf_out), redirect_stderr(buf_err):
115
- return crew.kickoff()
 
 
 
 
116
 
117
  # =========================
118
  # Sistema Multiagente (CrewAI)
@@ -123,42 +131,51 @@ class DemocraticConsensusCrewAI:
123
  self.question = question
124
  self.results = {"base_agents": {}, "supervisors": {}, "director": ""}
125
 
126
- # Un agente por nivel
127
- self.base_agent = Agent(
128
- role="Democratic Base Representative",
129
- goal="Generate democratic consensus for any given group of opinions, weighting all perspectives equally.",
130
- backstory=("You are a neutral representative. Given any group's opinions and a majority decision, "
131
- "you craft a fair, balanced consensus that aligns with that majority, acknowledging minorities as nuances."),
132
- verbose=False,
133
- allow_delegation=False,
134
- llm=LLM_BASE
135
- )
136
- self.supervisor_agent = Agent(
137
- role="Democratic Supervisor",
138
- goal="Refine and consolidate consensuses for a language or an orientation umbrella.",
139
- backstory=("You refine base consensuses for a given umbrella (language or orientation), "
140
- "keeping the specified majority as the core, integrating minority views as exceptions."),
141
- verbose=False,
142
- allow_delegation=False,
143
- llm=LLM_SUPERVISOR
144
- )
145
- self.director_agent = Agent(
146
- role="Digital President - Final Decision Maker",
147
- goal="Synthesize all supervisor consensuses into a final consensus that aligns with the absolute majority.",
148
- backstory=("You aggregate all umbrellas and issue a final democratic position respecting the global majority."),
149
- verbose=False,
150
- allow_delegation=False,
151
- llm=LLM_DIRECTOR
152
- )
 
 
 
 
 
 
153
 
154
- # Creación de tareas
155
  def create_base_tasks(self, large_strings_by_language: Dict, large_strings_by_party: Dict, majorities_decision: Dict):
 
 
 
156
  tasks = []
157
- # Por idioma
158
- for language, opinions_text in large_strings_by_language.items():
159
- maj = majorities_decision.get(language, {}).get('decision', 'AMBIGUOUS')
160
- t = Task(
161
- description=f"""
 
162
  MANDATORY INSTRUCTION: The consensus you generate MUST be based around the MAJORITY DECISION: {maj}
163
 
164
  Analyze the following {language} language opinions to answer the question: '{self.question}'
@@ -173,16 +190,16 @@ CRITICAL REQUIREMENTS:
173
 
174
  Return only the final consensus text that supports {maj}.
175
  """.strip(),
176
- agent=self.base_agent,
177
- expected_output=f"[BASE][LANG={language}][MAJ={maj}]"
178
- )
179
- tasks.append(t)
180
 
181
- # Por partido
182
- for party, opinions_text in large_strings_by_party.items():
183
- maj = majorities_decision.get(party, {}).get('decision', 'AMBIGUOUS')
184
- t = Task(
185
- description=f"""
186
  MANDATORY INSTRUCTION: The consensus you generate MUST be based around the MAJORITY DECISION: {maj}
187
 
188
  Analyze the following {party} party opinions to answer the question: '{self.question}'
@@ -197,21 +214,28 @@ CRITICAL REQUIREMENTS:
197
 
198
  Return only the final consensus text that supports {maj}.
199
  """.strip(),
200
- agent=self.base_agent,
201
- expected_output=f"[BASE][PARTY={party}][MAJ={maj}]"
202
- )
203
- tasks.append(t)
 
 
 
204
  return tasks
205
 
206
  def create_supervisor_tasks(self, base_results: Dict, majorities_decision: Dict):
 
 
 
207
  tasks = []
208
- # Idiomas
209
- for lang in ["German", "French", "Italian"]:
210
- key = ('LANGUAGE', lang)
211
- if key in base_results:
212
- maj = majorities_decision.get(lang, {}).get('decision', 'AMBIGUOUS')
213
- t = Task(
214
- description=f"""
 
215
  MANDATORY INSTRUCTION: Your refined consensus MUST be based around the MAJORITY DECISION: {maj}
216
 
217
  Refine and consolidate the democratic consensus for {lang} language regarding: '{self.question}'
@@ -225,23 +249,23 @@ CRITICAL REQUIREMENTS:
225
 
226
  Return only the refined consensus in English supporting {maj}.
227
  """.strip(),
228
- agent=self.supervisor_agent,
229
- expected_output=f"[SUPERVISOR][LANG={lang}][MAJ={maj}]"
230
- )
231
- tasks.append(t)
232
-
233
- # Orientaciones (unifica partidos)
234
- orientation_consensuses = {"LEFT": [], "CENTER": [], "RIGHT": []}
235
- for (kind, name), consensus in base_results.items():
236
- if kind == 'PARTY' and name in PARTY_TO_ORIENTATION:
237
- orientation_consensuses[PARTY_TO_ORIENTATION[name]].append(consensus)
238
-
239
- for orientation in ["LEFT", "CENTER", "RIGHT"]:
240
- if orientation_consensuses[orientation]:
241
- combined = "\n\n".join([f"Consensus {i+1}: {c}" for i, c in enumerate(orientation_consensuses[orientation], 1)])
242
- maj = majorities_decision.get(f"{orientation}_MAJORITY", {}).get('decision', 'AMBIGUOUS')
243
- t = Task(
244
- description=f"""
245
  MANDATORY INSTRUCTION: Your unified consensus MUST be based around the MAJORITY DECISION: {maj}
246
 
247
  Create a unified democratic consensus for {orientation} orientation regarding: '{self.question}'
@@ -255,17 +279,24 @@ CRITICAL REQUIREMENTS:
255
 
256
  Return only the meta-consensus in English supporting {maj}.
257
  """.strip(),
258
- agent=self.supervisor_agent,
259
- expected_output=f"[SUPERVISOR][ORIENT={orientation}][MAJ={maj}]"
260
- )
261
- tasks.append(t)
 
 
 
262
  return tasks
263
 
264
  def create_director_task(self, supervisor_results: Dict, majorities_decision: Dict):
265
- all_consensuses = "\n\n".join([f"{k}: {v}" for k, v in supervisor_results.items()])
266
- final_majority = majorities_decision.get('FINAL_MAJORITY', {}).get('decision', 'AMBIGUOUS')
267
- t = Task(
268
- description=f"""
 
 
 
 
269
  SUPREME MANDATORY INSTRUCTION: Your FINAL consensus MUST be based around the ABSOLUTE MAJORITY DECISION: {final_majority}
270
 
271
  As Digital President, synthesize the FINAL DEMOCRATIC CONSENSUS for the question: '{self.question}'
@@ -280,58 +311,82 @@ CRITICAL REQUIREMENTS:
280
 
281
  Return only the final consensus supporting {final_majority}.
282
  """.strip(),
283
- agent=self.director_agent,
284
- expected_output=f"[DIRECTOR][MAJ={final_majority}]"
285
- )
286
- return t
 
 
 
287
 
288
  def run_democratic_consensus_system(self, large_strings_by_language: Dict, large_strings_by_party: Dict, majorities_decision: Dict = {}) -> Dict:
289
- # Nivel 1: Base
290
- base_tasks = self.create_base_tasks(large_strings_by_language, large_strings_by_party, majorities_decision)
291
- base_results = {}
292
- if base_tasks:
293
- base_crew = Crew(agents=[self.base_agent], tasks=base_tasks, verbose=0, share_crew=False)
294
- base_out = _silent_kickoff(base_crew)
295
- for i, task in enumerate(base_tasks):
296
- raw = base_out.tasks_output[i].raw if i < len(base_out.tasks_output) else ""
297
- tag = task.expected_output
298
- if "[LANG=" in tag:
299
- lang = tag.split("[LANG=")[1].split("]")[0]
300
- base_results[('LANGUAGE', lang)] = raw
301
- elif "[PARTY=" in tag:
302
- party = tag.split("[PARTY=")[1].split("]")[0]
303
- base_results[('PARTY', party)] = raw
304
- self.results["base_agents"] = base_results
305
-
306
- # Nivel 2: Supervisor
307
- supervisor_tasks = self.create_supervisor_tasks(base_results, majorities_decision)
308
- supervisor_results = {}
309
- if supervisor_tasks:
310
- sup_crew = Crew(agents=[self.supervisor_agent], tasks=supervisor_tasks, verbose=0, share_crew=False)
311
- sup_out = _silent_kickoff(sup_crew)
312
- for i, task in enumerate(supervisor_tasks):
313
- raw = sup_out.tasks_output[i].raw if i < len(sup_out.tasks_output) else ""
314
- tag = task.expected_output
315
- if "[LANG=" in tag:
316
- lang = tag.split("[LANG=")[1].split("]")[0]
317
- supervisor_results[('LANGUAGE', lang)] = raw
318
- elif "[ORIENT=" in tag:
319
- orient = tag.split("[ORIENT=")[1].split("]")[0]
320
- supervisor_results[('ORIENTATION', orient)] = raw
321
- self.results["supervisors"] = supervisor_results
322
-
323
- # Nivel 3: Director
324
- director_task = self.create_director_task(supervisor_results, majorities_decision)
325
- dir_crew = Crew(agents=[self.director_agent], tasks=[director_task], verbose=0, share_crew=False)
326
- dir_out = _silent_kickoff(dir_crew)
327
- final_consensus = dir_out.tasks_output[0].raw if dir_out.tasks_output else "No final consensus generated"
328
- self.results["director"] = final_consensus
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
 
330
  # Limpieza
331
  gc.collect()
332
  return self.results
333
 
334
- # Wrapper
335
  def run_democratic_consensus_system(large_strings_by_language: Dict, large_strings_by_party: Dict, question: str = "", majorities_decision: Dict = {}) -> Dict:
336
  system = DemocraticConsensusCrewAI(question=question)
337
  return system.run_democratic_consensus_system(large_strings_by_language, large_strings_by_party, majorities_decision)
@@ -341,11 +396,6 @@ def run_democratic_consensus_system(large_strings_by_language: Dict, large_strin
341
  # =========================
342
 
343
  def process_excel_and_generate_consensus(excel_file_path):
344
- """
345
- Lee el Excel subido, calcula mayorías, corre el sistema de consenso y
346
- devuelve los textos + porcentajes para mostrarlos en la UI.
347
- Se espera que el Excel tenga: ['language', 'party_reply', 'label', 'comment'].
348
- """
349
  if not excel_file_path:
350
  return ("Error: No file uploaded", "", "", "", "", "", "", "", "",
351
  "", "", "", "", "", "", "")
@@ -361,22 +411,21 @@ def process_excel_and_generate_consensus(excel_file_path):
361
  "", "", "", "", "", "", "")
362
 
363
  # Extraer pregunta si existe
364
- if 'question_y' in df.columns and df['question_y'].dropna().size:
 
365
  question = str(df['question_y'].dropna().iloc[0])
366
- elif 'question' in df.columns and df['question'].dropna().size:
367
  question = str(df['question'].dropna().iloc[0])
368
- else:
369
- question = "Question not found in Excel"
370
 
371
  # Strings por idioma y partido
372
  large_strings_by_language = (
373
  df.groupby("language")["comment"]
374
- .apply(lambda g: "'; ".join([f"Opinion {i+1}: '{c}" for i, c in enumerate(g.dropna().astype(str))]))
375
  .to_dict()
376
  )
377
  large_strings_by_party = (
378
  df.groupby("party_reply")["comment"]
379
- .apply(lambda g: "'; ".join([f"Opinion {i+1}: '{c}" for i, c in enumerate(g.dropna().astype(str))]))
380
  .to_dict()
381
  )
382
 
@@ -418,6 +467,7 @@ def process_excel_and_generate_consensus(excel_file_path):
418
  left_percentage, center_percentage, right_percentage, final_percentage)
419
 
420
  except Exception as e:
 
421
  return (f"Error processing file: {e}", "", "", "", "", "", "", "", "",
422
  "", "", "", "", "", "", "")
423
 
@@ -426,159 +476,184 @@ def process_excel_and_generate_consensus(excel_file_path):
426
  # =========================
427
 
428
  def create_consensus_app():
429
- with gr.Blocks(
430
- title="🏛️ Consensus Statements",
431
- theme=gr.themes.Base(),
432
- css="""
433
- /* Estilos de banderas */
434
  .flag-container { display: flex; justify-content: center; align-items: center; gap: 15px; margin-bottom: 10px; }
435
  .flag-image { width: 50px; height: auto; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
436
-
437
- /* Estilos de las cajas de consenso */
438
  .consensus-box { padding: 15px; border-radius: 15px; margin: 10px; min-height: 300px; max-height: 500px;
439
  overflow-y: auto; word-wrap: break-word; display: flex; align-items: flex-start; justify-content: center;
440
  text-align: center; font-family: 'Georgia', 'Times New Roman', serif; font-weight: normal; }
441
  .consensus-box-content { font-size: 1.0em; line-height: 1.4; padding: 5px; width: 100%; }
442
-
443
  .consensus-box-german { background: linear-gradient(135deg, #FFB6C1, #FFC0CB); color: #333333; }
444
  .consensus-box-french { background: linear-gradient(135deg, #E6E6FA, #DDA0DD); color: #333333; }
445
  .consensus-box-italian { background: linear-gradient(135deg, #F0E68C, #F5DEB3); color: #333333; }
446
  .consensus-box-left { background: linear-gradient(135deg, #98FB98, #90EE90); color: #333333; }
447
  .consensus-box-center { background: linear-gradient(135deg, #87CEEB, #B0E0E6); color: #333333; }
448
  .consensus-box-right { background: linear-gradient(135deg, #FFDAB9, #FFE4B5); color: #333333; }
449
-
450
- /* Estilo para la caja del consenso final */
451
  .final-consensus-box { background: linear-gradient(135deg, #A5D6A7, #66BB6A); color: #333333; padding: 30px; border-radius: 15px;
452
  margin: 20px 0; box-shadow: 0 6px 10px rgba(0,0,0,0.15); min-height: 150px; max-height: 400px; overflow-y: auto;
453
  display: flex; align-items: flex-start; justify-content: center; text-align: center; font-family: 'Georgia', 'Times New Roman', serif;
454
  font-weight: normal; font-size: 1.1em; }
455
-
456
  .question-header { background: linear-gradient(135deg, #1e3a5f, #2d5a87); color: white; padding: 15px; border-radius: 10px;
457
  text-align: center; margin-bottom: 20px; font-family: 'Georgia', 'Times New Roman', serif; font-weight: normal; }
458
-
459
  .upload-section { text-align: center; margin: 20px 0; }
460
  .gradio-container { font-family: 'Georgia', 'Times New Roman', serif; }
461
-
462
- /* Botón personalizado */
463
  .custom-button-color { background-color: #2e8b57 !important; color: #ffffff !important; border-color: #2e8b57 !important; }
464
  """
465
- ) as demo:
466
- # Título y banderas
467
- gr.HTML("""
468
- <div class="flag-container">
469
- <img src="https://upload.wikimedia.org/wikipedia/commons/b/ba/Flag_of_Germany.svg" class="flag-image" alt="German Flag">
470
- <img src="https://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg" class="flag-image" alt="French Flag">
471
- <img src="https://upload.wikimedia.org/wikipedia/commons/0/03/Flag_of_Italy.svg" class="flag-image" alt="Italian Flag">
472
- </div>
473
- <h1 style='text-align: center; font-size: 1.8em; margin-bottom: 15px;'>Consensus Statements</h1>
474
- """)
475
-
476
- # Carga
477
- with gr.Row(elem_classes=["upload-section"]):
478
- with gr.Column():
479
- excel_input = gr.File(label="📊 UPLOAD EXCEL HERE!", file_types=[".xlsx", ".xls"], type="filepath")
480
- process_btn = gr.Button("🚀 Generate Consensus", elem_classes=["custom-button-color"], size="lg")
481
-
482
- status_output = gr.Textbox(label="Status", interactive=False, value="Ready to process Excel file...")
483
- question_display = gr.HTML(value='<div class="question-header">Upload an Excel file to see the question here</div>',
484
- elem_classes=["question-header"])
485
-
486
- # Estado
487
- all_data = gr.State()
488
-
489
- # Cajas
490
- with gr.Row():
491
- with gr.Column():
492
- german_output = gr.HTML(
493
- value='<div class="consensus-box-content">Consensus Statement:<br>German-speaking cantons<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
494
- elem_classes=["consensus-box", "consensus-box-german"]
495
- )
496
- with gr.Column():
497
- french_output = gr.HTML(
498
- value='<div class="consensus-box-content">Consensus Statement:<br>French-speaking cantons<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
499
- elem_classes=["consensus-box", "consensus-box-french"]
500
- )
501
- with gr.Column():
502
- italian_output = gr.HTML(
503
- value='<div class="consensus-box-content">Consensus Statement:<br>Italian-speaking cantons<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
504
- elem_classes=["consensus-box", "consensus-box-italian"]
505
- )
506
 
507
- with gr.Row():
508
- with gr.Column():
509
- left_output = gr.HTML(
510
- value='<div class="consensus-box-content">Consensus Statement:<br>LEFT<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
511
- elem_classes=["consensus-box", "consensus-box-left"]
512
- )
513
- with gr.Column():
514
- center_output = gr.HTML(
515
- value='<div class="consensus-box-content">Consensus Statement:<br>CENTER<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
516
- elem_classes=["consensus-box", "consensus-box-center"]
517
- )
518
- with gr.Column():
519
- right_output = gr.HTML(
520
- value='<div class="consensus-box-content">Consensus Statement:<br>RIGHT<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
521
- elem_classes=["consensus-box", "consensus-box-right"]
522
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
 
524
- final_output = gr.HTML(
525
- value='<div class="consensus-box-content">FINAL - CONSENSUS STATEMENT:<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
526
- elem_classes=["final-consensus-box"]
527
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528
 
529
- # Flujo
530
- def process_and_store_data(excel_file_path):
531
- data = process_excel_and_generate_consensus(excel_file_path)
532
- return data, data
533
-
534
- def update_display(data):
535
- if not data:
536
- return ("Error processing file", "", "", "", "", "", "", "", "")
537
- (status, question, german, french, italian, left, center, right, final,
538
- german_percentage, french_percentage, italian_percentage,
539
- left_percentage, center_percentage, right_percentage, final_percentage) = data
540
-
541
- question_html = f'<div class="question-header">Question: {question}</div>'
542
- german_html = f'<div class="consensus-box-content">Consensus Statement:<br>German-speaking cantons<br><br>{german}<br><b>SUPPORTING THIS DECISION: {german_percentage}</b></div>'
543
- french_html = f'<div class="consensus-box-content">Consensus Statement:<br>French-speaking cantons<br><br>{french}<br><b>SUPPORTING THIS DECISION: {french_percentage}</b></div>'
544
- italian_html = f'<div class="consensus-box-content">Consensus Statement:<br>Italian-speaking cantons<br><br>{italian}<br><b>SUPPORTING THIS DECISION: {italian_percentage}</b></div>'
545
- left_html = f'<div class="consensus-box-content">Consensus Statement:<br>LEFT<br><br>{left}<br><b>SUPPORTING THIS DECISION: {left_percentage}</b></div>'
546
- center_html = f'<div class="consensus-box-content">Consensus Statement:<br>CENTER<br><br>{center}<br><b>SUPPORTING THIS DECISION: {center_percentage}</b></div>'
547
- right_html = f'<div class="consensus-box-content">Consensus Statement:<br>RIGHT<br><br>{right}<br><b>SUPPORTING THIS DECISION: {right_percentage}</b></div>'
548
- final_html = f'<div class="consensus-box-content">FINAL - CONSENSUS STATEMENT:<br><br>{final}<br><b>SUPPORTING THIS DECISION: {final_percentage}</b></div>'
549
-
550
- return (status, question_html, german_html, french_html, italian_html,
551
- left_html, center_html, right_html, final_html)
552
-
553
- # Encadenamiento
554
- process_btn.click(
555
- fn=process_and_store_data,
556
- inputs=[excel_input],
557
- outputs=[all_data, all_data]
558
- ).then(
559
- fn=update_display,
560
- inputs=[all_data],
561
- outputs=[status_output, question_display, german_output, french_output, italian_output,
562
- left_output, center_output, right_output, final_output]
563
  )
564
 
565
- return demo
566
-
567
- # =========================
568
- # Main - Para HuggingFace Spaces
569
- # =========================
570
  # =========================
571
  # Main - Para HuggingFace Spaces
572
  # =========================
573
  if __name__ == "__main__":
574
- app = create_consensus_app()
575
-
576
- # Configuración de lanzamiento obligatoria para HuggingFace Spaces
577
- # Usamos server_name="0.0.0.0" y server_port=7860, que son estándar en HF.
578
- app.launch(
579
- share=False,
580
- server_name="0.0.0.0",
581
- server_port=7860,
582
- debug=False,
583
- inbrowser=False
584
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  # Consenso Democrático - App para HuggingFace Spaces
3
  # =========================
4
 
 
5
  import os
6
  import io
7
  import gc
8
+ import sys
9
  import time
10
  import warnings
11
  import pandas as pd
 
21
  print(f"Python version: {sys.version}")
22
  print(f"OPENAI_API_KEY present: {bool(os.getenv('OPENAI_API_KEY'))}")
23
  if os.getenv('OPENAI_API_KEY'):
24
+ key_length = len(os.getenv('OPENAI_API_KEY'))
25
+ key_start = os.getenv('OPENAI_API_KEY')[:10]
26
+ print(f"API key length: {key_length}")
27
+ print(f"API key starts with: {key_start}...")
28
  else:
29
  print("API key is None or empty")
30
  print("=== DEBUG END ===")
31
 
32
  # --- Configuración de entorno ---
 
 
33
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
34
  if not OPENAI_API_KEY:
35
  print("WARNING: OPENAI_API_KEY not found!")
36
+ # Para HuggingFace, esto puede ser normal durante el build
37
  else:
38
  os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
39
+ print(f"API key configured successfully")
40
 
 
41
  os.environ["USER_AGENT"] = os.getenv("USER_AGENT", "ConsensusApp")
42
  os.environ['CREWAI_DO_NOT_TELEMETRY'] = 'true'
43
  os.environ['CREWAI_SHARE_CREW'] = 'false'
 
45
  # Suprimir warnings ruidosos
46
  warnings.filterwarnings("ignore", category=UserWarning, module="crewai")
47
  warnings.filterwarnings("ignore", category=DeprecationWarning)
48
+ warnings.filterwarnings("ignore", category=FutureWarning)
49
 
50
  # =========================
51
  # Datos base y utilidades
52
  # =========================
53
 
 
54
  PARTY_TO_ORIENTATION = {
55
  "FDP": "RIGHT", "jf": "RIGHT", "CVP": "CENTER", "Grüne": "LEFT", "SVP": "RIGHT",
56
  "PdA": "LEFT", "SP": "LEFT", "glp": "CENTER", "JUSO": "LEFT", "BDP": "CENTER",
 
59
  "JBDP": "CENTER", "Die": "CENTER", "JFS": "RIGHT", "SD": "RIGHT", "JM": "CENTER"
60
  }
61
 
 
62
  LLM_BASE = "gpt-4o-mini"
63
  LLM_SUPERVISOR = "gpt-4o-mini"
64
  LLM_DIRECTOR = "gpt-4o-mini"
65
 
66
  def get_majority_decision_groups(df: pd.DataFrame) -> Dict:
 
 
 
 
 
 
 
67
  result = {}
68
 
69
  def calc_decision_and_percentages(series):
 
81
  return {"decision": decision, "percent_favor": favor_pct, "percent_against": against_pct}
82
 
83
  # Por idioma
84
+ try:
85
+ for lang, group in df.groupby('language')['label']:
86
+ result[lang] = calc_decision_and_percentages(group)
87
+ except Exception as e:
88
+ print(f"Error processing languages: {e}")
89
 
90
  # Por partido
91
+ try:
92
+ for party, group in df.groupby('party_reply')['label']:
93
+ result[party] = calc_decision_and_percentages(group)
94
+ except Exception as e:
95
+ print(f"Error processing parties: {e}")
96
 
97
  # Por orientación
98
+ try:
99
+ df_or = df.copy()
100
+ df_or['orientation'] = df_or['party_reply'].map(PARTY_TO_ORIENTATION)
101
+ df_or = df_or.dropna(subset=['orientation'])
102
+ if not df_or.empty:
103
+ for orient, group in df_or.groupby('orientation')['label']:
104
+ result[f"{orient}_MAJORITY"] = calc_decision_and_percentages(group)
105
+ except Exception as e:
106
+ print(f"Error processing orientations: {e}")
107
 
108
  # Global
109
+ try:
110
+ result['FINAL_MAJORITY'] = calc_decision_and_percentages(df['label'])
111
+ except Exception as e:
112
+ print(f"Error processing final majority: {e}")
113
+
114
  return result
115
 
 
116
  def _silent_kickoff(crew: Crew):
117
  buf_out, buf_err = io.StringIO(), io.StringIO()
118
  with redirect_stdout(buf_out), redirect_stderr(buf_err):
119
+ try:
120
+ return crew.kickoff()
121
+ except Exception as e:
122
+ print(f"Crew kickoff error: {e}")
123
+ return None
124
 
125
  # =========================
126
  # Sistema Multiagente (CrewAI)
 
131
  self.question = question
132
  self.results = {"base_agents": {}, "supervisors": {}, "director": ""}
133
 
134
+ try:
135
+ self.base_agent = Agent(
136
+ role="Democratic Base Representative",
137
+ goal="Generate democratic consensus for any given group of opinions, weighting all perspectives equally.",
138
+ backstory=("You are a neutral representative. Given any group's opinions and a majority decision, "
139
+ "you craft a fair, balanced consensus that aligns with that majority, acknowledging minorities as nuances."),
140
+ verbose=False,
141
+ allow_delegation=False,
142
+ llm=LLM_BASE
143
+ )
144
+ self.supervisor_agent = Agent(
145
+ role="Democratic Supervisor",
146
+ goal="Refine and consolidate consensuses for a language or an orientation umbrella.",
147
+ backstory=("You refine base consensuses for a given umbrella (language or orientation), "
148
+ "keeping the specified majority as the core, integrating minority views as exceptions."),
149
+ verbose=False,
150
+ allow_delegation=False,
151
+ llm=LLM_SUPERVISOR
152
+ )
153
+ self.director_agent = Agent(
154
+ role="Digital President - Final Decision Maker",
155
+ goal="Synthesize all supervisor consensuses into a final consensus that aligns with the absolute majority.",
156
+ backstory=("You aggregate all umbrellas and issue a final democratic position respecting the global majority."),
157
+ verbose=False,
158
+ allow_delegation=False,
159
+ llm=LLM_DIRECTOR
160
+ )
161
+ except Exception as e:
162
+ print(f"Error initializing agents: {e}")
163
+ # Crear agentes básicos como fallback
164
+ self.base_agent = None
165
+ self.supervisor_agent = None
166
+ self.director_agent = None
167
 
 
168
  def create_base_tasks(self, large_strings_by_language: Dict, large_strings_by_party: Dict, majorities_decision: Dict):
169
+ if not self.base_agent:
170
+ return []
171
+
172
  tasks = []
173
+ try:
174
+ # Por idioma
175
+ for language, opinions_text in large_strings_by_language.items():
176
+ maj = majorities_decision.get(language, {}).get('decision', 'AMBIGUOUS')
177
+ t = Task(
178
+ description=f"""
179
  MANDATORY INSTRUCTION: The consensus you generate MUST be based around the MAJORITY DECISION: {maj}
180
 
181
  Analyze the following {language} language opinions to answer the question: '{self.question}'
 
190
 
191
  Return only the final consensus text that supports {maj}.
192
  """.strip(),
193
+ agent=self.base_agent,
194
+ expected_output=f"[BASE][LANG={language}][MAJ={maj}]"
195
+ )
196
+ tasks.append(t)
197
 
198
+ # Por partido
199
+ for party, opinions_text in large_strings_by_party.items():
200
+ maj = majorities_decision.get(party, {}).get('decision', 'AMBIGUOUS')
201
+ t = Task(
202
+ description=f"""
203
  MANDATORY INSTRUCTION: The consensus you generate MUST be based around the MAJORITY DECISION: {maj}
204
 
205
  Analyze the following {party} party opinions to answer the question: '{self.question}'
 
214
 
215
  Return only the final consensus text that supports {maj}.
216
  """.strip(),
217
+ agent=self.base_agent,
218
+ expected_output=f"[BASE][PARTY={party}][MAJ={maj}]"
219
+ )
220
+ tasks.append(t)
221
+ except Exception as e:
222
+ print(f"Error creating base tasks: {e}")
223
+
224
  return tasks
225
 
226
  def create_supervisor_tasks(self, base_results: Dict, majorities_decision: Dict):
227
+ if not self.supervisor_agent:
228
+ return []
229
+
230
  tasks = []
231
+ try:
232
+ # Idiomas
233
+ for lang in ["German", "French", "Italian"]:
234
+ key = ('LANGUAGE', lang)
235
+ if key in base_results:
236
+ maj = majorities_decision.get(lang, {}).get('decision', 'AMBIGUOUS')
237
+ t = Task(
238
+ description=f"""
239
  MANDATORY INSTRUCTION: Your refined consensus MUST be based around the MAJORITY DECISION: {maj}
240
 
241
  Refine and consolidate the democratic consensus for {lang} language regarding: '{self.question}'
 
249
 
250
  Return only the refined consensus in English supporting {maj}.
251
  """.strip(),
252
+ agent=self.supervisor_agent,
253
+ expected_output=f"[SUPERVISOR][LANG={lang}][MAJ={maj}]"
254
+ )
255
+ tasks.append(t)
256
+
257
+ # Orientaciones (unifica partidos)
258
+ orientation_consensuses = {"LEFT": [], "CENTER": [], "RIGHT": []}
259
+ for (kind, name), consensus in base_results.items():
260
+ if kind == 'PARTY' and name in PARTY_TO_ORIENTATION:
261
+ orientation_consensuses[PARTY_TO_ORIENTATION[name]].append(consensus)
262
+
263
+ for orientation in ["LEFT", "CENTER", "RIGHT"]:
264
+ if orientation_consensuses[orientation]:
265
+ combined = "\n\n".join([f"Consensus {i+1}: {c}" for i, c in enumerate(orientation_consensuses[orientation], 1)])
266
+ maj = majorities_decision.get(f"{orientation}_MAJORITY", {}).get('decision', 'AMBIGUOUS')
267
+ t = Task(
268
+ description=f"""
269
  MANDATORY INSTRUCTION: Your unified consensus MUST be based around the MAJORITY DECISION: {maj}
270
 
271
  Create a unified democratic consensus for {orientation} orientation regarding: '{self.question}'
 
279
 
280
  Return only the meta-consensus in English supporting {maj}.
281
  """.strip(),
282
+ agent=self.supervisor_agent,
283
+ expected_output=f"[SUPERVISOR][ORIENT={orientation}][MAJ={maj}]"
284
+ )
285
+ tasks.append(t)
286
+ except Exception as e:
287
+ print(f"Error creating supervisor tasks: {e}")
288
+
289
  return tasks
290
 
291
  def create_director_task(self, supervisor_results: Dict, majorities_decision: Dict):
292
+ if not self.director_agent:
293
+ return None
294
+
295
+ try:
296
+ all_consensuses = "\n\n".join([f"{k}: {v}" for k, v in supervisor_results.items()])
297
+ final_majority = majorities_decision.get('FINAL_MAJORITY', {}).get('decision', 'AMBIGUOUS')
298
+ t = Task(
299
+ description=f"""
300
  SUPREME MANDATORY INSTRUCTION: Your FINAL consensus MUST be based around the ABSOLUTE MAJORITY DECISION: {final_majority}
301
 
302
  As Digital President, synthesize the FINAL DEMOCRATIC CONSENSUS for the question: '{self.question}'
 
311
 
312
  Return only the final consensus supporting {final_majority}.
313
  """.strip(),
314
+ agent=self.director_agent,
315
+ expected_output=f"[DIRECTOR][MAJ={final_majority}]"
316
+ )
317
+ return t
318
+ except Exception as e:
319
+ print(f"Error creating director task: {e}")
320
+ return None
321
 
322
  def run_democratic_consensus_system(self, large_strings_by_language: Dict, large_strings_by_party: Dict, majorities_decision: Dict = {}) -> Dict:
323
+ try:
324
+ # Nivel 1: Base
325
+ base_tasks = self.create_base_tasks(large_strings_by_language, large_strings_by_party, majorities_decision)
326
+ base_results = {}
327
+ if base_tasks and self.base_agent:
328
+ base_crew = Crew(agents=[self.base_agent], tasks=base_tasks, verbose=0, share_crew=False)
329
+ base_out = _silent_kickoff(base_crew)
330
+ if base_out and hasattr(base_out, 'tasks_output'):
331
+ for i, task in enumerate(base_tasks):
332
+ raw = base_out.tasks_output[i].raw if i < len(base_out.tasks_output) else ""
333
+ tag = task.expected_output
334
+ if "[LANG=" in tag:
335
+ lang = tag.split("[LANG=")[1].split("]")[0]
336
+ base_results[('LANGUAGE', lang)] = raw
337
+ elif "[PARTY=" in tag:
338
+ party = tag.split("[PARTY=")[1].split("]")[0]
339
+ base_results[('PARTY', party)] = raw
340
+ self.results["base_agents"] = base_results
341
+
342
+ # Nivel 2: Supervisor
343
+ supervisor_tasks = self.create_supervisor_tasks(base_results, majorities_decision)
344
+ supervisor_results = {}
345
+ if supervisor_tasks and self.supervisor_agent:
346
+ sup_crew = Crew(agents=[self.supervisor_agent], tasks=supervisor_tasks, verbose=0, share_crew=False)
347
+ sup_out = _silent_kickoff(sup_crew)
348
+ if sup_out and hasattr(sup_out, 'tasks_output'):
349
+ for i, task in enumerate(supervisor_tasks):
350
+ raw = sup_out.tasks_output[i].raw if i < len(sup_out.tasks_output) else ""
351
+ tag = task.expected_output
352
+ if "[LANG=" in tag:
353
+ lang = tag.split("[LANG=")[1].split("]")[0]
354
+ supervisor_results[('LANGUAGE', lang)] = raw
355
+ elif "[ORIENT=" in tag:
356
+ orient = tag.split("[ORIENT=")[1].split("]")[0]
357
+ supervisor_results[('ORIENTATION', orient)] = raw
358
+ self.results["supervisors"] = supervisor_results
359
+
360
+ # Nivel 3: Director
361
+ director_task = self.create_director_task(supervisor_results, majorities_decision)
362
+ final_consensus = "No final consensus generated"
363
+ if director_task and self.director_agent:
364
+ dir_crew = Crew(agents=[self.director_agent], tasks=[director_task], verbose=0, share_crew=False)
365
+ dir_out = _silent_kickoff(dir_crew)
366
+ if dir_out and hasattr(dir_out, 'tasks_output') and dir_out.tasks_output:
367
+ final_consensus = dir_out.tasks_output[0].raw
368
+ self.results["director"] = final_consensus
369
+
370
+ except Exception as e:
371
+ print(f"Error in consensus system: {e}")
372
+ # Generar resultados de fallback
373
+ self.results = {
374
+ "base_agents": {},
375
+ "supervisors": {
376
+ ('LANGUAGE', 'German'): "Error: Unable to generate consensus due to system issues.",
377
+ ('LANGUAGE', 'French'): "Error: Unable to generate consensus due to system issues.",
378
+ ('LANGUAGE', 'Italian'): "Error: Unable to generate consensus due to system issues.",
379
+ ('ORIENTATION', 'LEFT'): "Error: Unable to generate consensus due to system issues.",
380
+ ('ORIENTATION', 'CENTER'): "Error: Unable to generate consensus due to system issues.",
381
+ ('ORIENTATION', 'RIGHT'): "Error: Unable to generate consensus due to system issues."
382
+ },
383
+ "director": "Error: Unable to generate final consensus due to system issues."
384
+ }
385
 
386
  # Limpieza
387
  gc.collect()
388
  return self.results
389
 
 
390
  def run_democratic_consensus_system(large_strings_by_language: Dict, large_strings_by_party: Dict, question: str = "", majorities_decision: Dict = {}) -> Dict:
391
  system = DemocraticConsensusCrewAI(question=question)
392
  return system.run_democratic_consensus_system(large_strings_by_language, large_strings_by_party, majorities_decision)
 
396
  # =========================
397
 
398
  def process_excel_and_generate_consensus(excel_file_path):
 
 
 
 
 
399
  if not excel_file_path:
400
  return ("Error: No file uploaded", "", "", "", "", "", "", "", "",
401
  "", "", "", "", "", "", "")
 
411
  "", "", "", "", "", "", "")
412
 
413
  # Extraer pregunta si existe
414
+ question = "Question not found in Excel"
415
+ if 'question_y' in df.columns and not df['question_y'].dropna().empty:
416
  question = str(df['question_y'].dropna().iloc[0])
417
+ elif 'question' in df.columns and not df['question'].dropna().empty:
418
  question = str(df['question'].dropna().iloc[0])
 
 
419
 
420
  # Strings por idioma y partido
421
  large_strings_by_language = (
422
  df.groupby("language")["comment"]
423
+ .apply(lambda g: "'; ".join([f"Opinion {i+1}: '{str(c)}" for i, c in enumerate(g.dropna())]))
424
  .to_dict()
425
  )
426
  large_strings_by_party = (
427
  df.groupby("party_reply")["comment"]
428
+ .apply(lambda g: "'; ".join([f"Opinion {i+1}: '{str(c)}" for i, c in enumerate(g.dropna())]))
429
  .to_dict()
430
  )
431
 
 
467
  left_percentage, center_percentage, right_percentage, final_percentage)
468
 
469
  except Exception as e:
470
+ print(f"Error processing Excel: {e}")
471
  return (f"Error processing file: {e}", "", "", "", "", "", "", "", "",
472
  "", "", "", "", "", "", "")
473
 
 
476
  # =========================
477
 
478
  def create_consensus_app():
479
+ try:
480
+ demo = gr.Blocks(
481
+ title="🏛️ Consensus Statements",
482
+ theme=gr.themes.Default(),
483
+ css="""
484
  .flag-container { display: flex; justify-content: center; align-items: center; gap: 15px; margin-bottom: 10px; }
485
  .flag-image { width: 50px; height: auto; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
 
 
486
  .consensus-box { padding: 15px; border-radius: 15px; margin: 10px; min-height: 300px; max-height: 500px;
487
  overflow-y: auto; word-wrap: break-word; display: flex; align-items: flex-start; justify-content: center;
488
  text-align: center; font-family: 'Georgia', 'Times New Roman', serif; font-weight: normal; }
489
  .consensus-box-content { font-size: 1.0em; line-height: 1.4; padding: 5px; width: 100%; }
 
490
  .consensus-box-german { background: linear-gradient(135deg, #FFB6C1, #FFC0CB); color: #333333; }
491
  .consensus-box-french { background: linear-gradient(135deg, #E6E6FA, #DDA0DD); color: #333333; }
492
  .consensus-box-italian { background: linear-gradient(135deg, #F0E68C, #F5DEB3); color: #333333; }
493
  .consensus-box-left { background: linear-gradient(135deg, #98FB98, #90EE90); color: #333333; }
494
  .consensus-box-center { background: linear-gradient(135deg, #87CEEB, #B0E0E6); color: #333333; }
495
  .consensus-box-right { background: linear-gradient(135deg, #FFDAB9, #FFE4B5); color: #333333; }
 
 
496
  .final-consensus-box { background: linear-gradient(135deg, #A5D6A7, #66BB6A); color: #333333; padding: 30px; border-radius: 15px;
497
  margin: 20px 0; box-shadow: 0 6px 10px rgba(0,0,0,0.15); min-height: 150px; max-height: 400px; overflow-y: auto;
498
  display: flex; align-items: flex-start; justify-content: center; text-align: center; font-family: 'Georgia', 'Times New Roman', serif;
499
  font-weight: normal; font-size: 1.1em; }
 
500
  .question-header { background: linear-gradient(135deg, #1e3a5f, #2d5a87); color: white; padding: 15px; border-radius: 10px;
501
  text-align: center; margin-bottom: 20px; font-family: 'Georgia', 'Times New Roman', serif; font-weight: normal; }
 
502
  .upload-section { text-align: center; margin: 20px 0; }
503
  .gradio-container { font-family: 'Georgia', 'Times New Roman', serif; }
 
 
504
  .custom-button-color { background-color: #2e8b57 !important; color: #ffffff !important; border-color: #2e8b57 !important; }
505
  """
506
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
 
508
+ with demo:
509
+ # Título y banderas
510
+ gr.HTML("""
511
+ <div class="flag-container">
512
+ <img src="https://upload.wikimedia.org/wikipedia/commons/b/ba/Flag_of_Germany.svg" class="flag-image" alt="German Flag">
513
+ <img src="https://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg" class="flag-image" alt="French Flag">
514
+ <img src="https://upload.wikimedia.org/wikipedia/commons/0/03/Flag_of_Italy.svg" class="flag-image" alt="Italian Flag">
515
+ </div>
516
+ <h1 style='text-align: center; font-size: 1.8em; margin-bottom: 15px;'>Consensus Statements</h1>
517
+ """)
518
+
519
+ # Carga
520
+ with gr.Row(elem_classes=["upload-section"]):
521
+ with gr.Column():
522
+ excel_input = gr.File(label="📊 UPLOAD EXCEL HERE!", file_types=[".xlsx", ".xls"], type="filepath")
523
+ process_btn = gr.Button("🚀 Generate Consensus", elem_classes=["custom-button-color"], size="lg")
524
+
525
+ status_output = gr.Textbox(label="Status", interactive=False, value="Ready to process Excel file...")
526
+ question_display = gr.HTML(value='<div class="question-header">Upload an Excel file to see the question here</div>',
527
+ elem_classes=["question-header"])
528
+
529
+ # Estado
530
+ all_data = gr.State()
531
+
532
+ # Cajas
533
+ with gr.Row():
534
+ with gr.Column():
535
+ german_output = gr.HTML(
536
+ value='<div class="consensus-box-content">Consensus Statement:<br>German-speaking cantons<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
537
+ elem_classes=["consensus-box", "consensus-box-german"]
538
+ )
539
+ with gr.Column():
540
+ french_output = gr.HTML(
541
+ value='<div class="consensus-box-content">Consensus Statement:<br>French-speaking cantons<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
542
+ elem_classes=["consensus-box", "consensus-box-french"]
543
+ )
544
+ with gr.Column():
545
+ italian_output = gr.HTML(
546
+ value='<div class="consensus-box-content">Consensus Statement:<br>Italian-speaking cantons<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
547
+ elem_classes=["consensus-box", "consensus-box-italian"]
548
+ )
549
+
550
+ with gr.Row():
551
+ with gr.Column():
552
+ left_output = gr.HTML(
553
+ value='<div class="consensus-box-content">Consensus Statement:<br>LEFT<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
554
+ elem_classes=["consensus-box", "consensus-box-left"]
555
+ )
556
+ with gr.Column():
557
+ center_output = gr.HTML(
558
+ value='<div class="consensus-box-content">Consensus Statement:<br>CENTER<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
559
+ elem_classes=["consensus-box", "consensus-box-center"]
560
+ )
561
+ with gr.Column():
562
+ right_output = gr.HTML(
563
+ value='<div class="consensus-box-content">Consensus Statement:<br>RIGHT<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
564
+ elem_classes=["consensus-box", "consensus-box-right"]
565
+ )
566
+
567
+ final_output = gr.HTML(
568
+ value='<div class="consensus-box-content">FINAL - CONSENSUS STATEMENT:<br><br>Upload Excel file to see results<br><b>SUPPORTING THIS DECISION: --</b></div>',
569
+ elem_classes=["final-consensus-box"]
570
+ )
571
 
572
+ # Flujo
573
+ def process_and_store_data(excel_file_path):
574
+ data = process_excel_and_generate_consensus(excel_file_path)
575
+ return data, data
576
+
577
+ def update_display(data):
578
+ if not data:
579
+ return ("Error processing file", "", "", "", "", "", "", "", "")
580
+ (status, question, german, french, italian, left, center, right, final,
581
+ german_percentage, french_percentage, italian_percentage,
582
+ left_percentage, center_percentage, right_percentage, final_percentage) = data
583
+
584
+ question_html = f'<div class="question-header">Question: {question}</div>'
585
+ german_html = f'<div class="consensus-box-content">Consensus Statement:<br>German-speaking cantons<br><br>{german}<br><b>SUPPORTING THIS DECISION: {german_percentage}</b></div>'
586
+ french_html = f'<div class="consensus-box-content">Consensus Statement:<br>French-speaking cantons<br><br>{french}<br><b>SUPPORTING THIS DECISION: {french_percentage}</b></div>'
587
+ italian_html = f'<div class="consensus-box-content">Consensus Statement:<br>Italian-speaking cantons<br><br>{italian}<br><b>SUPPORTING THIS DECISION: {italian_percentage}</b></div>'
588
+ left_html = f'<div class="consensus-box-content">Consensus Statement:<br>LEFT<br><br>{left}<br><b>SUPPORTING THIS DECISION: {left_percentage}</b></div>'
589
+ center_html = f'<div class="consensus-box-content">Consensus Statement:<br>CENTER<br><br>{center}<br><b>SUPPORTING THIS DECISION: {center_percentage}</b></div>'
590
+ right_html = f'<div class="consensus-box-content">Consensus Statement:<br>RIGHT<br><br>{right}<br><b>SUPPORTING THIS DECISION: {right_percentage}</b></div>'
591
+ final_html = f'<div class="consensus-box-content">FINAL - CONSENSUS STATEMENT:<br><br>{final}<br><b>SUPPORTING THIS DECISION: {final_percentage}</b></div>'
592
+
593
+ return (status, question_html, german_html, french_html, italian_html,
594
+ left_html, center_html, right_html, final_html)
595
+
596
+ # Encadenamiento
597
+ process_btn.click(
598
+ fn=process_and_store_data,
599
+ inputs=[excel_input],
600
+ outputs=[all_data, all_data]
601
+ ).then(
602
+ fn=update_display,
603
+ inputs=[all_data],
604
+ outputs=[status_output, question_display, german_output, french_output, italian_output,
605
+ left_output, center_output, right_output, final_output]
606
+ )
607
 
608
+ return demo
609
+ except Exception as e:
610
+ print(f"Error creating Gradio app: {e}")
611
+ # Crear una interfaz de fallback muy simple
612
+ def simple_interface():
613
+ return "Error: Unable to create full interface. Please check system configuration."
614
+
615
+ return gr.Interface(
616
+ fn=simple_interface,
617
+ inputs=[],
618
+ outputs="text",
619
+ title="Consensus App - Error Mode"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
620
  )
621
 
 
 
 
 
 
622
  # =========================
623
  # Main - Para HuggingFace Spaces
624
  # =========================
625
  if __name__ == "__main__":
626
+ try:
627
+ print("Creating Gradio app...")
628
+ app = create_consensus_app()
629
+
630
+ print("Launching app...")
631
+ # Configuración específica para HuggingFace Spaces
632
+ app.launch(
633
+ share=False, # HuggingFace maneja el sharing
634
+ server_name="0.0.0.0",
635
+ server_port=7860,
636
+ debug=False,
637
+ inbrowser=False,
638
+ show_error=True,
639
+ quiet=False
640
+ )
641
+ except Exception as e:
642
+ print(f"Critical error launching app: {e}")
643
+ # Último recurso: crear interfaz básica
644
+ def emergency_interface():
645
+ return f"Application Error: {str(e)}"
646
+
647
+ emergency_app = gr.Interface(
648
+ fn=emergency_interface,
649
+ inputs=[],
650
+ outputs="text",
651
+ title="Emergency Mode"
652
+ )
653
+ emergency_app.launch(
654
+ share=False,
655
+ server_name="0.0.0.0",
656
+ server_port=7860,
657
+ debug=False,
658
+ inbrowser=False
659
+ )
requirements.txt CHANGED
@@ -1,5 +1,5 @@
1
- gradio==4.44.1 # Versión específica para corregir el bug de compatibilidad
2
  pandas>=2.0.0
3
  openpyxl>=3.1.0
4
- crewai>=0.28.0
5
- openai>=1.0.0
 
1
+ gradio==4.44.0
2
  pandas>=2.0.0
3
  openpyxl>=3.1.0
4
+ crewai>=0.28.0,<1.0.0
5
+ openai>=1.0.0,<2.0.0