Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files- src/app.py +96 -0
- src/main.py +121 -0
src/app.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import os
|
| 3 |
+
from main import get_english_tutor_agent, sesi_metne_cevir, metni_sese_cevir
|
| 4 |
+
|
| 5 |
+
st.set_page_config(page_title="AI English Tutor", page_icon="🇬🇧", layout="centered")
|
| 6 |
+
|
| 7 |
+
@st.cache_resource
|
| 8 |
+
def ajan_yukle():
|
| 9 |
+
return get_english_tutor_agent()
|
| 10 |
+
|
| 11 |
+
ajan, sistem_komutu = ajan_yukle()
|
| 12 |
+
|
| 13 |
+
# --- YAN MENÜ (SIDEBAR) AYARLARI ---
|
| 14 |
+
st.sidebar.title("⚙️ Eğitim Ayarları")
|
| 15 |
+
|
| 16 |
+
st.sidebar.markdown("**Sen Hangi Dilde Konuşacaksın?**")
|
| 17 |
+
ogrenci_dili = st.sidebar.radio(
|
| 18 |
+
"Mikrofon Algılama Dili",
|
| 19 |
+
["İngilizce", "Türkçe"],
|
| 20 |
+
help="İngilizce pratik yaparken 'İngilizce' seçili kalsın. Öğretmene Türkçe bir şey sormak istersen 'Türkçe'ye alabilirsin."
|
| 21 |
+
)
|
| 22 |
+
# Seçime göre kod belirle
|
| 23 |
+
stt_lang = "en-US" if ogrenci_dili == "İngilizce" else "tr-TR"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
st.sidebar.markdown("---")
|
| 27 |
+
st.sidebar.markdown("**Öğretmen Aksanı**")
|
| 28 |
+
ogretmen_sesi = st.sidebar.radio(
|
| 29 |
+
"AI Öğretmenin Sesi",
|
| 30 |
+
["Amerikalı (Aria)", "Türk (Emel)"],
|
| 31 |
+
help="Amerikalı Aria sadece İngilizceyi kusursuz okur, Türkçeyi bozarak okur. Türk Emel ise Türkçeyi kusursuz okur, İngilizceyi Türk aksanıyla okur."
|
| 32 |
+
)
|
| 33 |
+
# Seçime göre model belirle
|
| 34 |
+
tts_voice = "en-US-AriaNeural" if ogretmen_sesi == "Amerikalı (Aria)" else "tr-TR-EmelNeural"
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
# --- ARAYÜZ (UI) ---
|
| 38 |
+
st.title("🎙️ AI English Tutor")
|
| 39 |
+
st.markdown("Hello! İster yazarak ister konuşarak pratik yapabiliriz.")
|
| 40 |
+
|
| 41 |
+
if "mesaj_gecmisi" not in st.session_state:
|
| 42 |
+
st.session_state.mesaj_gecmisi = []
|
| 43 |
+
|
| 44 |
+
for mesaj in st.session_state.mesaj_gecmisi:
|
| 45 |
+
with st.chat_message(mesaj["role"]):
|
| 46 |
+
st.markdown(mesaj["content"])
|
| 47 |
+
|
| 48 |
+
col1, col2 = st.columns([1, 1])
|
| 49 |
+
with col1:
|
| 50 |
+
yazili_girdi = st.chat_input("Yazarak cevap ver...")
|
| 51 |
+
with col2:
|
| 52 |
+
sesli_girdi = st.audio_input("Veya konuşarak cevap ver")
|
| 53 |
+
|
| 54 |
+
kullanici_girdisi = None
|
| 55 |
+
|
| 56 |
+
if sesli_girdi is not None:
|
| 57 |
+
with open("gecici_ses.wav", "wb") as f:
|
| 58 |
+
f.write(sesli_girdi.getvalue())
|
| 59 |
+
|
| 60 |
+
with st.spinner(f"Sesin {ogrenci_dili} olarak dinleniyor..."):
|
| 61 |
+
# Seçilen dil kodunu fonksiyona gönderiyoruz
|
| 62 |
+
kullanici_girdisi = sesi_metne_cevir("gecici_ses.wav", dil_kodu=stt_lang)
|
| 63 |
+
|
| 64 |
+
elif yazili_girdi:
|
| 65 |
+
kullanici_girdisi = yazili_girdi
|
| 66 |
+
|
| 67 |
+
if kullanici_girdisi:
|
| 68 |
+
with st.chat_message("user"):
|
| 69 |
+
st.markdown(kullanici_girdisi)
|
| 70 |
+
st.session_state.mesaj_gecmisi.append({"role": "user", "content": kullanici_girdisi})
|
| 71 |
+
|
| 72 |
+
with st.chat_message("assistant"):
|
| 73 |
+
with st.spinner("Öğretmen cevap hazırlıyor..."):
|
| 74 |
+
try:
|
| 75 |
+
langgraph_mesajlari = [("system", sistem_komutu)]
|
| 76 |
+
for m in st.session_state.mesaj_gecmisi:
|
| 77 |
+
langgraph_mesajlari.append((m["role"], m["content"]))
|
| 78 |
+
|
| 79 |
+
cevap = ajan.invoke({"messages": langgraph_mesajlari})
|
| 80 |
+
ham_icerik = cevap["messages"][-1].content
|
| 81 |
+
|
| 82 |
+
if isinstance(ham_icerik, list):
|
| 83 |
+
son_mesaj = "".join([blok.get("text", "") for blok in ham_icerik if isinstance(blok, dict) and "text" in blok])
|
| 84 |
+
else:
|
| 85 |
+
son_mesaj = ham_icerik
|
| 86 |
+
|
| 87 |
+
st.markdown(son_mesaj)
|
| 88 |
+
st.session_state.mesaj_gecmisi.append({"role": "assistant", "content": son_mesaj})
|
| 89 |
+
|
| 90 |
+
with st.spinner("Seslendiriliyor..."):
|
| 91 |
+
# Seçilen öğretmen sesini fonksiyona gönderiyoruz
|
| 92 |
+
ses_dosyasi = metni_sese_cevir(son_mesaj, ses_modeli=tts_voice)
|
| 93 |
+
st.audio(ses_dosyasi, autoplay=True)
|
| 94 |
+
|
| 95 |
+
except Exception as e:
|
| 96 |
+
st.error(f"Sistem Hatası: {e}")
|
src/main.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import asyncio
|
| 3 |
+
import edge_tts
|
| 4 |
+
import speech_recognition as sr
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 7 |
+
from langgraph.prebuilt import create_react_agent
|
| 8 |
+
|
| 9 |
+
load_dotenv()
|
| 10 |
+
|
| 11 |
+
SISTEM_KOMUTU = """Sen İngilizce öğreten bir yapay zekâ ajanısın ve kullanıcıyla arkadaş gibi konuşursun.
|
| 12 |
+
|
| 13 |
+
Amaç:
|
| 14 |
+
Kullanıcının seviyesini belirleyip ona uygun şekilde İngilizceyi adım adım öğretmek ve onu akıcı şekilde konuşur hale getirmek.
|
| 15 |
+
|
| 16 |
+
Genel Kurallar:
|
| 17 |
+
|
| 18 |
+
* Cevapların kısa olsun (maksimum 1-2 cümle)
|
| 19 |
+
* Basit ve anlaşılır konuş
|
| 20 |
+
* Sohbet tarzında ilerle (ders anlatma)
|
| 21 |
+
* Samimi ve destekleyici ol
|
| 22 |
+
|
| 23 |
+
SEVİYE BELİRLEME:
|
| 24 |
+
|
| 25 |
+
* Konuşmanın başında kullanıcıya 2-3 basit soru sor
|
| 26 |
+
* Sorular İngilizce olsun (çok basit seviyede)
|
| 27 |
+
* Kullanıcının cevaplarına göre seviyesini tahmin et:
|
| 28 |
+
|
| 29 |
+
* Çok zorlanıyorsa → başlangıç (A1)
|
| 30 |
+
* Basit cevaplar veriyorsa → A1-A2
|
| 31 |
+
* Daha rahat konuşuyorsa → üst seviyeye geç
|
| 32 |
+
|
| 33 |
+
SEVİYEYE GÖRE DAVRANIŞ:
|
| 34 |
+
|
| 35 |
+
* Başlangıç seviyesinde:
|
| 36 |
+
|
| 37 |
+
* Daha fazla Türkçe destek ver
|
| 38 |
+
* Çok basit İngilizce kullan
|
| 39 |
+
* Seviye arttıkça:
|
| 40 |
+
|
| 41 |
+
* Türkçeyi azalt
|
| 42 |
+
* İngilizceyi artır
|
| 43 |
+
* Soruları biraz zorlaştır
|
| 44 |
+
|
| 45 |
+
ÖĞRETİM YAKLAŞIMI:
|
| 46 |
+
|
| 47 |
+
* Basitten zora ilerle (küçük adımlar)
|
| 48 |
+
* Aynı anda tek konuya odaklan
|
| 49 |
+
* Kullanıcı hazır olmadan zorlaştırma
|
| 50 |
+
* Sohbet içinde öğret
|
| 51 |
+
|
| 52 |
+
DİL KULLANIMI:
|
| 53 |
+
|
| 54 |
+
* Kullanıcı Türkçe yazarsa:
|
| 55 |
+
|
| 56 |
+
* Kısa Türkçe cevap ver
|
| 57 |
+
* Ardından basit İngilizce cümle ekle
|
| 58 |
+
* Kullanıcı İngilizce yazarsa:
|
| 59 |
+
|
| 60 |
+
* İngilizce cevap ver
|
| 61 |
+
* Gerekirse kısa Türkçe destek ekle
|
| 62 |
+
|
| 63 |
+
KULLANICIYI KONUŞTURMA:
|
| 64 |
+
|
| 65 |
+
* Her mesajda kullanıcıyı İngilizce yazmaya teşvik et
|
| 66 |
+
* Küçük görevler ver:
|
| 67 |
+
|
| 68 |
+
* “Sen de dene”
|
| 69 |
+
* “Bunu İngilizce söyleyebilir misin?”
|
| 70 |
+
* Tek seferde 1 soru sor
|
| 71 |
+
|
| 72 |
+
HATA DÜZELTME:
|
| 73 |
+
|
| 74 |
+
* Hataları doğal şekilde düzelt
|
| 75 |
+
* Uzun açıklama yapma
|
| 76 |
+
* Doğru cümleyi örnek olarak ver
|
| 77 |
+
|
| 78 |
+
İLERLEME:
|
| 79 |
+
|
| 80 |
+
* Kullanıcı iyi performans gösterirse:
|
| 81 |
+
|
| 82 |
+
* Soruları biraz zorlaştır
|
| 83 |
+
* Zorlanırsa:
|
| 84 |
+
|
| 85 |
+
* Basitleştir ve Türkçe destek ver
|
| 86 |
+
|
| 87 |
+
SOHBET:
|
| 88 |
+
|
| 89 |
+
* Günlük konular kullan (okul, arkadaşlar, hobiler)
|
| 90 |
+
* Kullanıcıyı rahat hissettir
|
| 91 |
+
* Motive edici ol
|
| 92 |
+
|
| 93 |
+
Amaç:
|
| 94 |
+
Kullanıcının korkmadan, adım adım İngilizce konuşabilmesini sağlamak
|
| 95 |
+
"""
|
| 96 |
+
|
| 97 |
+
def get_english_tutor_agent():
|
| 98 |
+
llm = ChatGoogleGenerativeAI(model="gemini-flash-lite-latest", temperature=0.7)
|
| 99 |
+
return create_react_agent(llm, []), SISTEM_KOMUTU
|
| 100 |
+
|
| 101 |
+
# SİHİRLİ DOKUNUŞ: Artık dil dışarıdan parametre olarak geliyor
|
| 102 |
+
def sesi_metne_cevir(audio_file_path, dil_kodu="en-US"):
|
| 103 |
+
r = sr.Recognizer()
|
| 104 |
+
try:
|
| 105 |
+
with sr.AudioFile(audio_file_path) as source:
|
| 106 |
+
audio = r.record(source)
|
| 107 |
+
metin = r.recognize_google(audio, language=dil_kodu)
|
| 108 |
+
return metin
|
| 109 |
+
except sr.UnknownValueError:
|
| 110 |
+
return "Anlayamadım, lütfen tekrar eder misin?"
|
| 111 |
+
except Exception as e:
|
| 112 |
+
return f"Ses çözümleme hatası: {e}"
|
| 113 |
+
|
| 114 |
+
# SİHİRLİ DOKUNUŞ: Ses modeli dışarıdan parametre olarak geliyor
|
| 115 |
+
def metni_sese_cevir(text, ses_modeli="en-US-AriaNeural", output_file_path="ogretmen_yaniti.mp3"):
|
| 116 |
+
async def uret():
|
| 117 |
+
communicate = edge_tts.Communicate(text, ses_modeli)
|
| 118 |
+
await communicate.save(output_file_path)
|
| 119 |
+
|
| 120 |
+
asyncio.run(uret())
|
| 121 |
+
return output_file_path
|