Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -25,29 +25,28 @@ import tempfile
|
|
| 25 |
import glob
|
| 26 |
import shutil
|
| 27 |
|
| 28 |
-
# ───
|
| 29 |
import pyarrow.parquet as pq
|
| 30 |
from sklearn.feature_extraction.text import TfidfVectorizer
|
| 31 |
from sklearn.metrics.pairwise import cosine_similarity
|
| 32 |
|
| 33 |
-
# ───
|
| 34 |
import httpx
|
| 35 |
from httpx import RemoteProtocolError
|
| 36 |
|
| 37 |
-
# ▸ backoff
|
| 38 |
try:
|
| 39 |
import backoff
|
| 40 |
except ImportError:
|
| 41 |
-
logging.warning("`backoff`
|
| 42 |
|
| 43 |
def _simple_backoff_on_exception(exceptions, *args, **kwargs):
|
| 44 |
"""
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
- exceptions :
|
| 48 |
-
- max_tries : kwargs
|
| 49 |
-
- base : kwargs
|
| 50 |
-
기타 인자는 무시합니다.
|
| 51 |
"""
|
| 52 |
max_tries = kwargs.get("max_tries", 3)
|
| 53 |
base = kwargs.get("base", 2)
|
|
@@ -64,7 +63,7 @@ except ImportError:
|
|
| 64 |
raise
|
| 65 |
sleep = base ** attempt
|
| 66 |
logging.info(
|
| 67 |
-
f"[retry {attempt}/{max_tries}] {fn.__name__} -> {e} … {sleep}s
|
| 68 |
)
|
| 69 |
time.sleep(sleep)
|
| 70 |
return wrapper
|
|
@@ -76,7 +75,7 @@ except ImportError:
|
|
| 76 |
backoff = _DummyBackoff()
|
| 77 |
|
| 78 |
|
| 79 |
-
# ─────────────────────────────── Environment Variables / Constants ─────────────────────────
|
| 80 |
|
| 81 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
|
| 82 |
BRAVE_KEY = os.getenv("SERPHOUSE_API_KEY", "") # Brave Search API
|
|
@@ -85,23 +84,23 @@ KAGGLE_KEY = os.getenv("KAGGLE_KEY", "")
|
|
| 85 |
KAGGLE_API_KEY = KAGGLE_KEY
|
| 86 |
|
| 87 |
if not (KAGGLE_USERNAME and KAGGLE_KEY):
|
| 88 |
-
raise RuntimeError("⚠️ KAGGLE_USERNAME
|
| 89 |
|
| 90 |
os.environ["KAGGLE_USERNAME"] = KAGGLE_USERNAME
|
| 91 |
os.environ["KAGGLE_KEY"] = KAGGLE_KEY
|
| 92 |
|
| 93 |
BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
|
| 94 |
-
IMAGE_API_URL = "http://211.233.58.201:7896" #
|
| 95 |
-
MAX_TOKENS = 7999 #
|
| 96 |
|
| 97 |
-
# ─────────────────────────────── Logging ───────────────────────────────
|
| 98 |
logging.basicConfig(
|
| 99 |
level=logging.INFO,
|
| 100 |
format="%(asctime)s - %(levelname)s - %(message)s"
|
| 101 |
)
|
| 102 |
|
| 103 |
|
| 104 |
-
# ───────────────────────────────
|
| 105 |
@st.cache_resource
|
| 106 |
def load_military_dataset():
|
| 107 |
"""
|
|
@@ -121,7 +120,7 @@ def load_military_dataset():
|
|
| 121 |
MIL_DF = load_military_dataset()
|
| 122 |
|
| 123 |
def is_military_query(text: str) -> bool:
|
| 124 |
-
"""
|
| 125 |
kw = [
|
| 126 |
"군사", "전술", "전투", "전쟁", "작전", "무기", "병력",
|
| 127 |
"military", "tactic", "warfare", "battle", "operation"
|
|
@@ -130,8 +129,8 @@ def is_military_query(text: str) -> bool:
|
|
| 130 |
|
| 131 |
def military_search(query: str, top_k: int = 3):
|
| 132 |
"""
|
| 133 |
-
|
| 134 |
-
|
| 135 |
"""
|
| 136 |
if MIL_DF is None:
|
| 137 |
return []
|
|
@@ -149,7 +148,8 @@ def military_search(query: str, top_k: int = 3):
|
|
| 149 |
logging.error(f"military_search error: {e}")
|
| 150 |
return []
|
| 151 |
|
| 152 |
-
|
|
|
|
| 153 |
KAGGLE_DATASETS = {
|
| 154 |
"general_business": {
|
| 155 |
"ref": "mohammadgharaei77/largest-2000-global-companies",
|
|
@@ -417,6 +417,7 @@ KAGGLE_DATASETS = {
|
|
| 417 |
}
|
| 418 |
}
|
| 419 |
|
|
|
|
| 420 |
SUN_TZU_STRATEGIES = [
|
| 421 |
{"계": "만천과해", "요약": "평범한 척, 몰래 진행", "조건": "상대가 지켜보고 있을 때", "행동": "루틴·평온함 과시", "목적": "경계 무력화", "예시": "규제기관 눈치 보는 신사업 파일럿"},
|
| 422 |
{"계": "위위구조", "요약": "뒤통수 치면 포위 풀린다", "조건": "우리 측이 압박받을 때", "행동": "적 본진 급습", "목적": "압박 해소", "예시": "경쟁사 핵심 고객 뺏기"},
|
|
@@ -458,7 +459,7 @@ SUN_TZU_STRATEGIES = [
|
|
| 458 |
|
| 459 |
physical_transformation_categories = {
|
| 460 |
"센서 기능": [
|
| 461 |
-
#
|
| 462 |
"시각 센서", "시각 감지", "청각 센서", "청각 감지", "촉각 센서", "촉각 감지",
|
| 463 |
"미각 센서", "미각 감지", "후각 센서", "후각 감지", "온도 센서", "온도 감지",
|
| 464 |
"습도 센서", "습도 감지", "압력 센서", "압력 감지", "가속도 센서", "가속도 감지",
|
|
@@ -476,7 +477,7 @@ physical_transformation_categories = {
|
|
| 476 |
"홀 효과 감지", "초음파 센서", "초음파 감지", "레이더 센서", "레이더 감지",
|
| 477 |
"라이다 센서", "라이다 감지", "터치 센서", "터치 감지", "제스처 센서", "제스처 감지",
|
| 478 |
"심박 센서", "심박 감지", "혈압 센서", "혈압 감지", "LAN", "WIFI", "블루투스", "생체 인증",
|
| 479 |
-
#
|
| 480 |
"다중 스펙트럼 센서", "다중 스펙트럼 감지", "깊이 인식 센서", "깊이 인식 감지",
|
| 481 |
"퀀텀 센서", "퀀텀 감지", "웨어러블 센서", "웨어러블 감지", "바이오마커 센서", "바이오마커 감지",
|
| 482 |
"임베디드 센서", "임베디드 감지", "IoT 센서 네트워크", "스트레인 센서", "스트레인 감지",
|
|
@@ -484,7 +485,7 @@ physical_transformation_categories = {
|
|
| 484 |
"스마트 먼지 센서", "환경 센서 그리드", "신경형태학적 센서", "두뇌-기계 인터페이스"
|
| 485 |
],
|
| 486 |
"크기와 형태 변화": [
|
| 487 |
-
#
|
| 488 |
"부피 늘어남", "부피 줄어듦", "길이 늘어남", "길이 줄어듦", "너비 늘어남", "너비 줄어남",
|
| 489 |
"높이 늘어남", "높이 줄어듦", "밀도 변화", "무게 증가", "무게 감소", "모양 변형",
|
| 490 |
"상태 변화", "불균등 변형", "복잡한 형태 변형", "비틀림", "꼬임", "불균일한 확장",
|
|
@@ -492,14 +493,14 @@ physical_transformation_categories = {
|
|
| 492 |
"물 저항", "먼지 저항", "찌그러짐", "복원", "접힘", "펼쳐짐", "압착", "팽창",
|
| 493 |
"늘어남", "수축", "구겨짐", "평평해짐", "뭉개짐", "단단해짐", "말림", "펴짐",
|
| 494 |
"꺾임", "구부러짐",
|
| 495 |
-
#
|
| 496 |
"4D 프린팅 변형", "형상 기억", "프랙탈 변화", "자가 조립", "자가 복구",
|
| 497 |
"기하학적 변환", "모듈화", "스마트 직물 변형", "매트릭스 구조 변형", "프로그래머블 변형",
|
| 498 |
"미시 스케일 변형", "거시 스케일 변형", "이방성 변형", "등방성 변형", "선택적 강성 변화",
|
| 499 |
"변형률 감응 구조", "형태학적 계산", "위상 변화", "경도 변화", "부드러움 변화"
|
| 500 |
],
|
| 501 |
"표면 및 외관 변화": [
|
| 502 |
-
#
|
| 503 |
"색상 변화", "질감 변화", "투명 변화", "불투명 변화", "반짝임 변화", "무광 변화",
|
| 504 |
"빛 반사 정도 변화", "무늬 변화", "각도에 따른 색상 변화", "빛에 따른 색상 변화",
|
| 505 |
"온도에 따른 색상 변화", "홀로그램 효과", "표면 각도별 빛 반사", "표면 모양 변형",
|
|
@@ -507,15 +508,15 @@ physical_transformation_categories = {
|
|
| 507 |
"선명함 변화", "광택 변화", "윤기 변화", "색조 변화", "채도 변화", "발광",
|
| 508 |
"형광", "빛 산란 효과", "빛 흡수 변화", "반투명 효과", "그림자 효과 변화",
|
| 509 |
"자외선 반응 변화", "야광 효과",
|
| 510 |
-
#
|
| 511 |
"생체모방 표면", "프로그래머블 질감", "촉각 피드백 표면", "열 반응성 표면",
|
| 512 |
"초소수성/초친수성 표면", "스마트 코팅", "마찰 계수 변화", "도금 효과", "위장 효과",
|
| 513 |
"양자점 효과", "메타표면 효과", "나노 구조화 표면", "전기변색 효과", "광변색 효과",
|
| 514 |
-
"압력변색 효과", "자기변색 효과", "항균 표면", "공기역학적 표면", "자기정렬 패턴",
|
| 515 |
"부착성 변화", "선택적 접착성"
|
| 516 |
],
|
| 517 |
"물질의 상태 변화": [
|
| 518 |
-
#
|
| 519 |
"고체 전환", "액체 전환", "기체 전환", "결정화", "용해", "산화", "부식",
|
| 520 |
"딱딱해짐", "부드러워짐", "특수 상태 전환", "무정형 전환", "결정형 전환", "성분 분리",
|
| 521 |
"미세 입자 형성", "미세 입자 분해", "젤 형성", "젤 풀어짐", "준안정 상태 변화",
|
|
@@ -523,7 +524,7 @@ physical_transformation_categories = {
|
|
| 523 |
"증발", "응축", "승화", "증착", "침전", "부유", "분산", "응집",
|
| 524 |
"건조", "습윤", "팽윤", "수축", "동결", "해동", "풍화", "침식",
|
| 525 |
"충전", "방전", "결합", "분리", "발효", "부패",
|
| 526 |
-
#
|
| 527 |
"초임계 상태 전환", "양자 상태 전환", "메타물질 상태 변화", "프로그래머블 물질 변화",
|
| 528 |
"소프트 로봇 물질 변화", "4D 프린팅 물질 변화", "바이오하이브리드 물질 변화",
|
| 529 |
"자가 조직화 물질", "자가 순환 물질", "자가 치유 물질", "생분해성 전환",
|
|
@@ -531,14 +532,14 @@ physical_transformation_categories = {
|
|
| 531 |
"스마트 유체 상태", "자극 반응성 상태", "형상기억 합금 상태", "초전도 상태"
|
| 532 |
],
|
| 533 |
"움직임 특성 변화": [
|
| 534 |
-
#
|
| 535 |
"가속", "감속", "일정 속도 유지", "진동", "진동 감소", "부딪힘", "튕김",
|
| 536 |
"회전 속도 증가", "회전 속도 감소", "회전 방향 변화", "불규칙 움직임", "멈췄다", "미끄러지는 현상",
|
| 537 |
"공진", "반공진", "유체 속 저항 변화", "유체 속 양력 변화", "움직임 저항 변화",
|
| 538 |
"복합 진동 움직임", "특수 유체 속 움직임", "회전-이동 연계 움직임", "관성 정지",
|
| 539 |
"충격 흡수", "충격 전달", "운동량 보존", "마찰력 변화", "관성 탈출", "불안정 균형",
|
| 540 |
"동적 안정성", "흔들림 감쇠", "경로 예측성", "회피 움직임",
|
| 541 |
-
#
|
| 542 |
"복합 운동학", "임의의 움직임", "재귀적 움직임", "흉내내는 움직임", "학습된 움직임",
|
| 543 |
"자율적 움직임", "군집 움직임", "비선형 움직임", "양자 움직임", "초음파 움직임",
|
| 544 |
"비대칭 움직임", "스토캐스틱 움직임", "카오스 움직임", "소프트 로보틱스 움직임",
|
|
@@ -546,7 +547,7 @@ physical_transformation_categories = {
|
|
| 546 |
"계층적 움직임 제어", "적응형 움직임 패턴"
|
| 547 |
],
|
| 548 |
"구조적 변화": [
|
| 549 |
-
#
|
| 550 |
"부품 추가", "부품 제거", "조립", "분해", "접기", "펴기", "변형", "원상복구",
|
| 551 |
"최적 구조 변화", "자가 재배열", "자연 패턴 형성", "자연 패턴 소멸", "규칙적 패턴 변화",
|
| 552 |
"모듈식 변형", "복잡성 증가 구조", "원래 모양 기억 효과", "시간에 따른 형태 변화",
|
|
@@ -554,20 +555,20 @@ physical_transformation_categories = {
|
|
| 554 |
"내부 구조 변화", "외부 구조 변화", "중심축 이동", "균형점 변화", "계층 구조 변화",
|
| 555 |
"지지 구조 변화", "응력 분산 구조", "충격 흡수 구조", "그리드 구조 변화", "매트릭스 구조 변화",
|
| 556 |
"상호 연결성 변화",
|
| 557 |
-
#
|
| 558 |
"텐세그리티 구조 변화", "바이오닉 구조", "메타물질 구조", "다중 안정 구조", "자가 진화 구조",
|
| 559 |
"자가 학습 구조", "생체모방 구조", "프랙탈 구조", "계층적 구조화", "에너지 흡수 구조",
|
| 560 |
"에너지 변환 구조", "적응형 구조", "위상 최적화", "다공성 구조", "기능적 경사 구조",
|
| 561 |
"다중 재료 구조", "초경량 구조", "초고강도 구조", "다기능성 구조", "내결함성 구조"
|
| 562 |
],
|
| 563 |
"공간 이동": [
|
| 564 |
-
#
|
| 565 |
"앞 이동", "뒤 이동", "좌 이동", "우 이동", "위 이동", "아래 이동",
|
| 566 |
"세로축 회전(고개 끄덕임)", "가로축 회전(고개 젓기)", "길이축 회전(옆으로 기울임)", "원 운동",
|
| 567 |
"나선형 이동", "관성에 의한 미끄러짐", "회전축 변화", "불규칙 회전", "흔들림 운동",
|
| 568 |
"포물선 이동", "무중력 부유", "수면 위 부유", "점프", "도약", "슬라이딩", "롤링",
|
| 569 |
"자유 낙하", "왕복 운동", "탄성 튕김", "관통", "회피 움직임", "지그재그 이동", "스윙 운동",
|
| 570 |
-
#
|
| 571 |
"양자 이동", "차원 간 이동", "가상 공간 이동", "증강 공간 이동", "무인 이동",
|
| 572 |
"군집 이동", "경로 최적화 이동", "상황 인식 이동", "생태계 통합 이동", "바이오닉 이동",
|
| 573 |
"미시 스케일 이동", "매크로 스케일 이동", "변형 기반 이동", "자기장 유도 이동",
|
|
@@ -575,14 +576,14 @@ physical_transformation_categories = {
|
|
| 575 |
"지능형 경로 탐색"
|
| 576 |
],
|
| 577 |
"시간 관련 변화": [
|
| 578 |
-
#
|
| 579 |
"노화", "풍화", "마모", "부식", "색 바램", "변색", "손상", "회복",
|
| 580 |
"수명 주기 변화", "사용자 상호작용에 따른 적응", "학습 기반 형태 최적화", "시간에 따른 물성 변화",
|
| 581 |
"집단 기억 효과", "문화적 의미 변화", "지연 반응", "이전 상태 의존 변화", "점진적 시간 변화",
|
| 582 |
"진화적 변화", "주기적 재생", "계절 변화 적응", "생체리듬 변화", "생애 주기 단계",
|
| 583 |
"성장", "퇴화", "자가 복구", "자가 재생", "자연 순환 적응", "지속성", "일시성",
|
| 584 |
"기억 효과", "지연된 작용", "누적 효과",
|
| 585 |
-
#
|
| 586 |
"시간 지연 효과", "예측 기반 변화", "학습 기반 변화", "인지적 시간 변화",
|
| 587 |
"시간 압축 경험", "시간 확장 경험", "시간적 패턴 감지", "시간적 패턴 생성",
|
| 588 |
"계절 인식 변화", "생체 시계 동기화", "시간 기반 프로그래밍", "연대기적 데이터 구조",
|
|
@@ -590,26 +591,26 @@ physical_transformation_categories = {
|
|
| 590 |
"사용 패턴 적응", "사용자 타임라인 통합", "예측 유지보수", "자가 최적화 타이밍"
|
| 591 |
],
|
| 592 |
"빛과 시각 효과": [
|
| 593 |
-
#
|
| 594 |
"발광", "소등", "빛 투과", "빛 차단", "빛 산란", "빛 집중", "색상 스펙트럼 변화",
|
| 595 |
"빛 회절", "빛 간섭", "홀로그램 생성", "레이저 효과", "빛 편광", "형광", "인광",
|
| 596 |
"자외선 발광", "적외선 발광", "광학적 착시", "빛 굴절", "그림자 생성", "그림자 제거",
|
| 597 |
"색수차 효과", "무지개 효과", "글로우 효과", "플래시 효과", "조명 패턴", "빔 효과",
|
| 598 |
"광 필터 효과", "빛의 방향성 변화", "투영 효과", "빛 감지", "빛 반응", "광도 변화",
|
| 599 |
-
#
|
| 600 |
"양자 발광", "메타 광학 효과", "프로그래머블 광학", "시야각 변화", "생체발광 모방",
|
| 601 |
"광역학 효과", "퍼셉션 변화 효과", "맥스웰리안 뷰", "광 컴퓨팅 효과", "광유전학 효과",
|
| 602 |
"생체 광학 모방", "비선형 광학 효과", "구조색 변화", "자가 발광", "광 결정 효과",
|
| 603 |
"양자점 방출", "나노 발광체", "증강 광학", "투명 디스플레이 효과", "광학 위장"
|
| 604 |
],
|
| 605 |
"소리와 진동 효과": [
|
| 606 |
-
#
|
| 607 |
"소리 발생", "소리 소멸", "음 높낮이 변화", "음량 변화", "음색 변화", "공명",
|
| 608 |
"반공명", "음향 진동", "초음파 발생", "저음파 발생", "소리 집중", "소리 분산",
|
| 609 |
"음향 반사", "음향 흡수", "음향 도플러 효과", "음파 간섭", "음향 공진", "진동 패턴 변화",
|
| 610 |
"타악 효과", "음향 피드백", "음향 차폐", "음향 증폭", "소리 지향성", "소리 왜곡",
|
| 611 |
"비트 생성", "배음 생성", "주파수 변조", "음향 충격파", "음향 필터링",
|
| 612 |
-
#
|
| 613 |
"메타 음향 효과", "방향성 음향", "3D 음향 효과", "생체음향 모방", "상황별 음향 변화",
|
| 614 |
"음향 위장", "음향 투명화", "음향 렌즈", "양자 음향 효과", "초저주파 효과",
|
| 615 |
"초고주파 효과", "음파 에너지 수확", "자가 조절 공명", "음향 홀로그래피",
|
|
@@ -617,7 +618,7 @@ physical_transformation_categories = {
|
|
| 617 |
"기능적 음향 표면", "구조 공진 조절"
|
| 618 |
],
|
| 619 |
"열 관련 변화": [
|
| 620 |
-
#
|
| 621 |
"온도 상승", "온도 하강", "열 팽창", "열 수축", "열 전달", "열 차단", "압력 상승",
|
| 622 |
"압력 하강", "열 변화에 따른 자화", "엔트로피 변화", "열전기 효과", "자기장에 의한 열 변화",
|
| 623 |
"상태 변화 중 열 저장", "상태 변화 중 열 방출", "열 스트레스 발생", "열 스트레스 해소",
|
|
@@ -625,14 +626,14 @@ physical_transformation_categories = {
|
|
| 625 |
"열 반사", "열 흡수", "냉각 응축", "열 활성화", "열 변색", "열 팽창 계수 변화",
|
| 626 |
"열 안정성 변화", "내열성", "내한성", "자가 발열", "열적 평형", "열적 불균형",
|
| 627 |
"열적 변형", "열 분산", "열 집중",
|
| 628 |
-
#
|
| 629 |
"열전 효과", "열광 효과", "프로그래머블 열 특성", "상변화 냉각", "상변화 가열",
|
| 630 |
"열 유도 기억", "열 광학 효과", "열 음향 효과", "열 기계 효과", "열 화학 반응",
|
| 631 |
"양자 열역학 효과", "근적외선 열 효과", "열 조절 표면", "열흐름 제어", "열 방출 최적화",
|
| 632 |
"열 캡처 최적화", "자가 조절 온도", "열 스위칭", "열 포커싱", "상변화 재료 활용"
|
| 633 |
],
|
| 634 |
"전기 및 자기 변화": [
|
| 635 |
-
#
|
| 636 |
"자성 생성", "자성 소멸", "전하량 증가", "전하량 감소", "전기장 생성", "전기장 소멸",
|
| 637 |
"자기장 생성", "자기장 소멸", "초전도 상태 전환", "강유전체 특성 변화", "양자 상태 변화",
|
| 638 |
"플라즈마 형성", "플라즈마 소멸", "스핀파 전달", "빛에 의한 전기 발생", "압력에 의한 전기 발생",
|
|
@@ -640,7 +641,7 @@ physical_transformation_categories = {
|
|
| 640 |
"전자기 유도", "전자기파 방출", "전자기파 흡수", "전기 용량 변화", "자기 이력 현상",
|
| 641 |
"전기적 분극", "전자 흐름 방향 변화", "전기적 공명", "전기적 차폐", "전기적 노출",
|
| 642 |
"자기 차폐", "자기 노출", "자기장 정렬", "유선(Wire)", "무선(Wireless)",
|
| 643 |
-
#
|
| 644 |
"양자 자성", "스핀트로닉스 효과", "마그네토일렉트릭 효과", "토폴로지컬 절연체 특성",
|
| 645 |
"초전도 양자 효과", "쿨롱 차단 효과", "조셉슨 효과", "홀 효과 변화", "전자기 투명성",
|
| 646 |
"자기 카이랄리티", "전자기 메타표면", "무선 전력 전송", "자기유변학적 효과",
|
|
@@ -648,14 +649,14 @@ physical_transformation_categories = {
|
|
| 648 |
"전자 스핀 제어", "고속 스위칭 자성"
|
| 649 |
],
|
| 650 |
"화학적 변화": [
|
| 651 |
-
#
|
| 652 |
"표면 코팅 변화", "물질 성분 변화", "화학 반응 변화", "촉매 작용 시작/중단",
|
| 653 |
"빛에 의한 화학 반응", "전기에 의한 화학 반응", "단분자막 형성", "분자 수준 구조 변화",
|
| 654 |
"생체 모방 표면 변화", "환경 반응형 물질 변화", "주기적 화학 반응", "산화", "환원",
|
| 655 |
"고분자화", "물 분해", "화합", "방사선 영향", "산-염기 반응", "중화 반응",
|
| 656 |
"이온화", "화학적 흡착/탈착", "촉매 효율 변화", "효소 활성 변화", "발색 반응",
|
| 657 |
"pH 변화", "화학적 평형 이동", "결합 형성/분해", "용해도 변화",
|
| 658 |
-
#
|
| 659 |
"프로그래머블 화학 반응", "자가 촉매 반응", "클릭 케미스트리", "광화학 반응",
|
| 660 |
"전기화학 반응", "초분자 화학 반응", "동적 공유 결합", "바이오오쏘고널 화학",
|
| 661 |
"화학적 컴퓨팅", "화학적 감지", "화학적 통신", "화학적 기억", "선택적 촉매",
|
|
@@ -663,13 +664,13 @@ physical_transformation_categories = {
|
|
| 663 |
"화학적 패턴 형성", "화학적 습도 조절", "화학적 정화"
|
| 664 |
],
|
| 665 |
"생물학적 변화": [
|
| 666 |
-
#
|
| 667 |
"성장/위축", "세포 분열/사멸", "생물 발광", "신진대사 변화", "면역 반응",
|
| 668 |
"호르몬 분비", "신경 반응", "유전적 발현", "적응/진화", "생체리듬 변화",
|
| 669 |
"재생/치유", "노화/성숙", "생체 모방 변화", "바이오필름 형성", "생물학적 분해",
|
| 670 |
"효소 활성화/비활성화", "생물학적 신호 전달", "스트레스 반응", "체온 조절", "생물학적 시계 변화",
|
| 671 |
"세포외 기질 변화", "생체 역학적 반응", "세포 운동성", "세포 극성 변화", "영양 상태 변화",
|
| 672 |
-
#
|
| 673 |
"합성 생물학 반응", "생물학적 컴퓨팅", "오가노이드 발달", "인공 조직 발달",
|
| 674 |
"생체적합성 변화", "면역학적 응답 제어", "후성유전학적 변화", "생물학적 리듬 조절",
|
| 675 |
"신경가소성 효과", "세포외 기질 리모델링", "체세포 리프로그래밍", "생체활성 표면 상호작용",
|
|
@@ -677,13 +678,13 @@ physical_transformation_categories = {
|
|
| 677 |
"바이오하이브리드 시스템", "세포 분화 조절", "생���신호 증폭", "생화학적 기억 형성"
|
| 678 |
],
|
| 679 |
"환경 상호작용": [
|
| 680 |
-
#
|
| 681 |
"온도 반응", "습도 반응", "기압 반응", "중력 반응", "자기장 반응",
|
| 682 |
"빛 반응", "소리 반응", "화학 물질 감지", "기계적 자극 감지", "전기 자극 반응",
|
| 683 |
"방사선 반응", "진동 감지", "pH 반응", "용매 반응", "기체 교환",
|
| 684 |
"환경 오염 반응", "날씨 반응", "계절 반응", "일주기 반응", "생태계 상호작용",
|
| 685 |
"공생/경쟁 반응", "포식/피식 관계", "군집 형성", "영역 설정", "이주 패턴", "정착 패턴",
|
| 686 |
-
#
|
| 687 |
"탄소 포집 및 변환", "생태계 복원 효과", "생물다양성 증진", "순환 경제 상호작용",
|
| 688 |
"도시 환경 통합", "스마트 환경 감지", "재생 가능 에너지 연계", "물 순환 상호작용",
|
| 689 |
"대기 질 상호작용", "자연 기반 솔루션 통합", "재해 복원력 증진", "기후 변화 적응",
|
|
@@ -691,7 +692,7 @@ physical_transformation_categories = {
|
|
| 691 |
"환경적 자가 수정", "지속가능한 자원 관리", "생태계 건강 모니터링", "생태 교란 방지"
|
| 692 |
],
|
| 693 |
"비즈니스 아이디어": [
|
| 694 |
-
#
|
| 695 |
"시장 재정의/신규 시장 개척",
|
| 696 |
"비즈니스 모델 혁신/디지털 전환",
|
| 697 |
"고객 경험 혁신/서비스 혁신",
|
|
@@ -702,7 +703,7 @@ physical_transformation_categories = {
|
|
| 702 |
"지속 가능한 성장/사회적 가치 창출",
|
| 703 |
"데이터 기반 의사결정/AI 도입",
|
| 704 |
"신기술 융합/혁신 투자",
|
| 705 |
-
#
|
| 706 |
"탄소중립 비즈니스 모델",
|
| 707 |
"순환경제 비즈니스 모델",
|
| 708 |
"구독 경제 모델",
|
|
@@ -720,7 +721,7 @@ physical_transformation_categories = {
|
|
| 720 |
"웰빙/웰니스 중심 비즈니스"
|
| 721 |
],
|
| 722 |
|
| 723 |
-
#
|
| 724 |
|
| 725 |
"사용자 인터페이스 및 상호작용": [
|
| 726 |
"제스처 인식", "제스처 제어", "음성 인식", "음성 제어", "시선 추적", "시선 제어",
|
|
@@ -797,91 +798,92 @@ physical_transformation_categories = {
|
|
| 797 |
]
|
| 798 |
}
|
| 799 |
|
| 800 |
-
# ──────────────────────────────
|
| 801 |
SWOT_FRAMEWORK = {
|
| 802 |
"strengths": {
|
| 803 |
-
"title": "
|
| 804 |
-
"description": "
|
| 805 |
"prompt_keywords": ["강점", "장점", "우위", "역량", "자산", "전문성", "strength", "advantage"]
|
| 806 |
},
|
| 807 |
"weaknesses": {
|
| 808 |
-
"title": "
|
| 809 |
-
"description": "
|
| 810 |
"prompt_keywords": ["약점", "단점", "부족", "한계", "취약점", "weakness", "limitation", "deficit"]
|
| 811 |
},
|
| 812 |
"opportunities": {
|
| 813 |
-
"title": "
|
| 814 |
-
"description": "
|
| 815 |
"prompt_keywords": ["기회", "가능성", "트렌드", "변화", "성장", "opportunity", "trend", "potential"]
|
| 816 |
},
|
| 817 |
"threats": {
|
| 818 |
-
"title": "
|
| 819 |
-
"description": "
|
| 820 |
"prompt_keywords": ["위협", "리스크", "경쟁", "위험", "장벽", "threat", "risk", "competition", "barrier"]
|
| 821 |
}
|
| 822 |
}
|
| 823 |
|
| 824 |
PORTER_FRAMEWORK = {
|
| 825 |
"rivalry": {
|
| 826 |
-
"title": "
|
| 827 |
-
"description": "
|
| 828 |
"prompt_keywords": ["경쟁", "경쟁사", "시장점유율", "가격경쟁", "competition", "rival", "market share"]
|
| 829 |
},
|
| 830 |
"new_entrants": {
|
| 831 |
-
"title": "
|
| 832 |
-
"description": "
|
| 833 |
"prompt_keywords": ["진입장벽", "신규", "스타트업", "entry barrier", "newcomer", "startup"]
|
| 834 |
},
|
| 835 |
"substitutes": {
|
| 836 |
-
"title": "
|
| 837 |
-
"description": "
|
| 838 |
"prompt_keywords": ["대체재", "대안", "substitute", "alternative", "replacement"]
|
| 839 |
},
|
| 840 |
"buyer_power": {
|
| 841 |
-
"title": "
|
| 842 |
-
"description": "
|
| 843 |
"prompt_keywords": ["고객", "구매자", "가격민감도", "협상력", "customer", "buyer power"]
|
| 844 |
},
|
| 845 |
"supplier_power": {
|
| 846 |
-
"title": "
|
| 847 |
-
"description": "
|
| 848 |
"prompt_keywords": ["공급자", "벤더", "원재료", "supplier", "vendor", "raw material"]
|
| 849 |
}
|
| 850 |
}
|
| 851 |
|
| 852 |
BCG_FRAMEWORK = {
|
| 853 |
"stars": {
|
| 854 |
-
"title": "
|
| 855 |
-
"description": "
|
| 856 |
"prompt_keywords": ["성장", "점유율", "중점", "투자", "star", "growth", "investment"]
|
| 857 |
},
|
| 858 |
"cash_cows": {
|
| 859 |
-
"title": "
|
| 860 |
-
"description": "
|
| 861 |
"prompt_keywords": ["안정", "수익", "현금", "전통", "cash cow", "profit", "mature"]
|
| 862 |
},
|
| 863 |
"question_marks": {
|
| 864 |
-
"title": "
|
| 865 |
-
"description": "
|
| 866 |
"prompt_keywords": ["가능성", "위험", "불확실", "잠재", "question mark", "uncertain", "potential"]
|
| 867 |
},
|
| 868 |
"dogs": {
|
| 869 |
-
"title": "
|
| 870 |
-
"description": "
|
| 871 |
"prompt_keywords": ["회수", "철수", "저성장", "비효율", "dog", "divest", "low growth"]
|
| 872 |
}
|
| 873 |
}
|
| 874 |
|
| 875 |
BUSINESS_FRAMEWORKS = {
|
| 876 |
-
"sunzi": "
|
| 877 |
-
"swot": "SWOT
|
| 878 |
-
"porter": "Porter
|
| 879 |
-
"bcg": "BCG
|
| 880 |
}
|
| 881 |
|
|
|
|
| 882 |
@dataclass
|
| 883 |
class Category:
|
| 884 |
-
"""
|
| 885 |
name_ko: str
|
| 886 |
name_en: str
|
| 887 |
tags: list[str]
|
|
@@ -951,26 +953,26 @@ def format_business_framework_analysis(framework_type: str, analysis_result: dic
|
|
| 951 |
if not analysis_result:
|
| 952 |
return ""
|
| 953 |
titles = {
|
| 954 |
-
'swot': '# SWOT
|
| 955 |
-
'porter': '# Porter
|
| 956 |
-
'bcg': '# BCG
|
| 957 |
}
|
| 958 |
-
md = f"{titles.get(framework_type, '#
|
| 959 |
-
md += "
|
| 960 |
for category, info in analysis_result.items():
|
| 961 |
md += f"## {info['title']}\n\n"
|
| 962 |
md += f"{info['description']}\n\n"
|
| 963 |
-
md += f"**
|
| 964 |
if info['keywords']:
|
| 965 |
-
md += "**
|
| 966 |
for keyword in info['keywords']:
|
| 967 |
md += f"- *{keyword}*\n"
|
| 968 |
md += "\n"
|
| 969 |
else:
|
| 970 |
-
md += "
|
| 971 |
return md
|
| 972 |
|
| 973 |
-
# ─────────────────────────────
|
| 974 |
def md_to_html(md_text: str, title: str = "Output") -> str:
|
| 975 |
html_content = markdown.markdown(
|
| 976 |
md_text,
|
|
@@ -1064,36 +1066,36 @@ def md_to_html(md_text: str, title: str = "Output") -> str:
|
|
| 1064 |
</html>
|
| 1065 |
"""
|
| 1066 |
|
| 1067 |
-
# ─────────────────────────────
|
| 1068 |
def process_text_file(uploaded_file):
|
| 1069 |
try:
|
| 1070 |
content = uploaded_file.read().decode('utf-8')
|
| 1071 |
-
return f"""#
|
| 1072 |
|
| 1073 |
{content}
|
| 1074 |
"""
|
| 1075 |
except Exception as e:
|
| 1076 |
-
logging.error(f"
|
| 1077 |
return f"**Error processing {uploaded_file.name}**: {str(e)}"
|
| 1078 |
|
| 1079 |
def process_csv_file(uploaded_file):
|
| 1080 |
try:
|
| 1081 |
df = pd.read_csv(uploaded_file)
|
| 1082 |
-
return f"""#
|
| 1083 |
|
| 1084 |
-
##
|
| 1085 |
-
-
|
| 1086 |
-
-
|
| 1087 |
-
-
|
| 1088 |
|
| 1089 |
-
##
|
| 1090 |
{df.head(5).to_markdown(index=False)}
|
| 1091 |
|
| 1092 |
-
##
|
| 1093 |
{df.describe().to_markdown()}
|
| 1094 |
"""
|
| 1095 |
except Exception as e:
|
| 1096 |
-
logging.error(f"CSV
|
| 1097 |
return f"**Error processing {uploaded_file.name}**: {str(e)}"
|
| 1098 |
|
| 1099 |
def process_pdf_file(uploaded_file):
|
|
@@ -1108,16 +1110,16 @@ def process_pdf_file(uploaded_file):
|
|
| 1108 |
pages_preview.append(f"--- Page {page_num+1} ---\n{page.extract_text()}")
|
| 1109 |
|
| 1110 |
preview_text = "\n\n".join(pages_preview)
|
| 1111 |
-
return f"""#
|
| 1112 |
|
| 1113 |
-
##
|
| 1114 |
-
-
|
| 1115 |
|
| 1116 |
-
##
|
| 1117 |
{preview_text}
|
| 1118 |
"""
|
| 1119 |
except Exception as e:
|
| 1120 |
-
logging.error(f"PDF
|
| 1121 |
return f"**Error processing {uploaded_file.name}**: {str(e)}"
|
| 1122 |
|
| 1123 |
def process_uploaded_files(uploaded_files):
|
|
@@ -1142,12 +1144,13 @@ def process_uploaded_files(uploaded_files):
|
|
| 1142 |
f"# Unsupported file: {file.name}\n\nThis file type is not supported for processing."
|
| 1143 |
)
|
| 1144 |
except Exception as e:
|
| 1145 |
-
logging.error(f"
|
| 1146 |
file_contents.append(f"# Error processing file: {file.name}\n\n{str(e)}")
|
| 1147 |
|
| 1148 |
-
return "\n\n#
|
|
|
|
| 1149 |
|
| 1150 |
-
# ─────────────────────────────
|
| 1151 |
def generate_image(prompt: str):
|
| 1152 |
if not prompt:
|
| 1153 |
return None, None
|
|
@@ -1180,11 +1183,11 @@ def generate_image(prompt: str):
|
|
| 1180 |
logging.error(f"Image generation error: {str(e)}", exc_info=True)
|
| 1181 |
return None, None
|
| 1182 |
|
| 1183 |
-
# ─────────────────────────────
|
| 1184 |
@st.cache_resource
|
| 1185 |
def check_kaggle_availability():
|
| 1186 |
if not KAGGLE_API_KEY:
|
| 1187 |
-
logging.warning("Kaggle API
|
| 1188 |
return False
|
| 1189 |
return True
|
| 1190 |
|
|
@@ -1223,7 +1226,7 @@ def search_kaggle_datasets(query: str, top: int = 5) -> list[dict]:
|
|
| 1223 |
@st.cache_data
|
| 1224 |
def download_and_analyze_dataset(dataset_ref: str, max_rows: int = 1000):
|
| 1225 |
if not (os.getenv("KAGGLE_USERNAME") and os.getenv("KAGGLE_KEY")):
|
| 1226 |
-
return "Kaggle API
|
| 1227 |
api = KaggleApi()
|
| 1228 |
api.authenticate()
|
| 1229 |
tmpdir = tempfile.mkdtemp()
|
|
@@ -1232,12 +1235,12 @@ def download_and_analyze_dataset(dataset_ref: str, max_rows: int = 1000):
|
|
| 1232 |
except Exception as e:
|
| 1233 |
logging.error(f"Dataset download failed ({dataset_ref}): {e}")
|
| 1234 |
shutil.rmtree(tmpdir)
|
| 1235 |
-
return f"
|
| 1236 |
|
| 1237 |
csv_files = glob.glob(f"{tmpdir}/**/*.csv", recursive=True)
|
| 1238 |
if not csv_files:
|
| 1239 |
shutil.rmtree(tmpdir)
|
| 1240 |
-
return "CSV
|
| 1241 |
|
| 1242 |
try:
|
| 1243 |
df = pd.read_csv(csv_files[0], nrows=max_rows)
|
|
@@ -1249,30 +1252,30 @@ def download_and_analyze_dataset(dataset_ref: str, max_rows: int = 1000):
|
|
| 1249 |
"missing_values": df.isnull().sum().to_dict()
|
| 1250 |
}
|
| 1251 |
except Exception as e:
|
| 1252 |
-
analysis = f"CSV
|
| 1253 |
|
| 1254 |
shutil.rmtree(tmpdir)
|
| 1255 |
return analysis
|
| 1256 |
|
| 1257 |
def format_kaggle_analysis_markdown_multi(analyses: list[dict]) -> str:
|
| 1258 |
"""
|
| 1259 |
-
|
| 1260 |
analyses = [ {"meta": {...}, "analysis": {... or str}}, ... ]
|
| 1261 |
"""
|
| 1262 |
if not analyses:
|
| 1263 |
-
return "# Kaggle
|
| 1264 |
-
md = "# Kaggle
|
| 1265 |
-
md += "
|
| 1266 |
for i, item in enumerate(analyses, 1):
|
| 1267 |
ds = item["meta"]
|
| 1268 |
ana = item["analysis"]
|
| 1269 |
md += f"## {i}. {ds['title']}\n\n"
|
| 1270 |
md += f"{ds['subtitle']}\n\n"
|
| 1271 |
-
md += f"- **
|
| 1272 |
-
md += f"- **URL**
|
| 1273 |
if isinstance(ana, dict):
|
| 1274 |
-
md += f"**
|
| 1275 |
-
md += "<details><summary>
|
| 1276 |
try:
|
| 1277 |
md += pd.DataFrame(ana["head"]).to_markdown(index=False) + "\n\n"
|
| 1278 |
except:
|
|
@@ -1287,23 +1290,21 @@ def format_kaggle_analysis_markdown_multi(analyses: list[dict]) -> str:
|
|
| 1287 |
md += "---\n\n"
|
| 1288 |
return md
|
| 1289 |
|
| 1290 |
-
# ─────────────────────────────
|
| 1291 |
@st.cache_resource
|
| 1292 |
def get_openai_client():
|
| 1293 |
if not OPENAI_API_KEY:
|
| 1294 |
-
raise RuntimeError("⚠️ OPENAI_API_KEY
|
| 1295 |
return OpenAI(
|
| 1296 |
api_key=OPENAI_API_KEY,
|
| 1297 |
timeout=60.0,
|
| 1298 |
max_retries=3
|
| 1299 |
)
|
| 1300 |
|
| 1301 |
-
# ─────────────────────
|
| 1302 |
def identify_decision_purpose(prompt: str) -> dict:
|
| 1303 |
"""
|
| 1304 |
-
|
| 1305 |
-
(기존의 의사결정 목적/제약 식별 로직을 재활용하되,
|
| 1306 |
-
design/invention 관련 키워드도 추가로 고려할 수 있음.)
|
| 1307 |
"""
|
| 1308 |
purpose_patterns = {
|
| 1309 |
'cost_reduction': [r'비용(\s*절감)?', r'예산', r'효율', r'저렴', r'경제', r'cost', r'saving', r'budget'],
|
|
@@ -1337,7 +1338,7 @@ def identify_decision_purpose(prompt: str) -> dict:
|
|
| 1337 |
'all_constraint_scores': constraint_scores
|
| 1338 |
}
|
| 1339 |
|
| 1340 |
-
# ─────────────────────────────
|
| 1341 |
def keywords(text: str, top: int = 8) -> str:
|
| 1342 |
words = re.findall(r'\b[a-zA-Z가-힣]{2,}\b', text.lower())
|
| 1343 |
stopwords = {
|
|
@@ -1356,7 +1357,7 @@ def keywords(text: str, top: int = 8) -> str:
|
|
| 1356 |
|
| 1357 |
def compute_relevance_scores(prompt: str, categories: list[Category]) -> dict:
|
| 1358 |
"""
|
| 1359 |
-
|
| 1360 |
"""
|
| 1361 |
prompt_lower = prompt.lower()
|
| 1362 |
prompt_tokens = set(re.findall(r'\b[a-zA-Z가-힣]{2,}\b', prompt_lower))
|
|
@@ -1380,51 +1381,41 @@ def compute_relevance_scores(prompt: str, categories: list[Category]) -> dict:
|
|
| 1380 |
if category.name_ko in prompt or category.name_en.lower() in prompt_lower:
|
| 1381 |
cat_score += 1
|
| 1382 |
|
| 1383 |
-
#
|
| 1384 |
if main_purpose:
|
| 1385 |
purpose_category_weights = {
|
| 1386 |
'cost_reduction': {
|
| 1387 |
-
# 기존 항목
|
| 1388 |
'구조적 변화': 1.5, '화학적 변화': 1.3, '비즈니스 아이디어': 1.5,
|
| 1389 |
'Structural Change': 1.5, 'Chemical Change': 1.3, 'Business Ideas': 1.5,
|
| 1390 |
-
# 추가 항목
|
| 1391 |
'에너지 변환 및 관리': 1.6, '데이터 및 정보 변환': 1.4, '지속가능성 및 환경 영향': 1.3,
|
| 1392 |
-
'Energy Conversion and Management': 1.6, 'Data and Information Transformation': 1.4,
|
| 1393 |
'Sustainability and Environmental Impact': 1.3
|
| 1394 |
},
|
| 1395 |
'innovation': {
|
| 1396 |
-
# 기존 항목
|
| 1397 |
'센서 기능': 1.5, '표면 및 외관 변화': 1.3, '비즈니스 아이디어': 1.5,
|
| 1398 |
'Sensor Functions': 1.5, 'Surface and Appearance Change': 1.3, 'Business Ideas': 1.5,
|
| 1399 |
-
# 추가 항목
|
| 1400 |
'사용자 인터페이스 및 상호작용': 1.6, '데이터 및 정보 변환': 1.4, '인지 및 심리적 변화': 1.3,
|
| 1401 |
'User Interface and Interaction': 1.6, 'Data and Information Transformation': 1.4,
|
| 1402 |
'Cognitive and Psychological Changes': 1.3
|
| 1403 |
},
|
| 1404 |
'risk_management': {
|
| 1405 |
-
# 기존 항목
|
| 1406 |
'환경 상호작용': 1.5, '시간 관련 변화': 1.3, '비즈니스 아이디어': 1.4,
|
| 1407 |
'Environmental Interaction': 1.5, 'Time-Related Change': 1.3, 'Business Ideas': 1.4,
|
| 1408 |
-
# 추가 항목
|
| 1409 |
'보안 및 프라이버시': 1.7, '지속가능성 및 환경 영향': 1.5, '데이터 및 정보 변환': 1.4,
|
| 1410 |
'Security and Privacy': 1.7, 'Sustainability and Environmental Impact': 1.5,
|
| 1411 |
'Data and Information Transformation': 1.4
|
| 1412 |
},
|
| 1413 |
'growth': {
|
| 1414 |
-
# 기존 항목
|
| 1415 |
'크기와 형태 변화': 1.4, '비즈니스 아이디어': 1.6, '구조적 변화': 1.3,
|
| 1416 |
'Size and Shape Change': 1.4, 'Business Ideas': 1.6, 'Structural Change': 1.3,
|
| 1417 |
-
# 추가 항목
|
| 1418 |
'사회적 상호작용 및 협업': 1.5, '데이터 및 정보 변환': 1.4, '사용자 인터페이스 및 상호작용': 1.3,
|
| 1419 |
'Social Interaction and Collaboration': 1.5, 'Data and Information Transformation': 1.4,
|
| 1420 |
'User Interface and Interaction': 1.3
|
| 1421 |
},
|
| 1422 |
'customer': {
|
| 1423 |
-
# 기존 항목
|
| 1424 |
'표면 및 외관 변화': 1.5, '센서 기능': 1.4, '빛과 시각 효과': 1.3, '비즈니스 아이디어': 1.4,
|
| 1425 |
'Surface and Appearance Change': 1.5, 'Sensor Functions': 1.4,
|
| 1426 |
'Light and Visual Effects': 1.3, 'Business Ideas': 1.4,
|
| 1427 |
-
# 추가 항목
|
| 1428 |
'사용자 인터페이스 및 상호작용': 1.7, '미학 및 감성 경험': 1.6, '인지 및 심리적 변화': 1.5,
|
| 1429 |
'사회적 상호작용 및 협업': 1.4,
|
| 1430 |
'User Interface and Interaction': 1.7, 'Aesthetics and Emotional Experience': 1.6,
|
|
@@ -1436,7 +1427,7 @@ def compute_relevance_scores(prompt: str, categories: list[Category]) -> dict:
|
|
| 1436 |
elif category.name_en in purpose_category_weights.get(main_purpose, {}):
|
| 1437 |
cat_score *= purpose_category_weights[main_purpose][category.name_en]
|
| 1438 |
|
| 1439 |
-
#
|
| 1440 |
for item in category.items:
|
| 1441 |
item_score = cat_score
|
| 1442 |
item_tokens = set(re.findall(r'\b[a-zA-Z가-힣]{2,}\b', item.lower()))
|
|
@@ -1458,8 +1449,7 @@ def generate_comparison_matrix(
|
|
| 1458 |
relevance_threshold: float = 0.2
|
| 1459 |
) -> list[tuple]:
|
| 1460 |
"""
|
| 1461 |
-
|
| 1462 |
-
(본래 의사결정 매트릭스였으나, 디자인/발명에 맞게 재활용)
|
| 1463 |
"""
|
| 1464 |
if relevance_scores is None:
|
| 1465 |
pool = [(c.name_ko, item) for c in categories for item in c.items]
|
|
@@ -1496,10 +1486,10 @@ def generate_comparison_matrix(
|
|
| 1496 |
evaluated_combinations.sort(key=lambda x: x[3], reverse=True)
|
| 1497 |
return evaluated_combinations[:max_combinations]
|
| 1498 |
|
| 1499 |
-
# ──────────────────────────────
|
| 1500 |
def smart_weight(cat_name, item, relevance, global_cnt, T):
|
| 1501 |
rare_boost = 1 / (global_cnt.get(item, 0) + 0.5)
|
| 1502 |
-
noise = random.random() ** (1 / T) # T
|
| 1503 |
relevance_weight = 1 - (T - 0.1) / 3.0
|
| 1504 |
return ((relevance * relevance_weight) + 0.1) * rare_boost * noise
|
| 1505 |
|
|
@@ -1514,8 +1504,8 @@ def generate_random_comparison_matrix(
|
|
| 1514 |
T: float = 1.3,
|
| 1515 |
):
|
| 1516 |
"""
|
| 1517 |
-
|
| 1518 |
-
|
| 1519 |
"""
|
| 1520 |
if seed is None:
|
| 1521 |
seed = random.randrange(2 ** 32)
|
|
@@ -1557,9 +1547,8 @@ def generate_random_comparison_matrix(
|
|
| 1557 |
combos.sort(key=lambda x: x[3], reverse=True)
|
| 1558 |
return combos[:max_combos]
|
| 1559 |
|
| 1560 |
-
# ─────────────────────────────
|
| 1561 |
PHYS_CATEGORIES: list[Category] = [
|
| 1562 |
-
# 기존 카테고리 유지
|
| 1563 |
Category(
|
| 1564 |
name_ko="센서 기능",
|
| 1565 |
name_en="Sensor Functions",
|
|
@@ -1656,8 +1645,6 @@ PHYS_CATEGORIES: list[Category] = [
|
|
| 1656 |
tags=["business", "idea", "비즈니스"],
|
| 1657 |
items=physical_transformation_categories["비즈니스 아이디어"]
|
| 1658 |
),
|
| 1659 |
-
|
| 1660 |
-
# 새로 추가된 카테고리
|
| 1661 |
Category(
|
| 1662 |
name_ko="사용자 인터페이스 및 상호작용",
|
| 1663 |
name_en="User Interface and Interaction",
|
|
@@ -1707,16 +1694,114 @@ PHYS_CATEGORIES: list[Category] = [
|
|
| 1707 |
items=physical_transformation_categories["미학 및 감성 경험"]
|
| 1708 |
)
|
| 1709 |
]
|
| 1710 |
-
# ──────────────────────────────── (중간 부분 생략 없이) ──────────────────────────
|
| 1711 |
|
| 1712 |
-
|
| 1713 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1714 |
"""
|
| 1715 |
-
|
| 1716 |
-
- 사용자 요청: "가장 우수한 10가지 아이디어"를 상세 설명
|
| 1717 |
-
- 결과 출력에 '가장 우수한 10가지 아이디어'에 포함되지 않은 '부가 아이디어' 30가지 리스트(한줄씩)도 설명
|
| 1718 |
-
- 결과 출력 시, 이미지 생성 자동화
|
| 1719 |
-
- Kaggle + 웹 검색 출처 제시
|
| 1720 |
"""
|
| 1721 |
cat_clause = (
|
| 1722 |
f'\n**추가 지침**: 선택된 카테고리 "{selected_category}"를 특별히 우선하여 고려하세요.\n'
|
|
@@ -1733,7 +1818,7 @@ def get_idea_system_prompt(selected_category: str | None = None,
|
|
| 1733 |
framework_instruction += "- Porter의 5 Forces\n"
|
| 1734 |
elif fw == "bcg":
|
| 1735 |
framework_instruction += "- BCG 매트릭스\n"
|
| 1736 |
-
|
| 1737 |
base_prompt = f"""
|
| 1738 |
당신은 창의적 디자인/발명 전문가 AI입니다.
|
| 1739 |
사용자가 입력한 주제를 분석하여,
|
|
@@ -1741,15 +1826,13 @@ def get_idea_system_prompt(selected_category: str | None = None,
|
|
| 1741 |
각 아이디어는 다음 요구를 충족해야 합니다:
|
| 1742 |
1) **아주 상세하게** 설명하여, 독자가 머릿속에 이미지를 그릴 수 있을 정도로 구체적으로 서술
|
| 1743 |
2) **이미지 프롬프트**도 함께 제시하여, 자동 이미지 생성이 되도록 하라
|
| 1744 |
-
- 예: `### 이미지 프롬프트\\n한 줄 영문 문구`
|
| 1745 |
3) **Kaggle 데이터셋**, **웹 검색**을 활용한 통찰(또는 참조)이 있으면 반드시 결과에 언급
|
| 1746 |
4) 최종 출력의 마지막에 **"출처"** 섹션을 만들고,
|
| 1747 |
- 웹 검색(Brave)에서 참조한 URL 3~5개
|
| 1748 |
- Kaggle 데이터셋 이름/URL(있다면)
|
| 1749 |
- 그 밖의 참고 자료
|
| 1750 |
-
5) **
|
| 1751 |
-
|
| 1752 |
-
|
| 1753 |
{framework_instruction}
|
| 1754 |
|
| 1755 |
## 아이디어 평가 기준
|
|
@@ -1802,12 +1885,10 @@ def get_idea_system_prompt(selected_category: str | None = None,
|
|
| 1802 |
* 약점(Weaknesses): 잠재적 약점 3가지 이상 및 이를 극복하기 위한 구체적인 방안
|
| 1803 |
* 기회(Opportunities): 외부 환경(기술, 시장, 정책 등)에서 발생하는 기회 요소 4가지 이상
|
| 1804 |
* 위협(Threats): 성공을 방해할 수 있는 외부 요인 3가지 이상과 각각에 대한 구체적 대응책
|
| 1805 |
-
|
| 1806 |
-
- 각 아이디어는 이 구조로 10개 아이디어 모두 동일하게 작성하라:
|
| 1807 |
-
|
| 1808 |
4. **부가적 통찰** (선택된 프레임워크 분석 결과)
|
| 1809 |
-
5. **
|
| 1810 |
-
- 예: `#### 부가 아이디어 X:\\n 한 줄로 자세한 한글 문구`
|
| 1811 |
6. **출처** (웹검색 링크, Kaggle 데이터셋 등)
|
| 1812 |
{cat_clause}
|
| 1813 |
아무리 길어도 이 요구사항을 준수하고, **오직 최종 완성된 답변**만 출력하십시오.
|
|
@@ -1815,12 +1896,12 @@ def get_idea_system_prompt(selected_category: str | None = None,
|
|
| 1815 |
"""
|
| 1816 |
return base_prompt.strip()
|
| 1817 |
|
| 1818 |
-
# ──────────────────────────────── 나머지 코드 (웹검색, kaggle, 이미지 생성 등) ──────────────────────────
|
| 1819 |
|
|
|
|
| 1820 |
@st.cache_data(ttl=3600)
|
| 1821 |
def brave_search(query: str, count: int = 20):
|
| 1822 |
if not BRAVE_KEY:
|
| 1823 |
-
raise RuntimeError("⚠️ SERPHOUSE_API_KEY (Brave API Key)
|
| 1824 |
headers = {
|
| 1825 |
"Accept": "application/json",
|
| 1826 |
"Accept-Encoding": "gzip",
|
|
@@ -1868,7 +1949,7 @@ def do_web_search(query: str) -> str:
|
|
| 1868 |
try:
|
| 1869 |
arts = brave_search(query, 20)
|
| 1870 |
if not arts:
|
| 1871 |
-
logging.warning("No search results
|
| 1872 |
return mock_results(query)
|
| 1873 |
hdr = "# Web Search Results\nUse the information below to spark new design/invention insights.\n\n"
|
| 1874 |
body = "\n".join(
|
|
@@ -1880,53 +1961,20 @@ def do_web_search(query: str) -> str:
|
|
| 1880 |
logging.error(f"Web search process failed: {str(e)}")
|
| 1881 |
return mock_results(query)
|
| 1882 |
|
| 1883 |
-
# ──────────────────────────────── (신규) 디자인/발명 아이디어 처리 함수 ─────────────────
|
| 1884 |
-
def process_invention_ideas(keyword: str):
|
| 1885 |
-
"""
|
| 1886 |
-
(이전에는 별도 버튼/프롬프트가 있었으나,
|
| 1887 |
-
이제 메인 프롬프트로 일원화되어 사실상 사용되지 않을 수도 있음.)
|
| 1888 |
-
"""
|
| 1889 |
-
if not keyword.strip():
|
| 1890 |
-
st.warning("키워드를 입력하세요.")
|
| 1891 |
-
return
|
| 1892 |
-
|
| 1893 |
-
st.info(f"디자인/발명 아이디어 생성 중... (키워드: **{keyword}**)")
|
| 1894 |
-
|
| 1895 |
-
# 모든 카테고리와 항목을 리스트업
|
| 1896 |
-
categories_text = []
|
| 1897 |
-
for cat_name, items in physical_transformation_categories.items():
|
| 1898 |
-
joined_items = ", ".join(items)
|
| 1899 |
-
categories_text.append(f"- {cat_name}: {joined_items}")
|
| 1900 |
-
categories_joined = "\n".join(categories_text)
|
| 1901 |
-
|
| 1902 |
-
prompt = f"""
|
| 1903 |
-
당신은 디자인/발명 전문가입니다.
|
| 1904 |
-
키워드: "{keyword}"
|
| 1905 |
-
아래는 카테고리+항목 목록입니다.
|
| 1906 |
-
{categories_joined}
|
| 1907 |
-
|
| 1908 |
-
이 키워드를 각 항목과 결합한 아이디어를 생각하고,
|
| 1909 |
-
타당한 것과 배제할 것을 분류하여 마크다운으로 출력하세요.
|
| 1910 |
-
"""
|
| 1911 |
-
try:
|
| 1912 |
-
client = get_openai_client()
|
| 1913 |
-
with st.spinner("Generating invention ideas..."):
|
| 1914 |
-
response = client.chat.completions.create(
|
| 1915 |
-
model="gpt-4.1-mini",
|
| 1916 |
-
messages=[{"role": "user", "content": prompt}],
|
| 1917 |
-
temperature=0.9,
|
| 1918 |
-
max_tokens=2500,
|
| 1919 |
-
)
|
| 1920 |
-
result_text = response.choices[0].message.content
|
| 1921 |
-
st.markdown(result_text)
|
| 1922 |
-
except Exception as e:
|
| 1923 |
-
st.error(f"오류 발생: {e}")
|
| 1924 |
|
| 1925 |
-
# ──────────────────────────
|
|
|
|
| 1926 |
def idea_generator_app():
|
| 1927 |
-
|
| 1928 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1929 |
|
|
|
|
| 1930 |
default_vals = {
|
| 1931 |
"ai_model": "gpt-4.1-mini",
|
| 1932 |
"messages": [],
|
|
@@ -1943,9 +1991,14 @@ def idea_generator_app():
|
|
| 1943 |
st.session_state[k] = v
|
| 1944 |
|
| 1945 |
sb = st.sidebar
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1946 |
st.session_state.temp = sb.slider(
|
| 1947 |
-
"Diversity
|
| 1948 |
-
help="0.1 =
|
| 1949 |
)
|
| 1950 |
|
| 1951 |
sb.title("Settings")
|
|
@@ -1968,24 +2021,17 @@ def idea_generator_app():
|
|
| 1968 |
sb.error("⚠️ KAGGLE_KEY not set.")
|
| 1969 |
st.session_state.kaggle_enabled = False
|
| 1970 |
|
| 1971 |
-
#
|
| 1972 |
-
|
| 1973 |
-
# 예시 주제
|
| 1974 |
-
example_topics = {
|
| 1975 |
-
"example1": "'고양이 장난감' 디자인",
|
| 1976 |
-
"example2": "재밍 대응 가능한 드론 디자인",
|
| 1977 |
-
"example3": "사용자 인터페이스(UI/UX) 혁신을 위한 웨어러블 기기 아이디어"
|
| 1978 |
-
}
|
| 1979 |
sb.subheader("Example Topics")
|
| 1980 |
c1, c2, c3 = sb.columns(3)
|
| 1981 |
-
if c1.button("
|
| 1982 |
-
process_example(
|
| 1983 |
-
if c2.button("
|
| 1984 |
-
process_example(
|
| 1985 |
-
if c3.button("UI/UX
|
| 1986 |
-
process_example(
|
| 1987 |
-
|
| 1988 |
-
#
|
| 1989 |
latest_ideas = next(
|
| 1990 |
(m["content"] for m in reversed(st.session_state.messages)
|
| 1991 |
if m["role"] == "assistant" and m["content"].strip()),
|
|
@@ -2001,12 +2047,12 @@ def idea_generator_app():
|
|
| 2001 |
d2.download_button("Download as HTML", md_to_html(latest_ideas, title),
|
| 2002 |
file_name=f"{title}.html", mime="text/html")
|
| 2003 |
|
| 2004 |
-
#
|
| 2005 |
up = sb.file_uploader("Load Conversation (.json)", type=["json"], key="json_uploader")
|
| 2006 |
if up:
|
| 2007 |
try:
|
| 2008 |
st.session_state.messages = json.load(up)
|
| 2009 |
-
sb.success("Conversation history loaded
|
| 2010 |
except Exception as e:
|
| 2011 |
sb.error(f"Failed to load: {e}")
|
| 2012 |
|
|
@@ -2018,7 +2064,7 @@ def idea_generator_app():
|
|
| 2018 |
mime="application/json"
|
| 2019 |
)
|
| 2020 |
|
| 2021 |
-
#
|
| 2022 |
st.subheader("File Upload (Optional)")
|
| 2023 |
uploaded_files = st.file_uploader(
|
| 2024 |
"Upload reference files (txt, csv, pdf)",
|
|
@@ -2051,7 +2097,7 @@ def idea_generator_app():
|
|
| 2051 |
if idx < len(uploaded_files) - 1:
|
| 2052 |
st.divider()
|
| 2053 |
|
| 2054 |
-
#
|
| 2055 |
skip_idx = st.session_state.get("_skip_dup_idx")
|
| 2056 |
for i, m in enumerate(st.session_state.messages):
|
| 2057 |
if skip_idx is not None and i == skip_idx:
|
|
@@ -2062,24 +2108,23 @@ def idea_generator_app():
|
|
| 2062 |
st.image(m["image"], caption=m.get("image_caption", ""))
|
| 2063 |
st.session_state["_skip_dup_idx"] = None
|
| 2064 |
|
| 2065 |
-
#
|
| 2066 |
-
prompt = st.chat_input("
|
| 2067 |
if prompt:
|
| 2068 |
process_input(prompt, uploaded_files)
|
| 2069 |
|
| 2070 |
sb.markdown("---")
|
| 2071 |
sb.markdown("Created by [VIDraft](https://discord.gg/openfreeai)")
|
| 2072 |
|
|
|
|
| 2073 |
def process_example(topic):
|
| 2074 |
process_input(topic, [])
|
| 2075 |
|
|
|
|
| 2076 |
def process_input(prompt: str, uploaded_files):
|
| 2077 |
"""
|
| 2078 |
-
|
| 2079 |
-
스트리밍 실패(RemoteProtocolError 등) 시 backoff 재시도 후
|
| 2080 |
-
최종적으로 non-stream 호출로 폴백.
|
| 2081 |
"""
|
| 2082 |
-
# ─── 대화 기록 중복 방지 ──────────────────────────────
|
| 2083 |
if not any(m["role"] == "user" and m["content"] == prompt for m in st.session_state.messages):
|
| 2084 |
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 2085 |
with st.chat_message("user"):
|
|
@@ -2091,7 +2136,6 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2091 |
and st.session_state.messages[i + 1]["role"] == "assistant"):
|
| 2092 |
return
|
| 2093 |
|
| 2094 |
-
# ─── 결과 생성 ───────────────────────────────────────
|
| 2095 |
with st.chat_message("assistant"):
|
| 2096 |
status = st.status("Preparing to generate invention ideas…")
|
| 2097 |
stream_placeholder = st.empty()
|
|
@@ -2103,10 +2147,18 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2103 |
|
| 2104 |
selected_cat = st.session_state.get("category_focus", None)
|
| 2105 |
selected_frameworks = st.session_state.get("selected_frameworks", [])
|
| 2106 |
-
|
| 2107 |
-
|
| 2108 |
-
|
| 2109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2110 |
|
| 2111 |
def category_context(sel):
|
| 2112 |
if sel:
|
|
@@ -2119,45 +2171,45 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2119 |
|
| 2120 |
search_content = kaggle_content = file_content = mil_content = None
|
| 2121 |
|
| 2122 |
-
#
|
| 2123 |
if use_web_search:
|
| 2124 |
status.update(label="Searching the web…")
|
| 2125 |
with st.spinner("Searching…"):
|
| 2126 |
search_content = do_web_search(keywords(prompt, top=5))
|
| 2127 |
|
| 2128 |
-
#
|
| 2129 |
if use_kaggle and check_kaggle_availability():
|
| 2130 |
-
status.update(label="Kaggle
|
| 2131 |
with st.spinner("Searching Kaggle…"):
|
| 2132 |
kaggle_kw = extract_kaggle_search_keywords(prompt)
|
| 2133 |
try:
|
| 2134 |
datasets = search_kaggle_datasets(kaggle_kw)
|
| 2135 |
except Exception as e:
|
| 2136 |
-
logging.warning(f"search_kaggle_datasets
|
| 2137 |
datasets = []
|
| 2138 |
analyses = []
|
| 2139 |
if datasets:
|
| 2140 |
-
status.update(label="Downloading &
|
| 2141 |
for ds in datasets:
|
| 2142 |
try:
|
| 2143 |
ana = download_and_analyze_dataset(ds["ref"])
|
| 2144 |
except Exception as e:
|
| 2145 |
-
logging.error(f"Kaggle
|
| 2146 |
-
ana = f"
|
| 2147 |
analyses.append({"meta": ds, "analysis": ana})
|
| 2148 |
if analyses:
|
| 2149 |
kaggle_content = format_kaggle_analysis_markdown_multi(analyses)
|
| 2150 |
|
| 2151 |
-
#
|
| 2152 |
if has_uploaded:
|
| 2153 |
status.update(label="Reading uploaded files…")
|
| 2154 |
with st.spinner("Processing files…"):
|
| 2155 |
file_content = process_uploaded_files(uploaded_files)
|
| 2156 |
|
| 2157 |
-
#
|
| 2158 |
if is_military_query(prompt):
|
| 2159 |
-
status.update(label="Searching military tactics
|
| 2160 |
-
with st.spinner("
|
| 2161 |
mil_rows = military_search(prompt)
|
| 2162 |
if mil_rows:
|
| 2163 |
mil_content = "# Military Tactics Dataset Reference\n\n"
|
|
@@ -2169,18 +2221,16 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2169 |
f"**Defense Reasoning:** {row['defense_reasoning']}\n\n---\n"
|
| 2170 |
)
|
| 2171 |
|
| 2172 |
-
# ─── 유저 콘텐츠 구성 ───────────────────────���──
|
| 2173 |
user_content = prompt
|
| 2174 |
for extra in (search_content, kaggle_content, file_content, mil_content):
|
| 2175 |
if extra:
|
| 2176 |
user_content += "\n\n" + extra
|
| 2177 |
|
| 2178 |
-
|
| 2179 |
-
status.update(label="분석 중…")
|
| 2180 |
decision_purpose = identify_decision_purpose(prompt)
|
| 2181 |
relevance_scores = compute_relevance_scores(prompt, PHYS_CATEGORIES)
|
| 2182 |
|
| 2183 |
-
status.update(label="
|
| 2184 |
T = st.session_state.temp
|
| 2185 |
k_cat_range = (4, 8) if T < 1.0 else (6, 10) if T < 2.0 else (8, 12)
|
| 2186 |
n_item_range = (2, 4) if T < 1.0 else (3, 6) if T < 2.0 else (4, 8)
|
|
@@ -2195,22 +2245,22 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2195 |
T=T,
|
| 2196 |
)
|
| 2197 |
|
| 2198 |
-
combos_table = "|
|
| 2199 |
for w, imp, conf, tot, cmb in combos:
|
| 2200 |
combo_str = " + ".join(f"{c[0]}-{c[1]}" for c in cmb)
|
| 2201 |
combos_table += f"| {combo_str} | {w} | {imp} | {conf:.1f} | {tot} |\n"
|
| 2202 |
|
| 2203 |
-
purpose_info = "\n\n##
|
| 2204 |
if decision_purpose['purposes']:
|
| 2205 |
-
purpose_info += "###
|
| 2206 |
for p, s in decision_purpose['purposes']:
|
| 2207 |
-
purpose_info += f"- **{p}** (
|
| 2208 |
if decision_purpose['constraints']:
|
| 2209 |
-
purpose_info += "\n###
|
| 2210 |
for c, s in decision_purpose['constraints']:
|
| 2211 |
-
purpose_info += f"- **{c}** (
|
| 2212 |
|
| 2213 |
-
#
|
| 2214 |
framework_contents = []
|
| 2215 |
for fw in selected_frameworks:
|
| 2216 |
if fw == "swot":
|
|
@@ -2227,11 +2277,11 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2227 |
)
|
| 2228 |
|
| 2229 |
if framework_contents:
|
| 2230 |
-
user_content += "\n\n## (Optional)
|
| 2231 |
|
| 2232 |
-
user_content += f"\n\n##
|
| 2233 |
|
| 2234 |
-
status.update(label="Generating final
|
| 2235 |
|
| 2236 |
api_messages = [
|
| 2237 |
{"role": "system", "content": sys_prompt},
|
|
@@ -2239,7 +2289,6 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2239 |
{"role": "user", "content": user_content},
|
| 2240 |
]
|
| 2241 |
|
| 2242 |
-
# ─── OpenAI Chat 호출 (backoff 재시도) ─────────────────
|
| 2243 |
@backoff.on_exception(
|
| 2244 |
(RemoteProtocolError, APITimeoutError, APIError), max_tries=3
|
| 2245 |
)
|
|
@@ -2253,7 +2302,6 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2253 |
stream=True
|
| 2254 |
)
|
| 2255 |
|
| 2256 |
-
|
| 2257 |
try:
|
| 2258 |
stream = safe_stream()
|
| 2259 |
for chunk in stream:
|
|
@@ -2261,7 +2309,7 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2261 |
full_response += chunk.choices[0].delta.content
|
| 2262 |
stream_placeholder.markdown(full_response + "▌")
|
| 2263 |
except (RemoteProtocolError, APITimeoutError, APIError) as stream_err:
|
| 2264 |
-
logging.warning(f"
|
| 2265 |
resp = client.chat.completions.create(
|
| 2266 |
model="gpt-4.1-mini",
|
| 2267 |
messages=api_messages,
|
|
@@ -2275,7 +2323,7 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2275 |
|
| 2276 |
status.update(label="Invention ideas created!", state="complete")
|
| 2277 |
|
| 2278 |
-
#
|
| 2279 |
img_data = img_caption = None
|
| 2280 |
if st.session_state.generate_image and full_response:
|
| 2281 |
match = re.search(r"###\s*이미지\s*프롬프트\s*\n+([^\n]+)", full_response, re.I)
|
|
@@ -2288,15 +2336,13 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2288 |
if img_data:
|
| 2289 |
st.image(img_data, caption=f"Visualized Concept – {img_caption}")
|
| 2290 |
|
| 2291 |
-
# ─── 세션 메시지 저장 ─────────────────────────────
|
| 2292 |
answer_msg = {"role": "assistant", "content": full_response}
|
| 2293 |
if img_data:
|
| 2294 |
-
answer_msg["image"]
|
| 2295 |
answer_msg["image_caption"] = img_caption
|
| 2296 |
st.session_state["_skip_dup_idx"] = len(st.session_state.messages)
|
| 2297 |
st.session_state.messages.append(answer_msg)
|
| 2298 |
|
| 2299 |
-
# ─── 다운로드 옵션 ──────────────────────────────
|
| 2300 |
st.subheader("Download This Output")
|
| 2301 |
col_md, col_html = st.columns(2)
|
| 2302 |
col_md.download_button(
|
|
@@ -2319,12 +2365,11 @@ def process_input(prompt: str, uploaded_files):
|
|
| 2319 |
|
| 2320 |
except Exception as e:
|
| 2321 |
logging.error("process_input error", exc_info=True)
|
| 2322 |
-
st.error(f"⚠️
|
| 2323 |
st.session_state.messages.append(
|
| 2324 |
-
{"role": "assistant", "content": f"⚠️
|
| 2325 |
)
|
| 2326 |
|
| 2327 |
-
|
| 2328 |
def main():
|
| 2329 |
idea_generator_app()
|
| 2330 |
|
|
|
|
| 25 |
import glob
|
| 26 |
import shutil
|
| 27 |
|
| 28 |
+
# ─── Additional libraries (must not be removed) ───────────────────────
|
| 29 |
import pyarrow.parquet as pq
|
| 30 |
from sklearn.feature_extraction.text import TfidfVectorizer
|
| 31 |
from sklearn.metrics.pairwise import cosine_similarity
|
| 32 |
|
| 33 |
+
# ─── Network stability libraries ──────────────────────────────────────
|
| 34 |
import httpx
|
| 35 |
from httpx import RemoteProtocolError
|
| 36 |
|
| 37 |
+
# ▸ If `backoff` is not installed, provide a quick fallback
|
| 38 |
try:
|
| 39 |
import backoff
|
| 40 |
except ImportError:
|
| 41 |
+
logging.warning("`backoff` module is missing. Using a simple fallback decorator.")
|
| 42 |
|
| 43 |
def _simple_backoff_on_exception(exceptions, *args, **kwargs):
|
| 44 |
"""
|
| 45 |
+
Lightweight exponential (base^n) retry decorator.
|
| 46 |
+
This mimics the core parameters of backoff.on_exception:
|
| 47 |
+
- exceptions : tuple or single exception type
|
| 48 |
+
- max_tries : from kwargs (default 3)
|
| 49 |
+
- base : from kwargs (default 2)
|
|
|
|
| 50 |
"""
|
| 51 |
max_tries = kwargs.get("max_tries", 3)
|
| 52 |
base = kwargs.get("base", 2)
|
|
|
|
| 63 |
raise
|
| 64 |
sleep = base ** attempt
|
| 65 |
logging.info(
|
| 66 |
+
f"[retry {attempt}/{max_tries}] {fn.__name__} -> {e} … waiting {sleep}s"
|
| 67 |
)
|
| 68 |
time.sleep(sleep)
|
| 69 |
return wrapper
|
|
|
|
| 75 |
backoff = _DummyBackoff()
|
| 76 |
|
| 77 |
|
| 78 |
+
# ─────────────────────────────────── Environment Variables / Constants ─────────────────────────
|
| 79 |
|
| 80 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
|
| 81 |
BRAVE_KEY = os.getenv("SERPHOUSE_API_KEY", "") # Brave Search API
|
|
|
|
| 84 |
KAGGLE_API_KEY = KAGGLE_KEY
|
| 85 |
|
| 86 |
if not (KAGGLE_USERNAME and KAGGLE_KEY):
|
| 87 |
+
raise RuntimeError("⚠️ Please set KAGGLE_USERNAME and KAGGLE_KEY environment variables first.")
|
| 88 |
|
| 89 |
os.environ["KAGGLE_USERNAME"] = KAGGLE_USERNAME
|
| 90 |
os.environ["KAGGLE_KEY"] = KAGGLE_KEY
|
| 91 |
|
| 92 |
BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
|
| 93 |
+
IMAGE_API_URL = "http://211.233.58.201:7896" # Example image generation API
|
| 94 |
+
MAX_TOKENS = 7999 # For safe token usage
|
| 95 |
|
| 96 |
+
# ────────────────────────────────── Logging ─────────────────────────────────
|
| 97 |
logging.basicConfig(
|
| 98 |
level=logging.INFO,
|
| 99 |
format="%(asctime)s - %(levelname)s - %(message)s"
|
| 100 |
)
|
| 101 |
|
| 102 |
|
| 103 |
+
# ─────────────────────────────── Military Dataset ─────────────────────────
|
| 104 |
@st.cache_resource
|
| 105 |
def load_military_dataset():
|
| 106 |
"""
|
|
|
|
| 120 |
MIL_DF = load_military_dataset()
|
| 121 |
|
| 122 |
def is_military_query(text: str) -> bool:
|
| 123 |
+
"""Return True if text includes military/tactical keywords."""
|
| 124 |
kw = [
|
| 125 |
"군사", "전술", "전투", "전쟁", "작전", "무기", "병력",
|
| 126 |
"military", "tactic", "warfare", "battle", "operation"
|
|
|
|
| 129 |
|
| 130 |
def military_search(query: str, top_k: int = 3):
|
| 131 |
"""
|
| 132 |
+
Use cosine similarity with scenario_description in mil.parquet
|
| 133 |
+
Return top K scenarios.
|
| 134 |
"""
|
| 135 |
if MIL_DF is None:
|
| 136 |
return []
|
|
|
|
| 148 |
logging.error(f"military_search error: {e}")
|
| 149 |
return []
|
| 150 |
|
| 151 |
+
|
| 152 |
+
# ───────────────────────────── Kaggle Datasets ───────────────────────────
|
| 153 |
KAGGLE_DATASETS = {
|
| 154 |
"general_business": {
|
| 155 |
"ref": "mohammadgharaei77/largest-2000-global-companies",
|
|
|
|
| 417 |
}
|
| 418 |
}
|
| 419 |
|
| 420 |
+
|
| 421 |
SUN_TZU_STRATEGIES = [
|
| 422 |
{"계": "만천과해", "요약": "평범한 척, 몰래 진행", "조건": "상대가 지켜보고 있을 때", "행동": "루틴·평온함 과시", "목적": "경계 무력화", "예시": "규제기관 눈치 보는 신사업 파일럿"},
|
| 423 |
{"계": "위위구조", "요약": "뒤통수 치면 포위 풀린다", "조건": "우리 측이 압박받을 때", "행동": "적 본진 급습", "목적": "압박 해소", "예시": "경쟁사 핵심 고객 뺏기"},
|
|
|
|
| 459 |
|
| 460 |
physical_transformation_categories = {
|
| 461 |
"센서 기능": [
|
| 462 |
+
# Original items
|
| 463 |
"시각 센서", "시각 감지", "청각 센서", "청각 감지", "촉각 센서", "촉각 감지",
|
| 464 |
"미각 센서", "미각 감지", "후각 센서", "후각 감지", "온도 센서", "온도 감지",
|
| 465 |
"습도 센서", "습도 감지", "압력 센서", "압력 감지", "가속도 센서", "가속도 감지",
|
|
|
|
| 477 |
"홀 효과 감지", "초음파 센서", "초음파 감지", "레이더 센서", "레이더 감지",
|
| 478 |
"라이다 센서", "라이다 감지", "터치 센서", "터치 감지", "제스처 센서", "제스처 감지",
|
| 479 |
"심박 센서", "심박 감지", "혈압 센서", "혈압 감지", "LAN", "WIFI", "블루투스", "생체 인증",
|
| 480 |
+
# Additional items
|
| 481 |
"다중 스펙트럼 센서", "다중 스펙트럼 감지", "깊이 인식 센서", "깊이 인식 감지",
|
| 482 |
"퀀텀 센서", "퀀텀 감지", "웨어러블 센서", "웨어러블 감지", "바이오마커 센서", "바이오마커 감지",
|
| 483 |
"임베디드 센서", "임베디드 감지", "IoT 센서 네트워크", "스트레인 센서", "스트레인 감지",
|
|
|
|
| 485 |
"스마트 먼지 센서", "환경 센서 그리드", "신경형태학적 센서", "두뇌-기계 인터페이스"
|
| 486 |
],
|
| 487 |
"크기와 형태 변화": [
|
| 488 |
+
# Original items
|
| 489 |
"부피 늘어남", "부피 줄어듦", "길이 늘어남", "길이 줄어듦", "너비 늘어남", "너비 줄어남",
|
| 490 |
"높이 늘어남", "높이 줄어듦", "밀도 변화", "무게 증가", "무게 감소", "모양 변형",
|
| 491 |
"상태 변화", "불균등 변형", "복잡한 형태 변형", "비틀림", "꼬임", "불균일한 확장",
|
|
|
|
| 493 |
"물 저항", "먼지 저항", "찌그러짐", "복원", "접힘", "펼쳐짐", "압착", "팽창",
|
| 494 |
"늘어남", "수축", "구겨짐", "평평해짐", "뭉개짐", "단단해짐", "말림", "펴짐",
|
| 495 |
"꺾임", "구부러짐",
|
| 496 |
+
# Additional items
|
| 497 |
"4D 프린팅 변형", "형상 기억", "프랙탈 변화", "자가 조립", "자가 복구",
|
| 498 |
"기하학적 변환", "모듈화", "스마트 직물 변형", "매트릭스 구조 변형", "프로그래머블 변형",
|
| 499 |
"미시 스케일 변형", "거시 스케일 변형", "이방성 변형", "등방성 변형", "선택적 강성 변화",
|
| 500 |
"변형률 감응 구조", "형태학적 계산", "위상 변화", "경도 변화", "부드러움 변화"
|
| 501 |
],
|
| 502 |
"표면 및 외관 변화": [
|
| 503 |
+
# Original items
|
| 504 |
"색상 변화", "질감 변화", "투명 변화", "불투명 변화", "반짝임 변화", "무광 변화",
|
| 505 |
"빛 반사 정도 변화", "무늬 변화", "각도에 따른 색상 변화", "빛에 따른 색상 변화",
|
| 506 |
"온도에 따른 색상 변화", "홀로그램 효과", "표면 각도별 빛 반사", "표면 모양 변형",
|
|
|
|
| 508 |
"선명함 변화", "광택 변화", "윤기 변화", "색조 변화", "채도 변화", "발광",
|
| 509 |
"형광", "빛 산란 효과", "빛 흡수 변화", "반투명 효과", "그림자 효과 변화",
|
| 510 |
"자외선 반응 변화", "야광 효과",
|
| 511 |
+
# Additional items
|
| 512 |
"생체모방 표면", "프로그래머블 질감", "촉각 피드백 표면", "열 반응성 표면",
|
| 513 |
"초소수성/초친수성 표면", "스마트 코팅", "마찰 계수 변화", "도금 효과", "위장 효과",
|
| 514 |
"양자점 효과", "메타표면 효과", "나노 구조화 표면", "전기변색 효과", "광변색 효과",
|
| 515 |
+
"압력변색 효과", "자기변색 효과", "항균 표면", "공기역학적 표면", "자기정렬 패턴",
|
| 516 |
"부착성 변화", "선택적 접착성"
|
| 517 |
],
|
| 518 |
"물질의 상태 변화": [
|
| 519 |
+
# Original items
|
| 520 |
"고체 전환", "액체 전환", "기체 전환", "결정화", "용해", "산화", "부식",
|
| 521 |
"딱딱해짐", "부드러워짐", "특수 상태 전환", "무정형 전환", "결정형 전환", "성분 분리",
|
| 522 |
"미세 입자 형성", "미세 입자 분해", "젤 형성", "젤 풀어짐", "준안정 상태 변화",
|
|
|
|
| 524 |
"증발", "응축", "승화", "증착", "침전", "부유", "분산", "응집",
|
| 525 |
"건조", "습윤", "팽윤", "수축", "동결", "해동", "풍화", "침식",
|
| 526 |
"충전", "방전", "결합", "분리", "발효", "부패",
|
| 527 |
+
# Additional items
|
| 528 |
"초임계 상태 전환", "양자 상태 전환", "메타물질 상태 변화", "프로그래머블 물질 변화",
|
| 529 |
"소프트 로봇 물질 변화", "4D 프린팅 물질 변화", "바이오하이브리드 물질 변화",
|
| 530 |
"자가 조직화 물질", "자가 순환 물질", "자가 치유 물질", "생분해성 전환",
|
|
|
|
| 532 |
"스마트 유체 상태", "자극 반응성 상태", "형상기억 합금 상태", "초전도 상태"
|
| 533 |
],
|
| 534 |
"움직임 특성 변화": [
|
| 535 |
+
# Original items
|
| 536 |
"가속", "감속", "일정 속도 유지", "진동", "진동 감소", "부딪힘", "튕김",
|
| 537 |
"회전 속도 증가", "회전 속도 감소", "회전 방향 변화", "불규칙 움직임", "멈췄다", "미끄러지는 현상",
|
| 538 |
"공진", "반공진", "유체 속 저항 변화", "유체 속 양력 변화", "움직임 저항 변화",
|
| 539 |
"복합 진동 움직임", "특수 유체 속 움직임", "회전-이동 연계 움직임", "관성 정지",
|
| 540 |
"충격 흡수", "충격 전달", "운동량 보존", "마찰력 변화", "관성 탈출", "불안정 균형",
|
| 541 |
"동적 안정성", "흔들림 감쇠", "경로 예측성", "회피 움직임",
|
| 542 |
+
# Additional items
|
| 543 |
"복합 운동학", "임의의 움직임", "재귀적 움직임", "흉내내는 움직임", "학습된 움직임",
|
| 544 |
"자율적 움직임", "군집 움직임", "비선형 움직임", "양자 움직임", "초음파 움직임",
|
| 545 |
"비대칭 움직임", "스토캐스틱 움직임", "카오스 움직임", "소프트 로보틱스 움직임",
|
|
|
|
| 547 |
"계층적 움직임 제어", "적응형 움직임 패턴"
|
| 548 |
],
|
| 549 |
"구조적 변화": [
|
| 550 |
+
# Original items
|
| 551 |
"부품 추가", "부품 제거", "조립", "분해", "접기", "펴기", "변형", "원상복구",
|
| 552 |
"최적 구조 변화", "자가 재배열", "자연 패턴 형성", "자연 패턴 소멸", "규칙적 패턴 변화",
|
| 553 |
"모듈식 변형", "복잡성 증가 구조", "원래 모양 기억 효과", "시간에 따른 형태 변화",
|
|
|
|
| 555 |
"내부 구조 변화", "외부 구조 변화", "중심축 이동", "균형점 변화", "계층 구조 변화",
|
| 556 |
"지지 구조 변화", "응력 분산 구조", "충격 흡수 구조", "그리드 구조 변화", "매트릭스 구조 변화",
|
| 557 |
"상호 연결성 변화",
|
| 558 |
+
# Additional items
|
| 559 |
"텐세그리티 구조 변화", "바이오닉 구조", "메타물질 구조", "다중 안정 구조", "자가 진화 구조",
|
| 560 |
"자가 학습 구조", "생체모방 구조", "프랙탈 구조", "계층적 구조화", "에너지 흡수 구조",
|
| 561 |
"에너지 변환 구조", "적응형 구조", "위상 최적화", "다공성 구조", "기능적 경사 구조",
|
| 562 |
"다중 재료 구조", "초경량 구조", "초고강도 구조", "다기능성 구조", "내결함성 구조"
|
| 563 |
],
|
| 564 |
"공간 이동": [
|
| 565 |
+
# Original items
|
| 566 |
"앞 이동", "뒤 이동", "좌 이동", "우 이동", "위 이동", "아래 이동",
|
| 567 |
"세로축 회전(고개 끄덕임)", "가로축 회전(고개 젓기)", "길이축 회전(옆으로 기울임)", "원 운동",
|
| 568 |
"나선형 이동", "관성에 의한 미끄러짐", "회전축 변화", "불규칙 회전", "흔들림 운동",
|
| 569 |
"포물선 이동", "무중력 부유", "수면 위 부유", "점프", "도약", "슬라이딩", "롤링",
|
| 570 |
"자유 낙하", "왕복 운동", "탄성 튕김", "관통", "회피 움직임", "지그재그 이동", "스윙 운동",
|
| 571 |
+
# Additional items
|
| 572 |
"양자 이동", "차원 간 이동", "가상 공간 이동", "증강 공간 이동", "무인 이동",
|
| 573 |
"군집 이동", "경로 최적화 이동", "상황 인식 이동", "생태계 통합 이동", "바이오닉 이동",
|
| 574 |
"미시 스케일 이동", "매크로 스케일 이동", "변형 기반 이동", "자기장 유도 이동",
|
|
|
|
| 576 |
"지능형 경로 탐색"
|
| 577 |
],
|
| 578 |
"시간 관련 변화": [
|
| 579 |
+
# Original items
|
| 580 |
"노화", "풍화", "마모", "부식", "색 바램", "변색", "손상", "회복",
|
| 581 |
"수명 주기 변화", "사용자 상호작용에 따른 적응", "학습 기반 형태 최적화", "시간에 따른 물성 변화",
|
| 582 |
"집단 기억 효과", "문화적 의미 변화", "지연 반응", "이전 상태 의존 변화", "점진적 시간 변화",
|
| 583 |
"진화적 변화", "주기적 재생", "계절 변화 적응", "생체리듬 변화", "생애 주기 단계",
|
| 584 |
"성장", "퇴화", "자가 복구", "자가 재생", "자연 순환 적응", "지속성", "일시성",
|
| 585 |
"기억 효과", "지연된 작용", "누적 효과",
|
| 586 |
+
# Additional items
|
| 587 |
"시간 지연 효과", "예측 기반 변화", "학습 기반 변화", "인지적 시간 변화",
|
| 588 |
"시간 압축 경험", "시간 확장 경험", "시간적 패턴 감지", "시간적 패턴 생성",
|
| 589 |
"계절 인식 변화", "생체 시계 동기화", "시간 기반 프로그래밍", "연대기적 데이터 구조",
|
|
|
|
| 591 |
"사용 패턴 적응", "사용자 타임라인 통합", "예측 유지보수", "자가 최적화 타이밍"
|
| 592 |
],
|
| 593 |
"빛과 시각 효과": [
|
| 594 |
+
# Original items
|
| 595 |
"발광", "소등", "빛 투과", "빛 차단", "빛 산란", "빛 집중", "색상 스펙트럼 변화",
|
| 596 |
"빛 회절", "빛 간섭", "홀로그램 생성", "레이저 효과", "빛 편광", "형광", "인광",
|
| 597 |
"자외선 발광", "적외선 발광", "광학적 착시", "빛 굴절", "그림자 생성", "그림자 제거",
|
| 598 |
"색수차 효과", "무지개 효과", "글로우 효과", "플래시 효과", "조명 패턴", "빔 효과",
|
| 599 |
"광 필터 효과", "빛의 방향성 변화", "투영 효과", "빛 감지", "빛 반응", "광도 변화",
|
| 600 |
+
# Additional items
|
| 601 |
"양자 발광", "메타 광학 효과", "프로그래머블 광학", "시야각 변화", "생체발광 모방",
|
| 602 |
"광역학 효과", "퍼셉션 변화 효과", "맥스웰리안 뷰", "광 컴퓨팅 효과", "광유전학 효과",
|
| 603 |
"생체 광학 모방", "비선형 광학 효과", "구조색 변화", "자가 발광", "광 결정 효과",
|
| 604 |
"양자점 방출", "나노 발광체", "증강 광학", "투명 디스플레이 효과", "광학 위장"
|
| 605 |
],
|
| 606 |
"소리와 진동 효과": [
|
| 607 |
+
# Original items
|
| 608 |
"소리 발생", "소리 소멸", "음 높낮이 변화", "음량 변화", "음색 변화", "공명",
|
| 609 |
"반공명", "음향 진동", "초음파 발생", "저음파 발생", "소리 집중", "소리 분산",
|
| 610 |
"음향 반사", "음향 흡수", "음향 도플러 효과", "음파 간섭", "음향 공진", "진동 패턴 변화",
|
| 611 |
"타악 효과", "음향 피드백", "음향 차폐", "음향 증폭", "소리 지향성", "소리 왜곡",
|
| 612 |
"비트 생성", "배음 생성", "주파수 변조", "음향 충격파", "음향 필터링",
|
| 613 |
+
# Additional items
|
| 614 |
"메타 음향 효과", "방향성 음향", "3D 음향 효과", "생체음향 모방", "상황별 음향 변화",
|
| 615 |
"음향 위장", "음향 투명화", "음향 렌즈", "양자 음향 효과", "초저주파 효과",
|
| 616 |
"초고주파 효과", "음파 에너지 수확", "자가 조절 공명", "음향 홀로그래피",
|
|
|
|
| 618 |
"기능적 음향 표면", "구조 공진 조절"
|
| 619 |
],
|
| 620 |
"열 관련 변화": [
|
| 621 |
+
# Original items
|
| 622 |
"온도 상승", "온도 하강", "열 팽창", "열 수축", "열 전달", "열 차단", "압력 상승",
|
| 623 |
"압력 하강", "열 변화에 따른 자화", "엔트로피 변화", "열전기 효과", "자기장에 의한 열 변화",
|
| 624 |
"상태 변화 중 열 저장", "상태 변화 중 열 방출", "열 스트레스 발생", "열 스트레스 해소",
|
|
|
|
| 626 |
"열 반사", "열 흡수", "냉각 응축", "열 활성화", "열 변색", "열 팽창 계수 변화",
|
| 627 |
"열 안정성 변화", "내열성", "내한성", "자가 발열", "열적 평형", "열적 불균형",
|
| 628 |
"열적 변형", "열 분산", "열 집중",
|
| 629 |
+
# Additional items
|
| 630 |
"열전 효과", "열광 효과", "프로그래머블 열 특성", "상변화 냉각", "상변화 가열",
|
| 631 |
"열 유도 기억", "열 광학 효과", "열 음향 효과", "열 기계 효과", "열 화학 반응",
|
| 632 |
"양자 열역학 효과", "근적외선 열 효과", "열 조절 표면", "열흐름 제어", "열 방출 최적화",
|
| 633 |
"열 캡처 최적화", "자가 조절 온도", "열 스위칭", "열 포커싱", "상변화 재료 활용"
|
| 634 |
],
|
| 635 |
"전기 및 자기 변화": [
|
| 636 |
+
# Original items
|
| 637 |
"자성 생성", "자성 소멸", "전하량 증가", "전하량 감소", "전기장 생성", "전기장 소멸",
|
| 638 |
"자기장 생성", "자기장 소멸", "초전도 상태 전환", "강유전체 특성 변화", "양자 상태 변화",
|
| 639 |
"플라즈마 형성", "플라즈마 소멸", "스핀파 전달", "빛에 의한 전기 발생", "압력에 의한 전기 발생",
|
|
|
|
| 641 |
"전자기 유도", "전자기파 방출", "전자기파 흡수", "전기 용량 변화", "자기 이력 현상",
|
| 642 |
"전기적 분극", "전자 흐름 방향 변화", "전기적 공명", "전기적 차폐", "전기적 노출",
|
| 643 |
"자기 차폐", "자기 노출", "자기장 정렬", "유선(Wire)", "무선(Wireless)",
|
| 644 |
+
# Additional items
|
| 645 |
"양자 자성", "스핀트로닉스 효과", "마그네토일렉트릭 효과", "토폴로지컬 절연체 특성",
|
| 646 |
"초전도 양자 효과", "쿨롱 차단 효과", "조셉슨 효과", "홀 효과 변화", "전자기 투명성",
|
| 647 |
"자기 카이랄리티", "전자기 메타표면", "무선 전력 전송", "자기유변학적 효과",
|
|
|
|
| 649 |
"전자 스핀 제어", "고속 스위칭 자성"
|
| 650 |
],
|
| 651 |
"화학적 변화": [
|
| 652 |
+
# Original items
|
| 653 |
"표면 코팅 변화", "물질 성분 변화", "화학 반응 변화", "촉매 작용 시작/중단",
|
| 654 |
"빛에 의한 화학 반응", "전기에 의한 화학 반응", "단분자막 형성", "분자 수준 구조 변화",
|
| 655 |
"생체 모방 표면 변화", "환경 반응형 물질 변화", "주기적 화학 반응", "산화", "환원",
|
| 656 |
"고분자화", "물 분해", "화합", "방사선 영향", "산-염기 반응", "중화 반응",
|
| 657 |
"이온화", "화학적 흡착/탈착", "촉매 효율 변화", "효소 활성 변화", "발색 반응",
|
| 658 |
"pH 변화", "화학적 평형 이동", "결합 형성/분해", "용해도 변화",
|
| 659 |
+
# Additional items
|
| 660 |
"프로그래머블 화학 반응", "자가 촉매 반응", "클릭 케미스트리", "광화학 반응",
|
| 661 |
"전기화학 반응", "초분자 화학 반응", "동적 공유 결합", "바이오오쏘고널 화학",
|
| 662 |
"화학적 컴퓨팅", "화학적 감지", "화학적 통신", "화학적 기억", "선택적 촉매",
|
|
|
|
| 664 |
"화학적 패턴 형성", "화학적 습도 조절", "화학적 정화"
|
| 665 |
],
|
| 666 |
"생물학적 변화": [
|
| 667 |
+
# Original items
|
| 668 |
"성장/위축", "세포 분열/사멸", "생물 발광", "신진대사 변화", "면역 반응",
|
| 669 |
"호르몬 분비", "신경 반응", "유전적 발현", "적응/진화", "생체리듬 변화",
|
| 670 |
"재생/치유", "노화/성숙", "생체 모방 변화", "바이오필름 형성", "생물학적 분해",
|
| 671 |
"효소 활성화/비활성화", "생물학적 신호 전달", "스트레스 반응", "체온 조절", "생물학적 시계 변화",
|
| 672 |
"세포외 기질 변화", "생체 역학적 반응", "세포 운동성", "세포 극성 변화", "영양 상태 변화",
|
| 673 |
+
# Additional items
|
| 674 |
"합성 생물학 반응", "생물학적 컴퓨팅", "오가노이드 발달", "인공 조직 발달",
|
| 675 |
"생체적합성 변화", "면역학적 응답 제어", "후성유전학적 변화", "생물학적 리듬 조절",
|
| 676 |
"신경가소성 효과", "세포외 기질 리모델링", "체세포 리프로그래밍", "생체활성 표면 상호작용",
|
|
|
|
| 678 |
"바이오하이브리드 시스템", "세포 분화 조절", "생���신호 증폭", "생화학적 기억 형성"
|
| 679 |
],
|
| 680 |
"환경 상호작용": [
|
| 681 |
+
# Original items
|
| 682 |
"온도 반응", "습도 반응", "기압 반응", "중력 반응", "자기장 반응",
|
| 683 |
"빛 반응", "소리 반응", "화학 물질 감지", "기계적 자극 감지", "전기 자극 반응",
|
| 684 |
"방사선 반응", "진동 감지", "pH 반응", "용매 반응", "기체 교환",
|
| 685 |
"환경 오염 반응", "날씨 반응", "계절 반응", "일주기 반응", "생태계 상호작용",
|
| 686 |
"공생/경쟁 반응", "포식/피식 관계", "군집 형성", "영역 설정", "이주 패턴", "정착 패턴",
|
| 687 |
+
# Additional items
|
| 688 |
"탄소 포집 및 변환", "생태계 복원 효과", "생물다양성 증진", "순환 경제 상호작용",
|
| 689 |
"도시 환경 통합", "스마트 환경 감지", "재생 가능 에너지 연계", "물 순환 상호작용",
|
| 690 |
"대기 질 상호작용", "자연 기반 솔루션 통합", "재해 복원력 증진", "기후 변화 적응",
|
|
|
|
| 692 |
"환경적 자가 수정", "지속가능한 자원 관리", "생태계 건강 모니터링", "생태 교란 방지"
|
| 693 |
],
|
| 694 |
"비즈니스 아이디어": [
|
| 695 |
+
# Original items
|
| 696 |
"시장 재정의/신규 시장 개척",
|
| 697 |
"비즈니스 모델 혁신/디지털 전환",
|
| 698 |
"고객 경험 혁신/서비스 혁신",
|
|
|
|
| 703 |
"지속 가능한 성장/사회적 가치 창출",
|
| 704 |
"데이터 기반 의사결정/AI 도입",
|
| 705 |
"신기술 융합/혁신 투자",
|
| 706 |
+
# Additional items
|
| 707 |
"탄소중립 비즈니스 모델",
|
| 708 |
"순환경제 비즈니스 모델",
|
| 709 |
"구독 경제 모델",
|
|
|
|
| 721 |
"웰빙/웰니스 중심 비즈니스"
|
| 722 |
],
|
| 723 |
|
| 724 |
+
# Newly added categories
|
| 725 |
|
| 726 |
"사용자 인터페이스 및 상호작용": [
|
| 727 |
"제스처 인식", "제스처 제어", "음성 인식", "음성 제어", "시선 추적", "시선 제어",
|
|
|
|
| 798 |
]
|
| 799 |
}
|
| 800 |
|
| 801 |
+
# ────────────────────────────── Frameworks ─────────────────────────────
|
| 802 |
SWOT_FRAMEWORK = {
|
| 803 |
"strengths": {
|
| 804 |
+
"title": "Strengths",
|
| 805 |
+
"description": "Internal positive factors – competitive advantages",
|
| 806 |
"prompt_keywords": ["강점", "장점", "우위", "역량", "자산", "전문성", "strength", "advantage"]
|
| 807 |
},
|
| 808 |
"weaknesses": {
|
| 809 |
+
"title": "Weaknesses",
|
| 810 |
+
"description": "Internal negative factors – internal limitations",
|
| 811 |
"prompt_keywords": ["약점", "단점", "부족", "한계", "취약점", "weakness", "limitation", "deficit"]
|
| 812 |
},
|
| 813 |
"opportunities": {
|
| 814 |
+
"title": "Opportunities",
|
| 815 |
+
"description": "External positive factors – potential to leverage",
|
| 816 |
"prompt_keywords": ["기회", "가능성", "트렌드", "변화", "성장", "opportunity", "trend", "potential"]
|
| 817 |
},
|
| 818 |
"threats": {
|
| 819 |
+
"title": "Threats",
|
| 820 |
+
"description": "External negative factors – threats or obstacles",
|
| 821 |
"prompt_keywords": ["위협", "리스크", "경쟁", "위험", "장벽", "threat", "risk", "competition", "barrier"]
|
| 822 |
}
|
| 823 |
}
|
| 824 |
|
| 825 |
PORTER_FRAMEWORK = {
|
| 826 |
"rivalry": {
|
| 827 |
+
"title": "Competitive Rivalry",
|
| 828 |
+
"description": "Intensity of competition among existing competitors",
|
| 829 |
"prompt_keywords": ["경쟁", "경쟁사", "시장점유율", "가격경쟁", "competition", "rival", "market share"]
|
| 830 |
},
|
| 831 |
"new_entrants": {
|
| 832 |
+
"title": "Threat of New Entrants",
|
| 833 |
+
"description": "Ease or difficulty of new players entering the market",
|
| 834 |
"prompt_keywords": ["진입장벽", "신규", "스타트업", "entry barrier", "newcomer", "startup"]
|
| 835 |
},
|
| 836 |
"substitutes": {
|
| 837 |
+
"title": "Threat of Substitutes",
|
| 838 |
+
"description": "Availability of substitute products/services",
|
| 839 |
"prompt_keywords": ["대체재", "대안", "substitute", "alternative", "replacement"]
|
| 840 |
},
|
| 841 |
"buyer_power": {
|
| 842 |
+
"title": "Buyer Power",
|
| 843 |
+
"description": "Negotiating leverage of customers",
|
| 844 |
"prompt_keywords": ["고객", "구매자", "가격민감도", "협상력", "customer", "buyer power"]
|
| 845 |
},
|
| 846 |
"supplier_power": {
|
| 847 |
+
"title": "Supplier Power",
|
| 848 |
+
"description": "Negotiating leverage of suppliers",
|
| 849 |
"prompt_keywords": ["공급자", "벤더", "원재료", "supplier", "vendor", "raw material"]
|
| 850 |
}
|
| 851 |
}
|
| 852 |
|
| 853 |
BCG_FRAMEWORK = {
|
| 854 |
"stars": {
|
| 855 |
+
"title": "Stars",
|
| 856 |
+
"description": "High growth, high market share – need investment",
|
| 857 |
"prompt_keywords": ["성장", "점유율", "중점", "투자", "star", "growth", "investment"]
|
| 858 |
},
|
| 859 |
"cash_cows": {
|
| 860 |
+
"title": "Cash Cows",
|
| 861 |
+
"description": "Low growth, high market share – generates cash flow",
|
| 862 |
"prompt_keywords": ["안정", "수익", "현금", "전통", "cash cow", "profit", "mature"]
|
| 863 |
},
|
| 864 |
"question_marks": {
|
| 865 |
+
"title": "Question Marks",
|
| 866 |
+
"description": "High growth, low market share – uncertain potential",
|
| 867 |
"prompt_keywords": ["가능성", "위험", "불확실", "잠재", "question mark", "uncertain", "potential"]
|
| 868 |
},
|
| 869 |
"dogs": {
|
| 870 |
+
"title": "Dogs",
|
| 871 |
+
"description": "Low growth, low market share – potential divest",
|
| 872 |
"prompt_keywords": ["회수", "철수", "저성장", "비효율", "dog", "divest", "low growth"]
|
| 873 |
}
|
| 874 |
}
|
| 875 |
|
| 876 |
BUSINESS_FRAMEWORKS = {
|
| 877 |
+
"sunzi": "Sun Tzu's 36 Stratagems",
|
| 878 |
+
"swot": "SWOT Analysis",
|
| 879 |
+
"porter": "Porter's 5 Forces",
|
| 880 |
+
"bcg": "BCG Matrix"
|
| 881 |
}
|
| 882 |
|
| 883 |
+
|
| 884 |
@dataclass
|
| 885 |
class Category:
|
| 886 |
+
"""Unified category/item structure."""
|
| 887 |
name_ko: str
|
| 888 |
name_en: str
|
| 889 |
tags: list[str]
|
|
|
|
| 953 |
if not analysis_result:
|
| 954 |
return ""
|
| 955 |
titles = {
|
| 956 |
+
'swot': '# SWOT Analysis',
|
| 957 |
+
'porter': '# Porter’s 5 Forces Analysis',
|
| 958 |
+
'bcg': '# BCG Matrix Analysis'
|
| 959 |
}
|
| 960 |
+
md = f"{titles.get(framework_type, '# Business Framework Analysis')}\n\n"
|
| 961 |
+
md += "Each factor includes a text analysis score and keywords found.\n\n"
|
| 962 |
for category, info in analysis_result.items():
|
| 963 |
md += f"## {info['title']}\n\n"
|
| 964 |
md += f"{info['description']}\n\n"
|
| 965 |
+
md += f"**Relevance Score**: {info['score']}\n\n"
|
| 966 |
if info['keywords']:
|
| 967 |
+
md += "**Keywords/Context**:\n"
|
| 968 |
for keyword in info['keywords']:
|
| 969 |
md += f"- *{keyword}*\n"
|
| 970 |
md += "\n"
|
| 971 |
else:
|
| 972 |
+
md += "No relevant keywords found.\n\n"
|
| 973 |
return md
|
| 974 |
|
| 975 |
+
# ───────────────────────────── Markdown -> HTML ───────────────────────
|
| 976 |
def md_to_html(md_text: str, title: str = "Output") -> str:
|
| 977 |
html_content = markdown.markdown(
|
| 978 |
md_text,
|
|
|
|
| 1066 |
</html>
|
| 1067 |
"""
|
| 1068 |
|
| 1069 |
+
# ───────────────────────────── Upload File Handling ───────────────────
|
| 1070 |
def process_text_file(uploaded_file):
|
| 1071 |
try:
|
| 1072 |
content = uploaded_file.read().decode('utf-8')
|
| 1073 |
+
return f"""# Uploaded Text File: {uploaded_file.name}
|
| 1074 |
|
| 1075 |
{content}
|
| 1076 |
"""
|
| 1077 |
except Exception as e:
|
| 1078 |
+
logging.error(f"Text file processing error: {str(e)}")
|
| 1079 |
return f"**Error processing {uploaded_file.name}**: {str(e)}"
|
| 1080 |
|
| 1081 |
def process_csv_file(uploaded_file):
|
| 1082 |
try:
|
| 1083 |
df = pd.read_csv(uploaded_file)
|
| 1084 |
+
return f"""# Uploaded CSV File: {uploaded_file.name}
|
| 1085 |
|
| 1086 |
+
## Basic Info
|
| 1087 |
+
- Rows: {df.shape[0]}
|
| 1088 |
+
- Columns: {df.shape[1]}
|
| 1089 |
+
- Column Names: {', '.join(df.columns.tolist())}
|
| 1090 |
|
| 1091 |
+
## First 5 Rows
|
| 1092 |
{df.head(5).to_markdown(index=False)}
|
| 1093 |
|
| 1094 |
+
## Basic Statistics
|
| 1095 |
{df.describe().to_markdown()}
|
| 1096 |
"""
|
| 1097 |
except Exception as e:
|
| 1098 |
+
logging.error(f"CSV file processing error: {str(e)}")
|
| 1099 |
return f"**Error processing {uploaded_file.name}**: {str(e)}"
|
| 1100 |
|
| 1101 |
def process_pdf_file(uploaded_file):
|
|
|
|
| 1110 |
pages_preview.append(f"--- Page {page_num+1} ---\n{page.extract_text()}")
|
| 1111 |
|
| 1112 |
preview_text = "\n\n".join(pages_preview)
|
| 1113 |
+
return f"""# Uploaded PDF File: {uploaded_file.name}
|
| 1114 |
|
| 1115 |
+
## Basic Info
|
| 1116 |
+
- Total Pages: {len(reader.pages)}
|
| 1117 |
|
| 1118 |
+
## Preview of First 5 Pages
|
| 1119 |
{preview_text}
|
| 1120 |
"""
|
| 1121 |
except Exception as e:
|
| 1122 |
+
logging.error(f"PDF file processing error: {str(e)}")
|
| 1123 |
return f"**Error processing {uploaded_file.name}**: {str(e)}"
|
| 1124 |
|
| 1125 |
def process_uploaded_files(uploaded_files):
|
|
|
|
| 1144 |
f"# Unsupported file: {file.name}\n\nThis file type is not supported for processing."
|
| 1145 |
)
|
| 1146 |
except Exception as e:
|
| 1147 |
+
logging.error(f"Error processing file {file.name}: {str(e)}")
|
| 1148 |
file_contents.append(f"# Error processing file: {file.name}\n\n{str(e)}")
|
| 1149 |
|
| 1150 |
+
return "\n\n# User Uploaded File Analysis\n\n" + "\n\n---\n\n".join(file_contents)
|
| 1151 |
+
|
| 1152 |
|
| 1153 |
+
# ───────────────────────────── Image Generation ──────────────────────
|
| 1154 |
def generate_image(prompt: str):
|
| 1155 |
if not prompt:
|
| 1156 |
return None, None
|
|
|
|
| 1183 |
logging.error(f"Image generation error: {str(e)}", exc_info=True)
|
| 1184 |
return None, None
|
| 1185 |
|
| 1186 |
+
# ───────────────────────────── Kaggle API ─────────────────────────────
|
| 1187 |
@st.cache_resource
|
| 1188 |
def check_kaggle_availability():
|
| 1189 |
if not KAGGLE_API_KEY:
|
| 1190 |
+
logging.warning("Kaggle API not available (KAGGLE_KEY is empty).")
|
| 1191 |
return False
|
| 1192 |
return True
|
| 1193 |
|
|
|
|
| 1226 |
@st.cache_data
|
| 1227 |
def download_and_analyze_dataset(dataset_ref: str, max_rows: int = 1000):
|
| 1228 |
if not (os.getenv("KAGGLE_USERNAME") and os.getenv("KAGGLE_KEY")):
|
| 1229 |
+
return "Missing Kaggle API credentials."
|
| 1230 |
api = KaggleApi()
|
| 1231 |
api.authenticate()
|
| 1232 |
tmpdir = tempfile.mkdtemp()
|
|
|
|
| 1235 |
except Exception as e:
|
| 1236 |
logging.error(f"Dataset download failed ({dataset_ref}): {e}")
|
| 1237 |
shutil.rmtree(tmpdir)
|
| 1238 |
+
return f"Error downloading dataset: {e}"
|
| 1239 |
|
| 1240 |
csv_files = glob.glob(f"{tmpdir}/**/*.csv", recursive=True)
|
| 1241 |
if not csv_files:
|
| 1242 |
shutil.rmtree(tmpdir)
|
| 1243 |
+
return "No CSV file found in the dataset."
|
| 1244 |
|
| 1245 |
try:
|
| 1246 |
df = pd.read_csv(csv_files[0], nrows=max_rows)
|
|
|
|
| 1252 |
"missing_values": df.isnull().sum().to_dict()
|
| 1253 |
}
|
| 1254 |
except Exception as e:
|
| 1255 |
+
analysis = f"CSV parsing error: {e}"
|
| 1256 |
|
| 1257 |
shutil.rmtree(tmpdir)
|
| 1258 |
return analysis
|
| 1259 |
|
| 1260 |
def format_kaggle_analysis_markdown_multi(analyses: list[dict]) -> str:
|
| 1261 |
"""
|
| 1262 |
+
For multiple Kaggle datasets (up to 3) meta/analysis results in a single markdown.
|
| 1263 |
analyses = [ {"meta": {...}, "analysis": {... or str}}, ... ]
|
| 1264 |
"""
|
| 1265 |
if not analyses:
|
| 1266 |
+
return "# Kaggle Datasets\n\nNo related dataset found.\n\n"
|
| 1267 |
+
md = "# Kaggle Dataset Analysis\n\n"
|
| 1268 |
+
md += "Review the following datasets for further insight:\n\n"
|
| 1269 |
for i, item in enumerate(analyses, 1):
|
| 1270 |
ds = item["meta"]
|
| 1271 |
ana = item["analysis"]
|
| 1272 |
md += f"## {i}. {ds['title']}\n\n"
|
| 1273 |
md += f"{ds['subtitle']}\n\n"
|
| 1274 |
+
md += f"- **Ref**: {ds['ref']}\n"
|
| 1275 |
+
md += f"- **URL**: [{ds['url']}]({ds['url']})\n\n"
|
| 1276 |
if isinstance(ana, dict):
|
| 1277 |
+
md += f"**Rows × Columns**: {ana['shape'][0]} × {ana['shape'][1]}\n\n"
|
| 1278 |
+
md += "<details><summary>Preview & Stats (Click to expand)</summary>\n\n"
|
| 1279 |
try:
|
| 1280 |
md += pd.DataFrame(ana["head"]).to_markdown(index=False) + "\n\n"
|
| 1281 |
except:
|
|
|
|
| 1290 |
md += "---\n\n"
|
| 1291 |
return md
|
| 1292 |
|
| 1293 |
+
# ───────────────────────────── OpenAI Client ──────────────────────────
|
| 1294 |
@st.cache_resource
|
| 1295 |
def get_openai_client():
|
| 1296 |
if not OPENAI_API_KEY:
|
| 1297 |
+
raise RuntimeError("⚠️ OPENAI_API_KEY is not set.")
|
| 1298 |
return OpenAI(
|
| 1299 |
api_key=OPENAI_API_KEY,
|
| 1300 |
timeout=60.0,
|
| 1301 |
max_retries=3
|
| 1302 |
)
|
| 1303 |
|
| 1304 |
+
# ───────────────────── Identify Decision Purpose (Design/Invention) ─────────────────────
|
| 1305 |
def identify_decision_purpose(prompt: str) -> dict:
|
| 1306 |
"""
|
| 1307 |
+
Identify main design/invention purposes or constraints from the prompt.
|
|
|
|
|
|
|
| 1308 |
"""
|
| 1309 |
purpose_patterns = {
|
| 1310 |
'cost_reduction': [r'비용(\s*절감)?', r'예산', r'효율', r'저렴', r'경제', r'cost', r'saving', r'budget'],
|
|
|
|
| 1338 |
'all_constraint_scores': constraint_scores
|
| 1339 |
}
|
| 1340 |
|
| 1341 |
+
# ───────────────────────────── Category Utility ──────────────────────
|
| 1342 |
def keywords(text: str, top: int = 8) -> str:
|
| 1343 |
words = re.findall(r'\b[a-zA-Z가-힣]{2,}\b', text.lower())
|
| 1344 |
stopwords = {
|
|
|
|
| 1357 |
|
| 1358 |
def compute_relevance_scores(prompt: str, categories: list[Category]) -> dict:
|
| 1359 |
"""
|
| 1360 |
+
Score how relevant each category/item is for the prompt, from design/invention perspective.
|
| 1361 |
"""
|
| 1362 |
prompt_lower = prompt.lower()
|
| 1363 |
prompt_tokens = set(re.findall(r'\b[a-zA-Z가-힣]{2,}\b', prompt_lower))
|
|
|
|
| 1381 |
if category.name_ko in prompt or category.name_en.lower() in prompt_lower:
|
| 1382 |
cat_score += 1
|
| 1383 |
|
| 1384 |
+
# Slight weighting by purpose
|
| 1385 |
if main_purpose:
|
| 1386 |
purpose_category_weights = {
|
| 1387 |
'cost_reduction': {
|
|
|
|
| 1388 |
'구조적 변화': 1.5, '화학적 변화': 1.3, '비즈니스 아이디어': 1.5,
|
| 1389 |
'Structural Change': 1.5, 'Chemical Change': 1.3, 'Business Ideas': 1.5,
|
|
|
|
| 1390 |
'에너지 변환 및 관리': 1.6, '데이터 및 정보 변환': 1.4, '지속가능성 및 환경 영향': 1.3,
|
| 1391 |
+
'Energy Conversion and Management': 1.6, 'Data and Information Transformation': 1.4,
|
| 1392 |
'Sustainability and Environmental Impact': 1.3
|
| 1393 |
},
|
| 1394 |
'innovation': {
|
|
|
|
| 1395 |
'센서 기능': 1.5, '표면 및 외관 변화': 1.3, '비즈니스 아이디어': 1.5,
|
| 1396 |
'Sensor Functions': 1.5, 'Surface and Appearance Change': 1.3, 'Business Ideas': 1.5,
|
|
|
|
| 1397 |
'사용자 인터페이스 및 상호작용': 1.6, '데이터 및 정보 변환': 1.4, '인지 및 심리적 변화': 1.3,
|
| 1398 |
'User Interface and Interaction': 1.6, 'Data and Information Transformation': 1.4,
|
| 1399 |
'Cognitive and Psychological Changes': 1.3
|
| 1400 |
},
|
| 1401 |
'risk_management': {
|
|
|
|
| 1402 |
'환경 상호작용': 1.5, '시간 관련 변화': 1.3, '비즈니스 아이디어': 1.4,
|
| 1403 |
'Environmental Interaction': 1.5, 'Time-Related Change': 1.3, 'Business Ideas': 1.4,
|
|
|
|
| 1404 |
'보안 및 프라이버시': 1.7, '지속가능성 및 환경 영향': 1.5, '데이터 및 정보 변환': 1.4,
|
| 1405 |
'Security and Privacy': 1.7, 'Sustainability and Environmental Impact': 1.5,
|
| 1406 |
'Data and Information Transformation': 1.4
|
| 1407 |
},
|
| 1408 |
'growth': {
|
|
|
|
| 1409 |
'크기와 형태 변화': 1.4, '비즈니스 아이디어': 1.6, '구조적 변화': 1.3,
|
| 1410 |
'Size and Shape Change': 1.4, 'Business Ideas': 1.6, 'Structural Change': 1.3,
|
|
|
|
| 1411 |
'사회적 상호작용 및 협업': 1.5, '데이터 및 정보 변환': 1.4, '사용자 인터페이스 및 상호작용': 1.3,
|
| 1412 |
'Social Interaction and Collaboration': 1.5, 'Data and Information Transformation': 1.4,
|
| 1413 |
'User Interface and Interaction': 1.3
|
| 1414 |
},
|
| 1415 |
'customer': {
|
|
|
|
| 1416 |
'표면 및 외관 변화': 1.5, '센서 기능': 1.4, '빛과 시각 효과': 1.3, '비즈니스 아이디어': 1.4,
|
| 1417 |
'Surface and Appearance Change': 1.5, 'Sensor Functions': 1.4,
|
| 1418 |
'Light and Visual Effects': 1.3, 'Business Ideas': 1.4,
|
|
|
|
| 1419 |
'사용자 인터페이스 및 상호작용': 1.7, '미학 및 감성 경험': 1.6, '인지 및 심리적 변화': 1.5,
|
| 1420 |
'사회적 상호작용 및 협업': 1.4,
|
| 1421 |
'User Interface and Interaction': 1.7, 'Aesthetics and Emotional Experience': 1.6,
|
|
|
|
| 1427 |
elif category.name_en in purpose_category_weights.get(main_purpose, {}):
|
| 1428 |
cat_score *= purpose_category_weights[main_purpose][category.name_en]
|
| 1429 |
|
| 1430 |
+
# Score by item tokens
|
| 1431 |
for item in category.items:
|
| 1432 |
item_score = cat_score
|
| 1433 |
item_tokens = set(re.findall(r'\b[a-zA-Z가-힣]{2,}\b', item.lower()))
|
|
|
|
| 1449 |
relevance_threshold: float = 0.2
|
| 1450 |
) -> list[tuple]:
|
| 1451 |
"""
|
| 1452 |
+
Generate multiple category/item combinations as 'idea' candidates.
|
|
|
|
| 1453 |
"""
|
| 1454 |
if relevance_scores is None:
|
| 1455 |
pool = [(c.name_ko, item) for c in categories for item in c.items]
|
|
|
|
| 1486 |
evaluated_combinations.sort(key=lambda x: x[3], reverse=True)
|
| 1487 |
return evaluated_combinations[:max_combinations]
|
| 1488 |
|
| 1489 |
+
# ────────────────────────────── Random Matrix Generator ─────────────────────────
|
| 1490 |
def smart_weight(cat_name, item, relevance, global_cnt, T):
|
| 1491 |
rare_boost = 1 / (global_cnt.get(item, 0) + 0.5)
|
| 1492 |
+
noise = random.random() ** (1 / T) # T bigger => noise is closer to 1
|
| 1493 |
relevance_weight = 1 - (T - 0.1) / 3.0
|
| 1494 |
return ((relevance * relevance_weight) + 0.1) * rare_boost * noise
|
| 1495 |
|
|
|
|
| 1504 |
T: float = 1.3,
|
| 1505 |
):
|
| 1506 |
"""
|
| 1507 |
+
Randomly generate a matrix of category/item combos to ensure diversity,
|
| 1508 |
+
helpful for design/invention idea expansion.
|
| 1509 |
"""
|
| 1510 |
if seed is None:
|
| 1511 |
seed = random.randrange(2 ** 32)
|
|
|
|
| 1547 |
combos.sort(key=lambda x: x[3], reverse=True)
|
| 1548 |
return combos[:max_combos]
|
| 1549 |
|
| 1550 |
+
# ───────────────────────────── Physical Categories ─────────────────────────
|
| 1551 |
PHYS_CATEGORIES: list[Category] = [
|
|
|
|
| 1552 |
Category(
|
| 1553 |
name_ko="센서 기능",
|
| 1554 |
name_en="Sensor Functions",
|
|
|
|
| 1645 |
tags=["business", "idea", "비즈니스"],
|
| 1646 |
items=physical_transformation_categories["비즈니스 아이디어"]
|
| 1647 |
),
|
|
|
|
|
|
|
| 1648 |
Category(
|
| 1649 |
name_ko="사용자 인터페이스 및 상호작용",
|
| 1650 |
name_en="User Interface and Interaction",
|
|
|
|
| 1694 |
items=physical_transformation_categories["미학 및 감성 경험"]
|
| 1695 |
)
|
| 1696 |
]
|
|
|
|
| 1697 |
|
| 1698 |
+
|
| 1699 |
+
# ────────────────────────── System Prompts in English/Korean ─────────────────────────
|
| 1700 |
+
def get_idea_system_prompt_en(selected_category: str | None = None,
|
| 1701 |
+
selected_frameworks: list | None = None) -> str:
|
| 1702 |
+
"""
|
| 1703 |
+
English system prompt for design/invention idea generation.
|
| 1704 |
+
"""
|
| 1705 |
+
cat_clause = (
|
| 1706 |
+
f'\n**Additional Note**: Please pay special attention to the selected category "{selected_category}"\n'
|
| 1707 |
+
) if selected_category else ""
|
| 1708 |
+
if not selected_frameworks:
|
| 1709 |
+
selected_frameworks = []
|
| 1710 |
+
framework_instruction = "\n\n### (Selected Additional Frameworks)\n"
|
| 1711 |
+
for fw in selected_frameworks:
|
| 1712 |
+
if fw == "sunzi":
|
| 1713 |
+
framework_instruction += "- Sun Tzu's 36 Stratagems\n"
|
| 1714 |
+
elif fw == "swot":
|
| 1715 |
+
framework_instruction += "- SWOT Analysis\n"
|
| 1716 |
+
elif fw == "porter":
|
| 1717 |
+
framework_instruction += "- Porter's 5 Forces\n"
|
| 1718 |
+
elif fw == "bcg":
|
| 1719 |
+
framework_instruction += "- BCG Matrix\n"
|
| 1720 |
+
|
| 1721 |
+
base_prompt = f"""
|
| 1722 |
+
You are a creative design/invention expert AI.
|
| 1723 |
+
Given the user's input, produce **"the top 5 design/invention ideas"** in detail.
|
| 1724 |
+
Each idea must meet these requirements:
|
| 1725 |
+
1) **Extensive detail** so the reader can visualize it
|
| 1726 |
+
2) Also provide an **image prompt** for each idea
|
| 1727 |
+
3) If you see insights from Kaggle datasets or web search, reference them
|
| 1728 |
+
4) At the end, add a **"Sources"** section with:
|
| 1729 |
+
- 3~5 URLs from the web search (Brave)
|
| 1730 |
+
- Any Kaggle dataset names/URLs
|
| 1731 |
+
- Other references
|
| 1732 |
+
5) Provide **10 additional ideas** outside the top 5, in a single line each,
|
| 1733 |
+
describing the core concept and novelty in one line
|
| 1734 |
+
|
| 1735 |
+
{framework_instruction}
|
| 1736 |
+
|
| 1737 |
+
## Idea Evaluation Criteria
|
| 1738 |
+
When selecting the top ideas, each is evaluated with the following weighting:
|
| 1739 |
+
1. **Innovation** (30%)
|
| 1740 |
+
2. **Feasibility** (25%)
|
| 1741 |
+
3. **Market Potential** (20%)
|
| 1742 |
+
4. **Social Impact** (15%)
|
| 1743 |
+
5. **Scalability** (10%)
|
| 1744 |
+
|
| 1745 |
+
Format your final answer in English, structured:
|
| 1746 |
+
1. **Topic Summary** (brief analysis approach, ~300 characters)
|
| 1747 |
+
2. **Top 5 Ideas Overview** (short summary and rationale, up to 400 words)
|
| 1748 |
+
3. **Top 5 Ideas Detailed**
|
| 1749 |
+
- For each idea:
|
| 1750 |
+
- ### Idea X: [Idea Name] (Score: x.x/10)
|
| 1751 |
+
- #### Core Concept
|
| 1752 |
+
* Provide a thorough explanation (~400 characters)
|
| 1753 |
+
* The specific problem it solves and importance
|
| 1754 |
+
* At least 3 main differences from existing solutions
|
| 1755 |
+
* Clearly define the unique value proposition
|
| 1756 |
+
- #### Detailed Design & Technical Implementation
|
| 1757 |
+
* Components, design specs, materials, production method
|
| 1758 |
+
* Dimensions, mechanics, feasibility details
|
| 1759 |
+
* At least 3 technical challenges + solutions
|
| 1760 |
+
* Potential patentable elements
|
| 1761 |
+
* Required key technologies/resources
|
| 1762 |
+
- #### Usage Scenarios & User Experience
|
| 1763 |
+
* At least 3 real usage scenarios with a short story
|
| 1764 |
+
* Define at least 2 user personas
|
| 1765 |
+
* Step-by-step user journey
|
| 1766 |
+
* Emotional connections and user feedback approach
|
| 1767 |
+
- #### Market Analysis & Business Model
|
| 1768 |
+
* TAM, SAM, SOM and growth rate estimates
|
| 1769 |
+
* Main customer segments + their needs
|
| 1770 |
+
* Compare with 5 existing competitor solutions (table) + advantage
|
| 1771 |
+
* Revenue model and streams
|
| 1772 |
+
* Market entry strategy + marketing approach
|
| 1773 |
+
* Scalable model analysis (business model canvas)
|
| 1774 |
+
- #### Implementation Roadmap & Resource Planning
|
| 1775 |
+
* Step-by-step plan (PoC, prototype, testing, production, etc.)
|
| 1776 |
+
* 6-month, 1-year, 3-year timeline + major milestones
|
| 1777 |
+
* Required team roles
|
| 1778 |
+
* Initial funding and fundraising strategy
|
| 1779 |
+
* Partnerships or external collaborations
|
| 1780 |
+
* Quality control and metrics
|
| 1781 |
+
- #### SWOT Analysis
|
| 1782 |
+
* Strengths (5 unique advantages)
|
| 1783 |
+
* Weaknesses (3 potential downsides + solutions)
|
| 1784 |
+
* Opportunities (4 external triggers for success)
|
| 1785 |
+
* Threats (3 external risks + mitigation)
|
| 1786 |
+
|
| 1787 |
+
4. **Additional Insights** (any selected frameworks or extra analysis)
|
| 1788 |
+
5. **Additional 10 Ideas** (in one line each)
|
| 1789 |
+
- Example: `#### Additional Idea X:\n One line description with novelty`
|
| 1790 |
+
|
| 1791 |
+
6. **Sources** (web links, Kaggle references, etc.)
|
| 1792 |
+
|
| 1793 |
+
{cat_clause}
|
| 1794 |
+
|
| 1795 |
+
No matter how long, strictly follow these instructions and output only the final completed text.
|
| 1796 |
+
(Do not reveal internal chain-of-thought.)
|
| 1797 |
+
"""
|
| 1798 |
+
return base_prompt.strip()
|
| 1799 |
+
|
| 1800 |
+
|
| 1801 |
+
def get_idea_system_prompt_kr(selected_category: str | None = None,
|
| 1802 |
+
selected_frameworks: list | None = None) -> str:
|
| 1803 |
"""
|
| 1804 |
+
Korean system prompt for design/invention idea generation.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1805 |
"""
|
| 1806 |
cat_clause = (
|
| 1807 |
f'\n**추가 지침**: 선택된 카테고리 "{selected_category}"를 특별히 우선하여 고려하세요.\n'
|
|
|
|
| 1818 |
framework_instruction += "- Porter의 5 Forces\n"
|
| 1819 |
elif fw == "bcg":
|
| 1820 |
framework_instruction += "- BCG 매트릭스\n"
|
| 1821 |
+
|
| 1822 |
base_prompt = f"""
|
| 1823 |
당신은 창의적 디자인/발명 전문가 AI입니다.
|
| 1824 |
사용자가 입력한 주제를 분석하여,
|
|
|
|
| 1826 |
각 아이디어는 다음 요구를 충족해야 합니다:
|
| 1827 |
1) **아주 상세하게** 설명하여, 독자가 머릿속에 이미지를 그릴 수 있을 정도로 구체적으로 서술
|
| 1828 |
2) **이미지 프롬프트**도 함께 제시하여, 자동 이미지 생성이 되도록 하라
|
|
|
|
| 1829 |
3) **Kaggle 데이터셋**, **웹 검색**을 활용한 통찰(또는 참조)이 있으면 반드시 결과에 언급
|
| 1830 |
4) 최종 출력의 마지막에 **"출처"** 섹션을 만들고,
|
| 1831 |
- 웹 검색(Brave)에서 참조한 URL 3~5개
|
| 1832 |
- Kaggle 데이터셋 이름/URL(있다면)
|
| 1833 |
- 그 밖의 참고 자료
|
| 1834 |
+
5) **추가 아이디어** 5개에 포함되지 않은 다음 순서 10개를 한 줄씩 간략하면서도 핵심 가치와 혁신점을 포함해 서술하라
|
| 1835 |
+
|
|
|
|
| 1836 |
{framework_instruction}
|
| 1837 |
|
| 1838 |
## 아이디어 평가 기준
|
|
|
|
| 1885 |
* 약점(Weaknesses): 잠재적 약점 3가지 이상 및 이를 극복하기 위한 구체적인 방안
|
| 1886 |
* 기회(Opportunities): 외부 환경(기술, 시장, 정책 등)에서 발생하는 기회 요소 4가지 이상
|
| 1887 |
* 위협(Threats): 성공을 방해할 수 있는 외부 요인 3가지 이상과 각각에 대한 구체적 대응책
|
| 1888 |
+
|
|
|
|
|
|
|
| 1889 |
4. **부가적 통찰** (선택된 프레임워크 분석 결과)
|
| 1890 |
+
5. **추가 아이디어** (TOP 5에 해당하지 않는 10가지 아이디어, 각각 한 줄로 간결하게 설명하되 해당 아이디어의 핵심 가치와 혁신점을 포함)
|
| 1891 |
+
- 예: `#### 부가 아이디어 X:\\n 한 줄로 자세한 한글 문구`
|
| 1892 |
6. **출처** (웹검색 링크, Kaggle 데이터셋 등)
|
| 1893 |
{cat_clause}
|
| 1894 |
아무리 길어도 이 요구사항을 준수하고, **오직 최종 완성된 답변**만 출력하십시오.
|
|
|
|
| 1896 |
"""
|
| 1897 |
return base_prompt.strip()
|
| 1898 |
|
|
|
|
| 1899 |
|
| 1900 |
+
# ────────────────────────── Web Search, Kaggle, etc. ─────────────────────────
|
| 1901 |
@st.cache_data(ttl=3600)
|
| 1902 |
def brave_search(query: str, count: int = 20):
|
| 1903 |
if not BRAVE_KEY:
|
| 1904 |
+
raise RuntimeError("⚠️ SERPHOUSE_API_KEY (Brave API Key) is missing.")
|
| 1905 |
headers = {
|
| 1906 |
"Accept": "application/json",
|
| 1907 |
"Accept-Encoding": "gzip",
|
|
|
|
| 1949 |
try:
|
| 1950 |
arts = brave_search(query, 20)
|
| 1951 |
if not arts:
|
| 1952 |
+
logging.warning("No Brave search results. Using fallback.")
|
| 1953 |
return mock_results(query)
|
| 1954 |
hdr = "# Web Search Results\nUse the information below to spark new design/invention insights.\n\n"
|
| 1955 |
body = "\n".join(
|
|
|
|
| 1961 |
logging.error(f"Web search process failed: {str(e)}")
|
| 1962 |
return mock_results(query)
|
| 1963 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1964 |
|
| 1965 |
+
# ────────────────────────── Main processing for design/invention ─────────────────────────
|
| 1966 |
+
|
| 1967 |
def idea_generator_app():
|
| 1968 |
+
# ---------------- Language Setting in Sidebar -----------------
|
| 1969 |
+
if "language" not in st.session_state:
|
| 1970 |
+
st.session_state["language"] = "English" # default
|
| 1971 |
+
|
| 1972 |
+
st.set_page_config(page_title="Ilúvatar: Creative Design & Invention AI", layout="wide")
|
| 1973 |
+
|
| 1974 |
+
st.title("Ilúvatar: Creative Design & Invention AI")
|
| 1975 |
+
st.caption("This system autonomously collects and analyzes big data to propose complex design/invention ideas.")
|
| 1976 |
|
| 1977 |
+
# Default states
|
| 1978 |
default_vals = {
|
| 1979 |
"ai_model": "gpt-4.1-mini",
|
| 1980 |
"messages": [],
|
|
|
|
| 1991 |
st.session_state[k] = v
|
| 1992 |
|
| 1993 |
sb = st.sidebar
|
| 1994 |
+
|
| 1995 |
+
# Language selection
|
| 1996 |
+
language_choice = sb.radio("Select Output Language", ["English", "Korean"], index=0 if st.session_state["language"] == "English" else 1)
|
| 1997 |
+
st.session_state["language"] = language_choice
|
| 1998 |
+
|
| 1999 |
st.session_state.temp = sb.slider(
|
| 2000 |
+
"Diversity Temperature", 0.1, 3.0, 1.3, 0.1,
|
| 2001 |
+
help="0.1 = Very conservative, 3.0 = Highly creative/random"
|
| 2002 |
)
|
| 2003 |
|
| 2004 |
sb.title("Settings")
|
|
|
|
| 2021 |
sb.error("⚠️ KAGGLE_KEY not set.")
|
| 2022 |
st.session_state.kaggle_enabled = False
|
| 2023 |
|
| 2024 |
+
# Example Topics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2025 |
sb.subheader("Example Topics")
|
| 2026 |
c1, c2, c3 = sb.columns(3)
|
| 2027 |
+
if c1.button("Cat Toy Design", key="ex1"):
|
| 2028 |
+
process_example("Cat toy design that includes interactive sensors")
|
| 2029 |
+
if c2.button("Jamming-resistant drone design", key="ex2"):
|
| 2030 |
+
process_example("A drone design resistant to jamming signals, including advanced comm protocols")
|
| 2031 |
+
if c3.button("Wearable UI/UX innovation", key="ex3"):
|
| 2032 |
+
process_example("A wearable device focusing on revolutionary UI/UX concepts")
|
| 2033 |
+
|
| 2034 |
+
# Download chat
|
| 2035 |
latest_ideas = next(
|
| 2036 |
(m["content"] for m in reversed(st.session_state.messages)
|
| 2037 |
if m["role"] == "assistant" and m["content"].strip()),
|
|
|
|
| 2047 |
d2.download_button("Download as HTML", md_to_html(latest_ideas, title),
|
| 2048 |
file_name=f"{title}.html", mime="text/html")
|
| 2049 |
|
| 2050 |
+
# Load/Save chat history
|
| 2051 |
up = sb.file_uploader("Load Conversation (.json)", type=["json"], key="json_uploader")
|
| 2052 |
if up:
|
| 2053 |
try:
|
| 2054 |
st.session_state.messages = json.load(up)
|
| 2055 |
+
sb.success("Conversation history loaded.")
|
| 2056 |
except Exception as e:
|
| 2057 |
sb.error(f"Failed to load: {e}")
|
| 2058 |
|
|
|
|
| 2064 |
mime="application/json"
|
| 2065 |
)
|
| 2066 |
|
| 2067 |
+
# File upload area
|
| 2068 |
st.subheader("File Upload (Optional)")
|
| 2069 |
uploaded_files = st.file_uploader(
|
| 2070 |
"Upload reference files (txt, csv, pdf)",
|
|
|
|
| 2097 |
if idx < len(uploaded_files) - 1:
|
| 2098 |
st.divider()
|
| 2099 |
|
| 2100 |
+
# Display existing messages
|
| 2101 |
skip_idx = st.session_state.get("_skip_dup_idx")
|
| 2102 |
for i, m in enumerate(st.session_state.messages):
|
| 2103 |
if skip_idx is not None and i == skip_idx:
|
|
|
|
| 2108 |
st.image(m["image"], caption=m.get("image_caption", ""))
|
| 2109 |
st.session_state["_skip_dup_idx"] = None
|
| 2110 |
|
| 2111 |
+
# Main chat input
|
| 2112 |
+
prompt = st.chat_input("Need a new design/invention idea? Share your context or goal here!")
|
| 2113 |
if prompt:
|
| 2114 |
process_input(prompt, uploaded_files)
|
| 2115 |
|
| 2116 |
sb.markdown("---")
|
| 2117 |
sb.markdown("Created by [VIDraft](https://discord.gg/openfreeai)")
|
| 2118 |
|
| 2119 |
+
|
| 2120 |
def process_example(topic):
|
| 2121 |
process_input(topic, [])
|
| 2122 |
|
| 2123 |
+
|
| 2124 |
def process_input(prompt: str, uploaded_files):
|
| 2125 |
"""
|
| 2126 |
+
Handle main chat input for design/invention ideas.
|
|
|
|
|
|
|
| 2127 |
"""
|
|
|
|
| 2128 |
if not any(m["role"] == "user" and m["content"] == prompt for m in st.session_state.messages):
|
| 2129 |
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 2130 |
with st.chat_message("user"):
|
|
|
|
| 2136 |
and st.session_state.messages[i + 1]["role"] == "assistant"):
|
| 2137 |
return
|
| 2138 |
|
|
|
|
| 2139 |
with st.chat_message("assistant"):
|
| 2140 |
status = st.status("Preparing to generate invention ideas…")
|
| 2141 |
stream_placeholder = st.empty()
|
|
|
|
| 2147 |
|
| 2148 |
selected_cat = st.session_state.get("category_focus", None)
|
| 2149 |
selected_frameworks = st.session_state.get("selected_frameworks", [])
|
| 2150 |
+
|
| 2151 |
+
# Decide prompt language
|
| 2152 |
+
if st.session_state["language"] == "Korean":
|
| 2153 |
+
sys_prompt = get_idea_system_prompt_kr(
|
| 2154 |
+
selected_category=selected_cat,
|
| 2155 |
+
selected_frameworks=selected_frameworks
|
| 2156 |
+
)
|
| 2157 |
+
else:
|
| 2158 |
+
sys_prompt = get_idea_system_prompt_en(
|
| 2159 |
+
selected_category=selected_cat,
|
| 2160 |
+
selected_frameworks=selected_frameworks
|
| 2161 |
+
)
|
| 2162 |
|
| 2163 |
def category_context(sel):
|
| 2164 |
if sel:
|
|
|
|
| 2171 |
|
| 2172 |
search_content = kaggle_content = file_content = mil_content = None
|
| 2173 |
|
| 2174 |
+
# 1) Web search
|
| 2175 |
if use_web_search:
|
| 2176 |
status.update(label="Searching the web…")
|
| 2177 |
with st.spinner("Searching…"):
|
| 2178 |
search_content = do_web_search(keywords(prompt, top=5))
|
| 2179 |
|
| 2180 |
+
# 2) Kaggle
|
| 2181 |
if use_kaggle and check_kaggle_availability():
|
| 2182 |
+
status.update(label="Kaggle dataset analysis…")
|
| 2183 |
with st.spinner("Searching Kaggle…"):
|
| 2184 |
kaggle_kw = extract_kaggle_search_keywords(prompt)
|
| 2185 |
try:
|
| 2186 |
datasets = search_kaggle_datasets(kaggle_kw)
|
| 2187 |
except Exception as e:
|
| 2188 |
+
logging.warning(f"search_kaggle_datasets error ignored: {e}")
|
| 2189 |
datasets = []
|
| 2190 |
analyses = []
|
| 2191 |
if datasets:
|
| 2192 |
+
status.update(label="Downloading & analyzing datasets…")
|
| 2193 |
for ds in datasets:
|
| 2194 |
try:
|
| 2195 |
ana = download_and_analyze_dataset(ds["ref"])
|
| 2196 |
except Exception as e:
|
| 2197 |
+
logging.error(f"Kaggle analysis error({ds['ref']}) : {e}")
|
| 2198 |
+
ana = f"Error analyzing dataset: {e}"
|
| 2199 |
analyses.append({"meta": ds, "analysis": ana})
|
| 2200 |
if analyses:
|
| 2201 |
kaggle_content = format_kaggle_analysis_markdown_multi(analyses)
|
| 2202 |
|
| 2203 |
+
# 3) Uploaded files
|
| 2204 |
if has_uploaded:
|
| 2205 |
status.update(label="Reading uploaded files…")
|
| 2206 |
with st.spinner("Processing files…"):
|
| 2207 |
file_content = process_uploaded_files(uploaded_files)
|
| 2208 |
|
| 2209 |
+
# 4) Military dataset
|
| 2210 |
if is_military_query(prompt):
|
| 2211 |
+
status.update(label="Searching military tactics…")
|
| 2212 |
+
with st.spinner("Analyzing military dataset…"):
|
| 2213 |
mil_rows = military_search(prompt)
|
| 2214 |
if mil_rows:
|
| 2215 |
mil_content = "# Military Tactics Dataset Reference\n\n"
|
|
|
|
| 2221 |
f"**Defense Reasoning:** {row['defense_reasoning']}\n\n---\n"
|
| 2222 |
)
|
| 2223 |
|
|
|
|
| 2224 |
user_content = prompt
|
| 2225 |
for extra in (search_content, kaggle_content, file_content, mil_content):
|
| 2226 |
if extra:
|
| 2227 |
user_content += "\n\n" + extra
|
| 2228 |
|
| 2229 |
+
status.update(label="Analyzing categories…")
|
|
|
|
| 2230 |
decision_purpose = identify_decision_purpose(prompt)
|
| 2231 |
relevance_scores = compute_relevance_scores(prompt, PHYS_CATEGORIES)
|
| 2232 |
|
| 2233 |
+
status.update(label="Generating category-based ideas…")
|
| 2234 |
T = st.session_state.temp
|
| 2235 |
k_cat_range = (4, 8) if T < 1.0 else (6, 10) if T < 2.0 else (8, 12)
|
| 2236 |
n_item_range = (2, 4) if T < 1.0 else (3, 6) if T < 2.0 else (4, 8)
|
|
|
|
| 2245 |
T=T,
|
| 2246 |
)
|
| 2247 |
|
| 2248 |
+
combos_table = "| Combination | Weight | Impact | Confidence | Score |\n|------|--------|--------|--------|-------|\n"
|
| 2249 |
for w, imp, conf, tot, cmb in combos:
|
| 2250 |
combo_str = " + ".join(f"{c[0]}-{c[1]}" for c in cmb)
|
| 2251 |
combos_table += f"| {combo_str} | {w} | {imp} | {conf:.1f} | {tot} |\n"
|
| 2252 |
|
| 2253 |
+
purpose_info = "\n\n## Design/Invention Goal Analysis\n"
|
| 2254 |
if decision_purpose['purposes']:
|
| 2255 |
+
purpose_info += "### Main Purposes\n"
|
| 2256 |
for p, s in decision_purpose['purposes']:
|
| 2257 |
+
purpose_info += f"- **{p}** (relevance: {s})\n"
|
| 2258 |
if decision_purpose['constraints']:
|
| 2259 |
+
purpose_info += "\n### Constraints\n"
|
| 2260 |
for c, s in decision_purpose['constraints']:
|
| 2261 |
+
purpose_info += f"- **{c}** (relevance: {s})\n"
|
| 2262 |
|
| 2263 |
+
# Additional frameworks
|
| 2264 |
framework_contents = []
|
| 2265 |
for fw in selected_frameworks:
|
| 2266 |
if fw == "swot":
|
|
|
|
| 2277 |
)
|
| 2278 |
|
| 2279 |
if framework_contents:
|
| 2280 |
+
user_content += "\n\n## (Optional) Additional Framework Analysis\n\n" + "\n\n".join(framework_contents)
|
| 2281 |
|
| 2282 |
+
user_content += f"\n\n## Category Matrix Analysis{purpose_info}\n{combos_table}"
|
| 2283 |
|
| 2284 |
+
status.update(label="Generating final ideas…")
|
| 2285 |
|
| 2286 |
api_messages = [
|
| 2287 |
{"role": "system", "content": sys_prompt},
|
|
|
|
| 2289 |
{"role": "user", "content": user_content},
|
| 2290 |
]
|
| 2291 |
|
|
|
|
| 2292 |
@backoff.on_exception(
|
| 2293 |
(RemoteProtocolError, APITimeoutError, APIError), max_tries=3
|
| 2294 |
)
|
|
|
|
| 2302 |
stream=True
|
| 2303 |
)
|
| 2304 |
|
|
|
|
| 2305 |
try:
|
| 2306 |
stream = safe_stream()
|
| 2307 |
for chunk in stream:
|
|
|
|
| 2309 |
full_response += chunk.choices[0].delta.content
|
| 2310 |
stream_placeholder.markdown(full_response + "▌")
|
| 2311 |
except (RemoteProtocolError, APITimeoutError, APIError) as stream_err:
|
| 2312 |
+
logging.warning(f"Streaming failed, fallback to non-stream: {stream_err}")
|
| 2313 |
resp = client.chat.completions.create(
|
| 2314 |
model="gpt-4.1-mini",
|
| 2315 |
messages=api_messages,
|
|
|
|
| 2323 |
|
| 2324 |
status.update(label="Invention ideas created!", state="complete")
|
| 2325 |
|
| 2326 |
+
# Auto image generation
|
| 2327 |
img_data = img_caption = None
|
| 2328 |
if st.session_state.generate_image and full_response:
|
| 2329 |
match = re.search(r"###\s*이미지\s*프롬프트\s*\n+([^\n]+)", full_response, re.I)
|
|
|
|
| 2336 |
if img_data:
|
| 2337 |
st.image(img_data, caption=f"Visualized Concept – {img_caption}")
|
| 2338 |
|
|
|
|
| 2339 |
answer_msg = {"role": "assistant", "content": full_response}
|
| 2340 |
if img_data:
|
| 2341 |
+
answer_msg["image"] = img_data
|
| 2342 |
answer_msg["image_caption"] = img_caption
|
| 2343 |
st.session_state["_skip_dup_idx"] = len(st.session_state.messages)
|
| 2344 |
st.session_state.messages.append(answer_msg)
|
| 2345 |
|
|
|
|
| 2346 |
st.subheader("Download This Output")
|
| 2347 |
col_md, col_html = st.columns(2)
|
| 2348 |
col_md.download_button(
|
|
|
|
| 2365 |
|
| 2366 |
except Exception as e:
|
| 2367 |
logging.error("process_input error", exc_info=True)
|
| 2368 |
+
st.error(f"⚠️ An error occurred: {e}")
|
| 2369 |
st.session_state.messages.append(
|
| 2370 |
+
{"role": "assistant", "content": f"⚠️ Error: {e}"}
|
| 2371 |
)
|
| 2372 |
|
|
|
|
| 2373 |
def main():
|
| 2374 |
idea_generator_app()
|
| 2375 |
|