Spaces:
Running
Running
| # ──────────────────────────────── Imports ──────────────────────────────── | |
| import os, json, re, logging, requests, markdown, time, io | |
| from datetime import datetime | |
| import streamlit as st | |
| st.set_page_config(layout="wide") | |
| from openai import OpenAI # OpenAI 라이브러리 | |
| from gradio_client import Client | |
| import pandas as pd | |
| import PyPDF2 # For handling PDF files | |
| # ──────────────────────────────── Environment Variables / Constants ───────────────────────── | |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") | |
| BRAVE_KEY = os.getenv("SERPHOUSE_API_KEY", "") # Keep this name | |
| BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search" | |
| IMAGE_API_URL = "http://211.233.58.201:7896" # 이미지 생성용 API | |
| MAX_TOKENS = 7999 | |
| # ──────────────────────────────── Physical Transformation Categories (KR & EN) ───────────────── | |
| physical_transformation_categories = { | |
| "센서 기능": [ | |
| "시각 센서/감지", "청각 센서/감지", "촉각 센서/감지", "미각 센서/감지", "후각 센서/감지", | |
| "온도 센서/감지", "습도 센서/감지", "압력 센서/감지", "가속도 센서/감지", "회전 센서/감지", | |
| "근접 센서/감지", "위치 센서/감지", "운동 센서/감지", "가스 센서/감지", "적외선 센서/감지", | |
| "자외선 센서/감지", "방사선 센서/감지", "자기장 센서/감지", "전기장 센서/감지", "화학물질 센서/감지", | |
| "생체신호 센서/감지", "진동 센서/감지", "소음 센서/감지", "빛 세기 센서/감지", "빛 파장 센서/감지", | |
| "기울기 센서/감지", "pH 센서/감지", "전류 센서/감지", "전압 센서/감지", "이미지 센서/감지", | |
| "거리 센서/감지", "깊이 센서/감지", "중력 센서/감지", "속도 센서/감지", "흐름 센서/감지", | |
| "수위 센서/감지", "탁도 센서/감지", "염도 센서/감지", "금속 감지", "압전 센서/감지", | |
| "광전 센서/감지", "열전대 센서/감지", "홀 효과 센서/감지", "초음파 센서/감지", "레이더 센서/감지", | |
| "라이다 센서/감지", "터치 센서/감지", "제스처 센서/감지", "심박 센서/감지", "혈압 센서/감지" | |
| ], | |
| "크기와 형태 변화": [ | |
| "부피 늘어남/줄어듦", "길이 늘어남/줄어듦", "너비 늘어남/줄어듦", "높이 늘어남/줄어듦", | |
| "밀도 변화", "무게 증가/감소", "모양 변형", "상태 변화", "불균등 변형", | |
| "복잡한 형태 변형", "비틀림/꼬임", "불균일한 확장/축소", "모서리 둥글게/날카롭게", | |
| "깨짐/갈라짐", "여러 조각 나눠짐", "물 저항", "먼지 저항", "찌그러짐/복원", | |
| "접힘/펼쳐짐", "압착/팽창", "늘어남/수축", "구겨짐/평평해짐", "뭉개짐/단단해짐", | |
| "말림/펴짐", "꺾임/구부러짐" | |
| ], | |
| "표면 및 외관 변화": [ | |
| "색상 변화", "질감 변화", "투명/불투명 변화", "반짝임/무광 변화", | |
| "빛 반사 정도 변화", "무늬 변화", "각도에 따른 색상 변화", "빛에 따른 색상 변화", | |
| "온도에 따른 색상 변화", "홀로그램 효과", "표면 각도별 빛 반사", "표면 모양 변형", | |
| "초미세 표면 구조 변화", "자가 세정 효과", "얼룩/패턴 생성", "흐림/선명함 변화", | |
| "광택/윤기 변화", "색조/채도 변화", "발광/형광", "빛 산란 효과", | |
| "빛 흡수 변화", "반투명 효과", "그림자 효과 변화", "자외선 반응 변화", | |
| "야광 효과" | |
| ], | |
| "물질의 상태 변화": [ | |
| "고체/액체/기체 전환", "결정화/용해", "산화/부식", "딱딱해짐/부드러워짐", | |
| "특수 상태 전환", "무정형/결정형 전환", "성분 분리", "미세 입자 형성/분해", | |
| "젤 형성/풀어짐", "준안정 상태 변화", "분자 자가 정렬/분해", "상태변화 지연 현상", | |
| "녹음", "굳음", "증발/응축", "승화/증착", "침전/부유", "분산/응집", | |
| "건조/습윤", "팽윤/수축", "동결/해동", "풍화/침식", "충전/방전", | |
| "결합/분리", "발효/부패" | |
| ], | |
| "움직임 특성 변화": [ | |
| "가속/감속", "일정 속도 유지", "진동/진동 감소", "부딪힘/튕김", | |
| "회전 속도 증가/감소", "회전 방향 변화", "불규칙 움직임", "멈췄다 미끄러지는 현상", | |
| "공진/반공진", "유체 속 저항/양력 변화", "움직임 저항 변화", "복합 진동 움직임", | |
| "특수 유체 속 움직임", "회전-이동 연계 움직임", "관성 정지", "충격 흡수", | |
| "충격 전달", "운동량 보존", "마찰력 변화", "관성 탈출", "불안정 균형", | |
| "동적 안정성", "흔들림 감쇠", "경로 예측성", "회피 움직임" | |
| ], | |
| "구조적 변화": [ | |
| "부품 추가/제거", "조립/분해", "접기/펴기", "변형/원상복구", "최적 구조 변화", | |
| "자가 재배열", "자연 패턴 형성/소멸", "규칙적 패턴 변화", "모듈식 변형", | |
| "복잡성 증가 구조", "원래 모양 기억 효과", "시간에 따른 형태 변화", "부분 제거", | |
| "부분 교체", "결합", "분리", "분할/통합", "중첩/겹침", "내부 구조 변화", | |
| "외부 구조 변화", "중심축 이동", "균형점 변화", "계층 구조 변화", "지지 구조 변화", | |
| "응력 분산 구조", "충격 흡수 구조", "그리드/매트릭스 구조 변화", "상호 연결성 변화" | |
| ], | |
| "공간 이동": [ | |
| "앞/뒤 이동", "좌/우 이동", "위/아래 이동", "세로축 회전(고개 끄덕임)", | |
| "가로축 회전(고개 젓기)", "길이축 회전(옆으로 기울임)", "원 운동", "나선형 이동", | |
| "관성에 의한 미끄러짐", "회전축 변화", "불규칙 회전", "흔들림 운동", "포물선 이동", | |
| "무중력 부유", "수면 위 부유", "점프/도약", "슬라이딩", "롤링", "자유 낙하", | |
| "왕복 운동", "탄성 튕김", "관통", "회피 움직임", "지그재그 이동", "스윙 운동" | |
| ], | |
| "시간 관련 변화": [ | |
| "노화/풍화", "마모/부식", "색 바램/변색", "손상/회복", "수명 주기 변화", | |
| "사용자 상호작용에 따른 적응", "학습 기반 형태 최적화", "시간에 따른 물성 변화", | |
| "집단 기억 효과", "문화적 의미 변화", "지연 반응", "이전 상태 의존 변화", | |
| "점진적 시간 변화", "진화적 변화", "주기적 재생", "계절 변화 적응", | |
| "생체리듬 변화", "생애 주기 단계", "성장/퇴화", "자가 복구/재생", | |
| "자연 순환 적응", "지속성/일시성", "기억 효과", "지연된 작용", "누적 효과" | |
| ], | |
| "빛과 시각 효과": [ | |
| "발광/소등", "빛 투과/차단", "빛 산란/집중", "색상 스펙트럼 변화", "빛 회절", | |
| "빛 간섭", "홀로그램 생성", "레이저 효과", "빛 편광", "형광/인광", | |
| "자외선/적외선 발광", "광학적 착시", "빛 굴절", "그림자 생성/제거", | |
| "색수차 효과", "무지개 효과", "글로우 효과", "플래시 효과", "조명 패턴", | |
| "빔 효과", "광 필터 효과", "빛의 방향성 변화", "투영 효과", "빛 감지/반응", | |
| "광도 변화" | |
| ], | |
| "소리와 진동 효과": [ | |
| "소리 발생/소멸", "음 높낮이 변화", "음량 변화", "음색 변화", | |
| "공명/반공명", "음향 진동", "초음파/저음파 발생", "소리 집중/분산", | |
| "음향 반사/흡수", "음향 도플러 효과", "음파 간섭", "음향 공진", | |
| "진동 패턴 변화", "타악 효과", "음향 피드백", "음향 차폐/증폭", | |
| "소리 지향성", "소리 왜곡", "비트 생성", "배음 생성", "주파수 변조", | |
| "음향 충격파", "음향 필터링" | |
| ], | |
| "열 관련 변화": [ | |
| "온도 상승/하강", "열 팽창/수축", "열 전달/차단", "압력 상승/하강", | |
| "열 변화에 따른 자화", "엔트로피 변화", "열전기 효과", "자기장에 의한 열 변화", | |
| "상태 변화 중 열 저장/방출", "열 스트레스 발생/해소", "급격한 온도 변화 영향", | |
| "복사 냉각/가열", "발열/흡열", "열 분포 변화", "열 반사/흡수", | |
| "냉각 응축", "열 활성화", "열 변색", "열 팽창 계수 변화", "열 안정성 변화", | |
| "내열성/내한성", "자가 발열", "열적 평형/불균형", "열적 변형", "열 분산/집중" | |
| ], | |
| "전기 및 자기 변화": [ | |
| "자성 생성/소멸", "전하량 증가/감소", "전기장 생성/소멸", "자기장 생성/소멸", | |
| "초전도 상태 전환", "강유전체 특성 변화", "양자 상태 변화", "플라즈마 형성/소멸", | |
| "스핀파 전달", "빛에 의한 전기 발생", "압력에 의한 전기 발생", "자기장 내 전류 변화", | |
| "전기 저항 변화", "전기 전도성 변화", "정전기 발생/방전", "전자기 유도", | |
| "전자기파 방출/흡수", "전기 용량 변화", "자기 이력 현상", "전기적 분극", | |
| "전자 흐름 방향 변화", "전기적 공명", "전기적 차폐/노출", "자기 차폐/노출", | |
| "자기장 정렬" | |
| ], | |
| "화학적 변화": [ | |
| "표면 코팅 변화", "물질 성분 변화", "화학 반응 변화", "촉매 작용 시작/중단", | |
| "빛에 의한 화학 반응", "전기에 의한 화학 반응", "단분자막 형성", "분자 수준 구조 변화", | |
| "생체 모방 표면 변화", "환경 반응형 물질 변화", "주기적 화학 반응", "산화", "환원", | |
| "고분자화", "물 분해", "화합", "방사선 영향", "산-염기 반응", "중화 반응", | |
| "이온화", "화학적 흡착/탈착", "촉매 효율 변화", "효소 활성 변화", "발색 반응", | |
| "pH 변화", "화학적 평형 이동", "결합 형성/분해", "용해도 변화" | |
| ], | |
| "생물학적 변화": [ | |
| "성장/위축", "세포 분열/사멸", "생물 발광", "신진대사 변화", "면역 반응", | |
| "호르몬 분비", "신경 반응", "유전적 발현", "적응/진화", "생체리듬 변화", | |
| "재생/치유", "노화/성숙", "생체 모방 변화", "바이오필름 형성", "생물학적 분해", | |
| "효소 활성화/비활성화", "생물학적 신호 전달", "스트레스 반응", "체온 조절", "생물학적 시계 변화", | |
| "세포외 기질 변화", "생체 역학적 반응", "세포 운동성", "세포 극성 변화", "영양 상태 변화" | |
| ], | |
| "환경 상호작용": [ | |
| "온도 반응", "습도 반응", "기압 반응", "중력 반응", "자기장 반응", | |
| "빛 반응", "소리 반응", "화학 물질 감지", "기계적 자극 감지", "전기 자극 반응", | |
| "방사선 반응", "진동 감지", "pH 반응", "용매 반응", "기체 교환", | |
| "환경 오염 반응", "날씨 반응", "계절 반응", "일주기 반응", "생태계 상호작용", | |
| "공생/경쟁 반응", "포식/피식 관계", "군집 형성", "영역 설정", "이주/정착 패턴" | |
| ], | |
| "비즈니스 아이디어": [ | |
| "시장 재정의/신규 시장 개척", | |
| "비즈니스 모델 혁신/디지털 전환", | |
| "고객 경험 혁신/서비스 혁신", | |
| "협력 및 파트너십 강화/생태계 구축", | |
| "글로벌 확장/지역화 전략", | |
| "운영 효율성 증대/원가 절감", | |
| "브랜드 리포지셔닝/이미지 전환", | |
| "지속 가능한 성장/사회적 가치 창출", | |
| "데이터 기반 의사결정/AI 도입", | |
| "신기술 융합/혁신 투자" | |
| ] | |
| } | |
| physical_transformation_categories_en = { | |
| "Sensor Functions": [ | |
| "Visual sensor/detection", "Auditory sensor/detection", "Tactile sensor/detection", "Taste sensor/detection", "Olfactory sensor/detection", | |
| "Temperature sensor/detection", "Humidity sensor/detection", "Pressure sensor/detection", "Acceleration sensor/detection", "Rotational sensor/detection", | |
| "Proximity sensor/detection", "Position sensor/detection", "Motion sensor/detection", "Gas sensor/detection", "Infrared sensor/detection", | |
| "Ultraviolet sensor/detection", "Radiation sensor/detection", "Magnetic sensor/detection", "Electric field sensor/detection", "Chemical sensor/detection", | |
| "Biosignal sensor/detection", "Vibration sensor/detection", "Noise sensor/detection", "Light intensity sensor/detection", "Light wavelength sensor/detection", | |
| "Tilt sensor/detection", "pH sensor/detection", "Current sensor/detection", "Voltage sensor/detection", "Image sensor/detection", | |
| "Distance sensor/detection", "Depth sensor/detection", "Gravity sensor/detection", "Speed sensor/detection", "Flow sensor/detection", | |
| "Water level sensor/detection", "Turbidity sensor/detection", "Salinity sensor/detection", "Metal detection", "Piezoelectric sensor/detection", | |
| "Photovoltaic sensor/detection", "Thermocouple sensor/detection", "Hall effect sensor/detection", "Ultrasonic sensor/detection", "Radar sensor/detection", | |
| "Lidar sensor/detection", "Touch sensor/detection", "Gesture sensor/detection", "Heart rate sensor/detection", "Blood pressure sensor/detection" | |
| ], | |
| "Size and Shape Change": [ | |
| "Volume increase/decrease", "Length increase/decrease", "Width increase/decrease", "Height increase/decrease", | |
| "Density change", "Weight increase/decrease", "Shape deformation", "State change", "Uneven deformation", | |
| "Complex shape deformation", "Twisting/entwining", "Non-uniform expansion/contraction", "Rounded/sharpened edges", | |
| "Cracking/splitting", "Fragmentation", "Water resistance", "Dust resistance", "Denting/recovery", | |
| "Folding/unfolding", "Compression/expansion", "Stretching/contraction", "Wrinkling/flattening", "Crushing/hardening", | |
| "Rolling/unrolling", "Bending/curving" | |
| ], | |
| "Surface and Appearance Change": [ | |
| "Color change", "Texture change", "Transparency change", "Glossy/matte change", | |
| "Light reflection variation", "Pattern change", "Angle-dependent color change", "Light-induced color change", | |
| "Temperature-dependent color change", "Holographic effect", "Angle-specific light reflection", "Surface shape alteration", | |
| "Nano-scale surface structure change", "Self-cleaning effect", "Stain/pattern formation", "Blurriness/clarity change", | |
| "Luster/shine change", "Hue/saturation change", "Luminescence/fluorescence", "Light scattering effect", | |
| "Light absorption change", "Translucency effect", "Shadow effect change", "UV response change", | |
| "Glow effect" | |
| ], | |
| "Material State Change": [ | |
| "Solid/liquid/gas transition", "Crystallization/dissolution", "Oxidation/corrosion", "Hardening/softening", | |
| "Special state transition", "Amorphous/crystalline transition", "Component separation", "Particle formation/disintegration", | |
| "Gel formation/dissolution", "Metastable state change", "Molecular self-assembly/disintegration", "Delayed state change", | |
| "Melting", "Solidification", "Evaporation/condensation", "Sublimation/deposition", "Precipitation/suspension", "Dispersion/aggregation", | |
| "Drying/moistening", "Swelling/shrinkage", "Freezing/thawing", "Weathering/erosion", "Charging/discharging", | |
| "Bonding/separation", "Fermentation/decay" | |
| ], | |
| "Movement Characteristics Change": [ | |
| "Acceleration/deceleration", "Maintaining constant speed", "Vibration/vibration reduction", "Collision/bouncing", | |
| "Increase/decrease in rotational speed", "Change in rotational direction", "Irregular movement", "Stop-and-slide phenomenon", | |
| "Resonance/anti-resonance", "Resistance/lift change in fluid", "Change in movement resistance", "Complex vibrational movement", | |
| "Movement in special fluid", "Rotational-translational movement", "Inertial stoppage", "Shock absorption", | |
| "Shock transfer", "Conservation of momentum", "Friction change", "Overcoming inertia", "Unstable equilibrium", | |
| "Dynamic stability", "Damping of oscillation", "Path predictability", "Evasive movement" | |
| ], | |
| "Structural Change": [ | |
| "Addition/removal of components", "Assembly/disassembly", "Folding/unfolding", "Deformation/recovery", "Optimal structural change", | |
| "Self-rearrangement", "Natural pattern formation/disappearance", "Regular pattern change", "Modular transformation", | |
| "Increased structural complexity", "Memory of original shape effect", "Shape change over time", "Partial removal", | |
| "Partial replacement", "Bonding", "Separation", "Division/integration", "Overlaying", "Internal structure change", | |
| "External structure change", "Shift of center axis", "Balance point change", "Hierarchical structure change", "Support structure change", | |
| "Stress distribution structure", "Shock absorption structure", "Grid/matrix structure change", "Interconnectivity change" | |
| ], | |
| "Spatial Movement": [ | |
| "Forward/backward movement", "Left/right movement", "Up/down movement", "Vertical axis rotation (nodding)", | |
| "Horizontal axis rotation (shaking head)", "Longitudinal axis rotation (tilting sideways)", "Circular motion", "Spiral movement", | |
| "Slipping due to inertia", "Change of rotation axis", "Irregular rotation", "Shaking movement", "Parabolic motion", | |
| "Zero-gravity floating", "Floating on water surface", "Jump/leap", "Sliding", "Rolling", "Free fall", | |
| "Reciprocating motion", "Elastic bouncing", "Penetration", "Evasive movement", "Zigzag movement", "Swinging movement" | |
| ], | |
| "Time-Related Change": [ | |
| "Aging/weathering", "Wear/corrosion", "Fading/discoloration", "Damage/recovery", "Lifecycle change", | |
| "Adaptation through user interaction", "Learning-based shape optimization", "Property change over time", | |
| "Collective memory effect", "Cultural significance change", "Delayed response", "History-dependent change", | |
| "Gradual time change", "Evolutionary change", "Periodic regeneration", "Seasonal adaptation", | |
| "Circadian rhythm change", "Lifecycle stage", "Growth/decline", "Self-repair/regeneration", | |
| "Natural cycle adaptation", "Persistence/transience", "Memory effect", "Delayed effect", "Cumulative effect" | |
| ], | |
| "Light and Visual Effects": [ | |
| "Illumination/shutdown", "Light transmission/blocking", "Light scattering/concentration", "Color spectrum change", "Light diffraction", | |
| "Light interference", "Hologram creation", "Laser effect", "Light polarization", "Fluorescence/phosphorescence", | |
| "UV/IR emission", "Optical illusion", "Light refraction", "Shadow creation/removal", | |
| "Chromatic aberration", "Rainbow effect", "Glow effect", "Flash effect", "Lighting pattern", | |
| "Beam effect", "Light filter effect", "Change in light direction", "Projection effect", "Light detection/response", | |
| "Luminance change" | |
| ], | |
| "Sound and Vibration Effects": [ | |
| "Sound generation/cessation", "Pitch change", "Volume change", "Timbre change", | |
| "Resonance/antiresonance", "Acoustic vibration", "Ultrasonic/infrasonic emission", "Sound concentration/distribution", | |
| "Sound reflection/absorption", "Acoustic Doppler effect", "Sound wave interference", "Acoustic resonance", | |
| "Vibration pattern change", "Percussive effect", "Audio feedback", "Sound shielding/amplification", | |
| "Directional sound", "Sound distortion", "Beat generation", "Harmonics generation", "Frequency modulation", | |
| "Acoustic shockwave", "Sound filtering" | |
| ], | |
| "Thermal Changes": [ | |
| "Temperature rise/fall", "Thermal expansion/contraction", "Heat transfer/blocking", "Pressure increase/decrease", | |
| "Magnetization due to heat change", "Entropy change", "Thermoelectric effect", "Magnetic-induced thermal change", | |
| "Heat storage/release during phase change", "Thermal stress buildup/release", "Impact of rapid temperature change", | |
| "Radiative cooling/heating", "Exothermic/endothermic", "Heat distribution change", "Heat reflection/absorption", | |
| "Cooling condensation", "Thermal activation", "Thermal discoloration", "Coefficient of thermal expansion change", "Thermal stability change", | |
| "Heat resistance/cold resistance", "Self-heating", "Thermal equilibrium/imbalance", "Thermal deformation", "Heat dispersion/concentration" | |
| ], | |
| "Electrical and Magnetic Changes": [ | |
| "Magnetism creation/cessation", "Charge increase/decrease", "Electric field creation/cessation", "Magnetic field creation/cessation", | |
| "Superconducting transition", "Ferroelectric property change", "Quantum state change", "Plasma formation/cessation", | |
| "Spin wave transmission", "Electricity generation by light", "Electricity generation by pressure", "Current change in magnetic field", | |
| "Electrical resistance change", "Electrical conductivity change", "Static electricity generation/discharge", "Electromagnetic induction", | |
| "Electromagnetic wave emission/absorption", "Capacitance change", "Magnetic hysteresis", "Electrical polarization", | |
| "Electron flow direction change", "Electrical resonance", "Electrical shielding/exposure", "Magnetic shielding/exposure", | |
| "Magnetic field alignment" | |
| ], | |
| "Chemical Change": [ | |
| "Surface coating change", "Material composition change", "Chemical reaction change", "Catalytic action start/stop", | |
| "Light-induced chemical reaction", "Electricity-induced chemical reaction", "Monolayer formation", "Molecular-level structural change", | |
| "Biomimetic surface change", "Environmentally responsive material change", "Periodic chemical reaction", "Oxidation", "Reduction", | |
| "Polymerization", "Water splitting", "Compound formation", "Radiation effects", "Acid-base reaction", "Neutralization reaction", | |
| "Ionization", "Chemical adsorption/desorption", "Catalytic efficiency change", "Enzyme activity change", "Colorimetric reaction", | |
| "pH change", "Chemical equilibrium shift", "Bond formation/breakage", "Solubility change" | |
| ], | |
| "Biological Change": [ | |
| "Growth/shrinkage", "Cell division/death", "Bioluminescence", "Metabolic change", "Immune response", | |
| "Hormone secretion", "Neural response", "Genetic expression", "Adaptation/evolution", "Circadian rhythm change", | |
| "Regeneration/healing", "Aging/maturation", "Biomimetic change", "Biofilm formation", "Biological degradation", | |
| "Enzyme activation/inactivation", "Biological signaling", "Stress response", "Thermoregulation", "Biological clock change", | |
| "Extracellular matrix change", "Biomechanical response", "Cell motility", "Cell polarity change", "Nutritional status change" | |
| ], | |
| "Environmental Interaction": [ | |
| "Temperature response", "Humidity response", "Pressure response", "Gravity response", "Magnetic field response", | |
| "Light response", "Sound response", "Chemical detection", "Mechanical stimulus detection", "Electrical stimulus response", | |
| "Radiation response", "Vibration detection", "pH response", "Solvent response", "Gas exchange", | |
| "Pollution response", "Weather response", "Seasonal response", "Circadian response", "Ecosystem interaction", | |
| "Symbiotic/competitive interaction", "Predator/prey relationship", "Swarm formation", "Territorial behavior", "Migration/settlement pattern" | |
| ], | |
| "Business Ideas": [ | |
| "Market redefinition / New market creation", | |
| "Business model innovation / Digital transformation", | |
| "Customer experience innovation / Service innovation", | |
| "Strengthened collaboration and partnerships / Ecosystem building", | |
| "Global expansion / Localization strategy", | |
| "Increased operational efficiency / Cost reduction", | |
| "Brand repositioning / Image transformation", | |
| "Sustainable growth / Social value creation", | |
| "Data-driven decision making / AI adoption", | |
| "Convergence of new technologies / Innovative investments" | |
| ] | |
| } | |
| # ──────────────────────────────── Logging ──────────────────────────────── | |
| logging.basicConfig(level=logging.INFO, | |
| format="%(asctime)s - %(levelname)s - %(message)s") | |
| # ──────────────────────────────── OpenAI Client ────────────────────────── | |
| def get_openai_client(): | |
| """Create an OpenAI client with timeout and retry settings.""" | |
| if not OPENAI_API_KEY: | |
| raise RuntimeError("⚠️ OPENAI_API_KEY 환경 변수가 설정되지 않았습니다.") | |
| return OpenAI( | |
| api_key=OPENAI_API_KEY, | |
| timeout=60.0, # 타임아웃 60초로 설정 | |
| max_retries=3 # 재시도 횟수 3회로 설정 | |
| ) | |
| # ──────────────────────────────── New System Prompt for Idea Generation ───────────────────── | |
| # ───────────────── System Prompt (REPORT STYLE) ───────────────── | |
| def get_idea_system_prompt(selected_category: str | None = None) -> str: | |
| """ | |
| 새로운 CCM(크로스 카테고리 매트릭스) 방법론을 적용한 시스템 프롬프트 | |
| """ | |
| cat_clause = ( | |
| f'\n**추가 지침**: 선택된 카테고리 "{selected_category}"에 특별한 주의를 기울이십시오. ' | |
| f'이 카테고리의 항목들을 2단계와 3단계 모두에서 우선적으로 고려하십시오.\n' | |
| ) if selected_category else "" | |
| prompt = f""" | |
| 반드시 한글(한국어)로 답변하라. 당신은 혁신 컨설턴트로서 CCM(크로스 카테고리 매트릭스) 방법론을 활용하여 창의적 아이디어를 도출합니다. | |
| ### CCM 방법론 프로세스 | |
| 다음 단계를 통해 체계적으로 아이디어를 도출하십시오: | |
| 1. **문제 분석 및 지식 베이스 구축** | |
| - 입력된 프롬프트의 요구사항을 명확히 정의하고 핵심 니즈 추출 | |
| - 웹검색 결과를 분석하여 관련 트렌드, 기술, 사례 파악 | |
| - 문제 해결에 필요한 핵심 기능과 속성 식별 | |
| 2. **카테고리별 적합성 매핑** | |
| - 모든 카테고리에서 문제 해결에 적합한 항목들을 식별 | |
| - 각 카테고리별로 최소 1개 이상의 관련 항목 선정 | |
| - 선정 항목과 문제 간의 연결성 설명 | |
| 3. **종합 매트릭스 생성 및 통합 솔루션** | |
| - 모든 카테고리에서 가장 적합한 항목을 선택하여 총체적 접근 | |
| - 선정된 각 항목의 구체적 적용 방식과 필요성 서술 | |
| - 모든 항목을 조합하여 통합적 솔루션 도출 | |
| ### 출력 형식 | |
| 최종 보고서를 다음 구조로 마크다운 형식으로 작성하십시오: | |
| ## 1️⃣ 문제 분석 및 지식 베이스 | |
| - **문제 정의**: [프롬프트 핵심 요구사항 요약] | |
| - **핵심 니즈**: [불릿 3-5개로 주요 니즈 리스트] | |
| - **현황 분석**: [웹검색이나 배경지식 기반 현재 상황 요약] | |
| ## 2️⃣ 카테고리별 적합성 매핑 | |
| [각 카테고리별로 가장 적합한 항목들을 표 형식으로 정리] | |
| | 카테고리 | 적합 항목 | 연결성 | | |
| |---------|----------|--------| | |
| | [카테고리1] | [선정 항목] | [문제와의 연결성 설명] | | |
| | [카테고리2] | [선정 항목] | [문제와의 연결성 설명] | | |
| (모든 카테고리에 대해 각각 작성) | |
| ## 3️⃣ CCM 통합 솔루션 | |
| ### 선정 카테고리/항목 및 적용 방안 | |
| [모든 카테고리를 활용한 종합적 접근] | |
| | 카테고리 | 선정 항목 | 구체적 적용 방안 | 필요성 및 장점 | | |
| |---------|----------|-----------------|--------------| | |
| | [카테고리1] | [항목1] | [구체적 적용 사례와 방식 설명] | [이 항목이 필요한 이유와 가져오는 장점] | | |
| | [카테고리2] | [항목2] | [구체적 적용 사례와 방식 설명] | [이 항목이 필요한 이유와 가져오는 장점] | | |
| ... | |
| ### 최종 아이디어: [아이디어 제목] | |
| - **개념 요약**: [2-3문장으로 핵심 아이디어 설명] | |
| - **상세 설명**: [3-4 문단으로 아이디어 구체화] | |
| - **구현 방식**: [아이디어 실현을 위한 단계/방법] | |
| - **차별화 요소**: [선정된 항목들이 어떻게 시너지를 내는지 자세히 설명] | |
| ### 사용자 시나리오 및 사례 | |
| [최소 2개의 구체적인 사용자 시나리오를 통해 아이디어가 실제로 어떻게 작동하는지 생생하게 묘사. 각 시나리오는 문제 상황, 사용자 행동, 솔루션 적용, 결과를 포함] | |
| ### 기대 효과 | |
| - [불릿 포인트로 3-5개 주요 기대효과 나열] | |
| ### 한계 및 도전 과제 | |
| - [불릿 포인트로 2-3개 잠재적 한계/도전 제시] | |
| ### 이미지 프롬프트 | |
| [아이디어를 시각화할 수 있는 영문 이미지 프롬프트 1줄] | |
| {cat_clause} | |
| Step-by-step 사고 과정을 따르되, 출력에는 최종 보고서만 표시하십시오. 각 카테고리 항목의 설명은 단순한 개요가 아닌, 즉시 연상되고 생생하게 묘사되는 형태로 구체적이고 실질적인 적용 예시와 함께 제시하십시오. | |
| """ | |
| return prompt.strip() | |
| # ──────────────────────────────── Brave Search API ──────────────────────── | |
| def brave_search(query: str, count: int = 20): | |
| """ | |
| Call the Brave Web Search API → list[dict] | |
| Returns fields: index, title, link, snippet, displayed_link | |
| """ | |
| if not BRAVE_KEY: | |
| raise RuntimeError("⚠️ SERPHOUSE_API_KEY (Brave API Key) 환경 변수가 비어있습니다.") | |
| headers = { | |
| "Accept": "application/json", | |
| "Accept-Encoding": "gzip", | |
| "X-Subscription-Token": BRAVE_KEY | |
| } | |
| params = {"q": query, "count": str(count)} | |
| for attempt in range(3): | |
| try: | |
| r = requests.get(BRAVE_ENDPOINT, headers=headers, params=params, timeout=15) | |
| r.raise_for_status() | |
| data = r.json() | |
| logging.info(f"Brave search result data structure: {list(data.keys())}") | |
| raw = data.get("web", {}).get("results") or data.get("results", []) | |
| if not raw: | |
| logging.warning(f"No Brave search results found. Response: {data}") | |
| raise ValueError("No search results found.") | |
| arts = [] | |
| for i, res in enumerate(raw[:count], 1): | |
| url = res.get("url", res.get("link", "")) | |
| host = re.sub(r"https?://(www\.)?", "", url).split("/")[0] | |
| arts.append({ | |
| "index": i, | |
| "title": res.get("title", "No title"), | |
| "link": url, | |
| "snippet": res.get("description", res.get("text", "No snippet")), | |
| "displayed_link": host | |
| }) | |
| logging.info(f"Brave search success: {len(arts)} results") | |
| return arts | |
| except Exception as e: | |
| logging.error(f"Brave search failure (attempt {attempt+1}/3): {e}") | |
| if attempt < 2: | |
| time.sleep(2) | |
| return [] | |
| def mock_results(query: str) -> str: | |
| """Fallback if search API fails""" | |
| ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| return (f"# Fallback Search Content (Generated: {ts})\n\n" | |
| f"The web search API request failed. Please generate the ideas based on general knowledge about '{query}'.\n\n" | |
| f"You may consider aspects such as:\n\n" | |
| f"- Basic definition or concept of {query}\n" | |
| f"- Commonly known facts or challenges\n" | |
| f"- Potential categories from the transformation list\n\n" | |
| f"Note: This is fallback guidance, not real-time data.\n\n") | |
| def do_web_search(query: str) -> str: | |
| """Perform web search and format the results.""" | |
| try: | |
| arts = brave_search(query, 20) | |
| if not arts: | |
| logging.warning("No search results, using fallback content") | |
| return mock_results(query) | |
| hdr = "# Web Search Results\nUse the information below to inspire or validate your ideas.\n\n" | |
| body = "\n".join( | |
| f"### Result {a['index']}: {a['title']}\n\n{a['snippet']}\n\n" | |
| f"**Source**: [{a['displayed_link']}]({a['link']})\n\n---\n" | |
| for a in arts | |
| ) | |
| return hdr + body | |
| except Exception as e: | |
| logging.error(f"Web search process failed: {str(e)}") | |
| return mock_results(query) | |
| # ──────────────────────────────── File Upload Handling ───────────────────── | |
| def process_text_file(file): | |
| """Handle text file""" | |
| try: | |
| content = file.read() | |
| file.seek(0) | |
| text = content.decode('utf-8', errors='ignore') | |
| if len(text) > 10000: | |
| text = text[:9700] + "...(truncated)..." | |
| result = f"## Text File: {file.name}\n\n" | |
| result += text | |
| return result | |
| except Exception as e: | |
| logging.error(f"Error processing text file: {str(e)}") | |
| return f"Error processing text file: {str(e)}" | |
| def process_csv_file(file): | |
| """Handle CSV file""" | |
| try: | |
| content = file.read() | |
| file.seek(0) | |
| df = pd.read_csv(io.BytesIO(content)) | |
| result = f"## CSV File: {file.name}\n\n" | |
| result += f"- Rows: {len(df)}\n" | |
| result += f"- Columns: {len(df.columns)}\n" | |
| result += f"- Column Names: {', '.join(df.columns.tolist())}\n\n" | |
| result += "### Data Preview\n\n" | |
| preview_df = df.head(10) | |
| try: | |
| markdown_table = preview_df.to_markdown(index=False) | |
| if markdown_table: | |
| result += markdown_table + "\n\n" | |
| else: | |
| result += "Unable to display CSV data.\n\n" | |
| except Exception as e: | |
| logging.error(f"Markdown table conversion error: {e}") | |
| result += "Displaying data as text:\n\n" | |
| result += str(preview_df) + "\n\n" | |
| num_cols = df.select_dtypes(include=['number']).columns | |
| if len(num_cols) > 0: | |
| result += "### Basic Statistical Information\n\n" | |
| try: | |
| stats_df = df[num_cols].describe().round(2) | |
| stats_markdown = stats_df.to_markdown() | |
| if stats_markdown: | |
| result += stats_markdown + "\n\n" | |
| else: | |
| result += "Unable to display statistical information.\n\n" | |
| except Exception as e: | |
| logging.error(f"Statistical info conversion error: {e}") | |
| result += "Unable to generate statistical information.\n\n" | |
| return result | |
| except Exception as e: | |
| logging.error(f"CSV file processing error: {str(e)}") | |
| return f"Error processing CSV file: {str(e)}" | |
| def process_pdf_file(file): | |
| """Handle PDF file""" | |
| try: | |
| file_bytes = file.read() | |
| file.seek(0) | |
| pdf_file = io.BytesIO(file_bytes) | |
| reader = PyPDF2.PdfReader(pdf_file, strict=False) | |
| result = f"## PDF File: {file.name}\n\n" | |
| result += f"- Total pages: {len(reader.pages)}\n\n" | |
| max_pages = min(5, len(reader.pages)) | |
| all_text = "" | |
| for i in range(max_pages): | |
| try: | |
| page = reader.pages[i] | |
| page_text = page.extract_text() | |
| current_page_text = f"### Page {i+1}\n\n" | |
| if page_text and len(page_text.strip()) > 0: | |
| if len(page_text) > 1500: | |
| current_page_text += page_text[:1500] + "...(truncated)...\n\n" | |
| else: | |
| current_page_text += page_text + "\n\n" | |
| else: | |
| current_page_text += "(No text could be extracted)\n\n" | |
| all_text += current_page_text | |
| if len(all_text) > 8000: | |
| all_text += "...(truncating remaining pages; PDF is too large)...\n\n" | |
| break | |
| except Exception as page_err: | |
| logging.error(f"Error processing PDF page {i+1}: {str(page_err)}") | |
| all_text += f"### Page {i+1}\n\n(Error extracting content: {str(page_err)})\n\n" | |
| if len(reader.pages) > max_pages: | |
| all_text += f"\nNote: Only the first {max_pages} pages are shown out of {len(reader.pages)} total.\n\n" | |
| result += "### PDF Content\n\n" + all_text | |
| return result | |
| except Exception as e: | |
| logging.error(f"PDF file processing error: {str(e)}") | |
| return f"## PDF File: {file.name}\n\nError occurred: {str(e)}\n\nThis PDF file cannot be processed." | |
| def process_uploaded_files(files): | |
| """Combine the contents of all uploaded files into one string.""" | |
| if not files: | |
| return None | |
| result = "# Uploaded File Contents\n\n" | |
| result += "Below is the content from the files provided by the user. Integrate this data as needed for generating ideas.\n\n" | |
| for file in files: | |
| try: | |
| ext = file.name.split('.')[-1].lower() | |
| if ext == 'txt': | |
| result += process_text_file(file) + "\n\n---\n\n" | |
| elif ext == 'csv': | |
| result += process_csv_file(file) + "\n\n---\n\n" | |
| elif ext == 'pdf': | |
| result += process_pdf_file(file) + "\n\n---\n\n" | |
| else: | |
| result += f"### Unsupported File: {file.name}\n\n---\n\n" | |
| except Exception as e: | |
| logging.error(f"File processing error {file.name}: {e}") | |
| result += f"### File processing error: {file.name}\n\nError: {e}\n\n---\n\n" | |
| return result | |
| # ──────────────────────────────── Image & Utility ───────────────────────── | |
| def generate_image(prompt, w=768, h=768, g=3.5, steps=30, seed=3): | |
| """Image generation function.""" | |
| if not prompt: | |
| return None, "Insufficient prompt" | |
| try: | |
| res = Client(IMAGE_API_URL).predict( | |
| prompt=prompt, width=w, height=h, guidance=g, | |
| inference_steps=steps, seed=seed, | |
| do_img2img=False, init_image=None, | |
| image2image_strength=0.8, resize_img=True, | |
| api_name="/generate_image" | |
| ) | |
| return res[0], f"Seed: {res[1]}" | |
| except Exception as e: | |
| logging.error(e) | |
| return None, str(e) | |
| def md_to_html(md: str, title="Idea Output"): | |
| """Convert Markdown to HTML.""" | |
| return f"<!DOCTYPE html><html><head><title>{title}</title><meta charset='utf-8'></head><body>{markdown.markdown(md)}</body></html>" | |
| def keywords(text: str, top=5): | |
| """Simple keyword extraction (for web search).""" | |
| cleaned = re.sub(r"[^가-힣a-zA-Z0-9\s]", "", text) | |
| return " ".join(cleaned.split()[:top]) | |
| # ──────────────────────────────── 헬퍼: 결과 기록·다운로드·자동저장 ────────── | |
| def write_output(md_text: str, prompt: str): | |
| """ | |
| • 대화 기록에 마크다운 답변 저장 | |
| • 다운로드 버튼(마크다운·HTML) 제공 | |
| • 자동 JSON 백업 | |
| """ | |
| # ① 채팅 기록에 추가 | |
| st.session_state.messages.append( | |
| {"role": "assistant", "content": md_text}) | |
| # ② 다운로드 버튼 | |
| st.subheader("Download This Output") | |
| col_md, col_html = st.columns(2) | |
| col_md.download_button( | |
| label="Markdown", | |
| data=md_text, | |
| file_name=f"{prompt[:30]}.md", | |
| mime="text/markdown" | |
| ) | |
| col_html.download_button( | |
| label="HTML", | |
| data=md_to_html(md_text, prompt[:30]), | |
| file_name=f"{prompt[:30]}.html", | |
| mime="text/html" | |
| ) | |
| # ③ 자동 JSON 저장 | |
| if st.session_state.auto_save: | |
| fn = f"chat_history_auto_{datetime.now():%Y%m%d_%H%M%S}.json" | |
| with open(fn, "w", encoding="utf-8") as fp: | |
| json.dump( | |
| st.session_state.messages, fp, | |
| ensure_ascii=False, indent=2 | |
| ) | |
| # ──────────────────────────────── Streamlit UI ──────────────────────────── | |
| def idea_generator_app(): | |
| st.title("Creative Idea Generator") | |
| # Set default session state | |
| if "ai_model" not in st.session_state: | |
| st.session_state.ai_model = "gpt-4.1-mini" | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [] | |
| if "auto_save" not in st.session_state: | |
| st.session_state.auto_save = True | |
| if "generate_image" not in st.session_state: | |
| st.session_state.generate_image = True # 기본값: True | |
| if "web_search_enabled" not in st.session_state: | |
| st.session_state.web_search_enabled = True | |
| # Sidebar UI | |
| sb = st.sidebar | |
| sb.title("Idea Generator Settings") | |
| sb.toggle("Auto Save", key="auto_save") | |
| sb.toggle("Auto Image Generation", key="generate_image") | |
| web_search_enabled = sb.toggle("Use Web Search", value=st.session_state.web_search_enabled) | |
| st.session_state.web_search_enabled = web_search_enabled | |
| if web_search_enabled: | |
| sb.info("✅ Web search results will be integrated.") | |
| # 예시 주제들 (원래 예시 블로그 토픽 -> 이제는 예시 아이디어 주제로 전환) | |
| example_topics = { | |
| "example1": "도시 물 부족 문제 해결을 위한 혁신적 방안", | |
| "example2": "노인 돌봄 서비스의 디지털 전환", | |
| "example3": "지속가능한 식품 포장 솔루션" | |
| } | |
| sb.subheader("Category Focus (Optional)") | |
| category_keys = ["(None)"] + list(physical_transformation_categories.keys()) | |
| sb.selectbox( | |
| "Generate ideas mainly using this category", | |
| options=category_keys, | |
| key="category_focus", | |
| index=0 # 기본값 "(None)" | |
| ) | |
| sb.subheader("Example Prompts") | |
| c1, c2, c3 = sb.columns(3) | |
| if c1.button("도시 물 부족 문제", key="ex1"): | |
| process_example(example_topics["example1"]) | |
| if c2.button("노인 돌봄 서비스", key="ex2"): | |
| process_example(example_topics["example2"]) | |
| if c3.button("지속가능한 식품 포장", key="ex3"): | |
| process_example(example_topics["example3"]) | |
| # Download the latest ideas | |
| latest_ideas = next( | |
| (m["content"] for m in reversed(st.session_state.messages) | |
| if m["role"] == "assistant" and m["content"].strip()), | |
| None | |
| ) | |
| if latest_ideas: | |
| title_match = re.search(r"# (.*?)(\n|$)", latest_ideas) | |
| title = title_match.group(1).strip() if title_match else "ideas" | |
| sb.subheader("Download Latest Ideas") | |
| d1, d2 = sb.columns(2) | |
| d1.download_button("Download as Markdown", latest_ideas, | |
| file_name=f"{title}.md", mime="text/markdown") | |
| d2.download_button("Download as HTML", md_to_html(latest_ideas, title), | |
| file_name=f"{title}.html", mime="text/html") | |
| # JSON conversation record upload | |
| up = sb.file_uploader("Load Conversation History (.json)", type=["json"], key="json_uploader") | |
| if up: | |
| try: | |
| st.session_state.messages = json.load(up) | |
| sb.success("Conversation history loaded successfully") | |
| except Exception as e: | |
| sb.error(f"Failed to load: {e}") | |
| # JSON conversation record download | |
| if sb.button("Download Conversation as JSON"): | |
| sb.download_button( | |
| "Save JSON", | |
| data=json.dumps(st.session_state.messages, ensure_ascii=False, indent=2), | |
| file_name="chat_history.json", | |
| mime="application/json" | |
| ) | |
| # File Upload | |
| st.subheader("File Upload (Optional)") | |
| uploaded_files = st.file_uploader( | |
| "Upload files to reference in the idea generation (txt, csv, pdf)", | |
| type=["txt", "csv", "pdf"], | |
| accept_multiple_files=True, | |
| key="file_uploader" | |
| ) | |
| if uploaded_files: | |
| file_count = len(uploaded_files) | |
| st.success(f"{file_count} files uploaded.") | |
| with st.expander("Preview Uploaded Files", expanded=False): | |
| for idx, file in enumerate(uploaded_files): | |
| st.write(f"**File Name:** {file.name}") | |
| ext = file.name.split('.')[-1].lower() | |
| if ext == 'txt': | |
| preview = file.read(1000).decode('utf-8', errors='ignore') | |
| file.seek(0) | |
| st.text_area( | |
| f"Preview of {file.name}", | |
| preview + ("..." if len(preview) >= 1000 else ""), | |
| height=150 | |
| ) | |
| elif ext == 'csv': | |
| try: | |
| df = pd.read_csv(file) | |
| file.seek(0) | |
| st.write("CSV Preview (up to 5 rows)") | |
| st.dataframe(df.head(5)) | |
| except Exception as e: | |
| st.error(f"CSV preview failed: {e}") | |
| elif ext == 'pdf': | |
| try: | |
| file_bytes = file.read() | |
| file.seek(0) | |
| pdf_file = io.BytesIO(file_bytes) | |
| reader = PyPDF2.PdfReader(pdf_file, strict=False) | |
| pc = len(reader.pages) | |
| st.write(f"PDF File: {pc} pages") | |
| if pc > 0: | |
| try: | |
| page_text = reader.pages[0].extract_text() | |
| preview = page_text[:500] if page_text else "(No text)" | |
| st.text_area("Preview of the first page", preview + "...", height=150) | |
| except: | |
| st.warning("Failed to extract text from the first page") | |
| except Exception as e: | |
| st.error(f"PDF preview failed: {e}") | |
| if idx < file_count - 1: | |
| st.divider() | |
| # Display existing messages in chat | |
| for m in st.session_state.messages: | |
| with st.chat_message(m["role"]): | |
| st.markdown(m["content"]) | |
| if "image" in m: | |
| st.image(m["image"], caption=m.get("image_caption", "")) | |
| # User input for idea generation | |
| prompt = st.chat_input("Enter a topic or concept to generate 3 new ideas.") | |
| if prompt: | |
| process_input(prompt, uploaded_files) | |
| # Sidebar footer | |
| sb.markdown("---") | |
| sb.markdown("Created by [Ginigen.com](https://ginigen.com) | [YouTube](https://www.youtube.com/@ginipickaistudio)") | |
| def process_example(topic): | |
| """Handle example prompts.""" | |
| process_input(topic, []) | |
| # ──────────────────────────────── process_input (중복 출력 제거) ──────────── | |
| def process_input(prompt: str, uploaded_files): | |
| """ | |
| 1) 사용자 입력을 GPT-4로 보내 창의적 아이디어 보고서 생성 | |
| 2) 선택적으로 이미지 생성 | |
| 3) 결과를 한 번만 저장·다운로드·백업 | |
| """ | |
| # ── 0. 사용자 메시지 기록 ────────────────────────────────────────────── | |
| if not any(m["role"] == "user" and m["content"] == prompt | |
| for m in st.session_state.messages): | |
| st.session_state.messages.append({"role": "user", "content": prompt}) | |
| with st.chat_message("user"): | |
| st.markdown(prompt) | |
| # ── 1. 어시스턴트 응답 영역 ────────────────────────────────────────── | |
| with st.chat_message("assistant"): | |
| placeholder = st.empty() | |
| message_placeholder = st.empty() | |
| full_response = "" | |
| use_web_search = st.session_state.web_search_enabled | |
| has_uploaded = bool(uploaded_files) | |
| try: | |
| # 1-A. 모델·상태 초기화 | |
| status = st.status("Preparing to generate ideas…") | |
| status.update(label="Initializing model…") | |
| client = get_openai_client() | |
| # 1-B. 시스템 프롬프트 + 카테고리 DB | |
| selected_cat = st.session_state.get("category_focus", "(None)") | |
| if selected_cat == "(None)": | |
| selected_cat = None | |
| sys_prompt = get_idea_system_prompt(selected_category=selected_cat) | |
| def category_context(sel): | |
| if sel: | |
| return json.dumps( | |
| {sel: physical_transformation_categories[sel]}, | |
| ensure_ascii=False) | |
| return "ALL_CATEGORIES: " + ", ".join( | |
| physical_transformation_categories.keys()) | |
| # 1-C. (선택) 웹 검색 + 파일 내용 | |
| search_content = None | |
| if use_web_search: | |
| status.update(label="Searching the web…") | |
| with st.spinner("Searching…"): | |
| search_content = do_web_search(keywords(prompt, top=5)) | |
| file_content = None | |
| if has_uploaded: | |
| status.update(label="Reading uploaded files…") | |
| with st.spinner("Processing files…"): | |
| file_content = process_uploaded_files(uploaded_files) | |
| # 1-D. 사용자 메시지 결합 | |
| user_content = prompt | |
| if search_content: | |
| user_content += "\n\n" + search_content | |
| if file_content: | |
| user_content += "\n\n" + file_content | |
| api_messages = [ | |
| {"role": "system", "content": sys_prompt}, | |
| {"role": "system", "name": "category_db", | |
| "content": category_context(selected_cat)}, | |
| {"role": "user", "content": user_content}, | |
| ] | |
| # ── 2. GPT-4 스트리밍 호출 ────────────────────────────────── | |
| status.update(label="Generating ideas…") | |
| stream = client.chat.completions.create( | |
| model="gpt-4.1-mini", | |
| messages=api_messages, | |
| temperature=1, | |
| max_tokens=MAX_TOKENS, | |
| top_p=1, | |
| stream=True | |
| ) | |
| for chunk in stream: | |
| if chunk.choices and chunk.choices[0].delta.content: | |
| full_response += chunk.choices[0].delta.content | |
| message_placeholder.markdown(full_response + "▌") | |
| message_placeholder.markdown(full_response) | |
| status.update(label="Ideas created!", state="complete") | |
| # ── 3. 이미지 생성 (선택) ─────────────────────────────────── | |
| if st.session_state.generate_image and full_response: | |
| ccm_match = re.search( | |
| r"###\s*이미지\s*프롬프트\s*\n+([^\n]+)", | |
| full_response, flags=re.IGNORECASE) | |
| legacy_match = None | |
| if not ccm_match: | |
| legacy_match = re.search( | |
| r"\|\s*(?:\*\*)?Image\s+Prompt(?:\*\*)?\s*\|\s*([^|\n]+)", | |
| full_response, flags=re.IGNORECASE) or \ | |
| re.search(r"(?i)Image\s+Prompt\s*[:\-]\s*([^\n]+)", | |
| full_response) | |
| match = ccm_match or legacy_match | |
| if match: | |
| raw_prompt = re.sub(r"[\r\n\"'\\]", " ", | |
| match.group(1)).strip() | |
| with st.spinner("아이디어 이미지 생성 중…"): | |
| img, cap = generate_image(raw_prompt) | |
| if img: | |
| st.image(img, caption=f"아이디어 시각화 – {cap}") | |
| st.session_state.messages.append({ | |
| "role": "assistant", | |
| "content": "", | |
| "image": img, | |
| "image_caption": f"아이디어 시각화 – {cap}" | |
| }) | |
| # ── 4. 결과를 **한 번만** 기록·다운로드·백업 ──────────────── | |
| write_output(full_response, prompt) | |
| except Exception as e: | |
| # 예외 발생 시: 로그 기록 + 사용자 알림만 수행 | |
| logging.error("process_input error", exc_info=True) | |
| placeholder.error(f"⚠️ 작업 중 오류가 발생했습니다: {e}") | |
| st.session_state.messages.append( | |
| {"role": "assistant", "content": f"⚠️ 오류: {e}"}) | |
| # ──────────────────────────────── main ──────────────────────────────────── | |
| def main(): | |
| idea_generator_app() | |
| if __name__ == "__main__": | |
| main() |