Download src/rag/rag_pipeline.py from Emasters-CodeGen/CodeGen-Emasters-IIITH: direct link, hf CLI and curl.
- Browser
- Download file 6.79 kB
-
https://huggingface.co/spaces/Emasters-CodeGen/CodeGen-Emasters-IIITH/resolve/main/src/rag/rag_pipeline.py
- Command line
-
hf download hf://spaces/Emasters-CodeGen/CodeGen-Emasters-IIITH/src/rag/rag_pipeline.py
-
curl -L -o rag_pipeline.py https://huggingface.co/spaces/Emasters-CodeGen/CodeGen-Emasters-IIITH/resolve/main/src/rag/rag_pipeline.py
6.79 kB
| import logging | |
| import torch | |
| from typing import Any, List, Tuple, Dict, Optional | |
| logger = logging.getLogger(__name__) | |
| METADATA_TEXT_FIELDS = ["generated_mongodb_query", "mongodb_query", "code", "sql", "SQL", "question", "text", "schema"] | |
| class RAGPipeline: | |
| def __init__( | |
| self, | |
| retriever: Any, | |
| generator: Optional[Any] = None, | |
| model: Optional[Any] = None, | |
| tokenizer: Optional[Any] = None, | |
| config: Any = None | |
| ): | |
| """ | |
| Args: | |
| retriever: An instance of IndexManager or HybridRetriever that has a `.search(query, k)` method. | |
| generator: An instance of GenerationPipeline (has `.generate(prompt)`). | |
| If provided, `model` and `tokenizer` are ignored. | |
| model: HuggingFace model (used if generator is None). | |
| tokenizer: HuggingFace tokenizer (used if generator is None). | |
| config: Generation configuration object. | |
| """ | |
| self.retriever = retriever | |
| self.generator = generator | |
| self.model = model | |
| self.tokenizer = tokenizer | |
| self.config = config | |
| if self.generator is None and (self.model is None or self.tokenizer is None): | |
| raise ValueError("Either `generator` or both `model` and `tokenizer` must be provided.") | |
| def retrieve_context(self, query: str, top_k: int = 3) -> List[Dict[str, Any]]: | |
| """ | |
| Retrieves relevant context using the hybrid retriever. | |
| Returns a list of metadata dictionaries. | |
| """ | |
| try: | |
| # The new HybridRetriever/IndexManager returns List[Dict] or List[Tuple[Dict, float]] | |
| results = self.retriever.search(query, k=top_k) | |
| # Handle different return formats gracefully | |
| if results and isinstance(results[0], tuple): | |
| return [metadata for metadata, score in results] | |
| elif results and isinstance(results[0], dict): | |
| return results | |
| else: | |
| return [] | |
| except Exception as e: | |
| logger.error(f"Error during context retrieval: {e}") | |
| return [] | |
| def _metadata_to_text(metadata: Any) -> str: | |
| """Extracts the most relevant text field from a metadata dictionary.""" | |
| if not isinstance(metadata, dict): | |
| return str(metadata) | |
| for field in METADATA_TEXT_FIELDS: | |
| if metadata.get(field): | |
| return str(metadata[field]) | |
| # Fallback: concatenate all string values | |
| return " | ".join([f"{k}: {v}" for k, v in metadata.items() if isinstance(v, str)]) | |
| def build_prompt(self, query: str, context_items: List[Dict[str, Any]]) -> str: | |
| context_str = "" | |
| for i, item in enumerate(context_items): | |
| mongodb_query = ( | |
| item.get('generated_mongodb_query', '') | |
| or item.get('mongodb_query', '') | |
| ) | |
| question = item.get('question', '') | |
| if isinstance(mongodb_query, list) and len(mongodb_query) > 0: | |
| mongodb_query = mongodb_query[0] | |
| if mongodb_query: | |
| context_str += f"--- Example {i + 1} ---\n" | |
| if question: | |
| context_str += f"Question: {question}\n" | |
| context_str += f"MongoDB Query: {mongodb_query}\n\n" | |
| prompt = ( | |
| "You are an expert MongoDB query generator. Given a question, output ONLY the MongoDB query.\n" | |
| "Do not include explanations, apologies, or extra text.\n\n" | |
| f"Context Examples:\n{context_str}\n" | |
| f"### Task: Generate MongoDB Query (MQL)\n" | |
| f"### Question:\n{query}\n\n" | |
| f"### MongoDB Query:\ndb." | |
| ) | |
| return prompt | |
| def _build_generation_kwargs(self) -> dict: | |
| eos_id = self.tokenizer.eos_token_id if self.tokenizer else None | |
| if isinstance(eos_id, torch.Tensor): | |
| eos_id = eos_id.item() | |
| kwargs = { | |
| "max_new_tokens": 128, | |
| "temperature": 0.2, | |
| "top_p": 0.95, | |
| "top_k": 50, | |
| "do_sample": True, | |
| "pad_token_id": eos_id, | |
| "eos_token_id": eos_id, | |
| "repetition_penalty": 1.2, | |
| "no_repeat_ngram_size": 3, | |
| } | |
| if self.config: | |
| kwargs["max_new_tokens"] = min(getattr(self.config, "max_length", 256), 128) | |
| kwargs["temperature"] = getattr(self.config, "temperature", 0.2) | |
| kwargs["top_p"] = getattr(self.config, "top_p", 0.95) | |
| kwargs["top_k"] = getattr(self.config, "top_k", 50) | |
| return kwargs | |
| def _clean_mongo_output(raw_output: str) -> str: | |
| stripped = raw_output.strip() | |
| if stripped.lower().startswith("db."): | |
| restored = stripped | |
| else: | |
| restored = "db." + stripped | |
| cleaned = restored.replace("```javascript", "").replace("```json", "").replace("```", "").strip() | |
| stop_markers = ["--->", "\n\n", "\nUser:", "\nQuestion:", "```", "###", "Human:"] | |
| for marker in stop_markers: | |
| idx = cleaned.find(marker) | |
| if idx != -1: | |
| cleaned = cleaned[:idx] | |
| lines = cleaned.split('\n') | |
| for line in lines: | |
| if line.strip().startswith("db."): | |
| return line.strip().rstrip(';') | |
| if "db." in cleaned: | |
| idx = cleaned.find("db.") | |
| return cleaned[idx:].split('\n')[0].strip().rstrip(';') | |
| return cleaned.strip() | |
| # Update generate_with_rag to clean the output before returning: | |
| def generate_with_rag(self, query: str, top_k: int = 3) -> Tuple[str, List[Dict[str, Any]]]: | |
| context_metadata = self.retrieve_context(query, top_k=top_k) | |
| prompt = self.build_prompt(query, context_metadata) | |
| if self.generator: | |
| generated_text = self.generator.generate(prompt) | |
| else: | |
| inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device) | |
| gen_kwargs = self._build_generation_kwargs() | |
| with torch.no_grad(): | |
| outputs = self.model.generate(**inputs, **gen_kwargs) | |
| generated_text = self.tokenizer.decode( | |
| outputs[0][inputs["input_ids"].shape[1]:], | |
| skip_special_tokens=True | |
| ).strip() | |
| generated_text = self._clean_mongo_output(generated_text) | |
| return generated_text, context_metadata | |
| def generate_program(self, prompt: str, top_k: int = 3) -> str: | |
| """Wrapper for simple program generation using RAG.""" | |
| generated_text, _ = self.generate_with_rag(query=prompt, top_k=top_k) | |
| return generated_text |