import os from dotenv import load_dotenv from langchain_groq import ChatGroq load_dotenv() llm = ChatGroq( model_name="llama-3.3-70b-versatile", groq_api_key=os.getenv("GROQ_API_KEY"), temperature=0.3 ) def generate_answer(question, retrieved_chunks, language="arabic", recent_history=None, conversation_summary=None): """ Generates the final answer using Groq, grounded in retrieved document chunks and aware of conversation context. recent_history: list of the last 1-2 (question, answer) tuples, kept in full detail conversation_summary: short summary of everything OLDER than the recent history, kept compact """ context = "\n\n".join(retrieved_chunks) history_text = "" if conversation_summary: history_text += f"Summary of earlier conversation:\n{conversation_summary}\n\n" if recent_history: history_text += "Most recent exchange(s):\n" for past_question, past_answer in recent_history: history_text += f"Q: {past_question}\nA: {past_answer}\n\n" if language == "arabic": prompt = f"""أنت مساعد ذكي متخصص في الإجابة على الأسئلة بناءً على النص المعطى فقط. {history_text if history_text else ""} السياق المتاح من الوثيقة: {context} السؤال الحالي: {question} التعليمات: - أجب باللغة العربية فقط - إذا كان السؤال يشير إلى محادثة سابقة (مثل "ذلك" أو "هذا")، استخدم ملخص المحادثة أو آخر تبادل لفهم المرجع - استخدم فقط المعلومات الموجودة في السياق أعلاه - لا تضف أي معلومات غير موجودة في الوثيقة، حتى لو كانت معلومات عامة صحيحة - إذا لم تجد الإجابة، قل بوضوح أنك لا تملك معلومات كافية الإجابة:""" else: prompt = f"""You are a helpful assistant that answers questions based on the provided document context. {history_text if history_text else ""} Document context (in Arabic): {context} Current question: {question} Instructions: - Answer ONLY in English - If the question refers to earlier conversation (like "that" or "it"), use the summary or recent exchange to understand the reference - Use the information found in the context above - If the answer is not in the context, clearly say you don't have enough information - Don't give information which is not in the PDF if something is not there in the PDF simply say you don't have enough information Answer:""" response = llm.invoke(prompt) return response.content def translate_to_arabic(english_text): """ Translates an English question into Arabic using Groq, so we can search our all-Arabic vector store accurately. This fixes cross-lingual retrieval failures on larger, more complex documents. """ prompt = f"""Translate the following English question into Modern Standard Arabic. Only output the Arabic translation, nothing else - no explanation, no quotes. English: {english_text} Arabic translation:""" response = llm.invoke(prompt) return response.content.strip() def summarize_conversation(existing_summary, old_question, old_answer): """ Takes the current running summary plus one older exchange that's about to fall out of the "recent" window, and asks Groq to fold it into an updated, still-short summary. This keeps conversation memory compact regardless of how long the chat gets, instead of sending the full raw history every time. """ prompt = f"""You are maintaining a brief running summary of a conversation between a user and an assistant about a document. Existing summary so far: {existing_summary if existing_summary else "(no summary yet - this is the first exchange to summarize)"} New exchange to fold in: User asked: {old_question} Assistant answered: {old_answer} Update the summary to include this new exchange, staying concise. Keep it to 2-4 sentences maximum. Focus on WHAT TOPICS were discussed and any specific facts/numbers that might be referenced later, not the exact wording. Output ONLY the updated summary, nothing else. Updated summary:""" response = llm.invoke(prompt) return response.content.strip()