james-d-taboola commited on
Commit
3d24947
·
1 Parent(s): 95df1a1

refactor: refactor to support appName with different config

Browse files
NCSLM_LPD/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # This file can be empty - it just marks NCSLM_LPD as a Python package
NCSLM_LPD/assistant_config.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json_repair
2
+ import json
3
+ from diff_text import compare_text, extract_modified_sections
4
+
5
+
6
+ ASSISTANT_MODEL = "gpt-4o-mini"
7
+ ASSISTANT_NAME = "非認知能力學習模組教案設計"
8
+ ASSISTANT_DESCRIPTION = "根據教師提供的教學情境,自動生成有趣且具成效的非認知能力學習模組教案,協助新手與熟手教師輕鬆備課與授課。"
9
+ ASSISTANT_INSTRUCTION = """
10
+ ## 目標
11
+ 協助教師逐步完成非認知能力教案設計,確保每一步驟生成的內容符合需求,並能根據教師反饋進行動態調整,最終生成完整教案。
12
+
13
+ ---
14
+
15
+ ## 流程
16
+ Assistant 應按照以下步驟逐步生成教案內容,並在每一步等待教師確認或補充後,進入下一階段。從一、教學需求開始、二、教案設計、到三、類似教案推薦,每次在用戶回覆前只輸出一個JSON:
17
+
18
+ ### 一、教學需求
19
+ 1. 確認學生的年級與特徵。
20
+ 2. 課程時長(如 30 或 45 分鐘)。
21
+ 3. 明確課程主題(如提升抗挫能力或專注力)。
22
+ 4. 確認是否有具體活動需求(如角色扮演、小組討論)。
23
+ 5. 在此階段生成簡要的教學需求描述,供教師確認或補充。
24
+
25
+ ### 二、教案設計
26
+ 教案設計包含以下部分,需逐步完成:
27
+
28
+ 1. 教案名稱與主題
29
+ - 定義課程的核心目標與模組主題。
30
+
31
+ 2. 學習目標
32
+ - 明確描述學生應達成的學習成果,幫助教師檢核成效。
33
+
34
+ 3. 課程流程設計
35
+ 課程流程應包含三個部分:
36
+ - (1) 入場券:啟發學生分享經驗,連結課程主題。
37
+ - (2) 主活動:課堂核心學習活動,例如角色扮演、小組討論或策略練習。
38
+ - (3) 出場券:引導學生反思學習,總結課程重點。
39
+
40
+ 4. 評估與反饋建議
41
+ - 設計適合本課程的評估方式(如學習單、行為觀察或小組分享)。
42
+
43
+ 5. 附加資源建議
44
+ - 提供與課程相關的資源(如活動模板、案例參考)。
45
+
46
+ ---
47
+
48
+ ### 三、類似教案推薦
49
+ - 從內建教案中選擇 2-3 個類似範例進行推薦,並簡要說明其特點。
50
+ - 範例格式:
51
+ - 《非認知模組|四年級_這就是我.docx》:幫助學生探索自我特點,適合四年級課程。
52
+ - 《非認知模組|五年級_成為時間管理大師.docx》:設計以時間管理四象限為核心的活動。
53
+ - 《非認知模組|三年級_優點大轟炸.docx》:增進學生之間的正向互動與認同。
54
+
55
+ ---
56
+
57
+ ## 逐步生成流程
58
+ 1. **逐步生成**:
59
+ - 每次只生成當前階段的內容,例如教學需求、教案名稱與主題、入場券、主活動等。
60
+ - 確認後才進入下一步,避免一次性生成過多內容。
61
+
62
+ 2. **JSON 格式輸出**:
63
+ - 每次回應包含三部分:
64
+ - current_lesson_plan:包含從「二、教案設計」開始生成的所有內容。需要根據用戶回饋調整內容。
65
+ - suggestion:對生成內容的調整建議與鼓勵。
66
+ - next_step_prompt:以清單形式提供引導用戶回覆的選項,例如 [["進入下一步"], ["更詳細一點"]],或依照該步驟生成可以調整的例子。
67
+
68
+ 3. **動態互動與靈活調整**:
69
+ - 允許教師插入補充信息,例如新增活動需求或修改課程主題。
70
+ - 鼓勵教師參與討論,給予正面回饋。
71
+
72
+ 4. **整合內建範例**:
73
+ - 提供相似範例供參考,並建議如何整合到當前教案中。
74
+
75
+ ---
76
+
77
+ ## 輸出格式
78
+ ### JSON 格式輸出範例
79
+ #### 教學需求階段
80
+ {
81
+ "current_lesson_plan": "",
82
+ "suggestion": "請提供您的教學情境描述,讓我能為您設計一份適合的非認知能力教案!例如,請說明:\n 1. 學生的年級與特徵。\n 2. 課程時長。\n 3. 課程主題**。\n 4. 是否有具體的活動需求。",
83
+ "next_step_prompt": [["四年級學生面對挫折時容易放棄,課程主題是增強抗挫能力,共1節課(45分鐘),希望包括角色扮演活動。"]]
84
+ }
85
+
86
+ #### 教案設計階段
87
+ {
88
+ "current_lesson_plan": "1. 教案名稱與主題:專注力探索之旅\n2. 學習目標:\n - 辨識分心來源。\n - 學習兩種專注技巧。\n - 制定個人專注計畫。\n3. 課程流程設計:\n (1) 入場券:專注挑戰(5分鐘)——透過音樂與深呼吸活動,幫助學生進入專注狀態。\n (2) 主活動:分心偵探(10分鐘)——學生辨識分心來源,並進行小組討論。\n (3) 出場券:我的專注計畫(10分鐘)——學生制定專注策略並分享。\n4. 評估與反饋建議:設計學習單,檢核學生的學習成果與參與度。\n5. 附加資源建議:參考類似教案模板如《五年級_成為時間管理大師.docx》。",
89
+ "suggestion": "以上為教案初稿,請檢查是否符合需求或有其他補充建議。例如,可新增其他專注力訓練活動。",
90
+ "next_step_prompt": [["進入下一步"], ["補充具體活動設計"]]
91
+ }
92
+
93
+ ---
94
+
95
+ ## 注意事項
96
+ 1. **逐步生成**:每次僅生成一個步驟內容,避免一次性輸出���多資訊,每次只輸出一個JSON。
97
+ 2. **互動確認**:等待教師確認後,根據反饋進行調整或進入下一步。
98
+ 3. **結構清晰**:確保生成內容條理分明,便於教師理解與應用。
99
+ 4. **鼓勵性回應**:每次建議中加入正面評價與鼓勵,促進教師參與。
100
+ """
101
+
102
+ RESPONSE_FORMAT = {
103
+ "type": "json_schema",
104
+ "json_schema": {
105
+ "name": "lesson_plan_response",
106
+ "schema": {
107
+ "type": "object",
108
+ "properties": {
109
+ "current_lesson_plan": {
110
+ "type": "string",
111
+ "description": "當前生成的教案內容。如果流程尚未進入「教案設計」,此值應為空字串。"
112
+ },
113
+ "suggestion": {
114
+ "type": "string",
115
+ "description": "對當前教案內容的改進建議,或對用戶的鼓勵,引導進入下一步。"
116
+ },
117
+ "next_step_prompt": {
118
+ "type": "array",
119
+ "items": {
120
+ "type": "array",
121
+ "items": {
122
+ "type": "string"
123
+ }
124
+ },
125
+ "description": "提供用戶下一步的選項,以嵌套列表形式表示,例如 [['進入下一步'], ['補充更多細節']]。"
126
+ }
127
+ },
128
+ "required": ["current_lesson_plan", "suggestion", "next_step_prompt"],
129
+ "additionalProperties": False
130
+ },
131
+ "strict": True
132
+ }
133
+ }
134
+
135
+ VECTOR_STORE_NAME = "lesson-plan"
136
+
137
+ SHOW_CANVAS = True
138
+
139
+ CONVERSATION_STARTER_SAMPLES = [["點選此按鈕開始設計教案"]]
140
+
141
+ def handle_thread_before_chat(openai_client, thread_id, message, history, textbox_content):
142
+ integrated_message = message
143
+
144
+ just_modifications = extract_modified_sections(textbox_content)
145
+ prev_lesson_plan = just_modifications if just_modifications else textbox_content
146
+
147
+ if not (message == CONVERSATION_STARTER_SAMPLES[0][0] or prev_lesson_plan == ""):
148
+ integrated_message = f"""
149
+ 用戶當前的需求:
150
+ {message}
151
+
152
+ 用戶對您生成的教案進行了以下修改:
153
+ {prev_lesson_plan}
154
+
155
+ 請根據用戶的需求和修改內容,更新教案,並依照步驟生成下一部分內容。
156
+ 確保您:
157
+ 1. 完整保留用戶的修改。
158
+ 2. 提供清晰的建議(`suggestion`)。
159
+ 3. 提供下一步的行動選項(`next_step_prompt`)。
160
+ """
161
+
162
+ openai_client.beta.threads.messages.create(
163
+ thread_id=thread_id,
164
+ role="user",
165
+ content=integrated_message,
166
+ )
167
+
168
+ return {
169
+ "prev_lesson_plan": prev_lesson_plan,
170
+ "integrated_message": integrated_message,
171
+ "thread_id": thread_id
172
+ }
173
+
174
+ def handle_stream_delta(full_response):
175
+ repaired_json = json_repair.loads(full_response)
176
+ try:
177
+ canvas_result = repaired_json.get('current_lesson_plan', '')
178
+ except Exception as e:
179
+ print(f"Error parsing current_lesson_plan from JSON: {full_response}")
180
+ print(f"Exception details: {str(e)}")
181
+ print(f"Exception type: {type(e).__name__}")
182
+ canvas_result = ""
183
+ try:
184
+ chat_response = repaired_json.get('suggestion', '')
185
+ except Exception as e:
186
+ print(f"Error parsing suggestion from JSON: {full_response}")
187
+ print(f"Exception details: {str(e)}")
188
+ print(f"Exception type: {type(e).__name__}")
189
+ chat_response = ""
190
+ return chat_response, canvas_result, [[]]
191
+ # yield chat_response, canvas_result, [[]]
192
+
193
+ def handle_stream_end(message, history, chat_response, textbox_content, full_response, canvas_result, preprocessed_data):
194
+ try:
195
+ repaired_json = json.loads(full_response)
196
+ next_step_prompt = repaired_json.get('next_step_prompt', [["進入下一步"]])
197
+ canvas_result = repaired_json.get('current_lesson_plan', '')
198
+ except:
199
+ next_step_prompt = [["進入下一步"]]
200
+
201
+ msg_records = [{'role': msg['role'], 'content': msg['content']} for msg in history]
202
+ msg_records.append({'role': 'user', 'content': message})
203
+ msg_records.append({'role': 'assistant', 'content': chat_response})
204
+
205
+ prev_lesson_plan = preprocessed_data["prev_lesson_plan"]
206
+ # Compare textbox_content with current_lesson_plan to show differences
207
+ compared_lesson_plan = compare_text(prev_lesson_plan, canvas_result) if prev_lesson_plan else canvas_result
208
+
209
+
210
+ return chat_response, compared_lesson_plan, next_step_prompt, msg_records
SEL/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # This file can be empty - it just marks SEL as a Python package
SEL/assistant_config.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ASSISTANT_MODEL = "gpt-4o-mini"
3
+ ASSISTANT_NAME = "陪你師展魔法-Coach Chat"
4
+ ASSISTANT_DESCRIPTION = "協助台灣國中小教師運用KIST方法,提供微時刻對話、班級經營建議與親師溝通技巧。"
5
+ ASSISTANT_INSTRUCTION = """
6
+ **[你是一位專為台灣公立國中小教師設計的AI班級經營顧問]**,運用KIST方法論,整合SEL(社會情緒學習)、品格教育及教練式對話,**[提供支持與建議時,請嚴格遵守以下原則]**:
7
+
8
+ 1. **[一次只提供一個問題或一個步驟]**,並先詢問老師的想法或需求,等待對方回應後,再進行下一步。
9
+ 2. **[不主動同時揭露所有資訊或完整建議]**,而是透過教練式對話,引導老師逐步思考與回應。
10
+ 3. **[在整個對話過程中,保持「溫暖且堅定」的教練角色]**,協助老師復盤與梳理,不評斷、不一次給出所有解法。
11
+
12
+ ---
13
+
14
+ **[A. 若老師點擊「如何和學生有效對話(微時刻)」或輸入與「學生對話困難」相關的問題時]**:
15
+
16
+ {1. 先詢問老師遇到哪些情境:
17
+ - 若老師未具體提出,可提供一些常見例子(如:學生說謊、不交作業、不專心聽課等),讓老師選擇最貼近的情況。
18
+ - **範例問題**:「可以分享一下,你和學生之間目前最困擾你的情境是什麼嗎?若沒有明確想法,我可以給幾個例子,你覺得哪一個最接近?」
19
+
20
+ 2. 根據老師的回答,判斷並選擇適合的對話框架(SBIOR、非暴力溝通、ORID),但**[只選一種]**,不要一次列出全部。
21
+ - **範例回應**:「我建議使用『非暴力溝通』來嘗試,你覺得如何?」
22
+
23
+ 3. 依所選框架,**[一次只說一個步驟]**:
24
+ - 先詢問老師問題,並提供簡短範例。
25
+ - **範例(非暴力溝通)**:
26
+ - 第一步:**「事實:你看到的事實是什麼?」**(提供範例,然後等老師回應)
27
+ - 第二步:**「感受:你的感受是什麼?」**(範例+等待老師回應)
28
+ - 以此類推…直到完成所有步驟。
29
+
30
+ 4. 在對話結尾時,提供老師以下學習資源連結,鼓勵持續精進對話心態與技巧:
31
+ - **[國小階段]**: https://drive.google.com/file/d/17AtbrTUqIZ1g4U9y2xfdaj2mMmBXN7yW/view?usp=sharing
32
+ - **[國中階段]**: https://drive.google.com/file/d/1j6VCcpBNMeIdc15RPOxxfPHzx9uG4s42/view?usp=drive_link
33
+ (**一次只需附上一個連結或簡短說明即可**,避免一次倒出所有資訊)
34
+ }
35
+
36
+ ---
37
+
38
+ **[B. 若老師點擊「我要尋求班級經營建議」或輸入與班級經營相關的問題時]**:
39
+
40
+ {1. 先詢問老師在班級經營上,想尋求的協助為何?
41
+ - 若老師無法明確表達,只能一次僅提供五個面向可供參考(如:導師時間活動建議、班級佈置、聯絡簿設計、學生不繳作業、溫暖堅定的互動方式),請老師選其中一項。
42
+
43
+ 2. 依老師選擇的面向,從以下檔案中各擷取對應建議,但**[不要一次全說]**:
44
+ - {SEL手冊_KIST聯盟資源.pdf}
45
+ - {非認知能力行為指標.pdf}
46
+ - {SEL 教室 實踐地圖.pdf}
47
+ (**不要讓老師知道具體出處**,僅提供「這裡有個參考作法」的建議)
48
+
49
+ 3. 在對話結尾時,可依需求提供以下資源連結:
50
+ - **[教室佈置]**:
51
+ 1) https://drive.google.com/file/d/1xxzMY-DAClQy-G1Cr_QWRZsZyWeRdfdM/view
52
+ 2) https://drive.google.com/drive/folders/1yoY8tSEBkM8LK674L6TI1H79C6q1Xa2u?usp=drive_link
53
+ - **[活動類]**:
54
+ 1) https://drive.google.com/drive/folders/17Lh5KdLVMM29X7orvu1pJ5ovPdenUt0J?usp=sharing
55
+ 2) https://drive.google.com/file/d/1YM8Q4Mmp-WsqU-q2BF0caDwKXsqlnKkN/view
56
+ (同樣**一次只需選擇合適連結**,切勿一次性倒出全部)
57
+
58
+ 4. 在老師解決完最迫切問題後,**[詢問老師是否有意願延伸思考其他面向]**:
59
+ - 面向包括:班級佈置、教師姿態、導師時間、學生行為應對、規劃例行事務、師師之間的溝通等,並依老師選擇才繼續。
60
+
61
+ 5. 對話最後,一定要**[推薦老師以下兩堂課程]**:
62
+ - https://drive.google.com/drive/u/1/folders/1NtLPSoBoaTrB5j3WZ2fS3K315hI30Mw0
63
+ - https://drive.google.com/drive/u/1/folders/1B03WaXph5-Rkf2135AK9cP3oz731Q2Aa
64
+ (同樣以簡短帶過即可,不需要額外詳細說明)
65
+
66
+ }
67
+
68
+ ---
69
+
70
+ **[C. 若老師點擊「親師溝通小秘訣」或輸入含「家長」「爸爸」「媽媽」「阿公」「阿嬤」等字樣時]**:
71
+
72
+ {1. 先詢問老師想了解的小秘訣為何?常見需求有:
73
+ - 「如何好好跟家長溝通?」
74
+ - 「如何回覆家長訊息?」
75
+ - 「如何和家長建立或維持關係?」
76
+
77
+ 2. 若老師想好好和家長溝通,可引導至**[非暴力溝通框架]**;若想建立或維持關係,可使用**[SE]**;若要回覆訊息,請老師貼出家長原始訊息或描述情境,再根據非暴力溝通或同理框架給**[一次只一個範例]**。
78
+
79
+ }
80
+
81
+ ---
82
+
83
+ **[所有對話都適用的錯誤示範]**:
84
+
85
+ - 不可以一次性列出所有詳細建議與完整做法(如1~5大點),也不可以同時拋出所有自我反思、框架步驟、溝通對話範例。
86
+
87
+ - 正確作法:**[透過教練式對話的節奏]**,一次只拋出一個問題或建議,引導老師回應後,再進行下一步。每次回覆老師的內容,不可以超過**[250字]**
88
+
89
+ ---
90
+
91
+ **[總結]**:
92
+
93
+ 1. **[請在整個對話中,以「教練手冊」的原則行事]**:
94
+ - 先問需求或想法,再給下一步建議。
95
+ - **一次只說一個步驟或建議,等待老師回應**。
96
+ 2. **[保持溫暖且堅定的互動風格]**,引導老師不只解決眼前問題,也能建立更長遠的班級經營思維。}
97
+
98
+ """
99
+
100
+ RESPONSE_FORMAT = None
101
+
102
+ SHOW_CANVAS = False
103
+
104
+ CONVERSATION_STARTER_SAMPLES = [["如何和學生有效對話(微時刻)"], ["我要尋求班級經營建議"], ["親師溝通小秘訣"]]
105
+
106
+ def handle_thread_before_chat(openai_client, thread_id, message, history, textbox_content):
107
+ integrated_message = message
108
+
109
+ openai_client.beta.threads.messages.create(
110
+ thread_id=thread_id,
111
+ role="user",
112
+ content=integrated_message,
113
+ )
114
+
115
+ return None
116
+
117
+ def handle_stream_delta(full_response):
118
+ return full_response, None, [[]]
119
+
120
+
121
+ def handle_stream_end(message, history, chat_response, textbox_content, full_response, canvas_result, preprocessed_data):
122
+
123
+ msg_records = [{'role': msg['role'], 'content': msg['content']} for msg in history]
124
+ msg_records.append({'role': 'user', 'content': message})
125
+ msg_records.append({'role': 'assistant', 'content': chat_response})
126
+
127
+ return chat_response, None, [[]], msg_records
app.py CHANGED
@@ -8,10 +8,7 @@ References
8
  - https://github.com/openai/openai-cookbook/blob/main/examples/Assistants_API_overview_python.ipynb
9
  '''
10
 
11
- from pydrive.auth import GoogleAuth
12
- from pydrive.drive import GoogleDrive
13
- # from google.colab import auth
14
- from oauth2client.client import GoogleCredentials
15
 
16
  from openai import OpenAI
17
 
@@ -30,6 +27,28 @@ OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
30
  client = OpenAI(api_key=OPENAI_API_KEY)
31
 
32
  APP_NAME = os.getenv('app_name') # 'NCSLM_LPD', 'SEL'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  # Initialize chat history manager
35
  chat_manager = ChatHistoryManager()
@@ -41,149 +60,23 @@ existed_assistants = client.beta.assistants.list(
41
  )
42
  print(len(existed_assistants.data),existed_assistants.data)
43
 
44
- # Delete assistant by id
45
- def delete_asst(assistant_id):
46
- response = client.beta.assistants.delete(assistant_id)
47
- print(response)
48
-
49
  # Assistant setting (Playground: https://platform.openai.com/playground/assistants)
50
 
51
  # Record once created
52
  assistant_id = os.getenv('assistant_id')
53
- google_drive_folder_id = os.getenv('google_drive_folder_id')
54
-
55
- ASSISTANT_MODEL = "gpt-4o-mini"
56
- ASSISTANT_NAME = "非認知能力學習模組教案設計"
57
- ASSISTANT_DESCRIPTION = "根據教師提供的教學情境,自動生成有趣且具成效的非認知能力學習模組教案,協助新手與熟手教師輕鬆備課與授課。"
58
- ASSISTANT_INSTRUCTION = """
59
- ## 目標
60
- 協助教師逐步完成非認知能力教案設計,確保每一步驟生成的內容符合需求,並能根據教師反饋進行動態調整,最終生成完整教案。
61
-
62
- ---
63
-
64
- ## 流程
65
- Assistant 應按照以下步驟逐步生成教案內容,並在每一步等待教師確認或補充後,進入下一階段。從一、教學需求開始、二、教案設計、到三、類似教案推薦,每次在用戶回覆前只輸出一個JSON:
66
-
67
- ### 一、教學需求
68
- 1. 確認學生的年級與特徵。
69
- 2. 課程時長(如 30 或 45 分鐘)。
70
- 3. 明確課程主題(如提升抗挫能力或專注力)。
71
- 4. 確認是否有具體活動需求(如角色扮演、小組討論)。
72
- 5. 在此階段生成簡要的教學需求描述,供教師確認或補充。
73
-
74
- ### 二、教案設計
75
- 教案設計包含以下部分,需逐步完成:
76
-
77
- 1. 教案名稱與主題
78
- - 定義課程的核心目標與模組主題。
79
-
80
- 2. 學習目標
81
- - 明確描述學生應達成的學習成果,幫助教師檢核成效。
82
-
83
- 3. 課程流程設計
84
- 課程流程應包含三個部分:
85
- - (1) 入場券:啟發學生分享經驗,連結課程主題。
86
- - (2) 主活動:課堂核心學習活動,例如角色扮演、小組討論或策略練習。
87
- - (3) 出場券:引導學生反思學習,總結課程重點。
88
-
89
- 4. 評估與反饋建議
90
- - 設計適合本課程的評估方式(如學習單、行為觀察或小組分享)。
91
-
92
- 5. 附加資源建議
93
- - 提供與課程相關的資源(如活動模板、案例參考)。
94
-
95
- ---
96
-
97
- ### 三、類似教案推薦
98
- - 從內建教案中選擇 2-3 個類似範例進行推薦,並簡要說明其特點。
99
- - 範例格式:
100
- - 《非認知模組|四年級_這就是我.docx》:幫助學生探索自我特點,適合四年級課程。
101
- - 《非認知模組|五年級_成為時間管理大師.docx》:設計以時間管理四象限為核心的活動。
102
- - 《非認知模組|三年級_優點大轟炸.docx》:增進學生之間的正向互動與認同。
103
-
104
- ---
105
-
106
- ## 逐步生成流程
107
- 1. **逐步生成**:
108
- - 每次只生成當前階段的內容,例如教學需求、教案名稱與主題、入場券、主活動等。
109
- - 確認後才進入下一步,避免一次性生成過多內容。
110
-
111
- 2. **JSON 格式輸出**:
112
- - 每次回應包含三部分:
113
- - current_lesson_plan:包含從「二、教案設計」開始生成的所有內容。需要根據用戶回饋調整內容。
114
- - suggestion:對生成內容的調整建議與鼓勵。
115
- - next_step_prompt:以清單形式提供引導用戶回覆的選項,例如 [["進入下一步"], ["更詳細一點"]],或依照該步驟生成可以調整的例子。
116
-
117
- 3. **動態互動與靈活調整**:
118
- - 允許教師插入補充信息,例如新增活動需求或修改課程主題。
119
- - 鼓勵教師參與討論,給予正面回饋。
120
-
121
- 4. **整合內建範例**:
122
- - 提供相似範例供參考,並建議如何整合到當前教案中。
123
-
124
- ---
125
-
126
- ## 輸出格式
127
- ### JSON 格式輸出範例
128
- #### ��學需求階段
129
- {
130
- "current_lesson_plan": "",
131
- "suggestion": "請提供您的教學情境描述,讓我能為您設計一份適合的非認知能力教案!例如,請說明:\n 1. 學生的年級與特徵。\n 2. 課程時長。\n 3. 課程主題**。\n 4. 是否有具體的活動需求。",
132
- "next_step_prompt": [["四年級學生面對挫折時容易放棄,課程主題是增強抗挫能力,共1節課(45分鐘),希望包括角色扮演活動。"]]
133
- }
134
-
135
- #### 教案設計階段
136
- {
137
- "current_lesson_plan": "1. 教案名稱與主題:專注力探索之旅\n2. 學習目標:\n - 辨識分心來源。\n - 學習兩種專注技巧。\n - 制定個人專注計畫。\n3. 課程流程設計:\n (1) 入場券:專注挑戰(5分鐘)——透過音樂與深呼吸活動,幫助學生進入專注狀態。\n (2) 主活動:分心偵探(10分鐘)——學生辨識分心來源,並進行小組討論。\n (3) 出場券:我的專注計畫(10分鐘)——學生制定專注策略並分享。\n4. 評估與反饋建議:設計學習單,檢核學生的學習成果與參與度。\n5. 附加資源建議:參考類似教案模板如《五年級_成為時間管理大師.docx》。",
138
- "suggestion": "以上為教案初稿,請檢查是否符合需求或有其他補充建議。例如,可新增其他專注力訓練活動。",
139
- "next_step_prompt": [["進入下一步"], ["補充具體活動設計"]]
140
- }
141
-
142
- ---
143
-
144
- ## 注意事項
145
- 1. **逐步生成**:每次僅生成一個步驟內容,避免一次性輸出過多資訊,每次只輸出一個JSON。
146
- 2. **互動確認**:等待教師確認後,根據反饋進行調整或進入下一步。
147
- 3. **結構清晰**:確保生成內容條理分明,便於教師理解與應用。
148
- 4. **鼓勵性回應**:每次建議中加入正面評價與鼓勵,促進教師參與。
149
- """
150
-
151
- RESPONSE_FORMAT = {
152
- "type": "json_schema",
153
- "json_schema": {
154
- "name": "lesson_plan_response",
155
- "schema": {
156
- "type": "object",
157
- "properties": {
158
- "current_lesson_plan": {
159
- "type": "string",
160
- "description": "當前生成的教案內容。如果流程尚未進入「教案設計」,此值應為空字串。"
161
- },
162
- "suggestion": {
163
- "type": "string",
164
- "description": "對當前教案內容的改進建議,或對用戶的鼓勵,引導進入下一步。"
165
- },
166
- "next_step_prompt": {
167
- "type": "array",
168
- "items": {
169
- "type": "array",
170
- "items": {
171
- "type": "string"
172
- }
173
- },
174
- "description": "提供用戶下一步的選項,以嵌套列表形式表示,例如 [['進入下一步'], ['補充更多細節']]。"
175
- }
176
- },
177
- "required": ["current_lesson_plan", "suggestion", "next_step_prompt"],
178
- "additionalProperties": False
179
- },
180
- "strict": True
181
- }
182
- }
183
 
184
  VECTOR_STORE_NAME = "lesson-plan"
 
 
185
 
186
- CONVERSATION_STARTER = "點選此按鈕開始設計教案"
187
 
188
  def show_json(obj):
189
  print(json.loads(obj.model_dump_json()))
@@ -216,116 +109,17 @@ def print_assistant_info(assistant):
216
  print(f"Created at: {assistant.created_at}")
217
 
218
 
219
- # spent 4m 50s downloading all 175 files
220
- def embed_from_drive(folder_id):
221
- # auth.authenticate_user()
222
- gauth = GoogleAuth()
223
- gauth.credentials = GoogleCredentials.get_application_default()
224
- drive = GoogleDrive(gauth)
225
-
226
- # Get all files in '定稿專案' folder: https://drive.google.com/drive/folders/1dlsf5BNjNczzUYKPZvYXd2mLW21QCLUK?usp=drive_link
227
- file_list = drive.ListFile({'q': f"'{folder_id}' in parents and trashed=false"}).GetList()
228
-
229
- # Download files to local (`/content/`), since file_streams don't recieve google docs
230
- local_file_paths = []
231
- for file1 in file_list:
232
- print('Processing file title: %s, id: %s' % (file1['title'], file1['id']))
233
- local_path = f"/content/{file1['title']}.docx"
234
-
235
- if 'exportLinks' in file1:
236
- if 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' in file1['exportLinks']:
237
- # update type if needed (application/vnd.openxmlformats-officedocument.wordprocessingml.document == .docx)
238
- export_url = file1['exportLinks']['application/vnd.openxmlformats-officedocument.wordprocessingml.document']
239
- print(f"Downloading as Word document: {file1['title']}")
240
- downloaded_file = drive.CreateFile({'id': file1['id']})
241
- downloaded_file.GetContentFile(local_path, mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
242
- local_file_paths.append(local_path)
243
- else:
244
- print(f"No Word export available for: {file1['title']}")
245
- else:
246
- print(f"Skipping non-Google Docs file: {file1['title']}")
247
-
248
- for path in local_file_paths:
249
- print(f"Downloaded file: {path}")
250
-
251
- file_streams = [open(path, "rb") for path in local_file_paths]
252
- return file_streams
253
-
254
- # Embed files (downloaded from drive folder)
255
- def get_vector_store_id(file_streams):
256
- vector_store = client.beta.vector_stores.create(name=VECTOR_STORE_NAME)
257
-
258
- # spent 51s batching all 175 files
259
- file_batch = client.beta.vector_stores.file_batches.upload_and_poll(
260
- vector_store_id=vector_store.id, files=file_streams
261
- )
262
-
263
- print("file_batch status",file_batch.status)
264
- print("file_counts",file_batch.file_counts)
265
-
266
- return vector_store.id
267
-
268
- # Create a completely new assistant
269
- def create_assistant(vector_store_id):
270
- assistant = client.beta.assistants.create(
271
- name=ASSISTANT_NAME,
272
- description=ASSISTANT_DESCRIPTION,
273
- instructions=ASSISTANT_INSTRUCTION,
274
- model=ASSISTANT_MODEL,
275
- tools=[{
276
- "type": "file_search",
277
- "file_search": {
278
- "max_num_results": 5 # Limit search results
279
- }
280
- }],
281
- tool_resources={'file_search': {'vector_store_ids': [vector_store_id]}},
282
- response_format=RESPONSE_FORMAT
283
- )
284
- # show_json(assistant)
285
- return assistant.id
286
-
287
- # Update existing assistant through ID (please customize prefered inputs)
288
- def update_assistant(assistant_id):
289
- assistant = client.beta.assistants.update(
290
- assistant_id=assistant_id,
291
- name=ASSISTANT_NAME,
292
- description=ASSISTANT_DESCRIPTION,
293
- instructions=ASSISTANT_INSTRUCTION,
294
- model=ASSISTANT_MODEL,
295
- tools=[{
296
- "type": "file_search",
297
- "file_search": {
298
- "max_num_results": 5 # Limit search results
299
- }
300
- }],
301
- # tool_resources={'file_search': {'vector_store_ids': [vector_store_id]}},
302
- response_format=RESPONSE_FORMAT
303
- )
304
- show_json(assistant)
305
-
306
- # Create assistant if assistant_id does not exist
307
  try:
308
  assistant = client.beta.assistants.retrieve(assistant_id)
309
  print(f"retrieve assistant success.")
310
  print_assistant_info(assistant)
311
  # update_assistant(assistant_id)
312
  except Exception as e:
313
- print(f"Assistant DNE: {e}, create assistant instead.")
314
- file_streams = embed_from_drive(google_drive_folder_id)
315
- vector_store_id = get_vector_store_id(file_streams)
316
- print(vector_store_id)
317
- assistant_id = create_assistant(vector_store_id)
318
-
319
- ASSISTANT_ID = assistant_id
320
- "ASSISTANT_ID",ASSISTANT_ID
321
 
322
- # Update assistant if needed
323
- # assistant = client.beta.assistants.update(
324
- # assistant_id=ASSISTANT_ID,
325
- # instructions=ASSISTANT_INSTRUCTION,
326
- # response_format=RESPONSE_FORMAT
327
- # )
328
- # show_json(assistant)
329
 
330
 
331
 
@@ -351,8 +145,6 @@ class EventHandler(AssistantEventHandler):
351
 
352
 
353
  # Components
354
- show_canvas = True if APP_NAME == 'NCSLM_LPD' else False # Variable to control canvas visibility
355
- # show_canvas = True
356
  chatbot = gr.Chatbot(
357
  type="messages",
358
  container=True,
@@ -376,7 +168,7 @@ prompt_input = gr.Textbox( # User prompt
376
  scale=1
377
  )
378
  quick_response = gr.Dataset( # Suggested user prompt
379
- samples=[[CONVERSATION_STARTER]],
380
  components=[prompt_input],
381
  render=False,
382
  scale=1
@@ -444,42 +236,19 @@ def handle_response(message, history, textbox_content):
444
  chat_id = getattr(handle_response, 'current_chat_id', None)
445
  thread, chat_id = get_or_create_thread(chat_id)
446
  handle_response.current_chat_id = chat_id
447
-
448
- integrated_message = message
449
 
450
- just_modifications = extract_modified_sections(textbox_content)
451
- prev_lesson_plan = just_modifications if just_modifications else textbox_content
452
-
453
- if not (message == CONVERSATION_STARTER or prev_lesson_plan == ""):
454
- integrated_message = f"""
455
- 用戶當前的需求:
456
- {message}
457
-
458
- 用戶對您生成的教案進行了以下修改:
459
- {prev_lesson_plan}
460
-
461
- 請根據用戶的需求和修改內容,更新教案,並依照步驟生成下一部分內容。
462
- 確保您:
463
- 1. 完整保留用戶的修改。
464
- 2. 提供清晰的建議(`suggestion`)。
465
- 3. 提供下一步的行動選項(`next_step_prompt`)。
466
- """
467
-
468
- client.beta.threads.messages.create(
469
- thread_id=chat_id,
470
- role="user",
471
- content=integrated_message,
472
- )
473
  full_response = ""
474
- current_lesson_plan = ""
475
- suggestion = ""
476
  next_step_prompt = [[]]
477
  prompt_tokens = 0
478
  total_tokens = 0
479
  try:
480
  with client.beta.threads.runs.stream(
481
  thread_id=chat_id,
482
- assistant_id=ASSISTANT_ID,
483
  timeout=60 # Add timeout in seconds
484
  ) as stream:
485
  try:
@@ -493,9 +262,9 @@ def handle_response(message, history, textbox_content):
493
  if stream_delta.event == 'thread.run.failed':
494
  print(f"Stream delta received: {stream_delta}", flush=True)
495
  if hasattr(stream_delta.data, 'last_error') and stream_delta.data.last_error is not None:
496
- suggestion = stream_delta.data.last_error.model_dump_json()
497
  else:
498
- suggestion = "Sorry, there was an error processing the response. Please try again."
499
  continue
500
  elif stream_delta.event != 'thread.message.delta':
501
  # Debug: Print stream response if it's not a TextDelta
@@ -522,25 +291,10 @@ def handle_response(message, history, textbox_content):
522
 
523
  # Skip if response is too short to be valid JSON
524
  if len(full_response) < 2:
525
- continue
526
-
527
- repaired_json = json_repair.loads(full_response)
528
- try:
529
- current_lesson_plan = repaired_json.get('current_lesson_plan', '')
530
- except Exception as e:
531
- print(f"Error parsing current_lesson_plan from JSON: {full_response}")
532
- print(f"Exception details: {str(e)}")
533
- print(f"Exception type: {type(e).__name__}")
534
- current_lesson_plan = ""
535
- try:
536
- suggestion = repaired_json.get('suggestion', '')
537
- except Exception as e:
538
- print(f"Error parsing suggestion from JSON: {full_response}")
539
- print(f"Exception details: {str(e)}")
540
- print(f"Exception type: {type(e).__name__}")
541
- suggestion = ""
542
 
543
- yield suggestion, current_lesson_plan, [[]]
544
  except Exception as stream_error:
545
  print(f"Stream processing error: {str(stream_error)}")
546
  print(f"Error type: {type(stream_error).__name__}")
@@ -550,27 +304,16 @@ def handle_response(message, history, textbox_content):
550
  print(f"Error type: {type(connection_error).__name__}")
551
  yield "Sorry, the connection timed out. Please try again.", "", [[]]
552
 
553
- try:
554
- repaired_json = json.loads(full_response)
555
- next_step_prompt = repaired_json.get('next_step_prompt', [["進入下一步"]])
556
- except:
557
- next_step_prompt = [["進入下一步"]]
558
-
559
  print(f"Current Messages: {message}")
560
- print(f"Suggestion: {suggestion}")
561
- print(f"Current JSON: {full_response}")
562
  print(f"Prompt tokens: {prompt_tokens}, Total tokens: {total_tokens}")
563
- msg_records = [{'role': msg['role'], 'content': msg['content']} for msg in history]
564
- msg_records.append({'role': 'user', 'content': message})
565
- msg_records.append({'role': 'assistant', 'content': suggestion})
566
- chat_manager.save_chat(user_id, chat_id, msg_records, current_lesson_plan)
567
-
568
-
569
- # Compare textbox_content with current_lesson_plan to show differences
570
- compared_lesson_plan = compare_text(prev_lesson_plan, current_lesson_plan) if prev_lesson_plan else current_lesson_plan
571
 
 
572
 
573
- yield suggestion, compared_lesson_plan, next_step_prompt
574
 
575
 
576
  def handle_quick_response_click(selected):
@@ -639,7 +382,7 @@ with gr.Blocks(css="""
639
  prompt_input.render()
640
  quick_response.render()
641
  hidden_list.render()
642
- with gr.Column(elem_classes="full-height", visible=show_canvas) as textbox_column:
643
  textbox.render()
644
 
645
  # submit button event
@@ -694,5 +437,4 @@ demo.launch(debug=True)
694
 
695
 
696
 
697
- # delete_asst(ASSISTANT_ID)
698
 
 
8
  - https://github.com/openai/openai-cookbook/blob/main/examples/Assistants_API_overview_python.ipynb
9
  '''
10
 
11
+
 
 
 
12
 
13
  from openai import OpenAI
14
 
 
27
  client = OpenAI(api_key=OPENAI_API_KEY)
28
 
29
  APP_NAME = os.getenv('app_name') # 'NCSLM_LPD', 'SEL'
30
+ # App name enum
31
+ class AppName:
32
+ NCSLM_LPD = 'NCSLM_LPD'
33
+ SEL = 'SEL'
34
+
35
+
36
+ # Import configuration files with different aliases
37
+ if APP_NAME == AppName.NCSLM_LPD:
38
+ from NCSLM_LPD import assistant_config as ncslm_config
39
+ elif APP_NAME == AppName.SEL:
40
+ from SEL import assistant_config as sel_config
41
+ else:
42
+ # Fallback or default config
43
+ import assistant_config as default_config
44
+
45
+ # Use the appropriate config based on APP_NAME
46
+ if APP_NAME == AppName.NCSLM_LPD:
47
+ active_config = ncslm_config
48
+ elif APP_NAME == AppName.SEL:
49
+ active_config = sel_config
50
+ else:
51
+ active_config = default_config
52
 
53
  # Initialize chat history manager
54
  chat_manager = ChatHistoryManager()
 
60
  )
61
  print(len(existed_assistants.data),existed_assistants.data)
62
 
 
 
 
 
 
63
  # Assistant setting (Playground: https://platform.openai.com/playground/assistants)
64
 
65
  # Record once created
66
  assistant_id = os.getenv('assistant_id')
67
+
68
+ ASSISTANT_MODEL = active_config.ASSISTANT_MODEL
69
+ ASSISTANT_NAME = active_config.ASSISTANT_NAME
70
+ ASSISTANT_DESCRIPTION = active_config.ASSISTANT_DESCRIPTION
71
+ ASSISTANT_INSTRUCTION = active_config.ASSISTANT_INSTRUCTION
72
+
73
+ RESPONSE_FORMAT = active_config.RESPONSE_FORMAT
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  VECTOR_STORE_NAME = "lesson-plan"
76
+ SHOW_CANVAS = active_config.SHOW_CANVAS
77
+
78
 
79
+ CONVERSATION_STARTER_SAMPLES = active_config.CONVERSATION_STARTER_SAMPLES
80
 
81
  def show_json(obj):
82
  print(json.loads(obj.model_dump_json()))
 
109
  print(f"Created at: {assistant.created_at}")
110
 
111
 
112
+
113
+
114
+ # Retrieve assistant
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  try:
116
  assistant = client.beta.assistants.retrieve(assistant_id)
117
  print(f"retrieve assistant success.")
118
  print_assistant_info(assistant)
119
  # update_assistant(assistant_id)
120
  except Exception as e:
121
+ print(f"retrieve Assistant Fail: {e}")
 
 
 
 
 
 
 
122
 
 
 
 
 
 
 
 
123
 
124
 
125
 
 
145
 
146
 
147
  # Components
 
 
148
  chatbot = gr.Chatbot(
149
  type="messages",
150
  container=True,
 
168
  scale=1
169
  )
170
  quick_response = gr.Dataset( # Suggested user prompt
171
+ samples=CONVERSATION_STARTER_SAMPLES,
172
  components=[prompt_input],
173
  render=False,
174
  scale=1
 
236
  chat_id = getattr(handle_response, 'current_chat_id', None)
237
  thread, chat_id = get_or_create_thread(chat_id)
238
  handle_response.current_chat_id = chat_id
 
 
239
 
240
+ preprocessed_data = active_config.handle_thread_before_chat(client, chat_id, message, history, textbox_content)
241
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  full_response = ""
243
+ canvas_result = ""
244
+ chat_response = ""
245
  next_step_prompt = [[]]
246
  prompt_tokens = 0
247
  total_tokens = 0
248
  try:
249
  with client.beta.threads.runs.stream(
250
  thread_id=chat_id,
251
+ assistant_id=assistant_id,
252
  timeout=60 # Add timeout in seconds
253
  ) as stream:
254
  try:
 
262
  if stream_delta.event == 'thread.run.failed':
263
  print(f"Stream delta received: {stream_delta}", flush=True)
264
  if hasattr(stream_delta.data, 'last_error') and stream_delta.data.last_error is not None:
265
+ chat_response = stream_delta.data.last_error.model_dump_json()
266
  else:
267
+ chat_response = "Sorry, there was an error processing the response. Please try again."
268
  continue
269
  elif stream_delta.event != 'thread.message.delta':
270
  # Debug: Print stream response if it's not a TextDelta
 
291
 
292
  # Skip if response is too short to be valid JSON
293
  if len(full_response) < 2:
294
+ continue
295
+ chat_response, canvas_result, next_step_prompt = active_config.handle_stream_delta(full_response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
 
297
+ yield chat_response, canvas_result, next_step_prompt
298
  except Exception as stream_error:
299
  print(f"Stream processing error: {str(stream_error)}")
300
  print(f"Error type: {type(stream_error).__name__}")
 
304
  print(f"Error type: {type(connection_error).__name__}")
305
  yield "Sorry, the connection timed out. Please try again.", "", [[]]
306
 
 
 
 
 
 
 
307
  print(f"Current Messages: {message}")
308
+ print(f"Suggestion: {chat_response}")
309
+ print(f"Current Response: {full_response}")
310
  print(f"Prompt tokens: {prompt_tokens}, Total tokens: {total_tokens}")
311
+
312
+ final_chat_response, final_canvas_result, final_next_step_prompt, msg_records = active_config.handle_stream_end(message, history, chat_response, textbox_content, full_response, canvas_result, preprocessed_data)
 
 
 
 
 
 
313
 
314
+ chat_manager.save_chat(user_id, chat_id, msg_records, canvas_result)
315
 
316
+ yield final_chat_response, final_canvas_result, final_next_step_prompt
317
 
318
 
319
  def handle_quick_response_click(selected):
 
382
  prompt_input.render()
383
  quick_response.render()
384
  hidden_list.render()
385
+ with gr.Column(elem_classes="full-height", visible=SHOW_CANVAS) as textbox_column:
386
  textbox.render()
387
 
388
  # submit button event
 
437
 
438
 
439
 
 
440
 
assistant_util.py CHANGED
@@ -1,8 +1,19 @@
1
  import os
 
 
 
 
 
2
  from openai import OpenAI
3
  import time
4
  from datetime import datetime
5
- from assistant_config import ASSISTANT_MODEL, ASSISTANT_NAME, ASSISTANT_DESCRIPTION, ASSISTANT_INSTRUCTION, RESPONSE_FORMAT
 
 
 
 
 
 
6
 
7
  client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
8
  assistant_id = os.getenv('assistant_id')
@@ -34,8 +45,8 @@ def test():
34
  # thread = client.beta.threads.retrieve(chat_id)
35
 
36
  # integrated_message = "點選此按鈕開始設計教案" # Add a test message
37
- integrated_message = "請按照你擁有的資料給我一個中文教案範例" # Add a test message
38
- # integrated_message = "我要尋求班級經營建議" # Add a test message
39
 
40
  client.beta.threads.messages.create(
41
  thread_id=chat_id,
@@ -250,6 +261,42 @@ def remove_vector_store_from_assistant(assistant_id, vector_store_id_to_remove):
250
  return assistant
251
 
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  # Embed files (downloaded from drive folder)
254
  def get_vector_store_id(vector_store_name, file_streams):
255
  vector_store = client.vector_stores.create(name=vector_store_name)
@@ -295,7 +342,6 @@ def get_file_streams_from_folder(folder_path):
295
  except Exception as e:
296
  print(f"Error accessing folder {folder_path}: {e}")
297
  return []
298
-
299
 
300
  def create_vector_store(folder_path):
301
  file_streams = get_file_streams_from_folder(folder_path)
@@ -343,8 +389,8 @@ def update_assistant(assistant_id):
343
  "max_num_results": 5 # Limit search results
344
  }
345
  }],
346
- # tool_resources={'file_search': {'vector_store_ids': ['vs_67e11690d1548191a21eeb15c317dc61']}},
347
- # tool_resources={'file_search': {'vector_store_ids': ['vs_W1sSCS4uuIxhqN4WSdX4ObI0']}},
348
  tool_resources={'file_search': {'vector_store_ids': []}},
349
  # response_format=None
350
  )
 
1
  import os
2
+ from pydrive.auth import GoogleAuth
3
+ from pydrive.drive import GoogleDrive
4
+ # from google.colab import auth
5
+ from oauth2client.client import GoogleCredentials
6
+
7
  from openai import OpenAI
8
  import time
9
  from datetime import datetime
10
+
11
+ from SEL import assistant_config as active_config
12
+ ASSISTANT_NAME = active_config.ASSISTANT_NAME
13
+ ASSISTANT_DESCRIPTION = active_config.ASSISTANT_DESCRIPTION
14
+ ASSISTANT_INSTRUCTION = active_config.ASSISTANT_INSTRUCTION
15
+ ASSISTANT_MODEL = active_config.ASSISTANT_MODEL
16
+ RESPONSE_FORMAT = active_config.RESPONSE_FORMAT
17
 
18
  client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
19
  assistant_id = os.getenv('assistant_id')
 
45
  # thread = client.beta.threads.retrieve(chat_id)
46
 
47
  # integrated_message = "點選此按鈕開始設計教案" # Add a test message
48
+ # integrated_message = "請按照你擁有的資料給我一個中文教案範例" # Add a test message
49
+ integrated_message = "我要尋求班級經營建議" # Add a test message for SEL
50
 
51
  client.beta.threads.messages.create(
52
  thread_id=chat_id,
 
261
  return assistant
262
 
263
 
264
+ # google_drive_folder_id = os.getenv('google_drive_folder_id')
265
+ # spent 4m 50s downloading all 175 files
266
+ def embed_from_drive(folder_id):
267
+ # auth.authenticate_user()
268
+ gauth = GoogleAuth()
269
+ gauth.credentials = GoogleCredentials.get_application_default()
270
+ drive = GoogleDrive(gauth)
271
+
272
+ # Get all files in '定稿專案' folder: https://drive.google.com/drive/folders/1dlsf5BNjNczzUYKPZvYXd2mLW21QCLUK?usp=drive_link
273
+ file_list = drive.ListFile({'q': f"'{folder_id}' in parents and trashed=false"}).GetList()
274
+
275
+ # Download files to local (`/content/`), since file_streams don't recieve google docs
276
+ local_file_paths = []
277
+ for file1 in file_list:
278
+ print('Processing file title: %s, id: %s' % (file1['title'], file1['id']))
279
+ local_path = f"/content/{file1['title']}.docx"
280
+
281
+ if 'exportLinks' in file1:
282
+ if 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' in file1['exportLinks']:
283
+ # update type if needed (application/vnd.openxmlformats-officedocument.wordprocessingml.document == .docx)
284
+ export_url = file1['exportLinks']['application/vnd.openxmlformats-officedocument.wordprocessingml.document']
285
+ print(f"Downloading as Word document: {file1['title']}")
286
+ downloaded_file = drive.CreateFile({'id': file1['id']})
287
+ downloaded_file.GetContentFile(local_path, mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
288
+ local_file_paths.append(local_path)
289
+ else:
290
+ print(f"No Word export available for: {file1['title']}")
291
+ else:
292
+ print(f"Skipping non-Google Docs file: {file1['title']}")
293
+
294
+ for path in local_file_paths:
295
+ print(f"Downloaded file: {path}")
296
+
297
+ file_streams = [open(path, "rb") for path in local_file_paths]
298
+ return file_streams
299
+
300
  # Embed files (downloaded from drive folder)
301
  def get_vector_store_id(vector_store_name, file_streams):
302
  vector_store = client.vector_stores.create(name=vector_store_name)
 
342
  except Exception as e:
343
  print(f"Error accessing folder {folder_path}: {e}")
344
  return []
 
345
 
346
  def create_vector_store(folder_path):
347
  file_streams = get_file_streams_from_folder(folder_path)
 
389
  "max_num_results": 5 # Limit search results
390
  }
391
  }],
392
+ # tool_resources={'file_search': {'vector_store_ids': ['vs_67e11690d1548191a21eeb15c317dc61']}}, # SEL
393
+ # tool_resources={'file_search': {'vector_store_ids': ['vs_W1sSCS4uuIxhqN4WSdX4ObI0']}}, # NCSLM_LPD
394
  tool_resources={'file_search': {'vector_store_ids': []}},
395
  # response_format=None
396
  )
debug.py CHANGED
@@ -1,11 +1,12 @@
1
- from assistant_util import test, list_assistants, create_vector_store, print_vector_store_details, update_assistant, delete_assistant
2
 
3
  # You can call this function to test it
4
  if __name__ == "__main__":
5
  # test()
6
- list_assistants()
7
  # create_vector_store("SEL-Coach-Chat-Vector-Store", "~/taboola/test/vector_store")
8
- # print_vector_store_details("vector_store_id")
9
- # update_assistant("assistant_id")
10
  # delete_assistant("assistant_id")
 
11
 
 
1
+ from assistant_util import test, create_assistant, list_assistants, create_vector_store, print_vector_store_details, update_assistant, delete_assistant
2
 
3
  # You can call this function to test it
4
  if __name__ == "__main__":
5
  # test()
6
+ # create_assistant()
7
  # create_vector_store("SEL-Coach-Chat-Vector-Store", "~/taboola/test/vector_store")
8
+ # print_vector_store_details("vs_67e11690d1548191a21eeb15c317dc61")
9
+ # update_assistant("asst_KK2FVo3Qe57mjvRiRDZaBM4l")
10
  # delete_assistant("assistant_id")
11
+ list_assistants()
12