| """SQL accuracy evaluation - tests NL-to-SQL generation quality.""" |
|
|
| import json |
| import time |
| from pathlib import Path |
| from typing import Any, Dict, List |
|
|
| from logger.logging import get_logger |
| from services.nl_to_sql_service import NLToSQLService |
|
|
| logger = get_logger(__name__) |
|
|
| |
| API_CALL_DELAY = 5 |
|
|
|
|
| class SQLAccuracyEvaluator: |
| """Evaluates SQL generation accuracy against gold-standard test queries.""" |
|
|
| def __init__(self): |
| self.nl_to_sql = NLToSQLService() |
| self.test_queries = self._load_test_queries() |
|
|
| def _load_test_queries(self) -> List[Dict]: |
| """Load test queries from JSON file.""" |
| path = Path(__file__).parent / "test_queries.json" |
| with open(path, "r") as f: |
| return json.load(f) |
|
|
| def evaluate(self, max_queries: int = None) -> Dict[str, Any]: |
| """Run SQL accuracy evaluation. |
| |
| Checks: |
| 1. SQL generation succeeds (no errors) |
| 2. Generated SQL contains expected keywords |
| 3. SQL executes without errors |
| |
| Returns: |
| Dict with accuracy, passed, failed, total, details |
| """ |
| queries = self.test_queries[:max_queries] if max_queries else self.test_queries |
| results = [] |
| passed = 0 |
| failed = 0 |
|
|
| for i, test in enumerate(queries): |
| question = test["question"] |
| expected_contains = test["expected_sql_contains"] |
|
|
| |
| if i > 0: |
| time.sleep(API_CALL_DELAY) |
|
|
| try: |
| logger.info(f"[{i+1}/{len(queries)}] Evaluating: {question[:60]}...") |
| result = self.nl_to_sql.execute(question) |
|
|
| sql = result.get("sql", "") |
| error = result.get("error", "") |
| has_rows = result.get("row_count", 0) > 0 or not error |
|
|
| |
| sql_upper = sql.upper() |
| missing_keywords = [ |
| kw for kw in expected_contains if kw.upper() not in sql_upper |
| ] |
|
|
| success = not error and sql and len(missing_keywords) == 0 |
|
|
| if success: |
| passed += 1 |
| else: |
| failed += 1 |
|
|
| results.append( |
| { |
| "id": test["id"], |
| "question": question, |
| "difficulty": test.get("difficulty", ""), |
| "generated_sql": sql, |
| "error": error, |
| "success": success, |
| "missing_keywords": missing_keywords, |
| "row_count": result.get("row_count", 0), |
| } |
| ) |
|
|
| except Exception as e: |
| failed += 1 |
| results.append( |
| { |
| "id": test["id"], |
| "question": question, |
| "success": False, |
| "error": str(e), |
| } |
| ) |
|
|
| total = len(queries) |
| return { |
| "accuracy": passed / total if total > 0 else 0, |
| "passed": passed, |
| "failed": failed, |
| "total": total, |
| "details": results, |
| } |
|
|