Spaces:
Sleeping
Sleeping
Commit ·
c2ab585
1
Parent(s): a771506
feat(ui): opening message in chat
Browse files- SEL/assistant_config.py +4 -0
- app/config/base.py +10 -9
- app/main.py +8 -2
- app/utils/chat.py +8 -8
SEL/assistant_config.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
| 1 |
|
| 2 |
ASSISTANT_MODEL = "gpt-4o-mini"
|
| 3 |
ASSISTANT_NAME = "陪你師展魔法-Coach Chat"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
ASSISTANT_DESCRIPTION = "協助台灣國中小教師運用KIST方法,提供微時刻對話、班級經營建議與親師溝通技巧。"
|
| 5 |
ASSISTANT_INSTRUCTION = """
|
| 6 |
你是一位 **專為台灣公立國中小老師設計的 AI 班級經營顧問/SEL 教練**。
|
|
|
|
| 1 |
|
| 2 |
ASSISTANT_MODEL = "gpt-4o-mini"
|
| 3 |
ASSISTANT_NAME = "陪你師展魔法-Coach Chat"
|
| 4 |
+
ASSISTANT_OPENING = """嗨,我是你的 SEL 陪伴教練。最近在和孩子互動時,有沒有哪個狀況,讓你感到特別困擾或疲憊?
|
| 5 |
+
|
| 6 |
+
如果一時想不到,也可以參考一下這些例子:像是學生說謊、不交作業、上課不專心、頂嘴、冷漠……你覺得哪個最貼近?
|
| 7 |
+
"""
|
| 8 |
ASSISTANT_DESCRIPTION = "協助台灣國中小教師運用KIST方法,提供微時刻對話、班級經營建議與親師溝通技巧。"
|
| 9 |
ASSISTANT_INSTRUCTION = """
|
| 10 |
你是一位 **專為台灣公立國中小老師設計的 AI 班級經營顧問/SEL 教練**。
|
app/config/base.py
CHANGED
|
@@ -12,6 +12,7 @@ class AppName:
|
|
| 12 |
REQUIRED_ATTRIBUTES: Set[str] = {
|
| 13 |
'ASSISTANT_MODEL',
|
| 14 |
'ASSISTANT_NAME',
|
|
|
|
| 15 |
'ASSISTANT_DESCRIPTION',
|
| 16 |
'ASSISTANT_INSTRUCTION',
|
| 17 |
'RESPONSE_FORMAT',
|
|
@@ -30,10 +31,10 @@ CONFIG_MAPPING: Dict[str, str] = {
|
|
| 30 |
def validate_config(config: Type) -> tuple[bool, list[str]]:
|
| 31 |
"""
|
| 32 |
Validate that the config has all required attributes.
|
| 33 |
-
|
| 34 |
Args:
|
| 35 |
config: The configuration module to validate
|
| 36 |
-
|
| 37 |
Returns:
|
| 38 |
tuple[bool, list[str]]: (is_valid, list of missing attributes)
|
| 39 |
"""
|
|
@@ -44,29 +45,29 @@ def get_app_name_and_config() -> tuple[str, Type]:
|
|
| 44 |
"""
|
| 45 |
Get the appropriate configuration module based on APP_NAME.
|
| 46 |
Validates that the config has all required attributes.
|
| 47 |
-
|
| 48 |
Returns:
|
| 49 |
Type: The configuration module
|
| 50 |
-
|
| 51 |
Raises:
|
| 52 |
ImportError: If the config module cannot be imported
|
| 53 |
ValueError: If the config is missing required attributes
|
| 54 |
"""
|
| 55 |
app_name = os.getenv('app_name')
|
| 56 |
config_path = CONFIG_MAPPING.get(app_name, CONFIG_MAPPING['default'])
|
| 57 |
-
|
| 58 |
try:
|
| 59 |
config = importlib.import_module(config_path)
|
| 60 |
except ImportError as e:
|
| 61 |
print(f"Error importing config {config_path}: {e}")
|
| 62 |
config = importlib.import_module(CONFIG_MAPPING['default'])
|
| 63 |
-
|
| 64 |
# Validate the config
|
| 65 |
is_valid, missing_attrs = validate_config(config)
|
| 66 |
if not is_valid:
|
| 67 |
error_msg = f"Config {config_path} is missing required attributes: {missing_attrs}"
|
| 68 |
print(f"Warning: {error_msg}")
|
| 69 |
-
|
| 70 |
# Try to load default config if current config is invalid
|
| 71 |
if config_path != CONFIG_MAPPING['default']:
|
| 72 |
print("Attempting to load default config...")
|
|
@@ -77,5 +78,5 @@ def get_app_name_and_config() -> tuple[str, Type]:
|
|
| 77 |
return default_config
|
| 78 |
else:
|
| 79 |
raise ValueError(error_msg)
|
| 80 |
-
|
| 81 |
-
return app_name, config
|
|
|
|
| 12 |
REQUIRED_ATTRIBUTES: Set[str] = {
|
| 13 |
'ASSISTANT_MODEL',
|
| 14 |
'ASSISTANT_NAME',
|
| 15 |
+
'ASSISTANT_OPENING',
|
| 16 |
'ASSISTANT_DESCRIPTION',
|
| 17 |
'ASSISTANT_INSTRUCTION',
|
| 18 |
'RESPONSE_FORMAT',
|
|
|
|
| 31 |
def validate_config(config: Type) -> tuple[bool, list[str]]:
|
| 32 |
"""
|
| 33 |
Validate that the config has all required attributes.
|
| 34 |
+
|
| 35 |
Args:
|
| 36 |
config: The configuration module to validate
|
| 37 |
+
|
| 38 |
Returns:
|
| 39 |
tuple[bool, list[str]]: (is_valid, list of missing attributes)
|
| 40 |
"""
|
|
|
|
| 45 |
"""
|
| 46 |
Get the appropriate configuration module based on APP_NAME.
|
| 47 |
Validates that the config has all required attributes.
|
| 48 |
+
|
| 49 |
Returns:
|
| 50 |
Type: The configuration module
|
| 51 |
+
|
| 52 |
Raises:
|
| 53 |
ImportError: If the config module cannot be imported
|
| 54 |
ValueError: If the config is missing required attributes
|
| 55 |
"""
|
| 56 |
app_name = os.getenv('app_name')
|
| 57 |
config_path = CONFIG_MAPPING.get(app_name, CONFIG_MAPPING['default'])
|
| 58 |
+
|
| 59 |
try:
|
| 60 |
config = importlib.import_module(config_path)
|
| 61 |
except ImportError as e:
|
| 62 |
print(f"Error importing config {config_path}: {e}")
|
| 63 |
config = importlib.import_module(CONFIG_MAPPING['default'])
|
| 64 |
+
|
| 65 |
# Validate the config
|
| 66 |
is_valid, missing_attrs = validate_config(config)
|
| 67 |
if not is_valid:
|
| 68 |
error_msg = f"Config {config_path} is missing required attributes: {missing_attrs}"
|
| 69 |
print(f"Warning: {error_msg}")
|
| 70 |
+
|
| 71 |
# Try to load default config if current config is invalid
|
| 72 |
if config_path != CONFIG_MAPPING['default']:
|
| 73 |
print("Attempting to load default config...")
|
|
|
|
| 78 |
return default_config
|
| 79 |
else:
|
| 80 |
raise ValueError(error_msg)
|
| 81 |
+
|
| 82 |
+
return app_name, config
|
app/main.py
CHANGED
|
@@ -32,6 +32,7 @@ existed_assistants = client.beta.assistants.list(
|
|
| 32 |
assistant_id = os.getenv('assistant_id')
|
| 33 |
ASSISTANT_MODEL = active_config.ASSISTANT_MODEL
|
| 34 |
ASSISTANT_NAME = active_config.ASSISTANT_NAME
|
|
|
|
| 35 |
ASSISTANT_DESCRIPTION = active_config.ASSISTANT_DESCRIPTION
|
| 36 |
ASSISTANT_INSTRUCTION = active_config.ASSISTANT_INSTRUCTION
|
| 37 |
RESPONSE_FORMAT = active_config.RESPONSE_FORMAT
|
|
@@ -106,7 +107,11 @@ def handle_new_chat(user_id, session_state):
|
|
| 106 |
|
| 107 |
def handle_page_load(username, session_state):
|
| 108 |
"""Handle page load - reset chat and update selector"""
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
# Main UI
|
| 112 |
with gr.Blocks(
|
|
@@ -184,7 +189,8 @@ with gr.Blocks(
|
|
| 184 |
with gr.Column():
|
| 185 |
chatbot.render()
|
| 186 |
prompt_input.render()
|
| 187 |
-
|
|
|
|
| 188 |
hidden_list.render()
|
| 189 |
with gr.Column(elem_classes="full-height", visible=SHOW_CANVAS) as textbox_column:
|
| 190 |
textbox.render()
|
|
|
|
| 32 |
assistant_id = os.getenv('assistant_id')
|
| 33 |
ASSISTANT_MODEL = active_config.ASSISTANT_MODEL
|
| 34 |
ASSISTANT_NAME = active_config.ASSISTANT_NAME
|
| 35 |
+
ASSISTANT_OPENING = active_config.ASSISTANT_OPENING
|
| 36 |
ASSISTANT_DESCRIPTION = active_config.ASSISTANT_DESCRIPTION
|
| 37 |
ASSISTANT_INSTRUCTION = active_config.ASSISTANT_INSTRUCTION
|
| 38 |
RESPONSE_FORMAT = active_config.RESPONSE_FORMAT
|
|
|
|
| 107 |
|
| 108 |
def handle_page_load(username, session_state):
|
| 109 |
"""Handle page load - reset chat and update selector"""
|
| 110 |
+
init_chat = []
|
| 111 |
+
if ASSISTANT_OPENING: # 檢查字串是否非空
|
| 112 |
+
init_chat.append({'role': 'assistant', 'content': ASSISTANT_OPENING})
|
| 113 |
+
|
| 114 |
+
return chat_manager.reset_chat_on_load(username, session_state, init_chat)
|
| 115 |
|
| 116 |
# Main UI
|
| 117 |
with gr.Blocks(
|
|
|
|
| 189 |
with gr.Column():
|
| 190 |
chatbot.render()
|
| 191 |
prompt_input.render()
|
| 192 |
+
# for K camp test path, allow users to start directly
|
| 193 |
+
# quick_response.render()
|
| 194 |
hidden_list.render()
|
| 195 |
with gr.Column(elem_classes="full-height", visible=SHOW_CANVAS) as textbox_column:
|
| 196 |
textbox.render()
|
app/utils/chat.py
CHANGED
|
@@ -35,13 +35,13 @@ class ChatManager:
|
|
| 35 |
current_chat_id = self.create_new_chat(session_state)
|
| 36 |
return current_chat_id
|
| 37 |
|
| 38 |
-
def reset_chat_on_load(self, username: str, session_state) -> Tuple[gr.update, list]:
|
| 39 |
"""Reset chat ID on page load and update chat selector"""
|
| 40 |
self.create_new_chat(session_state)
|
| 41 |
-
|
| 42 |
# Update chat selector with available chats for this user
|
| 43 |
chats = self.list_user_chats(username)
|
| 44 |
-
return gr.update(choices=[(c["preview"], c["chat_id"]) for c in chats], value=self.get_current_chat_id(session_state)),
|
| 45 |
|
| 46 |
def load_chat_history(self, user_id="default_user"):
|
| 47 |
"""Load chat history for a user"""
|
|
@@ -56,13 +56,13 @@ class ChatManager:
|
|
| 56 |
.select("chat_id,last_updated,messages") \
|
| 57 |
.eq("user_id", user_id) \
|
| 58 |
.execute()
|
| 59 |
-
|
| 60 |
chats = [{
|
| 61 |
"chat_id": chat["chat_id"],
|
| 62 |
"last_updated": chat["last_updated"],
|
| 63 |
"preview": chat["messages"][0]["content"] if chat["messages"] else "Empty chat"
|
| 64 |
} for chat in response.data]
|
| 65 |
-
|
| 66 |
return sorted(chats, key=lambda x: x["last_updated"], reverse=True)
|
| 67 |
|
| 68 |
def switch_chat(self, chat_id, user_id="default_user", session_state=None):
|
|
@@ -71,7 +71,7 @@ class ChatManager:
|
|
| 71 |
self.set_current_chat_id(session_state, chat_id)
|
| 72 |
messages = self.load_chat(user_id, chat_id)
|
| 73 |
return messages
|
| 74 |
-
|
| 75 |
def save_chat(self, user_id, chat_id, messages, current_lesson_plan, app_name):
|
| 76 |
"""Save chat history to Supabase"""
|
| 77 |
chat_data = {
|
|
@@ -82,7 +82,7 @@ class ChatManager:
|
|
| 82 |
"current_lesson_plan": current_lesson_plan,
|
| 83 |
"app_name": app_name
|
| 84 |
}
|
| 85 |
-
|
| 86 |
# Check if chat exists
|
| 87 |
existing_chat = self.supabase.table("chats") \
|
| 88 |
.select("*") \
|
|
@@ -110,7 +110,7 @@ class ChatManager:
|
|
| 110 |
.eq("user_id", user_id) \
|
| 111 |
.eq("chat_id", chat_id) \
|
| 112 |
.execute()
|
| 113 |
-
|
| 114 |
return response.data[0]["messages"] if response.data else []
|
| 115 |
|
| 116 |
def get_latest_chat_id(self, user_id):
|
|
|
|
| 35 |
current_chat_id = self.create_new_chat(session_state)
|
| 36 |
return current_chat_id
|
| 37 |
|
| 38 |
+
def reset_chat_on_load(self, username: str, session_state, init_chat=[]) -> Tuple[gr.update, list]:
|
| 39 |
"""Reset chat ID on page load and update chat selector"""
|
| 40 |
self.create_new_chat(session_state)
|
| 41 |
+
|
| 42 |
# Update chat selector with available chats for this user
|
| 43 |
chats = self.list_user_chats(username)
|
| 44 |
+
return gr.update(choices=[(c["preview"], c["chat_id"]) for c in chats], value=self.get_current_chat_id(session_state)), init_chat
|
| 45 |
|
| 46 |
def load_chat_history(self, user_id="default_user"):
|
| 47 |
"""Load chat history for a user"""
|
|
|
|
| 56 |
.select("chat_id,last_updated,messages") \
|
| 57 |
.eq("user_id", user_id) \
|
| 58 |
.execute()
|
| 59 |
+
|
| 60 |
chats = [{
|
| 61 |
"chat_id": chat["chat_id"],
|
| 62 |
"last_updated": chat["last_updated"],
|
| 63 |
"preview": chat["messages"][0]["content"] if chat["messages"] else "Empty chat"
|
| 64 |
} for chat in response.data]
|
| 65 |
+
|
| 66 |
return sorted(chats, key=lambda x: x["last_updated"], reverse=True)
|
| 67 |
|
| 68 |
def switch_chat(self, chat_id, user_id="default_user", session_state=None):
|
|
|
|
| 71 |
self.set_current_chat_id(session_state, chat_id)
|
| 72 |
messages = self.load_chat(user_id, chat_id)
|
| 73 |
return messages
|
| 74 |
+
|
| 75 |
def save_chat(self, user_id, chat_id, messages, current_lesson_plan, app_name):
|
| 76 |
"""Save chat history to Supabase"""
|
| 77 |
chat_data = {
|
|
|
|
| 82 |
"current_lesson_plan": current_lesson_plan,
|
| 83 |
"app_name": app_name
|
| 84 |
}
|
| 85 |
+
|
| 86 |
# Check if chat exists
|
| 87 |
existing_chat = self.supabase.table("chats") \
|
| 88 |
.select("*") \
|
|
|
|
| 110 |
.eq("user_id", user_id) \
|
| 111 |
.eq("chat_id", chat_id) \
|
| 112 |
.execute()
|
| 113 |
+
|
| 114 |
return response.data[0]["messages"] if response.data else []
|
| 115 |
|
| 116 |
def get_latest_chat_id(self, user_id):
|