Spaces:
Sleeping
Sleeping
Commit
·
dd547c8
1
Parent(s):
b1206db
Implement Gemini-based automatic chat name generation
Browse files- Replace Bedrock chat naming with Gemini model
- Add simple message detection (greetings use timestamp)
- Creative naming for meaningful messages using Gemini
- Auto-trigger after first user message
- Fallback to timestamp on Gemini API errors
- api_routes_v2.py +100 -70
api_routes_v2.py
CHANGED
|
@@ -353,80 +353,94 @@ def _record_model_attribution(
|
|
| 353 |
|
| 354 |
## helpers for presigned url chat name and some more updates
|
| 355 |
|
| 356 |
-
def
|
| 357 |
"""
|
| 358 |
-
|
| 359 |
-
Use the same model as other tasks if available (session.proposed_pipeline._model).
|
| 360 |
-
Fallback to env, then a sane default.
|
| 361 |
"""
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
|
|
|
|
|
|
|
|
|
| 369 |
|
| 370 |
-
def
|
| 371 |
"""
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
If invocation fails, returns 'New Chat'.
|
| 375 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
try:
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
"
|
| 383 |
-
"
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
contentType="application/json",
|
| 409 |
-
body=json.dumps(body),
|
| 410 |
-
)
|
| 411 |
-
payload = json.loads(resp["body"].read())
|
| 412 |
-
results = payload.get("results") or []
|
| 413 |
-
if results:
|
| 414 |
-
return (results[0].get("outputText") or "").strip().strip('"').strip() or "New Chat"
|
| 415 |
-
return "New Chat"
|
| 416 |
-
except Exception:
|
| 417 |
-
return "New Chat"
|
| 418 |
|
| 419 |
def _maybe_generate_chat_name(chat_id: str):
|
| 420 |
"""
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
Uses the same Bedrock model as used elsewhere where possible.
|
| 424 |
"""
|
| 425 |
try:
|
| 426 |
s = session_manager.get_session(chat_id) or {}
|
|
|
|
|
|
|
| 427 |
if s.get("chat_name"):
|
| 428 |
return
|
| 429 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
first_user = None
|
| 431 |
for m in msgs:
|
| 432 |
if (m.get("role") or "") == "user":
|
|
@@ -434,26 +448,38 @@ def _maybe_generate_chat_name(chat_id: str):
|
|
| 434 |
if not content.lower().startswith("uploaded file:"):
|
| 435 |
first_user = content
|
| 436 |
break
|
|
|
|
| 437 |
if not first_user:
|
| 438 |
return
|
|
|
|
|
|
|
| 439 |
file_name = (s.get("file_metadata") or {}).get("file_name")
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
f"
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
session_manager.update_session(
|
| 449 |
chat_id,
|
| 450 |
{
|
| 451 |
"chat_name": title[:100],
|
| 452 |
"chat_name_generated_at": datetime.utcnow().isoformat() + "Z",
|
| 453 |
-
"chat_name_model":
|
| 454 |
},
|
| 455 |
)
|
| 456 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
pass
|
| 458 |
|
| 459 |
def _generate_presigned_get_url(bucket: str, key: str, expires_in: int = 604800) -> Dict[str, str]:
|
|
@@ -585,6 +611,10 @@ def _add_and_mirror_message(chat_id: str, role: str, content: str, file_metadata
|
|
| 585 |
|
| 586 |
# 3. Save to S3 (this updates MongoDB metadata too)
|
| 587 |
_save_conversation_to_s3(chat_id, current_messages)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 588 |
|
| 589 |
def _assistant_response_payload(
|
| 590 |
chat_id: str,
|
|
|
|
| 353 |
|
| 354 |
## helpers for presigned url chat name and some more updates
|
| 355 |
|
| 356 |
+
def _is_simple_message(message: str) -> bool:
|
| 357 |
"""
|
| 358 |
+
Check if message is a simple greeting or test message that should use timestamp naming.
|
|
|
|
|
|
|
| 359 |
"""
|
| 360 |
+
if not message or len(message.strip()) > 30:
|
| 361 |
+
return False
|
| 362 |
+
|
| 363 |
+
simple_patterns = [
|
| 364 |
+
"hello", "hi", "hey", "test", "testing", "hola", "bonjour",
|
| 365 |
+
"namaste", "greetings", "good morning", "good afternoon", "good evening"
|
| 366 |
+
]
|
| 367 |
+
|
| 368 |
+
msg_lower = message.lower().strip()
|
| 369 |
+
return any(pattern in msg_lower for pattern in simple_patterns)
|
| 370 |
|
| 371 |
+
def _generate_chat_name_with_gemini(user_message: str, file_name: Optional[str] = None) -> str:
|
| 372 |
"""
|
| 373 |
+
Generate a creative chat name using Gemini model.
|
| 374 |
+
Returns generated name or falls back to timestamp on error.
|
|
|
|
| 375 |
"""
|
| 376 |
+
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
|
| 377 |
+
GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.0-flash")
|
| 378 |
+
GEMINI_ENDPOINT = f"https://generativelanguage.googleapis.com/v1beta/models/{GEMINI_MODEL}:generateContent"
|
| 379 |
+
|
| 380 |
+
if not GEMINI_API_KEY:
|
| 381 |
+
# Fallback to timestamp if no API key
|
| 382 |
+
return f"Chat - {datetime.utcnow().strftime('%Y-%m-%d %H:%M')}"
|
| 383 |
+
|
| 384 |
+
# Build prompt
|
| 385 |
+
prompt = (
|
| 386 |
+
"Create a succinct, creative, and descriptive 3-6 word title for this chat session.\n"
|
| 387 |
+
"The title should capture the essence of what the user wants to do.\n"
|
| 388 |
+
"Return ONLY the title, without quotes or extra text.\n\n"
|
| 389 |
+
f"User's first message: {user_message}\n"
|
| 390 |
+
)
|
| 391 |
+
|
| 392 |
+
if file_name:
|
| 393 |
+
prompt += f"File uploaded: {file_name}\n"
|
| 394 |
+
|
| 395 |
try:
|
| 396 |
+
import requests
|
| 397 |
+
response = requests.post(
|
| 398 |
+
f"{GEMINI_ENDPOINT}?key={GEMINI_API_KEY}",
|
| 399 |
+
headers={"Content-Type": "application/json"},
|
| 400 |
+
json={
|
| 401 |
+
"contents": [{"parts": [{"text": prompt}]}],
|
| 402 |
+
"generationConfig": {
|
| 403 |
+
"temperature": 0.7,
|
| 404 |
+
"maxOutputTokens": 50,
|
| 405 |
+
}
|
| 406 |
+
},
|
| 407 |
+
timeout=5, # Short timeout to avoid blocking
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
response.raise_for_status()
|
| 411 |
+
result = response.json()
|
| 412 |
+
|
| 413 |
+
# Extract text from Gemini response
|
| 414 |
+
title = result["candidates"][0]["content"]["parts"][0]["text"]
|
| 415 |
+
title = title.strip().strip('"').strip("'").strip()
|
| 416 |
+
|
| 417 |
+
# Validate title length (should be reasonable)
|
| 418 |
+
if len(title) > 100:
|
| 419 |
+
title = title[:100]
|
| 420 |
+
|
| 421 |
+
return title or f"Chat - {datetime.utcnow().strftime('%Y-%m-%d %H:%M')}"
|
| 422 |
+
|
| 423 |
+
except Exception as e:
|
| 424 |
+
print(f"Gemini chat name generation failed: {e}")
|
| 425 |
+
# Fallback to timestamp
|
| 426 |
+
return f"Chat - {datetime.utcnow().strftime('%Y-%m-%d %H:%M')}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
|
| 428 |
def _maybe_generate_chat_name(chat_id: str):
|
| 429 |
"""
|
| 430 |
+
Auto-generate a chat title after the first real user message.
|
| 431 |
+
Uses Gemini for creative naming, or timestamp for simple messages.
|
|
|
|
| 432 |
"""
|
| 433 |
try:
|
| 434 |
s = session_manager.get_session(chat_id) or {}
|
| 435 |
+
|
| 436 |
+
# Skip if chat name already exists
|
| 437 |
if s.get("chat_name"):
|
| 438 |
return
|
| 439 |
+
|
| 440 |
+
# Load messages from S3 (V3 architecture)
|
| 441 |
+
msgs = _load_conversation_from_s3(chat_id)
|
| 442 |
+
|
| 443 |
+
# Find first real user message (not file upload)
|
| 444 |
first_user = None
|
| 445 |
for m in msgs:
|
| 446 |
if (m.get("role") or "") == "user":
|
|
|
|
| 448 |
if not content.lower().startswith("uploaded file:"):
|
| 449 |
first_user = content
|
| 450 |
break
|
| 451 |
+
|
| 452 |
if not first_user:
|
| 453 |
return
|
| 454 |
+
|
| 455 |
+
# Get file name if available
|
| 456 |
file_name = (s.get("file_metadata") or {}).get("file_name")
|
| 457 |
+
|
| 458 |
+
# Check if it's a simple message
|
| 459 |
+
if _is_simple_message(first_user):
|
| 460 |
+
# Use timestamp for simple greetings
|
| 461 |
+
title = f"Chat - {datetime.utcnow().strftime('%Y-%m-%d %H:%M')}"
|
| 462 |
+
model_used = "timestamp"
|
| 463 |
+
else:
|
| 464 |
+
# Use Gemini for creative naming
|
| 465 |
+
title = _generate_chat_name_with_gemini(first_user, file_name)
|
| 466 |
+
model_used = os.getenv("GEMINI_MODEL", "gemini-2.0-flash")
|
| 467 |
+
|
| 468 |
+
# Update session with chat name
|
| 469 |
session_manager.update_session(
|
| 470 |
chat_id,
|
| 471 |
{
|
| 472 |
"chat_name": title[:100],
|
| 473 |
"chat_name_generated_at": datetime.utcnow().isoformat() + "Z",
|
| 474 |
+
"chat_name_model": model_used,
|
| 475 |
},
|
| 476 |
)
|
| 477 |
+
|
| 478 |
+
print(f"✅ Generated chat name for {chat_id}: '{title}' (using {model_used})")
|
| 479 |
+
|
| 480 |
+
except Exception as e:
|
| 481 |
+
print(f"Error generating chat name: {e}")
|
| 482 |
+
# Don't fail the request if chat naming fails
|
| 483 |
pass
|
| 484 |
|
| 485 |
def _generate_presigned_get_url(bucket: str, key: str, expires_in: int = 604800) -> Dict[str, str]:
|
|
|
|
| 611 |
|
| 612 |
# 3. Save to S3 (this updates MongoDB metadata too)
|
| 613 |
_save_conversation_to_s3(chat_id, current_messages)
|
| 614 |
+
|
| 615 |
+
# 4. Auto-generate chat name after first user message
|
| 616 |
+
if role == "user":
|
| 617 |
+
_maybe_generate_chat_name(chat_id)
|
| 618 |
|
| 619 |
def _assistant_response_payload(
|
| 620 |
chat_id: str,
|