# -*- coding: utf-8 -*- """ OpenRouter 클라이언트 테스트 모듈 다양한 모델을 순차적으로 테스트하여 응답 품질과 속도를 비교합니다. 사용법: python test_openrouter.py python test_openrouter.py --models gemini-2.5-flash,gpt-4o-mini python test_openrouter.py --test ocr python test_openrouter.py --test all """ from __future__ import annotations import os import sys import time import json import argparse import logging from typing import List, Dict, Any, Optional from datetime import datetime # 상위 디렉토리 경로 추가 sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..")) try: from src.titleManager.openrouter_client import OpenRouterTranslator, OpenRouterError except ImportError: # 직접 실행 시 경로 조정 sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "src", "titleManager")) from openrouter_client import OpenRouterTranslator, OpenRouterError # 로깅 설정 logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S" ) logger = logging.getLogger(__name__) # ============================================================ # 테스트 데이터 # ============================================================ # OCR 번역 테스트 데이터 OCR_TEST_DATA = { "product_name": "휴대용 미니 선풍기", "category": "가전/계절가전/선풍기", "ocr_results": [ {"text": "强力送风"}, {"text": "USB-C 快速充电"}, {"text": "3档风速调节"}, {"text": "超静音设计"}, {"text": "长续航8小时"}, {"text": "便携式设计"}, ] } # 상품 분석 테스트 데이터 PRODUCT_ANALYSIS_TEST_DATA = [ "삼성전자 갤럭시북4 프로 360 NT960QGK-K71A 16인치 인텔 코어 울트라7 터치스크린 문스톤 그레이", "Apple 맥북 프로 14인치 M3 Pro 칩 18GB 512GB 스페이스 블랙 MRX33KH/A", "LG전자 그램 17ZD90S-GX56K 17인치 인텔 코어 울트라5 16GB 256GB 화이트", ] # 옵션 번역 테스트 데이터 OPTION_TEST_DATA = { "product_name": "무선 블루투스 이어폰", "category": "디지털/음향기기/이어폰", "option_groups": [ {"id": 1, "source": ["白色", "黑色", "粉色", "蓝色"]}, {"id": 2, "source": ["标准版", "升级版", "豪华版"]}, {"id": 3, "source": ["单耳", "双耳", "双耳+充电仓"]}, ] } # 상품명 생성 테스트 데이터 PRODUCT_NAME_TEST_DATA = { "original_name": "无线蓝牙耳机降噪入耳式运动跑步超长续航", "keywords": ["무선이어폰", "블루투스", "노이즈캔슬링", "운동용", "방수"], "category": "디지털/음향기기/이어폰", "top_titles": [ "삼성 갤럭시버즈3 프로 무선 블루투스 이어폰 노이즈캔슬링", "애플 에어팟 프로 2세대 무선 이어폰 액티브 노이즈 캔슬링", "소니 WF-1000XM5 무선 이어폰 노캔 하이레졸루션", ] } # 테스트할 모델 목록 (저렴한 것부터) TEST_MODELS = [ # 무료/저렴한 모델 "gemini-flash-free", "gemini-2.0-flash", "gemini-2.5-flash", # 중간 가격대 "gpt-4o-mini", "claude-3-haiku", "deepseek-chat", "llama-3.3-70b", # 고가 모델 (선택적) # "gpt-4o", # "claude-3.5-sonnet", ] # ============================================================ # 테스트 함수들 # ============================================================ def test_health_check(client: OpenRouterTranslator) -> Dict[str, Any]: """API 연결 상태 테스트""" start = time.time() try: result = client.health_check() elapsed = time.time() - start return { "success": result, "elapsed": elapsed, "message": "OK" if result else "Failed" } except Exception as e: return { "success": False, "elapsed": time.time() - start, "message": str(e) } def test_ocr_translation(client: OpenRouterTranslator) -> Dict[str, Any]: """OCR 번역 테스트""" start = time.time() try: results = client.translate_ocr_texts( product_name=OCR_TEST_DATA["product_name"], category=OCR_TEST_DATA["category"], ocr_results=OCR_TEST_DATA["ocr_results"] ) elapsed = time.time() - start # 결과 검증 success = len(results) == len(OCR_TEST_DATA["ocr_results"]) non_empty = sum(1 for r in results if r.strip()) return { "success": success and non_empty > 0, "elapsed": elapsed, "input_count": len(OCR_TEST_DATA["ocr_results"]), "output_count": len(results), "non_empty_count": non_empty, "results": results } except Exception as e: return { "success": False, "elapsed": time.time() - start, "message": str(e) } def test_product_analysis(client: OpenRouterTranslator) -> Dict[str, Any]: """상품 분석 테스트""" start = time.time() try: test_text = PRODUCT_ANALYSIS_TEST_DATA[0] result = client.analyze_product_info(test_text) elapsed = time.time() - start # 결과 검증 has_brand = "브랜드" in result or "brand" in str(result).lower() has_name = "상품명" in result or "product" in str(result).lower() return { "success": has_brand or has_name, "elapsed": elapsed, "input": test_text, "result": result } except Exception as e: return { "success": False, "elapsed": time.time() - start, "message": str(e) } def test_option_translation(client: OpenRouterTranslator) -> Dict[str, Any]: """옵션 번역 테스트""" start = time.time() try: results = client.translate_option_groups( product_name=OPTION_TEST_DATA["product_name"], category=OPTION_TEST_DATA["category"], option_groups=OPTION_TEST_DATA["option_groups"] ) elapsed = time.time() - start # 결과 검증 success = len(results) == len(OPTION_TEST_DATA["option_groups"]) return { "success": success, "elapsed": elapsed, "input_count": len(OPTION_TEST_DATA["option_groups"]), "output_count": len(results), "results": results } except Exception as e: return { "success": False, "elapsed": time.time() - start, "message": str(e) } def test_product_name_generation(client: OpenRouterTranslator) -> Dict[str, Any]: """상품명 생성 테스트""" start = time.time() try: result = client.generate_product_name( original_name=PRODUCT_NAME_TEST_DATA["original_name"], keywords=PRODUCT_NAME_TEST_DATA["keywords"], category=PRODUCT_NAME_TEST_DATA["category"], top_titles=PRODUCT_NAME_TEST_DATA["top_titles"] ) elapsed = time.time() - start # 결과 검증 success = bool(result) and len(result) >= 20 return { "success": success, "elapsed": elapsed, "result": result, "length": len(result) if result else 0 } except Exception as e: return { "success": False, "elapsed": time.time() - start, "message": str(e) } def test_simple_chat(client: OpenRouterTranslator) -> Dict[str, Any]: """간단한 채팅 테스트""" start = time.time() try: result = client.ask_text( "한국의 수도는 어디인가요? 한 단어로만 답하세요.", system_prompt="짧고 간결하게 답변하세요." ) elapsed = time.time() - start # 결과 검증 (서울이 포함되어야 함) success = "서울" in result return { "success": success, "elapsed": elapsed, "result": result } except Exception as e: return { "success": False, "elapsed": time.time() - start, "message": str(e) } # ============================================================ # 메인 테스트 러너 # ============================================================ def run_tests( api_key: str, models: List[str], test_types: List[str], verbose: bool = True ) -> Dict[str, Any]: """ 여러 모델에 대해 테스트 실행 Args: api_key: OpenRouter API 키 models: 테스트할 모델 리스트 test_types: 테스트 유형 리스트 (health, chat, ocr, analysis, option, name, all) verbose: 상세 출력 여부 Returns: dict: 테스트 결과 """ all_results = {} # 테스트 함수 매핑 test_functions = { "health": ("Health Check", test_health_check), "chat": ("Simple Chat", test_simple_chat), "ocr": ("OCR Translation", test_ocr_translation), "analysis": ("Product Analysis", test_product_analysis), "option": ("Option Translation", test_option_translation), "name": ("Product Name Generation", test_product_name_generation), } # "all" 처리 if "all" in test_types: test_types = list(test_functions.keys()) print("\n" + "=" * 70) print(f"OpenRouter 모델 테스트 시작") print(f"테스트 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print(f"테스트 모델: {', '.join(models)}") print(f"테스트 항목: {', '.join(test_types)}") print("=" * 70 + "\n") for model in models: print(f"\n{'─' * 50}") print(f"📦 모델: {model}") print(f"{'─' * 50}") model_results = {} try: # 클라이언트 생성 client = OpenRouterTranslator( api_key=api_key, model=model, temperature=0.1, logger=logger ) model_info = client.get_model_info() print(f" 모델 ID: {model_info['model_id']}") print(f" 가격: 입력 ${model_info['pricing'].get('input', 'N/A')}/1M, 출력 ${model_info['pricing'].get('output', 'N/A')}/1M") print() # 각 테스트 실행 for test_key in test_types: if test_key not in test_functions: continue test_name, test_func = test_functions[test_key] print(f" 🔍 {test_name}...", end=" ", flush=True) result = test_func(client) model_results[test_key] = result if result["success"]: print(f"✅ ({result['elapsed']:.2f}s)") if verbose and "result" in result: if isinstance(result["result"], str): print(f" → {result['result'][:100]}...") elif isinstance(result["result"], list): print(f" → {result['result'][:3]}...") elif isinstance(result["result"], dict): print(f" → {json.dumps(result['result'], ensure_ascii=False)[:100]}...") else: print(f"❌ ({result.get('message', 'Unknown error')})") except OpenRouterError as e: print(f" ❌ 클라이언트 초기화 실패: {e}") model_results["error"] = str(e) except Exception as e: print(f" ❌ 예상치 못한 오류: {e}") model_results["error"] = str(e) all_results[model] = model_results # 결과 요약 print("\n" + "=" * 70) print("📊 테스트 결과 요약") print("=" * 70) # 테이블 헤더 header = ["모델"] + [test_functions[t][0] for t in test_types if t in test_functions] print(f"\n{'모델':<25}", end="") for test_key in test_types: if test_key in test_functions: print(f"{test_functions[test_key][0]:<20}", end="") print() print("-" * (25 + 20 * len([t for t in test_types if t in test_functions]))) for model, results in all_results.items(): print(f"{model:<25}", end="") for test_key in test_types: if test_key in test_functions: if test_key in results: status = "✅" if results[test_key]["success"] else "❌" elapsed = f"({results[test_key]['elapsed']:.1f}s)" print(f"{status} {elapsed:<16}", end="") else: print(f"{'—':<20}", end="") print() print("\n" + "=" * 70 + "\n") return all_results def main(): parser = argparse.ArgumentParser(description="OpenRouter 클라이언트 테스트") parser.add_argument( "--api-key", type=str, default=os.getenv("OPENROUTER_API_KEY"), help="OpenRouter API 키 (환경변수 OPENROUTER_API_KEY로도 설정 가능)" ) parser.add_argument( "--models", type=str, default=None, help="테스트할 모델 (쉼표로 구분, 예: gemini-2.5-flash,gpt-4o-mini)" ) parser.add_argument( "--test", type=str, default="all", help="테스트 유형 (health,chat,ocr,analysis,option,name,all)" ) parser.add_argument( "--verbose", "-v", action="store_true", help="상세 출력" ) parser.add_argument( "--list-models", action="store_true", help="사용 가능한 모델 목록 출력" ) args = parser.parse_args() # 모델 목록 출력 if args.list_models: print("\n사용 가능한 모델 목록:") print("-" * 50) for alias, model_id in OpenRouterTranslator.MODEL_MAP.items(): pricing = OpenRouterTranslator.MODEL_PRICING.get(model_id, {}) price_str = f"${pricing.get('input', '?')}/{pricing.get('output', '?')}" if pricing else "가격 미정" print(f" {alias:<25} → {model_id}") print() return # API 키 확인 if not args.api_key: print("❌ API 키가 필요합니다.") print(" --api-key 옵션을 사용하거나 OPENROUTER_API_KEY 환경변수를 설정하세요.") sys.exit(1) # 모델 목록 파싱 if args.models: models = [m.strip() for m in args.models.split(",")] else: models = TEST_MODELS # 테스트 유형 파싱 test_types = [t.strip() for t in args.test.split(",")] # 테스트 실행 run_tests( api_key=args.api_key, models=models, test_types=test_types, verbose=args.verbose ) if __name__ == "__main__": main()