Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -49,11 +49,102 @@ class OnnxBgeEmbeddings(Embeddings):
|
|
| 49 |
def embed_query(self, text):
|
| 50 |
return self._process_batch(["Represent this sentence for searching relevant passages: " + text])[0]
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
# ---------------------------------------------------------
|
| 53 |
# 2. OPTIMIZED LLM (Qwen 2.5 - 0.5B) - STRICT GRADING
|
| 54 |
# ---------------------------------------------------------
|
| 55 |
class LLMEvaluator:
|
| 56 |
def __init__(self):
|
|
|
|
| 57 |
self.repo_id = "onnx-community/Qwen2.5-0.5B-Instruct"
|
| 58 |
self.local_dir = "onnx_qwen_local"
|
| 59 |
|
|
@@ -84,37 +175,54 @@ class LLMEvaluator:
|
|
| 84 |
)
|
| 85 |
|
| 86 |
def evaluate(self, context, question, student_answer, max_marks):
|
| 87 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
messages = [
|
| 89 |
-
{"role": "system", "content":
|
| 90 |
-
{"role": "user", "content":
|
| 91 |
-
Task: Grade the student answer based ONLY on the Reference Text.
|
| 92 |
-
|
| 93 |
-
REFERENCE TEXT:
|
| 94 |
-
{context}
|
| 95 |
-
|
| 96 |
-
QUESTION:
|
| 97 |
-
{question}
|
| 98 |
-
|
| 99 |
-
STUDENT ANSWER:
|
| 100 |
-
{student_answer}
|
| 101 |
-
|
| 102 |
-
-----------------------------
|
| 103 |
-
GRADING LOGIC:
|
| 104 |
-
1. READ the Reference Text and use that as the ground truth. What does it actually say about the Question?
|
| 105 |
-
2. COMPARE it to the Student Answer, do not forcefully agree with the answer by seeing things that are not there. You are to penalise irrelevant text and contradictions whenever you encounter them.
|
| 106 |
-
3 START with 0 marks and IF the answers line up to the reference text in a meaningful way, then add marks porportionally. ONLY GIVE MARKS FOR CORRECT STATEMENT STRICTLY BASED ON THE REFERENCE TEXT AND NOTHING ELSE IN THIS WORLD.
|
| 107 |
-
4. IF the Student Answer claims things not found in the text , he is incorrect and HALLUCINATING. Do not give marks for that statment/phrase
|
| 108 |
-
5. IF the Student Answer contradicts the text (e.g., Text says "hide personality" but Student says "show personality"), Do not give marks for that statment/phrase
|
| 109 |
-
|
| 110 |
-
VERDICT:
|
| 111 |
-
- If wrong: 0/{max_marks}
|
| 112 |
-
- If correct: {max_marks}/{max_marks}
|
| 113 |
-
|
| 114 |
-
OUTPUT FORMAT:
|
| 115 |
-
Score: [X]/{max_marks}
|
| 116 |
-
Feedback: [Brief explanation citing the text]
|
| 117 |
-
"""}
|
| 118 |
]
|
| 119 |
|
| 120 |
input_text = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
|
@@ -123,16 +231,18 @@ class LLMEvaluator:
|
|
| 123 |
with torch.no_grad():
|
| 124 |
outputs = self.model.generate(
|
| 125 |
**inputs,
|
| 126 |
-
max_new_tokens=
|
| 127 |
-
temperature=0.
|
| 128 |
-
|
| 129 |
-
|
|
|
|
| 130 |
)
|
| 131 |
|
| 132 |
input_length = inputs['input_ids'].shape[1]
|
| 133 |
response = self.tokenizer.decode(outputs[0][input_length:], skip_special_tokens=True)
|
| 134 |
return response
|
| 135 |
|
|
|
|
| 136 |
# ---------------------------------------------------------
|
| 137 |
# 3. Main Application Logic
|
| 138 |
# ---------------------------------------------------------
|
|
|
|
| 49 |
def embed_query(self, text):
|
| 50 |
return self._process_batch(["Represent this sentence for searching relevant passages: " + text])[0]
|
| 51 |
|
| 52 |
+
# ---------------------------------------------------------
|
| 53 |
+
# 2. OPTIMIZED LLM (Qwen 2.5 - 0.5B) - STRICT GRADING
|
| 54 |
+
# ---------------------------------------------------------
|
| 55 |
+
# class LLMEvaluator:
|
| 56 |
+
# def __init__(self):
|
| 57 |
+
# self.repo_id = "onnx-community/Qwen2.5-0.5B-Instruct"
|
| 58 |
+
# self.local_dir = "onnx_qwen_local"
|
| 59 |
+
|
| 60 |
+
# print(f"🔄 Preparing CPU LLM: {self.repo_id}...")
|
| 61 |
+
|
| 62 |
+
# if not os.path.exists(self.local_dir):
|
| 63 |
+
# print(f"📥 Downloading FP16 model to {self.local_dir}...")
|
| 64 |
+
# snapshot_download(
|
| 65 |
+
# repo_id=self.repo_id,
|
| 66 |
+
# local_dir=self.local_dir,
|
| 67 |
+
# allow_patterns=["config.json", "generation_config.json", "tokenizer*", "special_tokens_map.json", "*.jinja", "onnx/model_fp16.onnx*"]
|
| 68 |
+
# )
|
| 69 |
+
# print("✅ Download complete.")
|
| 70 |
+
|
| 71 |
+
# self.tokenizer = AutoTokenizer.from_pretrained(self.local_dir)
|
| 72 |
+
|
| 73 |
+
# sess_options = SessionOptions()
|
| 74 |
+
# sess_options.graph_optimization_level = GraphOptimizationLevel.ORT_DISABLE_ALL
|
| 75 |
+
|
| 76 |
+
# self.model = ORTModelForCausalLM.from_pretrained(
|
| 77 |
+
# self.local_dir,
|
| 78 |
+
# subfolder="onnx",
|
| 79 |
+
# file_name="model_fp16.onnx",
|
| 80 |
+
# use_cache=True,
|
| 81 |
+
# use_io_binding=False,
|
| 82 |
+
# provider=PROVIDERS[0],
|
| 83 |
+
# session_options=sess_options
|
| 84 |
+
# )
|
| 85 |
+
|
| 86 |
+
# def evaluate(self, context, question, student_answer, max_marks):
|
| 87 |
+
# # OPTIMIZED PROMPT FOR SMALL MODELS (0.5B)
|
| 88 |
+
# messages = [
|
| 89 |
+
# {"role": "system", "content": "You are a strictest, literal academic grader in the whole. You ONLY grade based on the provided text. You DO NOT use outside knowledge."},
|
| 90 |
+
# {"role": "user", "content": f"""
|
| 91 |
+
# Task: Grade the student answer based ONLY on the Reference Text.
|
| 92 |
+
|
| 93 |
+
# REFERENCE TEXT:
|
| 94 |
+
# {context}
|
| 95 |
+
|
| 96 |
+
# QUESTION:
|
| 97 |
+
# {question}
|
| 98 |
+
|
| 99 |
+
# STUDENT ANSWER:
|
| 100 |
+
# {student_answer}
|
| 101 |
+
|
| 102 |
+
# -----------------------------
|
| 103 |
+
# GRADING LOGIC:
|
| 104 |
+
# 1. READ the Reference Text and use that as the ground truth. What does it actually say about the Question?
|
| 105 |
+
# 2. COMPARE it to the Student Answer, do not forcefully agree with the answer by seeing things that are not there. You are to penalise irrelevant text and contradictions whenever you encounter them.
|
| 106 |
+
# 3 START with 0 marks and IF the answers line up to the reference text in a meaningful way, then add marks porportionally. ONLY GIVE MARKS FOR CORRECT STATEMENT STRICTLY BASED ON THE REFERENCE TEXT AND NOTHING ELSE IN THIS WORLD.
|
| 107 |
+
# 4. IF the Student Answer claims things not found in the text , he is incorrect and HALLUCINATING. Do not give marks for that statment/phrase
|
| 108 |
+
# 5. IF the Student Answer contradicts the text (e.g., Text says "hide personality" but Student says "show personality"), Do not give marks for that statment/phrase
|
| 109 |
+
|
| 110 |
+
# VERDICT:
|
| 111 |
+
# - If wrong: 0/{max_marks}
|
| 112 |
+
# - If correct: {max_marks}/{max_marks}
|
| 113 |
+
|
| 114 |
+
# OUTPUT FORMAT:
|
| 115 |
+
# Score: [X]/{max_marks}
|
| 116 |
+
# Feedback: [Brief explanation citing the text]
|
| 117 |
+
# """}
|
| 118 |
+
# ]
|
| 119 |
+
|
| 120 |
+
# input_text = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
| 121 |
+
# inputs = self.tokenizer(input_text, return_tensors="pt")
|
| 122 |
+
|
| 123 |
+
# with torch.no_grad():
|
| 124 |
+
# outputs = self.model.generate(
|
| 125 |
+
# **inputs,
|
| 126 |
+
# max_new_tokens=100,
|
| 127 |
+
# temperature=0.00, # 0.0 = logic only, no creativity
|
| 128 |
+
# do_sample=False,
|
| 129 |
+
# repetition_penalty=1.2
|
| 130 |
+
# )
|
| 131 |
+
|
| 132 |
+
# input_length = inputs['input_ids'].shape[1]
|
| 133 |
+
# response = self.tokenizer.decode(outputs[0][input_length:], skip_special_tokens=True)
|
| 134 |
+
# return response
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
|
| 142 |
# ---------------------------------------------------------
|
| 143 |
# 2. OPTIMIZED LLM (Qwen 2.5 - 0.5B) - STRICT GRADING
|
| 144 |
# ---------------------------------------------------------
|
| 145 |
class LLMEvaluator:
|
| 146 |
def __init__(self):
|
| 147 |
+
# Qwen 0.5B is great for speed, but needs VERY specific prompts to be strict.
|
| 148 |
self.repo_id = "onnx-community/Qwen2.5-0.5B-Instruct"
|
| 149 |
self.local_dir = "onnx_qwen_local"
|
| 150 |
|
|
|
|
| 175 |
)
|
| 176 |
|
| 177 |
def evaluate(self, context, question, student_answer, max_marks):
|
| 178 |
+
# --- STRATEGY: FEW-SHOT PROMPTING & CHAIN OF THOUGHT ---
|
| 179 |
+
# Small models (0.5B) need examples to understand "Strictness".
|
| 180 |
+
|
| 181 |
+
system_prompt = """You are a strict automated grader. You grade ONLY based on the provided Context.
|
| 182 |
+
|
| 183 |
+
RULES:
|
| 184 |
+
1. If the Student Answer contains facts NOT found in the Context, Score is 0.
|
| 185 |
+
2. If the Student Answer contradicts the Context, Score is 0.
|
| 186 |
+
3. Do not use outside knowledge. If it's not in the text, it's wrong.
|
| 187 |
+
|
| 188 |
+
--- EXAMPLE 1 (WRONG ANSWER) ---
|
| 189 |
+
Context: The sky is blue because of Rayleigh scattering.
|
| 190 |
+
Question: Why is the sky blue?
|
| 191 |
+
Student Answer: Because the ocean reflects into it.
|
| 192 |
+
Analysis: The context mentions Rayleigh scattering. The student mentioned ocean reflection. These do not match.
|
| 193 |
+
Score: 0/{max_marks}
|
| 194 |
+
|
| 195 |
+
--- EXAMPLE 2 (CORRECT ANSWER) ---
|
| 196 |
+
Context: Mitochondria is the powerhouse of the cell.
|
| 197 |
+
Question: What is the mitochondria?
|
| 198 |
+
Student Answer: It is the powerhouse of the cell.
|
| 199 |
+
Analysis: The student answer matches the context text exactly.
|
| 200 |
+
Score: {max_marks}/{max_marks}
|
| 201 |
+
"""
|
| 202 |
+
|
| 203 |
+
user_prompt = f"""
|
| 204 |
+
--- NOW GRADE THIS ---
|
| 205 |
+
Context:
|
| 206 |
+
{context}
|
| 207 |
+
|
| 208 |
+
Question:
|
| 209 |
+
{question}
|
| 210 |
+
|
| 211 |
+
Student Answer:
|
| 212 |
+
{student_answer}
|
| 213 |
+
|
| 214 |
+
Task:
|
| 215 |
+
1. Analyze if the specific keywords in Student Answer exist in Context.
|
| 216 |
+
2. Assign a Score.
|
| 217 |
+
|
| 218 |
+
Output format:
|
| 219 |
+
Analysis: [Analysis here]
|
| 220 |
+
Score: [X]/{max_marks}
|
| 221 |
+
"""
|
| 222 |
+
|
| 223 |
messages = [
|
| 224 |
+
{"role": "system", "content": system_prompt},
|
| 225 |
+
{"role": "user", "content": user_prompt}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
]
|
| 227 |
|
| 228 |
input_text = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
|
|
|
| 231 |
with torch.no_grad():
|
| 232 |
outputs = self.model.generate(
|
| 233 |
**inputs,
|
| 234 |
+
max_new_tokens=150,
|
| 235 |
+
temperature=0.1, # Low temperature for facts
|
| 236 |
+
top_p=0.1, # Reduce creativity
|
| 237 |
+
do_sample=True,
|
| 238 |
+
repetition_penalty=1.1
|
| 239 |
)
|
| 240 |
|
| 241 |
input_length = inputs['input_ids'].shape[1]
|
| 242 |
response = self.tokenizer.decode(outputs[0][input_length:], skip_special_tokens=True)
|
| 243 |
return response
|
| 244 |
|
| 245 |
+
|
| 246 |
# ---------------------------------------------------------
|
| 247 |
# 3. Main Application Logic
|
| 248 |
# ---------------------------------------------------------
|