Spaces:
Runtime error
Runtime error
import os | |
import gradio as gr | |
import random | |
import time | |
import logging | |
import google.generativeai as genai | |
# Configure logging | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
handlers=[ | |
logging.FileHandler("api_debug.log"), | |
logging.StreamHandler() | |
] | |
) | |
logger = logging.getLogger("idea_generator") | |
# Get Gemini API Key from environment variable | |
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") | |
genai.configure(api_key=GEMINI_API_KEY) | |
# Helper function to select one option from a transformation string with a slash | |
def choose_alternative(transformation): | |
if "/" not in transformation: | |
return transformation | |
parts = transformation.split("/") | |
if len(parts) != 2: | |
return random.choice([part.strip() for part in parts]) | |
left = parts[0].strip() | |
right = parts[1].strip() | |
if " " in left: | |
tokens = left.split(" ", 1) | |
prefix = tokens[0] | |
if not right.startswith(prefix): | |
option1 = left | |
option2 = prefix + " " + right | |
else: | |
option1 = left | |
option2 = right | |
return random.choice([option1, option2]) | |
else: | |
return random.choice([left, right]) | |
# Physical transformation categories and related changes | |
physical_transformation_categories = { | |
"공간적 변화": [ | |
"전진/후진 이동", "좌/우 이동", "상승/하강", "피치 회전", "요 회전", "롤 회전", | |
"궤도 운동", "나선형 이동", "관성 드리프트", "자이로스코픽 세차 운동", "비대칭 회전", | |
"진자 운동", "탄도학적 이동", "무중력 상태에서의 부유" | |
], | |
"물리적 형상 변화": [ | |
"부피 증가/감소", "길이 신장/수축", "너비 확장/축소", "높이 증가/감소", | |
"밀도 변화", "무게 증가/감소", "형태 변형", "위상 변화", "비등방성 변형", | |
"비선형 변형", "뒤틀림/비틀림", "수축/팽창 비율 변화", "모서리 라운딩/샤프닝", | |
"파단/균열 진행", "세그먼트화" | |
], | |
"표면/외관 변화": [ | |
"색상 변화", "텍스처 변화", "투명도/불투명도 변화", "광택/무광 변화", | |
"반사율 변화", "패턴 변화", "이색성 효과", "광변색 효과", "열변색 효과", | |
"홀로그래픽 패턴 변화", "프레넬 반사", "표면 모핑", "나노구조 패턴 변화", | |
"자가 청소 효과" | |
], | |
"물질 상태 변화": [ | |
"고체화/액화/기화", "결정화/용해", "산화/부식", "경화/연화", "초임계 상태 전환", | |
"비정질화/결정화", "상분리", "콜로이드 형성/분해", "겔화/졸화", "준안정 상태 전이", | |
"자기조립/분해", "상전이 히스테리시스", "융해", "응고" | |
], | |
"열역학적 변화": [ | |
"온도 상승/냉각", "열팽창/수축", "열 전도/단열", "압력 증가/감소", "단열 자화/탈자화", | |
"엔트로피 변화", "열전기 효과", "자기열량 효과", "상변화 축열/방열", "열 스트레스 발생/완화", | |
"열충격 효과", "복사 냉각/가열" | |
], | |
"운동학적 변화": [ | |
"가속/감속", "등속 운동", "진동/진동 감쇠", "충돌/반발", "회전 속도 증가/감소", | |
"각속도 변화", "카오스적 운동", "스틱-슬립 현상", "공진/반공진", "유체역학적 양력/항력 변화", | |
"댐핑 계수 변화", "하모닉 모션 합성", "비뉴턴 유체 거동", "회전-병진 운동 커플링" | |
], | |
"구조적 변화": [ | |
"부품 추가/제거", "조립/분해", "접이/펼침", "변형/복원", "위상최적화 형상 변화", | |
"자가 재구성", "프랙탈 패턴 형성/소멸", "세포자동자 패턴 변화", "모듈식 변형", | |
"발현적 구조 형성", "형상 기억 효과", "4D 프린팅 구조 변화", "부분 제거", "부분 교환", | |
"결합", "분리" | |
], | |
"전기/자기적 변화": [ | |
"자화/탈자화", "전하 증가/감소", "전기장 생성/소멸", "자기장 생성/소멸", "초전도 상태 전이", | |
"강유전/반강유전 전이", "양자 스핀 상태 변화", "플라즈마 상태 형성/소멸", "스핀파 전파", | |
"광전 효과", "압전 효과", "홀 효과" | |
], | |
"화학적 변화": [ | |
"표면 코팅 변화", "물질 조성 변화", "화학 반응에 의한 변화", "촉매 활성화/비활성화", | |
"광화학 반응", "전기화학 반응", "자기조립 단분자막 형성", "분자컴퓨팅 변화", | |
"생체모방 표면 변화", "스마트 폴리머 반응", "화학적 진동 반응", "산화", "환원", | |
"중합", "가수분해", "융합", "방사화" | |
], | |
"시간 관련 변화": [ | |
"노화/풍화", "마모/부식", "퇴색/변색", "손상/복구", "수명 주기 단계 변화", | |
"사용자 인터랙션 기반 적응", "학습기반 형상 최적화", "시간적 물성 변화", "집단 기억 효과", | |
"문화적 의미 변화", "시간 지연 응답", "이력 의존적 변화", "퍼지 시간 거동", "진화적 변화", | |
"주기적 재생/재생성" | |
] | |
} | |
# Marketing transformation categories | |
marketing_transformation_categories = { | |
"브랜드/아이덴티티 변화": [ | |
"브랜드명 변경/리브랜딩", "로고 재설계/현대화", "슬로건 업데이트/변경", | |
"브랜드 컬러 팔레트 변경", "브랜드 보이스/톤 조정", "브랜드 포지셔닝 재정립", | |
"브랜드 스토리 재구성", "브랜드 아키텍처 재구성", "브랜드 퍼스널리티 진화", | |
"브랜드 이미지 재정립" | |
], | |
"제품/서비스 변화": [ | |
"기능적 업그레이드/다운그레이드", "포장/패키징 재설계", "제품 사이즈/용량 조정", | |
"SKU 확장/축소", "맞춤형/개인화 옵션 추가", "프리미엄/베이직 라인 출시", | |
"한정판/시즌 에디션 출시", "콜라보레이션/크로스오버 제품", "지속가능성 개선", | |
"번들링/언번들링 전략 변경" | |
], | |
"가격 전략 변화": [ | |
"프리미엄/럭셔리 가격 포지셔닝", "가격 하락/경제적 포지셔닝", "가치 기반 가격 책정", | |
"동적 가격 정책 도입", "멤버십/구독 모델 전환", "가격 세분화 전략", | |
"프로모션 가격 전략 변경", "심리적 가격 책정 적용", "번들 가격 전략 도입", | |
"프리미엄/이코노미 이중 전략" | |
], | |
"프로모션/커뮤니케이션 변화": [ | |
"메시지 프레이밍 변경", "타겟 오디언스 확장/재정의", "감성적/이성적 소구점 전환", | |
"스토리텔링 방식 변화", "콘텐츠 마케팅 전략 조정", "크리에이티브 방향성 변경", | |
"광고 캠페인 재설계", "브랜드 앰배서더/인플루언서 교체", "언어/트론 조정", | |
"비주얼 아이덴티티 업데이트" | |
], | |
"채널/유통 변화": [ | |
"온라인/오프라인 전략 전환", "옴니채널 통합/분리", "유통 파트너십 확장/축소", | |
"D2C(Direct-to-Consumer) 모델 도입", "리테일 경험 재설계", "서비스 딜리버리 방식 변경", | |
"팝업/이벤트 기반 유통 전략", "지역적 확장/축소", "파트너 생태계 재구성", | |
"디지털 마켓플레이스 전략 변화" | |
], | |
"고객 경험 변화": [ | |
"고객 여정 재설계", "퍼스널라이제이션/맞춤화 강화", "서비스 레벨 조정", | |
"로열티 프로그램 재구성", "고객 참여 메커니즘 변화", "사용자 인터페이스/UX 개선", | |
"고객 서비스 프로토콜 변경", "피드백 루프 재설계", "자가 서비스 옵션 확장", | |
"체험형 마케팅 요소 강화" | |
], | |
"디지털 마케팅 변화": [ | |
"검색 최적화(SEO) 전략 변경", "소셜 미디어 플랫폼 포트폴리오 조정", "콘텐츠 타입/포맷 다양화", | |
"데이터 기반 타겟팅 정교화", "마케팅 자동화 확장/조정", "디지털 광고 알고리즘 최적화", | |
"이메일 마케팅 전략 변화", "모바일 최적화 전략 강화", "AR/VR 마케팅 요소 통합", | |
"인공지능 기반 마케팅 도입" | |
], | |
"시장 포지셔닝 변화": [ | |
"시장 세그먼트 재정의", "경쟁사 대비 포지셔닝 변경", "틈새시장/메인스트림 전략 전환", | |
"산업/카테고리 횡단 재포지셔닝", "지리적 타겟 시장 확장/축소", "글로벌/로컬 포지셔닝 조정", | |
"문화적 맥락 적응", "주요 시장 메시지 재정의", "특성 차별화 강화/변경", | |
"가치 제안 재구성" | |
], | |
"혁신/트렌드 반응": [ | |
"첨단 기술 통합", "트렌드 얼리어답터/팔로워 포지셔닝", "지속가능성/친환경 전략 강화", | |
"사회적 책임 이니셔티브 도입", "디지털 트랜스포메이션 가속화", "헬스/웰빙 요소 강화", | |
"세대별 트렌드 반응 적용", "미래지향적 혁신 커뮤니케이션", "문화적 현상 연계 마케팅", | |
"크로스 인더스트리 혁신 적용" | |
], | |
"데이터/분석 기반 변화": [ | |
"고객 데이터 수집 방식 변경", "예측 분석 모델 도입/강화", "A/B 테스팅 체계 구축/확장", | |
"소비자 인사이트 발굴 방식 변화", "마케팅 ROI 측정 프레임워크 변경", "실시간 데이터 활용 체계 구축", | |
"프라이버시 중심 데이터 전략", "세그먼테이션 모델 고도화", "디지털 행동 분석 체계 강화", | |
"통합 마케팅 데이터 대시보드 구축" | |
] | |
} | |
def query_gemini_api(prompt): | |
try: | |
# Use a stable model | |
model = genai.GenerativeModel('gemini-pro') | |
# Generate content with a simple approach | |
response = model.generate_content(prompt) | |
# Check the response - defensive handling | |
try: | |
# First try .text attribute | |
if hasattr(response, 'text'): | |
return response.text | |
# Try accessing candidates (safely) | |
if hasattr(response, 'candidates') and response.candidates: | |
if len(response.candidates) > 0: | |
candidate = response.candidates[0] | |
# Try accessing content and parts | |
if hasattr(candidate, 'content'): | |
content = candidate.content | |
if hasattr(content, 'parts') and content.parts: | |
if len(content.parts) > 0: | |
return content.parts[0].text | |
# Direct parts access attempt | |
if hasattr(response, 'parts') and response.parts: | |
if len(response.parts) > 0: | |
return response.parts[0].text | |
# Fallback if all attempts fail | |
return "Unable to generate a response. API response structure is different than expected." | |
except Exception as inner_e: | |
logger.error(f"Error processing response: {inner_e}") | |
return f"An error occurred while processing the response: {str(inner_e)}" | |
except Exception as e: | |
logger.error(f"Error calling Gemini API: {e}") | |
# Check for API key validation error | |
if "API key not valid" in str(e): | |
return "API key is not valid. Please check your GEMINI_API_KEY environment variable." | |
return f"An error occurred while calling the API: {str(e)}" | |
# Generate enhanced descriptions without unnecessary questions or explanations | |
def enhance_with_llm(base_description, obj_name, category, is_marketing=False): | |
domain = "마케팅" if is_marketing else "물리적" | |
prompt = f"""다음은 '{obj_name}'의 '{domain} {category}' 관련 간단한 설명입니다: | |
"{base_description}" | |
해당 설명을 3-4문장의 창의적인 답변으로 확장하여 출력하라.""" | |
return query_gemini_api(prompt) | |
# Generate physical transformations for a single object | |
def generate_single_object_physical_transformations(obj): | |
results = {} | |
for category, transformations in physical_transformation_categories.items(): | |
transformation = choose_alternative(random.choice(transformations)) | |
base_description = f"{obj}이(가) {transformation} 현상을 보인다" | |
results[category] = {"base": base_description, "enhanced": None} | |
return results | |
# Generate marketing transformations for a single object | |
def generate_single_object_marketing_transformations(obj): | |
results = {} | |
for category, transformations in marketing_transformation_categories.items(): | |
transformation = choose_alternative(random.choice(transformations)) | |
base_description = f"{obj}이(가) {transformation} 전략을 실행했다" | |
results[category] = {"base": base_description, "enhanced": None} | |
return results | |
# Generate physical transformations for two objects | |
def generate_two_objects_physical_interaction(obj1, obj2): | |
results = {} | |
for category, transformations in physical_transformation_categories.items(): | |
transformation = choose_alternative(random.choice(transformations)) | |
template = random.choice([ | |
"{obj1}이(가) {obj2}에 결합하여 {change}가 발생했다", | |
"{obj1}과(와) {obj2}이(가) 충돌하면서 {change}가 일어났다" | |
]) | |
base_description = template.format(obj1=obj1, obj2=obj2, change=transformation) | |
results[category] = {"base": base_description, "enhanced": None} | |
return results | |
# Generate marketing transformations for two objects | |
def generate_two_objects_marketing_interaction(obj1, obj2): | |
results = {} | |
for category, transformations in marketing_transformation_categories.items(): | |
transformation = choose_alternative(random.choice(transformations)) | |
template = random.choice([ | |
"{obj1}이(가) {obj2}의 브랜드 특성을 활용하여 {change}를 진행했다", | |
"{obj1}과(와) {obj2}의 협업으로 {change}가 시장에 도입되었다" | |
]) | |
base_description = template.format(obj1=obj1, obj2=obj2, change=transformation) | |
results[category] = {"base": base_description, "enhanced": None} | |
return results | |
# Generate physical transformations for three objects | |
def generate_three_objects_physical_interaction(obj1, obj2, obj3): | |
results = {} | |
for category, transformations in physical_transformation_categories.items(): | |
transformation = choose_alternative(random.choice(transformations)) | |
template = random.choice([ | |
"{obj1}, {obj2}, {obj3}이(가) 삼각형 구조로 결합하여 {change}가 발생했다", | |
"{obj1}이(가) {obj2}와(과) {obj3} 사이에서 매개체 역할을 하며 {change}를 촉진했다" | |
]) | |
base_description = template.format(obj1=obj1, obj2=obj2, obj3=obj3, change=transformation) | |
results[category] = {"base": base_description, "enhanced": None} | |
return results | |
# Generate marketing transformations for three objects | |
def generate_three_objects_marketing_interaction(obj1, obj2, obj3): | |
results = {} | |
for category, transformations in marketing_transformation_categories.items(): | |
transformation = choose_alternative(random.choice(transformations)) | |
template = random.choice([ | |
"{obj1}, {obj2}, {obj3}이(가) 공동 마케팅으로 {change}가 시너지를 창출했다", | |
"{obj1}이(가) {obj2}와(과) {obj3}의 고객층을 통합하여 {change}로 새로운 시장을 창출했다" | |
]) | |
base_description = template.format(obj1=obj1, obj2=obj2, obj3=obj3, change=transformation) | |
results[category] = {"base": base_description, "enhanced": None} | |
return results | |
# Enhance descriptions using the LLM | |
def enhance_descriptions(results, objects, is_marketing=False): | |
obj_name = " 및 ".join([obj for obj in objects if obj]) | |
for category, result in results.items(): | |
result["enhanced"] = enhance_with_llm(result["base"], obj_name, category, is_marketing) | |
return results | |
# Generate physical transformations based on number of objects | |
def generate_physical_transformations(text1, text2=None, text3=None): | |
if text2 and text3: | |
results = generate_three_objects_physical_interaction(text1, text2, text3) | |
objects = [text1, text2, text3] | |
elif text2: | |
results = generate_two_objects_physical_interaction(text1, text2) | |
objects = [text1, text2] | |
else: | |
results = generate_single_object_physical_transformations(text1) | |
objects = [text1] | |
return enhance_descriptions(results, objects, is_marketing=False) | |
# Generate marketing transformations based on number of objects | |
def generate_marketing_transformations(text1, text2=None, text3=None): | |
if text2 and text3: | |
results = generate_three_objects_marketing_interaction(text1, text2, text3) | |
objects = [text1, text2, text3] | |
elif text2: | |
results = generate_two_objects_marketing_interaction(text1, text2) | |
objects = [text1, text2] | |
else: | |
results = generate_single_object_marketing_transformations(text1) | |
objects = [text1] | |
return enhance_descriptions(results, objects, is_marketing=True) | |
# Format results for display | |
def format_results(results): | |
formatted = "" | |
for category, result in results.items(): | |
formatted += f"## {category}\n**기본 변화**: {result['base']}\n\n**자세한 설명**: {result['enhanced']}\n\n---\n\n" | |
return formatted | |
# Process physical inputs and generate transformations | |
def process_physical_inputs(text1, text2, text3): | |
messages = [] | |
messages.append("입력값 확인 중...") | |
time.sleep(0.3) | |
text1 = text1.strip() if text1 else None | |
text2 = text2.strip() if text2 else None | |
text3 = text3.strip() if text3 else None | |
if not text1: | |
messages.append("오류: 최소 하나의 키워드를 입력해주세요.") | |
return "\n\n".join(messages) | |
messages.append("물리적 변화 아이디어 생성 중...") | |
time.sleep(0.3) | |
results = generate_physical_transformations(text1, text2, text3) | |
messages.append("결과 포맷팅 중...") | |
time.sleep(0.3) | |
formatted = format_results(results) | |
messages.append("완료!") | |
messages.append(formatted) | |
return "\n\n".join(messages) | |
# Process marketing inputs and generate transformations | |
def process_marketing_inputs(text1, text2, text3): | |
messages = [] | |
messages.append("입력값 확인 중...") | |
time.sleep(0.3) | |
text1 = text1.strip() if text1 else None | |
text2 = text2.strip() if text2 else None | |
text3 = text3.strip() if text3 else None | |
if not text1: | |
messages.append("오류: 최소 하나의 키워드를 입력해주세요.") | |
return "\n\n".join(messages) | |
messages.append("마케팅 변화 아이디어 생성 중...") | |
time.sleep(0.3) | |
results = generate_marketing_transformations(text1, text2, text3) | |
messages.append("결과 포맷팅 중...") | |
time.sleep(0.3) | |
formatted = format_results(results) | |
messages.append("완료!") | |
messages.append(formatted) | |
return "\n\n".join(messages) | |
# Check for API key and return warning message if needed | |
def get_warning_message(): | |
if not GEMINI_API_KEY: | |
return "⚠️ 환경 변수 GEMINI_API_KEY가 설정되지 않았습니다. Gemini API 키를 설정하세요." | |
return "" | |
# Create the Gradio UI | |
with gr.Blocks(title="키워드 기반 창의적 아이디어 생성기", theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo: | |
gr.HTML(""" | |
<style> | |
body { background: linear-gradient(135deg, #e0eafc, #cfdef3); font-family: 'Arial', sans-serif; } | |
.gradio-container { padding: 20px; } | |
h1, h2 { text-align: center; } | |
h1 { color: #333; } | |
h2 { color: #555; } | |
.output { background-color: #ffffff; padding: 15px; border-radius: 8px; } | |
.gr-button { background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; } | |
</style> | |
""") | |
gr.Markdown("# 🚀 키워드 기반 창의적 아이디어 생성기") | |
gr.Markdown("입력한 키워드를 바탕으로 창의적인 물리적/마케팅 변화 아이디어를 도출합니다. 최대 3개의 키워드를 입력할 수 있습니다.") | |
warning = gr.Markdown(get_warning_message()) | |
with gr.Row(): | |
with gr.Column(scale=1): | |
text_input1 = gr.Textbox(label="키워드 1 (필수)", placeholder="예: 스마트폰") | |
text_input2 = gr.Textbox(label="키워드 2 (선택)", placeholder="예: 인공지능") | |
text_input3 = gr.Textbox(label="키워드 3 (선택)", placeholder="예: 헬스케어") | |
submit_button = gr.Button("아이디어 생성하기") | |
with gr.Column(scale=2): | |
with gr.Tabs(): | |
with gr.TabItem("물리적 변화 아이디어", id="physical_tab"): | |
physical_output = gr.Markdown(label="물리적 변화 아이디어 결과") | |
with gr.TabItem("마케팅 변화 아이디어", id="marketing_tab"): | |
marketing_output = gr.Markdown(label="마케팅 변화 아이디어 결과") | |
# Add some example combinations | |
gr.Examples( | |
examples=[ | |
["스마트폰", "", ""], | |
["커피", "책", ""], | |
["자동차", "로봇", "인공지능"], | |
["운동화", "웨어러블", "건강"], | |
], | |
inputs=[text_input1, text_input2, text_input3], | |
) | |
# Connect the button click events to functions | |
submit_button.click(fn=process_physical_inputs, inputs=[text_input1, text_input2, text_input3], outputs=physical_output) | |
submit_button.click(fn=process_marketing_inputs, inputs=[text_input1, text_input2, text_input3], outputs=marketing_output) | |
if __name__ == "__main__": | |
demo.launch(debug=True) |