Spaces:
Sleeping
Sleeping
朱东升
commited on
Commit
·
edf1ecb
1
Parent(s):
890be77
update9
Browse files
app.py
CHANGED
@@ -28,12 +28,18 @@ completed_tasks = []
|
|
28 |
task_id_counter = 0
|
29 |
task_lock = threading.Lock()
|
30 |
last_update_time = datetime.now() # 替换update_event为时间戳跟踪
|
|
|
|
|
|
|
31 |
|
32 |
def trigger_ui_update():
|
33 |
"""触发UI更新事件"""
|
34 |
-
global last_update_time
|
35 |
with task_lock:
|
36 |
-
last_update_time = datetime.now() #
|
|
|
|
|
|
|
37 |
|
38 |
def get_next_task_id():
|
39 |
global task_id_counter
|
@@ -126,32 +132,53 @@ def process_task_queue():
|
|
126 |
|
127 |
# 更新任务状态
|
128 |
with task_lock:
|
129 |
-
|
130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
|
132 |
# 处理任务
|
|
|
133 |
result = evaluate(task_info["data"])
|
|
|
134 |
|
135 |
# 更新任务结果
|
136 |
with task_lock:
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
task_queue.task_done()
|
152 |
|
153 |
except Exception as e:
|
154 |
-
print(f"
|
|
|
|
|
155 |
time.sleep(1)
|
156 |
|
157 |
def evaluate(input_data):
|
@@ -317,7 +344,9 @@ def get_queue_status():
|
|
317 |
"completed_at": task["completed_at"].strftime("%Y-%m-%d %H:%M:%S") if "completed_at" in task else "",
|
318 |
"duration": str(task["completed_at"] - task["submitted_at"]) if "completed_at" in task else ""
|
319 |
} for task in completed_tasks[-5:] # 只显示最近5个完成的任务
|
320 |
-
]
|
|
|
|
|
321 |
}
|
322 |
|
323 |
def render_queue_status():
|
@@ -439,9 +468,20 @@ def render_queue_status():
|
|
439 |
return html
|
440 |
|
441 |
def refresh_ui():
|
442 |
-
"""定期刷新UI
|
443 |
-
|
444 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
445 |
|
446 |
def submit_json_data(json_data):
|
447 |
"""提交JSON数据处理接口"""
|
@@ -452,14 +492,63 @@ def submit_json_data(json_data):
|
|
452 |
else:
|
453 |
data = json_data
|
454 |
|
|
|
|
|
|
|
|
|
455 |
result = submit_task(data)
|
456 |
response = json.dumps(result, ensure_ascii=False, indent=2)
|
457 |
|
|
|
|
|
|
|
|
|
|
|
458 |
# 返回任务提交结果和最新的队列状态
|
459 |
-
|
|
|
460 |
except Exception as e:
|
|
|
461 |
return json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False, indent=2), render_queue_status()
|
462 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
# 创建Gradio接口
|
464 |
with gr.Blocks(title="代码评估服务", theme=gr.themes.Soft()) as demo:
|
465 |
gr.Markdown("""
|
@@ -467,9 +556,31 @@ with gr.Blocks(title="代码评估服务", theme=gr.themes.Soft()) as demo:
|
|
467 |
### 支持多种编程语言的代码评估服务
|
468 |
""")
|
469 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
470 |
with gr.Tab("任务队列状态"):
|
471 |
status_html = gr.HTML(render_queue_status)
|
|
|
472 |
refresh_button = gr.Button("刷新状态")
|
|
|
473 |
|
474 |
# 根据Gradio版本使用不同的事件注册方法
|
475 |
if 'gradio_version' not in locals():
|
@@ -478,129 +589,116 @@ with gr.Blocks(title="代码评估服务", theme=gr.themes.Soft()) as demo:
|
|
478 |
|
479 |
if gradio_version.startswith("3."):
|
480 |
# Gradio 3.x 方式
|
481 |
-
refresh_button.click(fn=refresh_ui, outputs=status_html, concurrency_limit=2)
|
482 |
else:
|
483 |
# Gradio 4.x 方式 (不使用concurrency_limit参数)
|
484 |
-
refresh_button.click(
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
#
|
491 |
-
|
492 |
-
|
493 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
494 |
<script>
|
495 |
-
//
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
|
|
501 |
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
//
|
510 |
-
|
511 |
-
|
512 |
-
button.textContent.includes('刷新') ||
|
513 |
-
button.innerHTML.includes('刷新状态')) {
|
514 |
-
refreshButton = button;
|
515 |
-
break;
|
516 |
-
}
|
517 |
-
}
|
518 |
|
519 |
-
// 如果没找到,尝试查找第一个标签页中的第一个按钮
|
520 |
-
if (!refreshButton) {
|
521 |
-
const firstTab = document.querySelector('.tabitem');
|
522 |
-
if (firstTab) {
|
523 |
-
const firstTabButton = firstTab.querySelector('button');
|
524 |
-
if (firstTabButton) {
|
525 |
-
refreshButton = firstTabButton;
|
526 |
-
}
|
527 |
-
}
|
528 |
-
}
|
529 |
-
|
530 |
-
// 如果找到按钮,模拟点击
|
531 |
if (refreshButton) {
|
532 |
refreshButton.click();
|
533 |
-
console.log("
|
534 |
-
} else {
|
535 |
-
console.warn("未找到刷新按钮");
|
536 |
-
// 如果找不到按钮,尝试重新加载整个页面
|
537 |
-
// 但要限制频率,避免无限刷新
|
538 |
-
if (consecutiveNoChange > 10) {
|
539 |
-
console.log("长时间未找到刷新按钮,刷新页面");
|
540 |
-
location.reload();
|
541 |
-
return;
|
542 |
-
}
|
543 |
}
|
544 |
-
|
545 |
-
//
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
if (rows.length > 0) {
|
556 |
-
activeTasks = rows;
|
557 |
-
break;
|
558 |
-
}
|
559 |
-
}
|
560 |
-
}
|
561 |
-
|
562 |
-
const currentTaskCount = activeTasks.length || 0;
|
563 |
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
568 |
}
|
569 |
-
|
570 |
-
lastTaskCount = currentTaskCount;
|
571 |
-
}, 500);
|
572 |
-
} catch (e) {
|
573 |
-
console.error("自动刷新错误:", e);
|
574 |
-
}
|
575 |
-
}
|
576 |
-
|
577 |
-
// 周期性检查刷新,确保即使在HF Spaces资源受限情况下也能工作
|
578 |
-
let refreshTimer = setInterval(autoRefresh, refreshInterval);
|
579 |
-
|
580 |
-
// 确保页面可见性变化时刷新机制正常工作
|
581 |
-
document.addEventListener('visibilitychange', function() {
|
582 |
-
if (document.visibilityState === 'visible') {
|
583 |
-
// 页面变为可见时立即刷新一次
|
584 |
-
autoRefresh();
|
585 |
-
// 如果计时器被清除,重新创建
|
586 |
-
if (!refreshTimer) {
|
587 |
-
refreshTimer = setInterval(autoRefresh, refreshInterval);
|
588 |
}
|
589 |
}
|
590 |
-
})
|
|
|
|
|
591 |
|
592 |
-
//
|
593 |
-
setTimeout(
|
594 |
}
|
595 |
|
596 |
-
//
|
597 |
if (document.readyState === 'loading') {
|
598 |
-
document.addEventListener('DOMContentLoaded',
|
599 |
} else {
|
600 |
-
|
601 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
602 |
</script>
|
603 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
604 |
|
605 |
with gr.Tab("提交新任务"):
|
606 |
with gr.Row():
|
@@ -677,18 +775,8 @@ with gr.Blocks(title="代码评估服务", theme=gr.themes.Soft()) as demo:
|
|
677 |
```
|
678 |
""")
|
679 |
|
680 |
-
#
|
681 |
-
|
682 |
-
api_input = gr.JSON()
|
683 |
-
api_output = gr.JSON()
|
684 |
-
|
685 |
-
# 根据Gradio版本使用不同的事件注册方法
|
686 |
-
if gradio_version.startswith("3."):
|
687 |
-
# Gradio 3.x 方式
|
688 |
-
api_input.change(fn=evaluate, inputs=api_input, outputs=api_output, concurrency_limit=2)
|
689 |
-
else:
|
690 |
-
# Gradio 4.x 方式
|
691 |
-
api_input.change(fn=evaluate, inputs=api_input, outputs=api_output)
|
692 |
|
693 |
if __name__ == "__main__":
|
694 |
# 检测Gradio版本以适配不同版本的API
|
@@ -696,78 +784,108 @@ if __name__ == "__main__":
|
|
696 |
gradio_version = getattr(gradio, "__version__", "unknown")
|
697 |
print(f"当前Gradio版本: {gradio_version}")
|
698 |
|
|
|
|
|
|
|
|
|
|
|
699 |
try:
|
700 |
-
#
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
else:
|
707 |
-
# Gradio 4.x 或更高版本可能有不同的队列API
|
708 |
-
try:
|
709 |
-
# 尝试新版本的queue方法
|
710 |
-
demo.queue(api_name="/api", max_size=30)
|
711 |
-
except:
|
712 |
-
# 如果失败,尝试不带参数的queue方法
|
713 |
-
demo.queue()
|
714 |
-
except Exception as e:
|
715 |
-
# 任何队列配置错误,记录后继续
|
716 |
-
print(f"配置队列时出错: {e}")
|
717 |
-
print("继续启动应用,但队列功能可能不可用")
|
718 |
-
|
719 |
-
# 针对Hugging Face Spaces环境的优化配置
|
720 |
-
is_hf_space = os.environ.get("SPACE_ID") is not None
|
721 |
-
server_port = int(os.environ.get("PORT", 7860))
|
722 |
-
server_name = "0.0.0.0" if is_hf_space else "127.0.0.1"
|
723 |
|
724 |
# 尝试使用兼容所有版本的参数启动
|
725 |
launch_kwargs = {
|
726 |
-
"server_name":
|
727 |
-
"server_port":
|
728 |
"share": False,
|
729 |
}
|
730 |
|
731 |
-
#
|
732 |
-
if gradio_version.startswith("3."):
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
742 |
except Exception as e:
|
743 |
-
# 记录错误并使用最简配置重试
|
744 |
print(f"启动时发生错误: {e}")
|
745 |
-
|
|
|
|
|
|
|
746 |
try:
|
747 |
-
|
748 |
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|
749 |
except Exception as e2:
|
750 |
print(f"最小配置启动也失败: {e2}")
|
|
|
751 |
|
752 |
# 终极回退方案:创建最简单的接口并启动
|
753 |
-
print("尝试创建备用界面...")
|
754 |
try:
|
755 |
-
|
756 |
|
|
|
757 |
def simple_evaluate(json_data):
|
758 |
try:
|
759 |
-
|
|
|
|
|
|
|
760 |
except Exception as e:
|
761 |
return {"error": str(e)}
|
762 |
|
763 |
backup_demo = gr.Interface(
|
764 |
fn=simple_evaluate,
|
765 |
inputs=gr.Textbox(label="JSON输入"),
|
766 |
-
outputs=gr.
|
767 |
title="代码评估服务 (备用界面)",
|
768 |
-
description="
|
769 |
)
|
770 |
|
771 |
backup_demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|
772 |
except Exception as e3:
|
773 |
print(f"备用界面也启动失败: {e3}")
|
|
|
|
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
|
|
|
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):
|
|
|
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():
|
|
|
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数据处理接口"""
|
|
|
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("""
|
|
|
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():
|
|
|
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():
|
|
|
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
|
|
|
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 |
+
# 启动应用到FastAPI
|
831 |
+
demo.launch(
|
832 |
+
**launch_kwargs,
|
833 |
+
app=app,
|
834 |
+
max_threads=5,
|
835 |
+
)
|
836 |
+
else:
|
837 |
+
# Gradio 3.x的方式
|
838 |
+
# 设置队列,并添加API支持
|
839 |
+
try:
|
840 |
+
demo.queue(api_open=True, max_size=30)
|
841 |
+
except Exception as e:
|
842 |
+
logger.warning(f"配置队列时出错: {e}")
|
843 |
+
|
844 |
+
# 启动应用
|
845 |
+
demo.launch(
|
846 |
+
**launch_kwargs,
|
847 |
+
debug=False,
|
848 |
+
show_api=True,
|
849 |
+
max_threads=5,
|
850 |
+
concurrency_limit=2
|
851 |
+
)
|
852 |
+
|
853 |
except Exception as e:
|
|
|
854 |
print(f"启动时发生错误: {e}")
|
855 |
+
import traceback
|
856 |
+
traceback.print_exc()
|
857 |
+
|
858 |
+
# 尝试最简配置启动
|
859 |
try:
|
860 |
+
print("使用最小配置重试...")
|
861 |
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|
862 |
except Exception as e2:
|
863 |
print(f"最小配置启动也失败: {e2}")
|
864 |
+
traceback.print_exc()
|
865 |
|
866 |
# 终极回退方案:创建最简单的接口并启动
|
|
|
867 |
try:
|
868 |
+
print("尝试创建备用界面...")
|
869 |
|
870 |
+
import gradio as gr
|
871 |
def simple_evaluate(json_data):
|
872 |
try:
|
873 |
+
print(f"备用界面收到请求: {json_data[:100] if isinstance(json_data, str) else 'Not a string'}")
|
874 |
+
data = json.loads(json_data) if isinstance(json_data, str) else json_data
|
875 |
+
result = submit_task(data)
|
876 |
+
return json.dumps(result, ensure_ascii=False)
|
877 |
except Exception as e:
|
878 |
return {"error": str(e)}
|
879 |
|
880 |
backup_demo = gr.Interface(
|
881 |
fn=simple_evaluate,
|
882 |
inputs=gr.Textbox(label="JSON输入"),
|
883 |
+
outputs=gr.Textbox(label="结果"),
|
884 |
title="代码评估服务 (备用界面)",
|
885 |
+
description="原界面启动失败,这是简化版本。提交格式: [{\"language\": \"python\", \"prompt\": \"def add(a, b):\\n\", \"processed_completions\": [\" return a + b\"], \"tests\": \"assert add(1, 2) == 3\"}]"
|
886 |
)
|
887 |
|
888 |
backup_demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|
889 |
except Exception as e3:
|
890 |
print(f"备用界面也启动失败: {e3}")
|
891 |
+
traceback.print_exc()
|