gaeunseo commited on
Commit
7cc2d09
ยท
verified ยท
1 Parent(s): 1e50702

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -171
app.py CHANGED
@@ -3,130 +3,74 @@ 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():
127
- """
128
- ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ, ๋นˆ Human ๋งํ’์„ ๊ณผ ์˜ค๋ฅธ์ชฝ ๐Ÿง‘ ์ด๋ชจํ‹ฐ์ฝ˜์„ ํฌํ•จํ•œ ์ดˆ๊ธฐ HTML ๋ฐ˜ํ™˜
129
- """
130
  wrapper_start = (
131
  """<div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">"""
132
  )
@@ -137,10 +81,6 @@ def get_initial_human_html():
137
  return wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end
138
 
139
  def stream_human_message():
140
- """
141
- Start Typing ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, ์ „์—ญ ๋ณ€์ˆ˜ human_message์˜ ๋‚ด์šฉ์„ ํ•œ ๊ธ€์ž์”ฉ ํƒ€์ดํ•‘ ํšจ๊ณผ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
142
- ์ด์ „ ์ƒํƒœ(โœ‚๏ธ ์•„์ด์ฝ˜, ํšŒ์ƒ‰ ์ฒ˜๋ฆฌ ๋“ฑ)๋Š” ๋ฆฌ์…‹๋ฉ๋‹ˆ๋‹ค.
143
- """
144
  bubble_content = ""
145
  wrapper_start = (
146
  """<div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">"""
@@ -149,44 +89,41 @@ 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,27 +134,31 @@ 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
  """
208
  <script>
209
  document.addEventListener("click", function(event) {
 
210
  if (event.target && event.target.matches("div.speech-bubble.human span[data-index]")) {
211
  var span = event.target;
212
  var container = span.closest("div.speech-bubble.human");
 
213
  var oldScissors = container.querySelectorAll("span.scissor");
214
  oldScissors.forEach(function(s) { s.remove(); });
 
215
  var spans = container.querySelectorAll("span[data-index]");
216
  spans.forEach(function(s) { s.style.color = ''; });
 
217
  var scissor = document.createElement('span');
218
  scissor.textContent = 'โœ‚๏ธ';
219
  scissor.classList.add("scissor");
220
  container.insertBefore(scissor, span.nextSibling);
 
221
  var cutIndex = parseInt(span.getAttribute("data-index"));
222
  spans.forEach(function(s) {
223
  var idx = parseInt(s.getAttribute("data-index"));
@@ -225,30 +166,21 @@ with gr.Blocks() as demo:
225
  s.style.color = "grey";
226
  }
227
  });
 
 
 
 
 
 
228
  }
229
  });
230
  </script>
231
  """
232
  )
233
- # (B) ์ถ”๊ฐ€ ์Šคํฌ๋ฆฝํŠธ: Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, human_message div์˜ innerText์—์„œ "โœ‚๏ธ" ๊ธฐ์ค€์œผ๋กœ ํŽธ์ง‘๋œ ํ…์ŠคํŠธ(์•ž๋ถ€๋ถ„)๋ฅผ ์ˆจ๊น€ ํ…์ŠคํŠธ๋ฐ•์Šค์— ์—…๋ฐ์ดํŠธ
234
- gr.HTML(
235
- """
236
- <script>
237
- document.addEventListener("DOMContentLoaded", function() {
238
- var submitBtn = document.getElementById("submit_btn");
239
- if(submitBtn){
240
- submitBtn.addEventListener("click", function(){
241
- var humanDiv = document.getElementById("human_message");
242
- if(humanDiv){
243
- var edited_text = humanDiv.innerText.split("โœ‚๏ธ")[0];
244
- document.getElementById("edited_text_input").value = edited_text;
245
- }
246
- });
247
- }
248
- });
249
- </script>
250
- """
251
- )
252
  # (C) CSS ์Šคํƒ€์ผ
253
  gr.HTML(
254
  """
@@ -305,9 +237,7 @@ with gr.Blocks() as demo:
305
  gr.Markdown("## Chat Interface")
306
 
307
  with gr.Column(elem_classes="chat-container"):
308
- # Human ๋งํ’์„  (์ดˆ๊ธฐ: ๋นˆ ๋ฉ”์‹œ์ง€ + ๐Ÿง‘ ์ด๋ชจํ‹ฐ์ฝ˜)
309
  human_bubble = gr.HTML(get_initial_human_html())
310
- # AI ๋งํ’์„  (์™ผ์ชฝ: ๐Ÿค– ์ด๋ชจํ‹ฐ์ฝ˜ + ๋ฉ”์‹œ์ง€)
311
  ai_html = f"""
312
  <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;">
313
  <div class="emoji">๐Ÿค–</div>
@@ -319,12 +249,10 @@ with gr.Blocks() as demo:
319
  # ์ˆจ๊น€ ํ…์ŠคํŠธ๋ฐ•์Šค (ํŽธ์ง‘๋œ ํ…์ŠคํŠธ ์ €์žฅ์šฉ)
320
  edited_text_input = gr.Textbox(visible=False, elem_id="edited_text_input")
321
 
322
- # ๋ฒ„ํŠผ ์˜์—ญ: Start Typing๊ณผ Submit ๋ฒ„ํŠผ์„ ๊ฐ™์€ ํ–‰์— ๋ฐฐ์น˜
323
  with gr.Row():
324
  start_button = gr.Button("Start Typing")
325
  submit_button = gr.Button("Submit", elem_id="submit_btn")
326
 
327
- # ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
328
  start_button.click(fn=stream_human_message, outputs=human_bubble)
329
  submit_button.click(fn=submit_edit, inputs=edited_text_input, outputs=[human_bubble, ai_bubble])
330
 
 
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
+ if not os.path.exists(DATA_FILE):
15
+ ds = load_dataset("gaeunseo/Taskmaster_sample_data", split="train")
16
+ data = ds.to_pandas()
17
+ if "used" not in data.columns:
18
+ data["used"] = False
19
+ if "overlapping" not in data.columns:
20
+ data["overlapping"] = ""
21
+ if "text" not in data.columns:
22
+ data["text"] = ""
23
+ data.to_csv(DATA_FILE, index=False)
24
+ return data
25
+ else:
26
+ with data_lock:
27
+ df = pd.read_csv(DATA_FILE)
28
+ return df
 
 
 
 
 
 
 
29
 
30
+ def load_global_data():
31
+ with data_lock:
32
+ df = pd.read_csv(DATA_FILE)
33
+ return df
34
 
35
+ def save_global_data(df):
36
+ with data_lock:
37
+ df.to_csv(DATA_FILE, index=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ global_data = initialize_global_data()
 
 
 
 
 
40
 
41
  def get_random_row_from_dataset():
42
+ global global_data
43
+ global_data = load_global_data() # ์ตœ์‹  ๋ฐ์ดํ„ฐ ๋กœ๋“œ
44
+ groups = global_data.groupby('conversation_id')
45
+ valid_groups = []
46
+ for cid, group in groups:
47
+ if group['used'].apply(lambda x: bool(x) == False).all() and (group['overlapping'] == "TT").any():
48
+ valid_groups.append((cid, group))
49
+ if not valid_groups:
50
+ return None
51
+ chosen_cid, chosen_group = random.choice(valid_groups)
52
+ global_data.loc[global_data['conversation_id'] == chosen_cid, 'used'] = True
53
+ save_global_data(global_data)
54
+ chosen_rows = chosen_group[chosen_group['overlapping'] == "TT"]
55
+ if chosen_rows.empty:
56
+ return None
57
+ chosen_row = chosen_rows.iloc[0]
58
+ return chosen_row.to_dict()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ row = get_random_row_from_dataset()
61
+ if row is None:
 
 
 
 
62
  human_message = "No valid conversation available."
63
  ai_message = "No valid conversation available."
64
  else:
65
+ raw_text = row['text']
66
+ human_message = raw_text.split("[turn]")[0].strip()
67
+ ai_message = raw_text.split("[turn]")[1].strip()
68
 
69
  #############################################
70
+ # ์ฑ„ํŒ… ์ธํ„ฐํŽ˜์ด์Šค ๊ด€๋ จ ํ•จ์ˆ˜
71
  #############################################
72
 
73
  def get_initial_human_html():
 
 
 
74
  wrapper_start = (
75
  """<div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">"""
76
  )
 
81
  return wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end
82
 
83
  def stream_human_message():
 
 
 
 
84
  bubble_content = ""
85
  wrapper_start = (
86
  """<div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">"""
 
89
  bubble_end = "</div>"
90
  emoji_html = "<div class='emoji'>๐Ÿง‘</div>"
91
  wrapper_end = "</div>"
92
+
93
+ # ์ดˆ๊ธฐ ์ƒํƒœ
94
  yield wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end
95
+
96
+ # ํ•œ ๊ธ€์ž์”ฉ ํƒ€์ดํ•‘ ํšจ๊ณผ ์ ์šฉ
97
  for i, ch in enumerate(human_message):
98
+ bubble_content += f"<span data-index='{i}'>{ch}</span>"
99
+ current_html = wrapper_start + bubble_start + bubble_content + bubble_end + emoji_html + wrapper_end
100
+ yield current_html
101
+ time.sleep(0.05)
102
 
103
  def submit_edit(edited_text):
104
+ global human_message, ai_message
105
+ data = load_global_data()
106
+ new_row = {
107
+ "conversation_id": "edited_" + str(random.randint(1000, 9999)),
108
+ "used": False,
109
+ "overlapping": "",
110
+ "text": edited_text,
111
+ "human_message": edited_text,
112
+ "ai_message": ""
113
+ }
114
+ new_df = pd.DataFrame([new_row])
115
+ data = pd.concat([data, new_df], ignore_index=True)
116
+ save_global_data(data)
117
+
118
+ new_row_data = get_random_row_from_dataset()
119
+ if new_row_data is None:
120
+ human_message = "No valid conversation available."
121
+ ai_message = "No valid conversation available."
122
+ else:
123
+ raw_text = new_row_data['text']
124
+ human_message = raw_text.split("[turn]")[0].strip()
125
+ ai_message = raw_text.split("[turn]")[1].strip()
126
+
 
 
 
 
 
 
 
127
  new_human_html = get_initial_human_html()
128
  new_ai_html = f"""
129
  <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;">
 
134
  return new_human_html, new_ai_html
135
 
136
  #############################################
137
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ
138
  #############################################
139
 
140
  with gr.Blocks() as demo:
141
+ # (A) ํ…์ŠคํŠธ ํด๋ฆญ ์‹œ โœ‚๏ธ ์•„์ด์ฝ˜ ์ถ”๊ฐ€ ๋ฐ ํšŒ์ƒ‰ ์ฒ˜๋ฆฌ, ๊ทธ๋ฆฌ๊ณ  ๋ฐ”๋กœ hidden input ์—…๋ฐ์ดํŠธ
 
142
  gr.HTML(
143
  """
144
  <script>
145
  document.addEventListener("click", function(event) {
146
+ // human ๋ฉ”์‹œ์ง€์˜ span์„ ํด๋ฆญํ•˜๋ฉด ์ฒ˜๋ฆฌ
147
  if (event.target && event.target.matches("div.speech-bubble.human span[data-index]")) {
148
  var span = event.target;
149
  var container = span.closest("div.speech-bubble.human");
150
+ // ์ด์ „์— ์ถ”๊ฐ€๋œ โœ‚๏ธ ์•„์ด์ฝ˜ ์ œ๊ฑฐ
151
  var oldScissors = container.querySelectorAll("span.scissor");
152
  oldScissors.forEach(function(s) { s.remove(); });
153
+ // ๋ชจ๋“  span์˜ ์ƒ‰์ƒ ์ดˆ๊ธฐํ™”
154
  var spans = container.querySelectorAll("span[data-index]");
155
  spans.forEach(function(s) { s.style.color = ''; });
156
+ // โœ‚๏ธ ์•„์ด์ฝ˜ ์ƒ์„ฑ ํ›„ span ๋ฐ”๋กœ ๋’ค์— ์‚ฝ์ž…
157
  var scissor = document.createElement('span');
158
  scissor.textContent = 'โœ‚๏ธ';
159
  scissor.classList.add("scissor");
160
  container.insertBefore(scissor, span.nextSibling);
161
+ // ํด๋ฆญํ•œ span์˜ data-index๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ทธ ๋’ค ํ…์ŠคํŠธ ํšŒ์ƒ‰ ์ฒ˜๋ฆฌ
162
  var cutIndex = parseInt(span.getAttribute("data-index"));
163
  spans.forEach(function(s) {
164
  var idx = parseInt(s.getAttribute("data-index"));
 
166
  s.style.color = "grey";
167
  }
168
  });
169
+ // ์—ฌ๊ธฐ์„œ ๋ฐ”๋กœ human ๋ฉ”์‹œ์ง€์—์„œ โœ‚๏ธ ์ด์ „ ํ…์ŠคํŠธ๋ฅผ hidden input์— ์—…๋ฐ์ดํŠธ
170
+ var edited_text = container.innerText.split("โœ‚๏ธ")[0];
171
+ var hiddenInput = document.getElementById("edited_text_input");
172
+ if(hiddenInput) {
173
+ hiddenInput.value = edited_text;
174
+ }
175
  }
176
  });
177
  </script>
178
  """
179
  )
180
+
181
+ # (B) ๋” ์ด์ƒ submit ํด๋ฆญ ์‹œ ๋ณ„๋„์˜ ์Šคํฌ๋ฆฝํŠธ๋กœ ์—…๋ฐ์ดํŠธํ•  ํ•„์š” ์—†์Œ
182
+ # ์›๋ž˜ ์žˆ๋˜ DOMContentLoaded ์Šคํฌ๋ฆฝํŠธ๋Š” ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
183
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  # (C) CSS ์Šคํƒ€์ผ
185
  gr.HTML(
186
  """
 
237
  gr.Markdown("## Chat Interface")
238
 
239
  with gr.Column(elem_classes="chat-container"):
 
240
  human_bubble = gr.HTML(get_initial_human_html())
 
241
  ai_html = f"""
242
  <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;">
243
  <div class="emoji">๐Ÿค–</div>
 
249
  # ์ˆจ๊น€ ํ…์ŠคํŠธ๋ฐ•์Šค (ํŽธ์ง‘๋œ ํ…์ŠคํŠธ ์ €์žฅ์šฉ)
250
  edited_text_input = gr.Textbox(visible=False, elem_id="edited_text_input")
251
 
 
252
  with gr.Row():
253
  start_button = gr.Button("Start Typing")
254
  submit_button = gr.Button("Submit", elem_id="submit_btn")
255
 
 
256
  start_button.click(fn=stream_human_message, outputs=human_bubble)
257
  submit_button.click(fn=submit_edit, inputs=edited_text_input, outputs=[human_bubble, ai_bubble])
258