Spaces:
Sleeping
Sleeping
| 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, | |
| } |