Spaces:
Build error
Build error
| import time | |
| import gradio as gr | |
| import random | |
| import os | |
| import threading | |
| import pandas as pd | |
| from datasets import load_dataset | |
| # CSV ํ์ผ ๊ฒฝ๋ก์ ๋์ ์ ๊ทผ์ ์ํ Lock ์ ์ธ | |
| DATA_FILE = "global_data.csv" | |
| data_lock = threading.Lock() | |
| def initialize_global_data(): | |
| """ | |
| DATA_FILE์ด ์กด์ฌํ์ง ์์ผ๋ฉด, gaeunseo/Taskmaster_sample_data ๋ฐ์ดํฐ์ ์ ๋ก๋ํ์ฌ DataFrame์ผ๋ก ๋ณํํ ํ CSV ํ์ผ๋ก ์ ์ฅํฉ๋๋ค. | |
| ์ด๋ฏธ ํ์ผ์ด ์์ผ๋ฉด ํ์ผ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด DataFrame์ ๋ฐํํฉ๋๋ค. | |
| """ | |
| if not os.path.exists(DATA_FILE): | |
| ds = load_dataset("gaeunseo/Taskmaster_sample_data", split="train") | |
| data = ds.to_pandas() | |
| # ํ์ํ ์ปฌ๋ผ์ด ์์ผ๋ฉด ์ถ๊ฐํฉ๋๋ค. | |
| if "used" not in data.columns: | |
| data["used"] = False | |
| if "overlapping" not in data.columns: | |
| data["overlapping"] = "" | |
| if "text" not in data.columns: | |
| data["text"] = "" | |
| data.to_csv(DATA_FILE, index=False) | |
| return data | |
| else: | |
| with data_lock: | |
| df = pd.read_csv(DATA_FILE) | |
| return df | |
| def load_global_data(): | |
| """CSV ํ์ผ์์ global_data DataFrame์ ์ฝ์ด์ต๋๋ค.""" | |
| with data_lock: | |
| df = pd.read_csv(DATA_FILE) | |
| return df | |
| def save_global_data(df): | |
| """DataFrame์ CSV ํ์ผ์ ์ ์ฅํฉ๋๋ค.""" | |
| with data_lock: | |
| df.to_csv(DATA_FILE, index=False) | |
| # CSV ํ์ผ์ ์ ์ฅ๋ global_data ์ด๊ธฐํ | |
| global_data = initialize_global_data() | |
| def get_random_row_from_dataset(): | |
| """ | |
| CSV ํ์ผ์ ์ ์ฅ๋ global_data์์, | |
| 1. conversation_id๋ณ๋ก ๊ทธ๋ฃนํํ๊ณ , | |
| 2. ๊ฐ ๊ทธ๋ฃน์์ ๋ชจ๋ ํ์ used ์ปฌ๋ผ์ด False์ด๋ฉฐ, ๊ทธ๋ฃน ๋ด์ overlapping ์ปฌ๋ผ์ด "TT"์ธ ํ์ด ์กด์ฌํ๋ ๊ทธ๋ฃน๋ง valid๋ก ๊ฐ์ฃผํฉ๋๋ค. | |
| validํ ๊ทธ๋ฃน๋ค ์ค ๋๋คํ๊ฒ ํ๋์ ๊ทธ๋ฃน์ ์ ํํ ํ, | |
| - ํด๋น ๊ทธ๋ฃน์ ๋ชจ๋ ํ์ used ๊ฐ์ True๋ก ์ ๋ฐ์ดํธ(์ฆ, ์ ์ฒด ๊ทธ๋ฃน์ ํ ๋น)ํ๊ณ CSV ํ์ผ์ ์ ์ฅํฉ๋๋ค. | |
| - ์ ํ๋ ๊ทธ๋ฃน ๋ด์์ overlapping ์ปฌ๋ผ์ด "TT"์ธ ํ(์ฌ๋ฌ ๊ฐ๋ผ๋ฉด ์ฒซ ๋ฒ์งธ)์ ๋ฐํํฉ๋๋ค. | |
| """ | |
| global global_data | |
| global_data = load_global_data() # ์ต์ ๋ฐ์ดํฐ ๋ก๋ | |
| groups = global_data.groupby('conversation_id') | |
| valid_groups = [] | |
| for cid, group in groups: | |
| # ๋ชจ๋ ํ์ used ๊ฐ์ด False์ด๊ณ , ๊ทธ๋ฃน ๋ด์ overlapping ๊ฐ์ด "TT"์ธ ํ์ด ์๋ ๊ทธ๋ฃน ํํฐ๋ง | |
| if group['used'].apply(lambda x: bool(x) == False).all() and (group['overlapping'] == "TT").any(): | |
| valid_groups.append((cid, group)) | |
| if not valid_groups: | |
| return None | |
| chosen_cid, chosen_group = random.choice(valid_groups) | |
| # ์ ์ฒด ๊ทธ๋ฃน์ used ๊ฐ์ True๋ก ์ ๋ฐ์ดํธ | |
| global_data.loc[global_data['conversation_id'] == chosen_cid, 'used'] = True | |
| save_global_data(global_data) | |
| # ์ ํ๋ ๊ทธ๋ฃน ๋ด์์ overlapping ์ปฌ๋ผ์ด "TT"์ธ ํ์ ๋ฐํ (์ฌ๋ฌ ๊ฐ๋ผ๋ฉด ์ฒซ ๋ฒ์งธ) | |
| chosen_rows = chosen_group[chosen_group['overlapping'] == "TT"] | |
| if chosen_rows.empty: | |
| return None | |
| chosen_row = chosen_rows.iloc[0] | |
| return chosen_row.to_dict() | |
| # --- ์ด๊ธฐ ๋ํ ๋ถ๋ฌ์ค๊ธฐ --- | |
| # ๋ฐ์ดํฐ์ ์ text ์ปฌ๋ผ์ "[turn]"์ ๊ธฐ์ค์ผ๋ก ๋ํ๊ฐ ๊ตฌ๋ถ๋์ด ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. | |
| row = get_random_row_from_dataset() | |
| if row is None: | |
| human_message = "No valid conversation available." | |
| ai_message = "No valid conversation available." | |
| else: | |
| raw_text = row['text'] | |
| human_message = raw_text.split("[turn]")[0].strip() | |
| ai_message = raw_text.split("[turn]")[1].strip() | |
| ############################################# | |
| # ์ฑํ ์ธํฐํ์ด์ค ๊ด๋ จ ํจ์ (๋งํ์ , ํ์ดํ ํจ๊ณผ, ํธ์ง ๊ธฐ๋ฅ) | |
| ############################################# | |
| def get_initial_human_html(): | |
| """ | |
| ํ์ด์ง ๋ก๋ ์, ๋น Human ๋งํ์ ๊ณผ ์ค๋ฅธ์ชฝ ๐ง ์ด๋ชจํฐ์ฝ์ ํฌํจํ ์ด๊ธฐ HTML ๋ฐํ | |
| """ | |
| wrapper_start = ( | |
| """<div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">""" | |
| ) | |
| bubble_start = """<div id="human_message" class="speech-bubble human">""" | |
| bubble_end = "</div>" | |
| emoji_html = "<div class='emoji'>๐ง</div>" | |
| wrapper_end = "</div>" | |
| return wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end | |
| def stream_human_message(): | |
| """ | |
| Start Typing ๋ฒํผ ํด๋ฆญ ์, ์ ์ญ ๋ณ์ human_message์ ๋ด์ฉ์ ํ ๊ธ์์ฉ ํ์ดํ ํจ๊ณผ๋ก ์ถ๋ ฅํฉ๋๋ค. | |
| ์ด์ ์ํ(โ๏ธ ์์ด์ฝ, ํ์ ์ฒ๋ฆฌ ๋ฑ)๋ ๋ฆฌ์ ๋ฉ๋๋ค. | |
| """ | |
| bubble_content = "" | |
| wrapper_start = ( | |
| """<div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">""" | |
| ) | |
| bubble_start = """<div id="human_message" class="speech-bubble human">""" | |
| bubble_end = "</div>" | |
| emoji_html = "<div class='emoji'>๐ง</div>" | |
| wrapper_end = "</div>" | |
| # ์ด๊ธฐ ์ํ: ๋น ๋งํ์ ๊ณผ ์ด๋ชจํฐ์ฝ | |
| yield wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end | |
| # human_message๋ฅผ ํ ๊ธ์์ฉ ์ถ๊ฐ (ํ์ดํ ํจ๊ณผ) | |
| for i, ch in enumerate(human_message): | |
| bubble_content += f"<span data-index='{i}'>{ch}</span>" | |
| current_html = wrapper_start + bubble_start + bubble_content + bubble_end + emoji_html + wrapper_end | |
| yield current_html | |
| time.sleep(0.05) | |
| def submit_edit(edited_text): | |
| """ | |
| Submit ๋ฒํผ ํด๋ฆญ ์ ํธ์ถ๋๋ ํจ์. | |
| 1. ํธ์ง๋ human ๋ฉ์์ง(โ๏ธ ์๋ถ๋ถ)๋ฅผ ์ ํ์ผ๋ก global_data์ ์ถ๊ฐํฉ๋๋ค. | |
| 2. get_random_row_from_dataset()์ ํตํด ์๋ก์ด ๋ํ๋ฅผ ๊ฐ์ ธ์ค๊ณ , ์ ์ญ ๋ณ์ human_message์ ai_message๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค. | |
| 3. ์ด๊ธฐ ์ํ์ human ๋งํ์ ๊ณผ ai ๋งํ์ HTML์ ๋ฐํํ์ฌ ์ธํฐํ์ด์ค๋ฅผ ๋ฆฌ์ ํฉ๋๋ค. | |
| """ | |
| global human_message, ai_message | |
| data = load_global_data() | |
| new_row = { | |
| "conversation_id": "edited_" + str(random.randint(1000, 9999)), | |
| "used": False, | |
| "overlapping": "", | |
| "text": edited_text, | |
| "human_message": edited_text, | |
| "ai_message": "" | |
| } | |
| new_df = pd.DataFrame([new_row]) | |
| data = pd.concat([data, new_df], ignore_index=True) | |
| save_global_data(data) | |
| new_row_data = get_random_row_from_dataset() | |
| if new_row_data is None: | |
| human_message = "No valid conversation available." | |
| ai_message = "No valid conversation available." | |
| else: | |
| raw_text = new_row_data['text'] | |
| human_message = raw_text.split("[turn]")[0].strip() | |
| ai_message = raw_text.split("[turn]")[1].strip() | |
| new_human_html = get_initial_human_html() | |
| new_ai_html = f""" | |
| <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;"> | |
| <div class="emoji">๐ค</div> | |
| <div id="ai_message" class="speech-bubble ai">{ai_message}</div> | |
| </div> | |
| """ | |
| return new_human_html, new_ai_html | |
| ############################################# | |
| # Gradio ์ธํฐํ์ด์ค ๊ตฌ์ฑ | |
| ############################################# | |
| with gr.Blocks() as demo: | |
| # (A) ํ์ด์ง ์๋จ ์คํฌ๋ฆฝํธ: Human ๋งํ์ ๋ด์ ๊ฐ <span data-index="...">๋ฅผ ํด๋ฆญํ๋ฉด, | |
| # ํด๋น ์์น์ โ๏ธ ์์ด์ฝ์ด ์ฝ์ ๋๊ณ , ๊ทธ ์ดํ ํ ์คํธ๊ฐ ํ์์ผ๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค. | |
| gr.HTML( | |
| """ | |
| <script> | |
| document.addEventListener("click", function(event) { | |
| if (event.target && event.target.matches("div.speech-bubble.human span[data-index]")) { | |
| var span = event.target; | |
| var container = span.closest("div.speech-bubble.human"); | |
| var oldScissors = container.querySelectorAll("span.scissor"); | |
| oldScissors.forEach(function(s) { s.remove(); }); | |
| var spans = container.querySelectorAll("span[data-index]"); | |
| spans.forEach(function(s) { s.style.color = ''; }); | |
| var scissor = document.createElement('span'); | |
| scissor.textContent = 'โ๏ธ'; | |
| scissor.classList.add("scissor"); | |
| container.insertBefore(scissor, span.nextSibling); | |
| var cutIndex = parseInt(span.getAttribute("data-index")); | |
| spans.forEach(function(s) { | |
| var idx = parseInt(s.getAttribute("data-index")); | |
| if (idx > cutIndex) { | |
| s.style.color = "grey"; | |
| } | |
| }); | |
| } | |
| }); | |
| </script> | |
| """ | |
| ) | |
| # (B) ์ถ๊ฐ ์คํฌ๋ฆฝํธ: Submit ๋ฒํผ ํด๋ฆญ ์, human_message div์ innerText์์ "โ๏ธ"๋ฅผ ๊ธฐ์ค์ผ๋ก ํธ์ง๋ ํ ์คํธ(์๋ถ๋ถ)๋ฅผ ์จ๊น ํ ์คํธ๋ฐ์ค์ ์ ๋ฐ์ดํธ | |
| gr.HTML( | |
| """ | |
| <script> | |
| document.addEventListener("DOMContentLoaded", function() { | |
| var submitBtn = document.getElementById("submit_btn"); | |
| if(submitBtn){ | |
| submitBtn.addEventListener("click", function(){ | |
| var humanDiv = document.getElementById("human_message"); | |
| if(humanDiv){ | |
| var edited_text = humanDiv.innerText.split("โ๏ธ")[0]; | |
| document.getElementById("edited_text_input").value = edited_text; | |
| } | |
| }); | |
| } | |
| }); | |
| </script> | |
| """ | |
| ) | |
| # (C) CSS ์คํ์ผ | |
| gr.HTML( | |
| """ | |
| <style> | |
| .chat-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| width: 100%; | |
| } | |
| .speech-bubble { | |
| position: relative; | |
| padding: 10px 15px; | |
| border-radius: 15px; | |
| max-width: 70%; | |
| font-family: sans-serif; | |
| font-size: 16px; | |
| line-height: 1.4; | |
| } | |
| .human { | |
| background: #d0f0d0; | |
| margin-right: 10px; | |
| } | |
| .human::after { | |
| content: ""; | |
| position: absolute; | |
| right: -10px; | |
| top: 10px; | |
| border-width: 10px 0 10px 10px; | |
| border-style: solid; | |
| border-color: transparent transparent transparent #d0f0d0; | |
| } | |
| .ai { | |
| background: #e0e0e0; | |
| margin-left: 10px; | |
| } | |
| .ai::after { | |
| content: ""; | |
| position: absolute; | |
| left: -10px; | |
| top: 10px; | |
| border-width: 10px 10px 10px 0; | |
| border-style: solid; | |
| border-color: transparent #e0e0e0 transparent transparent; | |
| } | |
| .emoji { | |
| font-size: 24px; | |
| line-height: 1; | |
| } | |
| </style> | |
| """ | |
| ) | |
| gr.Markdown("## Chat Interface") | |
| with gr.Column(elem_classes="chat-container"): | |
| # Human ๋งํ์ (์ด๊ธฐ: ๋น ๋ฉ์์ง + ๐ง ์ด๋ชจํฐ์ฝ) | |
| human_bubble = gr.HTML(get_initial_human_html()) | |
| # AI ๋งํ์ (์ผ์ชฝ: ๐ค ์ด๋ชจํฐ์ฝ + ๋ฉ์์ง) | |
| ai_html = f""" | |
| <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;"> | |
| <div class="emoji">๐ค</div> | |
| <div id="ai_message" class="speech-bubble ai">{ai_message}</div> | |
| </div> | |
| """ | |
| ai_bubble = gr.HTML(ai_html) | |
| # ์จ๊น ํ ์คํธ๋ฐ์ค (ํธ์ง๋ ํ ์คํธ ์ ์ฅ์ฉ) | |
| edited_text_input = gr.Textbox(visible=False, elem_id="edited_text_input") | |
| # ๋ฒํผ ์์ญ: Start Typing๊ณผ Submit ๋ฒํผ์ ๊ฐ์ ํ์ ๋ฐฐ์น | |
| with gr.Row(): | |
| start_button = gr.Button("Start Typing") | |
| submit_button = gr.Button("Submit", elem_id="submit_btn") | |
| # ๋ฒํผ ์ด๋ฒคํธ ์ฐ๊ฒฐ | |
| start_button.click(fn=stream_human_message, outputs=human_bubble) | |
| submit_button.click(fn=submit_edit, inputs=edited_text_input, outputs=[human_bubble, ai_bubble]) | |
| demo.launch() |