"""Manages the chat history and communication with the AI model API.""" import os from dotenv import load_dotenv import requests import json load_dotenv() class ChatManager: """Handles sending messages and maintaining conversation history. Connects to an OpenAI-compatible REST endpoint configured via environment variables. All messages (user, assistant, system) are kept in memory so the full conversation is sent with every request. """ def __init__(self): self.api_host = os.getenv("HOST") self.api_port = os.getenv("PORT") self.api_key = os.getenv("API_KEY") self.model = os.getenv("MODEL") # API endpoint URL (OpenAI-compatible format) self.api_url = f"http://{self.api_host}:{self.api_port}/v1/chat/completions" # Chat history stored in memory self.chat_history = [] def add_message(self, role: str, content: str) -> None: """Append a single message to the conversation history.""" self.chat_history.append({"role": role, "content": content}) def get_history(self) -> list: """Return a copy of the conversation history.""" return list(self.chat_history) def clear_history(self) -> None: """Wipe the conversation history (starts a fresh chat).""" self.chat_history = [] def send_message(self, user_message: str) -> str: """Send a user message to the AI and return its reply. Adds the user message to history, calls the API with the full history as context, and appends the AI reply to history before returning it. """ # Add user message to history self.add_message("user", user_message) try: # Prepare request to OpenAI-compatible API headers = { "Content-Type": "application/json", } # Add API key if available if self.api_key and self.api_key != "EMPTY": headers["Authorization"] = f"Bearer {self.api_key}" # Full history is sent so the model has multi-turn conversation context payload = { "model": self.model, "messages": self.chat_history, "temperature": 0.7, "max_tokens": 2000, "stream": False, } # Make API request response = requests.post( self.api_url, headers=headers, json=payload, timeout=30 ) # Check if request was successful if response.status_code != 200: error_msg = f"API Error {response.status_code}: {response.text}" raise Exception(error_msg) # Parse response response_data = response.json() # Extract AI message if "choices" in response_data and len(response_data["choices"]) > 0: ai_message = response_data["choices"][0]["message"]["content"] # Add AI response to history self.add_message("assistant", ai_message) return ai_message else: raise Exception("Invalid API response format") except requests.exceptions.RequestException as e: error_msg = f"Connection Error: {str(e)}" # Add error message to history so user sees it self.add_message("assistant", f"Error: {error_msg}") raise Exception(error_msg) except json.JSONDecodeError as e: error_msg = f"JSON Decode Error: {str(e)}" self.add_message("assistant", f"Error: {error_msg}") raise Exception(error_msg) except Exception as e: error_msg = f"Error: {str(e)}" self.add_message("assistant", f"Error: {error_msg}") raise Exception(error_msg) def get_chat_display(self) -> list: """Return a copy of the history suitable for display in the UI.""" return [ {"role": msg["role"], "content": msg["content"]} for msg in self.chat_history ]