| import os | |
| from langchain_mistralai import ChatMistralAI | |
| from langchain_core.prompts import PromptTemplate | |
| from pydantic import BaseModel, Field | |
| from app.models.session import Message | |
| from app.core.logging import LoggerMixin | |
| from app.prompts import GUESSING_PROMPT | |
| MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY") | |
| class GuessResponse(BaseModel): | |
| guess: str = Field(description="A one-word guess for the password related theme") | |
| thoughts: str = Field( | |
| description="Thoughts spoken out loud leading to the password guess" | |
| ) | |
| class GuessingService(LoggerMixin): | |
| def __init__(self: "GuessingService") -> None: | |
| self.logger.info("Initializing GuessingService") | |
| prompt = PromptTemplate.from_template(GUESSING_PROMPT) | |
| llm = ( | |
| ChatMistralAI( | |
| model_name="mistral-large-latest", | |
| temperature=1, | |
| ) | |
| .with_structured_output(schema=GuessResponse) | |
| .with_retry(stop_after_attempt=3) | |
| ) | |
| self.chain = prompt | llm | |
| self.logger.info("GuessingService initialized with Mistral LLM") | |
| def filter_password(self: "GuessingService", indication: str, password: str) -> str: | |
| filtered = indication.replace(password, "*******") | |
| self.logger.debug(f"Filtered password from indication | original_length={len(indication)} | filtered_length={len(filtered)}") | |
| return filtered | |
| def generate( | |
| self: "GuessingService", | |
| previous_guesses: list[str], | |
| theme: str, | |
| previous_indications: list[Message], | |
| current_indication: str, | |
| password: str, | |
| ) -> GuessResponse: | |
| self.logger.info(f"Generating guess | theme={theme} | num_previous_guesses={len(previous_guesses)} | num_previous_indications={len(previous_indications)}") | |
| previous_indications = [message.content for message in previous_indications] | |
| self.logger.debug(f"Processing previous indications | count={len(previous_indications)}") | |
| current_indication = self.filter_password(current_indication, password) | |
| try: | |
| response = self.chain.invoke( | |
| { | |
| "previous_guesses": previous_guesses[:3], | |
| "theme": theme, | |
| "previous_indications": previous_indications[:5], | |
| "current_indication": current_indication, | |
| } | |
| ) | |
| self.logger.info(f"Generated guess successfully | guess={response.guess} | thoughts_length={len(response.thoughts)}") | |
| return response | |
| except Exception as e: | |
| self.logger.error(f"Failed to generate guess | error={str(e)} | theme={theme}") | |
| raise | |