QuantumChecker 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuantumChecker
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: A package to evaluate homework submissions in Python, SQL, PowerBI, and SSIS.
5
5
  Author: Qobiljon
6
6
  Author-email: qobiljonkhayrullayev@gmail.com
@@ -37,13 +37,12 @@ Q1: What is a Python list? Explain with an example.
37
37
  Q2: Write an SQL query to select all records from a table named 'students'.
38
38
  """
39
39
  answer_path = "sample_submissions/student1_answer.py"
40
- api_keys = ["your_api_key_1", "your_api_key_2"]
41
40
  question_type = "python"
42
41
 
43
42
  result = await evaluator.evaluate_from_content(
44
43
  question_content=question_content,
45
44
  answer_path=answer_path,
46
- api_keys=api_keys,
45
+ api_key="your_api_key",
47
46
  question_type=question_type
48
47
  )
49
48
  print(result)
@@ -0,0 +1,138 @@
1
+ import logging
2
+ import os
3
+ import zipfile
4
+ from datetime import datetime
5
+ from typing import List, Dict
6
+ from .python_evaluator import PythonEvaluator
7
+ from .sql_evaluator import SQLEvaluator
8
+ from .powerbi_evaluator import PowerBIEvaluator
9
+ from .ssis_evaluator import SSISEvaluator
10
+ import asyncio
11
+
12
+ _logger_cache = {}
13
+
14
+ class HomeworkEvaluator:
15
+ EVALUATOR_REGISTRY = {
16
+ "python": PythonEvaluator,
17
+ "sql": SQLEvaluator,
18
+ "powerbi": PowerBIEvaluator,
19
+ "ssis": SSISEvaluator
20
+ }
21
+
22
+ EXTENSION_TO_TYPE = {
23
+ ".py": "python",
24
+ ".sql": "sql",
25
+ ".pbit": "powerbi",
26
+ ".pdf": "powerbi",
27
+ ".dtsx": "ssis",
28
+ ".DTSX": "ssis",
29
+ ".txt": "text",
30
+ ".md": "text"
31
+ }
32
+
33
+ def __init__(self, log_level: int = logging.INFO):
34
+ self.log_level = log_level
35
+ self._lock = asyncio.Lock()
36
+ self._last_request_time = None
37
+
38
+ def _get_logger(self, log_type: str) -> logging.Logger:
39
+ log_name = f"{log_type}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}"
40
+ if log_name not in _logger_cache:
41
+ logger = logging.getLogger(log_name)
42
+ logger.setLevel(self.log_level)
43
+ if not logger.handlers:
44
+ handler = logging.StreamHandler()
45
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
46
+ logger.addHandler(handler)
47
+ _logger_cache[log_name] = logger
48
+ return _logger_cache[log_name]
49
+
50
+ def parse_questions(self, content: str) -> List[str]:
51
+ logger = self._get_logger("QuantumCheck.main")
52
+ questions = [q.strip() for q in content.split("\n\n") if q.strip()]
53
+ if not questions:
54
+ raise ValueError("No valid questions found in content")
55
+ return questions
56
+
57
+ def _detect_zip_content_type(self, zip_path: str, logger: logging.Logger) -> str:
58
+ with zipfile.ZipFile(zip_path, "r") as zip_ref:
59
+ extensions = {os.path.splitext(name)[1].lower() for name in zip_ref.namelist()}
60
+ file_types = [self.EXTENSION_TO_TYPE.get(ext, "text") for ext in extensions if ext]
61
+ if "python" in file_types:
62
+ return "python"
63
+ elif "sql" in file_types:
64
+ return "sql"
65
+ elif "powerbi" in file_types:
66
+ return "powerbi"
67
+ elif "ssis" in file_types:
68
+ return "ssis"
69
+ else:
70
+ return "text"
71
+
72
+ async def evaluate_from_content(
73
+ self,
74
+ question_content: str,
75
+ answer_path: str,
76
+ api_key: str,
77
+ question_type: str
78
+ ) -> Dict[str, any]:
79
+ async with self._lock:
80
+ now = datetime.now()
81
+ if self._last_request_time:
82
+ elapsed = (now - self._last_request_time).total_seconds()
83
+ if elapsed < 5:
84
+ await asyncio.sleep(5 - elapsed)
85
+ self._last_request_time = datetime.now()
86
+
87
+ logger = self._get_logger("QuantumCheck.main")
88
+
89
+ try:
90
+ questions = self.parse_questions(question_content)
91
+ except ValueError as e:
92
+ return {
93
+ "score": 0,
94
+ "feedback": f"Error parsing question content: {str(e)}",
95
+ "issues": [str(e)],
96
+ "recommendations": []
97
+ }
98
+
99
+ answer_path = answer_path.strip()
100
+ _, ext = os.path.splitext(answer_path)
101
+ ext = ext.lower()
102
+
103
+ if ext == ".zip":
104
+ logger = self._get_logger("zip")
105
+ file_type = self._detect_zip_content_type(answer_path, logger)
106
+ else:
107
+ file_type = self.EXTENSION_TO_TYPE.get(ext, "text")
108
+ logger = self._get_logger(file_type)
109
+
110
+ eval_type = question_type if question_type in self.EVALUATOR_REGISTRY else file_type
111
+
112
+ if not os.path.exists(answer_path):
113
+ return {
114
+ "score": 0,
115
+ "feedback": f"Answer file not found: {answer_path}",
116
+ "issues": [f"Answer file not found: {answer_path}"],
117
+ "recommendations": []
118
+ }
119
+
120
+ evaluator_class = self.EVALUATOR_REGISTRY.get(eval_type, PythonEvaluator)
121
+ evaluator = evaluator_class(api_key)
122
+
123
+ try:
124
+ evaluation = evaluator.evaluate(questions, answer_path, temp_dir=f"temp_extract_{os.getpid()}")
125
+ return {
126
+ "score": evaluation.get("score", 0),
127
+ "feedback": evaluation.get("feedback", "No feedback provided"),
128
+ "issues": evaluation.get("issues", []),
129
+ "recommendations": evaluation.get("recommendations", [])
130
+ }
131
+
132
+ except Exception as e:
133
+ return {
134
+ "score": 0,
135
+ "feedback": f"Evaluation failed: {str(e)}",
136
+ "issues": [str(e)],
137
+ "recommendations": []
138
+ }
@@ -37,7 +37,7 @@ logging.basicConfig(
37
37
 
38
38
 
39
39
  class GeminiFlashModel:
40
- def __init__(self, api_key: str, model_name: str = "gemini-2.0-flash"):
40
+ def __init__(self, api_key: str, model_name: str = "gemini-1.5-flash"):
41
41
  api_key = os.getenv("GEMINI_API_KEY") or api_key
42
42
  if not api_key:
43
43
  raise ValueError("API key not found in .env file or environment variables.")
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
19
19
 
20
20
 
21
21
  class GeminiFlashModel:
22
- def __init__(self, api_key: str, model_name: str = "gemini-2.0-flash"):
22
+ def __init__(self, api_key: str, model_name: str = "gemini-1.5-flash"):
23
23
  if not api_key:
24
24
  raise ValueError("API key is required.")
25
25
  self.api_key = api_key
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
19
19
 
20
20
 
21
21
  class GeminiFlashModel:
22
- def __init__(self, api_key: str, model_name: str = "gemini-2.0-flash"):
22
+ def __init__(self, api_key: str, model_name: str = "gemini-1.5-flash"):
23
23
  if not api_key:
24
24
  raise ValueError("API key is required.")
25
25
  self.api_key = api_key
@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
16
16
 
17
17
 
18
18
  class GeminiFlashModel:
19
- def __init__(self, api_key: str, model_name: str = "gemini-2.0-flash"):
19
+ def __init__(self, api_key: str, model_name: str = "gemini-1.5-flash"):
20
20
  if not api_key:
21
21
  raise ValueError("API key is required.")
22
22
  self.api_key = api_key
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuantumChecker
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: A package to evaluate homework submissions in Python, SQL, PowerBI, and SSIS.
5
5
  Author: Qobiljon
6
6
  Author-email: qobiljonkhayrullayev@gmail.com
@@ -37,13 +37,12 @@ Q1: What is a Python list? Explain with an example.
37
37
  Q2: Write an SQL query to select all records from a table named 'students'.
38
38
  """
39
39
  answer_path = "sample_submissions/student1_answer.py"
40
- api_keys = ["your_api_key_1", "your_api_key_2"]
41
40
  question_type = "python"
42
41
 
43
42
  result = await evaluator.evaluate_from_content(
44
43
  question_content=question_content,
45
44
  answer_path=answer_path,
46
- api_keys=api_keys,
45
+ api_key="your_api_key",
47
46
  question_type=question_type
48
47
  )
49
48
  print(result)
@@ -12,5 +12,4 @@ QuantumChecker.egg-info/SOURCES.txt
12
12
  QuantumChecker.egg-info/dependency_links.txt
13
13
  QuantumChecker.egg-info/requires.txt
14
14
  QuantumChecker.egg-info/top_level.txt
15
- tests/test.py
16
- tests/test2.py
15
+ tests/test.py
@@ -11,13 +11,12 @@ Q1: What is a Python list? Explain with an example.
11
11
  Q2: Write an SQL query to select all records from a table named 'students'.
12
12
  """
13
13
  answer_path = "sample_submissions/student1_answer.py"
14
- api_keys = ["your_api_key_1", "your_api_key_2"]
15
14
  question_type = "python"
16
15
 
17
16
  result = await evaluator.evaluate_from_content(
18
17
  question_content=question_content,
19
18
  answer_path=answer_path,
20
- api_keys=api_keys,
19
+ api_key="your_api_key",
21
20
  question_type=question_type
22
21
  )
23
22
  print(result)
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="QuantumChecker",
5
- version="0.3.0",
5
+ version="0.3.2",
6
6
  author="Qobiljon",
7
7
  author_email="qobiljonkhayrullayev@gmail.com",
8
8
  description="A package to evaluate homework submissions in Python, SQL, PowerBI, and SSIS.",
@@ -0,0 +1,82 @@
1
+ import asyncio
2
+ from QuantumCheck import HomeworkEvaluator
3
+
4
+ question_sets = {
5
+ "python_beginner": "Write a Python function to calculate factorial.\nWrite a Python script to reverse a string.",
6
+ "power_bi": "Create a Power BI report with a bar chart.\nExplain DAX measures for sales analysis.",
7
+ "sql": "Write a SQL query to join two tables.\nWrite a SQL query for aggregate functions.",
8
+ "ssis": "Design an SSIS package for data import.\nExplain SSIS control flow tasks."
9
+ }
10
+
11
+ answer_paths = {
12
+ "python": ["../tests/answer/python1.zip"],
13
+ "powerbi": ["../tests/answer/real.zip"],
14
+ "sql": ["../tests/answer/sql3.zip"],
15
+ "ssis": ["../tests/answer/answer.dtsx"]
16
+ }
17
+
18
+ question_type_mapping = {
19
+ "python_beginner": "python",
20
+ "power_bi": "powerbi",
21
+ "sql": "sql",
22
+ "ssis": "ssis"
23
+ }
24
+
25
+
26
+ def format_score(score):
27
+ if score >= 90:
28
+ return f"🟢 Excellent ({score}⭐)"
29
+ elif score >= 75:
30
+ return f"🟡 Good ({score})"
31
+ elif score >= 50:
32
+ return f"🟠 Pass ({score})"
33
+ else:
34
+ return f"🔴 Fail ({score})"
35
+ API_KEY = "<KEY>"
36
+ async def run_evaluation(evaluator, q_key, q_content, question_type, answer_path, index):
37
+ try:
38
+ evaluation = await evaluator.evaluate_from_content(
39
+ question_content=q_content,
40
+ answer_path=answer_path,
41
+ api_key=API_KEY,
42
+ question_type=question_type
43
+ )
44
+ score = evaluation.get("score", 0)
45
+ return (q_key, index, "success", score)
46
+ except Exception as e:
47
+ return (q_key, index, "error", str(e))
48
+
49
+ async def main():
50
+ evaluator = HomeworkEvaluator()
51
+ tasks = []
52
+
53
+ for q_key, q_content in question_sets.items():
54
+ question_type = question_type_mapping[q_key]
55
+ paths = answer_paths.get(question_type, [])
56
+ if not paths:
57
+ print(f"⚠️ No answer paths found for question type '{question_type}'")
58
+ continue
59
+ for i in range(10): # run each set 10 times
60
+ for path in paths:
61
+ task = run_evaluation(evaluator, q_key, q_content, question_type, path, i + 1)
62
+ tasks.append(task)
63
+
64
+ results = await asyncio.gather(*tasks)
65
+
66
+ # Group results by question key
67
+ grouped = {}
68
+ for q_key, index, status, output in results:
69
+ if q_key not in grouped:
70
+ grouped[q_key] = []
71
+ grouped[q_key].append((index, status, output))
72
+
73
+ # Sort and print all at once, grouped by question
74
+ for q_key in grouped:
75
+ print(f"\n📘 {q_key.upper()} Results")
76
+ for index, status, output in sorted(grouped[q_key], key=lambda x: x[0]):
77
+ if status == "success":
78
+ print(f" ⏱️ Run {index:02}: {format_score(output)}")
79
+ else:
80
+ print(f" ⏱️ Run {index:02}: ❌ Error - {output}")
81
+
82
+ asyncio.run(main())
@@ -1,230 +0,0 @@
1
- import logging
2
- import os
3
- import zipfile
4
- from datetime import datetime
5
- from typing import List, Dict
6
- from .python_evaluator import PythonEvaluator
7
- from .sql_evaluator import SQLEvaluator
8
- from .powerbi_evaluator import PowerBIEvaluator
9
- from .ssis_evaluator import SSISEvaluator
10
- import asyncio
11
-
12
- _logger_cache = {}
13
-
14
- class HomeworkEvaluator:
15
- EVALUATOR_REGISTRY = {
16
- "python": PythonEvaluator,
17
- "sql": SQLEvaluator,
18
- "powerbi": PowerBIEvaluator,
19
- "ssis": SSISEvaluator
20
- }
21
-
22
- EXTENSION_TO_TYPE = {
23
- ".py": "python",
24
- ".sql": "sql",
25
- ".pbit": "powerbi",
26
- ".pdf": "powerbi",
27
- ".dtsx": "ssis",
28
- ".DTSX": "ssis",
29
- ".txt": "text",
30
- ".md": "text"
31
- }
32
-
33
- API_NAME_MAPPING = {
34
- "python": "Google Gemini API",
35
- "sql": "Google Gemini API",
36
- "powerbi": "Google Gemini API",
37
- "ssis": "Google Gemini API",
38
- "text": "Google Gemini API"
39
- }
40
-
41
- def __init__(self, log_level: int = logging.INFO):
42
- self.log_level = log_level
43
- self._successful_key_cache = {}
44
- self._rate_limit_delay = {}
45
- self._invalid_key_cache = set()
46
- self._lock = asyncio.Lock()
47
- self._last_request_time = None
48
-
49
- def _get_logger(self, log_type: str) -> logging.Logger:
50
- log_name = f"{log_type}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}"
51
- if log_name not in _logger_cache:
52
- logger = logging.getLogger(log_name)
53
- logger.setLevel(self.log_level)
54
- if not logger.handlers:
55
- handler = logging.StreamHandler()
56
- handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
57
- logger.addHandler(handler)
58
- _logger_cache[log_name] = logger
59
- return _logger_cache[log_name]
60
-
61
- def parse_questions(self, content: str) -> List[str]:
62
- logger = self._get_logger("QuantumCheck.main")
63
- questions = [q.strip() for q in content.split("\n\n") if q.strip()]
64
- if not questions:
65
- raise ValueError("No valid questions found in content")
66
- return questions
67
-
68
- def _detect_zip_content_type(self, zip_path: str, logger: logging.Logger) -> str:
69
- with zipfile.ZipFile(zip_path, "r") as zip_ref:
70
- extensions = {os.path.splitext(name)[1].lower() for name in zip_ref.namelist()}
71
- file_types = [self.EXTENSION_TO_TYPE.get(ext, "text") for ext in extensions if ext]
72
- if "python" in file_types:
73
- return "python"
74
- elif "sql" in file_types:
75
- return "sql"
76
- elif "powerbi" in file_types:
77
- return "powerbi"
78
- elif "ssis" in file_types:
79
- return "ssis"
80
- else:
81
- return "text"
82
-
83
- async def evaluate_from_content(
84
- self,
85
- question_content: str,
86
- answer_path: str,
87
- api_keys: List[str],
88
- question_type: str,
89
- retry_count: int = 0
90
- ) -> Dict[str, any]:
91
- async with self._lock:
92
- now = datetime.now()
93
- if self._last_request_time:
94
- elapsed = (now - self._last_request_time).total_seconds()
95
- if elapsed < 30:
96
- await asyncio.sleep(30 - elapsed)
97
- self._last_request_time = datetime.now()
98
-
99
- try:
100
- questions = self.parse_questions(question_content)
101
- except ValueError as e:
102
- logger = self._get_logger("QuantumCheck.main")
103
- return {
104
- "score": 0,
105
- "feedback": f"Error parsing question content: {str(e)}",
106
- "issues": [str(e)],
107
- "recommendations": [],
108
- "used_api_key_index": None,
109
- "used_api_name": None
110
- }
111
-
112
- answer_path = answer_path.strip()
113
- _, ext = os.path.splitext(answer_path)
114
- ext = ext.lower()
115
-
116
- if ext == ".zip":
117
- logger = self._get_logger("zip")
118
- file_type = self._detect_zip_content_type(answer_path, logger)
119
- else:
120
- file_type = self.EXTENSION_TO_TYPE.get(ext, "text")
121
- logger = self._get_logger(file_type)
122
-
123
- eval_type = question_type if question_type in self.EVALUATOR_REGISTRY else file_type
124
-
125
- if not os.path.exists(answer_path):
126
- return {
127
- "score": 0,
128
- "feedback": f"Answer file not found: {answer_path}",
129
- "issues": [f"Answer file not found: {answer_path}"],
130
- "recommendations": [],
131
- "used_api_key_index": None,
132
- "used_api_name": None
133
- }
134
-
135
- evaluator_class = self.EVALUATOR_REGISTRY.get(eval_type, PythonEvaluator)
136
- last_error_messages = []
137
-
138
- available_keys = [(i + 1, key) for i, key in enumerate(api_keys) if key not in self._invalid_key_cache]
139
-
140
- cached_key_idx = self._successful_key_cache.get(eval_type)
141
- if cached_key_idx is not None and cached_key_idx < len(api_keys):
142
- cached_key = api_keys[cached_key_idx]
143
- if cached_key not in self._invalid_key_cache:
144
- available_keys.insert(0, (cached_key_idx + 1, cached_key))
145
-
146
- if not available_keys:
147
- return {
148
- "score": 0,
149
- "feedback": "No valid API keys available.",
150
- "issues": ["All API keys are invalid or rate-limited."],
151
- "recommendations": [],
152
- "used_api_key_index": None,
153
- "used_api_name": None
154
- }
155
-
156
- for idx, key in available_keys:
157
- if key in self._rate_limit_delay:
158
- delay_until = self._rate_limit_delay[key]
159
- current_time = datetime.now()
160
- delay_until_time = datetime.fromtimestamp(delay_until)
161
- if current_time < delay_until_time:
162
- continue
163
- else:
164
- del self._rate_limit_delay[key]
165
-
166
- evaluator = evaluator_class(key)
167
- api_name = getattr(evaluator, 'get_api_name', lambda: self.API_NAME_MAPPING.get(eval_type, "Unknown API"))()
168
-
169
- try:
170
- evaluation = evaluator.evaluate(questions, answer_path, temp_dir=f"temp_extract_{os.getpid()}_{idx}")
171
- feedback = evaluation.get("feedback", "").lower()
172
- issues = " ".join(evaluation.get("issues", [])).lower()
173
-
174
- if any(phrase in feedback or phrase in issues for phrase in ["api key not valid", "api_key_invalid"]):
175
- last_error_messages.append(f"API key #{idx} invalid.")
176
- self._invalid_key_cache.add(key)
177
- continue
178
-
179
- if any(phrase in feedback or phrase in issues for phrase in ["429", "too many requests", "rate limit"]):
180
- last_error_messages.append(f"API key #{idx} rate limited.")
181
- self._rate_limit_delay[key] = datetime.now().timestamp() + 300
182
- continue
183
-
184
- if any(phrase in feedback or phrase in issues for phrase in ["503", "service unavailable"]):
185
- last_error_messages.append(f"API key #{idx} service unavailable.")
186
- self._rate_limit_delay[key] = datetime.now().timestamp() + 7200
187
- continue
188
-
189
- if evaluation.get("score", 0) == 0 and "evaluation not returned" in feedback:
190
- last_error_messages.append(f"API key #{idx} returned invalid evaluation.")
191
- continue
192
-
193
- self._successful_key_cache[eval_type] = idx - 1
194
- return {
195
- "score": evaluation.get("score", 0),
196
- "feedback": evaluation.get("feedback", "No feedback provided"),
197
- "issues": evaluation.get("issues", []),
198
- "recommendations": evaluation.get("recommendations", []),
199
- "used_api_key_index": idx,
200
- "used_api_name": api_name
201
- }
202
-
203
- except Exception as e:
204
- last_error_messages.append(f"Exception with key #{idx}: {str(e)}")
205
- if "429" in str(e) or "rate limit" in str(e).lower():
206
- self._rate_limit_delay[key] = datetime.now().timestamp() + 300
207
- elif "503" in str(e) or "service unavailable" in str(e).lower():
208
- self._rate_limit_delay[key] = datetime.now().timestamp() + 7200
209
- continue
210
-
211
- if retry_count < 3 and self._rate_limit_delay:
212
- next_available_ts = min(self._rate_limit_delay.values())
213
- wait_time = max(0, next_available_ts - datetime.now().timestamp())
214
- await asyncio.sleep(wait_time + 1)
215
- return await self.evaluate_from_content(
216
- question_content=question_content,
217
- answer_path=answer_path,
218
- api_keys=api_keys,
219
- question_type=question_type,
220
- retry_count=retry_count + 1
221
- )
222
-
223
- return {
224
- "score": 0,
225
- "feedback": "Evaluation failed with all API keys." if retry_count >= 3 else "All API keys are temporarily unavailable.",
226
- "issues": last_error_messages if last_error_messages else ["All API keys failed to evaluate the submission."],
227
- "recommendations": [],
228
- "used_api_key_index": None,
229
- "used_api_name": None
230
- }
@@ -1,135 +0,0 @@
1
- import asyncio
2
- import logging
3
- import random
4
- from concurrent.futures import ThreadPoolExecutor
5
- from typing import List, Dict
6
- from QuantumCheck import HomeworkEvaluator
7
- from backoff import on_exception, expo
8
- from google.api_core.exceptions import TooManyRequests
9
-
10
-
11
- logging.basicConfig(
12
- level=logging.INFO,
13
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
14
- handlers=[
15
- logging.FileHandler("evaluation.log", encoding="utf-8"),
16
- logging.StreamHandler()
17
- ]
18
- )
19
- logger = logging.getLogger(__name__)
20
-
21
-
22
- question_sets = {
23
- "python_beginner": "Your Python beginner questions content here...",
24
- "power_bi": "Your Power BI questions content here...",
25
- "sql": "Your SQL questions content here...",
26
- "ssis": "Your SSIS questions content here..."
27
- }
28
-
29
- answer_paths = {
30
- "python": ["../tests/answer/python1.zip"],
31
- "power_bi": ["../tests/answer/real.zip"],
32
- "sql": ["../tests/answer/sql3.zip"],
33
- "ssis":["../tests/answer/answer.dtsx"]
34
- }
35
-
36
- question_type_mapping = {
37
- "python_beginner": "python",
38
- "power_bi": "power_bi",
39
- "sql": "sql",
40
- "ssis": "ssis"
41
- }
42
-
43
- api_keys = []
44
-
45
-
46
- evaluator = HomeworkEvaluator(log_level=logging.INFO)
47
-
48
- @on_exception(expo, TooManyRequests, max_tries=5, max_time=300)
49
- async def evaluate_async(question_text: str, answer_path: str, api_keys: List[str], question_type: str) -> Dict:
50
- """Wrap the async evaluate function with exponential backoff for rate limit errors."""
51
- try:
52
- return await evaluator.evaluate_from_content(
53
- question_content=question_text,
54
- answer_path=answer_path,
55
- api_keys=api_keys,
56
- question_type=question_type
57
- )
58
- except Exception as e:
59
- logger.error(f"Evaluation failed: {str(e)}")
60
- return {
61
- "score": 0,
62
- "feedback": f"Evaluation failed: {str(e)}",
63
- "issues": [str(e)],
64
- "recommendations": [],
65
- "used_api_key_index": None,
66
- "used_api_name": None
67
- }
68
-
69
- async def main(num_requests: int = 10, max_workers: int = 6):
70
- resource_exhausted_count = 0
71
- logger.info(f"Starting evaluation with {num_requests} requests and {max_workers} workers")
72
-
73
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
74
- loop = asyncio.get_running_loop()
75
- tasks = []
76
-
77
- for i in range(num_requests):
78
- question_key = random.choice(list(question_sets.keys()))
79
- question_text = question_sets[question_key]
80
- question_type = question_type_mapping.get(question_key, "python")
81
- answer_path = random.choice(answer_paths.get(question_type, answer_paths["python"]))
82
-
83
- logger.info(f"Scheduling Request #{i + 1} for '{question_key}' with '{answer_path}'")
84
- task = loop.create_task(evaluate_async(question_text, answer_path, api_keys, question_type))
85
- tasks.append((i + 1, task))
86
-
87
-
88
- results = []
89
- for i, task in tasks:
90
- try:
91
- result = await task
92
- if "resource exhausted" in str(result.get("feedback", "")).lower():
93
- resource_exhausted_count += 1
94
- results.append((i, result))
95
- except Exception as e:
96
- logger.error(f"Request #{i} failed with error: {str(e)}")
97
- if "resource exhausted" in str(e).lower():
98
- resource_exhausted_count += 1
99
- results.append((i, {
100
- "score": 0,
101
- "feedback": f"Request failed: {str(e)}",
102
- "issues": [str(e)],
103
- "recommendations": [],
104
- "used_api_key_index": None,
105
- "used_api_name": None
106
- }))
107
-
108
- logger.info("All evaluations completed")
109
-
110
- for i, result in results:
111
- score = result.get("score", 0)
112
- feedback = result.get("feedback", "")
113
- used_key_index = result.get("used_api_key_index", "N/A")
114
- used_api_name = result.get("used_api_name", "N/A")
115
-
116
- feedback_words = feedback.split()
117
- partial_feedback = " ".join(feedback_words[:40]) + ("..." if len(feedback_words) > 10 else "")
118
-
119
- if score == 0 and "failed" in feedback.lower():
120
- logger.error(f"Request #{i} failed with error: {feedback}")
121
- print(f"X Request #{i} failed with error: {feedback}")
122
- else:
123
- logger.info(f"Request #{i} succeeded: Score = {score}, API Index = {used_key_index}, API Name = {used_api_name}")
124
- print(f"O Request #{i} succeeded: Score = {score}, API Index = {used_key_index}, API Name = {used_api_name}")
125
- print(f" Feedback preview: {partial_feedback}\n")
126
-
127
- logger.info(f"Total 'resource exhausted' errors encountered: {resource_exhausted_count}")
128
- print(f"Total 'resource exhausted' errors encountered: {resource_exhausted_count}")
129
-
130
- if __name__ == "__main__":
131
- try:
132
- asyncio.run(main(num_requests=100, max_workers=20))
133
- except Exception as e:
134
- logger.error(f"Fatal error in main: {e}")
135
- print(f"Fatal error in main: {e}")
@@ -1,31 +0,0 @@
1
- import asyncio
2
- from QuantumCheck import HomeworkEvaluator
3
-
4
- async def main():
5
- evaluator = HomeworkEvaluator()
6
-
7
- question_content = """
8
- Q1: Write a Python function that calculates the factorial of a number.
9
-
10
- Q2: What is the difference between a list and a tuple in Python?
11
- """
12
-
13
- answer_path = "answer/python1.zip"
14
- api_keys = []
15
-
16
- question_type = "python"
17
-
18
- result = await evaluator.evaluate_from_content(
19
- question_content=question_content,
20
- answer_path=answer_path,
21
- api_keys=api_keys,
22
- question_type=question_type
23
- )
24
-
25
- print("Evaluation Result:")
26
- print(result["score"])
27
- print(result["feedback"])
28
-
29
-
30
- if __name__ == "__main__":
31
- asyncio.run(main())
File without changes