ginipick commited on
Commit
5475f41
·
verified ·
1 Parent(s): 6d7cb2d

fix: 2-phase model/space fetch — basic info immediate + LLM best-effort + sort fallback (resolves empty models/spaces issue)

Browse files
Files changed (1) hide show
  1. app.py +180 -92
app.py CHANGED
@@ -1946,25 +1946,69 @@ class AdvancedAIAnalyzer:
1946
 
1947
  return all_news
1948
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1949
  def fetch_huggingface_models(self, limit: int = 30) -> List[Dict]:
1950
- """허깅페이스 트렌딩 모델 30개 수집 (실제 API)"""
1951
  print(f"🤗 허깅페이스 트렌딩 모델 {limit}개 수집 중...")
1952
-
1953
  models_list = []
1954
-
 
1955
  try:
1956
- # Hugging Face API 사용
1957
  api = HfApi()
1958
-
1959
- # trending 순위로 모델 가져오기
1960
- models = list(api.list_models(
1961
- sort="trending_score",
1962
- direction=-1,
1963
- limit=limit
1964
- ))
1965
-
 
 
 
 
 
 
 
1966
  print(f"📊 API에서 {len(models)}개 모델 받음")
1967
-
1968
  for idx, model in enumerate(models[:limit], 1):
1969
  try:
1970
  model_info = {
@@ -1973,107 +2017,145 @@ class AdvancedAIAnalyzer:
1973
  'likes': getattr(model, 'likes', 0) or 0,
1974
  'task': getattr(model, 'pipeline_tag', 'N/A') or 'N/A',
1975
  'url': f"https://huggingface.co/{model.id}",
1976
- 'rank': idx
 
1977
  }
1978
-
1979
- # LLM 분석 추가 (모델 카드 분석)
1980
- print(f" 🔍 {idx}. {model.id} 분석 중...")
1981
- model_info['analysis'] = self.llm_analyzer.analyze_model(
1982
- model_info['name'],
1983
- model_info['task'],
1984
- model_info['downloads']
1985
- )
1986
-
1987
  models_list.append(model_info)
1988
-
1989
- # API rate limit 방지를 위한 짧은 대기
1990
- time.sleep(0.5)
1991
-
1992
- # 진행상황 표시
1993
- if idx % 5 == 0:
1994
- print(f" ✓ {idx}개 모델 처리 완료...")
1995
-
1996
  except Exception as e:
1997
- print(f" ⚠️ 모델 {idx} 처리 오류: {e}")
1998
  continue
1999
-
2000
- print(f"✅ {len(models_list)}개 트렌딩 모델 수집 완료\n")
2001
-
2002
- # DB 저장
2003
  if models_list:
2004
- save_models_to_db(models_list)
2005
-
2006
- return models_list
2007
-
 
2008
  except Exception as e:
2009
- print(f"❌ 모델 수집 오류: {e}")
2010
  print("💾 DB에서 이전 데이터 로드 시도...\n")
2011
  return load_models_from_db()
2012
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2013
  def fetch_huggingface_spaces(self, limit: int = 30) -> List[Dict]:
2014
- """허깅페이스 트렌딩 스페이스 30개 수집 (실제 API)"""
2015
  print(f"🚀 허깅페이스 트렌딩 스페이스 {limit}개 수집 중...")
2016
-
2017
  spaces_list = []
2018
-
 
2019
  try:
2020
- # Hugging Face API 사용
2021
  api = HfApi()
2022
-
2023
- # trending 순위로 스페이스 가져오기
2024
- spaces = list(api.list_spaces(
2025
- sort="trending_score",
2026
- direction=-1,
2027
- limit=limit
2028
- ))
2029
-
 
 
 
 
 
 
2030
  print(f"📊 API에서 {len(spaces)}개 스페이스 받음")
2031
-
2032
  for idx, space in enumerate(spaces[:limit], 1):
2033
  try:
2034
  space_info = {
2035
  'space_id': space.id,
2036
  'name': space.id.split('/')[-1] if '/' in space.id else space.id,
2037
- 'author': space.author,
2038
  'title': getattr(space, 'title', space.id) or space.id,
2039
  'likes': getattr(space, 'likes', 0) or 0,
2040
  'url': f"https://huggingface.co/spaces/{space.id}",
2041
  'sdk': getattr(space, 'sdk', 'gradio') or 'gradio',
2042
  'rank': idx
2043
  }
2044
-
2045
- # LLM 분석 추가 (app.py 분석)
2046
- print(f" 🔍 {idx}. {space.id} 분석 중...")
2047
- space_analysis = self.llm_analyzer.analyze_space(
2048
- space_info['name'],
2049
- space_info['space_id'],
2050
- space_info['title']
2051
- )
2052
-
2053
- space_info['simple_explanation'] = space_analysis['simple_explanation']
2054
- space_info['tech_stack'] = space_analysis['tech_stack']
2055
  space_info['description'] = space_info['title']
2056
-
2057
  spaces_list.append(space_info)
2058
-
2059
- # API rate limit 방지를 위한 짧은 대기
2060
- time.sleep(0.5)
2061
-
2062
- # 진행상황 표시
2063
- if idx % 5 == 0:
2064
- print(f" ✓ {idx}개 스페이스 처리 완료...")
2065
-
2066
  except Exception as e:
2067
- print(f" ⚠️ 스페이스 {idx} 처리 오류: {e}")
2068
  continue
2069
-
2070
- print(f"✅ {len(spaces_list)}개 트렌딩 스페이스 수집 완료\n")
2071
-
2072
- # DB에 저장
2073
  if spaces_list:
2074
- save_spaces_to_db(spaces_list)
2075
-
 
 
 
 
 
 
 
 
 
 
 
2076
  return spaces_list
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2077
 
2078
  except Exception as e:
2079
  print(f"❌ 스페이스 수집 오류: {e}")
@@ -2132,7 +2214,7 @@ class AdvancedAIAnalyzer:
2132
  analyzed_spaces = self.fetch_huggingface_spaces(30)
2133
  else:
2134
  print("💾 DB 우선 로드 모드\n")
2135
-
2136
  # DB에서 먼저 로드
2137
  analyzed_news = load_news_from_db()
2138
  if not analyzed_news:
@@ -2140,18 +2222,24 @@ class AdvancedAIAnalyzer:
2140
  analyzed_news = self.analyze_all_news()
2141
  else:
2142
  print(f"✅ DB에서 {len(analyzed_news)}개 뉴스 로드\n")
2143
-
 
2144
  analyzed_models = load_models_from_db()
2145
- if not analyzed_models:
2146
- print("🤗 DB 모델 없음 → 새로 수집")
2147
- analyzed_models = self.fetch_huggingface_models(30)
 
 
2148
  else:
2149
  print(f"✅ DB에서 {len(analyzed_models)}개 모델 로드\n")
2150
-
 
2151
  analyzed_spaces = load_spaces_from_db()
2152
- if not analyzed_spaces:
2153
- print("🚀 DB 스페이스 없음 → 새로 수집")
2154
- analyzed_spaces = self.fetch_huggingface_spaces(30)
 
 
2155
  else:
2156
  print(f"✅ DB에서 {len(analyzed_spaces)}개 스페이스 로드\n")
2157
 
 
1946
 
1947
  return all_news
1948
 
1949
+ def _fallback_model_analysis(self, model_info: Dict) -> str:
1950
+ """LLM 호출 실패/지연 시 사용할 즉시 fallback 설명"""
1951
+ name = model_info['name'].split('/')[-1]
1952
+ task = model_info.get('task', 'N/A') or 'N/A'
1953
+ dl = model_info.get('downloads', 0)
1954
+ task_kr = {
1955
+ 'text-generation': '글을 자동으로 만들어주는',
1956
+ 'image-to-text': '사진을 보고 설명을 써주는',
1957
+ 'text-to-image': '글을 읽고 그림을 그려주는',
1958
+ 'image-text-to-text': '이미지와 글을 함께 이해하는 멀티모달',
1959
+ 'text-to-speech': '글을 음성으로 변환하는',
1960
+ 'automatic-speech-recognition': '음성을 글로 받아쓰는',
1961
+ 'translation': '다른 언어로 번역해주는',
1962
+ 'question-answering': '질문에 답해주는',
1963
+ 'summarization': '긴 글을 짧게 요약해주는',
1964
+ 'text-classification': '글을 분류해주는',
1965
+ 'feature-extraction': '의미 벡터를 추출하는',
1966
+ 'sentence-similarity': '문장 의미 유사도를 비교하는',
1967
+ 'fill-mask': '빈칸을 채워주는',
1968
+ 'any-to-any': '여러 modality 처리하는',
1969
+ }.get(task, '특별한 기능을 하는')
1970
+ if dl > 10_000_000: pop = '엄청나게 많은'
1971
+ elif dl > 1_000_000: pop = '아주 많은'
1972
+ elif dl > 100_000: pop = '많은'
1973
+ elif dl > 10_000: pop = '꽤 많은'
1974
+ else: pop = '어느 정도'
1975
+ return f"이 모델은 {task_kr} AI예요. {pop} 사람들이 다운로드해서 사용하고 있어요. {name}라는 이름으로 유명해요!"
1976
+
1977
+ def _fallback_space_analysis(self, space_info: Dict) -> Dict:
1978
+ name = space_info.get('name', 'Space')
1979
+ sdk = space_info.get('sdk', 'gradio') or 'gradio'
1980
+ tech = ['Python', sdk.capitalize() if sdk else 'Gradio']
1981
+ return {
1982
+ 'simple_explanation': f"{name}는 웹브라우저에서 바로 AI를 체험해볼 수 있는 곳이에요. 설치 없이도 사용할 수 있어서 편리해요!",
1983
+ 'tech_stack': tech,
1984
+ }
1985
+
1986
  def fetch_huggingface_models(self, limit: int = 30) -> List[Dict]:
1987
+ """허깅페이스 트렌딩 모델 수집 — 2-phase (기본정보 즉시 저장 → LLM은 best-effort)"""
1988
  print(f"🤗 허깅페이스 트렌딩 모델 {limit}개 수집 중...")
1989
+
1990
  models_list = []
1991
+
1992
+ # ===== PHASE 1: HF API에서 기본 정보 즉시 수집 =====
1993
  try:
 
1994
  api = HfApi()
1995
+ try:
1996
+ models = list(api.list_models(
1997
+ sort="trending_score",
1998
+ direction=-1,
1999
+ limit=limit
2000
+ ))
2001
+ except (TypeError, Exception) as e_sort:
2002
+ # trending_score 미지원 huggingface_hub 버전 fallback
2003
+ print(f" ⚠️ trending_score sort 미지원 ({e_sort}), downloads로 fallback")
2004
+ models = list(api.list_models(
2005
+ sort="downloads",
2006
+ direction=-1,
2007
+ limit=limit
2008
+ ))
2009
+
2010
  print(f"📊 API에서 {len(models)}개 모델 받음")
2011
+
2012
  for idx, model in enumerate(models[:limit], 1):
2013
  try:
2014
  model_info = {
 
2017
  'likes': getattr(model, 'likes', 0) or 0,
2018
  'task': getattr(model, 'pipeline_tag', 'N/A') or 'N/A',
2019
  'url': f"https://huggingface.co/{model.id}",
2020
+ 'rank': idx,
2021
+ 'analysis': '' # PHASE 2에서 채움
2022
  }
2023
+ # PHASE 1 즉시 fallback 설명 부여 — 무조건 비어있지 않게
2024
+ model_info['analysis'] = self._fallback_model_analysis(model_info)
 
 
 
 
 
 
 
2025
  models_list.append(model_info)
 
 
 
 
 
 
 
 
2026
  except Exception as e:
2027
+ print(f" ⚠️ 모델 {idx} 기본정보 오류: {e}")
2028
  continue
2029
+
2030
+ print(f"✅ PHASE 1 완료: {len(models_list)}개 기본정보 확보")
2031
+
2032
+ # PHASE 1 결과 즉시 DB 저장 (LLM 분석 실패에도 데이터 보존)
2033
  if models_list:
2034
+ try:
2035
+ save_models_to_db(models_list)
2036
+ print(f"💾 DB 저장 완료 (PHASE 1)")
2037
+ except Exception as e:
2038
+ print(f" ⚠️ DB 저장 오류 (PHASE 1): {e}")
2039
  except Exception as e:
2040
+ print(f"❌ HF API 모델 fetch 오류: {e}")
2041
  print("💾 DB에서 이전 데이터 로드 시도...\n")
2042
  return load_models_from_db()
2043
+
2044
+ # ===== PHASE 2: LLM 분석 best-effort 보강 (실패해도 fallback 유지) =====
2045
+ if not getattr(self.llm_analyzer, 'api_available', False):
2046
+ print(f"ℹ️ LLM API 미활성 → PHASE 2 건너뜀, fallback 설명만 사용\n")
2047
+ return models_list
2048
+
2049
+ print(f"🔍 PHASE 2: LLM 분석 시도 (best-effort, 실패해도 fallback 유지)")
2050
+ enriched = 0
2051
+ for idx, m in enumerate(models_list, 1):
2052
+ try:
2053
+ llm_result = self.llm_analyzer.analyze_model(m['name'], m['task'], m['downloads'])
2054
+ if llm_result and len(llm_result.strip()) > 10:
2055
+ m['analysis'] = llm_result
2056
+ enriched += 1
2057
+ if idx % 5 == 0:
2058
+ print(f" ✓ {idx}/{len(models_list)} 처리 ({enriched} 보강 완료)")
2059
+ except Exception as e:
2060
+ # fallback 그대로 유지
2061
+ continue
2062
+ print(f"✅ PHASE 2 완료: {enriched}/{len(models_list)} LLM 보강\n")
2063
+
2064
+ # 최종 DB 갱신
2065
+ try:
2066
+ save_models_to_db(models_list)
2067
+ except Exception as e:
2068
+ print(f" ⚠️ DB 최종 저장 오류: {e}")
2069
+
2070
+ return models_list
2071
+
2072
  def fetch_huggingface_spaces(self, limit: int = 30) -> List[Dict]:
2073
+ """허깅페이스 트렌딩 스페이스 수집 — 2-phase (기본정보 즉시 저장 → LLM은 best-effort)"""
2074
  print(f"🚀 허깅페이스 트렌딩 스페이스 {limit}개 수집 중...")
2075
+
2076
  spaces_list = []
2077
+
2078
+ # ===== PHASE 1: HF API에서 기본 정보 즉시 수집 =====
2079
  try:
 
2080
  api = HfApi()
2081
+ try:
2082
+ spaces = list(api.list_spaces(
2083
+ sort="trending_score",
2084
+ direction=-1,
2085
+ limit=limit
2086
+ ))
2087
+ except (TypeError, Exception) as e_sort:
2088
+ print(f" ⚠️ trending_score sort 미지원 ({e_sort}), likes로 fallback")
2089
+ spaces = list(api.list_spaces(
2090
+ sort="likes",
2091
+ direction=-1,
2092
+ limit=limit
2093
+ ))
2094
+
2095
  print(f"📊 API에서 {len(spaces)}개 스페이스 받음")
2096
+
2097
  for idx, space in enumerate(spaces[:limit], 1):
2098
  try:
2099
  space_info = {
2100
  'space_id': space.id,
2101
  'name': space.id.split('/')[-1] if '/' in space.id else space.id,
2102
+ 'author': space.author if hasattr(space, 'author') and space.author else (space.id.split('/')[0] if '/' in space.id else 'unknown'),
2103
  'title': getattr(space, 'title', space.id) or space.id,
2104
  'likes': getattr(space, 'likes', 0) or 0,
2105
  'url': f"https://huggingface.co/spaces/{space.id}",
2106
  'sdk': getattr(space, 'sdk', 'gradio') or 'gradio',
2107
  'rank': idx
2108
  }
2109
+ # PHASE 1 즉시 fallback
2110
+ fb = self._fallback_space_analysis(space_info)
2111
+ space_info['simple_explanation'] = fb['simple_explanation']
2112
+ space_info['tech_stack'] = fb['tech_stack']
 
 
 
 
 
 
 
2113
  space_info['description'] = space_info['title']
 
2114
  spaces_list.append(space_info)
 
 
 
 
 
 
 
 
2115
  except Exception as e:
2116
+ print(f" ⚠️ 스페이스 {idx} 기본정보 오류: {e}")
2117
  continue
2118
+
2119
+ print(f"✅ PHASE 1 완료: {len(spaces_list)}개 기본정보 확보")
2120
+
 
2121
  if spaces_list:
2122
+ try:
2123
+ save_spaces_to_db(spaces_list)
2124
+ print(f"💾 DB 저장 완료 (PHASE 1)")
2125
+ except Exception as e:
2126
+ print(f" ⚠️ DB 저장 오류 (PHASE 1): {e}")
2127
+ except Exception as e:
2128
+ print(f"❌ HF API 스페이스 fetch 오류: {e}")
2129
+ print("💾 DB에서 이전 데이터 로드 시도...\n")
2130
+ return load_spaces_from_db()
2131
+
2132
+ # ===== PHASE 2: LLM 분석 best-effort 보강 =====
2133
+ if not getattr(self.llm_analyzer, 'api_available', False):
2134
+ print(f"ℹ️ LLM API 미활성 → PHASE 2 건너뜀, fallback 설명만 사용\n")
2135
  return spaces_list
2136
+
2137
+ print(f"🔍 PHASE 2: LLM 분석 시도 (best-effort)")
2138
+ enriched = 0
2139
+ for idx, s in enumerate(spaces_list, 1):
2140
+ try:
2141
+ llm = self.llm_analyzer.analyze_space(s['name'], s['space_id'], s['title'])
2142
+ if llm and llm.get('simple_explanation') and len(llm['simple_explanation'].strip()) > 10:
2143
+ s['simple_explanation'] = llm['simple_explanation']
2144
+ if llm.get('tech_stack'):
2145
+ s['tech_stack'] = llm['tech_stack']
2146
+ enriched += 1
2147
+ if idx % 5 == 0:
2148
+ print(f" ✓ {idx}/{len(spaces_list)} 처리 ({enriched} 보강 완료)")
2149
+ except Exception as e:
2150
+ continue
2151
+ print(f"✅ PHASE 2 완료: {enriched}/{len(spaces_list)} LLM 보강\n")
2152
+
2153
+ try:
2154
+ save_spaces_to_db(spaces_list)
2155
+ except Exception as e:
2156
+ print(f" ⚠️ DB 최종 저장 오류: {e}")
2157
+
2158
+ return spaces_list
2159
 
2160
  except Exception as e:
2161
  print(f"❌ 스페이스 수집 오류: {e}")
 
2214
  analyzed_spaces = self.fetch_huggingface_spaces(30)
2215
  else:
2216
  print("💾 DB 우선 로드 모드\n")
2217
+
2218
  # DB에서 먼저 로드
2219
  analyzed_news = load_news_from_db()
2220
  if not analyzed_news:
 
2222
  analyzed_news = self.analyze_all_news()
2223
  else:
2224
  print(f"✅ DB에서 {len(analyzed_news)}개 뉴스 로드\n")
2225
+
2226
+ # 모델: DB가 비었거나 모델 수가 비정상적으로 적으면 새로 수집
2227
  analyzed_models = load_models_from_db()
2228
+ if len(analyzed_models) < 5:
2229
+ print(f"🤗 DB 모델 {len(analyzed_models)}개 (부족) → 새로 수집")
2230
+ fresh_models = self.fetch_huggingface_models(30)
2231
+ if fresh_models:
2232
+ analyzed_models = fresh_models
2233
  else:
2234
  print(f"✅ DB에서 {len(analyzed_models)}개 모델 로드\n")
2235
+
2236
+ # 스페이스: DB가 비었거나 스페이스 수가 비정상적으로 적으면 새로 수집
2237
  analyzed_spaces = load_spaces_from_db()
2238
+ if len(analyzed_spaces) < 5:
2239
+ print(f"🚀 DB 스페이스 {len(analyzed_spaces)}개 (부족) → 새로 수집")
2240
+ fresh_spaces = self.fetch_huggingface_spaces(30)
2241
+ if fresh_spaces:
2242
+ analyzed_spaces = fresh_spaces
2243
  else:
2244
  print(f"✅ DB에서 {len(analyzed_spaces)}개 스페이스 로드\n")
2245