iman1377/teacher
0
1import streamlit as st2import os3import tempfile4 5from langchain_community.document_loaders import PyPDFLoader6from langchain_text_splitters import RecursiveCharacterTextSplitter7from langchain_huggingface import HuggingFaceEmbeddings8from langchain_community.vectorstores import FAISS9 10from langchain_groq import ChatGroq11 12from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder13from langchain_core.chat_history import InMemoryChatMessageHistory14from langchain_core.runnables import RunnableWithMessageHistory15 16# ---------------------------------------------------17# Streamlit UI18# ---------------------------------------------------19st.set_page_config(page_title="استاد MIT", layout="wide")20 21st.markdown("""22<style>23 .stChatMessage { direction: rtl; text-align: right; }24 .stMarkdown { direction: rtl; text-align: right; }25 .stMarkdown > div > p { direction: rtl; text-align: right; }26 .stSpinner { direction: rtl; text-align: right; }27 p { direction: rtl; text-align: right; }28 h1, h2, h3, h4, h5, h6 { direction: rtl; text-align: right; }29 li { direction: rtl; text-align: right; }30 code { direction: ltr; text-align: left; unicode-bidi: embed; } /* برای کدهای انگلیسی LTR نگه داریم */31</style>32""", unsafe_allow_html=True)33 34st.title("🎓 استاد خصوصی کنکور ارشد")35 36with st.sidebar:37 api_key = st.text_input("Groq API Key", type="password")38 uploaded_file = st.file_uploader("کتاب PDF را آپلود کنید", type="pdf")39 40 if st.button("پاک کردن حافظه گفتگو"):41 st.session_state.histories = {}42 st.rerun()43 44# ---------------------------------------------------45# PDF → Vectorstore46# ---------------------------------------------------47@st.cache_resource48def process_pdf(file):49 with tempfile.NamedTemporaryFile(delete=False) as tmp:50 tmp.write(file.getvalue())51 temp_path = tmp.name52 53 loader = PyPDFLoader(temp_path)54 docs = loader.load()55 56 # خرد کردن متن PDF57 splitter = RecursiveCharacterTextSplitter(58 chunk_size=1000,59 chunk_overlap=15060 )61 chunks = splitter.split_documents(docs)62 63 # پاکسازی متنها از newline64 for doc in chunks:65 doc.page_content = doc.page_content.replace("\n", " ")66 67 embeddings = HuggingFaceEmbeddings(68 model_name="sentence-transformers/all-MiniLM-L6-v2"69 )70 71 vectorstore = FAISS.from_documents(chunks, embeddings)72 os.remove(temp_path)73 return vectorstore74 75# ---------------------------------------------------76# حافظه گفتگو77# ---------------------------------------------------78if "histories" not in st.session_state:79 st.session_state.histories = {}80 81def get_history(session_id):82 if session_id not in st.session_state.histories:83 st.session_state.histories[session_id] = InMemoryChatMessageHistory()84 return st.session_state.histories[session_id]85 86# ---------------------------------------------------87# Prompt Template حرفهای88# ---------------------------------------------------89prompt = ChatPromptTemplate.from_messages([90 ("system",91 """92You are a top-tier MIT professor AND an Iranian Konkur (entrance exam) instructor.93Your mission is TEACHING, not just answering and translating.94 95You must ALWAYS:961. Detect and analyze examples inside the retrieved PDF context.972. If the context contains an example, solve it step-by-step like a Konkur teacher.983. Extract formulas, definitions, and key points.994. Warn the student about common misconceptions and traps.1005. Produce 1–3 NEW similar practice problems with answers.1016. Use Persian for teaching. Use English only for technical terms.1027. When answering:103 - بخش ۱: خلاصه مفهوم اصلی104 - بخش ۲: تحلیل خط به خط محتوای PDF مربوطه105 - بخش ۳: تحلیل کامل مثالهای موجود در PDF106 - بخش ۴: مثالهای جدید مشابه برای تمرین107 - بخش ۵: نکات کنکوری، دامها، روش میانبر108 109Your teaching style must be:110- precise111- structured112- exam-oriented113- clear and deep114 115CONTEXT FROM BOOK:116{context}117"""),118 MessagesPlaceholder(variable_name="chat_history"),119 ("user",120 """121پرسش دانشجو:122{question}123""")124])125 126# ---------------------------------------------------127# ساخت chain RAG + LLM128# ---------------------------------------------------129def build_chain(vectorstore, api_key):130 retriever = vectorstore.as_retriever(search_kwargs={"k": 4})131 132 llm = ChatGroq(133 groq_api_key=api_key,134 model_name="openai/gpt-oss-120b",135 temperature=0.3136 )137 138 chain = (139 {140 "context": lambda x: "\n\n".join(141 doc.page_content for doc in retriever.invoke(x["question"])142 ),143 "question": lambda x: x["question"],144 "chat_history": lambda x: x["chat_history"]145 }146 | prompt147 | llm148 )149 150 return RunnableWithMessageHistory(151 chain,152 get_history,153 input_messages_key="question",154 history_messages_key="chat_history"155 )156 157# ---------------------------------------------------158# اجرای چت159# ---------------------------------------------------160if uploaded_file and api_key:161 162 vectorstore = process_pdf(uploaded_file)163 chat = build_chain(vectorstore, api_key)164 165 st.success("کتاب پردازش شد. سوال خود را بپرسید.")166 167 session_id = "student"168 history = get_history(session_id)169 170 # نمایش تاریخچه چت171 for msg in history.messages:172 with st.chat_message("assistant" if msg.type == "ai" else "user"):173 st.markdown(msg.content, unsafe_allow_html=True) # از markdown برای پشتیبانی بهتر RTL استفاده کن174 175 # دریافت سوال جدید176 if question := st.chat_input("سوال خود را بپرسید..."):177 with st.chat_message("user"):178 st.write(question)179 180 with st.chat_message("assistant"):181 with st.spinner("در حال فکر کردن..."):182 result = chat.invoke(183 {"question": question},184 config={"configurable": {"session_id": session_id}}185 )186 st.markdown(result.content, unsafe_allow_html=True) # از markdown برای RTL بهتر187 188else:189 st.info("لطفاً API Key و PDF را وارد کنید.")