import os import gradio as gr import random import time import logging import google.generativeai as genai 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") GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") genai.configure(api_key=GEMINI_API_KEY) 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 = { "공간 이동": [ "앞/뒤 이동", "좌/우 이동", "위/아래 이동", "세로축 회전(고개 끄덕임)", "가로축 회전(고개 젓기)", "길이축 회전(옆으로 기울임)", "원 운동", "나선형 이동", "관성에 의한 미끄러짐", "회전축 변화", "불규칙 회전", "흔들림 운동", "포물선 이동", "무중력 부유", "수면 위 부유", "점프/도약", "슬라이딩", "롤링", "자유 낙하", "왕복 운동", "탄성 튕김", "관통", "회피 움직임", "지그재그 이동", "스윙 운동" ], "크기와 형태 변화": [ "부피 늘어남/줄어듦", "길이 늘어남/줄어듦", "너비 늘어남/줄어듦", "높이 늘어남/줄어듦", "밀도 변화", "무게 증가/감소", "모양 변형", "상태 변화", "불균등 변형", "복잡한 형태 변형", "비틀림/꼬임", "불균일한 확장/축소", "모서리 둥글게/날카롭게", "깨짐/갈라짐", "여러 조각 나눠짐", "물 저항", "먼지 저항", "찌그러짐/복원", "접힘/펼쳐짐", "압착/팽창", "늘어남/수축", "구겨짐/평평해짐", "뭉개짐/단단해짐", "말림/펴짐", "꺾임/구부러짐" ], # ... (중간 생략: 나머지 카테고리는 동일) # 생략된 부분도 기존 코드 그대로 유지 } def query_gemini_api(prompt): try: model = genai.GenerativeModel('gemini-2.0-flash-thinking-exp-01-21') response = model.generate_content(prompt) try: if hasattr(response, 'text'): return response.text if hasattr(response, 'candidates') and response.candidates: if len(response.candidates) > 0: candidate = response.candidates[0] if hasattr(candidate, 'content'): content = candidate.content if hasattr(content, 'parts') and content.parts: if len(content.parts) > 0: return content.parts[0].text if hasattr(response, 'parts') and response.parts: if len(response.parts) > 0: return response.parts[0].text 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}") 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)}" def enhance_with_llm(base_description, obj_name, category): prompt = f""" 다음은 '{obj_name}'의 '{category}' 관련 간단한 설명입니다: "{base_description}" 위 내용을 보다 구체화하여, 1) 창의적인 모델/컨셉/형상의 변화에 대한 이해, 2) 혁신 포인트와 기능성 등을 중심으로 3~4문장의 아이디어로 확장해 주세요. """ return query_gemini_api(prompt) def generate_single_object_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 def generate_two_objects_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 def generate_three_objects_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 def enhance_descriptions(results, objects): 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) return results def generate_transformations(text1, text2=None, text3=None): if text2 and text3: results = generate_three_objects_interaction(text1, text2, text3) objects = [text1, text2, text3] elif text2: results = generate_two_objects_interaction(text1, text2) objects = [text1, text2] else: results = generate_single_object_transformations(text1) objects = [text1] return enhance_descriptions(results, objects) 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 ############################################################################## # 스트리밍(Streaming) 방식으로 출력하는 함수: yield를 사용 ############################################################################## def process_inputs_stream(text1, text2, text3): # 1) 첫 메시지 yield "입력값 확인 중..." 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: yield "오류: 최소 하나의 키워드를 입력해주세요." return # 여기서 함수 종료 # 2) 다음 메시지 yield "창의적인 모델/컨셉/형상 변화 아이디어 생성 중..." time.sleep(0.3) # 3) 실제 아이디어 생성 results = generate_transformations(text1, text2, text3) # 4) 중간 단계 출력 yield "결과 포맷팅 중..." time.sleep(0.3) # 5) 최종 결과 정리 formatted = format_results(results) # 6) 결과 출력 yield formatted # 7) 완료 yield "완료!" def get_warning_message(): if not GEMINI_API_KEY: return "⚠️ 환경 변수 GEMINI_API_KEY가 설정되지 않았습니다. Gemini API 키를 설정하세요." return "" with gr.Blocks(title="키워드 기반 창의적 변화 아이디어 생성기", theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo: gr.HTML(""" """) 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.TabItem("창의적인 모델/컨셉/형상 변화 아이디어", id="creative_tab"): # Markdown 출력 idea_output = gr.Markdown(label="아이디어 결과") gr.Examples( examples=[ ["스마트폰", "", ""], ["커피", "책", ""], ["자동차", "로봇", "인공지능"], ["운동화", "웨어러블", "건강"], ], inputs=[text_input1, text_input2, text_input3], ) # stream=True 옵션을 통해 함수가 yield하는 문자열을 실시간 출력 submit_button.click( fn=process_inputs_stream, inputs=[text_input1, text_input2, text_input3], outputs=idea_output, stream=True ) if __name__ == "__main__": demo.launch(debug=True)