File size: 5,032 Bytes
d49524c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# app.py - FastAPI backend for Math Solution Classifier

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from pydantic import BaseModel
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(title="Math Solution Classifier API")

# Add CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Global variables for model and tokenizer
model = None
tokenizer = None
label_mapping = {0: "correct", 1: "conceptually-flawed", 2: "computationally-flawed"}

class ClassificationRequest(BaseModel):
    question: str
    solution: str

class ClassificationResponse(BaseModel):
    classification: str
    confidence: float

def load_model():
    """Load your trained model here"""
    global model, tokenizer
    
    try:
        # Replace these with your actual model path/name
        # Option 1: Load from local files
        # model = AutoModelForSequenceClassification.from_pretrained("./your_model_directory")
        # tokenizer = AutoTokenizer.from_pretrained("./your_model_directory")
        
        # Option 2: Load from Hugging Face Hub (if you upload your model there)
        # model = AutoModelForSequenceClassification.from_pretrained("your-username/your-model-name")
        # tokenizer = AutoTokenizer.from_pretrained("your-username/your-model-name")
        
        # For now, we'll use a placeholder - replace this with your actual model loading
        logger.warning("Using placeholder model loading - replace with your actual model!")
        
        # Placeholder model loading (replace this!)
        model_name = "distilbert-base-uncased"  # Replace with your model
        tokenizer = AutoTokenizer.from_pretrained(model_name)
        model = AutoModelForSequenceClassification.from_pretrained(
            model_name, 
            num_labels=3,
            ignore_mismatched_sizes=True
        )
        
        logger.info("Model loaded successfully")
        
    except Exception as e:
        logger.error(f"Error loading model: {e}")
        raise

def classify_solution(question: str, solution: str) -> tuple:
    """
    Classify the math solution
    Returns: (classification_label, confidence_score)
    """
    try:
        # Combine question and solution for input
        text_input = f"Question: {question}\nSolution: {solution}"
        
        # Tokenize input
        inputs = tokenizer(
            text_input,
            return_tensors="pt",
            truncation=True,
            padding=True,
            max_length=512
        )
        
        # Get model prediction
        with torch.no_grad():
            outputs = model(**inputs)
            predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
            predicted_class = torch.argmax(predictions, dim=-1).item()
            confidence = predictions[0][predicted_class].item()
        
        classification = label_mapping[predicted_class]
        
        return classification, confidence
        
    except Exception as e:
        logger.error(f"Error during classification: {e}")
        raise HTTPException(status_code=500, detail=f"Classification error: {str(e)}")

@app.on_event("startup")
async def startup_event():
    """Load model on startup"""
    logger.info("Loading model...")
    load_model()

@app.post("/classify", response_model=ClassificationResponse)
async def classify_math_solution(request: ClassificationRequest):
    """
    Classify a math solution as correct, conceptually flawed, or computationally flawed
    """
    if not model or not tokenizer:
        raise HTTPException(status_code=503, detail="Model not loaded")
    
    if not request.question.strip() or not request.solution.strip():
        raise HTTPException(status_code=400, detail="Both question and solution are required")
    
    try:
        classification, confidence = classify_solution(request.question, request.solution)
        
        return ClassificationResponse(
            classification=classification,
            confidence=confidence
        )
        
    except Exception as e:
        logger.error(f"Classification failed: {e}")
        raise HTTPException(status_code=500, detail="Classification failed")

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {"status": "healthy", "model_loaded": model is not None}

# Serve the frontend (for Hugging Face Spaces)
app.mount("/static", StaticFiles(directory="static"), name="static")

@app.get("/")
async def serve_frontend():
    return FileResponse("static/index.html")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=7860)  # Port 7860 is standard for HF Spaces