Update app.py
Browse files
app.py
CHANGED
|
@@ -1,20 +1,26 @@
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""
|
| 3 |
-
AI 뉴스 & 허깅페이스 트렌딩 LLM 분석 웹앱 (
|
| 4 |
-
파일명:
|
| 5 |
|
| 6 |
주요 기능:
|
| 7 |
-
1.
|
| 8 |
-
2.
|
| 9 |
-
3.
|
| 10 |
-
|
|
|
|
|
|
|
| 11 |
5. 탭 UI (뉴스/모델/스페이스)
|
| 12 |
|
| 13 |
실행 방법:
|
| 14 |
1. pip install Flask requests beautifulsoup4 huggingface_hub
|
| 15 |
-
2. export FIREWORKS_API_KEY="your-api-key-here"
|
| 16 |
-
3. python
|
| 17 |
4. 브라우저에서 http://localhost:7860 접속
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
"""
|
| 19 |
|
| 20 |
from flask import Flask, render_template_string, jsonify, request
|
|
@@ -35,10 +41,6 @@ app.config['JSON_AS_ASCII'] = False
|
|
| 35 |
# 데이터베이스 파일 경로
|
| 36 |
DB_PATH = 'ai_news_analysis.db'
|
| 37 |
|
| 38 |
-
# Fireworks AI API 설정
|
| 39 |
-
FIREWORKS_API_KEY = os.environ.get('FIREWORKS_API_KEY', '')
|
| 40 |
-
FIREWORKS_API_URL = "https://api.fireworks.ai/inference/v1/chat/completions"
|
| 41 |
-
|
| 42 |
|
| 43 |
# ============================================
|
| 44 |
# HTML 템플릿 (탭 UI 포함)
|
|
@@ -540,7 +542,7 @@ HTML_TEMPLATE = """
|
|
| 540 |
<body>
|
| 541 |
<div class="container">
|
| 542 |
<h1>🤖 AI 뉴스 & 허깅페이스 LLM 분석</h1>
|
| 543 |
-
<p class="subtitle">초등학생도 이해하는 AI 트렌드 분석 시스템
|
| 544 |
|
| 545 |
<!-- 통계 카드 -->
|
| 546 |
<div class="stats">
|
|
@@ -717,12 +719,12 @@ HTML_TEMPLATE = """
|
|
| 717 |
|
| 718 |
<!-- 푸터 -->
|
| 719 |
<div class="footer">
|
| 720 |
-
<p>🤖 AI 뉴스 LLM 분석 시스템 v3.1
|
| 721 |
<p style="margin-top: 10px; font-size: 0.9em;">
|
| 722 |
-
💾 SQLite DB 영구 저장 | 🤗 Hugging Face Trending API |
|
| 723 |
</p>
|
| 724 |
<p style="margin-top: 10px; font-size: 0.85em; color: #999;">
|
| 725 |
-
데이터 출처: AI Times, Hugging Face | 분석: Fireworks AI
|
| 726 |
</p>
|
| 727 |
</div>
|
| 728 |
</div>
|
|
@@ -742,7 +744,7 @@ HTML_TEMPLATE = """
|
|
| 742 |
document.getElementById(tabName + '-content').classList.add('active');
|
| 743 |
}
|
| 744 |
|
| 745 |
-
console.log('✅ AI 뉴스 LLM 분석 시스템 로드 완료
|
| 746 |
</script>
|
| 747 |
</body>
|
| 748 |
</html>
|
|
@@ -845,7 +847,7 @@ def save_news_to_db(news_list: List[Dict]):
|
|
| 845 |
))
|
| 846 |
saved_count += 1
|
| 847 |
except sqlite3.IntegrityError:
|
| 848 |
-
pass
|
| 849 |
|
| 850 |
conn.commit()
|
| 851 |
conn.close()
|
|
@@ -999,7 +1001,7 @@ def load_spaces_from_db() -> List[Dict]:
|
|
| 999 |
'simple_explanation': row[7],
|
| 1000 |
'tech_stack': json.loads(row[8]) if row[8] else [],
|
| 1001 |
'rank': row[9],
|
| 1002 |
-
'description': row[3]
|
| 1003 |
})
|
| 1004 |
|
| 1005 |
conn.close()
|
|
@@ -1007,44 +1009,35 @@ def load_spaces_from_db() -> List[Dict]:
|
|
| 1007 |
|
| 1008 |
|
| 1009 |
# ============================================
|
| 1010 |
-
#
|
| 1011 |
# ============================================
|
| 1012 |
|
| 1013 |
-
class
|
| 1014 |
-
"""Fireworks AI
|
| 1015 |
|
| 1016 |
-
def __init__(self
|
| 1017 |
-
self.api_key =
|
| 1018 |
-
self.api_url =
|
| 1019 |
-
self.
|
| 1020 |
-
|
| 1021 |
-
if not self.
|
| 1022 |
-
print("⚠️ FIREWORKS_API_KEY
|
| 1023 |
-
self.api_available = False
|
| 1024 |
-
else:
|
| 1025 |
-
self.api_available = True
|
| 1026 |
-
print("✅ Fireworks AI API 연결 준비 완료")
|
| 1027 |
|
| 1028 |
-
def call_llm(self,
|
| 1029 |
-
"""Fireworks AI
|
| 1030 |
if not self.api_available:
|
| 1031 |
-
return
|
| 1032 |
|
| 1033 |
try:
|
| 1034 |
payload = {
|
| 1035 |
-
"model":
|
| 1036 |
"max_tokens": max_tokens,
|
| 1037 |
"top_p": 1,
|
| 1038 |
"top_k": 40,
|
| 1039 |
"presence_penalty": 0,
|
| 1040 |
"frequency_penalty": 0,
|
| 1041 |
"temperature": 0.6,
|
| 1042 |
-
"messages":
|
| 1043 |
-
{
|
| 1044 |
-
"role": "user",
|
| 1045 |
-
"content": prompt
|
| 1046 |
-
}
|
| 1047 |
-
]
|
| 1048 |
}
|
| 1049 |
|
| 1050 |
headers = {
|
|
@@ -1053,66 +1046,55 @@ class FireworksLLMAnalyzer:
|
|
| 1053 |
"Authorization": f"Bearer {self.api_key}"
|
| 1054 |
}
|
| 1055 |
|
| 1056 |
-
response = requests.post(
|
| 1057 |
-
|
| 1058 |
-
|
| 1059 |
-
|
| 1060 |
-
|
| 1061 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1062 |
|
| 1063 |
if response.status_code == 200:
|
| 1064 |
-
|
| 1065 |
-
|
|
|
|
|
|
|
|
|
|
| 1066 |
else:
|
| 1067 |
-
|
| 1068 |
-
return ""
|
| 1069 |
-
|
| 1070 |
except Exception as e:
|
| 1071 |
-
print(f"⚠️
|
| 1072 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1073 |
|
| 1074 |
def analyze_news_simple(self, title: str, content: str = "") -> Dict:
|
| 1075 |
"""뉴스 기사를 초등학생 수준으로 분석"""
|
| 1076 |
|
| 1077 |
-
prompt = f"""다음 AI 뉴스를 초등학생(10살)도 이해할 수 있게 분석해주세요.
|
| 1078 |
-
|
| 1079 |
-
뉴스 제목: {title}
|
| 1080 |
-
|
| 1081 |
-
다음 형식으로 JSON 응답해주세요:
|
| 1082 |
-
{{
|
| 1083 |
-
"summary": "초등학생이 이해할 수 있는 쉬운 요약 (2-3문장)",
|
| 1084 |
-
"significance": "이 뉴스가 왜 중요한지 쉽게 설명 (2문장)",
|
| 1085 |
-
"impact_level": "high 또는 medium 또는 low",
|
| 1086 |
-
"impact_text": "높음 또는 중간 또는 낮음",
|
| 1087 |
-
"impact_description": "영향도에 대한 설명 (1-2문장)",
|
| 1088 |
-
"action": "우리가 할 수 있는 것 (1-2문장)"
|
| 1089 |
-
}}
|
| 1090 |
-
|
| 1091 |
-
JSON만 출력하고 다른 설명은 하지 마세요."""
|
| 1092 |
-
|
| 1093 |
-
response = self.call_llm(prompt, max_tokens=1500)
|
| 1094 |
-
|
| 1095 |
-
if response:
|
| 1096 |
-
try:
|
| 1097 |
-
# JSON 파싱
|
| 1098 |
-
response = response.strip()
|
| 1099 |
-
if response.startswith("```json"):
|
| 1100 |
-
response = response[7:]
|
| 1101 |
-
if response.endswith("```"):
|
| 1102 |
-
response = response[:-3]
|
| 1103 |
-
response = response.strip()
|
| 1104 |
-
|
| 1105 |
-
analysis = json.loads(response)
|
| 1106 |
-
return analysis
|
| 1107 |
-
except json.JSONDecodeError as e:
|
| 1108 |
-
print(f"⚠️ JSON 파싱 오류: {e}")
|
| 1109 |
-
print(f"응답: {response[:200]}")
|
| 1110 |
-
|
| 1111 |
-
# 기본 분석 (API 실패 시)
|
| 1112 |
-
return self._get_fallback_news_analysis(title)
|
| 1113 |
-
|
| 1114 |
-
def _get_fallback_news_analysis(self, title: str) -> Dict:
|
| 1115 |
-
"""API 실패 시 기본 분석"""
|
| 1116 |
analysis_templates = {
|
| 1117 |
"챗GPT": {
|
| 1118 |
"summary": "마이크로소프트(MS)라는 큰 회사가 챗GPT라는 AI를 너무 많은 사람들이 사용해서, 컴퓨터를 보관하는 큰 건물(데이터센터)이 부족하다고 말했어요.",
|
|
@@ -1140,10 +1122,12 @@ JSON만 출력하고 다른 설명은 하지 마세요."""
|
|
| 1140 |
}
|
| 1141 |
}
|
| 1142 |
|
|
|
|
| 1143 |
for keyword, template in analysis_templates.items():
|
| 1144 |
if keyword.lower() in title.lower():
|
| 1145 |
return template
|
| 1146 |
|
|
|
|
| 1147 |
return {
|
| 1148 |
"summary": f"'{title}'라는 AI 관련 뉴스가 나왔어요. AI 기술이 계속 발전하고 있다는 소식이에요.",
|
| 1149 |
"significance": "AI는 우리 생활을 더 편리하게 만들어주는 기술이에요.",
|
|
@@ -1154,23 +1138,43 @@ JSON만 출력하고 다른 설명은 하지 마세요."""
|
|
| 1154 |
}
|
| 1155 |
|
| 1156 |
def analyze_model(self, model_name: str, task: str, downloads: int) -> str:
|
| 1157 |
-
"""허깅페이스 모델 분석"""
|
|
|
|
|
|
|
|
|
|
| 1158 |
|
| 1159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1160 |
|
| 1161 |
-
|
| 1162 |
-
태스크: {task}
|
| 1163 |
-
다운로드 수: {downloads:,}
|
| 1164 |
|
| 1165 |
-
|
| 1166 |
-
|
|
|
|
|
|
|
| 1167 |
|
| 1168 |
-
|
| 1169 |
-
|
| 1170 |
-
|
| 1171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1172 |
|
| 1173 |
-
#
|
| 1174 |
task_explanations = {
|
| 1175 |
"text-generation": "글을 자동으로 만들어주는",
|
| 1176 |
"image-to-text": "사진을 보고 설명을 써주는",
|
|
@@ -1196,39 +1200,65 @@ JSON만 출력하고 다른 설명은 하지 마세요."""
|
|
| 1196 |
|
| 1197 |
return f"이 모델은 {task_desc} AI예요. {popularity} 사람들이 다운로드해서 사용하고 있어요. {model_name.split('/')[-1]}라는 이름으로 유명해요!"
|
| 1198 |
|
| 1199 |
-
def analyze_space(self, space_name: str, description: str) -> Dict:
|
| 1200 |
-
"""허깅페이스 스페이스 분석"""
|
| 1201 |
|
| 1202 |
-
|
| 1203 |
-
|
| 1204 |
-
|
| 1205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1206 |
|
| 1207 |
-
|
| 1208 |
-
{{
|
| 1209 |
-
"simple_explanation": "이 스페이스가 무엇을 하는지 쉽게 설명 (2-3문장)",
|
| 1210 |
-
"tech_stack": ["기술1", "기술2", "기술3"]
|
| 1211 |
-
}}
|
| 1212 |
|
| 1213 |
-
|
|
|
|
|
|
|
|
|
|
| 1214 |
|
| 1215 |
-
|
| 1216 |
-
|
| 1217 |
-
|
| 1218 |
-
|
| 1219 |
-
|
| 1220 |
-
if response.startswith("```json"):
|
| 1221 |
-
response = response[7:]
|
| 1222 |
-
if response.endswith("```"):
|
| 1223 |
-
response = response[:-3]
|
| 1224 |
-
response = response.strip()
|
| 1225 |
|
| 1226 |
-
|
| 1227 |
-
|
| 1228 |
-
|
| 1229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1230 |
|
| 1231 |
-
#
|
| 1232 |
return {
|
| 1233 |
"simple_explanation": f"{space_name}는 웹브라우저에서 바로 AI를 체험해볼 수 있는 곳이에요. 설치 없이도 사용할 수 있어서 편리해요! 마치 온라인 게임처럼 바로 접속해서 AI를 사용할 수 있답니다.",
|
| 1234 |
"tech_stack": ["Python", "Gradio", "Transformers", "PyTorch"]
|
|
@@ -1240,10 +1270,10 @@ JSON만 출력하고 다른 설명은 하지 마세요."""
|
|
| 1240 |
# ============================================
|
| 1241 |
|
| 1242 |
class AdvancedAIAnalyzer:
|
| 1243 |
-
"""LLM 기반 고급 AI 뉴스 분석기
|
| 1244 |
|
| 1245 |
def __init__(self):
|
| 1246 |
-
self.llm_analyzer =
|
| 1247 |
self.huggingface_data = {
|
| 1248 |
"models": [],
|
| 1249 |
"spaces": []
|
|
@@ -1257,9 +1287,12 @@ class AdvancedAIAnalyzer:
|
|
| 1257 |
models_list = []
|
| 1258 |
|
| 1259 |
try:
|
|
|
|
| 1260 |
api = HfApi()
|
|
|
|
|
|
|
| 1261 |
models = list(api.list_models(
|
| 1262 |
-
sort="
|
| 1263 |
direction=-1,
|
| 1264 |
limit=limit
|
| 1265 |
))
|
|
@@ -1277,8 +1310,8 @@ class AdvancedAIAnalyzer:
|
|
| 1277 |
'rank': idx
|
| 1278 |
}
|
| 1279 |
|
| 1280 |
-
# LLM 분석 추가
|
| 1281 |
-
print(f"
|
| 1282 |
model_info['analysis'] = self.llm_analyzer.analyze_model(
|
| 1283 |
model_info['name'],
|
| 1284 |
model_info['task'],
|
|
@@ -1287,9 +1320,12 @@ class AdvancedAIAnalyzer:
|
|
| 1287 |
|
| 1288 |
models_list.append(model_info)
|
| 1289 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1290 |
if idx % 5 == 0:
|
| 1291 |
print(f" ✓ {idx}개 모델 처리 완료...")
|
| 1292 |
-
time.sleep(0.5) # Rate limit 방지
|
| 1293 |
|
| 1294 |
except Exception as e:
|
| 1295 |
print(f" ⚠️ 모델 {idx} 처리 오류: {e}")
|
|
@@ -1297,6 +1333,7 @@ class AdvancedAIAnalyzer:
|
|
| 1297 |
|
| 1298 |
print(f"✅ {len(models_list)}개 트렌딩 모델 수집 완료")
|
| 1299 |
|
|
|
|
| 1300 |
if models_list:
|
| 1301 |
save_models_to_db(models_list)
|
| 1302 |
|
|
@@ -1314,9 +1351,12 @@ class AdvancedAIAnalyzer:
|
|
| 1314 |
spaces_list = []
|
| 1315 |
|
| 1316 |
try:
|
|
|
|
| 1317 |
api = HfApi()
|
|
|
|
|
|
|
| 1318 |
spaces = list(api.list_spaces(
|
| 1319 |
-
sort="
|
| 1320 |
direction=-1,
|
| 1321 |
limit=limit
|
| 1322 |
))
|
|
@@ -1336,10 +1376,11 @@ class AdvancedAIAnalyzer:
|
|
| 1336 |
'rank': idx
|
| 1337 |
}
|
| 1338 |
|
| 1339 |
-
# LLM 분석 추가
|
| 1340 |
-
print(f"
|
| 1341 |
space_analysis = self.llm_analyzer.analyze_space(
|
| 1342 |
space_info['name'],
|
|
|
|
| 1343 |
space_info['title']
|
| 1344 |
)
|
| 1345 |
|
|
@@ -1349,9 +1390,12 @@ class AdvancedAIAnalyzer:
|
|
| 1349 |
|
| 1350 |
spaces_list.append(space_info)
|
| 1351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1352 |
if idx % 5 == 0:
|
| 1353 |
print(f" ✓ {idx}개 스페이스 처리 완료...")
|
| 1354 |
-
time.sleep(0.5) # Rate limit 방지
|
| 1355 |
|
| 1356 |
except Exception as e:
|
| 1357 |
print(f" ⚠️ 스페이스 {idx} 처리 오류: {e}")
|
|
@@ -1359,6 +1403,7 @@ class AdvancedAIAnalyzer:
|
|
| 1359 |
|
| 1360 |
print(f"✅ {len(spaces_list)}개 트렌딩 스페이스 수집 완료")
|
| 1361 |
|
|
|
|
| 1362 |
if spaces_list:
|
| 1363 |
save_spaces_to_db(spaces_list)
|
| 1364 |
|
|
@@ -1401,9 +1446,7 @@ class AdvancedAIAnalyzer:
|
|
| 1401 |
news = self.create_sample_news()
|
| 1402 |
analyzed_news = []
|
| 1403 |
|
| 1404 |
-
for
|
| 1405 |
-
print(f" 🧠 뉴스 {idx}/{len(news)} LLM 분석 중...")
|
| 1406 |
-
|
| 1407 |
analysis = self.llm_analyzer.analyze_news_simple(
|
| 1408 |
article['title'],
|
| 1409 |
""
|
|
@@ -1411,19 +1454,22 @@ class AdvancedAIAnalyzer:
|
|
| 1411 |
|
| 1412 |
article['analysis'] = analysis
|
| 1413 |
analyzed_news.append(article)
|
| 1414 |
-
|
| 1415 |
-
time.sleep(0.5) # Rate limit 방지
|
| 1416 |
|
| 1417 |
print(f"✅ {len(analyzed_news)}개 뉴스 분석 완료")
|
| 1418 |
|
|
|
|
| 1419 |
save_news_to_db(analyzed_news)
|
| 1420 |
|
| 1421 |
return analyzed_news
|
| 1422 |
|
| 1423 |
def get_all_data(self, force_refresh: bool = False) -> Dict:
|
| 1424 |
-
"""모든 데이터 수집 및 분석
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1425 |
print("\n" + "="*60)
|
| 1426 |
-
print("🚀 AI 뉴스 & 허깅페이스 LLM 분석 시작
|
| 1427 |
print("="*60 + "\n")
|
| 1428 |
|
| 1429 |
if force_refresh:
|
|
@@ -1434,6 +1480,7 @@ class AdvancedAIAnalyzer:
|
|
| 1434 |
else:
|
| 1435 |
print("💾 DB 우선 로드 모드")
|
| 1436 |
|
|
|
|
| 1437 |
analyzed_news = load_news_from_db()
|
| 1438 |
if not analyzed_news:
|
| 1439 |
print("📰 DB에 뉴스 없음 → 새로 수집")
|
|
@@ -1455,6 +1502,7 @@ class AdvancedAIAnalyzer:
|
|
| 1455 |
else:
|
| 1456 |
print(f"✅ DB에서 {len(analyzed_spaces)}개 스페이스 로드")
|
| 1457 |
|
|
|
|
| 1458 |
stats = {
|
| 1459 |
'total_news': len(analyzed_news),
|
| 1460 |
'hf_models': len(analyzed_models),
|
|
@@ -1484,6 +1532,7 @@ class AdvancedAIAnalyzer:
|
|
| 1484 |
def index():
|
| 1485 |
"""메인 페이지"""
|
| 1486 |
try:
|
|
|
|
| 1487 |
force_refresh = request.args.get('refresh', 'false').lower() == 'true'
|
| 1488 |
|
| 1489 |
analyzer = AdvancedAIAnalyzer()
|
|
@@ -1551,6 +1600,7 @@ def api_refresh():
|
|
| 1551 |
def health():
|
| 1552 |
"""헬스 체크"""
|
| 1553 |
try:
|
|
|
|
| 1554 |
conn = sqlite3.connect(DB_PATH)
|
| 1555 |
cursor = conn.cursor()
|
| 1556 |
cursor.execute("SELECT COUNT(*) FROM news")
|
|
@@ -1563,15 +1613,17 @@ def health():
|
|
| 1563 |
|
| 1564 |
return jsonify({
|
| 1565 |
"status": "healthy",
|
| 1566 |
-
"service": "AI News LLM Analyzer
|
| 1567 |
"version": "3.1.0",
|
| 1568 |
-
"llm_provider": "Fireworks AI (Qwen3-235B)",
|
| 1569 |
"database": {
|
| 1570 |
"connected": True,
|
| 1571 |
"news_count": news_count,
|
| 1572 |
"models_count": models_count,
|
| 1573 |
"spaces_count": spaces_count
|
| 1574 |
},
|
|
|
|
|
|
|
|
|
|
| 1575 |
"timestamp": datetime.now().isoformat()
|
| 1576 |
})
|
| 1577 |
except Exception as e:
|
|
@@ -1592,18 +1644,20 @@ if __name__ == '__main__':
|
|
| 1592 |
╔════════════════════════════════════════════════════════════╗
|
| 1593 |
║ ║
|
| 1594 |
║ 🤖 AI 뉴스 & 허깅페이스 LLM 분석 웹앱 v3.1 ║
|
| 1595 |
-
║ 🔥 Powered by Fireworks AI (Qwen3-235B) ║
|
| 1596 |
║ ║
|
| 1597 |
╚════════════════════════════════════════════════════════════╝
|
| 1598 |
|
| 1599 |
✨ 주요 기능:
|
| 1600 |
-
• 🔥 Fireworks AI LLM으로 실제 분석
|
| 1601 |
• 💾 SQLite DB 영구 스토리지
|
| 1602 |
• 📰 뉴스 초등학생 수준 분석
|
| 1603 |
-
• 🤗 허깅페이스 트렌딩 모델 TOP 30
|
| 1604 |
-
• 🚀 허깅페이스 트렌딩 스페이스 TOP 30
|
|
|
|
| 1605 |
• 🎨 탭 UI (뉴스/모델/스페이스)
|
| 1606 |
|
|
|
|
|
|
|
|
|
|
| 1607 |
🚀 서버 정보:
|
| 1608 |
📍 메인: http://localhost:{port}
|
| 1609 |
🔄 강제갱신: http://localhost:{port}/?refresh=true
|
|
@@ -1612,11 +1666,11 @@ if __name__ == '__main__':
|
|
| 1612 |
💚 Health: http://localhost:{port}/health
|
| 1613 |
|
| 1614 |
💾 데이터베이스: {DB_PATH}
|
| 1615 |
-
🔑 API 키 상태: {'✅ 설정됨' if FIREWORKS_API_KEY else '⚠️ 미설정 (샘플 분석 사용)'}
|
| 1616 |
|
| 1617 |
초기화 중...
|
| 1618 |
""")
|
| 1619 |
|
|
|
|
| 1620 |
try:
|
| 1621 |
init_database()
|
| 1622 |
except Exception as e:
|
|
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""
|
| 3 |
+
AI 뉴스 & 허깅페이스 트렌딩 LLM 분석 웹앱 (완전판 v3.1)
|
| 4 |
+
파일명: app_advanced.py
|
| 5 |
|
| 6 |
주요 기능:
|
| 7 |
+
1. SQLite DB 영구 스토리지
|
| 8 |
+
2. 실제 Hugging Face Trending API 연동 (모델/스페이스 30위)
|
| 9 |
+
3. Fireworks AI (Qwen3-235B) 실시간 LLM 분석
|
| 10 |
+
- 모델 카드 자동 분석 (README.md)
|
| 11 |
+
- 스페이스 코드 자동 분석 (app.py)
|
| 12 |
+
4. 초등학생도 이해 가능한 쉬운 설명
|
| 13 |
5. 탭 UI (뉴스/모델/스페이스)
|
| 14 |
|
| 15 |
실행 방법:
|
| 16 |
1. pip install Flask requests beautifulsoup4 huggingface_hub
|
| 17 |
+
2. export FIREWORKS_API_KEY="your-api-key-here" # 필수!
|
| 18 |
+
3. python app_advanced.py
|
| 19 |
4. 브라우저에서 http://localhost:7860 접속
|
| 20 |
+
|
| 21 |
+
환경변수:
|
| 22 |
+
- FIREWORKS_API_KEY: Fireworks AI API 키 (필수)
|
| 23 |
+
- PORT: 서버 포트 (기본값: 7860)
|
| 24 |
"""
|
| 25 |
|
| 26 |
from flask import Flask, render_template_string, jsonify, request
|
|
|
|
| 41 |
# 데이터베이스 파일 경로
|
| 42 |
DB_PATH = 'ai_news_analysis.db'
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
# ============================================
|
| 46 |
# HTML 템플릿 (탭 UI 포함)
|
|
|
|
| 542 |
<body>
|
| 543 |
<div class="container">
|
| 544 |
<h1>🤖 AI 뉴스 & 허깅페이스 LLM 분석</h1>
|
| 545 |
+
<p class="subtitle">초등학생도 이해하는 AI 트렌드 분석 시스템 🎓</p>
|
| 546 |
|
| 547 |
<!-- 통계 카드 -->
|
| 548 |
<div class="stats">
|
|
|
|
| 719 |
|
| 720 |
<!-- 푸터 -->
|
| 721 |
<div class="footer">
|
| 722 |
+
<p>🤖 AI 뉴스 LLM 분석 시스템 v3.1</p>
|
| 723 |
<p style="margin-top: 10px; font-size: 0.9em;">
|
| 724 |
+
💾 SQLite DB 영구 저장 | 🤗 Hugging Face Trending API | 🧠 Powered by Fireworks AI (Qwen3-235B)
|
| 725 |
</p>
|
| 726 |
<p style="margin-top: 10px; font-size: 0.85em; color: #999;">
|
| 727 |
+
데이터 출처: AI Times, Hugging Face | 실시간 분석: Fireworks AI
|
| 728 |
</p>
|
| 729 |
</div>
|
| 730 |
</div>
|
|
|
|
| 744 |
document.getElementById(tabName + '-content').classList.add('active');
|
| 745 |
}
|
| 746 |
|
| 747 |
+
console.log('✅ AI 뉴스 LLM 분석 시스템 로드 완료');
|
| 748 |
</script>
|
| 749 |
</body>
|
| 750 |
</html>
|
|
|
|
| 847 |
))
|
| 848 |
saved_count += 1
|
| 849 |
except sqlite3.IntegrityError:
|
| 850 |
+
pass # 이미 존재하는 뉴스
|
| 851 |
|
| 852 |
conn.commit()
|
| 853 |
conn.close()
|
|
|
|
| 1001 |
'simple_explanation': row[7],
|
| 1002 |
'tech_stack': json.loads(row[8]) if row[8] else [],
|
| 1003 |
'rank': row[9],
|
| 1004 |
+
'description': row[3] # title을 description으로 사용
|
| 1005 |
})
|
| 1006 |
|
| 1007 |
conn.close()
|
|
|
|
| 1009 |
|
| 1010 |
|
| 1011 |
# ============================================
|
| 1012 |
+
# LLM 분석기 클래스
|
| 1013 |
# ============================================
|
| 1014 |
|
| 1015 |
+
class LLMAnalyzer:
|
| 1016 |
+
"""Fireworks AI (Qwen3) 기반 LLM 분석기"""
|
| 1017 |
|
| 1018 |
+
def __init__(self):
|
| 1019 |
+
self.api_key = os.environ.get('FIREWORKS_API_KEY', '')
|
| 1020 |
+
self.api_url = "https://api.fireworks.ai/inference/v1/chat/completions"
|
| 1021 |
+
self.api_available = bool(self.api_key)
|
| 1022 |
+
|
| 1023 |
+
if not self.api_available:
|
| 1024 |
+
print("⚠️ FIREWORKS_API_KEY 환경변수가 설정되지 않았습니다. 템플릿 모드로 동작합니다.")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1025 |
|
| 1026 |
+
def call_llm(self, messages: List[Dict], max_tokens: int = 2000) -> str:
|
| 1027 |
+
"""Fireworks AI API 호출"""
|
| 1028 |
if not self.api_available:
|
| 1029 |
+
return None
|
| 1030 |
|
| 1031 |
try:
|
| 1032 |
payload = {
|
| 1033 |
+
"model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
|
| 1034 |
"max_tokens": max_tokens,
|
| 1035 |
"top_p": 1,
|
| 1036 |
"top_k": 40,
|
| 1037 |
"presence_penalty": 0,
|
| 1038 |
"frequency_penalty": 0,
|
| 1039 |
"temperature": 0.6,
|
| 1040 |
+
"messages": messages
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1041 |
}
|
| 1042 |
|
| 1043 |
headers = {
|
|
|
|
| 1046 |
"Authorization": f"Bearer {self.api_key}"
|
| 1047 |
}
|
| 1048 |
|
| 1049 |
+
response = requests.post(self.api_url, headers=headers, json=payload, timeout=30)
|
| 1050 |
+
response.raise_for_status()
|
| 1051 |
+
|
| 1052 |
+
result = response.json()
|
| 1053 |
+
return result['choices'][0]['message']['content']
|
| 1054 |
+
|
| 1055 |
+
except Exception as e:
|
| 1056 |
+
print(f" ⚠️ LLM API 호출 오류: {e}")
|
| 1057 |
+
return None
|
| 1058 |
+
|
| 1059 |
+
def fetch_model_card(self, model_id: str) -> str:
|
| 1060 |
+
"""허깅페이스 모델 카드(README.md) 가져오기"""
|
| 1061 |
+
try:
|
| 1062 |
+
url = f"https://huggingface.co/{model_id}/raw/main/README.md"
|
| 1063 |
+
response = requests.get(url, timeout=10)
|
| 1064 |
|
| 1065 |
if response.status_code == 200:
|
| 1066 |
+
content = response.text
|
| 1067 |
+
# 너무 긴 경우 앞부분만 (약 3000자)
|
| 1068 |
+
if len(content) > 3000:
|
| 1069 |
+
content = content[:3000] + "\n...(후략)"
|
| 1070 |
+
return content
|
| 1071 |
else:
|
| 1072 |
+
return None
|
|
|
|
|
|
|
| 1073 |
except Exception as e:
|
| 1074 |
+
print(f" ⚠️ 모델 카드 가져오기 오류: {e}")
|
| 1075 |
+
return None
|
| 1076 |
+
|
| 1077 |
+
def fetch_space_code(self, space_id: str) -> str:
|
| 1078 |
+
"""허깅페이스 스페이스 app.py 가져오기"""
|
| 1079 |
+
try:
|
| 1080 |
+
url = f"https://huggingface.co/spaces/{space_id}/raw/main/app.py"
|
| 1081 |
+
response = requests.get(url, timeout=10)
|
| 1082 |
+
|
| 1083 |
+
if response.status_code == 200:
|
| 1084 |
+
content = response.text
|
| 1085 |
+
# 너무 긴 경우 앞부분만 (약 2000자)
|
| 1086 |
+
if len(content) > 2000:
|
| 1087 |
+
content = content[:2000] + "\n...(후략)"
|
| 1088 |
+
return content
|
| 1089 |
+
else:
|
| 1090 |
+
return None
|
| 1091 |
+
except Exception as e:
|
| 1092 |
+
print(f" ⚠️ 스페이스 코드 가져오기 오류: {e}")
|
| 1093 |
+
return None
|
| 1094 |
|
| 1095 |
def analyze_news_simple(self, title: str, content: str = "") -> Dict:
|
| 1096 |
"""뉴스 기사를 초등학생 수준으로 분석"""
|
| 1097 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1098 |
analysis_templates = {
|
| 1099 |
"챗GPT": {
|
| 1100 |
"summary": "마이크로소프트(MS)라는 큰 회사가 챗GPT라는 AI를 너무 많은 사람들이 사용해서, 컴퓨터를 보관하는 큰 건물(데이터센터)이 부족하다고 말했어요.",
|
|
|
|
| 1122 |
}
|
| 1123 |
}
|
| 1124 |
|
| 1125 |
+
# 키워드 매칭으로 템플릿 선택
|
| 1126 |
for keyword, template in analysis_templates.items():
|
| 1127 |
if keyword.lower() in title.lower():
|
| 1128 |
return template
|
| 1129 |
|
| 1130 |
+
# 기본 분석
|
| 1131 |
return {
|
| 1132 |
"summary": f"'{title}'라는 AI 관련 뉴스가 나왔어요. AI 기술이 계속 발전하고 있다는 소식이에요.",
|
| 1133 |
"significance": "AI는 우리 생활을 더 편리하게 만들어주는 기술이에요.",
|
|
|
|
| 1138 |
}
|
| 1139 |
|
| 1140 |
def analyze_model(self, model_name: str, task: str, downloads: int) -> str:
|
| 1141 |
+
"""허깅페이스 모델 분석 - 모델 카드를 LLM으로 분석"""
|
| 1142 |
+
|
| 1143 |
+
# 1. 모델 카드 가져오기
|
| 1144 |
+
model_card = self.fetch_model_card(model_name)
|
| 1145 |
|
| 1146 |
+
# 2. LLM으로 분석
|
| 1147 |
+
if model_card and self.api_available:
|
| 1148 |
+
try:
|
| 1149 |
+
messages = [
|
| 1150 |
+
{
|
| 1151 |
+
"role": "system",
|
| 1152 |
+
"content": "당신은 초등학생도 이해할 수 있게 AI 모델을 쉽게 설명하는 전문가입니다. 한국어로 답변하세요."
|
| 1153 |
+
},
|
| 1154 |
+
{
|
| 1155 |
+
"role": "user",
|
| 1156 |
+
"content": f"""다음은 허깅페이스 모델 '{model_name}'의 모델 카드입니다:
|
| 1157 |
|
| 1158 |
+
{model_card}
|
|
|
|
|
|
|
| 1159 |
|
| 1160 |
+
이 모델을 초등학생이 이해할 수 있도록 3-4문장으로 쉽게 설명해주세요. 다음 내용을 포함하세요:
|
| 1161 |
+
1. 이 모델이 무엇을 하는지
|
| 1162 |
+
2. 어떤 특징이 있는지
|
| 1163 |
+
3. 누가 사용하면 좋은지
|
| 1164 |
|
| 1165 |
+
답변은 반드시 3-4문장의 한국어로만 작성하세요."""
|
| 1166 |
+
}
|
| 1167 |
+
]
|
| 1168 |
+
|
| 1169 |
+
result = self.call_llm(messages, max_tokens=500)
|
| 1170 |
+
|
| 1171 |
+
if result:
|
| 1172 |
+
return result.strip()
|
| 1173 |
+
|
| 1174 |
+
except Exception as e:
|
| 1175 |
+
print(f" ⚠️ 모델 분석 LLM 오류: {e}")
|
| 1176 |
|
| 1177 |
+
# 3. Fallback: 템플릿 기반 설명
|
| 1178 |
task_explanations = {
|
| 1179 |
"text-generation": "글을 자동으로 만들어주는",
|
| 1180 |
"image-to-text": "사진을 보고 설명을 써주는",
|
|
|
|
| 1200 |
|
| 1201 |
return f"이 모델은 {task_desc} AI예요. {popularity} 사람들이 다운로드해서 사용하고 있어요. {model_name.split('/')[-1]}라는 이름으로 유명해요!"
|
| 1202 |
|
| 1203 |
+
def analyze_space(self, space_name: str, space_id: str, description: str) -> Dict:
|
| 1204 |
+
"""허깅페이스 스페이스 분석 - app.py를 LLM으로 분석"""
|
| 1205 |
|
| 1206 |
+
# 1. app.py 코드 가져오기
|
| 1207 |
+
app_code = self.fetch_space_code(space_id)
|
| 1208 |
+
|
| 1209 |
+
# 2. LLM으로 분석
|
| 1210 |
+
if app_code and self.api_available:
|
| 1211 |
+
try:
|
| 1212 |
+
messages = [
|
| 1213 |
+
{
|
| 1214 |
+
"role": "system",
|
| 1215 |
+
"content": "당신은 초등학생도 이해할 수 있게 AI 애플리케이션을 쉽게 설명하는 전문가입니다. 한국어로 답변하세요."
|
| 1216 |
+
},
|
| 1217 |
+
{
|
| 1218 |
+
"role": "user",
|
| 1219 |
+
"content": f"""다음은 허깅페이스 스페이스 '{space_name}'의 app.py 코드입니다:
|
| 1220 |
|
| 1221 |
+
{app_code}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1222 |
|
| 1223 |
+
이 앱을 초등학생이 이해할 수 있도록 3-4문장으로 쉽게 설명해주세요. 다음 내용을 포함하세요:
|
| 1224 |
+
1. 이 앱이 무엇을 하는지
|
| 1225 |
+
2. 어떤 기술을 사용하는지
|
| 1226 |
+
3. 어떻게 활용할 수 있는지
|
| 1227 |
|
| 1228 |
+
답변은 반드시 3-4문장의 한국어로만 작성하세요."""
|
| 1229 |
+
}
|
| 1230 |
+
]
|
| 1231 |
+
|
| 1232 |
+
result = self.call_llm(messages, max_tokens=500)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1233 |
|
| 1234 |
+
if result:
|
| 1235 |
+
# 기술 스택 추출 시도
|
| 1236 |
+
tech_stack = []
|
| 1237 |
+
if 'gradio' in app_code.lower():
|
| 1238 |
+
tech_stack.append('Gradio')
|
| 1239 |
+
if 'streamlit' in app_code.lower():
|
| 1240 |
+
tech_stack.append('Streamlit')
|
| 1241 |
+
if 'transformers' in app_code.lower():
|
| 1242 |
+
tech_stack.append('Transformers')
|
| 1243 |
+
if 'torch' in app_code.lower() or 'pytorch' in app_code.lower():
|
| 1244 |
+
tech_stack.append('PyTorch')
|
| 1245 |
+
if 'tensorflow' in app_code.lower():
|
| 1246 |
+
tech_stack.append('TensorFlow')
|
| 1247 |
+
if 'diffusers' in app_code.lower():
|
| 1248 |
+
tech_stack.append('Diffusers')
|
| 1249 |
+
|
| 1250 |
+
if not tech_stack:
|
| 1251 |
+
tech_stack = ['Python', 'AI']
|
| 1252 |
+
|
| 1253 |
+
return {
|
| 1254 |
+
"simple_explanation": result.strip(),
|
| 1255 |
+
"tech_stack": tech_stack
|
| 1256 |
+
}
|
| 1257 |
+
|
| 1258 |
+
except Exception as e:
|
| 1259 |
+
print(f" ⚠️ 스페이스 분석 LLM 오류: {e}")
|
| 1260 |
|
| 1261 |
+
# 3. Fallback: 템플릿 기반 설명
|
| 1262 |
return {
|
| 1263 |
"simple_explanation": f"{space_name}는 웹브라우저에서 바로 AI를 체험해볼 수 있는 곳이에요. 설치 없이도 사용할 수 있어서 편리해요! 마치 온라인 게임처럼 바로 접속해서 AI를 사용할 수 있답니다.",
|
| 1264 |
"tech_stack": ["Python", "Gradio", "Transformers", "PyTorch"]
|
|
|
|
| 1270 |
# ============================================
|
| 1271 |
|
| 1272 |
class AdvancedAIAnalyzer:
|
| 1273 |
+
"""LLM 기반 고급 AI 뉴스 분석기"""
|
| 1274 |
|
| 1275 |
def __init__(self):
|
| 1276 |
+
self.llm_analyzer = LLMAnalyzer()
|
| 1277 |
self.huggingface_data = {
|
| 1278 |
"models": [],
|
| 1279 |
"spaces": []
|
|
|
|
| 1287 |
models_list = []
|
| 1288 |
|
| 1289 |
try:
|
| 1290 |
+
# Hugging Face API 사용
|
| 1291 |
api = HfApi()
|
| 1292 |
+
|
| 1293 |
+
# trending 순위로 모델 가져오기
|
| 1294 |
models = list(api.list_models(
|
| 1295 |
+
sort="trending_score",
|
| 1296 |
direction=-1,
|
| 1297 |
limit=limit
|
| 1298 |
))
|
|
|
|
| 1310 |
'rank': idx
|
| 1311 |
}
|
| 1312 |
|
| 1313 |
+
# LLM 분석 추가 (모델 카드 분석)
|
| 1314 |
+
print(f" 🔍 {idx}. {model.id} 분석 중...")
|
| 1315 |
model_info['analysis'] = self.llm_analyzer.analyze_model(
|
| 1316 |
model_info['name'],
|
| 1317 |
model_info['task'],
|
|
|
|
| 1320 |
|
| 1321 |
models_list.append(model_info)
|
| 1322 |
|
| 1323 |
+
# API rate limit 방지를 위한 짧은 대기
|
| 1324 |
+
time.sleep(0.5)
|
| 1325 |
+
|
| 1326 |
+
# 진행상황 표시
|
| 1327 |
if idx % 5 == 0:
|
| 1328 |
print(f" ✓ {idx}개 모델 처리 완료...")
|
|
|
|
| 1329 |
|
| 1330 |
except Exception as e:
|
| 1331 |
print(f" ⚠️ 모델 {idx} 처리 오류: {e}")
|
|
|
|
| 1333 |
|
| 1334 |
print(f"✅ {len(models_list)}개 트렌딩 모델 수집 완료")
|
| 1335 |
|
| 1336 |
+
# DB에 저장
|
| 1337 |
if models_list:
|
| 1338 |
save_models_to_db(models_list)
|
| 1339 |
|
|
|
|
| 1351 |
spaces_list = []
|
| 1352 |
|
| 1353 |
try:
|
| 1354 |
+
# Hugging Face API 사용
|
| 1355 |
api = HfApi()
|
| 1356 |
+
|
| 1357 |
+
# trending 순위로 스페이스 가져오기
|
| 1358 |
spaces = list(api.list_spaces(
|
| 1359 |
+
sort="trending_score",
|
| 1360 |
direction=-1,
|
| 1361 |
limit=limit
|
| 1362 |
))
|
|
|
|
| 1376 |
'rank': idx
|
| 1377 |
}
|
| 1378 |
|
| 1379 |
+
# LLM 분석 추가 (app.py 분석)
|
| 1380 |
+
print(f" 🔍 {idx}. {space.id} 분석 중...")
|
| 1381 |
space_analysis = self.llm_analyzer.analyze_space(
|
| 1382 |
space_info['name'],
|
| 1383 |
+
space_info['space_id'],
|
| 1384 |
space_info['title']
|
| 1385 |
)
|
| 1386 |
|
|
|
|
| 1390 |
|
| 1391 |
spaces_list.append(space_info)
|
| 1392 |
|
| 1393 |
+
# API rate limit 방지를 위한 짧은 대기
|
| 1394 |
+
time.sleep(0.5)
|
| 1395 |
+
|
| 1396 |
+
# 진행상황 표시
|
| 1397 |
if idx % 5 == 0:
|
| 1398 |
print(f" ✓ {idx}개 스페이스 처리 완료...")
|
|
|
|
| 1399 |
|
| 1400 |
except Exception as e:
|
| 1401 |
print(f" ⚠️ 스페이스 {idx} 처리 오류: {e}")
|
|
|
|
| 1403 |
|
| 1404 |
print(f"✅ {len(spaces_list)}개 트렌딩 스페이스 수집 완료")
|
| 1405 |
|
| 1406 |
+
# DB에 저장
|
| 1407 |
if spaces_list:
|
| 1408 |
save_spaces_to_db(spaces_list)
|
| 1409 |
|
|
|
|
| 1446 |
news = self.create_sample_news()
|
| 1447 |
analyzed_news = []
|
| 1448 |
|
| 1449 |
+
for article in news:
|
|
|
|
|
|
|
| 1450 |
analysis = self.llm_analyzer.analyze_news_simple(
|
| 1451 |
article['title'],
|
| 1452 |
""
|
|
|
|
| 1454 |
|
| 1455 |
article['analysis'] = analysis
|
| 1456 |
analyzed_news.append(article)
|
|
|
|
|
|
|
| 1457 |
|
| 1458 |
print(f"✅ {len(analyzed_news)}개 뉴스 분석 완료")
|
| 1459 |
|
| 1460 |
+
# DB에 저장
|
| 1461 |
save_news_to_db(analyzed_news)
|
| 1462 |
|
| 1463 |
return analyzed_news
|
| 1464 |
|
| 1465 |
def get_all_data(self, force_refresh: bool = False) -> Dict:
|
| 1466 |
+
"""모든 데이터 수집 및 분석
|
| 1467 |
+
|
| 1468 |
+
Args:
|
| 1469 |
+
force_refresh: True면 새로 수집, False면 DB에서 로드 후 없으면 수집
|
| 1470 |
+
"""
|
| 1471 |
print("\n" + "="*60)
|
| 1472 |
+
print("🚀 AI 뉴스 & 허깅페이스 LLM 분석 시작")
|
| 1473 |
print("="*60 + "\n")
|
| 1474 |
|
| 1475 |
if force_refresh:
|
|
|
|
| 1480 |
else:
|
| 1481 |
print("💾 DB 우선 로드 모드")
|
| 1482 |
|
| 1483 |
+
# DB에서 먼저 로드
|
| 1484 |
analyzed_news = load_news_from_db()
|
| 1485 |
if not analyzed_news:
|
| 1486 |
print("📰 DB에 뉴스 없음 → 새로 수집")
|
|
|
|
| 1502 |
else:
|
| 1503 |
print(f"✅ DB에서 {len(analyzed_spaces)}개 스페이스 로드")
|
| 1504 |
|
| 1505 |
+
# 통계
|
| 1506 |
stats = {
|
| 1507 |
'total_news': len(analyzed_news),
|
| 1508 |
'hf_models': len(analyzed_models),
|
|
|
|
| 1532 |
def index():
|
| 1533 |
"""메인 페이지"""
|
| 1534 |
try:
|
| 1535 |
+
# refresh 파라미터 확인
|
| 1536 |
force_refresh = request.args.get('refresh', 'false').lower() == 'true'
|
| 1537 |
|
| 1538 |
analyzer = AdvancedAIAnalyzer()
|
|
|
|
| 1600 |
def health():
|
| 1601 |
"""헬스 체크"""
|
| 1602 |
try:
|
| 1603 |
+
# DB 연결 확인
|
| 1604 |
conn = sqlite3.connect(DB_PATH)
|
| 1605 |
cursor = conn.cursor()
|
| 1606 |
cursor.execute("SELECT COUNT(*) FROM news")
|
|
|
|
| 1613 |
|
| 1614 |
return jsonify({
|
| 1615 |
"status": "healthy",
|
| 1616 |
+
"service": "AI News LLM Analyzer",
|
| 1617 |
"version": "3.1.0",
|
|
|
|
| 1618 |
"database": {
|
| 1619 |
"connected": True,
|
| 1620 |
"news_count": news_count,
|
| 1621 |
"models_count": models_count,
|
| 1622 |
"spaces_count": spaces_count
|
| 1623 |
},
|
| 1624 |
+
"fireworks_api": {
|
| 1625 |
+
"configured": bool(os.environ.get('FIREWORKS_API_KEY'))
|
| 1626 |
+
},
|
| 1627 |
"timestamp": datetime.now().isoformat()
|
| 1628 |
})
|
| 1629 |
except Exception as e:
|
|
|
|
| 1644 |
╔════════════════════════════════════════════════════════════╗
|
| 1645 |
║ ║
|
| 1646 |
║ 🤖 AI 뉴스 & 허깅페이스 LLM 분석 웹앱 v3.1 ║
|
|
|
|
| 1647 |
║ ║
|
| 1648 |
╚════════════════════════════════════════════════════════════╝
|
| 1649 |
|
| 1650 |
✨ 주요 기능:
|
|
|
|
| 1651 |
• 💾 SQLite DB 영구 스토리지
|
| 1652 |
• 📰 뉴스 초등학생 수준 분석
|
| 1653 |
+
• 🤗 허깅페이스 트렌딩 모델 TOP 30 (모델 카드 분석)
|
| 1654 |
+
• 🚀 허깅페이스 트렌딩 스페이스 TOP 30 (app.py 분석)
|
| 1655 |
+
• 🧠 Fireworks AI (Qwen3-235B) 실시간 LLM 분석
|
| 1656 |
• 🎨 탭 UI (뉴스/모델/스페이스)
|
| 1657 |
|
| 1658 |
+
🔑 API 설정:
|
| 1659 |
+
FIREWORKS_API_KEY: {"✅ 설정됨" if os.environ.get('FIREWORKS_API_KEY') else "❌ 미설정 (템플릿 모드)"}
|
| 1660 |
+
|
| 1661 |
🚀 서버 정보:
|
| 1662 |
📍 메인: http://localhost:{port}
|
| 1663 |
🔄 강제갱신: http://localhost:{port}/?refresh=true
|
|
|
|
| 1666 |
💚 Health: http://localhost:{port}/health
|
| 1667 |
|
| 1668 |
💾 데이터베이스: {DB_PATH}
|
|
|
|
| 1669 |
|
| 1670 |
초기화 중...
|
| 1671 |
""")
|
| 1672 |
|
| 1673 |
+
# 데이터베이스 초기화
|
| 1674 |
try:
|
| 1675 |
init_database()
|
| 1676 |
except Exception as e:
|