Spaces:
Build error
Build error
| import time | |
| import gradio as gr | |
| import random | |
| import json | |
| import os | |
| import threading | |
| from datasets import load_dataset | |
| # --- ํ์ผ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๊ด๋ฆฌ ์ค์ --- | |
| DATA_FILE = "global_data.json" | |
| data_lock = threading.Lock() | |
| def initialize_global_data(): | |
| """ | |
| DATA_FILE์ด ์์ผ๋ฉด Dataset์ ๋ถ๋ฌ์์ ์ ์ฅํ๊ณ , ์์ผ๋ฉด ํ์ผ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํฉ๋๋ค. | |
| """ | |
| if not os.path.exists(DATA_FILE): | |
| ds = load_dataset("gaeunseo/Taskmaster_sample_data", split="train") | |
| data = list(ds) | |
| with data_lock: | |
| with open(DATA_FILE, "w", encoding="utf-8") as f: | |
| json.dump(data, f, ensure_ascii=False, indent=2) | |
| return data | |
| else: | |
| with data_lock: | |
| with open(DATA_FILE, "r", encoding="utf-8") as f: | |
| data = json.load(f) | |
| return data | |
| def load_global_data(): | |
| """ํ์ผ์์ global_data๋ฅผ ์ฝ์ด์ต๋๋ค.""" | |
| with data_lock: | |
| with open(DATA_FILE, "r", encoding="utf-8") as f: | |
| data = json.load(f) | |
| return data | |
| def save_global_data(data): | |
| """๋ฐ์ดํฐ๋ฅผ ํ์ผ์ ์ ์ฅํฉ๋๋ค.""" | |
| with data_lock: | |
| with open(DATA_FILE, "w", encoding="utf-8") as f: | |
| json.dump(data, f, ensure_ascii=False, indent=2) | |
| # ์ด๊ธฐ global_data ํ์ผ ์์ฑ ๋๋ ๋ก๋ | |
| global_data = initialize_global_data() | |
| def get_random_row_from_dataset(): | |
| """ | |
| DATA_FILE์ ์ ์ฅ๋ global_data์์, | |
| - conversation_id๋ณ๋ก ๊ทธ๋ฃนํํ๊ณ , | |
| - ๊ฐ ๊ทธ๋ฃน์ ๋ชจ๋ ํ์ used๊ฐ False์ด๋ฉฐ, | |
| - ๊ทธ๋ฃน ๋ด์ overlapping ์ปฌ๋ผ์ด "TT"์ธ ํ์ด ์๋ ๊ทธ๋ฃน๋ง valid๋ก ๊ฐ์ฃผํฉ๋๋ค. | |
| validํ ๊ทธ๋ฃน๋ค ์ค ๋๋คํ๊ฒ ํ๋์ ๊ทธ๋ฃน์ ์ ํํ ํ, | |
| - ํด๋น ๊ทธ๋ฃน์ ๋ชจ๋ ํ์ used ๊ฐ์ True๋ก ์ ๋ฐ์ดํธ(์ฆ, ์ ์ฒด ๊ทธ๋ฃน์ ํ ๋น)ํ๊ณ ํ์ผ์ ์ ์ฅํ ๋ค, | |
| - ์ ํ๋ ๊ทธ๋ฃน ๋ด์์ overlapping ์ปฌ๋ผ์ด "TT"์ธ ํ์ ๋ฐํํฉ๋๋ค. | |
| ์ด ๋ฐฉ์์ผ๋ก ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๋์์ ํธ์ถํ๋๋ผ๋ ์ ๊ธ์ ํตํด atomicํ๊ฒ ์ ๋ฐ์ดํธํ๋ฏ๋ก, | |
| ์๋ก ๋ค๋ฅธ ์ฌ์ฉ์์๊ฒ ์๋ก ๋ค๋ฅธ conversation_id ๊ทธ๋ฃน์ด ํ ๋น๋ฉ๋๋ค. | |
| """ | |
| with data_lock: | |
| data = load_global_data() | |
| conversation_groups = {} | |
| for row in data: | |
| cid = row["conversation_id"] | |
| conversation_groups.setdefault(cid, []).append(row) | |
| valid_groups = [ | |
| group for group in conversation_groups.values() | |
| if all(not r["used"] for r in group) and any(r["overlapping"] == "TT" for r in group) | |
| ] | |
| if not valid_groups: | |
| return None | |
| chosen_group = random.choice(valid_groups) | |
| # ๊ทธ๋ฃน ๋ด์ ๋ชจ๋ ํ์ used๋ฅผ True๋ก ์ ๋ฐ์ดํธํ์ฌ ์ ์ฒด ๊ทธ๋ฃน์ด ํ ๋น๋๋๋ก ํจ | |
| for row in chosen_group: | |
| row["used"] = True | |
| save_global_data(data) | |
| # ์ ํ๋ ๊ทธ๋ฃน ๋ด์์ overlapping ์ปฌ๋ผ์ด "TT"์ธ ํ์ ๋ฐํ (์ฌ๋ฌ ๊ฐ๋ผ๋ฉด ์ฒซ ๋ฒ์งธ) | |
| for row in chosen_group: | |
| if row["overlapping"] == "TT": | |
| return row | |
| # --- ์ด๊ธฐ ๋ํ ์ค์ --- | |
| # Dataset์ 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 | |
| # ์ ํ ์์ฑ (์ conversation_id๋ ์์ ์์ฑ) | |
| new_row = { | |
| "conversation_id": "edited_" + str(random.randint(1000, 9999)), | |
| "used": False, | |
| "overlapping": "", | |
| "text": edited_text, | |
| "human_message": edited_text, | |
| "ai_message": "" | |
| } | |
| # ํ์ผ์์ global_data ๋ถ๋ฌ์์ ์ ํ ์ถ๊ฐ ๋ฐ ์ ์ฅ | |
| data = load_global_data() | |
| data.append(new_row) | |
| 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) | |
| # ์จ๊น ํ ์คํธ๋ฐ์ค: ํธ์ง๋ ํ ์คํธ(โ๏ธ ์๋ถ๋ถ)๋ฅผ ์ ์ฅ (visible=False) | |
| edited_text_input = gr.Textbox(visible=False, elem_id="edited_text_input") | |
| # ๋ฒํผ ์์ญ: Start Typing๊ณผ Submit ๋ฒํผ์ ๊ฐ์ ํ์ ๋ฐฐ์น (Download ๋ฒํผ ์ ๊ฑฐ) | |
| 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() |