Spaces:
Running
Running
import os | |
import json | |
import re | |
import logging | |
import requests | |
import markdown | |
import time | |
import io | |
import random | |
import hashlib | |
from datetime import datetime | |
from dataclasses import dataclass | |
from itertools import combinations, product | |
from typing import Iterator | |
import streamlit as st | |
import pandas as pd | |
import PyPDF2 # For handling PDF files | |
from collections import Counter | |
from openai import OpenAI # OpenAI λΌμ΄λΈλ¬λ¦¬ | |
from gradio_client import Client | |
from kaggle.api.kaggle_api_extended import KaggleApi | |
import tempfile | |
import glob | |
import shutil | |
# βββ μΆκ°λ λΌμ΄λΈλ¬λ¦¬(μ λ λλ½ κΈμ§) βββββββββββββββββββββββββββββββ | |
import pyarrow.parquet as pq | |
from sklearn.feature_extraction.text import TfidfVectorizer | |
from sklearn.metrics.pairwise import cosine_similarity | |
# βββββββββββββββββββββββββββββββ Environment Variables / Constants βββββββββββββββββββββββββ | |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") | |
BRAVE_KEY = os.getenv("SERPHOUSE_API_KEY", "") # Brave Search API | |
KAGGLE_USERNAME = os.getenv("KAGGLE_USERNAME", "") | |
KAGGLE_KEY = os.getenv("KAGGLE_KEY", "") | |
KAGGLE_API_KEY = KAGGLE_KEY | |
if not (KAGGLE_USERNAME and KAGGLE_KEY): | |
raise RuntimeError("β οΈ KAGGLE_USERNAMEκ³Ό KAGGLE_KEY νκ²½λ³μλ₯Ό λ¨Όμ μ€μ νμΈμ.") | |
os.environ["KAGGLE_USERNAME"] = KAGGLE_USERNAME | |
os.environ["KAGGLE_KEY"] = KAGGLE_KEY | |
BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search" | |
IMAGE_API_URL = "http://211.233.58.201:7896" # μμ μ΄λ―Έμ§ μμ±μ© API | |
MAX_TOKENS = 7999 | |
# βββββββββββββββββββββββββββββββ Logging βββββββββββββββββββββββββββββββ | |
logging.basicConfig( | |
level=logging.INFO, | |
format="%(asctime)s - %(levelname)s - %(message)s" | |
) | |
# βββββββββββββββββββββββββββββββ κ΅°μ¬(λ°λ¦¬ν°λ¦¬) μ μ λ°μ΄ν°μ λ‘λ βββββββββββββββββ | |
def load_military_dataset(): | |
""" | |
mil.parquet (index, scenario_description, attack_reasoning, defense_reasoning) | |
""" | |
path = os.path.join(os.path.dirname(__file__), "mil.parquet") | |
if not os.path.exists(path): | |
logging.warning("mil.parquet not found β military support disabled.") | |
return None | |
try: | |
df = pq.read_table(path).to_pandas() | |
return df | |
except Exception as e: | |
logging.error(f"Failed to read mil.parquet: {e}") | |
return None | |
MIL_DF = load_military_dataset() | |
def is_military_query(text: str) -> bool: | |
"""κ΅°μ¬/μ μ κ΄λ ¨ ν€μλκ° λ±μ₯νλ©΄ True λ°ν""" | |
kw = [ | |
"κ΅°μ¬", "μ μ ", "μ ν¬", "μ μ", "μμ ", "무기", "λ³λ ₯", | |
"military", "tactic", "warfare", "battle", "operation" | |
] | |
return any(k.lower() in text.lower() for k in kw) | |
def military_search(query: str, top_k: int = 3): | |
""" | |
mil.parquetμ scenario_description μ΄κ³Ό μ½μ¬μΈ μ μ¬λ λΆμνμ¬ | |
queryμ κ°μ₯ μ μ¬ν μμ μλ리μ€λ₯Ό λ°ν | |
""" | |
if MIL_DF is None: | |
return [] | |
try: | |
corpus = MIL_DF["scenario_description"].tolist() | |
vec = TfidfVectorizer().fit_transform([query] + corpus) | |
sims = cosine_similarity(vec[0:1], vec[1:]).flatten() | |
top_idx = sims.argsort()[-top_k:][::-1] | |
return MIL_DF.iloc[top_idx][[ | |
"scenario_description", | |
"attack_reasoning", | |
"defense_reasoning" | |
]].to_dict("records") | |
except Exception as e: | |
logging.error(f"military_search error: {e}") | |
return [] | |
# βββββββββββββββββββββββββββββββ Kaggle Datasets ββββββββββββββββββββββββ | |
KAGGLE_DATASETS = { | |
"general_business": { | |
"ref": "mohammadgharaei77/largest-2000-global-companies", | |
"title": "Largest 2000 Global Companies", | |
"subtitle": "Comprehensive data about the world's largest companies", | |
"url": "https://www.kaggle.com/datasets/mohammadgharaei77/largest-2000-global-companies", | |
"keywords": ["business", "company", "corporation", "enterprise", "global", "λΉμ¦λμ€", "κΈ°μ ", "νμ¬", "κΈλ‘λ²", "κΈ°μ κ°μΉ"] | |
}, | |
"global_development": { | |
"ref": "michaelmatta0/global-development-indicators-2000-2020", | |
"title": "Global Development Indicators (2000-2020)", | |
"subtitle": "Economic and social indicators for countries worldwide", | |
"url": "https://www.kaggle.com/datasets/michaelmatta0/global-development-indicators-2000-2020", | |
"keywords": ["development", "economy", "global", "indicators", "social", "κ²½μ ", "λ°μ ", "μ§ν", "μ¬ν", "κ΅κ°", "κΈλ‘λ²"] | |
}, | |
"startup_ideas": { | |
"ref": "rohitsahoo/100-startup-ideas", | |
"title": "Startup Idea Generator Dataset", | |
"subtitle": "A variety of startup ideas", | |
"url": "https://www.kaggle.com/datasets/rohitsahoo/100-startup-ideas", | |
"keywords": ["startup", "innovation", "business idea", "entrepreneurship", "μ€ννΈμ ", "μ°½μ ", "νμ ", "μμ΄λμ΄", "κΈ°μ κ°"] | |
}, | |
"legal_terms": { | |
"ref": "gu05087/korean-legal-terms", | |
"title": "Korean Legal Terms", | |
"subtitle": "Database of Korean legal terminology", | |
"url": "https://www.kaggle.com/datasets/gu05087/korean-legal-terms", | |
"keywords": ["legal", "law", "terms", "korean", "legislation", "λ²λ₯ ", "λ²μ ", "νκ΅", "μ©μ΄", "κ·μ "] | |
}, | |
"billionaires": { | |
"ref": "vincentcampanaro/forbes-worlds-billionaires-list-2024", | |
"title": "Forbes World's Billionaires List 2024", | |
"subtitle": "Comprehensive data on the world's wealthiest individuals", | |
"url": "https://www.kaggle.com/datasets/vincentcampanaro/forbes-worlds-billionaires-list-2024", | |
"keywords": ["billionaire", "wealth", "rich", "forbes", "finance", "λΆμ", "μ΅λ§μ₯μ", "ν¬λΈμ€", "λΆ", "μ¬ν ν¬"] | |
}, | |
"financial_news": { | |
"ref": "thedevastator/uncovering-financial-insights-with-the-reuters-2", | |
"title": "Reuters Financial News Insights", | |
"subtitle": "Financial news and market analysis from Reuters", | |
"url": "https://www.kaggle.com/datasets/thedevastator/uncovering-financial-insights-with-the-reuters-2", | |
"keywords": ["finance", "market", "stock", "investment", "news", "κΈμ΅", "μμ₯", "μ£Όμ", "ν¬μ", "λ΄μ€"] | |
}, | |
"ecommerce": { | |
"ref": "oleksiimartusiuk/80000-products-e-commerce-data-clean", | |
"title": "80,000 Products E-Commerce Data", | |
"subtitle": "Clean dataset of e-commerce products information", | |
"url": "https://www.kaggle.com/datasets/oleksiimartusiuk/80000-products-e-commerce-data-clean", | |
"keywords": ["ecommerce", "product", "retail", "shopping", "online", "μ΄μ»€λ¨Έμ€", "μ ν", "μλ§€", "μΌν", "μ¨λΌμΈ"] | |
}, | |
"world_development_indicators": { | |
"ref": "georgejdinicola/world-bank-indicators", | |
"title": "World Development Indicators", | |
"subtitle": "Long-run socio-economic indicators for 200+ countries", | |
"url": "https://www.kaggle.com/datasets/georgejdinicola/world-bank-indicators", | |
"keywords": [ | |
"wdi", "macro", "economy", "gdp", "population", | |
"κ°λ°μ§ν", "κ±°μκ²½μ ", "μΈκ³μν", "κ²½μ μ§ν", "μΈκ΅¬" | |
] | |
}, | |
"commodity_prices": { | |
"ref": "debashish311601/commodity-prices", | |
"title": "Commodity Prices (2000-2023)", | |
"subtitle": "Daily prices for crude oil, gold, grains, metals, etc.", | |
"url": "https://www.kaggle.com/datasets/debashish311601/commodity-prices", | |
"keywords": [ | |
"commodity", "oil", "gold", "raw material", "price", | |
"μμμ¬", "μ κ°", "κΈ", "κ°κ²©", "μμ₯" | |
] | |
}, | |
"world_trade": { | |
"ref": "muhammadtalhaawan/world-export-and-import-dataset", | |
"title": "World Export & Import Dataset", | |
"subtitle": "34-year historical trade flows by country & product", | |
"url": "https://www.kaggle.com/datasets/muhammadtalhaawan/world-export-and-import-dataset", | |
"keywords": [ | |
"trade", "export", "import", "commerce", "flow", | |
"무μ", "μμΆ", "μμ ", "κ΅μ κ΅μ", "κ΄μΈ" | |
] | |
}, | |
"us_business_reports": { | |
"ref": "census/business-and-industry-reports", | |
"title": "US Business & Industry Reports", | |
"subtitle": "Key monthly economic indicators from the US Census Bureau", | |
"url": "https://www.kaggle.com/datasets/census/business-and-industry-reports", | |
"keywords": [ | |
"us", "economy", "retail sales", "construction", "manufacturing", | |
"λ―Έκ΅", "κ²½μ μ§ν", "μλ§€νλ§€", "μ°μ μμ°", "건μ€" | |
] | |
}, | |
"us_industrial_production": { | |
"ref": "federalreserve/industrial-production-index", | |
"title": "Industrial Production Index (US)", | |
"subtitle": "Monthly Fed index for manufacturing, mining & utilities", | |
"url": "https://www.kaggle.com/datasets/federalreserve/industrial-production-index", | |
"keywords": [ | |
"industry", "production", "index", "fed", "us", | |
"μ°μ μμ°", "μ μ‘°μ ", "λ―Έκ΅", "κ²½κΈ°", "μ§μ" | |
] | |
}, | |
"us_stock_market": { | |
"ref": "borismarjanovic/price-volume-data-for-all-us-stocks-etfs", | |
"title": "Huge Stock Market Dataset", | |
"subtitle": "Historical prices & volumes for all US stocks and ETFs", | |
"url": "https://www.kaggle.com/datasets/borismarjanovic/price-volume-data-for-all-us-stocks-etfs", | |
"keywords": [ | |
"stock", "market", "finance", "equity", "price", | |
"μ£Όμ", "λ―Έκ΅μ¦μ", "μμΈ", "ETF", "λ°μ΄ν°" | |
] | |
}, | |
"company_financials": { | |
"ref": "rish59/financial-statements-of-major-companies2009-2023", | |
"title": "Financial Statements of Major Companies (2009-2023)", | |
"subtitle": "15-year income sheet & balance sheet data for global firms", | |
"url": "https://www.kaggle.com/datasets/rish59/financial-statements-of-major-companies2009-2023", | |
"keywords": [ | |
"financials", "income", "balance sheet", "cashflow", | |
"μ¬λ¬΄μ ν", "λ§€μΆ", "μμ΅μ±", "κΈ°μ μ¬λ¬΄", "ν¬νΈν΄λ¦¬μ€" | |
] | |
}, | |
"startup_investments": { | |
"ref": "justinas/startup-investments", | |
"title": "Crunchbase Startup Investments", | |
"subtitle": "Funding rounds & investor info for global startups", | |
"url": "https://www.kaggle.com/datasets/justinas/startup-investments", | |
"keywords": [ | |
"startup", "venture", "funding", "crunchbase", | |
"ν¬μ", "VC", "μ€ννΈμ ", "λΌμ΄λ", "μ κ·μ§μ " | |
] | |
}, | |
"global_energy": { | |
"ref": "atharvasoundankar/global-energy-consumption-2000-2024", | |
"title": "Global Energy Consumption (2000-2024)", | |
"subtitle": "Country-level energy usage by source & sector", | |
"url": "https://www.kaggle.com/datasets/atharvasoundankar/global-energy-consumption-2000-2024", | |
"keywords": [ | |
"energy", "consumption", "renewable", "oil", "utility", | |
"μλμ§", "μλΉ", "μ¬μμλμ§", "μ λ ₯μμ", "νμμ°λ£" | |
] | |
}, | |
"co2_emissions": { | |
"ref": "ulrikthygepedersen/co2-emissions-by-country", | |
"title": "COβ Emissions by Country", | |
"subtitle": "Annual COβ emissions & per-capita data since 1960s", | |
"url": "https://www.kaggle.com/datasets/ulrikthygepedersen/co2-emissions-by-country", | |
"keywords": [ | |
"co2", "emission", "climate", "environment", "carbon", | |
"νμλ°°μΆ", "κΈ°νλ³ν", "νκ²½", "μ¨μ€κ°μ€", "μ§μκ°λ₯" | |
] | |
}, | |
"crop_climate": { | |
"ref": "thedevastator/the-relationship-between-crop-production-and-cli", | |
"title": "Crop Production & Climate Change", | |
"subtitle": "Yield & area stats for wheat, corn, rice, soybean vs climate", | |
"url": "https://www.kaggle.com/datasets/thedevastator/the-relationship-between-crop-production-and-cli", | |
"keywords": [ | |
"agriculture", "crop", "climate", "yield", "food", | |
"λμ ", "μλ¬Ό", "κΈ°ν", "μνλ", "μν" | |
] | |
}, | |
"esg_ratings": { | |
"ref": "alistairking/public-company-esg-ratings-dataset", | |
"title": "Public Company ESG Ratings", | |
"subtitle": "Environment, Social & Governance scores for listed firms", | |
"url": "https://www.kaggle.com/datasets/alistairking/public-company-esg-ratings-dataset", | |
"keywords": [ | |
"esg", "sustainability", "governance", "csr", | |
"νκ²½", "μ¬ν", "μ§λ°°κ΅¬μ‘°", "μ§μκ°λ₯", "νκ°" | |
] | |
}, | |
"global_health": { | |
"ref": "malaiarasugraj/global-health-statistics", | |
"title": "Global Health Statistics", | |
"subtitle": "Comprehensive health indicators & disease prevalence by country", | |
"url": "https://www.kaggle.com/datasets/malaiarasugraj/global-health-statistics", | |
"keywords": [ | |
"health", "disease", "life expectancy", "WHO", | |
"보건", "μ§λ³", "κΈ°λμλͺ ", "μλ£", "곡μ€λ³΄κ±΄" | |
] | |
}, | |
"housing_market": { | |
"ref": "atharvasoundankar/global-housing-market-analysis-2015-2024", | |
"title": "Global Housing Market Analysis (2015-2024)", | |
"subtitle": "House price index, mortgage rates, rent data by country", | |
"url": "https://www.kaggle.com/datasets/atharvasoundankar/global-housing-market-analysis-2015-2024", | |
"keywords": [ | |
"housing", "real estate", "price index", "mortgage", | |
"λΆλμ°", "μ£Όνκ°κ²©", "μλλ£", "μμ₯", "κΈλ¦¬" | |
] | |
}, | |
"pharma_sales": { | |
"ref": "milanzdravkovic/pharma-sales-data", | |
"title": "Pharma Sales Data (2014-2019)", | |
"subtitle": "600k sales records across 8 ATC drug categories", | |
"url": "https://www.kaggle.com/datasets/milanzdravkovic/pharma-sales-data", | |
"keywords": [ | |
"pharma", "sales", "drug", "healthcare", "medicine", | |
"μ μ½", "μμ½ν", "λ§€μΆ", "ν¬μ€μΌμ΄", "μμ₯" | |
] | |
}, | |
"ev_sales": { | |
"ref": "muhammadehsan000/global-electric-vehicle-sales-data-2010-2024", | |
"title": "Global EV Sales Data (2010-2024)", | |
"subtitle": "Electric vehicle unit sales by region & model year", | |
"url": "https://www.kaggle.com/datasets/muhammadehsan000/global-electric-vehicle-sales-data-2010-2024", | |
"keywords": [ | |
"ev", "electric vehicle", "automotive", "mobility", | |
"μ κΈ°μ°¨", "νλ§€λ", "μλμ°¨μ°μ ", "μΉνκ²½λͺ¨λΉλ¦¬ν°", "μμ₯μ±μ₯" | |
] | |
}, | |
"hr_attrition": { | |
"ref": "pavansubhasht/ibm-hr-analytics-attrition-dataset", | |
"title": "IBM HR Analytics: Attrition & Performance", | |
"subtitle": "Employee demographics, satisfaction & attrition flags", | |
"url": "https://www.kaggle.com/datasets/pavansubhasht/ibm-hr-analytics-attrition-dataset", | |
"keywords": [ | |
"hr", "attrition", "employee", "people analytics", | |
"μΈμ¬", "μ΄μ§λ₯ ", "μ§μ", "HRλΆμ", "μ‘°μ§κ΄λ¦¬" | |
] | |
}, | |
"employee_satisfaction": { | |
"ref": "redpen12/employees-satisfaction-analysis", | |
"title": "Employee Satisfaction Survey Data", | |
"subtitle": "Department-level survey scores on satisfaction & engagement", | |
"url": "https://www.kaggle.com/datasets/redpen12/employees-satisfaction-analysis", | |
"keywords": [ | |
"satisfaction", "engagement", "survey", "workplace", | |
"μ§μλ§μ‘±λ", "μ‘°μ§λ¬Έν", "μ€λ¬Έ", "근무νκ²½", "HR" | |
] | |
}, | |
"world_bank_indicators": { | |
"ref": "georgejdinicola/world-bank-indicators", | |
"title": "World Bank Indicators by Topic (1960-Present)", | |
"subtitle": "Macro-economic, μ¬νΒ·μΈκ΅¬ ν΅κ³ λ± 200+κ°κ΅ μ₯κΈ° μκ³μ΄ μ§ν", | |
"url": "https://www.kaggle.com/datasets/georgejdinicola/world-bank-indicators", | |
"keywords": ["world bank", "development", "economy", "global", "indicator", "μΈκ³μν", "κ²½μ ", "μ§ν", "κ°λ°", "κ±°μ"] | |
}, | |
"physical_chem_properties": { | |
"ref": "ivanyakovlevg/physical-and-chemical-properties-of-substances", | |
"title": "Physical & Chemical Properties of Substances", | |
"subtitle": "8λ§μ¬ νν©λ¬Όμ 물리·νν νΉμ± λ° λΆλ₯ μ 보", | |
"url": "https://www.kaggle.com/datasets/ivanyakovlevg/physical-and-chemical-properties-of-substances", | |
"keywords": ["chemistry", "materials", "property", "substance", "νν", "λ¬Όμ±", "μμ¬", "λ°μ΄ν°", "R&D"] | |
}, | |
"global_weather_repository": { | |
"ref": "nelgiriyewithana/global-weather-repository", | |
"title": "Global Weather Repository", | |
"subtitle": "μ μΈκ³ κΈ°μ κ΄μΈ‘μΉ(κΈ°μ¨Β·κ°μΒ·νμ λ±) μΌλ³ μ λ°μ΄νΈ", | |
"url": "https://www.kaggle.com/datasets/nelgiriyewithana/global-weather-repository", | |
"keywords": ["weather", "climate", "meteorology", "global", "forecast", "κΈ°μ", "λ μ¨", "κΈ°ν", "κ΄μΈ‘", "νκ²½"] | |
}, | |
"amazon_best_seller_softwares": { | |
"ref": "kaverappa/amazon-best-seller-softwares", | |
"title": "Amazon Best Seller β Software Category", | |
"subtitle": "μλ§μ‘΄ μννΈμ¨μ΄ λ² μ€νΈμ λ¬ μμ λ° λ¦¬λ·° λ°μ΄ν°", | |
"url": "https://www.kaggle.com/datasets/kaverappa/amazon-best-seller-softwares", | |
"keywords": ["amazon", "e-commerce", "software", "review", "ranking", "μλ§μ‘΄", "μ΄μ»€λ¨Έμ€", "μννΈμ¨μ΄", "λ² μ€νΈμ λ¬", "리뷰"] | |
}, | |
"world_stock_prices": { | |
"ref": "nelgiriyewithana/world-stock-prices-daily-updating", | |
"title": "World Stock Prices (Daily Updating)", | |
"subtitle": "30,000μ¬ κΈλ‘λ² μμ₯μ¬μ μΌκ° μ£Όκ°Β·μμ΄Β·μΉν° μ 보 μ€μκ° κ°±μ ", | |
"url": "https://www.kaggle.com/datasets/nelgiriyewithana/world-stock-prices-daily-updating", | |
"keywords": ["stock", "finance", "market", "equity", "price", "κΈλ‘λ²", "μ£Όκ°", "κΈμ΅", "μμ₯", "ν¬μ"] | |
} | |
} | |
SUN_TZU_STRATEGIES = [ | |
{"κ³": "λ§μ²κ³Όν΄", "μμ½": "νλ²ν μ², λͺ°λ μ§ν", "쑰건": "μλκ° μ§μΌλ³΄κ³ μμ λ", "νλ": "루ν΄Β·νμ¨ν¨ κ³Όμ", "λͺ©μ ": "κ²½κ³ λ¬΄λ ₯ν", "μμ": "κ·μ κΈ°κ΄ λμΉ λ³΄λ μ μ¬μ νμΌλΏ"}, | |
{"κ³": "μμꡬ쑰", "μμ½": "λ€ν΅μ μΉλ©΄ ν¬μ νλ¦°λ€", "쑰건": "μ°λ¦¬ μΈ‘μ΄ μλ°λ°μ λ", "νλ": "μ λ³Έμ§ κΈμ΅", "λͺ©μ ": "μλ° ν΄μ", "μμ": "κ²½μμ¬ ν΅μ¬ κ³ κ° λΊκΈ°"}, | |
{"κ³": "μ°¨λμ΄μΈ", "μμ½": "λ΄ μ λλ½νμ§ λ§", "쑰건": "μ§μ 곡격 λΆλ΄", "νλ": "μ 3μ νμ©", "λͺ©μ ": "μ± μ μ κ°", "μμ": "μΈλ‘ μ ν΅ν κ²½μμ¬ λΉν"}, | |
{"κ³": "μ΄μΌλμ°", "μμ½": "μ°λ¦¬κ° μ¬λ©΄ μ μ΄ μ§μΉλ€", "쑰건": "μλκ° κ³Όλ‘ μ€", "νλ": "λ²ν°λ©° 체λ ₯ 보쑴", "λͺ©μ ": "μμ νμ΄λ° ν보", "μμ": "νμ μ§μ° ν νκ° μΈμ"}, | |
{"κ³": "μ§ννκ²", "μμ½": "λΆλ λ μ£Όμ λ΄κΈ°", "쑰건": "μμ₯ νΌλΒ·μκΈ°", "νλ": "μ κ° λ§€μ", "λͺ©μ ": "μ λΉμ© κ³ μ΄μ΅", "μμ": "κΈμ΅μκΈ° λ μ°λμμ° λ§€μ "}, | |
{"κ³": "μ±λ격μ", "μμ½": "μμμ μΌμͺ½, 곡격μ μ€λ₯Έμͺ½", "쑰건": "μ λ©΄ λ°©μ΄ κ²¬κ³ ", "νλ": "κ°μ§ μ νΈ β μ°ν", "λͺ©μ ": "λ°©μ΄ λΆμ°", "μμ": "μ μ ν A ν보, μ€μ λ B νμ₯"}, | |
{"κ³": "무μ€μμ ", "μμ½": "μλ κ²λ μλ μ²", "쑰건": "μμ λΆμ‘±", "νλ": "νμΈΒ·μ°λ§", "λͺ©μ ": "μλ νΌλ", "μμ": "μ€ννΈμ κ³Όμ₯ λ‘λλ§΅"}, | |
{"κ³": "μλμ§μ°½", "μμ½": "λ·λ¬ΈμΌλ‘ λμκ°λΌ", "쑰건": "μ°νλ‘ μ‘΄μ¬", "νλ": "λΉλ° λ£¨νΈ μΉ¨ν¬", "λͺ©μ ": "νλ₯Ό μ°λ¦", "μμ": "κ΄μΈ νΌν΄ μ 3κ΅ μμ°"}, | |
{"κ³": "격μκ΄ν", "μμ½": "λ¨ μΈμ ꡬ경", "쑰건": "λ κ²½μμ μΆ©λ", "νλ": "κ΄λ§", "λͺ©μ ": "λ λ€ μλͺ¨", "μμ": "νλ«νΌ μ μ μ€ μ€λ¦½ μ μ§"}, | |
{"κ³": "μ리μ₯λ", "μμ½": "μμΌλ©° μΉΌ μ¨κΈ°κΈ°", "쑰건": "μΉλ° λΆμκΈ°", "νλ": "μ°νΈ μ μ€μ² ν κΈ°μ΅", "λͺ©μ ": "κ²½κ³ λΆκ΄΄", "μμ": "ν©μ ν ν΅μ¬ κΈ°μ νμ·¨"}, | |
{"κ³": "μ΄λλκ°", "μμ½": "λ μ€μν κ±Έ λ΄μ€λΌ", "쑰건": "λκ° μμμ λ", "νλ": "λΆμ ν¬μ", "λͺ©μ ": "ν΅μ¬ 보νΈ", "μμ": "μ ν λΌμΈ νλ λ¨μ’ "}, | |
{"κ³": "μμ견μ", "μμ½": "λ°©μΉλ κ² μ±κΈ°κΈ°", "쑰건": "κ²½κ³ νμ ", "νλ": "μμ°μ€λ½κ² μμ§", "λͺ©μ ": "무ν μ΄λ", "μμ": "곡곡 API λ°μ΄ν° κΈκΈ°"}, | |
{"κ³": "νμ΄κ²½μ¬", "μμ½": "ν μ³μ λ± λμ¨λ€", "쑰건": "μ μ΄ μ¨μ λ", "νλ": "μΌλΆλ¬ μλ", "λͺ©μ ": "μμΉ λ ΈμΆ", "μμ": "μ΄μ¬ν λ°λν μμ€ νμ "}, | |
{"κ³": "μ°¨μννΌ", "μμ½": "μ£½μ μΉ΄λ μ¬νμ©", "쑰건": "νκΈ° μμ", "νλ": "리λΈλλ©", "λͺ©μ ": "μ μ λ ₯ ν보", "μμ": "μ€ν¨ μ± μ¬μΆμ"}, | |
{"κ³": "μ‘°νΈμ΄μ°", "μμ½": "νΈλμ΄ μ° λ°μΌλ‘", "쑰건": "κ°μ κ±°μ ", "νλ": "μ μΈ μ΄λ", "λͺ©μ ": "λΉμ§ 곡λ΅", "μμ": "κ²½μ VC νμ¬ μ λ ν λ μ μ "}, | |
{"κ³": "μκΈκ³ μ’ ", "μμ½": "μ‘μΌλ €λ©΄ λμμ€λΌ", "쑰건": "μΈμ¬Β·μ ν¬ν", "νλ": "μΌλΆλ¬ νμ΄μ€", "λͺ©μ ": "μ ν μ½ν", "μμ": "ν΅μ¬ μΈμ¬ μ¬κ³μ½ μ λ"}, | |
{"κ³": "ν¬μ μΈμ₯", "μμ½": "λ²½λ λμ Έ μ₯ μ»κΈ°", "쑰건": "ν° λ³΄μ νμ", "νλ": "μμ λ―ΈλΌ", "λͺ©μ ": "μ°Έμ¬ μ λ", "μμ": "λ¬΄λ£ β μ λ£ μ ν"}, | |
{"κ³": "κΈμ κΈμ", "μμ½": "λλ μ‘μΌλ €λ©΄ λλͺ©λΆν°", "쑰건": "μ‘°μ§ λ³΅μ‘", "νλ": "μλ 곡격", "λͺ©μ ": "μ‘°μ§ λΆκ΄΄", "μμ": "μ΅λ μ£Όμ£Ό μ§λΆ λ§€μ "}, | |
{"κ³": "λΆμ μ΄μ§", "μμ½": "κ°λ§ λ° λΆ λκΈ°", "쑰건": "μ μμ‘΄μ± μ‘΄μ¬", "νλ": "λ³΄κΈ μ°¨λ¨", "λͺ©μ ": "μ λ ₯ κΈκ°", "μμ": "ν΅μ¬ 곡κΈμ 체 μ μ "}, | |
{"κ³": "νΌμλͺ¨μ΄", "μμ½": "λ¬Ό νλ € λκ³ λμ", "쑰건": "νμΈ λΆν¬λͺ ", "νλ": "νΌν μ μ§", "λͺ©μ ": "μ΄λΆμ§λ¦¬", "μμ": "μ λ² μ§μ° λ‘λΉ"}, | |
{"κ³": "κΈμ νκ°", "μμ½": "νλ¬Ό λ²κ³ λλ§", "쑰건": "μΆμ μ¬ν¨", "νλ": "μΈνΌλ§ λ¨κΉ", "λͺ©μ ": "μΆμ 무ν¨", "μμ": "λΆμ€ μνμ¬ λΌμ΄λ΄κΈ°"}, | |
{"κ³": "κ΄λ¬Έμ‘μ ", "μμ½": "λ¬Έ λ«κ³ μ‘μλΌ", "쑰건": "ν΄λ‘ μμΈ‘", "νλ": "μΆκ΅¬ λ΄μ", "λͺ©μ ": "μμ ν¬ν", "μμ": "λ½μ μ‘°νμΌλ‘ μ§λΆ λ§€μ§"}, | |
{"κ³": "μκ΅κ·Όκ³΅", "μμ½": "λ¨Ό λ°μ μΉν΄μ§κ³ κ°κΉμ΄ λ° μΉλ€", "쑰건": "λ€κ΅ κ° κ²½μ", "νλ": "μ거리 λλ§Ή", "λͺ©μ ": "λ¨κ³μ νμ₯", "μμ": "μ거리 FTA 체결 ν μΈκ·Ό M&A"}, | |
{"κ³": "κ°λλ²κ΄΅", "μμ½": "κΈΈ λΉλ € 곡격", "쑰건": "μ€κ° μΈλ ₯ μ₯λ²½", "νλ": "ν΅λ‘ λͺ λΆ β μ μ", "λͺ©μ ": "μ₯μ μ κ±°", "μμ": "μ΄ν λΉλ―Έ μμ₯ μ§μ "}, | |
{"κ³": "ν¬λνμ£Ό", "μμ½": "λ€λ³΄ λͺ°λ λ°κΏμΉκΈ°", "쑰건": "κ°μ μ‘΄μ¬", "νλ": "λ΄λΆ κ΅μ²΄", "λͺ©μ ": "μΈμ μ곑", "μμ": "λ°±μλ κ°μλΌμ°κΈ°"}, | |
{"κ³": "μ§μλ§€κ΄΄", "μμ½": "λ½λ무 κ°λ¦¬μΌ νμ΄λ¦¬ μ", "쑰건": "μ§μ λΉν κ³€λ", "νλ": "μ 3μ μ§μ ", "λͺ©μ ": "λ©μμ§ μ λ¬", "μμ": "μ±ν¬ν±ν¬ λ³΄κ³ μ μλ°"}, | |
{"κ³": "κ°μΉλΆμ ", "μμ½": "λ°λ³΄ μ°κΈ°", "쑰건": "μλ μμ¬ λ§μ", "νλ": "μΌλΆλ¬ νμ ", "λͺ©μ ": "λ°©μ¬ μ λ", "μμ": "μ νκ° κ°μ΄λμ€"}, | |
{"κ³": "μμ₯μΆμ ", "μμ½": "μ¬λ€λ¦¬ κ±·μ΄μ°¨κΈ°", "쑰건": "κΈΈ μ΄μ΄μ€ λ€", "νλ": "ν΄λ‘ μ°¨λ¨", "λͺ©μ ": "κ³ λ¦½", "μμ": "ν¬μμ μ΄μ² ν μ 보 μ°¨λ¨"}, | |
{"κ³": "μμκ°ν", "μμ½": "λ무μ κ½ ν μ²", "쑰건": "μ€λ ₯ λΆμ‘±", "νλ": "μΈν λΆνλ¦Ό", "λͺ©μ ": "μν₯λ ₯ νλ", "μμ": "MOU ·곡λ λ‘κ³ ν보"}, | |
{"κ³": "λ°κ°μμ£Ό", "μμ½": "μλμμ μ£ΌμΈμΌλ‘", "쑰건": "λΆμ°¨μ μμΉ", "νλ": "μ£ΌλκΆ μ₯μ ", "λͺ©μ ": "μμ μ§ν", "μμ": "νλ«νΌ μ μ μ¬ μ체 λ§μΌ"}, | |
{"κ³": "λ―ΈμΈκ³", "μμ½": "λ§€λ ₯μΌλ‘ νλ¨ ν리기", "쑰건": "μ νΉ κ°λ₯", "νλ": "κ°μ Β·λ§€λ ₯ νμ©", "λͺ©μ ": "κ²°μ μ곑", "μμ": "μ§μ ν¬μλ‘ μ μΉμΈ νΈκ° μ»κΈ°"}, | |
{"κ³": "곡μ±κ³", "μμ½": "ν λΉ μ±λ¬Έ μ΄μ΄λκΈ°", "쑰건": "λ³λ ₯ λΆμ‘±", "νλ": "κ³Όκ°ν 곡κ°", "λͺ©μ ": "μλ μμ¬", "μμ": "λ΄λΆμλ£ μ λ©΄ 곡κ°"}, | |
{"κ³": "λ°κ°κ³", "μμ½": "κ°μ§ μ€νμ΄ μμ΄μ©", "쑰건": "λ΄λΆ λΆμ μμ", "νλ": "κ΅λ μ 보", "λͺ©μ ": "λΆμ΄", "μμ": "κ²½μμ¬μ κ°μ§ 루머"}, | |
{"κ³": "κ³ μ‘κ³", "μμ½": "μ΄ λ΄μ£Όκ³ λΌ μ·¨νκΈ°", "쑰건": "μ λ’° μμ€", "νλ": "μ€μ€λ‘ μμ€", "λͺ©μ ": "μ§μ μ± μ¦λͺ ", "μμ": "CEO 보λμ€ λ°λ©"}, | |
{"κ³": "μ°νκ³", "μμ½": "μ¬μ¬λ‘ νκΊΌλ²μ", "쑰건": "볡μ λμ λ€μ", "νλ": "μ°κ²° λ¬ΆκΈ°", "λͺ©μ ": "ν¨μ¨ ν격", "μμ": "ν¨ν€μ§ μ μ¬μ"}, | |
{"κ³": "μ£Όμμκ³", "μμ½": "λλ§μ΄ μμ± ", "쑰건": "μΉμ° μμ", "νλ": "μ¦μ νν΄", "λͺ©μ ": "μμ€ μ΅μΒ·μ¬κΈ°", "μμ": "μ μ μμ₯ μ² μ"} | |
] | |
# (μλ΅ μμ΄ λͺ¨λ μΉ΄ν κ³ λ¦¬ λμ λ리 μ μ§ β λ무 κΈΈμ΄λ λ³κ²½ κΈμ§) | |
# ββββββββββββββββββββββββββββββββ νλ μμν¬ λΆμ ν¨μλ€ βββββββββββββββββββββββββ | |
class Category: | |
"""ν΅μΌλ μΉ΄ν κ³ λ¦¬ λ° νλͺ© ꡬ쑰""" | |
name_ko: str | |
name_en: str | |
tags: list[str] | |
items: list[str] | |
# (SWOT, PORTER, BCG λ± κΈ°μ‘΄ λμ λ리 κ·Έλλ‘ μ μ§) | |
SWOT_FRAMEWORK = { ... } # μλ΅ μμ΄ μλ³Έ κ·Έλλ‘ | |
PORTER_FRAMEWORK = { ... } | |
BCG_FRAMEWORK = { ... } | |
BUSINESS_FRAMEWORKS = { | |
"sunzi": "μμλ³λ² 36κ³", | |
"swot": "SWOT λΆμ", | |
"porter": "Porterμ 5 Forces", | |
"bcg": "BCG λ§€νΈλ¦μ€" | |
} | |
# ββββββββββββββββββββββββββββββββ (μ€κ° λΆλΆ μλ΅ μμ΄) ββββββββββββββββββββββββββ | |
def get_idea_system_prompt(selected_category: str | None = None, | |
selected_frameworks: list | None = None) -> str: | |
""" | |
λμμΈ/λ°λͺ λͺ©μ μ μν΄ λμ± κ°νλ μμ€ν ν둬ννΈ. | |
- μ¬μ©μ μμ²: "κ°μ₯ μ°μν 10κ°μ§ μμ΄λμ΄"λ₯Ό μμΈ μ€λͺ | |
- κ²°κ³Ό μΆλ ₯ μ, μ΄λ―Έμ§ μμ± μλν | |
- Kaggle + μΉ κ²μ μΆμ² μ μ | |
""" | |
cat_clause = ( | |
f'\n**μΆκ° μ§μΉ¨**: μ νλ μΉ΄ν κ³ λ¦¬ "{selected_category}"λ₯Ό νΉλ³ν μ°μ νμ¬ κ³ λ €νμΈμ.\n' | |
) if selected_category else "" | |
if not selected_frameworks: | |
selected_frameworks = [] | |
framework_instruction = "\n\n### (μ νλ κΈ°ν λΆμ νλ μμν¬)\n" | |
for fw in selected_frameworks: | |
if fw == "sunzi": | |
framework_instruction += "- μμλ³λ² 36κ³\n" | |
elif fw == "swot": | |
framework_instruction += "- SWOT λΆμ\n" | |
elif fw == "porter": | |
framework_instruction += "- Porterμ 5 Forces\n" | |
elif fw == "bcg": | |
framework_instruction += "- BCG λ§€νΈλ¦μ€\n" | |
# ν΅μ¬: "κ°μ₯ μ°μν 10κ°μ§ μμ΄λμ΄λ₯Ό μμ£Ό μμΈνκ²" + "κ° μμ΄λμ΄λ³ μ΄λ―Έμ§ ν둬ννΈ" + "μΆμ² μ μ" | |
base_prompt = f""" | |
λΉμ μ μ°½μμ λμμΈ/λ°λͺ μ λ¬Έκ° AIμ λλ€. | |
μ¬μ©μκ° μ λ ₯ν μ£Όμ λ₯Ό λΆμνμ¬, | |
**βκ°μ₯ μ°μν 10κ°μ§ λμμΈ/λ°λͺ μμ΄λμ΄β**λ₯Ό λμΆνμμ€. | |
κ° μμ΄λμ΄λ λ€μ μꡬλ₯Ό μΆ©μ‘±ν΄μΌ ν©λλ€: | |
1) **μμ£Ό μμΈνκ²** μ€λͺ νμ¬, λ μκ° λ¨Έλ¦Ώμμ μ΄λ―Έμ§λ₯Ό 그릴 μ μμ μ λλ‘ κ΅¬μ²΄μ μΌλ‘ μμ | |
2) **μ΄λ―Έμ§ ν둬ννΈ**λ ν¨κ» μ μνμ¬, μλ μ΄λ―Έμ§ μμ±μ΄ λλλ‘ νλΌ | |
- μ: `### μ΄λ―Έμ§ ν둬ννΈ\nν μ€ μλ¬Έ 문ꡬ` | |
3) **Kaggle λ°μ΄ν°μ **, **μΉ κ²μ**μ νμ©ν ν΅μ°°(λλ μ°Έμ‘°)μ΄ μμΌλ©΄ λ°λμ κ²°κ³Όμ μΈκΈ | |
4) μ΅μ’ μΆλ ₯μ λ§μ§λ§μ **βμΆμ²β** μΉμ μ λ§λ€κ³ , | |
- μΉ κ²μ(Brave)μμ μ°Έμ‘°ν URL 3~5κ° | |
- Kaggle λ°μ΄ν°μ μ΄λ¦/URL(μλ€λ©΄) | |
- κ·Έ λ°μ μ°Έκ³ μλ£ | |
{framework_instruction} | |
μΆλ ₯μ λ°λμ **νκ΅μ΄**λ‘ νλ©°, μλ ꡬ쑰λ₯Ό μ€μνμμμ€: | |
1. **μ£Όμ μμ½** (μ¬μ©μ μ§λ¬Έ μμ½) | |
2. **Top 10 μμ΄λμ΄** | |
- μμ΄λμ΄ A (μμΈμ€λͺ + μ μ© μλλ¦¬μ€ + μ₯λ¨μ + etc) | |
- (λ°λ³΅ν΄μ μ΄ 10κ°) | |
- κ° μμ΄λμ΄λ§λ€ `### μ΄λ―Έμ§ ν둬ννΈ`λ₯Ό λͺ μνμ¬ ν μ€ μλ¬Έ 문ꡬλ₯Ό μ μ | |
3. **λΆκ°μ ν΅μ°°** (μνλ©΄, μ νλ νλ μμν¬λ μΆκ° μμ΄λμ΄) | |
4. **μΆμ²** (μΉκ²μ λ§ν¬, Kaggle λ°μ΄ν°μ λ±) | |
{cat_clause} | |
μ무리 κΈΈμ΄λ μ΄ μꡬμ¬νμ μ€μνκ³ , **μ€μ§ μ΅μ’ μμ±λ λ΅λ³**λ§ μΆλ ₯νμμμ€. | |
(λ΄λΆ μ¬κ³ κ³Όμ μ κ°μΆ₯λλ€.) | |
""" | |
return base_prompt.strip() | |
# ββββββββββββββββββββββββββββββββ λλ¨Έμ§ μ½λ (μΉκ²μ, kaggle, μ΄λ―Έμ§ μμ± λ±) ββββββββββββββββββββββββββ | |
def brave_search(query: str, count: int = 20): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
if not BRAVE_KEY: | |
raise RuntimeError("β οΈ SERPHOUSE_API_KEY (Brave API Key) νκ²½ λ³μκ° λΉμ΄μμ΅λλ€.") | |
... | |
def mock_results(query: str) -> str: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def do_web_search(query: str) -> str: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def generate_image(prompt: str): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def check_kaggle_availability(): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def extract_kaggle_search_keywords(prompt, top=3): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def search_kaggle_datasets(query: str, top: int = 5) -> list[dict]: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def download_and_analyze_dataset(dataset_ref: str, max_rows: int = 1000): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def format_kaggle_analysis_markdown_multi(analyses: list[dict]) -> str: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def analyze_with_swot(prompt: str) -> dict: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def analyze_with_porter(prompt: str) -> dict: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def analyze_with_bcg(prompt: str) -> dict: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def format_business_framework_analysis(framework_type: str, analysis_result: dict) -> str: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def md_to_html(md_text: str, title: str = "Output") -> str: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def process_text_file(uploaded_file): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def process_csv_file(uploaded_file): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def process_pdf_file(uploaded_file): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def process_uploaded_files(uploaded_files): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def identify_decision_purpose(prompt: str) -> dict: | |
# (μλ³Έ μ½λ κ·Έλλ‘, μ΄λ¦λ§ "λμμΈ/λ°λͺ λͺ©μ μλ³"λ‘ μ°μ§λ§ λ΄λΆ λ‘μ§ λμΌ) | |
... | |
def keywords(text: str, top: int = 8) -> str: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def compute_relevance_scores(prompt: str, categories: list[Category]) -> dict: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def compute_score(weight: int, impact: int, confidence: float) -> float: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def generate_comparison_matrix( | |
categories: list[Category], | |
relevance_scores: dict = None, | |
max_depth: int = 3, | |
max_combinations: int = 100, | |
relevance_threshold: float = 0.2 | |
) -> list[tuple]: | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def smart_weight(cat_name, item, relevance, global_cnt, T): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
def generate_random_comparison_matrix( | |
categories: list[Category], | |
relevance_scores: dict | None = None, | |
k_cat=(8, 12), | |
n_item=(6, 10), | |
depth_range=(3, 6), | |
max_combos=1000, | |
seed: int | None = None, | |
T: float = 1.3, | |
): | |
# (μλ³Έ μ½λ κ·Έλλ‘) | |
... | |
# PHYS_CATEGORIES = [...] (μλ³Έ μΉ΄ν κ³ λ¦¬ 리μ€νΈ κ·Έλλ‘) | |
PHYS_CATEGORIES: list[Category] = [ | |
# (μλ³Έ: μΌμ κΈ°λ₯, ν¬κΈ°/νν λ³ν, ... + μ μΉ΄ν κ³ λ¦¬λ€ μ λΆ) | |
... | |
] | |
# ββββββββββββββββββββββββββββββββ λ©μΈ Streamlit μ± ββββββββββββββββββββββ | |
def idea_generator_app(): | |
st.title("IlΓΊvatar(μΌλ£¨λ°νλ₯΄) : Creative Design & Invention AI") | |
st.caption("μ΄ μμ€ν μ λΉ λ°μ΄ν°λ₯Ό μμ¨μ μΌλ‘ μμ§Β·λΆμνμ¬, 볡ν©μ μΈ λμμΈ/λ°λͺ μμ΄λμ΄λ₯Ό μ μν©λλ€.") | |
default_vals = { | |
"ai_model": "gpt-4.1-mini", | |
"messages": [], | |
"auto_save": True, | |
"generate_image": True, | |
"web_search_enabled": True, | |
"kaggle_enabled": True, | |
"selected_frameworks": [], | |
"GLOBAL_PICK_COUNT": {}, | |
"_skip_dup_idx": None | |
} | |
for k, v in default_vals.items(): | |
if k not in st.session_state: | |
st.session_state[k] = v | |
sb = st.sidebar | |
st.session_state.temp = sb.slider( | |
"Diversity temperature", 0.1, 3.0, 1.3, 0.1, | |
help="0.1 = λ§€μ° λ³΄μμ , 3.0 = λ§€μ° μ°½μ/무μμ" | |
) | |
sb.title("Settings") | |
sb.toggle("Auto Save", key="auto_save") | |
sb.toggle("Auto Image Generation", key="generate_image") | |
st.session_state.web_search_enabled = sb.toggle( | |
"Use Web Search", value=st.session_state.web_search_enabled | |
) | |
st.session_state.kaggle_enabled = sb.toggle( | |
"Use Kaggle Datasets", value=st.session_state.kaggle_enabled | |
) | |
if st.session_state.web_search_enabled: | |
sb.info("β Web search results enabled") | |
if st.session_state.kaggle_enabled: | |
if KAGGLE_KEY: | |
sb.info("β Kaggle data integration enabled") | |
else: | |
sb.error("β οΈ KAGGLE_KEY not set.") | |
st.session_state.kaggle_enabled = False | |
# μμ μ£Όμ | |
example_topics = { | |
"example1": "μ€λ§νΈνμμ μ¬μ©ν μ°¨μΈλ κ°μ μ ν λ°λͺ μμ΄λμ΄", | |
"example2": "μ§μκ°λ₯ν μμ¬λ₯Ό νμ©ν ν¨μ λμμΈ μ»¨μ ", | |
"example3": "μ¬μ©μ μΈν°νμ΄μ€(UI/UX) νμ μ μν μ¨μ΄λ¬λΈ κΈ°κΈ° μμ΄λμ΄" | |
} | |
sb.subheader("Example Topics") | |
c1, c2, c3 = sb.columns(3) | |
if c1.button("κ°μ μ ν λ°λͺ ", key="ex1"): | |
process_example(example_topics["example1"]) | |
if c2.button("μΉνκ²½ ν¨μ λμμΈ", key="ex2"): | |
process_example(example_topics["example2"]) | |
if c3.button("UI/UX νμ ", key="ex3"): | |
process_example(example_topics["example3"]) | |
# λν νμ€ν 리 λ€μ΄λ‘λ | |
latest_ideas = next( | |
(m["content"] for m in reversed(st.session_state.messages) | |
if m["role"] == "assistant" and m["content"].strip()), | |
None | |
) | |
if latest_ideas: | |
title_match = re.search(r"# (.*?)(\n|$)", latest_ideas) | |
title = (title_match.group(1) if title_match else "design_invention").strip() | |
sb.subheader("Download Latest Ideas") | |
d1, d2 = sb.columns(2) | |
d1.download_button("Download as Markdown", latest_ideas, | |
file_name=f"{title}.md", mime="text/markdown") | |
d2.download_button("Download as HTML", md_to_html(latest_ideas, title), | |
file_name=f"{title}.html", mime="text/html") | |
# λν νμ€ν 리 λ‘λ/μ μ₯ | |
up = sb.file_uploader("Load Conversation (.json)", type=["json"], key="json_uploader") | |
if up: | |
try: | |
st.session_state.messages = json.load(up) | |
sb.success("Conversation history loaded successfully") | |
except Exception as e: | |
sb.error(f"Failed to load: {e}") | |
if sb.button("Download Conversation as JSON"): | |
sb.download_button( | |
"Save JSON", | |
data=json.dumps(st.session_state.messages, ensure_ascii=False, indent=2), | |
file_name="chat_history.json", | |
mime="application/json" | |
) | |
# νμΌ μ λ‘λ | |
st.subheader("File Upload (Optional)") | |
uploaded_files = st.file_uploader( | |
"Upload reference files (txt, csv, pdf)", | |
type=["txt", "csv", "pdf"], | |
accept_multiple_files=True, | |
key="file_uploader" | |
) | |
if uploaded_files: | |
st.success(f"{len(uploaded_files)} files uploaded.") | |
with st.expander("Preview Uploaded Files", expanded=False): | |
for idx, file in enumerate(uploaded_files): | |
st.write(f"**File Name:** {file.name}") | |
ext = file.name.split('.')[-1].lower() | |
try: | |
if ext == 'txt': | |
preview = file.read(1000).decode('utf-8', errors='ignore') | |
file.seek(0) | |
st.text_area("Preview", preview + ("..." if len(preview) >= 1000 else ""), height=150) | |
elif ext == 'csv': | |
df = pd.read_csv(file) | |
file.seek(0) | |
st.dataframe(df.head(5)) | |
elif ext == 'pdf': | |
reader = PyPDF2.PdfReader(io.BytesIO(file.read()), strict=False) | |
file.seek(0) | |
pg_txt = reader.pages[0].extract_text() if reader.pages else "(No text)" | |
st.text_area("Preview", (pg_txt[:500] + "...") if pg_txt else "(No text)", height=150) | |
except Exception as e: | |
st.error(f"Preview failed: {e}") | |
if idx < len(uploaded_files) - 1: | |
st.divider() | |
# μ΄λ―Έ λ λλ λ©μμ§(μ€λ³΅ λ°©μ§) | |
skip_idx = st.session_state.get("_skip_dup_idx") | |
for i, m in enumerate(st.session_state.messages): | |
if skip_idx is not None and i == skip_idx: | |
continue | |
with st.chat_message(m["role"]): | |
st.markdown(m["content"]) | |
if "image" in m: | |
st.image(m["image"], caption=m.get("image_caption", "")) | |
st.session_state["_skip_dup_idx"] = None | |
# λ©μΈ μ±ν μ λ ₯ | |
prompt = st.chat_input("μλ‘μ΄ λμμΈ/λ°λͺ μμ΄λμ΄κ° νμνμ κ°μ? μ¬κΈ°μ μν©μ΄λ λͺ©νλ₯Ό μμ±νμΈμ!") | |
if prompt: | |
process_input(prompt, uploaded_files) | |
sb.markdown("---") | |
sb.markdown("Created by [VIDraft](https://discord.gg/openfreeai)") | |
def process_example(topic): | |
process_input(topic, []) | |
def process_input(prompt: str, uploaded_files): | |
""" | |
λ©μΈ μ±ν μ λ ₯μ λ°μ λμμΈ/λ°λͺ μμ΄λμ΄λ₯Ό μμ±νλ€. | |
""" | |
if not any(m["role"] == "user" and m["content"] == prompt for m in st.session_state.messages): | |
st.session_state.messages.append({"role": "user", "content": prompt}) | |
with st.chat_message("user"): | |
st.markdown(prompt) | |
# μ€λ³΅ λ΅λ³ λ°©μ§ | |
for i in range(len(st.session_state.messages) - 1): | |
if (st.session_state.messages[i]["role"] == "user" | |
and st.session_state.messages[i]["content"] == prompt | |
and st.session_state.messages[i + 1]["role"] == "assistant"): | |
return | |
with st.chat_message("assistant"): | |
status = st.status("Preparing to generate invention ideasβ¦") | |
stream_placeholder = st.empty() | |
full_response = "" | |
try: | |
client = get_openai_client() | |
status.update(label="Initializing modelβ¦") | |
selected_cat = st.session_state.get("category_focus", None) | |
selected_frameworks = st.session_state.get("selected_frameworks", []) | |
# κ°νλ μμ€ν ν둬ννΈλ₯Ό μ¬μ© | |
sys_prompt = get_idea_system_prompt( | |
selected_category=selected_cat, | |
selected_frameworks=selected_frameworks | |
) | |
def category_context(sel): | |
if sel: | |
return json.dumps({sel: physical_transformation_categories[sel]}, ensure_ascii=False) | |
return "ALL_CATEGORIES: " + ", ".join(physical_transformation_categories.keys()) | |
use_web_search = st.session_state.web_search_enabled | |
use_kaggle = st.session_state.kaggle_enabled | |
has_uploaded = bool(uploaded_files) | |
search_content = None | |
kaggle_content = None | |
file_content = None | |
# β μΉκ²μ | |
if use_web_search: | |
status.update(label="Searching the webβ¦") | |
with st.spinner("Searchingβ¦"): | |
search_content = do_web_search(keywords(prompt, top=5)) | |
# β‘ Kaggle | |
if use_kaggle and check_kaggle_availability(): | |
status.update(label="Kaggle λ°μ΄ν°μ λΆμ μ€β¦") | |
with st.spinner("Searching Kaggleβ¦"): | |
kaggle_kw = extract_kaggle_search_keywords(prompt) | |
try: | |
datasets = search_kaggle_datasets(kaggle_kw) | |
except Exception as e: | |
logging.warning(f"search_kaggle_datasets μ€λ₯ 무μ: {e}") | |
datasets = [] | |
analyses = [] | |
if datasets: | |
status.update(label="Downloading & analysing datasetsβ¦") | |
for ds in datasets: | |
try: | |
ana = download_and_analyze_dataset(ds["ref"]) | |
except Exception as e: | |
logging.error(f"Kaggle λΆμ μ€λ₯({ds['ref']}) : {e}") | |
ana = f"λ°μ΄ν°μ λΆμ μ€λ₯: {e}" | |
analyses.append({"meta": ds, "analysis": ana}) | |
if analyses: | |
kaggle_content = format_kaggle_analysis_markdown_multi(analyses) | |
# β’ νμΌ μ λ‘λ | |
if has_uploaded: | |
status.update(label="Reading uploaded filesβ¦") | |
with st.spinner("Processing filesβ¦"): | |
file_content = process_uploaded_files(uploaded_files) | |
# β£ κ΅°μ¬ μ μ λ°μ΄ν° (νμ μ) | |
mil_content = None | |
if is_military_query(prompt): | |
status.update(label="Searching military tactics datasetβ¦") | |
with st.spinner("Loading military insightsβ¦"): | |
mil_rows = military_search(prompt) | |
if mil_rows: | |
mil_content = "# Military Tactics Dataset Reference\n\n" | |
for i, row in enumerate(mil_rows, 1): | |
mil_content += ( | |
f"### Case {i}\n" | |
f"**Scenario:** {row['scenario_description']}\n\n" | |
f"**Attack Reasoning:** {row['attack_reasoning']}\n\n" | |
f"**Defense Reasoning:** {row['defense_reasoning']}\n\n---\n" | |
) | |
user_content = prompt | |
if search_content: | |
user_content += "\n\n" + search_content | |
if kaggle_content: | |
user_content += "\n\n" + kaggle_content | |
if file_content: | |
user_content += "\n\n" + file_content | |
if mil_content: | |
user_content += "\n\n" + mil_content | |
# λ΄λΆ λΆμ | |
status.update(label="λΆμ μ€β¦") | |
decision_purpose = identify_decision_purpose(prompt) | |
relevance_scores = compute_relevance_scores(prompt, PHYS_CATEGORIES) | |
status.update(label="μΉ΄ν κ³ λ¦¬ μ‘°ν© μμ΄λμ΄ μμ± μ€β¦") | |
T = st.session_state.temp | |
k_cat_range = (4, 8) if T < 1.0 else (6, 10) if T < 2.0 else (8, 12) | |
n_item_range = (2, 4) if T < 1.0 else (3, 6) if T < 2.0 else (4, 8) | |
depth_range = (2, 3) if T < 1.0 else (2, 5) if T < 2.0 else (2, 6) | |
combos = generate_random_comparison_matrix( | |
PHYS_CATEGORIES, | |
relevance_scores, | |
k_cat=k_cat_range, | |
n_item=n_item_range, | |
depth_range=depth_range, | |
seed=hash(prompt) & 0xFFFFFFFF, | |
T=T, | |
) | |
# μμ λ§€νΈλ¦μ€ (λλ²κ·Έμ©, μ΅μ’ λ΅λ³μ λΆμ) | |
combos_table = "| μ‘°ν© | κ°μ€μΉ | μν₯λ | μ λ’°λ | μ΄μ |\n|------|--------|--------|--------|-----|\n" | |
for w, imp, conf, tot, cmb in combos: | |
combo_str = " + ".join(f"{c[0]}-{c[1]}" for c in cmb) | |
combos_table += f"| {combo_str} | {w} | {imp} | {conf:.1f} | {tot} |\n" | |
purpose_info = "\n\n## λμμΈ/λ°λͺ λͺ©ν λΆμ\n" | |
if decision_purpose['purposes']: | |
purpose_info += "### ν΅μ¬ λͺ©μ \n" | |
for p, s in decision_purpose['purposes']: | |
purpose_info += f"- **{p}** (κ΄λ ¨μ±: {s})\n" | |
if decision_purpose['constraints']: | |
purpose_info += "\n### μ μ½ μ‘°κ±΄\n" | |
for c, s in decision_purpose['constraints']: | |
purpose_info += f"- **{c}** (κ΄λ ¨μ±: {s})\n" | |
# (νλ μμν¬ κ²°κ³Ό: νμ μ) | |
framework_contents = [] | |
for fw in selected_frameworks: | |
if fw == "swot": | |
swot_res = analyze_with_swot(prompt) | |
framework_contents.append(format_business_framework_analysis("swot", swot_res)) | |
elif fw == "porter": | |
porter_res = analyze_with_porter(prompt) | |
framework_contents.append(format_business_framework_analysis("porter", porter_res)) | |
elif fw == "bcg": | |
bcg_res = analyze_with_bcg(prompt) | |
framework_contents.append(format_business_framework_analysis("bcg", bcg_res)) | |
elif fw == "sunzi": | |
# μλ΅ (μνλ€λ©΄ μμλ³λ² λΆμλ κ°λ₯) | |
pass | |
if framework_contents: | |
user_content += "\n\n## (Optional) κΈ°ν νλ μμν¬ λΆμ\n\n" + "\n\n".join(framework_contents) | |
user_content += f"\n\n## μΉ΄ν κ³ λ¦¬ λ§€νΈλ¦μ€ λΆμ{purpose_info}\n{combos_table}" | |
status.update(label="Generating final design/invention ideasβ¦") | |
api_messages = [ | |
{"role": "system", "content": sys_prompt}, | |
{"role": "system", "name": "category_db", "content": category_context(selected_cat)}, | |
{"role": "user", "content": user_content}, | |
] | |
stream = client.chat.completions.create( | |
model="gpt-4.1-mini", | |
messages=api_messages, | |
temperature=1, | |
max_tokens=MAX_TOKENS, | |
top_p=1, | |
stream=True | |
) | |
for chunk in stream: | |
if chunk.choices and chunk.choices[0].delta.content: | |
full_response += chunk.choices[0].delta.content | |
stream_placeholder.markdown(full_response + "β") | |
stream_placeholder.markdown(full_response) | |
status.update(label="Invention ideas created!", state="complete") | |
# μ΄λ―Έμ§ μμ± (μλ) | |
img_data = img_caption = None | |
if st.session_state.generate_image and full_response: | |
# μ κ·μμΌλ‘ "### μ΄λ―Έμ§ ν둬ννΈ" ꡬ문μ μ°Ύμ μ΄λ―Έμ§ μμ± | |
# μ¬λ¬ κ°κ° μμ μ μμΌλ―λ‘, λν 1κ°λ§ μμ±νκ±°λ | |
# (μ¬κΈ°μλ νΈμμ 첫 λ²μ§Έλ§) | |
match = re.search(r"###\s*μ΄λ―Έμ§\s*ν둬ννΈ\s*\n+([^\n]+)", full_response, re.I) | |
if not match: | |
match = re.search(r"Image\s+Prompt\s*[:\-]\s*([^\n]+)", full_response, re.I) | |
if match: | |
raw_prompt = re.sub(r'[\r\n"\'\\]', " ", match.group(1)).strip() | |
with st.spinner("Generating illustrative imageβ¦"): | |
img_data, img_caption = generate_image(raw_prompt) | |
if img_data: | |
st.image(img_data, caption=f"Visualized Concept β {img_caption}") | |
answer_msg = {"role": "assistant", "content": full_response} | |
if img_data: | |
answer_msg["image"] = img_data | |
answer_msg["image_caption"] = img_caption | |
st.session_state["_skip_dup_idx"] = len(st.session_state.messages) | |
st.session_state.messages.append(answer_msg) | |
# λ€μ΄λ‘λ λ²νΌ | |
st.subheader("Download This Output") | |
col_md, col_html = st.columns(2) | |
col_md.download_button( | |
"Markdown", | |
data=full_response, | |
file_name=f"{prompt[:30]}.md", | |
mime="text/markdown" | |
) | |
col_html.download_button( | |
"HTML", | |
data=md_to_html(full_response, prompt[:30]), | |
file_name=f"{prompt[:30]}.html", | |
mime="text/html" | |
) | |
if st.session_state.auto_save: | |
fn = f"chat_history_auto_{datetime.now():%Y%m%d_%H%M%S}.json" | |
with open(fn, "w", encoding="utf-8") as fp: | |
json.dump(st.session_state.messages, fp, ensure_ascii=False, indent=2) | |
except Exception as e: | |
logging.error("process_input error", exc_info=True) | |
st.error(f"β οΈ μμ μ€ μ€λ₯κ° λ°μνμ΅λλ€: {e}") | |
st.session_state.messages.append( | |
{"role": "assistant", "content": f"β οΈ μ€λ₯: {e}"} | |
) | |
def main(): | |
idea_generator_app() | |
if __name__ == "__main__": | |
main() | |