lanna_lalala;- commited on
Commit
7c45ed6
·
1 Parent(s): 02b47c0

try a thing

Browse files
Files changed (2) hide show
  1. app.py +2 -1
  2. utils/api.py +77 -33
app.py CHANGED
@@ -12,11 +12,12 @@ from phase.Student_view import chatbot, lesson, quiz, game, teacherlink
12
  from phase.Teacher_view import classmanage,studentlist,contentmanage
13
  from phase.Student_view.games import profitpuzzle
14
  from utils import db,api
15
- import os
16
 
17
  from utils.api import BACKEND
18
  st.sidebar.caption(f"Backend URL: {BACKEND}")
19
 
 
20
  DISABLE_DB = os.getenv("DISABLE_DB", "1") == "1"
21
 
22
  try:
 
12
  from phase.Teacher_view import classmanage,studentlist,contentmanage
13
  from phase.Student_view.games import profitpuzzle
14
  from utils import db,api
15
+ import os, requests
16
 
17
  from utils.api import BACKEND
18
  st.sidebar.caption(f"Backend URL: {BACKEND}")
19
 
20
+
21
  DISABLE_DB = os.getenv("DISABLE_DB", "1") == "1"
22
 
23
  try:
utils/api.py CHANGED
@@ -1,21 +1,53 @@
1
  # utils/api.py
2
- import os, requests
 
 
3
 
4
  # ---- Setup ----
5
  BACKEND = (os.getenv("BACKEND_URL") or "").strip().rstrip("/")
6
  if not BACKEND:
 
7
  raise RuntimeError("BACKEND_URL is not set in Space secrets.")
8
 
9
- TOKEN = (os.getenv("BACKEND_TOKEN") or "").strip()
 
 
10
  DEFAULT_TIMEOUT = int(os.getenv("BACKEND_TIMEOUT", "30"))
11
 
12
  _session = requests.Session()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  if TOKEN:
14
  _session.headers["Authorization"] = f"Bearer {TOKEN}"
15
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  def _req(method: str, path: str, **kw):
18
- # make sure path starts with '/'
19
  if not path.startswith("/"):
20
  path = "/" + path
21
  url = f"{BACKEND}{path}"
@@ -29,68 +61,80 @@ def _req(method: str, path: str, **kw):
29
  body = r.text[:500]
30
  except Exception:
31
  pass
32
- raise RuntimeError(f"{method} {path} failed [{r.status_code}]: {body}") from e
 
 
 
 
 
 
 
33
  except requests.RequestException as e:
34
  raise RuntimeError(f"{method} {path} failed: {e.__class__.__name__}: {e}") from e
35
  return r
36
 
37
-
38
  # ---- Health ----
39
  def health():
40
- return _req("GET", "/health").json()
41
-
 
 
 
 
 
 
 
 
42
 
43
  # ---- Legacy agent endpoints (keep) ----
44
  def start_agent(student_id: int, lesson_id: int, level_slug: str):
45
- return _req("POST", "/agent/start",
46
- json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug}).json()
47
 
48
  def get_quiz(student_id: int, lesson_id: int, level_slug: str):
49
- return _req("POST", "/agent/quiz",
50
- json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug}).json()["items"]
 
51
 
52
  def grade_quiz(student_id: int, lesson_id: int, level_slug: str,
53
  answers: list[str], assignment_id: int | None = None):
54
- d = _req("POST", "/agent/grade",
55
- json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug,
56
- "answers": answers, "assignment_id": assignment_id}).json()
57
  return d["score"], d["total"]
58
 
59
  def next_step(student_id: int, lesson_id: int, level_slug: str,
60
  answers: list[str], assignment_id: int | None = None):
61
- return _req("POST", "/agent/coach_or_celebrate",
62
- json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug,
63
- "answers": answers, "assignment_id": assignment_id}).json()
64
-
65
 
66
  # ---- Auth ----
67
  def login(email: str, password: str):
68
- return _req("POST", "/auth/login", json={"email": email, "password": password}).json()
69
 
70
  def signup_student(name: str, email: str, password: str, level_label: str, country_label: str):
71
- return _req("POST", "/auth/signup/student",
72
- json={"name": name, "email": email, "password": password,
73
- "level_label": level_label, "country_label": country_label}).json()
74
 
75
  def signup_teacher(title: str, name: str, email: str, password: str):
76
- return _req("POST", "/auth/signup/teacher",
77
- json={"title": title, "name": name, "email": email, "password": password}).json()
78
-
79
 
80
  # ---- New LangGraph-backed endpoints ----
81
  def fetch_lesson_content(lesson: str, module: str, topic: str):
82
- payload = {"lesson": lesson, "module": module, "topic": topic}
83
- r = _req("POST", "/lesson", json=payload).json()
84
  return r["lesson_content"]
85
 
86
  def submit_lesson_quiz(lesson: str, module: str, topic: str, responses: dict):
87
- payload = {"lesson": lesson, "module": module, "topic": topic, "responses": responses}
88
- return _req("POST", "/lesson-quiz", json=payload).json()
89
 
90
  def submit_practice_quiz(lesson: str, responses: dict):
91
- payload = {"lesson": lesson, "responses": responses}
92
- return _req("POST", "/practice-quiz", json=payload).json()
93
 
94
  def send_to_chatbot(messages: list[dict]):
95
- payload = {"messages": messages}
96
- return _req("POST", "/chatbot", json=payload).json()
 
1
  # utils/api.py
2
+ import os, json, requests
3
+ from urllib3.util.retry import Retry
4
+ from requests.adapters import HTTPAdapter
5
 
6
  # ---- Setup ----
7
  BACKEND = (os.getenv("BACKEND_URL") or "").strip().rstrip("/")
8
  if not BACKEND:
9
+ # Fail fast at import; Streamlit will surface this in the sidebar on first run
10
  raise RuntimeError("BACKEND_URL is not set in Space secrets.")
11
 
12
+ # Accept either BACKEND_TOKEN or HF_TOKEN
13
+ TOKEN = (os.getenv("BACKEND_TOKEN") or os.getenv("HF_TOKEN") or "").strip()
14
+
15
  DEFAULT_TIMEOUT = int(os.getenv("BACKEND_TIMEOUT", "30"))
16
 
17
  _session = requests.Session()
18
+
19
+ # Light retry for transient network/server blips
20
+ retry = Retry(
21
+ total=3,
22
+ connect=3,
23
+ read=3,
24
+ backoff_factor=0.5,
25
+ status_forcelist=(429, 500, 502, 503, 504),
26
+ allowed_methods=frozenset(["GET", "POST", "PUT", "PATCH", "DELETE"]),
27
+ )
28
+ _session.mount("https://", HTTPAdapter(max_retries=retry))
29
+ _session.mount("http://", HTTPAdapter(max_retries=retry))
30
+
31
+ # Default headers
32
+ _session.headers.update({
33
+ "Accept": "application/json, */*;q=0.1",
34
+ "User-Agent": "FinEdu-Frontend/1.0 (+spaces)",
35
+ })
36
  if TOKEN:
37
  _session.headers["Authorization"] = f"Bearer {TOKEN}"
38
 
39
+ def _json_or_raise(resp: requests.Response):
40
+ ctype = resp.headers.get("content-type", "")
41
+ if "application/json" in ctype:
42
+ return resp.json()
43
+ # Try to parse anyway; show a helpful error if not JSON
44
+ try:
45
+ return resp.json()
46
+ except Exception:
47
+ snippet = (resp.text or "")[:300]
48
+ raise RuntimeError(f"Expected JSON but got {ctype or 'unknown'}:\n{snippet}")
49
 
50
  def _req(method: str, path: str, **kw):
 
51
  if not path.startswith("/"):
52
  path = "/" + path
53
  url = f"{BACKEND}{path}"
 
61
  body = r.text[:500]
62
  except Exception:
63
  pass
64
+ status = getattr(r, "status_code", "?")
65
+ # Give nicer hints for common auth misconfigs
66
+ if status in (401, 403):
67
+ raise RuntimeError(
68
+ f"{method} {path} failed [{status}] – auth rejected. "
69
+ f"Check BACKEND_TOKEN/HF_TOKEN permissions and that the backend Space is private/readable."
70
+ ) from e
71
+ raise RuntimeError(f"{method} {path} failed [{status}]: {body}") from e
72
  except requests.RequestException as e:
73
  raise RuntimeError(f"{method} {path} failed: {e.__class__.__name__}: {e}") from e
74
  return r
75
 
 
76
  # ---- Health ----
77
  def health():
78
+ # Prefer /health but allow root fallback if you change the backend later
79
+ try:
80
+ return _json_or_raise(_req("GET", "/health"))
81
+ except Exception:
82
+ # best-effort fallback
83
+ try:
84
+ _req("GET", "/")
85
+ return {"ok": True}
86
+ except Exception:
87
+ return {"ok": False}
88
 
89
  # ---- Legacy agent endpoints (keep) ----
90
  def start_agent(student_id: int, lesson_id: int, level_slug: str):
91
+ return _json_or_raise(_req("POST", "/agent/start",
92
+ json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug}))
93
 
94
  def get_quiz(student_id: int, lesson_id: int, level_slug: str):
95
+ d = _json_or_raise(_req("POST", "/agent/quiz",
96
+ json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug}))
97
+ return d["items"]
98
 
99
  def grade_quiz(student_id: int, lesson_id: int, level_slug: str,
100
  answers: list[str], assignment_id: int | None = None):
101
+ d = _json_or_raise(_req("POST", "/agent/grade",
102
+ json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug,
103
+ "answers": answers, "assignment_id": assignment_id}))
104
  return d["score"], d["total"]
105
 
106
  def next_step(student_id: int, lesson_id: int, level_slug: str,
107
  answers: list[str], assignment_id: int | None = None):
108
+ return _json_or_raise(_req("POST", "/agent/coach_or_celebrate",
109
+ json={"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug,
110
+ "answers": answers, "assignment_id": assignment_id}))
 
111
 
112
  # ---- Auth ----
113
  def login(email: str, password: str):
114
+ return _json_or_raise(_req("POST", "/auth/login", json={"email": email, "password": password}))
115
 
116
  def signup_student(name: str, email: str, password: str, level_label: str, country_label: str):
117
+ return _json_or_raise(_req("POST", "/auth/signup/student",
118
+ json={"name": name, "email": email, "password": password,
119
+ "level_label": level_label, "country_label": country_label}))
120
 
121
  def signup_teacher(title: str, name: str, email: str, password: str):
122
+ return _json_or_raise(_req("POST", "/auth/signup/teacher",
123
+ json={"title": title, "name": name, "email": email, "password": password}))
 
124
 
125
  # ---- New LangGraph-backed endpoints ----
126
  def fetch_lesson_content(lesson: str, module: str, topic: str):
127
+ r = _json_or_raise(_req("POST", "/lesson",
128
+ json={"lesson": lesson, "module": module, "topic": topic}))
129
  return r["lesson_content"]
130
 
131
  def submit_lesson_quiz(lesson: str, module: str, topic: str, responses: dict):
132
+ return _json_or_raise(_req("POST", "/lesson-quiz",
133
+ json={"lesson": lesson, "module": module, "topic": topic, "responses": responses}))
134
 
135
  def submit_practice_quiz(lesson: str, responses: dict):
136
+ return _json_or_raise(_req("POST", "/practice-quiz",
137
+ json={"lesson": lesson, "responses": responses}))
138
 
139
  def send_to_chatbot(messages: list[dict]):
140
+ return _json_or_raise(_req("POST", "/chatbot", json={"messages": messages}))