朱东升 commited on
Commit
a3558a8
·
1 Parent(s): 33da27c
Files changed (1) hide show
  1. app.py +495 -763
app.py CHANGED
@@ -3,13 +3,14 @@ import json
3
  import importlib
4
  import os
5
  import sys
6
- import time
7
  from pathlib import Path
8
  import concurrent.futures
9
  import multiprocessing
 
10
  import threading
11
  import queue
12
- from datetime import datetime, timedelta
 
13
  from src.containerized_eval import eval_string_script
14
 
15
  # 添加当前目录和src目录到模块搜索路径
@@ -20,166 +21,66 @@ if current_dir not in sys.path:
20
  if src_dir not in sys.path:
21
  sys.path.append(src_dir)
22
 
23
- # 添加全局任务队列和任务状态跟踪
24
  task_queue = queue.Queue()
25
- task_results = {}
26
- active_tasks = {}
27
- completed_tasks = []
28
- task_id_counter = 0
29
- task_lock = threading.Lock()
30
- last_update_time = datetime.now() # 替换update_event为时间戳跟踪
31
- # 持久化的状态用于API访问和轮询更新
32
- status_poll_counter = 0 # 用于强制更新UI,即使状态没有变化
33
- last_background_update = datetime.now() # 最后一次后台更新时间
34
-
35
- def trigger_ui_update():
36
- """触发UI更新事件"""
37
- global last_update_time, status_poll_counter, last_background_update
38
- with task_lock:
39
- last_update_time = datetime.now() # 更新时间戳
40
- status_poll_counter += 1 # 递增计数器,确保每次更新都被捕获
41
- last_background_update = last_update_time # 更新后台状态
42
- print(f"UI更新被触发: {datetime.now().strftime('%H:%M:%S')} [计数: {status_poll_counter}]")
43
-
44
- def get_next_task_id():
45
- global task_id_counter
46
- with task_lock:
47
- task_id_counter += 1
48
- return f"task_{task_id_counter}"
49
-
50
- def submit_task(input_data):
51
- """提交任务到队列
52
-
53
- Args:
54
- input_data: 列表(批量处理多个测试用例)
55
-
56
- Returns:
57
- str: 任务ID
58
- """
59
- try:
60
- if not isinstance(input_data, list):
61
- return {"status": "error", "message": "Input must be a list"}
62
-
63
- task_id = get_next_task_id()
64
- with task_lock:
65
- estimated_time = estimate_completion_time(input_data)
66
- task_info = {
67
- "id": task_id,
68
- "data": input_data,
69
- "status": "queued",
70
- "submitted_at": datetime.now(),
71
- "estimated_completion_time": estimated_time,
72
- "items_count": len(input_data)
73
- }
74
- active_tasks[task_id] = task_info
75
- task_queue.put(task_info)
76
-
77
- # 触发UI更新
78
- trigger_ui_update()
79
-
80
- # 如果这是第一个任务,启动处理线程
81
- if len(active_tasks) == 1:
82
- threading.Thread(target=process_task_queue, daemon=True).start()
83
-
84
- return {"status": "success", "task_id": task_id}
85
-
86
- except Exception as e:
87
- return {"status": "error", "message": str(e)}
88
 
89
- def estimate_completion_time(input_data):
90
- """估计完成任务所需的时间
91
-
92
- Args:
93
- input_data: 任务数据
94
-
95
- Returns:
96
- timedelta: 估计的完成时间
97
- """
98
- # 在Hugging Face Spaces环境中,资源通常受限,调整处理时间预估
99
- # 假设每个任务项平均需要5秒处理(HF环境中可能更慢)
100
- avg_time_per_item = 5
101
- total_items = len(input_data)
102
-
103
- # Hugging Face Spaces通常有限制的CPU资源
104
- # 保守估计并行处理能力
105
- try:
106
- cpu_count = multiprocessing.cpu_count()
107
- except:
108
- # 如果获取失败,假设只有2个CPU
109
- cpu_count = 2
110
-
111
- # 在HF环境中,即使有多核也可能性能受限,降低并行因子
112
- parallel_factor = min(2, total_items) # 限制最多2个并行任务
113
-
114
- if parallel_factor > 0:
115
- estimated_seconds = (total_items * avg_time_per_item) / parallel_factor
116
- # 为了避免过于乐观的估计,增加30%的缓冲时间
117
- estimated_seconds = estimated_seconds * 1.3
118
- return timedelta(seconds=round(estimated_seconds))
119
- else:
120
- return timedelta(seconds=0)
121
-
122
- def process_task_queue():
123
- """处理任务队列的后台线程"""
124
- while True:
125
  try:
126
- if task_queue.empty():
127
- time.sleep(0.5)
128
- continue
129
-
130
- task_info = task_queue.get()
131
- task_id = task_info["id"]
132
-
133
- # 更新任务状态
134
- with task_lock:
135
- if task_id in active_tasks: # 确保任务仍存在
136
- active_tasks[task_id]["status"] = "processing"
137
- print(f"任务 {task_id} 开始处理,当前时间: {datetime.now().strftime('%H:%M:%S')}")
138
- trigger_ui_update() # 状态变更为处理中时更新UI
139
- else:
140
- print(f"警告: 任务 {task_id} 不在活跃任务列表中")
141
- task_queue.task_done()
142
- continue
143
 
144
  # 处理任务
145
- print(f"开始评估任务 {task_id},数据项数: {len(task_info['data'])}")
146
- result = evaluate(task_info["data"])
147
- print(f"任务 {task_id} 评估完成,结果数: {len(result) if isinstance(result, list) else 'Not a list'}")
148
 
149
- # 更新任务结果
150
- with task_lock:
151
- if task_id in active_tasks: # 再次确保任务仍存在
152
- completed_time = datetime.now()
153
- active_tasks[task_id]["status"] = "completed"
154
- active_tasks[task_id]["completed_at"] = completed_time
155
- active_tasks[task_id]["result"] = result
156
-
157
- # 计算处理持续时间
158
- start_time = active_tasks[task_id]["submitted_at"]
159
- duration = completed_time - start_time
160
- print(f"任务 {task_id} 已完成,耗时: {duration},当前时间: {completed_time.strftime('%H:%M:%S')}")
161
-
162
- # 将任务移至已完成列表
163
- completed_tasks.append(active_tasks[task_id])
164
- del active_tasks[task_id]
165
-
166
- # 保留最近的20个已完成任务
167
- if len(completed_tasks) > 20:
168
- completed_tasks.pop(0)
169
-
170
- # 状态更新后强制触发UI更新
171
- trigger_ui_update()
172
- print(f"任务 {task_id} 完成后触发UI更新: {last_update_time}")
173
- else:
174
- print(f"警告: 任务 {task_id} 在处理完成后不在活跃任务列表中")
175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  task_queue.task_done()
177
 
 
 
 
178
  except Exception as e:
179
- print(f"处理任务队列出错: {str(e)}")
180
- import traceback
181
- traceback.print_exc()
182
- time.sleep(1)
 
 
183
 
184
  def evaluate(input_data):
185
  """评估代码的主函数
@@ -190,67 +91,27 @@ def evaluate(input_data):
190
  Returns:
191
  list: 包含评估结果的列表
192
  """
193
- # 打印Gradio版本,用于调试
194
- import gradio
195
- print(f"Gradio version: {gradio.__version__}")
196
-
197
  try:
198
  if not isinstance(input_data, list):
199
  return {"status": "Exception", "error": "Input must be a list"}
200
 
201
  results = []
202
-
203
- # 在HF Spaces环境中可能受限,降低并行数量
204
- try:
205
- max_workers = min(multiprocessing.cpu_count(), 2) # 最多2个并行任务
206
- except:
207
- max_workers = 2 # 如果无法获取,默认为2
208
-
209
- # 增加超时处理
210
  with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
211
- future_to_item = {}
212
- # 分批处理,每批最多5个任务,避免资源耗尽
213
- batch_size = 5
214
- for i in range(0, len(input_data), batch_size):
215
- batch = input_data[i:i+batch_size]
216
- # 为每个任务提交并记录
217
- for item in batch:
218
- future = executor.submit(evaluate_single_case, item)
219
- future_to_item[future] = item
220
-
221
- # 等待当前批次完成
222
- for future in concurrent.futures.as_completed(future_to_item):
223
- item = future_to_item[future]
224
- try:
225
- # 设置较短的超时时间,避免任务卡死
226
- result = future.result(timeout=60) # 60秒超时
227
- item.update(result)
228
- results.append(item)
229
- except concurrent.futures.TimeoutError:
230
- # 处理超时情况
231
- item.update({
232
- "status": "Timeout",
233
- "error": "Task processing timed out in Hugging Face environment"
234
- })
235
- results.append(item)
236
- except Exception as e:
237
- # 处理其他异常
238
- item.update({
239
- "status": "Exception",
240
- "error": f"Error in Hugging Face environment: {str(e)}"
241
- })
242
- results.append(item)
243
-
244
- # 清空当前批次
245
- future_to_item = {}
246
-
247
- # 短暂休息,让系统喘息
248
- time.sleep(0.5)
249
-
250
  return results
251
 
252
  except Exception as e:
253
- return {"status": "Exception", "error": f"Evaluation error in Hugging Face environment: {str(e)}"}
254
 
255
  def evaluate_single_case(input_data):
256
  """评估单个代码用例
@@ -280,7 +141,7 @@ def evaluate_single_case(input_data):
280
  results.append(result)
281
 
282
  return results[0]
283
-
284
  except Exception as e:
285
  return {"status": "Exception", "error": str(e)}
286
 
@@ -302,601 +163,472 @@ def evaluate_code(code, language):
302
  except Exception as e:
303
  return {"status": "Exception", "error": str(e)}
304
 
305
- def get_queue_status():
306
- """获取当前队列状态
 
 
307
 
 
 
 
308
  Returns:
309
- dict: 包含队列状态的字典
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  """
311
- with task_lock:
312
- queued_tasks = [task for task in active_tasks.values() if task["status"] == "queued"]
313
- processing_tasks = [task for task in active_tasks.values() if task["status"] == "processing"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
 
315
- # 计算总的预计完成时间
316
- total_estimated_time = timedelta(seconds=0)
317
- for task in active_tasks.values():
318
- if isinstance(task["estimated_completion_time"], timedelta):
319
- total_estimated_time += task["estimated_completion_time"]
 
320
 
321
- # 计算队列中的项目总数
322
- total_items = sum(task["items_count"] for task in active_tasks.values())
323
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  return {
325
- "queued_tasks": len(queued_tasks),
326
- "processing_tasks": len(processing_tasks),
327
- "total_tasks": len(active_tasks),
328
- "total_items": total_items,
329
- "estimated_completion_time": str(total_estimated_time),
330
- "active_tasks": [
331
- {
332
- "id": task["id"],
333
- "status": task["status"],
334
- "items_count": task["items_count"],
335
- "submitted_at": task["submitted_at"].strftime("%Y-%m-%d %H:%M:%S"),
336
- "estimated_completion": str(task["estimated_completion_time"])
337
- } for task in active_tasks.values()
338
- ],
339
- "recent_completed": [
340
- {
341
- "id": task["id"],
342
- "items_count": task["items_count"],
343
- "submitted_at": task["submitted_at"].strftime("%Y-%m-%d %H:%M:%S"),
344
- "completed_at": task["completed_at"].strftime("%Y-%m-%d %H:%M:%S") if "completed_at" in task else "",
345
- "duration": str(task["completed_at"] - task["submitted_at"]) if "completed_at" in task else ""
346
- } for task in completed_tasks[-5:] # 只显示最近5个完成的任务
347
- ],
348
- "poll_counter": status_poll_counter, # 添加轮询计数器
349
- "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
350
  }
351
 
352
- def render_queue_status():
353
- """渲染队列状态UI
354
 
 
 
 
355
  Returns:
356
- str: HTML格式的队列状态显示
357
  """
358
- status = get_queue_status()
359
-
360
- html = f"""
361
- <div style="font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; background: #f9f9f9; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
362
- <div style="display: flex; justify-content: space-between; margin-bottom: 20px;">
363
- <div style="background: #fff; padding: 15px; border-radius: 8px; width: 30%; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
364
- <h3 style="margin: 0 0 10px; color: #444; font-size: 16px;">排队任务</h3>
365
- <p style="font-size: 28px; font-weight: bold; margin: 0; color: #3498db;">{status['queued_tasks']}</p>
366
- </div>
367
- <div style="background: #fff; padding: 15px; border-radius: 8px; width: 30%; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
368
- <h3 style="margin: 0 0 10px; color: #444; font-size: 16px;">处理中任务</h3>
369
- <p style="font-size: 28px; font-weight: bold; margin: 0; color: #e74c3c;">{status['processing_tasks']}</p>
370
- </div>
371
- <div style="background: #fff; padding: 15px; border-radius: 8px; width: 30%; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
372
- <h3 style="margin: 0 0 10px; color: #444; font-size: 16px;">总项目数</h3>
373
- <p style="font-size: 28px; font-weight: bold; margin: 0; color: #2ecc71;">{status['total_items']}</p>
374
- </div>
375
- </div>
376
 
377
- <div style="background: #fff; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
378
- <h2 style="margin: 0 0 15px; color: #333; font-size: 18px;">预计完成时间</h2>
379
- <div style="display: flex; align-items: center;">
380
- <div style="font-size: 24px; font-weight: bold; color: #9b59b6;">{status['estimated_completion_time']}</div>
381
- <div style="margin-left: 10px; color: #777; font-size: 14px;">(小时:分钟:秒)</div>
382
- </div>
 
 
 
 
 
 
 
 
 
383
  </div>
 
384
 
385
- <div style="background: #fff; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
386
- <h2 style="margin: 0 0 15px; color: #333; font-size: 18px;">活跃任务</h2>
387
- <table style="width: 100%; border-collapse: collapse;">
388
- <thead>
389
- <tr style="background: #f2f2f2;">
390
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">任务ID</th>
391
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">状态</th>
392
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">项目数</th>
393
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">提交时间</th>
394
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">预计完成</th>
395
- </tr>
396
- </thead>
397
- <tbody>
398
  """
 
 
399
 
400
- if status['active_tasks']:
401
- for task in status['active_tasks']:
402
- status_color = "#3498db" if task['status'] == "queued" else "#e74c3c"
403
- html += f"""
404
- <tr>
405
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['id']}</td>
406
- <td style="padding: 12px; border-bottom: 1px solid #eee;">
407
- <span style="padding: 4px 8px; border-radius: 4px; font-size: 12px; background: {status_color}; color: white;">
408
- {task['status']}
409
- </span>
410
- </td>
411
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['items_count']}</td>
412
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['submitted_at']}</td>
413
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['estimated_completion']}</td>
414
- </tr>
415
- """
416
- else:
417
- html += f"""
418
- <tr>
419
- <td colspan="5" style="padding: 15px; text-align: center; color: #777;">当前没有活跃任务</td>
420
- </tr>
421
  """
422
-
423
- html += """
424
- </tbody>
425
- </table>
 
 
 
 
 
 
 
426
  </div>
427
-
428
- <div style="background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
429
- <h2 style="margin: 0 0 15px; color: #333; font-size: 18px;">最近完成的任务</h2>
430
- <table style="width: 100%; border-collapse: collapse;">
431
- <thead>
432
- <tr style="background: #f2f2f2;">
433
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">任务ID</th>
434
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">项目数</th>
435
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">提交时间</th>
436
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">完成时间</th>
437
- <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd;">持续时间</th>
438
- </tr>
439
- </thead>
440
- <tbody>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  """
 
442
 
443
- if status['recent_completed']:
444
- for task in status['recent_completed']:
445
- html += f"""
446
- <tr>
447
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['id']}</td>
448
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['items_count']}</td>
449
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['submitted_at']}</td>
450
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['completed_at']}</td>
451
- <td style="padding: 12px; border-bottom: 1px solid #eee;">{task['duration']}</td>
452
- </tr>
453
- """
454
- else:
455
- html += f"""
456
- <tr>
457
- <td colspan="5" style="padding: 15px; text-align: center; color: #777;">暂无已完成任务</td>
458
- </tr>
459
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
 
461
- html += """
462
- </tbody>
463
- </table>
464
  </div>
 
 
 
 
 
 
 
 
 
 
465
  </div>
466
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
467
 
468
- return html
 
 
 
469
 
470
- def refresh_ui():
471
- """定期刷新UI函数,确保显示最新状态"""
472
- global status_poll_counter
473
- print(f"UI刷新被调用: {datetime.now().strftime('%H:%M:%S')} [状态计数: {status_poll_counter}]")
474
- return render_queue_status(), status_poll_counter
 
 
 
 
 
475
 
476
- def get_status_for_polling():
477
- """为AJAX轮询提供的状态获取函数,简化返回数据量"""
478
- global status_poll_counter
479
- # 返回最新状态和计数器值,用于客户端判断是否有更新
480
- return {
481
- "status_html": render_queue_status(),
482
- "poll_counter": status_poll_counter,
483
- "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
484
- }
485
 
486
- def submit_json_data(json_data):
487
- """提交JSON数据处理接口"""
488
- try:
489
- # 解析JSON字符串
490
- if isinstance(json_data, str):
491
- data = json.loads(json_data)
492
- else:
493
- data = json_data
494
-
495
- # 为调试记录提交来源
496
- print(f"接收到任务提交: {len(data) if isinstance(data, list) else 'Not a list'} 项")
497
- print(f"当前队列状态 (提交前): 活跃任务 {len(active_tasks)}, 已完成任务 {len(completed_tasks)}")
498
-
499
- result = submit_task(data)
500
- response = json.dumps(result, ensure_ascii=False, indent=2)
501
-
502
- # 强制触发UI更新
503
- trigger_ui_update()
504
- print(f"任务提交后触发UI更新: {last_update_time}")
505
- print(f"当前队列状态 (提交后): 活跃任务 {len(active_tasks)}, 已完成任务 {len(completed_tasks)}")
506
-
507
- # 返回任务提交结果和最新的队列状态
508
- status_html = render_queue_status()
509
- return response, status_html
510
- except Exception as e:
511
- print(f"提交任务出错: {str(e)}")
512
- return json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False, indent=2), render_queue_status()
513
-
514
- # API端点函数,用于获取最新队列状态
515
- def api_get_queue_status():
516
- """API端点,获取最新队列状态"""
517
- status = get_queue_status()
518
- status["html"] = render_queue_status() # 添加HTML渲染版本
519
- return status
520
-
521
- # 后台UI更新线程
522
- def background_ui_updater():
523
- """每隔一段时间检查任务状态,如有变化则触发更新"""
524
- global last_background_update
525
- print("后台UI更新线程已启动")
526
-
527
- while True:
528
- try:
529
- # 检查队列状态
530
- current_time = datetime.now()
531
- time_since_last_update = (current_time - last_background_update).total_seconds()
532
-
533
- # 如果30秒内没有更新,且有活跃任务,触发更新
534
- with task_lock:
535
- has_active_tasks = len(active_tasks) > 0
536
-
537
- if time_since_last_update > 30 and has_active_tasks:
538
- print(f"后台更新: 有{len(active_tasks)}个活跃任务,{time_since_last_update:.1f}秒未更新")
539
- trigger_ui_update()
540
-
541
- # 如果2分钟内没有更新,无论如何都触发一次更新保持UI活跃
542
- if time_since_last_update > 120:
543
- print(f"后台更新: 保活触发,{time_since_last_update:.1f}秒未更新")
544
- trigger_ui_update()
545
-
546
- except Exception as e:
547
- print(f"后台更新出错: {str(e)}")
548
-
549
- # 每5秒检查一次
550
- time.sleep(5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
 
552
  # 创建Gradio接口
553
- with gr.Blocks(title="代码评估服务", theme=gr.themes.Soft()) as demo:
554
- gr.Markdown("""
555
- # 代码评估服务
556
- ### 支持多种编程语言的代码评估服务
557
- """)
558
-
559
- # 添加隐藏的API处理组件
560
- with gr.Row(visible=False):
561
- api_input = gr.JSON()
562
- api_output = gr.JSON()
563
-
564
- # 设置API触发器
565
- def api_trigger(data):
566
- """处理API请求的函数"""
567
- print(f"通过API接收到请求: {len(data) if isinstance(data, list) else 'Not a list'}")
568
- try:
569
- result = submit_task(data)
570
- # 强制触发UI更新
571
- trigger_ui_update()
572
- return result
573
- except Exception as e:
574
- print(f"API处理出错: {str(e)}")
575
- return {"status": "error", "message": str(e)}
576
-
577
- api_input.change(fn=api_trigger, inputs=api_input, outputs=api_output)
578
 
579
- with gr.Tab("任务队列状态"):
580
- status_html = gr.HTML(render_queue_status)
581
- poll_counter = gr.Number(value=0, visible=False) # 隐藏的计数器,用于触发更新
582
- refresh_button = gr.Button("刷新状态")
583
- status_text = gr.Markdown("上次更新时间: 未更新")
584
-
585
- # 根据Gradio版本使用不同的事件注册方法
586
- if 'gradio_version' not in locals():
587
- import gradio
588
- gradio_version = getattr(gradio, "__version__", "unknown")
589
-
590
- if gradio_version.startswith("3."):
591
- # Gradio 3.x 方式
592
- refresh_button.click(fn=refresh_ui, outputs=[status_html, poll_counter], concurrency_limit=2)
593
- else:
594
- # Gradio 4.x 方式 (不使用concurrency_limit参数)
595
- refresh_button.click(
596
- fn=refresh_ui,
597
- outputs=[status_html, poll_counter],
598
- every=10 # 每10秒自动刷新一次
599
- )
600
-
601
- # API断点,用于轮询最新状态
602
- status_polling_input = gr.Number(value=0, visible=False, label="轮询触发器")
603
- status_polling_output = gr.JSON(visible=False, label="轮询结果")
604
-
605
- status_polling_input.change(
606
- fn=get_status_for_polling,
607
- inputs=[],
608
- outputs=status_polling_output
609
- )
610
-
611
- # 使用JavaScript实现自动轮询
612
- polling_js = """
613
- <script>
614
- // 初始化轮询计数器
615
- let lastPollCounter = 0;
616
- let pollingActive = true;
617
-
618
- // 轮询函数
619
- async function pollStatus() {
620
- if (!pollingActive) return;
621
-
622
- try {
623
- // 查找JSON输出元素 (在Gradio 4.x中,元素ID可能不同)
624
- const jsonElements = document.querySelectorAll('[data-testid="json"]');
625
- const statusElements = document.querySelectorAll('[id*="status_html"]');
626
-
627
- // 如果找不到元素,使用普通AJAX请求
628
- if (!jsonElements.length) {
629
- // 通过已有的刷新按钮点击来刷新
630
- const refreshButton = [...document.querySelectorAll('button')].find(btn =>
631
- btn.textContent.includes('刷新状态'));
632
-
633
- if (refreshButton) {
634
- refreshButton.click();
635
- console.log("刷新按钮被触发");
636
- }
637
- } else {
638
- // 使用Gradio API调用来获取最新状态
639
- // 构建API请求的路径
640
- const apiBase = window.location.pathname.endsWith('/')
641
- ? window.location.pathname
642
- : window.location.pathname + '/';
643
-
644
- const response = await fetch(apiBase + 'api/queue/status');
645
- if (response.ok) {
646
- const data = await response.json();
647
- const statusHtml = statusElements[0];
648
-
649
- // 如果有状态更新,更新UI
650
- if (statusHtml && data.status) {
651
- // 根据API返回更新页面状态
652
- console.log("服务器状态已更新");
653
-
654
- // 触发刷新按钮
655
- const refreshButton = [...document.querySelectorAll('button')].find(btn =>
656
- btn.textContent.includes('刷新状态'));
657
-
658
- if (refreshButton) {
659
- refreshButton.click();
660
- }
661
- }
662
- }
663
- }
664
- } catch (err) {
665
- console.error("轮询错误:", err);
666
- }
667
-
668
- // 继续轮询
669
- setTimeout(pollStatus, 5000); // 每5秒轮询一次
670
- }
671
-
672
- // 页面加载完成后开始轮询
673
- if (document.readyState === 'loading') {
674
- document.addEventListener('DOMContentLoaded', () => setTimeout(pollStatus, 1000));
675
- } else {
676
- setTimeout(pollStatus, 1000);
677
- }
678
 
679
- // 页面可见性变化时管理轮询
680
- document.addEventListener('visibilitychange', function() {
681
- pollingActive = document.visibilityState === 'visible';
682
- if (pollingActive) {
683
- // 页面变为可见时立即轮询一次
684
- pollStatus();
685
- }
686
- });
687
- </script>
688
- """
689
-
690
- gr.HTML(polling_js)
691
-
692
- # 以下是原来的自动刷新脚本,保留但不使用
693
- auto_refresh_js = """
694
- <script>
695
- // 兼容Gradio 3.x和4.x的自动刷新机制 - 仅作为备用
696
- console.log('自动刷新机制已加载,但已被新的轮询系统替代');
697
- </script>
698
- """
699
-
700
- # 不显示旧的自动刷新脚本
701
- # gr.HTML(auto_refresh_js)
702
 
703
- with gr.Tab("提交新任务"):
704
  with gr.Row():
705
- with gr.Column():
706
- json_input = gr.Textbox(
707
- label="输入JSON数据",
708
- placeholder='[{"language": "python", "prompt": "def add(a, b):\\n", "processed_completions": [" return a + b"], "tests": "assert add(1, 2) == 3"}]',
709
- lines=10
710
- )
711
- submit_button = gr.Button("提交任务")
712
 
713
- with gr.Column():
714
- result_output = gr.Textbox(label="提交结果", lines=5)
715
-
716
- # 根据Gradio版本使用不同的事件注册方法
717
- if gradio_version.startswith("3."):
718
- # Gradio 3.x 方式
719
- submit_button.click(fn=submit_json_data, inputs=json_input, outputs=[result_output, status_html], concurrency_limit=2)
720
- else:
721
- # Gradio 4.x 方式
722
- submit_button.click(fn=submit_json_data, inputs=json_input, outputs=[result_output, status_html])
723
-
724
- with gr.Tab("API文档"):
725
- gr.Markdown("""
726
- ## API 文档
727
-
728
- ### 1. 提交任务
729
-
730
- **请求:**
731
-
732
- ```
733
- POST /api/predict
734
- Content-Type: application/json
735
-
736
- [
737
- {
738
- "language": "python",
739
- "prompt": "def add(a, b):\\n",
740
- "processed_completions": [" return a + b"],
741
- "tests": "assert add(1, 2) == 3"
742
- }
743
- ]
744
- ```
745
-
746
- **响应:**
747
-
748
- ```json
749
- {
750
- "status": "success",
751
- "task_id": "task_1"
752
- }
753
- ```
754
-
755
- ### 2. 查询任务状态
756
-
757
- **请求:**
758
-
759
- ```
760
- GET /api/status
761
- ```
762
-
763
- **响应:**
764
-
765
- ```json
766
- {
767
- "queued_tasks": 1,
768
- "processing_tasks": 2,
769
- "total_tasks": 3,
770
- "total_items": 15,
771
- "estimated_completion_time": "0:05:30",
772
- "active_tasks": [...],
773
- "recent_completed": [...]
774
- }
775
- ```
776
- """)
777
-
778
- # 这里不再添加状态API端点,避免与FastAPI冲突
779
- # demo.queue(api_open=True).add_api_route("/api/queue/status", api_get_queue_status, methods=["GET"])
780
-
781
- if __name__ == "__main__":
782
- # 检测Gradio版本以适配不同版本的API
783
- import gradio
784
- gradio_version = getattr(gradio, "__version__", "unknown")
785
- print(f"当前Gradio版本: {gradio_version}")
786
 
787
- # 启动后台UI更新线程
788
- bg_thread = threading.Thread(target=background_ui_updater, daemon=True)
789
- bg_thread.start()
790
- print("后台UI更新线程已启动")
 
791
 
 
 
 
 
 
792
  try:
793
- # 设置标准日志记录,确保足够的调试信息
794
- import logging
795
- logging.basicConfig(level=logging.INFO,
796
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
797
- logger = logging.getLogger("CodeEvalService")
798
- logger.info(f"启动代码评估服务,Gradio版本: {gradio_version}")
799
-
800
- # 尝试使用兼容所有版本的参数启动
801
- launch_kwargs = {
802
- "server_name": "0.0.0.0",
803
- "server_port": int(os.environ.get("PORT", 7860)),
804
- "share": False,
805
- }
806
-
807
- # Gradio 4.x专用的FastAPI初始化
808
- if not gradio_version.startswith("3."):
809
- # 针对Gradio 4的API配置
810
- import fastapi
811
- from fastapi import FastAPI
812
-
813
- # 创建FastAPI应用
814
- app = FastAPI(title="代码评估服务API")
815
-
816
- # 添加队列状态API端点
817
- @app.get("/api/queue/status")
818
- def get_queue_status_api():
819
- return api_get_queue_status()
820
-
821
- # 添加任务提交API端点
822
- @app.post("/api/submit_task")
823
- async def submit_task_api(data: list):
824
- try:
825
- result = submit_task(data)
826
- return result
827
- except Exception as e:
828
- return {"status": "error", "message": str(e)}
829
-
830
- # 添加/evaluate API端点
831
- @app.post("/evaluate")
832
- async def evaluate_api(data: list):
833
- try:
834
- result = evaluate(data)
835
- return result
836
- except Exception as e:
837
- return {"status": "error", "message": str(e)}
838
-
839
- # 启动应用到FastAPI
840
- demo.launch(
841
- **launch_kwargs,
842
- app=app,
843
- max_threads=5,
844
- )
845
- else:
846
- # Gradio 3.x的方式
847
- # 设置队列,并添加API支持
848
- try:
849
- demo.queue(api_open=True, max_size=30)
850
- # 添加/evaluate API端点
851
- demo.add_api_route("/evaluate", evaluate, methods=["POST"])
852
- except Exception as e:
853
- logger.warning(f"配置队列时出错: {e}")
854
-
855
- # 启动应用
856
- demo.launch(
857
- **launch_kwargs,
858
- debug=False,
859
- show_api=True,
860
- max_threads=5,
861
- concurrency_limit=2
862
- )
863
-
864
- except Exception as e:
865
- print(f"启动时��生错误: {e}")
866
- import traceback
867
- traceback.print_exc()
868
-
869
- # 尝试最简配置启动
870
- try:
871
- print("使用最小配置重试...")
872
- demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
873
- except Exception as e2:
874
- print(f"最小配置启动也失败: {e2}")
875
- traceback.print_exc()
876
-
877
- # 终极回退方案:创建最简单的接口并启动
878
- try:
879
- print("尝试创建备用界面...")
880
-
881
- import gradio as gr
882
- def simple_evaluate(json_data):
883
- try:
884
- print(f"备用界面收到请求: {json_data[:100] if isinstance(json_data, str) else 'Not a string'}")
885
- data = json.loads(json_data) if isinstance(json_data, str) else json_data
886
- result = submit_task(data)
887
- return json.dumps(result, ensure_ascii=False)
888
- except Exception as e:
889
- return {"error": str(e)}
890
-
891
- backup_demo = gr.Interface(
892
- fn=simple_evaluate,
893
- inputs=gr.Textbox(label="JSON输入"),
894
- outputs=gr.Textbox(label="结果"),
895
- title="代码评估服务 (备用界面)",
896
- description="原界面启动失败,这是简化版本。提交格式: [{\"language\": \"python\", \"prompt\": \"def add(a, b):\\n\", \"processed_completions\": [\" return a + b\"], \"tests\": \"assert add(1, 2) == 3\"}]"
897
- )
898
-
899
- backup_demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
900
- except Exception as e3:
901
- print(f"备用界面也启动失败: {e3}")
902
- traceback.print_exc()
 
3
  import importlib
4
  import os
5
  import sys
 
6
  from pathlib import Path
7
  import concurrent.futures
8
  import multiprocessing
9
+ import time
10
  import threading
11
  import queue
12
+ import uuid
13
+ from datetime import datetime
14
  from src.containerized_eval import eval_string_script
15
 
16
  # 添加当前目录和src目录到模块搜索路径
 
21
  if src_dir not in sys.path:
22
  sys.path.append(src_dir)
23
 
24
+ # 创建消息队列
25
  task_queue = queue.Queue()
26
+ # 存储任务状态的字典
27
+ task_status = {}
28
+ # 存储任务历史的列表,最多保存最近10个任务
29
+ task_history = []
30
+ # 用于保护共享资源的锁
31
+ lock = threading.Lock()
32
+ # 工作线程数
33
+ worker_threads = multiprocessing.cpu_count()
34
+ # 后台线程是否运行的标志
35
+ running = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ def queue_processor():
38
+ """处理队列中的任务"""
39
+ while running:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  try:
41
+ # 从队列中获取任务,如果队列为空等待0.1秒
42
+ task_id, input_data, request_time = task_queue.get(timeout=0.1)
43
+ with lock:
44
+ task_status[task_id]['status'] = 'processing'
45
+ task_status[task_id]['start_time'] = time.time()
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  # 处理任务
48
+ result = evaluate(input_data)
 
 
49
 
50
+ # 更新任务状态
51
+ end_time = time.time()
52
+ process_time = end_time - task_status[task_id]['start_time']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ with lock:
55
+ task_status[task_id]['status'] = 'completed'
56
+ task_status[task_id]['result'] = result
57
+ task_status[task_id]['end_time'] = end_time
58
+ task_status[task_id]['process_time'] = process_time
59
+
60
+ # 更新任务历史
61
+ task_history.append({
62
+ 'task_id': task_id,
63
+ 'request_time': request_time,
64
+ 'process_time': process_time,
65
+ 'status': 'completed'
66
+ })
67
+ # 只保留最近10个任务
68
+ while len(task_history) > 10:
69
+ task_history.pop(0)
70
+
71
+ # 标记任务完成
72
  task_queue.task_done()
73
 
74
+ except queue.Empty:
75
+ # 队列为空,继续等待
76
+ continue
77
  except Exception as e:
78
+ if 'task_id' in locals():
79
+ with lock:
80
+ task_status[task_id]['status'] = 'error'
81
+ task_status[task_id]['error'] = str(e)
82
+ task_status[task_id]['end_time'] = time.time()
83
+ task_queue.task_done()
84
 
85
  def evaluate(input_data):
86
  """评估代码的主函数
 
91
  Returns:
92
  list: 包含评估结果的列表
93
  """
 
 
 
 
94
  try:
95
  if not isinstance(input_data, list):
96
  return {"status": "Exception", "error": "Input must be a list"}
97
 
98
  results = []
99
+ max_workers = multiprocessing.cpu_count()
 
 
 
 
 
 
 
100
  with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
101
+ future_to_item = {executor.submit(evaluate_single_case, item): item for item in input_data}
102
+ for future in concurrent.futures.as_completed(future_to_item):
103
+ item = future_to_item[future]
104
+ try:
105
+ result = future.result()
106
+ item.update(result)
107
+ results.append(item)
108
+ except Exception as e:
109
+ item.update({"status": "Exception", "error": str(e)})
110
+ results.append(item)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  return results
112
 
113
  except Exception as e:
114
+ return {"status": "Exception", "error": str(e)}
115
 
116
  def evaluate_single_case(input_data):
117
  """评估单个代码用例
 
141
  results.append(result)
142
 
143
  return results[0]
144
+
145
  except Exception as e:
146
  return {"status": "Exception", "error": str(e)}
147
 
 
163
  except Exception as e:
164
  return {"status": "Exception", "error": str(e)}
165
 
166
+ def synchronous_evaluate(input_data):
167
+ """同步评估代码,兼容原来的接口
168
+
169
+ 这个函数会阻塞直到评估完成,然后返回结果
170
 
171
+ Args:
172
+ input_data: 要评估的输入数据
173
+
174
  Returns:
175
+ dict: 评估结果
176
+ """
177
+ # 获取队列当前状态
178
+ queue_info = get_queue_status()
179
+ waiting_tasks = queue_info['waiting_tasks']
180
+
181
+ # 创建任务
182
+ task_id = str(uuid.uuid4())
183
+ request_time = time.time()
184
+
185
+ with lock:
186
+ # 创建任务状态记录
187
+ task_status[task_id] = {
188
+ 'status': 'queued',
189
+ 'queued_time': request_time,
190
+ 'queue_position': task_queue.qsize() + 1,
191
+ 'synchronous': True # 标记为同步任务
192
+ }
193
+
194
+ # 将任务添加到队列
195
+ task_queue.put((task_id, input_data, request_time))
196
+
197
+ # 等待任务完成
198
+ while True:
199
+ with lock:
200
+ if task_id in task_status:
201
+ status = task_status[task_id]['status']
202
+ if status == 'completed':
203
+ result = task_status[task_id]['result']
204
+ # 任务完成后清理状态
205
+ task_status.pop(task_id, None)
206
+ return result
207
+ elif status == 'error':
208
+ error = task_status[task_id].get('error', '未知错误')
209
+ # 任务出错后清理状态
210
+ task_status.pop(task_id, None)
211
+ return {"status": "Exception", "error": error}
212
+
213
+ # 短暂睡眠避免CPU占用过高
214
+ time.sleep(0.1)
215
+
216
+ def enqueue_task(input_data):
217
+ """将任务添加到队列
218
+
219
+ Args:
220
+ input_data: 要处理的任务数据
221
+
222
+ Returns:
223
+ dict: 包含任务ID和状态的字典
224
  """
225
+ task_id = str(uuid.uuid4())
226
+ request_time = time.time()
227
+
228
+ with lock:
229
+ # 创建任务状态��录
230
+ task_status[task_id] = {
231
+ 'status': 'queued',
232
+ 'queued_time': request_time,
233
+ 'queue_position': task_queue.qsize() + 1
234
+ }
235
+
236
+ # 将任务添加到队列
237
+ task_queue.put((task_id, input_data, request_time))
238
+
239
+ return {
240
+ 'task_id': task_id,
241
+ 'status': 'queued',
242
+ 'queue_position': task_status[task_id]['queue_position'],
243
+ 'estimated_wait': task_status[task_id]['queue_position'] * 5 # 估计等待时间,假设每个任务平均5秒
244
+ }
245
+
246
+ def check_status(task_id):
247
+ """检查任务状态
248
+
249
+ Args:
250
+ task_id: 任务ID
251
 
252
+ Returns:
253
+ dict: 包含任务状态的字典
254
+ """
255
+ with lock:
256
+ if task_id not in task_status:
257
+ return {'status': 'not_found'}
258
 
259
+ status_info = task_status[task_id].copy()
 
260
 
261
+ # 如果任务已完成,从状态字典中移除(避免内存泄漏)
262
+ if status_info['status'] in ['completed', 'error'] and time.time() - status_info.get('end_time', 0) > 3600:
263
+ task_status.pop(task_id, None)
264
+
265
+ return status_info
266
+
267
+ def get_queue_status():
268
+ """获取队列状态
269
+
270
+ Returns:
271
+ dict: 包含队列状态的字典
272
+ """
273
+ with lock:
274
+ queue_size = task_queue.qsize()
275
+ active_tasks = sum(1 for status in task_status.values() if status['status'] == 'processing')
276
+ waiting_tasks = sum(1 for status in task_status.values() if status['status'] == 'queued')
277
+
278
+ recent_tasks = task_history[-5:] if task_history else []
279
+ avg_time = 0
280
+ if recent_tasks:
281
+ avg_time = sum(task['process_time'] for task in recent_tasks) / len(recent_tasks)
282
+
283
  return {
284
+ 'queue_size': queue_size,
285
+ 'active_tasks': active_tasks,
286
+ 'waiting_tasks': waiting_tasks,
287
+ 'worker_threads': worker_threads,
288
+ 'estimated_wait': waiting_tasks * avg_time if avg_time > 0 else waiting_tasks * 5,
289
+ 'recent_tasks': recent_tasks
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
291
 
292
+ def format_time(seconds):
293
+ """格式化时间为易读格式
294
 
295
+ Args:
296
+ seconds: 秒数
297
+
298
  Returns:
299
+ str: 格式化的时间字符串
300
  """
301
+ if seconds < 60:
302
+ return f"{seconds:.1f}秒"
303
+ elif seconds < 3600:
304
+ minutes = int(seconds / 60)
305
+ seconds = seconds % 60
306
+ return f"{minutes}分{seconds:.1f}秒"
307
+ else:
308
+ hours = int(seconds / 3600)
309
+ minutes = int((seconds % 3600) / 60)
310
+ return f"{hours}小时{minutes}分钟"
311
+
312
+ def ui_submit(input_json):
313
+ """提交任务到队列的UI函数
314
+
315
+ Args:
316
+ input_json: JSON格式的输入数据
 
 
317
 
318
+ Returns:
319
+ tuple: 任务ID和初始状态信息
320
+ """
321
+ try:
322
+ input_data = json.loads(input_json) if isinstance(input_json, str) else input_json
323
+ response = enqueue_task(input_data)
324
+ task_id = response['task_id']
325
+
326
+ status_html = f"""
327
+ <div class="status-card">
328
+ <h3>任务状态</h3>
329
+ <p><b>任务ID:</b> {task_id}</p>
330
+ <p><b>状态:</b> 已入队</p>
331
+ <p><b>队列位置:</b> {response['queue_position']}</p>
332
+ <p><b>预计等待时间:</b> {format_time(response['estimated_wait'])}</p>
333
  </div>
334
+ """
335
 
336
+ return task_id, status_html
337
+ except Exception as e:
338
+ return None, f"<div class='error-message'>提交失败: {str(e)}</div>"
339
+
340
+ def ui_check_status(task_id):
341
+ """检查任务状态的UI函数
342
+
343
+ Args:
344
+ task_id: 任务ID
345
+
346
+ Returns:
347
+ str: 包含任务状态的HTML
 
348
  """
349
+ if not task_id:
350
+ return "<div class='notice'>请提供任务ID</div>"
351
 
352
+ status = check_status(task_id)
353
+
354
+ if status['status'] == 'not_found':
355
+ return "<div class='error-message'>任务未找到</div>"
356
+
357
+ if status['status'] == 'queued':
358
+ queue_info = get_queue_status()
359
+ est_wait = queue_info['estimated_wait'] / queue_info['waiting_tasks'] * status['queue_position'] if queue_info['waiting_tasks'] > 0 else 0
360
+
361
+ return f"""
362
+ <div class="status-card">
363
+ <h3>任务状态</h3>
364
+ <p><b>任务ID:</b> {task_id}</p>
365
+ <p><b>状态:</b> 等待中</p>
366
+ <p><b>队列位置:</b> {status['queue_position']}</p>
367
+ <p><b>入队时间:</b> {datetime.fromtimestamp(status['queued_time']).strftime('%H:%M:%S')}</p>
368
+ <p><b>预计等待时间:</b> {format_time(est_wait)}</p>
369
+ </div>
 
 
 
370
  """
371
+
372
+ if status['status'] == 'processing':
373
+ process_time = time.time() - status['start_time']
374
+
375
+ return f"""
376
+ <div class="status-card">
377
+ <h3>任务状态</h3>
378
+ <p><b>任务ID:</b> {task_id}</p>
379
+ <p><b>状态:</b> 处理中</p>
380
+ <p><b>已处理时间:</b> {format_time(process_time)}</p>
381
+ <p><b>入队时间:</b> {datetime.fromtimestamp(status['queued_time']).strftime('%H:%M:%S')}</p>
382
  </div>
383
+ """
384
+
385
+ if status['status'] == 'completed':
386
+ result = status.get('result', {})
387
+
388
+ return f"""
389
+ <div class="status-card success">
390
+ <h3>任务状态</h3>
391
+ <p><b>任务ID:</b> {task_id}</p>
392
+ <p><b>状态:</b> 已完成</p>
393
+ <p><b>处理时间:</b> {format_time(status['process_time'])}</p>
394
+ <p><b>完成时间:</b> {datetime.fromtimestamp(status['end_time']).strftime('%H:%M:%S')}</p>
395
+ </div>
396
+ """
397
+
398
+ if status['status'] == 'error':
399
+ return f"""
400
+ <div class="status-card error">
401
+ <h3>任务状态</h3>
402
+ <p><b>任务ID:</b> {task_id}</p>
403
+ <p><b>状态:</b> 错误</p>
404
+ <p><b>错误信息:</b> {status.get('error', '未知错误')}</p>
405
+ </div>
406
+ """
407
+
408
+ return "<div class='error-message'>未知状态</div>"
409
+
410
+ def ui_get_queue_info():
411
+ """获取队列信息的UI函数
412
+
413
+ Returns:
414
+ str: 包含队列信息的HTML
415
  """
416
+ queue_info = get_queue_status()
417
 
418
+ tasks_html = ""
419
+ for task in reversed(queue_info['recent_tasks']):
420
+ tasks_html += f"""
421
+ <tr>
422
+ <td>{task['task_id'][:8]}...</td>
423
+ <td>{datetime.fromtimestamp(task['request_time']).strftime('%H:%M:%S')}</td>
424
+ <td>{format_time(task['process_time'])}</td>
425
+ </tr>
 
 
 
 
 
 
 
 
426
  """
427
+
428
+ return f"""
429
+ <div class="queue-info-card">
430
+ <h3>队列状态</h3>
431
+ <div class="queue-stats">
432
+ <div class="stat-item">
433
+ <div class="stat-value">{queue_info['waiting_tasks']}</div>
434
+ <div class="stat-label">等待中</div>
435
+ </div>
436
+ <div class="stat-item">
437
+ <div class="stat-value">{queue_info['active_tasks']}</div>
438
+ <div class="stat-label">处理中</div>
439
+ </div>
440
+ <div class="stat-item">
441
+ <div class="stat-value">{queue_info['worker_threads']}</div>
442
+ <div class="stat-label">工作线程</div>
443
+ </div>
444
+ </div>
445
 
446
+ <div class="wait-time">
447
+ <p><b>当前预计等待时间:</b> {format_time(queue_info['estimated_wait'])}</p>
 
448
  </div>
449
+
450
+ <h4>最近处理的任务</h4>
451
+ <table class="recent-tasks">
452
+ <tr>
453
+ <th>任务ID</th>
454
+ <th>请求时间</th>
455
+ <th>处理时间</th>
456
+ </tr>
457
+ {tasks_html}
458
+ </table>
459
  </div>
460
  """
461
+
462
+ def ui_get_result(task_id):
463
+ """获取任务结果的UI函数
464
+
465
+ Args:
466
+ task_id: 任务ID
467
+
468
+ Returns:
469
+ 任务结果
470
+ """
471
+ if not task_id:
472
+ return None
473
+
474
+ status = check_status(task_id)
475
 
476
+ if status['status'] == 'completed':
477
+ return status.get('result', None)
478
+
479
+ return None
480
 
481
+ def launch_workers():
482
+ """启动工作线程"""
483
+ global running
484
+ running = True
485
+
486
+ # 创建工作线程
487
+ for _ in range(worker_threads):
488
+ worker = threading.Thread(target=queue_processor)
489
+ worker.daemon = True
490
+ worker.start()
491
 
492
+ # 自定义CSS
493
+ custom_css = """
494
+ .container {
495
+ max-width: 1200px;
496
+ margin: 0 auto;
497
+ }
 
 
 
498
 
499
+ .status-card, .queue-info-card {
500
+ background: #f7f7f7;
501
+ border-radius: 10px;
502
+ padding: 15px;
503
+ margin: 10px 0;
504
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
505
+ }
506
+
507
+ .status-card.success {
508
+ background: #e7f5e7;
509
+ border-left: 5px solid #28a745;
510
+ }
511
+
512
+ .status-card.error {
513
+ background: #f8d7da;
514
+ border-left: 5px solid #dc3545;
515
+ }
516
+
517
+ .error-message {
518
+ color: #dc3545;
519
+ font-weight: bold;
520
+ padding: 10px;
521
+ background: #f8d7da;
522
+ border-radius: 5px;
523
+ }
524
+
525
+ .notice {
526
+ color: #0c5460;
527
+ background-color: #d1ecf1;
528
+ padding: 10px;
529
+ border-radius: 5px;
530
+ }
531
+
532
+ .queue-stats {
533
+ display: flex;
534
+ justify-content: space-around;
535
+ margin: 20px 0;
536
+ }
537
+
538
+ .stat-item {
539
+ text-align: center;
540
+ }
541
+
542
+ .stat-value {
543
+ font-size: 24px;
544
+ font-weight: bold;
545
+ color: #007bff;
546
+ }
547
+
548
+ .stat-label {
549
+ color: #6c757d;
550
+ }
551
+
552
+ .wait-time {
553
+ text-align: center;
554
+ margin: 15px 0;
555
+ padding: 10px;
556
+ background: #e2e3e5;
557
+ border-radius: 5px;
558
+ }
559
+
560
+ .recent-tasks {
561
+ width: 100%;
562
+ border-collapse: collapse;
563
+ margin-top: 10px;
564
+ }
565
+
566
+ .recent-tasks th, .recent-tasks td {
567
+ border: 1px solid #dee2e6;
568
+ padding: 8px;
569
+ text-align: center;
570
+ }
571
+
572
+ .recent-tasks th {
573
+ background-color: #e9ecef;
574
+ }
575
+
576
+ .tabs {
577
+ margin-top: 20px;
578
+ }
579
+ """
580
+
581
+ # 初始化并启动工作线程
582
+ launch_workers()
583
 
584
  # 创建Gradio接口
585
+ with gr.Blocks(css=custom_css) as demo:
586
+ gr.Markdown("# 代码评估服务")
587
+ gr.Markdown("支持多种编程语言的代码评估服务,采用队列机制处理请求")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
 
589
+ with gr.Tab("提交任务"):
590
+ with gr.Row():
591
+ with gr.Column(scale=2):
592
+ input_json = gr.JSON(label="输入数据")
593
+ submit_btn = gr.Button("提交任务", variant="primary")
594
+ task_id_output = gr.Textbox(label="任务ID", visible=True)
595
+ status_html = gr.HTML(label="状态")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
 
597
+ with gr.Column(scale=1):
598
+ queue_info_html = gr.HTML()
599
+ refresh_queue_btn = gr.Button("刷新队列状态")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
 
601
+ with gr.Tab("查询任务"):
602
  with gr.Row():
603
+ with gr.Column(scale=1):
604
+ check_task_id = gr.Textbox(label="任务ID")
605
+ check_btn = gr.Button("查询状态", variant="primary")
606
+ get_result_btn = gr.Button("获取结果")
 
 
 
607
 
608
+ with gr.Column(scale=2):
609
+ check_status_html = gr.HTML()
610
+ result_output = gr.JSON(label="任务结果")
611
+
612
+ # 定义更新函数
613
+ def update_queue_info():
614
+ return ui_get_queue_info()
615
+
616
+ # 定时更新队列信息
617
+ demo.load(update_queue_info, None, queue_info_html, every=5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
618
 
619
+ # 事件处理
620
+ submit_btn.click(ui_submit, inputs=[input_json], outputs=[task_id_output, status_html])
621
+ refresh_queue_btn.click(update_queue_info, None, queue_info_html)
622
+ check_btn.click(ui_check_status, inputs=[check_task_id], outputs=[check_status_html])
623
+ get_result_btn.click(ui_get_result, inputs=[check_task_id], outputs=[result_output])
624
 
625
+ # 添加兼容原有接口的评估端点
626
+ demo.queue()
627
+ evaluate_endpoint = demo.load(fn=synchronous_evaluate, inputs=gr.JSON(), outputs=gr.JSON(), api_name="evaluate")
628
+
629
+ if __name__ == "__main__":
630
  try:
631
+ demo.launch()
632
+ finally:
633
+ # 停止工作线程
634
+ running = False