| import streamlit as st |
| import requests |
| import json |
| import os |
| import graphviz |
| import time |
| from dotenv import load_dotenv |
| from prompts import SYSTEM_INSTRUCTIONS |
|
|
| st.set_page_config(page_title="StepWise: Algorithmic Thinking Mentor", layout="wide") |
| load_dotenv() |
| api_key = os.getenv("API_KEY") |
|
|
| def call_gemini(messages): |
| url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent?key={api_key}" |
| headers = {'Content-Type': 'application/json'} |
| contents = [] |
| for m in messages: |
| role = "model" if m["role"] == "assistant" else "user" |
| contents.append({"role": role, "parts": [{"text": m["content"]}]}) |
| payload = {"contents": contents} |
| for attempt in range(3): |
| try: |
| response = requests.post(url, headers=headers, json=payload) |
| if response.status_code == 200: |
| return response.json()['candidates'][0]['content']['parts'][0]['text'] |
| elif response.status_code in [429, 503]: |
| time.sleep(5) |
| continue |
| else: |
| return f"🚨 API Hatası: {response.status_code}" |
| except: |
| time.sleep(2) |
| continue |
| return "🚨 Sunucu yoğun, lütfen tekrar dene." |
|
|
| if "messages" not in st.session_state: st.session_state.messages = [] |
| if "flow_nodes" not in st.session_state: st.session_state.flow_nodes = [] |
| if "flow_edges" not in st.session_state: st.session_state.flow_edges = [] |
| if "mode" not in st.session_state: st.session_state.mode = "chat" |
|
|
| with st.sidebar: |
| st.title("🚀 StepWise Paneli") |
| st.divider() |
|
|
| if st.button("📚 Konu Tekrarı", use_container_width=True): |
| st.session_state.mode = "chat" |
| st.session_state.messages = [{"role": "user", "content": f"SİSTEM: {SYSTEM_INSTRUCTIONS}\n\nGÖREV: Merhaba! Lütfen öğrenciye konuyu en baştan anlatmaya başla."}] |
| with st.spinner("Öğretmen hazırlanıyor..."): |
| res = call_gemini(st.session_state.messages) |
| st.session_state.messages.append({"role": "assistant", "content": res}) |
| st.rerun() |
|
|
| if st.button("📐 Akış Şeması İnşası", use_container_width=True): |
| st.session_state.mode = "flowchart" |
| st.session_state.flow_nodes, st.session_state.flow_edges = [], [] |
| st.session_state.messages = [{"role": "user", "content": f"SİSTEM: {SYSTEM_INSTRUCTIONS}\n\nGÖREV: Akış Şeması modundayız. Lütfen öğrenciye içinde mutlaka bir KARAR yapısı olan orta zorlukta bir problem ver ve çizmesini iste."}] |
| with st.spinner("Senaryo oluşturuluyor..."): |
| res = call_gemini(st.session_state.messages) |
| st.session_state.messages.append({"role": "assistant", "content": res}) |
| st.rerun() |
|
|
| if st.button("🎁 Girdi-Çıktı Kutusu", use_container_width=True): |
| st.session_state.mode = "blackbox" |
| st.session_state.messages = [{"role": "user", "content": f"SİSTEM: {SYSTEM_INSTRUCTIONS}\n\nGÖREV: Girdi-Çıktı oyun modundayız. Gizli bir kural belirle, kategoriyi söyle ve ilk Girdi: X, Çıktı: Y örneğini ver."}] |
| with st.spinner("Kutu hazırlanıyor..."): |
| res = call_gemini(st.session_state.messages) |
| st.session_state.messages.append({"role": "assistant", "content": res}) |
| st.rerun() |
|
|
| if st.button("🔧 Algoritma Tamirhanesi", use_container_width=True): |
| st.session_state.mode = "repair" |
| st.session_state.messages = [{"role": "user", "content": f"SİSTEM: {SYSTEM_INSTRUCTIONS}\n\nGÖREV: Tamirhane modundayız. Lütfen adımları birbirine karışmış bozuk bir senaryo ver ve öğrenciden doğru sıralamayı (A-B-C gibi) iste."}] |
| with st.spinner("Arıza tespit ediliyor..."): |
| res = call_gemini(st.session_state.messages) |
| st.session_state.messages.append({"role": "assistant", "content": res}) |
| st.rerun() |
|
|
| st.divider() |
| if st.button("🗑️ Her Şeyi Sıfırla", use_container_width=True): |
| st.session_state.messages, st.session_state.flow_nodes, st.session_state.flow_edges, st.session_state.mode = [], [], [], "chat" |
| st.rerun() |
|
|
| st.title("🤖 StepWise: Algoritmik Düşünme Mentoru") |
|
|
| if st.session_state.mode == "repair": |
| st.info("🔧 **Tamirhane Modu:** Aşağıdaki adımları mantıklı bir sıraya dizebilir misin? Tahminini aşağıya yaz!") |
|
|
| elif st.session_state.mode == "flowchart": |
| with st.container(border=True): |
| st.subheader("🛠️ Akış Şeması Tasarım Alanı") |
| c1, c2, c3 = st.columns([2, 3, 1]) |
| with c1: |
| shape_type = st.selectbox("Şekil:", ["Elips (Başla/Bitir)", "Dikdörtgen (İşlem)", "Eşkenar Dörtgen (Karar)", "Paralelkenar (Bilgi Girişi)"]) |
| with c2: |
| node_text = st.text_input("Metin:", key="node_input") |
| with c3: |
| st.write("") |
| if st.button("➕ Ekle", use_container_width=True): |
| if node_text: |
| st.session_state.flow_nodes.append({"id": str(len(st.session_state.flow_nodes)), "text": node_text, "type": shape_type}) |
| st.rerun() |
| if st.session_state.flow_nodes: |
| bc1, bc2, bc3, bc4 = st.columns([2, 2, 2, 1]) |
| with bc1: |
| source = st.selectbox("Kaynak:", options=st.session_state.flow_nodes, format_func=lambda x: f"{x['id']}: {x['text']}") |
| with bc2: |
| target = st.selectbox("Hedef:", options=st.session_state.flow_nodes, format_func=lambda x: f"{x['id']}: {x['text']}") |
| with bc3: |
| label = st.selectbox("Yol:", ["", "Evet", "Hayır"]) if "Eşkenar" in source["type"] else "" |
| with bc4: |
| st.write("") |
| if st.button("🔗 Bağla", use_container_width=True): |
| st.session_state.flow_edges.append({"from": source["id"], "to": target["id"], "label": label}) |
| st.rerun() |
| dot = graphviz.Digraph() |
| for node in st.session_state.flow_nodes: |
| shape = "ellipse" if "Elips" in node["type"] else "box" if "Dikdörtgen" in node["type"] else "diamond" if "Eşkenar" in node["type"] else "parallelogram" |
| dot.node(node["id"], node["text"], shape=shape, color="blue", style="filled", fillcolor="white") |
| for edge in st.session_state.flow_edges: |
| dot.edge(edge["from"], edge["to"], label=edge["label"]) |
| st.graphviz_chart(dot) |
| b1, b2 = st.columns(2) |
| if b1.button("🗑️ Şemayı Temizle"): |
| st.session_state.flow_nodes, st.session_state.flow_edges = [], [] |
| st.rerun() |
| if b2.button("✅ Bitti ve Kontrol Et"): |
| node_map = {n['id']: n['text'] for n in st.session_state.flow_nodes} |
| schema_desc = " | ".join([f"{node_map[e['from']]} -> {node_map[e['to']]} ({e['label']})" for e in st.session_state.flow_edges]) |
| st.session_state.messages.append({"role": "user", "content": f"SİSTEM: Öğrenci şemayı bitirdi. Yapı şu şekilde: {schema_desc}. Lütfen analiz et ve geri bildirim ver."}) |
| with st.spinner("Kontrol ediliyor..."): |
| res = call_gemini(st.session_state.messages) |
| st.session_state.messages.append({"role": "assistant", "content": res}) |
| st.rerun() |
|
|
| elif st.session_state.mode == "blackbox": |
| with st.container(border=True): |
| st.subheader("🎁 Sihirli Kara Kutu") |
| box_viz = graphviz.Digraph() |
| box_viz.attr(rankdir='LR') |
| box_viz.node("IN", "Girdi", shape="parallelogram", style="filled", fillcolor="#E1BEE7") |
| box_viz.node("BOX", "???", shape="box", style="filled", fillcolor="#212121", fontcolor="white") |
| box_viz.node("OUT", "Çıktı", shape="parallelogram", style="filled", fillcolor="#C8E6C9") |
| box_viz.edge("IN", "BOX"); box_viz.edge("BOX", "OUT") |
| st.graphviz_chart(box_viz) |
|
|
| st.divider() |
| for message in st.session_state.messages: |
| if not message["content"].startswith("SİSTEM"): |
| with st.chat_message(message["role"]): |
| st.markdown(message["content"]) |
|
|
| if prompt := st.chat_input("Mesajını yaz..."): |
| st.session_state.messages.append({"role": "user", "content": prompt}) |
| with st.chat_message("user"): st.markdown(prompt) |
| with st.chat_message("assistant"): |
| res = call_gemini(st.session_state.messages) |
| st.markdown(res) |
| st.session_state.messages.append({"role": "assistant", "content": res}) |