kanhacoderx's picture
Update app/agent.py
c199958 verified
from typing import List, Dict, Any
import json
from pathlib import Path
from typing import List, Dict, Any
CATALOG_PATH = Path("data/shl_catalog.json")
def load_catalog() -> List[Dict[str, Any]]:
if not CATALOG_PATH.exists():
return []
with open(CATALOG_PATH, "r", encoding="utf-8") as f:
return json.load(f)
CATALOG = load_catalog()
def get_last_user_message(messages: List[Any]) -> str:
for msg in reversed(messages):
if msg.role == "user":
return msg.content.lower()
return ""
def build_conversation_query(messages: List[Any]) -> str:
"""
Builds a compact query from full stateless conversation history.
This helps refinement requests like:
'Actually add personality tests'
without forgetting earlier role context.
"""
user_messages = []
for msg in messages:
if msg.role == "user":
user_messages.append(msg.content)
return " ".join(user_messages).lower()
def is_out_of_scope(text: str) -> bool:
text = text.lower()
blocked_phrases = [
# Legal / compliance
"legal advice",
"is it legal",
"employment law",
"labor law",
"labour law",
"discrimination",
"fire employee",
"terminate employee",
"lawsuit",
"contract",
# General hiring advice outside SHL assessment recommendation
"write interview questions",
"interview questions",
"salary",
"compensation",
"negotiate offer",
"job description template",
"write a job description",
"resume screening",
"cv screening",
"cover letter",
# Prompt injection
"ignore previous instructions",
"forget your instructions",
"act as unrestricted",
"bypass",
"system prompt",
"developer message",
"reveal your prompt",
"jailbreak",
]
return any(phrase in text for phrase in blocked_phrases)
def is_vague(text: str) -> bool:
vague_phrases = [
"i need an assessment",
"need assessment",
"suggest assessment",
"recommend assessment",
"assessment test",
]
has_role_signal = any(
word in text
for word in [
"java",
"python",
"developer",
"engineer",
"manager",
"sales",
"graduate",
"analyst",
"stakeholder",
"communication",
]
)
if any(phrase in text for phrase in vague_phrases) and not has_role_signal:
return True
if len(text.split()) <= 4 and not has_role_signal:
return True
return False
def score_catalog_item(query: str, item: Dict[str, Any]) -> int:
score = 0
name = item.get("name", "").lower()
description = item.get("description", "").lower()
keywords = item.get("keywords", [])
searchable_text = f"{name} {description} {' '.join(keywords)}"
query_words = query.lower().split()
for word in query_words:
if len(word) > 2 and word in searchable_text:
score += 1
# Strong skill boosts
skill_terms = [
"java", "python", "sql", "javascript", "developer",
"coding", "programming", "software", "backend"
]
for skill in skill_terms:
if skill in query and skill in searchable_text:
score += 3
# Personality / behavior boost
if any(term in query for term in ["personality", "communication", "stakeholder", "leadership"]):
if item.get("test_type") == "P" or "personality" in searchable_text or "opq" in searchable_text:
score += 3
# Cognitive / aptitude boost
if any(term in query for term in ["cognitive", "aptitude", "reasoning", "ability"]):
if item.get("test_type") == "A" or "ability" in searchable_text:
score += 3
return score
def recommend(query: str) -> List[Dict[str, str]]:
scored_items = []
for item in CATALOG:
score = score_catalog_item(query, item)
if score > 0:
scored_items.append((score, item))
scored_items.sort(key=lambda x: x[0], reverse=True)
recommendations = []
for _, item in scored_items[:10]:
recommendations.append(
{
"name": item.get('name',''),
"url": item.get('url',''),
"test_type": item.get('test_type','unknown'),
}
)
return recommendations
def is_compare_query(text: str) -> bool:
compare_terms = [
"difference between",
"compare",
"vs",
"versus",
"different from",
"which is better",
]
text = text.lower()
return any(term in text for term in compare_terms)
def find_matching_assessments(text: str, limit: int = 5) -> List[Dict[str, Any]]:
text = text.lower()
matches = []
for item in CATALOG:
name = item.get("name", "").lower()
description = item.get("description", "").lower()
keywords = " ".join(item.get("keywords", [])).lower()
searchable_text = f"{name} {description} {keywords}"
score = 0
# Direct name match
for token in text.split():
if len(token) > 2 and token in name:
score += 3
elif len(token) > 2 and token in searchable_text:
score += 1
if score > 0:
matches.append((score, item))
matches.sort(key=lambda x: x[0], reverse=True)
return [item for _, item in matches[:limit]]
def compare_assessments(query: str) -> Dict[str, Any]:
matches = find_matching_assessments(query, limit=4)
if len(matches) < 2:
return {
"reply": "I can compare SHL assessments, but I need two assessment names from the catalog. Please mention both assessments you want to compare.",
"recommendations": [],
"end_of_conversation": False,
}
a = matches[0]
b = matches[1]
a_name = a.get("name", "Assessment 1")
b_name = b.get("name", "Assessment 2")
a_type = a.get("test_type", "Unknown")
b_type = b.get("test_type", "Unknown")
a_desc = a.get("description", "")
b_desc = b.get("description", "")
reply = (
f"Here is a catalog-grounded comparison:\n\n"
f"{a_name} focuses on: {a_desc[:300] if a_desc else 'description not available in catalog data'}\n"
f"Test type: {a_type}\n\n"
f"{b_name} focuses on: {b_desc[:300] if b_desc else 'description not available in catalog data'}\n"
f"Test type: {b_type}\n\n"
f"Use {a_name} when the role requirement matches its catalog description. "
f"Use {b_name} when the hiring need is closer to its catalog description."
)
return {
"reply": reply,
"recommendations": [
{
"name": a.get("name", ""),
"url": a.get("url", ""),
"test_type": a.get("test_type", "Unknown"),
},
{
"name": b.get("name", ""),
"url": b.get("url", ""),
"test_type": b.get("test_type", "Unknown"),
},
],
"end_of_conversation": False,
}
def run_agent(messages: List[Any]) -> Dict[str, Any]:
query = build_conversation_query(messages)
last_user_query = get_last_user_message(messages)
if not query:
return {
"reply": "Please share the role or skills you want to assess using SHL assessments.",
"recommendations": [],
"end_of_conversation": False,
}
if is_out_of_scope(query):
return {
"reply": "I can only help with SHL assessment recommendations, comparisons, and refinements based on the SHL catalog.",
"recommendations": [],
"end_of_conversation": False,
}
if is_vague(query):
return {
"reply": "Sure. Which role, skill area, and seniority level are you hiring for?",
"recommendations": [],
"end_of_conversation": False,
}
# Important safety check
# If real SHL catalog JSON is missing or not loaded, don't crash and don't hallucinate.
if not CATALOG:
return {
"reply": "The SHL catalog is not loaded yet. Please try again after the catalog data is available.",
"recommendations": [],
"end_of_conversation": False,
}
if is_compare_query(last_user_query):
return compare_assessments(last_user_query)
recommendations = recommend(query)
if recommendations:
return {
"reply": f"Based on the role details, here are {len(recommendations)} SHL assessments that may fit this hiring need.",
"recommendations": recommendations,
"end_of_conversation": False,
}
return {
"reply": "Could you share the target role, required skills, and seniority level so I can recommend relevant SHL assessments?",
"recommendations": [],
"end_of_conversation": False,
}