aiqcamp commited on
Commit
b9f4ccd
·
verified ·
1 Parent(s): 0950619

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +411 -216
app.py CHANGED
@@ -1,232 +1,427 @@
1
  import os
2
  import gradio as gr
3
- from gradio import ChatMessage
4
- from typing import Iterator
 
5
  import google.generativeai as genai
6
- import time # Import time module for potential debugging/delay
7
 
8
- # get Gemini API Key from the environ variable
 
 
 
 
 
 
 
 
 
 
 
9
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
10
  genai.configure(api_key=GEMINI_API_KEY)
11
 
12
- # we will be using the Gemini 2.0 Flash model with Thinking capabilities
13
- model = genai.GenerativeModel("gemini-2.0-flash-thinking-exp-01-21")
14
-
15
-
16
- def format_chat_history(messages: list) -> list:
17
- """
18
- Formats the chat history into a structure Gemini can understand
19
- """
20
- formatted_history = []
21
- for message in messages:
22
- # Skip thinking messages (messages with metadata)
23
- if not (message.get("role") == "assistant" and "metadata" in message):
24
- formatted_history.append({
25
- "role": "user" if message.get("role") == "user" else "assistant",
26
- "parts": [message.get("content", "")]
27
- })
28
- return formatted_history
29
-
30
- def stream_gemini_response(user_message: str, messages: list) -> Iterator[list]:
31
- """
32
- Streams thoughts and response with conversation history support for text input only.
33
- """
34
- if not user_message.strip(): # Robust check: if text message is empty or whitespace
35
- messages.append(ChatMessage(role="assistant", content="Please provide a non-empty text message. Empty input is not allowed.")) # More specific message
36
- yield messages
37
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
 
39
  try:
40
- print(f"\n=== New Request (Text) ===")
41
- print(f"User message: {user_message}")
42
-
43
- # Format chat history for Gemini
44
- chat_history = format_chat_history(messages)
45
-
46
- # Initialize Gemini chat
47
- chat = model.start_chat(history=chat_history)
48
- response = chat.send_message(user_message, stream=True)
49
-
50
- # Initialize buffers and flags
51
- thought_buffer = ""
52
- response_buffer = ""
53
- thinking_complete = False
54
-
55
- # Add initial thinking message
56
- messages.append(
57
- ChatMessage(
58
- role="assistant",
59
- content="",
60
- metadata={"title": "⚙️ Thinking: *The thoughts produced by the model are experimental"}
61
- )
62
- )
63
-
64
- for chunk in response:
65
- parts = chunk.candidates[0].content.parts
66
- current_chunk = parts[0].text
67
-
68
- if len(parts) == 2 and not thinking_complete:
69
- # Complete thought and start response
70
- thought_buffer += current_chunk
71
- print(f"\n=== Complete Thought ===\n{thought_buffer}")
72
-
73
- messages[-1] = ChatMessage(
74
- role="assistant",
75
- content=thought_buffer,
76
- metadata={"title": "⚙️ Thinking: *The thoughts produced by the model are experimental"}
77
- )
78
- yield messages
79
-
80
- # Start response
81
- response_buffer = parts[1].text
82
- print(f"\n=== Starting Response ===\n{response_buffer}")
83
-
84
- messages.append(
85
- ChatMessage(
86
- role="assistant",
87
- content=response_buffer
88
- )
89
- )
90
- thinking_complete = True
91
-
92
- elif thinking_complete:
93
- # Stream response
94
- response_buffer += current_chunk
95
- print(f"\n=== Response Chunk ===\n{current_chunk}")
96
-
97
- messages[-1] = ChatMessage(
98
- role="assistant",
99
- content=response_buffer
100
- )
101
-
102
- else:
103
- # Stream thinking
104
- thought_buffer += current_chunk
105
- print(f"\n=== Thinking Chunk ===\n{current_chunk}")
106
-
107
- messages[-1] = ChatMessage(
108
- role="assistant",
109
- content=thought_buffer,
110
- metadata={"title": "⚙️ Thinking: *The thoughts produced by the model are experimental"}
111
- )
112
- #time.sleep(0.05) #Optional: Uncomment this line to add a slight delay for debugging/visualization of streaming. Remove for final version
113
-
114
- yield messages
115
-
116
- print(f"\n=== Final Response ===\n{response_buffer}")
117
-
118
  except Exception as e:
119
- print(f"\n=== Error ===\n{str(e)}")
120
- messages.append(
121
- ChatMessage(
122
- role="assistant",
123
- content=f"I apologize, but I encountered an error: {str(e)}"
124
- )
125
- )
126
- yield messages
127
-
128
- def user_message(msg: str, history: list) -> tuple[str, list]:
129
- """Adds user message to chat history"""
130
- history.append(ChatMessage(role="user", content=msg))
131
- return "", history
132
-
133
-
134
- # Create the Gradio interface
135
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo: # Using Soft theme with adjusted hues for a refined look
136
- gr.Markdown("# Chat with Gemini 2.0 Flash and See its Thoughts 💭")
137
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2Faiqcamp-Gemini2-Flash-Thinking.hf.space">
140
- <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Faiqcamp-Gemini2-Flash-Thinking.hf.space&countColor=%23263759" />
141
- </a>""")
142
-
143
-
144
- chatbot = gr.Chatbot(
145
- type="messages",
146
- label="Gemini2.0 'Thinking' Chatbot (Streaming Output)", #Label now indicates streaming
147
- render_markdown=True,
148
- scale=1,
149
- avatar_images=(None,"https://lh3.googleusercontent.com/oxz0sUBF0iYoN4VvhqWTmux-cxfD1rxuYkuFEfm1SFaseXEsjjE4Je_C_V3UQPuJ87sImQK3HfQ3RXiaRnQetjaZbjJJUkiPL5jFJ1WRl5FKJZYibUA=w214-h214-n-nu")
150
- )
151
-
152
- with gr.Row(equal_height=True):
153
- input_box = gr.Textbox(
154
- lines=1,
155
- label="Chat Message",
156
- placeholder="Type your message here...",
157
- scale=4
158
- )
159
-
160
- clear_button = gr.Button("Clear Chat", scale=1)
161
-
162
- # Add example prompts - removed file upload examples. Kept text focused examples.
163
- example_prompts = [
164
- ["Write a short poem about the sunset."],
165
- ["Explain the theory of relativity in simple terms."],
166
- ["If a train leaves Chicago at 6am traveling at 60mph, and another train leaves New York at 8am traveling at 80mph, at what time will they meet?"],
167
- ["Summarize the plot of Hamlet."],
168
- ["Write a haiku about a cat."]
169
- ]
170
-
171
  gr.Examples(
172
- examples=example_prompts,
173
- inputs=input_box,
174
- label="Examples: Try these prompts to see Gemini's thinking!",
175
- examples_per_page=5 # Adjust as needed
176
- )
177
-
178
-
179
- # Set up event handlers
180
- msg_store = gr.State("") # Store for preserving user message
181
-
182
- input_box.submit(
183
- lambda msg: (msg, msg, ""), # Store message and clear input
184
- inputs=[input_box],
185
- outputs=[msg_store, input_box, input_box],
186
- queue=False
187
- ).then(
188
- user_message, # Add user message to chat
189
- inputs=[msg_store, chatbot],
190
- outputs=[input_box, chatbot],
191
- queue=False
192
- ).then(
193
- stream_gemini_response, # Generate and stream response
194
- inputs=[msg_store, chatbot],
195
- outputs=chatbot
196
  )
 
 
 
 
197
 
198
- clear_button.click(
199
- lambda: ([], "", ""),
200
- outputs=[chatbot, input_box, msg_store],
201
- queue=False
202
- )
203
-
204
- gr.Markdown( # Description moved to the bottom - updated for text-only
205
- """
206
- <br><br><br> <!-- Add some vertical space -->
207
- ---
208
- ### About this Chatbot
209
- This chatbot demonstrates the experimental 'thinking' capability of the **Gemini 2.0 Flash** model.
210
- You can observe the model's thought process as it generates responses, displayed with the "⚙️ Thinking" prefix.
211
-
212
- **Try out the example prompts below to see Gemini in action!**
213
-
214
- **Key Features:**
215
- * Powered by Google's **Gemini 2.0 Flash** model.
216
- * Shows the model's **thoughts** before the final answer (experimental feature).
217
- * Supports **conversation history** for multi-turn chats.
218
- * Uses **streaming** for a more interactive experience.
219
- **Instructions:**
220
- 1. Type your message in the input box below or select an example.
221
- 2. Press Enter or click Submit to send.
222
- 3. Observe the chatbot's "Thinking" process followed by the final response.
223
- 4. Use the "Clear Chat" button to start a new conversation.
224
-
225
- *Please note*: The 'thinking' feature is experimental and the quality of thoughts may vary.
226
- """
227
- )
228
-
229
-
230
- # Launch the interface
231
  if __name__ == "__main__":
232
  demo.launch(debug=True)
 
1
  import os
2
  import gradio as gr
3
+ import random
4
+ import time
5
+ import logging
6
  import google.generativeai as genai
 
7
 
8
+ # Configure logging
9
+ logging.basicConfig(
10
+ level=logging.INFO,
11
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
12
+ handlers=[
13
+ logging.FileHandler("api_debug.log"),
14
+ logging.StreamHandler()
15
+ ]
16
+ )
17
+ logger = logging.getLogger("idea_generator")
18
+
19
+ # Get Gemini API Key from environment variable
20
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
21
  genai.configure(api_key=GEMINI_API_KEY)
22
 
23
+ # Helper function to select one option from a transformation string with a slash
24
+ def choose_alternative(transformation):
25
+ if "/" not in transformation:
26
+ return transformation
27
+ parts = transformation.split("/")
28
+ if len(parts) != 2:
29
+ return random.choice([part.strip() for part in parts])
30
+ left = parts[0].strip()
31
+ right = parts[1].strip()
32
+ if " " in left:
33
+ tokens = left.split(" ", 1)
34
+ prefix = tokens[0]
35
+ if not right.startswith(prefix):
36
+ option1 = left
37
+ option2 = prefix + " " + right
38
+ else:
39
+ option1 = left
40
+ option2 = right
41
+ return random.choice([option1, option2])
42
+ else:
43
+ return random.choice([left, right])
44
+
45
+ # Physical transformation categories and related changes
46
+ physical_transformation_categories = {
47
+ "공간적 변화": [
48
+ "전진/후진 이동", "좌/우 이동", "상승/하강", "피치 회전", "요 회전", "롤 회전",
49
+ "궤도 운동", "나선형 이동", "관성 드리프트", "자이로스코픽 세차 운동", "비대칭 회전",
50
+ "진자 운동", "탄도학적 이동", "무중력 상태에서의 부유"
51
+ ],
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
+ "발현적 구조 형성", "형상 기억 효과", "4D 프린팅 구조 변화", "부분 제거", "부분 교환",
83
+ "결합", "분리"
84
+ ],
85
+ "전기/자기적 변화": [
86
+ "자화/탈자화", "전하 증가/감소", "전기장 생성/소멸", "자기장 생성/소멸", "초전도 상태 전이",
87
+ "강유전/반강유전 전이", "양자 스핀 상태 변화", "플라즈마 상태 형성/소멸", "스핀파 전파",
88
+ "광전 효과", "압전 효과", "홀 효과"
89
+ ],
90
+ "화학적 변화": [
91
+ "표면 코팅 변화", "물질 조성 변화", "화학 반응에 의한 변화", "촉매 활성화/비활성화",
92
+ "광화학 반응", "전기화학 반응", "자기조립 단분자막 형성", "분자컴퓨팅 변화",
93
+ "생체모방 표면 변화", "스마트 폴리머 반응", "화학적 진동 반응", "산화", "환원",
94
+ "중합", "가수분해", "융합", "방사화"
95
+ ],
96
+ "시간 관련 변화": [
97
+ "노화/풍화", "마모/부식", "퇴색/변색", "손상/복구", "수명 주기 단계 변화",
98
+ "사용자 인터랙션 기반 적응", "학습기반 형상 최적화", "시간적 물성 변화", "집단 기억 효과",
99
+ "문화적 의미 변화", "시간 지연 응답", "이력 의존적 변화", "퍼지 시간 거동", "진화적 변화",
100
+ "주기적 재생/재생성"
101
+ ]
102
+ }
103
+
104
+ # Marketing transformation categories
105
+ marketing_transformation_categories = {
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
+ # Use a stable model
171
+ model = genai.GenerativeModel('gemini-pro')
172
+
173
+ # Generate content with a simple approach
174
+ response = model.generate_content(prompt)
175
+
176
+ # Check the response - defensive handling
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:
204
+ logger.error(f"Error processing response: {inner_e}")
205
+ return f"An error occurred while processing the response: {str(inner_e)}"
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
+ # Generate enhanced descriptions without unnecessary questions or explanations
217
+ def enhance_with_llm(base_description, obj_name, category, is_marketing=False):
218
+ domain = "마케팅" if is_marketing else "물리적"
219
+ prompt = f"""다음은 '{obj_name}'의 '{domain} {category}' 관련 간단한 설명입니다:
220
+ "{base_description}"
221
+ 해당 설명을 3-4문장의 창의적인 답변으로 확장하여 출력하라."""
222
+ return query_gemini_api(prompt)
223
+
224
+ # Generate physical transformations for a single object
225
+ def generate_single_object_physical_transformations(obj):
226
+ results = {}
227
+ for category, transformations in physical_transformation_categories.items():
228
+ transformation = choose_alternative(random.choice(transformations))
229
+ base_description = f"{obj}이(가) {transformation} 현상을 보인다"
230
+ results[category] = {"base": base_description, "enhanced": None}
231
+ return results
232
+
233
+ # Generate marketing transformations for a single object
234
+ def generate_single_object_marketing_transformations(obj):
235
+ results = {}
236
+ for category, transformations in marketing_transformation_categories.items():
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))
247
+ template = random.choice([
248
+ "{obj1}이(가) {obj2}에 결합하여 {change}가 발생했다",
249
+ "{obj1}과(와) {obj2}이(가) 충돌하면서 {change}가 일어났다"
250
+ ])
251
+ base_description = template.format(obj1=obj1, obj2=obj2, change=transformation)
252
+ results[category] = {"base": base_description, "enhanced": None}
253
+ return results
254
+
255
+ # Generate marketing transformations for two objects
256
+ def generate_two_objects_marketing_interaction(obj1, obj2):
257
+ results = {}
258
+ for category, transformations in marketing_transformation_categories.items():
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))
273
+ template = random.choice([
274
+ "{obj1}, {obj2}, {obj3}이(가) 삼각형 구조로 결합하여 {change}가 발생했다",
275
+ "{obj1}이(가) {obj2}와(과) {obj3} 사이에서 매개체 역할을 하며 {change}를 촉진했다"
276
+ ])
277
+ base_description = template.format(obj1=obj1, obj2=obj2, obj3=obj3, change=transformation)
278
+ results[category] = {"base": base_description, "enhanced": None}
279
+ return results
280
+
281
+ # Generate marketing transformations for three objects
282
+ def generate_three_objects_marketing_interaction(obj1, obj2, obj3):
283
+ results = {}
284
+ for category, transformations in marketing_transformation_categories.items():
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, is_marketing)
299
+ return results
300
+
301
+ # Generate physical transformations based on number of objects
302
+ def generate_physical_transformations(text1, text2=None, text3=None):
303
+ if text2 and text3:
304
+ results = generate_three_objects_physical_interaction(text1, text2, text3)
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 = generate_three_objects_marketing_interaction(text1, text2, text3)
318
+ objects = [text1, text2, text3]
319
+ elif text2:
320
+ results = generate_two_objects_marketing_interaction(text1, text2)
321
+ objects = [text1, text2]
322
+ else:
323
+ results = generate_single_object_marketing_transformations(text1)
324
+ objects = [text1]
325
+ return enhance_descriptions(results, objects, is_marketing=True)
326
+
327
+ # Format results for display
328
+ def format_results(results):
329
+ formatted = ""
330
+ for category, result in results.items():
331
+ formatted += f"## {category}\n**기본 변화**: {result['base']}\n\n**자세한 설명**: {result['enhanced']}\n\n---\n\n"
332
+ return formatted
333
+
334
+ # Process physical inputs and generate transformations
335
+ def process_physical_inputs(text1, text2, text3):
336
+ messages = []
337
+ messages.append("입력값 확인 중...")
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)
360
+ text1 = text1.strip() if text1 else None
361
+ text2 = text2.strip() if text2 else None
362
+ text3 = text3.strip() if text3 else None
363
+ if not text1:
364
+ messages.append("오류: 최소 하나의 키워드를 입력해주세요.")
365
+ return "\n\n".join(messages)
366
+ messages.append("마케팅 변화 아이디어 생성 중...")
367
+ time.sleep(0.3)
368
+ results = generate_marketing_transformations(text1, text2, text3)
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
+ # Check for API key and return warning message if needed
377
+ def get_warning_message():
378
+ if not GEMINI_API_KEY:
379
+ return "⚠️ 환경 변수 GEMINI_API_KEY가 설정되지 않았습니다. Gemini API 키를 설정하세요."
380
+ return ""
381
+
382
+ # Create the Gradio UI
383
+ with gr.Blocks(title="키워드 기반 창의적 아이디어 생성기", theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo:
384
+ gr.HTML("""
385
+ <style>
386
+ body { background: linear-gradient(135deg, #e0eafc, #cfdef3); font-family: 'Arial', sans-serif; }
387
+ .gradio-container { padding: 20px; }
388
+ h1, h2 { text-align: center; }
389
+ h1 { color: #333; }
390
+ h2 { color: #555; }
391
+ .output { background-color: #ffffff; padding: 15px; border-radius: 8px; }
392
+ .gr-button { background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; }
393
+ </style>
394
+ """)
395
+ gr.Markdown("# 🚀 키워드 기반 창의적 아이디어 생성기")
396
+ gr.Markdown("입력한 키워드를 바탕으로 창의적인 물리적/마케팅 변화 아이디어를 도출합니다. 최대 3개의 키워드를 입력할 수 있습니다.")
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.Tabs():
406
+ with gr.TabItem("물리적 변화 아이디어", id="physical_tab"):
407
+ physical_output = gr.Markdown(label="물리적 변화 아이디어 결과")
408
+ with gr.TabItem("마케팅 변화 아이디어", id="marketing_tab"):
409
+ marketing_output = gr.Markdown(label="마케팅 변화 아이디어 결과")
410
 
411
+ # Add some example combinations
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  gr.Examples(
413
+ examples=[
414
+ ["스마트폰", "", ""],
415
+ ["커피", "책", ""],
416
+ ["자동차", "로봇", "인공지능"],
417
+ ["운동화", "웨어러블", "건강"],
418
+ ],
419
+ inputs=[text_input1, text_input2, text_input3],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  )
421
+
422
+ # Connect the button click events to functions
423
+ submit_button.click(fn=process_physical_inputs, inputs=[text_input1, text_input2, text_input3], outputs=physical_output)
424
+ submit_button.click(fn=process_marketing_inputs, inputs=[text_input1, text_input2, text_input3], outputs=marketing_output)
425
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  if __name__ == "__main__":
427
  demo.launch(debug=True)