#!/usr/bin/env python3 """ SAGE提交处理 - OSS模式 使用阿里云OSS替代git/http提交方式 """ import json import os import sys from datetime import datetime from typing import Dict, Any, Tuple from pathlib import Path # 导入OSS提交处理器 try: from src.oss.oss_submission_handler import OSSSubmissionHandler OSS_AVAILABLE = True except ImportError as e: print(f"⚠️ OSS模块不可用,将使用备用模式: {e}") OSS_AVAILABLE = False def format_error(msg): return f"

{msg}

" def format_success(msg): return f"

{msg}

" def format_warning(msg): return f"

{msg}

" def validate_sage_submission(submission_data: Dict[str, Any]) -> Tuple[bool, str]: """验证SAGE基准提交格式""" # 检查必需的顶级字段 required_fields = ["submission_org", "submission_email", "predictions"] for field in required_fields: if field not in submission_data: return False, f"缺少必需字段: {field}" # 验证邮箱格式(基本验证) email = submission_data["submission_email"] if "@" not in email or "." not in email: return False, "邮箱格式无效" # 验证predictions predictions = submission_data["predictions"] if not isinstance(predictions, list) or len(predictions) == 0: return False, "predictions必须是非空列表" for i, prediction in enumerate(predictions): # 检查必需的prediction字段 pred_required_fields = ["original_question_id", "content", "reasoning_content"] for field in pred_required_fields: if field not in prediction: return False, f"预测{i}中缺少字段: {field}" # 验证content数组 content = prediction["content"] reasoning_content = prediction["reasoning_content"] if not isinstance(content, list) or len(content) != 4: return False, f"预测{i}的content必须是包含4个项目的列表" if not isinstance(reasoning_content, list): return False, f"预测{i}的reasoning_content必须是列表类型" # # reasoning_content可以为空列表,或者包含4个项目 # if len(reasoning_content) != 0 and len(reasoning_content) != 4: # return False, f"预测{i}的reasoning_content必须是空列表或包含4个项目的列表" # 验证question ID if not isinstance(prediction["original_question_id"], int): return False, f"预测{i}的question ID必须是整数" return True, "提交格式有效" def save_submission_file(submission_data: Dict[str, Any], submissions_dir: str = "./submissions") -> str: """保存提交文件到指定目录""" # 确保submissions目录存在 os.makedirs(submissions_dir, exist_ok=True) # 生成文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") org_name = submission_data["submission_org"].replace(" ", "_").replace("/", "_").replace("\\", "_") filename = f"submission_{org_name}_{timestamp}.json" # 完整文件路径 file_path = os.path.join(submissions_dir, filename) # 保存文件 with open(file_path, 'w', encoding='utf-8') as f: json.dump(submission_data, f, indent=2, ensure_ascii=False) return file_path def process_sage_submission_simple(submission_file, model_name=None, org_name=None, email=None) -> str: """ 处理SAGE基准提交文件 - 文件收集模式 只负责验证和保存,不进行评测 """ try: # 读取提交的文件 if submission_file is None: return format_error("❌ 没有上传文件。请选择一个JSON文件。") # submission_file是文件路径字符串 try: with open(submission_file, 'r', encoding='utf-8') as f: content = f.read() except Exception as e: return format_error(f"❌ 读取文件时出错: {str(e)}") # 解析JSON try: submission_data = json.loads(content) except json.JSONDecodeError as e: return format_error(f"❌ JSON格式无效: {str(e)}") # 如果表单提供了模型名、组织名和邮箱,使用表单数据 if model_name: submission_data["model_name"] = model_name.strip() if org_name and email: submission_data["submission_org"] = org_name.strip() submission_data["submission_email"] = email.strip() # 验证提交格式 is_valid, message = validate_sage_submission(submission_data) if not is_valid: return format_error(f"❌ 提交验证失败: {message}") # 保存提交文件 try: saved_path = save_submission_file(submission_data) print(f"✅ 提交文件已保存到: {saved_path}") # OSS上传策略 if OSS_AVAILABLE: try: # 使用OSS提交处理器 oss_handler = OSSSubmissionHandler() result = oss_handler.process_sage_submission(submission_data, org_name, email) # 如果OSS处理成功,直接返回结果 if "提交成功" in result: return result else: # OSS失败,继续使用备用模式 print(f"⚠️ OSS提交失败,使用备用模式: {result}") except Exception as e: print(f"⚠️ OSS提交异常,使用备用模式: {e}") # 备用模式:本地保存 filename = os.path.basename(saved_path) # 生成成功消息 org = submission_data["submission_org"] email_addr = submission_data["submission_email"] num_predictions = len(submission_data["predictions"]) success_msg = format_success(f""" 🎉 提交成功!

📋 提交信息:
• 组织: {org}
• 邮箱: {email_addr}
• 预测数量: {num_predictions} 个问题
• 文件名: {filename}

🚀 存储状态:
文件已保存到本地存储,等待系统同步到评测环境。

评测流程:
您的提交将使用LLM-as-Judge进行自动评估,包括科学推理能力的全面测试。
评测完成后,结果将自动出现在排行榜中。

🕐 预计时间:
• 正常情况: 5-15分钟
• 同步延迟: 15-60分钟

🧪 感谢您参与SAGE科学推理基准测试! """) return success_msg except Exception as e: return format_error(f"❌ 保存提交文件时出错: {str(e)}") except Exception as e: return format_error(f"❌ 提交处理失败: {str(e)}") def get_submission_stats(submissions_dir: str = "./submissions") -> Dict[str, Any]: """获取提交统计信息""" if not os.path.exists(submissions_dir): return {"total": 0, "recent": []} submissions = [] for filename in os.listdir(submissions_dir): if filename.startswith("submission_") and filename.endswith(".json"): file_path = os.path.join(submissions_dir, filename) try: with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) # 提取信息 timestamp_str = filename.split("_")[-1].replace(".json", "") try: timestamp = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S") formatted_time = timestamp.strftime("%Y-%m-%d %H:%M") except: formatted_time = timestamp_str submissions.append({ "org": data.get("submission_org", "Unknown"), "email": data.get("submission_email", ""), "time": formatted_time, "predictions": len(data.get("predictions", [])) }) except Exception: continue # 按时间排序,最新的在前 submissions.sort(key=lambda x: x["time"], reverse=True) return { "total": len(submissions), "recent": submissions[:10] # 最近10个 } # 移除了原有的HTTP推送函数,现在使用OSS模式