Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,166 +12,106 @@ print("google-genai version loaded")
|
|
| 12 |
# For Hugging Face Spaces, use Secrets
|
| 13 |
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "")
|
| 14 |
|
| 15 |
-
TITLE = """<h1 align="center" style="
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
-webkit-text-fill-color: transparent;
|
| 19 |
-
background-clip: text;
|
| 20 |
-
font-size: 2.8em;
|
| 21 |
-
font-weight: 800;
|
| 22 |
-
margin: 20px 0 10px 0;
|
| 23 |
-
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);">
|
| 24 |
-
π¬ Gemini AI Chatbot
|
| 25 |
-
</h1>"""
|
| 26 |
|
| 27 |
-
SUBTITLE = """<
|
| 28 |
-
|
| 29 |
-
font-size: 1.3em;
|
| 30 |
-
margin: 10px 0 5px 0;
|
| 31 |
-
font-weight: 600;">
|
| 32 |
-
π¨ Powered by Google Gemini AI
|
| 33 |
-
</h3>"""
|
| 34 |
|
| 35 |
DESCRIPTION = """
|
| 36 |
-
<p align="center" style="
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
margin: 15px auto;
|
| 40 |
-
max-width: 600px;
|
| 41 |
-
line-height: 1.6;">
|
| 42 |
-
Chat with Google's advanced AI model. Get your free API key from
|
| 43 |
-
<a href="https://aistudio.google.com/app/apikey"
|
| 44 |
-
target="_blank"
|
| 45 |
-
style="color: #667eea; font-weight: 600; text-decoration: none; border-bottom: 2px solid #667eea;">
|
| 46 |
-
Google AI Studio
|
| 47 |
-
</a>
|
| 48 |
</p>
|
| 49 |
"""
|
| 50 |
|
| 51 |
IMAGE_WIDTH = 512
|
| 52 |
|
| 53 |
-
#
|
| 54 |
CUSTOM_CSS = """
|
| 55 |
-
/* Main container with soft gradient */
|
| 56 |
.gradio-container {
|
| 57 |
max-width: 1400px !important;
|
| 58 |
margin: auto !important;
|
| 59 |
-
background: linear-gradient(to bottom, #
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
| 61 |
}
|
| 62 |
|
| 63 |
-
/* Better button styling */
|
| 64 |
button {
|
| 65 |
-
background: linear-gradient(
|
| 66 |
border: none !important;
|
| 67 |
color: white !important;
|
| 68 |
font-weight: 600 !important;
|
| 69 |
-
border-radius:
|
| 70 |
-
padding:
|
| 71 |
transition: all 0.3s ease !important;
|
| 72 |
-
box-shadow: 0 4px 6px rgba(102, 126, 234, 0.2) !important;
|
| 73 |
}
|
| 74 |
|
| 75 |
button:hover {
|
| 76 |
-
background: linear-gradient(
|
| 77 |
-
transform:
|
| 78 |
-
box-shadow: 0 6px 12px rgba(102, 126, 234, 0.3) !important;
|
| 79 |
}
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
/* Input fields with better visibility */
|
| 86 |
-
.input-text, textarea, input[type="text"], input[type="password"] {
|
| 87 |
-
border: 2px solid #cbd5e0 !important;
|
| 88 |
border-radius: 10px !important;
|
| 89 |
-
padding:
|
| 90 |
-
background: white !important;
|
| 91 |
-
color: #2d3748 !important;
|
| 92 |
-
font-size: 15px !important;
|
| 93 |
}
|
| 94 |
|
| 95 |
.input-text:focus, textarea:focus, input:focus {
|
| 96 |
-
border-color: #
|
| 97 |
-
box-shadow: 0 0
|
| 98 |
outline: none !important;
|
| 99 |
-
background: #ffffff !important;
|
| 100 |
}
|
| 101 |
|
| 102 |
-
/* Labels with better readability */
|
| 103 |
label {
|
| 104 |
-
color: #
|
| 105 |
font-weight: 600 !important;
|
| 106 |
-
font-size: 14px !important;
|
| 107 |
margin-bottom: 8px !important;
|
| 108 |
}
|
| 109 |
|
| 110 |
-
/* Chatbot
|
| 111 |
-
.
|
| 112 |
-
|
| 113 |
-
border-radius: 15px !important;
|
| 114 |
-
background: white !important;
|
| 115 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important;
|
| 116 |
}
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
| 121 |
color: white !important;
|
| 122 |
-
border-radius:
|
| 123 |
padding: 12px 16px !important;
|
| 124 |
-
margin: 8px 0 !important;
|
| 125 |
-
box-shadow: 0 2px 4px rgba(102, 126, 234, 0.2) !important;
|
| 126 |
-
font-size: 15px !important;
|
| 127 |
-
line-height: 1.5 !important;
|
| 128 |
}
|
| 129 |
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
border-radius: 18px 18px 18px 4px !important;
|
| 135 |
padding: 12px 16px !important;
|
| 136 |
-
|
| 137 |
-
border-left: 4px solid #667eea !important;
|
| 138 |
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important;
|
| 139 |
-
font-size: 15px !important;
|
| 140 |
-
line-height: 1.6 !important;
|
| 141 |
}
|
| 142 |
|
| 143 |
/* Accordion styling */
|
| 144 |
.accordion {
|
| 145 |
-
background:
|
| 146 |
-
border: 2px solid #
|
| 147 |
-
border-radius:
|
| 148 |
-
margin:
|
| 149 |
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important;
|
| 150 |
}
|
| 151 |
|
| 152 |
-
/* Image
|
| 153 |
.image-container {
|
| 154 |
-
border: 3px
|
| 155 |
border-radius: 15px !important;
|
| 156 |
-
padding:
|
| 157 |
background: white !important;
|
| 158 |
-
transition: all 0.3s ease !important;
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
.image-container:hover {
|
| 162 |
-
border-color: #667eea !important;
|
| 163 |
-
background: #f7fafc !important;
|
| 164 |
}
|
| 165 |
|
| 166 |
/* Slider styling */
|
| 167 |
input[type="range"] {
|
| 168 |
-
accent-color: #
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
/* Info text */
|
| 172 |
-
.info {
|
| 173 |
-
color: #718096 !important;
|
| 174 |
-
font-size: 13px !important;
|
| 175 |
}
|
| 176 |
|
| 177 |
/* Better spacing */
|
|
@@ -180,62 +120,13 @@ input[type="range"] {
|
|
| 180 |
}
|
| 181 |
|
| 182 |
.block {
|
| 183 |
-
padding:
|
| 184 |
-
border-radius:
|
| 185 |
-
background: white !important;
|
| 186 |
}
|
| 187 |
|
| 188 |
-
/* Remove footer */
|
| 189 |
footer {
|
| 190 |
display: none !important;
|
| 191 |
}
|
| 192 |
-
|
| 193 |
-
/* Placeholder text */
|
| 194 |
-
::placeholder {
|
| 195 |
-
color: #a0aec0 !important;
|
| 196 |
-
opacity: 1 !important;
|
| 197 |
-
}
|
| 198 |
-
|
| 199 |
-
/* Scrollbar styling */
|
| 200 |
-
::-webkit-scrollbar {
|
| 201 |
-
width: 10px;
|
| 202 |
-
}
|
| 203 |
-
|
| 204 |
-
::-webkit-scrollbar-track {
|
| 205 |
-
background: #f1f1f1;
|
| 206 |
-
border-radius: 10px;
|
| 207 |
-
}
|
| 208 |
-
|
| 209 |
-
::-webkit-scrollbar-thumb {
|
| 210 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 211 |
-
border-radius: 10px;
|
| 212 |
-
}
|
| 213 |
-
|
| 214 |
-
::-webkit-scrollbar-thumb:hover {
|
| 215 |
-
background: linear-gradient(135deg, #5a67d8 0%, #6b46a1 100%);
|
| 216 |
-
}
|
| 217 |
-
|
| 218 |
-
/* Example prompts styling */
|
| 219 |
-
.examples {
|
| 220 |
-
background: white !important;
|
| 221 |
-
border-radius: 12px !important;
|
| 222 |
-
padding: 15px !important;
|
| 223 |
-
border: 2px solid #e2e8f0 !important;
|
| 224 |
-
}
|
| 225 |
-
|
| 226 |
-
/* Better contrast for dark text */
|
| 227 |
-
.markdown-text {
|
| 228 |
-
color: #2d3748 !important;
|
| 229 |
-
}
|
| 230 |
-
|
| 231 |
-
/* Success/Error messages */
|
| 232 |
-
.success {
|
| 233 |
-
color: #38a169 !important;
|
| 234 |
-
}
|
| 235 |
-
|
| 236 |
-
.error {
|
| 237 |
-
color: #e53e3e !important;
|
| 238 |
-
}
|
| 239 |
"""
|
| 240 |
|
| 241 |
# ===================== HELPERS =====================
|
|
@@ -274,6 +165,7 @@ def user(message: str, history: Optional[List[Dict]]) -> tuple:
|
|
| 274 |
if not message or message.strip() == "":
|
| 275 |
return "", history
|
| 276 |
|
|
|
|
| 277 |
history.append({"role": "user", "content": message.strip()})
|
| 278 |
return "", history
|
| 279 |
except Exception as e:
|
|
@@ -292,36 +184,42 @@ def bot(
|
|
| 292 |
):
|
| 293 |
"""Generate bot response with comprehensive error handling."""
|
| 294 |
try:
|
|
|
|
| 295 |
if not history or len(history) == 0:
|
| 296 |
yield history or []
|
| 297 |
return
|
| 298 |
|
|
|
|
| 299 |
api_key = google_key.strip() if google_key else GOOGLE_API_KEY
|
| 300 |
if not api_key:
|
| 301 |
history.append({
|
| 302 |
"role": "assistant",
|
| 303 |
-
"content": "
|
| 304 |
})
|
| 305 |
yield history
|
| 306 |
return
|
| 307 |
|
|
|
|
| 308 |
try:
|
| 309 |
client = genai.Client(api_key=api_key)
|
| 310 |
except Exception as e:
|
| 311 |
history.append({
|
| 312 |
"role": "assistant",
|
| 313 |
-
"content": f"β **Invalid API
|
| 314 |
})
|
| 315 |
yield history
|
| 316 |
return
|
| 317 |
|
|
|
|
| 318 |
user_message = history[-1]["content"]
|
| 319 |
|
|
|
|
| 320 |
temperature = max(0.0, min(2.0, float(temperature)))
|
| 321 |
max_output_tokens = max(1, min(8192, int(max_output_tokens)))
|
| 322 |
top_k = max(1, min(100, int(top_k))) if top_k > 0 else None
|
| 323 |
top_p = max(0.0, min(1.0, float(top_p)))
|
| 324 |
|
|
|
|
| 325 |
config = types.GenerateContentConfig(
|
| 326 |
temperature=temperature,
|
| 327 |
max_output_tokens=max_output_tokens,
|
|
@@ -332,6 +230,7 @@ def bot(
|
|
| 332 |
if preprocess_stop_sequences(stop_sequences):
|
| 333 |
config.stop_sequences = preprocess_stop_sequences(stop_sequences)
|
| 334 |
|
|
|
|
| 335 |
if image_prompt is None:
|
| 336 |
contents = [user_message]
|
| 337 |
else:
|
|
@@ -339,12 +238,13 @@ def bot(
|
|
| 339 |
if processed_image is None:
|
| 340 |
history.append({
|
| 341 |
"role": "assistant",
|
| 342 |
-
"content": "
|
| 343 |
})
|
| 344 |
yield history
|
| 345 |
return
|
| 346 |
contents = [user_message, processed_image]
|
| 347 |
|
|
|
|
| 348 |
try:
|
| 349 |
response = client.models.generate_content_stream(
|
| 350 |
model='gemini-2.0-flash-exp',
|
|
@@ -360,20 +260,24 @@ def bot(
|
|
| 360 |
yield history
|
| 361 |
time.sleep(0.01)
|
| 362 |
|
|
|
|
| 363 |
if not history[-1]["content"]:
|
| 364 |
-
history[-1]["content"] = "β οΈ
|
| 365 |
yield history
|
| 366 |
|
| 367 |
except Exception as e:
|
| 368 |
error_message = str(e)
|
| 369 |
if "API_KEY_INVALID" in error_message or "invalid" in error_message.lower():
|
| 370 |
-
error_msg = "β **Invalid API Key**
|
| 371 |
elif "quota" in error_message.lower():
|
| 372 |
-
error_msg = "
|
| 373 |
else:
|
| 374 |
-
error_msg = f"β **Error**
|
| 375 |
|
| 376 |
-
history.append({
|
|
|
|
|
|
|
|
|
|
| 377 |
yield history
|
| 378 |
return
|
| 379 |
|
|
@@ -382,7 +286,7 @@ def bot(
|
|
| 382 |
if history:
|
| 383 |
history.append({
|
| 384 |
"role": "assistant",
|
| 385 |
-
"content": f"β **Critical
|
| 386 |
})
|
| 387 |
yield history or []
|
| 388 |
|
|
@@ -391,7 +295,7 @@ def clear_chat():
|
|
| 391 |
return [], None
|
| 392 |
|
| 393 |
# ===================== UI =====================
|
| 394 |
-
with gr.Blocks(title="Gemini
|
| 395 |
|
| 396 |
# Header
|
| 397 |
gr.HTML(TITLE)
|
|
@@ -400,45 +304,43 @@ with gr.Blocks(title="Gemini AI Chatbot") as demo:
|
|
| 400 |
|
| 401 |
# Main chat interface
|
| 402 |
with gr.Row(equal_height=False):
|
| 403 |
-
with gr.Column(scale=1, min_width=
|
| 404 |
image_prompt = gr.Image(
|
| 405 |
type="pil",
|
| 406 |
-
label="
|
| 407 |
-
height=
|
| 408 |
elem_classes="image-container"
|
| 409 |
)
|
| 410 |
|
| 411 |
api_key = gr.Textbox(
|
| 412 |
label="π Google API Key",
|
| 413 |
type="password",
|
| 414 |
-
placeholder="
|
| 415 |
-
info="
|
| 416 |
)
|
| 417 |
|
| 418 |
-
|
| 419 |
-
clear_btn = gr.Button("ποΈ Clear Chat", variant="secondary", size="sm", scale=1)
|
| 420 |
|
| 421 |
with gr.Column(scale=2):
|
| 422 |
chatbot = gr.Chatbot(
|
| 423 |
-
label="π¬
|
| 424 |
-
height=
|
| 425 |
-
elem_classes="chatbot"
|
| 426 |
)
|
| 427 |
|
| 428 |
# Message input
|
| 429 |
with gr.Row():
|
| 430 |
msg = gr.Textbox(
|
| 431 |
-
placeholder="
|
| 432 |
-
label="Message",
|
| 433 |
show_label=False,
|
| 434 |
scale=9,
|
| 435 |
lines=2,
|
| 436 |
-
max_lines=
|
| 437 |
)
|
| 438 |
-
submit_btn = gr.Button("
|
| 439 |
|
| 440 |
# Parameters accordion
|
| 441 |
-
with gr.Accordion("βοΈ
|
| 442 |
with gr.Row():
|
| 443 |
temperature = gr.Slider(
|
| 444 |
minimum=0,
|
|
@@ -446,53 +348,53 @@ with gr.Blocks(title="Gemini AI Chatbot") as demo:
|
|
| 446 |
value=1.0,
|
| 447 |
step=0.1,
|
| 448 |
label="π‘οΈ Temperature",
|
| 449 |
-
info="
|
| 450 |
)
|
| 451 |
max_tokens = gr.Slider(
|
| 452 |
minimum=100,
|
| 453 |
maximum=8192,
|
| 454 |
value=2048,
|
| 455 |
step=100,
|
| 456 |
-
label="π Max Tokens",
|
| 457 |
-
info="Maximum
|
| 458 |
-
)
|
| 459 |
-
|
| 460 |
-
with gr.Row():
|
| 461 |
-
top_k = gr.Slider(
|
| 462 |
-
minimum=0,
|
| 463 |
-
maximum=100,
|
| 464 |
-
value=40,
|
| 465 |
-
step=1,
|
| 466 |
-
label="π Top-K",
|
| 467 |
-
info="Token selection pool size"
|
| 468 |
-
)
|
| 469 |
-
top_p = gr.Slider(
|
| 470 |
-
minimum=0,
|
| 471 |
-
maximum=1,
|
| 472 |
-
value=0.95,
|
| 473 |
-
step=0.05,
|
| 474 |
-
label="π Top-P",
|
| 475 |
-
info="Nucleus sampling threshold"
|
| 476 |
)
|
| 477 |
|
| 478 |
stop_seq = gr.Textbox(
|
| 479 |
-
label="π Stop Sequences",
|
| 480 |
-
placeholder="STOP, END
|
| 481 |
info="Optional sequences to stop generation",
|
| 482 |
value=""
|
| 483 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
|
| 485 |
# Examples
|
| 486 |
gr.Examples(
|
| 487 |
examples=[
|
| 488 |
["Explain quantum computing in simple terms"],
|
| 489 |
-
["Write a Python function to
|
| 490 |
-
["What are
|
| 491 |
-
["
|
| 492 |
],
|
| 493 |
inputs=msg,
|
| 494 |
-
label="π‘
|
| 495 |
-
elem_classes="examples"
|
| 496 |
)
|
| 497 |
|
| 498 |
# Event handlers
|
|
@@ -503,7 +405,16 @@ with gr.Blocks(title="Gemini AI Chatbot") as demo:
|
|
| 503 |
queue=False
|
| 504 |
).then(
|
| 505 |
bot,
|
| 506 |
-
inputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
outputs=chatbot
|
| 508 |
)
|
| 509 |
|
|
@@ -514,7 +425,16 @@ with gr.Blocks(title="Gemini AI Chatbot") as demo:
|
|
| 514 |
queue=False
|
| 515 |
).then(
|
| 516 |
bot,
|
| 517 |
-
inputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 518 |
outputs=chatbot
|
| 519 |
)
|
| 520 |
|
|
@@ -528,12 +448,10 @@ if __name__ == "__main__":
|
|
| 528 |
demo.queue(max_size=20)
|
| 529 |
demo.launch(
|
| 530 |
theme=gr.themes.Soft(
|
| 531 |
-
primary_hue="
|
| 532 |
secondary_hue="blue",
|
| 533 |
-
neutral_hue="slate",
|
| 534 |
spacing_size="md",
|
| 535 |
-
radius_size="lg"
|
| 536 |
-
font=["Inter", "ui-sans-serif", "system-ui", "sans-serif"]
|
| 537 |
),
|
| 538 |
css=CUSTOM_CSS,
|
| 539 |
ssr_mode=False
|
|
|
|
| 12 |
# For Hugging Face Spaces, use Secrets
|
| 13 |
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "")
|
| 14 |
|
| 15 |
+
TITLE = """<h1 align="center" style="background: linear-gradient(90deg, #9D50BB 0%, #6E48AA 50%, #4A90E2 100%);
|
| 16 |
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 3em; font-weight: bold; margin-bottom: 10px;">
|
| 17 |
+
Gemini Chatbot π₯ with Kelwa</h1>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
SUBTITLE = """<h2 align="center" style="color: #B19CD9; font-size: 1.5em; margin-top: 0;">
|
| 20 |
+
π¨ Create with Multimodal Gemini</h2>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
DESCRIPTION = """
|
| 23 |
+
<p align="center" style="color: #6E48AA; font-size: 1.1em;">
|
| 24 |
+
Enter your Google API Key to start chatting with Gemini AI! Get your API key from
|
| 25 |
+
<a href="https://aistudio.google.com/app/apikey" target="_blank" style="color: #9D50BB; font-weight: bold;">Google AI Studio</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
</p>
|
| 27 |
"""
|
| 28 |
|
| 29 |
IMAGE_WIDTH = 512
|
| 30 |
|
| 31 |
+
# Custom CSS for purple and baby blue theme
|
| 32 |
CUSTOM_CSS = """
|
|
|
|
| 33 |
.gradio-container {
|
| 34 |
max-width: 1400px !important;
|
| 35 |
margin: auto !important;
|
| 36 |
+
background: linear-gradient(to bottom, #f0e6ff 0%, #e6f3ff 100%) !important;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
.contain {
|
| 40 |
+
max-width: 100% !important;
|
| 41 |
}
|
| 42 |
|
|
|
|
| 43 |
button {
|
| 44 |
+
background: linear-gradient(90deg, #9D50BB 0%, #6E48AA 100%) !important;
|
| 45 |
border: none !important;
|
| 46 |
color: white !important;
|
| 47 |
font-weight: 600 !important;
|
| 48 |
+
border-radius: 8px !important;
|
| 49 |
+
padding: 10px 20px !important;
|
| 50 |
transition: all 0.3s ease !important;
|
|
|
|
| 51 |
}
|
| 52 |
|
| 53 |
button:hover {
|
| 54 |
+
background: linear-gradient(90deg, #B19CD9 0%, #89CFF0 100%) !important;
|
| 55 |
+
transform: scale(1.05) !important;
|
|
|
|
| 56 |
}
|
| 57 |
|
| 58 |
+
.input-text, textarea, input {
|
| 59 |
+
border: 2px solid #B19CD9 !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
border-radius: 10px !important;
|
| 61 |
+
padding: 10px !important;
|
|
|
|
|
|
|
|
|
|
| 62 |
}
|
| 63 |
|
| 64 |
.input-text:focus, textarea:focus, input:focus {
|
| 65 |
+
border-color: #6E48AA !important;
|
| 66 |
+
box-shadow: 0 0 10px rgba(157, 80, 187, 0.3) !important;
|
| 67 |
outline: none !important;
|
|
|
|
| 68 |
}
|
| 69 |
|
|
|
|
| 70 |
label {
|
| 71 |
+
color: #6E48AA !important;
|
| 72 |
font-weight: 600 !important;
|
|
|
|
| 73 |
margin-bottom: 8px !important;
|
| 74 |
}
|
| 75 |
|
| 76 |
+
/* Chatbot message styling */
|
| 77 |
+
.message-wrap {
|
| 78 |
+
padding: 10px !important;
|
|
|
|
|
|
|
|
|
|
| 79 |
}
|
| 80 |
|
| 81 |
+
.user {
|
| 82 |
+
background: linear-gradient(135deg, #9D50BB 0%, #6E48AA 100%) !important;
|
|
|
|
| 83 |
color: white !important;
|
| 84 |
+
border-radius: 15px !important;
|
| 85 |
padding: 12px 16px !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
}
|
| 87 |
|
| 88 |
+
.bot {
|
| 89 |
+
background: linear-gradient(135deg, #E6F3FF 0%, #D4E4F7 100%) !important;
|
| 90 |
+
color: #333 !important;
|
| 91 |
+
border-radius: 15px !important;
|
|
|
|
| 92 |
padding: 12px 16px !important;
|
| 93 |
+
border-left: 4px solid #89CFF0 !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
}
|
| 95 |
|
| 96 |
/* Accordion styling */
|
| 97 |
.accordion {
|
| 98 |
+
background: rgba(177, 156, 217, 0.1) !important;
|
| 99 |
+
border: 2px solid #B19CD9 !important;
|
| 100 |
+
border-radius: 10px !important;
|
| 101 |
+
margin: 10px 0 !important;
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
+
/* Image container */
|
| 105 |
.image-container {
|
| 106 |
+
border: 3px solid #89CFF0 !important;
|
| 107 |
border-radius: 15px !important;
|
| 108 |
+
padding: 10px !important;
|
| 109 |
background: white !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
}
|
| 111 |
|
| 112 |
/* Slider styling */
|
| 113 |
input[type="range"] {
|
| 114 |
+
accent-color: #9D50BB !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
}
|
| 116 |
|
| 117 |
/* Better spacing */
|
|
|
|
| 120 |
}
|
| 121 |
|
| 122 |
.block {
|
| 123 |
+
padding: 15px !important;
|
| 124 |
+
border-radius: 10px !important;
|
|
|
|
| 125 |
}
|
| 126 |
|
|
|
|
| 127 |
footer {
|
| 128 |
display: none !important;
|
| 129 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
"""
|
| 131 |
|
| 132 |
# ===================== HELPERS =====================
|
|
|
|
| 165 |
if not message or message.strip() == "":
|
| 166 |
return "", history
|
| 167 |
|
| 168 |
+
# Messages format: list of dicts with 'role' and 'content'
|
| 169 |
history.append({"role": "user", "content": message.strip()})
|
| 170 |
return "", history
|
| 171 |
except Exception as e:
|
|
|
|
| 184 |
):
|
| 185 |
"""Generate bot response with comprehensive error handling."""
|
| 186 |
try:
|
| 187 |
+
# Validate history
|
| 188 |
if not history or len(history) == 0:
|
| 189 |
yield history or []
|
| 190 |
return
|
| 191 |
|
| 192 |
+
# Validate API key
|
| 193 |
api_key = google_key.strip() if google_key else GOOGLE_API_KEY
|
| 194 |
if not api_key:
|
| 195 |
history.append({
|
| 196 |
"role": "assistant",
|
| 197 |
+
"content": "β **Error**: API key is missing. Please provide a valid Google API key in the textbox below.\n\nGet your free API key from [Google AI Studio](https://aistudio.google.com/app/apikey)"
|
| 198 |
})
|
| 199 |
yield history
|
| 200 |
return
|
| 201 |
|
| 202 |
+
# Configure API with new package
|
| 203 |
try:
|
| 204 |
client = genai.Client(api_key=api_key)
|
| 205 |
except Exception as e:
|
| 206 |
history.append({
|
| 207 |
"role": "assistant",
|
| 208 |
+
"content": f"β **Error**: Invalid API key.\n\n{str(e)}\n\nPlease check your API key and try again."
|
| 209 |
})
|
| 210 |
yield history
|
| 211 |
return
|
| 212 |
|
| 213 |
+
# Get user message
|
| 214 |
user_message = history[-1]["content"]
|
| 215 |
|
| 216 |
+
# Validate parameters
|
| 217 |
temperature = max(0.0, min(2.0, float(temperature)))
|
| 218 |
max_output_tokens = max(1, min(8192, int(max_output_tokens)))
|
| 219 |
top_k = max(1, min(100, int(top_k))) if top_k > 0 else None
|
| 220 |
top_p = max(0.0, min(1.0, float(top_p)))
|
| 221 |
|
| 222 |
+
# Build config
|
| 223 |
config = types.GenerateContentConfig(
|
| 224 |
temperature=temperature,
|
| 225 |
max_output_tokens=max_output_tokens,
|
|
|
|
| 230 |
if preprocess_stop_sequences(stop_sequences):
|
| 231 |
config.stop_sequences = preprocess_stop_sequences(stop_sequences)
|
| 232 |
|
| 233 |
+
# Prepare content
|
| 234 |
if image_prompt is None:
|
| 235 |
contents = [user_message]
|
| 236 |
else:
|
|
|
|
| 238 |
if processed_image is None:
|
| 239 |
history.append({
|
| 240 |
"role": "assistant",
|
| 241 |
+
"content": "β **Error**: Could not process the image. Please try uploading another image."
|
| 242 |
})
|
| 243 |
yield history
|
| 244 |
return
|
| 245 |
contents = [user_message, processed_image]
|
| 246 |
|
| 247 |
+
# Generate response
|
| 248 |
try:
|
| 249 |
response = client.models.generate_content_stream(
|
| 250 |
model='gemini-2.0-flash-exp',
|
|
|
|
| 260 |
yield history
|
| 261 |
time.sleep(0.01)
|
| 262 |
|
| 263 |
+
# Handle empty response
|
| 264 |
if not history[-1]["content"]:
|
| 265 |
+
history[-1]["content"] = "β οΈ No response generated. The content may have been blocked by safety filters. Please try a different prompt."
|
| 266 |
yield history
|
| 267 |
|
| 268 |
except Exception as e:
|
| 269 |
error_message = str(e)
|
| 270 |
if "API_KEY_INVALID" in error_message or "invalid" in error_message.lower():
|
| 271 |
+
error_msg = "β **Invalid API Key**. Please check your Google API key and try again."
|
| 272 |
elif "quota" in error_message.lower():
|
| 273 |
+
error_msg = "β **Quota Exceeded**. You've reached the API usage limit. Please try again later or check your quota."
|
| 274 |
else:
|
| 275 |
+
error_msg = f"β **Error generating response**: {error_message}"
|
| 276 |
|
| 277 |
+
history.append({
|
| 278 |
+
"role": "assistant",
|
| 279 |
+
"content": error_msg
|
| 280 |
+
})
|
| 281 |
yield history
|
| 282 |
return
|
| 283 |
|
|
|
|
| 286 |
if history:
|
| 287 |
history.append({
|
| 288 |
"role": "assistant",
|
| 289 |
+
"content": f"β **Critical error**: {str(e)}"
|
| 290 |
})
|
| 291 |
yield history or []
|
| 292 |
|
|
|
|
| 295 |
return [], None
|
| 296 |
|
| 297 |
# ===================== UI =====================
|
| 298 |
+
with gr.Blocks(title="Gemini Chatbot with Kelwa") as demo:
|
| 299 |
|
| 300 |
# Header
|
| 301 |
gr.HTML(TITLE)
|
|
|
|
| 304 |
|
| 305 |
# Main chat interface
|
| 306 |
with gr.Row(equal_height=False):
|
| 307 |
+
with gr.Column(scale=1, min_width=300):
|
| 308 |
image_prompt = gr.Image(
|
| 309 |
type="pil",
|
| 310 |
+
label="πΌοΈ Upload Image (Optional)",
|
| 311 |
+
height=350,
|
| 312 |
elem_classes="image-container"
|
| 313 |
)
|
| 314 |
|
| 315 |
api_key = gr.Textbox(
|
| 316 |
label="π Google API Key",
|
| 317 |
type="password",
|
| 318 |
+
placeholder="Enter your Google API key here...",
|
| 319 |
+
info="Your API key is not stored. Get one from Google AI Studio."
|
| 320 |
)
|
| 321 |
|
| 322 |
+
clear_btn = gr.Button("ποΈ Clear Chat", variant="secondary", size="sm")
|
|
|
|
| 323 |
|
| 324 |
with gr.Column(scale=2):
|
| 325 |
chatbot = gr.Chatbot(
|
| 326 |
+
label="π¬ Gemini Chat",
|
| 327 |
+
height=550
|
|
|
|
| 328 |
)
|
| 329 |
|
| 330 |
# Message input
|
| 331 |
with gr.Row():
|
| 332 |
msg = gr.Textbox(
|
| 333 |
+
placeholder="β¨ Type your message here and press Enter...",
|
| 334 |
+
label="Your Message",
|
| 335 |
show_label=False,
|
| 336 |
scale=9,
|
| 337 |
lines=2,
|
| 338 |
+
max_lines=5
|
| 339 |
)
|
| 340 |
+
submit_btn = gr.Button("Send π€", scale=1, variant="primary")
|
| 341 |
|
| 342 |
# Parameters accordion
|
| 343 |
+
with gr.Accordion("βοΈ Generation Parameters", open=False):
|
| 344 |
with gr.Row():
|
| 345 |
temperature = gr.Slider(
|
| 346 |
minimum=0,
|
|
|
|
| 348 |
value=1.0,
|
| 349 |
step=0.1,
|
| 350 |
label="π‘οΈ Temperature",
|
| 351 |
+
info="Higher = more creative, Lower = more focused"
|
| 352 |
)
|
| 353 |
max_tokens = gr.Slider(
|
| 354 |
minimum=100,
|
| 355 |
maximum=8192,
|
| 356 |
value=2048,
|
| 357 |
step=100,
|
| 358 |
+
label="π Max Output Tokens",
|
| 359 |
+
info="Maximum length of response"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 360 |
)
|
| 361 |
|
| 362 |
stop_seq = gr.Textbox(
|
| 363 |
+
label="π Stop Sequences (comma-separated)",
|
| 364 |
+
placeholder="STOP, END, ###",
|
| 365 |
info="Optional sequences to stop generation",
|
| 366 |
value=""
|
| 367 |
)
|
| 368 |
+
|
| 369 |
+
with gr.Accordion("π¬ Advanced Parameters", open=False):
|
| 370 |
+
with gr.Row():
|
| 371 |
+
top_k = gr.Slider(
|
| 372 |
+
minimum=0,
|
| 373 |
+
maximum=100,
|
| 374 |
+
value=40,
|
| 375 |
+
step=1,
|
| 376 |
+
label="π Top-K",
|
| 377 |
+
info="Limits token selection pool (0 = disabled)"
|
| 378 |
+
)
|
| 379 |
+
top_p = gr.Slider(
|
| 380 |
+
minimum=0,
|
| 381 |
+
maximum=1,
|
| 382 |
+
value=0.95,
|
| 383 |
+
step=0.01,
|
| 384 |
+
label="π Top-P (Nucleus)",
|
| 385 |
+
info="Cumulative probability threshold"
|
| 386 |
+
)
|
| 387 |
|
| 388 |
# Examples
|
| 389 |
gr.Examples(
|
| 390 |
examples=[
|
| 391 |
["Explain quantum computing in simple terms"],
|
| 392 |
+
["Write a Python function to sort a list"],
|
| 393 |
+
["What are the health benefits of meditation?"],
|
| 394 |
+
["Create a short story about a robot learning to paint"],
|
| 395 |
],
|
| 396 |
inputs=msg,
|
| 397 |
+
label="π‘ Example Prompts"
|
|
|
|
| 398 |
)
|
| 399 |
|
| 400 |
# Event handlers
|
|
|
|
| 405 |
queue=False
|
| 406 |
).then(
|
| 407 |
bot,
|
| 408 |
+
inputs=[
|
| 409 |
+
api_key,
|
| 410 |
+
image_prompt,
|
| 411 |
+
temperature,
|
| 412 |
+
max_tokens,
|
| 413 |
+
stop_seq,
|
| 414 |
+
top_k,
|
| 415 |
+
top_p,
|
| 416 |
+
chatbot
|
| 417 |
+
],
|
| 418 |
outputs=chatbot
|
| 419 |
)
|
| 420 |
|
|
|
|
| 425 |
queue=False
|
| 426 |
).then(
|
| 427 |
bot,
|
| 428 |
+
inputs=[
|
| 429 |
+
api_key,
|
| 430 |
+
image_prompt,
|
| 431 |
+
temperature,
|
| 432 |
+
max_tokens,
|
| 433 |
+
stop_seq,
|
| 434 |
+
top_k,
|
| 435 |
+
top_p,
|
| 436 |
+
chatbot
|
| 437 |
+
],
|
| 438 |
outputs=chatbot
|
| 439 |
)
|
| 440 |
|
|
|
|
| 448 |
demo.queue(max_size=20)
|
| 449 |
demo.launch(
|
| 450 |
theme=gr.themes.Soft(
|
| 451 |
+
primary_hue="purple",
|
| 452 |
secondary_hue="blue",
|
|
|
|
| 453 |
spacing_size="md",
|
| 454 |
+
radius_size="lg"
|
|
|
|
| 455 |
),
|
| 456 |
css=CUSTOM_CSS,
|
| 457 |
ssr_mode=False
|