Spaces:
Sleeping
Sleeping
| # File: src/resume_rag.py | |
| # Purpose: Resume chunking, embedding (sentence-transformers), ChromaDB indexing, | |
| # and skill extraction via Groq (no OpenAI required) | |
| import json | |
| import re | |
| from typing import List | |
| import chromadb | |
| from chromadb.utils import embedding_functions | |
| from groq import Groq | |
| from config import ( | |
| GROQ_API_KEY, GROQ_LLM_MODEL, | |
| EMBEDDING_MODEL, CHROMA_PERSIST_DIR, | |
| CHUNK_SIZE, CHUNK_OVERLAP, TOP_K_RESUME_CHUNKS, | |
| ) | |
| # Local sentence-transformers embeddings — no API key needed | |
| _st_ef = embedding_functions.SentenceTransformerEmbeddingFunction( | |
| model_name=EMBEDDING_MODEL | |
| ) | |
| # Groq client | |
| _groq = Groq(api_key=GROQ_API_KEY) | |
| def _chunk_text(text: str, size: int = CHUNK_SIZE, overlap: int = CHUNK_OVERLAP) -> List[str]: | |
| words = text.split() | |
| chunks, i = [], 0 | |
| while i < len(words): | |
| chunks.append(" ".join(words[i: i + size])) | |
| i += size - overlap | |
| return chunks | |
| def build_resume_index(session_id: int, resume_text: str) -> chromadb.Collection: | |
| client = chromadb.PersistentClient(path=CHROMA_PERSIST_DIR) | |
| collection_name = f"resume_{session_id}" | |
| try: | |
| client.delete_collection(collection_name) | |
| except Exception: | |
| pass | |
| collection = client.create_collection( | |
| name=collection_name, | |
| embedding_function=_st_ef, | |
| ) | |
| chunks = _chunk_text(resume_text) | |
| collection.add( | |
| documents=chunks, | |
| ids=[f"chunk_{i}" for i in range(len(chunks))], | |
| ) | |
| return collection | |
| def retrieve_resume_context(session_id: int, query: str, top_k: int = TOP_K_RESUME_CHUNKS) -> str: | |
| client = chromadb.PersistentClient(path=CHROMA_PERSIST_DIR) | |
| collection_name = f"resume_{session_id}" | |
| try: | |
| collection = client.get_collection( | |
| name=collection_name, | |
| embedding_function=_st_ef, | |
| ) | |
| except Exception: | |
| return "" | |
| results = collection.query( | |
| query_texts=[query], | |
| n_results=min(top_k, collection.count()), | |
| ) | |
| docs = results.get("documents", [[]])[0] | |
| return "\n".join(docs) | |
| def extract_skills_from_resume(resume_text: str) -> List[str]: | |
| prompt = ( | |
| "Extract a concise list of technical skills, tools, and frameworks mentioned in the " | |
| "following resume. Return ONLY a valid JSON array of strings. " | |
| "No explanation, no markdown, no extra text.\n\n" | |
| f"Resume:\n{resume_text[:3000]}" | |
| ) | |
| response = _groq.chat.completions.create( | |
| model=GROQ_LLM_MODEL, | |
| messages=[{"role": "user", "content": prompt}], | |
| temperature=0, | |
| max_tokens=400, | |
| ) | |
| raw = response.choices[0].message.content.strip() | |
| raw = re.sub(r"```json|```", "", raw).strip() | |
| try: | |
| return json.loads(raw) | |
| except Exception: | |
| return [] |