Spaces:
Sleeping
Sleeping
import json | |
from pathlib import Path | |
import os | |
from langchain_google_genai import ChatGoogleGenerativeAI | |
# --- Data Loading --- | |
try: | |
print("Loading personas for RecommendationHandler module...") | |
# ABSOLUTE_PATH_TO_CONFIG = '/Users/jts-ai-sumana/rabbitlife_gemini/config/personas.json' | |
# with open(ABSOLUTE_PATH_TO_CONFIG, "r", encoding="utf-8") as f: | |
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent | |
CONFIG_PATH = PROJECT_ROOT / "config" / "personas.json" | |
with open(CONFIG_PATH, "r", encoding="utf-8") as f: | |
PERSONAS = json.load(f) | |
print(f"✅ Personas config loaded successfully from: {CONFIG_PATH}") | |
except Exception as e: | |
PERSONAS = {} | |
print(f"‼️ ERROR: Could not load personas.json: {e}") | |
# --- LLM Initialization --- | |
try: | |
INTEREST_LLM = ChatGoogleGenerativeAI( | |
model="gemini-2.5-flash", | |
google_api_key=os.getenv("GOOGLE_API_KEY"), | |
temperature=0, | |
) | |
print("✅ LLM for interest classification initialized.") | |
except Exception as e: | |
INTEREST_LLM = None | |
print(f"‼️ ERROR initializing LLM for RecommendationHandler: {e}") | |
# RECOMMENDATION ### | |
def generate_recommendation_from_profile( | |
age: int, | |
gender: str, # M, F | |
salary: int, | |
original_interest: str | |
) -> dict: | |
if not PERSONAS or not INTEREST_LLM: | |
return {"error": "Module not initialized correctly."} | |
# print(f"\n--- Generating Recommendation Profile ---") | |
# print(f"Input Data: Age={age}, Gender='{gender}', Salary={salary}, Interest='{original_interest}'") | |
# 1. หา Persona ที่ตรงกับอายุ | |
matched_persona = next((details for _, details in PERSONAS.items() if details.get("age_min", -1) <= age <= details.get("age_max", -1)), None) | |
if not matched_persona: | |
return {"error": f"No matching persona found for age {age}."} | |
# print(f"-> Matched Persona: '{matched_persona.get('persona_name', 'N/A')}'") | |
# 2. หา Tier ที่ตรงกับเงินเดือน | |
products_from_tier = [] | |
salary_tiers = matched_persona.get('recommendations_by_salary', []) | |
for tier in salary_tiers: | |
if tier.get("salary_min", 0) <= salary <= tier.get("salary_max", float('inf')): | |
products_from_tier = tier.get("products", []) | |
# print(f"-> Matched Salary Tier: '{tier.get('tier_name')}'") | |
break | |
if not products_from_tier and salary_tiers: | |
products_from_tier = salary_tiers[0].get("products", []) | |
# 3. รวบรวม product จาก Tier และเพศ | |
all_tier_products = [p for p in products_from_tier if p.get('gender') == 'all' or p.get('gender') == gender] | |
# 4. กรองตาม Interest - LLM | |
interest_prompt = f"""Analyze the user's interest and classify it into ONE of these categories: | |
'ประกันคุ้มครองชีวิต', 'ประกันเพื่อการลงทุน', 'ประกันสุขภาพ', 'ประกันอุบัติเหตุ', 'ทั่วไป'. | |
User Interest: "{original_interest}" | |
Category:""" | |
response = INTEREST_LLM.invoke(interest_prompt) | |
target_insurance_type = response.content.strip() | |
# print(f"-> LLM classified interest as: '{target_insurance_type}'") | |
products_to_recommend = all_tier_products | |
searched_outside_tier = False | |
valid_types = ['ประกันเพื่อการลงทุน', 'ประกันสุขภาพ', 'ประกันอุบัติเหตุ', 'ประกันคุ้มครองชีวิต'] | |
if target_insurance_type in valid_types: | |
# print(f"-> Filtering for interest: '{target_insurance_type}' across all tiers...") | |
all_persona_products = [] | |
for tier_fallback in matched_persona.get('recommendations_by_salary', []): | |
all_persona_products.extend(tier_fallback.get('products', [])) | |
# print(all_persona_products) | |
gender_filtered_products = [p for p in all_persona_products if p.get('gender') == 'all' or p.get('gender') == gender] | |
final_filtered_products = [p for p in gender_filtered_products if p.get('insurance_type') == target_insurance_type] | |
if final_filtered_products: | |
# print(f"-> Found {len(final_filtered_products)} matching products.") | |
products_to_recommend = final_filtered_products | |
else: | |
print(f"-> CRITICAL: No product of type '{target_insurance_type}' found anywhere in this persona.") | |
return {"error": f"ขออภัยค่ะ เราไม่พบผลิตภัณฑ์ประเภท '{target_insurance_type}' ที่เหมาะสมกับโปรไฟล์ของคุณในขณะนี้ค่ะ"} | |
# 5. ตรวจสอบและเพิ่มสัญญาหลักถ้าจำเป็น | |
has_basic_plan = any(p.get('plan_type') == 'Basic' for p in products_to_recommend) | |
auto_added_main_plan = False | |
if not has_basic_plan and any(p.get('plan_type') == 'Rider' for p in products_to_recommend): | |
# print("-> Filtered list only has Riders. Adding a default main plan.") | |
default_main_plans = [p for p in all_tier_products if p.get('plan_type') == 'Basic'] | |
if default_main_plans: | |
products_to_recommend.insert(0, default_main_plans[0]) | |
auto_added_main_plan = True | |
# 6. จัดกลุ่มและเตรียมข้อมูลสำหรับส่งคืน | |
main_plans_pre = [p for p in products_to_recommend if p.get('plan_type') == 'Basic'] | |
main_plans = [dict(t) for t in {tuple(d.items()) for d in main_plans_pre}] | |
riders_pre = [p for p in products_to_recommend if p.get('plan_type') == 'Rider'] | |
riders = [dict(t) for t in {tuple(d.items()) for d in riders_pre}] | |
print(main_plans) | |
main_plans_str = "ไม่มีแผนประกันหลักที่แนะนำ" | |
if main_plans: | |
main_plans_str = "\n".join([f"• **{p.get('product_name')}**: {p.get('product_description')}" for p in main_plans]) | |
riders_str = "ไม่มีสัญญาเพิ่มเติมที่แนะนำ" | |
if riders: | |
riders_str = "\n".join([f"• **{p.get('product_name')}**: {p.get('product_description')}" for p in riders]) | |
return { | |
"age": age, "gender": gender, "salary": f"{salary:,}", | |
"persona_name": matched_persona['persona_name'], | |
"persona_description": matched_persona['description'], | |
"original_interest": original_interest, | |
"main_plans_str": main_plans_str.strip(), | |
"riders_str": riders_str.strip(), | |
"auto_added_main_plan": auto_added_main_plan, | |
"searched_outside_tier": searched_outside_tier, | |
"interest_category": target_insurance_type if target_insurance_type != 'ทั่วไป' else 'ประกัน' | |
} |