Spaces:
Running
Running
ChenyuRabbitLove
commited on
Commit
•
115ff47
1
Parent(s):
cabe12b
feat: add completion reward
Browse files- app.py +364 -11
- css/style.css +121 -1
- data/completion_reward_issue_status.json +0 -0
- medias/azure.png +0 -0
- medias/lumina.png +0 -0
- medias/solara.png +0 -0
- medias/verdant.png +0 -0
- utils/completion_reward.py +531 -0
- utils/completion_reward_utils.py +77 -0
- utils/mes_player_activity_model.py +6 -7
- utils/mes_player_model.py +1 -3
- utils/utils.py +8 -3
app.py
CHANGED
@@ -13,12 +13,20 @@ from utils.utils import (
|
|
13 |
save_latest_player_data,
|
14 |
render_finished,
|
15 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
seafoam = Seafoam()
|
18 |
|
19 |
|
20 |
def get_player_info(player_backend_user_id):
|
21 |
-
|
22 |
try:
|
23 |
with open("latest_player_data.json", "r", encoding="utf-8") as file:
|
24 |
player_info = json.load(file)
|
@@ -27,7 +35,7 @@ def get_player_info(player_backend_user_id):
|
|
27 |
save_latest_player_data()
|
28 |
with open("latest_player_data.json", "r", encoding="utf-8") as file:
|
29 |
player_info = json.load(file)
|
30 |
-
|
31 |
if player_backend_user_id in player_info:
|
32 |
return player_info[player_backend_user_id]
|
33 |
else:
|
@@ -43,12 +51,32 @@ def get_player_info(player_backend_user_id):
|
|
43 |
|
44 |
return new_player.to_dict()
|
45 |
|
|
|
46 |
def create_new_player_activity():
|
47 |
return PlayerActivity()
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
# start of gradio interface
|
50 |
with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
|
51 |
player_info = gr.State()
|
|
|
|
|
52 |
player_activity_tracker = gr.State(create_new_player_activity)
|
53 |
|
54 |
with gr.Row():
|
@@ -87,15 +115,15 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
|
|
87 |
|
88 |
with gr.Column(scale=1):
|
89 |
adventure_description = gr.Markdown(
|
90 |
-
|
91 |
-
|
92 |
adventure = gr.Slider(
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
achievements_description = gr.Markdown(
|
100 |
"# 達成成就", elem_id="achievements_description"
|
101 |
)
|
@@ -124,6 +152,146 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
|
|
124 |
pull_newest_player_data = gr.Textbox("", visible=False)
|
125 |
update_status = gr.Textbox("", visible=False)
|
126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
# actions when player login
|
128 |
# define args
|
129 |
send_player_login_info = dict(
|
@@ -133,10 +301,14 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
|
|
133 |
)
|
134 |
|
135 |
player_info_query_btn.click(get_player_info, player_backend_id, player_info).then(
|
|
|
|
|
136 |
render_player_data,
|
137 |
player_info,
|
138 |
[avatar, pet_gallery, badge_gallery, adventure_log, achievements, adventure],
|
139 |
-
).then(
|
|
|
|
|
140 |
|
141 |
pull_newest_player_data.submit(
|
142 |
save_latest_player_data,
|
@@ -145,5 +317,186 @@ with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
|
|
145 |
api_name="pull_newest_player_data",
|
146 |
)
|
147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
if __name__ == "__main__":
|
149 |
demo.launch()
|
|
|
13 |
save_latest_player_data,
|
14 |
render_finished,
|
15 |
)
|
16 |
+
from utils.completion_reward import CompletionReward
|
17 |
+
from utils.completion_reward_utils import (
|
18 |
+
get_llm_response,
|
19 |
+
set_player_name,
|
20 |
+
set_player_selected_character,
|
21 |
+
create_certificate,
|
22 |
+
complete_reward,
|
23 |
+
check_is_in_completion_reward,
|
24 |
+
)
|
25 |
|
26 |
seafoam = Seafoam()
|
27 |
|
28 |
|
29 |
def get_player_info(player_backend_user_id):
|
|
|
30 |
try:
|
31 |
with open("latest_player_data.json", "r", encoding="utf-8") as file:
|
32 |
player_info = json.load(file)
|
|
|
35 |
save_latest_player_data()
|
36 |
with open("latest_player_data.json", "r", encoding="utf-8") as file:
|
37 |
player_info = json.load(file)
|
38 |
+
|
39 |
if player_backend_user_id in player_info:
|
40 |
return player_info[player_backend_user_id]
|
41 |
else:
|
|
|
51 |
|
52 |
return new_player.to_dict()
|
53 |
|
54 |
+
|
55 |
def create_new_player_activity():
|
56 |
return PlayerActivity()
|
57 |
|
58 |
+
|
59 |
+
def get_player_logs(player_backend_user_id):
|
60 |
+
with open("processed_adventure_logs.json") as file:
|
61 |
+
player_logs = json.load(file)
|
62 |
+
try:
|
63 |
+
player_logs = player_logs[player_backend_user_id]
|
64 |
+
return player_logs
|
65 |
+
except KeyError:
|
66 |
+
logging.error(
|
67 |
+
f"Player {player_backend_user_id} not found in processed_player_adventure_logs.json"
|
68 |
+
)
|
69 |
+
|
70 |
+
|
71 |
+
def init_reward():
|
72 |
+
return CompletionReward()
|
73 |
+
|
74 |
+
|
75 |
# start of gradio interface
|
76 |
with gr.Blocks(theme=seafoam, css=get_content("css/style.css")) as demo:
|
77 |
player_info = gr.State()
|
78 |
+
player_logs = gr.State()
|
79 |
+
completion_reward = gr.State(init_reward)
|
80 |
player_activity_tracker = gr.State(create_new_player_activity)
|
81 |
|
82 |
with gr.Row():
|
|
|
115 |
|
116 |
with gr.Column(scale=1):
|
117 |
adventure_description = gr.Markdown(
|
118 |
+
"# 冒險階段", elem_id="adventure_description"
|
119 |
+
)
|
120 |
adventure = gr.Slider(
|
121 |
+
value=0,
|
122 |
+
show_label=False,
|
123 |
+
interactive=False,
|
124 |
+
elem_id="adventure_slider",
|
125 |
+
info="",
|
126 |
+
)
|
127 |
achievements_description = gr.Markdown(
|
128 |
"# 達成成就", elem_id="achievements_description"
|
129 |
)
|
|
|
152 |
pull_newest_player_data = gr.Textbox("", visible=False)
|
153 |
update_status = gr.Textbox("", visible=False)
|
154 |
|
155 |
+
with gr.Tab("完賽獎勵"):
|
156 |
+
with gr.Row():
|
157 |
+
start_make_reward = gr.Button(
|
158 |
+
"開始製作完賽獎勵!", visible=True, elem_id="start_make_reward"
|
159 |
+
)
|
160 |
+
|
161 |
+
with gr.Row():
|
162 |
+
not_participate = gr.Markdown(
|
163 |
+
"# 同學並未參與 2023 的星空探險隊唷!", visible=False, elem_id="not_participate"
|
164 |
+
)
|
165 |
+
|
166 |
+
with gr.Row():
|
167 |
+
not_start = gr.Markdown(
|
168 |
+
"# 完賽獎勵還沒有開放申請,但是就快了,請敬請期待!", visible=False, elem_id="not_start"
|
169 |
+
)
|
170 |
+
|
171 |
+
with gr.Row():
|
172 |
+
already_issued = gr.Image(visible=False)
|
173 |
+
|
174 |
+
with gr.Row():
|
175 |
+
player_name_title = gr.Markdown(
|
176 |
+
"# 選擇玩家暱稱", visible=False, elem_id="player_name_title"
|
177 |
+
)
|
178 |
+
|
179 |
+
with gr.Row():
|
180 |
+
player_name = gr.Textbox(
|
181 |
+
label="玩家暱稱",
|
182 |
+
info="請輸入想要用在完賽獎勵上的玩家暱稱,上限為 10 字,獎勵發送後無法更改也無法補發,還請同學們謹慎填寫。",
|
183 |
+
interactive=True,
|
184 |
+
elem_id="player_name",
|
185 |
+
visible=False,
|
186 |
+
)
|
187 |
+
|
188 |
+
with gr.Row():
|
189 |
+
confirm_player_name = gr.Button(
|
190 |
+
"確認暱稱", elem_id="confirm_player_name", visible=False
|
191 |
+
)
|
192 |
+
cancel_player_name = gr.Button(
|
193 |
+
"取消", elem_id="cancel_player_name", visible=False
|
194 |
+
)
|
195 |
+
|
196 |
+
with gr.Row():
|
197 |
+
player_name_next_step = gr.Button(
|
198 |
+
"下一步", visible=False, elem_id="player_name_next_step"
|
199 |
+
)
|
200 |
+
|
201 |
+
with gr.Row():
|
202 |
+
story_title = gr.Markdown("# 選擇冒險故事", visible=False, elem_id="story_title")
|
203 |
+
|
204 |
+
with gr.Row():
|
205 |
+
story_description = gr.Markdown(
|
206 |
+
"有五位星際夥伴來為你撰寫專屬於你的冒險故事,每位星際夥伴都會得到同學冒險週記的內容,並寫成一段故事,請同學選擇自己最喜歡的一段故事��這段故事將會被用來印在獎狀上<br><br>小叮嚀:請同學選擇喜歡的故事內容,而不是星際夥伴唷!星際夥伴可是不會和同學們一起冒險的!最後,撰寫故事的過程大概需要幾分鐘的時間,請同學耐心等待",
|
207 |
+
visible=False,
|
208 |
+
elem_id="story_description",
|
209 |
+
)
|
210 |
+
|
211 |
+
with gr.Row():
|
212 |
+
openai_description = gr.Markdown(
|
213 |
+
"# 露米娜", elem_id="openai_description", visible=False
|
214 |
+
)
|
215 |
+
aws_description = gr.Markdown(
|
216 |
+
"# 索拉拉", elem_id="aws_description", visible=False
|
217 |
+
)
|
218 |
+
google_description = gr.Markdown(
|
219 |
+
"# 薇丹特", elem_id="google_description", visible=False
|
220 |
+
)
|
221 |
+
mtk_description = gr.Markdown(
|
222 |
+
"# 蔚藍", elem_id="mtk_description", visible=False
|
223 |
+
)
|
224 |
+
|
225 |
+
with gr.Row():
|
226 |
+
openai_img = gr.Image(
|
227 |
+
"medias/lumina.png",
|
228 |
+
visible=False,
|
229 |
+
elem_id="openai_img",
|
230 |
+
interactive=False,
|
231 |
+
show_download_button=False,
|
232 |
+
)
|
233 |
+
aws_img = gr.Image(
|
234 |
+
"medias/solara.png",
|
235 |
+
visible=False,
|
236 |
+
elem_id="aws_img",
|
237 |
+
interactive=False,
|
238 |
+
show_download_button=False,
|
239 |
+
)
|
240 |
+
google_img = gr.Image(
|
241 |
+
"medias/verdant.png",
|
242 |
+
visible=False,
|
243 |
+
elem_id="google_img",
|
244 |
+
interactive=False,
|
245 |
+
show_download_button=False,
|
246 |
+
)
|
247 |
+
mtk_img = gr.Image(
|
248 |
+
"medias/azure.png",
|
249 |
+
visible=False,
|
250 |
+
elem_id="mtk_img",
|
251 |
+
interactive=False,
|
252 |
+
show_download_button=False,
|
253 |
+
)
|
254 |
+
|
255 |
+
with gr.Row():
|
256 |
+
start_generate_story = gr.Button(
|
257 |
+
"開始寫作故事", visible=False, elem_id="start_generate_story"
|
258 |
+
)
|
259 |
+
|
260 |
+
with gr.Row():
|
261 |
+
bot1 = gr.Chatbot(visible=False)
|
262 |
+
bot2 = gr.Chatbot(visible=False)
|
263 |
+
bot3 = gr.Chatbot(visible=False)
|
264 |
+
bot4 = gr.Chatbot(visible=False)
|
265 |
+
|
266 |
+
with gr.Row():
|
267 |
+
select_story = gr.Radio(
|
268 |
+
["露米娜", "索拉拉", "薇丹特", "蔚藍"],
|
269 |
+
interactive=True,
|
270 |
+
label="選擇故事",
|
271 |
+
visible=False,
|
272 |
+
elem_id="select_story",
|
273 |
+
)
|
274 |
+
|
275 |
+
with gr.Row():
|
276 |
+
confirm_story = gr.Button("確認故事", visible=False, elem_id="confirm_story")
|
277 |
+
cancel_story = gr.Button("取消", visible=False, elem_id="cancel_story")
|
278 |
+
|
279 |
+
with gr.Row():
|
280 |
+
start_generate_certificate = gr.Button(
|
281 |
+
"開始製作完賽獎勵!", visible=False, elem_id="start_generate_certificate"
|
282 |
+
)
|
283 |
+
processing = gr.Button(
|
284 |
+
"製作中...",
|
285 |
+
visible=False,
|
286 |
+
elem_id="processing",
|
287 |
+
size="lg",
|
288 |
+
interactive=False,
|
289 |
+
)
|
290 |
+
complete = gr.Markdown("# 完成!", visible=False, elem_id="complete")
|
291 |
+
|
292 |
+
with gr.Row():
|
293 |
+
reward_result = gr.Image(visible=False)
|
294 |
+
|
295 |
# actions when player login
|
296 |
# define args
|
297 |
send_player_login_info = dict(
|
|
|
301 |
)
|
302 |
|
303 |
player_info_query_btn.click(get_player_info, player_backend_id, player_info).then(
|
304 |
+
get_player_logs, player_backend_id, player_logs
|
305 |
+
).then(
|
306 |
render_player_data,
|
307 |
player_info,
|
308 |
[avatar, pet_gallery, badge_gallery, adventure_log, achievements, adventure],
|
309 |
+
).then(
|
310 |
+
**send_player_login_info
|
311 |
+
)
|
312 |
|
313 |
pull_newest_player_data.submit(
|
314 |
save_latest_player_data,
|
|
|
317 |
api_name="pull_newest_player_data",
|
318 |
)
|
319 |
|
320 |
+
def create_visibility_updates(visible, count):
|
321 |
+
return tuple(gr.update(visible=visible) for _ in range(count))
|
322 |
+
|
323 |
+
start_make_reward.click(
|
324 |
+
check_is_in_completion_reward,
|
325 |
+
player_backend_id,
|
326 |
+
[
|
327 |
+
start_make_reward,
|
328 |
+
player_name_title,
|
329 |
+
player_name,
|
330 |
+
confirm_player_name,
|
331 |
+
not_participate,
|
332 |
+
not_start,
|
333 |
+
already_issued,
|
334 |
+
],
|
335 |
+
queue=False,
|
336 |
+
)
|
337 |
+
|
338 |
+
set_player_name_args = dict(
|
339 |
+
fn=set_player_name,
|
340 |
+
inputs=[completion_reward, player_name, player_backend_id],
|
341 |
+
outputs=None,
|
342 |
+
queue=False,
|
343 |
+
)
|
344 |
+
|
345 |
+
confirm_player_name.click(
|
346 |
+
lambda: (gr.update(interactive=False), gr.update(visible=False)),
|
347 |
+
None,
|
348 |
+
[player_name, confirm_player_name],
|
349 |
+
queue=False,
|
350 |
+
).then(
|
351 |
+
lambda: create_visibility_updates(True, 2),
|
352 |
+
None,
|
353 |
+
[cancel_player_name, player_name_next_step],
|
354 |
+
queue=False,
|
355 |
+
).then(
|
356 |
+
**set_player_name_args
|
357 |
+
)
|
358 |
+
|
359 |
+
cancel_player_name.click(
|
360 |
+
lambda: (gr.update(interactive=True), gr.update(visible=True)),
|
361 |
+
None,
|
362 |
+
[player_name, confirm_player_name],
|
363 |
+
queue=False,
|
364 |
+
).then(
|
365 |
+
lambda: create_visibility_updates(False, 2),
|
366 |
+
None,
|
367 |
+
[cancel_player_name, player_name_next_step],
|
368 |
+
queue=False,
|
369 |
+
)
|
370 |
+
|
371 |
+
player_name_next_step.click(
|
372 |
+
lambda: create_visibility_updates(False, 5),
|
373 |
+
None,
|
374 |
+
[
|
375 |
+
player_name,
|
376 |
+
player_name_next_step,
|
377 |
+
confirm_player_name,
|
378 |
+
player_name_title,
|
379 |
+
cancel_player_name,
|
380 |
+
],
|
381 |
+
queue=False,
|
382 |
+
).then(
|
383 |
+
lambda: create_visibility_updates(True, 11),
|
384 |
+
None,
|
385 |
+
[
|
386 |
+
openai_img,
|
387 |
+
aws_img,
|
388 |
+
google_img,
|
389 |
+
mtk_img,
|
390 |
+
story_title,
|
391 |
+
story_description,
|
392 |
+
openai_description,
|
393 |
+
aws_description,
|
394 |
+
google_description,
|
395 |
+
mtk_description,
|
396 |
+
start_generate_story,
|
397 |
+
],
|
398 |
+
queue=False,
|
399 |
+
)
|
400 |
+
|
401 |
+
get_llm_response_args = dict(
|
402 |
+
fn=get_llm_response,
|
403 |
+
inputs=[completion_reward, player_logs],
|
404 |
+
outputs=[bot1, bot2, bot3, bot4],
|
405 |
+
queue=False,
|
406 |
+
)
|
407 |
+
|
408 |
+
start_generate_story.click(
|
409 |
+
lambda: gr.update(visible=False), None, start_generate_story, queue=False
|
410 |
+
).then(
|
411 |
+
lambda: create_visibility_updates(True, 4),
|
412 |
+
None,
|
413 |
+
[bot1, bot2, bot3, bot4],
|
414 |
+
queue=False,
|
415 |
+
).then(
|
416 |
+
**get_llm_response_args
|
417 |
+
).then(
|
418 |
+
lambda: gr.update(visible=True), None, [select_story], queue=False
|
419 |
+
)
|
420 |
+
|
421 |
+
select_story.select(
|
422 |
+
lambda: gr.update(visible=True), None, confirm_story, queue=False
|
423 |
+
)
|
424 |
+
|
425 |
+
set_player_selected_character_args = dict(
|
426 |
+
fn=set_player_selected_character,
|
427 |
+
inputs=[completion_reward, select_story],
|
428 |
+
outputs=None,
|
429 |
+
queue=False,
|
430 |
+
)
|
431 |
+
|
432 |
+
confirm_story.click(
|
433 |
+
lambda: gr.update(interactive=False), None, [select_story], queue=False
|
434 |
+
).then(lambda: gr.update(visible=False), None, [confirm_story], queue=False).then(
|
435 |
+
lambda: (gr.update(visible=True), gr.update(visible=True)),
|
436 |
+
None,
|
437 |
+
[start_generate_certificate, cancel_story],
|
438 |
+
queue=False,
|
439 |
+
).then(
|
440 |
+
**set_player_selected_character_args
|
441 |
+
)
|
442 |
+
|
443 |
+
cancel_story.click(
|
444 |
+
lambda: gr.update(interactive=True), None, [select_story], queue=False
|
445 |
+
).then(lambda: gr.update(visible=False), None, [cancel_story], queue=False).then(
|
446 |
+
lambda: (gr.update(visible=False), gr.update(visible=True)),
|
447 |
+
None,
|
448 |
+
[start_generate_certificate, confirm_story],
|
449 |
+
queue=False,
|
450 |
+
)
|
451 |
+
|
452 |
+
create_certificate_args = dict(
|
453 |
+
fn=create_certificate,
|
454 |
+
inputs=[completion_reward],
|
455 |
+
outputs=reward_result,
|
456 |
+
queue=False,
|
457 |
+
)
|
458 |
+
|
459 |
+
complete_reward_args = dict(
|
460 |
+
fn=complete_reward,
|
461 |
+
inputs=[completion_reward],
|
462 |
+
outputs=None,
|
463 |
+
queue=False,
|
464 |
+
)
|
465 |
+
|
466 |
+
start_generate_certificate.click(
|
467 |
+
lambda: create_visibility_updates(False, 18),
|
468 |
+
None,
|
469 |
+
[
|
470 |
+
openai_img,
|
471 |
+
aws_img,
|
472 |
+
google_img,
|
473 |
+
mtk_img,
|
474 |
+
story_title,
|
475 |
+
story_description,
|
476 |
+
openai_description,
|
477 |
+
aws_description,
|
478 |
+
google_description,
|
479 |
+
mtk_description,
|
480 |
+
bot1,
|
481 |
+
bot2,
|
482 |
+
bot3,
|
483 |
+
bot4,
|
484 |
+
select_story,
|
485 |
+
processing,
|
486 |
+
cancel_story,
|
487 |
+
start_generate_certificate,
|
488 |
+
],
|
489 |
+
queue=False,
|
490 |
+
).then(lambda: gr.update(visible=True), None, [processing], queue=False).then(
|
491 |
+
**create_certificate_args
|
492 |
+
).then(
|
493 |
+
lambda: (gr.update(visible=True), gr.update(visible=False)),
|
494 |
+
None,
|
495 |
+
[complete, processing],
|
496 |
+
queue=False,
|
497 |
+
).then(
|
498 |
+
**complete_reward_args
|
499 |
+
)
|
500 |
+
|
501 |
if __name__ == "__main__":
|
502 |
demo.launch()
|
css/style.css
CHANGED
@@ -246,4 +246,124 @@ input[type="range"]::-ms-track {
|
|
246 |
box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.75) inset !important;
|
247 |
border-radius: 10px !important; /* Rounded corners of the track */
|
248 |
}
|
249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.75) inset !important;
|
247 |
border-radius: 10px !important; /* Rounded corners of the track */
|
248 |
}
|
249 |
+
|
250 |
+
|
251 |
+
#player_name_title {
|
252 |
+
margin: 5vh auto;
|
253 |
+
}
|
254 |
+
|
255 |
+
#player_name {
|
256 |
+
padding: 6vh 6vw 10vh 6vw;
|
257 |
+
}
|
258 |
+
|
259 |
+
#player_name [data-testid="block-info"]{
|
260 |
+
font-size: 2rem;
|
261 |
+
padding: 1vh 0 1vh 0;
|
262 |
+
display: block;
|
263 |
+
color: #000;
|
264 |
+
text-align: center !important;
|
265 |
+
}
|
266 |
+
|
267 |
+
#player_name div{
|
268 |
+
color: #444;
|
269 |
+
text-align: center;
|
270 |
+
}
|
271 |
+
|
272 |
+
#player_name textarea{
|
273 |
+
border-style: solid;
|
274 |
+
border-color: rgba(244, 250, 150, 0.2);
|
275 |
+
border-width: 1px;
|
276 |
+
background: rgba(255,255,255, 0.2);
|
277 |
+
}
|
278 |
+
|
279 |
+
#story_title {
|
280 |
+
margin: 5vh auto;
|
281 |
+
}
|
282 |
+
|
283 |
+
#story_description p {
|
284 |
+
font-size: 1.3rem;
|
285 |
+
text-align: center;
|
286 |
+
width: 70%;
|
287 |
+
margin: 5vh auto;
|
288 |
+
}
|
289 |
+
|
290 |
+
#story_description {
|
291 |
+
border: #12d2ab solid 1px !important;
|
292 |
+
border-radius: 10px;
|
293 |
+
}
|
294 |
+
|
295 |
+
#start_make_reward {
|
296 |
+
background-color: #12d2ab;
|
297 |
+
margin: 20vh 10vw;
|
298 |
+
height: 30vh;
|
299 |
+
font-size: 2rem;
|
300 |
+
}
|
301 |
+
|
302 |
+
#not_participate {
|
303 |
+
margin: 20vh 10vw;
|
304 |
+
font-size: 2rem;
|
305 |
+
}
|
306 |
+
|
307 |
+
#not_start {
|
308 |
+
margin: 20vh 10vw;
|
309 |
+
font-size: 2rem;
|
310 |
+
}
|
311 |
+
|
312 |
+
#start_generate_story, #confirm_player_name, #player_name_next_step, #confirm_story, #start_generate_certificate {
|
313 |
+
background-color: #48A1DD;
|
314 |
+
}
|
315 |
+
|
316 |
+
#cancel_player_name, #cancel_story {
|
317 |
+
background-color: #eb6584;
|
318 |
+
}
|
319 |
+
|
320 |
+
#openai_img, #aws_img, #google_img, #mtk_img, #ntu_img {
|
321 |
+
border-radius: 50%;
|
322 |
+
transform: scale(0.7);
|
323 |
+
}
|
324 |
+
|
325 |
+
#openai_img [aria-label="Download"], #aws_img [aria-label="Download"], #google_img [aria-label="Download"], #mtk_img [aria-label="Download"], #ntu_img [aria-label="Download"] {
|
326 |
+
display: none;
|
327 |
+
}
|
328 |
+
|
329 |
+
[aria-label="Loading response"] {
|
330 |
+
background: rgba(0,0,0,0.1);
|
331 |
+
}
|
332 |
+
|
333 |
+
.selected {
|
334 |
+
color: #fff;
|
335 |
+
background: #12d2ab;
|
336 |
+
border: 2px solid #02b28f;
|
337 |
+
border-radius: 8px;
|
338 |
+
}
|
339 |
+
|
340 |
+
.message {
|
341 |
+
background: rgba(0,0,0,0.05);
|
342 |
+
border: None;
|
343 |
+
}
|
344 |
+
|
345 |
+
.code_wrap {
|
346 |
+
background: rgba(0,0,0,0.1);
|
347 |
+
border-radius: 10px;
|
348 |
+
padding: 1vh 1vw 1vh 1vw;
|
349 |
+
margin: 1vh 1vw 1vh 1vw;
|
350 |
+
}
|
351 |
+
|
352 |
+
#select_story {
|
353 |
+
display: flex;
|
354 |
+
justify-content: space-evenly;
|
355 |
+
}
|
356 |
+
|
357 |
+
#select_story label{
|
358 |
+
background: #48A1DD;
|
359 |
+
color: #fff;
|
360 |
+
}
|
361 |
+
|
362 |
+
#select_story input{
|
363 |
+
background-color: #2563eb;
|
364 |
+
}
|
365 |
+
|
366 |
+
#select_story [data-testid="block-info"] {
|
367 |
+
color: #000;
|
368 |
+
margin: auto;
|
369 |
+
}
|
data/completion_reward_issue_status.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
medias/azure.png
ADDED
medias/lumina.png
ADDED
medias/solara.png
ADDED
medias/verdant.png
ADDED
utils/completion_reward.py
ADDED
@@ -0,0 +1,531 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import json
|
3 |
+
import time
|
4 |
+
import io
|
5 |
+
import os
|
6 |
+
import re
|
7 |
+
import requests
|
8 |
+
import textwrap
|
9 |
+
import random
|
10 |
+
import hashlib
|
11 |
+
from datetime import datetime
|
12 |
+
from PIL import Image, ImageDraw, ImageFilter, ImageFont
|
13 |
+
|
14 |
+
import anthropic_bedrock
|
15 |
+
import gradio as gr
|
16 |
+
from opencc import OpenCC
|
17 |
+
from openai import OpenAI
|
18 |
+
from anthropic_bedrock import AnthropicBedrock, HUMAN_PROMPT, AI_PROMPT
|
19 |
+
from google.auth.transport.requests import Request
|
20 |
+
from google.oauth2.service_account import Credentials
|
21 |
+
from google import auth
|
22 |
+
from google.cloud import bigquery
|
23 |
+
from google.cloud import storage
|
24 |
+
|
25 |
+
|
26 |
+
class CompletionReward:
|
27 |
+
def __init__(self):
|
28 |
+
self.player_backend_user_id = None
|
29 |
+
self.player_name = None
|
30 |
+
self.background_url = None
|
31 |
+
self.player_selected_character = None
|
32 |
+
self.player_selected_model = None
|
33 |
+
self.player_selected_paragraph = None
|
34 |
+
self.paragraph_openai = None
|
35 |
+
self.paragraph_aws = None
|
36 |
+
self.paragraph_google = None
|
37 |
+
self.paragraph_mtk = None
|
38 |
+
self.player_certificate_url = None
|
39 |
+
self.openai_agent = OpenAIAgent()
|
40 |
+
self.aws_agent = AWSAgent()
|
41 |
+
self.google_agent = GoogleAgent()
|
42 |
+
self.mtk_agent = MTKAgent()
|
43 |
+
self.shuffled_response_order = {}
|
44 |
+
self.paragraph_map = {
|
45 |
+
"openai": self.paragraph_openai,
|
46 |
+
"aws": self.paragraph_aws,
|
47 |
+
"google": self.paragraph_google,
|
48 |
+
"mtk": self.paragraph_mtk,
|
49 |
+
}
|
50 |
+
SERVICE_ACCOUNT_INFO = os.getenv("GBQ_TOKEN")
|
51 |
+
service_account_info_dict = json.loads(SERVICE_ACCOUNT_INFO)
|
52 |
+
|
53 |
+
creds = Credentials.from_service_account_info(service_account_info_dict)
|
54 |
+
self.gbq_client = bigquery.Client(
|
55 |
+
credentials=creds, project=service_account_info_dict["project_id"]
|
56 |
+
)
|
57 |
+
self.gcs_client = storage.Client(
|
58 |
+
credentials=creds, project=service_account_info_dict["project_id"]
|
59 |
+
)
|
60 |
+
|
61 |
+
def get_llm_response(self, player_logs):
|
62 |
+
agents_responses = {
|
63 |
+
"openai": self.openai_agent.get_story(player_logs),
|
64 |
+
"aws": self.aws_agent.get_story(player_logs),
|
65 |
+
"google": self.google_agent.get_story(player_logs),
|
66 |
+
"mtk": self.mtk_agent.get_story(player_logs),
|
67 |
+
}
|
68 |
+
self.paragraph_openai = agents_responses["openai"]
|
69 |
+
self.paragraph_aws = agents_responses["aws"]
|
70 |
+
self.paragraph_google = agents_responses["google"]
|
71 |
+
self.paragraph_mtk = agents_responses["mtk"]
|
72 |
+
response_items = list(agents_responses.items())
|
73 |
+
random.shuffle(response_items)
|
74 |
+
|
75 |
+
self.shuffled_response_order = {
|
76 |
+
str(index): agent for index, (agent, _) in enumerate(response_items)
|
77 |
+
}
|
78 |
+
|
79 |
+
shuffled_responses = tuple(response for _, response in response_items)
|
80 |
+
return (
|
81 |
+
[(None, shuffled_responses[0])],
|
82 |
+
[(None, shuffled_responses[1])],
|
83 |
+
[(None, shuffled_responses[2])],
|
84 |
+
[(None, shuffled_responses[3])],
|
85 |
+
)
|
86 |
+
|
87 |
+
def set_player_name(self, player_name, player_backend_user_id):
|
88 |
+
self.player_backend_user_id = player_backend_user_id
|
89 |
+
self.player_name = player_name
|
90 |
+
|
91 |
+
def set_background_url(self, background_url):
|
92 |
+
self.background_url = background_url
|
93 |
+
|
94 |
+
def set_player_backend_user_id(self, player_backend_user_id):
|
95 |
+
self.player_backend_user_id = player_backend_user_id
|
96 |
+
|
97 |
+
def set_player_selected_character(self, player_selected_character):
|
98 |
+
character_map = {
|
99 |
+
"露米娜": "0",
|
100 |
+
"索拉拉": "1",
|
101 |
+
"薇丹特": "2",
|
102 |
+
"蔚藍": "3",
|
103 |
+
}
|
104 |
+
self.player_selected_character = player_selected_character
|
105 |
+
self.player_selected_model = self.shuffled_response_order[
|
106 |
+
character_map[player_selected_character]
|
107 |
+
]
|
108 |
+
self.player_selected_paragraph = self.get_paragraph_by_model(
|
109 |
+
self.player_selected_model
|
110 |
+
)
|
111 |
+
|
112 |
+
def get_paragraph_by_model(self, model):
|
113 |
+
return getattr(self, f"paragraph_{model}", None)
|
114 |
+
|
115 |
+
def create_certificate(self):
|
116 |
+
image_url = self.openai_agent.get_background()
|
117 |
+
self.set_background_url(image_url)
|
118 |
+
source_file = ImageProcessor.generate_reward(
|
119 |
+
image_url,
|
120 |
+
self.player_name,
|
121 |
+
self.player_selected_paragraph,
|
122 |
+
self.player_backend_user_id,
|
123 |
+
)
|
124 |
+
|
125 |
+
public_url = self.upload_blob_and_get_public_url(
|
126 |
+
"mes_completion_rewards", source_file, f"2023_mes/{source_file}"
|
127 |
+
)
|
128 |
+
self.player_certificate_url = public_url
|
129 |
+
|
130 |
+
return gr.Image(public_url, visible=True)
|
131 |
+
|
132 |
+
def to_dict(self):
|
133 |
+
return {
|
134 |
+
"player_backend_user_id": self.player_backend_user_id,
|
135 |
+
"player_name": self.player_name,
|
136 |
+
"background_url": self.background_url,
|
137 |
+
"player_selected_model": self.player_selected_model,
|
138 |
+
"player_selected_paragraph": self.player_selected_paragraph,
|
139 |
+
"paragraph_openai": self.paragraph_openai,
|
140 |
+
"paragraph_aws": self.paragraph_aws,
|
141 |
+
"paragraph_google": self.paragraph_google,
|
142 |
+
"paragraph_mtk": self.paragraph_mtk,
|
143 |
+
"player_certificate_url": self.player_certificate_url,
|
144 |
+
"created_at_date": datetime.now().date(),
|
145 |
+
}
|
146 |
+
|
147 |
+
def insert_data_into_bigquery(self, client, dataset_id, table_id, rows_to_insert):
|
148 |
+
table_ref = client.dataset(dataset_id).table(table_id)
|
149 |
+
table = client.get_table(table_ref)
|
150 |
+
|
151 |
+
errors = client.insert_rows(table, rows_to_insert)
|
152 |
+
|
153 |
+
if errors:
|
154 |
+
logging.info("Errors occurred while inserting rows:")
|
155 |
+
for error in errors:
|
156 |
+
print(error)
|
157 |
+
else:
|
158 |
+
logging.info(f"Inserted {len(rows_to_insert)} rows successfully.")
|
159 |
+
|
160 |
+
def complete_reward(
|
161 |
+
self,
|
162 |
+
):
|
163 |
+
insert_row = self.to_dict()
|
164 |
+
self.insert_data_into_bigquery(
|
165 |
+
self.gbq_client, "streaming_log", "log_mes_completion_rewards", [insert_row]
|
166 |
+
)
|
167 |
+
logging.info(
|
168 |
+
f"Player {insert_row['player_backend_user_id']} rendered successfully."
|
169 |
+
)
|
170 |
+
|
171 |
+
with open("../data/completion_reward_issue_status.json") as f:
|
172 |
+
completion_reward_issue_status_dict = json.load(f)
|
173 |
+
|
174 |
+
completion_reward_issue_status_dict[
|
175 |
+
insert_row["player_backend_user_id"]
|
176 |
+
] = self.player_certificate_url
|
177 |
+
|
178 |
+
with open("../data/completion_reward_issue_status.json", "w") as f:
|
179 |
+
json.dump(completion_reward_issue_status_dict, f)
|
180 |
+
|
181 |
+
def upload_blob_and_get_public_url(
|
182 |
+
self, bucket_name, source_file_name, destination_blob_name
|
183 |
+
):
|
184 |
+
"""Uploads a file to the bucket and makes it publicly accessible."""
|
185 |
+
# Initialize a storage client
|
186 |
+
bucket = self.gcs_client.bucket(bucket_name)
|
187 |
+
blob = bucket.blob(destination_blob_name)
|
188 |
+
|
189 |
+
# Upload the file
|
190 |
+
blob.upload_from_filename(source_file_name)
|
191 |
+
|
192 |
+
# The public URL can be used to directly access the uploaded file via HTTP
|
193 |
+
public_url = blob.public_url
|
194 |
+
|
195 |
+
logging.info(f"File {source_file_name} uploaded to {destination_blob_name}.")
|
196 |
+
|
197 |
+
return public_url
|
198 |
+
|
199 |
+
|
200 |
+
class OpenAIAgent:
|
201 |
+
def __init__(self):
|
202 |
+
self.temperature = 0.8
|
203 |
+
self.frequency_penalty = 0
|
204 |
+
self.presence_penalty = 0
|
205 |
+
self.max_tokens = 2048
|
206 |
+
|
207 |
+
def get_story(self, user_log):
|
208 |
+
system_prompt = """
|
209 |
+
我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
|
210 |
+
- 以「你」稱呼學生
|
211 |
+
- 請用 500 字以內的短篇故事
|
212 |
+
- 試著合併故事記錄成一段連貫、有吸引力的故事
|
213 |
+
- 請使用 zh_TW
|
214 |
+
最後,I'll tip $200
|
215 |
+
"""
|
216 |
+
|
217 |
+
user_log = f"""
|
218 |
+
```{user_log}
|
219 |
+
```
|
220 |
+
"""
|
221 |
+
|
222 |
+
messages = [
|
223 |
+
{
|
224 |
+
"role": "system",
|
225 |
+
"content": f"{system_prompt}",
|
226 |
+
},
|
227 |
+
{
|
228 |
+
"role": "user",
|
229 |
+
"content": f"{user_log}",
|
230 |
+
},
|
231 |
+
]
|
232 |
+
|
233 |
+
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
234 |
+
response = None
|
235 |
+
|
236 |
+
retry_attempts = 0
|
237 |
+
while retry_attempts < 5:
|
238 |
+
try:
|
239 |
+
response = client.chat.completions.create(
|
240 |
+
model="gpt-3.5-turbo",
|
241 |
+
messages=messages,
|
242 |
+
temperature=self.temperature,
|
243 |
+
max_tokens=self.max_tokens,
|
244 |
+
frequency_penalty=self.frequency_penalty,
|
245 |
+
presence_penalty=self.presence_penalty,
|
246 |
+
)
|
247 |
+
chinese_converter = OpenCC("s2tw")
|
248 |
+
return chinese_converter.convert(response.choices[0].message.content)
|
249 |
+
|
250 |
+
except Exception as e:
|
251 |
+
retry_attempts += 1
|
252 |
+
logging.error(f"Attempt {retry_attempts}: {e}")
|
253 |
+
time.sleep(1 * retry_attempts)
|
254 |
+
|
255 |
+
def get_background(self):
|
256 |
+
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
257 |
+
image_url = None
|
258 |
+
|
259 |
+
retry_attempts = 0
|
260 |
+
while retry_attempts < 5:
|
261 |
+
try:
|
262 |
+
response = client.images.generate(
|
263 |
+
model="dall-e-3",
|
264 |
+
prompt="Create an image in a retro Ghibli style, with a focus on a universe theme. The artwork should maintain the traditional hand-drawn animation look characteristic of Ghibli. Imagine a scene set in outer space or a fantastical cosmic environment, rich with vibrant and varied color palettes to capture the mystery and majesty of the universe. The background should be detailed, showcasing stars, planets, and nebulae, blending the Ghibli style's nostalgia and emotional depth with the awe-inspiring aspects of space. The overall feel should be timeless, merging the natural wonder of the cosmos with the storytelling and emotional resonance typical of the retro Ghibli aesthetic. Soft lighting and gentle shading should be used to enhance the dreamlike, otherworldly quality of the scene.",
|
265 |
+
size="1024x1024",
|
266 |
+
quality="standard",
|
267 |
+
n=1,
|
268 |
+
)
|
269 |
+
|
270 |
+
image_url = response.data[0].url
|
271 |
+
return image_url
|
272 |
+
|
273 |
+
except Exception as e:
|
274 |
+
retry_attempts += 1
|
275 |
+
logging.error(f"Attempt {retry_attempts}: {e}")
|
276 |
+
time.sleep(1 * retry_attempts) # exponential backoff
|
277 |
+
|
278 |
+
|
279 |
+
class AWSAgent:
|
280 |
+
def get_story(self, user_log):
|
281 |
+
system_prompt = """
|
282 |
+
我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
|
283 |
+
- 以「你」稱呼學生
|
284 |
+
- 請用 500
|
285 |
+
- 試著合併故事記錄成一段連貫、有吸引力的故事
|
286 |
+
- 請使用 zh_TW
|
287 |
+
最後,I'll tip $200
|
288 |
+
"""
|
289 |
+
|
290 |
+
user_log = f"""
|
291 |
+
```{user_log}
|
292 |
+
```
|
293 |
+
"""
|
294 |
+
client = AnthropicBedrock(
|
295 |
+
aws_access_key=os.getenv("AWS_ACCESS_KEY"),
|
296 |
+
aws_secret_key=os.getenv("AWS_SECRET_KEY"),
|
297 |
+
aws_region="us-west-2",
|
298 |
+
)
|
299 |
+
|
300 |
+
retry_attempts = 0
|
301 |
+
while retry_attempts < 5:
|
302 |
+
try:
|
303 |
+
completion = client.completions.create(
|
304 |
+
model="anthropic.claude-v2",
|
305 |
+
max_tokens_to_sample=2048,
|
306 |
+
prompt=f"{anthropic_bedrock.HUMAN_PROMPT}{system_prompt},以下是我的故事紀錄```{user_log}``` {anthropic_bedrock.AI_PROMPT}",
|
307 |
+
)
|
308 |
+
chinese_converter = OpenCC("s2tw")
|
309 |
+
return chinese_converter.convert(completion.completion)
|
310 |
+
|
311 |
+
except Exception as e:
|
312 |
+
retry_attempts += 1
|
313 |
+
logging.error(f"Attempt {retry_attempts}: {e}")
|
314 |
+
time.sleep(1 * retry_attempts)
|
315 |
+
|
316 |
+
|
317 |
+
class GoogleAgent:
|
318 |
+
def get_story(self, user_log):
|
319 |
+
credentials, _ = auth.default()
|
320 |
+
credentials.refresh(Request())
|
321 |
+
|
322 |
+
url = f"https://us-central1-aiplatform.googleapis.com/v1/projects/junyiacademy/locations/us-central1/publishers/google/models/gemini-pro:streamGenerateContent?"
|
323 |
+
|
324 |
+
# Headers
|
325 |
+
headers = {
|
326 |
+
"Authorization": f"Bearer {credentials.token}",
|
327 |
+
"Content-Type": "application/json",
|
328 |
+
}
|
329 |
+
system_prompt = """
|
330 |
+
我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
|
331 |
+
- 以「你」稱呼學生
|
332 |
+
- 請用 500
|
333 |
+
- 試著合併故事記錄成一段連貫、有吸引力的故事
|
334 |
+
- 請使用 zh_TW
|
335 |
+
最後,I'll tip $200
|
336 |
+
"""
|
337 |
+
|
338 |
+
user_log = f"""
|
339 |
+
```{user_log}
|
340 |
+
```
|
341 |
+
"""
|
342 |
+
|
343 |
+
# Provided payload
|
344 |
+
payload = {
|
345 |
+
"contents": [
|
346 |
+
{
|
347 |
+
"role": "USER",
|
348 |
+
"parts": {"text": f"{system_prompt}, 以下是我的冒險故事 ```{user_log}```"},
|
349 |
+
},
|
350 |
+
],
|
351 |
+
"safety_settings": {
|
352 |
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
353 |
+
"threshold": "BLOCK_LOW_AND_ABOVE",
|
354 |
+
},
|
355 |
+
"generation_config": {
|
356 |
+
"temperature": 0.2,
|
357 |
+
"topP": 0.8,
|
358 |
+
"topK": 40,
|
359 |
+
"maxOutputTokens": 2048,
|
360 |
+
},
|
361 |
+
}
|
362 |
+
|
363 |
+
retry_attempts = 0
|
364 |
+
while retry_attempts < 5:
|
365 |
+
try:
|
366 |
+
response = requests.post(url, headers=headers, data=json.dumps(payload))
|
367 |
+
chinese_converter = OpenCC("s2tw")
|
368 |
+
|
369 |
+
final_story = ""
|
370 |
+
for i in response.json():
|
371 |
+
final_story += i["candidates"][0]["content"]["parts"][0]["text"]
|
372 |
+
return chinese_converter.convert(final_story)
|
373 |
+
|
374 |
+
except Exception as e:
|
375 |
+
retry_attempts += 1
|
376 |
+
logging.error(f"Attempt {retry_attempts}: {e}")
|
377 |
+
time.sleep(1 * retry_attempts)
|
378 |
+
|
379 |
+
|
380 |
+
class MTKAgent:
|
381 |
+
def get_story(self, user_log):
|
382 |
+
system_prompt = """
|
383 |
+
我正在舉辦一個學習型的活動,參與活動的學生為 1-9 年級,我為他們設計了一個獨特的故事機制,每天每個學生都會收到屬於自己獨特的冒險紀錄,現在我需要你協助我將這些冒險紀錄,製作成一段冒險故事,請
|
384 |
+
- 以「你」稱呼學生
|
385 |
+
- 請用 500
|
386 |
+
- 試著合併故事記錄成一段連貫、有吸引力的故事
|
387 |
+
- 請使用 zh_TW
|
388 |
+
"""
|
389 |
+
|
390 |
+
user_log = f"""
|
391 |
+
```{user_log}
|
392 |
+
```
|
393 |
+
"""
|
394 |
+
|
395 |
+
BASE_URL = "http://35.229.245.251:8008/v1"
|
396 |
+
TOKEN = os.getenv("MTK_TOKEN")
|
397 |
+
MODEL_NAME = "model7-c-chat"
|
398 |
+
TEMPERATURE = 1
|
399 |
+
MAX_TOKENS = 1024
|
400 |
+
TOP_P = 0
|
401 |
+
PRESENCE_PENALTY = 0
|
402 |
+
FREQUENCY_PENALTY = 0
|
403 |
+
message = f"{system_prompt}, 以下是我的冒險故事 ```{user_log}```"
|
404 |
+
url = os.path.join(BASE_URL, "chat/completions")
|
405 |
+
headers = {
|
406 |
+
"accept": "application/json",
|
407 |
+
"Authorization": f"Bearer {TOKEN}",
|
408 |
+
"Content-Type": "application/json",
|
409 |
+
}
|
410 |
+
data = {
|
411 |
+
"model": MODEL_NAME,
|
412 |
+
"messages": str(message),
|
413 |
+
"temperature": TEMPERATURE,
|
414 |
+
"n": 1,
|
415 |
+
"max_tokens": MAX_TOKENS,
|
416 |
+
"stop": "",
|
417 |
+
"top_p": TOP_P,
|
418 |
+
"logprobs": 0,
|
419 |
+
"echo": False,
|
420 |
+
"presence_penalty": PRESENCE_PENALTY,
|
421 |
+
"frequency_penalty": FREQUENCY_PENALTY,
|
422 |
+
}
|
423 |
+
|
424 |
+
retry_attempts = 0
|
425 |
+
while retry_attempts < 5:
|
426 |
+
try:
|
427 |
+
response = requests.post(
|
428 |
+
url, headers=headers, data=json.dumps(data)
|
429 |
+
).json()
|
430 |
+
response_text = response["choices"][0]["message"]["content"]
|
431 |
+
|
432 |
+
matched_content = re.search("```(.+)", response_text, re.DOTALL)
|
433 |
+
extracted_content = (
|
434 |
+
matched_content.group(1).strip() if matched_content else None
|
435 |
+
)
|
436 |
+
|
437 |
+
chinese_converter = OpenCC("s2tw")
|
438 |
+
|
439 |
+
if extracted_content:
|
440 |
+
return chinese_converter.convert(extracted_content)
|
441 |
+
else:
|
442 |
+
return chinese_converter.convert(response_text)
|
443 |
+
|
444 |
+
except Exception as e:
|
445 |
+
retry_attempts += 1
|
446 |
+
logging.error(f"Attempt {retry_attempts}: {e}")
|
447 |
+
time.sleep(1 * retry_attempts)
|
448 |
+
|
449 |
+
|
450 |
+
class ImageProcessor:
|
451 |
+
@staticmethod
|
452 |
+
def draw_shadow(
|
453 |
+
image, box, radius, offset=(10, 10), shadow_color=(0, 0, 0, 128), blur_radius=5
|
454 |
+
):
|
455 |
+
shadow_image = Image.new("RGBA", image.size, (0, 0, 0, 0))
|
456 |
+
shadow_draw = ImageDraw.Draw(shadow_image)
|
457 |
+
shadow_box = [
|
458 |
+
box[0] + offset[0],
|
459 |
+
box[1] + offset[1],
|
460 |
+
box[2] + offset[0],
|
461 |
+
box[3] + offset[1],
|
462 |
+
]
|
463 |
+
shadow_draw.rounded_rectangle(shadow_box, fill=shadow_color, radius=radius)
|
464 |
+
shadow_image = shadow_image.filter(ImageFilter.GaussianBlur(blur_radius))
|
465 |
+
image.paste(shadow_image, (0, 0), shadow_image)
|
466 |
+
|
467 |
+
@staticmethod
|
468 |
+
def generate_reward(url, player_name, paragraph, player_backend_user_id):
|
469 |
+
retry_attempts = 0
|
470 |
+
while retry_attempts < 5:
|
471 |
+
try:
|
472 |
+
response = requests.get(url)
|
473 |
+
break
|
474 |
+
except requests.RequestException as e:
|
475 |
+
retry_attempts += 1
|
476 |
+
logging.error(f"Attempt {retry_attempts}: {e}")
|
477 |
+
time.sleep(1 * retry_attempts) # exponential backoff
|
478 |
+
|
479 |
+
image_bytes = io.BytesIO(response.content)
|
480 |
+
img = Image.open(image_bytes)
|
481 |
+
|
482 |
+
tmp_img = Image.new("RGBA", img.size, (0, 0, 0, 0))
|
483 |
+
draw = ImageDraw.Draw(tmp_img)
|
484 |
+
|
485 |
+
# Draw the box
|
486 |
+
left, right = 50, img.width - 50
|
487 |
+
box_height = 500
|
488 |
+
top = (img.height - box_height) // 2
|
489 |
+
bottom = (img.height + box_height) // 2
|
490 |
+
border_radius = 20
|
491 |
+
|
492 |
+
# Draw the rounded rectangle
|
493 |
+
fill_color = (255, 255, 255, 220)
|
494 |
+
draw.rounded_rectangle(
|
495 |
+
[left, top, right, bottom],
|
496 |
+
fill=fill_color,
|
497 |
+
outline=None,
|
498 |
+
radius=border_radius,
|
499 |
+
)
|
500 |
+
|
501 |
+
img.paste(Image.alpha_composite(img.convert("RGBA"), tmp_img), (0, 0), tmp_img)
|
502 |
+
|
503 |
+
draw = ImageDraw.Draw(img)
|
504 |
+
|
505 |
+
# Draw the text
|
506 |
+
title_font = ImageFont.truetype("NotoSansTC-Bold.ttf", 36)
|
507 |
+
body_font = ImageFont.truetype("NotoSansTC-Light.ttf", 12)
|
508 |
+
|
509 |
+
# Title text
|
510 |
+
title = f"光束守護者 - {player_name} 的冒險故事"
|
511 |
+
title_x, title_y = left + 20, top + 20 # Adjust padding as needed
|
512 |
+
draw.text((title_x, title_y), title, font=title_font, fill="black")
|
513 |
+
|
514 |
+
# Paragraph text with newlines
|
515 |
+
body_x, body_y = left + 20, title_y + 60 # Adjust position as needed
|
516 |
+
|
517 |
+
for line in paragraph.split("\n"):
|
518 |
+
wrapped_lines = textwrap.wrap(line, width=55)
|
519 |
+
for wrapped_line in wrapped_lines:
|
520 |
+
draw.text((body_x, body_y), wrapped_line, font=body_font, fill="black")
|
521 |
+
body_y += 30
|
522 |
+
|
523 |
+
# Save the image with the text
|
524 |
+
|
525 |
+
def get_md5_hash(text):
|
526 |
+
return hashlib.md5(text.encode("utf-8")).hexdigest()
|
527 |
+
|
528 |
+
updated_image_path = f"certificate_{get_md5_hash(player_backend_user_id)}.png"
|
529 |
+
img.save(updated_image_path)
|
530 |
+
|
531 |
+
return updated_image_path
|
utils/completion_reward_utils.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
|
3 |
+
import gradio as gr
|
4 |
+
|
5 |
+
|
6 |
+
def get_llm_response(completion_reward, *args):
|
7 |
+
return completion_reward.get_llm_response(*args)
|
8 |
+
|
9 |
+
|
10 |
+
def set_player_name(completion_reward, *args):
|
11 |
+
return completion_reward.set_player_name(*args)
|
12 |
+
|
13 |
+
|
14 |
+
def display_class_info(completion_reward, *args):
|
15 |
+
return completion_reward.display_class_info(*args)
|
16 |
+
|
17 |
+
|
18 |
+
def set_player_selected_character(completion_reward, *args):
|
19 |
+
return completion_reward.set_player_selected_character(*args)
|
20 |
+
|
21 |
+
|
22 |
+
def create_certificate(completion_reward, *args):
|
23 |
+
return completion_reward.create_certificate(*args)
|
24 |
+
|
25 |
+
|
26 |
+
def complete_reward(completion_reward, *args):
|
27 |
+
return completion_reward.complete_reward(*args)
|
28 |
+
|
29 |
+
|
30 |
+
def check_is_in_completion_reward(player_backend_user_id):
|
31 |
+
with open("../data/completion_reward_issue_status.json") as f:
|
32 |
+
completion_reward_issue_status_dict = json.load(f)
|
33 |
+
|
34 |
+
if player_backend_user_id in completion_reward_issue_status_dict:
|
35 |
+
preview_list = ["[email protected]", "http://googleid.junyiacademy.org/111939868185365078143"]
|
36 |
+
if player_backend_user_id in preview_list:
|
37 |
+
value = completion_reward_issue_status_dict[player_backend_user_id]
|
38 |
+
if value == "not_issued":
|
39 |
+
return (
|
40 |
+
gr.update(visible=False),
|
41 |
+
gr.update(visible=True),
|
42 |
+
gr.update(visible=True),
|
43 |
+
gr.update(visible=True),
|
44 |
+
gr.update(visible=False),
|
45 |
+
gr.update(visible=False),
|
46 |
+
gr.update(visible=False),
|
47 |
+
)
|
48 |
+
else:
|
49 |
+
return (
|
50 |
+
gr.update(visible=False),
|
51 |
+
gr.update(visible=False),
|
52 |
+
gr.update(visible=False),
|
53 |
+
gr.update(visible=False),
|
54 |
+
gr.update(visible=False),
|
55 |
+
gr.update(visible=False),
|
56 |
+
gr.Image(value, visible=True),
|
57 |
+
)
|
58 |
+
else:
|
59 |
+
return (
|
60 |
+
gr.update(visible=False),
|
61 |
+
gr.update(visible=False),
|
62 |
+
gr.update(visible=False),
|
63 |
+
gr.update(visible=False),
|
64 |
+
gr.update(visible=False),
|
65 |
+
gr.update(visible=True),
|
66 |
+
gr.update(visible=False),
|
67 |
+
)
|
68 |
+
|
69 |
+
else:
|
70 |
+
return (
|
71 |
+
gr.update(visible=False),
|
72 |
+
gr.update(visible=False),
|
73 |
+
gr.update(visible=False),
|
74 |
+
gr.update(visible=False),
|
75 |
+
gr.update(visible=True),
|
76 |
+
gr.update(visible=False),
|
77 |
+
)
|
utils/mes_player_activity_model.py
CHANGED
@@ -1,20 +1,19 @@
|
|
1 |
from datetime import datetime
|
2 |
|
3 |
-
|
|
|
4 |
def __init__(self) -> None:
|
5 |
self.player_backend_user_id = None
|
6 |
self.login_timestamp = datetime.now()
|
7 |
self.rendered_timestamp = None
|
8 |
|
9 |
def render_finished(self, player_info):
|
10 |
-
self.player_backend_user_id = player_info[
|
11 |
self.rendered_timestamp = datetime.now()
|
12 |
|
13 |
def to_dict(self):
|
14 |
return {
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
}
|
19 |
-
|
20 |
-
|
|
|
1 |
from datetime import datetime
|
2 |
|
3 |
+
|
4 |
+
class PlayerActivity:
|
5 |
def __init__(self) -> None:
|
6 |
self.player_backend_user_id = None
|
7 |
self.login_timestamp = datetime.now()
|
8 |
self.rendered_timestamp = None
|
9 |
|
10 |
def render_finished(self, player_info):
|
11 |
+
self.player_backend_user_id = player_info["player_backend_user_id"]
|
12 |
self.rendered_timestamp = datetime.now()
|
13 |
|
14 |
def to_dict(self):
|
15 |
return {
|
16 |
+
"player_backend_user_id": self.player_backend_user_id,
|
17 |
+
"login_timestamp": self.login_timestamp,
|
18 |
+
"rendered_timestamp": self.rendered_timestamp,
|
19 |
}
|
|
|
|
utils/mes_player_model.py
CHANGED
@@ -35,9 +35,7 @@ class Player:
|
|
35 |
# and the rewards_status is not yet initialized
|
36 |
if init:
|
37 |
self.assign_group()
|
38 |
-
self.add_adventure_log(
|
39 |
-
"你並未參與這次與狐貍貓的冒險,請在下次活動中參與冒險吧!"
|
40 |
-
)
|
41 |
if available_achievements is not None:
|
42 |
self.rewards_status.update(
|
43 |
{
|
|
|
35 |
# and the rewards_status is not yet initialized
|
36 |
if init:
|
37 |
self.assign_group()
|
38 |
+
self.add_adventure_log("你並未參與這次與狐貍貓的冒險,請在下次活動中參與冒險吧!")
|
|
|
|
|
39 |
if available_achievements is not None:
|
40 |
self.rewards_status.update(
|
41 |
{
|
utils/utils.py
CHANGED
@@ -207,6 +207,7 @@ def render_player_data(player_info: gr.State):
|
|
207 |
current_story,
|
208 |
)
|
209 |
|
|
|
210 |
def insert_data_into_bigquery(client, dataset_id, table_id, rows_to_insert):
|
211 |
# Specify the destination table
|
212 |
table_ref = client.dataset(dataset_id).table(table_id)
|
@@ -223,9 +224,13 @@ def insert_data_into_bigquery(client, dataset_id, table_id, rows_to_insert):
|
|
223 |
else:
|
224 |
logging.info(f"Inserted {len(rows_to_insert)} rows successfully.")
|
225 |
|
|
|
226 |
def render_finished(player_activity, *args):
|
227 |
player_activity.render_finished(*args)
|
228 |
insert_row = player_activity.to_dict()
|
229 |
-
insert_data_into_bigquery(
|
230 |
-
|
231 |
-
|
|
|
|
|
|
|
|
207 |
current_story,
|
208 |
)
|
209 |
|
210 |
+
|
211 |
def insert_data_into_bigquery(client, dataset_id, table_id, rows_to_insert):
|
212 |
# Specify the destination table
|
213 |
table_ref = client.dataset(dataset_id).table(table_id)
|
|
|
224 |
else:
|
225 |
logging.info(f"Inserted {len(rows_to_insert)} rows successfully.")
|
226 |
|
227 |
+
|
228 |
def render_finished(player_activity, *args):
|
229 |
player_activity.render_finished(*args)
|
230 |
insert_row = player_activity.to_dict()
|
231 |
+
insert_data_into_bigquery(
|
232 |
+
client, "streaming_log", "log_mes_player_login_activity", [insert_row]
|
233 |
+
)
|
234 |
+
logging.info(
|
235 |
+
f"Player {insert_row['player_backend_user_id']} rendered successfully."
|
236 |
+
)
|