Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
AI κΈ°λ° μκΆ λΆμ μμ€ν
-
|
| 3 |
Dataset: https://huggingface.co/datasets/ginipick/market
|
| 4 |
"""
|
| 5 |
import gradio as gr
|
|
@@ -168,34 +168,15 @@ class MarketAnalyzer:
|
|
| 168 |
"""ν¬κ΄μ μΈ μΈμ¬μ΄νΈ μμ±"""
|
| 169 |
insights = []
|
| 170 |
|
| 171 |
-
# 1. μ
μ’
λ³ μ ν¬ μ (μκΆμ
μ’
μ€λΆλ₯)
|
| 172 |
insights.append(self._create_top_categories_chart())
|
| 173 |
-
|
| 174 |
-
# 2. λλΆλ₯λ³ λΆν¬ (νμ΄ μ°¨νΈ)
|
| 175 |
insights.append(self._create_major_category_pie())
|
| 176 |
-
|
| 177 |
-
# 3. μΈ΅λ³ λΆν¬ μμΈ λΆμ
|
| 178 |
insights.append(self._create_floor_analysis())
|
| 179 |
-
|
| 180 |
-
# 4. μ§μλ³ μ
μ’
λ€μμ± μ§μ
|
| 181 |
insights.append(self._create_diversity_index())
|
| 182 |
-
|
| 183 |
-
# 5. νλμ°¨μ΄μ¦ vs κ°μΈμ¬μ
μ λΆμ
|
| 184 |
insights.append(self._create_franchise_analysis())
|
| 185 |
-
|
| 186 |
-
# 6. μ
μ’
λ³ μΈ΅ μ νΈλ
|
| 187 |
insights.append(self._create_floor_preference())
|
| 188 |
-
|
| 189 |
-
# 7. μκ΅°κ΅¬λ³ μκΆ λ°μ§λ TOP 20
|
| 190 |
insights.append(self._create_district_density())
|
| 191 |
-
|
| 192 |
-
# 8. μ
μ’
μκ΄κ΄κ³ (κ°μ μ§μμ μμ£Ό λνλλ μ
μ’
)
|
| 193 |
insights.append(self._create_category_correlation())
|
| 194 |
-
|
| 195 |
-
# 9. μλΆλ₯ νΈλ λ (μμ 20κ°)
|
| 196 |
insights.append(self._create_subcategory_trends())
|
| 197 |
-
|
| 198 |
-
# 10. μ§μλ³ νΉν μ
μ’
|
| 199 |
insights.append(self._create_regional_specialization())
|
| 200 |
|
| 201 |
return insights
|
|
@@ -244,7 +225,6 @@ class MarketAnalyzer:
|
|
| 244 |
floor_data = self.df['μΈ΅μ 보_μ«μ'].dropna()
|
| 245 |
floor_counts = floor_data.value_counts().sort_index()
|
| 246 |
|
| 247 |
-
# μ§ν, 1μΈ΅, 2μΈ΅ μ΄μμΌλ‘ κ·Έλ£Ήν
|
| 248 |
underground = floor_counts[floor_counts.index < 0].sum()
|
| 249 |
first_floor = floor_counts.get(1, 0)
|
| 250 |
upper_floors = floor_counts[floor_counts.index > 1].sum()
|
|
@@ -273,9 +253,8 @@ class MarketAnalyzer:
|
|
| 273 |
if 'μꡰꡬλͺ
' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 274 |
return None
|
| 275 |
|
| 276 |
-
# κ° μκ΅°κ΅¬λ³ μ
μ’
λ€μμ± κ³μ° (μ
μ’
μ / μ 체 μ ν¬ μ)
|
| 277 |
diversity_data = []
|
| 278 |
-
for district in self.df['μꡰꡬλͺ
'].unique()[:20]:
|
| 279 |
district_df = self.df[self.df['μꡰꡬλͺ
'] == district]
|
| 280 |
num_categories = district_df['μκΆμ
μ’
μ€λΆλ₯λͺ
'].nunique()
|
| 281 |
total_stores = len(district_df)
|
|
@@ -307,7 +286,6 @@ class MarketAnalyzer:
|
|
| 307 |
if 'λΈλλλͺ
' not in self.df.columns:
|
| 308 |
return None
|
| 309 |
|
| 310 |
-
# λΈλλλͺ
μ΄ μμΌλ©΄ νλμ°¨μ΄μ¦λ‘ κ°μ£Ό
|
| 311 |
franchise_count = self.df['λΈλλλͺ
'].notna().sum()
|
| 312 |
individual_count = self.df['λΈλλλͺ
'].isna().sum()
|
| 313 |
|
|
@@ -333,7 +311,6 @@ class MarketAnalyzer:
|
|
| 333 |
if 'μΈ΅μ 보_μ«μ' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 334 |
return None
|
| 335 |
|
| 336 |
-
# μμ 10κ° μ
μ’
μ ν
|
| 337 |
top_categories = self.df['μκΆμ
μ’
μ€λΆλ₯λͺ
'].value_counts().head(10).index
|
| 338 |
floor_pref_data = []
|
| 339 |
|
|
@@ -390,21 +367,17 @@ class MarketAnalyzer:
|
|
| 390 |
return {'type': 'plot', 'data': fig, 'title': 'μ§μ λ°μ§λ λΆμ'}
|
| 391 |
|
| 392 |
def _create_category_correlation(self) -> Dict:
|
| 393 |
-
"""μ
μ’
μκ΄κ΄κ³
|
| 394 |
if 'μꡰꡬλͺ
' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 395 |
return None
|
| 396 |
|
| 397 |
-
# μμ 10κ° μ
μ’
λ§ μ ν
|
| 398 |
top_categories = self.df['μκΆμ
μ’
μ€λΆλ₯λͺ
'].value_counts().head(10).index.tolist()
|
| 399 |
-
|
| 400 |
-
# κ° μꡰꡬλ³λ‘ μ
μ’
μ‘΄μ¬ μ¬λΆ λ§€νΈλ¦μ€ μμ±
|
| 401 |
districts = self.df['μꡰꡬλͺ
'].unique()
|
| 402 |
correlation_matrix = np.zeros((len(top_categories), len(top_categories)))
|
| 403 |
|
| 404 |
for i, cat1 in enumerate(top_categories):
|
| 405 |
for j, cat2 in enumerate(top_categories):
|
| 406 |
if i != j:
|
| 407 |
-
# λ μ
μ’
μ΄ κ°μ μꡰꡬμ μ‘΄μ¬νλ νμ
|
| 408 |
coexist_count = 0
|
| 409 |
for district in districts:
|
| 410 |
district_df = self.df[self.df['μꡰꡬλͺ
'] == district]
|
|
@@ -456,7 +429,6 @@ class MarketAnalyzer:
|
|
| 456 |
if 'μλλͺ
' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 457 |
return None
|
| 458 |
|
| 459 |
-
# κ° μλλ³ μμ 3κ° μ
μ’
|
| 460 |
specialization_data = []
|
| 461 |
for region in self.df['μλλͺ
'].unique():
|
| 462 |
region_df = self.df[self.df['μλλͺ
'] == region]
|
|
@@ -490,7 +462,6 @@ class MarketAnalyzer:
|
|
| 490 |
|
| 491 |
m = folium.Map(location=[center_lat, center_lon], zoom_start=11, tiles='OpenStreetMap')
|
| 492 |
|
| 493 |
-
# ννΈλ§΅
|
| 494 |
heat_data = [[row['μοΏ½οΏ½'], row['κ²½λ']] for _, row in df_sample.iterrows()]
|
| 495 |
HeatMap(heat_data, radius=15, blur=25, max_zoom=13).add_to(m)
|
| 496 |
|
|
@@ -522,7 +493,6 @@ class LLMQueryProcessor:
|
|
| 522 |
"""Fireworks AI κΈ°λ° μμ°μ΄ μ²λ¦¬ (μ€νΈλ¦¬λ° μ§μ + μΉκ²μ)"""
|
| 523 |
|
| 524 |
def __init__(self, api_key: str = None):
|
| 525 |
-
# νκ²½λ³μμμ API ν€ κ°μ Έμ€κΈ°
|
| 526 |
self.api_key = api_key or os.getenv("FIREWORKS_API_KEY")
|
| 527 |
self.base_url = "https://api.fireworks.ai/inference/v1/chat/completions"
|
| 528 |
|
|
@@ -532,7 +502,6 @@ class LLMQueryProcessor:
|
|
| 532 |
def process_query_stream(self, query: str, data_context: Dict, chat_history: List = None, web_search_results: str = None):
|
| 533 |
"""μμ°μ΄ 쿼리 μ²λ¦¬ (μ€νΈλ¦¬λ° λͺ¨λ) - μΉκ²μ κ²°κ³Ό ν¬ν¨"""
|
| 534 |
|
| 535 |
-
# μΉκ²μ κ²°κ³Όκ° μμΌλ©΄ 컨ν
μ€νΈμ μΆκ°
|
| 536 |
web_context = ""
|
| 537 |
if web_search_results and "β οΈ" not in web_search_results:
|
| 538 |
web_context = f"""
|
|
@@ -551,7 +520,8 @@ class LLMQueryProcessor:
|
|
| 551 |
|
| 552 |
ꡬ체μ μΈ μ«μμ λΉμ¨λ‘ μ λμ λΆμμ μ 곡νμΈμ.
|
| 553 |
μ°½μ
, ν¬μ, κ²½μ λΆμ κ΄μ μμ μ€μ©μ μΈμ¬μ΄νΈλ₯Ό μ 곡νμΈμ.
|
| 554 |
-
μΉ κ²μ κ²°κ³Όκ° μ 곡λ κ²½μ° μ΅μ νΈλ λμ ν¨κ» λΆμνμΈμ.
|
|
|
|
| 555 |
|
| 556 |
messages = [{"role": "system", "content": system_prompt}]
|
| 557 |
if chat_history:
|
|
@@ -563,7 +533,7 @@ class LLMQueryProcessor:
|
|
| 563 |
"max_tokens": 4800,
|
| 564 |
"temperature": 0.7,
|
| 565 |
"messages": messages,
|
| 566 |
-
"stream": True
|
| 567 |
}
|
| 568 |
|
| 569 |
headers = {
|
|
@@ -577,16 +547,15 @@ class LLMQueryProcessor:
|
|
| 577 |
headers=headers,
|
| 578 |
json=payload,
|
| 579 |
timeout=60,
|
| 580 |
-
stream=True
|
| 581 |
)
|
| 582 |
|
| 583 |
if response.status_code == 200:
|
| 584 |
-
# μ€νΈλ¦¬λ° μλ΅ μ²λ¦¬
|
| 585 |
for line in response.iter_lines():
|
| 586 |
if line:
|
| 587 |
line_text = line.decode('utf-8')
|
| 588 |
if line_text.startswith('data: '):
|
| 589 |
-
data_str = line_text[6:]
|
| 590 |
if data_str.strip() == '[DONE]':
|
| 591 |
break
|
| 592 |
try:
|
|
@@ -639,15 +608,16 @@ def load_data(regions):
|
|
| 639 |
|
| 640 |
app_state.analyzer = MarketAnalyzer(df)
|
| 641 |
|
| 642 |
-
# κΈ°λ³Έ ν΅κ³
|
| 643 |
stats = f"""
|
| 644 |
β
**λ°μ΄ν° λ‘λ μλ£!**
|
| 645 |
-
|
| 646 |
-
π
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
|
|
|
|
|
|
| 651 |
"""
|
| 652 |
|
| 653 |
return stats, gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)
|
|
@@ -670,7 +640,6 @@ def generate_insights():
|
|
| 670 |
else:
|
| 671 |
result.append(None)
|
| 672 |
|
| 673 |
-
# λΆμ‘±ν μ°¨νΈλ NoneμΌλ‘ μ±μ°κΈ°
|
| 674 |
while len(result) < 11:
|
| 675 |
result.append(None)
|
| 676 |
|
|
@@ -678,7 +647,7 @@ def generate_insights():
|
|
| 678 |
|
| 679 |
|
| 680 |
def chat_respond(message, history):
|
| 681 |
-
"""μ±λ΄ μλ΅ (μ€νΈλ¦¬λ° λͺ¨λ + μΉκ²μ)
|
| 682 |
if app_state.analyzer is None:
|
| 683 |
yield history + [[message, "β λ¨Όμ λ°μ΄ν°λ₯Ό λ‘λν΄μ£ΌμΈμ!"]]
|
| 684 |
return
|
|
@@ -686,39 +655,31 @@ def chat_respond(message, history):
|
|
| 686 |
data_context = app_state.analyzer.analyze_for_llm()
|
| 687 |
|
| 688 |
try:
|
| 689 |
-
# LLM νλ‘μΈμ μ΄κΈ°ν
|
| 690 |
if app_state.llm_processor is None:
|
| 691 |
app_state.llm_processor = LLMQueryProcessor()
|
| 692 |
|
| 693 |
-
# Brave Search ν΄λΌμ΄μΈνΈ μ΄κΈ°ν
|
| 694 |
if app_state.brave_client is None:
|
| 695 |
try:
|
| 696 |
app_state.brave_client = BraveSearchClient()
|
| 697 |
except:
|
| 698 |
app_state.brave_client = None
|
| 699 |
|
| 700 |
-
# π μλ μΉκ²μ μν
|
| 701 |
web_results = None
|
| 702 |
if app_state.brave_client and app_state.brave_client.api_key:
|
| 703 |
-
# κ²μ 쿼리 μμ± (μ¬μ©μ μ§λ¬Έμμ ν΅μ¬ ν€μλ μΆμΆ)
|
| 704 |
search_query = f"νκ΅ μκΆ μ°½μ
νΈλ λ {message}"
|
| 705 |
web_results = app_state.brave_client.search(search_query, count=3)
|
| 706 |
|
| 707 |
-
# λν νμ€ν 리 ꡬμ±
|
| 708 |
chat_hist = []
|
| 709 |
for user_msg, bot_msg in history:
|
| 710 |
chat_hist.append({"role": "user", "content": user_msg})
|
| 711 |
chat_hist.append({"role": "assistant", "content": bot_msg})
|
| 712 |
|
| 713 |
-
# μ λ©μμ§ μΆκ°
|
| 714 |
history = history + [[message, ""]]
|
| 715 |
|
| 716 |
-
# μΉκ²μ μν νμ
|
| 717 |
if web_results and "β οΈ" not in web_results:
|
| 718 |
history[-1][1] = "π μΉ κ²μ μ€...\n\n"
|
| 719 |
yield history
|
| 720 |
|
| 721 |
-
# μ€νΈλ¦¬λ° μλ΅ (μΉκ²μ κ²°κ³Ό ν¬ν¨)
|
| 722 |
full_response = ""
|
| 723 |
for chunk in app_state.llm_processor.process_query_stream(message, data_context, chat_hist, web_results):
|
| 724 |
full_response += chunk
|
|
@@ -726,7 +687,6 @@ def chat_respond(message, history):
|
|
| 726 |
yield history
|
| 727 |
|
| 728 |
except ValueError as e:
|
| 729 |
-
# API ν€κ° μλ κ²½μ° κΈ°λ³Έ ν΅κ³ μ 곡
|
| 730 |
response = f"""π **κΈ°λ³Έ λ°μ΄ν° λΆμ κ²°κ³Ό**
|
| 731 |
|
| 732 |
**μ 체 νν©**
|
|
@@ -738,119 +698,520 @@ def chat_respond(message, history):
|
|
| 738 |
νκ²½λ³μλ₯Ό μ€μ νμΈμ:
|
| 739 |
```bash
|
| 740 |
export FIREWORKS_API_KEY="your_api_key_here"
|
| 741 |
-
export BRAVE_API_KEY="your_brave_api_key_here"
|
| 742 |
-
```
|
| 743 |
-
|
| 744 |
-
λλ Hugging Face Spaceμμλ Settings > Variables μμ μ€μ νμΈμ."""
|
| 745 |
|
| 746 |
history = history + [[message, response]]
|
| 747 |
yield history
|
| 748 |
|
| 749 |
|
| 750 |
# ============================================================================
|
| 751 |
-
#
|
| 752 |
# ============================================================================
|
| 753 |
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 758 |
|
| 759 |
-
|
| 760 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
|
| 762 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 763 |
gr.HTML("""
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
padding: 10px 20px;
|
| 779 |
-
border-radius: 25px;
|
| 780 |
-
text-decoration: none;
|
| 781 |
-
font-weight: 600;
|
| 782 |
-
font-size: 0.95em;
|
| 783 |
-
transition: all 0.3s ease;
|
| 784 |
-
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
| 785 |
-
position: relative;
|
| 786 |
-
overflow: hidden;
|
| 787 |
-
}
|
| 788 |
-
|
| 789 |
-
.badge::before {
|
| 790 |
-
content: '';
|
| 791 |
-
position: absolute;
|
| 792 |
-
top: 0;
|
| 793 |
-
left: -100%;
|
| 794 |
-
width: 100%;
|
| 795 |
-
height: 100%;
|
| 796 |
-
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
|
| 797 |
-
transition: left 0.5s;
|
| 798 |
-
}
|
| 799 |
-
|
| 800 |
-
.badge:hover::before {
|
| 801 |
-
left: 100%;
|
| 802 |
-
}
|
| 803 |
-
|
| 804 |
-
.badge:hover {
|
| 805 |
-
transform: translateY(-3px);
|
| 806 |
-
box-shadow: 0 6px 25px rgba(0,0,0,0.3);
|
| 807 |
-
}
|
| 808 |
-
|
| 809 |
-
.badge-kakao {
|
| 810 |
-
background: linear-gradient(135deg, #FEE500 0%, #FFEB3B 100%);
|
| 811 |
-
color: #3C1E1E;
|
| 812 |
-
}
|
| 813 |
-
|
| 814 |
-
.badge-kakao:hover {
|
| 815 |
-
background: linear-gradient(135deg, #FFD700 0%, #FFC107 100%);
|
| 816 |
-
}
|
| 817 |
-
|
| 818 |
-
.badge-ginigen {
|
| 819 |
-
background: linear-gradient(135deg, #00D9FF 0%, #0099FF 100%);
|
| 820 |
-
color: white;
|
| 821 |
-
}
|
| 822 |
-
|
| 823 |
-
.badge-ginigen:hover {
|
| 824 |
-
background: linear-gradient(135deg, #00C4E6 0%, #0080E6 100%);
|
| 825 |
-
}
|
| 826 |
-
|
| 827 |
-
.badge-icon {
|
| 828 |
-
font-size: 1.2em;
|
| 829 |
-
}
|
| 830 |
-
</style>
|
| 831 |
|
| 832 |
-
|
| 833 |
-
|
| 834 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 835 |
<span>μ€νμ±ν
λ°λ‘κ°κΈ°</span>
|
| 836 |
</a>
|
| 837 |
-
<a href="https://ginigen.ai" target="_blank" class="badge badge-
|
| 838 |
-
<span
|
| 839 |
<span>λλ
Έ λ°λλ μ λμ¨ λ¬΄λ£ μλΉμ€</span>
|
| 840 |
</a>
|
| 841 |
</div>
|
| 842 |
""")
|
| 843 |
-
|
| 844 |
|
| 845 |
-
|
| 846 |
-
|
| 847 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 848 |
|
| 849 |
-
|
| 850 |
-
|
| 851 |
-
|
| 852 |
-
|
| 853 |
-
|
| 854 |
|
| 855 |
region_select = gr.CheckboxGroup(
|
| 856 |
choices=list(MarketDataLoader.REGIONS.keys()),
|
|
@@ -858,12 +1219,22 @@ with gr.Blocks(title="AI μκΆ λΆμ μμ€ν
Pro", theme=gr.themes.Soft()) a
|
|
| 858 |
label="π λΆμ μ§μ μ ν (μ΅λ 5κ° κΆμ₯)"
|
| 859 |
)
|
| 860 |
|
| 861 |
-
load_btn = gr.Button(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 862 |
|
| 863 |
-
|
| 864 |
-
|
| 865 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 866 |
with gr.Tabs() as tabs:
|
|
|
|
| 867 |
with gr.Tab("π μΈμ¬μ΄νΈ λμ보λ", id=0) as tab1:
|
| 868 |
insights_content = gr.Column(visible=False)
|
| 869 |
|
|
@@ -875,38 +1246,42 @@ with gr.Blocks(title="AI μκΆ λΆμ μμ€ν
Pro", theme=gr.themes.Soft()) a
|
|
| 875 |
gr.Markdown("### π 10κ°μ§ μ¬μΈ΅ μκΆ μΈμ¬μ΄νΈ")
|
| 876 |
|
| 877 |
with gr.Row():
|
| 878 |
-
chart1 = gr.Plot(label="μ
μ’
λ³ μ ν¬ μ")
|
| 879 |
-
chart2 = gr.Plot(label="λλΆλ₯ λΆν¬")
|
| 880 |
|
| 881 |
with gr.Row():
|
| 882 |
-
chart3 = gr.Plot(label="μΈ΅λ³ λΆν¬")
|
| 883 |
-
chart4 = gr.Plot(label="μ
μ’
λ€μμ±")
|
| 884 |
|
| 885 |
with gr.Row():
|
| 886 |
-
chart5 = gr.Plot(label="νλμ°¨μ΄μ¦ λΆμ")
|
| 887 |
-
chart6 = gr.Plot(label="μΈ΅ μ νΈλ")
|
| 888 |
|
| 889 |
with gr.Row():
|
| 890 |
-
chart7 = gr.Plot(label="μ§μ λ°μ§λ")
|
| 891 |
-
chart8 = gr.Plot(label="μ
μ’
μκ΄κ΄κ³")
|
| 892 |
|
| 893 |
with gr.Row():
|
| 894 |
-
chart9 = gr.Plot(label="μλΆλ₯ νΈλ λ")
|
| 895 |
-
chart10 = gr.Plot(label="μ§μ νΉν")
|
| 896 |
|
| 897 |
-
|
|
|
|
| 898 |
chat_content = gr.Column(visible=False)
|
| 899 |
|
| 900 |
with chat_content:
|
| 901 |
gr.Markdown("""
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
""")
|
| 908 |
|
| 909 |
-
chatbot = gr.Chatbot(
|
|
|
|
|
|
|
|
|
|
| 910 |
|
| 911 |
with gr.Row():
|
| 912 |
msg_input = gr.Textbox(
|
|
@@ -914,15 +1289,37 @@ with gr.Blocks(title="AI μκΆ λΆμ μμ€ν
Pro", theme=gr.themes.Soft()) a
|
|
| 914 |
show_label=False,
|
| 915 |
scale=4
|
| 916 |
)
|
| 917 |
-
submit_btn = gr.Button("μ μ‘", variant="primary", scale=1)
|
| 918 |
|
| 919 |
-
# μν λ²νΌλ€
|
| 920 |
with gr.Row():
|
| 921 |
-
sample_btn1 = gr.Button("
|
| 922 |
-
sample_btn2 = gr.Button("μΉν¨μ§ ν¬ν μ§μ?", size="sm")
|
| 923 |
-
sample_btn3 = gr.Button("1
|
| 924 |
-
sample_btn4 = gr.Button("νλμ°¨μ΄μ¦ μ μ μ¨?", size="sm")
|
| 925 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 926 |
# μ΄λ²€νΈ νΈλ€λ¬
|
| 927 |
load_btn.click(
|
| 928 |
fn=load_data,
|
|
@@ -933,7 +1330,7 @@ with gr.Blocks(title="AI μκΆ λΆμ μμ€ν
Pro", theme=gr.themes.Soft()) a
|
|
| 933 |
outputs=[map_output, chart1, chart2, chart3, chart4, chart5, chart6, chart7, chart8, chart9, chart10]
|
| 934 |
)
|
| 935 |
|
| 936 |
-
# μ±λ΄ μ΄λ²€νΈ
|
| 937 |
submit_btn.click(
|
| 938 |
fn=chat_respond,
|
| 939 |
inputs=[msg_input, chatbot],
|
|
@@ -959,49 +1356,11 @@ with gr.Blocks(title="AI μκΆ λΆμ μμ€ν
Pro", theme=gr.themes.Soft()) a
|
|
| 959 |
yield result
|
| 960 |
return handler
|
| 961 |
|
| 962 |
-
sample_btn1.click(fn=create_sample_click("κ°λ¨μμ μΉ΄ν
|
| 963 |
-
sample_btn2.click(fn=create_sample_click("
|
| 964 |
-
sample_btn3.click(fn=create_sample_click("1μΈ΅μ΄ μ 리ν
|
| 965 |
-
sample_btn4.click(fn=create_sample_click("νλμ°¨μ΄μ¦
|
| 966 |
-
|
| 967 |
-
gr.Markdown("""
|
| 968 |
-
---
|
| 969 |
-
### π μ¬μ© κ°μ΄λ
|
| 970 |
-
1. μ§μ μ ν β 2. λ°μ΄ν° λ‘λ β 3. 10κ°μ§ μΈμ¬μ΄νΈ νμΈ λλ AIμκ² μ§λ¬Έ
|
| 971 |
-
|
| 972 |
-
### π AI μ±λ΄ + μΉκ²μ νμ±ν λ°©λ²
|
| 973 |
-
νκ²½λ³μ μ€μ :
|
| 974 |
-
```bash
|
| 975 |
-
export FIREWORKS_API_KEY="your_api_key_here"
|
| 976 |
-
export BRAVE_API_KEY="your_brave_api_key_here" # μΉκ²μ κΈ°λ₯μ© (μ ν)
|
| 977 |
-
```
|
| 978 |
-
|
| 979 |
-
Hugging Face Spaceμμλ:
|
| 980 |
-
1. Settings λ©λ΄ ν΄λ¦
|
| 981 |
-
2. Variables ν μ ν
|
| 982 |
-
3. New variable μΆκ°:
|
| 983 |
-
- `FIREWORKS_API_KEY` (νμ - AI λΆμμ©)
|
| 984 |
-
- `BRAVE_API_KEY` (μ ν - μΉκ²μ νμ±νμ©)
|
| 985 |
-
|
| 986 |
-
π **μΉκ²μ κΈ°λ₯**: BRAVE_API_KEY μ€μ μ μλμΌλ‘ μ΅μ μκΆ νΈλ λλ₯Ό κ²μνμ¬ λ΅λ³μ λ°μν©λλ€!
|
| 987 |
-
|
| 988 |
-
### π μ 곡λλ 10κ°μ§ λΆμ
|
| 989 |
-
1. **μ
μ’
λ³ μ ν¬ μ**: κ°μ₯ λ§μ μ
μ’
TOP 15
|
| 990 |
-
2. **λλΆλ₯ λΆν¬**: μλ§€/μμ/μλΉμ€ λ± λλΆλ₯ λΉμ¨
|
| 991 |
-
3. **μΈ΅λ³ λΆν¬**: μ§ν/1μΈ΅/μμΈ΅ μ
μ§ λΆμ
|
| 992 |
-
4. **μ
μ’
λ€μμ±**: μ§μλ³ μ
μ’
λ€μμ± μ§μ
|
| 993 |
-
5. **νλμ°¨μ΄μ¦ λΆμ**: κ°μΈ vs νλμ°¨μ΄μ¦ λΉμ¨
|
| 994 |
-
6. **μΈ΅ μ νΈλ**: μ
μ’
λ³ μ νΈ μΈ΅μ
|
| 995 |
-
7. **μ§μ λ°μ§λ**: μ ν¬ μ μμ μ§μ
|
| 996 |
-
8. **μ
μ’
μκ΄κ΄κ³**: κ°μ΄ λνλλ μ
μ’
ν¨ν΄
|
| 997 |
-
9. **μλΆλ₯ νΈλ λ**: μΈλΆ μ
μ’
λΆν¬
|
| 998 |
-
10. **μ§μ νΉν**: κ° μ§μμ νΉν μ
μ’
|
| 999 |
-
|
| 1000 |
-
π‘ **Tip**: API ν€ μμ΄λ 10κ°μ§ μκ°ν λΆμκ³Ό κΈ°λ³Έ ν΅κ³λ₯Ό νμΈν μ μμ΅λλ€!
|
| 1001 |
-
|
| 1002 |
-
β‘ **NEW!** μ±λ΄μ΄ μ΄μ μ€μκ° μ€νΈλ¦¬λ°μΌλ‘ μλ΅ν©λλ€!
|
| 1003 |
-
π **NEW!** Brave Search μΉκ²μμΌλ‘ μ΅μ μκΆ νΈλ λλ₯Ό μλ λ°μν©λλ€!
|
| 1004 |
-
""")
|
| 1005 |
|
| 1006 |
# μ€ν
|
| 1007 |
if __name__ == "__main__":
|
|
|
|
| 1 |
"""
|
| 2 |
+
AI κΈ°λ° μκΆ λΆμ μμ€ν
- Comic Classic Theme λ²μ
|
| 3 |
Dataset: https://huggingface.co/datasets/ginipick/market
|
| 4 |
"""
|
| 5 |
import gradio as gr
|
|
|
|
| 168 |
"""ν¬κ΄μ μΈ μΈμ¬μ΄νΈ μμ±"""
|
| 169 |
insights = []
|
| 170 |
|
|
|
|
| 171 |
insights.append(self._create_top_categories_chart())
|
|
|
|
|
|
|
| 172 |
insights.append(self._create_major_category_pie())
|
|
|
|
|
|
|
| 173 |
insights.append(self._create_floor_analysis())
|
|
|
|
|
|
|
| 174 |
insights.append(self._create_diversity_index())
|
|
|
|
|
|
|
| 175 |
insights.append(self._create_franchise_analysis())
|
|
|
|
|
|
|
| 176 |
insights.append(self._create_floor_preference())
|
|
|
|
|
|
|
| 177 |
insights.append(self._create_district_density())
|
|
|
|
|
|
|
| 178 |
insights.append(self._create_category_correlation())
|
|
|
|
|
|
|
| 179 |
insights.append(self._create_subcategory_trends())
|
|
|
|
|
|
|
| 180 |
insights.append(self._create_regional_specialization())
|
| 181 |
|
| 182 |
return insights
|
|
|
|
| 225 |
floor_data = self.df['μΈ΅μ 보_μ«μ'].dropna()
|
| 226 |
floor_counts = floor_data.value_counts().sort_index()
|
| 227 |
|
|
|
|
| 228 |
underground = floor_counts[floor_counts.index < 0].sum()
|
| 229 |
first_floor = floor_counts.get(1, 0)
|
| 230 |
upper_floors = floor_counts[floor_counts.index > 1].sum()
|
|
|
|
| 253 |
if 'μꡰꡬλͺ
' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 254 |
return None
|
| 255 |
|
|
|
|
| 256 |
diversity_data = []
|
| 257 |
+
for district in self.df['μꡰꡬλͺ
'].unique()[:20]:
|
| 258 |
district_df = self.df[self.df['μꡰꡬλͺ
'] == district]
|
| 259 |
num_categories = district_df['μκΆμ
μ’
μ€λΆλ₯λͺ
'].nunique()
|
| 260 |
total_stores = len(district_df)
|
|
|
|
| 286 |
if 'λΈλλλͺ
' not in self.df.columns:
|
| 287 |
return None
|
| 288 |
|
|
|
|
| 289 |
franchise_count = self.df['λΈλλλͺ
'].notna().sum()
|
| 290 |
individual_count = self.df['λΈλλλͺ
'].isna().sum()
|
| 291 |
|
|
|
|
| 311 |
if 'μΈ΅μ 보_μ«μ' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 312 |
return None
|
| 313 |
|
|
|
|
| 314 |
top_categories = self.df['μκΆμ
μ’
μ€λΆλ₯λͺ
'].value_counts().head(10).index
|
| 315 |
floor_pref_data = []
|
| 316 |
|
|
|
|
| 367 |
return {'type': 'plot', 'data': fig, 'title': 'μ§μ λ°μ§λ λΆμ'}
|
| 368 |
|
| 369 |
def _create_category_correlation(self) -> Dict:
|
| 370 |
+
"""μ
μ’
μκ΄κ΄κ³"""
|
| 371 |
if 'μꡰꡬλͺ
' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 372 |
return None
|
| 373 |
|
|
|
|
| 374 |
top_categories = self.df['μκΆμ
μ’
μ€λΆλ₯λͺ
'].value_counts().head(10).index.tolist()
|
|
|
|
|
|
|
| 375 |
districts = self.df['μꡰꡬλͺ
'].unique()
|
| 376 |
correlation_matrix = np.zeros((len(top_categories), len(top_categories)))
|
| 377 |
|
| 378 |
for i, cat1 in enumerate(top_categories):
|
| 379 |
for j, cat2 in enumerate(top_categories):
|
| 380 |
if i != j:
|
|
|
|
| 381 |
coexist_count = 0
|
| 382 |
for district in districts:
|
| 383 |
district_df = self.df[self.df['μꡰꡬλͺ
'] == district]
|
|
|
|
| 429 |
if 'μλλͺ
' not in self.df.columns or 'μκΆμ
μ’
μ€λΆλ₯λͺ
' not in self.df.columns:
|
| 430 |
return None
|
| 431 |
|
|
|
|
| 432 |
specialization_data = []
|
| 433 |
for region in self.df['μλλͺ
'].unique():
|
| 434 |
region_df = self.df[self.df['μλλͺ
'] == region]
|
|
|
|
| 462 |
|
| 463 |
m = folium.Map(location=[center_lat, center_lon], zoom_start=11, tiles='OpenStreetMap')
|
| 464 |
|
|
|
|
| 465 |
heat_data = [[row['μοΏ½οΏ½'], row['κ²½λ']] for _, row in df_sample.iterrows()]
|
| 466 |
HeatMap(heat_data, radius=15, blur=25, max_zoom=13).add_to(m)
|
| 467 |
|
|
|
|
| 493 |
"""Fireworks AI κΈ°λ° μμ°μ΄ μ²λ¦¬ (μ€νΈλ¦¬λ° μ§μ + μΉκ²μ)"""
|
| 494 |
|
| 495 |
def __init__(self, api_key: str = None):
|
|
|
|
| 496 |
self.api_key = api_key or os.getenv("FIREWORKS_API_KEY")
|
| 497 |
self.base_url = "https://api.fireworks.ai/inference/v1/chat/completions"
|
| 498 |
|
|
|
|
| 502 |
def process_query_stream(self, query: str, data_context: Dict, chat_history: List = None, web_search_results: str = None):
|
| 503 |
"""μμ°μ΄ 쿼리 μ²λ¦¬ (μ€νΈλ¦¬λ° λͺ¨λ) - μΉκ²μ κ²°κ³Ό ν¬ν¨"""
|
| 504 |
|
|
|
|
| 505 |
web_context = ""
|
| 506 |
if web_search_results and "β οΈ" not in web_search_results:
|
| 507 |
web_context = f"""
|
|
|
|
| 520 |
|
| 521 |
ꡬ체μ μΈ μ«μμ λΉμ¨λ‘ μ λμ λΆμμ μ 곡νμΈμ.
|
| 522 |
μ°½μ
, ν¬μ, κ²½μ λΆμ κ΄μ μμ μ€μ©μ μΈμ¬μ΄νΈλ₯Ό μ 곡νμΈμ.
|
| 523 |
+
μΉ κ²μ κ²°κ³Όκ° μ 곡λ κ²½μ° μ΅μ νΈλ λμ ν¨κ» λΆμνμΈμ.
|
| 524 |
+
λ°λμ νκ΅μ΄λ‘ λ΅λ³νμΈμ."""
|
| 525 |
|
| 526 |
messages = [{"role": "system", "content": system_prompt}]
|
| 527 |
if chat_history:
|
|
|
|
| 533 |
"max_tokens": 4800,
|
| 534 |
"temperature": 0.7,
|
| 535 |
"messages": messages,
|
| 536 |
+
"stream": True
|
| 537 |
}
|
| 538 |
|
| 539 |
headers = {
|
|
|
|
| 547 |
headers=headers,
|
| 548 |
json=payload,
|
| 549 |
timeout=60,
|
| 550 |
+
stream=True
|
| 551 |
)
|
| 552 |
|
| 553 |
if response.status_code == 200:
|
|
|
|
| 554 |
for line in response.iter_lines():
|
| 555 |
if line:
|
| 556 |
line_text = line.decode('utf-8')
|
| 557 |
if line_text.startswith('data: '):
|
| 558 |
+
data_str = line_text[6:]
|
| 559 |
if data_str.strip() == '[DONE]':
|
| 560 |
break
|
| 561 |
try:
|
|
|
|
| 608 |
|
| 609 |
app_state.analyzer = MarketAnalyzer(df)
|
| 610 |
|
|
|
|
| 611 |
stats = f"""
|
| 612 |
β
**λ°μ΄ν° λ‘λ μλ£!**
|
| 613 |
+
{'=' * 40}
|
| 614 |
+
π **λΆμ ν΅κ³**
|
| 615 |
+
β’ μ΄ μ ν¬: {len(df):,}κ°
|
| 616 |
+
β’ λΆμ μ§μ: {', '.join(regions)}
|
| 617 |
+
β’ μ
μ’
μ: {df['μκΆμ
μ’
μ€λΆλ₯λͺ
'].nunique()}κ°
|
| 618 |
+
β’ λλΆλ₯: {df['μκΆμ
μ’
λλΆλ₯λͺ
'].nunique()}κ°
|
| 619 |
+
{'=' * 40}
|
| 620 |
+
π‘ μ΄μ μΈμ¬μ΄νΈλ₯Ό νμΈνκ±°λ AIμκ² μ§λ¬ΈνμΈμ!
|
| 621 |
"""
|
| 622 |
|
| 623 |
return stats, gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)
|
|
|
|
| 640 |
else:
|
| 641 |
result.append(None)
|
| 642 |
|
|
|
|
| 643 |
while len(result) < 11:
|
| 644 |
result.append(None)
|
| 645 |
|
|
|
|
| 647 |
|
| 648 |
|
| 649 |
def chat_respond(message, history):
|
| 650 |
+
"""μ±λ΄ μλ΅ (μ€νΈλ¦¬λ° λͺ¨λ + μΉκ²μ)"""
|
| 651 |
if app_state.analyzer is None:
|
| 652 |
yield history + [[message, "β λ¨Όμ λ°μ΄ν°λ₯Ό λ‘λν΄μ£ΌμΈμ!"]]
|
| 653 |
return
|
|
|
|
| 655 |
data_context = app_state.analyzer.analyze_for_llm()
|
| 656 |
|
| 657 |
try:
|
|
|
|
| 658 |
if app_state.llm_processor is None:
|
| 659 |
app_state.llm_processor = LLMQueryProcessor()
|
| 660 |
|
|
|
|
| 661 |
if app_state.brave_client is None:
|
| 662 |
try:
|
| 663 |
app_state.brave_client = BraveSearchClient()
|
| 664 |
except:
|
| 665 |
app_state.brave_client = None
|
| 666 |
|
|
|
|
| 667 |
web_results = None
|
| 668 |
if app_state.brave_client and app_state.brave_client.api_key:
|
|
|
|
| 669 |
search_query = f"νκ΅ μκΆ μ°½μ
νΈλ λ {message}"
|
| 670 |
web_results = app_state.brave_client.search(search_query, count=3)
|
| 671 |
|
|
|
|
| 672 |
chat_hist = []
|
| 673 |
for user_msg, bot_msg in history:
|
| 674 |
chat_hist.append({"role": "user", "content": user_msg})
|
| 675 |
chat_hist.append({"role": "assistant", "content": bot_msg})
|
| 676 |
|
|
|
|
| 677 |
history = history + [[message, ""]]
|
| 678 |
|
|
|
|
| 679 |
if web_results and "β οΈ" not in web_results:
|
| 680 |
history[-1][1] = "π μΉ κ²μ μ€...\n\n"
|
| 681 |
yield history
|
| 682 |
|
|
|
|
| 683 |
full_response = ""
|
| 684 |
for chunk in app_state.llm_processor.process_query_stream(message, data_context, chat_hist, web_results):
|
| 685 |
full_response += chunk
|
|
|
|
| 687 |
yield history
|
| 688 |
|
| 689 |
except ValueError as e:
|
|
|
|
| 690 |
response = f"""π **κΈ°λ³Έ λ°μ΄ν° λΆμ κ²°κ³Ό**
|
| 691 |
|
| 692 |
**μ 체 νν©**
|
|
|
|
| 698 |
νκ²½λ³μλ₯Ό μ€μ νμΈμ:
|
| 699 |
```bash
|
| 700 |
export FIREWORKS_API_KEY="your_api_key_here"
|
| 701 |
+
export BRAVE_API_KEY="your_brave_api_key_here"
|
| 702 |
+
```"""
|
|
|
|
|
|
|
| 703 |
|
| 704 |
history = history + [[message, response]]
|
| 705 |
yield history
|
| 706 |
|
| 707 |
|
| 708 |
# ============================================================================
|
| 709 |
+
# π¨ Comic Classic Theme CSS
|
| 710 |
# ============================================================================
|
| 711 |
|
| 712 |
+
css = """
|
| 713 |
+
/* ===== π¨ Google Fonts Import ===== */
|
| 714 |
+
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&family=Noto+Sans+KR:wght@400;700&display=swap');
|
| 715 |
+
|
| 716 |
+
/* ===== π¨ Comic Classic λ°°κ²½ - λΉν°μ§ νμ΄νΌ + λνΈ ν¨ν΄ ===== */
|
| 717 |
+
.gradio-container {
|
| 718 |
+
background-color: #FEF9C3 !important;
|
| 719 |
+
background-image:
|
| 720 |
+
radial-gradient(#1F2937 1px, transparent 1px) !important;
|
| 721 |
+
background-size: 20px 20px !important;
|
| 722 |
+
min-height: 100vh !important;
|
| 723 |
+
font-family: 'Noto Sans KR', 'Comic Neue', cursive, sans-serif !important;
|
| 724 |
+
}
|
| 725 |
+
|
| 726 |
+
/* ===== νκΉ
νμ΄μ€ μλ¨ μμ μ¨κΉ ===== */
|
| 727 |
+
.huggingface-space-header,
|
| 728 |
+
#space-header,
|
| 729 |
+
.space-header,
|
| 730 |
+
[class*="space-header"],
|
| 731 |
+
.svelte-1ed2p3z,
|
| 732 |
+
.space-header-badge,
|
| 733 |
+
.header-badge,
|
| 734 |
+
[data-testid="space-header"],
|
| 735 |
+
.svelte-kqij2n,
|
| 736 |
+
.svelte-1ax1toq,
|
| 737 |
+
.embed-container > div:first-child {
|
| 738 |
+
display: none !important;
|
| 739 |
+
visibility: hidden !important;
|
| 740 |
+
height: 0 !important;
|
| 741 |
+
width: 0 !important;
|
| 742 |
+
overflow: hidden !important;
|
| 743 |
+
opacity: 0 !important;
|
| 744 |
+
pointer-events: none !important;
|
| 745 |
+
}
|
| 746 |
+
|
| 747 |
+
/* ===== Footer μμ μ¨κΉ ===== */
|
| 748 |
+
footer,
|
| 749 |
+
.footer,
|
| 750 |
+
.gradio-container footer,
|
| 751 |
+
.built-with,
|
| 752 |
+
[class*="footer"],
|
| 753 |
+
.gradio-footer,
|
| 754 |
+
.main-footer,
|
| 755 |
+
div[class*="footer"],
|
| 756 |
+
.show-api,
|
| 757 |
+
.built-with-gradio,
|
| 758 |
+
a[href*="gradio.app"],
|
| 759 |
+
a[href*="huggingface.co/spaces"] {
|
| 760 |
+
display: none !important;
|
| 761 |
+
visibility: hidden !important;
|
| 762 |
+
height: 0 !important;
|
| 763 |
+
padding: 0 !important;
|
| 764 |
+
margin: 0 !important;
|
| 765 |
+
}
|
| 766 |
+
|
| 767 |
+
/* ===== λ©μΈ 컨ν
μ΄λ ===== */
|
| 768 |
+
#col-container {
|
| 769 |
+
max-width: 1400px;
|
| 770 |
+
margin: 0 auto;
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
/* ===== π¨ ν€λ νμ΄ν - μ½λ―Ή μ€νμΌ ===== */
|
| 774 |
+
.header-text h1 {
|
| 775 |
+
font-family: 'Bangers', cursive !important;
|
| 776 |
+
color: #1F2937 !important;
|
| 777 |
+
font-size: 3.2rem !important;
|
| 778 |
+
font-weight: 400 !important;
|
| 779 |
+
text-align: center !important;
|
| 780 |
+
margin-bottom: 0.5rem !important;
|
| 781 |
+
text-shadow:
|
| 782 |
+
4px 4px 0px #FACC15,
|
| 783 |
+
6px 6px 0px #1F2937 !important;
|
| 784 |
+
letter-spacing: 3px !important;
|
| 785 |
+
-webkit-text-stroke: 2px #1F2937 !important;
|
| 786 |
+
}
|
| 787 |
+
|
| 788 |
+
/* ===== π¨ μλΈνμ΄ν ===== */
|
| 789 |
+
.subtitle {
|
| 790 |
+
text-align: center !important;
|
| 791 |
+
font-family: 'Noto Sans KR', 'Comic Neue', cursive !important;
|
| 792 |
+
font-size: 1.1rem !important;
|
| 793 |
+
color: #1F2937 !important;
|
| 794 |
+
margin-bottom: 1.5rem !important;
|
| 795 |
+
font-weight: 700 !important;
|
| 796 |
+
}
|
| 797 |
+
|
| 798 |
+
/* ===== π¨ μΉ΄λ/ν¨λ - λ§ν νλ μ μ€νμΌ ===== */
|
| 799 |
+
.gr-panel,
|
| 800 |
+
.gr-box,
|
| 801 |
+
.gr-form,
|
| 802 |
+
.block,
|
| 803 |
+
.gr-group {
|
| 804 |
+
background: #FFFFFF !important;
|
| 805 |
+
border: 3px solid #1F2937 !important;
|
| 806 |
+
border-radius: 8px !important;
|
| 807 |
+
box-shadow: 6px 6px 0px #1F2937 !important;
|
| 808 |
+
transition: all 0.2s ease !important;
|
| 809 |
+
}
|
| 810 |
+
|
| 811 |
+
.gr-panel:hover,
|
| 812 |
+
.block:hover {
|
| 813 |
+
transform: translate(-2px, -2px) !important;
|
| 814 |
+
box-shadow: 8px 8px 0px #1F2937 !important;
|
| 815 |
+
}
|
| 816 |
+
|
| 817 |
+
/* ===== π¨ μ
λ ₯ νλ (Textbox) ===== */
|
| 818 |
+
textarea,
|
| 819 |
+
input[type="text"],
|
| 820 |
+
input[type="number"] {
|
| 821 |
+
background: #FFFFFF !important;
|
| 822 |
+
border: 3px solid #1F2937 !important;
|
| 823 |
+
border-radius: 8px !important;
|
| 824 |
+
color: #1F2937 !important;
|
| 825 |
+
font-family: 'Noto Sans KR', 'Comic Neue', cursive !important;
|
| 826 |
+
font-size: 1rem !important;
|
| 827 |
+
font-weight: 700 !important;
|
| 828 |
+
transition: all 0.2s ease !important;
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
textarea:focus,
|
| 832 |
+
input[type="text"]:focus,
|
| 833 |
+
input[type="number"]:focus {
|
| 834 |
+
border-color: #3B82F6 !important;
|
| 835 |
+
box-shadow: 4px 4px 0px #3B82F6 !important;
|
| 836 |
+
outline: none !important;
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
textarea::placeholder {
|
| 840 |
+
color: #9CA3AF !important;
|
| 841 |
+
font-weight: 400 !important;
|
| 842 |
+
}
|
| 843 |
+
|
| 844 |
+
/* ===== π¨ Primary λ²νΌ - μ½λ―Ή λΈλ£¨ ===== */
|
| 845 |
+
.gr-button-primary,
|
| 846 |
+
button.primary,
|
| 847 |
+
.gr-button.primary {
|
| 848 |
+
background: #3B82F6 !important;
|
| 849 |
+
border: 3px solid #1F2937 !important;
|
| 850 |
+
border-radius: 8px !important;
|
| 851 |
+
color: #FFFFFF !important;
|
| 852 |
+
font-family: 'Noto Sans KR', 'Bangers', cursive !important;
|
| 853 |
+
font-weight: 700 !important;
|
| 854 |
+
font-size: 1.2rem !important;
|
| 855 |
+
letter-spacing: 1px !important;
|
| 856 |
+
padding: 14px 28px !important;
|
| 857 |
+
box-shadow: 5px 5px 0px #1F2937 !important;
|
| 858 |
+
transition: all 0.1s ease !important;
|
| 859 |
+
text-shadow: 1px 1px 0px #1F2937 !important;
|
| 860 |
+
}
|
| 861 |
+
|
| 862 |
+
.gr-button-primary:hover,
|
| 863 |
+
button.primary:hover,
|
| 864 |
+
.gr-button.primary:hover {
|
| 865 |
+
background: #2563EB !important;
|
| 866 |
+
transform: translate(-2px, -2px) !important;
|
| 867 |
+
box-shadow: 7px 7px 0px #1F2937 !important;
|
| 868 |
+
}
|
| 869 |
+
|
| 870 |
+
.gr-button-primary:active,
|
| 871 |
+
button.primary:active,
|
| 872 |
+
.gr-button.primary:active {
|
| 873 |
+
transform: translate(3px, 3px) !important;
|
| 874 |
+
box-shadow: 2px 2px 0px #1F2937 !important;
|
| 875 |
+
}
|
| 876 |
+
|
| 877 |
+
/* ===== π¨ Secondary λ²νΌ - μ½λ―Ή λ λ ===== */
|
| 878 |
+
.gr-button-secondary,
|
| 879 |
+
button.secondary {
|
| 880 |
+
background: #EF4444 !important;
|
| 881 |
+
border: 3px solid #1F2937 !important;
|
| 882 |
+
border-radius: 8px !important;
|
| 883 |
+
color: #FFFFFF !important;
|
| 884 |
+
font-family: 'Noto Sans KR', 'Bangers', cursive !important;
|
| 885 |
+
font-weight: 700 !important;
|
| 886 |
+
font-size: 1rem !important;
|
| 887 |
+
letter-spacing: 1px !important;
|
| 888 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 889 |
+
transition: all 0.1s ease !important;
|
| 890 |
+
text-shadow: 1px 1px 0px #1F2937 !important;
|
| 891 |
+
}
|
| 892 |
+
|
| 893 |
+
.gr-button-secondary:hover,
|
| 894 |
+
button.secondary:hover {
|
| 895 |
+
background: #DC2626 !important;
|
| 896 |
+
transform: translate(-2px, -2px) !important;
|
| 897 |
+
box-shadow: 6px 6px 0px #1F2937 !important;
|
| 898 |
+
}
|
| 899 |
+
|
| 900 |
+
/* ===== π¨ Small λ²νΌ ===== */
|
| 901 |
+
button.sm,
|
| 902 |
+
.gr-button-sm {
|
| 903 |
+
background: #10B981 !important;
|
| 904 |
+
border: 2px solid #1F2937 !important;
|
| 905 |
+
border-radius: 6px !important;
|
| 906 |
+
color: #FFFFFF !important;
|
| 907 |
+
font-family: 'Noto Sans KR', cursive !important;
|
| 908 |
+
font-weight: 700 !important;
|
| 909 |
+
font-size: 0.9rem !important;
|
| 910 |
+
padding: 8px 16px !important;
|
| 911 |
+
box-shadow: 3px 3px 0px #1F2937 !important;
|
| 912 |
+
transition: all 0.1s ease !important;
|
| 913 |
+
}
|
| 914 |
+
|
| 915 |
+
button.sm:hover,
|
| 916 |
+
.gr-button-sm:hover {
|
| 917 |
+
background: #059669 !important;
|
| 918 |
+
transform: translate(-1px, -1px) !important;
|
| 919 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 920 |
+
}
|
| 921 |
+
|
| 922 |
+
/* ===== π¨ λ‘κ·Έ μΆλ ₯ μμ ===== */
|
| 923 |
+
.info-log textarea {
|
| 924 |
+
background: #1F2937 !important;
|
| 925 |
+
color: #10B981 !important;
|
| 926 |
+
font-family: 'Courier New', monospace !important;
|
| 927 |
+
font-size: 0.9rem !important;
|
| 928 |
+
font-weight: 400 !important;
|
| 929 |
+
border: 3px solid #10B981 !important;
|
| 930 |
+
border-radius: 8px !important;
|
| 931 |
+
box-shadow: 4px 4px 0px #10B981 !important;
|
| 932 |
+
}
|
| 933 |
+
|
| 934 |
+
/* ===== π¨ μμ½λμΈ - λ§νμ μ€νμΌ ===== */
|
| 935 |
+
.gr-accordion {
|
| 936 |
+
background: #FACC15 !important;
|
| 937 |
+
border: 3px solid #1F2937 !important;
|
| 938 |
+
border-radius: 8px !important;
|
| 939 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 940 |
+
}
|
| 941 |
+
|
| 942 |
+
.gr-accordion-header {
|
| 943 |
+
color: #1F2937 !important;
|
| 944 |
+
font-family: 'Noto Sans KR', 'Comic Neue', cursive !important;
|
| 945 |
+
font-weight: 700 !important;
|
| 946 |
+
font-size: 1.1rem !important;
|
| 947 |
+
}
|
| 948 |
+
|
| 949 |
+
/* ===== π¨ 체ν¬λ°μ€ κ·Έλ£Ή ===== */
|
| 950 |
+
.gr-checkbox-group {
|
| 951 |
+
background: #FFFFFF !important;
|
| 952 |
+
border: 3px solid #1F2937 !important;
|
| 953 |
+
border-radius: 8px !important;
|
| 954 |
+
padding: 10px !important;
|
| 955 |
+
}
|
| 956 |
+
|
| 957 |
+
input[type="checkbox"] {
|
| 958 |
+
accent-color: #3B82F6 !important;
|
| 959 |
+
width: 18px !important;
|
| 960 |
+
height: 18px !important;
|
| 961 |
+
}
|
| 962 |
+
|
| 963 |
+
/* ===== π¨ ν μ€νμΌ ===== */
|
| 964 |
+
.gr-tab-nav {
|
| 965 |
+
background: #FACC15 !important;
|
| 966 |
+
border: 3px solid #1F2937 !important;
|
| 967 |
+
border-radius: 8px 8px 0 0 !important;
|
| 968 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 969 |
+
}
|
| 970 |
+
|
| 971 |
+
.gr-tab-nav button {
|
| 972 |
+
font-family: 'Noto Sans KR', 'Comic Neue', cursive !important;
|
| 973 |
+
font-weight: 700 !important;
|
| 974 |
+
color: #1F2937 !important;
|
| 975 |
+
border: none !important;
|
| 976 |
+
padding: 12px 20px !important;
|
| 977 |
+
}
|
| 978 |
+
|
| 979 |
+
.gr-tab-nav button.selected {
|
| 980 |
+
background: #3B82F6 !important;
|
| 981 |
+
color: #FFFFFF !important;
|
| 982 |
+
border-radius: 6px 6px 0 0 !important;
|
| 983 |
+
}
|
| 984 |
+
|
| 985 |
+
/* ===== π¨ μ±λ΄ μ€νμΌ ===== */
|
| 986 |
+
.gr-chatbot {
|
| 987 |
+
background: #FFFFFF !important;
|
| 988 |
+
border: 3px solid #1F2937 !important;
|
| 989 |
+
border-radius: 8px !important;
|
| 990 |
+
box-shadow: 6px 6px 0px #1F2937 !important;
|
| 991 |
+
}
|
| 992 |
+
|
| 993 |
+
.gr-chatbot .message {
|
| 994 |
+
font-family: 'Noto Sans KR', sans-serif !important;
|
| 995 |
+
}
|
| 996 |
+
|
| 997 |
+
/* ===== π¨ λΌλ²¨ μ€νμΌ ===== */
|
| 998 |
+
label,
|
| 999 |
+
.gr-input-label,
|
| 1000 |
+
.gr-block-label {
|
| 1001 |
+
color: #1F2937 !important;
|
| 1002 |
+
font-family: 'Noto Sans KR', 'Comic Neue', cursive !important;
|
| 1003 |
+
font-weight: 700 !important;
|
| 1004 |
+
font-size: 1rem !important;
|
| 1005 |
+
}
|
| 1006 |
+
|
| 1007 |
+
/* ===== π¨ Markdown μ€νμΌ ===== */
|
| 1008 |
+
.gr-markdown {
|
| 1009 |
+
font-family: 'Noto Sans KR', 'Comic Neue', cursive !important;
|
| 1010 |
+
color: #1F2937 !important;
|
| 1011 |
+
}
|
| 1012 |
+
|
| 1013 |
+
.gr-markdown h1,
|
| 1014 |
+
.gr-markdown h2,
|
| 1015 |
+
.gr-markdown h3 {
|
| 1016 |
+
font-family: 'Bangers', 'Noto Sans KR', cursive !important;
|
| 1017 |
+
color: #1F2937 !important;
|
| 1018 |
+
text-shadow: 2px 2px 0px #FACC15 !important;
|
| 1019 |
+
}
|
| 1020 |
+
|
| 1021 |
+
/* ===== π¨ Plot μμ ===== */
|
| 1022 |
+
.gr-plot {
|
| 1023 |
+
border: 3px solid #1F2937 !important;
|
| 1024 |
+
border-radius: 8px !important;
|
| 1025 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 1026 |
+
background: #FFFFFF !important;
|
| 1027 |
+
}
|
| 1028 |
+
|
| 1029 |
+
/* ===== π¨ HTML μμ (μ§λ) ===== */
|
| 1030 |
+
.gr-html {
|
| 1031 |
+
border: 4px solid #1F2937 !important;
|
| 1032 |
+
border-radius: 8px !important;
|
| 1033 |
+
box-shadow: 6px 6px 0px #FACC15 !important;
|
| 1034 |
+
overflow: hidden !important;
|
| 1035 |
+
}
|
| 1036 |
+
|
| 1037 |
+
/* ===== π¨ μ€ν¬λ‘€λ° - μ½λ―Ή μ€νμΌ ===== */
|
| 1038 |
+
::-webkit-scrollbar {
|
| 1039 |
+
width: 12px;
|
| 1040 |
+
height: 12px;
|
| 1041 |
+
}
|
| 1042 |
+
|
| 1043 |
+
::-webkit-scrollbar-track {
|
| 1044 |
+
background: #FEF9C3;
|
| 1045 |
+
border: 2px solid #1F2937;
|
| 1046 |
+
}
|
| 1047 |
+
|
| 1048 |
+
::-webkit-scrollbar-thumb {
|
| 1049 |
+
background: #3B82F6;
|
| 1050 |
+
border: 2px solid #1F2937;
|
| 1051 |
+
border-radius: 0px;
|
| 1052 |
+
}
|
| 1053 |
+
|
| 1054 |
+
::-webkit-scrollbar-thumb:hover {
|
| 1055 |
+
background: #EF4444;
|
| 1056 |
+
}
|
| 1057 |
+
|
| 1058 |
+
/* ===== π¨ μ ν νμ΄λΌμ΄νΈ ===== */
|
| 1059 |
+
::selection {
|
| 1060 |
+
background: #FACC15;
|
| 1061 |
+
color: #1F2937;
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
/* ===== π¨ λ§ν¬ μ€νμΌ ===== */
|
| 1065 |
+
a {
|
| 1066 |
+
color: #3B82F6 !important;
|
| 1067 |
+
text-decoration: none !important;
|
| 1068 |
+
font-weight: 700 !important;
|
| 1069 |
+
}
|
| 1070 |
+
|
| 1071 |
+
a:hover {
|
| 1072 |
+
color: #EF4444 !important;
|
| 1073 |
+
}
|
| 1074 |
+
|
| 1075 |
+
/* ===== π¨ Row/Column κ°κ²© ===== */
|
| 1076 |
+
.gr-row {
|
| 1077 |
+
gap: 1.5rem !important;
|
| 1078 |
+
}
|
| 1079 |
+
|
| 1080 |
+
.gr-column {
|
| 1081 |
+
gap: 1rem !important;
|
| 1082 |
+
}
|
| 1083 |
+
|
| 1084 |
+
/* ===== π¨ Badge μ€νμΌ ===== */
|
| 1085 |
+
.badge-container {
|
| 1086 |
+
display: flex;
|
| 1087 |
+
justify-content: center;
|
| 1088 |
+
gap: 15px;
|
| 1089 |
+
flex-wrap: wrap;
|
| 1090 |
+
margin: 20px 0;
|
| 1091 |
+
}
|
| 1092 |
+
|
| 1093 |
+
.comic-badge {
|
| 1094 |
+
display: inline-flex;
|
| 1095 |
+
align-items: center;
|
| 1096 |
+
gap: 8px;
|
| 1097 |
+
padding: 12px 24px;
|
| 1098 |
+
border: 3px solid #1F2937;
|
| 1099 |
+
border-radius: 8px;
|
| 1100 |
+
text-decoration: none;
|
| 1101 |
+
font-weight: 700;
|
| 1102 |
+
font-size: 1em;
|
| 1103 |
+
transition: all 0.2s ease;
|
| 1104 |
+
box-shadow: 4px 4px 0px #1F2937;
|
| 1105 |
+
font-family: 'Noto Sans KR', sans-serif;
|
| 1106 |
+
}
|
| 1107 |
+
|
| 1108 |
+
.comic-badge:hover {
|
| 1109 |
+
transform: translate(-2px, -2px);
|
| 1110 |
+
box-shadow: 6px 6px 0px #1F2937;
|
| 1111 |
+
}
|
| 1112 |
+
|
| 1113 |
+
.comic-badge-yellow {
|
| 1114 |
+
background: #FACC15;
|
| 1115 |
+
color: #1F2937;
|
| 1116 |
+
}
|
| 1117 |
+
|
| 1118 |
+
.comic-badge-blue {
|
| 1119 |
+
background: #3B82F6;
|
| 1120 |
+
color: #FFFFFF;
|
| 1121 |
+
}
|
| 1122 |
+
|
| 1123 |
+
.comic-badge-green {
|
| 1124 |
+
background: #10B981;
|
| 1125 |
+
color: #FFFFFF;
|
| 1126 |
+
}
|
| 1127 |
+
|
| 1128 |
+
/* ===== λ°μν μ‘°μ ===== */
|
| 1129 |
+
@media (max-width: 768px) {
|
| 1130 |
+
.header-text h1 {
|
| 1131 |
+
font-size: 2rem !important;
|
| 1132 |
+
text-shadow:
|
| 1133 |
+
3px 3px 0px #FACC15,
|
| 1134 |
+
4px 4px 0px #1F2937 !important;
|
| 1135 |
+
}
|
| 1136 |
|
| 1137 |
+
.gr-button-primary,
|
| 1138 |
+
button.primary {
|
| 1139 |
+
padding: 12px 20px !important;
|
| 1140 |
+
font-size: 1rem !important;
|
| 1141 |
+
}
|
| 1142 |
+
|
| 1143 |
+
.gr-panel,
|
| 1144 |
+
.block {
|
| 1145 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 1146 |
+
}
|
| 1147 |
+
}
|
| 1148 |
+
|
| 1149 |
+
/* ===== π¨ λ€ν¬λͺ¨λ λΉνμ±ν ===== */
|
| 1150 |
+
@media (prefers-color-scheme: dark) {
|
| 1151 |
+
.gradio-container {
|
| 1152 |
+
background-color: #FEF9C3 !important;
|
| 1153 |
+
}
|
| 1154 |
+
}
|
| 1155 |
+
"""
|
| 1156 |
|
| 1157 |
+
|
| 1158 |
+
# ============================================================================
|
| 1159 |
+
# Gradio UI
|
| 1160 |
+
# ============================================================================
|
| 1161 |
+
|
| 1162 |
+
with gr.Blocks(title="AI μκΆ λΆμ μμ€ν
", css=css) as demo:
|
| 1163 |
+
|
| 1164 |
+
# HOME Badge
|
| 1165 |
gr.HTML("""
|
| 1166 |
+
<div style="text-align: center; margin: 20px 0 10px 0;">
|
| 1167 |
+
<a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
|
| 1168 |
+
<img src="https://img.shields.io/static/v1?label=π HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
|
| 1169 |
+
</a>
|
| 1170 |
+
</div>
|
| 1171 |
+
""")
|
| 1172 |
+
|
| 1173 |
+
# Header Title
|
| 1174 |
+
gr.Markdown(
|
| 1175 |
+
"""
|
| 1176 |
+
# πͺ AI μκΆ λΆμ μμ€ν
PRO π
|
| 1177 |
+
""",
|
| 1178 |
+
elem_classes="header-text"
|
| 1179 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1180 |
|
| 1181 |
+
gr.Markdown(
|
| 1182 |
+
"""
|
| 1183 |
+
<p class="subtitle">β‘ μ κ΅ μκ°(μκΆ) λ°μ΄ν° μ€μκ° λΆμ | μ€νΈλ¦¬λ° + μΉκ²μ π | 10κ°μ§ μ¬μΈ΅ μΈμ¬μ΄νΈ π</p>
|
| 1184 |
+
""",
|
| 1185 |
+
)
|
| 1186 |
+
|
| 1187 |
+
# λ°°μ§
|
| 1188 |
+
gr.HTML("""
|
| 1189 |
+
<div class="badge-container">
|
| 1190 |
+
<a href="https://open.kakao.com/o/peIe8KWh" target="_blank" class="comic-badge comic-badge-yellow">
|
| 1191 |
+
<span>π¬</span>
|
| 1192 |
<span>μ€νμ±ν
λ°λ‘κ°κΈ°</span>
|
| 1193 |
</a>
|
| 1194 |
+
<a href="https://ginigen.ai" target="_blank" class="comic-badge comic-badge-blue">
|
| 1195 |
+
<span>π</span>
|
| 1196 |
<span>λλ
Έ λ°λλ μ λμ¨ λ¬΄λ£ μλΉμ€</span>
|
| 1197 |
</a>
|
| 1198 |
</div>
|
| 1199 |
""")
|
|
|
|
| 1200 |
|
| 1201 |
+
# API μν
|
| 1202 |
+
api_status = "β
μ€μ λ¨" if os.getenv("FIREWORKS_API_KEY") else "β οΈ λ―Έμ€μ "
|
| 1203 |
+
brave_status = "β
νμ±ν" if os.getenv("BRAVE_API_KEY") else "β οΈ λΉνμ±ν"
|
| 1204 |
+
|
| 1205 |
+
with gr.Row(equal_height=False):
|
| 1206 |
+
# μΌμͺ½ μ»¬λΌ - μ€μ
|
| 1207 |
+
with gr.Column(scale=1, min_width=300):
|
| 1208 |
+
gr.Markdown("### βοΈ λΆμ μ€μ ")
|
| 1209 |
|
| 1210 |
+
gr.Markdown(f"""
|
| 1211 |
+
**π API μν**
|
| 1212 |
+
- Fireworks AI: {api_status}
|
| 1213 |
+
- Brave Search: {brave_status}
|
| 1214 |
+
""")
|
| 1215 |
|
| 1216 |
region_select = gr.CheckboxGroup(
|
| 1217 |
choices=list(MarketDataLoader.REGIONS.keys()),
|
|
|
|
| 1219 |
label="π λΆμ μ§μ μ ν (μ΅λ 5κ° κΆμ₯)"
|
| 1220 |
)
|
| 1221 |
|
| 1222 |
+
load_btn = gr.Button(
|
| 1223 |
+
"π λ°μ΄ν° λ‘λνκΈ°!",
|
| 1224 |
+
variant="primary",
|
| 1225 |
+
size="lg"
|
| 1226 |
+
)
|
| 1227 |
|
| 1228 |
+
with gr.Accordion("π λ‘λ μν", open=True):
|
| 1229 |
+
status_box = gr.Markdown(
|
| 1230 |
+
"π μ§μμ μ ννκ³ λ°μ΄ν°λ₯Ό λ‘λνμΈμ!",
|
| 1231 |
+
elem_classes="info-log"
|
| 1232 |
+
)
|
| 1233 |
+
|
| 1234 |
+
# μ€λ₯Έμͺ½ μ»¬λΌ - λ©μΈ μ½ν
μΈ
|
| 1235 |
+
with gr.Column(scale=3, min_width=600):
|
| 1236 |
with gr.Tabs() as tabs:
|
| 1237 |
+
# ν 1: μΈμ¬μ΄νΈ λμ보λ
|
| 1238 |
with gr.Tab("π μΈμ¬μ΄νΈ λμ보λ", id=0) as tab1:
|
| 1239 |
insights_content = gr.Column(visible=False)
|
| 1240 |
|
|
|
|
| 1246 |
gr.Markdown("### π 10κ°μ§ μ¬μΈ΅ μκΆ μΈμ¬μ΄νΈ")
|
| 1247 |
|
| 1248 |
with gr.Row():
|
| 1249 |
+
chart1 = gr.Plot(label="π μ
μ’
λ³ μ ν¬ μ")
|
| 1250 |
+
chart2 = gr.Plot(label="π λλΆλ₯ λΆν¬")
|
| 1251 |
|
| 1252 |
with gr.Row():
|
| 1253 |
+
chart3 = gr.Plot(label="π’ μΈ΅λ³ λΆν¬")
|
| 1254 |
+
chart4 = gr.Plot(label="π¨ μ
μ’
λ€μμ±")
|
| 1255 |
|
| 1256 |
with gr.Row():
|
| 1257 |
+
chart5 = gr.Plot(label="πͺ νλμ°¨μ΄μ¦ λΆμ")
|
| 1258 |
+
chart6 = gr.Plot(label="π μΈ΅ μ νΈλ")
|
| 1259 |
|
| 1260 |
with gr.Row():
|
| 1261 |
+
chart7 = gr.Plot(label="π₯ μ§μ λ°μ§λ")
|
| 1262 |
+
chart8 = gr.Plot(label="π μ
μ’
μκ΄κ΄κ³")
|
| 1263 |
|
| 1264 |
with gr.Row():
|
| 1265 |
+
chart9 = gr.Plot(label="π μλΆλ₯ νΈλ λ")
|
| 1266 |
+
chart10 = gr.Plot(label="π― μ§μ νΉν")
|
| 1267 |
|
| 1268 |
+
# ν 2: AI μ±λ΄
|
| 1269 |
+
with gr.Tab("π€ AI λΆμ μ±λ΄ β‘π", id=1) as tab2:
|
| 1270 |
chat_content = gr.Column(visible=False)
|
| 1271 |
|
| 1272 |
with chat_content:
|
| 1273 |
gr.Markdown("""
|
| 1274 |
+
### π‘ μμ μ§λ¬Έ
|
| 1275 |
+
κ°λ¨μμ μΉ΄ν μ°½μ
? | μΉν¨μ§ ν¬ν μ§μ? | 1μΈ΅μ΄ μ 리ν μ
μ’
? | νλμ°¨μ΄μ¦ μ μ μ¨?
|
| 1276 |
+
|
| 1277 |
+
β‘ **μ€νΈλ¦¬λ°**: AI μλ΅μ΄ μ€μκ°μΌλ‘ νμλ©λλ€!
|
| 1278 |
+
π **μΉκ²μ**: μ΅μ μκΆ νΈλ λλ₯Ό μλ λ°μν©λλ€!
|
| 1279 |
""")
|
| 1280 |
|
| 1281 |
+
chatbot = gr.Chatbot(
|
| 1282 |
+
height=450,
|
| 1283 |
+
label="AI μκΆ λΆμ μ΄μμ€ν΄νΈ"
|
| 1284 |
+
)
|
| 1285 |
|
| 1286 |
with gr.Row():
|
| 1287 |
msg_input = gr.Textbox(
|
|
|
|
| 1289 |
show_label=False,
|
| 1290 |
scale=4
|
| 1291 |
)
|
| 1292 |
+
submit_btn = gr.Button("π μ μ‘", variant="primary", scale=1)
|
| 1293 |
|
|
|
|
| 1294 |
with gr.Row():
|
| 1295 |
+
sample_btn1 = gr.Button("β κ°λ¨ μΉ΄ν μ°½μ
?", size="sm")
|
| 1296 |
+
sample_btn2 = gr.Button("π μΉν¨μ§ ν¬ν μ§μ?", size="sm")
|
| 1297 |
+
sample_btn3 = gr.Button("π’ 1μΈ΅ μ 리ν μ
μ’
?", size="sm")
|
| 1298 |
+
sample_btn4 = gr.Button("πͺ νλμ°¨μ΄μ¦ μ μ μ¨?", size="sm")
|
| 1299 |
|
| 1300 |
+
# μ¬μ© κ°μ΄λ
|
| 1301 |
+
gr.Markdown("""
|
| 1302 |
+
---
|
| 1303 |
+
### π μ¬μ© κ°μ΄λ
|
| 1304 |
+
1οΈβ£ μ§μ μ ν β 2οΈβ£ λ°μ΄ν° λ‘λ β 3οΈβ£ 10κ°μ§ μΈμ¬μ΄νΈ νμΈ λλ AIμκ² μ§λ¬Έ!
|
| 1305 |
+
|
| 1306 |
+
### π μ 곡λλ 10κ°μ§ λΆμ
|
| 1307 |
+
| λΆμ νλͺ© | μ€λͺ
|
|
| 1308 |
+
|----------|------|
|
| 1309 |
+
| π μ
μ’
λ³ μ ν¬ μ | κ°μ₯ λ§μ μ
μ’
TOP 15 |
|
| 1310 |
+
| π λλΆλ₯ λΆν¬ | μλ§€/μμ/μλΉμ€ λ± λΉμ¨ |
|
| 1311 |
+
| π’ μΈ΅λ³ λΆν¬ | μ§ν/1μΈ΅/μμΈ΅ μ
μ§ λΆμ |
|
| 1312 |
+
| π¨ μ
μ’
λ€μμ± | μ§μλ³ μ
μ’
λ€μμ± μ§μ |
|
| 1313 |
+
| πͺ νλμ°¨μ΄μ¦ λΆμ | κ°μΈ vs νλμ°¨μ΄μ¦ λΉμ¨ |
|
| 1314 |
+
| π μΈ΅ μ νΈλ | μ
μ’
λ³ μ νΈ μΈ΅μ |
|
| 1315 |
+
| π₯ μ§μ λ°μ§λ | μ ν¬ μ μμ μ§μ |
|
| 1316 |
+
| π μ
μ’
μκ΄κ΄κ³ | κ°μ΄ λνλλ μ
μ’
ν¨ν΄ |
|
| 1317 |
+
| π μλΆλ₯ νΈλ λ | μΈλΆ μ
μ’
λΆν¬ |
|
| 1318 |
+
| π― μ§μ νΉν | κ° μ§μμ νΉν μ
μ’
|
|
| 1319 |
+
|
| 1320 |
+
π‘ **Tip**: API ν€ μμ΄λ 10κ°μ§ μκ°ν λΆμκ³Ό κΈ°λ³Έ οΏ½οΏ½οΏ½κ³λ₯Ό νμΈν μ μμ΅λλ€!
|
| 1321 |
+
""")
|
| 1322 |
+
|
| 1323 |
# μ΄λ²€νΈ νΈλ€λ¬
|
| 1324 |
load_btn.click(
|
| 1325 |
fn=load_data,
|
|
|
|
| 1330 |
outputs=[map_output, chart1, chart2, chart3, chart4, chart5, chart6, chart7, chart8, chart9, chart10]
|
| 1331 |
)
|
| 1332 |
|
| 1333 |
+
# μ±λ΄ μ΄λ²€νΈ
|
| 1334 |
submit_btn.click(
|
| 1335 |
fn=chat_respond,
|
| 1336 |
inputs=[msg_input, chatbot],
|
|
|
|
| 1356 |
yield result
|
| 1357 |
return handler
|
| 1358 |
|
| 1359 |
+
sample_btn1.click(fn=create_sample_click("κ°λ¨μμ μΉ΄ν μ°½μ
νλ €λ©΄ μ΄λ»κ² ν΄μΌ νλμ?"), inputs=[chatbot], outputs=[chatbot])
|
| 1360 |
+
sample_btn2.click(fn=create_sample_click("μΉν¨μ§μ΄ κ°μ₯ ν¬νλ μ§μμ μ΄λμΈκ°μ?"), inputs=[chatbot], outputs=[chatbot])
|
| 1361 |
+
sample_btn3.click(fn=create_sample_click("1μΈ΅μ΄ μ 리ν μ
μ’
μ 무μμΈκ°μ?"), inputs=[chatbot], outputs=[chatbot])
|
| 1362 |
+
sample_btn4.click(fn=create_sample_click("νλμ°¨μ΄μ¦ μ μ μ¨μ΄ λμ μ
μ’
μ?"), inputs=[chatbot], outputs=[chatbot])
|
| 1363 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1364 |
|
| 1365 |
# μ€ν
|
| 1366 |
if __name__ == "__main__":
|