Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, Request | |
| from pydantic import BaseModel | |
| from typing import List | |
| from transformers import pipeline | |
| from PIL import Image | |
| import re, os | |
| import gradio as gr | |
| import torch | |
| app = FastAPI() | |
| # 1. LLM ํ์ดํ๋ผ์ธ ์ด๊ธฐํ (SmolLM3 ๋ชจ๋ธ) | |
| print("[torch] is available:", torch.cuda.is_available()) | |
| print("[device] default:", torch.device("cuda" if torch.cuda.is_available() else "cpu")) | |
| llm = pipeline("text-generation", model="HuggingFaceTB/SmolLM3-3B", device=0 if torch.cuda.is_available() else -1) | |
| # 2. ๊ฐ์ ๋ฐ ์ํฉ๋ณ ์ด๋ฏธ์ง ๋งคํ | |
| ''' | |
| ์ด๋ฏธ์ง ๋งคํ ์์ | |
| ----------------------------- | |
| ์ด๋ฏธ์ง๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ง์ด๋ฃ์ผ๋ฉด (./asset/face/) | |
| happy.png | |
| sad.png | |
| angry.png | |
| => | |
| ์ด๋ฐ ๋์ ๋๋ฆฌ ํ์์ผ๋ก ๋ฐํ๋จ | |
| { | |
| "happy": "happy.png", | |
| "sad": "sad.png", | |
| "angry": "angry.png" | |
| } | |
| => | |
| ๋ชจ๋ธ ์ถ๋ ฅ์ ๊ฐ์ ๋ถ๋ถ์ ๋์๋๋ ์ด๋ฏธ์ง ์ถ๋ ฅ | |
| ''' | |
| def load_faces(face_dir="assets/face"): | |
| if not os.path.exists(face_dir): | |
| os.makedirs(face_dir) | |
| emotion_to_face = {} | |
| for filename in os.listdir(face_dir): | |
| if filename.endswith(".png"): | |
| emotion = os.path.splitext(filename)[0] # 'happy.png' โ 'happy' | |
| emotion_to_face[emotion] = filename # "happy": "happy.png" | |
| return emotion_to_face | |
| def load_bgs(bg_dir="assets/bg"): | |
| if not os.path.exists(bg_dir): | |
| os.makedirs(bg_dir) | |
| situation_to_bg = {} | |
| for filename in os.listdir(bg_dir): | |
| if filename.endswith(".png"): | |
| emotion = os.path.splitext(filename)[0] # 'happy.png' โ 'happy' | |
| situation_to_bg[emotion] = filename # "happy": "happy.png" | |
| return situation_to_bg | |
| emotion_to_face = load_faces() | |
| situation_to_bg = load_bgs() | |
| # 3. ์ถ๋ ฅ ๋ผ์ธ ํ์ฑ ํจ์ | |
| def parse_output(text: str): | |
| pattern = r'"(.*?)"\s*\(emotion:\s*(\w+),\s*situation:\s*(\w+)\)' | |
| results = [] | |
| for line in text.strip().split('\n'): | |
| match = re.match(pattern, line.strip()) | |
| if match: | |
| results.append({ | |
| "text": match.group(1), | |
| "emotion": match.group(2), | |
| "situation": match.group(3) | |
| }) | |
| return results | |
| # 4. ์ด๋ฏธ์ง ํฉ์ฑ ํจ์ | |
| def combine_images(bg_path, face_path): | |
| try: | |
| bg = Image.open(bg_path).convert("RGBA") | |
| except FileNotFoundError: | |
| print(f"[warning] ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์์: {bg_path}") | |
| return None | |
| try: | |
| face = Image.open(face_path).convert("RGBA") | |
| except FileNotFoundError: | |
| print(f"[warning] ์บ๋ฆญํฐ ์ด๋ฏธ์ง ์์: {face_path}") | |
| return None | |
| # ์ด๋ฏธ์ง ํฉ์ฑ | |
| bg.paste(face, (0, 0), face) | |
| return bg | |
| # 5. ์ฑ๋ด ์ฒ๋ฆฌ ํจ์ (Gradio์ฉ) | |
| ''' | |
| ์ง๊ธ๊น์ง ๋ํ ๋ด์ฉ์ ๋ชจ๋ ํ๋กฌํํธ๋ก ๋ฃ์ด์ ๋ํ๋ด์ฉ์ ๊ธฐ์ตํ๋๋ก ํจ | |
| ''' | |
| def build_prompt(chat_history, user_msg): | |
| system_prompt = ( | |
| "You are Aria, a cheerful and expressive fantasy mage." | |
| " Respond in multiple steps if needed." | |
| " Format: \"text\" (emotion: tag, situation: tag)" | |
| ) | |
| dialogue = "" | |
| for item in chat_history: | |
| if item["role"] == "user": | |
| dialogue += f"User: {item['text']}\n" | |
| elif item["role"] == "bot": | |
| dialogue += f"Aria: {item['text']}\n" | |
| dialogue += f"User: {user_msg}\nAria:" | |
| return system_prompt + "\n" + dialogue | |
| def character_chat(prompt): | |
| full_prompt = build_prompt(chat_history, prompt) | |
| #raw_output = llm(full_prompt, max_new_tokens=300)[0]['generated_text'] | |
| raw_output = '"์ฐ์ค์" (emotion: tag, situation: tag)' | |
| parsed = parse_output(raw_output) | |
| result_outputs = [] | |
| for i, item in enumerate(parsed): | |
| face = emotion_to_face.get(item['emotion'], "neutral.png") | |
| bg = situation_to_bg.get(item['situation'], "default.jpg") | |
| composite = combine_images(os.path.join("assets/bg", bg), os.path.join("assets/face", face)) | |
| img_path = None #์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด ์ถ๋ ฅ ์ํจ | |
| if composite: | |
| img_path = f"static/output_{i}.png" | |
| composite.save(img_path) | |
| result_outputs.append((item['text'], img_path)) | |
| return result_outputs | |
| # 6. Gradio UI with chat history | |
| chat_history = [] | |
| with gr.Blocks(css=""" | |
| .chat-box { max-height: 500px; overflow-y: auto; padding: 10px; border: 1px solid #ccc; border-radius: 10px; } | |
| .bubble-left { background-color: #f1f0f0; border-radius: 10px; padding: 10px; margin: 5px; max-width: 70%; float: left; clear: both; } | |
| .bubble-right { background-color: #d1e7ff; border-radius: 10px; padding: 10px; margin: 5px; max-width: 70%; float: right; clear: both; text-align: right; } | |
| .image-preview { margin: 5px 0; max-width: 100%; border-radius: 10px; } | |
| """) as demo: | |
| gr.Markdown("์ฑ๋ด") | |
| with gr.Column(): | |
| chat_output = gr.HTML(value="<div class='chat-box' id='chat-box'></div>") | |
| user_input = gr.Textbox(label="Say something to Aria", placeholder="Type here and press Enter") | |
| def render_chat(): | |
| html = "" | |
| for item in chat_history: | |
| if item['role'] == 'user': | |
| html += f"<div class='bubble-right'>{item['text']}</div>" | |
| elif item['role'] == 'bot': | |
| bubble = f"<div class='bubble-left'>{item['text']}" | |
| if 'image' in item and item['image']: | |
| bubble += f"<br><img class='image-preview' src='{item['image']}'>" | |
| bubble += "</div>" | |
| html += bubble | |
| return html | |
| def on_submit(user_msg): | |
| chat_history.append({"role": "user", "text": user_msg}) | |
| bot_results = character_chat(user_msg) | |
| for item in bot_results: | |
| try: | |
| text, image_path = item # unpack ์๋ | |
| except (ValueError, TypeError): | |
| # unpack ์๋๋ฉด ๊ธฐ๋ณธ๊ฐ ์ฒ๋ฆฌ (์ด๋ฏธ์ง ์์ด) | |
| text = str(item) | |
| image_path = None | |
| chat_entry = {"role": "bot", "text": text} | |
| if image_path: | |
| chat_entry["image"] = image_path | |
| chat_history.append(chat_entry) | |
| new_chat_html = render_chat() | |
| return f"<div class='chat-box' id='chat-box'>{new_chat_html}</div>", "" | |
| user_input.submit(on_submit, inputs=user_input, outputs=[chat_output, user_input]) | |
| if __name__ == "__main__": | |
| demo.launch() |