llm-deployment / scripts /round2.py
dp291203's picture
push
8afaf1e
Raw
History Blame Contribute Delete
9.15 kB
"""
Round 2 task distribution script for LLM Deployment System.
This script handles distributing round 2 tasks to students who have
completed their round 1 repositories.
"""
import json
import time
import argparse
from datetime import datetime, timezone
from typing import Dict, List, Any, Optional
import requests
from .config import config
from .database import db_manager, TaskStatus, EvaluationStatus
from .logger import get_logger, log_request_info
from .task_generator import get_task_generator
from .db_utils import DatabaseUtils
logger = get_logger(__name__)
class Round2Distributor:
"""Handles round 2 task distribution."""
def __init__(self):
self.task_generator = get_task_generator()
self.distribution_log = []
def get_eligible_students(self) -> List[Dict[str, Any]]:
"""Get students who are eligible for round 2 tasks."""
eligible_students = []
try:
# Get all submissions with completed repositories
with db_manager.get_session() as session:
# Get all submissions
submissions = session.query(db_manager.Submission).all()
for submission in submissions:
# Check if student has completed round 1
round1_tasks = DatabaseUtils.get_tasks_by_submission(session, submission.id, round=1)
completed_round1 = any(
task.status == TaskStatus.RECEIVED and task.repos
for task in round1_tasks
)
if completed_round1:
# Check if student already has round 2 tasks
round2_tasks = DatabaseUtils.get_tasks_by_submission(session, submission.id, round=2)
if not round2_tasks:
eligible_students.append({
'submission_id': submission.id,
'email': submission.email,
'endpoint': submission.endpoint,
'secret': submission.secret,
'github_username': submission.github_username,
'github_repo_url': submission.github_repo_url
})
logger.info(f"Found {len(eligible_students)} students eligible for round 2")
return eligible_students
except Exception as e:
logger.error(f"Failed to get eligible students: {e}")
raise
def distribute_tasks(self, students: List[Dict[str, Any]], delay: float = 1.0) -> Dict[str, Any]:
"""Distribute round 2 tasks to eligible students."""
results = {
'total': len(students),
'successful': 0,
'failed': 0,
'errors': []
}
logger.info(f"Starting round 2 distribution to {len(students)} students")
for i, student in enumerate(students):
try:
logger.info(f"Processing student {i+1}/{len(students)}: {student['email']}")
# Generate round 2 task for student
task = self.task_generator.generate_task(
email=student['email'],
round_num=2
)
# Send task to student's endpoint
success = self._send_task_to_student(student, task, delay)
if success:
results['successful'] += 1
# Store in database
db_task = db_manager.create_task(student['submission_id'], task)
db_manager.update_task_status(task['task_id'], TaskStatus.SENT)
self.distribution_log.append({
'email': student['email'],
'task_id': task['task_id'],
'status': 'sent',
'timestamp': datetime.now(timezone.utc).isoformat()
})
else:
results['failed'] += 1
self.distribution_log.append({
'email': student['email'],
'status': 'failed',
'timestamp': datetime.now(timezone.utc).isoformat()
})
except Exception as e:
logger.error(f"Error processing student {student['email']}: {e}")
results['failed'] += 1
results['errors'].append({
'email': student['email'],
'error': str(e)
})
logger.info(f"Round 2 distribution completed: {results['successful']}/{results['total']} successful")
return results
def _send_task_to_student(self, student: Dict[str, Any], task: Dict[str, Any], delay: float) -> bool:
"""Send round 2 task to student's API endpoint."""
try:
# Prepare request payload
payload = {
'email': student['email'],
'secret': student['secret']
}
headers = {
'Content-Type': 'application/json',
'User-Agent': 'LLM-Deployment-System/1.0'
}
# Send request
response = requests.post(
student['endpoint'],
json=payload,
headers=headers,
timeout=30
)
# Check response
if response.status_code == 200:
logger.info(f"Successfully sent round 2 task to {student['email']}")
time.sleep(delay) # Rate limiting
return True
else:
logger.warning(f"Failed to send round 2 task to {student['email']}: HTTP {response.status_code}")
return False
except requests.RequestException as e:
logger.error(f"Request error sending round 2 task to {student['email']}: {e}")
return False
except Exception as e:
logger.error(f"Unexpected error sending round 2 task to {student['email']}: {e}")
return False
def save_distribution_log(self, log_file: str = 'round2_distribution_log.json'):
"""Save distribution log to file."""
try:
with open(log_file, 'w') as f:
json.dump(self.distribution_log, f, indent=2)
logger.info(f"Saved round 2 distribution log to {log_file}")
except Exception as e:
logger.error(f"Failed to save round 2 distribution log: {e}")
def generate_summary_report(self) -> str:
"""Generate a summary report of the round 2 distribution."""
if not self.distribution_log:
return "No round 2 distribution data available"
total = len(self.distribution_log)
sent = len([log for log in self.distribution_log if log['status'] == 'sent'])
failed = len([log for log in self.distribution_log if log['status'] == 'failed'])
report = f"""
Round 2 Task Distribution Summary
================================
Total students eligible: {total}
Successfully sent: {sent}
Failed: {failed}
Success rate: {(sent/total)*100:.1f}%
Timestamp: {datetime.now(timezone.utc).isoformat()}
"""
return report
def main():
"""Main function for command-line usage."""
parser = argparse.ArgumentParser(description='Distribute Round 2 tasks to eligible students')
parser.add_argument('--delay', type=float, default=1.0, help='Delay between requests (seconds)')
parser.add_argument('--log-file', default='round2_distribution_log.json', help='Distribution log file')
parser.add_argument('--dry-run', action='store_true', help='Show what would be sent without actually sending')
parser.add_argument('--min-eligible', type=int, default=1, help='Minimum number of eligible students required')
args = parser.parse_args()
try:
distributor = Round2Distributor()
# Get eligible students
students = distributor.get_eligible_students()
if len(students) < args.min_eligible:
logger.error(f"Only {len(students)} eligible students found, minimum {args.min_eligible} required")
exit(1)
if args.dry_run:
print(f"Dry run: Would process {len(students)} eligible students")
for student in students[:5]: # Show first 5
print(f" - {student['email']} -> {student['endpoint']}")
if len(students) > 5:
print(f" ... and {len(students) - 5} more")
return
# Distribute tasks
results = distributor.distribute_tasks(students, args.delay)
# Save log
distributor.save_distribution_log(args.log_file)
# Print summary
print(distributor.generate_summary_report())
# Exit with appropriate code
if results['failed'] == 0:
exit(0)
else:
exit(1)
except Exception as e:
logger.error(f"Round 2 distribution failed: {e}")
exit(1)
if __name__ == '__main__':
main()