piskochatboot / app.py
koesan's picture
Update app.py
a936f83 verified
from fastapi import FastAPI, Request, UploadFile, File
from fastapi.middleware.cors import CORSMiddleware
from deepface import DeepFace
import cv2
import numpy as np
import tempfile
import os
import google.generativeai as genai
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Google GenAI yapılandırması (Kullanıcının verdiği şekilde bırakıldı)
os.environ['GOOGLE_API_KEY'] = "AIzaSyCfAzWCivzmDJdeUZtCzhvpWvPTVlRhhwg" # Lütfen gerçek API anahtarınızı kullanın ve güvenli yönetin.
try:
genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))
# Kullanıcının belirttiği model adı. Eğer bu model mevcut değilse veya erişiminiz yoksa hata alırsınız.
model = genai.GenerativeModel("models/gemini-2.0-flash-lite") # Önerilen daha yaygın model: "gemini-1.5-flash-latest" veya "gemini-pro"
# Orijinal: "models/gemini-2.0-flash-lite"
except Exception as e:
print(f"Google GenAI yapılandırma hatası: {e}. Model None olarak ayarlandı.")
model = None
system_prompt = (
"Sen deneyimli ama sade bir dille konuşan, içten ve empatik bir psikologsun. "
"Danışanla konuşurken kısa, samimi ve destekleyici cevaplar ver. "
"Yargılamadan dinle, anlayış göster ve gerekirse profesyonel destek almanın doğal bir şey olduğunu hatırlat. "
"Sade konuş, insan gibi, uzun uzun açıklama yapma. Danışanı yormadan cesaret ver."
"Önceki konuşmaları da dikkate alarak hastanın ismini ve vereceğin cevabı da önceki konuşmalara göre ver."
"Sürekli olarak aynı türde cevaplar verme. Gerçek bir insan piskolog gibi davran. Yapay zeka olduğun anlşılmasın."
)
@app.post("/chat")
async def chat(request: Request):
content_type = request.headers.get("content-type", "")
# Görsel geldiyse
if "multipart/form-data" in content_type:
form = await request.form()
image_file: UploadFile = form.get("image")
if image_file:
temp_file_path = None
try:
# Geçici dosyayı güvenli bir şekilde oluştur
with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file_obj: # Dosya uzantısı eklemek iyi bir pratiktir
temp_file_obj.write(await image_file.read())
temp_file_path = temp_file_obj.name
img = cv2.imread(temp_file_path)
if img is None:
# Geçici dosyayı silmeyi unutma
if temp_file_path and os.path.exists(temp_file_path):
os.remove(temp_file_path)
return {"response": "Görüntü dosyası okunamadı veya bozuk."}
# DeepFace analizi
# Bazen ilk analizde tüm özellikler gelmeyebilir, hepsini isteyelim
# enforce_detection=False yüz bulunamazsa hata vermez, None döner veya boş liste.
# True yaparsak yüz bulamazsa exception atar.
analysis_results = DeepFace.analyze(img_path=temp_file_path, actions=["age", "gender", "emotion"], enforce_detection=True)
# DeepFace birden fazla yüz bulursa ilkini alır
if not analysis_results: # Eğer enforce_detection=False ise bu kontrol gerekebilir
if temp_file_path and os.path.exists(temp_file_path):
os.remove(temp_file_path)
return {"response": "Görüntüde yüz tespit edilemedi."}
analysis = analysis_results[0]
age = analysis["age"]
gender_probabilities = analysis["gender"]
emotion = analysis["dominant_emotion"]
# Cinsiyetin en yüksek olasılığını seç
gender = "Man" if gender_probabilities["Man"] > gender_probabilities["Woman"] else "Woman"
# İstemciye "Erkek" veya "Kadın" olarak göndermek daha iyi olabilir, ancak mevcut yapıyı koruyoruz.
# GenAI yapısına uyumlu döndür (Kullanıcının istediği gibi)
# Bu endpointten age, gender, emotion'ı ayrı JSON field'ları olarak döndürmek,
# /stress_evaluation'a gönderimi kolaylaştırır. Ancak mevcut yapı korunuyor.
response_text = f"Bu kişi {gender} ve yaklaşık {age} yaşında. Yüz ifadesine göre şu anki duygusu: {emotion}."
return {"response": response_text, "age": age, "gender": gender, "emotion": emotion} # age, gender, emotion eklendi
except Exception as e:
return {"response": f"Yüz analizi yapılamadı: {str(e)}"}
finally:
# Geçici dosyayı her zaman sil
if temp_file_path and os.path.exists(temp_file_path):
os.remove(temp_file_path)
else:
return {"response": "Görsel dosya bulunamadı."}
# Metin gelmişse: GenAI tarafı
if not model:
return {"response": "GenAI modeli yüklenemedi. Lütfen API anahtarınızı ve model adını kontrol edin."}
try:
body = await request.json()
except Exception:
return {"response": "Geçersiz JSON formatı."}
user_input = body.get("message", "")
previous_messages = body.get("previousMessages", [])
full_prompt = system_prompt + "\n\n"
for message in previous_messages:
full_prompt += f"Danışan: {message['user']}\nPsikolog: {message['bot']}\n"
full_prompt += f"Danışan: {user_input}\nPsikolog:"
try:
response = model.generate_content([full_prompt])
return {"response": response.text.strip()}
except Exception as e:
return {"response": f"GenAI hatası: {str(e)}"}
# Sadece puanları içeren listeler (sıralama önemli!)
stress_factors_6_12 = [
20, 40, 25, 30, 35, 50, 60, 40, 45, 100,
70, 63, 68, 50, 45, 50, 46, 47, 40, 56,
53, 35, 30, 41
]
stress_factors_12_18 = [
40, 50, 35, 30, 45, 35, 25, 40, 40, 100,
100, 70, 90, 63, 68, 50, 53, 56, 50, 51,
47, 45, 53, 41, 34, 37, 31, 50
]
stress_factors_19_plus = [
100, 73, 65, 63, 63, 53, 50, 47, 45, 45,
44, 40, 39, 39, 39, 38, 37, 36, 35, 32,
30, 29, 29, 29, 28, 26, 26, 25, 24, 23,
20, 20, 20, 19, 19, 18, 17, 16
]
@app.post("/stress_evaluation")
async def stress_evaluation(request: Request):
if not model: # Modelin yüklenip yüklenmediğini kontrol et
return {"response": "GenAI modeli yüklenemedi. Lütfen API anahtarınızı ve model adını kontrol edin."}
data = await request.json()
age_str = data.get("age") # /chat endpointinden gelen age
gender_from_chat = data.get("gender") # /chat endpointinden gelen gender ("Man" veya "Woman")
emotion_from_chat = data.get("emotion") # /chat endpointinden gelen emotion
stress_answers = data.get("stressAnswers", []) # [0, 1, 1, 0, ...] formatında
if age_str is None or gender_from_chat is None or emotion_from_chat is None or not stress_answers:
return {"response": "Eksik bilgi gönderildi: yaş, cinsiyet, duygu ve stres cevapları gereklidir."}
try:
age = int(age_str)
except ValueError:
return {"response": "Yaş değeri sayı olmalıdır."}
# Yaşa göre puan listesi
age_group_name = ""
if 6 <= age <= 12:
factors = stress_factors_6_12
age_group_name = "6-12 yaş"
elif 13 <= age <= 18:
factors = stress_factors_12_18
age_group_name = "13-18 yaş"
elif age >= 19: # 19 ve üzeri için aynı liste
factors = stress_factors_19_plus
age_group_name = "19 yaş ve üzeri"
else:
return {"response": f"Desteklenmeyen yaş grubu: {age}. Lütfen 6 yaş ve üzeri bir değer girin."}
if len(stress_answers) != len(factors):
return {"response": f"{age_group_name} için {len(factors)} adet stres cevabı bekleniyor. Gönderilen: {len(stress_answers)}"}
# Stres puanını hesapla
total_score = sum(score for selected, score in zip(stress_answers, factors) if selected == 1)
# Stres yüzdesi (0-100 aralığında)
max_possible_score = sum(factors)
stress_percentage = (total_score / max_possible_score) * 100 if max_possible_score > 0 else 0
# Seviye, risk ve oda önerisi belirle (Yüzdeye göre)
stress_level_description = ""
risk_info = ""
specific_room_recommendation_text = ""
gender_tr = "Erkek" if gender_from_chat == "Man" else "Kadın" # Prompt için Türkçeleştirme
if stress_percentage < 34: # Düşük stres (%0-33)
stress_level_description = "Düşük stres"
risk_info = "Bu seviyede stres genellikle yönetilebilirdir."
specific_room_recommendation_text = (
"Görünüşe göre stres seviyen şu an için oldukça iyi bir düzeyde. Bu harika! "
"Yine de gün içinde biraz mola vermek ve zihnini dinlendirmek istersen, uygulamamızdaki **Sera Odası** tam sana göre olabilir. "
"Orada doğanın sakinleştirici sesleri ve görüntüleriyle huzur bulabilirsin."
)
elif stress_percentage < 67: # Orta stres (%34-66)
stress_level_description = "Orta düzey stres (%50 hastalık riski)"
risk_info = "Bu seviyedeki stres, uzun vadede bazı sağlık sorunları için yaklaşık %50 civarında bir risk oluşturabilir. Bu yüzden dikkatli olmakta ve önlem almakta fayda var."
specific_room_recommendation_text = (
"Stres seviyen orta düzeyde görünüyor. Bazen hayatın koşturmacası içinde böyle hissetmek doğal. "
"Bu konuda bir adım atmak istersen, uygulamamızdaki **Meditasyon Odası**'na bir göz atabilirsin. "
"Farklı rehberli meditasyon seansları ile gevşeyebilir ve zihnine bir mola verebilirsin. Denemeye ne dersin?"
)
else: # Yüksek stres (%67-100)
stress_level_description = "Yüksek stres (%80 hastalık riski)"
risk_info = "Yüksek stres, %80'e varan oranlarda sağlık sorunları riskini beraberinde getirebilir. Bu durumu ciddiye almak ve kendine iyi bakmak gerçekten önemli."
specific_room_recommendation_text = (
"Stres seviyenin yüksek olduğunu görüyorum. Lütfen unutma, bu konuda yalnız değilsin ve destek aramak çok doğal bir adım. "
"Uygulamamız üzerinden bir **uzman psikologla canlı görüşme** yaparak profesyonel yardım almayı düşünebilirsin. "
"Bir uzmanla konuşmak, yaşadıklarınla başa çıkmana yardımcı olabilir."
)
# GenAI için prompt oluşturma
# system_prompt'u burada kullanmıyoruz, çünkü o daha çok genel chatbot davranışı için.
# Bu endpoint spesifik bir görev için (stres değerlendirme sonrası tavsiye).
# Ancak, eğer istenirse system_prompt'un bir kısmı buraya da eklenebilir.
# Şimdilik sadece görev odaklı bir prompt.
prompt_for_bot = (
f"Danışan {age} yaşında bir {gender_tr}. Stres testi sonuçlarına göre stres yüzdesi %{stress_percentage:.0f}. "
f"Bu durum '{stress_level_description}' olarak değerlendiriliyor. Testi yaparkenki yüz ifadesi '{emotion_from_chat}' şeklindeydi. {risk_info}\n\n"
f"Lütfen bu danışana durumuyla ilgili kısa, samimi ve destekleyici bir geri bildirim ver. "
f"Konuşmanın sonunda, ona şu öneriyi yap: '{specific_room_recommendation_text}'\n\n"
"Psikolog rolünde, sade bir dille konuş, danışanı yormadan cesaretlendir ve anlayış göster."
)
# print(f"DEBUG: GenAI Prompt: {prompt_for_bot}") # Debug için
try:
# Sadece prompt'u gönderiyoruz, çünkü bu tek seferlik bir tavsiye, devam eden bir sohbet değil.
response = model.generate_content([prompt_for_bot])
advice_from_genai = response.text.strip()
except Exception as e:
advice_from_genai = (
f"Şu anda sana özel bir tavsiye oluştururken bir sorunla karşılaştım. "
f"Ancak genel olarak, stres seviyenin '{stress_level_description}' olduğunu ve bununla ilgili {risk_info.lower()} bilmelisin. "
f"Durumuna göre {specific_room_recommendation_text.split('.')[0].lower()} gibi aktiviteler iyi gelebilir. "
f"(Hata: {str(e)})"
)
return {
"stress_score_raw": total_score, # Ham puan
"stress_percentage": round(stress_percentage, 2), # Yüzdeyi 0-100 arası gönder
"stress_level_description": stress_level_description,
"advice": advice_from_genai # GenAI'den gelen tam tavsiye
}