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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +412 -195
app.py CHANGED
@@ -1,232 +1,449 @@
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
  from typing import Iterator
7
+
8
  import google.generativeai as genai
9
+ from gradio import ChatMessage # ChatMessage 구조 사용 (Thinking/Response 구분 가능)
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 설정
22
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
23
  genai.configure(api_key=GEMINI_API_KEY)
24
 
25
+ # 사용할 Gemini 2.0 Flash 모델 (Thinking 기능 포함)
26
  model = genai.GenerativeModel("gemini-2.0-flash-thinking-exp-01-21")
27
 
28
+ ##############################################################################
29
+ # 변환 문자열에서 슬래시("/")로 구분된 두 옵션 중 하나 선택
30
+ ##############################################################################
31
+ def choose_alternative(transformation):
32
+ if "/" not in transformation:
33
+ return transformation
34
+ parts = transformation.split("/")
35
+ if len(parts) != 2:
36
+ return random.choice([part.strip() for part in parts])
37
+ left = parts[0].strip()
38
+ right = parts[1].strip()
39
+ if " " in left:
40
+ tokens = left.split(" ", 1)
41
+ prefix = tokens[0]
42
+ if not right.startswith(prefix):
43
+ option1 = left
44
+ option2 = prefix + " " + right
45
+ else:
46
+ option1 = left
47
+ option2 = right
48
+ return random.choice([option1, option2])
49
+ else:
50
+ return random.choice([left, right])
51
+
52
+ ##############################################################################
53
+ # 카테고리 사전 (일부만 발췌 가능. 여기서는 예시로 3개만 유지)
54
+ ##############################################################################
55
+ physical_transformation_categories = {
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
+ "빛에 의한 화학 반응", "전기에 의한 화학 반응", "단분자막 형성", "분자 수준 계산 변화",
132
+ "자연 모방 표면 변화", "환경 반응형 물질 변화", "주기적 화학 반응", "산화", "환원",
133
+ "고분자화", "물 분해", "화합", "방사선 영향", "산-염기 반응", "중화 반응",
134
+ "이온화", "화학적 흡착/탈착", "촉매 효율 변화", "효소 활성 변화", "발색 반응",
135
+ "pH 변화", "화학적 평형 이동", "결합 형성/분해", "용해도 변화"
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
+
174
+ "환경 상호작용": [
175
+ "온도 반응", "습도 반응", "기압 반응", "중력 반응", "자기장 반응",
176
+ "빛 반응", "소리 반응", "화학 물질 감지", "기계적 자극 감지", "전기 자극 반응",
177
+ "방사선 반응", "진동 감지", "pH 반응", "용매 반응", "기체 교환",
178
+ "환경 오염 반응", "날씨 반응", "계절 변화 반응", "일주기 반응", "생태계 상호작용",
179
+ "공생/경쟁 반응", "포식/피식 관계", "군집 형성", "영역 설정", "이주/정착 패턴"
180
+ ],
181
+
182
+ "센서 기능": [
183
+ "시각 센서/감지", "청각 센서/감지", "촉각 센서/감지", "미각 센서/감지", "후각 센서/감지",
184
+ "온도 센서/감지", "습도 센서/감지", "압력 센서/감지", "가속도 센서/감지", "회전 센서/감지",
185
+ "근접 센서/감지", "위치 센서/감지", "운동 센서/감지", "가스 센서/감지", "적외선 센서/감지",
186
+ "자외선 센서/감지", "방사선 센서/감지", "자기장 센서/감지", "전기장 센서/감지", "화학물질 센서/감지",
187
+ "생체신호 센서/감지", "진동 센서/감지", "소음 센서/감지", "빛 세기 센서/감지", "빛 파장 센서/감지",
188
+ "기울기 센서/감지", "pH 센서/감지", "전류 센서/감지", "전압 센서/감지", "이미지 센서/감지",
189
+ "거리 센서/감지", "깊이 센서/감지", "중력 센서/감지", "속도 센서/감지", "흐름 센서/감지",
190
+ "수위 센서/감지", "탁도 센서/감지", "염도 센서/감지", "금속 감지", "압전 센서/감지",
191
+ "광전 센서/감지", "열전대 센서/감지", "홀 효과 센서/감지", "초음파 센서/감지", "레이더 센서/감지",
192
+ "라이다 센서/감지", "터치 센서/감지", "제스처 센서/감지", "심박 센서/감지", "혈압 센서/감지"
193
+ ]
194
+ }
195
 
196
+ ##############################################################################
197
+ # 스트리밍용 Gemini API 함수:
198
+ # - 'Thinking' 단계(아이디어 내부 추론)와 최종 'Response' 단계로 구성
199
+ ##############################################################################
200
+ def query_gemini_api_stream(prompt: str) -> Iterator[str]:
 
 
 
 
 
 
 
 
 
 
201
  """
202
+ Gemini 2.0 Flash with 'Thinking' 부분과 'Response' 부분을
203
+ 분리하여 스트리밍(Chunk)으로 제공한다.
204
  """
205
+ # chat 초기화 (history 없이 단발성 호출)
206
+ chat = model.start_chat(history=[])
207
+ response = chat.send_message(prompt, stream=True)
208
+
209
+ thought_buffer = ""
210
+ response_buffer = ""
211
+ thinking_complete = False
212
+
213
+ for chunk in response:
214
+ # chunk에는 candidates[0].content.parts가 들어있다
215
+ parts = chunk.candidates[0].content.parts
216
+
217
+ # 예시) parts가 2개이면 (0: Thinking, 1: Response 시작)
218
+ # 외에는 1개씩 끊어서 들어올 수 있음
219
+ if len(parts) == 2 and not thinking_complete:
220
+ # 아직 Thinking 중인데, 완성된 Thinking + Response 시작이 한 번에 옴
221
+ thought_buffer += parts[0].text
222
+ yield f"[Thinking Chunk] {parts[0].text}"
223
+
224
+ response_buffer = parts[1].text
225
+ yield f"[Response Start] {parts[1].text}"
226
+
227
+ thinking_complete = True
228
+ elif thinking_complete:
229
+ # 이미 Thinking은 끝남 → Response를 이어서 스트리밍
 
 
 
 
 
 
 
230
  current_chunk = parts[0].text
231
+ response_buffer += current_chunk
232
+ yield current_chunk
233
+ else:
234
+ # Thinking 진행 중 (parts가 1개씩 추가됨)
235
+ current_chunk = parts[0].text
236
+ thought_buffer += current_chunk
237
+ yield f"[Thinking Chunk] {current_chunk}"
238
 
239
+ # 스트리밍 완료 최종 결과 한번에 제공할 수도 있음
240
+ yield f"\n[Final Response]\n{response_buffer}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
 
242
+ ##############################################################################
243
+ # 카테고리별 간단 설명을 'Thinking' + 'Response'로 확장 (스트리밍)
244
+ ##############################################################################
245
+ def enhance_with_llm_stream(base_description, obj_name, category) -> Iterator[str]:
246
+ """
247
+ 기존 enhance_with_llm를 스트리밍 형태로 바꾼 함수:
248
+ 'Thinking' + 'Response' 단계를 chunk로 순차 전달
249
+ """
250
+ prompt = f"""
251
+ 다음은 '{obj_name}'의 '{category}' 관련 간단한 설명입니다:
252
+ "{base_description}"
253
+ 위 내용을 보다 구체화하여,
254
+ 1) 창의적인 모델/컨셉/형상의 변화에 대한 이해,
255
+ 2) 혁신 포인트와 기능성 등을 중심으로
256
+ 3~4문장의 아이디어로 확장해 주세요.
257
+ """
258
+ # query_gemini_api_stream()로부터 chunk를 받아 그대로 yield
259
+ for chunk in query_gemini_api_stream(prompt):
260
+ yield chunk
261
+
262
+ ##############################################################################
263
+ # 한 키워드(오브젝트)에 대한 기본 아이디어(카테고리별) 생성
264
+ ##############################################################################
265
+ def generate_single_object_transformations(obj):
266
+ results = {}
267
+ for category, transformations in physical_transformation_categories.items():
268
+ transformation = choose_alternative(random.choice(transformations))
269
+ base_description = f"{obj}이(가) {transformation} 현상을 보인다"
270
+ results[category] = {"base": base_description, "enhanced": ""}
271
+ return results
272
+
273
+ ##############################################################################
274
+ # 2개 키워드 상호작용
275
+ ##############################################################################
276
+ def generate_two_objects_interaction(obj1, obj2):
277
+ results = {}
278
+ for category, transformations in physical_transformation_categories.items():
279
+ transformation = choose_alternative(random.choice(transformations))
280
+ template = random.choice([
281
+ "{obj1}이(가) {obj2}�� 결합하여 {change}가 발생했다",
282
+ "{obj1}과(와) {obj2}이(가) 충돌하면서 {change}가 일어났다"
283
+ ])
284
+ base_description = template.format(obj1=obj1, obj2=obj2, change=transformation)
285
+ results[category] = {"base": base_description, "enhanced": ""}
286
+ return results
287
+
288
+ ##############################################################################
289
+ # 3개 키워드 상호작용
290
+ ##############################################################################
291
+ def generate_three_objects_interaction(obj1, obj2, obj3):
292
+ results = {}
293
+ for category, transformations in physical_transformation_categories.items():
294
+ transformation = choose_alternative(random.choice(transformations))
295
+ template = random.choice([
296
+ "{obj1}, {obj2}, {obj3}이(가) 삼각형 구조로 결합하여 {change}가 발생했다",
297
+ "{obj1}이(가) {obj2}와(과) {obj3} 사이에서 매개체 역할을 하며 {change}를 촉진했다"
298
+ ])
299
+ base_description = template.format(obj1=obj1, obj2=obj2, obj3=obj3, change=transformation)
300
+ results[category] = {"base": base_description, "enhanced": ""}
301
+ return results
302
+
303
+ ##############################################################################
304
+ # 실제 변환 생성 로직
305
+ ##############################################################################
306
+ def generate_transformations(text1, text2=None, text3=None):
307
+ if text2 and text3:
308
+ results = generate_three_objects_interaction(text1, text2, text3)
309
+ objects = [text1, text2, text3]
310
+ elif text2:
311
+ results = generate_two_objects_interaction(text1, text2)
312
+ objects = [text1, text2]
313
+ else:
314
+ results = generate_single_object_transformations(text1)
315
+ objects = [text1]
316
+ return results, objects
317
+
318
+ ##############################################################################
319
+ # 스트리밍: 각 카테고리별로 'Thinking' + 'Response' 부분을 실시간 전달
320
+ ##############################################################################
321
+ def process_inputs_stream(text1, text2, text3) -> Iterator[list]:
322
+ """
323
+ Gradio의 Chatbot 형식에 맞춰서,
324
+ [(role='assistant'|'user', content=...), ...] 형태로 yield한다.
325
+ 생각(Thinking) 단계와 최종 응답을 분리해서 실시간 전송.
326
+ """
327
+ messages = []
328
 
329
+ # 1) 입력값 확인
330
+ yield [("assistant", "입력값 확인 중...")]
331
+ time.sleep(0.3)
 
 
 
332
 
333
+ text1 = text1.strip() if text1 else None
334
+ text2 = text2.strip() if text2 else None
335
+ text3 = text3.strip() if text3 else None
336
+ if not text1:
337
+ yield [("assistant", "오류: 최소 하나의 키워드를 입력해주세요.")]
338
+ return
339
 
340
+ # 2) 아이디어 생성
341
+ yield [("assistant", "창의적인 모델/컨셉/형상 변화 아이디어 생성 중... (카테고리별 분석)")]
342
+ time.sleep(0.3)
343
+ results, objects = generate_transformations(text1, text2, text3)
344
+
345
+ # 카테고리별 스트리밍 처리
346
+ obj_name = " 및 ".join([obj for obj in objects if obj])
347
+
348
+ for i, (category, result_dict) in enumerate(results.items(), start=1):
349
+ base_desc = result_dict["base"]
350
+
351
+ # 카테고리 안내 출력
352
+ yield [("assistant", f"**[{i}/{len(results)}] 카테고리:** {category}\n\n기본 아이디어: {base_desc}\n\n지금부터 Thinking + Response를 단계적으로 스트리밍합니다...")]
353
+ time.sleep(0.5)
354
+
355
+ # 스트리밍 LLM 호출
356
+ thinking_text = ""
357
+ response_text = ""
358
+ is_thinking_done = False
359
+
360
+ # enhance_with_llm_stream 호출
361
+ for chunk in enhance_with_llm_stream(base_desc, obj_name, category):
362
+ if chunk.startswith("[Thinking Chunk]"):
363
+ # 생각 파트
364
+ thinking_text += chunk.replace("[Thinking Chunk]", "")
365
+ messages_to_user = f"**[Thinking]**\n{thinking_text}"
366
+ yield [("assistant", messages_to_user)]
367
+ elif chunk.startswith("[Response Start]"):
368
+ # 응답 시작 시점
369
+ is_thinking_done = True
370
+ # 남아있는 부분은 response_text로
371
+ partial = chunk.replace("[Response Start]", "")
372
+ response_text += partial
373
+ messages_to_user = f"**[Final Response 시작]**\n{partial}"
374
+ yield [("assistant", messages_to_user)]
375
+ elif chunk.startswith("[Final Response]"):
376
+ # 최종 종료
377
+ final = chunk.replace("[Final Response]", "")
378
+ response_text += f"\n{final}"
379
+ yield [("assistant", f"**[최종 Response]**\n{response_text.strip()}")]
380
+ else:
381
+ # 일반 응답 스트리밍
382
+ if is_thinking_done:
383
+ response_text += chunk
384
+ yield [("assistant", f"**[응답 진행]**\n{response_text}") ]
385
+ else:
386
+ thinking_text += chunk
387
+ yield [("assistant", f"**[Thinking]**\n{thinking_text}")]
388
 
389
+ # 카테고리 응답 완료
390
+ result_dict["enhanced"] = response_text
 
 
 
 
 
 
 
391
 
392
+ # 3) 전체 카테고리 완료
393
+ yield [("assistant", "**모든 카테고리에 대한 스트리밍이 완료되었습니다!**")]
 
 
394
 
395
 
396
+ ##############################################################################
397
+ # Gradio UI
398
+ ##############################################################################
399
+ with gr.Blocks(title="스트리밍 예제: Gemini 2.0 Flash Thinking",
400
+ theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo:
401
 
402
+ gr.Markdown("# 🚀 키워드 기반 창의적 변화 아이디어 (Gemini 2.0 Flash Thinking, Streaming)")
403
+ gr.Markdown("키워드 1~3개를 입력하면, **카테고리별로** 'Thinking'과 'Response'가 실시간 스트리밍됩니다.")
 
 
404
 
 
405
  chatbot = gr.Chatbot(
406
+ label="카테고리별 아이디어(Thinking + Response) 스트리밍",
407
+ type="tuple", # (role, content) 쌍의 리스트로 전달
408
+ render_markdown=True
 
 
409
  )
410
 
411
+ with gr.Row():
412
+ with gr.Column(scale=1):
413
+ text_input1 = gr.Textbox(label="키워드 1 (필수)", placeholder="예: 자동차")
414
+ text_input2 = gr.Textbox(label="키워드 2 (선택)", placeholder="예: 로봇")
415
+ text_input3 = gr.Textbox(label="키워드 3 (선택)", placeholder="예: 인공지능")
416
+ submit_button = gr.Button("아이디어 생성하기")
 
 
 
 
 
 
 
 
 
 
 
 
417
 
418
+ clear_button = gr.Button("대화 지우기")
419
+
420
+ with gr.Column(scale=2):
421
+ # 이미 chatbot이 자리를 차지하므로 패스
422
+ pass
 
423
 
424
+ def clear_chat():
425
+ return []
426
 
427
+ examples = [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
 
429
+ ["자동차", "", ""],
430
+ ["스마트폰", "인공지능", ""],
431
+ ["드론", "인공지능", ""],
432
+ ["운동화", "웨어러블", "건강"],
433
+ ]
434
+ gr.Examples(examples=examples, inputs=[text_input1, text_input2, text_input3])
435
 
436
+ submit_button.click(
437
+ fn=process_inputs_stream,
438
+ inputs=[text_input1, text_input2, text_input3],
439
+ outputs=chatbot,
440
+ stream=True # 스트리밍 출력
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  )
442
 
443
+ clear_button.click(
444
+ fn=clear_chat,
445
+ outputs=chatbot
446
+ )
447
 
 
448
  if __name__ == "__main__":
449
+ demo.launch(debug=True)