R-Kentaren commited on
Commit
c4c43f1
Β·
verified Β·
1 Parent(s): ccb935d

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +5 -0
  2. code/server/routes.py +79 -0
  3. index.html +200 -9
README.md CHANGED
@@ -8,6 +8,11 @@ sdk_version: 6.14.0
8
  python_version: '3.11'
9
  app_file: app.py
10
  pinned: false
 
 
 
 
 
11
  ---
12
 
13
  ## SoniCoder
 
8
  python_version: '3.11'
9
  app_file: app.py
10
  pinned: false
11
+ hf_oauth: true
12
+ hf_oauth_scopes:
13
+ - read-repos
14
+ - write-repos
15
+ - manage-repos
16
  ---
17
 
18
  ## SoniCoder
code/server/routes.py CHANGED
@@ -10,6 +10,7 @@ Defines all HTTP and API endpoints:
10
  - API push_hf β†’ push to HuggingFace Hub
11
  - API switch_model β†’ switch between loaded models
12
  - API upload_image β†’ upload image for VLM inference
 
13
  """
14
 
15
  from __future__ import annotations
@@ -470,6 +471,84 @@ def handle_chat(
470
  })
471
 
472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  @app.api(name="push_hf", concurrency_limit=1)
474
  def handle_push_hf(
475
  exec_context_json: str,
 
10
  - API push_hf β†’ push to HuggingFace Hub
11
  - API switch_model β†’ switch between loaded models
12
  - API upload_image β†’ upload image for VLM inference
13
+ - API hf_auth β†’ get HF OAuth profile & organizations
14
  """
15
 
16
  from __future__ import annotations
 
471
  })
472
 
473
 
474
+ @app.api(name="hf_auth", concurrency_limit=4)
475
+ def handle_hf_auth(
476
+ oauth_token: str = "",
477
+ ) -> str:
478
+ """Get HuggingFace OAuth profile and list of organizations.
479
+
480
+ If oauth_token is provided (from Gradio OAuth), uses it to fetch user info.
481
+ Otherwise, returns empty auth info.
482
+ """
483
+ try:
484
+ import gradio as gr
485
+ from huggingface_hub import whoami
486
+
487
+ token = oauth_token.strip() if oauth_token else ""
488
+
489
+ if not token:
490
+ yield json.dumps({
491
+ "authenticated": False,
492
+ "username": "",
493
+ "name": "",
494
+ "picture": "",
495
+ "organizations": [],
496
+ "message": "Not signed in. Click Sign In to authenticate with HuggingFace.",
497
+ })
498
+ return
499
+
500
+ # Get user info using the OAuth token
501
+ user_info = whoami(token=token)
502
+ username = user_info.get("name", "")
503
+ fullname = user_info.get("fullname", username)
504
+
505
+ # Get avatar
506
+ avatar_url = ""
507
+ avatar_info = user_info.get("avatarUrl", "")
508
+ if avatar_info:
509
+ avatar_url = avatar_info
510
+
511
+ # Get organizations
512
+ orgs = []
513
+ for org in user_info.get("orgs", []):
514
+ orgs.append({
515
+ "name": org.get("name", ""),
516
+ "avatar": org.get("avatarUrl", ""),
517
+ })
518
+
519
+ # Also check orgRoles for role info
520
+ org_roles = user_info.get("orgRoles", [])
521
+ for role_info in org_roles:
522
+ org_name = role_info.get("org", "")
523
+ role = role_info.get("role", "member")
524
+ # Add role info to existing org if found
525
+ for org in orgs:
526
+ if org["name"] == org_name:
527
+ org["role"] = role
528
+ break
529
+
530
+ yield json.dumps({
531
+ "authenticated": True,
532
+ "username": username,
533
+ "name": fullname,
534
+ "picture": avatar_url,
535
+ "organizations": orgs,
536
+ "token": token,
537
+ "message": f"Signed in as {username}",
538
+ })
539
+
540
+ except Exception as exc:
541
+ logger.exception("HF auth check failed")
542
+ yield json.dumps({
543
+ "authenticated": False,
544
+ "username": "",
545
+ "name": "",
546
+ "picture": "",
547
+ "organizations": [],
548
+ "message": f"Auth check failed: {str(exc)}",
549
+ })
550
+
551
+
552
  @app.api(name="push_hf", concurrency_limit=1)
553
  def handle_push_hf(
554
  exec_context_json: str,
index.html CHANGED
@@ -943,6 +943,36 @@ body.hide-thinking .think-block { display: none; }
943
  cursor: not-allowed;
944
  }
945
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
946
  .deploy-status {
947
  margin-top: 10px;
948
  padding: 8px 12px;
@@ -1189,16 +1219,42 @@ body.hide-thinking .think-block { display: none; }
1189
  <div class="tab-pane" id="pane-deploy">
1190
  <div class="deploy-section">
1191
  <div class="deploy-title">&#128640; Deploy to HuggingFace</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1192
  <div class="deploy-field">
1193
- <label for="hf-repo-name">Repository Name</label>
1194
- <input type="text" id="hf-repo-name" placeholder="username/my-app" autocomplete="off">
1195
- <div class="deploy-hint">Format: username/repo-name or just repo-name</div>
 
 
1196
  </div>
 
1197
  <div class="deploy-field">
1198
- <label for="hf-token">HuggingFace Token</label>
 
 
 
 
 
 
1199
  <input type="password" id="hf-token" placeholder="hf_xxxxxxxxxxxxxxxxxxxxx" autocomplete="off">
1200
- <div class="deploy-hint">Get your token at <a href="https://huggingface.co/settings/tokens" target="_blank">huggingface.co/settings/tokens</a></div>
1201
  </div>
 
1202
  <div class="deploy-field">
1203
  <label for="hf-space-sdk">Space SDK</label>
1204
  <select id="hf-space-sdk">
@@ -1321,6 +1377,9 @@ document.addEventListener('DOMContentLoaded', () => {
1321
 
1322
  // Poll model status
1323
  pollModelStatus();
 
 
 
1324
  });
1325
 
1326
  function autoResize() {
@@ -2435,23 +2494,155 @@ function toggleSearchPanel(badge) {
2435
  }
2436
 
2437
  // ═══════════════════════════════════════════════════════
2438
- // HUGGINGFACE PUSH
2439
  // ═══════════════════════════════════════════════════════
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2440
  async function pushToHuggingFace() {
 
 
2441
  const repoName = document.getElementById('hf-repo-name').value.trim();
2442
  const hfToken = document.getElementById('hf-token').value.trim();
2443
  const spaceSdk = document.getElementById('hf-space-sdk').value;
2444
  const statusEl = document.getElementById('deploy-status');
2445
 
2446
- if (!repoName) {
 
 
 
 
 
 
2447
  statusEl.className = 'deploy-status error';
2448
  statusEl.textContent = 'Please enter a repository name.';
2449
  statusEl.style.display = 'block';
2450
  return;
2451
  }
 
2452
  if (!hfToken) {
2453
  statusEl.className = 'deploy-status error';
2454
- statusEl.textContent = 'Please enter your HuggingFace token.';
2455
  statusEl.style.display = 'block';
2456
  return;
2457
  }
@@ -2477,7 +2668,7 @@ async function pushToHuggingFace() {
2477
  method: 'POST',
2478
  headers: { 'Content-Type': 'application/json' },
2479
  body: JSON.stringify({
2480
- data: [execContextJSON, repoName, hfToken, spaceSdk, 'true']
2481
  })
2482
  });
2483
 
 
943
  cursor: not-allowed;
944
  }
945
 
946
+ #btn-hf-login {
947
+ background: linear-gradient(135deg, #FFD21E, #FF9D00);
948
+ border: none;
949
+ color: #1a1a1a;
950
+ font-family: var(--font-mono);
951
+ font-size: 12px;
952
+ font-weight: 600;
953
+ padding: 8px 16px;
954
+ border-radius: 20px;
955
+ cursor: pointer;
956
+ transition: all var(--transition);
957
+ letter-spacing: 0.5px;
958
+ }
959
+ #btn-hf-login:hover {
960
+ box-shadow: 0 0 12px rgba(255,210,30,0.5);
961
+ transform: translateY(-1px);
962
+ }
963
+
964
+ #hf-owner {
965
+ width: 100%;
966
+ background: var(--bg-code);
967
+ color: var(--gray-light);
968
+ border: 1px solid var(--border);
969
+ font-family: var(--font-mono);
970
+ font-size: 12px;
971
+ padding: 6px 10px;
972
+ border-radius: var(--radius);
973
+ cursor: pointer;
974
+ }
975
+
976
  .deploy-status {
977
  margin-top: 10px;
978
  padding: 8px 12px;
 
1219
  <div class="tab-pane" id="pane-deploy">
1220
  <div class="deploy-section">
1221
  <div class="deploy-title">&#128640; Deploy to HuggingFace</div>
1222
+
1223
+ <!-- OAuth Login Section -->
1224
+ <div class="deploy-field" id="hf-auth-section">
1225
+ <label>Sign In</label>
1226
+ <div id="hf-auth-container">
1227
+ <button id="btn-hf-login" onclick="loginWithHF()">&#129309; Sign in with HuggingFace</button>
1228
+ <div id="hf-user-info" style="display:none;">
1229
+ <img id="hf-user-avatar" src="" alt="" style="width:24px;height:24px;border-radius:50%;vertical-align:middle;margin-right:6px;">
1230
+ <span id="hf-user-name" style="color:var(--green);font-weight:600;"></span>
1231
+ <button id="btn-hf-logout" onclick="logoutHF()" style="margin-left:8px;font-size:10px;color:var(--red);background:none;border:none;cursor:pointer;">Sign out</button>
1232
+ </div>
1233
+ </div>
1234
+ <div class="deploy-hint" id="hf-auth-hint">Sign in with OAuth β€” no token paste needed</div>
1235
+ </div>
1236
+
1237
+ <!-- Push to: User or Org selector -->
1238
  <div class="deploy-field">
1239
+ <label for="hf-owner">Push to</label>
1240
+ <select id="hf-owner">
1241
+ <option value="">Sign in to see options</option>
1242
+ </select>
1243
+ <div class="deploy-hint">Select your account or an organization</div>
1244
  </div>
1245
+
1246
  <div class="deploy-field">
1247
+ <label for="hf-repo-name">Repository Name</label>
1248
+ <input type="text" id="hf-repo-name" placeholder="my-app" autocomplete="off">
1249
+ <div class="deploy-hint">The repo will be created at owner/repo-name</div>
1250
+ </div>
1251
+
1252
+ <div class="deploy-field" id="hf-manual-token-section">
1253
+ <label for="hf-token">HuggingFace Token <span style="font-weight:400;color:var(--gray-dim);">(manual fallback)</span></label>
1254
  <input type="password" id="hf-token" placeholder="hf_xxxxxxxxxxxxxxxxxxxxx" autocomplete="off">
1255
+ <div class="deploy-hint">Only needed if OAuth is unavailable. <a href="https://huggingface.co/settings/tokens" target="_blank">Get token</a></div>
1256
  </div>
1257
+
1258
  <div class="deploy-field">
1259
  <label for="hf-space-sdk">Space SDK</label>
1260
  <select id="hf-space-sdk">
 
1377
 
1378
  // Poll model status
1379
  pollModelStatus();
1380
+
1381
+ // Check for HuggingFace OAuth session
1382
+ checkGradioOAuth();
1383
  });
1384
 
1385
  function autoResize() {
 
2494
  }
2495
 
2496
  // ═══════════════════════════════════════════════════════
2497
+ // HUGGINGFACE OAUTH + PUSH
2498
  // ═══════════════════════════════════════════════════════
2499
+
2500
+ // OAuth state
2501
+ state.hfAuth = {
2502
+ authenticated: false,
2503
+ token: '',
2504
+ username: '',
2505
+ name: '',
2506
+ picture: '',
2507
+ organizations: [],
2508
+ };
2509
+
2510
+ async function loginWithHF() {
2511
+ // On HuggingFace Spaces, Gradio handles OAuth via /login/huggingface
2512
+ // We redirect to the Gradio OAuth endpoint
2513
+ const currentUrl = window.location.href;
2514
+ const loginUrl = `/login/huggingface?callback_url=${encodeURIComponent(currentUrl)}`;
2515
+ window.location.href = loginUrl;
2516
+ }
2517
+
2518
+ function logoutHF() {
2519
+ state.hfAuth = {
2520
+ authenticated: false,
2521
+ token: '',
2522
+ username: '',
2523
+ name: '',
2524
+ picture: '',
2525
+ organizations: [],
2526
+ };
2527
+ // Update UI
2528
+ document.getElementById('btn-hf-login').style.display = '';
2529
+ document.getElementById('hf-user-info').style.display = 'none';
2530
+ document.getElementById('hf-manual-token-section').style.display = '';
2531
+ document.getElementById('hf-auth-hint').textContent = 'Sign in with OAuth β€” no token paste needed';
2532
+ // Reset owner dropdown
2533
+ const ownerSelect = document.getElementById('hf-owner');
2534
+ ownerSelect.innerHTML = '<option value="">Sign in to see options</option>';
2535
+ // Check for Gradio OAuth token in session
2536
+ checkGradioOAuth();
2537
+ }
2538
+
2539
+ async function checkGradioOAuth() {
2540
+ // Try to get the OAuth token from Gradio's session
2541
+ // Gradio stores the token in cookies/session after OAuth login
2542
+ try {
2543
+ // Check if we have a Gradio session with OAuth token
2544
+ const resp = await fetch('/gradio_api/call/hf_auth', {
2545
+ method: 'POST',
2546
+ headers: { 'Content-Type': 'application/json' },
2547
+ body: JSON.stringify({ data: [''] })
2548
+ });
2549
+
2550
+ if (!resp.ok) return;
2551
+
2552
+ const { event_id } = await resp.json();
2553
+ const eventSource = new EventSource(`/gradio_api/call/hf_auth/${event_id}`);
2554
+
2555
+ eventSource.addEventListener('complete', (e) => {
2556
+ try {
2557
+ const dataArray = JSON.parse(e.data);
2558
+ const result = JSON.parse(dataArray[0]);
2559
+ if (result.authenticated) {
2560
+ handleAuthResult(result);
2561
+ }
2562
+ } catch (err) { /* not authenticated */ }
2563
+ eventSource.close();
2564
+ });
2565
+
2566
+ eventSource.addEventListener('error', () => { eventSource.close(); });
2567
+ } catch (err) {
2568
+ // OAuth not available β€” show manual token input
2569
+ }
2570
+ }
2571
+
2572
+ function handleAuthResult(result) {
2573
+ state.hfAuth = {
2574
+ authenticated: result.authenticated,
2575
+ token: result.token || '',
2576
+ username: result.username || '',
2577
+ name: result.name || '',
2578
+ picture: result.picture || '',
2579
+ organizations: result.organizations || [],
2580
+ };
2581
+
2582
+ if (result.authenticated) {
2583
+ // Update UI β€” show user info
2584
+ document.getElementById('btn-hf-login').style.display = 'none';
2585
+ document.getElementById('hf-user-info').style.display = '';
2586
+ document.getElementById('hf-user-name').textContent = result.name || result.username;
2587
+ if (result.picture) {
2588
+ document.getElementById('hf-user-avatar').src = result.picture;
2589
+ document.getElementById('hf-user-avatar').style.display = '';
2590
+ } else {
2591
+ document.getElementById('hf-user-avatar').style.display = 'none';
2592
+ }
2593
+ document.getElementById('hf-auth-hint').textContent = `Signed in as ${result.username}`;
2594
+ document.getElementById('hf-manual-token-section').style.display = 'none';
2595
+
2596
+ // Populate owner dropdown
2597
+ const ownerSelect = document.getElementById('hf-owner');
2598
+ ownerSelect.innerHTML = '';
2599
+
2600
+ // Add user as first option
2601
+ const userOpt = document.createElement('option');
2602
+ userOpt.value = result.username;
2603
+ userOpt.textContent = `${result.username} (you)`;
2604
+ ownerSelect.appendChild(userOpt);
2605
+
2606
+ // Add organizations
2607
+ if (result.organizations && result.organizations.length > 0) {
2608
+ result.organizations.forEach(org => {
2609
+ const opt = document.createElement('option');
2610
+ opt.value = org.name;
2611
+ const roleSuffix = org.role ? ` (${org.role})` : '';
2612
+ opt.textContent = `${org.name}${roleSuffix}`;
2613
+ ownerSelect.appendChild(opt);
2614
+ });
2615
+ }
2616
+
2617
+ // Auto-fill token in hidden field for push
2618
+ document.getElementById('hf-token').value = result.token;
2619
+ }
2620
+ }
2621
+
2622
  async function pushToHuggingFace() {
2623
+ const ownerSelect = document.getElementById('hf-owner');
2624
+ const owner = ownerSelect.value;
2625
  const repoName = document.getElementById('hf-repo-name').value.trim();
2626
  const hfToken = document.getElementById('hf-token').value.trim();
2627
  const spaceSdk = document.getElementById('hf-space-sdk').value;
2628
  const statusEl = document.getElementById('deploy-status');
2629
 
2630
+ // Build full repo name
2631
+ let fullRepoName = repoName;
2632
+ if (owner && repoName && !repoName.includes('/')) {
2633
+ fullRepoName = `${owner}/${repoName}`;
2634
+ }
2635
+
2636
+ if (!fullRepoName) {
2637
  statusEl.className = 'deploy-status error';
2638
  statusEl.textContent = 'Please enter a repository name.';
2639
  statusEl.style.display = 'block';
2640
  return;
2641
  }
2642
+
2643
  if (!hfToken) {
2644
  statusEl.className = 'deploy-status error';
2645
+ statusEl.textContent = 'Please sign in with HuggingFace or enter a token.';
2646
  statusEl.style.display = 'block';
2647
  return;
2648
  }
 
2668
  method: 'POST',
2669
  headers: { 'Content-Type': 'application/json' },
2670
  body: JSON.stringify({
2671
+ data: [execContextJSON, fullRepoName, hfToken, spaceSdk, 'true']
2672
  })
2673
  });
2674