gaeunseo commited on
Commit
c8068c7
ยท
verified ยท
1 Parent(s): 82fe083

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -133
app.py CHANGED
@@ -3,124 +3,92 @@ import gradio as gr
3
  import random
4
  import os
5
  import threading
 
6
  from datasets import load_dataset
7
 
8
- from sqlalchemy import create_engine, Column, Integer, String, Boolean, Text
9
- from sqlalchemy.ext.declarative import declarative_base
10
- from sqlalchemy.orm import sessionmaker
11
 
12
- #############################################
13
- # 1. SQLAlchemy ์„ค์ • ๋ฐ ๋ชจ๋ธ ์ •์˜
14
- #############################################
15
-
16
- DATABASE_URL = "mysql://root:0112@127.0.0.1:3306/Taskmaster_sample_data"
17
-
18
- # SQLite์˜ ๋™์‹œ ์ ‘๊ทผ์„ ์œ„ํ•ด check_same_thread ์˜ต์…˜ ์„ค์ •
19
- engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
20
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
21
- Base = declarative_base()
22
-
23
- class Conversation(Base):
24
- __tablename__ = "conversations"
25
- id = Column(Integer, primary_key=True, index=True)
26
- conversation_id = Column(String, index=True)
27
- used = Column(Boolean, default=False)
28
- overlapping = Column(String)
29
- text = Column(Text)
30
- human_message = Column(Text)
31
- ai_message = Column(Text)
32
-
33
- # ํ…Œ์ด๋ธ” ์ƒ์„ฑ
34
- Base.metadata.create_all(bind=engine)
35
-
36
- # ๋™์‹œ ์ ‘๊ทผ์„ ์œ„ํ•œ ์ „์—ญ ๋ฝ
37
- db_lock = threading.Lock()
38
-
39
- #############################################
40
- # 2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜
41
- #############################################
42
-
43
- def init_db():
44
  """
45
- ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด, Hugging Face ๋ฐ์ดํ„ฐ์…‹์„ ๋กœ๋“œํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค.
 
46
  """
47
- session = SessionLocal()
48
- try:
49
- count = session.query(Conversation).count()
50
- if count == 0:
51
- ds = load_dataset("gaeunseo/Taskmaster_sample_data", split="train")
52
- data = list(ds)
53
- for row in data:
54
- # ๊ธฐ๋ณธ๊ฐ’ ์ฑ„์šฐ๊ธฐ: used๋Š” False, overlapping์€ row์— ์—†๋‹ค๋ฉด ๋นˆ ๋ฌธ์ž์—ด๋กœ ์ฒ˜๋ฆฌ
55
- text = row.get("text", "")
56
- conv = Conversation(
57
- conversation_id = row.get("conversation_id", ""),
58
- used = row.get("used", False),
59
- overlapping = row.get("overlapping", ""),
60
- text = text,
61
- human_message = text.split("[turn]")[0].strip() if "[turn]" in text else text,
62
- ai_message = text.split("[turn]")[1].strip() if "[turn]" in text and len(text.split("[turn]")) > 1 else ""
63
- )
64
- session.add(conv)
65
- session.commit()
66
- finally:
67
- session.close()
68
 
69
- # ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ (์ตœ์ดˆ ์‹คํ–‰ ์‹œ ๋ฐ์ดํ„ฐ์…‹์„ ๋ถˆ๋Ÿฌ์™€ DB์— ์ €์žฅ)
70
- init_db()
 
 
 
71
 
72
- #############################################
73
- # 3. ๋Œ€ํ™” ๊ทธ๋ฃน์„ ํ• ๋‹นํ•˜๋Š” ํ•จ์ˆ˜ (๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€)
74
- #############################################
 
 
 
 
75
 
76
  def get_random_row_from_dataset():
77
  """
78
- ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ conversation_id๋ณ„๋กœ ๊ทธ๋ฃนํ™”ํ•œ ํ›„,
79
- - ๊ทธ๋ฃน ๋‚ด์˜ ๋ชจ๋“  ํ–‰์˜ used๊ฐ€ False์ด๊ณ ,
80
- - ๊ทธ๋ฃน ๋‚ด์— overlapping ์ปฌ๋Ÿผ์ด "TT"์ธ ํ–‰์ด ์žˆ๋Š” ๊ทธ๋ฃน๋งŒ valid๋กœ ๊ฐ„์ฃผํ•ฉ๋‹ˆ๋‹ค.
81
- validํ•œ ๊ทธ๋ฃน ์ค‘ ๋žœ๋คํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์„ ์„ ํƒํ•œ ํ›„,
82
- - ํ•ด๋‹น ๊ทธ๋ฃน์˜ ๋ชจ๋“  ํ–‰์˜ used ๊ฐ’์„ True๋กœ ์—…๋ฐ์ดํŠธํ•˜์—ฌ(์ฆ‰, ๊ทธ๋ฃน ์ „์ฒด๋ฅผ ํ• ๋‹น)
83
- - ์„ ํƒ๋œ ๊ทธ๋ฃน ๋‚ด์—์„œ overlapping ์ปฌ๋Ÿผ์ด "TT"์ธ ์ฒซ ๋ฒˆ์งธ ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
84
  """
85
- session = SessionLocal()
86
- try:
87
- with db_lock:
88
- convs = session.query(Conversation).all()
89
- groups = {}
90
- for conv in convs:
91
- groups.setdefault(conv.conversation_id, []).append(conv)
92
- valid_groups = []
93
- for cid, group in groups.items():
94
- if all(not conv.used for conv in group) and any(conv.overlapping == "TT" for conv in group):
95
- valid_groups.append(group)
96
- if not valid_groups:
97
- return None
98
- chosen_group = random.choice(valid_groups)
99
- for conv in chosen_group:
100
- conv.used = True
101
- session.commit()
102
- for conv in chosen_group:
103
- if conv.overlapping == "TT":
104
- return conv
105
- return None
106
- finally:
107
- session.close()
108
-
109
- #############################################
110
- # 4. ์ดˆ๊ธฐ ๋Œ€ํ™” ์„ค์ • (์ „์—ญ ๋ณ€์ˆ˜ ์„ค์ •)
111
- #############################################
112
 
113
- initial_conv = get_random_row_from_dataset()
114
- if initial_conv is None:
 
 
115
  human_message = "No valid conversation available."
116
  ai_message = "No valid conversation available."
117
  else:
118
- raw_text = initial_conv.text
119
- human_message = raw_text.split("[turn]")[0].strip() if "[turn]" in raw_text else raw_text
120
- ai_message = raw_text.split("[turn]")[1].strip() if "[turn]" in raw_text and len(raw_text.split("[turn]")) > 1 else ""
121
 
122
  #############################################
123
- # 5. ์ฑ„ํŒ… ์ธํ„ฐํŽ˜์ด์Šค ๊ด€๋ จ ํ•จ์ˆ˜ (๋งํ’์„ , ํƒ€์ดํ•‘ ํšจ๊ณผ, ํŽธ์ง‘ ๊ธฐ๋Šฅ)
124
  #############################################
125
 
126
  def get_initial_human_html():
@@ -149,44 +117,47 @@ def stream_human_message():
149
  bubble_end = "</div>"
150
  emoji_html = "<div class='emoji'>๐Ÿง‘</div>"
151
  wrapper_end = "</div>"
 
 
152
  yield wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end
 
 
153
  for i, ch in enumerate(human_message):
154
- bubble_content += f"<span data-index='{i}'>{ch}</span>"
155
- current_html = wrapper_start + bubble_start + bubble_content + bubble_end + emoji_html + wrapper_end
156
- yield current_html
157
- time.sleep(0.05)
158
 
159
  def submit_edit(edited_text):
160
  """
161
  Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜.
162
- 1. ํŽธ์ง‘๋œ human ๋ฉ”์‹œ์ง€(โœ‚๏ธ ์•ž๋ถ€๋ถ„)๋ฅผ ์ƒˆ ํ–‰์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
163
  2. get_random_row_from_dataset()์„ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋Œ€ํ™”๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์ „์—ญ ๋ณ€์ˆ˜ human_message์™€ ai_message๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
164
  3. ์ดˆ๊ธฐ ์ƒํƒœ์˜ human ๋งํ’์„ ๊ณผ ai ๋งํ’์„  HTML์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฆฌ์…‹ํ•ฉ๋‹ˆ๋‹ค.
165
  """
166
- session = SessionLocal()
167
- try:
168
- with db_lock:
169
- new_conv = Conversation(
170
- conversation_id = "edited_" + str(random.randint(1000, 9999)),
171
- used = False,
172
- overlapping = "",
173
- text = edited_text,
174
- human_message = edited_text,
175
- ai_message = ""
176
- )
177
- session.add(new_conv)
178
- session.commit()
179
- new_conv_data = get_random_row_from_dataset()
180
- if new_conv_data is None:
181
- global human_message, ai_message
182
- human_message = "No valid conversation available."
183
- ai_message = "No valid conversation available."
184
- else:
185
- raw_text = new_conv_data.text
186
- human_message = raw_text.split("[turn]")[0].strip() if "[turn]" in raw_text else raw_text
187
- ai_message = raw_text.split("[turn]")[1].strip() if "[turn]" in raw_text and len(raw_text.split("[turn]")) > 1 else ""
188
- finally:
189
- session.close()
190
  new_human_html = get_initial_human_html()
191
  new_ai_html = f"""
192
  <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;">
@@ -197,11 +168,11 @@ def submit_edit(edited_text):
197
  return new_human_html, new_ai_html
198
 
199
  #############################################
200
- # 6. Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ
201
  #############################################
202
 
203
  with gr.Blocks() as demo:
204
- # (A) ํŽ˜์ด์ง€ ์ƒ๋‹จ ์Šคํฌ๋ฆฝํŠธ: Human ๋งํ’์„  ๋‚ด์˜ ๊ฐ <span data-index="..."> ํด๋ฆญ ์‹œ,
205
  # ํ•ด๋‹น ์œ„์น˜์— โœ‚๏ธ ์•„์ด์ฝ˜์ด ์‚ฝ์ž…๋˜๊ณ , ๊ทธ ์ดํ›„ ํ…์ŠคํŠธ๊ฐ€ ํšŒ์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.
206
  gr.HTML(
207
  """
@@ -230,7 +201,8 @@ with gr.Blocks() as demo:
230
  </script>
231
  """
232
  )
233
- # (B) ์ถ”๊ฐ€ ์Šคํฌ๋ฆฝํŠธ: Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, human_message div์˜ innerText์—์„œ "โœ‚๏ธ" ๊ธฐ์ค€์œผ๋กœ ํŽธ์ง‘๋œ ํ…์ŠคํŠธ(์•ž๋ถ€๋ถ„)๋ฅผ ์ˆจ๊น€ ํ…์ŠคํŠธ๋ฐ•์Šค์— ์—…๋ฐ์ดํŠธ
 
234
  gr.HTML(
235
  """
236
  <script>
@@ -249,6 +221,7 @@ with gr.Blocks() as demo:
249
  </script>
250
  """
251
  )
 
252
  # (C) CSS ์Šคํƒ€์ผ
253
  gr.HTML(
254
  """
 
3
  import random
4
  import os
5
  import threading
6
+ import pandas as pd
7
  from datasets import load_dataset
8
 
9
+ # CSV ํŒŒ์ผ ๊ฒฝ๋กœ์™€ ๋™์‹œ ์ ‘๊ทผ์„ ์œ„ํ•œ Lock ์„ ์–ธ
10
+ DATA_FILE = "global_data.csv"
11
+ data_lock = threading.Lock()
12
 
13
+ def initialize_global_data():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  """
15
+ DATA_FILE์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด, gaeunseo/Taskmaster_sample_data ๋ฐ์ดํ„ฐ์…‹์„ ๋กœ๋“œํ•˜์—ฌ DataFrame์œผ๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ CSV ํŒŒ์ผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
16
+ ์ด๋ฏธ ํŒŒ์ผ์ด ์žˆ์œผ๋ฉด ํŒŒ์ผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด DataFrame์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
17
  """
18
+ if not os.path.exists(DATA_FILE):
19
+ ds = load_dataset("gaeunseo/Taskmaster_sample_data", split="train")
20
+ data = ds.to_pandas()
21
+ # ํ•„์š”ํ•œ ์ปฌ๋Ÿผ์ด ์—†์œผ๋ฉด ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
22
+ if "used" not in data.columns:
23
+ data["used"] = False
24
+ if "overlapping" not in data.columns:
25
+ data["overlapping"] = ""
26
+ if "text" not in data.columns:
27
+ data["text"] = ""
28
+ data.to_csv(DATA_FILE, index=False)
29
+ return data
30
+ else:
31
+ with data_lock:
32
+ df = pd.read_csv(DATA_FILE)
33
+ return df
 
 
 
 
 
34
 
35
+ def load_global_data():
36
+ """CSV ํŒŒ์ผ์—์„œ global_data DataFrame์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค."""
37
+ with data_lock:
38
+ df = pd.read_csv(DATA_FILE)
39
+ return df
40
 
41
+ def save_global_data(df):
42
+ """DataFrame์„ CSV ํŒŒ์ผ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค."""
43
+ with data_lock:
44
+ df.to_csv(DATA_FILE, index=False)
45
+
46
+ # CSV ํŒŒ์ผ์— ์ €์žฅ๋œ global_data ์ดˆ๊ธฐํ™”
47
+ global_data = initialize_global_data()
48
 
49
  def get_random_row_from_dataset():
50
  """
51
+ CSV ํŒŒ์ผ์— ์ €์žฅ๋œ global_data์—์„œ,
52
+ 1. conversation_id๋ณ„๋กœ ๊ทธ๋ฃนํ™”ํ•˜๊ณ ,
53
+ 2. ๊ฐ ๊ทธ๋ฃน์—์„œ ๋ชจ๋“  ํ–‰์˜ used ์ปฌ๋Ÿผ์ด False์ด๋ฉฐ, ๊ทธ๋ฃน ๋‚ด์— overlapping ์ปฌ๋Ÿผ์ด "TT"์ธ ํ–‰์ด ์กด์žฌํ•˜๋Š” ๊ทธ๋ฃน๋งŒ valid๋กœ ๊ฐ„์ฃผํ•ฉ๋‹ˆ๋‹ค.
54
+ validํ•œ ๊ทธ๋ฃน๏ฟฝ๏ฟฝ ์ค‘ ๋žœ๋คํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์„ ์„ ํƒํ•œ ํ›„,
55
+ - ํ•ด๋‹น ๊ทธ๋ฃน์˜ ๋ชจ๋“  ํ–‰์˜ used ๊ฐ’์„ True๋กœ ์—…๋ฐ์ดํŠธ(์ฆ‰, ์ „์ฒด ๊ทธ๋ฃน์„ ํ• ๋‹น)ํ•˜๊ณ  CSV ํŒŒ์ผ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
56
+ - ์„ ํƒ๋œ ๊ทธ๋ฃน ๋‚ด์—์„œ overlapping ์ปฌ๋Ÿผ์ด "TT"์ธ ํ–‰(์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด ์ฒซ ๋ฒˆ์งธ)์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
57
  """
58
+ global global_data
59
+ global_data = load_global_data() # ์ตœ์‹  ๋ฐ์ดํ„ฐ ๋กœ๋“œ
60
+ groups = global_data.groupby('conversation_id')
61
+ valid_groups = []
62
+ for cid, group in groups:
63
+ # ๋ชจ๋“  ํ–‰์˜ used ๊ฐ’์ด False์ด๊ณ , ๊ทธ๋ฃน ๋‚ด์— overlapping ๊ฐ’์ด "TT"์ธ ํ–‰์ด ์žˆ๋Š” ๊ทธ๋ฃน ํ•„ํ„ฐ๋ง
64
+ if group['used'].apply(lambda x: bool(x) == False).all() and (group['overlapping'] == "TT").any():
65
+ valid_groups.append((cid, group))
66
+ if not valid_groups:
67
+ return None
68
+ chosen_cid, chosen_group = random.choice(valid_groups)
69
+ # ์ „์ฒด ๊ทธ๋ฃน์˜ used ๊ฐ’์„ True๋กœ ์—…๋ฐ์ดํŠธ
70
+ global_data.loc[global_data['conversation_id'] == chosen_cid, 'used'] = True
71
+ save_global_data(global_data)
72
+ # ์„ ํƒ๋œ ๊ทธ๋ฃน ๋‚ด์—์„œ overlapping ์ปฌ๋Ÿผ์ด "TT"์ธ ํ–‰์„ ๋ฐ˜ํ™˜ (์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด ์ฒซ ๋ฒˆ์งธ)
73
+ chosen_rows = chosen_group[chosen_group['overlapping'] == "TT"]
74
+ if chosen_rows.empty:
75
+ return None
76
+ chosen_row = chosen_rows.iloc[0]
77
+ return chosen_row.to_dict()
 
 
 
 
 
 
 
78
 
79
+ # --- ์ดˆ๊ธฐ ๋Œ€ํ™” ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ---
80
+ # ๋ฐ์ดํ„ฐ์…‹์˜ text ์ปฌ๋Ÿผ์€ "[turn]"์„ ๊ธฐ์ค€์œผ๋กœ ๋Œ€ํ™”๊ฐ€ ๊ตฌ๋ถ„๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
81
+ row = get_random_row_from_dataset()
82
+ if row is None:
83
  human_message = "No valid conversation available."
84
  ai_message = "No valid conversation available."
85
  else:
86
+ raw_text = row['text']
87
+ human_message = raw_text.split("[turn]")[0].strip()
88
+ ai_message = raw_text.split("[turn]")[1].strip()
89
 
90
  #############################################
91
+ # ์ฑ„ํŒ… ์ธํ„ฐํŽ˜์ด์Šค ๊ด€๋ จ ํ•จ์ˆ˜ (๋งํ’์„ , ํƒ€์ดํ•‘ ํšจ๊ณผ, ํŽธ์ง‘ ๊ธฐ๋Šฅ)
92
  #############################################
93
 
94
  def get_initial_human_html():
 
117
  bubble_end = "</div>"
118
  emoji_html = "<div class='emoji'>๐Ÿง‘</div>"
119
  wrapper_end = "</div>"
120
+
121
+ # ์ดˆ๊ธฐ ์ƒํƒœ: ๋นˆ ๋งํ’์„ ๊ณผ ์ด๋ชจํ‹ฐ์ฝ˜
122
  yield wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end
123
+
124
+ # human_message๋ฅผ ํ•œ ๊ธ€์ž์”ฉ ์ถ”๊ฐ€ (ํƒ€์ดํ•‘ ํšจ๊ณผ)
125
  for i, ch in enumerate(human_message):
126
+ bubble_content += f"<span data-index='{i}'>{ch}</span>"
127
+ current_html = wrapper_start + bubble_start + bubble_content + bubble_end + emoji_html + wrapper_end
128
+ yield current_html
129
+ time.sleep(0.05)
130
 
131
  def submit_edit(edited_text):
132
  """
133
  Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜.
134
+ 1. ํŽธ์ง‘๋œ human ๋ฉ”์‹œ์ง€(โœ‚๏ธ ์•ž๋ถ€๋ถ„)๋ฅผ ์ƒˆ ํ–‰์œผ๋กœ global_data์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
135
  2. get_random_row_from_dataset()์„ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋Œ€ํ™”๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์ „์—ญ ๋ณ€์ˆ˜ human_message์™€ ai_message๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
136
  3. ์ดˆ๊ธฐ ์ƒํƒœ์˜ human ๋งํ’์„ ๊ณผ ai ๋งํ’์„  HTML์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฆฌ์…‹ํ•ฉ๋‹ˆ๋‹ค.
137
  """
138
+ global human_message, ai_message
139
+ data = load_global_data()
140
+ new_row = {
141
+ "conversation_id": "edited_" + str(random.randint(1000, 9999)),
142
+ "used": False,
143
+ "overlapping": "",
144
+ "text": edited_text,
145
+ "human_message": edited_text,
146
+ "ai_message": ""
147
+ }
148
+ new_df = pd.DataFrame([new_row])
149
+ data = pd.concat([data, new_df], ignore_index=True)
150
+ save_global_data(data)
151
+
152
+ new_row_data = get_random_row_from_dataset()
153
+ if new_row_data is None:
154
+ human_message = "No valid conversation available."
155
+ ai_message = "No valid conversation available."
156
+ else:
157
+ raw_text = new_row_data['text']
158
+ human_message = raw_text.split("[turn]")[0].strip()
159
+ ai_message = raw_text.split("[turn]")[1].strip()
160
+
 
161
  new_human_html = get_initial_human_html()
162
  new_ai_html = f"""
163
  <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;">
 
168
  return new_human_html, new_ai_html
169
 
170
  #############################################
171
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ
172
  #############################################
173
 
174
  with gr.Blocks() as demo:
175
+ # (A) ํŽ˜์ด์ง€ ์ƒ๋‹จ ์Šคํฌ๋ฆฝํŠธ: Human ๋งํ’์„  ๋‚ด์˜ ๊ฐ <span data-index="...">๋ฅผ ํด๋ฆญํ•˜๋ฉด,
176
  # ํ•ด๋‹น ์œ„์น˜์— โœ‚๏ธ ์•„์ด์ฝ˜์ด ์‚ฝ์ž…๋˜๊ณ , ๊ทธ ์ดํ›„ ํ…์ŠคํŠธ๊ฐ€ ํšŒ์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.
177
  gr.HTML(
178
  """
 
201
  </script>
202
  """
203
  )
204
+
205
+ # (B) ์ถ”๊ฐ€ ์Šคํฌ๋ฆฝํŠธ: Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, human_message div์˜ innerText์—์„œ "โœ‚๏ธ"๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŽธ์ง‘๋œ ํ…์ŠคํŠธ(์•ž๋ถ€๋ถ„)๋ฅผ ์ˆจ๊น€ ํ…์ŠคํŠธ๋ฐ•์Šค์— ์—…๋ฐ์ดํŠธ
206
  gr.HTML(
207
  """
208
  <script>
 
221
  </script>
222
  """
223
  )
224
+
225
  # (C) CSS ์Šคํƒ€์ผ
226
  gr.HTML(
227
  """