yoshizen commited on
Commit
ecb4e3d
·
verified ·
1 Parent(s): ac9c0a8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +176 -154
app.py CHANGED
@@ -4,11 +4,7 @@ import pandas as pd
4
  import torch
5
  import gradio as gr
6
  from tqdm import tqdm
7
- from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
8
- from llama_index.core import Settings
9
- from llama_index.core.tools import FunctionTool
10
- from llama_index.core.agent import ReActAgent
11
- from llama_index.llms.huggingface import HuggingFaceLLM
12
  from typing import List, Dict, Any, Tuple, Optional
13
  import json
14
  import ast
@@ -18,6 +14,7 @@ import io
18
  import base64
19
  import logging
20
  import time
 
21
 
22
  # Настройка логирования
23
  logging.basicConfig(level=logging.INFO)
@@ -29,64 +26,38 @@ MODEL_NAME = "google/flan-t5-xxl"
29
  API_RETRIES = 3
30
  API_TIMEOUT = 45
31
 
32
- # === ИСПРАВЛЕННОЕ ЯДРО СИСТЕМЫ ===
33
  class GAIAThoughtProcessor:
34
  def __init__(self):
 
 
 
35
  # Оптимизированная загрузка модели
36
- self.llm = HuggingFaceLLM(
37
- model_name=MODEL_NAME,
38
- tokenizer_name=MODEL_NAME,
39
- context_window=2048,
40
- max_new_tokens=512,
41
  device_map="auto",
42
- model_kwargs={
43
- "torch_dtype": torch.float16,
44
- "load_in_4bit": True,
45
- "device_map": "auto"
46
- },
47
- generate_kwargs={"temperature": 0.01, "do_sample": False}
48
- )
49
- self.tools = self._create_gaia_tools()
50
- self.agent = ReActAgent.from_tools(
51
- self.tools,
52
- llm=self.llm,
53
- verbose=True,
54
- max_iterations=10,
55
- react_mode="plan_and_solve"
56
  )
57
- logger.info("⚙️ Инициализирован GAIAThoughtProcessor с %d инструментами", len(self.tools))
58
-
59
- def _create_gaia_tools(self) -> List[FunctionTool]:
60
- """Создает инструменты, соответствующие спецификации GAIA"""
61
- return [
62
- FunctionTool.from_defaults(
63
- fn=self._math_solver,
64
- name="math_solver",
65
- description="Вычисляет математические выражения. Ввод: строка с выражением (например, '2+2*3')"
66
- ),
67
- FunctionTool.from_defaults(
68
- fn=self._table_analyzer,
69
- name="table_analyzer",
70
- description="Анализирует табличные данные. Ввод: (table_data:str, query:str)"
71
- ),
72
- FunctionTool.from_defaults(
73
- fn=self._text_processor,
74
- name="text_processor",
75
- description="Операции с текстом: reverse, count_words, extract_numbers. Ввод: (text:str, operation:str)"
76
- ),
77
- FunctionTool.from_defaults(
78
- fn=self._image_processor,
79
- name="image_processor",
80
- description="Анализирует изображения. Ввод: base64 изображения или URL"
81
- )
82
- ]
83
 
84
  def _math_solver(self, expression: str) -> str:
85
  """Безопасное вычисление математических выражений"""
86
  try:
87
  # Очистка выражения
88
  clean_expr = re.sub(r"[^0-9+\-*/().^√π]", "", expression)
89
- # Поддержка математических констант и функций
90
  context = {
91
  "sqrt": np.sqrt,
92
  "log": np.log,
@@ -99,13 +70,13 @@ class GAIAThoughtProcessor:
99
  }
100
  return str(eval(clean_expr, {"__builtins__": None}, context))
101
  except Exception as e:
102
- logger.error("Math error: %s", e)
103
  return f"Math Error: {str(e)}"
104
 
105
  def _table_analyzer(self, table_data: str, query: str) -> str:
106
- """Анализ табличных данных с поддержкой сложных запросов"""
107
  try:
108
- # Определение формата таблицы
109
  if "\t" in table_data:
110
  df = pd.read_csv(io.StringIO(table_data), sep="\t")
111
  elif "," in table_data:
@@ -113,30 +84,26 @@ class GAIAThoughtProcessor:
113
  else:
114
  df = pd.read_fwf(io.StringIO(table_data))
115
 
116
- # Выполнение pandas-запроса
117
- if "sum" in query.lower():
 
118
  return str(df.sum(numeric_only=True).to_dict())
119
- elif "mean" in query.lower():
120
  return str(df.mean(numeric_only=True).to_dict())
121
- elif "max" in query.lower():
122
  return str(df.max(numeric_only=True).to_dict())
123
- elif "min" in query.lower():
124
  return str(df.min(numeric_only=True).to_dict())
125
- elif "count" in query.lower():
126
  return str(df.count().to_dict())
127
  else:
128
- # Обработка пользовательских запросов
129
- try:
130
- result = df.query(query)
131
- return result.to_string()
132
- except:
133
- return df.describe().to_string()
134
  except Exception as e:
135
- logger.error("Table error: %s", e)
136
  return f"Table Error: {str(e)}"
137
 
138
  def _text_processor(self, text: str, operation: str) -> str:
139
- """Операции с текстом с поддержкой GAIA спецификации"""
140
  operation = operation.lower()
141
  if operation == "reverse":
142
  return text[::-1]
@@ -152,7 +119,7 @@ class GAIAThoughtProcessor:
152
  return f"Unsupported operation: {operation}"
153
 
154
  def _image_processor(self, image_input: str) -> str:
155
- """Обработка изображений с поддержкой URL и base64"""
156
  try:
157
  # Обработка URL
158
  if image_input.startswith("http"):
@@ -168,58 +135,107 @@ class GAIAThoughtProcessor:
168
  else:
169
  return "Invalid image format"
170
 
171
- # Анализ изображения
172
  description = (
173
  f"Format: {img.format}, Size: {img.size}, "
174
  f"Mode: {img.mode}, Colors: {len(set(img.getdata()))}"
175
  )
176
  return description
177
  except (UnidentifiedImageError, requests.exceptions.RequestException) as e:
178
- logger.error("Image processing error: %s", e)
179
  return f"Image Error: {str(e)}"
180
  except Exception as e:
181
  logger.exception("Unexpected image error")
182
  return f"Unexpected Error: {str(e)}"
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  def process_question(self, question: str, task_id: str) -> str:
185
- """Обработка вопроса с учетом спецификации GAIA"""
186
  try:
187
- # Декомпозиция задачи
188
  decomposition_prompt = (
189
- f"Декомпозируй задачу GAIA ({task_id}) на шаги:\n{question}\n\n"
190
- "Шаги (разделены точкой с запятой):"
 
 
191
  )
192
- steps_response = self.llm.complete(decomposition_prompt)
193
- steps = [s.strip() for s in steps_response.text.split(";") if s.strip()]
194
 
195
- # Выполнение шагов
 
 
 
196
  results = []
197
  for step in steps:
198
  if step:
199
  try:
200
- result = self.agent.chat(step)
201
- results.append(f"{step}: {result}")
 
 
 
 
 
 
 
202
  except Exception as e:
203
- results.append(f"{step}: ERROR - {str(e)}")
204
 
205
- # Синтез финального ответа
206
  synthesis_prompt = (
207
  f"Задача GAIA {task_id}:\n{question}\n\n"
208
  "Выполненные шаги:\n" + "\n".join(results) +
209
- "\n\nФинальный ответ в формате JSON:"
210
  )
211
- final_response = self.llm.complete(synthesis_prompt)
 
212
 
213
  # Извлечение чистого ответа
214
- answer_match = re.search(r'\{.*\}', final_response.text, re.DOTALL)
215
- if answer_match:
216
- return answer_match.group(0)
217
  else:
218
- return json.dumps({
219
- "final_answer": final_response.text.strip(),
220
- "task_id": task_id,
221
- "reasoning_steps": results
222
- })
 
223
  except Exception as e:
224
  logger.exception("Processing failed")
225
  return json.dumps({
@@ -228,7 +244,7 @@ class GAIAThoughtProcessor:
228
  "final_answer": f"SYSTEM ERROR: {str(e)}"
229
  })
230
 
231
- # === ИСПРАВЛЕННАЯ СИСТЕМА ОЦЕНКИ ===
232
  class GAIAEvaluationRunner:
233
  def __init__(self, api_url: str = DEFAULT_API_URL):
234
  self.api_url = api_url
@@ -240,7 +256,7 @@ class GAIAEvaluationRunner:
240
  "User-Agent": "GAIA-Mastermind/1.0",
241
  "Content-Type": "application/json"
242
  })
243
- logger.info("🌐 Инициализирован GAIAEvaluationRunner для %s", api_url)
244
 
245
  def run_evaluation(self, agent, username: str, agent_code: str, progress=tqdm):
246
  # Получение вопросов
@@ -253,27 +269,22 @@ class GAIAEvaluationRunner:
253
  answers = []
254
  for i, q in enumerate(progress(questions, desc="🧠 Processing GAIA")):
255
  try:
256
- # GAIA-specific: task_id обязателен
257
  task_id = q.get("task_id", f"unknown_{i}")
258
-
259
- # Обработка вопроса
260
  json_response = agent.process_question(q["question"], task_id)
261
 
262
- # Парсинг и валидация ответа
263
  try:
264
  response_obj = json.loads(json_response)
265
  final_answer = response_obj.get("final_answer", "")
266
-
267
- # GAIA-требование: ответ должен быть строкой
268
  if not isinstance(final_answer, str):
269
  final_answer = str(final_answer)
270
  except json.JSONDecodeError:
271
  final_answer = json_response
272
 
273
- # Формирование ответа согласно GAIA API
274
  answers.append({
275
  "task_id": task_id,
276
- "answer": final_answer[:500] # GAIA limitation
277
  })
278
 
279
  # Запись результатов
@@ -284,7 +295,7 @@ class GAIAEvaluationRunner:
284
  "Status": "Processed"
285
  })
286
  except Exception as e:
287
- logger.error("Task %s failed: %s", task_id, e)
288
  answers.append({
289
  "task_id": task_id,
290
  "answer": f"ERROR: {str(e)}"
@@ -301,7 +312,7 @@ class GAIAEvaluationRunner:
301
  return submission_result, score, len(questions), pd.DataFrame(results)
302
 
303
  def _fetch_questions(self) -> Tuple[list, str]:
304
- """Получение вопросов с обработкой GAIA спецификации"""
305
  for _ in range(API_RETRIES):
306
  try:
307
  response = self.session.get(
@@ -309,17 +320,14 @@ class GAIAEvaluationRunner:
309
  timeout=API_TIMEOUT
310
  )
311
 
312
- # Обработка GAIA статусов
313
  if response.status_code == 200:
314
  questions = response.json()
315
  if not isinstance(questions, list):
316
  return [], "Invalid response format: expected list"
317
 
318
- # Обогащение данных для мультимодальных задач
319
  for q in questions:
320
  q.setdefault("task_id", f"id_{hash(q['question']) % 100000}")
321
- if "image" in q:
322
- q["question"] = f"[IMAGE] {q['question']}"
323
  return questions, "success"
324
 
325
  elif response.status_code == 429:
@@ -327,20 +335,17 @@ class GAIAEvaluationRunner:
327
  time.sleep(5)
328
  continue
329
 
330
- elif response.status_code == 404:
331
- return [], "API endpoint not found"
332
-
333
  else:
334
  return [], f"API error: HTTP {response.status_code}"
335
 
336
  except Exception as e:
337
- logger.error("Fetch error: %s", e)
338
  return [], f"Connection error: {str(e)}"
339
 
340
  return [], "API unavailable after retries"
341
 
342
  def _submit_answers(self, username: str, agent_code: str, answers: list) -> Tuple[str, int]:
343
- """Отправка ответов согласно GAIA API спецификации"""
344
  payload = {
345
  "username": username.strip(),
346
  "agent_code": agent_code.strip(),
@@ -355,7 +360,6 @@ class GAIAEvaluationRunner:
355
  timeout=API_TIMEOUT * 2
356
  )
357
 
358
- # Обработка GAIA статусов
359
  if response.status_code == 200:
360
  result = response.json()
361
  score = result.get("score", 0)
@@ -363,7 +367,7 @@ class GAIAEvaluationRunner:
363
 
364
  elif response.status_code == 400:
365
  error = response.json().get("error", "Invalid request")
366
- logger.error("Validation error: %s", error)
367
  return f"Validation Error: {error}", 0
368
 
369
  elif response.status_code == 429:
@@ -375,12 +379,12 @@ class GAIAEvaluationRunner:
375
  return f"HTTP Error {response.status_code}", 0
376
 
377
  except Exception as e:
378
- logger.error("Submit error: %s", e)
379
  return f"Connection Error: {str(e)}", 0
380
 
381
  return "Submission failed after retries", 0
382
 
383
- # === ОПТИМИЗИРОВАННЫЙ ИНТЕРФЕЙС ===
384
  def run_evaluation(username: str, agent_code: str, progress=gr.Progress()):
385
  progress(0, desc="⚡ Инициализация GAIA Mastermind...")
386
  try:
@@ -392,33 +396,58 @@ def run_evaluation(username: str, agent_code: str, progress=gr.Progress()):
392
  progress(0.1, desc="🌐 Подключение к GAIA API...")
393
  runner = GAIAEvaluationRunner()
394
 
395
- # Обертка tqdm для Gradio
396
- class ProgressWrapper:
397
- def __init__(self, total, progress):
398
- self.total = total
399
- self.progress = progress
400
- self.current = 0
 
 
 
 
 
 
 
 
 
401
 
402
- def update(self, n=1):
403
- self.current += n
404
- self.progress(self.current / self.total, desc=f"🧠 Обработка задач ({self.current}/{self.total})")
 
 
 
405
 
406
- def __iter__(self):
407
- return self
 
 
408
 
409
- def __next__(self):
410
- if self.current >= self.total:
411
- raise StopIteration
412
- return self.current
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
 
414
- return runner.run_evaluation(
415
- agent,
416
- username,
417
- agent_code,
418
- progress=ProgressWrapper
419
- )
420
 
421
- # === ИНТЕЛЛЕКТУАЛЬНЫЙ ИНТЕРФЕЙС ===
422
  with gr.Blocks(
423
  title="🧠 GAIA Mastermind",
424
  theme=gr.themes.Soft(),
@@ -431,8 +460,8 @@ with gr.Blocks(
431
  <div style="text-align:center; background: linear-gradient(135deg, #0f2027, #203a43);
432
  padding: 20px; border-radius: 15px; color: white; box-shadow: 0 10px 20px rgba(0,0,0,0.3);">
433
  <h1>🧠 GAIA Mastermind</h1>
434
- <h3>Многошаговое решение задач с Tree-of-Thought</h3>
435
- <p>Соответствует спецификации GAIA API v1.2</p>
436
  </div>
437
  """)
438
 
@@ -452,7 +481,7 @@ with gr.Blocks(
452
  run_btn = gr.Button("🚀 Запустить оценку", variant="primary", scale=1)
453
 
454
  gr.Markdown("### ⚙️ Статус системы")
455
- sys_info = gr.Textbox(label="Системная информация", interactive=False)
456
 
457
  with gr.Column(scale=2):
458
  gr.Markdown("### 📊 Результаты GAIA")
@@ -484,11 +513,8 @@ with gr.Blocks(
484
 
485
  # Системная информация
486
  def get_system_info():
487
- return (
488
- f"Device: {'GPU ✅' if torch.cuda.is_available() else 'CPU ⚠️'}, "
489
- f"Model: {MODEL_NAME}, "
490
- f"API: {DEFAULT_API_URL}"
491
- )
492
 
493
  demo.load(get_system_info, inputs=None, outputs=sys_info)
494
 
@@ -497,15 +523,11 @@ with gr.Blocks(
497
  inputs=[username, agent_code],
498
  outputs=[result_output, correct_output, total_output, results_table],
499
  concurrency_limit=1,
500
- show_progress="minimal",
501
- api_name="run_evaluation"
502
  )
503
 
504
  if __name__ == "__main__":
505
- demo.queue(
506
- max_size=5,
507
- api_open=False
508
- ).launch(
509
  server_name="0.0.0.0",
510
  server_port=7860,
511
  share=False,
 
4
  import torch
5
  import gradio as gr
6
  from tqdm import tqdm
7
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, pipeline
 
 
 
 
8
  from typing import List, Dict, Any, Tuple, Optional
9
  import json
10
  import ast
 
14
  import base64
15
  import logging
16
  import time
17
+ import sys
18
 
19
  # Настройка логирования
20
  logging.basicConfig(level=logging.INFO)
 
26
  API_RETRIES = 3
27
  API_TIMEOUT = 45
28
 
29
+ # === ЯДРО СИСТЕМЫ (без зависимостей от llama_index) ===
30
  class GAIAThoughtProcessor:
31
  def __init__(self):
32
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
33
+ logger.info(f"⚡ Инициализация GAIAThoughtProcessor на {self.device.upper()}")
34
+
35
  # Оптимизированная загрузка модели
36
+ self.tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
37
+ self.model = AutoModelForSeq2SeqLM.from_pretrained(
38
+ MODEL_NAME,
 
 
39
  device_map="auto",
40
+ torch_dtype=torch.float16 if "cuda" in self.device else torch.float32,
41
+ low_cpu_mem_usage=True
42
+ ).eval()
43
+
44
+ # Создаем пайплайн для генерации текста
45
+ self.text_generator = pipeline(
46
+ "text2text-generation",
47
+ model=self.model,
48
+ tokenizer=self.tokenizer,
49
+ device=self.device,
50
+ max_new_tokens=512
 
 
 
51
  )
52
+
53
+ logger.info("✅ GAIAThoughtProcessor готов")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  def _math_solver(self, expression: str) -> str:
56
  """Безопасное вычисление математических выражений"""
57
  try:
58
  # Очистка выражения
59
  clean_expr = re.sub(r"[^0-9+\-*/().^√π]", "", expression)
60
+ # Поддержка математических функций
61
  context = {
62
  "sqrt": np.sqrt,
63
  "log": np.log,
 
70
  }
71
  return str(eval(clean_expr, {"__builtins__": None}, context))
72
  except Exception as e:
73
+ logger.error(f"Math error: {e}")
74
  return f"Math Error: {str(e)}"
75
 
76
  def _table_analyzer(self, table_data: str, query: str) -> str:
77
+ """Анализ табличных данных"""
78
  try:
79
+ # Автоопределение формата таблицы
80
  if "\t" in table_data:
81
  df = pd.read_csv(io.StringIO(table_data), sep="\t")
82
  elif "," in table_data:
 
84
  else:
85
  df = pd.read_fwf(io.StringIO(table_data))
86
 
87
+ # Выполнение запросов
88
+ query = query.lower()
89
+ if "sum" in query:
90
  return str(df.sum(numeric_only=True).to_dict())
91
+ elif "mean" in query:
92
  return str(df.mean(numeric_only=True).to_dict())
93
+ elif "max" in query:
94
  return str(df.max(numeric_only=True).to_dict())
95
+ elif "min" in query:
96
  return str(df.min(numeric_only=True).to_dict())
97
+ elif "count" in query:
98
  return str(df.count().to_dict())
99
  else:
100
+ return df.describe().to_string()
 
 
 
 
 
101
  except Exception as e:
102
+ logger.error(f"Table error: {e}")
103
  return f"Table Error: {str(e)}"
104
 
105
  def _text_processor(self, text: str, operation: str) -> str:
106
+ """Операции с текстом"""
107
  operation = operation.lower()
108
  if operation == "reverse":
109
  return text[::-1]
 
119
  return f"Unsupported operation: {operation}"
120
 
121
  def _image_processor(self, image_input: str) -> str:
122
+ """Обработка изображений"""
123
  try:
124
  # Обработка URL
125
  if image_input.startswith("http"):
 
135
  else:
136
  return "Invalid image format"
137
 
138
+ # Базовый анализ изображения
139
  description = (
140
  f"Format: {img.format}, Size: {img.size}, "
141
  f"Mode: {img.mode}, Colors: {len(set(img.getdata()))}"
142
  )
143
  return description
144
  except (UnidentifiedImageError, requests.exceptions.RequestException) as e:
145
+ logger.error(f"Image processing error: {e}")
146
  return f"Image Error: {str(e)}"
147
  except Exception as e:
148
  logger.exception("Unexpected image error")
149
  return f"Unexpected Error: {str(e)}"
150
 
151
+ def _call_tool(self, tool_name: str, arguments: str) -> str:
152
+ """Вызов инструмента по имени"""
153
+ try:
154
+ # Парсинг аргументов
155
+ args = [a.strip() for a in arguments.split(",")]
156
+
157
+ if tool_name == "math_solver":
158
+ return self._math_solver(args[0])
159
+ elif tool_name == "table_analyzer":
160
+ return self._table_analyzer(args[0], args[1])
161
+ elif tool_name == "text_processor":
162
+ return self._text_processor(args[0], args[1])
163
+ elif tool_name == "image_processor":
164
+ return self._image_processor(args[0])
165
+ else:
166
+ return f"Unknown tool: {tool_name}"
167
+ except Exception as e:
168
+ return f"Tool Error: {str(e)}"
169
+
170
+ def _generate_response(self, prompt: str) -> str:
171
+ """Генерация ответа с помощью модели"""
172
+ try:
173
+ result = self.text_generator(
174
+ prompt,
175
+ max_new_tokens=256,
176
+ num_beams=3,
177
+ early_stopping=True,
178
+ temperature=0.01
179
+ )
180
+ return result[0]['generated_text']
181
+ except Exception as e:
182
+ logger.error(f"Generation error: {e}")
183
+ return f"Generation Error: {str(e)}"
184
+ finally:
185
+ # Очистка памяти GPU
186
+ if "cuda" in self.device:
187
+ torch.cuda.empty_cache()
188
+
189
  def process_question(self, question: str, task_id: str) -> str:
190
+ """Обработка вопроса с декомпозицией на шаги"""
191
  try:
192
+ # Шаг 1: Декомпозиция задачи
193
  decomposition_prompt = (
194
+ f"Декомпозируй задачу GAIA ({task_id}) на шаги. "
195
+ f"Используй инструменты: math_solver, table_analyzer, text_processor, image_processor.\n\n"
196
+ f"Задача: {question}\n\n"
197
+ "Шаги (формат: [tool_name] arguments):"
198
  )
 
 
199
 
200
+ steps_response = self._generate_response(decomposition_prompt)
201
+ steps = [s.strip() for s in steps_response.split("\n") if s.strip()]
202
+
203
+ # Шаг 2: Выполнение шагов
204
  results = []
205
  for step in steps:
206
  if step:
207
  try:
208
+ # Извлечение инструмента и аргументов
209
+ match = re.match(r"\[(\w+)\]\s*(.+)", step)
210
+ if match:
211
+ tool_name = match.group(1)
212
+ arguments = match.group(2)
213
+ result = self._call_tool(tool_name, arguments)
214
+ results.append(f"{step} -> {result}")
215
+ else:
216
+ results.append(f"{step} -> ERROR: Invalid format")
217
  except Exception as e:
218
+ results.append(f"{step} -> ERROR: {str(e)}")
219
 
220
+ # Шаг 3: Синтез финального ответа
221
  synthesis_prompt = (
222
  f"Задача GAIA {task_id}:\n{question}\n\n"
223
  "Выполненные шаги:\n" + "\n".join(results) +
224
+ "\n\nФинальный ответ в формате JSON (только поле final_answer):"
225
  )
226
+
227
+ final_response = self._generate_response(synthesis_prompt)
228
 
229
  # Извлечение чистого ответа
230
+ if "final_answer" in final_response:
231
+ return json.dumps({"final_answer": final_response})
 
232
  else:
233
+ # Попробуем извлечь ответ из текста
234
+ answer_match = re.search(r'\{.*\}', final_response, re.DOTALL)
235
+ if answer_match:
236
+ return answer_match.group(0)
237
+ else:
238
+ return json.dumps({"final_answer": final_response.strip()})
239
  except Exception as e:
240
  logger.exception("Processing failed")
241
  return json.dumps({
 
244
  "final_answer": f"SYSTEM ERROR: {str(e)}"
245
  })
246
 
247
+ # === СИСТЕМА ОЦЕНКИ ===
248
  class GAIAEvaluationRunner:
249
  def __init__(self, api_url: str = DEFAULT_API_URL):
250
  self.api_url = api_url
 
256
  "User-Agent": "GAIA-Mastermind/1.0",
257
  "Content-Type": "application/json"
258
  })
259
+ logger.info(f"🌐 Инициализирован GAIAEvaluationRunner для {api_url}")
260
 
261
  def run_evaluation(self, agent, username: str, agent_code: str, progress=tqdm):
262
  # Получение вопросов
 
269
  answers = []
270
  for i, q in enumerate(progress(questions, desc="🧠 Processing GAIA")):
271
  try:
 
272
  task_id = q.get("task_id", f"unknown_{i}")
 
 
273
  json_response = agent.process_question(q["question"], task_id)
274
 
275
+ # Парсинг ответа
276
  try:
277
  response_obj = json.loads(json_response)
278
  final_answer = response_obj.get("final_answer", "")
 
 
279
  if not isinstance(final_answer, str):
280
  final_answer = str(final_answer)
281
  except json.JSONDecodeError:
282
  final_answer = json_response
283
 
284
+ # Формирование ответа для GAIA API
285
  answers.append({
286
  "task_id": task_id,
287
+ "answer": final_answer[:500] # Ограничение длины
288
  })
289
 
290
  # Запись результатов
 
295
  "Status": "Processed"
296
  })
297
  except Exception as e:
298
+ logger.error(f"Task {task_id} failed: {e}")
299
  answers.append({
300
  "task_id": task_id,
301
  "answer": f"ERROR: {str(e)}"
 
312
  return submission_result, score, len(questions), pd.DataFrame(results)
313
 
314
  def _fetch_questions(self) -> Tuple[list, str]:
315
+ """Получение вопросов с API"""
316
  for _ in range(API_RETRIES):
317
  try:
318
  response = self.session.get(
 
320
  timeout=API_TIMEOUT
321
  )
322
 
 
323
  if response.status_code == 200:
324
  questions = response.json()
325
  if not isinstance(questions, list):
326
  return [], "Invalid response format: expected list"
327
 
328
+ # Добавление task_id если отсутствует
329
  for q in questions:
330
  q.setdefault("task_id", f"id_{hash(q['question']) % 100000}")
 
 
331
  return questions, "success"
332
 
333
  elif response.status_code == 429:
 
335
  time.sleep(5)
336
  continue
337
 
 
 
 
338
  else:
339
  return [], f"API error: HTTP {response.status_code}"
340
 
341
  except Exception as e:
342
+ logger.error(f"Fetch error: {e}")
343
  return [], f"Connection error: {str(e)}"
344
 
345
  return [], "API unavailable after retries"
346
 
347
  def _submit_answers(self, username: str, agent_code: str, answers: list) -> Tuple[str, int]:
348
+ """Отправка ответов на сервер"""
349
  payload = {
350
  "username": username.strip(),
351
  "agent_code": agent_code.strip(),
 
360
  timeout=API_TIMEOUT * 2
361
  )
362
 
 
363
  if response.status_code == 200:
364
  result = response.json()
365
  score = result.get("score", 0)
 
367
 
368
  elif response.status_code == 400:
369
  error = response.json().get("error", "Invalid request")
370
+ logger.error(f"Validation error: {error}")
371
  return f"Validation Error: {error}", 0
372
 
373
  elif response.status_code == 429:
 
379
  return f"HTTP Error {response.status_code}", 0
380
 
381
  except Exception as e:
382
+ logger.error(f"Submit error: {e}")
383
  return f"Connection Error: {str(e)}", 0
384
 
385
  return "Submission failed after retries", 0
386
 
387
+ # === ИНТЕРФЕЙС GRADIO ===
388
  def run_evaluation(username: str, agent_code: str, progress=gr.Progress()):
389
  progress(0, desc="⚡ Инициализация GAIA Mastermind...")
390
  try:
 
396
  progress(0.1, desc="🌐 Подключение к GAIA API...")
397
  runner = GAIAEvaluationRunner()
398
 
399
+ # Получение вопросов
400
+ questions, status = runner._fetch_questions()
401
+ if status != "success":
402
+ return status, 0, 0, pd.DataFrame()
403
+
404
+ # Обработка вопросов с прогрессом
405
+ results = []
406
+ answers = []
407
+ total = len(questions)
408
+
409
+ for i, q in enumerate(questions):
410
+ progress(i / total, desc=f"🧠 Обработка задач ({i+1}/{total})")
411
+ try:
412
+ task_id = q.get("task_id", f"unknown_{i}")
413
+ json_response = agent.process_question(q["question"], task_id)
414
 
415
+ # Парсинг ответа
416
+ try:
417
+ response_obj = json.loads(json_response)
418
+ final_answer = response_obj.get("final_answer", "")
419
+ except:
420
+ final_answer = json_response
421
 
422
+ answers.append({
423
+ "task_id": task_id,
424
+ "answer": str(final_answer)[:500]
425
+ })
426
 
427
+ results.append({
428
+ "Task ID": task_id,
429
+ "Question": q["question"][:150] + "..." if len(q["question"]) > 150 else q["question"],
430
+ "Answer": str(final_answer)[:200],
431
+ "Status": "Processed"
432
+ })
433
+ except Exception as e:
434
+ logger.error(f"Task {task_id} failed: {e}")
435
+ answers.append({
436
+ "task_id": task_id,
437
+ "answer": f"ERROR: {str(e)}"
438
+ })
439
+ results.append({
440
+ "Task ID": task_id,
441
+ "Question": "Error",
442
+ "Answer": f"ERROR: {str(e)}",
443
+ "Status": "Failed"
444
+ })
445
 
446
+ # Отправка ответов
447
+ submission_result, score = runner._submit_answers(username, agent_code, answers)
448
+ return submission_result, score, total, pd.DataFrame(results)
 
 
 
449
 
450
+ # Создание интерфейса
451
  with gr.Blocks(
452
  title="🧠 GAIA Mastermind",
453
  theme=gr.themes.Soft(),
 
460
  <div style="text-align:center; background: linear-gradient(135deg, #0f2027, #203a43);
461
  padding: 20px; border-radius: 15px; color: white; box-shadow: 0 10px 20px rgba(0,0,0,0.3);">
462
  <h1>🧠 GAIA Mastermind</h1>
463
+ <h3>Многошаговое решение задач с декомпозицией</h3>
464
+ <p>Соответствует спецификации GAIA API</p>
465
  </div>
466
  """)
467
 
 
481
  run_btn = gr.Button("🚀 Запустить оценку", variant="primary", scale=1)
482
 
483
  gr.Markdown("### ⚙️ Статус системы")
484
+ sys_info = gr.Textbox(label="Системная информация", interactive=False, value="")
485
 
486
  with gr.Column(scale=2):
487
  gr.Markdown("### 📊 Результаты GAIA")
 
513
 
514
  # Системная информация
515
  def get_system_info():
516
+ device = "GPU ✅" if torch.cuda.is_available() else "CPU ⚠️"
517
+ return f"Device: {device} | Model: {MODEL_NAME} | API: {DEFAULT_API_URL}"
 
 
 
518
 
519
  demo.load(get_system_info, inputs=None, outputs=sys_info)
520
 
 
523
  inputs=[username, agent_code],
524
  outputs=[result_output, correct_output, total_output, results_table],
525
  concurrency_limit=1,
526
+ show_progress="minimal"
 
527
  )
528
 
529
  if __name__ == "__main__":
530
+ demo.queue(max_size=5).launch(
 
 
 
531
  server_name="0.0.0.0",
532
  server_port=7860,
533
  share=False,