Zinebhm commited on
Commit
5b2503a
·
verified ·
1 Parent(s): 5e6188d

Update frontend/app.py

Browse files
Files changed (1) hide show
  1. frontend/app.py +112 -62
frontend/app.py CHANGED
@@ -1,10 +1,13 @@
1
- import streamlit as st
2
- import requests
3
  import json
4
- import pandas as pd
5
  from datetime import datetime
6
 
7
- API_URL = "http://127.0.0.1:8001"
 
 
 
 
 
8
 
9
  st.set_page_config(
10
  page_title="LearnLanguage 2026 • Tutor",
@@ -12,48 +15,72 @@ st.set_page_config(
12
  layout="wide",
13
  )
14
 
15
- # ---------------- SESSION ----------------
16
- if "messages" not in st.session_state:
17
- st.session_state.messages = []
 
 
 
 
 
18
 
19
- if "last" not in st.session_state:
20
- st.session_state.last = None
21
 
22
- if "mode" not in st.session_state:
23
- st.session_state.mode = "conversation"
 
 
 
 
 
 
24
 
25
- if "token" not in st.session_state:
26
- st.session_state.token = None
 
 
 
 
 
 
 
27
 
28
 
29
- # ---------------- STREAM FUNCTION ----------------
30
  def stream_chat(message: str):
 
 
 
31
 
32
- payload = {
33
- "message": message,
34
- "mode": st.session_state.mode
35
- }
 
 
36
 
37
- headers = {
38
- "Authorization": f"Bearer {st.session_state.token}"
39
- }
40
 
41
- r = requests.post(
42
- f"{API_URL}/chat",
43
- json=payload,
44
- headers=headers,
45
- timeout=180
46
- )
47
 
48
- r.raise_for_status()
 
49
 
50
- data = r.json()
 
51
 
52
- yield ("final", data)
53
- # ---------------- SIDEBAR AUTH ----------------
 
 
54
  with st.sidebar:
55
  st.markdown("## 🔐 Account")
56
 
 
 
 
 
 
 
57
  tabL, tabR = st.tabs(["Login", "Register"])
58
 
59
  with tabL:
@@ -61,13 +88,22 @@ with st.sidebar:
61
  pwd = st.text_input("Password", type="password", key="login_pwd")
62
 
63
  if st.button("Login", use_container_width=True):
64
- r = requests.post(
65
  f"{API_URL}/auth/login",
66
- json={"email": email, "password": pwd}
 
67
  )
68
 
 
 
 
69
  if r.status_code == 200:
70
- st.session_state.token = r.json()["token"]
 
 
 
 
 
71
  st.success("Logged in ✅")
72
  st.rerun()
73
  else:
@@ -79,43 +115,67 @@ with st.sidebar:
79
  pwd2 = st.text_input("Password", type="password", key="reg_pwd")
80
 
81
  if st.button("Create account", use_container_width=True):
82
- r = requests.post(
83
  f"{API_URL}/auth/register",
84
- json={"email": email2, "username": username2, "password": pwd2}
 
85
  )
86
 
 
 
 
87
  if r.status_code == 200:
88
- st.session_state.token = r.json()["token"]
 
 
 
 
 
89
  st.success("Account created ✅")
90
  st.rerun()
91
  else:
92
  st.error(r.text)
93
 
 
94
  if st.session_state.token:
95
- me = requests.get(
96
  f"{API_URL}/auth/me",
97
- headers={"Authorization": f"Bearer {st.session_state.token}"}
 
98
  )
99
 
100
- if me.status_code == 200:
101
- st.success(f"Connected as: {me.json()['username']}")
 
 
 
102
 
103
  if st.button("Logout", use_container_width=True):
104
  st.session_state.token = None
105
  st.session_state.messages = []
 
106
  st.rerun()
107
 
 
 
 
 
108
 
109
- # ---------------- MAIN HEADER ----------------
110
  st.title("🧠 LearnLanguage • Streaming Tutor")
111
  st.caption("Streaming replies • Corrections • Exercises • Progress-ready")
112
 
113
-
114
- # ---------------- INPUT ----------------
115
  if not st.session_state.token:
116
  st.warning("Please login first to start chatting.")
117
  st.stop()
118
 
 
 
 
 
 
 
 
119
  colA, colB = st.columns([5, 1])
120
 
121
  with colA:
@@ -127,28 +187,19 @@ with colA:
127
  with colB:
128
  send = st.button("Send 🚀", use_container_width=True)
129
 
130
-
131
- # ---------------- SEND LOGIC ----------------
132
  if send and user_msg.strip():
133
-
134
  ts = datetime.now().strftime("%H:%M")
135
 
136
- st.session_state.messages.append({
137
- "role": "user",
138
- "text": user_msg,
139
- "ts": ts
140
- })
141
 
142
- streamed_text = ""
143
  placeholder = st.empty()
144
 
145
  try:
146
  for kind, data in stream_chat(user_msg):
147
-
148
  if kind == "text":
149
- streamed_text += data
150
- placeholder.markdown(f"**Tutor (streaming…)**\n\n{streamed_text}")
151
-
152
  else:
153
  res = data
154
  st.session_state.last = res
@@ -161,11 +212,12 @@ if send and user_msg.strip():
161
 
162
  st.rerun()
163
 
 
 
164
  except Exception as e:
165
  st.error(f"Streaming error: {e}")
166
 
167
-
168
- # ---------------- CHAT HISTORY ----------------
169
  st.markdown("## 💬 Conversation")
170
 
171
  for m in st.session_state.messages[-30:]:
@@ -173,12 +225,10 @@ for m in st.session_state.messages[-30:]:
173
  st.markdown(f"**{who} • {m['ts']}**")
174
  st.write(m["text"])
175
 
176
-
177
- # ---------------- TUTOR PANEL ----------------
178
  st.markdown("## 🧾 Tutor Panel")
179
 
180
  res = st.session_state.last or {}
181
-
182
  tabs = st.tabs(["Feedback", "Exercises", "Raw JSON"])
183
 
184
  with tabs[0]:
 
1
+ import os
 
2
  import json
 
3
  from datetime import datetime
4
 
5
+ import pandas as pd
6
+ import requests
7
+ import streamlit as st
8
+
9
+ # ================== CONFIG ==================
10
+ API_URL = os.getenv("API_URL", "http://127.0.0.1:8001")
11
 
12
  st.set_page_config(
13
  page_title="LearnLanguage 2026 • Tutor",
 
15
  layout="wide",
16
  )
17
 
18
+ # ================== HELPERS ==================
19
+ def api_alive() -> bool:
20
+ """Check if backend is reachable."""
21
+ try:
22
+ r = requests.get(f"{API_URL}/", timeout=3)
23
+ return r.status_code == 200
24
+ except Exception:
25
+ return False
26
 
 
 
27
 
28
+ def safe_post(url: str, payload: dict, headers: dict | None = None, timeout: int = 30):
29
+ """POST helper with nice error handling."""
30
+ try:
31
+ r = requests.post(url, json=payload, headers=headers, timeout=timeout)
32
+ return r
33
+ except requests.exceptions.RequestException as e:
34
+ st.error(f"Connection error: {e}")
35
+ return None
36
 
37
+
38
+ def safe_get(url: str, headers: dict | None = None, timeout: int = 10):
39
+ """GET helper with nice error handling."""
40
+ try:
41
+ r = requests.get(url, headers=headers, timeout=timeout)
42
+ return r
43
+ except requests.exceptions.RequestException as e:
44
+ st.error(f"Connection error: {e}")
45
+ return None
46
 
47
 
 
48
  def stream_chat(message: str):
49
+ """Call backend /chat endpoint (non-streaming in your backend, returns final JSON)."""
50
+ payload = {"message": message, "mode": st.session_state.mode}
51
+ headers = {"Authorization": f"Bearer {st.session_state.token}"}
52
 
53
+ r = safe_post(f"{API_URL}/chat", payload, headers=headers, timeout=180)
54
+ if r is None:
55
+ raise RuntimeError("Backend not reachable.")
56
+ r.raise_for_status()
57
+ data = r.json()
58
+ yield ("final", data)
59
 
 
 
 
60
 
61
+ # ================== SESSION STATE ==================
62
+ if "messages" not in st.session_state:
63
+ st.session_state.messages = []
 
 
 
64
 
65
+ if "last" not in st.session_state:
66
+ st.session_state.last = None
67
 
68
+ if "mode" not in st.session_state:
69
+ st.session_state.mode = "conversation"
70
 
71
+ if "token" not in st.session_state:
72
+ st.session_state.token = None
73
+
74
+ # ================== SIDEBAR ==================
75
  with st.sidebar:
76
  st.markdown("## 🔐 Account")
77
 
78
+ # Show backend status
79
+ if api_alive():
80
+ st.success("Backend: online ✅")
81
+ else:
82
+ st.error("Backend: offline ❌")
83
+
84
  tabL, tabR = st.tabs(["Login", "Register"])
85
 
86
  with tabL:
 
88
  pwd = st.text_input("Password", type="password", key="login_pwd")
89
 
90
  if st.button("Login", use_container_width=True):
91
+ r = safe_post(
92
  f"{API_URL}/auth/login",
93
+ {"email": email, "password": pwd},
94
+ timeout=20
95
  )
96
 
97
+ if r is None:
98
+ st.stop()
99
+
100
  if r.status_code == 200:
101
+ try:
102
+ st.session_state.token = r.json()["token"]
103
+ except Exception:
104
+ st.error("Login response is not valid JSON.")
105
+ st.stop()
106
+
107
  st.success("Logged in ✅")
108
  st.rerun()
109
  else:
 
115
  pwd2 = st.text_input("Password", type="password", key="reg_pwd")
116
 
117
  if st.button("Create account", use_container_width=True):
118
+ r = safe_post(
119
  f"{API_URL}/auth/register",
120
+ {"email": email2, "username": username2, "password": pwd2},
121
+ timeout=20
122
  )
123
 
124
+ if r is None:
125
+ st.stop()
126
+
127
  if r.status_code == 200:
128
+ try:
129
+ st.session_state.token = r.json()["token"]
130
+ except Exception:
131
+ st.error("Register response is not valid JSON.")
132
+ st.stop()
133
+
134
  st.success("Account created ✅")
135
  st.rerun()
136
  else:
137
  st.error(r.text)
138
 
139
+ # If user logged in: show profile + logout
140
  if st.session_state.token:
141
+ me = safe_get(
142
  f"{API_URL}/auth/me",
143
+ headers={"Authorization": f"Bearer {st.session_state.token}"},
144
+ timeout=10
145
  )
146
 
147
+ if me is not None and me.status_code == 200:
148
+ try:
149
+ st.success(f"Connected as: {me.json().get('username', '—')}")
150
+ except Exception:
151
+ st.info("Connected, but /auth/me returned invalid JSON.")
152
 
153
  if st.button("Logout", use_container_width=True):
154
  st.session_state.token = None
155
  st.session_state.messages = []
156
+ st.session_state.last = None
157
  st.rerun()
158
 
159
+ # Stop early if backend is down
160
+ if not api_alive():
161
+ st.error("API backend not running (FastAPI). Check Space Logs.")
162
+ st.stop()
163
 
164
+ # ================== MAIN ==================
165
  st.title("🧠 LearnLanguage • Streaming Tutor")
166
  st.caption("Streaming replies • Corrections • Exercises • Progress-ready")
167
 
 
 
168
  if not st.session_state.token:
169
  st.warning("Please login first to start chatting.")
170
  st.stop()
171
 
172
+ # Optional: mode selector
173
+ st.session_state.mode = st.selectbox(
174
+ "Mode",
175
+ options=["conversation", "correction", "exercise"],
176
+ index=["conversation", "correction", "exercise"].index(st.session_state.mode)
177
+ )
178
+
179
  colA, colB = st.columns([5, 1])
180
 
181
  with colA:
 
187
  with colB:
188
  send = st.button("Send 🚀", use_container_width=True)
189
 
190
+ # ================== SEND LOGIC ==================
 
191
  if send and user_msg.strip():
 
192
  ts = datetime.now().strftime("%H:%M")
193
 
194
+ st.session_state.messages.append({"role": "user", "text": user_msg, "ts": ts})
 
 
 
 
195
 
 
196
  placeholder = st.empty()
197
 
198
  try:
199
  for kind, data in stream_chat(user_msg):
 
200
  if kind == "text":
201
+ # (your backend currently returns final JSON only)
202
+ placeholder.markdown(f"**Tutor (streaming…)**\n\n{data}")
 
203
  else:
204
  res = data
205
  st.session_state.last = res
 
212
 
213
  st.rerun()
214
 
215
+ except requests.HTTPError as e:
216
+ st.error(f"API returned an error: {e}")
217
  except Exception as e:
218
  st.error(f"Streaming error: {e}")
219
 
220
+ # ================== CHAT HISTORY ==================
 
221
  st.markdown("## 💬 Conversation")
222
 
223
  for m in st.session_state.messages[-30:]:
 
225
  st.markdown(f"**{who} • {m['ts']}**")
226
  st.write(m["text"])
227
 
228
+ # ================== TUTOR PANEL ==================
 
229
  st.markdown("## 🧾 Tutor Panel")
230
 
231
  res = st.session_state.last or {}
 
232
  tabs = st.tabs(["Feedback", "Exercises", "Raw JSON"])
233
 
234
  with tabs[0]: