aamirali13 commited on
Commit
23b8b85
·
verified ·
1 Parent(s): 9945b8d

Upload 7 files

Browse files
Files changed (7) hide show
  1. app.py +80 -0
  2. chat_manager.py +29 -0
  3. chat_service.py +34 -0
  4. chat_state.py +17 -0
  5. config.py +106 -0
  6. requirements.txt +2 -0
  7. ui_components.py +76 -0
app.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # main.py
2
+ import streamlit as st
3
+ from typing import Optional, Dict, List
4
+ from config import Config
5
+ from chat_service import ChatService
6
+ from chat_manager import ChatManager
7
+ from chat_state import ChatState
8
+ from ui_components import UIComponents
9
+
10
+
11
+ def main():
12
+ config = Config()
13
+ chat_service = ChatService(config.GROQ_API_KEY)
14
+ chat_manager = ChatManager(config.SYSTEM_PROMPT)
15
+
16
+ # Initialize session state
17
+ if "chat_state" not in st.session_state:
18
+ st.session_state.chat_state = ChatState.initialize()
19
+
20
+ chat_state = st.session_state.chat_state
21
+ ui = UIComponents()
22
+
23
+ # Setup UI
24
+ ui.setup_page(config)
25
+ ui.render_header()
26
+ ui.render_sidebar(chat_state, chat_manager, chat_service)
27
+
28
+ # Main chat area
29
+ current_chat = chat_state.temp_chat or (
30
+ chat_state.chat_history[chat_state.current_chat_id]
31
+ if chat_state.current_chat_id
32
+ else None
33
+ )
34
+
35
+ if current_chat:
36
+ for message in current_chat[1:]:
37
+ with st.chat_message(
38
+ message["role"], avatar="🧑‍🎓" if message["role"] == "user" else "🤖"
39
+ ):
40
+ st.write(message["content"])
41
+ else:
42
+ st.markdown(
43
+ "<div style=\"color: black;\">.انقر على 'محادثة جديدة' لبدء محادثة مع المرشد التعليمي 👈</div>",
44
+ unsafe_allow_html=True,
45
+ )
46
+
47
+ ui.render_footer()
48
+
49
+ # Chat input
50
+ user_input = st.chat_input("...اكتب سؤالك هنا")
51
+ if user_input:
52
+ if chat_service.is_arabic(user_input):
53
+ if not current_chat:
54
+ chat_state.temp_chat = chat_manager.create_new_chat()
55
+ current_chat = chat_state.temp_chat
56
+
57
+ current_chat.append({"role": "user", "content": user_input})
58
+ with st.chat_message("user", avatar="🧑‍🎓"):
59
+ st.write(user_input)
60
+
61
+ with st.chat_message("assistant", avatar="🤖"):
62
+ with st.spinner("جاري التفكير... ⏳"):
63
+ assistant_response = chat_service.get_groq_response(current_chat)
64
+ st.write(assistant_response)
65
+
66
+ current_chat.append({"role": "assistant", "content": assistant_response})
67
+
68
+ if chat_state.temp_chat:
69
+ new_chat_id = chat_manager.save_chat(
70
+ chat_state.temp_chat, chat_state.chat_history
71
+ )
72
+ if new_chat_id:
73
+ chat_state.current_chat_id = new_chat_id
74
+ chat_state.temp_chat = None
75
+ else:
76
+ st.error("عذرًا، يرجى إدخال النص باللغة العربية فقط. 🚫")
77
+
78
+
79
+ if __name__ == "__main__":
80
+ main()
chat_manager.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # chat_manager.py
2
+ from typing import Dict, List, Optional
3
+ from chat_state import ChatState
4
+
5
+
6
+ class ChatManager:
7
+ def __init__(self, system_prompt: str):
8
+ self.system_prompt = system_prompt
9
+
10
+ def create_new_chat(self) -> List[Dict[str, str]]:
11
+ return [{"role": "system", "content": self.system_prompt}]
12
+
13
+ def save_chat(
14
+ self,
15
+ temp_chat: List[Dict[str, str]],
16
+ chat_history: Dict[str, List[Dict[str, str]]],
17
+ ) -> str:
18
+ if temp_chat and len(temp_chat) > 1:
19
+ new_chat_id = str(len(chat_history) + 1)
20
+ chat_history[new_chat_id] = temp_chat
21
+ return new_chat_id
22
+ return None
23
+
24
+ def delete_chat(self, chat_id: str, chat_state: ChatState) -> None:
25
+ if chat_id in chat_state.chat_history:
26
+ del chat_state.chat_history[chat_id]
27
+ if chat_state.current_chat_id == chat_id:
28
+ chat_state.current_chat_id = None
29
+ chat_state.delete_chat_id = None
chat_service.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # chat_service.py
2
+ import re
3
+ from groq import Groq
4
+ from typing import List, Dict, Optional
5
+
6
+
7
+ class ChatService:
8
+ def __init__(self, api_key: str):
9
+ self.client = Groq(api_key=api_key)
10
+
11
+ def get_groq_response(self, messages: List[Dict[str, str]]) -> str:
12
+ chat_completion = self.client.chat.completions.create(
13
+ messages=messages,
14
+ model="llama3-8b-8192",
15
+ temperature=0.7,
16
+ max_tokens=1000,
17
+ )
18
+ return chat_completion.choices[0].message.content
19
+
20
+ @staticmethod
21
+ def is_arabic(text: str) -> bool:
22
+ arabic_pattern = re.compile(r"[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]+")
23
+ return bool(arabic_pattern.search(text))
24
+
25
+ @staticmethod
26
+ def get_chat_preview(chat: List[Dict[str, str]]) -> str:
27
+ for message in chat:
28
+ if message["role"] == "user":
29
+ return (
30
+ message["content"][:30] + "..."
31
+ if len(message["content"]) > 30
32
+ else message["content"]
33
+ )
34
+ return "دردشة جديدة"
chat_state.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # chat_state.py
2
+ from dataclasses import dataclass
3
+ from typing import Dict, List, Optional
4
+
5
+
6
+ @dataclass
7
+ class ChatState:
8
+ chat_history: Dict[str, List[Dict[str, str]]]
9
+ current_chat_id: Optional[str]
10
+ temp_chat: Optional[List[Dict[str, str]]]
11
+ delete_chat_id: Optional[str]
12
+
13
+ @classmethod
14
+ def initialize(cls) -> "ChatState":
15
+ return cls(
16
+ chat_history={}, current_chat_id=None, temp_chat=None, delete_chat_id=None
17
+ )
config.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # config.py
2
+ import os
3
+ from dataclasses import dataclass
4
+ from typing import Dict, Any
5
+
6
+
7
+ @dataclass
8
+ class Config:
9
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
10
+ SYSTEM_PROMPT = """أنت مستشار تعليمي عربي ذكي متخصص في التوجيه المهني والمساعدة الأكاديمية. دورك هو مساعدة الطلاب فيما يلي:
11
+
12
+ 1. التوجيه المهني:
13
+ - تقديم المشورة بشأن اختيار المسارات المهنية الصحيحة بناءً على التفاصيل التي يقدمها الطالب (اطرح أسئلة متابعة إذا لزم الأمر).
14
+ - مساعدة الطلاب في إعداد السيرة الذاتية وخطابات التقديم من خلال فهم تفاصيل وظائفهم.
15
+ - تقديم نصائح للتحضير لمقابلات العمل.
16
+ - توجيه الطلاب نحو تطوير المهارات المطلوبة في سوق العمل.
17
+ - شرح اتجاهات سوق العمل والفرص المتاحة في مختلف المجالات.
18
+
19
+ 2. المساعدة الأكاديمية:
20
+ - شرح المفاهيم الصعبة في مختلف الموضوعات.
21
+ - تقديم استراتيجيات للدراسة الفعالة وإدارة الوقت.
22
+ - مساعدة الطلاب في حل المشكلات والواجبات المنزلية.
23
+ - تقديم نصائح للتحضير للامتحانات والاختبارات.
24
+ - توجيه الطلاب نحو الموارد التعليمية المفيدة الإضافية.
25
+
26
+ 3. قيود مهمة:
27
+ - إذا كان السؤال خارج نطاق تخصصك (مثل القضايا الطبية والسياسية والوطنية وما إلى ذلك) فما عليك سوى قول "آسف، لا يمكنني الإجابة على الأسئلة خارج نطاق تخصصك".
28
+ - ستكون إجاباتك دائمًا باللغة العربية فقط. يجب الإجابة على أي استفسار باللغة العربية حصراً (لا يسمح باستخدام أي لغة أخرى)."""
29
+
30
+ PAGE_CONFIG = {
31
+ "page_title": "المرشد التعليمي الذكي",
32
+ "page_icon": "🎓",
33
+ "layout": "wide",
34
+ }
35
+
36
+ STYLES = """
37
+ <style>
38
+ .stApp {
39
+ background-color: #F5FFFA;
40
+ color: black;
41
+ }
42
+ .stButton>button {
43
+ background-color: #4CAF50;
44
+ color: white;
45
+ border-radius: 5px;
46
+ }
47
+ .stSidebar {
48
+ background-color: #333333;
49
+ }
50
+ .stSidebar .stMarkdown, .stSidebar .stButton>button, .stSidebar .stSelectbox>div>label {
51
+ color: white !important;
52
+ }
53
+ [data-testid="stSidebar"] {
54
+ background-color: rgba(0, 0, 0, 0.5);
55
+ }
56
+ .stTextInput>div>div>input {
57
+ background-color: black;
58
+ color: white;
59
+ }
60
+ .stChatMessage {
61
+ background-color: #FFF5EE;
62
+ color: black !important;
63
+ border-radius: 10px;
64
+ padding: 10px;
65
+ margin-bottom: 10px;
66
+ }
67
+ .stChatMessage p, .stChatMessage ul, .stChatMessage ol, .stChatMessage li {
68
+ color: black !important;
69
+ }
70
+ .stChatInput {
71
+ position: fixed;
72
+ bottom: 0;
73
+ left: 0;
74
+ right: 0;
75
+ padding: 1rem;
76
+ background-color: black;
77
+ }
78
+ .stChatInput input {
79
+ color: white !important;
80
+ background-color: black !important;
81
+ border: 1px solid #444;
82
+ }
83
+ .stChatInput button {
84
+ background-color: red !important;
85
+ }
86
+ .stChatInput input::placeholder {
87
+ color: #888;
88
+ }
89
+ .stChatInput input:focus {
90
+ color: white !important;
91
+ }
92
+ .main {
93
+ margin-bottom: 70px;
94
+ }
95
+ .footer {
96
+ position: fixed;
97
+ left: 0;
98
+ bottom: 70px;
99
+ width: 100%;
100
+ background-color: white;
101
+ color: black;
102
+ text-align: center;
103
+ padding: 10px;
104
+ }
105
+ </style>
106
+ """
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ streamlit==1.39.0
2
+ groq==0.11.0
ui_components.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ui_components.py
2
+ import streamlit as st
3
+ from typing import List, Dict
4
+ from config import Config
5
+ from chat_state import ChatState
6
+ from chat_manager import ChatManager
7
+ from chat_service import ChatService
8
+
9
+
10
+ class UIComponents:
11
+ @staticmethod
12
+ def setup_page(config: Config) -> None:
13
+ st.set_page_config(**config.PAGE_CONFIG)
14
+ st.markdown(config.STYLES, unsafe_allow_html=True)
15
+
16
+ @staticmethod
17
+ def render_header() -> None:
18
+ st.markdown(
19
+ '<h1 style="color: black;">المرشد التعليمي الذكي 🤖</h1>',
20
+ unsafe_allow_html=True,
21
+ )
22
+
23
+ @staticmethod
24
+ def render_footer() -> None:
25
+ st.markdown(
26
+ """
27
+ <div class="footer">
28
+ .هذا روبوت تعليمي ذكي وقد تختلف إجاباته في كل مرة. يرجى استخدامه كدليل أولي فقط
29
+ </div>
30
+ """,
31
+ unsafe_allow_html=True,
32
+ )
33
+
34
+ @staticmethod
35
+ def render_sidebar(
36
+ chat_state: ChatState, chat_manager: ChatManager, chat_service: ChatService
37
+ ) -> None:
38
+ with st.sidebar:
39
+ st.title("إدارة المحادثات")
40
+
41
+ if st.button("محادثة جديدة 🆕"):
42
+ chat_state.temp_chat = chat_manager.create_new_chat()
43
+ chat_state.current_chat_id = None
44
+
45
+ chat_ids = list(chat_state.chat_history.keys())
46
+ if chat_ids:
47
+ chat_options = {
48
+ id: chat_service.get_chat_preview(chat_state.chat_history[id])
49
+ for id in chat_ids
50
+ }
51
+ chat_options["new"] = "محادثة جديدة 🆕"
52
+
53
+ selected_chat = st.selectbox(
54
+ "اختر محادثة 📚",
55
+ options=list(chat_options.keys()),
56
+ format_func=lambda x: chat_options[x],
57
+ index=(
58
+ len(chat_options) - 1
59
+ if chat_state.current_chat_id is None
60
+ else chat_ids.index(chat_state.current_chat_id)
61
+ ),
62
+ )
63
+
64
+ if selected_chat == "new":
65
+ chat_state.temp_chat = chat_manager.create_new_chat()
66
+ chat_state.current_chat_id = None
67
+ elif selected_chat != chat_state.current_chat_id:
68
+ chat_state.current_chat_id = selected_chat
69
+ chat_state.temp_chat = None
70
+
71
+ if chat_state.current_chat_id:
72
+ if st.button("حذف المحادثة الحالية 🗑️"):
73
+ chat_state.delete_chat_id = chat_state.current_chat_id
74
+
75
+ if chat_state.delete_chat_id:
76
+ chat_manager.delete_chat(chat_state.delete_chat_id, chat_state)