nagur-shareef-shaik's picture
Add Application Code
cd6f412
import logging
import json
from pathlib import Path
from typing import Tuple, Dict, Any, Optional
from insucompass.services import llm_provider
from insucompass.config import settings
from insucompass.prompts.prompt_loader import load_prompt
# Configure logging
logging.basicConfig(level=settings.LOG_LEVEL, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
llm = llm_provider.get_gemini_llm()
class ProfileBuilder:
"""
A service class that manages the entire conversational profile building process.
It determines the next question and intelligently updates the profile with user answers.
"""
def __init__(self):
"""Initializes the ProfileBuilder, loading all necessary prompts."""
try:
self.question_prompt = load_prompt("profile_agent")
self.updater_prompt = load_prompt("profile_updater")
logger.info("ProfileBuilder initialized successfully with all prompts.")
except FileNotFoundError as e:
logger.critical(f"A required prompt file was not found: {e}. ProfileBuilder cannot function.")
raise
def get_next_question(self, current_profile: Dict[str, Any], conversation_history: Dict[str, Any]) -> str:
"""
Analyzes the user's current profile and determines the next question to ask,
or signals that the profile is complete.
Args:
current_profile: A dictionary representing the user's profile data.
Returns:
A string containing the next question for the user, or "PROFILE_COMPLETE".
"""
logger.debug(f"Determining next question for profile: {current_profile}")
profile_json_str = json.dumps(current_profile, indent=2)
conversation_history_str = json.dumps(conversation_history, indent=2)
full_prompt = f"{self.question_prompt}\n\n### User Profile\n{profile_json_str} \n\n ### Conversation History{conversation_history_str}"
try:
response = llm.invoke(full_prompt)
next_step = response.content.strip()
logger.info(f"LLM returned next step: '{next_step}'")
return next_step
except Exception as e:
logger.error(f"LLM error during question generation: {e}")
return "I'm sorry, I'm having a little trouble right now. Could we try that again?"
def update_profile_with_answer(
self,
current_profile: Dict[str, Any],
last_question: str,
user_answer: str
) -> Dict[str, Any]:
"""
Uses an LLM to intelligently update the user's profile with their latest answer.
Args:
current_profile: The user's profile before the update.
last_question: The question the user is answering.
user_answer: The user's free-text answer.
Returns:
The updated profile dictionary.
"""
logger.debug(f"Updating profile with answer: '{user_answer}' for question: '{last_question}'")
profile_json_str = json.dumps(current_profile, indent=2)
# Construct the prompt for the updater LLM call
full_prompt = (
f"{self.updater_prompt}\n\n"
f"current_profile: {profile_json_str}\n\n"
f"last_question_asked: \"{last_question}\"\n\n"
f"user_answer: \"{user_answer}\""
)
try:
response = llm.invoke(full_prompt)
response_content = response.content.strip()
# Clean the response to ensure it's valid JSON
# The LLM can sometimes wrap the JSON in markdown
if response_content.startswith("```json"):
response_content = response_content[7:-3].strip()
updated_profile = json.loads(response_content)
logger.info("Successfully updated profile with user's answer.")
return updated_profile
except json.JSONDecodeError as e:
logger.error(f"Failed to decode JSON from LLM response: {e}\nResponse was: {response.content}")
# Return the original profile to avoid data corruption
return current_profile
except Exception as e:
logger.error(f"LLM error during profile update: {e}")
# Return the original profile on error
return current_profile
def run_conversation_turn(
self,
current_profile: Dict[str, Any],
last_user_answer: str = None
) -> Tuple[Dict[str, Any], str]:
"""
Executes a full turn of the profile-building conversation.
Args:
current_profile: The current state of the user's profile.
last_user_answer: The user's answer from the previous turn, if any.
Returns:
A tuple containing:
- The updated profile dictionary.
- The next question to ask the user (or "PROFILE_COMPLETE").
"""
# First, determine the question that *would have been asked* for the current state
# This is the question the user just answered.
question_that_was_asked = self.get_next_question(current_profile)
profile_after_update = current_profile
# If there was an answer from the user, update the profile
if last_user_answer and question_that_was_asked != "PROFILE_COMPLETE":
profile_after_update = self.update_profile_with_answer(
current_profile=current_profile,
last_question=question_that_was_asked,
user_answer=last_user_answer
)
# Now, with the potentially updated profile, get the *next* question
next_question_to_ask = self.get_next_question(profile_after_update)
return profile_after_update, next_question_to_ask
profile_builder = ProfileBuilder()