Hitakshi26 commited on
Commit
e12c660
·
1 Parent(s): 7045d49

Fix HF auth username + activate login + guard notebook selection

Browse files
src/backend/auth.py CHANGED
@@ -1,7 +1,33 @@
 
1
  import gradio as gr
2
 
 
3
  def require_login(request: gr.Request) -> str:
 
 
 
 
 
 
 
 
4
  username = getattr(request, "username", None)
5
- if not username:
6
- raise gr.Error("Please log in using 'Sign in with Hugging Face' to use this app.")
7
- return username
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
  import gradio as gr
3
 
4
+
5
  def require_login(request: gr.Request) -> str:
6
+ """
7
+ Hugging Face Spaces OAuth provides user info via request in some Gradio versions,
8
+ but not always. We use multiple fallbacks:
9
+ 1) request.username (best case)
10
+ 2) HF-proxy headers (x-forwarded-*)
11
+ 3) local/dev fallback
12
+ """
13
+ # 1) Best-case Gradio field
14
  username = getattr(request, "username", None)
15
+ if username:
16
+ return str(username)
17
+
18
+ # 2) Fallback: HF spaces headers (varies by proxy/version)
19
+ headers = getattr(request, "headers", {}) or {}
20
+ for key in [
21
+ "x-forwarded-user",
22
+ "x-hf-user",
23
+ "x-forwarded-preferred-username",
24
+ "x-auth-request-preferred-username",
25
+ ]:
26
+ if key in headers and headers[key]:
27
+ return str(headers[key])
28
+
29
+ # 3) Optional local fallback (so app doesn't hard-crash during dev)
30
+ if os.getenv("HF_SPACE_ID") is None:
31
+ return "localuser"
32
+
33
+ raise gr.Error("Please log in using 'Sign in with Hugging Face' to use this app.")
src/frontend/callbacks.py CHANGED
@@ -1,129 +1,204 @@
 
1
  import time
2
  from datetime import datetime
3
  import gradio as gr
4
 
5
  from src.backend.notebooks import create_notebook, rename_notebook, delete_notebook
6
  from src.storage.index_store import list_notebooks
7
- from src.storage.paths import ensure_tree
8
  from src.storage.chat_store import append_chat, load_chat
9
  from src.storage.artifact_store import list_artifacts as list_artifacts_store, next_artifact_path
10
  from src.backend.ingest import ingest_files as ingest_files_backend, ingest_url as ingest_url_backend
11
  from src.backend.rag import retrieve, rag_answer
12
- from src.backend.artifacts import generate_report, generate_quiz, generate_podcast_transcript, transcript_to_mp3
 
 
 
 
 
 
13
 
14
  def now_iso():
15
  return datetime.utcnow().isoformat() + "Z"
16
 
 
 
 
 
 
 
17
  def chat_pairs(history):
18
  pairs = []
19
  last_user = None
20
  for m in history:
21
  if m.get("role") == "user":
22
- last_user = m.get("content","")
23
  elif m.get("role") == "assistant":
24
- pairs.append((last_user or "", m.get("content","")))
25
  last_user = None
26
  return pairs
27
 
 
28
  def ui_bootstrap(username: str):
29
  nbs = list_notebooks(username)
 
30
  if not nbs:
31
  nb_id = create_notebook(username, "My First Notebook")
32
  nbs = list_notebooks(username)
33
  current = nb_id
34
  else:
35
  current = nbs[0][1]
 
36
  ensure_tree(username, current)
37
  history = load_chat(username, current)
38
- return gr.Dropdown(choices=nbs, value=current), chat_pairs(history), list_artifacts_store(username, current)
 
 
 
39
 
40
  def on_switch_notebook(username: str, notebook_id: str):
 
41
  ensure_tree(username, notebook_id)
42
  history = load_chat(username, notebook_id)
43
  return chat_pairs(history), list_artifacts_store(username, notebook_id)
44
 
 
45
  def on_create_notebook(username: str, name: str):
 
46
  nb_id = create_notebook(username, name)
47
  nbs = list_notebooks(username)
 
48
  return gr.Dropdown(choices=nbs, value=nb_id), [], list_artifacts_store(username, nb_id)
49
 
 
50
  def on_rename_notebook(username: str, notebook_id: str, new_name: str):
 
 
 
 
51
  rename_notebook(username, notebook_id, new_name)
52
  return gr.Dropdown(choices=list_notebooks(username), value=notebook_id)
53
 
 
54
  def on_delete_notebook(username: str, notebook_id: str):
 
55
  delete_notebook(username, notebook_id)
 
56
  return ui_bootstrap(username)
57
 
 
58
  def on_ingest_files(username: str, notebook_id: str, files):
 
59
  if not files:
60
  raise gr.Error("Upload at least one file.")
61
  added = ingest_files_backend(username, notebook_id, files)
62
  return f"Ingested files. Added {added} chunks."
63
 
 
64
  def on_ingest_url(username: str, notebook_id: str, url: str):
 
65
  url = (url or "").strip()
66
  if not url:
67
  raise gr.Error("Enter a URL.")
68
  added = ingest_url_backend(username, notebook_id, url)
69
  return f"Ingested URL. Added {added} chunks."
70
 
 
71
  def on_chat(username: str, notebook_id: str, chatbot, msg: str):
 
 
72
  msg = (msg or "").strip()
73
  if not msg:
74
  return chatbot, ""
 
75
  t0 = time.time()
76
- append_chat(username, notebook_id, {"role":"user","content":msg,"ts":now_iso()})
 
 
77
  hits = retrieve(username, notebook_id, msg, k=6)
78
  ans = rag_answer(msg, hits)
79
- append_chat(username, notebook_id, {"role":"assistant","content":ans,"ts":now_iso(),"latency_ms":int((time.time()-t0)*1000)})
 
 
 
 
 
 
 
 
 
 
 
80
  chatbot = chatbot + [(msg, ans)]
81
  return chatbot, ""
82
 
 
83
  def on_report(username: str, notebook_id: str, topic: str, extra: str):
 
 
84
  topic = (topic or "").strip()
85
  if not topic:
86
  raise gr.Error("Enter a topic.")
 
87
  hits = retrieve(username, notebook_id, topic, k=6)
88
  if not hits:
89
- raise gr.Error("No sources yet. Ingest first.")
 
90
  md = generate_report(topic, hits, extra)
91
  out = next_artifact_path(username, notebook_id, "reports", ".md")
92
- open(out, "w", encoding="utf-8").write(md)
 
 
93
  return "Report generated.", list_artifacts_store(username, notebook_id), out
94
 
 
95
  def on_quiz(username: str, notebook_id: str, topic: str, extra: str):
 
 
96
  topic = (topic or "").strip()
97
  if not topic:
98
  raise gr.Error("Enter a topic.")
 
99
  hits = retrieve(username, notebook_id, topic, k=6)
100
  if not hits:
101
- raise gr.Error("No sources yet. Ingest first.")
 
102
  md = generate_quiz(topic, hits, extra)
103
  out = next_artifact_path(username, notebook_id, "quizzes", ".md")
104
- open(out, "w", encoding="utf-8").write(md)
 
 
105
  return "Quiz generated.", list_artifacts_store(username, notebook_id), out
106
 
 
107
  def on_podcast(username: str, notebook_id: str, topic: str, extra: str):
 
 
108
  topic = (topic or "").strip()
109
  if not topic:
110
  raise gr.Error("Enter a topic.")
 
111
  hits = retrieve(username, notebook_id, topic, k=6)
112
  if not hits:
113
- raise gr.Error("No sources yet. Ingest first.")
 
114
  md = generate_podcast_transcript(topic, hits, extra)
 
115
  md_path = next_artifact_path(username, notebook_id, "podcasts", ".md")
116
- open(md_path, "w", encoding="utf-8").write(md)
 
117
 
118
  mp3_path = next_artifact_path(username, notebook_id, "podcasts", ".mp3")
119
  transcript_to_mp3(md, mp3_path)
120
 
121
  return "Podcast generated.", list_artifacts_store(username, notebook_id), md_path, mp3_path
122
 
 
123
  def on_download(username: str, notebook_id: str, selection: str):
124
- import os
125
- from src.storage.paths import nb_root
126
  if not selection:
127
  return None
 
128
  p = os.path.join(nb_root(username, notebook_id), "artifacts", selection)
129
  return p if os.path.exists(p) else None
 
1
+ import os
2
  import time
3
  from datetime import datetime
4
  import gradio as gr
5
 
6
  from src.backend.notebooks import create_notebook, rename_notebook, delete_notebook
7
  from src.storage.index_store import list_notebooks
8
+ from src.storage.paths import ensure_tree, nb_root
9
  from src.storage.chat_store import append_chat, load_chat
10
  from src.storage.artifact_store import list_artifacts as list_artifacts_store, next_artifact_path
11
  from src.backend.ingest import ingest_files as ingest_files_backend, ingest_url as ingest_url_backend
12
  from src.backend.rag import retrieve, rag_answer
13
+ from src.backend.artifacts import (
14
+ generate_report,
15
+ generate_quiz,
16
+ generate_podcast_transcript,
17
+ transcript_to_mp3,
18
+ )
19
+
20
 
21
  def now_iso():
22
  return datetime.utcnow().isoformat() + "Z"
23
 
24
+
25
+ def _require_notebook(notebook_id: str):
26
+ if not notebook_id:
27
+ raise gr.Error("Please create/select a notebook first.")
28
+
29
+
30
  def chat_pairs(history):
31
  pairs = []
32
  last_user = None
33
  for m in history:
34
  if m.get("role") == "user":
35
+ last_user = m.get("content", "")
36
  elif m.get("role") == "assistant":
37
+ pairs.append((last_user or "", m.get("content", "")))
38
  last_user = None
39
  return pairs
40
 
41
+
42
  def ui_bootstrap(username: str):
43
  nbs = list_notebooks(username)
44
+
45
  if not nbs:
46
  nb_id = create_notebook(username, "My First Notebook")
47
  nbs = list_notebooks(username)
48
  current = nb_id
49
  else:
50
  current = nbs[0][1]
51
+
52
  ensure_tree(username, current)
53
  history = load_chat(username, current)
54
+ artifacts = list_artifacts_store(username, current)
55
+
56
+ return gr.Dropdown(choices=nbs, value=current), chat_pairs(history), artifacts
57
+
58
 
59
  def on_switch_notebook(username: str, notebook_id: str):
60
+ _require_notebook(notebook_id)
61
  ensure_tree(username, notebook_id)
62
  history = load_chat(username, notebook_id)
63
  return chat_pairs(history), list_artifacts_store(username, notebook_id)
64
 
65
+
66
  def on_create_notebook(username: str, name: str):
67
+ name = (name or "").strip() or "Untitled Notebook"
68
  nb_id = create_notebook(username, name)
69
  nbs = list_notebooks(username)
70
+ ensure_tree(username, nb_id)
71
  return gr.Dropdown(choices=nbs, value=nb_id), [], list_artifacts_store(username, nb_id)
72
 
73
+
74
  def on_rename_notebook(username: str, notebook_id: str, new_name: str):
75
+ _require_notebook(notebook_id)
76
+ new_name = (new_name or "").strip()
77
+ if not new_name:
78
+ raise gr.Error("Enter a new notebook name.")
79
  rename_notebook(username, notebook_id, new_name)
80
  return gr.Dropdown(choices=list_notebooks(username), value=notebook_id)
81
 
82
+
83
  def on_delete_notebook(username: str, notebook_id: str):
84
+ _require_notebook(notebook_id)
85
  delete_notebook(username, notebook_id)
86
+ # Return the bootstrap tuple (dropdown, chat, artifacts)
87
  return ui_bootstrap(username)
88
 
89
+
90
  def on_ingest_files(username: str, notebook_id: str, files):
91
+ _require_notebook(notebook_id)
92
  if not files:
93
  raise gr.Error("Upload at least one file.")
94
  added = ingest_files_backend(username, notebook_id, files)
95
  return f"Ingested files. Added {added} chunks."
96
 
97
+
98
  def on_ingest_url(username: str, notebook_id: str, url: str):
99
+ _require_notebook(notebook_id)
100
  url = (url or "").strip()
101
  if not url:
102
  raise gr.Error("Enter a URL.")
103
  added = ingest_url_backend(username, notebook_id, url)
104
  return f"Ingested URL. Added {added} chunks."
105
 
106
+
107
  def on_chat(username: str, notebook_id: str, chatbot, msg: str):
108
+ _require_notebook(notebook_id)
109
+
110
  msg = (msg or "").strip()
111
  if not msg:
112
  return chatbot, ""
113
+
114
  t0 = time.time()
115
+
116
+ append_chat(username, notebook_id, {"role": "user", "content": msg, "ts": now_iso()})
117
+
118
  hits = retrieve(username, notebook_id, msg, k=6)
119
  ans = rag_answer(msg, hits)
120
+
121
+ append_chat(
122
+ username,
123
+ notebook_id,
124
+ {
125
+ "role": "assistant",
126
+ "content": ans,
127
+ "ts": now_iso(),
128
+ "latency_ms": int((time.time() - t0) * 1000),
129
+ },
130
+ )
131
+
132
  chatbot = chatbot + [(msg, ans)]
133
  return chatbot, ""
134
 
135
+
136
  def on_report(username: str, notebook_id: str, topic: str, extra: str):
137
+ _require_notebook(notebook_id)
138
+
139
  topic = (topic or "").strip()
140
  if not topic:
141
  raise gr.Error("Enter a topic.")
142
+
143
  hits = retrieve(username, notebook_id, topic, k=6)
144
  if not hits:
145
+ raise gr.Error("No sources yet. Ingest files/URL first.")
146
+
147
  md = generate_report(topic, hits, extra)
148
  out = next_artifact_path(username, notebook_id, "reports", ".md")
149
+ with open(out, "w", encoding="utf-8") as f:
150
+ f.write(md)
151
+
152
  return "Report generated.", list_artifacts_store(username, notebook_id), out
153
 
154
+
155
  def on_quiz(username: str, notebook_id: str, topic: str, extra: str):
156
+ _require_notebook(notebook_id)
157
+
158
  topic = (topic or "").strip()
159
  if not topic:
160
  raise gr.Error("Enter a topic.")
161
+
162
  hits = retrieve(username, notebook_id, topic, k=6)
163
  if not hits:
164
+ raise gr.Error("No sources yet. Ingest files/URL first.")
165
+
166
  md = generate_quiz(topic, hits, extra)
167
  out = next_artifact_path(username, notebook_id, "quizzes", ".md")
168
+ with open(out, "w", encoding="utf-8") as f:
169
+ f.write(md)
170
+
171
  return "Quiz generated.", list_artifacts_store(username, notebook_id), out
172
 
173
+
174
  def on_podcast(username: str, notebook_id: str, topic: str, extra: str):
175
+ _require_notebook(notebook_id)
176
+
177
  topic = (topic or "").strip()
178
  if not topic:
179
  raise gr.Error("Enter a topic.")
180
+
181
  hits = retrieve(username, notebook_id, topic, k=6)
182
  if not hits:
183
+ raise gr.Error("No sources yet. Ingest files/URL first.")
184
+
185
  md = generate_podcast_transcript(topic, hits, extra)
186
+
187
  md_path = next_artifact_path(username, notebook_id, "podcasts", ".md")
188
+ with open(md_path, "w", encoding="utf-8") as f:
189
+ f.write(md)
190
 
191
  mp3_path = next_artifact_path(username, notebook_id, "podcasts", ".mp3")
192
  transcript_to_mp3(md, mp3_path)
193
 
194
  return "Podcast generated.", list_artifacts_store(username, notebook_id), md_path, mp3_path
195
 
196
+
197
  def on_download(username: str, notebook_id: str, selection: str):
198
+ _require_notebook(notebook_id)
199
+
200
  if not selection:
201
  return None
202
+
203
  p = os.path.join(nb_root(username, notebook_id), "artifacts", selection)
204
  return p if os.path.exists(p) else None
src/frontend/ui.py CHANGED
@@ -23,6 +23,7 @@ def build_app():
23
  gr.Markdown("# 📓 NotebookLM Clone (HF Auth + Chroma + RAG)")
24
 
25
  login = gr.LoginButton()
 
26
 
27
  username_state = gr.State("")
28
 
 
23
  gr.Markdown("# 📓 NotebookLM Clone (HF Auth + Chroma + RAG)")
24
 
25
  login = gr.LoginButton()
26
+ login.activate()
27
 
28
  username_state = gr.State("")
29