Spaces:
Sleeping
Sleeping
Download app.py from ssboost/G378DFE4AS: direct link, hf CLI and curl.
- Browser
- Download file 28.9 kB
-
https://huggingface.co/spaces/ssboost/G378DFE4AS/resolve/main/app.py
- Command line
-
hf download hf://spaces/ssboost/G378DFE4AS/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ssboost/G378DFE4AS/resolve/main/app.py
28.9 kB
| import os | |
| import gradio as gr | |
| import time | |
| import logging | |
| import random | |
| import uuid | |
| from datetime import datetime | |
| from langdetect import detect, DetectorFactory | |
| import google.generativeai as genai | |
| from dotenv import load_dotenv | |
| DetectorFactory.seed = 0 | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # ํ๊ฒฝ ๋ณ์ ๋ก๋ | |
| load_dotenv() | |
| # ํ๊ฒฝ๋ณ์์์ API ํค ์ค์ ๋ก๋ | |
| def get_gemini_api_configs(): | |
| """ํ๊ฒฝ๋ณ์์์ Gemini API ํค ์ค์ ์ ๋ก๋""" | |
| api_configs_str = os.getenv('GEMINI_API_CONFIGS', '') | |
| if not api_configs_str: | |
| logger.error("GEMINI_API_CONFIGS ํ๊ฒฝ๋ณ์๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.") | |
| return [] | |
| try: | |
| # ํ๊ฒฝ๋ณ์ ๊ฐ์ exec๋ก ์คํํ์ฌ ์ค์ ๋ก๋ | |
| local_vars = {} | |
| exec(api_configs_str, {}, local_vars) | |
| return local_vars.get('API_KEYS_LIST', []) | |
| except Exception as e: | |
| logger.error(f"ํ๊ฒฝ๋ณ์ ํ์ฑ ์ค๋ฅ: {e}") | |
| return [] | |
| # API ํค ๊ด๋ฆฌ๋ฅผ ์ํ ์ ์ญ ๋ณ์ | |
| api_key_manager = { | |
| 'keys': [], | |
| 'current_index': -1, | |
| 'failed_keys': set(), # ์คํจํ ํค๋ค์ ์ถ์ | |
| 'is_initialized': False | |
| } | |
| # API ํค ๋ก๋ ํจ์ (๊ฐ์ ๋ ๋ฒ์ ) | |
| def load_api_keys(): | |
| """ํ๊ฒฝ๋ณ์์์ API ํค๋ฅผ ๋ก๋ํฉ๋๋ค.""" | |
| # ํ๊ฒฝ๋ณ์์์ API ํค ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ | |
| api_keys_from_env = get_gemini_api_configs() | |
| # ๋น ํค๋ ํ๋ ์ด์คํ๋ ์ ๊ฑฐ | |
| api_keys = [ | |
| key.strip() for key in api_keys_from_env | |
| if key and key.strip() and not key.startswith("YOUR_") and not key.startswith("your_") | |
| ] | |
| # ์ค๋ณต ์ ๊ฑฐ | |
| api_keys = list(dict.fromkeys(api_keys)) | |
| if not api_keys: | |
| logger.error("API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค. GEMINI_API_CONFIGS ํ๊ฒฝ๋ณ์์ ์ค์ API ํค๋ฅผ ์ถ๊ฐํ์ธ์.") | |
| raise ValueError("API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค. GEMINI_API_CONFIGS ํ๊ฒฝ๋ณ์์ ์ค์ API ํค๋ฅผ ์ถ๊ฐํด์ฃผ์ธ์.") | |
| logger.info(f"์ด {len(api_keys)}๊ฐ์ API ํค๊ฐ ๋ก๋๋์์ต๋๋ค.") | |
| return api_keys | |
| def initialize_api_keys(): | |
| """API ํค ๊ด๋ฆฌ์๋ฅผ ์ด๊ธฐํํฉ๋๋ค.""" | |
| global api_key_manager | |
| if api_key_manager['is_initialized']: | |
| return | |
| try: | |
| api_key_manager['keys'] = load_api_keys() | |
| api_key_manager['is_initialized'] = True | |
| logger.info("API ํค ๊ด๋ฆฌ์ ์ด๊ธฐํ ์๋ฃ") | |
| except Exception as e: | |
| logger.error(f"API ํค ์ด๊ธฐํ ์คํจ: {str(e)}") | |
| raise e | |
| def get_next_api_key(): | |
| """๋ค์ ์ฌ์ฉํ API ํค๋ฅผ ๋ฐํํฉ๋๋ค. ์คํจํ ํค๋ ๊ฑด๋๋๋๋ค.""" | |
| global api_key_manager | |
| # ์ด๊ธฐํ ํ์ธ | |
| if not api_key_manager['is_initialized']: | |
| initialize_api_keys() | |
| available_keys = [ | |
| key for i, key in enumerate(api_key_manager['keys']) | |
| if i not in api_key_manager['failed_keys'] | |
| ] | |
| if not available_keys: | |
| # ๋ชจ๋ ํค๊ฐ ์คํจํ์ผ๋ฉด ์คํจ ๋ชฉ๋ก์ ์ด๊ธฐํํ๊ณ ๋ค์ ์๋ | |
| logger.warning("๋ชจ๋ API ํค๊ฐ ์คํจํ์ต๋๋ค. ์คํจ ๋ชฉ๋ก์ ์ด๊ธฐํํ๊ณ ๋ค์ ์๋ํฉ๋๋ค.") | |
| api_key_manager['failed_keys'].clear() | |
| available_keys = api_key_manager['keys'] | |
| # ์ฒซ ๋ฒ์งธ ์ฌ์ฉ์ ๋๋ค์ผ๋ก ์ ํ | |
| if api_key_manager['current_index'] == -1: | |
| available_indices = [ | |
| i for i, key in enumerate(api_key_manager['keys']) | |
| if i not in api_key_manager['failed_keys'] | |
| ] | |
| api_key_manager['current_index'] = random.choice(available_indices) | |
| logger.info(f"์ฒซ ๋ฒ์งธ API ํค ์ ํ: ๋๋ค ์ธ๋ฑ์ค {api_key_manager['current_index'] + 1}") | |
| else: | |
| # ์ดํ ์ฌ์ฉ์ ์์ฐจ์ ์ผ๋ก ๋ค์ ํค ์ ํ (์คํจํ ํค๋ ๊ฑด๋๋) | |
| original_index = api_key_manager['current_index'] | |
| for _ in range(len(api_key_manager['keys'])): | |
| api_key_manager['current_index'] = (api_key_manager['current_index'] + 1) % len(api_key_manager['keys']) | |
| if api_key_manager['current_index'] not in api_key_manager['failed_keys']: | |
| break | |
| if api_key_manager['current_index'] in api_key_manager['failed_keys']: | |
| # ๋ชจ๋ ํค๋ฅผ ์๋ํ์ง๋ง ์ฌ์ฉํ ์ ์๋ ํค๊ฐ ์์ | |
| logger.warning("์ฌ์ฉ ๊ฐ๋ฅํ API ํค๊ฐ ์์ต๋๋ค. ์คํจ ๋ชฉ๋ก์ ์ด๊ธฐํํฉ๋๋ค.") | |
| api_key_manager['failed_keys'].clear() | |
| api_key_manager['current_index'] = 0 | |
| logger.info(f"๋ค์ API ํค ์ ํ: ์ธ๋ฑ์ค {api_key_manager['current_index'] + 1}") | |
| return api_key_manager['keys'][api_key_manager['current_index']] | |
| def mark_api_key_failed(api_key): | |
| """API ํค๋ฅผ ์คํจ ๋ชฉ๋ก์ ์ถ๊ฐํฉ๋๋ค.""" | |
| global api_key_manager | |
| try: | |
| key_index = api_key_manager['keys'].index(api_key) | |
| api_key_manager['failed_keys'].add(key_index) | |
| logger.warning(f"API ํค ์ธ๋ฑ์ค {key_index + 1}๋ฅผ ์คํจ ๋ชฉ๋ก์ ์ถ๊ฐํ์ต๋๋ค.") | |
| except ValueError: | |
| logger.error("์คํจํ API ํค๋ฅผ ๋ชฉ๋ก์์ ์ฐพ์ ์ ์์ต๋๋ค.") | |
| def test_api_key(api_key): | |
| """API ํค๊ฐ ์ ํจํ์ง ํ ์คํธํฉ๋๋ค.""" | |
| try: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel(model_name="gemini-2.0-flash") | |
| # ๊ฐ๋จํ ํ ์คํธ ์์ฒญ | |
| response = model.generate_content("Test", generation_config={ | |
| "max_output_tokens": 10, | |
| "temperature": 0.1, | |
| }) | |
| if response and response.text: | |
| return True | |
| return False | |
| except Exception as e: | |
| logger.error(f"API ํค ํ ์คํธ ์คํจ: {str(e)}") | |
| return False | |
| def get_working_api_key(): | |
| """์๋ํ๋ API ํค๋ฅผ ์ฐพ์ ๋ฐํํฉ๋๋ค.""" | |
| max_attempts = len(api_key_manager['keys']) if api_key_manager['is_initialized'] else 5 | |
| for attempt in range(max_attempts): | |
| try: | |
| api_key = get_next_api_key() | |
| if test_api_key(api_key): | |
| logger.info(f"์๋ํ๋ API ํค๋ฅผ ์ฐพ์์ต๋๋ค. (์๋ {attempt + 1}ํ)") | |
| return api_key | |
| else: | |
| mark_api_key_failed(api_key) | |
| logger.warning(f"API ํค ํ ์คํธ ์คํจ. ๋ค์ ํค๋ก ์๋ํฉ๋๋ค. (์๋ {attempt + 1}ํ)") | |
| except Exception as e: | |
| logger.error(f"API ํค ๊ฐ์ ธ์ค๊ธฐ ์คํจ: {str(e)}") | |
| raise Exception("์ฌ์ฉ ๊ฐ๋ฅํ API ํค๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.") | |
| # ํ๊ตญ์ด ์์ฐ์ค๋ฝ๊ฒ ์กฐ๊ฑด (ํ-์ ๋ฒ์ญ์ฉ) | |
| KO_EN_CONDITIONS = """ | |
| ์ถ๊ฐ ์กฐ๊ฑด (ํ๊ตญ์ด ์์ฐ์ค๋ฝ๊ฒ ํ๊ธฐ): | |
| - ๋ฒ์ญ์ฒด๊ฐ ์๋ ์์ฐ์ค๋ฝ๊ณ ๋งค๋๋ฌ์ด ํ๊ตญ์ด๋ฅผ ์ต์ฐ์ ์ ์ผ๋ก ์์ฑ | |
| - ์๋ ์กฐ๊ฑด์ ๋ฐ๋ผ์ฃผ์ธ์: | |
| 1. ๋ฌธ๋ฒ์ ์ ํ์ฑ | |
| - ํผ๋๋ฌธ๋ณด๋ค ๋ฅ๋๋ฌธ ์ฐ์ | |
| - ๋๋ช ์ฌ ์ต์ํ | |
| - ๋ช ์ฌํ๋ณด๋ค ๋์ฌ/ํ์ฉ์ฌ ์ฐ์ | |
| - ํ์ฌ์งํํ๋ณด๋ค ๋จ์ํ์ฌ๋ ์๋ฃํ ์ฐ์ | |
| - ์ฃผ์ด-๋ชฉ์ ์ด-์์ ์ด ์์ ์ ์ง | |
| - "~์์ด์" ํํ ์ง์ | |
| 2. ์ฃผ์ ๋ฌธ๋งฅ์ ๋ง๋ ์์ฐ์ค๋ฌ์ด ํ๋ฆ | |
| 3. ์ฃผ์ ์ ์ํฉ์ ์ด์ธ๋ฆฌ๋ ์ดํ ์ ํ | |
| 4. ๋ฌธํ์ ์ ํฉ์ฑ ๊ณ ๋ ค | |
| 5. ์ ์์ ๋์์ค ๋ฐ์ (๊ณต๊ฐ ์ด๋๋ ํํ) | |
| 6. ์ง์ญ/์์ญ ๊ท ํ ์ ์ง | |
| 7. ์ฃผ์ ํค์๋ ๋ฐ๋ณต ์ธ๊ธ ๊ธ์ง | |
| 8. AI๊ฐ ์ด ๊ธ์ฒ๋ผ ๋ณด์ด์ง ์๋๋ก ์ฃผ์ | |
| """ | |
| # ์์คํ ๋ฉ์์ง (ํ-์ค ๋ฒ์ญ์ฉ) | |
| KO_ZH_SYSTEM_MESSAGE = """๋น์ ์ ํ๊ตญ์ด-์ค๊ตญ์ด ์๋ฐฉํฅ ๋ฒ์ญ ์ด์์คํดํธ์ ๋๋ค. | |
| ์๋ ์กฐ๊ฑด์ ์ถฉ์คํ ๋ฐ๋ฅด์ธ์. | |
| [๋ชฉ์ ๋ฐ ์ํฉ] | |
| - ์ฌ์ฉ์ ๋ชฉ์ : 1688 ์์ฑ, ๋๋งค, ์ ํต ๊ด๋ จ๋ ์ํฉ์์ ์ธ์ด ์ฅ๋ฒฝ ์์ด ์ํํ ์ํต์ ๋๋๋ค. | |
| - ์ฌ์ฉ์ ์ ๋ ฅ์ด ํ๊ตญ์ด์ผ ๊ฒฝ์ฐ ์์ฐ์ค๋ฝ๊ณ ๋งฅ๋ฝ์ ๋ง๊ฒ ์ค๊ตญ์ด๋ก ๋ฒ์ญํ๋ค. | |
| - ์ฌ์ฉ์ ์ ๋ ฅ์ด ์ค๊ตญ์ด์ผ ๊ฒฝ์ฐ ์์ฐ์ค๋ฝ๊ณ ๋งฅ๋ฝ์ ๋ง๊ฒ ํ๊ตญ์ด๋ก ๋ฒ์ญํ๋ค. | |
| [ํ๊ตญ์ด ์์ฐ์ค๋ฝ๊ฒ ์กฐ๊ฑด์ ๋ฆฌ] | |
| 1. ๋ฌธ๋ฒ์ ์ ํ์ฑ | |
| - ํผ๋๋ฌธ ๋์ ๋ฅ๋๋ฌธ | |
| - ๋๋ช ์ฌ ์ต์ํ | |
| - ๋ช ์ฌํ๋ณด๋ค ๋์ฌ ๋ฐ ํ์ฉ์ฌ ์ฐ์ | |
| - ๋จ์ํ์ฌํ์ด๋ ์๋ฃํ ์ฐ์ | |
| - ๋ฌธ์ฅ ๊ตฌ์กฐ๋ ์ฃผ์ด-๋ชฉ์ ์ด-๋์ฌ | |
| - "~์์ด์" ํํ ์ฌ์ฉ ์ง์ | |
| 2. ์ฃผ์ ๋งฅ๋ฝ์ ๋ง๋ ์์ฐ์ค๋ฌ์ด ํํ | |
| 3. ์ ์ ํ ์ดํ ์ ํ(์์ฑ, ๋๋งค, ์ ํต ์ํฉ ๊ณ ๋ ค) | |
| 4. ๋ฌธํ์ , ์ํฉ์ ์ ํฉ์ฑ | |
| 5. ์ ์์ ๋์์ค ์กฐํ | |
| 6. ์ง์ญ๊ณผ ์์ญ์ ๊ท ํ | |
| 7. ์ฃผ์ ํค์๋ ๋ฐ๋ณต ๊ธ์ง | |
| 8. ์์ฑ AI ํฐ ๋์ง ์๋๋ก ์ฃผ์ | |
| [์ถ๊ฐ์ฌํญ] | |
| - ๋ฒ์ญ ์ ์ด์ํ ๋ฒ์ญ์ฒด๋ฅผ ํผํ๊ณ ์์ฐ์ค๋ฌ์ด ํํ์ ์ฌ์ฉํ๋ค. | |
| - ์ถ๋ ฅ์ ๋ฒ์ญ๋ ๊ฒฐ๊ณผ๋ฌธ๋ง ์ ์ํ๋ค. | |
| """ | |
| # 1์ฐจ ์นดํ ๊ณ ๋ฆฌ ๋ฐ 2์ฐจ ์ง๋ฌธ ๋ชฉ๋ก ์ ์ | |
| categories = { | |
| "๊ธฐ๋ณธ ์ธ์ฌ ๋ฐ ๊ฑฐ๋ ์์": [ | |
| "์๋ ํ์ธ์, ๊ท์ฌ์ ์ ํ์ด ๋ง์์ ๋ญ๋๋ค. ์์ธํ ์ค๋ช ๋ถํ๋๋ฆฝ๋๋ค.", | |
| "์ ํ์ด ํ์ฌ ์ฌ๊ณ ๊ฐ ์๋์?", | |
| "๋๋งค ๊ฑฐ๋๋ฅผ ํ๊ณ ์ถ์ต๋๋ค. ๊ฐ๋ฅํ๊ฐ์?", | |
| "์๋ก์ด ๊ณ ๊ฐ์ธ๋ฐ ๊ฑฐ๋๋ฅผ ์์ํ ์ ์์๊น์?", | |
| "ํน์ ํ๊ตญ ๊ณ ๊ฐ๋ค๊ณผ๋ ๊ฑฐ๋๋ฅผ ํ๊ณ ๊ณ์ ๊ฐ์?", | |
| "์ ํ์ ๋ํ ์์ธํ ์ค๋ช ์๋ ๋ธ๋ก์์ด๊ฐ ์๋์?", | |
| "์ ํ์ด ์ธ๊ธฐ ์ํ์ธ๊ฐ์? ํ๋งค ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ ์ ์๋์?", | |
| "ํ๋งค ์ง์ญ์ด๋ ์ ํ์ด ์๋์?", | |
| "์ด ์ ํ์ ํ์ฌ ์์ฅ ํธ๋ ๋๋ ์ด๋ค๊ฐ์?", | |
| "ํ๋งค ๊ธฐ๋ก์ด ์๋ ์ ํ์ธ์ง ํ์ธํ ์ ์๋์?" | |
| ], | |
| "์ ํ ์์ธ ๋ฐ ํ์ง ๊ด๋ จ ์ง๋ฌธ": [ | |
| "์ด ์ ํ์ ์ฃผ์ ๊ธฐ๋ฅ์ ๋ฌด์์ธ๊ฐ์?", | |
| "ํ์ง ํ ์คํธ๋ฅผ ํต๊ณผํ ์ ํ์ธ๊ฐ์?", | |
| "์ ํ์ ์์ฌ๋ฃ๋ ๋ฌด์์ธ๊ฐ์?", | |
| "์์ฐ์ง๋ ์ด๋์ธ๊ฐ์?", | |
| "์ ํ์ ์ฌ์ฉํ๊ธฐ์ ์์ ํ๊ฐ์?", | |
| "์นํ๊ฒฝ ์ธ์ฆ์ ๋ฐ์ ์ ํ์ธ๊ฐ์?", | |
| "์ ํ ์ฌ์ฉ ๊ธฐ๊ฐ(๋ด๊ตฌ์ฑ)์ ์ด๋ ์ ๋์ธ๊ฐ์?", | |
| "๋ณด์ฆ ๊ธฐ๊ฐ์ ์ด๋ป๊ฒ ๋๋์?", | |
| "์ฌ์ฉ ์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ฉด ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผ ํ๋์?", | |
| "์ฌ์ฉ ์ค๋ช ์๊ฐ ํฌํจ๋์ด ์๋์?" | |
| ], | |
| "๊ฐ๊ฒฉ ๋ฐ ๊ฒฐ์ ๊ด๋ จ ์ง๋ฌธ": [ | |
| "๋๋งค ๊ฐ๊ฒฉ์ ์ผ๋ง์ธ๊ฐ์?", | |
| "๋๋ ๊ตฌ๋งค ์ ํ ์ธ์ด ๊ฐ๋ฅํ๊ฐ์?", | |
| "์ฒซ ๊ฑฐ๋ ๊ณ ๊ฐ์ ์ํ ํน๋ณ ํํ์ด ์๋์?", | |
| "๊ฒฐ์ ์กฐ๊ฑด์ ์๋ ค์ฃผ์ธ์.", | |
| "๋ถ๋ถ ๊ฒฐ์ ํ ์๊ธ ๊ฒฐ์ ๊ฐ ๊ฐ๋ฅํ๊ฐ์?", | |
| "๋๋ ์ฃผ๋ฌธ ์ ์ด๊ธฐ ๋ณด์ฆ๊ธ์ ์๊ตฌํ์๋์?", | |
| "๋ฐฐ์ก๋ฃ ํฌํจ ๊ฐ๊ฒฉ์ธ๊ฐ์?", | |
| "์ถ๊ฐ ๋น์ฉ์ด ๋ฐ์ํ ์ ์๋์?", | |
| "ํ ์ธ ์ฟ ํฐ์ด๋ ํ๋ก๋ชจ์ ์ฝ๋๋ ์๋์?", | |
| "์ ๊ธฐ ๊ตฌ๋งค ๊ณ ๊ฐ์ ์ํ ํ ์ธ ์ ์ฑ ์ด ์๋์?" | |
| ], | |
| "๋ฐฐ์ก ๋ฐ ๋ฌผ๋ฅ ๊ด๋ จ ์ง๋ฌธ": [ | |
| "๋ฐฐ์ก์ ์ด๋ ํ๋ฐฐ์ฌ๋ฅผ ํตํด ์งํ๋๋์?", | |
| "์์ ๋ฐฐ์ก ๊ธฐ๊ฐ์ ์ผ๋ง๋ ๋๋์?", | |
| "ํ๊ตญ๊น์ง ๋ฐฐ์ก์ด ๊ฐ๋ฅํ๊ฐ์?", | |
| "๊ตญ์ ๋ฐฐ์ก๋ฃ๋ ์ด๋ป๊ฒ ๊ณ์ฐ๋๋์?", | |
| "๋๋ ์ฃผ๋ฌธ ์ ๋ฐฐ์ก๋น ํ ์ธ ํํ์ด ์๋์?", | |
| "๋ฐฐ์ก ์ค ํ์์ด ๋ฐ์ํ๋ฉด ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์?", | |
| "์ฃผ๋ฌธ ํ ๋ช ์ผ ๋ด์ ๋ฐฐ์ก์ด ์์๋๋์?", | |
| "๋ฐฐ์ก ์ถ์ ๋ฒํธ๋ฅผ ์ ๊ณตํ์๋์?", | |
| "์ฃผ๋ฌธ ์ํ๋ฅผ ์ด๋ป๊ฒ ํ์ธํ ์ ์๋์?", | |
| "๋ฐฐ์ก๋ฃ๋ฅผ ์ง์ ๋ถ๋ดํด์ผ ํ๋์?" | |
| ] | |
| } | |
| def guess_lang(text: str) -> str: | |
| text = text.strip() | |
| if not text: | |
| return "" | |
| try: | |
| lang = detect(text) | |
| if lang in ['ko', 'en', 'zh-cn', 'zh-tw']: | |
| if lang.startswith('zh'): | |
| return 'zh' | |
| return lang | |
| else: | |
| # langdetect ๊ฒฐ๊ณผ๊ฐ ko, en, zh๊ฐ ์๋ ๊ฒฝ์ฐ ํด๋ฆฌ์คํฑ ์ ์ฉ | |
| if all('a' <= c.lower() <= 'z' for c in text if c.isalpha()): | |
| return 'en' | |
| # ํ๊ธ ์์ ๋ฒ์ ํ๋จ | |
| elif any('\uac00' <= c <= '\ud7a3' for c in text): | |
| return 'ko' | |
| # ์ค๊ตญ์ด ๋ฒ์ ํ๋จ | |
| elif any('\u4e00' <= c <= '\u9fff' for c in text): | |
| return 'zh' | |
| else: | |
| # ๋ํดํธ ์์ด ์ฒ๋ฆฌ | |
| return 'en' | |
| except: | |
| # langdetect ์คํจ ์ ํด๋ฆฌ์คํฑ ์ ์ฉ | |
| if all('a' <= c.lower() <= 'z' for c in text if c.isalpha()): | |
| return 'en' | |
| elif any('\uac00' <= c <= '\ud7a3' for c in text): | |
| return 'ko' | |
| elif any('\u4e00' <= c <= '\u9fff' for c in text): | |
| return 'zh' | |
| else: | |
| return 'en' | |
| def validate_input(text: str, target_langs: list) -> bool: | |
| lang = guess_lang(text) | |
| logger.info(f"๊ฐ์ง๋(์ถ์ ) ์ธ์ด: {lang}") | |
| return lang in target_langs | |
| def translate_ko_en(input_text: str): | |
| logger.info("ํ-์ ๋ฒ์ญ ์์") | |
| if not validate_input(input_text, ['ko', 'en']): | |
| return "์ ํจํ์ง ์์ ์ ๋ ฅ์ ๋๋ค. ํ๊ตญ์ด ๋๋ ์์ด ํ ์คํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์." | |
| try: | |
| detected_lang = guess_lang(input_text) | |
| logger.info(f"๊ฐ์ง๋(์ถ์ ) ์ธ์ด: {detected_lang}") | |
| if detected_lang == 'ko': | |
| direction = "Korean to English" | |
| input_lang = "Korean" | |
| output_lang = "English" | |
| else: | |
| direction = "English to Korean" | |
| input_lang = "English" | |
| output_lang = "Korean" | |
| logger.info(f"๋ฒ์ญ ๋ฐฉํฅ: {direction}") | |
| current_time = int(time.time() * 1000) | |
| random.seed(current_time) | |
| temperature = random.uniform(0.4, 0.85) | |
| top_p = random.uniform(0.9, 0.98) | |
| request_id = str(uuid.uuid4())[:8] | |
| timestamp_micro = int(time.time() * 1000000) % 1000 | |
| current_hour = datetime.now().hour | |
| time_context = f"Consider the current time is {current_hour}:00." | |
| # ํ๊ตญ์ด ์ถ๋ ฅ ์ ์์ฐ์ค๋ฌ์ด ํ๊ตญ์ด ์กฐ๊ฑด ์ถ๊ฐ | |
| korean_conditions = "" | |
| if output_lang == "Korean": | |
| korean_conditions = KO_EN_CONDITIONS | |
| system_prompt = "You are a helpful translator." | |
| prompt = f""" | |
| Translate the following {input_lang} text into {output_lang}: | |
| {input_text} | |
| Rules: | |
| 1. Output ONLY the {output_lang} translation without additional labels or formatting. | |
| 2. Preserve the original meaning and intent. | |
| 3. No explanations or meta-commentary. | |
| 4. No formatting beyond basic text. | |
| 5. {time_context} | |
| [Seed: {current_time}] | |
| {korean_conditions} | |
| """ | |
| logger.debug(f"ํ๋กฌํํธ:\n{prompt}") | |
| # ์๋ํ๋ API ํค ๊ฐ์ ธ์ค๊ธฐ | |
| api_key = get_working_api_key() | |
| genai.configure(api_key=api_key) | |
| # Gemini ๋ชจ๋ธ ์ค์ | |
| generation_config = { | |
| "max_output_tokens": 200, | |
| "temperature": temperature, | |
| "top_p": top_p, | |
| } | |
| # Gemini ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ | |
| model = genai.GenerativeModel( | |
| model_name="gemini-2.0-flash", | |
| generation_config=generation_config, | |
| ) | |
| # ๋ฒ์ญ ์คํ | |
| full_prompt = f"{system_prompt}\n\n{prompt}" | |
| response = model.generate_content(full_prompt) | |
| translated = response.text.strip() | |
| logger.info("Gemini ๋ฒ์ญ ์๋ฃ") | |
| logger.debug(f"๋ฒ์ญ ๊ฒฐ๊ณผ:\n{translated}") | |
| return translated | |
| except Exception as e: | |
| logger.error(f"๋ฒ์ญ ์ค ์์์น ๋ชปํ ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| # API ํค ๋ฌธ์ ์ธ ๊ฒฝ์ฐ ํด๋น ํค๋ฅผ ์คํจ ๋ชฉ๋ก์ ์ถ๊ฐ | |
| if "api" in str(e).lower() or "key" in str(e).lower() or "quota" in str(e).lower(): | |
| try: | |
| current_key = api_key_manager['keys'][api_key_manager['current_index']] | |
| mark_api_key_failed(current_key) | |
| logger.info("API ํค ๋ฌธ์ ๋ก ์ธํ ์ค๋ฅ. ๋ค์ ํค๋ก ์ฌ์๋ํ์ธ์.") | |
| except: | |
| pass | |
| return f"๋ฒ์ญ ์ค ์์์น ๋ชปํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}" | |
| def translate_ko_zh(input_text: str): | |
| logger.info("ํ-์ค ๋ฒ์ญ ์์") | |
| if not validate_input(input_text, ['ko', 'zh']): | |
| return "์ ํจํ์ง ์์ ์ ๋ ฅ์ ๋๋ค. ํ๊ตญ์ด ๋๋ ์ค๊ตญ์ด ํ ์คํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์." | |
| try: | |
| detected_lang = guess_lang(input_text) | |
| logger.info(f"๊ฐ์ง๋(์ถ์ ) ์ธ์ด: {detected_lang}") | |
| if detected_lang == 'ko': | |
| direction = "Korean to Chinese" | |
| input_lang = "Korean" | |
| output_lang = "Chinese" | |
| else: | |
| direction = "Chinese to Korean" | |
| input_lang = "Chinese" | |
| output_lang = "Korean" | |
| logger.info(f"๋ฒ์ญ ๋ฐฉํฅ: {direction}") | |
| current_time = int(time.time() * 1000) | |
| random.seed(current_time) | |
| temperature = random.uniform(0.5, 0.8) | |
| top_p = random.uniform(0.9, 0.98) | |
| # ์๋ํ๋ API ํค ๊ฐ์ ธ์ค๊ธฐ | |
| api_key = get_working_api_key() | |
| genai.configure(api_key=api_key) | |
| # Gemini ๋ชจ๋ธ ์ค์ | |
| generation_config = { | |
| "max_output_tokens": 1024, | |
| "temperature": temperature, | |
| "top_p": top_p, | |
| } | |
| # Gemini ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ | |
| model = genai.GenerativeModel( | |
| model_name="gemini-2.0-flash", | |
| generation_config=generation_config, | |
| ) | |
| # ํ๋กฌํํธ ์ค๋น | |
| prompt = f"{KO_ZH_SYSTEM_MESSAGE}\n\n{input_text}" | |
| # ๋ฒ์ญ ์คํ | |
| response = model.generate_content(prompt) | |
| translated = response.text.strip() | |
| logger.info("Gemini ๋ฒ์ญ ์๋ฃ") | |
| logger.debug(f"๋ฒ์ญ ๊ฒฐ๊ณผ:\n{translated}") | |
| return translated | |
| except Exception as e: | |
| logger.error(f"๋ฒ์ญ ์ค ์์์น ๋ชปํ ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| # API ํค ๋ฌธ์ ์ธ ๊ฒฝ์ฐ ํด๋น ํค๋ฅผ ์คํจ ๋ชฉ๋ก์ ์ถ๊ฐ | |
| if "api" in str(e).lower() or "key" in str(e).lower() or "quota" in str(e).lower(): | |
| try: | |
| current_key = api_key_manager['keys'][api_key_manager['current_index']] | |
| mark_api_key_failed(current_key) | |
| logger.info("API ํค ๋ฌธ์ ๋ก ์ธํ ์ค๋ฅ. ๋ค์ ํค๋ก ์ฌ์๋ํ์ธ์.") | |
| except: | |
| pass | |
| return f"๋ฒ์ญ ์ค ์์์น ๋ชปํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}" | |
| def update_subcategories(category): | |
| if category in categories: | |
| return gr.update(choices=categories[category], value=None) | |
| else: | |
| return gr.update(choices=[], value=None) | |
| def set_input_text(selected_text): | |
| return selected_text | |
| # ์ปค์คํ CSS ์คํ์ผ | |
| custom_css = """ | |
| :root { | |
| --primary-color: #FB7F0D; | |
| --secondary-color: #ff9a8b; | |
| --accent-color: #FF6B6B; | |
| --background-color: #FFF3E9; | |
| --card-bg: #ffffff; | |
| --text-color: #334155; | |
| --border-radius: 18px; | |
| --shadow: 0 8px 30px rgba(251, 127, 13, 0.08); | |
| } | |
| body { | |
| font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif; | |
| background-color: var(--background-color); | |
| color: var(--text-color); | |
| line-height: 1.6; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| .gradio-container { | |
| width: 100%; | |
| margin: 0 auto; | |
| padding: 20px; | |
| background-color: var(--background-color); | |
| } | |
| .custom-header { | |
| background: #FF7F00; | |
| padding: 2rem; | |
| border-radius: 15px; | |
| margin-bottom: 20px; | |
| box-shadow: var(--shadow); | |
| text-align: center; | |
| } | |
| .custom-header h1 { | |
| margin: 0; | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| color: black; | |
| } | |
| .custom-header p { | |
| margin: 10px 0 0; | |
| font-size: 1.2rem; | |
| color: black; | |
| } | |
| .custom-frame { | |
| background-color: var(--card-bg); | |
| border: 1px solid rgba(0, 0, 0, 0.04); | |
| border-radius: var(--border-radius); | |
| padding: 20px; | |
| margin: 10px 0; | |
| box-shadow: var(--shadow); | |
| } | |
| .custom-section-group { | |
| margin-top: 20px; | |
| padding: 0; | |
| border: none; | |
| border-radius: 0; | |
| background-color: var(--background-color); | |
| box-shadow: none !important; | |
| } | |
| .custom-button { | |
| border-radius: 30px !important; | |
| background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important; | |
| color: white !important; | |
| font-size: 18px !important; | |
| padding: 10px 20px !important; | |
| border: none; | |
| box-shadow: 0 4px 8px rgba(251, 127, 13, 0.25); | |
| transition: transform 0.3s ease; | |
| } | |
| .custom-button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3); | |
| } | |
| .custom-title { | |
| font-size: 28px; | |
| font-weight: bold; | |
| margin-bottom: 10px; | |
| color: var(--text-color); | |
| border-bottom: 2px solid var(--primary-color); | |
| padding-bottom: 5px; | |
| } | |
| .image-container { | |
| border-radius: var(--border-radius); | |
| overflow: hidden; | |
| border: 1px solid rgba(0, 0, 0, 0.08); | |
| transition: all 0.3s ease; | |
| background-color: white; | |
| } | |
| .image-container:hover { | |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); | |
| } | |
| .gr-input, .gr-text-input, .gr-sample-inputs { | |
| border-radius: var(--border-radius) !important; | |
| border: 1px solid #dddddd !important; | |
| padding: 12px !important; | |
| box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .gr-input:focus, .gr-text-input:focus { | |
| border-color: var(--primary-color) !important; | |
| outline: none !important; | |
| box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important; | |
| } | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(0, 0, 0, 0.05); | |
| border-radius: 10px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--primary-color); | |
| border-radius: 10px; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-out; | |
| } | |
| @media (max-width: 768px) { | |
| .button-grid { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| } | |
| .section-title { | |
| display: flex; | |
| align-items: center; | |
| font-size: 20px; | |
| font-weight: 700; | |
| color: #333333; | |
| margin-bottom: 10px; | |
| padding-bottom: 5px; | |
| border-bottom: 2px solid #FB7F0D; | |
| font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif; | |
| } | |
| .section-title img { | |
| margin-right: 10px; | |
| width: 24px; | |
| height: 24px; | |
| } | |
| .guide-container { | |
| background-color: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--shadow); | |
| padding: 1.5rem; | |
| margin-bottom: 1.5rem; | |
| border: 1px solid rgba(0, 0, 0, 0.04); | |
| } | |
| .guide-title { | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| color: var(--primary-color); | |
| margin-bottom: 1.5rem; | |
| padding-bottom: 0.5rem; | |
| border-bottom: 2px solid var(--primary-color); | |
| display: flex; | |
| align-items: center; | |
| } | |
| .guide-title i { | |
| margin-right: 0.8rem; | |
| font-size: 1.5rem; | |
| } | |
| .guide-item { | |
| display: flex; | |
| margin-bottom: 1rem; | |
| align-items: flex-start; | |
| } | |
| .guide-number { | |
| background-color: var(--primary-color); | |
| color: white; | |
| width: 25px; | |
| height: 25px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: bold; | |
| margin-right: 10px; | |
| flex-shrink: 0; | |
| } | |
| .guide-text { | |
| flex: 1; | |
| line-height: 1.6; | |
| } | |
| .guide-text a { | |
| color: var(--primary-color); | |
| text-decoration: underline; | |
| font-weight: 600; | |
| } | |
| .guide-text a:hover { | |
| color: var(--accent-color); | |
| } | |
| .guide-highlight { | |
| background-color: rgba(251, 127, 13, 0.1); | |
| padding: 2px 5px; | |
| border-radius: 4px; | |
| font-weight: 500; | |
| } | |
| """ | |
| # FontAwesome ์์ด์ฝ ํฌํจ | |
| fontawesome_link = """ | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" /> | |
| """ | |
| def create_interface(): | |
| with gr.Blocks(css=custom_css, theme=gr.themes.Soft( | |
| primary_hue=gr.themes.Color( | |
| c50="#FFF7ED", | |
| c100="#FFEDD5", | |
| c200="#FED7AA", | |
| c300="#FDBA74", | |
| c400="#FB923C", | |
| c500="#F97316", | |
| c600="#EA580C", | |
| c700="#C2410C", | |
| c800="#9A3412", | |
| c900="#7C2D12", | |
| c950="#431407", | |
| ), | |
| secondary_hue="zinc", | |
| neutral_hue="zinc", | |
| font=("Pretendard", "sans-serif") | |
| )) as demo: | |
| gr.HTML(fontawesome_link) | |
| with gr.Tabs() as tabs: | |
| with gr.TabItem("ํ๊ตญ์ด-์ค๊ตญ์ด ๋ฒ์ญ"): | |
| with gr.Column(elem_classes="custom-frame"): | |
| gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/3097/3097412.png"> ์นดํ ๊ณ ๋ฆฌ ์ ํ</div>') | |
| with gr.Row(): | |
| category_dropdown = gr.Dropdown( | |
| label="1์ฐจ ์นดํ ๊ณ ๋ฆฌ ์ ํ", | |
| choices=list(categories.keys()), | |
| value=None | |
| ) | |
| subcategory_dropdown = gr.Dropdown( | |
| label="2์ฐจ ์ง๋ฌธ ์ ํ", | |
| choices=[], | |
| value=None | |
| ) | |
| with gr.Column(elem_classes="custom-frame"): | |
| gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/4297/4297825.png"> ๋ฒ์ญ</div>') | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| kz_input_box = gr.Textbox( | |
| label="์ ๋ ฅ (ํ๊ตญ์ด ๋๋ ์ค๊ตญ์ด)", | |
| lines=8, | |
| placeholder="๋ฒ์ญํ ํ ์คํธ๋ฅผ ์ ๋ ฅํ์ธ์..." | |
| ) | |
| with gr.Column(scale=1): | |
| kz_output_box = gr.Textbox( | |
| label="๋ฒ์ญ ๊ฒฐ๊ณผ", | |
| lines=8 | |
| ) | |
| with gr.Column(elem_classes="custom-frame"): | |
| gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/1022/1022293.png"> ๋ฒ์ญ ์คํ</div>') | |
| kz_translate_button = gr.Button("๋ฒ์ญํ๊ธฐ", elem_classes="custom-button") | |
| # 1์ฐจ ์ ํ ์ 2์ฐจ ํญ๋ชฉ ์ ๋ฐ์ดํธ | |
| category_dropdown.change( | |
| fn=update_subcategories, | |
| inputs=category_dropdown, | |
| outputs=subcategory_dropdown | |
| ) | |
| # 2์ฐจ ์ ํ ์ ์ ๋ ฅ๋์ ํด๋น ๊ฐ ์๋ ์ ๋ ฅ | |
| subcategory_dropdown.change( | |
| fn=set_input_text, | |
| inputs=subcategory_dropdown, | |
| outputs=kz_input_box | |
| ) | |
| # ๋ฒ์ญ ํจ์ | |
| kz_translate_button.click( | |
| fn=translate_ko_zh, | |
| inputs=kz_input_box, | |
| outputs=kz_output_box | |
| ) | |
| with gr.TabItem("ํ๊ตญ์ด-์์ด ๋ฒ์ญ"): | |
| with gr.Column(elem_classes="custom-frame"): | |
| gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/4297/4297825.png"> ๋ฒ์ญ</div>') | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| ke_input_box = gr.Textbox( | |
| label="์ ๋ ฅ ํ ์คํธ (ํ๊ตญ์ด ๋๋ ์์ด)", | |
| placeholder="๋ฒ์ญํ ํ ์คํธ๋ฅผ ์ ๋ ฅํ์ธ์...", | |
| lines=8 | |
| ) | |
| with gr.Column(scale=1): | |
| ke_output_box = gr.Textbox( | |
| label="๋ฒ์ญ ๊ฒฐ๊ณผ", | |
| lines=8, | |
| interactive=False | |
| ) | |
| with gr.Column(elem_classes="custom-frame"): | |
| gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/1022/1022293.png"> ๋ฒ์ญ ์คํ</div>') | |
| ke_translate_button = gr.Button("๋ฒ์ญํ๊ธฐ", elem_classes="custom-button") | |
| # ๋ฒ์ญ ํจ์ | |
| ke_translate_button.click( | |
| fn=translate_ko_en, | |
| inputs=ke_input_box, | |
| outputs=ke_output_box | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_interface() | |
| demo.launch() |