Download text_utils.py from ssboost/f23f43gf: direct link, hf CLI and curl.
- Browser
- Download file 10 kB
-
https://huggingface.co/spaces/ssboost/f23f43gf/resolve/main/text_utils.py
- Command line
-
hf download hf://spaces/ssboost/f23f43gf/text_utils.py
-
curl -L -o text_utils.py https://huggingface.co/spaces/ssboost/f23f43gf/resolve/main/text_utils.py
10 kB
| """ | |
| ํ ์คํธ ์ฒ๋ฆฌ ๊ด๋ จ ์ ํธ๋ฆฌํฐ ํจ์ ๋ชจ์ | |
| - ํ ์คํธ ๋ถ๋ฆฌ ๋ฐ ์ ์ | |
| - ํค์๋ ์ถ์ถ | |
| - Gemini API ํค ํตํฉ ๊ด๋ฆฌ ์ ์ฉ | |
| """ | |
| import re | |
| import google.generativeai as genai | |
| import os | |
| import logging | |
| import api_utils # API ํค ํตํฉ ๊ด๋ฆฌ๋ฅผ ์ํ ์ํฌํธ | |
| # ๋ก๊น ์ค์ | |
| logger = logging.getLogger(__name__) | |
| logger.setLevel(logging.INFO) | |
| formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
| handler = logging.StreamHandler() | |
| handler.setFormatter(formatter) | |
| logger.addHandler(handler) | |
| # ===== Gemini ๋ชจ๋ธ ๊ด๋ฆฌ ํจ์๋ค ===== | |
| def get_gemini_model(): | |
| """api_utils์์ Gemini ๋ชจ๋ธ ๊ฐ์ ธ์ค๊ธฐ (ํตํฉ ๊ด๋ฆฌ)""" | |
| try: | |
| model = api_utils.get_gemini_model() | |
| if model: | |
| logger.info("Gemini ๋ชจ๋ธ ๋ก๋ ์ฑ๊ณต (api_utils ํตํฉ ๊ด๋ฆฌ)") | |
| return model | |
| else: | |
| logger.warning("์ฌ์ฉ ๊ฐ๋ฅํ Gemini API ํค๊ฐ ์์ต๋๋ค.") | |
| return None | |
| except Exception as e: | |
| logger.error(f"Gemini ๋ชจ๋ธ ๋ก๋ ์คํจ: {e}") | |
| return None | |
| # ํ ์คํธ ๋ถ๋ฆฌ ๋ฐ ์ ์ ํจ์ | |
| def clean_and_split(text, only_korean=False): | |
| """ํ ์คํธ๋ฅผ ๋ถ๋ฆฌํ๊ณ ์ ์ ํ๋ ํจ์""" | |
| text = re.sub(r"[()\[\]-]", " ", text) | |
| text = text.replace("/", " ") | |
| if only_korean: | |
| # ํ๊ธ๋ง ์ถ์ถ ์ต์ ์ด ์ผ์ง ๊ฒฝ์ฐ | |
| # ๊ณต๋ฐฑ์ด๋ ์ผํ๋ก ๊ตฌ๋ถํ ๋ค ํ๊ธ๋ง ์ถ์ถ | |
| words = re.split(r"[ ,]", text) | |
| cleaned = [] | |
| for word in words: | |
| word = word.strip() | |
| # ํ๊ธ๋ง ๋จ๊ธฐ๊ณ ๋ค๋ฅธ ๋ฌธ์๋ ์ ๊ฑฐ | |
| word = re.sub(r"[^๊ฐ-ํฃ]", "", word) | |
| if word and len(word) >= 1: # ๋น ๋ฌธ์์ด์ด ์๋๊ณ 1๊ธ์ ์ด์์ธ ๊ฒฝ์ฐ๋ง ์ถ๊ฐ | |
| cleaned.append(word) | |
| else: | |
| # ํ๊ธ๋ง ์ถ์ถ ์ต์ ์ด ๊บผ์ง ๊ฒฝ์ฐ - ๋จ์ด ํต์งธ๋ก ์ฒ๋ฆฌ | |
| # ๊ณต๋ฐฑ๊ณผ ์ผํ๋ก ๊ตฌ๋ถํ์ฌ ๋จ์ด ์ ์ฒด๋ฅผ ์ ์ง | |
| words = re.split(r"[,\s]+", text) | |
| cleaned = [] | |
| for word in words: | |
| word = word.strip() | |
| if word and len(word) >= 1: # ๋น ๋ฌธ์์ด์ด ์๋๊ณ 1๊ธ์ ์ด์์ธ ๊ฒฝ์ฐ๋ง ์ถ๊ฐ | |
| cleaned.append(word) | |
| return cleaned | |
| def filter_keywords_with_gemini(pairs, gemini_model=None): | |
| """Gemini AI๋ฅผ ์ฌ์ฉํ์ฌ ํค์๋ ์กฐํฉ ํํฐ๋ง (๊ฐ์ ๋ฒ์ ) - API ํค ํตํฉ ๊ด๋ฆฌ""" | |
| if gemini_model is None: | |
| # api_utils์์ Gemini ๋ชจ๋ธ ๊ฐ์ ธ์ค๊ธฐ | |
| gemini_model = get_gemini_model() | |
| if gemini_model is None: | |
| logger.error("Gemini ๋ชจ๋ธ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. ๋ชจ๋ ํค์๋๋ฅผ ์ ์งํฉ๋๋ค.") | |
| # ์์ ํ๊ฒ ์ฒ๋ฆฌ: ๋ชจ๋ ํค์๋๋ฅผ ์ ์ง | |
| all_keywords = set() | |
| for pair in pairs: | |
| for keyword in pair: | |
| all_keywords.add(keyword) | |
| return list(all_keywords) | |
| # ๋ชจ๋ ํค์๋๋ฅผ ๋ชฉ๋ก์ผ๋ก ์ถ์ถ (์ ๊ฑฐ๋ ํค์๋ ํ์ธ์ฉ) | |
| all_keywords = set() | |
| for pair in pairs: | |
| for keyword in pair: | |
| all_keywords.add(keyword) | |
| # ๋๋ฌด ๋ง์ ์์ด ์์ผ๋ฉด ์ ํ | |
| max_pairs = 50 # ์ต๋ 50๊ฐ ์๋ง ์ฒ๋ฆฌ | |
| pairs_to_process = list(pairs)[:max_pairs] if len(pairs) > max_pairs else pairs | |
| logger.info(f"ํํฐ๋งํ ํค์๋ ์: ์ด {len(pairs)}๊ฐ ์ค {len(pairs_to_process)}๊ฐ ์ฒ๋ฆฌ") | |
| # ๋ณด์์ ์ธ ํ๋กฌํํธ ์ฌ์ฉ - ํค์๋ ์ ๊ฑฐ ์ต์ํ | |
| prompt = ( | |
| "๋ค์์ ์๋น์๊ฐ ๊ฒ์ํ ๊ฐ๋ฅ์ฑ์ด ์๋ ํค์๋ ์ ๋ชฉ๋ก์ ๋๋ค.\n" | |
| "๊ฐ ์์ ๊ฐ์ ๋จ์ด ์กฐํฉ์ด์ง๋ง ์์๋ง ๋ค๋ฅธ ๊ฒฝ์ฐ์ ๋๋ค (์: ์์ง์ค์ง์ด vs ์ค์ง์ด์์ง).\n\n" | |
| "์๋์ ๊ธฐ์ค์ ๋ฐ๋ผ ๊ฐ ์์์ ๋ ์์ฐ์ค๋ฌ์ด ํค์๋๋ฅผ ์ ํํด์ฃผ์ธ์:\n" | |
| "1. ์๋น์๊ฐ ์ผ์์ ์ผ๋ก ์ฌ์ฉํ๋ ์์ฐ์ค๋ฌ์ด ํํ์ ์ฐ์ ์ ํํ์ธ์.\n" | |
| "2. ๋ ํค์๋๊ฐ ๋ชจ๋ ์์ฐ์ค๋ฝ๊ฑฐ๋ ์๋ฏธ๊ฐ ์ฝ๊ฐ ๋ค๋ฅด๋ค๋ฉด, ๋ฐ๋์ ๋ ๋ค ์ ์งํ์ธ์.\n" | |
| "3. ํ์คํ ๋น์์ฐ์ค๋ฝ๊ฑฐ๋ ์ด์ํ ๊ฒฝ์ฐ์๋ง ์ ๊ฑฐํ์ธ์.\n" | |
| "4. ๋ถํ์คํ ๊ฒฝ์ฐ์๋ ๋ฐ๋์ ํค์๋๋ฅผ ์ ์งํ์ธ์.\n" | |
| "5. ์ซ์๋ ์์ด๊ฐ ํฌํจ๋ ํค์๋๋ ํ๊ธ ๋ฉ์ธ ํค์๋๊ฐ ์์ชฝ์ ์ค๋ ํํ๋ฅผ ์ ํํ์ธ์. (์: '10kg ์ค์ง์ด' ๋ณด๋ค '์ค์ง์ด 10kg' ์ ํ)\n" | |
| "6. ๊ฒ์๋์ด 0์ธ ํค์๋๋ผ๋ ์ผ์์ ์ธ ํํ์ด๋ผ๋ฉด ๊ฐ๋ฅํ ์ ์งํ์ธ์. ๋ช ๋ฐฑํ๊ฒ ๋น์ ์์ ์ธ ํํ๋ง ์ ๊ฑฐํ์ธ์.\n\n" | |
| "์ฃผ์: ๊ธฐ๋ณธ์ ์ผ๋ก ๋๋ถ๋ถ์ ํค์๋๋ฅผ ์ ์งํ๊ณ , ๋งค์ฐ ๋ช ํํ๊ฒ ๋น์์ฐ์ค๋ฌ์ด ๊ฒ๋ง ์ ๊ฑฐํ์ธ์.\n\n" | |
| "๊ฒฐ๊ณผ๋ ๋ค์ ํ์์ผ๋ก ์ ๊ณตํด์ฃผ์ธ์:\n" | |
| "- ์ ํ๋ ํค์๋ (์ด์ : ์์ฐ์ค๋ฌ์ด ํํ์ด๊ธฐ ๋๋ฌธ)\n" | |
| "- ์ ํ๋ ํค์๋1, ์ ํ๋ ํค์๋2 (์ด์ : ๋ ๋ค ์์ฐ์ค๋ฝ๊ณ ์๋ฏธ๊ฐ ์กฐ๊ธ ๋ค๋ฆ)\n\n" | |
| ) | |
| # ํค์๋ ์ ๋ชฉ๋ก | |
| formatted = "\n".join([f"- {a}, {b}" for a, b in pairs_to_process]) | |
| full_prompt = prompt + formatted | |
| try: | |
| # ํ์์์ ์ถ๊ฐ | |
| logger.info(f"Gemini API ํธ์ถ ์์ - {len(pairs_to_process)}๊ฐ ํค์๋ ์ ์ฒ๋ฆฌ ์ค...") | |
| # ์๋ต ๋ฐ๊ธฐ (ํ์์์ ๊ธฐ๋ฅ์ด ์์ผ๋ฉด ์ถ๊ฐ) | |
| response = gemini_model.generate_content(full_prompt) | |
| logger.info("Gemini API ์๋ต ์ฑ๊ณต") | |
| lines = response.text.strip().split("\n") | |
| # ์ ํ๋ ํค์๋ ์ถ์ถ (์ผํ๋ก ๊ตฌ๋ถ๋ ๊ฒฝ์ฐ ๋ชจ๋ ํฌํจ) | |
| final_keywords = [] | |
| for line in lines: | |
| if line.startswith("-"): | |
| # ์ด์ ๋ถ๋ถ ์ ๊ฑฐ | |
| keywords_part = line.strip("- ").split("(์ด์ :")[0].strip() | |
| # ์ผํ๋ก ๊ตฌ๋ถ๋ ํค์๋ ๋ชจ๋ ์ถ๊ฐ | |
| for kw in keywords_part.split(","): | |
| kw = kw.strip() | |
| if kw: | |
| final_keywords.append(kw) | |
| # ์ฒ๋ฆฌ๋์ง ์์ ์์ ์ฒซ ๋ฒ์งธ ํค์๋๋ ์ถ๊ฐ (LLM์ด ์ฒ๋ฆฌํ์ง ์์ ํค์๋) | |
| if len(pairs) > max_pairs: | |
| logger.info(f"์ถ๊ฐ ํค์๋ ์ฒ๋ฆฌ: ๋จ์ {len(pairs) - max_pairs}๊ฐ ์์ ์ฒซ ๋ฒ์งธ ํค์๋ ์ถ๊ฐ") | |
| for pair in list(pairs)[max_pairs:]: | |
| # ๊ฐ ์์ ์ฒซ ๋ฒ์งธ ํค์๋๋ง ์ฌ์ฉ | |
| final_keywords.append(pair[0]) | |
| # ์ ํ๋ ํค์๋๊ฐ ์์ผ๋ฉด ๊ธฐ์กด ํค์๋ ๋ชจ๋ ๋ฐํ | |
| if not final_keywords: | |
| logger.warning("๊ฒฝ๊ณ : ์ ํ๋ ํค์๋๊ฐ ์์ด ๋ชจ๋ ํค์๋๋ฅผ ์ ์งํฉ๋๋ค.") | |
| final_keywords = list(all_keywords) | |
| # ์์ ๊ฐ์ ์์ | |
| corrected_keywords = [] | |
| # ๋จ์์ ์ซ์ ๊ด๋ จ ์ ๊ท์ ํจํด | |
| unit_pattern = re.compile(r'(?i)(kg|g|mm|cm|ml|l|๋ฆฌํฐ|๊ฐ|ํฉ|๋ฐ์ค|์ธํธ|2l|l2)') | |
| number_pattern = re.compile(r'\d+') | |
| for kw in final_keywords: | |
| # ๊ณต๋ฐฑ์ผ๋ก ๋ถ๋ฆฌ | |
| if ' ' in kw: | |
| parts = kw.split() | |
| first_part = parts[0] | |
| # ์ฒซ ๋ถ๋ถ์ด ๋จ์๋ ์ซ์๋ฅผ ํฌํจํ๋์ง ํ์ธ | |
| if (unit_pattern.search(first_part) or number_pattern.search(first_part)) and len(parts) > 1: | |
| # ์์ ๋ฐ๊พธ๊ธฐ: ๋จ์/์ซ์ ๋ถ๋ถ์ ๋ค๋ก ์ด๋ | |
| corrected_kw = " ".join(parts[1:] + [first_part]) | |
| logger.info(f"ํค์๋ ์์ ๊ฐ์ ์์ : '{kw}' -> '{corrected_kw}'") | |
| corrected_keywords.append(corrected_kw) | |
| else: | |
| corrected_keywords.append(kw) | |
| else: | |
| corrected_keywords.append(kw) | |
| # ํน๋ณ ์ฒ๋ฆฌ: "L ์ค์ง์ด", "2L ์ค์ง์ด" ๊ฐ์ ๊ฒฝ์ฐ๋ฅผ ๋ช ์์ ์ผ๋ก ํ์ธํ๊ณ ์์ | |
| specific_fixes = [] | |
| for kw in corrected_keywords: | |
| # ํน์ ํจํด ์ฒดํฌ | |
| l_pattern = re.compile(r'^([0-9]*L) (.+)$', re.IGNORECASE) | |
| match = l_pattern.match(kw) | |
| if match: | |
| # L ๋จ์๋ฅผ ๋ค๋ก ์ด๋ | |
| l_part = match.group(1) | |
| main_part = match.group(2) | |
| fixed_kw = f"{main_part} {l_part}" | |
| logger.info(f"ํน์ ํจํด ์์ : '{kw}' -> '{fixed_kw}'") | |
| specific_fixes.append(fixed_kw) | |
| else: | |
| specific_fixes.append(kw) | |
| # ์ ๊ฑฐ๋ ํค์๋ ๋ชฉ๋ก ํ์ธ | |
| selected_set = set(specific_fixes) | |
| removed_keywords = all_keywords - selected_set | |
| # ์ ๊ฑฐ๋ ํค์๋ ์ถ๋ ฅ | |
| logger.info("\n=== LLM์ ์ํด ์ ๊ฑฐ๋ ํค์๋ ๋ชฉ๋ก ===") | |
| for kw in removed_keywords: | |
| logger.info(f" - {kw}") | |
| logger.info(f"์ด {len(all_keywords)}๊ฐ ์ค {len(removed_keywords)}๊ฐ ์ ๊ฑฐ๋จ ({len(selected_set)}๊ฐ ์ ์ง)\n") | |
| return specific_fixes | |
| except Exception as e: | |
| logger.error(f"Gemini ์ค๋ฅ: {e}") | |
| logger.error("์ค๋ฅ ๋ฐ์์ผ๋ก ์ธํด ๋ชจ๋ ํค์๋๋ฅผ ์ ์งํฉ๋๋ค.") | |
| logger.error(f"์ค๋ฅ ์ ํ: {type(e).__name__}") | |
| import traceback | |
| traceback.print_exc() | |
| # ์์ ํ๊ฒ ์ฒ๋ฆฌ: ๋ชจ๋ ํค์๋๋ฅผ ์ ์ง | |
| logger.info(f"์์ ๋ชจ๋: {len(all_keywords)}๊ฐ ํค์๋ ๋ชจ๋ ์ ์ง") | |
| return list(all_keywords) | |
| def get_search_volume_range(total_volume): | |
| """์ด ๊ฒ์๋์ ๊ธฐ๋ฐ์ผ๋ก ๊ฒ์๋ ๊ตฌ๊ฐ์ ๋ฐํ""" | |
| if total_volume == 0: | |
| return "100๋ฏธ๋ง" | |
| elif total_volume <= 100: | |
| return "100๋ฏธ๋ง" | |
| elif total_volume <= 1000: | |
| return "1000๋ฏธ๋ง" | |
| elif total_volume <= 2000: | |
| return "2000๋ฏธ๋ง" | |
| elif total_volume <= 5000: | |
| return "5000๋ฏธ๋ง" | |
| elif total_volume <= 10000: | |
| return "10000๋ฏธ๋ง" | |
| else: | |
| return "10000์ด์" |