fix: 2-phase model/space fetch — basic info immediate + LLM best-effort + sort fallback (resolves empty models/spaces issue)
Browse files
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 |
-
"""허깅페이스 트렌딩 모델
|
| 1951 |
print(f"🤗 허깅페이스 트렌딩 모델 {limit}개 수집 중...")
|
| 1952 |
-
|
| 1953 |
models_list = []
|
| 1954 |
-
|
|
|
|
| 1955 |
try:
|
| 1956 |
-
# Hugging Face API 사용
|
| 1957 |
api = HfApi()
|
| 1958 |
-
|
| 1959 |
-
|
| 1960 |
-
|
| 1961 |
-
|
| 1962 |
-
|
| 1963 |
-
|
| 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 |
-
|
| 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}
|
| 1998 |
continue
|
| 1999 |
-
|
| 2000 |
-
print(f"✅ {len(models_list)}개
|
| 2001 |
-
|
| 2002 |
-
# DB
|
| 2003 |
if models_list:
|
| 2004 |
-
|
| 2005 |
-
|
| 2006 |
-
|
| 2007 |
-
|
|
|
|
| 2008 |
except Exception as e:
|
| 2009 |
-
print(f"❌ 모델
|
| 2010 |
print("💾 DB에서 이전 데이터 로드 시도...\n")
|
| 2011 |
return load_models_from_db()
|
| 2012 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2013 |
def fetch_huggingface_spaces(self, limit: int = 30) -> List[Dict]:
|
| 2014 |
-
"""허깅페이스 트렌딩 스페이스
|
| 2015 |
print(f"🚀 허깅페이스 트렌딩 스페이스 {limit}개 수집 중...")
|
| 2016 |
-
|
| 2017 |
spaces_list = []
|
| 2018 |
-
|
|
|
|
| 2019 |
try:
|
| 2020 |
-
# Hugging Face API 사용
|
| 2021 |
api = HfApi()
|
| 2022 |
-
|
| 2023 |
-
|
| 2024 |
-
|
| 2025 |
-
|
| 2026 |
-
|
| 2027 |
-
|
| 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 |
-
|
| 2046 |
-
|
| 2047 |
-
|
| 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}
|
| 2068 |
continue
|
| 2069 |
-
|
| 2070 |
-
print(f"✅ {len(spaces_list)}개
|
| 2071 |
-
|
| 2072 |
-
# DB에 저장
|
| 2073 |
if spaces_list:
|
| 2074 |
-
|
| 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
|
| 2146 |
-
print("🤗 DB
|
| 2147 |
-
|
|
|
|
|
|
|
| 2148 |
else:
|
| 2149 |
print(f"✅ DB에서 {len(analyzed_models)}개 모델 로드\n")
|
| 2150 |
-
|
|
|
|
| 2151 |
analyzed_spaces = load_spaces_from_db()
|
| 2152 |
-
if
|
| 2153 |
-
print("🚀 DB
|
| 2154 |
-
|
|
|
|
|
|
|
| 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 |
|