CodeGen-Emasters-IIITH / src /generators /text_to_mongo_generator.py
geekydraco's picture
Upload 54 files
3581a58 verified
Raw History Blame Contribute Delete
4.51 kB
import json
import time
import logging
from pathlib import Path
from typing import List, Dict, Any
from tqdm import tqdm
logger = logging.getLogger(__name__)
class TextToMongoGenerator:
def __init__(self, pipeline, config: Any = None):
self.pipeline = pipeline
self.config = config
def _build_prompt(self, question: str, schema: str = "") -> str:
prompt = "### Task: Generate MongoDB Query (MQL)\n"
if schema:
prompt += f"### Collection Schema / Structure:\n{schema}\n\n"
prompt += f"### Question:\n{question}\n\n### MongoDB Query:\n db."
return prompt
def generate_single(self, question: str, schema: str = "") -> Dict[str, Any]:
"""Generates a single MongoDB query and measures latency."""
prompt = self._build_prompt(question, schema)
start_time = time.perf_counter()
raw_output = self.pipeline.generate(prompt)
end_time = time.perf_counter()
latency_ms = (end_time - start_time) * 1000
# Clean up hallucinations and repetition loops
generated_query = self._clean_mongo_output(raw_output)
return {
"generated_query": generated_query,
"latency_ms": round(latency_ms, 2)
}
@staticmethod
def clean_mongo_output(raw_output: str) -> str:
stripped = raw_output.strip()
if stripped.lower().startswith("db."):
# Model re-emitted the prefix itself; don't duplicate it.
restored = stripped
else:
restored = "db." + stripped
cleaned = (
restored.replace("```javascript", "")
.replace("```json", "")
.replace("```js", "")
.replace("```", "")
.strip()
)
# Stop at common hallucination markers
stop_markers = ["--->", "\n\n", "\nUser:", "\nQuestion:", "```", "###", "Human:"]
for marker in stop_markers:
idx = cleaned.find(marker)
if idx != -1:
cleaned = cleaned[:idx]
# Extract the first valid MongoDB query line
lines = cleaned.split('\n')
for line in lines:
line = line.strip()
if line.startswith("db."):
return line.rstrip(';').strip()
# Fallback: find "db." anywhere in the text
if "db." in cleaned:
idx = cleaned.find("db.")
end_idx = len(cleaned)
for marker in stop_markers + ["\n"]:
pos = cleaned.find(marker, idx)
if pos != -1:
end_idx = min(end_idx, pos)
return cleaned[idx:end_idx].strip().rstrip(';')
return cleaned.strip()
_clean_mongo_output = clean_mongo_output
def generate_batch(self, dataset: List[Dict[str, Any]], output_path: str = None) -> List[Dict[str, Any]]:
results = []
logger.info(f"🚀 Starting batch MongoDB query generation for {len(dataset)} examples...")
for item in tqdm(dataset, desc="Generating MongoDB Queries", unit="query"):
query_id = item.get("query_id", f"sample_{len(results)}")
database_id = item.get("database_id", "unknown")
question = item.get("question", "")
schema = item.get("schema", "")
gold_query = item.get("generated_mongodb_query", item.get("gold_query", ""))
if isinstance(gold_query, list) and len(gold_query) > 0:
gold_query = gold_query[0]
if not question:
continue
gen_result = self.generate_single(question, schema)
result_entry = {
"query_id": query_id,
"database_id": database_id,
"question": question,
"gold_query": gold_query,
"generated_query": gen_result["generated_query"],
"latency_ms": gen_result["latency_ms"],
}
results.append(result_entry)
if output_path:
self.save_results(results, output_path)
return results
def save_results(self, results: List[Dict[str, Any]], output_path: str):
path = Path(output_path)
path.parent.mkdir(parents=True, exist_ok=True)
output_data = {"results": results}
with open(path, "w", encoding="utf-8") as f:
json.dump(output_data, f, indent=4, ensure_ascii=False)
logger.info(f"✅ Successfully saved {len(results)} generated queries to {path}")