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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -412
app.py CHANGED
@@ -1,449 +1,229 @@
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)
 
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
+ **Try out the example prompts below to see Gemini in action!**
212
+ **Key Features:**
213
+ * Powered by Google's **Gemini 2.0 Flash** model.
214
+ * Shows the model's **thoughts** before the final answer (experimental feature).
215
+ * Supports **conversation history** for multi-turn chats.
216
+ * Uses **streaming** for a more interactive experience.
217
+ **Instructions:**
218
+ 1. Type your message in the input box below or select an example.
219
+ 2. Press Enter or click Submit to send.
220
+ 3. Observe the chatbot's "Thinking" process followed by the final response.
221
+ 4. Use the "Clear Chat" button to start a new conversation.
222
+ *Please note*: The 'thinking' feature is experimental and the quality of thoughts may vary.
223
+ """
224
+ )
225
+
226
+
227
+ # Launch the interface
228
  if __name__ == "__main__":
229
+ demo.launch(debug=True)