ginipick commited on
Commit
9600d1c
·
verified ·
1 Parent(s): d41bc49

Delete app-backup2.py

Browse files
Files changed (1) hide show
  1. app-backup2.py +0 -1418
app-backup2.py DELETED
@@ -1,1418 +0,0 @@
1
- import gradio as gr
2
- import requests
3
- import json
4
- import os
5
- from datetime import datetime, timedelta
6
- from concurrent.futures import ThreadPoolExecutor
7
- from functools import lru_cache
8
- from requests.adapters import HTTPAdapter
9
- from requests.packages.urllib3.util.retry import Retry
10
- from openai import OpenAI
11
- from bs4 import BeautifulSoup
12
- import re # re 모듈 추가
13
- import json
14
- import os
15
- from datetime import datetime
16
- import sqlite3
17
- import pathlib
18
-
19
- # DB 초기화 함수
20
- def init_db():
21
- db_path = pathlib.Path("search_results.db")
22
- conn = sqlite3.connect(db_path)
23
- c = conn.cursor()
24
- c.execute('''CREATE TABLE IF NOT EXISTS searches
25
- (id INTEGER PRIMARY KEY AUTOINCREMENT,
26
- keyword TEXT,
27
- country TEXT,
28
- results TEXT,
29
- timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)''')
30
- conn.commit()
31
- conn.close()
32
-
33
- # 검색 결과 저장 함수
34
- def save_to_db(keyword, country, results):
35
- conn = sqlite3.connect("search_results.db")
36
- c = conn.cursor()
37
- c.execute("INSERT INTO searches (keyword, country, results) VALUES (?, ?, ?)",
38
- (keyword, country, json.dumps(results)))
39
- conn.commit()
40
- conn.close()
41
-
42
- # DB에서 검색 결과 불러오기 함수
43
- def load_from_db(keyword, country):
44
- conn = sqlite3.connect("search_results.db")
45
- c = conn.cursor()
46
- c.execute("SELECT results, timestamp FROM searches WHERE keyword=? AND country=? ORDER BY timestamp DESC LIMIT 1",
47
- (keyword, country))
48
- result = c.fetchone()
49
- conn.close()
50
- if result:
51
- return json.loads(result[0]), result[1]
52
- return None, None
53
-
54
- # 삼성/미국 검색 함수
55
- def search_samsung_us():
56
- error_message, articles = serphouse_search("samsung", "United States")
57
- if not error_message and articles:
58
- save_to_db("samsung", "United States", articles)
59
- return display_results(articles)
60
- return "검색 결과가 없습니다."
61
-
62
- # DB에서 삼성/미국 결과 불러오기 함수
63
- def load_samsung_us():
64
- results, timestamp = load_from_db("samsung", "United States")
65
- if results:
66
- return f"저장 시간: {timestamp}\n\n" + display_results(results)
67
- return "저장된 결과가 없습니다."
68
-
69
- # 결과 표시 함수
70
- def display_results(articles):
71
- output = ""
72
- for idx, article in enumerate(articles, 1):
73
- output += f"### {idx}. {article['title']}\n"
74
- output += f"출처: {article['channel']}\n"
75
- output += f"시간: {article['time']}\n"
76
- output += f"링크: {article['link']}\n"
77
- output += f"요약: {article['snippet']}\n\n"
78
- return output
79
-
80
-
81
- ACCESS_TOKEN = os.getenv("HF_TOKEN")
82
- if not ACCESS_TOKEN:
83
- raise ValueError("HF_TOKEN environment variable is not set")
84
-
85
- client = OpenAI(
86
- base_url="https://api-inference.huggingface.co/v1/",
87
- api_key=ACCESS_TOKEN,
88
- )
89
-
90
- MAX_COUNTRY_RESULTS = 100 # 국가별 최대 결과 수
91
- MAX_GLOBAL_RESULTS = 1000 # 전세계 최대 결과 수
92
-
93
- def create_article_components(max_results):
94
- article_components = []
95
- for i in range(max_results):
96
- with gr.Group(visible=False) as article_group:
97
- title = gr.Markdown()
98
- image = gr.Image(width=200, height=150)
99
- snippet = gr.Markdown()
100
- info = gr.Markdown()
101
-
102
- article_components.append({
103
- 'group': article_group,
104
- 'title': title,
105
- 'image': image,
106
- 'snippet': snippet,
107
- 'info': info,
108
- 'index': i,
109
- })
110
- return article_components
111
-
112
- API_KEY = os.getenv("SERPHOUSE_API_KEY")
113
-
114
- # 국가별 언어 코드 매핑
115
- COUNTRY_LANGUAGES = {
116
- "United States": "en",
117
- "KOREA": "ko",
118
- "United Kingdom": "en",
119
- "Taiwan": "zh-TW",
120
- "Canada": "en",
121
- "Australia": "en",
122
- "Germany": "de",
123
- "France": "fr",
124
- "Japan": "ja",
125
- "China": "zh",
126
- "India": "hi",
127
- "Brazil": "pt",
128
- "Mexico": "es",
129
- "Russia": "ru",
130
- "Italy": "it",
131
- "Spain": "es",
132
- "Netherlands": "nl",
133
- "Singapore": "en",
134
- "Hong Kong": "zh-HK",
135
- "Indonesia": "id",
136
- "Malaysia": "ms",
137
- "Philippines": "tl",
138
- "Thailand": "th",
139
- "Vietnam": "vi",
140
- "Belgium": "nl",
141
- "Denmark": "da",
142
- "Finland": "fi",
143
- "Ireland": "en",
144
- "Norway": "no",
145
- "Poland": "pl",
146
- "Sweden": "sv",
147
- "Switzerland": "de",
148
- "Austria": "de",
149
- "Czech Republic": "cs",
150
- "Greece": "el",
151
- "Hungary": "hu",
152
- "Portugal": "pt",
153
- "Romania": "ro",
154
- "Turkey": "tr",
155
- "Israel": "he",
156
- "Saudi Arabia": "ar",
157
- "United Arab Emirates": "ar",
158
- "South Africa": "en",
159
- "Argentina": "es",
160
- "Chile": "es",
161
- "Colombia": "es",
162
- "Peru": "es",
163
- "Venezuela": "es",
164
- "New Zealand": "en",
165
- "Bangladesh": "bn",
166
- "Pakistan": "ur",
167
- "Egypt": "ar",
168
- "Morocco": "ar",
169
- "Nigeria": "en",
170
- "Kenya": "sw",
171
- "Ukraine": "uk",
172
- "Croatia": "hr",
173
- "Slovakia": "sk",
174
- "Bulgaria": "bg",
175
- "Serbia": "sr",
176
- "Estonia": "et",
177
- "Latvia": "lv",
178
- "Lithuania": "lt",
179
- "Slovenia": "sl",
180
- "Luxembourg": "fr",
181
- "Malta": "mt",
182
- "Cyprus": "el",
183
- "Iceland": "is"
184
- }
185
-
186
- COUNTRY_LOCATIONS = {
187
- "United States": "United States",
188
- "KOREA": "kr",
189
- "United Kingdom": "United Kingdom",
190
- "Taiwan": "Taiwan",
191
- "Canada": "Canada",
192
- "Australia": "Australia",
193
- "Germany": "Germany",
194
- "France": "France",
195
- "Japan": "Japan",
196
- "China": "China",
197
- "India": "India",
198
- "Brazil": "Brazil",
199
- "Mexico": "Mexico",
200
- "Russia": "Russia",
201
- "Italy": "Italy",
202
- "Spain": "Spain",
203
- "Netherlands": "Netherlands",
204
- "Singapore": "Singapore",
205
- "Hong Kong": "Hong Kong",
206
- "Indonesia": "Indonesia",
207
- "Malaysia": "Malaysia",
208
- "Philippines": "Philippines",
209
- "Thailand": "Thailand",
210
- "Vietnam": "Vietnam",
211
- "Belgium": "Belgium",
212
- "Denmark": "Denmark",
213
- "Finland": "Finland",
214
- "Ireland": "Ireland",
215
- "Norway": "Norway",
216
- "Poland": "Poland",
217
- "Sweden": "Sweden",
218
- "Switzerland": "Switzerland",
219
- "Austria": "Austria",
220
- "Czech Republic": "Czech Republic",
221
- "Greece": "Greece",
222
- "Hungary": "Hungary",
223
- "Portugal": "Portugal",
224
- "Romania": "Romania",
225
- "Turkey": "Turkey",
226
- "Israel": "Israel",
227
- "Saudi Arabia": "Saudi Arabia",
228
- "United Arab Emirates": "United Arab Emirates",
229
- "South Africa": "South Africa",
230
- "Argentina": "Argentina",
231
- "Chile": "Chile",
232
- "Colombia": "Colombia",
233
- "Peru": "Peru",
234
- "Venezuela": "Venezuela",
235
- "New Zealand": "New Zealand",
236
- "Bangladesh": "Bangladesh",
237
- "Pakistan": "Pakistan",
238
- "Egypt": "Egypt",
239
- "Morocco": "Morocco",
240
- "Nigeria": "Nigeria",
241
- "Kenya": "Kenya",
242
- "Ukraine": "Ukraine",
243
- "Croatia": "Croatia",
244
- "Slovakia": "Slovakia",
245
- "Bulgaria": "Bulgaria",
246
- "Serbia": "Serbia",
247
- "Estonia": "Estonia",
248
- "Latvia": "Latvia",
249
- "Lithuania": "Lithuania",
250
- "Slovenia": "Slovenia",
251
- "Luxembourg": "Luxembourg",
252
- "Malta": "Malta",
253
- "Cyprus": "Cyprus",
254
- "Iceland": "Iceland"
255
- }
256
-
257
- # 지역 정의
258
- # 동아시아 지역
259
- COUNTRY_LANGUAGES_EAST_ASIA = {
260
- "KOREA": "ko",
261
- "Taiwan": "zh-TW",
262
- "Japan": "ja",
263
- "China": "zh",
264
- "Hong Kong": "zh-HK"
265
- }
266
-
267
- COUNTRY_LOCATIONS_EAST_ASIA = {
268
- "KOREA": "KOREA",
269
- "Taiwan": "Taiwan",
270
- "Japan": "Japan",
271
- "China": "China",
272
- "Hong Kong": "Hong Kong"
273
- }
274
-
275
- # 동남아시아/오세아니아 지역
276
- COUNTRY_LANGUAGES_SOUTHEAST_ASIA_OCEANIA = {
277
- "Indonesia": "id",
278
- "Malaysia": "ms",
279
- "Philippines": "tl",
280
- "Thailand": "th",
281
- "Vietnam": "vi",
282
- "Singapore": "en",
283
- "Papua New Guinea": "en",
284
- "Australia": "en",
285
- "New Zealand": "en"
286
- }
287
-
288
- COUNTRY_LOCATIONS_SOUTHEAST_ASIA_OCEANIA = {
289
- "Indonesia": "Indonesia",
290
- "Malaysia": "Malaysia",
291
- "Philippines": "Philippines",
292
- "Thailand": "Thailand",
293
- "Vietnam": "Vietnam",
294
- "Singapore": "Singapore",
295
- "Papua New Guinea": "Papua New Guinea",
296
- "Australia": "Australia",
297
- "New Zealand": "New Zealand"
298
- }
299
-
300
- # 동유럽 지역
301
- COUNTRY_LANGUAGES_EAST_EUROPE = {
302
- "Poland": "pl",
303
- "Czech Republic": "cs",
304
- "Greece": "el",
305
- "Hungary": "hu",
306
- "Romania": "ro",
307
- "Ukraine": "uk",
308
- "Croatia": "hr",
309
- "Slovakia": "sk",
310
- "Bulgaria": "bg",
311
- "Serbia": "sr",
312
- "Estonia": "et",
313
- "Latvia": "lv",
314
- "Lithuania": "lt",
315
- "Slovenia": "sl",
316
- "Malta": "mt",
317
- "Cyprus": "el",
318
- "Iceland": "is",
319
- "Russia": "ru"
320
- }
321
-
322
- COUNTRY_LOCATIONS_EAST_EUROPE = {
323
- "Poland": "Poland",
324
- "Czech Republic": "Czech Republic",
325
- "Greece": "Greece",
326
- "Hungary": "Hungary",
327
- "Romania": "Romania",
328
- "Ukraine": "Ukraine",
329
- "Croatia": "Croatia",
330
- "Slovakia": "Slovakia",
331
- "Bulgaria": "Bulgaria",
332
- "Serbia": "Serbia",
333
- "Estonia": "Estonia",
334
- "Latvia": "Latvia",
335
- "Lithuania": "Lithuania",
336
- "Slovenia": "Slovenia",
337
- "Malta": "Malta",
338
- "Cyprus": "Cyprus",
339
- "Iceland": "Iceland",
340
- "Russia": "Russia"
341
- }
342
-
343
- # 서유럽 지역
344
- COUNTRY_LANGUAGES_WEST_EUROPE = {
345
- "Germany": "de",
346
- "France": "fr",
347
- "Italy": "it",
348
- "Spain": "es",
349
- "Netherlands": "nl",
350
- "Belgium": "nl",
351
- "Ireland": "en",
352
- "Sweden": "sv",
353
- "Switzerland": "de",
354
- "Austria": "de",
355
- "Portugal": "pt",
356
- "Luxembourg": "fr",
357
- "United Kingdom": "en"
358
- }
359
-
360
- COUNTRY_LOCATIONS_WEST_EUROPE = {
361
- "Germany": "Germany",
362
- "France": "France",
363
- "Italy": "Italy",
364
- "Spain": "Spain",
365
- "Netherlands": "Netherlands",
366
- "Belgium": "Belgium",
367
- "Ireland": "Ireland",
368
- "Sweden": "Sweden",
369
- "Switzerland": "Switzerland",
370
- "Austria": "Austria",
371
- "Portugal": "Portugal",
372
- "Luxembourg": "Luxembourg",
373
- "United Kingdom": "United Kingdom"
374
- }
375
-
376
- # 중동/아프리카 지역
377
- COUNTRY_LANGUAGES_ARAB_AFRICA = {
378
- "South Africa": "en",
379
- "Nigeria": "en",
380
- "Kenya": "sw",
381
- "Egypt": "ar",
382
- "Morocco": "ar",
383
- "Saudi Arabia": "ar",
384
- "United Arab Emirates": "ar",
385
- "Israel": "he"
386
- }
387
-
388
- COUNTRY_LOCATIONS_ARAB_AFRICA = {
389
- "South Africa": "South Africa",
390
- "Nigeria": "Nigeria",
391
- "Kenya": "Kenya",
392
- "Egypt": "Egypt",
393
- "Morocco": "Morocco",
394
- "Saudi Arabia": "Saudi Arabia",
395
- "United Arab Emirates": "United Arab Emirates",
396
- "Israel": "Israel"
397
- }
398
-
399
- # 아메리카 지역
400
- COUNTRY_LANGUAGES_AMERICA = {
401
- "United States": "en",
402
- "Canada": "en",
403
- "Mexico": "es",
404
- "Brazil": "pt",
405
- "Argentina": "es",
406
- "Chile": "es",
407
- "Colombia": "es",
408
- "Peru": "es",
409
- "Venezuela": "es"
410
- }
411
-
412
- COUNTRY_LOCATIONS_AMERICA = {
413
- "United States": "United States",
414
- "Canada": "Canada",
415
- "Mexico": "Mexico",
416
- "Brazil": "Brazil",
417
- "Argentina": "Argentina",
418
- "Chile": "Chile",
419
- "Colombia": "Colombia",
420
- "Peru": "Peru",
421
- "Venezuela": "Venezuela"
422
- }
423
-
424
- # 지역 선택 리스트
425
- REGIONS = [
426
- "동아시아",
427
- "동남아시아/오세아니아",
428
- "동유럽",
429
- "서유럽",
430
- "중동/아프리카",
431
- "아메리카"
432
- ]
433
-
434
-
435
- @lru_cache(maxsize=100)
436
- def translate_query(query, country):
437
- try:
438
- if is_english(query):
439
- return query
440
-
441
- if country in COUNTRY_LANGUAGES:
442
- if country == "South Korea":
443
- return query
444
-
445
- target_lang = COUNTRY_LANGUAGES[country]
446
-
447
- url = "https://translate.googleapis.com/translate_a/single"
448
- params = {
449
- "client": "gtx",
450
- "sl": "auto",
451
- "tl": target_lang,
452
- "dt": "t",
453
- "q": query
454
- }
455
-
456
- session = requests.Session()
457
- retries = Retry(total=3, backoff_factor=0.5)
458
- session.mount('https://', HTTPAdapter(max_retries=retries))
459
-
460
- response = session.get(url, params=params, timeout=(5, 10))
461
- translated_text = response.json()[0][0][0]
462
- return translated_text
463
-
464
- return query
465
-
466
- except Exception as e:
467
- print(f"번역 오류: {str(e)}")
468
- return query
469
-
470
-
471
- @lru_cache(maxsize=200)
472
- def translate_to_korean(text):
473
- try:
474
- url = "https://translate.googleapis.com/translate_a/single"
475
- params = {
476
- "client": "gtx",
477
- "sl": "auto",
478
- "tl": "ko",
479
- "dt": "t",
480
- "q": text
481
- }
482
-
483
- session = requests.Session()
484
- retries = Retry(total=3, backoff_factor=0.5)
485
- session.mount('https://', HTTPAdapter(max_retries=retries))
486
-
487
- response = session.get(url, params=params, timeout=(5, 10))
488
- translated_text = response.json()[0][0][0]
489
- return translated_text
490
- except Exception as e:
491
- print(f"한글 번역 오류: {str(e)}")
492
- return text
493
-
494
- def is_english(text):
495
- return all(ord(char) < 128 for char in text.replace(' ', '').replace('-', '').replace('_', ''))
496
-
497
- def is_korean(text):
498
- return any('\uAC00' <= char <= '\uD7A3' for char in text)
499
-
500
- def search_serphouse(query, country, page=1, num_result=10):
501
- url = "https://api.serphouse.com/serp/live"
502
-
503
- now = datetime.utcnow()
504
- yesterday = now - timedelta(days=1)
505
- date_range = f"{yesterday.strftime('%Y-%m-%d')},{now.strftime('%Y-%m-%d')}"
506
-
507
- translated_query = translate_query(query, country)
508
-
509
- payload = {
510
- "data": {
511
- "q": translated_query,
512
- "domain": "google.com",
513
- "loc": COUNTRY_LOCATIONS.get(country, "United States"),
514
- "lang": COUNTRY_LANGUAGES.get(country, "en"),
515
- "device": "desktop",
516
- "serp_type": "news",
517
- "page": "1",
518
- "num": "100",
519
- "date_range": date_range,
520
- "sort_by": "date"
521
- }
522
- }
523
-
524
- headers = {
525
- "accept": "application/json",
526
- "content-type": "application/json",
527
- "authorization": f"Bearer {API_KEY}"
528
- }
529
-
530
- try:
531
- # 세션 설정 개선
532
- session = requests.Session()
533
-
534
- # 재시도 설정 강화
535
- retries = Retry(
536
- total=5, # 최대 재시도 횟수 증가
537
- backoff_factor=1, # 재시도 간격 증가
538
- status_forcelist=[500, 502, 503, 504, 429], # 재시도할 HTTP 상태 코드
539
- allowed_methods=["POST"] # POST 요청에 대한 재시도 허용
540
- )
541
-
542
- # 타임아웃 설정 조정
543
- adapter = HTTPAdapter(max_retries=retries)
544
- session.mount('http://', adapter)
545
- session.mount('https://', adapter)
546
-
547
- # 타임아웃 값 증가 (connect timeout, read timeout)
548
- response = session.post(
549
- url,
550
- json=payload,
551
- headers=headers,
552
- timeout=(30, 30) # 연결 타임아웃 30초, 읽기 타임아웃 30초
553
- )
554
-
555
- response.raise_for_status()
556
- return {"results": response.json(), "translated_query": translated_query}
557
-
558
- except requests.exceptions.Timeout:
559
- return {
560
- "error": "검색 시간이 초과되었습니다. 잠시 후 다시 시도해주세요.",
561
- "translated_query": query
562
- }
563
- except requests.exceptions.RequestException as e:
564
- return {
565
- "error": f"검색 중 오류가 발생했습니다: {str(e)}",
566
- "translated_query": query
567
- }
568
- except Exception as e:
569
- return {
570
- "error": f"예기치 않은 오류가 발생했습니다: {str(e)}",
571
- "translated_query": query
572
- }
573
-
574
- def format_results_from_raw(response_data):
575
- if "error" in response_data:
576
- return "Error: " + response_data["error"], []
577
-
578
- try:
579
- results = response_data["results"]
580
- translated_query = response_data["translated_query"]
581
-
582
- news_results = results.get('results', {}).get('results', {}).get('news', [])
583
- if not news_results:
584
- return "검색 결과가 없습니다.", []
585
-
586
- # 한국 도메인 및 한국 관련 키워드 필터링
587
- korean_domains = ['.kr', 'korea', 'korean', 'yonhap', 'hankyung', 'chosun',
588
- 'donga', 'joins', 'hani', 'koreatimes', 'koreaherald']
589
- korean_keywords = ['korea', 'korean', 'seoul', 'busan', 'incheon', 'daegu',
590
- 'gwangju', 'daejeon', 'ulsan', 'sejong']
591
-
592
- filtered_articles = []
593
- for idx, result in enumerate(news_results, 1):
594
- url = result.get("url", result.get("link", "")).lower()
595
- title = result.get("title", "").lower()
596
- channel = result.get("channel", result.get("source", "")).lower()
597
-
598
- # 한국 관련 컨텐츠 필터링
599
- is_korean_content = any(domain in url or domain in channel for domain in korean_domains) or \
600
- any(keyword in title.lower() for keyword in korean_keywords)
601
-
602
- if not is_korean_content:
603
- filtered_articles.append({
604
- "index": idx,
605
- "title": result.get("title", "제목 없음"),
606
- "link": url,
607
- "snippet": result.get("snippet", "내용 없음"),
608
- "channel": result.get("channel", result.get("source", "알 수 없음")),
609
- "time": result.get("time", result.get("date", "알 수 없는 시간")),
610
- "image_url": result.get("img", result.get("thumbnail", "")),
611
- "translated_query": translated_query
612
- })
613
-
614
- return "", filtered_articles
615
- except Exception as e:
616
- return f"결과 처리 중 오류 발생: {str(e)}", []
617
-
618
- def serphouse_search(query, country):
619
- response_data = search_serphouse(query, country)
620
- return format_results_from_raw(response_data)
621
-
622
-
623
- def search_and_display(query, country, articles_state, progress=gr.Progress()):
624
- with ThreadPoolExecutor(max_workers=3) as executor:
625
- progress(0, desc="검색어 번역 중...")
626
- future_translation = executor.submit(translate_query, query, country)
627
- translated_query = future_translation.result()
628
- translated_display = f"**원본 검색어:** {query}\n**번역된 검색어:** {translated_query}" if translated_query != query else f"**검색어:** {query}"
629
-
630
- progress(0.3, desc="검색 중...")
631
- response_data = search_serphouse(query, country)
632
-
633
- progress(0.6, desc="결과 처리 중...")
634
- error_message, articles = format_results_from_raw(response_data)
635
-
636
- outputs = []
637
- outputs.append(gr.update(value="검색을 진행중입니다...", visible=True))
638
- outputs.append(gr.update(value=translated_display, visible=True))
639
-
640
- if error_message:
641
- outputs.append(gr.update(value=error_message, visible=True))
642
- for comp in article_components:
643
- outputs.extend([
644
- gr.update(visible=False), gr.update(), gr.update(),
645
- gr.update(), gr.update()
646
- ])
647
- articles_state = []
648
- else:
649
- outputs.append(gr.update(value="", visible=False))
650
- if not error_message and articles:
651
- futures = []
652
- for article in articles:
653
- future = executor.submit(translate_to_korean, article['snippet'])
654
- futures.append((article, future))
655
-
656
- progress(0.8, desc="번역 처리 중...")
657
- for article, future in futures:
658
- article['korean_summary'] = future.result()
659
-
660
- total_articles = len(articles)
661
- for idx, comp in enumerate(article_components):
662
- progress((idx + 1) / total_articles, desc=f"결과 표시 중... {idx + 1}/{total_articles}")
663
- if idx < len(articles):
664
- article = articles[idx]
665
- image_url = article['image_url']
666
- image_update = gr.update(value=image_url, visible=True) if image_url and not image_url.startswith('data:image') else gr.update(value=None, visible=False)
667
-
668
- outputs.extend([
669
- gr.update(visible=True),
670
- gr.update(value=f"### [{article['title']}]({article['link']})"),
671
- image_update,
672
- gr.update(value=f"**요약:** {article['snippet']}\n\n**한글 요약:** {article['korean_summary']}"),
673
- gr.update(value=f"**출처:** {article['channel']} | **시간:** {article['time']}")
674
- ])
675
- else:
676
- outputs.extend([
677
- gr.update(visible=False), gr.update(), gr.update(),
678
- gr.update(), gr.update()
679
- ])
680
- articles_state = articles
681
-
682
- progress(1.0, desc="완료!")
683
- outputs.append(articles_state)
684
- outputs[0] = gr.update(value="", visible=False)
685
-
686
- return outputs
687
-
688
- def get_region_countries(region):
689
- """선택된 지역의 국가 및 언어 정보 반환"""
690
- if region == "동아시아":
691
- return COUNTRY_LOCATIONS_EAST_ASIA, COUNTRY_LANGUAGES_EAST_ASIA
692
- elif region == "동남아시아/오세아니아":
693
- return COUNTRY_LOCATIONS_SOUTHEAST_ASIA_OCEANIA, COUNTRY_LANGUAGES_SOUTHEAST_ASIA_OCEANIA
694
- elif region == "동유럽":
695
- return COUNTRY_LOCATIONS_EAST_EUROPE, COUNTRY_LANGUAGES_EAST_EUROPE
696
- elif region == "서유럽":
697
- return COUNTRY_LOCATIONS_WEST_EUROPE, COUNTRY_LANGUAGES_WEST_EUROPE
698
- elif region == "중동/아프리카":
699
- return COUNTRY_LOCATIONS_ARAB_AFRICA, COUNTRY_LANGUAGES_ARAB_AFRICA
700
- elif region == "아메리카":
701
- return COUNTRY_LOCATIONS_AMERICA, COUNTRY_LANGUAGES_AMERICA
702
- return {}, {}
703
-
704
- def search_global(query, region, articles_state_global):
705
- """지역별 검색 함수"""
706
- status_msg = f"{region} 지역 검색을 시작합니다..."
707
- all_results = []
708
-
709
- outputs = [
710
- gr.update(value=status_msg, visible=True),
711
- gr.update(value=f"**검색어:** {query}", visible=True),
712
- ]
713
-
714
- for _ in global_article_components:
715
- outputs.extend([
716
- gr.update(visible=False), gr.update(), gr.update(),
717
- gr.update(), gr.update()
718
- ])
719
- outputs.append([])
720
-
721
- yield outputs
722
-
723
- # 선택된 지역의 국가 정보 가져오기
724
- locations, languages = get_region_countries(region)
725
- total_countries = len(locations)
726
-
727
- for idx, (country, location) in enumerate(locations.items(), 1):
728
- try:
729
- status_msg = f"{region} - {country} 검색 중... ({idx}/{total_countries} 국가)"
730
- outputs[0] = gr.update(value=status_msg, visible=True)
731
- yield outputs
732
-
733
- error_message, articles = serphouse_search(query, country)
734
- if not error_message and articles:
735
- for article in articles:
736
- article['source_country'] = country
737
- article['region'] = region
738
-
739
- all_results.extend(articles)
740
- sorted_results = sorted(all_results, key=lambda x: x.get('time', ''), reverse=True)
741
-
742
- seen_urls = set()
743
- unique_results = []
744
- for article in sorted_results:
745
- url = article.get('link', '')
746
- if url not in seen_urls:
747
- seen_urls.add(url)
748
- unique_results.append(article)
749
-
750
- unique_results = unique_results[:MAX_GLOBAL_RESULTS]
751
-
752
- outputs = [
753
- gr.update(value=f"{region} - {idx}/{total_countries} 국가 검색 완료\n현재까지 발견된 뉴스: {len(unique_results)}건", visible=True),
754
- gr.update(value=f"**검색어:** {query} | **지역:** {region}", visible=True),
755
- ]
756
-
757
- for idx, comp in enumerate(global_article_components):
758
- if idx < len(unique_results):
759
- article = unique_results[idx]
760
- image_url = article.get('image_url', '')
761
- image_update = gr.update(value=image_url, visible=True) if image_url and not image_url.startswith('data:image') else gr.update(value=None, visible=False)
762
-
763
- korean_summary = translate_to_korean(article['snippet'])
764
-
765
- outputs.extend([
766
- gr.update(visible=True),
767
- gr.update(value=f"### [{article['title']}]({article['link']})"),
768
- image_update,
769
- gr.update(value=f"**요약:** {article['snippet']}\n\n**한글 요약:** {korean_summary}"),
770
- gr.update(value=f"**출처:** {article['channel']} | **국가:** {article['source_country']} | **지역:** {article['region']} | **시간:** {article['time']}")
771
- ])
772
- else:
773
- outputs.extend([
774
- gr.update(visible=False),
775
- gr.update(),
776
- gr.update(),
777
- gr.update(),
778
- gr.update()
779
- ])
780
-
781
- outputs.append(unique_results)
782
- yield outputs
783
-
784
- except Exception as e:
785
- print(f"Error searching {country}: {str(e)}")
786
- continue
787
-
788
- final_status = f"{region} 검색 완료! 총 {len(unique_results)}개의 뉴스가 발견되었습니다."
789
- outputs[0] = gr.update(value=final_status, visible=True)
790
- yield outputs
791
-
792
- css = """
793
- /* 전역 스타일 */
794
- footer {visibility: hidden;}
795
-
796
- /* 레이아웃 컨테이너 */
797
- #status_area {
798
- background: rgba(255, 255, 255, 0.9);
799
- padding: 15px;
800
- border-bottom: 1px solid #ddd;
801
- margin-bottom: 20px;
802
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
803
- }
804
-
805
- #results_area {
806
- padding: 10px;
807
- margin-top: 10px;
808
- }
809
-
810
- /* 탭 스타일 */
811
- .tabs {
812
- border-bottom: 2px solid #ddd !important;
813
- margin-bottom: 20px !important;
814
- }
815
-
816
- .tab-nav {
817
- border-bottom: none !important;
818
- margin-bottom: 0 !important;
819
- }
820
-
821
- .tab-nav button {
822
- font-weight: bold !important;
823
- padding: 10px 20px !important;
824
- }
825
-
826
- .tab-nav button.selected {
827
- border-bottom: 2px solid #1f77b4 !important;
828
- color: #1f77b4 !important;
829
- }
830
-
831
- /* 상태 메시지 */
832
- #status_area .markdown-text {
833
- font-size: 1.1em;
834
- color: #2c3e50;
835
- padding: 10px 0;
836
- }
837
-
838
- /* 기본 컨테이너 */
839
- .group {
840
- border: 1px solid #eee;
841
- padding: 15px;
842
- margin-bottom: 15px;
843
- border-radius: 5px;
844
- background: white;
845
- }
846
-
847
- /* 버튼 스타일 */
848
- .primary-btn {
849
- background: #1f77b4 !important;
850
- border: none !important;
851
- }
852
-
853
- /* 입력 필드 */
854
- .textbox {
855
- border: 1px solid #ddd !important;
856
- border-radius: 4px !important;
857
- }
858
-
859
- /* 프로그레스바 컨테이너 */
860
- .progress-container {
861
- position: fixed;
862
- top: 0;
863
- left: 0;
864
- width: 100%;
865
- height: 6px;
866
- background: #e0e0e0;
867
- z-index: 1000;
868
- }
869
-
870
- /* 프로그레스바 */
871
- .progress-bar {
872
- height: 100%;
873
- background: linear-gradient(90deg, #2196F3, #00BCD4);
874
- box-shadow: 0 0 10px rgba(33, 150, 243, 0.5);
875
- transition: width 0.3s ease;
876
- animation: progress-glow 1.5s ease-in-out infinite;
877
- }
878
-
879
- /* 프로그레스 텍스트 */
880
- .progress-text {
881
- position: fixed;
882
- top: 8px;
883
- left: 50%;
884
- transform: translateX(-50%);
885
- background: #333;
886
- color: white;
887
- padding: 4px 12px;
888
- border-radius: 15px;
889
- font-size: 14px;
890
- z-index: 1001;
891
- box-shadow: 0 2px 5px rgba(0,0,0,0.2);
892
- }
893
-
894
- /* 프로그레스바 애니메이션 */
895
- @keyframes progress-glow {
896
- 0% {
897
- box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
898
- }
899
- 50% {
900
- box-shadow: 0 0 20px rgba(33, 150, 243, 0.8);
901
- }
902
- 100% {
903
- box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
904
- }
905
- }
906
-
907
- /* 반응형 디자인 */
908
- @media (max-width: 768px) {
909
- .group {
910
- padding: 10px;
911
- margin-bottom: 15px;
912
- }
913
-
914
- .progress-text {
915
- font-size: 12px;
916
- padding: 3px 10px;
917
- }
918
- }
919
-
920
- /* 로딩 상태 표시 개선 */
921
- .loading {
922
- opacity: 0.7;
923
- pointer-events: none;
924
- transition: opacity 0.3s ease;
925
- }
926
-
927
- /* 결과 컨테이너 애니메이션 */
928
- .group {
929
- transition: all 0.3s ease;
930
- opacity: 0;
931
- transform: translateY(20px);
932
- }
933
-
934
- .group.visible {
935
- opacity: 1;
936
- transform: translateY(0);
937
- }
938
-
939
- /* Examples 스타일링 */
940
- .examples-table {
941
- margin-top: 10px !important;
942
- margin-bottom: 20px !important;
943
- }
944
-
945
- .examples-table button {
946
- background-color: #f0f0f0 !important;
947
- border: 1px solid #ddd !important;
948
- border-radius: 4px !important;
949
- padding: 5px 10px !important;
950
- margin: 2px !important;
951
- transition: all 0.3s ease !important;
952
- }
953
-
954
- .examples-table button:hover {
955
- background-color: #e0e0e0 !important;
956
- transform: translateY(-1px) !important;
957
- box-shadow: 0 2px 5px rgba(0,0,0,0.1) !important;
958
- }
959
-
960
- .examples-table .label {
961
- font-weight: bold !important;
962
- color: #444 !important;
963
- margin-bottom: 5px !important;
964
- }
965
- """
966
-
967
-
968
- def get_article_content(url):
969
- try:
970
- headers = {
971
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
972
- }
973
- session = requests.Session()
974
- retries = Retry(total=3, backoff_factor=0.5)
975
- session.mount('https://', HTTPAdapter(max_retries=retries))
976
-
977
- response = session.get(url, headers=headers, timeout=30)
978
- response.raise_for_status()
979
- soup = BeautifulSoup(response.content, 'html.parser')
980
-
981
- # 메타 데이터 추출
982
- title = soup.find('meta', property='og:title') or soup.find('title')
983
- title = title.get('content', '') if hasattr(title, 'get') else title.string if title else ''
984
-
985
- description = soup.find('meta', property='og:description') or soup.find('meta', {'name': 'description'})
986
- description = description.get('content', '') if description else ''
987
-
988
- # 본문 추출 개선
989
- article_content = ''
990
-
991
- # 일반적인 기사 본문 컨테이너 검색
992
- content_selectors = [
993
- 'article', '.article-body', '.article-content', '#article-body',
994
- '.story-body', '.post-content', '.entry-content', '.content-body',
995
- '[itemprop="articleBody"]', '.story-content'
996
- ]
997
-
998
- for selector in content_selectors:
999
- content = soup.select_one(selector)
1000
- if content:
1001
- # 불필요한 요소 제거
1002
- for tag in content.find_all(['script', 'style', 'nav', 'header', 'footer', 'aside']):
1003
- tag.decompose()
1004
-
1005
- # 단락 추출
1006
- paragraphs = content.find_all(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
1007
- if paragraphs:
1008
- article_content = '\n\n'.join([p.get_text().strip() for p in paragraphs if p.get_text().strip()])
1009
- break
1010
-
1011
- # 백업 방법: 모든 단락 추출
1012
- if not article_content:
1013
- paragraphs = soup.find_all('p')
1014
- article_content = '\n\n'.join([p.get_text().strip() for p in paragraphs if len(p.get_text().strip()) > 50])
1015
-
1016
- # 최종 콘텐츠 구성
1017
- full_content = f"Title: {title}\n\nDescription: {description}\n\nContent:\n{article_content}"
1018
-
1019
- # 텍스트 정제
1020
- full_content = re.sub(r'\s+', ' ', full_content) # 연속된 공백 제거
1021
- full_content = re.sub(r'\n\s*\n', '\n\n', full_content) # 연속된 빈 줄 제거
1022
-
1023
- return full_content.strip()
1024
-
1025
- except Exception as e:
1026
- print(f"Crawling error details: {str(e)}") # 디버깅을 위한 상세 에러 출력
1027
- return f"Error crawling content: {str(e)}"
1028
-
1029
- def respond(url, history, system_message, max_tokens, temperature, top_p):
1030
- if not url.startswith('http'):
1031
- history.append((url, "올바른 URL을 입력해주세요."))
1032
- return history
1033
-
1034
- try:
1035
- article_content = get_article_content(url)
1036
-
1037
- translation_prompt = f"""다음 영문 기사를 한국어로 번역하고 기사를 작성해주세요.
1038
-
1039
- 1단계: 전문 번역
1040
- ===번역 시작===
1041
- {article_content}
1042
- ===번역 끝===
1043
-
1044
- 2단계: 기사 작성 가이드라인
1045
- 다음 요구사항에 따라 한국어 기사를 작성하세요:
1046
-
1047
- 1. 구조
1048
- - 헤드라인: 핵심 내용을 담은 강력한 제목
1049
- - 부제목: 헤드라인 보완 설명
1050
- - 리드문: 기사의 핵심을 요약한 첫 문단
1051
- - 본문: 상세 내용 전개
1052
-
1053
- 2. 작성 규칙
1054
- - 객관적이고 정확한 사실 전달
1055
- - 문장은 '다.'로 종결
1056
- - 단락 간 자연스러운 흐름
1057
- - 인용구는 따옴표 처리
1058
- - 핵심 정보를 앞부분에 배치
1059
- - 전문 용어는 적절한 설명 추가
1060
-
1061
- 3. 형식
1062
- - 적절한 단락 구분
1063
- - 읽기 쉬운 문장 길이
1064
- - 논리적인 정보 구성
1065
-
1066
- 각 단계는 '===번역===', '===기사==='로 명확히 구분하여 출력하세요.
1067
- """
1068
-
1069
- messages = [
1070
- {
1071
- "role": "system",
1072
- "content": system_message
1073
- },
1074
- {"role": "user", "content": translation_prompt}
1075
- ]
1076
-
1077
- history.append((url, "번역 및 기사 작성을 시작합니다..."))
1078
-
1079
- full_response = ""
1080
- for message in client.chat.completions.create(
1081
- model="CohereForAI/c4ai-command-r-plus-08-2024",
1082
- max_tokens=max_tokens,
1083
- stream=True,
1084
- temperature=temperature,
1085
- top_p=top_p,
1086
- messages=messages,
1087
- ):
1088
- if hasattr(message.choices[0].delta, 'content'):
1089
- token = message.choices[0].delta.content
1090
- if token:
1091
- full_response += token
1092
- history[-1] = (url, full_response)
1093
- yield history
1094
-
1095
- except Exception as e:
1096
- error_message = f"처리 중 오류가 발생했습니다: {str(e)}"
1097
- history.append((url, error_message))
1098
- yield history
1099
-
1100
- return history
1101
-
1102
-
1103
- def continue_writing(history, system_message, max_tokens, temperature, top_p):
1104
- if not history:
1105
- return history
1106
-
1107
- last_response = history[-1][1] if history else ""
1108
- continue_prompt = f"""이전 내용을 이어서 계속 작성해주세요.
1109
- 마지막 응답: {last_response}
1110
-
1111
- 추가 지침:
1112
- 1. 이전 내용의 맥락을 유지하며 자연스럽게 이어서 작성
1113
- 2. 새로운 정보나 상세 설명을 추가
1114
- 3. 필요한 경우 보충 설명이나 분석 제공
1115
- 4. 기사 형식과 스타일 유지
1116
- 5. 필요한 경우 추가적인 이미지 프롬프트 생성
1117
- """
1118
-
1119
- # 메시지 구조 수정
1120
- messages = [
1121
- {"role": "system", "content": system_message},
1122
- {"role": "user", "content": continue_prompt} # 사용자 메시지로 시작
1123
- ]
1124
-
1125
- try:
1126
- full_response = ""
1127
- for message in client.chat.completions.create(
1128
- model="CohereForAI/c4ai-command-r-plus-08-2024",
1129
- max_tokens=max_tokens,
1130
- stream=True,
1131
- temperature=temperature,
1132
- top_p=top_p,
1133
- messages=messages,
1134
- ):
1135
- if hasattr(message.choices[0].delta, 'content'):
1136
- token = message.choices[0].delta.content
1137
- if token:
1138
- full_response += token
1139
- # 이전 대화 기록을 유지하면서 새로운 응답 추가
1140
- new_history = history.copy()
1141
- new_history.append(("계속 작성", full_response))
1142
- yield new_history
1143
-
1144
- except Exception as e:
1145
- error_message = f"계속 작성 중 오류가 발생했습니다: {str(e)}"
1146
- new_history = history.copy()
1147
- new_history.append(("오류", error_message))
1148
- yield new_history
1149
-
1150
- return history
1151
-
1152
- with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", css=css, title="NewsAI 서비스") as iface:
1153
- init_db() # DB 초기화
1154
-
1155
- with gr.Tabs():
1156
- # DB 저장/불러오기 탭
1157
- with gr.Tab("DB 검색"):
1158
- gr.Markdown("삼성/미국 검색 결과를 DB에 저장하고 불러옵니다.")
1159
-
1160
- with gr.Row():
1161
- search_button = gr.Button("검색: samsung/미국", variant="primary")
1162
- load_button = gr.Button("출력: samsung/미국", variant="secondary")
1163
-
1164
- results_display = gr.Markdown()
1165
-
1166
- # 버튼 이벤트 연결
1167
- search_button.click(
1168
- fn=search_samsung_us,
1169
- outputs=results_display
1170
- )
1171
-
1172
- load_button.click(
1173
- fn=load_samsung_us,
1174
- outputs=results_display
1175
- )
1176
-
1177
- with gr.Tab("국가별"):
1178
- gr.Markdown("검색어를 입력하고 원하는 국가(한국 제외)를를 선택하면, 검색어와 일치하는 24시간 이내 뉴스를 최대 100개 출력합니다.")
1179
- gr.Markdown("국가 선택후 검색어에 '한글'을 입력하면 현지 언어로 번역되어 검색합니다. 예: 'Taiwan' 국가 선택후 '삼성' 입력시 '三星'으로 자동 검색")
1180
-
1181
- with gr.Column():
1182
- with gr.Row():
1183
- query = gr.Textbox(label="검색어")
1184
- country = gr.Dropdown(
1185
- choices=sorted(list(COUNTRY_LOCATIONS.keys())),
1186
- label="국가",
1187
- value="United States"
1188
- )
1189
-
1190
- # Examples 추가
1191
- gr.Examples(
1192
- examples=[
1193
- "artificial intelligence",
1194
- "NVIDIA",
1195
- "OPENAI",
1196
- "META LLAMA",
1197
- "black forest labs",
1198
- "GOOGLE gemini",
1199
- "anthropic Claude",
1200
- "X.AI",
1201
- "HUGGINGFACE",
1202
- "HYNIX",
1203
- "Large Language model",
1204
- "CHATGPT",
1205
- "StabilityAI",
1206
- "MISTRALAI",
1207
- "QWEN",
1208
- "MIDJOURNEY",
1209
- "GPU"
1210
- ],
1211
- inputs=query,
1212
- label="자주 사용되는 검색어"
1213
- )
1214
-
1215
- status_message = gr.Markdown("", visible=True)
1216
- translated_query_display = gr.Markdown(visible=False)
1217
- search_button = gr.Button("검색", variant="primary")
1218
-
1219
- progress = gr.Progress()
1220
- articles_state = gr.State([])
1221
-
1222
- article_components = []
1223
- for i in range(100):
1224
- with gr.Group(visible=False) as article_group:
1225
- title = gr.Markdown()
1226
- image = gr.Image(width=200, height=150)
1227
- snippet = gr.Markdown()
1228
- info = gr.Markdown()
1229
-
1230
- article_components.append({
1231
- 'group': article_group,
1232
- 'title': title,
1233
- 'image': image,
1234
- 'snippet': snippet,
1235
- 'info': info,
1236
- 'index': i,
1237
- })
1238
-
1239
- # 전세계 탭
1240
- with gr.Tab("전세계"):
1241
- gr.Markdown("대륙별로 24시간 이내 뉴스를 검색합니다.")
1242
-
1243
- with gr.Column():
1244
- with gr.Column(elem_id="status_area"):
1245
- with gr.Row():
1246
- query_global = gr.Textbox(label="검색어")
1247
- region_select = gr.Dropdown(
1248
- choices=REGIONS,
1249
- label="지역 선택",
1250
- value="동아시아"
1251
- )
1252
- search_button_global = gr.Button("검색", variant="primary")
1253
-
1254
- status_message_global = gr.Markdown("")
1255
- translated_query_display_global = gr.Markdown("")
1256
-
1257
- with gr.Column(elem_id="results_area"):
1258
- articles_state_global = gr.State([])
1259
- global_article_components = []
1260
- for i in range(MAX_GLOBAL_RESULTS):
1261
- with gr.Group(visible=False) as article_group:
1262
- title = gr.Markdown()
1263
- image = gr.Image(width=200, height=150)
1264
- snippet = gr.Markdown()
1265
- info = gr.Markdown()
1266
-
1267
- global_article_components.append({
1268
- 'group': article_group,
1269
- 'title': title,
1270
- 'image': image,
1271
- 'snippet': snippet,
1272
- 'info': info,
1273
- 'index': i,
1274
- })
1275
-
1276
- # AI 번역 탭
1277
- with gr.Tab("AI 기사 생성"):
1278
- gr.Markdown("뉴스 URL을 입력하면 AI가 한국어로 번역하여 기사 형식으로 작성합니다.")
1279
- gr.Markdown("이미지 생성: https://huggingface.co/spaces/ginipick/FLUXllama ")
1280
-
1281
- with gr.Column():
1282
- chatbot = gr.Chatbot(height=600)
1283
-
1284
- with gr.Row():
1285
- url_input = gr.Textbox(
1286
- label="뉴스 URL",
1287
- placeholder="https://..."
1288
- )
1289
-
1290
- with gr.Row():
1291
- translate_button = gr.Button("기사 생성", variant="primary")
1292
- continue_button = gr.Button("계속 이어서 작성", variant="secondary")
1293
-
1294
- with gr.Accordion("고급 설정", open=False):
1295
- system_message = gr.Textbox(
1296
- value="""You are a professional translator and journalist. Follow these steps strictly:
1297
- 1. TRANSLATION
1298
- - Start with ===번역=== marker
1299
- - Provide accurate Korean translation
1300
- - Maintain original meaning and context
1301
- 2. ARTICLE WRITING
1302
- - Start with ===기사=== marker
1303
- - Write a new Korean news article based on the translation
1304
- - Follow newspaper article format
1305
- - Use formal news writing style
1306
- - End sentences with '다.'
1307
- - Include headline and subheadline
1308
- - Organize paragraphs clearly
1309
- - Put key information first
1310
- - Use quotes appropriately
1311
-
1312
- 3. IMAGE PROMPT GENERATION
1313
- - Start with ===이미지 프롬프트=== marker
1314
- - Create detailed Korean prompts for image generation
1315
- - Prompts should reflect the article's main theme and content
1316
- - Include key visual elements mentioned in the article
1317
- - Specify style, mood, and composition
1318
- - Format: "이미지 설명: [상세 설명]"
1319
- - Add style keywords: "스타일: [관련 키워드들]"
1320
- - Add mood keywords: "분위기: [관련 키워드들]"
1321
- IMPORTANT:
1322
- - Must complete all three steps in order
1323
- - Clearly separate each section with markers
1324
- - Never skip or combine steps
1325
- - Ensure image prompts align with article content""",
1326
- label="System message"
1327
- )
1328
-
1329
- max_tokens = gr.Slider(
1330
- minimum=1,
1331
- maximum=7800,
1332
- value=7624,
1333
- step=1,
1334
- label="Max new tokens"
1335
- )
1336
- temperature = gr.Slider(
1337
- minimum=0.1,
1338
- maximum=4.0,
1339
- value=0.7,
1340
- step=0.1,
1341
- label="Temperature"
1342
- )
1343
- top_p = gr.Slider(
1344
- minimum=0.1,
1345
- maximum=1.0,
1346
- value=0.95,
1347
- step=0.05,
1348
- label="Top-P"
1349
- )
1350
-
1351
- # 이벤트 연결 부분
1352
- # 국가별 탭 이벤트
1353
- search_outputs = [status_message, translated_query_display, gr.Markdown(visible=False)]
1354
- for comp in article_components:
1355
- search_outputs.extend([
1356
- comp['group'], comp['title'], comp['image'],
1357
- comp['snippet'], comp['info']
1358
- ])
1359
- search_outputs.append(articles_state)
1360
-
1361
- search_button.click(
1362
- fn=search_and_display,
1363
- inputs=[query, country, articles_state],
1364
- outputs=search_outputs,
1365
- show_progress=True
1366
- )
1367
-
1368
- # 전세계 탭 이벤트
1369
- global_search_outputs = [status_message_global, translated_query_display_global]
1370
- for comp in global_article_components:
1371
- global_search_outputs.extend([
1372
- comp['group'], comp['title'], comp['image'],
1373
- comp['snippet'], comp['info']
1374
- ])
1375
- global_search_outputs.append(articles_state_global)
1376
-
1377
- search_button_global.click(
1378
- fn=search_global,
1379
- inputs=[query_global, region_select, articles_state_global],
1380
- outputs=global_search_outputs,
1381
- show_progress=True
1382
- )
1383
-
1384
- # AI 번역 탭 이벤트
1385
- translate_button.click(
1386
- fn=respond,
1387
- inputs=[
1388
- url_input,
1389
- chatbot,
1390
- system_message,
1391
- max_tokens,
1392
- temperature,
1393
- top_p,
1394
- ],
1395
- outputs=chatbot
1396
- )
1397
-
1398
- # 계속 작성 버튼 이벤트
1399
- continue_button.click(
1400
- fn=continue_writing,
1401
- inputs=[
1402
- chatbot,
1403
- system_message,
1404
- max_tokens,
1405
- temperature,
1406
- top_p,
1407
- ],
1408
- outputs=chatbot
1409
- )
1410
-
1411
- iface.launch(
1412
- server_name="0.0.0.0",
1413
- server_port=7860,
1414
- share=True,
1415
- auth=("gini","pick"),
1416
- ssl_verify=False,
1417
- show_error=True
1418
- )