aiqcamp commited on
Commit
1df16d0
·
verified ·
1 Parent(s): 7697fb0

Create app-backup.py

Browse files
Files changed (1) hide show
  1. app-backup.py +465 -0
app-backup.py ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ import os
4
+ import gradio as gr
5
+ import random
6
+ import time
7
+ import logging
8
+ import google.generativeai as genai
9
+
10
+ # 로깅 설정
11
+ logging.basicConfig(
12
+ level=logging.INFO,
13
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
14
+ handlers=[
15
+ logging.FileHandler("api_debug.log"),
16
+ logging.StreamHandler()
17
+ ]
18
+ )
19
+ logger = logging.getLogger("idea_generator")
20
+
21
+ # Gemini API 키는 환경 변수 GEMINI_API_KEY에서 가져옵니다.
22
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
23
+ genai.configure(api_key=GEMINI_API_KEY)
24
+
25
+ # 슬래시("/")가 포함된 변환 문자열에서 두 옵션 중 하나만 선택하는 헬퍼 함수
26
+ def choose_alternative(transformation):
27
+ if "/" not in transformation:
28
+ return transformation
29
+ parts = transformation.split("/")
30
+ if len(parts) != 2:
31
+ return random.choice([part.strip() for part in parts])
32
+ left = parts[0].strip()
33
+ right = parts[1].strip()
34
+ if " " in left:
35
+ tokens = left.split(" ", 1)
36
+ prefix = tokens[0]
37
+ if not right.startswith(prefix):
38
+ option1 = left
39
+ option2 = prefix + " " + right
40
+ else:
41
+ option1 = left
42
+ option2 = right
43
+ return random.choice([option1, option2])
44
+ else:
45
+ return random.choice([left, right])
46
+
47
+ # 창의적인 모델/컨셉/형상 변화 아이디어를 위한 카테고리 (기존 '물리적 변화' 사전 유지)
48
+
49
+
50
+ # 창의적인 모델/컨셉/형상 변화 아이디어를 위한 카테고리 (기존 '물리적 변화' 사전 유지)
51
+ physical_transformation_categories = {
52
+ "공간 이동": [
53
+ "앞/뒤 이동", "좌/우 이동", "위/아래 이동", "세로축 회전(고개 끄덕임)",
54
+ "가로축 회전(고개 젓기)", "길이축 회전(옆으로 기울임)", "원 운동", "나선형 이동",
55
+ "관성에 의한 미끄러짐", "회전축 변화", "불규칙 회전", "흔들림 운동", "포물선 이동",
56
+ "무중력 부유", "수면 위 부유", "점프/도약", "슬라이딩", "롤링", "자유 낙하",
57
+ "왕복 운동", "탄성 튕김", "관통", "회피 움직임", "지그재그 이동", "스윙 운동"
58
+ ],
59
+
60
+ "크기와 형태 변화": [
61
+ "부피 늘어남/줄어듦", "길이 늘어남/줄어듦", "너비 늘어남/줄어듦", "높이 늘어남/줄어듦",
62
+ "밀도 변화", "무게 증가/감소", "모양 변형", "상태 변화", "불균등 변형",
63
+ "복잡한 형태 변형", "비틀림/꼬임", "불균일한 확장/축소", "모서리 둥글게/날카롭게",
64
+ "깨짐/갈라짐", "여러 조각 나눠짐", "물 저항", "먼지 저항", "찌그러짐/복원",
65
+ "접힘/펼쳐짐", "압착/팽창", "늘어남/수축", "구겨짐/평평해짐", "뭉개짐/단단해짐",
66
+ "말림/펴짐", "꺾임/구부러짐"
67
+ ],
68
+
69
+ "표면 및 외관 변화": [
70
+ "색상 변화", "질감 변화", "투명/불투명 변화", "반짝임/무광 변화",
71
+ "빛 반사 정도 변화", "무늬 변화", "각도에 따른 색상 변화", "빛에 따른 색상 변화",
72
+ "온도에 따른 색상 변화", "홀로그램 효과", "표면 각도별 빛 반사", "표면 모양 변형",
73
+ "초미세 표면 구조 변화", "자가 세정 효과", "얼룩/패턴 생성", "흐림/선명함 변화",
74
+ "광택/윤기 변화", "색조/채도 변화", "발광/형광", "빛 산란 효과",
75
+ "빛 흡수 변화", "반투명 효과", "그림자 효과 변화", "자외선 반응 변화",
76
+ "야광 효과"
77
+ ],
78
+
79
+ "물질의 상태 변화": [
80
+ "고체/액체/기체 전환", "결정화/용해", "산화/부식", "딱딱해짐/부드러워짐",
81
+ "특수 상태 전환", "무정형/결정형 전환", "성분 분리", "미세 입자 형성/분해",
82
+ "젤 형성/풀어짐", "준안정 상태 변화", "분자 자가 정렬/분해", "상태변화 지연 현상",
83
+ "녹음", "굳음", "증발/응축", "승화/증착", "침전/부유", "분산/응집",
84
+ "건조/습윤", "팽윤/수축", "동결/해동", "풍화/침식", "충전/방전",
85
+ "결합/분리", "발효/부패"
86
+ ],
87
+
88
+ "열 관련 변화": [
89
+ "온도 상승/하강", "열에 의한 팽창/수축", "열 전달/차단", "압력 상승/하강",
90
+ "열 변화에 따른 자화", "무질서도 변화", "열전기 현상", "자기장에 의한 열 변화",
91
+ "상태변화 중 열 저장/방출", "열 스트레스 발생/해소", "급격한 온도 변화 영향",
92
+ "복사열에 의한 냉각/가열", "발열/흡열", "열 분포 변화", "열 반사/흡수",
93
+ "냉각 응축", "열 활성화", "열 변색", "열 팽창 계수 변화", "열 안정성 변화",
94
+ "내열성/내한성", "자기발열", "열적 평형/불균형", "열적 변형", "열 분산/집중"
95
+ ],
96
+
97
+ "움직임 특성 변화": [
98
+ "가속/감속", "일정 속도 유지", "진동/진동 감소", "부딪힘/튕김",
99
+ "회전 속도 증가/감소", "회전 방향 변화", "불규칙 움직임", "멈췄다 미끄러지는 현상",
100
+ "공진/반공진", "유체 속 저항/양력 변화", "움직임 저항 변화", "복합 진동 움직임",
101
+ "특수 유체 속 움직임", "회전-이동 연계 움직임", "관성 정지", "충격 흡수",
102
+ "충격 전달", "운동량 보존", "마찰력 변화", "관성 탈출", "불안정 균형",
103
+ "동적 안정성", "흔들림 감쇠", "경로 예측성", "회피 움직임"
104
+ ],
105
+
106
+ "구조적 변화": [
107
+ "부품 추가/제거", "조립/분해", "접기/펴기", "변형/원상복구", "최적 구조 변화",
108
+ "자가 재배열", "자연 패턴 형성/소멸", "규칙적 패턴 변화", "모듈식 변형",
109
+ "복잡성 증가 구조", "원래 모양 기억 효과", "시간에 따른 형태 변화", "부분 제거",
110
+ "부분 교체", "결합", "분리", "분할/통합", "중첩/겹침", "내부 구조 변화",
111
+ "외부 구조 변화", "중심축 이동", "균형점 변화", "계층 구조 변화", "지지 구조 변화",
112
+ "응력 분산 구조", "충격 흡수 구조", "그리드/매트릭스 구조 변화", "상호 연결성 변화"
113
+ ],
114
+
115
+ "전기 및 자기 변화": [
116
+ "자성 생성/소멸", "전하량 증가/감소", "전기장 생성/소멸", "자기장 생성/소멸",
117
+ "초전도 상태 전환", "강유전체 특성 변화", "양자 상태 변화", "플라즈마 상태 형성/소멸",
118
+ "스핀파 전달", "빛에 의한 전기 발생", "압력에 의한 전기 발생", "자기장 속 전류 변화",
119
+ "전기 저항 변화", "전기 전도성 변화", "정전기 발생/방전", "전자기 유도",
120
+ "전자기파 방출/흡수", "전기 용량 변화", "자기 이력 현상", "전기적 분극",
121
+ "전자 흐름 방향 변화", "전기적 공명", "전기적 차폐/노출", "자기 차폐/노출",
122
+ "자기장 방향 정렬"
123
+ ],
124
+
125
+ "화학적 변화": [
126
+ "표면 코팅 변화", "물질 성분 변화", "화학 반응 변화", "촉매 작용 시작/중단",
127
+ "빛에 의한 화학 반응", "전기에 의한 화학 반응", "단분자막 형성", "분자 수준 계산 변화",
128
+ "자연 모방 표면 변화", "환경 반응형 물질 변화", "주기적 화학 반응", "산화", "환원",
129
+ "고분자화", "물 분해", "화합", "방사선 영향", "산-염기 반응", "중화 반응",
130
+ "이온화", "화학적 흡착/탈착", "촉매 효율 변화", "효소 활성 변화", "발색 반응",
131
+ "pH 변화", "화학적 평형 이동", "결합 형성/분해", "용해도 변화"
132
+ ],
133
+
134
+ "시간 관련 변화": [
135
+ "노화/풍화", "마모/부식", "색 바램/변색", "손상/회복", "수명 주기 변화",
136
+ "사용자 상호작용에 따른 적응", "학습 기반 형태 최적화", "시간에 따른 물성 변화",
137
+ "집단 기억 효과", "문화적 의미 변화", "지연 반응", "이전 상태 의존 변화",
138
+ "점진적 시간 변화", "진화적 변화", "주기적 재생", "계절 변화 적응",
139
+ "생체리듬 변화", "생애 주기 단계", "성장/퇴화", "자기 복구/재생",
140
+ "자연 순환 적응", "지속성/일시성", "기억 효과", "지연된 작용", "누적 효과"
141
+ ],
142
+
143
+ "빛과 시각 효과": [
144
+ "발광/소등", "빛 투과/차단", "빛 산란/집중", "색상 스펙트럼 변화", "빛 회절",
145
+ "빛 간섭", "홀로그램 생성", "레이저 효과", "빛 편광", "형광/인광",
146
+ "자외선/적외선 발광", "광학적 착시", "빛 굴절", "그림자 생성/제거",
147
+ "색수차 효과", "무지개 효과", "글로우 효과", "플래시 효과", "조명 패턴",
148
+ "빔 효과", "광 필터 효과", "빛의 방향성 변화", "투영 효과", "빛 감지/반응",
149
+ "광도 변화"
150
+ ],
151
+
152
+ "소리와 진동 효과": [
153
+ "소리 발생/소멸", "소리 높낮이 변화", "소리 크기 변화", "음색 변화",
154
+ "공명/반공명", "음향 진동", "초음파/저음파 발생", "음향 집중/분산",
155
+ "음향 반사/흡수", "음향 도플러 효과", "음파 간섭", "음향 공진",
156
+ "진동 패턴 변화", "타악 효과", "음향 피드백", "음향 차폐/증폭",
157
+ "소리 지향성", "음향 왜곡", "비트 생성", "하모닉스 생성", "주파수 변조",
158
+ "음향 충격파", "음향 필터링", "음파 전파 패턴", "진동 댐핑"
159
+ ],
160
+
161
+ "생물학적 변화": [
162
+ "생장/위축", "세포 분열/사멸", "생물 발광", "신진대사 변화", "면역 반응",
163
+ "호르몬 분비", "신경 반응", "유전적 발현", "적응/진화", "생체리듬 변화",
164
+ "재생/치유", "노화/성숙", "생체 모방 변화", "바이오필름 형성", "생물학적 분해",
165
+ "효소 활성화/비활성화", "생물학�� 신호 전달", "스트레스 반응", "체온 조절",
166
+ "생물학적 시계 변화", "세포외 기질 변화", "생체 역학적 반응", "세포 운동성",
167
+ "세포 극성 변화", "영양 상태 변화"
168
+ ],
169
+
170
+ "환경 상호작용": [
171
+ "온도 반응", "습도 반응", "기압 반응", "중력 반응", "자기장 반응",
172
+ "빛 반응", "소리 반응", "화학 물질 감지", "기계적 자극 감지", "전기 자극 반응",
173
+ "방사선 반응", "진동 감지", "pH 반응", "용매 반응", "기체 교환",
174
+ "환경 오염 반응", "날씨 반응", "계절 변화 반응", "일주기 반응", "생태계 상호작용",
175
+ "공생/경쟁 반응", "포식/피식 관계", "군집 형성", "영역 설정", "이주/정착 패턴"
176
+ ],
177
+
178
+ "센서 기능": [
179
+ "시각 센서/감지", "청각 센서/감지", "촉각 센서/감지", "미각 센서/감지", "후각 센서/감지",
180
+ "온도 센서/감지", "습도 센서/감지", "압력 센서/감지", "가속도 센서/감지", "회전 센서/감지",
181
+ "근접 센서/감지", "위치 센서/감지", "운동 센서/감지", "가스 센서/감지", "적외선 센서/감지",
182
+ "자외선 센서/감지", "방사선 센서/감지", "자기장 센서/감지", "전기장 센서/감지", "화학물질 센서/감지",
183
+ "생체신호 센서/감지", "진동 센서/감지", "소음 센서/감지", "빛 세기 센서/감지", "빛 파장 센서/감지",
184
+ "기울기 센서/감지", "pH 센서/감지", "전류 센서/감지", "전압 센서/감지", "이미지 센서/감지",
185
+ "거리 센서/감지", "깊이 센서/감지", "중력 센서/감지", "속도 센서/감지", "흐름 센서/감지",
186
+ "수위 센서/감지", "탁도 센서/감지", "염도 센서/감지", "금속 감지", "압전 센서/감지",
187
+ "광전 센서/감지", "열전대 센서/감지", "홀 효과 센서/감지", "초음파 센서/감지", "레이더 센서/감지",
188
+ "라이다 센서/감지", "터치 센서/감지", "제스처 센서/감지", "심박 센서/감지", "혈압 센서/감지"
189
+ ]
190
+ }
191
+
192
+ ##############################################################################
193
+ # Gemini API 호출 함수 (예: gemini-2.0-flash-thinking-exp-01-21 -> 다른 모델 사용 시 수정)
194
+ ##############################################################################
195
+ def query_gemini_api(prompt):
196
+ try:
197
+ # 예시: 기존 gemini-2.0... 대신, 다른 모델이 필요하다면 교체하세요.
198
+ model = genai.GenerativeModel('gemini-2.0-flash-thinking-exp-01-21')
199
+
200
+ response = model.generate_content(prompt)
201
+
202
+ # 응답 구조 방어적으로 처리
203
+ try:
204
+ if hasattr(response, 'text'):
205
+ return response.text
206
+
207
+ if hasattr(response, 'candidates') and response.candidates:
208
+ if len(response.candidates) > 0:
209
+ candidate = response.candidates[0]
210
+ if hasattr(candidate, 'content'):
211
+ content = candidate.content
212
+ if hasattr(content, 'parts') and content.parts:
213
+ if len(content.parts) > 0:
214
+ return content.parts[0].text
215
+ if hasattr(response, 'parts') and response.parts:
216
+ if len(response.parts) > 0:
217
+ return response.parts[0].text
218
+
219
+ return "Unable to generate a response. API response structure is different than expected."
220
+
221
+ except Exception as inner_e:
222
+ logger.error(f"Error processing response: {inner_e}")
223
+ return f"An error occurred while processing the response: {str(inner_e)}"
224
+
225
+ except Exception as e:
226
+ logger.error(f"Error calling Gemini API: {e}")
227
+ if "API key not valid" in str(e):
228
+ return "API key is not valid. Please check your GEMINI_API_KEY environment variable."
229
+ return f"An error occurred while calling the API: {str(e)}"
230
+
231
+ ##############################################################################
232
+ # 설명 확장 함수: "모델/컨셉/형상의 변화에 대한 이해와 혁신 포인트, 기능성 등을 중심"으로
233
+ ##############################################################################
234
+ def enhance_with_llm(base_description, obj_name, category):
235
+ prompt = f"""
236
+ 다음은 '{obj_name}'의 '{category}' 관련 간단한 설명입니다:
237
+ "{base_description}"
238
+ 위 내용을 보다 구체화하여,
239
+ 1) 창의적인 모델/컨셉/형상의 변화에 대한 이해,
240
+ 2) 혁신 포인트와 기능성 등을 중심으로
241
+ 3~4문장의 아이디어로 확장해 주세요.
242
+ """
243
+ return query_gemini_api(prompt)
244
+
245
+ ##############################################################################
246
+ # 단일 키워드(오브젝트)에 대한 "창의적 변화 아이디어" 생성
247
+ ##############################################################################
248
+ def generate_single_object_transformations(obj):
249
+ results = {}
250
+ for category, transformations in physical_transformation_categories.items():
251
+ transformation = choose_alternative(random.choice(transformations))
252
+ base_description = f"{obj}이(가) {transformation} 현상을 보인다"
253
+ results[category] = {"base": base_description, "enhanced": None}
254
+ return results
255
+
256
+ ##############################################################################
257
+ # 두 키워드에 대한 "창의적 변화 아이디어" 생성
258
+ ##############################################################################
259
+ def generate_two_objects_interaction(obj1, obj2):
260
+ results = {}
261
+ for category, transformations in physical_transformation_categories.items():
262
+ transformation = choose_alternative(random.choice(transformations))
263
+ template = random.choice([
264
+ "{obj1}이(가) {obj2}에 결합하여 {change}가 발생했다",
265
+ "{obj1}과(와) {obj2}이(가) 충돌하면서 {change}가 일어났다"
266
+ ])
267
+ base_description = template.format(obj1=obj1, obj2=obj2, change=transformation)
268
+ results[category] = {"base": base_description, "enhanced": None}
269
+ return results
270
+
271
+ ##############################################################################
272
+ # 세 키워드에 대한 "창의적 변화 아이디어" 생성
273
+ ##############################################################################
274
+ def generate_three_objects_interaction(obj1, obj2, obj3):
275
+ results = {}
276
+ for category, transformations in physical_transformation_categories.items():
277
+ transformation = choose_alternative(random.choice(transformations))
278
+ template = random.choice([
279
+ "{obj1}, {obj2}, {obj3}이(가) 삼각형 구조로 결합하여 {change}가 발생했다",
280
+ "{obj1}이(가) {obj2}와(과) {obj3} 사이에서 매개체 역할을 하며 {change}를 촉진했다"
281
+ ])
282
+ base_description = template.format(obj1=obj1, obj2=obj2, obj3=obj3, change=transformation)
283
+ results[category] = {"base": base_description, "enhanced": None}
284
+ return results
285
+
286
+ ##############################################################################
287
+ # 생성된 기본 설명을 LLM을 통해 확장
288
+ ##############################################################################
289
+ def enhance_descriptions(results, objects):
290
+ # progress 인자 제거
291
+ obj_name = " 및 ".join([obj for obj in objects if obj])
292
+
293
+ for category, result in results.items():
294
+ result["enhanced"] = enhance_with_llm(result["base"], obj_name, category)
295
+
296
+ return results
297
+
298
+ ##############################################################################
299
+ # 사용자 입력(최대 3개 키워드)에 따라 창의적 변화 아이디어 생성
300
+ ##############################################################################
301
+ def generate_transformations(text1, text2=None, text3=None):
302
+ # progress 인자 제거
303
+
304
+ if text2 and text3:
305
+ results = generate_three_objects_interaction(text1, text2, text3)
306
+ objects = [text1, text2, text3]
307
+ elif text2:
308
+ results = generate_two_objects_interaction(text1, text2)
309
+ objects = [text1, text2]
310
+ else:
311
+ results = generate_single_object_transformations(text1)
312
+ objects = [text1]
313
+
314
+ return enhance_descriptions(results, objects)
315
+
316
+ ##############################################################################
317
+ # 결과 포맷팅
318
+ ##############################################################################
319
+ def format_results(results):
320
+ formatted = ""
321
+ for category, result in results.items():
322
+ formatted += f"## {category}\n**기본 아이디어**: {result['base']}\n\n**확장된 아이디어**: {result['enhanced']}\n\n---\n\n"
323
+ return formatted
324
+
325
+ ##############################################################################
326
+ # Gradio UI에서 호출할 함수
327
+ ##############################################################################
328
+ def process_inputs(text1, text2, text3, progress=gr.Progress()):
329
+ text1 = text1.strip() if text1 else None
330
+ text2 = text2.strip() if text2 else None
331
+ text3 = text3.strip() if text3 else None
332
+
333
+ if not text1:
334
+ return "오류: 최소 하나의 키워드를 입력해주세요."
335
+
336
+ # 진행 상태 메시지 (중첩 리스트 대신 단순 문자열 사용)
337
+ status_message = ""
338
+
339
+ # 키워드 정보 확인
340
+ keyword_info = f"키워드: {text1}"
341
+ if text2:
342
+ keyword_info += f", {text2}"
343
+ if text3:
344
+ keyword_info += f", {text3}"
345
+
346
+ # 결과 생성 (progress 인자 전달하지 않음)
347
+ try:
348
+ # 진행 상태를 단계별로 표시
349
+ progress(0.05, desc="아이디어 생성 준비 중...")
350
+ time.sleep(0.3) # 시각적 효과를 위한 짧은 지연
351
+
352
+ progress(0.1, desc="창의적인 모델/컨셉/형상 변화 아이디어 생성 시작...")
353
+
354
+ # text1, text2, text3만 전달하고 progress는 전달하지 않음
355
+ results = generate_transformations(text1, text2, text3)
356
+
357
+ progress(0.8, desc="결과 포맷팅 중...")
358
+ formatted = format_results(results)
359
+
360
+ progress(1.0, desc="완료!")
361
+ return formatted
362
+
363
+ except Exception as e:
364
+ logger.error(f"Error in process_inputs: {e}")
365
+ return f"처리 중 오류가 발생했습니다: {str(e)}"
366
+
367
+ ##############################################################################
368
+ # API 키 경고 메시지
369
+ ##############################################################################
370
+ def get_warning_message():
371
+ if not GEMINI_API_KEY:
372
+ return "⚠️ 환경 변수 GEMINI_API_KEY가 설정되지 않았습니다. Gemini API 키를 설정하세요."
373
+ return ""
374
+
375
+ ##############################################################################
376
+ # Gradio UI
377
+ ##############################################################################
378
+ with gr.Blocks(title="키워드 기반 창의적 변화 아이디어 생성기",
379
+ theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo:
380
+
381
+ gr.HTML("""
382
+ <style>
383
+ body { background: linear-gradient(135deg, #e0eafc, #cfdef3); font-family: 'Arial', sans-serif; }
384
+ .gradio-container { padding: 20px; }
385
+ h1, h2 { text-align: center; }
386
+ h1 { color: #333; }
387
+ h2 { color: #555; }
388
+ .output { background-color: #ffffff; padding: 15px; border-radius: 8px; }
389
+ .gr-button { background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; }
390
+ .progress-message { color: #2196F3; font-weight: bold; margin-top: 10px; }
391
+ </style>
392
+ """)
393
+
394
+ gr.Markdown("# 🚀 키워드 기반 창의적 변화 아이디어 생성기")
395
+ gr.Markdown("입력한 **키워드**(최대 3개)를 바탕으로, **창의적인 모델/컨셉/형상 변화**에 대한 이해와 **혁신 포인트**, **기능성** 등을 중심으로 확장된 아이디어를 제시합니다.")
396
+
397
+ warning = gr.Markdown(get_warning_message())
398
+
399
+ # 좌측 입력 영역
400
+ with gr.Row():
401
+ with gr.Column(scale=1):
402
+ text_input1 = gr.Textbox(label="키워드 1 (필수)", placeholder="예: 스마트폰")
403
+ text_input2 = gr.Textbox(label="키워드 2 (선택)", placeholder="예: 인공지능")
404
+ text_input3 = gr.Textbox(label="키워드 3 (선택)", placeholder="예: 헬스케어")
405
+
406
+ # 입력과 버튼 사이에 상태 메시지를 표시할 영역 추가
407
+ status_msg = gr.Markdown("💡 '아이디어 생성하기' 버튼을 클릭하면 아이디어 생성이 시작됩니다.")
408
+
409
+ # 처리 중일 때만 표시되는 상태 메시지
410
+ processing_indicator = gr.HTML("""
411
+ <div style="display: flex; justify-content: center; align-items: center; margin: 10px 0;">
412
+ <div style="border: 5px solid #f3f3f3; border-top: 5px solid #3498db; border-radius: 50%; width: 30px; height: 30px; animation: spin 2s linear infinite;"></div>
413
+ <p style="margin-left: 10px; font-weight: bold; color: #3498db;">처리 중입니다...</p>
414
+ </div>
415
+ <style>
416
+ @keyframes spin {
417
+ 0% { transform: rotate(0deg); }
418
+ 100% { transform: rotate(360deg); }
419
+ }
420
+ </style>
421
+ """, visible=False)
422
+
423
+ submit_button = gr.Button("아이디어 생성하기", variant="primary")
424
+
425
+ # 우측 출력 영역
426
+ with gr.Column(scale=2):
427
+ idea_output = gr.Markdown(label="아이디어 결과")
428
+
429
+ # 예시 입력
430
+ gr.Examples(
431
+ examples=[
432
+ ["스마트폰", "", ""],
433
+ ["자동차", "", ""],
434
+ ["자동차", "인공지능", ""],
435
+ ["드론", "인공지능", ""],
436
+ ["운동화", "웨어러블", "건강"],
437
+ ],
438
+ inputs=[text_input1, text_input2, text_input3],
439
+ )
440
+
441
+ # 처리 시작 전 함수 (로딩 표시기 보이게)
442
+ def show_processing_indicator():
443
+ return gr.update(visible=True)
444
+
445
+ # 처리 완료 후 함수 (로딩 표시기 숨기기)
446
+ def hide_processing_indicator():
447
+ return gr.update(visible=False)
448
+
449
+ # 버튼 이벤트 - 여러 함수 순차 실행
450
+ submit_button.click(
451
+ fn=show_processing_indicator,
452
+ inputs=None,
453
+ outputs=processing_indicator
454
+ ).then(
455
+ fn=process_inputs,
456
+ inputs=[text_input1, text_input2, text_input3],
457
+ outputs=idea_output
458
+ ).then(
459
+ fn=hide_processing_indicator,
460
+ inputs=None,
461
+ outputs=processing_indicator
462
+ )
463
+
464
+ if __name__ == "__main__":
465
+ demo.launch(debug=True)