gaeunseo commited on
Commit
7b078cc
·
verified ·
1 Parent(s): a4decd8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +191 -82
app.py CHANGED
@@ -1,113 +1,222 @@
1
- import gradio as gr
2
- from datasets import load_dataset
3
  import random
4
- import re
 
 
 
 
 
 
5
 
6
- def load_random_conversations():
 
 
 
7
  """
8
- 데이터셋 'gaeunseo/Taskmaster_sample_data'의 train split에서
9
- conversation_id별로 그룹화한 후, 모든 행의 used 컬럼이 False인 그룹만 필터링합니다.
10
- 랜덤하게 2개 그룹을 선택하여 그룹의 utterance를 줄바꿈으로 연결한 문자열 두 개를 반환합니다.
11
  """
12
- ds = load_dataset("gaeunseo/Taskmaster_sample_data")["train"]
13
-
14
- # conversation_id별로 그룹화
15
- groups = {}
16
- for row in ds:
17
- cid = row["conversation_id"]
18
- groups.setdefault(cid, []).append(row)
19
-
20
- # 모든 행의 used가 False인 그룹만 선택
21
- valid_groups = [grp for grp in groups.values() if all(not row["used"] for row in grp)]
22
-
23
- if len(valid_groups) < 2:
24
- return "Not enough unused conversations", "Not enough unused conversations"
25
-
26
- # 유효한 그룹 중 랜덤하게 2개 선택
27
- selected_groups = random.sample(valid_groups, 2)
28
-
29
- # 각 그룹의 모든 utterance를 줄바꿈(\n)으로 이어붙임
30
- conv_A = "\n".join(row["utterance"] for row in selected_groups[0])
31
- conv_B = "\n".join(row["utterance"] for row in selected_groups[1])
32
-
33
- return conv_A, conv_B
34
 
35
- def format_chat_html(conv_text):
 
 
 
 
 
 
 
 
 
 
 
 
36
  """
37
- conv_text 문자열을 [turn]와 [BC] 토큰을 기준으로 분할한 후,
38
- 발화를 HTML 말풍선 형태로 변환합니다.
39
-
40
- - 번째 발화(인간): 오른쪽 정렬, 말풍선 오른쪽에 🧑 이모티콘 표시
41
- - 번째 발화(AI): 왼쪽 정렬, 말풍선 왼쪽에 🤖 이모티콘 표시
 
 
42
  """
43
- # [turn]와 [BC] 토큰을 구분자로 발화 분할
44
- utterances = re.split(r'\[turn\]|\[BC\]', conv_text)
45
- utterances = [utt.strip() for utt in utterances if utt.strip()]
46
-
47
- # HTML 컨테이너 (채팅창처럼 보이도록)
48
- html = '<div style="display: flex; flex-direction: column; gap: 10px; background-color: #f0f0f0; padding: 10px;">'
49
-
50
- for i, utterance in enumerate(utterances):
51
- if i % 2 == 0:
52
- # 첫 번째, 세 번째 등 (인간 발화): 오른쪽 정렬
53
- bubble = f'''
54
- <div style="align-self: flex-end; background-color: #dcf8c6; padding: 10px; border-radius: 10px; max-width: 70%; position: relative;">
55
- <div style="text-align: right;">{utterance} <span>🧑</span></div>
56
- </div>
57
- '''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  else:
59
- # 번째, 네 번째 등 (AI 발화): 왼쪽 정렬
60
- bubble = f'''
61
- <div style="align-self: flex-start; background-color: #fff; padding: 10px; border-radius: 10px; max-width: 70%; position: relative; border: 1px solid #ccc;">
62
- <div style="text-align: left;"><span>🤖</span> {utterance}</div>
63
- </div>
64
- '''
65
- html += bubble
66
- html += '</div>'
67
- return html
68
-
69
- def load_and_format_conversations():
70
  """
71
- 데이터셋에서 랜덤하게 대화 문자열을 가져온 후, 각각 format_chat_html()을 통해
72
- HTML 형식의 채팅창처럼 보이게 변환하여 반환합니다.
 
 
 
 
 
 
73
  """
74
- conv_A, conv_B = load_random_conversations()
75
- # 에러 메시지인 경우 그대로 HTML 태그로 감싸서 반환
76
- if conv_A.startswith("Not enough"):
77
- return f"<p>{conv_A}</p>", f"<p>{conv_B}</p>"
78
- html_A = format_chat_html(conv_A)
79
- html_B = format_chat_html(conv_B)
80
- return html_A, html_B
81
-
82
- # 평가 버튼 클릭 시 업데이트할 전역 변수
83
- statement = ""
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  def update_statement(val):
86
  global statement
87
  statement = val
88
  return statement
89
 
 
 
 
 
90
  with gr.Blocks() as demo:
91
- # 상단: 좌우에 각각 대화 내용을 HTML로 표시 (Conversation A와 Conversation B)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  with gr.Row():
93
- conv_A_html = gr.HTML(label="Conversation A")
94
- conv_B_html = gr.HTML(label="Conversation B")
95
 
96
- # "Load Random Conversations" 버튼을 눌러 데이터셋에서 대화 데이터를 불러오고 HTML로 변환
97
  load_btn = gr.Button("Load Random Conversations")
98
- load_btn.click(fn=load_and_format_conversations, inputs=[], outputs=[conv_A_html, conv_B_html])
99
 
100
- # 하단: 평가 버튼 4개 배치
101
  with gr.Row():
102
  btn_both_good = gr.Button("Both good") # "둘 다 좋음" → "BG"
103
  btn_a_better = gr.Button("A is better") # "A가 더 좋음" → "AG"
104
  btn_b_better = gr.Button("B is better") # "B가 더 좋음" → "BG"
105
  btn_both_bad = gr.Button("Both not good") # "둘 다 별로임" → "BB"
106
 
107
- # 선택한 평가값을 표시하는 텍스트박스
108
- statement_output = gr.Textbox(label="Selected Statement", value="", interactive=False)
109
 
110
- # 각 버튼 클릭 시 전역 변수 statement 업데이트
111
  btn_both_good.click(fn=lambda: update_statement("BG"), inputs=[], outputs=statement_output)
112
  btn_a_better.click(fn=lambda: update_statement("AG"), inputs=[], outputs=statement_output)
113
  btn_b_better.click(fn=lambda: update_statement("BG"), inputs=[], outputs=statement_output)
 
1
+ import os
2
+ import threading
3
  import random
4
+ import pandas as pd
5
+ from datasets import load_dataset
6
+ import gradio as gr
7
+
8
+ #############################################
9
+ # CSV 파일 관련 함수 및 전역 데이터 초기화
10
+ #############################################
11
 
12
+ DATA_FILE = "Interface1.csv"
13
+ data_lock = threading.Lock()
14
+
15
+ def initialize_global_data():
16
  """
17
+ CSV 파일(DATA_FILE)이 없으면, Hugging Face 데이터셋(gaeunseo/Taskmaster_sample_data)의 train split
18
+ DataFrame으로 변환한 필요한 컬럼(used, overlapping, text)을 추가하고 CSV로 저장합니다.
19
+ 이미 파일이 있으면 파일에서 데이터를 읽어 DataFrame을 반환합니다.
20
  """
21
+ if not os.path.exists(DATA_FILE):
22
+ ds = load_dataset("gaeunseo/Taskmaster_sample_data", split="train")
23
+ data = ds.to_pandas()
24
+ # 필요한 컬럼이 없으면 추가
25
+ if "used" not in data.columns:
26
+ data["used"] = False
27
+ if "overlapping" not in data.columns:
28
+ data["overlapping"] = ""
29
+ if "text" not in data.columns:
30
+ data["text"] = ""
31
+ data.to_csv(DATA_FILE, index=False)
32
+ return data
33
+ else:
34
+ with data_lock:
35
+ df = pd.read_csv(DATA_FILE)
36
+ return df
37
+
38
+ def load_global_data():
39
+ """CSV 파일에서 global_data DataFrame을 읽어옵니다."""
40
+ with data_lock:
41
+ df = pd.read_csv(DATA_FILE)
42
+ return df
43
 
44
+ def save_global_data(df):
45
+ """DataFrame을 CSV 파일에 저장합니다."""
46
+ with data_lock:
47
+ df.to_csv(DATA_FILE, index=False)
48
+
49
+ # CSV 파일에 저장된 global_data 초기화
50
+ global_data = initialize_global_data()
51
+
52
+ #############################################
53
+ # 데이터셋에서 랜덤 대화 행 선택 함수
54
+ #############################################
55
+
56
+ def get_random_row_from_dataset():
57
  """
58
+ CSV 파일에 저장된 global_data에서,
59
+ 1. conversation_id별로 그룹화하고,
60
+ 2. 각 그룹에서 모든 행의 used 컬럼이 False이며,
61
+ 그룹 내에 overlapping 컬럼이 "TT"인 행이 존재하는 그룹만 valid로 간주합니다.
62
+ valid한 그룹들 랜덤하게 하나의 그룹을 선택한 후,
63
+ - 해당 그룹의 모든 행의 used 값을 True로 업데이트하고 CSV 파일에 저장합니다.
64
+ - 선택된 그룹 내에서 overlapping 컬럼이 "TT"인 행(여러 개라면 첫 번째)을 dict로 반환합니다.
65
  """
66
+ global global_data
67
+ global_data = load_global_data() # 최신 데이터 ���드
68
+ groups = global_data.groupby('conversation_id')
69
+ valid_groups = []
70
+ for cid, group in groups:
71
+ # 모든 행의 used가 False이고, 그룹 내에 overlapping이 "TT"인 행이 존재하는 그룹 선택
72
+ if group['used'].apply(lambda x: bool(x) == False).all() and (group['overlapping'] == "TT").any():
73
+ valid_groups.append((cid, group))
74
+ if not valid_groups:
75
+ return None
76
+ chosen_cid, chosen_group = random.choice(valid_groups)
77
+ # 선택된 그룹의 모든 행의 used 값을 True로 업데이트
78
+ global_data.loc[global_data['conversation_id'] == chosen_cid, 'used'] = True
79
+ save_global_data(global_data)
80
+ # 선택된 그룹 내에서 overlapping이 "TT"인 행(여러 개일 경우 첫 번째) 선택
81
+ chosen_rows = chosen_group[chosen_group['overlapping'] == "TT"]
82
+ if chosen_rows.empty:
83
+ return None
84
+ chosen_row = chosen_rows.iloc[0]
85
+ return chosen_row.to_dict()
86
+
87
+ #############################################
88
+ # 대화 HTML 생성 함수
89
+ #############################################
90
+
91
+ def format_conversation_html(row):
92
+ """
93
+ 전달받은 row(dict)를 기반으로 대화 내용을 HTML로 포맷합니다.
94
+ text 컬럼은 "[turn]"을 기준으로 발화가 구분되어 있으며,
95
+ - 첫 번째 발화(인간)는 오른쪽 정렬과 말풍선 오른쪽의 🧑 아이콘으로 표시,
96
+ - 두 번째 발화(AI)는 왼쪽 정렬과 말풍선 왼쪽의 🤖 아이콘으로 표시합니다.
97
+ """
98
+ if row is None:
99
+ human_message = "No valid conversation available."
100
+ ai_message = "No valid conversation available."
101
+ else:
102
+ raw_text = row.get('text', '')
103
+ parts = raw_text.split("[turn]")
104
+ if len(parts) >= 2:
105
+ human_message = parts[0].strip()
106
+ ai_message = parts[1].strip()
107
  else:
108
+ human_message = raw_text.strip()
109
+ ai_message = ""
110
+
111
+ # 인간 말풍선 (오른쪽 정렬, 🧑 아이콘)
112
+ human_html = f"""
113
+ <div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">
114
+ <div class="speech-bubble human" style="background: #d0f0d0; padding: 10px 15px; border-radius: 15px; max-width: 70%; text-align: right;">
115
+ {human_message}
116
+ </div>
117
+ <div class="emoji" style="font-size: 24px; line-height: 1;">🧑</div>
118
+ </div>
119
  """
120
+ # AI 말풍선 (왼쪽 정렬, 🤖 아이콘)
121
+ ai_html = f"""
122
+ <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;">
123
+ <div class="emoji" style="font-size: 24px; line-height: 1;">🤖</div>
124
+ <div class="speech-bubble ai" style="background: #e0e0e0; padding: 10px 15px; border-radius: 15px; max-width: 70%; text-align: left;">
125
+ {ai_message}
126
+ </div>
127
+ </div>
128
  """
129
+ conversation_html = f"""
130
+ <div class="chat-container" style="display: flex; flex-direction: column; gap: 10px;">
131
+ {human_html}
132
+ {ai_html}
133
+ </div>
134
+ """
135
+ return conversation_html
 
 
 
136
 
137
+ def load_two_conversations_html():
138
+ """
139
+ get_random_row_from_dataset() 함수를 두 번 호출하여
140
+ Conversation A와 Conversation B 각각의 row를 가져온 후,
141
+ format_conversation_html()로 HTML을 생성하여 반환합니다.
142
+ """
143
+ row_A = get_random_row_from_dataset()
144
+ row_B = get_random_row_from_dataset()
145
+ conv_A_html = format_conversation_html(row_A)
146
+ conv_B_html = format_conversation_html(row_B)
147
+ return conv_A_html, conv_B_html
148
+
149
+ #############################################
150
+ # 평가 버튼 관련 함수
151
+ #############################################
152
+
153
+ # 전역 변수 statement (버튼 클릭 시 저장할 값)
154
+ statement = ""
155
  def update_statement(val):
156
  global statement
157
  statement = val
158
  return statement
159
 
160
+ #############################################
161
+ # Gradio 인터페이스 구성
162
+ #############################################
163
+
164
  with gr.Blocks() as demo:
165
+ # (A) CSS 스타일 (채팅 말풍선 관련)
166
+ gr.HTML(
167
+ """
168
+ <style>
169
+ .chat-container {
170
+ display: flex;
171
+ flex-direction: column;
172
+ gap: 10px;
173
+ width: 100%;
174
+ }
175
+ .speech-bubble {
176
+ position: relative;
177
+ padding: 10px 15px;
178
+ border-radius: 15px;
179
+ max-width: 70%;
180
+ font-family: sans-serif;
181
+ font-size: 16px;
182
+ line-height: 1.4;
183
+ }
184
+ .human {
185
+ background: #d0f0d0;
186
+ }
187
+ .ai {
188
+ background: #e0e0e0;
189
+ }
190
+ .emoji {
191
+ font-size: 24px;
192
+ line-height: 1;
193
+ }
194
+ </style>
195
+ """
196
+ )
197
+
198
+ gr.Markdown("## Conversation Comparison")
199
+
200
+ # 좌측: Conversation A, 우측: Conversation B
201
  with gr.Row():
202
+ conv_A = gr.HTML(label="Conversation A")
203
+ conv_B = gr.HTML(label="Conversation B")
204
 
205
+ # "Load Random Conversations" 버튼 클릭 대화를 불러옴
206
  load_btn = gr.Button("Load Random Conversations")
207
+ load_btn.click(fn=load_two_conversations_html, inputs=[], outputs=[conv_A, conv_B])
208
 
209
+ # 평가 버튼 영역 (하단)
210
  with gr.Row():
211
  btn_both_good = gr.Button("Both good") # "둘 다 좋음" → "BG"
212
  btn_a_better = gr.Button("A is better") # "A가 더 좋음" → "AG"
213
  btn_b_better = gr.Button("B is better") # "B가 더 좋음" → "BG"
214
  btn_both_bad = gr.Button("Both not good") # "둘 다 별로임" → "BB"
215
 
216
+ # 선택된 평가값을 보여주는 텍스트박스
217
+ statement_output = gr.Textbox(label="Selected Statement", interactive=False)
218
 
219
+ # 각 버튼 클릭 시 전역 변수 statement 업데이트
220
  btn_both_good.click(fn=lambda: update_statement("BG"), inputs=[], outputs=statement_output)
221
  btn_a_better.click(fn=lambda: update_statement("AG"), inputs=[], outputs=statement_output)
222
  btn_b_better.click(fn=lambda: update_statement("BG"), inputs=[], outputs=statement_output)