Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -5,7 +5,7 @@ import time
|
|
5 |
import logging
|
6 |
import google.generativeai as genai
|
7 |
|
8 |
-
#
|
9 |
logging.basicConfig(
|
10 |
level=logging.INFO,
|
11 |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
@@ -16,11 +16,11 @@ logging.basicConfig(
|
|
16 |
)
|
17 |
logger = logging.getLogger("idea_generator")
|
18 |
|
19 |
-
#
|
20 |
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
21 |
genai.configure(api_key=GEMINI_API_KEY)
|
22 |
|
23 |
-
#
|
24 |
def choose_alternative(transformation):
|
25 |
if "/" not in transformation:
|
26 |
return transformation
|
@@ -42,7 +42,7 @@ def choose_alternative(transformation):
|
|
42 |
else:
|
43 |
return random.choice([left, right])
|
44 |
|
45 |
-
#
|
46 |
physical_transformation_categories = {
|
47 |
"공간적 변화": [
|
48 |
"전진/후진 이동", "좌/우 이동", "상승/하강", "피치 회전", "요 회전", "롤 회전",
|
@@ -101,103 +101,33 @@ physical_transformation_categories = {
|
|
101 |
]
|
102 |
}
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
"브랜드명 변경/리브랜딩", "로고 재설계/현대화", "슬로건 업데이트/변경",
|
108 |
-
"브랜드 컬러 팔레트 변경", "브랜드 보이스/톤 조정", "브랜드 포지셔닝 재정립",
|
109 |
-
"브랜드 스토리 재구성", "브랜드 아키텍처 재구성", "브랜드 퍼스널리티 진화",
|
110 |
-
"브랜드 이미지 재정립"
|
111 |
-
],
|
112 |
-
"제품/서비스 변화": [
|
113 |
-
"기능적 업그레이드/다운그레이드", "포장/패키징 재설계", "제품 사이즈/용량 조정",
|
114 |
-
"SKU 확장/축소", "맞춤형/개인화 옵션 추가", "프리미엄/베이직 라인 출시",
|
115 |
-
"한정판/시즌 에디션 출시", "콜라보레이션/크로스오버 제품", "지속가능성 개선",
|
116 |
-
"번들링/언번들링 전략 변경"
|
117 |
-
],
|
118 |
-
"가격 전략 변화": [
|
119 |
-
"프리미엄/럭셔리 가격 포지셔닝", "가격 하락/경제적 포지셔닝", "가치 기반 가격 책정",
|
120 |
-
"동적 가격 정책 도입", "멤버십/구독 모델 전환", "가격 세분화 전략",
|
121 |
-
"프로모션 가격 전략 변경", "심리적 가격 책정 적용", "번들 가격 전략 도입",
|
122 |
-
"프리미엄/이코노미 이중 전략"
|
123 |
-
],
|
124 |
-
"프로모션/커뮤니케이션 변화": [
|
125 |
-
"메시지 프레이밍 변경", "타겟 오디언스 확장/재정의", "감성적/이성적 소구점 전환",
|
126 |
-
"스토리텔링 방식 변화", "콘텐츠 마케팅 전략 조정", "크리에이티브 방향성 변경",
|
127 |
-
"광고 캠페인 재설계", "브랜드 앰배서더/인플루언서 교체", "언어/트론 조정",
|
128 |
-
"비주얼 아이덴티티 업데이트"
|
129 |
-
],
|
130 |
-
"채널/유통 변화": [
|
131 |
-
"온라인/오프라인 전략 전환", "옴니채널 통합/분리", "유통 파트너십 확장/축소",
|
132 |
-
"D2C(Direct-to-Consumer) 모델 도입", "리테일 경험 재설계", "서비스 딜리버리 방식 변경",
|
133 |
-
"팝업/이벤트 기반 유통 전략", "지역적 확장/축소", "파트너 생태계 재구성",
|
134 |
-
"디지털 마켓플레이스 전략 변화"
|
135 |
-
],
|
136 |
-
"고객 경험 변화": [
|
137 |
-
"고객 여정 재설계", "퍼스널라이제이션/맞춤화 강화", "서비스 레벨 조정",
|
138 |
-
"로열티 프로그램 재구성", "고객 참여 메커니즘 변화", "사용자 인터페이스/UX 개선",
|
139 |
-
"고객 서비스 프로토콜 변경", "피드백 루프 재설계", "자가 서비스 옵션 확장",
|
140 |
-
"체험형 마케팅 요소 강화"
|
141 |
-
],
|
142 |
-
"디지털 마케팅 변화": [
|
143 |
-
"검색 최적화(SEO) 전략 변경", "소셜 미디어 플랫폼 포트폴리오 조정", "콘텐츠 타입/포맷 다양화",
|
144 |
-
"데이터 기반 타겟팅 정교화", "마케팅 자동화 확장/조정", "디지털 광고 알고리즘 최적화",
|
145 |
-
"이메일 마케팅 전략 변화", "모바일 최적화 전략 강화", "AR/VR 마케팅 요소 통합",
|
146 |
-
"인공지능 기반 마케팅 도입"
|
147 |
-
],
|
148 |
-
"시장 포지셔닝 변화": [
|
149 |
-
"시장 세그먼트 재정의", "경쟁사 대비 포지셔닝 변경", "틈새시장/메인스트림 전략 전환",
|
150 |
-
"산업/카테고리 횡단 재포지셔닝", "지리적 타겟 시장 확장/축소", "글로벌/로컬 포지셔닝 조정",
|
151 |
-
"문화적 맥락 적응", "주요 시장 메시지 재정의", "특성 차별화 강화/변경",
|
152 |
-
"가치 제안 재구성"
|
153 |
-
],
|
154 |
-
"혁신/트렌드 반응": [
|
155 |
-
"첨단 기술 통합", "트렌드 얼리어답터/팔로워 포지셔닝", "지속가능성/친환경 전략 강화",
|
156 |
-
"사회적 책임 이니셔티브 도입", "디지털 트랜스포메이션 가속화", "헬스/웰빙 요소 강화",
|
157 |
-
"세대별 트렌드 반응 적용", "미래지향적 혁신 커뮤니케이션", "문화적 현상 연계 마케팅",
|
158 |
-
"크로스 인더스트리 혁신 적용"
|
159 |
-
],
|
160 |
-
"데이터/분석 기반 변화": [
|
161 |
-
"고객 데이터 수집 방식 변경", "예측 분석 모델 도입/강화", "A/B 테스팅 체계 구축/확장",
|
162 |
-
"소비자 인사이트 발굴 방식 변화", "마케팅 ROI 측정 프레임워크 변경", "실시간 데이터 활용 체계 구축",
|
163 |
-
"프라이버시 중심 데이터 전략", "세그먼테이션 모델 고도화", "디지털 행동 분석 체계 강화",
|
164 |
-
"통합 마케팅 데이터 대시보드 구축"
|
165 |
-
]
|
166 |
-
}
|
167 |
-
|
168 |
def query_gemini_api(prompt):
|
169 |
try:
|
170 |
-
#
|
171 |
model = genai.GenerativeModel('gemini-2.0-flash-thinking-exp-01-21')
|
172 |
|
173 |
-
# Generate content with a simple approach
|
174 |
response = model.generate_content(prompt)
|
175 |
-
|
176 |
-
#
|
177 |
try:
|
178 |
-
# First try .text attribute
|
179 |
if hasattr(response, 'text'):
|
180 |
return response.text
|
181 |
|
182 |
-
# Try accessing candidates (safely)
|
183 |
if hasattr(response, 'candidates') and response.candidates:
|
184 |
if len(response.candidates) > 0:
|
185 |
candidate = response.candidates[0]
|
186 |
-
|
187 |
-
# Try accessing content and parts
|
188 |
if hasattr(candidate, 'content'):
|
189 |
content = candidate.content
|
190 |
-
|
191 |
if hasattr(content, 'parts') and content.parts:
|
192 |
if len(content.parts) > 0:
|
193 |
return content.parts[0].text
|
194 |
-
|
195 |
-
# Direct parts access attempt
|
196 |
if hasattr(response, 'parts') and response.parts:
|
197 |
if len(response.parts) > 0:
|
198 |
return response.parts[0].text
|
199 |
-
|
200 |
-
# Fallback if all attempts fail
|
201 |
return "Unable to generate a response. API response structure is different than expected."
|
202 |
|
203 |
except Exception as inner_e:
|
@@ -206,23 +136,29 @@ def query_gemini_api(prompt):
|
|
206 |
|
207 |
except Exception as e:
|
208 |
logger.error(f"Error calling Gemini API: {e}")
|
209 |
-
|
210 |
-
# Check for API key validation error
|
211 |
if "API key not valid" in str(e):
|
212 |
return "API key is not valid. Please check your GEMINI_API_KEY environment variable."
|
213 |
-
|
214 |
return f"An error occurred while calling the API: {str(e)}"
|
215 |
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
|
|
|
|
220 |
"{base_description}"
|
221 |
-
|
|
|
|
|
|
|
|
|
|
|
222 |
return query_gemini_api(prompt)
|
223 |
|
224 |
-
|
225 |
-
|
|
|
|
|
226 |
results = {}
|
227 |
for category, transformations in physical_transformation_categories.items():
|
228 |
transformation = choose_alternative(random.choice(transformations))
|
@@ -230,17 +166,10 @@ def generate_single_object_physical_transformations(obj):
|
|
230 |
results[category] = {"base": base_description, "enhanced": None}
|
231 |
return results
|
232 |
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
transformation = choose_alternative(random.choice(transformations))
|
238 |
-
base_description = f"{obj}이(가) {transformation} 전략을 실행했다"
|
239 |
-
results[category] = {"base": base_description, "enhanced": None}
|
240 |
-
return results
|
241 |
-
|
242 |
-
# Generate physical transformations for two objects
|
243 |
-
def generate_two_objects_physical_interaction(obj1, obj2):
|
244 |
results = {}
|
245 |
for category, transformations in physical_transformation_categories.items():
|
246 |
transformation = choose_alternative(random.choice(transformations))
|
@@ -252,21 +181,10 @@ def generate_two_objects_physical_interaction(obj1, obj2):
|
|
252 |
results[category] = {"base": base_description, "enhanced": None}
|
253 |
return results
|
254 |
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
transformation = choose_alternative(random.choice(transformations))
|
260 |
-
template = random.choice([
|
261 |
-
"{obj1}이(가) {obj2}의 브랜드 특성을 활용하여 {change}를 진행했다",
|
262 |
-
"{obj1}과(와) {obj2}의 협업으로 {change}가 시장에 도입되었다"
|
263 |
-
])
|
264 |
-
base_description = template.format(obj1=obj1, obj2=obj2, change=transformation)
|
265 |
-
results[category] = {"base": base_description, "enhanced": None}
|
266 |
-
return results
|
267 |
-
|
268 |
-
# Generate physical transformations for three objects
|
269 |
-
def generate_three_objects_physical_interaction(obj1, obj2, obj3):
|
270 |
results = {}
|
271 |
for category, transformations in physical_transformation_categories.items():
|
272 |
transformation = choose_alternative(random.choice(transformations))
|
@@ -278,82 +196,43 @@ def generate_three_objects_physical_interaction(obj1, obj2, obj3):
|
|
278 |
results[category] = {"base": base_description, "enhanced": None}
|
279 |
return results
|
280 |
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
transformation = choose_alternative(random.choice(transformations))
|
286 |
-
template = random.choice([
|
287 |
-
"{obj1}, {obj2}, {obj3}이(가) 공동 마케팅으로 {change}가 시너지를 창출했다",
|
288 |
-
"{obj1}이(가) {obj2}와(과) {obj3}의 고객층을 통합하여 {change}로 새로운 시장을 창출했다"
|
289 |
-
])
|
290 |
-
base_description = template.format(obj1=obj1, obj2=obj2, obj3=obj3, change=transformation)
|
291 |
-
results[category] = {"base": base_description, "enhanced": None}
|
292 |
-
return results
|
293 |
-
|
294 |
-
# Enhance descriptions using the LLM
|
295 |
-
def enhance_descriptions(results, objects, is_marketing=False):
|
296 |
obj_name = " 및 ".join([obj for obj in objects if obj])
|
297 |
for category, result in results.items():
|
298 |
-
result["enhanced"] = enhance_with_llm(result["base"], obj_name, category
|
299 |
return results
|
300 |
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
objects = [text1, text2, text3]
|
306 |
-
elif text2:
|
307 |
-
results = generate_two_objects_physical_interaction(text1, text2)
|
308 |
-
objects = [text1, text2]
|
309 |
-
else:
|
310 |
-
results = generate_single_object_physical_transformations(text1)
|
311 |
-
objects = [text1]
|
312 |
-
return enhance_descriptions(results, objects, is_marketing=False)
|
313 |
-
|
314 |
-
# Generate marketing transformations based on number of objects
|
315 |
-
def generate_marketing_transformations(text1, text2=None, text3=None):
|
316 |
if text2 and text3:
|
317 |
-
results =
|
318 |
objects = [text1, text2, text3]
|
319 |
elif text2:
|
320 |
-
results =
|
321 |
objects = [text1, text2]
|
322 |
else:
|
323 |
-
results =
|
324 |
objects = [text1]
|
325 |
-
return enhance_descriptions(results, objects
|
326 |
|
327 |
-
|
|
|
|
|
328 |
def format_results(results):
|
329 |
formatted = ""
|
330 |
for category, result in results.items():
|
331 |
-
formatted += f"## {category}\n**기본
|
332 |
return formatted
|
333 |
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
time.sleep(0.3)
|
339 |
-
text1 = text1.strip() if text1 else None
|
340 |
-
text2 = text2.strip() if text2 else None
|
341 |
-
text3 = text3.strip() if text3 else None
|
342 |
-
if not text1:
|
343 |
-
messages.append("오류: 최소 하나의 키워드를 입력해주세요.")
|
344 |
-
return "\n\n".join(messages)
|
345 |
-
messages.append("물리적 변화 아이디어 생성 중...")
|
346 |
-
time.sleep(0.3)
|
347 |
-
results = generate_physical_transformations(text1, text2, text3)
|
348 |
-
messages.append("결과 포맷팅 중...")
|
349 |
-
time.sleep(0.3)
|
350 |
-
formatted = format_results(results)
|
351 |
-
messages.append("완료!")
|
352 |
-
messages.append(formatted)
|
353 |
-
return "\n\n".join(messages)
|
354 |
-
|
355 |
-
# Process marketing inputs and generate transformations
|
356 |
-
def process_marketing_inputs(text1, text2, text3):
|
357 |
messages = []
|
358 |
messages.append("입력값 확인 중...")
|
359 |
time.sleep(0.3)
|
@@ -363,24 +242,34 @@ def process_marketing_inputs(text1, text2, text3):
|
|
363 |
if not text1:
|
364 |
messages.append("오류: 최소 하나의 키워드를 입력해주세요.")
|
365 |
return "\n\n".join(messages)
|
366 |
-
|
|
|
367 |
time.sleep(0.3)
|
368 |
-
results =
|
|
|
369 |
messages.append("결과 포맷팅 중...")
|
370 |
time.sleep(0.3)
|
371 |
formatted = format_results(results)
|
|
|
372 |
messages.append("완료!")
|
373 |
messages.append(formatted)
|
|
|
374 |
return "\n\n".join(messages)
|
375 |
|
376 |
-
|
|
|
|
|
377 |
def get_warning_message():
|
378 |
if not GEMINI_API_KEY:
|
379 |
return "⚠️ 환경 변수 GEMINI_API_KEY가 설정되지 않았습니다. Gemini API 키를 설정하세요."
|
380 |
return ""
|
381 |
|
382 |
-
|
383 |
-
|
|
|
|
|
|
|
|
|
384 |
gr.HTML("""
|
385 |
<style>
|
386 |
body { background: linear-gradient(135deg, #e0eafc, #cfdef3); font-family: 'Arial', sans-serif; }
|
@@ -392,23 +281,26 @@ with gr.Blocks(title="키워드 기반 창의적 아이디어 생성기", theme=
|
|
392 |
.gr-button { background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; }
|
393 |
</style>
|
394 |
""")
|
395 |
-
|
396 |
-
gr.Markdown("
|
|
|
|
|
397 |
warning = gr.Markdown(get_warning_message())
|
|
|
|
|
398 |
with gr.Row():
|
399 |
with gr.Column(scale=1):
|
400 |
text_input1 = gr.Textbox(label="키워드 1 (필수)", placeholder="예: 스마트폰")
|
401 |
text_input2 = gr.Textbox(label="키워드 2 (선택)", placeholder="예: 인공지능")
|
402 |
text_input3 = gr.Textbox(label="키워드 3 (선택)", placeholder="예: 헬스케어")
|
403 |
submit_button = gr.Button("아이디어 생성하기")
|
|
|
|
|
404 |
with gr.Column(scale=2):
|
405 |
-
with gr.
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
marketing_output = gr.Markdown(label="마케팅 변화 아이디어 결과")
|
410 |
-
|
411 |
-
# Add some example combinations
|
412 |
gr.Examples(
|
413 |
examples=[
|
414 |
["스마트폰", "", ""],
|
@@ -419,9 +311,10 @@ with gr.Blocks(title="키워드 기반 창의적 아이디어 생성기", theme=
|
|
419 |
inputs=[text_input1, text_input2, text_input3],
|
420 |
)
|
421 |
|
422 |
-
#
|
423 |
-
submit_button.click(fn=
|
424 |
-
|
|
|
425 |
|
426 |
if __name__ == "__main__":
|
427 |
-
demo.launch(debug=True)
|
|
|
5 |
import logging
|
6 |
import google.generativeai as genai
|
7 |
|
8 |
+
# 로깅 설정
|
9 |
logging.basicConfig(
|
10 |
level=logging.INFO,
|
11 |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
|
16 |
)
|
17 |
logger = logging.getLogger("idea_generator")
|
18 |
|
19 |
+
# Gemini API 키는 환경 변수 GEMINI_API_KEY에서 가져옵니다.
|
20 |
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
21 |
genai.configure(api_key=GEMINI_API_KEY)
|
22 |
|
23 |
+
# 슬래시("/")가 포함된 변환 문자열에서 두 옵션 중 하나만 선택하는 헬퍼 함수
|
24 |
def choose_alternative(transformation):
|
25 |
if "/" not in transformation:
|
26 |
return transformation
|
|
|
42 |
else:
|
43 |
return random.choice([left, right])
|
44 |
|
45 |
+
# 창의적인 모델/컨셉/형상 변화 아이디어를 위한 카테고리 (기존 '물리적 변화' 사전 유지)
|
46 |
physical_transformation_categories = {
|
47 |
"공간적 변화": [
|
48 |
"전진/후진 이동", "좌/우 이동", "상승/하강", "피치 회전", "요 회전", "롤 회전",
|
|
|
101 |
]
|
102 |
}
|
103 |
|
104 |
+
##############################################################################
|
105 |
+
# Gemini API 호출 함수 (예: gemini-2.0-flash-thinking-exp-01-21 -> 다른 모델 사용 시 수정)
|
106 |
+
##############################################################################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
def query_gemini_api(prompt):
|
108 |
try:
|
109 |
+
# 예시: 기존 gemini-2.0... 대신, 다른 모델이 필요하다면 교체하세요.
|
110 |
model = genai.GenerativeModel('gemini-2.0-flash-thinking-exp-01-21')
|
111 |
|
|
|
112 |
response = model.generate_content(prompt)
|
113 |
+
|
114 |
+
# 응답 구조 방어적으로 처리
|
115 |
try:
|
|
|
116 |
if hasattr(response, 'text'):
|
117 |
return response.text
|
118 |
|
|
|
119 |
if hasattr(response, 'candidates') and response.candidates:
|
120 |
if len(response.candidates) > 0:
|
121 |
candidate = response.candidates[0]
|
|
|
|
|
122 |
if hasattr(candidate, 'content'):
|
123 |
content = candidate.content
|
|
|
124 |
if hasattr(content, 'parts') and content.parts:
|
125 |
if len(content.parts) > 0:
|
126 |
return content.parts[0].text
|
|
|
|
|
127 |
if hasattr(response, 'parts') and response.parts:
|
128 |
if len(response.parts) > 0:
|
129 |
return response.parts[0].text
|
130 |
+
|
|
|
131 |
return "Unable to generate a response. API response structure is different than expected."
|
132 |
|
133 |
except Exception as inner_e:
|
|
|
136 |
|
137 |
except Exception as e:
|
138 |
logger.error(f"Error calling Gemini API: {e}")
|
|
|
|
|
139 |
if "API key not valid" in str(e):
|
140 |
return "API key is not valid. Please check your GEMINI_API_KEY environment variable."
|
|
|
141 |
return f"An error occurred while calling the API: {str(e)}"
|
142 |
|
143 |
+
##############################################################################
|
144 |
+
# 설명 확장 함수: "모델/컨셉/형상의 변화에 대한 이해와 혁신 포인트, 기능성 등을 중심"으로
|
145 |
+
##############################################################################
|
146 |
+
def enhance_with_llm(base_description, obj_name, category):
|
147 |
+
prompt = f"""
|
148 |
+
다음은 '{obj_name}'의 '{category}' 관련 간단한 설명입니다:
|
149 |
"{base_description}"
|
150 |
+
|
151 |
+
위 내용을 보다 구체화하여,
|
152 |
+
1) 창의적인 모델/컨셉/형상의 변화에 대한 이해,
|
153 |
+
2) 혁신 포인트와 기능성 등을 중심으로
|
154 |
+
3~4문장의 아이디어로 확장해 주세요.
|
155 |
+
"""
|
156 |
return query_gemini_api(prompt)
|
157 |
|
158 |
+
##############################################################################
|
159 |
+
# 단일 키워드(오브젝트)에 대한 "창의적 변화 아이디어" 생성
|
160 |
+
##############################################################################
|
161 |
+
def generate_single_object_transformations(obj):
|
162 |
results = {}
|
163 |
for category, transformations in physical_transformation_categories.items():
|
164 |
transformation = choose_alternative(random.choice(transformations))
|
|
|
166 |
results[category] = {"base": base_description, "enhanced": None}
|
167 |
return results
|
168 |
|
169 |
+
##############################################################################
|
170 |
+
# 두 키워드에 대한 "창의적 변화 아이디어" 생성
|
171 |
+
##############################################################################
|
172 |
+
def generate_two_objects_interaction(obj1, obj2):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
results = {}
|
174 |
for category, transformations in physical_transformation_categories.items():
|
175 |
transformation = choose_alternative(random.choice(transformations))
|
|
|
181 |
results[category] = {"base": base_description, "enhanced": None}
|
182 |
return results
|
183 |
|
184 |
+
##############################################################################
|
185 |
+
# 세 키워드에 대한 "창의적 변화 아이디어" 생성
|
186 |
+
##############################################################################
|
187 |
+
def generate_three_objects_interaction(obj1, obj2, obj3):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
results = {}
|
189 |
for category, transformations in physical_transformation_categories.items():
|
190 |
transformation = choose_alternative(random.choice(transformations))
|
|
|
196 |
results[category] = {"base": base_description, "enhanced": None}
|
197 |
return results
|
198 |
|
199 |
+
##############################################################################
|
200 |
+
# 생성된 기본 설명을 LLM을 통해 확장
|
201 |
+
##############################################################################
|
202 |
+
def enhance_descriptions(results, objects):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
203 |
obj_name = " 및 ".join([obj for obj in objects if obj])
|
204 |
for category, result in results.items():
|
205 |
+
result["enhanced"] = enhance_with_llm(result["base"], obj_name, category)
|
206 |
return results
|
207 |
|
208 |
+
##############################################################################
|
209 |
+
# 사용자 입력(최대 3개 키워드)에 따라 창의적 변화 아이디어 생성
|
210 |
+
##############################################################################
|
211 |
+
def generate_transformations(text1, text2=None, text3=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
if text2 and text3:
|
213 |
+
results = generate_three_objects_interaction(text1, text2, text3)
|
214 |
objects = [text1, text2, text3]
|
215 |
elif text2:
|
216 |
+
results = generate_two_objects_interaction(text1, text2)
|
217 |
objects = [text1, text2]
|
218 |
else:
|
219 |
+
results = generate_single_object_transformations(text1)
|
220 |
objects = [text1]
|
221 |
+
return enhance_descriptions(results, objects)
|
222 |
|
223 |
+
##############################################################################
|
224 |
+
# 결과 포맷팅
|
225 |
+
##############################################################################
|
226 |
def format_results(results):
|
227 |
formatted = ""
|
228 |
for category, result in results.items():
|
229 |
+
formatted += f"## {category}\n**기본 아이디어**: {result['base']}\n\n**확장된 아이디어**: {result['enhanced']}\n\n---\n\n"
|
230 |
return formatted
|
231 |
|
232 |
+
##############################################################################
|
233 |
+
# Gradio UI에서 호출할 함수
|
234 |
+
##############################################################################
|
235 |
+
def process_inputs(text1, text2, text3):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
messages = []
|
237 |
messages.append("입력값 확인 중...")
|
238 |
time.sleep(0.3)
|
|
|
242 |
if not text1:
|
243 |
messages.append("오류: 최소 하나의 키워드를 입력해주세요.")
|
244 |
return "\n\n".join(messages)
|
245 |
+
|
246 |
+
messages.append("창의적인 모델/컨셉/형상 변화 아이디어 생성 중...")
|
247 |
time.sleep(0.3)
|
248 |
+
results = generate_transformations(text1, text2, text3)
|
249 |
+
|
250 |
messages.append("결과 포맷팅 중...")
|
251 |
time.sleep(0.3)
|
252 |
formatted = format_results(results)
|
253 |
+
|
254 |
messages.append("완료!")
|
255 |
messages.append(formatted)
|
256 |
+
|
257 |
return "\n\n".join(messages)
|
258 |
|
259 |
+
##############################################################################
|
260 |
+
# API 키 경고 메시지
|
261 |
+
##############################################################################
|
262 |
def get_warning_message():
|
263 |
if not GEMINI_API_KEY:
|
264 |
return "⚠️ 환경 변수 GEMINI_API_KEY가 설정되지 않았습니다. Gemini API 키를 설정하세요."
|
265 |
return ""
|
266 |
|
267 |
+
##############################################################################
|
268 |
+
# Gradio UI
|
269 |
+
##############################################################################
|
270 |
+
with gr.Blocks(title="키워드 기반 창의적 변화 아이디어 생성기",
|
271 |
+
theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo:
|
272 |
+
|
273 |
gr.HTML("""
|
274 |
<style>
|
275 |
body { background: linear-gradient(135deg, #e0eafc, #cfdef3); font-family: 'Arial', sans-serif; }
|
|
|
281 |
.gr-button { background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; }
|
282 |
</style>
|
283 |
""")
|
284 |
+
|
285 |
+
gr.Markdown("# 🚀 키워드 기반 창의적 변화 아이디어 생성기")
|
286 |
+
gr.Markdown("입력한 **키워드**(최대 3개)를 바탕으로, **창의적인 모델/컨셉/형상 변화**에 대한 이해와 **혁신 포인트**, **기능성** 등을 중심으로 확장된 아이디어를 제시합니다.")
|
287 |
+
|
288 |
warning = gr.Markdown(get_warning_message())
|
289 |
+
|
290 |
+
# 좌측 입력 영역
|
291 |
with gr.Row():
|
292 |
with gr.Column(scale=1):
|
293 |
text_input1 = gr.Textbox(label="키워드 1 (필수)", placeholder="예: 스마트폰")
|
294 |
text_input2 = gr.Textbox(label="키워드 2 (선택)", placeholder="예: 인공지능")
|
295 |
text_input3 = gr.Textbox(label="키워드 3 (선택)", placeholder="예: 헬스케어")
|
296 |
submit_button = gr.Button("아이디어 생성하기")
|
297 |
+
|
298 |
+
# 우측 출력 영역 (탭은 하나만 사용)
|
299 |
with gr.Column(scale=2):
|
300 |
+
with gr.TabItem("창의적인 모델/컨셉/형상 변화 아이디어", id="creative_tab"):
|
301 |
+
idea_output = gr.Markdown(label="아이디어 결과")
|
302 |
+
|
303 |
+
# 예시 입력
|
|
|
|
|
|
|
304 |
gr.Examples(
|
305 |
examples=[
|
306 |
["스마트폰", "", ""],
|
|
|
311 |
inputs=[text_input1, text_input2, text_input3],
|
312 |
)
|
313 |
|
314 |
+
# 버튼 이벤트: 첫 번째 탭에만 연결
|
315 |
+
submit_button.click(fn=process_inputs,
|
316 |
+
inputs=[text_input1, text_input2, text_input3],
|
317 |
+
outputs=idea_output)
|
318 |
|
319 |
if __name__ == "__main__":
|
320 |
+
demo.launch(debug=True)
|