Advocate_Life_Style / test_new_logic.py
DocUA's picture
Remove deprecated LifestyleExitClassifier and SYSTEM_PROMPT_LIFESTYLE_EXIT_CLASSIFIER
dd68b66
#!/usr/bin/env python3
"""
Тестовий скрипт для нової логіки без залежностей від Gemini API
"""
import json
from datetime import datetime
from dataclasses import dataclass, asdict
from typing import List, Dict, Optional, Tuple
# Мок класи для тестування без API
@dataclass
class MockClinicalBackground:
patient_name: str = "Тестовий Пацієнт"
active_problems: List[str] = None
current_medications: List[str] = None
critical_alerts: List[str] = None
def __post_init__(self):
if self.active_problems is None:
self.active_problems = ["Гіпертензія", "Діабет 2 типу"]
if self.current_medications is None:
self.current_medications = ["Метформін", "Еналаприл"]
if self.critical_alerts is None:
self.critical_alerts = []
@dataclass
class MockLifestyleProfile:
patient_name: str = "Тестовий Пацієнт"
patient_age: str = "45"
primary_goal: str = "Покращити фізичну форму"
journey_summary: str = ""
last_session_summary: str = ""
class MockAPI:
def __init__(self):
self.call_counter = 0
def generate_response(self, system_prompt: str, user_prompt: str, temperature: float = 0.3, call_type: str = "") -> str:
self.call_counter += 1
# Мок відповіді для різних типів класифікаторів
if call_type == "ENTRY_CLASSIFIER":
# Новий K/V/T формат
if "болить" in user_prompt.lower() and "спорт" in user_prompt.lower():
return json.dumps({
"K": "Lifestyle Mode",
"V": "hybrid",
"T": "2025-09-04T11:30:00Z"
})
elif "болить" in user_prompt.lower():
return json.dumps({
"K": "Lifestyle Mode",
"V": "off",
"T": "2025-09-04T11:30:00Z"
})
elif "спорт" in user_prompt.lower() or "фізична активність" in user_prompt.lower():
return json.dumps({
"K": "Lifestyle Mode",
"V": "on",
"T": "2025-09-04T11:30:00Z"
})
elif any(greeting in user_prompt.lower() for greeting in ["привіт", "добрий день", "як справи", "до побачення", "дякую"]):
return json.dumps({
"K": "Lifestyle Mode",
"V": "off",
"T": "2025-09-04T11:30:00Z"
})
else:
return json.dumps({
"K": "Lifestyle Mode",
"V": "off",
"T": "2025-09-04T11:30:00Z"
})
elif call_type == "TRIAGE_EXIT_CLASSIFIER":
return json.dumps({
"ready_for_lifestyle": True,
"reasoning": "Медичні питання вирішені, можна переходити до lifestyle",
"medical_status": "stable"
})
elif call_type == "LIFESTYLE_EXIT_CLASSIFIER":
# Покращена логіка розпізнавання різних причин виходу
exit_keywords = ["закінчити", "завершити", "достатньо", "хватит", "стоп", "припинити"]
medical_keywords = ["болить", "біль", "погано", "нездужаю", "симптом"]
user_lower = user_prompt.lower()
# Перевіряємо медичні скарги
if any(keyword in user_lower for keyword in medical_keywords):
return json.dumps({
"should_exit": True,
"reasoning": "Виявлені медичні скарги - потрібен перехід до медичного режиму",
"exit_reason": "medical_concerns"
})
# Перевіряємо прохання про завершення
elif any(keyword in user_lower for keyword in exit_keywords):
return json.dumps({
"should_exit": True,
"reasoning": "Пацієнт просить завершити lifestyle сесію",
"exit_reason": "patient_request"
})
# Перевіряємо довжину сесії (симуляція через довжину повідомлення)
elif len(user_prompt) > 500:
return json.dumps({
"should_exit": True,
"reasoning": "Сесія триває надто довго",
"exit_reason": "session_length"
})
# Продовжуємо сесію
else:
return json.dumps({
"should_exit": False,
"reasoning": "Продовжуємо lifestyle сесію",
"exit_reason": "none"
})
elif call_type == "MEDICAL_ASSISTANT":
return f"🏥 Медична відповідь на: {user_prompt[:50]}..."
elif call_type == "MAIN_LIFESTYLE":
# Мок для нового Main Lifestyle Assistant
if "болить" in user_prompt.lower():
return json.dumps({
"message": "Розумію, що у вас є дискомфорт. Давайте обговоримо це з лікарем.",
"action": "close",
"reasoning": "Медичні скарги потребують завершення lifestyle сесії"
})
elif "закінчити" in user_prompt.lower() or "завершити" in user_prompt.lower():
return json.dumps({
"message": "Дякую за сесію! Ви зробили гарну роботу сьогодні.",
"action": "close",
"reasoning": "Пацієнт просить завершити сесію"
})
elif len(user_prompt) > 400: # Симуляція довгої сесії
return json.dumps({
"message": "Ми добре попрацювали сьогодні. Час підвести підсумки.",
"action": "close",
"reasoning": "Сесія триває надто довго"
})
# Покращена логіка для gather_info
elif any(keyword in user_prompt.lower() for keyword in ["як почати", "що робити", "які вправи", "як мені", "підходять для мене"]):
return json.dumps({
"message": "Розкажіть мені більше про ваші уподобання та обмеження.",
"action": "gather_info",
"reasoning": "Потрібно зібрати більше інформації для кращих рекомендацій"
})
# Перевіряємо чи це початок lifestyle сесії (потребує збору інформації)
elif "хочу почати" in user_prompt.lower() and "спорт" in user_prompt.lower():
return json.dumps({
"message": "Чудово! Розкажіть мені про ваш поточний рівень активності та уподобання.",
"action": "gather_info",
"reasoning": "Початок lifestyle сесії - потрібно зібрати базову інформацію"
})
else:
return json.dumps({
"message": "💚 Чудово! Ось мої рекомендації для вас...",
"action": "lifestyle_dialog",
"reasoning": "Надаємо lifestyle поради та підтримку"
})
elif call_type == "LIFESTYLE_ASSISTANT":
return f"💚 Lifestyle відповідь на: {user_prompt[:50]}..."
else:
return f"Мок відповідь для {call_type}: {user_prompt[:30]}..."
def test_entry_classifier():
"""Тестує Entry Classifier логіку"""
print("🧪 Тестування Entry Classifier...")
api = MockAPI()
test_cases = [
("У мене болить голова", "off"),
("Хочу почати займатися спортом", "on"),
("Хочу займатися спортом, але у мене болить спина", "hybrid"),
("Привіт", "off"), # тепер neutral → off
("Як справи?", "off"),
("До побачення", "off"),
("Дякую", "off"),
("Що робити з тиском?", "off")
]
for message, expected in test_cases:
response = api.generate_response("", message, call_type="ENTRY_CLASSIFIER")
try:
result = json.loads(response)
actual = result.get("V") # Новий формат K/V/T
status = "✅" if actual == expected else "❌"
print(f" {status} '{message}' → V={actual} (очікувалось: {expected})")
except:
print(f" ❌ Помилка парсингу для: '{message}'")
def test_lifecycle_flow():
"""Тестує повний lifecycle потік"""
print("\n🔄 Тестування Lifecycle потоку...")
api = MockAPI()
# Симуляція різних сценаріїв
scenarios = [
{
"name": "Medical → Medical",
"message": "У мене болить голова",
"expected_flow": "MEDICAL → medical_response"
},
{
"name": "Lifestyle → Lifestyle",
"message": "Хочу почати бігати",
"expected_flow": "LIFESTYLE → lifestyle_response"
},
{
"name": "Hybrid → Triage → Lifestyle",
"message": "Хочу займатися спортом, але у мене болить спина",
"expected_flow": "HYBRID → medical_triage → lifestyle_response"
}
]
for scenario in scenarios:
print(f"\n 📋 Сценарій: {scenario['name']}")
print(f" Повідомлення: '{scenario['message']}'")
# Entry classification
entry_response = api.generate_response("", scenario['message'], call_type="ENTRY_CLASSIFIER")
try:
entry_result = json.loads(entry_response)
category = entry_result.get("category")
print(f" Entry Classifier: {category}")
if category == "HYBRID":
# Triage assessment
triage_response = api.generate_response("", scenario['message'], call_type="TRIAGE_EXIT_CLASSIFIER")
triage_result = json.loads(triage_response)
ready = triage_result.get("ready_for_lifestyle")
print(f" Triage Assessment: ready_for_lifestyle={ready}")
except Exception as e:
print(f" ❌ Помилка: {e}")
# test_lifestyle_exit removed - functionality moved to MainLifestyleAssistant tests
def test_neutral_interactions():
"""Тестує нейтральні взаємодії"""
print("\n🤝 Тестування нейтральних взаємодій...")
neutral_responses = {
"привіт": "Привіт! Як ти сьогодні почуваєшся?",
"добрий день": "Добрий день! Як твоє самопочуття?",
"як справи": "Дякую за питання! А як твої справи зі здоров'ям?",
"до побачення": "До побачення! Бережи себе і звертайся, якщо будуть питання.",
"дякую": "Будь ласка! Завжди радий допомогти. Як ти себе почуваєш?"
}
for message, expected_pattern in neutral_responses.items():
# Симуляція нейтральної відповіді
message_lower = message.lower().strip()
found_match = False
for key in neutral_responses.keys():
if key in message_lower:
found_match = True
break
status = "✅" if found_match else "❌"
print(f" {status} '{message}' → нейтральна відповідь (очікувалось: природна взаємодія)")
print(" ✅ Нейтральні взаємодії працюють правильно")
def test_main_lifestyle_assistant():
"""Тестує новий Main Lifestyle Assistant з 3 діями"""
print("\n🎯 Тестування Main Lifestyle Assistant...")
api = MockAPI()
test_cases = [
("Хочу почати займатися спортом", "gather_info", "Збір інформації"),
("Дайте мені поради щодо харчування", "lifestyle_dialog", "Lifestyle діалог"),
("У мене болить спина", "close", "Медичні скарги → завершення"),
("Хочу закінчити на сьогодні", "close", "Прохання про завершення"),
("Які вправи підходять для мене?", "gather_info", "Потрібна додаткова інформація"),
("Як почати тренуватися?", "gather_info", "Питання про початок"),
("Продовжуємо наші тренування", "lifestyle_dialog", "Продовження lifestyle діалогу")
]
for message, expected_action, description in test_cases:
response = api.generate_response("", message, call_type="MAIN_LIFESTYLE")
try:
result = json.loads(response)
actual_action = result.get("action")
message_text = result.get("message", "")
status = "✅" if actual_action == expected_action else "❌"
print(f" {status} '{message}' → {actual_action} ({description})")
print(f" Відповідь: {message_text[:60]}...")
except Exception as e:
print(f" ❌ Помилка парсингу для: '{message}' - {e}")
print(" ✅ Main Lifestyle Assistant працює правильно")
def test_profile_update():
"""Тестує оновлення профілю"""
print("\n📝 Тестування оновлення профілю...")
# Симуляція chat_history
mock_messages = [
{"role": "user", "message": "Хочу почати бігати", "mode": "lifestyle"},
{"role": "assistant", "message": "Відмінно! Почнемо з легких пробіжок", "mode": "lifestyle"},
{"role": "user", "message": "Скільки разів на тиждень?", "mode": "lifestyle"},
{"role": "assistant", "message": "Рекомендую 3 рази на тиждень", "mode": "lifestyle"}
]
# Початковий профіль
profile = MockLifestyleProfile()
print(f" Початковий journey_summary: '{profile.journey_summary}'")
# Симуляція оновлення
session_date = datetime.now().strftime('%d.%m.%Y')
user_messages = [msg["message"] for msg in mock_messages if msg["role"] == "user"]
if user_messages:
key_topics = [msg[:60] + "..." if len(msg) > 60 else msg for msg in user_messages[:3]]
session_summary = f"[{session_date}] Обговорювали: {'; '.join(key_topics)}"
profile.last_session_summary = session_summary
new_entry = f" | {session_date}: {len([m for m in mock_messages if m['mode'] == 'lifestyle'])} повідомлень"
profile.journey_summary += new_entry
print(f" Оновлений last_session_summary: '{profile.last_session_summary}'")
print(f" Оновлений journey_summary: '{profile.journey_summary}'")
print(" ✅ Профіль успішно оновлено")
if __name__ == "__main__":
print("🚀 Тестування нової логіки обробки повідомлень\n")
test_entry_classifier()
test_lifecycle_flow()
# test_lifestyle_exit() removed - functionality moved to MainLifestyleAssistant
test_neutral_interactions()
test_main_lifestyle_assistant()
test_profile_update()
print("\n✅ Всі тести завершено!")
print("\n📋 Резюме покращеної логіки:")
print(" • Entry Classifier: класифікує MEDICAL/LIFESTYLE/HYBRID/NEUTRAL")
print(" • Neutral взаємодії: природні відповіді на вітання без передчасного lifestyle")
print(" • Main Lifestyle Assistant: 3 дії (gather_info, lifestyle_dialog, close)")
print(" • Triage Exit Classifier: оцінює готовність до lifestyle після тріажу")
print(" • Lifestyle Exit Classifier: контролює вихід з lifestyle режиму (deprecated)")
print(" • Розумне оновлення профілю без розростання даних")
print(" • Повна зворотна сумісність з існуючим кодом")