ginipick commited on
Commit
b834271
·
verified ·
1 Parent(s): 95c306a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +338 -293
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
- 가벼운 지수(backoff=2^n) 재시도 데코레이터.
46
- backoff.on_exception API의 필수 인자만 흉내냅니다.
47
- - exceptions : 재시도 대상 예외(tuple 또는 단일)
48
- - max_tries : kwargs 로 지정(기본 3)
49
- - base : kwargs 로 지정(기본 2, 지수 배수)
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 KAGGLE_KEY 환경변수를 먼저 설정하세요.")
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" # 예시 이미지 생성용 API
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
- """군사/전술 관련 키워드가 등장하면 True ���환"""
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
- mil.parquet의 scenario_description 열과 코사인 유사도 분석하여
134
- query와 가장 유사한 상위 시나리오를 반환
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
- # ─────────────────────────────── Kaggle Datasets ────────────────────────
 
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": "강점 (Strengths)",
804
- "description": "내부적 긍정 요소 - 조직이 가진 경쟁 우위 요소",
805
  "prompt_keywords": ["강점", "장점", "우위", "역량", "자산", "전문성", "strength", "advantage"]
806
  },
807
  "weaknesses": {
808
- "title": "약점 (Weaknesses)",
809
- "description": "내부적 부정 요소 - 개선이 필요한 내부 한계",
810
  "prompt_keywords": ["약점", "단점", "부족", "한계", "취약점", "weakness", "limitation", "deficit"]
811
  },
812
  "opportunities": {
813
- "title": "기회 (Opportunities)",
814
- "description": "외부적 긍정 요소 - 활용 가능한 외부 환경 변화",
815
  "prompt_keywords": ["기회", "가능성", "트렌드", "변화", "성장", "opportunity", "trend", "potential"]
816
  },
817
  "threats": {
818
- "title": "위협 (Threats)",
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": "스타 (Stars)",
855
- "description": "높은 성장률, 높은 시장점유율 - 추가 투자 필요",
856
  "prompt_keywords": ["성장", "점유율", "중점", "투자", "star", "growth", "investment"]
857
  },
858
  "cash_cows": {
859
- "title": "현금젖소 (Cash Cows)",
860
- "description": "낮은 성장률, 높은 시장점유율 - 현금흐름 창출",
861
  "prompt_keywords": ["안정", "수익", "현금", "전통", "cash cow", "profit", "mature"]
862
  },
863
  "question_marks": {
864
- "title": "물음표 (Question Marks)",
865
- "description": "높은 성장률, 낮은 시장점유율 - 선택적 투자/철수",
866
  "prompt_keywords": ["가능성", "위험", "불확실", "잠재", "question mark", "uncertain", "potential"]
867
  },
868
  "dogs": {
869
- "title": "개 (Dogs)",
870
- "description": "낮은 성장률, 낮은 시장점유율 - 철수 고려",
871
  "prompt_keywords": ["회수", "철수", "저성장", "비효율", "dog", "divest", "low growth"]
872
  }
873
  }
874
 
875
  BUSINESS_FRAMEWORKS = {
876
- "sunzi": "손자병법 36",
877
- "swot": "SWOT 분석",
878
- "porter": "Porter 5 Forces",
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 5 Forces 분석 결과',
956
- 'bcg': '# BCG 매트릭스 분석 결과'
957
  }
958
- md = f"{titles.get(framework_type, '# 경영 프레임워크 분석')}\n\n"
959
- md += " 요소별 텍스트 분석 점수와 관련 키워드입니다.\n\n"
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"**관련성 점수**: {info['score']}\n\n"
964
  if info['keywords']:
965
- md += "**관련 키워드 및 컨텍스트**:\n"
966
  for keyword in info['keywords']:
967
  md += f"- *{keyword}*\n"
968
  md += "\n"
969
  else:
970
- md += "관련 키워드가 발견되지 않았습니다.\n\n"
971
  return md
972
 
973
- # ──────────────────────────────── 마크다운 HTML 변환 ─────────────────────────
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"""# 업로드된 텍스트 파일: {uploaded_file.name}
1072
 
1073
  {content}
1074
  """
1075
  except Exception as e:
1076
- logging.error(f"텍스트 파일 처리 오류: {str(e)}")
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"""# 업로드된 CSV 파일: {uploaded_file.name}
1083
 
1084
- ## 기본 정보
1085
- - 수: {df.shape[0]}
1086
- - 수: {df.shape[1]}
1087
- - 이름: {', '.join(df.columns.tolist())}
1088
 
1089
- ## 5 데이터 미리보기
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 파일 처리 오류: {str(e)}")
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"""# 업로드된 PDF 파일: {uploaded_file.name}
1112
 
1113
- ## 기본 정보
1114
- - 페이지 수: {len(reader.pages)}
1115
 
1116
- ## 처음 5개 페이지 내용 미리보기
1117
  {preview_text}
1118
  """
1119
  except Exception as e:
1120
- logging.error(f"PDF 파일 처리 오류: {str(e)}")
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"파일 처리 오류 {file.name}: {str(e)}")
1146
  file_contents.append(f"# Error processing file: {file.name}\n\n{str(e)}")
1147
 
1148
- return "\n\n# 사용자 업로드 파일 분석\n\n" + "\n\n---\n\n".join(file_contents)
 
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
- # ──────────────────────────────── Kaggle API 관련 ───────────────────────
1184
  @st.cache_resource
1185
  def check_kaggle_availability():
1186
  if not KAGGLE_API_KEY:
1187
- logging.warning("Kaggle API 사용할 없습니다. (KAGGLE_KEY 비어 있음)")
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"데이터셋 다운로드 오류: {e}"
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 파싱 오류: {e}"
1253
 
1254
  shutil.rmtree(tmpdir)
1255
  return analysis
1256
 
1257
  def format_kaggle_analysis_markdown_multi(analyses: list[dict]) -> str:
1258
  """
1259
- 여러 Kaggle 데이터셋(최대 3) 메타‧분석 결과를 한꺼번에 마크다운으로 반환
1260
  analyses = [ {"meta": {...}, "analysis": {... or str}}, ... ]
1261
  """
1262
  if not analyses:
1263
- return "# Kaggle 데이터셋\n\n관련 데이터셋을 찾을 수 없습니다.\n\n"
1264
- md = "# Kaggle 데이터셋 분석 결과\n\n"
1265
- md += "다음 데이터셋을 검토하여 아이디어 형성에 참고하세요.\n\n"
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"- **참조** : {ds['ref']}\n"
1272
- md += f"- **URL** : [{ds['url']}]({ds['url']})\n\n"
1273
  if isinstance(ana, dict):
1274
- md += f"**행 × 열** : {ana['shape'][0]} × {ana['shape'][1]}\n\n"
1275
- md += "<details><summary>미리보기 & 통계 (펼치기)</summary>\n\n"
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
- # ──────────────────────────────── OpenAI Client ──────────────────────────
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
- # ──────────────────────────────── Diverse Matrix Generator ────────────────────
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 클수록 noise 1에 가까움
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
- # ──────────────────────────────── PHYS_CATEGORIES ────────────────────────
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
- def get_idea_system_prompt(selected_category: str | None = None,
1713
- selected_frameworks: list | None = None) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # 핵심: "가장 우수한 10가지 아이디어를 아주 상세하게" + "각 아이디어별 이미지 프롬프트" + "출처 제시"
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) **부가 아이디어** 5가지에 포함되지 않은 다음 순서 10개를 자세하게 작성하여 한줄로 라인별로 설명/출력
1751
- - 예: `#### 부가 아이디어 X:\\n한 줄 한글 문구`
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. **부가 아이디어** (TOP 5에 해당하지 않는 10가지 아이디어, 각각 한 줄로 간결하게 설명하되 해당 아이디어의 핵심 가치와 혁신점을 포함)
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 from Brave. Using fallback.")
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
- # ──────────────────────────────── Streamlit 메인 ──────────────────────
 
1926
  def idea_generator_app():
1927
- st.title("Ilúvatar(일루바타르) : Creative Design & Invention AI")
1928
- st.caption(" 시스템은 빅데이터를 자율적으로 수집·분석하여, 복합적인 디자인/발명 아이디어를 제안합니다.")
 
 
 
 
 
 
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 temperature", 0.1, 3.0, 1.3, 0.1,
1948
- help="0.1 = 매우 보수적, 3.0 = 매우 창의/무작위"
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
- # (디자인/발명용 사이드바 항목은 삭제됨, 메인 prompt에서 처리)
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("고양이 장난감", key="ex1"):
1982
- process_example(example_topics["example1"])
1983
- if c2.button("재밍 대응 드론", key="ex2"):
1984
- process_example(example_topics["example2"])
1985
- if c3.button("UI/UX 혁신", key="ex3"):
1986
- process_example(example_topics["example3"])
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 successfully")
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
- sys_prompt = get_idea_system_prompt(
2107
- selected_category=selected_cat,
2108
- selected_frameworks=selected_frameworks
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
- # Kaggle
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 오류 무시: {e}")
2137
  datasets = []
2138
  analyses = []
2139
  if datasets:
2140
- status.update(label="Downloading & analysing datasets…")
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 분석 오류({ds['ref']}) : {e}")
2146
- ana = f"데이터셋 분석 오류: {e}"
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 dataset…")
2160
- with st.spinner("Loading military insights…"):
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 = "| 조합 | 가중치 | 영향도 | 신뢰도 | 총점 |\n|------|--------|--------|--------|-----|\n"
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## 디자인/발명 목표 분석\n"
2204
  if decision_purpose['purposes']:
2205
- purpose_info += "### 핵심 목적\n"
2206
  for p, s in decision_purpose['purposes']:
2207
- purpose_info += f"- **{p}** (관련성: {s})\n"
2208
  if decision_purpose['constraints']:
2209
- purpose_info += "\n### 제약 조건\n"
2210
  for c, s in decision_purpose['constraints']:
2211
- purpose_info += f"- **{c}** (관련성: {s})\n"
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) 기타 프레임워크 분석\n\n" + "\n\n".join(framework_contents)
2231
 
2232
- user_content += f"\n\n## 카테고리 매트릭스 분석{purpose_info}\n{combos_table}"
2233
 
2234
- status.update(label="Generating final design/invention ideas…")
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"스트리밍 실패, non-stream 폴백: {stream_err}")
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"] = img_data
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"⚠️ 작업 오류가 발생했습니다: {e}")
2323
  st.session_state.messages.append(
2324
- {"role": "assistant", "content": f"⚠️ 오류: {e}"}
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