shl-recommender / app /Responder.py
devpatel1012's picture
Initial commit of SHL Assessment Recommender
ca20ec1
Raw
History Blame Contribute Delete
4.52 kB
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from app.schemas import ChatResponse
from app.config import HUGGINGFACEHUB_API_TOKEN
from app.Llm_utils import invoke_with_retry, UpstreamUnavailableError
class ResponderStage:
"""Stage 3: Phraser stage. Uses the specific action kind
determined by the deterministic planner, context data, and real catalog
candidates to compile a highly contextual response.
"""
def __init__(self):
# Upgraded to Llama-3.1-8B-Instruct
self.llm = ChatOpenAI(
model="meta-llama/Llama-3.1-8B-Instruct:novita",
api_key=HUGGINGFACEHUB_API_TOKEN,
base_url="https://router.huggingface.co/v1",
max_tokens=700,
temperature=0.2,
)
self.parser = JsonOutputParser(pydantic_object=ChatResponse)
self.prompt = PromptTemplate(
template="""You are an expert SHL Assessment Consultant writing ONE highly tailored response turn. A planning step has already decided the strategy β€” your only job is to execute the phrasing naturally based on the catalog data provided below.
Action to perform: {action}
- "recommend": Present or update an assessment shortlist.
- "compare": Explicitly explain structural differences between specific tests requested by the user. Do not attach a recommendations array.
- "redirect": Address user constraints or handle legal/regulatory compliance pushbacks.
Context about the request:
- Target Audience: {role_summary}
- Purpose: {purpose}
- Updating a prior list: {updating}
- Prior shortlist (if updating/redirecting): {prior_recommendations}
Real catalog candidates available (Choose and discuss ONLY from this list):
{candidates}
Conversation so far:
{history}
Latest User Message:
{input}
CRITICAL LEGAL GUARDRAIL:
If the Latest User Message asks whether an assessment is legally required, legal obligations under laws like HIPAA, or whether an SHL test legally satisfies a compliance mandate, you MUST explicitly refuse to give legal advice. State clearly that legal compliance and regulatory obligations are outside what you can advise on, and direct them to their legal or compliance team. You can only confirm what the test measures, not its legal sufficiency.
RULES:
1. Speak like a senior human consultant. Cut out all robotic filler text. Jump straight into the logic and expertise.
2. Account for catalog constraints explicitly. Call out language limitations if requested tests don't match the requested language.
3. UPDATING A SHORTLIST: If "Updating a prior list" is True and you have a "Prior shortlist", you MUST physically copy every valid test from the prior shortlist into your new JSON "recommendations" array. Then, ADD the new tests from the candidates list. Do NOT drop the old tests unless requested.
4. NEVER leak raw JSON arrays, Python dictionaries, or brackets into the "reply" prose.
5. THE "RECOMMEND + REFINE" STRATEGY: If you are providing a shortlist but feel a specific detail (like seniority, spoken language, or cognitive needs) is missing, provide the preliminary recommendations AND end your text reply by asking the user a specific question to narrow it down further.
6. Set "end_of_conversation" to false.
Respond with ONLY the JSON object matching this schema:
{format_instructions}""",
input_variables=["action", "role_summary", "purpose", "updating",
"prior_recommendations", "candidates", "history", "input"],
partial_variables={"format_instructions": self.parser.get_format_instructions()},
)
self.chain = self.prompt | self.llm | self.parser
def run(self, **kwargs) -> dict:
try:
return invoke_with_retry(self.chain, kwargs)
except UpstreamUnavailableError as e:
print(f"[Responder] Upstream failure: {e}")
return {
"reply": "I'm having trouble reaching the assessment engine right now β€” could you try again in a moment?",
"recommendations": [],
"end_of_conversation": False,
}
except Exception as e:
print(f"[Responder] Parse failure: {e}")
return {
"reply": "I ran into an issue formatting that recommendation β€” could you rephrase your last message?",
"recommendations": [],
"end_of_conversation": False,
}