LucasUn commited on
Commit
cdfb14d
·
verified ·
1 Parent(s): 7e45a1e

Remove fixed researcher access code

Browse files
Files changed (1) hide show
  1. app.py +11 -18
app.py CHANGED
@@ -19,7 +19,6 @@ from gradio_client import Client
19
  from pydantic import BaseModel, Field
20
 
21
 
22
- ACCESS_CODE = os.getenv("ACCESS_CODE", "")
23
  SESSION_SECRET = os.getenv("SESSION_SECRET", "")
24
  HF_TOKEN = os.getenv("HF_TOKEN", "")
25
  TARGET_SPACE_ID = os.getenv("TARGET_SPACE_ID", "ASQ2/ASQ")
@@ -34,10 +33,6 @@ PAIRING_TTL_SECONDS = int(os.getenv("PAIRING_TTL_SECONDS", "600"))
34
  logger = logging.getLogger("asq_gateway")
35
 
36
 
37
- class AuthRequest(BaseModel):
38
- code: str = Field(min_length=6, max_length=6, pattern=r"^\d{6}$")
39
-
40
-
41
  class PrepareRequest(BaseModel):
42
  background: str = Field(min_length=20, max_length=MAX_BACKGROUND_CHARS)
43
 
@@ -91,7 +86,7 @@ def _new_interview_code() -> str:
91
  _cleanup_expired()
92
  for _ in range(100):
93
  code = f"{secrets.randbelow(1_000_000):06d}"
94
- if code != ACCESS_CODE and code not in interview_codes:
95
  return code
96
  raise RuntimeError("Could not allocate an interview code")
97
 
@@ -100,7 +95,7 @@ def _new_pairing_code() -> str:
100
  _cleanup_expired()
101
  for _ in range(100):
102
  code = f"{secrets.randbelow(1_000_000):06d}"
103
- if code != ACCESS_CODE and code not in pairing_codes and code not in interview_codes:
104
  return code
105
  raise RuntimeError("Could not allocate a pairing code")
106
 
@@ -262,7 +257,7 @@ def session_view(session: dict[str, Any], role: str) -> dict[str, Any]:
262
  return response
263
 
264
 
265
- api = FastAPI(title="ASQ Rokid Gateway", version="0.3.0")
266
  api.add_middleware(
267
  CORSMiddleware,
268
  allow_origins=["*"],
@@ -280,18 +275,16 @@ def mobile_app() -> str:
280
  def health() -> dict[str, Any]:
281
  return {
282
  "ok": True,
283
- "configured": bool(ACCESS_CODE and SESSION_SECRET and HF_TOKEN),
284
  "target": TARGET_SPACE_ID,
285
- "version": "0.3.0",
286
  }
287
 
288
 
289
  @api.post("/auth")
290
- def authenticate(request: AuthRequest) -> dict[str, Any]:
291
- if not ACCESS_CODE or not SESSION_SECRET:
292
  raise HTTPException(status_code=503, detail="Gateway is not configured")
293
- if not hmac.compare_digest(request.code, ACCESS_CODE):
294
- raise HTTPException(status_code=401, detail="Incorrect code")
295
  _cleanup_expired()
296
  session_id = secrets.token_urlsafe(18)
297
  expires_at = int(time.time()) + SESSION_TTL_SECONDS
@@ -454,24 +447,24 @@ button{border:0;background:#087a54;color:#fff;padding:14px;font-weight:700;margi
454
  .hypo{white-space:pre-wrap;line-height:1.5;background:#f7faf8;padding:14px;border-radius:12px;max-height:320px;overflow:auto}
455
  </style></head><body><main class="card">
456
  <h1>ASQ Interview Setup</h1><p class="muted">Open ASQ on the Rokid glasses first. The glasses will display a six-digit pairing code; enter that code on this phone.</p>
457
- <section id="login"><label for="access">Researcher access code</label><input id="access" inputmode="numeric" maxlength="6" placeholder="Six digits"><button id="loginBtn">Continue</button></section>
458
- <section id="prepare" class="step hidden"><label for="pairingCode">Code shown on the glasses</label><input id="pairingCode" inputmode="numeric" maxlength="6" placeholder="Six digits"><label for="background" style="margin-top:16px">Case and interview background</label><textarea id="background" placeholder="Enter the case facts, context, existing evidence, interview goals, and any constraints..."></textarea><button id="prepareBtn">Connect glasses and prepare interview</button></section>
459
  <section id="progress" class="step hidden"><div id="status" class="status">Connecting the glasses…</div><div id="hypoWrap" class="hidden"><h2>Initial hypotheses</h2><div id="hypotheses" class="hypo"></div></div></section>
460
  </main><script>
461
  let token="", timer=null; const $=id=>document.getElementById(id);
462
  async function api(path, options={}){const headers={"Content-Type":"application/json",...(options.headers||{})};if(token)headers.Authorization=`Bearer ${token}`;const r=await fetch(path,{...options,headers});let data={};try{data=await r.json()}catch{}if(!r.ok)throw new Error(data.detail||`Request failed (${r.status})`);return data}
463
  function errorMessage(e){$("status").textContent=e.message;$("progress").classList.remove("hidden")}
464
- $("loginBtn").onclick=async()=>{try{const data=await api("/auth",{method:"POST",body:JSON.stringify({code:$("access").value.trim()})});token=data.token;$("login").classList.add("hidden");$("prepare").classList.remove("hidden")}catch(e){errorMessage(e)}};
465
  $("prepareBtn").onclick=async()=>{const pairingCode=$("pairingCode").value.trim();const background=$("background").value.trim();if(!/^\d{6}$/.test(pairingCode)){errorMessage("Enter the six-digit code shown on the glasses.");return}if(background.length<20){errorMessage("Please provide at least 20 characters of background.");return}$("prepareBtn").disabled=true;$("progress").classList.remove("hidden");$("status").textContent="Connecting the glasses and generating initial hypotheses…";try{await api("/pair/claim",{method:"POST",body:JSON.stringify({code:pairingCode})});const data=await api("/prepare",{method:"POST",body:JSON.stringify({background})});show(data);timer=setInterval(poll,3000)}catch(e){errorMessage(e);$("prepareBtn").disabled=false}};
466
  async function poll(){try{show(await api("/session"))}catch(e){clearInterval(timer);errorMessage(e)}}
467
  function show(data){if(data.status==="ready"){clearInterval(timer);$("status").textContent="Ready. The glasses are connected and can begin the interview.";$("hypotheses").textContent=data.hypotheses||"No hypotheses returned.";$("hypoWrap").classList.remove("hidden")}else if(data.status==="error"){clearInterval(timer);$("status").textContent=data.error||"Model startup failed.";$("prepareBtn").disabled=false}else{$("status").textContent="Glasses connected. Starting the model and generating initial hypotheses…"}}
 
468
  </script></body></html>"""
469
 
470
 
471
  with gr.Blocks(title="ASQ Rokid Gateway") as status_page:
472
  gr.Markdown(
473
  "# ASQ Rokid Gateway\n"
474
- "This service provides the authenticated phone preparation and Rokid interview APIs.\n\n"
475
  "Open the site root on a phone to prepare an interview. Use `GET /health` for service status."
476
  )
477
 
 
19
  from pydantic import BaseModel, Field
20
 
21
 
 
22
  SESSION_SECRET = os.getenv("SESSION_SECRET", "")
23
  HF_TOKEN = os.getenv("HF_TOKEN", "")
24
  TARGET_SPACE_ID = os.getenv("TARGET_SPACE_ID", "ASQ2/ASQ")
 
33
  logger = logging.getLogger("asq_gateway")
34
 
35
 
 
 
 
 
36
  class PrepareRequest(BaseModel):
37
  background: str = Field(min_length=20, max_length=MAX_BACKGROUND_CHARS)
38
 
 
86
  _cleanup_expired()
87
  for _ in range(100):
88
  code = f"{secrets.randbelow(1_000_000):06d}"
89
+ if code not in interview_codes:
90
  return code
91
  raise RuntimeError("Could not allocate an interview code")
92
 
 
95
  _cleanup_expired()
96
  for _ in range(100):
97
  code = f"{secrets.randbelow(1_000_000):06d}"
98
+ if code not in pairing_codes and code not in interview_codes:
99
  return code
100
  raise RuntimeError("Could not allocate a pairing code")
101
 
 
257
  return response
258
 
259
 
260
+ api = FastAPI(title="ASQ Rokid Gateway", version="0.4.0")
261
  api.add_middleware(
262
  CORSMiddleware,
263
  allow_origins=["*"],
 
275
  def health() -> dict[str, Any]:
276
  return {
277
  "ok": True,
278
+ "configured": bool(SESSION_SECRET and HF_TOKEN),
279
  "target": TARGET_SPACE_ID,
280
+ "version": "0.4.0",
281
  }
282
 
283
 
284
  @api.post("/auth")
285
+ def create_owner_session() -> dict[str, Any]:
286
+ if not SESSION_SECRET:
287
  raise HTTPException(status_code=503, detail="Gateway is not configured")
 
 
288
  _cleanup_expired()
289
  session_id = secrets.token_urlsafe(18)
290
  expires_at = int(time.time()) + SESSION_TTL_SECONDS
 
447
  .hypo{white-space:pre-wrap;line-height:1.5;background:#f7faf8;padding:14px;border-radius:12px;max-height:320px;overflow:auto}
448
  </style></head><body><main class="card">
449
  <h1>ASQ Interview Setup</h1><p class="muted">Open ASQ on the Rokid glasses first. The glasses will display a six-digit pairing code; enter that code on this phone.</p>
450
+ <section id="prepare"><label for="pairingCode">Code shown on the glasses</label><input id="pairingCode" inputmode="numeric" maxlength="6" placeholder="Six digits"><label for="background" style="margin-top:16px">Case and interview background</label><textarea id="background" placeholder="Enter the case facts, context, existing evidence, interview goals, and any constraints..."></textarea><button id="prepareBtn" disabled>Connect glasses and prepare interview</button></section>
 
451
  <section id="progress" class="step hidden"><div id="status" class="status">Connecting the glasses…</div><div id="hypoWrap" class="hidden"><h2>Initial hypotheses</h2><div id="hypotheses" class="hypo"></div></div></section>
452
  </main><script>
453
  let token="", timer=null; const $=id=>document.getElementById(id);
454
  async function api(path, options={}){const headers={"Content-Type":"application/json",...(options.headers||{})};if(token)headers.Authorization=`Bearer ${token}`;const r=await fetch(path,{...options,headers});let data={};try{data=await r.json()}catch{}if(!r.ok)throw new Error(data.detail||`Request failed (${r.status})`);return data}
455
  function errorMessage(e){$("status").textContent=e.message;$("progress").classList.remove("hidden")}
456
+ async function begin(){try{const data=await api("/auth",{method:"POST"});token=data.token;$("prepareBtn").disabled=false}catch(e){errorMessage(e)}}
457
  $("prepareBtn").onclick=async()=>{const pairingCode=$("pairingCode").value.trim();const background=$("background").value.trim();if(!/^\d{6}$/.test(pairingCode)){errorMessage("Enter the six-digit code shown on the glasses.");return}if(background.length<20){errorMessage("Please provide at least 20 characters of background.");return}$("prepareBtn").disabled=true;$("progress").classList.remove("hidden");$("status").textContent="Connecting the glasses and generating initial hypotheses…";try{await api("/pair/claim",{method:"POST",body:JSON.stringify({code:pairingCode})});const data=await api("/prepare",{method:"POST",body:JSON.stringify({background})});show(data);timer=setInterval(poll,3000)}catch(e){errorMessage(e);$("prepareBtn").disabled=false}};
458
  async function poll(){try{show(await api("/session"))}catch(e){clearInterval(timer);errorMessage(e)}}
459
  function show(data){if(data.status==="ready"){clearInterval(timer);$("status").textContent="Ready. The glasses are connected and can begin the interview.";$("hypotheses").textContent=data.hypotheses||"No hypotheses returned.";$("hypoWrap").classList.remove("hidden")}else if(data.status==="error"){clearInterval(timer);$("status").textContent=data.error||"Model startup failed.";$("prepareBtn").disabled=false}else{$("status").textContent="Glasses connected. Starting the model and generating initial hypotheses…"}}
460
+ begin();
461
  </script></body></html>"""
462
 
463
 
464
  with gr.Blocks(title="ASQ Rokid Gateway") as status_page:
465
  gr.Markdown(
466
  "# ASQ Rokid Gateway\n"
467
+ "This service provides phone preparation and Rokid interview pairing APIs.\n\n"
468
  "Open the site root on a phone to prepare an interview. Use `GET /health` for service status."
469
  )
470