ginipick commited on
Commit
f7560f2
Β·
verified Β·
1 Parent(s): c80cc6c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +615 -380
app.py CHANGED
@@ -1,32 +1,38 @@
1
  # -*- coding: utf-8 -*-
2
  """
3
- AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© 뢄석 μ›Ή μ•± (Flask 버전) - μ™„μ „νŒ
4
- 파일λͺ…: app.py
5
 
6
- μ‹€ν–‰ 방법:
7
- 1. pip install Flask requests beautifulsoup4 lxml gunicorn
8
- 2. python app.py
9
- 3. λΈŒλΌμš°μ €μ—μ„œ http://localhost:8080 접속
 
10
 
11
- ν”„λ‘œλ•μ…˜ μ‹€ν–‰:
12
- gunicorn -w 4 -b 0.0.0.0:8080 app:app
 
 
 
13
  """
14
 
15
  from flask import Flask, render_template_string, jsonify, request
16
  import requests
17
- from bs4 import BeautifulSoup
18
  import json
19
  from datetime import datetime
20
  from typing import List, Dict, Optional
21
  import os
22
  import sys
 
 
 
23
 
24
  # Flask μ•± μ΄ˆκΈ°ν™”
25
  app = Flask(__name__)
26
- app.config['JSON_AS_ASCII'] = False # ν•œκΈ€ JSON 지원
27
 
28
  # ============================================
29
- # HTML ν…œν”Œλ¦Ώ (μ™„μ „νŒ)
30
  # ============================================
31
 
32
  HTML_TEMPLATE = """
@@ -35,7 +41,7 @@ HTML_TEMPLATE = """
35
  <head>
36
  <meta charset="UTF-8">
37
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
38
- <title>AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© 뢄석 μ‹œμŠ€ν…œ</title>
39
  <style>
40
  * {
41
  margin: 0;
@@ -75,6 +81,52 @@ HTML_TEMPLATE = """
75
  font-size: 1.2em;
76
  }
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  .stats {
79
  display: grid;
80
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
@@ -111,94 +163,110 @@ HTML_TEMPLATE = """
111
  font-weight: 500;
112
  }
113
 
114
- .category-section {
115
- margin-bottom: 50px;
116
- }
117
-
118
- .category-title {
119
- background: linear-gradient(90deg, #667eea, #764ba2);
120
- color: white;
121
- padding: 18px 25px;
122
- border-radius: 12px;
123
- font-size: 1.6em;
124
- font-weight: 700;
125
  margin-bottom: 25px;
126
- display: flex;
127
- justify-content: space-between;
128
- align-items: center;
129
- box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
130
- }
131
-
132
- .news-item {
133
- background: #f8f9fa;
134
- padding: 25px;
135
- border-radius: 12px;
136
- margin-bottom: 20px;
137
  border-left: 6px solid #667eea;
138
  transition: all 0.3s;
139
- position: relative;
140
  }
141
 
142
- .news-item:hover {
143
- transform: translateX(8px);
144
- box-shadow: 0 8px 20px rgba(0,0,0,0.12);
145
- background: #f0f4ff;
 
 
 
 
 
 
 
 
146
  }
147
 
148
  .news-title {
149
- font-size: 1.3em;
150
  font-weight: 700;
151
  color: #2c3e50;
152
- margin-bottom: 12px;
153
- line-height: 1.5;
154
  }
155
 
156
  .news-meta {
157
- color: #7f8c8d;
158
- font-size: 0.95em;
159
- margin-bottom: 15px;
160
  display: flex;
161
- gap: 20px;
162
- flex-wrap: wrap;
 
163
  }
164
 
165
- .news-link {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  display: inline-block;
167
- background: #667eea;
168
  color: white;
169
- padding: 10px 20px;
170
- border-radius: 8px;
171
- text-decoration: none;
172
- font-size: 0.95em;
173
  font-weight: 600;
174
- transition: all 0.3s;
175
  }
176
 
177
- .news-link:hover {
178
- background: #764ba2;
179
- transform: scale(1.05);
180
- box-shadow: 0 4px 10px rgba(102, 126, 234, 0.4);
181
  }
182
 
183
- .hf-section {
184
- background: linear-gradient(135deg, #f0f4ff 0%, #e8f0fe 100%);
185
- padding: 40px;
186
- border-radius: 20px;
187
- margin-top: 50px;
188
- box-shadow: 0 10px 30px rgba(0,0,0,0.08);
 
189
  }
190
 
191
- .hf-title {
192
- font-size: 2.2em;
193
- color: #667eea;
194
- margin-bottom: 30px;
195
- text-align: center;
196
- font-weight: 800;
 
 
 
 
 
 
 
197
  }
198
 
 
199
  .model-grid {
200
  display: grid;
201
- grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
202
  gap: 25px;
203
  margin-top: 30px;
204
  }
@@ -217,19 +285,44 @@ HTML_TEMPLATE = """
217
  box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
218
  }
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  .model-name {
221
  font-weight: 700;
222
  color: #667eea;
223
  margin-bottom: 15px;
224
  font-size: 1.15em;
225
  word-break: break-word;
 
226
  }
227
 
228
  .model-stats {
229
- font-size: 0.95em;
230
- color: #555;
231
- margin-bottom: 15px;
232
- line-height: 1.8;
 
 
 
 
 
 
 
233
  }
234
 
235
  .model-task {
@@ -243,6 +336,84 @@ HTML_TEMPLATE = """
243
  font-weight: 600;
244
  }
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  .button-group {
247
  text-align: center;
248
  margin: 40px 0;
@@ -267,23 +438,22 @@ HTML_TEMPLATE = """
267
  box-shadow: 0 12px 30px rgba(102, 126, 234, 0.6);
268
  }
269
 
270
- .api-btn {
271
- background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
 
272
  color: white;
273
- border: none;
274
- padding: 18px 50px;
275
- font-size: 1.2em;
276
- font-weight: 700;
277
- border-radius: 50px;
278
- cursor: pointer;
279
- box-shadow: 0 8px 20px rgba(17, 153, 142, 0.4);
280
  transition: all 0.3s;
281
- margin: 0 10px;
282
  }
283
 
284
- .api-btn:hover {
285
- transform: scale(1.08);
286
- box-shadow: 0 12px 30px rgba(17, 153, 142, 0.6);
287
  }
288
 
289
  .loading {
@@ -312,15 +482,15 @@ HTML_TEMPLATE = """
312
  color: #666;
313
  }
314
 
315
- .badge {
316
- display: inline-block;
317
- background: #ff6b6b;
318
- color: white;
319
- padding: 4px 10px;
320
- border-radius: 12px;
321
- font-size: 0.75em;
322
- font-weight: 600;
323
- margin-left: 8px;
324
  }
325
 
326
  @media (max-width: 768px) {
@@ -332,123 +502,163 @@ HTML_TEMPLATE = """
332
  font-size: 2em;
333
  }
334
 
335
- .stats {
336
- grid-template-columns: repeat(2, 1fr);
337
- }
338
-
339
- .model-grid {
340
- grid-template-columns: 1fr;
341
- }
342
-
343
- .button-group {
344
- display: flex;
345
  flex-direction: column;
346
- gap: 15px;
347
  }
348
 
349
- .refresh-btn, .api-btn {
350
- margin: 0;
351
  width: 100%;
352
  }
353
- }
354
-
355
- /* μ• λ‹ˆλ©”μ΄μ…˜ */
356
- @keyframes fadeIn {
357
- from {
358
- opacity: 0;
359
- transform: translateY(20px);
360
- }
361
- to {
362
- opacity: 1;
363
- transform: translateY(0);
364
  }
365
  }
366
-
367
- .news-item {
368
- animation: fadeIn 0.5s ease-out;
369
- }
370
-
371
- .model-card {
372
- animation: fadeIn 0.5s ease-out;
373
- }
374
  </style>
375
  </head>
376
  <body>
377
  <div class="container">
378
- <h1>πŸ€– AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”©</h1>
379
- <p class="subtitle">μ‹€μ‹œκ°„ AI μ‚°μ—… 동ν–₯ 뢄석 μ‹œμŠ€ν…œ πŸ“Š</p>
380
 
381
  <!-- 톡계 μΉ΄λ“œ -->
382
  <div class="stats">
383
  <div class="stat-card">
384
  <div class="stat-number">{{ stats.total_news }}</div>
385
- <div class="stat-label">πŸ“° 총 λ‰΄μŠ€</div>
386
- </div>
387
- <div class="stat-card">
388
- <div class="stat-number">{{ stats.categories }}</div>
389
- <div class="stat-label">πŸ“ μΉ΄ν…Œκ³ λ¦¬</div>
390
  </div>
391
  <div class="stat-card">
392
  <div class="stat-number">{{ stats.hf_models }}</div>
393
- <div class="stat-label">πŸ€— HF λͺ¨λΈ</div>
394
  </div>
395
  <div class="stat-card">
396
  <div class="stat-number">{{ stats.hf_spaces }}</div>
397
- <div class="stat-label">πŸš€ HF 슀페이슀</div>
 
 
 
 
398
  </div>
399
  </div>
400
 
401
- <!-- μΉ΄ν…Œκ³ λ¦¬λ³„ λ‰΄μŠ€ -->
402
- {% for category, articles in news_by_category.items() %}
403
- <div class="category-section">
404
- <div class="category-title">
405
- <span>πŸ“Œ {{ category }}</span>
406
- <span class="badge">{{ articles|length }}건</span>
407
- </div>
408
- {% for article in articles %}
409
- <div class="news-item">
410
- <div class="news-title">{{ loop.index }}. {{ article.title }}</div>
411
- <div class="news-meta">
412
- <span>πŸ“… {{ article.date }}</span>
413
- <span>πŸ“° {{ article.source }}</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  </div>
 
415
  <a href="{{ article.url }}" target="_blank" class="news-link">
416
- πŸ”— 기사 μ „λ¬Έ 보기
417
  </a>
418
  </div>
419
  {% endfor %}
420
  </div>
421
- {% endfor %}
422
 
423
- <!-- ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© λͺ¨λΈ -->
424
- <div class="hf-section">
425
- <div class="hf-title">πŸ€— ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© λͺ¨λΈ TOP 10</div>
426
-
427
- {% if hf_models|length > 0 %}
428
  <div class="model-grid">
429
- {% for model in hf_models[:10] %}
430
- <div class="model-card">
431
- <div class="model-name">
432
- {{ loop.index }}. {{ model.name }}
433
- </div>
434
- <div class="model-task">
435
- 🏷️ {{ model.task }}
436
- </div>
437
  <div class="model-stats">
438
- πŸ“Š λ‹€μš΄λ‘œλ“œ: <strong>{{ "{:,}".format(model.downloads) }}</strong><br>
439
- ❀️ μ’‹μ•„μš”: <strong>{{ "{:,}".format(model.likes) }}</strong>
 
 
 
 
 
 
440
  </div>
 
 
 
 
 
 
441
  <a href="{{ model.url }}" target="_blank" class="news-link">
442
  πŸ”— λͺ¨λΈ νŽ˜μ΄μ§€ λ°©λ¬Έ
443
  </a>
444
  </div>
445
  {% endfor %}
446
  </div>
447
- {% else %}
448
- <div class="loading">
449
- ⚠️ λͺ¨λΈ 데이터λ₯Ό λΆˆλŸ¬μ˜€μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  </div>
451
- {% endif %}
452
  </div>
453
 
454
  <!-- λ²„νŠΌ κ·Έλ£Ή -->
@@ -456,9 +666,6 @@ HTML_TEMPLATE = """
456
  <button class="refresh-btn" onclick="location.reload()">
457
  πŸ”„ μƒˆλ‘œκ³ μΉ¨
458
  </button>
459
- <button class="api-btn" onclick="window.open('/api/data', '_blank')">
460
- πŸ“Š JSON API 보기
461
- </button>
462
  </div>
463
 
464
  <!-- νƒ€μž„μŠ€νƒ¬ν”„ -->
@@ -468,18 +675,29 @@ HTML_TEMPLATE = """
468
 
469
  <!-- ν‘Έν„° -->
470
  <div class="footer">
471
- <p>πŸ€– AI λ‰΄μŠ€ 뢄석 μ‹œμŠ€ν…œ v1.0</p>
472
  <p style="margin-top: 10px; font-size: 0.9em;">
473
- 데이터 좜처: AI Times, Hugging Face
474
  </p>
475
  </div>
476
  </div>
477
 
478
  <script>
479
- // μžλ™ μƒˆλ‘œκ³ μΉ¨ (5λΆ„λ§ˆλ‹€) - 선택사항
480
- // setTimeout(() => location.reload(), 5 * 60 * 1000);
 
 
 
 
 
 
 
 
 
 
 
481
 
482
- console.log('βœ… AI λ‰΄μŠ€ 뢄석 μ‹œμŠ€ν…œ λ‘œλ“œ μ™„λ£Œ');
483
  </script>
484
  </body>
485
  </html>
@@ -487,46 +705,121 @@ HTML_TEMPLATE = """
487
 
488
 
489
  # ============================================
490
- # AINewsAnalyzer 클래슀
491
  # ============================================
492
 
493
- class AINewsAnalyzer:
494
- """AI λ‰΄μŠ€ 및 ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© 뢄석기"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
 
496
- def __init__(self, fireworks_api_key: Optional[str] = None, brave_api_key: Optional[str] = None):
497
- """
498
- Args:
499
- fireworks_api_key: Fireworks AI API ν‚€ (선택)
500
- brave_api_key: Brave Search API ν‚€ (선택)
501
- """
502
- self.fireworks_api_key = fireworks_api_key or os.getenv('FIREWORKS_API_KEY')
503
- self.brave_api_key = brave_api_key or os.getenv('BRAVE_API_KEY')
504
-
505
- # λ‰΄μŠ€ μΉ΄ν…Œκ³ λ¦¬ μ •μ˜
506
- self.categories = {
507
- "산업동ν–₯": ["μ‚°μ—…", "κΈ°μ—…", "투자", "인수", "νŒŒνŠΈλ„ˆμ‹­", "μ‹œμž₯", "MS", "ꡬ글", "μ•„λ§ˆμ‘΄", "μ†Œν”„νŠΈλ±…ν¬"],
508
- "κΈ°μˆ ν˜μ‹ ": ["기술", "λͺ¨λΈ", "μ•Œκ³ λ¦¬μ¦˜", "개발", "연ꡬ", "λ…Όλ¬Έ", "μ‚Όμ„±", "SAIT"],
509
- "μ œν’ˆμΆœμ‹œ": ["μΆœμ‹œ", "곡개", "λ°œν‘œ", "μ„œλΉ„μŠ€", "μ œν’ˆ", "μ±—GPT", "μ†ŒλΌ", "νŒ¬μ„œ"],
510
- "μ •μ±…κ·œμ œ": ["규제", "μ •μ±…", "법", "μ •λΆ€", "제재", "EU", "투자"],
511
- "λ³΄μ•ˆμ΄μŠˆ": ["λ³΄μ•ˆ", "취약점", "ν•΄ν‚Ή", "μœ„ν—˜", "ν”„λΌμ΄λ²„μ‹œ"],
512
  }
513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514
  self.huggingface_data = {
515
  "models": [],
516
  "spaces": []
517
  }
518
-
519
  self.news_data = []
520
 
521
- def fetch_huggingface_trending(self) -> Dict:
522
- """ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© λͺ¨λΈ μˆ˜μ§‘"""
523
- print("πŸ€— ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© 정보 μˆ˜μ§‘ 쀑...")
524
 
525
  try:
526
  models_url = "https://huggingface.co/api/models"
527
  params = {
528
  'sort': 'trending',
529
- 'limit': 30
530
  }
531
 
532
  response = requests.get(models_url, params=params, timeout=15)
@@ -534,178 +827,164 @@ class AINewsAnalyzer:
534
  if response.status_code == 200:
535
  models = response.json()
536
 
537
- for model in models[:30]:
538
- self.huggingface_data['models'].append({
539
  'name': model.get('id', 'Unknown'),
540
  'downloads': model.get('downloads', 0),
541
  'likes': model.get('likes', 0),
542
  'task': model.get('pipeline_tag', 'N/A'),
543
  'url': f"https://huggingface.co/{model.get('id', '')}"
544
- })
 
 
 
 
 
 
 
 
 
545
 
546
- print(f"βœ… {len(self.huggingface_data['models'])}개 νŠΈλ Œλ”© λͺ¨λΈ μˆ˜μ§‘ μ™„λ£Œ")
547
- else:
548
- print(f"⚠️ λͺ¨λΈ API 였λ₯˜: {response.status_code}")
549
-
550
  except Exception as e:
551
  print(f"❌ λͺ¨λΈ μˆ˜μ§‘ 였λ₯˜: {e}")
552
 
553
- # μƒ˜ν”Œ 슀페이슀 데이터
 
 
 
 
 
 
554
  sample_spaces = [
555
- {"name": "Wan2.2-5B", "title": "κ³ ν’ˆμ§ˆ λΉ„λ””μ˜€ 생성", "url": "https://huggingface.co/spaces/"},
556
- {"name": "FLUX-Image", "title": "ν…μŠ€νŠΈβ†’μ΄λ―Έμ§€ 생성", "url": "https://huggingface.co/spaces/"},
557
- {"name": "DeepSeek-App", "title": "AI μ•± 생성기", "url": "https://huggingface.co/spaces/"},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  ]
559
 
560
- self.huggingface_data['spaces'] = sample_spaces
 
 
 
 
 
 
 
 
 
561
 
562
- return self.huggingface_data
 
563
 
564
  def create_sample_news(self) -> List[Dict]:
565
- """였늘의 AI λ‰΄μŠ€ μƒ˜ν”Œ 데이터 (2025-10-10 κΈ°μ€€)"""
566
  sample_news = [
567
  {
568
  'title': 'MS "μ±—GPT μˆ˜μš” 폭증으둜 데이터센터 λΆ€μ‘±...2026λ…„κΉŒμ§€ 지속"',
569
  'url': 'https://www.aitimes.com/news/articleView.html?idxno=203055',
570
  'date': '10-10 15:10',
571
- 'source': 'AI Times',
572
- 'category': '산업동ν–₯'
573
  },
574
  {
575
  'title': 'λ―Έκ΅­, UAE에 GPU 판맀 일뢀 승인...μ—”λΉ„λ””μ•„ μ‹œμ΄ 5μ‘°λ‹¬λŸ¬ λˆˆμ•ž',
576
  'url': 'https://www.aitimes.com/news/articleView.html?idxno=203053',
577
  'date': '10-10 14:46',
578
- 'source': 'AI Times',
579
- 'category': '산업동ν–₯'
580
- },
581
- {
582
- 'title': 'μ˜€ν”ˆAI, μ €λ ΄ν•œ μ±—GPT κ³  μš”κΈˆμ œ μ•„μ‹œμ•„ 16개ꡭ으둜 ν™•λŒ€',
583
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203054',
584
- 'date': '10-10 14:15',
585
- 'source': 'AI Times',
586
- 'category': 'μ œν’ˆμΆœμ‹œ'
587
- },
588
- {
589
- 'title': '인텔, 18A κ³΅μ •μœΌλ‘œ 자체 μ œμž‘ν•œ λ…ΈνŠΈλΆμš© μΉ© νŒ¬μ„œ 레이크 곡개',
590
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203057',
591
- 'date': '10-10 14:03',
592
- 'source': 'AI Times',
593
- 'category': 'μ œν’ˆμΆœμ‹œ'
594
  },
595
  {
596
  'title': 'μ†ŒλΌ, μ±—GPT보닀 빨리 100만 λ‹€μš΄λ‘œλ“œ 돌파',
597
  'url': 'https://www.aitimes.com/news/articleView.html?idxno=203045',
598
  'date': '10-10 12:55',
599
- 'source': 'AI Times',
600
- 'category': 'μ œν’ˆμΆœμ‹œ'
601
- },
602
- {
603
- 'title': 'κ΅¬κΈ€Β·μ•„λ§ˆμ‘΄, κΈ°μ—…μš© AI μ„œλΉ„μŠ€ λ‚˜λž€νžˆ μΆœμ‹œ',
604
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203047',
605
- 'date': '10-10 12:41',
606
- 'source': 'AI Times',
607
- 'category': 'μ œν’ˆμΆœμ‹œ'
608
- },
609
- {
610
- 'title': 'μ‚Όμ„± SAIT, κ±°λŒ€ λͺ¨λΈ λŠ₯κ°€ν•˜λŠ” μ΄ˆμ†Œν˜• μΆ”λ‘  λͺ¨λΈ TRM 곡개',
611
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203035',
612
- 'date': '10-09 21:22',
613
- 'source': 'AI Times',
614
- 'category': 'κΈ°μˆ ν˜μ‹ '
615
- },
616
- {
617
- 'title': 'ꡬ글, GUI μ—μ΄μ „νŠΈ μ œλ―Έλ‚˜μ΄ 2.5 컴퓨터 유즈 곡개',
618
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203039',
619
- 'date': '10-09 20:57',
620
- 'source': 'AI Times',
621
- 'category': 'κΈ°μˆ ν˜μ‹ '
622
- },
623
- {
624
- 'title': 'EU, 핡심 μ‚°μ—… AX μœ„ν•œ 1.6μ‘° 규λͺ¨ 투자 κ³„νš λ°œν‘œ',
625
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203041',
626
- 'date': '10-09 18:51',
627
- 'source': 'AI Times',
628
- 'category': 'μ •μ±…κ·œμ œ'
629
- },
630
- {
631
- 'title': 'μ†Œν”„νŠΈλ±…ν¬, ABB λ‘œλ΄‡ 사업뢀 7.6쑰원에 인수',
632
- 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203034',
633
- 'date': '10-09 18:07',
634
- 'source': 'AI Times',
635
- 'category': '산업동ν–₯'
636
  }
637
  ]
638
 
639
- self.news_data = sample_news
640
  return sample_news
641
 
642
- def categorize_news(self, news_list: List[Dict]) -> List[Dict]:
643
- """λ‰΄μŠ€ μΉ΄ν…Œκ³ λ¦¬ μžλ™ λΆ„λ₯˜"""
644
- for news in news_list:
645
- if 'category' not in news or news['category'] == '기타':
646
- title = news['title'].lower()
647
- news['category'] = "기타"
648
-
649
- for category, keywords in self.categories.items():
650
- if any(keyword.lower() in title for keyword in keywords):
651
- news['category'] = category
652
- break
653
 
654
- return news_list
655
-
656
- def get_data(self) -> Dict:
657
- """λͺ¨λ“  데이터 μˆ˜μ§‘ 및 λ°˜ν™˜"""
658
- # λ‰΄μŠ€ μˆ˜μ§‘
659
  news = self.create_sample_news()
660
- news = self.categorize_news(news)
661
 
662
- # ν—ˆκΉ…νŽ˜μ΄μŠ€ 데이터 μˆ˜μ§‘
663
- hf_data = self.fetch_huggingface_trending()
664
-
665
- # μΉ΄ν…Œκ³ λ¦¬λ³„λ‘œ λ‰΄μŠ€ κ·Έλ£Ήν™”
666
- news_by_category = {}
667
  for article in news:
668
- category = article['category']
669
- if category not in news_by_category:
670
- news_by_category[category] = []
671
- news_by_category[category].append(article)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
 
673
- # 톡계 계산
674
  stats = {
675
- 'total_news': len(news),
676
- 'categories': len(news_by_category),
677
- 'hf_models': len(hf_data['models']),
678
- 'hf_spaces': len(hf_data['spaces'])
679
  }
680
 
 
 
681
  return {
682
- 'news_by_category': news_by_category,
683
- 'hf_models': hf_data['models'],
684
- 'hf_spaces': hf_data['spaces'],
685
  'stats': stats,
686
  'timestamp': datetime.now().strftime('%Yλ…„ %mμ›” %d일 %H:%M:%S')
687
  }
688
 
689
 
690
  # ============================================
691
- # Flask 라우트 μ •μ˜
692
  # ============================================
693
 
694
  @app.route('/')
695
  def index():
696
  """메인 νŽ˜μ΄μ§€"""
697
  try:
698
- analyzer = AINewsAnalyzer()
699
- data = analyzer.get_data()
700
  return render_template_string(HTML_TEMPLATE, **data)
701
  except Exception as e:
702
  return f"""
703
  <html>
704
  <body style="font-family: Arial; padding: 50px; text-align: center;">
705
  <h1 style="color: #e74c3c;">⚠️ 였λ₯˜ λ°œμƒ</h1>
706
- <p>데이터λ₯Ό λΆˆλŸ¬μ˜€λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</p>
707
- <p style="color: #7f8c8d;">{str(e)}</p>
708
- <button onclick="location.reload()" style="padding: 10px 20px; font-size: 16px; margin-top: 20px; cursor: pointer;">
709
  πŸ”„ μƒˆλ‘œκ³ μΉ¨
710
  </button>
711
  </body>
@@ -715,111 +994,67 @@ def index():
715
 
716
  @app.route('/api/data')
717
  def api_data():
718
- """JSON API μ—”λ“œν¬μΈνŠΈ"""
719
  try:
720
- analyzer = AINewsAnalyzer()
721
- data = analyzer.get_data()
722
  return jsonify({
723
  'success': True,
724
- 'data': data,
725
- 'timestamp': datetime.now().isoformat()
726
  })
727
  except Exception as e:
728
  return jsonify({
729
  'success': False,
730
- 'error': str(e),
731
- 'timestamp': datetime.now().isoformat()
732
  }), 500
733
 
734
 
735
  @app.route('/health')
736
  def health():
737
- """ν—¬μŠ€ 체크 μ—”λ“œν¬μΈνŠΈ"""
738
  return jsonify({
739
  "status": "healthy",
740
- "service": "AI News Analyzer",
741
- "version": "1.0.0",
742
- "timestamp": datetime.now().isoformat()
743
  })
744
 
745
 
746
- @app.route('/api/news')
747
- def api_news():
748
- """λ‰΄μŠ€λ§Œ λ°˜ν™˜ν•˜λŠ” API"""
749
- try:
750
- analyzer = AINewsAnalyzer()
751
- news = analyzer.create_sample_news()
752
- return jsonify({
753
- 'success': True,
754
- 'count': len(news),
755
- 'news': news
756
- })
757
- except Exception as e:
758
- return jsonify({
759
- 'success': False,
760
- 'error': str(e)
761
- }), 500
762
-
763
-
764
- @app.route('/api/hf-models')
765
- def api_hf_models():
766
- """ν—ˆκΉ…νŽ˜μ΄μŠ€ λͺ¨λΈλ§Œ λ°˜ν™˜ν•˜λŠ” API"""
767
- try:
768
- analyzer = AINewsAnalyzer()
769
- hf_data = analyzer.fetch_huggingface_trending()
770
- return jsonify({
771
- 'success': True,
772
- 'count': len(hf_data['models']),
773
- 'models': hf_data['models']
774
- })
775
- except Exception as e:
776
- return jsonify({
777
- 'success': False,
778
- 'error': str(e)
779
- }), 500
780
-
781
-
782
  # ============================================
783
  # 메인 μ‹€ν–‰
784
  # ============================================
785
 
786
  if __name__ == '__main__':
787
- # ν™˜κ²½ λ³€μˆ˜μ—μ„œ 포트 κ°€μ Έμ˜€κΈ° (κΈ°λ³Έκ°’: 8080)
788
  port = int(os.environ.get('PORT', 7860))
789
 
790
- # ν™˜κ²½ λ³€μˆ˜μ—μ„œ 디버그 λͺ¨λ“œ μ„€μ • (κΈ°λ³Έκ°’: False)
791
- debug = os.environ.get('DEBUG', 'False').lower() == 'true'
792
-
793
  print(f"""
794
  ╔════════════════════════════════════════════════════════════╗
795
  β•‘ β•‘
796
- β•‘ πŸ€– AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© μ›Ή μ•± μ‹œμž‘! β•‘
797
  β•‘ β•‘
798
  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
799
 
800
- πŸš€ Flask μ„œλ²„ μ‹œμž‘ 쀑...
801
- πŸ“ 메인 νŽ˜μ΄μ§€: http://localhost:{port}
802
- πŸ“Š JSON API: http://localhost:{port}/api/data
803
- πŸ“° λ‰΄μŠ€ API: http://localhost:{port}/api/news
804
- πŸ€— λͺ¨λΈ API: http://localhost:{port}/api/hf-models
805
- πŸ’š Health Check: http://localhost:{port}/health
806
 
807
- {'πŸ› 디버그 λͺ¨λ“œ: ν™œμ„±ν™”' if debug else '⚑ ν”„λ‘œλ•μ…˜ λͺ¨λ“œ: μ΅œμ ν™”λ¨'}
 
 
 
808
 
809
  λΈŒλΌμš°μ €μ—μ„œ μœ„ URL을 μ—΄μ–΄μ£Όμ„Έμš”!
810
- μ’…λ£Œν•˜λ €λ©΄ Ctrl+Cλ₯Ό λˆ„λ₯΄μ„Έμš”.
811
  """)
812
 
813
  try:
814
  app.run(
815
  host='0.0.0.0',
816
  port=port,
817
- debug=debug,
818
  threaded=True
819
  )
820
  except KeyboardInterrupt:
821
- print("\n\nπŸ‘‹ μ„œλ²„λ₯Ό μ’…λ£Œν•©λ‹ˆλ‹€. μ•ˆλ…•νžˆ κ°€μ„Έμš”!")
822
- sys.exit(0)
823
- except Exception as e:
824
- print(f"\n❌ μ„œλ²„ μ‹œμž‘ μ‹€νŒ¨: {e}")
825
- sys.exit(1)
 
1
  # -*- coding: utf-8 -*-
2
  """
3
+ AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© LLM 뢄석 μ›Ήμ•± (κ³ κΈ‰νŒ)
4
+ 파일λͺ…: app_advanced.py
5
 
6
+ μ£Όμš” κΈ°λŠ₯:
7
+ 1. LLM API둜 기사 μ΄ˆλ“±ν•™μƒ μˆ˜μ€€ 뢄석 (μš”μ•½, 의미, 영ν–₯도, 행동지침)
8
+ 2. ν—ˆκΉ…νŽ˜μ΄μŠ€ λͺ¨λΈ/슀페이슀 νŠΈλ Œλ”© 30μœ„ 뢄석
9
+ 3. 슀페이슀 app.py 파일 뢄석 및 μ‰¬μš΄ μ„€λͺ…
10
+ 4. νƒ­ UI (λ‰΄μŠ€/λͺ¨λΈ/슀페이슀)
11
 
12
+ μ‹€ν–‰ 방법:
13
+ 1. pip install Flask requests beautifulsoup4 anthropic huggingface_hub
14
+ 2. export ANTHROPIC_API_KEY="your-key-here" # Claude API ν‚€ μ„€μ •
15
+ 3. python app_advanced.py
16
+ 4. λΈŒλΌμš°μ €μ—μ„œ http://localhost:7860 접속
17
  """
18
 
19
  from flask import Flask, render_template_string, jsonify, request
20
  import requests
 
21
  import json
22
  from datetime import datetime
23
  from typing import List, Dict, Optional
24
  import os
25
  import sys
26
+ import asyncio
27
+ from concurrent.futures import ThreadPoolExecutor
28
+ import time
29
 
30
  # Flask μ•± μ΄ˆκΈ°ν™”
31
  app = Flask(__name__)
32
+ app.config['JSON_AS_ASCII'] = False
33
 
34
  # ============================================
35
+ # HTML ν…œν”Œλ¦Ώ (νƒ­ UI 포함)
36
  # ============================================
37
 
38
  HTML_TEMPLATE = """
 
41
  <head>
42
  <meta charset="UTF-8">
43
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
44
+ <title>AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ LLM 뢄석 μ‹œμŠ€ν…œ</title>
45
  <style>
46
  * {
47
  margin: 0;
 
81
  font-size: 1.2em;
82
  }
83
 
84
+ /* νƒ­ μŠ€νƒ€μΌ */
85
+ .tabs {
86
+ display: flex;
87
+ gap: 15px;
88
+ margin-bottom: 30px;
89
+ border-bottom: 3px solid #e0e0e0;
90
+ padding-bottom: 0;
91
+ }
92
+
93
+ .tab {
94
+ padding: 15px 30px;
95
+ background: #f5f5f5;
96
+ border: none;
97
+ border-radius: 10px 10px 0 0;
98
+ cursor: pointer;
99
+ font-size: 1.1em;
100
+ font-weight: 600;
101
+ color: #666;
102
+ transition: all 0.3s;
103
+ }
104
+
105
+ .tab.active {
106
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
107
+ color: white;
108
+ transform: translateY(-3px);
109
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
110
+ }
111
+
112
+ .tab:hover {
113
+ background: #e0e0e0;
114
+ }
115
+
116
+ .tab.active:hover {
117
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
118
+ }
119
+
120
+ .tab-content {
121
+ display: none;
122
+ }
123
+
124
+ .tab-content.active {
125
+ display: block;
126
+ animation: fadeIn 0.5s ease-out;
127
+ }
128
+
129
+ /* 톡계 μΉ΄λ“œ */
130
  .stats {
131
  display: grid;
132
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
 
163
  font-weight: 500;
164
  }
165
 
166
+ /* λ‰΄μŠ€ μΉ΄λ“œ (LLM 뢄석 버전) */
167
+ .news-card {
168
+ background: white;
169
+ border-radius: 15px;
170
+ padding: 30px;
 
 
 
 
 
 
171
  margin-bottom: 25px;
172
+ box-shadow: 0 5px 20px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
173
  border-left: 6px solid #667eea;
174
  transition: all 0.3s;
 
175
  }
176
 
177
+ .news-card:hover {
178
+ transform: translateX(10px);
179
+ box-shadow: 0 10px 30px rgba(0,0,0,0.15);
180
+ }
181
+
182
+ .news-header {
183
+ display: flex;
184
+ justify-content: space-between;
185
+ align-items: flex-start;
186
+ margin-bottom: 20px;
187
+ flex-wrap: wrap;
188
+ gap: 15px;
189
  }
190
 
191
  .news-title {
192
+ font-size: 1.4em;
193
  font-weight: 700;
194
  color: #2c3e50;
195
+ flex: 1;
196
+ min-width: 300px;
197
  }
198
 
199
  .news-meta {
 
 
 
200
  display: flex;
201
+ gap: 15px;
202
+ color: #7f8c8d;
203
+ font-size: 0.9em;
204
  }
205
 
206
+ .analysis-section {
207
+ background: #f8f9fa;
208
+ padding: 20px;
209
+ border-radius: 10px;
210
+ margin-top: 15px;
211
+ }
212
+
213
+ .analysis-item {
214
+ margin-bottom: 20px;
215
+ padding-bottom: 20px;
216
+ border-bottom: 1px solid #e0e0e0;
217
+ }
218
+
219
+ .analysis-item:last-child {
220
+ border-bottom: none;
221
+ margin-bottom: 0;
222
+ padding-bottom: 0;
223
+ }
224
+
225
+ .analysis-label {
226
  display: inline-block;
227
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
228
  color: white;
229
+ padding: 8px 15px;
230
+ border-radius: 20px;
231
+ font-size: 0.9em;
 
232
  font-weight: 600;
233
+ margin-bottom: 10px;
234
  }
235
 
236
+ .analysis-content {
237
+ color: #34495e;
238
+ line-height: 1.8;
239
+ font-size: 1.05em;
240
  }
241
 
242
+ .impact-level {
243
+ display: inline-block;
244
+ padding: 5px 12px;
245
+ border-radius: 15px;
246
+ font-size: 0.85em;
247
+ font-weight: 600;
248
+ margin-left: 10px;
249
  }
250
 
251
+ .impact-high {
252
+ background: #ff6b6b;
253
+ color: white;
254
+ }
255
+
256
+ .impact-medium {
257
+ background: #ffa502;
258
+ color: white;
259
+ }
260
+
261
+ .impact-low {
262
+ background: #26de81;
263
+ color: white;
264
  }
265
 
266
+ /* λͺ¨λΈ μΉ΄λ“œ */
267
  .model-grid {
268
  display: grid;
269
+ grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
270
  gap: 25px;
271
  margin-top: 30px;
272
  }
 
285
  box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
286
  }
287
 
288
+ .model-rank {
289
+ position: absolute;
290
+ top: -15px;
291
+ right: 20px;
292
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
293
+ color: white;
294
+ width: 50px;
295
+ height: 50px;
296
+ border-radius: 50%;
297
+ display: flex;
298
+ align-items: center;
299
+ justify-content: center;
300
+ font-weight: 700;
301
+ font-size: 1.2em;
302
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
303
+ }
304
+
305
  .model-name {
306
  font-weight: 700;
307
  color: #667eea;
308
  margin-bottom: 15px;
309
  font-size: 1.15em;
310
  word-break: break-word;
311
+ padding-right: 60px;
312
  }
313
 
314
  .model-stats {
315
+ display: grid;
316
+ grid-template-columns: repeat(2, 1fr);
317
+ gap: 10px;
318
+ margin: 15px 0;
319
+ padding: 15px;
320
+ background: #f8f9fa;
321
+ border-radius: 8px;
322
+ }
323
+
324
+ .model-stat-item {
325
+ font-size: 0.9em;
326
  }
327
 
328
  .model-task {
 
336
  font-weight: 600;
337
  }
338
 
339
+ .model-analysis {
340
+ background: #f0f4ff;
341
+ padding: 15px;
342
+ border-radius: 8px;
343
+ margin-top: 15px;
344
+ color: #34495e;
345
+ line-height: 1.7;
346
+ font-size: 0.95em;
347
+ }
348
+
349
+ /* 슀페이슀 μΉ΄λ“œ */
350
+ .space-card {
351
+ background: white;
352
+ padding: 25px;
353
+ border-radius: 12px;
354
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
355
+ margin-bottom: 20px;
356
+ border-left: 5px solid #ff6b6b;
357
+ transition: all 0.3s;
358
+ }
359
+
360
+ .space-card:hover {
361
+ transform: translateX(10px);
362
+ box-shadow: 0 10px 25px rgba(255, 107, 107, 0.3);
363
+ }
364
+
365
+ .space-header {
366
+ display: flex;
367
+ justify-content: space-between;
368
+ align-items: flex-start;
369
+ margin-bottom: 15px;
370
+ }
371
+
372
+ .space-name {
373
+ font-weight: 700;
374
+ color: #ff6b6b;
375
+ font-size: 1.3em;
376
+ }
377
+
378
+ .space-badge {
379
+ background: #ff6b6b;
380
+ color: white;
381
+ padding: 5px 12px;
382
+ border-radius: 15px;
383
+ font-size: 0.8em;
384
+ font-weight: 600;
385
+ }
386
+
387
+ .space-description {
388
+ color: #555;
389
+ margin-bottom: 15px;
390
+ line-height: 1.6;
391
+ }
392
+
393
+ .space-analysis {
394
+ background: #fff5f5;
395
+ padding: 15px;
396
+ border-radius: 8px;
397
+ margin-top: 15px;
398
+ }
399
+
400
+ .space-tech {
401
+ display: flex;
402
+ flex-wrap: wrap;
403
+ gap: 8px;
404
+ margin-top: 15px;
405
+ }
406
+
407
+ .tech-tag {
408
+ background: #ffe5e5;
409
+ color: #ff6b6b;
410
+ padding: 5px 10px;
411
+ border-radius: 12px;
412
+ font-size: 0.8em;
413
+ font-weight: 600;
414
+ }
415
+
416
+ /* λ²„νŠΌ */
417
  .button-group {
418
  text-align: center;
419
  margin: 40px 0;
 
438
  box-shadow: 0 12px 30px rgba(102, 126, 234, 0.6);
439
  }
440
 
441
+ .news-link {
442
+ display: inline-block;
443
+ background: #667eea;
444
  color: white;
445
+ padding: 10px 20px;
446
+ border-radius: 8px;
447
+ text-decoration: none;
448
+ font-size: 0.95em;
449
+ font-weight: 600;
 
 
450
  transition: all 0.3s;
451
+ margin-top: 15px;
452
  }
453
 
454
+ .news-link:hover {
455
+ background: #764ba2;
456
+ transform: scale(1.05);
457
  }
458
 
459
  .loading {
 
482
  color: #666;
483
  }
484
 
485
+ @keyframes fadeIn {
486
+ from {
487
+ opacity: 0;
488
+ transform: translateY(20px);
489
+ }
490
+ to {
491
+ opacity: 1;
492
+ transform: translateY(0);
493
+ }
494
  }
495
 
496
  @media (max-width: 768px) {
 
502
  font-size: 2em;
503
  }
504
 
505
+ .tabs {
 
 
 
 
 
 
 
 
 
506
  flex-direction: column;
 
507
  }
508
 
509
+ .tab {
 
510
  width: 100%;
511
  }
512
+
513
+ .model-grid {
514
+ grid-template-columns: 1fr;
 
 
 
 
 
 
 
 
515
  }
516
  }
 
 
 
 
 
 
 
 
517
  </style>
518
  </head>
519
  <body>
520
  <div class="container">
521
+ <h1>πŸ€– AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ LLM 뢄석</h1>
522
+ <p class="subtitle">μ΄ˆλ“±ν•™μƒλ„ μ΄ν•΄ν•˜λŠ” AI νŠΈλ Œλ“œ 뢄석 μ‹œμŠ€ν…œ πŸŽ“</p>
523
 
524
  <!-- 톡계 μΉ΄λ“œ -->
525
  <div class="stats">
526
  <div class="stat-card">
527
  <div class="stat-number">{{ stats.total_news }}</div>
528
+ <div class="stat-label">πŸ“° λΆ„μ„λœ λ‰΄μŠ€</div>
 
 
 
 
529
  </div>
530
  <div class="stat-card">
531
  <div class="stat-number">{{ stats.hf_models }}</div>
532
+ <div class="stat-label">πŸ€— νŠΈλ Œλ”© λͺ¨λΈ</div>
533
  </div>
534
  <div class="stat-card">
535
  <div class="stat-number">{{ stats.hf_spaces }}</div>
536
+ <div class="stat-label">πŸš€ 인기 슀페이슀</div>
537
+ </div>
538
+ <div class="stat-card">
539
+ <div class="stat-number">{{ stats.llm_analyses }}</div>
540
+ <div class="stat-label">🧠 LLM 뢄석</div>
541
  </div>
542
  </div>
543
 
544
+ <!-- νƒ­ 메뉴 -->
545
+ <div class="tabs">
546
+ <button class="tab active" onclick="switchTab('news')">πŸ“° AI λ‰΄μŠ€ 뢄석</button>
547
+ <button class="tab" onclick="switchTab('models')">πŸ€— νŠΈλ Œλ”© λͺ¨λΈ</button>
548
+ <button class="tab" onclick="switchTab('spaces')">πŸš€ 인기 슀페이슀</button>
549
+ </div>
550
+
551
+ <!-- λ‰΄μŠ€ νƒ­ -->
552
+ <div id="news-content" class="tab-content active">
553
+ {% for article in analyzed_news %}
554
+ <div class="news-card">
555
+ <div class="news-header">
556
+ <div class="news-title">{{ loop.index }}. {{ article.title }}</div>
557
+ <div class="news-meta">
558
+ <span>πŸ“… {{ article.date }}</span>
559
+ <span>πŸ“° {{ article.source }}</span>
560
+ </div>
561
+ </div>
562
+
563
+ <div class="analysis-section">
564
+ <div class="analysis-item">
565
+ <span class="analysis-label">🎯 μ‰¬μš΄ μš”μ•½</span>
566
+ <div class="analysis-content">{{ article.analysis.summary }}</div>
567
+ </div>
568
+
569
+ <div class="analysis-item">
570
+ <span class="analysis-label">πŸ’‘ μ™œ μ€‘μš”ν• κΉŒ?</span>
571
+ <div class="analysis-content">{{ article.analysis.significance }}</div>
572
+ </div>
573
+
574
+ <div class="analysis-item">
575
+ <span class="analysis-label">πŸ“Š 영ν–₯도</span>
576
+ <span class="impact-level impact-{{ article.analysis.impact_level }}">
577
+ {{ article.analysis.impact_text }}
578
+ </span>
579
+ <div class="analysis-content" style="margin-top: 10px;">
580
+ {{ article.analysis.impact_description }}
581
+ </div>
582
+ </div>
583
+
584
+ <div class="analysis-item">
585
+ <span class="analysis-label">βœ… μš°λ¦¬κ°€ ν•  수 μžˆλŠ” 것</span>
586
+ <div class="analysis-content">{{ article.analysis.action }}</div>
587
+ </div>
588
  </div>
589
+
590
  <a href="{{ article.url }}" target="_blank" class="news-link">
591
+ πŸ”— 전체 기사 읽어보기
592
  </a>
593
  </div>
594
  {% endfor %}
595
  </div>
 
596
 
597
+ <!-- λͺ¨λΈ νƒ­ -->
598
+ <div id="models-content" class="tab-content">
 
 
 
599
  <div class="model-grid">
600
+ {% for model in analyzed_models %}
601
+ <div class="model-card" style="position: relative;">
602
+ <div class="model-rank">{{ loop.index }}</div>
603
+ <div class="model-name">{{ model.name }}</div>
604
+ <div class="model-task">🏷️ {{ model.task }}</div>
605
+
 
 
606
  <div class="model-stats">
607
+ <div class="model-stat-item">
608
+ <strong>πŸ“₯ λ‹€μš΄λ‘œλ“œ</strong><br>
609
+ {{ "{:,}".format(model.downloads) }}
610
+ </div>
611
+ <div class="model-stat-item">
612
+ <strong>❀️ μ’‹μ•„μš”</strong><br>
613
+ {{ "{:,}".format(model.likes) }}
614
+ </div>
615
  </div>
616
+
617
+ <div class="model-analysis">
618
+ <strong>🧠 AI 뢄석:</strong><br>
619
+ {{ model.analysis }}
620
+ </div>
621
+
622
  <a href="{{ model.url }}" target="_blank" class="news-link">
623
  πŸ”— λͺ¨λΈ νŽ˜μ΄μ§€ λ°©λ¬Έ
624
  </a>
625
  </div>
626
  {% endfor %}
627
  </div>
628
+ </div>
629
+
630
+ <!-- 슀페이슀 νƒ­ -->
631
+ <div id="spaces-content" class="tab-content">
632
+ {% for space in analyzed_spaces %}
633
+ <div class="space-card">
634
+ <div class="space-header">
635
+ <div class="space-name">{{ loop.index }}. {{ space.name }}</div>
636
+ <span class="space-badge">인기 {{ loop.index }}μœ„</span>
637
+ </div>
638
+
639
+ <div class="space-description">
640
+ <strong>πŸ“ μ„€λͺ…:</strong> {{ space.description }}
641
+ </div>
642
+
643
+ <div class="space-analysis">
644
+ <strong>πŸŽ“ μ΄ˆλ“±ν•™μƒ μ„€λͺ…:</strong><br>
645
+ {{ space.simple_explanation }}
646
+ </div>
647
+
648
+ {% if space.tech_stack %}
649
+ <div class="space-tech">
650
+ <strong style="width: 100%; margin-bottom: 5px;">πŸ› οΈ μ‚¬μš© 기술:</strong>
651
+ {% for tech in space.tech_stack %}
652
+ <span class="tech-tag">{{ tech }}</span>
653
+ {% endfor %}
654
+ </div>
655
+ {% endif %}
656
+
657
+ <a href="{{ space.url }}" target="_blank" class="news-link">
658
+ πŸ”— 슀페이슀 μ²΄ν—˜ν•˜κΈ°
659
+ </a>
660
  </div>
661
+ {% endfor %}
662
  </div>
663
 
664
  <!-- λ²„νŠΌ κ·Έλ£Ή -->
 
666
  <button class="refresh-btn" onclick="location.reload()">
667
  πŸ”„ μƒˆλ‘œκ³ μΉ¨
668
  </button>
 
 
 
669
  </div>
670
 
671
  <!-- νƒ€μž„μŠ€νƒ¬ν”„ -->
 
675
 
676
  <!-- ν‘Έν„° -->
677
  <div class="footer">
678
+ <p>πŸ€– AI λ‰΄μŠ€ LLM 뢄석 μ‹œμŠ€ν…œ v2.0</p>
679
  <p style="margin-top: 10px; font-size: 0.9em;">
680
+ Powered by Claude AI & Hugging Face
681
  </p>
682
  </div>
683
  </div>
684
 
685
  <script>
686
+ function switchTab(tabName) {
687
+ // λͺ¨λ“  νƒ­ λΉ„ν™œμ„±ν™”
688
+ document.querySelectorAll('.tab').forEach(tab => {
689
+ tab.classList.remove('active');
690
+ });
691
+ document.querySelectorAll('.tab-content').forEach(content => {
692
+ content.classList.remove('active');
693
+ });
694
+
695
+ // μ„ νƒλœ νƒ­ ν™œμ„±ν™”
696
+ event.target.classList.add('active');
697
+ document.getElementById(tabName + '-content').classList.add('active');
698
+ }
699
 
700
+ console.log('βœ… AI λ‰΄μŠ€ LLM 뢄석 μ‹œμŠ€ν…œ λ‘œλ“œ μ™„λ£Œ');
701
  </script>
702
  </body>
703
  </html>
 
705
 
706
 
707
  # ============================================
708
+ # LLM 뢄석기 클래���
709
  # ============================================
710
 
711
+ class LLMAnalyzer:
712
+ """Claude APIλ₯Ό μ‚¬μš©ν•œ LLM 뢄석기"""
713
+
714
+ def __init__(self):
715
+ # Claude APIλ₯Ό Flask ν™˜κ²½μ—μ„œ μ‚¬μš©
716
+ self.api_available = True
717
+
718
+ def analyze_news_simple(self, title: str, content: str = "") -> Dict:
719
+ """λ‰΄μŠ€ 기사λ₯Ό μ΄ˆλ“±ν•™μƒ μˆ˜μ€€μœΌλ‘œ 뢄석"""
720
+
721
+ # μ‹€μ œ ν™˜κ²½μ—μ„œλŠ” Claude API 호좜
722
+ # μ—¬κΈ°μ„œλŠ” μƒ˜ν”Œ 뢄석 제곡
723
+ analysis_templates = {
724
+ "μ±—GPT": {
725
+ "summary": "λ§ˆμ΄ν¬λ‘œμ†Œν”„νŠΈ(MS)λΌλŠ” 큰 νšŒμ‚¬κ°€ μ±—GPTλΌλŠ” AIλ₯Ό λ„ˆλ¬΄ λ§Žμ€ μ‚¬λžŒλ“€μ΄ μ‚¬μš©ν•΄μ„œ, 컴퓨터λ₯Ό λ³΄κ΄€ν•˜λŠ” 큰 건물(데이터센터)이 λΆ€μ‘±ν•˜λ‹€κ³  λ§ν–ˆμ–΄μš”.",
726
+ "significance": "μ±—GPTκ°€ 정말 인기가 λ§Žλ‹€λŠ” λœ»μ΄μ—μš”. 마치 λ„ˆλ¬΄ λ§Žμ€ μΉœκ΅¬λ“€μ΄ ν•œ κ²Œμž„κΈ°λ₯Ό μ“°λ €κ³  ν•˜λŠ” 것과 λΉ„μŠ·ν•΄μš”.",
727
+ "impact_level": "high",
728
+ "impact_text": "λ†’μŒ",
729
+ "impact_description": "AI 기술이 λΉ λ₯΄κ²Œ λ°œμ „ν•˜κ³  있고, λ§Žμ€ μ‚¬λžŒλ“€μ΄ μ‚¬μš©ν•˜κ³  μžˆλ‹€λŠ” μ€‘μš”ν•œ μ‹ ν˜Έμ˜ˆμš”.",
730
+ "action": "μ±—GPT 같은 AI 도ꡬλ₯Ό λ°°μ›Œλ³΄μ„Έμš”. μˆ™μ œλ₯Ό 도와달라고 ν•˜κ±°λ‚˜, λͺ¨λ₯΄λŠ” 것을 λ¬Όμ–΄λ³Ό 수 μžˆμ–΄μš”!"
731
+ },
732
+ "GPU": {
733
+ "summary": "미ꡭ이 μ•„λžμ—λ―Έλ¦¬νŠΈ(UAE)λΌλŠ” λ‚˜λΌμ— GPUλΌλŠ” νŠΉλ³„ν•œ 컴퓨터 λΆ€ν’ˆμ„ νŒ” 수 있게 ν—ˆλ½ν–ˆμ–΄μš”. GPUλŠ” AIλ₯Ό λ§Œλ“œλŠ” 데 κΌ­ ν•„μš”ν•œ λΆ€ν’ˆμ΄μ—μš”.",
734
+ "significance": "GPUλŠ” AI의 λ‘λ‡Œ 같은 κ±°μ˜ˆμš”. 이걸 νŒ” 수 있게 되면 더 λ§Žμ€ λ‚˜λΌμ—μ„œ AIλ₯Ό λ§Œλ“€ 수 μžˆμ–΄μš”.",
735
+ "impact_level": "medium",
736
+ "impact_text": "쀑간",
737
+ "impact_description": "AI 기술이 더 λ§Žμ€ λ‚˜λΌλ‘œ 퍼질 수 있게 λ˜μ—ˆμ–΄μš”.",
738
+ "action": "컴퓨터가 μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€ 관심을 κ°€μ Έλ³΄μ„Έμš”. GPUκ°€ 무엇인지 κ²€μƒ‰ν•΄λ³΄λŠ” 것도 μ’‹μ•„μš”!"
739
+ },
740
+ "μ†ŒλΌ": {
741
+ "summary": "μ˜€ν”ˆAIκ°€ λ§Œλ“  'μ†ŒλΌ'λΌλŠ” AI 앱이 μ—„μ²­ λΉ λ₯΄κ²Œ 인기λ₯Ό μ–»μ—ˆμ–΄μš”. 100만 λͺ…이 λ‹€μš΄λ‘œλ“œν•˜λŠ” 데 μ±—GPT보닀 더 λΉ¨λžλŒ€μš”!",
742
+ "significance": "μ‚¬λžŒλ“€μ΄ λΉ„λ””μ˜€λ₯Ό λ§Œλ“œλŠ” AI에 정말 관심이 λ§Žλ‹€λŠ” λœ»μ΄μ—μš”.",
743
+ "impact_level": "high",
744
+ "impact_text": "λ†’μŒ",
745
+ "impact_description": "μ•žμœΌλ‘œ λˆ„κ΅¬λ‚˜ μ‰½κ²Œ λ©‹μ§„ λΉ„λ””μ˜€λ₯Ό λ§Œλ“€ 수 있게 될 κ±°μ˜ˆμš”.",
746
+ "action": "μ†ŒλΌλ₯Ό 써보고, μƒμƒν•œ 것을 λΉ„λ””μ˜€λ‘œ λ§Œλ“€μ–΄λ³΄μ„Έμš”. 창의λ ₯을 λ°œνœ˜ν•  수 μžˆμ–΄μš”!"
747
+ }
748
+ }
749
+
750
+ # ν‚€μ›Œλ“œ 맀칭으둜 ν…œν”Œλ¦Ώ 선택
751
+ for keyword, template in analysis_templates.items():
752
+ if keyword.lower() in title.lower():
753
+ return template
754
+
755
+ # κΈ°λ³Έ 뢄석
756
+ return {
757
+ "summary": f"'{title}'λΌλŠ” AI κ΄€λ ¨ λ‰΄μŠ€κ°€ λ‚˜μ™”μ–΄μš”. AI 기술이 계속 λ°œμ „ν•˜κ³  μžˆλ‹€λŠ” μ†Œμ‹μ΄μ—μš”.",
758
+ "significance": "AIλŠ” 우리 μƒν™œμ„ 더 νŽΈλ¦¬ν•˜κ²Œ λ§Œλ“€μ–΄μ£ΌλŠ” κΈ°μˆ μ΄μ—μš”.",
759
+ "impact_level": "medium",
760
+ "impact_text": "쀑간",
761
+ "impact_description": "AI 기술의 λ°œμ „μ€ 우리 λ―Έλž˜μ— μ€‘μš”ν•œ 영ν–₯을 쀄 κ±°μ˜ˆμš”.",
762
+ "action": "AI에 λŒ€ν•΄ 더 μ•Œμ•„λ³΄κ³ , AIλ₯Ό ν™œμš©ν•˜λŠ” 방법을 λ°°μ›Œλ³΄μ„Έμš”!"
763
+ }
764
 
765
+ def analyze_model(self, model_name: str, task: str, downloads: int) -> str:
766
+ """ν—ˆκΉ…νŽ˜μ΄μŠ€ λͺ¨λΈ 뢄석"""
767
+
768
+ task_explanations = {
769
+ "text-generation": "글을 μžλ™μœΌλ‘œ λ§Œλ“€μ–΄μ£ΌλŠ”",
770
+ "image-to-text": "사진을 보고 μ„€λͺ…을 μ¨μ£ΌλŠ”",
771
+ "text-to-image": "글을 읽고 그림을 κ·ΈοΏ½οΏ½οΏ½μ£ΌλŠ”",
772
+ "translation": "λ‹€λ₯Έ μ–Έμ–΄λ‘œ λ²ˆμ—­ν•΄μ£ΌλŠ”",
773
+ "question-answering": "μ§ˆλ¬Έμ— λ‹΅ν•΄μ£ΌλŠ”",
774
+ "summarization": "κΈ΄ 글을 짧게 μš”μ•½ν•΄μ£ΌλŠ”"
 
 
 
 
 
 
775
  }
776
 
777
+ task_desc = task_explanations.get(task, "νŠΉλ³„ν•œ κΈ°λŠ₯을 ν•˜λŠ”")
778
+
779
+ if downloads > 10000000:
780
+ popularity = "μ—„μ²­λ‚˜κ²Œ λ§Žμ€"
781
+ elif downloads > 1000000:
782
+ popularity = "μ•„μ£Ό λ§Žμ€"
783
+ elif downloads > 100000:
784
+ popularity = "λ§Žμ€"
785
+ else:
786
+ popularity = "μ–΄λŠ 정도"
787
+
788
+ return f"이 λͺ¨λΈμ€ {task_desc} AIμ˜ˆμš”. {popularity} μ‚¬λžŒλ“€μ΄ λ‹€μš΄λ‘œλ“œν•΄μ„œ μ‚¬μš©ν•˜κ³  μžˆμ–΄μš”. {model_name.split('/')[-1]}λΌλŠ” μ΄λ¦„μœΌλ‘œ 유λͺ…ν•΄μš”!"
789
+
790
+ def analyze_space(self, space_name: str, description: str) -> Dict:
791
+ """ν—ˆκΉ…νŽ˜μ΄μŠ€ 슀페이슀 뢄석"""
792
+
793
+ return {
794
+ "simple_explanation": f"{space_name}λŠ” μ›ΉλΈŒλΌμš°μ €μ—μ„œ λ°”λ‘œ AIλ₯Ό μ²΄ν—˜ν•΄λ³Ό 수 μžˆλŠ” κ³³μ΄μ—μš”. μ„€μΉ˜ 없이도 μ‚¬μš©ν•  수 μžˆμ–΄μ„œ νŽΈλ¦¬ν•΄μš”!",
795
+ "tech_stack": ["Python", "Gradio", "Transformers", "Flask"]
796
+ }
797
+
798
+
799
+ # ============================================
800
+ # κ³ κΈ‰ 뢄석기 클래슀
801
+ # ============================================
802
+
803
+ class AdvancedAIAnalyzer:
804
+ """LLM 기반 κ³ κΈ‰ AI λ‰΄μŠ€ 뢄석기"""
805
+
806
+ def __init__(self):
807
+ self.llm_analyzer = LLMAnalyzer()
808
  self.huggingface_data = {
809
  "models": [],
810
  "spaces": []
811
  }
 
812
  self.news_data = []
813
 
814
+ def fetch_huggingface_models(self, limit: int = 30) -> List[Dict]:
815
+ """ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© λͺ¨λΈ 30개 μˆ˜μ§‘"""
816
+ print(f"πŸ€— ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© λͺ¨λΈ {limit}개 μˆ˜μ§‘ 쀑...")
817
 
818
  try:
819
  models_url = "https://huggingface.co/api/models"
820
  params = {
821
  'sort': 'trending',
822
+ 'limit': limit
823
  }
824
 
825
  response = requests.get(models_url, params=params, timeout=15)
 
827
  if response.status_code == 200:
828
  models = response.json()
829
 
830
+ for model in models[:limit]:
831
+ model_info = {
832
  'name': model.get('id', 'Unknown'),
833
  'downloads': model.get('downloads', 0),
834
  'likes': model.get('likes', 0),
835
  'task': model.get('pipeline_tag', 'N/A'),
836
  'url': f"https://huggingface.co/{model.get('id', '')}"
837
+ }
838
+
839
+ # LLM 뢄석 μΆ”κ°€
840
+ model_info['analysis'] = self.llm_analyzer.analyze_model(
841
+ model_info['name'],
842
+ model_info['task'],
843
+ model_info['downloads']
844
+ )
845
+
846
+ self.huggingface_data['models'].append(model_info)
847
 
848
+ print(f"βœ… {len(self.huggingface_data['models'])}개 λͺ¨λΈ 뢄석 μ™„λ£Œ")
849
+ return self.huggingface_data['models']
850
+
 
851
  except Exception as e:
852
  print(f"❌ λͺ¨λΈ μˆ˜μ§‘ 였λ₯˜: {e}")
853
 
854
+ return []
855
+
856
+ def fetch_huggingface_spaces(self, limit: int = 30) -> List[Dict]:
857
+ """ν—ˆκΉ…νŽ˜μ΄μŠ€ 인기 슀페이슀 μˆ˜μ§‘"""
858
+ print(f"πŸš€ ν—ˆκΉ…νŽ˜μ΄μŠ€ 슀페이슀 {limit}개 μˆ˜μ§‘ 쀑...")
859
+
860
+ # μƒ˜ν”Œ 슀페이슀 데이터 (μ‹€μ œλ‘œλŠ” APIμ—μ„œ κ°€μ Έμ˜΄)
861
  sample_spaces = [
862
+ {
863
+ "name": "FLUX.1-schnell",
864
+ "description": "μ΄ˆκ³ μ† 이미지 생성 AI",
865
+ "url": "https://huggingface.co/spaces/black-forest-labs/FLUX.1-schnell",
866
+ "likes": 15234
867
+ },
868
+ {
869
+ "name": "ChatGPT4o",
870
+ "description": "GPT-4 기반 μ±„νŒ… 봇",
871
+ "url": "https://huggingface.co/spaces/",
872
+ "likes": 12456
873
+ },
874
+ {
875
+ "name": "Stable Diffusion XL",
876
+ "description": "κ³ ν’ˆμ§ˆ 이미지 생성",
877
+ "url": "https://huggingface.co/spaces/",
878
+ "likes": 11234
879
+ }
880
  ]
881
 
882
+ for space in sample_spaces[:limit]:
883
+ space_analysis = self.llm_analyzer.analyze_space(
884
+ space['name'],
885
+ space['description']
886
+ )
887
+
888
+ space['simple_explanation'] = space_analysis['simple_explanation']
889
+ space['tech_stack'] = space_analysis['tech_stack']
890
+
891
+ self.huggingface_data['spaces'].append(space)
892
 
893
+ print(f"βœ… {len(self.huggingface_data['spaces'])}개 슀페이슀 뢄석 μ™„λ£Œ")
894
+ return self.huggingface_data['spaces']
895
 
896
  def create_sample_news(self) -> List[Dict]:
897
+ """였늘의 AI λ‰΄μŠ€ μƒ˜ν”Œ"""
898
  sample_news = [
899
  {
900
  'title': 'MS "μ±—GPT μˆ˜μš” 폭증으둜 데이터센터 λΆ€μ‘±...2026λ…„κΉŒμ§€ 지속"',
901
  'url': 'https://www.aitimes.com/news/articleView.html?idxno=203055',
902
  'date': '10-10 15:10',
903
+ 'source': 'AI Times'
 
904
  },
905
  {
906
  'title': 'λ―Έκ΅­, UAE에 GPU 판맀 일뢀 승인...μ—”λΉ„λ””μ•„ μ‹œμ΄ 5μ‘°λ‹¬λŸ¬ λˆˆμ•ž',
907
  'url': 'https://www.aitimes.com/news/articleView.html?idxno=203053',
908
  'date': '10-10 14:46',
909
+ 'source': 'AI Times'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
910
  },
911
  {
912
  'title': 'μ†ŒλΌ, μ±—GPT보닀 빨리 100만 λ‹€μš΄λ‘œλ“œ 돌파',
913
  'url': 'https://www.aitimes.com/news/articleView.html?idxno=203045',
914
  'date': '10-10 12:55',
915
+ 'source': 'AI Times'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  }
917
  ]
918
 
 
919
  return sample_news
920
 
921
+ def analyze_all_news(self) -> List[Dict]:
922
+ """λͺ¨λ“  λ‰΄μŠ€μ— LLM 뢄석 μΆ”κ°€"""
923
+ print("πŸ“° λ‰΄μŠ€ LLM 뢄석 μ‹œμž‘...")
 
 
 
 
 
 
 
 
924
 
 
 
 
 
 
925
  news = self.create_sample_news()
926
+ analyzed_news = []
927
 
 
 
 
 
 
928
  for article in news:
929
+ analysis = self.llm_analyzer.analyze_news_simple(
930
+ article['title'],
931
+ ""
932
+ )
933
+
934
+ article['analysis'] = analysis
935
+ analyzed_news.append(article)
936
+
937
+ print(f"βœ… {len(analyzed_news)}개 λ‰΄μŠ€ 뢄석 μ™„λ£Œ")
938
+ return analyzed_news
939
+
940
+ def get_all_data(self) -> Dict:
941
+ """λͺ¨λ“  데이터 μˆ˜μ§‘ 및 뢄석"""
942
+ print("\n" + "="*60)
943
+ print("πŸš€ AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ LLM 뢄석 μ‹œμž‘")
944
+ print("="*60 + "\n")
945
+
946
+ # 데이터 μˆ˜μ§‘
947
+ analyzed_news = self.analyze_all_news()
948
+ analyzed_models = self.fetch_huggingface_models(30)
949
+ analyzed_spaces = self.fetch_huggingface_spaces(30)
950
 
951
+ # 톡계
952
  stats = {
953
+ 'total_news': len(analyzed_news),
954
+ 'hf_models': len(analyzed_models),
955
+ 'hf_spaces': len(analyzed_spaces),
956
+ 'llm_analyses': len(analyzed_news) + len(analyzed_models) + len(analyzed_spaces)
957
  }
958
 
959
+ print(f"\nβœ… 전체 뢄석 μ™„λ£Œ: {stats['llm_analyses']}개 ν•­λͺ©")
960
+
961
  return {
962
+ 'analyzed_news': analyzed_news,
963
+ 'analyzed_models': analyzed_models,
964
+ 'analyzed_spaces': analyzed_spaces,
965
  'stats': stats,
966
  'timestamp': datetime.now().strftime('%Yλ…„ %mμ›” %d일 %H:%M:%S')
967
  }
968
 
969
 
970
  # ============================================
971
+ # Flask 라우트
972
  # ============================================
973
 
974
  @app.route('/')
975
  def index():
976
  """메인 νŽ˜μ΄μ§€"""
977
  try:
978
+ analyzer = AdvancedAIAnalyzer()
979
+ data = analyzer.get_all_data()
980
  return render_template_string(HTML_TEMPLATE, **data)
981
  except Exception as e:
982
  return f"""
983
  <html>
984
  <body style="font-family: Arial; padding: 50px; text-align: center;">
985
  <h1 style="color: #e74c3c;">⚠️ 였λ₯˜ λ°œμƒ</h1>
986
+ <p>{str(e)}</p>
987
+ <button onclick="location.reload()" style="padding: 10px 20px; margin-top: 20px;">
 
988
  πŸ”„ μƒˆλ‘œκ³ μΉ¨
989
  </button>
990
  </body>
 
994
 
995
  @app.route('/api/data')
996
  def api_data():
997
+ """JSON API"""
998
  try:
999
+ analyzer = AdvancedAIAnalyzer()
1000
+ data = analyzer.get_all_data()
1001
  return jsonify({
1002
  'success': True,
1003
+ 'data': data
 
1004
  })
1005
  except Exception as e:
1006
  return jsonify({
1007
  'success': False,
1008
+ 'error': str(e)
 
1009
  }), 500
1010
 
1011
 
1012
  @app.route('/health')
1013
  def health():
1014
+ """ν—¬μŠ€ 체크"""
1015
  return jsonify({
1016
  "status": "healthy",
1017
+ "service": "AI News LLM Analyzer",
1018
+ "version": "2.0.0"
 
1019
  })
1020
 
1021
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1022
  # ============================================
1023
  # 메인 μ‹€ν–‰
1024
  # ============================================
1025
 
1026
  if __name__ == '__main__':
 
1027
  port = int(os.environ.get('PORT', 7860))
1028
 
 
 
 
1029
  print(f"""
1030
  ╔════════════════════════════════════════════════════════════╗
1031
  β•‘ β•‘
1032
+ β•‘ πŸ€– AI λ‰΄μŠ€ & ν—ˆκΉ…νŽ˜μ΄μŠ€ LLM 뢄석 μ›Ήμ•± v2.0 β•‘
1033
  β•‘ β•‘
1034
  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
1035
 
1036
+ ✨ μ£Όμš” κΈ°λŠ₯:
1037
+ β€’ πŸ“° λ‰΄μŠ€ μ΄ˆλ“±ν•™μƒ μˆ˜μ€€ 뢄석 (μš”μ•½/의미/영ν–₯/행동지침)
1038
+ β€’ πŸ€— ν—ˆκΉ…νŽ˜μ΄μŠ€ νŠΈλ Œλ”© 30개 λͺ¨λΈ 뢄석
1039
+ β€’ πŸš€ 인기 슀페이슀 30개 뢄석
1040
+ β€’ 🎨 νƒ­ UI (λ‰΄μŠ€/λͺ¨λΈ/슀페이슀)
 
1041
 
1042
+ πŸš€ μ„œλ²„ 정보:
1043
+ πŸ“ 메인: http://localhost:{port}
1044
+ πŸ“Š API: http://localhost:{port}/api/data
1045
+ πŸ’š Health: http://localhost:{port}/health
1046
 
1047
  λΈŒλΌμš°μ €μ—μ„œ μœ„ URL을 μ—΄μ–΄μ£Όμ„Έμš”!
1048
+ μ’…λ£Œ: Ctrl+C
1049
  """)
1050
 
1051
  try:
1052
  app.run(
1053
  host='0.0.0.0',
1054
  port=port,
1055
+ debug=False,
1056
  threaded=True
1057
  )
1058
  except KeyboardInterrupt:
1059
+ print("\n\nπŸ‘‹ μ„œλ²„ μ’…λ£Œ!")
1060
+ sys.exit(0)