# -*- coding: utf-8 -*- """ OpenRouter 클라이언트 테스트 스크립트 사용법: python modules/test_openrouter_client.py 환경변수 설정: export OPENROUTER_API_KEY="sk-or-v1-xxxxx" 또는 코드에서 직접 설정 """ import os import sys import time import logging from typing import List, Dict, Any, Optional from dataclasses import dataclass # 로깅 설정 logging.basicConfig( level=logging.INFO, format='[%(asctime)s] [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) logger = logging.getLogger(__name__) # 모듈 경로 추가 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from modules.openrouter_client import OpenRouterTranslator # ============================================================================= # 로그에서 추출한 샘플 OCR 데이터 # ============================================================================= # 샘플 1: 이미지 35 (7개 텍스트, 모두 중국어) SAMPLE_1: List[Dict[str, Any]] = [ {'text': '高密【拉毛布】', 'confidence': 0.8697245717048645}, {'text': '柔中带韧不易坏', 'confidence': 0.9874316453933716}, {'text': '安静无声助力深度睡眠', 'confidence': 0.9960897564888}, {'text': 'newpet家的', 'confidence': 0.9988763928413391}, {'text': '别人家的', 'confidence': 0.9997605085372925}, {'text': '密织拉毛布保暖还结实', 'confidence': 0.9967950582504272}, {'text': '劣质无纺布一拉就烂一洗就散', 'confidence': 0.9917935729026794}, ] # 샘플 2: 이미지 37 (3개 텍스트, 모두 중국어) SAMPLE_2: List[Dict[str, Any]] = [ {'text': '隐藏拉链不易咬', 'confidence': 0.9800551533699036}, {'text': '宠物更安全', 'confidence': 0.9966630935668945}, {'text': '隐藏式拉链+顺滑手感告别突元五金', 'confidence': 0.9670320749282837}, ] # 샘플 3: 이미지 38 (12개 중 11개 중국어, Productparameters 제외됨) SAMPLE_3: List[Dict[str, Any]] = [ {'text': '产品参数', 'confidence': 0.995430052280426}, {'text': '品牌', 'confidence': 0.9999157786369324}, {'text': '名称', 'confidence': 0.9999277591705322}, {'text': 'NewPet妞派特', 'confidence': 0.9976658225059509}, {'text': '加厚防风圆弧房子', 'confidence': 0.9947051405906677}, {'text': '适用对象', 'confidence': 0.9994360208511353}, {'text': '产品材质', 'confidence': 0.9989043474197388}, {'text': '猫犬通用', 'confidence': 0.9970399141311646}, {'text': '高密度海绵、牛津布', 'confidence': 0.995124101638794}, {'text': '颜色', 'confidence': 0.9999042749404907}, {'text': '多色可选', 'confidence': 0.9995521903038025}, ] # 샘플 4: 이미지 4 (9개 중 8개 중국어, newpet 제외됨) SAMPLE_4: List[Dict[str, Any]] = [ {'text': '工厂直销', 'confidence': 0.9989292621612549}, {'text': '品质稳定有保证', 'confidence': 0.9961501359939575}, {'text': '实力源头厂家,13年畅销好产品', 'confidence': 0.9883608222007751}, {'text': 'M稳定货源', 'confidence': 0.8720976114273071}, {'text': '诚实守信', 'confidence': 0.9912816882133484}, {'text': 'M品质放心', 'confidence': 0.9013170003890991}, {'text': '德国品质·13年畅销', 'confidence': 0.9565986394882202}, {'text': '连续登顶天猫宠物窝TOP榜', 'confidence': 0.9966885447502136}, ] # 샘플 5: 이미지 1 (8개 텍스트, 모두 중국어) SAMPLE_5: List[Dict[str, Any]] = [ {'text': '二', 'confidence': 0.9579324722290039}, {'text': '暖意不容等待', 'confidence': 0.9924479126930237}, {'text': '趁"冷"下单,立享温暖', 'confidence': 0.9866428971290588}, {'text': '降温预警', 'confidence': 0.9913228750228882}, {'text': '广州', 'confidence': 0.9998736381530762}, {'text': '低至10°℃-9℃', 'confidence': 0.9398331046104431}, {'text': '部分城市', 'confidence': 0.9955496191978455}, {'text': '未来7天降温情况', 'confidence': 0.9742034077644348}, ] # 필터링 전 원본 데이터 (중국어 없는 텍스트 포함) SAMPLE_3_WITH_ENGLISH: List[Dict[str, Any]] = [ {'text': 'Productparameters', 'confidence': 0.9952481389045715}, # 영어 (필터링됨) {'text': '产品参数', 'confidence': 0.995430052280426}, {'text': '品牌', 'confidence': 0.9999157786369324}, {'text': '名称', 'confidence': 0.9999277591705322}, {'text': 'NewPet妞派特', 'confidence': 0.9976658225059509}, {'text': '加厚防风圆弧房子', 'confidence': 0.9947051405906677}, {'text': '适用对象', 'confidence': 0.9994360208511353}, {'text': '产品材质', 'confidence': 0.9989043474197388}, {'text': '猫犬通用', 'confidence': 0.9970399141311646}, {'text': '高密度海绵、牛津布', 'confidence': 0.995124101638794}, {'text': '颜色', 'confidence': 0.9999042749404907}, {'text': '多色可选', 'confidence': 0.9995521903038025}, ] SAMPLE_4_WITH_ENGLISH: List[Dict[str, Any]] = [ {'text': '工厂直销', 'confidence': 0.9989292621612549}, {'text': '品质稳定有保证', 'confidence': 0.9961501359939575}, {'text': '实力源头厂家,13年畅销好产品', 'confidence': 0.9883608222007751}, {'text': 'M稳定货源', 'confidence': 0.8720976114273071}, {'text': '诚实守信', 'confidence': 0.9912816882133484}, {'text': 'M品质放心', 'confidence': 0.9013170003890991}, {'text': 'newpet', 'confidence': 0.9935563206672668}, # 영어 (필터링됨) {'text': '德国品质·13年畅销', 'confidence': 0.9565986394882202}, {'text': '连续登顶天猫宠物窝TOP榜', 'confidence': 0.9966885447502136}, ] # ============================================================================= # 테스트 함수들 # ============================================================================= @dataclass class BenchmarkResult: """벤치마크 결과 데이터 클래스""" model_id: str elapsed_time: float success: bool results: Optional[List[str]] = None error: Optional[str] = None def print_separator(title: str = ""): """구분선 출력""" print("\n" + "=" * 80) if title: print(f" {title}") print("=" * 80) print() def test_translate_ocr_texts( translator: OpenRouterTranslator, ocr_results: List[Dict[str, Any]], product_name: str = "테스트 상품", category: str = "테스트 카테고리", sample_name: str = "샘플" ): """translate_ocr_texts 메서드 테스트""" print_separator(f"{sample_name} - translate_ocr_texts 테스트") print(f"입력 텍스트 ({len(ocr_results)}개):") for i, item in enumerate(ocr_results, 1): print(f" {i}. {item['text']}") print(f"\n상품명: {product_name}") print(f"카테고리: {category}") print(f"모델: {translator.get_current_model()['name']}") print("\n번역 중...") try: results = translator.translate_ocr_texts( product_name=product_name, category=category, ocr_results=ocr_results ) print(f"\n번역 결과 ({len(results)}개):") for i, (original, translated) in enumerate(zip(ocr_results, results), 1): print(f" {i}. {original['text']} → {translated}") return results except Exception as e: print(f"\n❌ 오류 발생: {e}") import traceback traceback.print_exc() return None def test_run_llm_translation( translator: OpenRouterTranslator, ocr_results: List[Dict[str, Any]], product_name: str = "테스트 상품", category: str = "테스트 카테고리", steps: int = 2, sample_name: str = "샘플" ): """run_llm_translation 메서드 테스트 (steps 지원)""" print_separator(f"{sample_name} - run_llm_translation 테스트 (steps={steps})") print(f"입력 텍스트 ({len(ocr_results)}개):") for i, item in enumerate(ocr_results, 1): print(f" {i}. {item['text']}") print(f"\n상품명: {product_name}") print(f"카테고리: {category}") print(f"모델: {translator.get_current_model()['name']}") print(f"번역 단계: {steps} ({'직역만' if steps == 1 else '직역+마케팅톤 변환'})") print("\n번역 중...") try: results = translator.run_llm_translation( product_name=product_name, category=category, ocr_results=ocr_results, steps=steps ) print(f"\n번역 결과 ({len(results)}개):") for i, (original, translated) in enumerate(zip(ocr_results, results), 1): print(f" {i}. {original['text']} → {translated}") return results except Exception as e: print(f"\n❌ 오류 발생: {e}") import traceback traceback.print_exc() return None def test_filtering_comparison( translator: OpenRouterTranslator, filtered_results: List[Dict[str, Any]], unfiltered_results: List[Dict[str, Any]], product_name: str = "테스트 상품", category: str = "테스트 카테고리" ): """필터링 전/후 비교 테스트""" print_separator("필터링 전/후 비교 테스트") print("=" * 80) print(" 중국어가 없는 텍스트를 포함한 경우 (필터링 전)") print("=" * 80) print(f"\n입력 텍스트 ({len(unfiltered_results)}개):") for i, item in enumerate(unfiltered_results, 1): has_chinese = any('\u4e00' <= char <= '\u9fff' for char in item['text']) marker = "✓" if has_chinese else "✗ (필터링됨)" print(f" {i}. {item['text']} {marker}") print("\n번역 중...") try: results_unfiltered = translator.translate_ocr_texts( product_name=product_name, category=category, ocr_results=unfiltered_results ) print("\n번역 결과:") for i, (original, translated) in enumerate(zip(unfiltered_results, results_unfiltered), 1): has_chinese = any('\u4e00' <= char <= '\u9fff' for char in original['text']) marker = "" if has_chinese else " (원본 유지)" print(f" {i}. {original['text']} → {translated}{marker}") except Exception as e: print(f"\n❌ 오류 발생: {e}") import traceback traceback.print_exc() print("\n" + "=" * 80) print(" 중국어만 포함한 경우 (필터링 후)") print("=" * 80) print(f"\n입력 텍스트 ({len(filtered_results)}개):") for i, item in enumerate(filtered_results, 1): print(f" {i}. {item['text']}") print("\n번역 중...") try: results_filtered = translator.translate_ocr_texts( product_name=product_name, category=category, ocr_results=filtered_results ) print("\n번역 결과:") for i, (original, translated) in enumerate(zip(filtered_results, results_filtered), 1): print(f" {i}. {original['text']} → {translated}") except Exception as e: print(f"\n❌ 오류 발생: {e}") import traceback traceback.print_exc() print("\n" + "=" * 80) print(" 분석 및 권장사항") print("=" * 80) print(""" 중국어가 없는 텍스트를 필터링하는 것에 대한 분석: 1. 맥락 이해 측면: - 중국어가 없는 텍스트(영어, 숫자 등)는 보통 브랜드명, 모델명, 또는 이미 번역된 텍스트일 가능성이 높음 - 이러한 텍스트를 포함하면 LLM이 전체 맥락을 더 잘 이해할 수 있음 - 예: "newpet" 브랜드명이 있으면 "newpet家的"의 번역 품질이 향상될 수 있음 2. 토큰 사용 측면: - 필터링하지 않으면 입력 토큰이 증가함 - 하지만 맥락 정보가 추가되어 번역 품질이 향상될 수 있음 - 비용과 품질의 트레이드오프 3. 권장사항: - 브랜드명, 모델명 등은 포함하는 것이 좋음 (맥락 이해 향상) - 순수 영어 설명문은 제외해도 무방 (중국어 번역 대상이 아님) - 숫자, 기호는 포함 (상품 정보의 일부) - 최종 결정은 번역 품질과 비용을 고려하여 선택 """) def benchmark_models( api_key: str, model_ids: List[str], ocr_results: List[Dict[str, Any]], product_name: str = "테스트 상품", category: str = "테스트 카테고리", use_llm_translation: bool = False, steps: int = 1 ) -> List[BenchmarkResult]: """ 여러 모델을 벤치마크하여 비교 Args: api_key: OpenRouter API 키 model_ids: 테스트할 모델 ID 리스트 ocr_results: OCR 결과 데이터 product_name: 상품명 category: 카테고리 use_llm_translation: True면 run_llm_translation 사용, False면 translate_ocr_texts 사용 steps: run_llm_translation 사용 시 번역 단계 (1=직역만, 2=직역+마케팅톤 변환) Returns: 벤치마크 결과 리스트 """ benchmark_results: List[BenchmarkResult] = [] method_name = "run_llm_translation" if use_llm_translation else "translate_ocr_texts" print_separator("벤치마크 시작") print(f"테스트 모델 수: {len(model_ids)}") print(f"입력 텍스트 수: {len(ocr_results)}") print(f"상품명: {product_name}") print(f"카테고리: {category}") print(f"사용 메서드: {method_name}") if use_llm_translation: print(f"번역 단계: {steps} ({'직역만' if steps == 1 else '직역+마케팅톤 변환'})") print() for idx, model_id in enumerate(model_ids, 1): print(f"[{idx}/{len(model_ids)}] 모델: {model_id}") print("-" * 80) start_time = time.time() success = False results = None error_msg = None try: # 번역기 초기화 (타임아웃 10초) translator = OpenRouterTranslator( api_key=api_key, model_id=model_id, timeout=10, logger=logger ) # 번역 실행 (플래그에 따라 메서드 선택) if use_llm_translation: results = translator.run_llm_translation( product_name=product_name, category=category, ocr_results=ocr_results, steps=steps ) # results = translator.run_combined_llm_translation( # product_name=product_name, # category=category, # ocr_results=ocr_results, # # steps=steps # ) else: results = translator.translate_ocr_texts( product_name=product_name, category=category, ocr_results=ocr_results ) success = True elapsed_time = time.time() - start_time print(f"✅ 성공 - 소요 시간: {elapsed_time:.2f}초") print(f"번역 결과:") for i, (original, translated) in enumerate(zip(ocr_results, results), 1): print(f" {i}. {original['text']} → {translated}") except Exception as e: elapsed_time = time.time() - start_time error_msg = str(e) print(f"❌ 실패 - 소요 시간: {elapsed_time:.2f}초") print(f"오류: {error_msg}") import traceback traceback.print_exc() benchmark_results.append(BenchmarkResult( model_id=model_id, elapsed_time=elapsed_time, success=success, results=results, error=error_msg )) print() return benchmark_results def print_benchmark_summary(benchmark_results: List[BenchmarkResult]): """벤치마크 결과 요약 출력""" print_separator("벤치마크 결과 요약") # 성공한 결과만 필터링 successful_results = [r for r in benchmark_results if r.success] failed_results = [r for r in benchmark_results if not r.success] if successful_results: # 실행 시간 기준 정렬 sorted_results = sorted(successful_results, key=lambda x: x.elapsed_time) print("✅ 성공한 모델:") print("-" * 80) print(f"{'순위':<6} {'모델 ID':<50} {'소요 시간':<15} {'상태'}") print("-" * 80) for rank, result in enumerate(sorted_results, 1): status = "✅ 성공" print(f"{rank:<6} {result.model_id:<50} {result.elapsed_time:>10.2f}초 {status}") print() print("📊 통계:") times = [r.elapsed_time for r in successful_results] print(f" 평균 시간: {sum(times) / len(times):.2f}초") print(f" 최소 시간: {min(times):.2f}초 ({sorted_results[0].model_id})") print(f" 최대 시간: {max(times):.2f}초 ({sorted_results[-1].model_id})") print() if failed_results: print("❌ 실패한 모델:") print("-" * 80) for result in failed_results: print(f" {result.model_id}") print(f" 오류: {result.error}") print(f" 소요 시간: {result.elapsed_time:.2f}초") print() # 결과 비교 if len(successful_results) > 1: print_separator("번역 결과 비교") # 첫 번째 성공한 모델의 결과를 기준으로 비교 base_result = successful_results[0] print(f"기준 모델: {base_result.model_id}") print() for result in successful_results[1:]: if result.results and base_result.results: print(f"모델: {result.model_id}") print("-" * 80) differences = [] for i, (base_trans, comp_trans) in enumerate(zip(base_result.results, result.results), 1): if base_trans != comp_trans: differences.append(i) print(f" 차이 {len(differences)}: 항목 {i}") print(f" 기준: {base_trans}") print(f" 비교: {comp_trans}") if not differences: print(" 모든 번역 결과가 기준 모델과 동일합니다.") else: print(f" 총 {len(differences)}개 항목에서 차이 발견") print() def main(): """메인 테스트 함수""" print_separator("OpenRouter 클라이언트 벤치마크 테스트") # ============================================================================= # 여기서 모델과 API 키를 직접 지정하세요 # ============================================================================= API_KEY = os.getenv("OPENROUTER_API_KEY", "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") # 테스트 데이터 product_name = "NewPet 반려동물 House" category = "반려동물용품" test_sample = SAMPLE_1 # 또는 SAMPLE_2, SAMPLE_3 등 # 벤치마크 설정 USE_LLM_TRANSLATION = True # True: run_llm_translation 사용, False: translate_ocr_texts 사용 STEPS = 2 # run_llm_translation 사용 시 번역 단계 (1=직역만, 2=직역+마케팅톤 변환) # 벤치마크할 모델 ID 리스트 MODEL_IDS = [ # "xiaomi/mimo-v2-flash:free", # "openai/gpt-oss-20b:deepinfra/fp4", # "mistralai/devstral-2512:free", # "mistralai/mistral-7b-instruct:free", # "openai/gpt-oss-20b:gmicloud/fp4", # "z-ai/glm-4.5-air:novita/bf16", # "deepseek/deepseek-v3.2:atlas-cloud/fp8", "openai/gpt-5-nano:azure", # "google/gemma-3n-e4b-it:together", # "google/gemma-3-4b-it:deepinfra/bf16", # 추가 모델 ID를 여기에 추가하세요 ] # 벤치마크 실행 benchmark_results = benchmark_models( api_key=API_KEY, model_ids=MODEL_IDS, ocr_results=test_sample, product_name=product_name, category=category, use_llm_translation=USE_LLM_TRANSLATION, steps=STEPS ) # 결과 요약 출력 print_benchmark_summary(benchmark_results) print_separator("벤치마크 완료") if __name__ == "__main__": main()