gaeunseo commited on
Commit
92d2ea7
ยท
verified ยท
1 Parent(s): 09af6b3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -106
app.py CHANGED
@@ -3,92 +3,124 @@ import gradio as gr
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,47 +149,44 @@ def stream_human_message():
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,11 +197,11 @@ def submit_edit(edited_text):
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,8 +230,7 @@ with gr.Blocks() as demo:
201
  </script>
202
  """
203
  )
204
-
205
- # (B) ์ถ”๊ฐ€ ์Šคํฌ๋ฆฝํŠธ: Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, human_message div์˜ innerText์—์„œ "โœ‚๏ธ"๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŽธ์ง‘๋œ ํ…์ŠคํŠธ(์•ž๋ถ€๋ถ„)๋ฅผ ์ˆจ๊น€ ํ…์ŠคํŠธ๋ฐ•์Šค์— ์—…๋ฐ์ดํŠธ
206
  gr.HTML(
207
  """
208
  <script>
@@ -221,7 +249,6 @@ with gr.Blocks() as demo:
221
  </script>
222
  """
223
  )
224
-
225
  # (C) CSS ์Šคํƒ€์ผ
226
  gr.HTML(
227
  """
 
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 = "sqlite:///global_data.db"
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
  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
  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
  </script>
231
  """
232
  )
233
+ # (B) ์ถ”๊ฐ€ ์Šคํฌ๋ฆฝํŠธ: Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, human_message div์˜ innerText์—์„œ "โœ‚๏ธ" ๊ธฐ์ค€์œผ๋กœ ํŽธ์ง‘๋œ ํ…์ŠคํŠธ(์•ž๋ถ€๋ถ„)๋ฅผ ์ˆจ๊น€ ํ…์ŠคํŠธ๋ฐ•์Šค์— ์—…๋ฐ์ดํŠธ
 
234
  gr.HTML(
235
  """
236
  <script>
 
249
  </script>
250
  """
251
  )
 
252
  # (C) CSS ์Šคํƒ€์ผ
253
  gr.HTML(
254
  """