Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,232 +1,449 @@
|
|
| 1 |
import os
|
| 2 |
import gradio as gr
|
| 3 |
-
|
|
|
|
|
|
|
| 4 |
from typing import Iterator
|
|
|
|
| 5 |
import google.generativeai as genai
|
| 6 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
#
|
| 9 |
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
| 10 |
genai.configure(api_key=GEMINI_API_KEY)
|
| 11 |
|
| 12 |
-
#
|
| 13 |
model = genai.GenerativeModel("gemini-2.0-flash-thinking-exp-01-21")
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 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 |
-
|
|
|
|
| 33 |
"""
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
#
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
#
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 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 |
-
|
| 69 |
-
|
| 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 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
| 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 |
-
|
| 129 |
-
"""
|
| 130 |
-
history.append(ChatMessage(role="user", content=msg))
|
| 131 |
-
return "", history
|
| 132 |
|
| 133 |
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
|
|
|
|
|
|
| 137 |
|
| 138 |
-
|
| 139 |
-
gr.
|
| 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 |
-
|
| 146 |
-
|
| 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(
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
label="
|
| 156 |
-
|
| 157 |
-
|
| 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 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
)
|
| 177 |
|
|
|
|
|
|
|
| 178 |
|
| 179 |
-
|
| 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 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
|
|
|
| 203 |
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 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)
|