import hashlib import logging import time from collections import OrderedDict from typing import Any, Dict, List, Optional from groq import Groq from config import GROQ_API_KEY from prompts import SUMMARIZATION_PROMPT logger = logging.getLogger(__name__) class ConversationSummarizer: MAX_HISTORY_TURNS = 10 SUMMARY_MAX_TOKENS = 300 RECENT_MESSAGES_TO_KEEP = 8 MAX_INPUT_TEXT_LENGTH = 2000 MAX_CACHE_SIZE = 100 def __init__( self, api_key: Optional[str] = None, model: str = "llama-3.1-8b-instant", max_history_turns: int = 10, ): self._api_key = api_key or GROQ_API_KEY self._model = model self._max_turns = max_history_turns self._client: Optional[Groq] = None self._summary_cache: OrderedDict[str, str] = OrderedDict() @property def client(self) -> Groq: if self._client is None: if not self._api_key: raise ValueError("GROQ_API_KEY not configured") self._client = Groq(api_key=self._api_key, timeout=5.0) return self._client def _generate_cache_key(self, messages: List[Dict[str, str]]) -> str: parts = [] for msg in messages: content = str(msg.get("content", ""))[:50] parts.append(content) raw = "||".join(parts) return hashlib.md5(raw.encode("utf-8")).hexdigest() def summarize_if_needed(self, chat_history: List[Dict[str, str]]) -> List[Dict[str, str]]: if len(chat_history) <= self._max_turns * 2: return chat_history try: recent_messages = chat_history[-self.RECENT_MESSAGES_TO_KEEP:] older_messages = chat_history[:-self.RECENT_MESSAGES_TO_KEEP] db_messages = [m for m in older_messages if m.get("is_database")] non_db_older = [m for m in older_messages if not m.get("is_database")] summary = self._summarize_conversation(non_db_older) if non_db_older else "" result = [] if summary: result.append({ "role": "system", "content": f"Önceki konuşma özeti: {summary}", }) result.extend(db_messages) result.extend(recent_messages) return result except Exception as e: logger.warning(f"Summarization failed, returning original history: {e}") return chat_history def _summarize_conversation(self, messages: List[Dict[str, str]]) -> str: cache_key = self._generate_cache_key(messages) if cache_key in self._summary_cache: self._summary_cache.move_to_end(cache_key) logger.debug("Conversation summary cache hit") return self._summary_cache[cache_key] conversation_parts = [] for msg in messages: role = msg.get("role", "unknown") content = str(msg.get("content", "")) conversation_parts.append(f"{role}: {content}") conversation_text = "\n".join(conversation_parts) if len(conversation_text) > self.MAX_INPUT_TEXT_LENGTH: conversation_text = conversation_text[-self.MAX_INPUT_TEXT_LENGTH:] prompt = SUMMARIZATION_PROMPT.format(conversation_text=conversation_text) try: response = self.client.chat.completions.create( model=self._model, messages=[{"role": "user", "content": prompt}], temperature=0.3, max_tokens=self.SUMMARY_MAX_TOKENS, ) summary = response.choices[0].message.content.strip() if not summary: summary = "Konuşma geçmişi mevcut ama özeti oluşturulamadı." except Exception as e: logger.warning(f"Summary generation failed: {e}") summary = "Konuşma geçmişi mevcut ama özeti oluşturulamadı." while len(self._summary_cache) >= self.MAX_CACHE_SIZE: self._summary_cache.popitem(last=False) self._summary_cache[cache_key] = summary return summary def clear_cache(self) -> None: self._summary_cache.clear() _summarizer_instance: Optional[ConversationSummarizer] = None def get_conversation_summarizer( api_key: Optional[str] = None, model: Optional[str] = None, ) -> ConversationSummarizer: global _summarizer_instance if _summarizer_instance is None: _summarizer_instance = ConversationSummarizer( api_key=api_key, model=model or "llama-3.1-8b-instant", ) return _summarizer_instance def reset_conversation_summarizer() -> None: global _summarizer_instance _summarizer_instance = None