""" agents/clarification_agent.py ------------------------------- Clarification Agent for AutoDevAgent. Checks whether a user task is clear enough to generate correct code and tests. If the task is ambiguous, incomplete, or contradictory it returns a concise question asking the user to clarify. Design: - Uses the fast model (8B) — it's a lightweight classification + Q-gen task. - Returns {"clear": True} or {"clear": False, "question": "..."} - Runs in app.py BEFORE the LangGraph pipeline starts, so the pipeline never wastes tokens on an unclear task. - Only asks for clarification when genuinely needed — simple, well-formed tasks ("write a function to reverse a string") always pass through. Usage: from agents.clarification_agent import ClarificationAgent agent = ClarificationAgent() result = agent.check("Write a Python function") if not result["clear"]: print(result["question"]) """ import logging from typing import Any from langchain_groq import ChatGroq from langchain_core.messages import SystemMessage, HumanMessage from config import settings logger = logging.getLogger(__name__) CLARIFICATION_SYSTEM = """ You are a requirements analyst for a code generation assistant that writes Python and SQL code. Your job is to decide if a user's task description is clear enough to generate correct, testable code — without guessing. A task is CLEAR if: - The programming language or goal is inferable (Python function, SQL query, etc.) - The inputs and expected outputs can be reasonably inferred - There is enough detail to write a correct implementation A task is UNCLEAR if: - It is too vague to know what to implement (e.g. "make something cool") - Key information is missing that would change the implementation significantly (e.g. "sort the data" — what data? what format? ascending or descending?) - It is contradictory or impossible to implement as stated - It is a single word or fragment with no actionable meaning Rules: - Be LENIENT — most standard programming tasks are clear enough. Do not ask for clarification on well-known patterns (reverse a string, fibonacci, etc.). - If UNCLEAR, ask ONE short, specific question (max 20 words) that would give enough info to proceed. Do not ask multiple questions. - Never ask for clarification on things the agent can reasonably assume (e.g. don't ask "should I use a function or a class?" for a simple task). Respond with ONLY valid JSON — no explanation, no markdown: {"clear": true} or {"clear": false, "question": "Your single clarifying question here."} """.strip() class ClarificationAgent: """ Checks if a task description is clear enough to generate code. Returns a dict: {"clear": True} {"clear": False, "question": "..."} """ def __init__(self) -> None: self._llm = ChatGroq( api_key=settings.groq_api_key, model=settings.groq_model_fast, # 8B — lightweight task temperature=0.0, max_tokens=120, request_timeout=settings.groq_request_timeout, ) def check(self, task: str) -> dict[str, Any]: """ Check if the task is clear enough to proceed. Args: task: The user's raw task description. Returns: {"clear": True} — task is actionable, proceed {"clear": False, "question": str} — needs clarification """ if not task or not task.strip(): return {"clear": False, "question": "Please describe what you'd like me to build."} try: response = self._llm.invoke([ SystemMessage(content=CLARIFICATION_SYSTEM), HumanMessage(content=f"Task: {task.strip()}"), ]) raw = response.content.strip() # Parse JSON response import json, re # Strip markdown fences if model wrapped it raw = re.sub(r'^```[a-z]*\n?', '', raw).rstrip('`').strip() result = json.loads(raw) if result.get("clear") is True: logger.info("ClarificationAgent: task is clear — proceeding") return {"clear": True} else: question = result.get("question", "Could you provide more details about the expected inputs and outputs?") logger.info("ClarificationAgent: task needs clarification — %s", question) return {"clear": False, "question": question} except Exception as e: # On any failure, let the pipeline proceed — don't block on a clarification error logger.warning("ClarificationAgent failed (%s) — proceeding anyway", e) return {"clear": True}