Spaces:
Sleeping
Sleeping
Upload 9194 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- DockerFile +20 -0
- api.py +53 -0
- cache.py +45 -0
- counselor.py +300 -0
- db.py +51 -0
- docs/ai ml specialist/dataset9000_5400.txt +1 -0
- docs/ai ml specialist/dataset9000_5401.txt +1 -0
- docs/ai ml specialist/dataset9000_5402.txt +1 -0
- docs/ai ml specialist/dataset9000_5403.txt +1 -0
- docs/ai ml specialist/dataset9000_5404.txt +1 -0
- docs/ai ml specialist/dataset9000_5405.txt +1 -0
- docs/ai ml specialist/dataset9000_5406.txt +1 -0
- docs/ai ml specialist/dataset9000_5407.txt +1 -0
- docs/ai ml specialist/dataset9000_5408.txt +1 -0
- docs/ai ml specialist/dataset9000_5409.txt +1 -0
- docs/ai ml specialist/dataset9000_5410.txt +1 -0
- docs/ai ml specialist/dataset9000_5411.txt +1 -0
- docs/ai ml specialist/dataset9000_5412.txt +1 -0
- docs/ai ml specialist/dataset9000_5413.txt +1 -0
- docs/ai ml specialist/dataset9000_5414.txt +1 -0
- docs/ai ml specialist/dataset9000_5415.txt +1 -0
- docs/ai ml specialist/dataset9000_5416.txt +1 -0
- docs/ai ml specialist/dataset9000_5417.txt +1 -0
- docs/ai ml specialist/dataset9000_5418.txt +1 -0
- docs/ai ml specialist/dataset9000_5419.txt +1 -0
- docs/ai ml specialist/dataset9000_5420.txt +1 -0
- docs/ai ml specialist/dataset9000_5421.txt +1 -0
- docs/ai ml specialist/dataset9000_5422.txt +1 -0
- docs/ai ml specialist/dataset9000_5423.txt +1 -0
- docs/ai ml specialist/dataset9000_5424.txt +1 -0
- docs/ai ml specialist/dataset9000_5425.txt +1 -0
- docs/ai ml specialist/dataset9000_5426.txt +1 -0
- docs/ai ml specialist/dataset9000_5427.txt +1 -0
- docs/ai ml specialist/dataset9000_5428.txt +1 -0
- docs/ai ml specialist/dataset9000_5429.txt +1 -0
- docs/ai ml specialist/dataset9000_5430.txt +1 -0
- docs/ai ml specialist/dataset9000_5431.txt +1 -0
- docs/ai ml specialist/dataset9000_5432.txt +1 -0
- docs/ai ml specialist/dataset9000_5433.txt +1 -0
- docs/ai ml specialist/dataset9000_5434.txt +1 -0
- docs/ai ml specialist/dataset9000_5435.txt +1 -0
- docs/ai ml specialist/dataset9000_5436.txt +1 -0
- docs/ai ml specialist/dataset9000_5437.txt +1 -0
- docs/ai ml specialist/dataset9000_5438.txt +1 -0
- docs/ai ml specialist/dataset9000_5439.txt +1 -0
- docs/ai ml specialist/dataset9000_5440.txt +1 -0
- docs/ai ml specialist/dataset9000_5441.txt +1 -0
- docs/ai ml specialist/dataset9000_5442.txt +1 -0
- docs/ai ml specialist/dataset9000_5443.txt +1 -0
- docs/ai ml specialist/dataset9000_5444.txt +1 -0
DockerFile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use an official Python runtime as a parent image
|
| 2 |
+
FROM python:3.9-slim
|
| 3 |
+
|
| 4 |
+
# Set the working directory in the container
|
| 5 |
+
WORKDIR /code
|
| 6 |
+
|
| 7 |
+
# Copy the requirements file into the container
|
| 8 |
+
COPY ./requirements.txt /code/requirements.txt
|
| 9 |
+
|
| 10 |
+
# Install all the Python libraries your bot needs
|
| 11 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
| 12 |
+
|
| 13 |
+
# Copy all your project files into the container
|
| 14 |
+
COPY . /code/
|
| 15 |
+
|
| 16 |
+
# Make port 8000 available to the world outside this container
|
| 17 |
+
EXPOSE 8000
|
| 18 |
+
|
| 19 |
+
# The command to start your bot's API server
|
| 20 |
+
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]
|
api.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# api.py (Corrected with CORS)
|
| 2 |
+
|
| 3 |
+
from fastapi import FastAPI
|
| 4 |
+
from fastapi.responses import StreamingResponse
|
| 5 |
+
from pydantic import BaseModel
|
| 6 |
+
from counselor import UltraAdvancedHybridCounselor
|
| 7 |
+
import logging
|
| 8 |
+
from fastapi.middleware.cors import CORSMiddleware # Import the CORS middleware
|
| 9 |
+
|
| 10 |
+
logging.basicConfig(
|
| 11 |
+
level=logging.INFO,
|
| 12 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s',
|
| 13 |
+
handlers=[logging.FileHandler('logs/api.log', encoding='utf-8'), logging.StreamHandler()]
|
| 14 |
+
)
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
app = FastAPI()
|
| 18 |
+
|
| 19 |
+
# --- START OF CORRECTION ---
|
| 20 |
+
# This block allows your React frontend (e.g., running on http://localhost:3000)
|
| 21 |
+
# to make requests to your Python backend without being blocked by browser security.
|
| 22 |
+
origins = [
|
| 23 |
+
"http://localhost:3000",
|
| 24 |
+
"http://localhost:3001",
|
| 25 |
+
]
|
| 26 |
+
|
| 27 |
+
app.add_middleware(
|
| 28 |
+
CORSMiddleware,
|
| 29 |
+
allow_origins=origins,
|
| 30 |
+
allow_credentials=True,
|
| 31 |
+
allow_methods=["*"], # Allows all methods (GET, POST, etc.)
|
| 32 |
+
allow_headers=["*"], # Allows all headers
|
| 33 |
+
)
|
| 34 |
+
# --- END OF CORRECTION ---
|
| 35 |
+
|
| 36 |
+
counselor = UltraAdvancedHybridCounselor()
|
| 37 |
+
|
| 38 |
+
class CounselRequest(BaseModel):
|
| 39 |
+
query: str
|
| 40 |
+
session_id: str
|
| 41 |
+
|
| 42 |
+
@app.post("/counsel")
|
| 43 |
+
async def counsel(request: CounselRequest):
|
| 44 |
+
async def stream_gen():
|
| 45 |
+
try:
|
| 46 |
+
# Correctly iterate over the async generator
|
| 47 |
+
async for chunk in counselor.get_comprehensive_answer(request.query, request.session_id):
|
| 48 |
+
yield chunk.encode('utf-8')
|
| 49 |
+
except Exception as e:
|
| 50 |
+
logger.error(f"Streaming error: {e}")
|
| 51 |
+
yield "An error occurred during streaming.".encode('utf-8')
|
| 52 |
+
|
| 53 |
+
return StreamingResponse(stream_gen(), media_type="text/plain")
|
cache.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import redis
|
| 2 |
+
from dotenv import load_dotenv
|
| 3 |
+
import os
|
| 4 |
+
import pickle
|
| 5 |
+
import logging
|
| 6 |
+
|
| 7 |
+
logging.basicConfig(
|
| 8 |
+
level=logging.INFO,
|
| 9 |
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
| 10 |
+
handlers=[logging.FileHandler('logs/cache.log', encoding='utf-8'), logging.StreamHandler()]
|
| 11 |
+
)
|
| 12 |
+
logger = logging.getLogger(__name__)
|
| 13 |
+
|
| 14 |
+
load_dotenv()
|
| 15 |
+
|
| 16 |
+
class RedisCache:
|
| 17 |
+
def __init__(self):
|
| 18 |
+
try:
|
| 19 |
+
self.client = redis.Redis(
|
| 20 |
+
host=os.getenv('REDIS_HOST', 'localhost'),
|
| 21 |
+
port=int(os.getenv('REDIS_PORT', 6379)),
|
| 22 |
+
db=0,
|
| 23 |
+
decode_responses=False
|
| 24 |
+
)
|
| 25 |
+
self.client.ping()
|
| 26 |
+
logger.info("Connected to Redis")
|
| 27 |
+
except Exception as e:
|
| 28 |
+
logger.error(f"Failed to connect to Redis: {e}")
|
| 29 |
+
raise
|
| 30 |
+
|
| 31 |
+
def get(self, key):
|
| 32 |
+
try:
|
| 33 |
+
value = self.client.get(key)
|
| 34 |
+
if value:
|
| 35 |
+
return pickle.loads(value)
|
| 36 |
+
return None
|
| 37 |
+
except Exception as e:
|
| 38 |
+
logger.error(f"Error retrieving from cache: {e}")
|
| 39 |
+
return None
|
| 40 |
+
|
| 41 |
+
def set(self, key, value, ttl=7200):
|
| 42 |
+
try:
|
| 43 |
+
self.client.setex(key, ttl, pickle.dumps(value))
|
| 44 |
+
except Exception as e:
|
| 45 |
+
logger.error(f"Error setting cache: {e}")
|
counselor.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import hashlib
|
| 3 |
+
import logging
|
| 4 |
+
from typing import AsyncGenerator, Dict, Any
|
| 5 |
+
from transformers import DistilBertTokenizer, DistilBertForSequenceClassification
|
| 6 |
+
import torch
|
| 7 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 8 |
+
from langchain.prompts import PromptTemplate
|
| 9 |
+
from langchain.chains import LLMChain
|
| 10 |
+
from rag import RAGComponent
|
| 11 |
+
from db import SessionDB
|
| 12 |
+
from cache import RedisCache
|
| 13 |
+
import joblib
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from dotenv import load_dotenv
|
| 16 |
+
import os
|
| 17 |
+
|
| 18 |
+
load_dotenv()
|
| 19 |
+
|
| 20 |
+
logging.basicConfig(
|
| 21 |
+
level=logging.INFO,
|
| 22 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s',
|
| 23 |
+
handlers=[logging.FileHandler('logs/counselor.log', encoding='utf-8'), logging.StreamHandler()]
|
| 24 |
+
)
|
| 25 |
+
logger = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
class UltraAdvancedHybridCounselor:
|
| 28 |
+
def __init__(self):
|
| 29 |
+
self.model_path = "./models"
|
| 30 |
+
self.label_encoder_path = "./models/label_encoder.pkl"
|
| 31 |
+
try:
|
| 32 |
+
self.tokenizer = DistilBertTokenizer.from_pretrained(self.model_path)
|
| 33 |
+
self.model = DistilBertForSequenceClassification.from_pretrained(self.model_path)
|
| 34 |
+
self.label_encoder = joblib.load(self.label_encoder_path) if Path(self.label_encoder_path).exists() else None
|
| 35 |
+
except Exception as e:
|
| 36 |
+
logger.error(f"Error loading model or tokenizer: {e}")
|
| 37 |
+
raise
|
| 38 |
+
self.rag = RAGComponent()
|
| 39 |
+
self.db = SessionDB()
|
| 40 |
+
# Check if Redis should be used
|
| 41 |
+
self.use_redis = os.getenv("USE_REDIS", "false").lower() == "true"
|
| 42 |
+
if self.use_redis:
|
| 43 |
+
try:
|
| 44 |
+
self.cache = RedisCache()
|
| 45 |
+
except Exception as e:
|
| 46 |
+
logger.error(f"Failed to initialize RedisCache: {e}. Falling back to no caching.")
|
| 47 |
+
self.cache = None
|
| 48 |
+
else:
|
| 49 |
+
logger.info("Redis caching disabled (USE_REDIS=false).")
|
| 50 |
+
self.cache = None
|
| 51 |
+
self.llm = ChatGoogleGenerativeAI(
|
| 52 |
+
model="gemini-1.5-pro-latest",
|
| 53 |
+
temperature=0.1,
|
| 54 |
+
max_tokens=4096,
|
| 55 |
+
google_api_key=os.getenv("GOOGLE_API_KEY")
|
| 56 |
+
)
|
| 57 |
+
self._setup_prompts()
|
| 58 |
+
|
| 59 |
+
def _setup_prompts(self):
|
| 60 |
+
self.intent_template = """
|
| 61 |
+
You are an expert career intent classifier. Classify the user's query into one of these categories:
|
| 62 |
+
- career_recommendation: Questions about suggested careers based on skills/interests
|
| 63 |
+
- resume_advice: Advice on resumes, CVs, cover letters
|
| 64 |
+
- interview_prep: Preparation for interviews, questions, behavioral advice
|
| 65 |
+
- salary_info: Salary expectations, negotiation, market rates
|
| 66 |
+
- career_transition: Switching careers, upskilling, education paths
|
| 67 |
+
- general_question: Any other career-related query
|
| 68 |
+
|
| 69 |
+
Query: {query}
|
| 70 |
+
|
| 71 |
+
Respond only with the category name.
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
self.enhancer_template = """
|
| 75 |
+
You are an elite career counselor. An advanced ML model recommends '{recommendation}' with {confidence:.1%} confidence.
|
| 76 |
+
Similar profiles: {rag_context}
|
| 77 |
+
Conversation history: {history}
|
| 78 |
+
|
| 79 |
+
Provide a comprehensive response including:
|
| 80 |
+
1. **Role Overview**: Daily responsibilities and key tasks
|
| 81 |
+
2. **Fit Analysis**: How it aligns with the user's profile
|
| 82 |
+
3. **Skills & Education**: Essential skills, degrees, certifications
|
| 83 |
+
4. **Career Ladder**: Progression from entry to executive levels
|
| 84 |
+
5. **Market Insights**: Job demand, salary ranges (2025), growth projections
|
| 85 |
+
6. **Action Plan**: 7 specific, timed steps to pursue this career
|
| 86 |
+
7. **Alternatives**: 4 related careers with brief pros/cons
|
| 87 |
+
|
| 88 |
+
Tone: Encouraging, professional, empowering. Structure with headers.
|
| 89 |
+
|
| 90 |
+
User Query: {user_query}
|
| 91 |
+
"""
|
| 92 |
+
|
| 93 |
+
self.resume_template = """
|
| 94 |
+
As a top resume expert, provide detailed advice for the user's query: {question}
|
| 95 |
+
Conversation history: {history}
|
| 96 |
+
|
| 97 |
+
Include:
|
| 98 |
+
- Structure recommendations
|
| 99 |
+
- Key sections and content tips
|
| 100 |
+
- Tailoring for ATS
|
| 101 |
+
- Common mistakes to avoid
|
| 102 |
+
- Examples specific to career fields
|
| 103 |
+
|
| 104 |
+
Use bullet points for clarity.
|
| 105 |
+
"""
|
| 106 |
+
|
| 107 |
+
self.interview_template = """
|
| 108 |
+
Expert interview coach response for: {question}
|
| 109 |
+
Conversation history: {history}
|
| 110 |
+
|
| 111 |
+
Cover:
|
| 112 |
+
- Common questions and sample answers
|
| 113 |
+
- Behavioral STAR method examples
|
| 114 |
+
- Body language and virtual tips
|
| 115 |
+
- Follow-up strategies
|
| 116 |
+
- Industry-specific advice
|
| 117 |
+
|
| 118 |
+
Structured and actionable.
|
| 119 |
+
"""
|
| 120 |
+
|
| 121 |
+
self.salary_template = """
|
| 122 |
+
Career salary specialist: Answer {question}
|
| 123 |
+
Conversation history: {history}
|
| 124 |
+
|
| 125 |
+
Provide:
|
| 126 |
+
- Current (2025) ranges by experience/level/location
|
| 127 |
+
- Negotiation tactics
|
| 128 |
+
- Factors influencing pay (skills, certs)
|
| 129 |
+
- Benefits and perks overview
|
| 130 |
+
- Sources for up-to-date data
|
| 131 |
+
|
| 132 |
+
Use tables for ranges.
|
| 133 |
+
"""
|
| 134 |
+
|
| 135 |
+
self.transition_template = """
|
| 136 |
+
Career transition advisor: Guide on {question}
|
| 137 |
+
Conversation history: {history}
|
| 138 |
+
|
| 139 |
+
Include:
|
| 140 |
+
- Assessment of current vs target skills
|
| 141 |
+
- Upskilling resources (courses, books)
|
| 142 |
+
- Networking strategies
|
| 143 |
+
- Timeline estimates
|
| 144 |
+
- Success stories/examples
|
| 145 |
+
|
| 146 |
+
Motivational and step-by-step.
|
| 147 |
+
"""
|
| 148 |
+
|
| 149 |
+
self.general_template = """
|
| 150 |
+
Comprehensive career advisor response for: {question}
|
| 151 |
+
Conversation history: {history}
|
| 152 |
+
|
| 153 |
+
Ensure depth, practicality, and relevance to career development.
|
| 154 |
+
"""
|
| 155 |
+
|
| 156 |
+
self.intent_chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(self.intent_template))
|
| 157 |
+
self.enhancer_chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(self.enhancer_template))
|
| 158 |
+
self.resume_chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(self.resume_template))
|
| 159 |
+
self.interview_chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(self.interview_template))
|
| 160 |
+
self.salary_chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(self.salary_template))
|
| 161 |
+
self.transition_chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(self.transition_template))
|
| 162 |
+
self.general_chain = LLMChain(llm=self.llm, prompt=PromptTemplate.from_template(self.general_template))
|
| 163 |
+
|
| 164 |
+
async def classify_intent(self, query: str) -> str:
|
| 165 |
+
if not self.cache:
|
| 166 |
+
try:
|
| 167 |
+
response = await self.intent_chain.ainvoke({"query": query})
|
| 168 |
+
intent = response['text'].strip().lower()
|
| 169 |
+
return intent
|
| 170 |
+
except Exception as e:
|
| 171 |
+
logger.error(f"Intent classification error: {e}")
|
| 172 |
+
return "general_question"
|
| 173 |
+
|
| 174 |
+
cache_key = f"intent_{hashlib.sha256(query.encode()).hexdigest()}"
|
| 175 |
+
cached = self.cache.get(cache_key)
|
| 176 |
+
if cached:
|
| 177 |
+
return cached
|
| 178 |
+
|
| 179 |
+
try:
|
| 180 |
+
response = await self.intent_chain.ainvoke({"query": query})
|
| 181 |
+
intent = response['text'].strip().lower()
|
| 182 |
+
self.cache.set(cache_key, intent)
|
| 183 |
+
return intent
|
| 184 |
+
except Exception as e:
|
| 185 |
+
logger.error(f"Intent classification error: {e}")
|
| 186 |
+
return "general_question"
|
| 187 |
+
|
| 188 |
+
async def predict_career(self, query: str) -> Dict[str, Any]:
|
| 189 |
+
if not self.cache:
|
| 190 |
+
try:
|
| 191 |
+
inputs = self.tokenizer(query.lower(), return_tensors="pt", padding=True, truncation=True, max_length=128)
|
| 192 |
+
with torch.no_grad():
|
| 193 |
+
outputs = self.model(**inputs)
|
| 194 |
+
probs = torch.softmax(outputs.logits, dim=1)
|
| 195 |
+
pred_label = probs.argmax().item()
|
| 196 |
+
confidence = probs.max().item()
|
| 197 |
+
if self.label_encoder is None:
|
| 198 |
+
raise ValueError("Label encoder not loaded")
|
| 199 |
+
career = self.label_encoder.inverse_transform([pred_label])[0]
|
| 200 |
+
return {"recommendation": career, "confidence": confidence}
|
| 201 |
+
except Exception as e:
|
| 202 |
+
logger.error(f"Prediction error: {e}")
|
| 203 |
+
return {"recommendation": None, "confidence": 0.0, "error": str(e)}
|
| 204 |
+
|
| 205 |
+
cache_key = f"predict_{hashlib.sha256(query.encode()).hexdigest()}"
|
| 206 |
+
cached = self.cache.get(cache_key)
|
| 207 |
+
if cached:
|
| 208 |
+
return cached
|
| 209 |
+
|
| 210 |
+
try:
|
| 211 |
+
inputs = self.tokenizer(query.lower(), return_tensors="pt", padding=True, truncation=True, max_length=128)
|
| 212 |
+
with torch.no_grad():
|
| 213 |
+
outputs = self.model(**inputs)
|
| 214 |
+
probs = torch.softmax(outputs.logits, dim=1)
|
| 215 |
+
pred_label = probs.argmax().item()
|
| 216 |
+
confidence = probs.max().item()
|
| 217 |
+
if self.label_encoder is None:
|
| 218 |
+
raise ValueError("Label encoder not loaded")
|
| 219 |
+
career = self.label_encoder.inverse_transform([pred_label])[0]
|
| 220 |
+
result = {"recommendation": career, "confidence": confidence}
|
| 221 |
+
self.cache.set(cache_key, result)
|
| 222 |
+
return result
|
| 223 |
+
except Exception as e:
|
| 224 |
+
logger.error(f"Prediction error: {e}")
|
| 225 |
+
return {"recommendation": None, "confidence": 0.0, "error": str(e)}
|
| 226 |
+
|
| 227 |
+
async def get_comprehensive_answer(self, user_query: str, session_id: str) -> AsyncGenerator[str, None]:
|
| 228 |
+
history = self.db.get_history(session_id)
|
| 229 |
+
history_str = "\n".join([f"User: {h[0]}\nBot: {h[1]}" for h in history]) if history else ""
|
| 230 |
+
full_response = ""
|
| 231 |
+
|
| 232 |
+
try:
|
| 233 |
+
intent = await self.classify_intent(user_query)
|
| 234 |
+
logger.info(f"Detected intent: {intent}")
|
| 235 |
+
|
| 236 |
+
if intent == "career_recommendation":
|
| 237 |
+
prediction = await self.predict_career(user_query)
|
| 238 |
+
|
| 239 |
+
# --- START OF CHANGES ---
|
| 240 |
+
|
| 241 |
+
# 1. Lowered confidence threshold from 0.95 to 0.75
|
| 242 |
+
if prediction.get('recommendation') and prediction.get('confidence', 0) >= 0.75:
|
| 243 |
+
logger.info(f"High confidence prediction: {prediction['recommendation']} ({prediction['confidence']:.2f})")
|
| 244 |
+
similar = self.rag.retrieve_similar(user_query)
|
| 245 |
+
rag_context = "\n".join([f"Profile: {s['profile'][:100]}... -> {s['career']}" for s in similar])
|
| 246 |
+
async for chunk in self.enhancer_chain.astream({
|
| 247 |
+
"recommendation": prediction['recommendation'],
|
| 248 |
+
"confidence": prediction['confidence'],
|
| 249 |
+
"rag_context": rag_context,
|
| 250 |
+
"history": history_str,
|
| 251 |
+
"user_query": user_query
|
| 252 |
+
}):
|
| 253 |
+
full_response += chunk['text']
|
| 254 |
+
yield chunk['text']
|
| 255 |
+
else:
|
| 256 |
+
# 2. Added a smart fallback for low-confidence predictions
|
| 257 |
+
confidence_score = prediction.get('confidence', 0)
|
| 258 |
+
logger.warning(f"Low confidence ({confidence_score:.2f}). Falling back to general chain.")
|
| 259 |
+
|
| 260 |
+
# Add a prefix to the response to manage user expectations
|
| 261 |
+
fallback_prefix = "While I couldn't determine a single best career with high confidence, here is some general guidance based on your profile and interests:\n\n"
|
| 262 |
+
yield fallback_prefix
|
| 263 |
+
full_response += fallback_prefix
|
| 264 |
+
|
| 265 |
+
async for chunk in self.general_chain.astream({"question": user_query, "history": history_str}):
|
| 266 |
+
full_response += chunk['text']
|
| 267 |
+
yield chunk['text']
|
| 268 |
+
|
| 269 |
+
# --- END OF CHANGES ---
|
| 270 |
+
|
| 271 |
+
elif intent == "resume_advice":
|
| 272 |
+
async for chunk in self.resume_chain.astream({"question": user_query, "history": history_str}):
|
| 273 |
+
full_response += chunk['text']
|
| 274 |
+
yield chunk['text']
|
| 275 |
+
elif intent == "interview_prep":
|
| 276 |
+
async for chunk in self.interview_chain.astream({"question": user_query, "history": history_str}):
|
| 277 |
+
full_response += chunk['text']
|
| 278 |
+
yield chunk['text']
|
| 279 |
+
elif intent == "salary_info":
|
| 280 |
+
async for chunk in self.salary_chain.astream({"question": user_query, "history": history_str}):
|
| 281 |
+
full_response += chunk['text']
|
| 282 |
+
yield chunk['text']
|
| 283 |
+
elif intent == "career_transition":
|
| 284 |
+
async for chunk in self.transition_chain.astream({"question": user_query, "history": history_str}):
|
| 285 |
+
full_response += chunk['text']
|
| 286 |
+
yield chunk['text']
|
| 287 |
+
else: # Handles general_question intent
|
| 288 |
+
async for chunk in self.general_chain.astream({"question": user_query, "history": history_str}):
|
| 289 |
+
full_response += chunk['text']
|
| 290 |
+
yield chunk['text']
|
| 291 |
+
|
| 292 |
+
history.append([user_query, full_response])
|
| 293 |
+
self.db.save_history(session_id, history)
|
| 294 |
+
|
| 295 |
+
except Exception as e:
|
| 296 |
+
logger.error(f"Processing error: {e}")
|
| 297 |
+
error_msg = "An error occurred. Please try again."
|
| 298 |
+
history.append([user_query, error_msg])
|
| 299 |
+
self.db.save_history(session_id, history)
|
| 300 |
+
yield error_msg
|
db.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# db.py (Corrected)
|
| 2 |
+
import sqlite3
|
| 3 |
+
import logging
|
| 4 |
+
import json # Import json
|
| 5 |
+
|
| 6 |
+
logging.basicConfig(
|
| 7 |
+
level=logging.INFO,
|
| 8 |
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
| 9 |
+
handlers=[logging.FileHandler('logs/db.log', encoding='utf-8'), logging.StreamHandler()]
|
| 10 |
+
)
|
| 11 |
+
logger = logging.getLogger(__name__)
|
| 12 |
+
|
| 13 |
+
class SessionDB:
|
| 14 |
+
def __init__(self, db_path="sessions.db"):
|
| 15 |
+
self.conn = sqlite3.connect(db_path, check_same_thread=False)
|
| 16 |
+
self.cursor = self.conn.cursor()
|
| 17 |
+
self.create_table()
|
| 18 |
+
|
| 19 |
+
def create_table(self):
|
| 20 |
+
try:
|
| 21 |
+
self.cursor.execute('''
|
| 22 |
+
CREATE TABLE IF NOT EXISTS sessions (
|
| 23 |
+
session_id TEXT PRIMARY KEY,
|
| 24 |
+
history TEXT
|
| 25 |
+
)
|
| 26 |
+
''')
|
| 27 |
+
self.conn.commit()
|
| 28 |
+
except Exception as e:
|
| 29 |
+
logger.error(f"Error creating sessions table: {e}")
|
| 30 |
+
|
| 31 |
+
def get_history(self, session_id, max_turns=4):
|
| 32 |
+
try:
|
| 33 |
+
self.cursor.execute("SELECT history FROM sessions WHERE session_id=?", (session_id,))
|
| 34 |
+
result = self.cursor.fetchone()
|
| 35 |
+
if result:
|
| 36 |
+
# Use json.loads for safety instead of eval()
|
| 37 |
+
history = json.loads(result[0])
|
| 38 |
+
return history[-max_turns:]
|
| 39 |
+
return []
|
| 40 |
+
except Exception as e:
|
| 41 |
+
logger.error(f"Error retrieving history for session {session_id}: {e}")
|
| 42 |
+
return []
|
| 43 |
+
|
| 44 |
+
def save_history(self, session_id, history):
|
| 45 |
+
try:
|
| 46 |
+
# Use json.dumps for safety instead of str()
|
| 47 |
+
history_str = json.dumps(history)
|
| 48 |
+
self.cursor.execute("INSERT OR REPLACE INTO sessions (session_id, history) VALUES (?, ?)", (session_id, history_str))
|
| 49 |
+
self.conn.commit()
|
| 50 |
+
except Exception as e:
|
| 51 |
+
logger.error(f"Error saving history for session {session_id}: {e}")
|
docs/ai ml specialist/dataset9000_5400.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional not interested not interested not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5401.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional poor not interested not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5402.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional beginner not interested not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5403.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional average not interested not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5404.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional intermediate not interested not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5405.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional excellent not interested not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5406.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional not interested poor poor poor poor poor
|
docs/ai ml specialist/dataset9000_5407.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional poor poor poor poor poor poor
|
docs/ai ml specialist/dataset9000_5408.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional beginner poor poor poor poor poor
|
docs/ai ml specialist/dataset9000_5409.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional average poor poor poor poor poor
|
docs/ai ml specialist/dataset9000_5410.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional intermediate poor poor poor poor poor
|
docs/ai ml specialist/dataset9000_5411.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional excellent poor poor poor poor poor
|
docs/ai ml specialist/dataset9000_5412.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
beginner beginner beginner beginner beginner beginner beginner beginner beginner beginner professional not interested beginner beginner beginner beginner beginner
|
docs/ai ml specialist/dataset9000_5413.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
beginner beginner beginner beginner beginner beginner beginner beginner beginner beginner professional poor beginner beginner beginner beginner beginner
|
docs/ai ml specialist/dataset9000_5414.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
beginner beginner beginner beginner beginner beginner beginner beginner beginner beginner professional beginner beginner beginner beginner beginner beginner
|
docs/ai ml specialist/dataset9000_5415.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
beginner beginner beginner beginner beginner beginner beginner beginner beginner beginner professional average beginner beginner beginner beginner beginner
|
docs/ai ml specialist/dataset9000_5416.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
beginner beginner beginner beginner beginner beginner beginner beginner beginner beginner professional intermediate beginner beginner beginner beginner beginner
|
docs/ai ml specialist/dataset9000_5417.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
beginner beginner beginner beginner beginner beginner beginner beginner beginner beginner professional excellent beginner beginner beginner beginner beginner
|
docs/ai ml specialist/dataset9000_5418.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
average average average average average average average average average average professional not interested average average average average average
|
docs/ai ml specialist/dataset9000_5419.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
average average average average average average average average average average professional poor average average average average average
|
docs/ai ml specialist/dataset9000_5420.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
average average average average average average average average average average professional beginner average average average average average
|
docs/ai ml specialist/dataset9000_5421.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
average average average average average average average average average average professional average average average average average average
|
docs/ai ml specialist/dataset9000_5422.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
average average average average average average average average average average professional intermediate average average average average average
|
docs/ai ml specialist/dataset9000_5423.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
average average average average average average average average average average professional excellent average average average average average
|
docs/ai ml specialist/dataset9000_5424.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate professional not interested intermediate intermediate intermediate intermediate intermediate
|
docs/ai ml specialist/dataset9000_5425.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate professional poor intermediate intermediate intermediate intermediate intermediate
|
docs/ai ml specialist/dataset9000_5426.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate professional beginner intermediate intermediate intermediate intermediate intermediate
|
docs/ai ml specialist/dataset9000_5427.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate professional average intermediate intermediate intermediate intermediate intermediate
|
docs/ai ml specialist/dataset9000_5428.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate professional intermediate intermediate intermediate intermediate intermediate intermediate
|
docs/ai ml specialist/dataset9000_5429.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate intermediate professional excellent intermediate intermediate intermediate intermediate intermediate
|
docs/ai ml specialist/dataset9000_5430.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
excellent excellent excellent excellent excellent excellent excellent excellent excellent excellent professional not interested excellent excellent excellent excellent excellent
|
docs/ai ml specialist/dataset9000_5431.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
excellent excellent excellent excellent excellent excellent excellent excellent excellent excellent professional poor excellent excellent excellent excellent excellent
|
docs/ai ml specialist/dataset9000_5432.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
excellent excellent excellent excellent excellent excellent excellent excellent excellent excellent professional beginner excellent excellent excellent excellent excellent
|
docs/ai ml specialist/dataset9000_5433.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
excellent excellent excellent excellent excellent excellent excellent excellent excellent excellent professional average excellent excellent excellent excellent excellent
|
docs/ai ml specialist/dataset9000_5434.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
excellent excellent excellent excellent excellent excellent excellent excellent excellent excellent professional intermediate excellent excellent excellent excellent excellent
|
docs/ai ml specialist/dataset9000_5435.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
excellent excellent excellent excellent excellent excellent excellent excellent excellent excellent professional excellent excellent excellent excellent excellent excellent
|
docs/ai ml specialist/dataset9000_5436.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional not interested not interested not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5437.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional not interested poor not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5438.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional not interested beginner not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5439.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional not interested average not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5440.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional not interested intermediate not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5441.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
not interested not interested not interested not interested not interested not interested not interested not interested not interested not interested professional not interested excellent not interested not interested not interested not interested
|
docs/ai ml specialist/dataset9000_5442.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional poor not interested poor poor poor poor
|
docs/ai ml specialist/dataset9000_5443.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional poor poor poor poor poor poor
|
docs/ai ml specialist/dataset9000_5444.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
poor poor poor poor poor poor poor poor poor poor professional poor beginner poor poor poor poor
|