Skip to content

Commit 2a022bb

Browse files
fix: add input validation and caching to vibe-check per review
1 parent dfa332b commit 2a022bb

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

‎backend/ai_service.py‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,3 +962,96 @@ def generate_chat_response(user_message: str, conversation_history: list = []) -
962962
]
963963
import random
964964
return random.choice(variations)
965+
966+
# =========================================================================
967+
# VIBE-CHECK ENDPOINT LOGIC
968+
# Implements strict validation and caching per PR #903 feedback.
969+
# =========================================================================
970+
971+
import hashlib
972+
from cache_service import cache_service
973+
974+
def generate_vibe_check(raw_vibe_prompt: str, raw_count: int = 5) -> Optional[dict]:
975+
"""
976+
Generate an AI vibe check with strict server-side validation and caching.
977+
978+
Args:
979+
raw_vibe_prompt: The raw emotional/vibe query from the user.
980+
raw_count: The requested number of book recommendations.
981+
982+
Returns:
983+
Dict containing the AI response and recommendations, or None on failure.
984+
"""
985+
986+
# ---------------------------------------------------------
987+
# 1. VALIDATION: Clamp the Count (Security & Cost Control)
988+
# ---------------------------------------------------------
989+
try:
990+
count = int(raw_count)
991+
if count < 1:
992+
count = 1
993+
elif count > 10: # Hard limit to prevent API abuse
994+
count = 10
995+
except (ValueError, TypeError):
996+
count = 5 # Safe default
997+
998+
# ---------------------------------------------------------
999+
# 2. VALIDATION: Vibe Prompt Length (Prompt Injection Prevention)
1000+
# ---------------------------------------------------------
1001+
if not raw_vibe_prompt or not isinstance(raw_vibe_prompt, str):
1002+
logger.warning("generate_vibe_check: Empty or invalid prompt received.")
1003+
return None
1004+
1005+
safe_vibe_prompt = raw_vibe_prompt.strip()
1006+
1007+
# Restrict prompt to 400 characters to prevent huge payload bills
1008+
if len(safe_vibe_prompt) > 400:
1009+
logger.warning(f"generate_vibe_check: Prompt truncated from {len(safe_vibe_prompt)} chars.")
1010+
safe_vibe_prompt = safe_vibe_prompt[:400]
1011+
1012+
# ---------------------------------------------------------
1013+
# 3. CACHING: Prevent Expensive Redundant LLM Calls
1014+
# ---------------------------------------------------------
1015+
# Create a deterministic hash of the sanitized inputs
1016+
prompt_hash = hashlib.md5(f"{safe_vibe_prompt}_{count}".encode('utf-8')).hexdigest()
1017+
cache_key = f"vibe_check_{prompt_hash}"
1018+
1019+
cached_result = cache_service.get(cache_key)
1020+
if cached_result:
1021+
logger.info(f"generate_vibe_check: Serving cached response for key {cache_key}")
1022+
return cached_result
1023+
1024+
# ---------------------------------------------------------
1025+
# 4. LLM GENERATION
1026+
# ---------------------------------------------------------
1027+
if not llm_service.is_available():
1028+
logger.error("generate_vibe_check: No LLM service available.")
1029+
return None
1030+
1031+
# Construct a specific prompt for the vibe check
1032+
system_prompt = (
1033+
f"You are a literary vibe-checker. The user has described their current mood: '{safe_vibe_prompt}'. "
1034+
f"Give them a short, poetic reading diagnosis (under 50 words) and recommend exactly {count} books "
1035+
"that perfectly match this specific emotional frequency. Format your response clearly."
1036+
)
1037+
1038+
try:
1039+
response_text = llm_service.generate_text(
1040+
prompt=system_prompt,
1041+
max_tokens=llm_service.config.get('recommendation_max_tokens', 250)
1042+
)
1043+
1044+
if response_text:
1045+
result = {
1046+
"vibe_diagnosis": response_text,
1047+
"sanitized_query": safe_vibe_prompt,
1048+
"count_returned": count
1049+
}
1050+
1051+
# Cache the successful result for 24 hours
1052+
cache_service.set(cache_key, result, timeout=86400)
1053+
return result
1054+
1055+
except Exception as e:
1056+
logger.error(f"generate_vibe_check failed: {str(e)}", exc_info=True)
1057+
return None

0 commit comments

Comments
 (0)