mo7ammad2012 commited on
Commit
15f81f8
·
verified ·
1 Parent(s): db29358
Files changed (1) hide show
  1. index(2).html +57 -187
index(2).html CHANGED
@@ -42,7 +42,6 @@
42
  border-radius:14px;outline:none;font:inherit;transition:.18s ease
43
  }
44
  textarea{min-height:168px;resize:vertical;padding:14px;line-height:1.52}
45
- textarea.token-pool{min-height:108px;font:12px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace}
46
  select,input{height:48px;padding:0 13px}
47
  textarea:focus,select:focus,input:focus{border-color:rgba(139,108,255,.9);box-shadow:0 0 0 4px rgba(139,108,255,.12)}
48
  .row{display:grid;grid-template-columns:1fr 1fr;gap:11px}
@@ -107,11 +106,6 @@
107
  </select>
108
  </div>
109
 
110
- <div class="field">
111
- <label for="hfTokens">Hugging Face tokens · one per line · max 50</label>
112
- <textarea id="hfTokens" class="token-pool" autocomplete="off" autocapitalize="off" spellcheck="false" placeholder="hf_token_1&#10;hf_token_2&#10;hf_token_3"></textarea>
113
- </div>
114
-
115
  <div class="field">
116
  <label for="prompt">Prompt</label>
117
  <textarea id="prompt" maxlength="5000" placeholder="Describe the subject, environment, camera, lighting, mood and composition..."></textarea>
@@ -156,12 +150,11 @@
156
  <div class="queuebox">
157
  <span class="qchip" id="queuedChip">Queued: 0</span>
158
  <span class="qchip" id="activeChip">Active: none</span>
159
- <span class="qchip" id="tokenChip">Token: anonymous</span>
160
  <span class="qchip" id="engineChip">Engines: 20</span>
161
  </div>
162
 
163
  <div class="fine">
164
- This Static Space keeps a private FIFO queue in your browser, uses the tokens in order without pre-checking them, switches only after quota/rate-limit/authentication failure, and keeps the existing 20-engine backend failover. Tokens are kept only in this open page.
165
  </div>
166
  </div>
167
 
@@ -320,11 +313,6 @@ let api = null;
320
  let activeBackend = null;
321
  let preferredBackendIndex = 0;
322
  let jobCounter = 0;
323
- const MAX_HF_TOKENS = 50;
324
- let hfTokens = [];
325
- let activeTokenIndex = 0;
326
- let connectedTokenIndex = -1;
327
- let tokenPoolSignature = "";
328
  const backendFailures = new Map();
329
  const BACKEND_FAILURE_COOLDOWN_MS = 10 * 60 * 1000;
330
  const BACKEND_CONNECT_TIMEOUT_MS = 18 * 1000;
@@ -451,42 +439,8 @@ function normalise(value) {
451
  return String(value || "").toLowerCase().replace(/[^a-z0-9]+/g, "");
452
  }
453
 
454
- function parseHfTokens(value) {
455
- const unique = new Set();
456
- for (const part of String(value || "").split(/[\s,;]+/)) {
457
- const token = part.trim();
458
- if (!token || !token.startsWith("hf_") || unique.has(token)) continue;
459
- unique.add(token);
460
- if (unique.size >= MAX_HF_TOKENS) break;
461
- }
462
- return [...unique];
463
- }
464
-
465
- function syncTokenPool() {
466
- const nextTokens = parseHfTokens($("hfTokens").value);
467
- const nextSignature = nextTokens.join("\n");
468
-
469
- if (nextSignature !== tokenPoolSignature) {
470
- hfTokens = nextTokens;
471
- tokenPoolSignature = nextSignature;
472
- activeTokenIndex = 0;
473
- resetBackendConnection();
474
- }
475
-
476
- updateQueueUI();
477
- return hfTokens;
478
- }
479
-
480
- function activeToken() {
481
- return hfTokens[activeTokenIndex] || "";
482
- }
483
-
484
  function updateQueueUI() {
485
  $("queuedChip").textContent = `Queued: ${localQueue.length}`;
486
- $("tokenChip").textContent = hfTokens.length
487
- ? `Token: ${activeTokenIndex + 1}/${hfTokens.length}`
488
- : "Token: anonymous";
489
-
490
  if (!processing) {
491
  $("activeChip").textContent = "Active: none";
492
  return;
@@ -567,7 +521,6 @@ function resetBackendConnection() {
567
  client = null;
568
  api = null;
569
  activeBackend = null;
570
- connectedTokenIndex = -1;
571
  updateQueueUI();
572
  }
573
 
@@ -575,11 +528,7 @@ async function connectBackend(index) {
575
  const backend = BACKEND_SPACES[index];
576
  if (!backend) throw new Error("Unknown backend index.");
577
 
578
- if (
579
- client &&
580
- activeBackend?.id === backend.id &&
581
- connectedTokenIndex === activeTokenIndex
582
- ) return client;
583
 
584
  resetBackendConnection();
585
  activeBackend = backend;
@@ -588,22 +537,15 @@ async function connectBackend(index) {
588
  $("loadingTitle").textContent = `Connecting to ${backend.label}…`;
589
  $("queueStatus").textContent = backend.id;
590
 
591
- const handleSpaceStatus = status => {
592
- if (status?.status && status.status !== "running") {
593
- $("queueStatus").textContent = `${backend.label}: ${status.status}`;
594
- }
595
- };
596
-
597
- const connectOptions = {
598
- events: ["data", "status"],
599
- space_status: handleSpaceStatus,
600
- status_callback: handleSpaceStatus
601
- };
602
- const token = activeToken();
603
- if (token) connectOptions.token = token;
604
-
605
  const newClient = await withTimeout(
606
- Client.connect(backend.id, connectOptions),
 
 
 
 
 
 
 
607
  BACKEND_CONNECT_TIMEOUT_MS,
608
  `${backend.label} connection timed out`
609
  );
@@ -620,7 +562,6 @@ async function connectBackend(index) {
620
 
621
  client = newClient;
622
  api = newApi;
623
- connectedTokenIndex = activeTokenIndex;
624
  return client;
625
  }
626
 
@@ -634,138 +575,74 @@ function isCallerZeroGpuQuotaError(error) {
634
  text.includes("exceeded your free zerogpu quota") ||
635
  text.includes("zerogpu quota") && text.includes("left") ||
636
  text.includes("daily gpu quota") ||
637
- text.includes("gpu quota") && text.includes("exceeded") ||
638
  text.includes("try again in") && text.includes("quota")
639
  );
640
  }
641
 
642
- function isTokenRateLimitError(error) {
643
- const text = errorText(error).toLowerCase();
644
- return (
645
- isCallerZeroGpuQuotaError(error) ||
646
- text.includes("too many requests") ||
647
- text.includes("rate limit") ||
648
- text.includes("rate-limit") ||
649
- /(^|\D)429(\D|$)/.test(text)
650
- );
651
- }
652
-
653
- function isTokenAuthenticationError(error) {
654
- const text = errorText(error).toLowerCase();
655
- return (
656
- text.includes("invalid token") ||
657
- text.includes("token is invalid") ||
658
- text.includes("unauthorized") ||
659
- text.includes("authentication failed") ||
660
- text.includes("invalid credentials") ||
661
- /(^|\D)401(\D|$)/.test(text)
662
- );
663
- }
664
-
665
- function rotateToNextToken(reason) {
666
- if (!hfTokens.length || activeTokenIndex + 1 >= hfTokens.length) return false;
667
-
668
- const previousNumber = activeTokenIndex + 1;
669
- activeTokenIndex += 1;
670
- resetBackendConnection();
671
- $("queueStatus").textContent =
672
- `Token ${previousNumber} ${reason}. Switching to token ${activeTokenIndex + 1}/${hfTokens.length}…`;
673
- updateQueueUI();
674
- return true;
675
- }
676
-
677
  async function generateWithBackendFailover(job) {
678
  const errors = [];
679
- const remainingTokenAttempts = hfTokens.length
680
- ? hfTokens.length - activeTokenIndex
681
- : 1;
682
- let attemptedTokens = 0;
683
-
684
- while (attemptedTokens < remainingTokenAttempts) {
685
- let switchedToken = false;
686
 
687
- for (let offset = 0; offset < BACKEND_SPACES.length; offset++) {
688
- const index = (preferredBackendIndex + offset) % BACKEND_SPACES.length;
689
- const backend = BACKEND_SPACES[index];
690
 
691
- if (backendIsCoolingDown(backend.id)) {
692
- continue;
693
- }
694
-
695
- try {
696
- await connectBackend(index);
697
- $("queueStatus").textContent = `${backend.label}: loading AIDMA LoRA`;
698
- const loaded = await loadSelectedLora(job);
699
 
700
- $("queueStatus").textContent =
701
- `${backend.label}: waiting for remote GPU and final image`;
702
- await runGeneration(job, loaded.selectedIndex, loaded.trigger);
 
703
 
704
- preferredBackendIndex = index;
705
- backendFailures.delete(backend.id);
706
- return;
707
- } catch (error) {
708
- const message = errorText(error);
709
- console.error(`${backend.label} failed:`, error);
710
 
711
- const quotaOrRateLimit = isTokenRateLimitError(error);
712
- const authenticationFailure = isTokenAuthenticationError(error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
713
 
714
- if (hfTokens.length && (quotaOrRateLimit || authenticationFailure)) {
715
- attemptedTokens += 1;
716
- errors.push(`Token ${activeTokenIndex + 1}: ${message || "unavailable"}`);
717
- const reason = authenticationFailure ? "was rejected" : "reached its quota/rate limit";
718
 
719
- if (attemptedTokens < remainingTokenAttempts && rotateToNextToken(reason)) {
720
- switchedToken = true;
 
 
 
 
 
721
  break;
722
  }
723
-
724
- resetBackendConnection();
725
- activeTokenIndex = 0;
726
- updateQueueUI();
727
- throw new Error(
728
- "All configured Hugging Face tokens reached quota/rate-limit or were rejected. " +
729
- errors.join(" | ")
730
- );
731
- }
732
-
733
- if (!hfTokens.length && quotaOrRateLimit) {
734
- resetBackendConnection();
735
- throw new Error(message);
736
- }
737
-
738
- errors.push(`${backend.label}: ${message || "unknown error"}`);
739
- markBackendFailed(backend.id);
740
- resetBackendConnection();
741
-
742
- if (offset + 1 < BACKEND_SPACES.length) {
743
- let nextBackend = null;
744
- for (let ahead = offset + 1; ahead < BACKEND_SPACES.length; ahead++) {
745
- const nextIndex = (preferredBackendIndex + ahead) % BACKEND_SPACES.length;
746
- const candidate = BACKEND_SPACES[nextIndex];
747
- if (!backendIsCoolingDown(candidate.id)) {
748
- nextBackend = candidate;
749
- break;
750
- }
751
- }
752
- $("queueStatus").textContent = nextBackend
753
- ? `${backend.label} failed. Switching to ${nextBackend.label}…`
754
- : `${backend.label} failed. No healthy backend remains.`;
755
  }
 
 
 
756
  }
757
  }
758
-
759
- if (switchedToken) continue;
760
-
761
- throw new Error(
762
- "All configured AIDMA Spaces failed. " + errors.join(" | ")
763
- );
764
  }
 
 
 
 
765
  }
766
 
767
  function snapshotJob() {
768
- syncTokenPool();
769
  const prompt = $("prompt").value.trim();
770
  if (!prompt) throw new Error("Write a prompt first.");
771
 
@@ -1015,13 +892,6 @@ $("generate").addEventListener("click", () => {
1015
  }
1016
  });
1017
 
1018
- $("hfTokens").addEventListener("input", () => {
1019
- const preview = parseHfTokens($("hfTokens").value);
1020
- $("tokenChip").textContent = preview.length
1021
- ? `Tokens entered: ${preview.length}/${MAX_HF_TOKENS}`
1022
- : "Token: anonymous";
1023
- });
1024
-
1025
  $("prompt").addEventListener("keydown", event => {
1026
  if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
1027
  event.preventDefault();
@@ -1032,4 +902,4 @@ $("prompt").addEventListener("keydown", event => {
1032
  updateQueueUI();
1033
  </script>
1034
  </body>
1035
- </html>
 
42
  border-radius:14px;outline:none;font:inherit;transition:.18s ease
43
  }
44
  textarea{min-height:168px;resize:vertical;padding:14px;line-height:1.52}
 
45
  select,input{height:48px;padding:0 13px}
46
  textarea:focus,select:focus,input:focus{border-color:rgba(139,108,255,.9);box-shadow:0 0 0 4px rgba(139,108,255,.12)}
47
  .row{display:grid;grid-template-columns:1fr 1fr;gap:11px}
 
106
  </select>
107
  </div>
108
 
 
 
 
 
 
109
  <div class="field">
110
  <label for="prompt">Prompt</label>
111
  <textarea id="prompt" maxlength="5000" placeholder="Describe the subject, environment, camera, lighting, mood and composition..."></textarea>
 
150
  <div class="queuebox">
151
  <span class="qchip" id="queuedChip">Queued: 0</span>
152
  <span class="qchip" id="activeChip">Active: none</span>
 
153
  <span class="qchip" id="engineChip">Engines: 20</span>
154
  </div>
155
 
156
  <div class="fine">
157
+ This Static Space keeps a private FIFO queue in your browser and checks up to 20 AIDMA backend candidates, automatically skipping unavailable or incompatible Spaces.
158
  </div>
159
  </div>
160
 
 
313
  let activeBackend = null;
314
  let preferredBackendIndex = 0;
315
  let jobCounter = 0;
 
 
 
 
 
316
  const backendFailures = new Map();
317
  const BACKEND_FAILURE_COOLDOWN_MS = 10 * 60 * 1000;
318
  const BACKEND_CONNECT_TIMEOUT_MS = 18 * 1000;
 
439
  return String(value || "").toLowerCase().replace(/[^a-z0-9]+/g, "");
440
  }
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  function updateQueueUI() {
443
  $("queuedChip").textContent = `Queued: ${localQueue.length}`;
 
 
 
 
444
  if (!processing) {
445
  $("activeChip").textContent = "Active: none";
446
  return;
 
521
  client = null;
522
  api = null;
523
  activeBackend = null;
 
524
  updateQueueUI();
525
  }
526
 
 
528
  const backend = BACKEND_SPACES[index];
529
  if (!backend) throw new Error("Unknown backend index.");
530
 
531
+ if (client && activeBackend?.id === backend.id) return client;
 
 
 
 
532
 
533
  resetBackendConnection();
534
  activeBackend = backend;
 
537
  $("loadingTitle").textContent = `Connecting to ${backend.label}…`;
538
  $("queueStatus").textContent = backend.id;
539
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  const newClient = await withTimeout(
541
+ Client.connect(backend.id, {
542
+ events: ["data", "status"],
543
+ space_status: status => {
544
+ if (status?.status && status.status !== "running") {
545
+ $("queueStatus").textContent = `${backend.label}: ${status.status}`;
546
+ }
547
+ }
548
+ }),
549
  BACKEND_CONNECT_TIMEOUT_MS,
550
  `${backend.label} connection timed out`
551
  );
 
562
 
563
  client = newClient;
564
  api = newApi;
 
565
  return client;
566
  }
567
 
 
575
  text.includes("exceeded your free zerogpu quota") ||
576
  text.includes("zerogpu quota") && text.includes("left") ||
577
  text.includes("daily gpu quota") ||
 
578
  text.includes("try again in") && text.includes("quota")
579
  );
580
  }
581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
  async function generateWithBackendFailover(job) {
583
  const errors = [];
 
 
 
 
 
 
 
584
 
585
+ for (let offset = 0; offset < BACKEND_SPACES.length; offset++) {
586
+ const index = (preferredBackendIndex + offset) % BACKEND_SPACES.length;
587
+ const backend = BACKEND_SPACES[index];
588
 
589
+ if (backendIsCoolingDown(backend.id)) {
590
+ continue;
591
+ }
 
 
 
 
 
592
 
593
+ try {
594
+ await connectBackend(index);
595
+ $("queueStatus").textContent = `${backend.label}: loading AIDMA LoRA`;
596
+ const loaded = await loadSelectedLora(job);
597
 
598
+ $("queueStatus").textContent =
599
+ `${backend.label}: waiting for remote GPU and final image`;
600
+ await runGeneration(job, loaded.selectedIndex, loaded.trigger);
 
 
 
601
 
602
+ preferredBackendIndex = index;
603
+ backendFailures.delete(backend.id);
604
+ return;
605
+ } catch (error) {
606
+ const message = errorText(error);
607
+ console.error(`${backend.label} failed:`, error);
608
+
609
+ /*
610
+ ZeroGPU usage quota belongs to the caller, not to this individual Space.
611
+ Trying another ZeroGPU Space would return the same caller-quota failure,
612
+ so this specific error is surfaced immediately.
613
+ */
614
+ if (isCallerZeroGpuQuotaError(error)) {
615
+ resetBackendConnection();
616
+ throw new Error(message);
617
+ }
618
 
619
+ errors.push(`${backend.label}: ${message || "unknown error"}`);
620
+ markBackendFailed(backend.id);
621
+ resetBackendConnection();
 
622
 
623
+ if (offset + 1 < BACKEND_SPACES.length) {
624
+ let nextBackend = null;
625
+ for (let ahead = offset + 1; ahead < BACKEND_SPACES.length; ahead++) {
626
+ const nextIndex = (preferredBackendIndex + ahead) % BACKEND_SPACES.length;
627
+ const candidate = BACKEND_SPACES[nextIndex];
628
+ if (!backendIsCoolingDown(candidate.id)) {
629
+ nextBackend = candidate;
630
  break;
631
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  }
633
+ $("queueStatus").textContent = nextBackend
634
+ ? `${backend.label} failed. Switching to ${nextBackend.label}…`
635
+ : `${backend.label} failed. No healthy backend remains.`;
636
  }
637
  }
 
 
 
 
 
 
638
  }
639
+
640
+ throw new Error(
641
+ "All configured AIDMA Spaces failed. " + errors.join(" | ")
642
+ );
643
  }
644
 
645
  function snapshotJob() {
 
646
  const prompt = $("prompt").value.trim();
647
  if (!prompt) throw new Error("Write a prompt first.");
648
 
 
892
  }
893
  });
894
 
 
 
 
 
 
 
 
895
  $("prompt").addEventListener("keydown", event => {
896
  if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
897
  event.preventDefault();
 
902
  updateQueueUI();
903
  </script>
904
  </body>
905
+ </html>