Logankunfall bibibi12345 commited on
Commit
86bd8c5
·
verified ·
1 Parent(s): 3119427

- fixed hunyuan size. fixed saving. fixed image preview size. added large preview. (354cde7300c3ab020b96db4fc712cfb1cceed183)
- Merge branch 'main' of https://huggingface.co/spaces/Logankunfall/chutes into pr/1 (1847d877e3b32b3d0512da35fd46a33091513bd1)
- fixed zimage guidance scale bug (decc758d5dab5fa9608c36c8f15a89b57628ec4d)
- fixed model list (0dedc144416e73e1f77ebabf3ea506f3c6daae5c)
- fixed model list (b8b2a75f3e8b57338cc5518c0a78cacc81af0c36)
- fixed retry bug. fixed highdream (95030c642c2eb5d2bd12861fc9a8fb9cf56a6e30)
- fixed model list (8931d96bfb8910274ee64d5439fff7f18a35b5ef)
- fixed model list (ce3b21b3c65057b3cc03d258ace0dd74efc11e3a)
- fixed model list (e393d27a3b3ebdd1ed25bc20cda8c5844401f432)


Co-authored-by: solom <bibibi12345@users.noreply.huggingface.co>

Files changed (3) hide show
  1. data/models.json +2 -9
  2. public/index.html +13 -4
  3. server.js +83 -78
data/models.json CHANGED
@@ -1,22 +1,15 @@
1
  [
 
2
  { "id": "JuggernautXL", "name": "JuggernautXL", "free": true },
3
- { "id": "hidream", "name": "hidream", "free": false },
4
- { "id": "FLUX.1-dev", "name": "FLUX.1-dev", "free": false },
5
  { "id": "FLUX.1-schnell", "name": "FLUX.1-schnell", "free": false },
6
  { "id": "chroma", "name": "chroma", "free": true },
7
- { "id": "neta-lumina", "name": "neta-lumina", "free": true },
8
- { "id": "nova-anime3d-xl", "name": "nova-anime3d-xl", "free": false },
9
  { "id": "Illustrij", "name": "Illustrij", "free": false },
10
  { "id": "stabilityai/stable-diffusion-xl-base-1.0", "name": "stable-diffusion-xl-base-1.0", "free": false },
11
  { "id": "iLustMix", "name": "iLustMix", "free": false },
12
- { "id": "Animij", "name": "Animij", "free": false },
13
  { "id": "NovaFurryXL", "name": "NovaFurryXL", "free": false },
14
  { "id": "Lykon/dreamshaper-xl-1-0", "name": "dreamshaper-xl-1-0", "free": false },
15
- { "id": "Shitao/OmniGen-v1", "name": "OmniGen-v1", "free": false },
16
  { "id": "diagonalge/Booba", "name": "Booba", "free": false },
17
- { "id": "HassakuXL", "name": "HassakuXL", "free": false },
18
- { "id": "nova-cartoon-xl", "name": "nova-cartoon-xl", "free": false },
19
- { "id": "orphic-lora", "name": "orphic-lora", "free": false },
20
  { "id": "diagonalge/ConstShaper", "name": "ConstShaper", "free": false },
21
  { "id": "qwen-image", "name": "qwen-image", "free": false },
22
  { "id": "qwen-image-edit", "name": "qwen-image-edit", "free": false, "upstream_id": "qwen-image", "upstream_url": "https://chutes-qwen-image-edit-2509.chutes.ai/generate" },
 
1
  [
2
+ { "id": "JuggernautXL-Ragnarok", "name": "JuggernautXL-Ragnarok", "free": true },
3
  { "id": "JuggernautXL", "name": "JuggernautXL", "free": true },
4
+ { "id": "qwen-image", "name": "qwen-image", "free": false },
 
5
  { "id": "FLUX.1-schnell", "name": "FLUX.1-schnell", "free": false },
6
  { "id": "chroma", "name": "chroma", "free": true },
 
 
7
  { "id": "Illustrij", "name": "Illustrij", "free": false },
8
  { "id": "stabilityai/stable-diffusion-xl-base-1.0", "name": "stable-diffusion-xl-base-1.0", "free": false },
9
  { "id": "iLustMix", "name": "iLustMix", "free": false },
 
10
  { "id": "NovaFurryXL", "name": "NovaFurryXL", "free": false },
11
  { "id": "Lykon/dreamshaper-xl-1-0", "name": "dreamshaper-xl-1-0", "free": false },
 
12
  { "id": "diagonalge/Booba", "name": "Booba", "free": false },
 
 
 
13
  { "id": "diagonalge/ConstShaper", "name": "ConstShaper", "free": false },
14
  { "id": "qwen-image", "name": "qwen-image", "free": false },
15
  { "id": "qwen-image-edit", "name": "qwen-image-edit", "free": false, "upstream_id": "qwen-image", "upstream_url": "https://chutes-qwen-image-edit-2509.chutes.ai/generate" },
public/index.html CHANGED
@@ -1052,7 +1052,7 @@
1052
  }
1053
 
1054
  function genRandomSeed() {
1055
- return Math.floor(Math.random() * 4294967295);
1056
  }
1057
 
1058
  async function fetchModels() {
@@ -1194,6 +1194,15 @@
1194
  return params;
1195
  }
1196
 
 
 
 
 
 
 
 
 
 
1197
  function createPlaceholderCard(i, params) {
1198
  const wrap = document.createElement('div');
1199
  wrap.className = 'card';
@@ -1203,7 +1212,7 @@
1203
  wrap.appendChild(ph);
1204
  const meta = document.createElement('div');
1205
  meta.className = 'meta';
1206
- meta.innerHTML = `${params.model} | ${params.width}x${params.height}`;
1207
  wrap.appendChild(meta);
1208
  qs('#gallery').prepend(wrap);
1209
  return wrap;
@@ -1248,7 +1257,7 @@
1248
  const metaContent = document.createElement('div');
1249
  metaContent.className = 'meta-content';
1250
  metaContent.innerHTML = `
1251
- <div style="margin-bottom: 4px;">尺寸: ${params.width}x${params.height}</div>
1252
  <div style="margin-bottom: 4px;">步数: ${params.num_inference_steps} | 引导: ${params.guidance_scale}</div>
1253
  <div style="margin-bottom: 8px; font-size: 12px; line-height: 1.3; word-break: break-all;">${params.prompt}</div>
1254
  `;
@@ -2033,7 +2042,7 @@ function isViewerOpen() {
2033
 
2034
  modalImg.src = imageUrl;
2035
  if (params) {
2036
- modalInfo.textContent = `${params.width}×${params.height} | ${params.model}`;
2037
  } else {
2038
  modalInfo.textContent = '';
2039
  }
 
1052
  }
1053
 
1054
  function genRandomSeed() {
1055
+ return Math.floor(Math.random() * 100000000);
1056
  }
1057
 
1058
  async function fetchModels() {
 
1194
  return params;
1195
  }
1196
 
1197
+ // Helper to get display size string (resolution for hidream, width x height for others)
1198
+ function getDisplaySize(params) {
1199
+ const isHidream = (params.model || '').toLowerCase() === 'hidream';
1200
+ if (isHidream && params.resolution) {
1201
+ return params.resolution;
1202
+ }
1203
+ return `${params.width}x${params.height}`;
1204
+ }
1205
+
1206
  function createPlaceholderCard(i, params) {
1207
  const wrap = document.createElement('div');
1208
  wrap.className = 'card';
 
1212
  wrap.appendChild(ph);
1213
  const meta = document.createElement('div');
1214
  meta.className = 'meta';
1215
+ meta.innerHTML = `${params.model} | ${getDisplaySize(params)}`;
1216
  wrap.appendChild(meta);
1217
  qs('#gallery').prepend(wrap);
1218
  return wrap;
 
1257
  const metaContent = document.createElement('div');
1258
  metaContent.className = 'meta-content';
1259
  metaContent.innerHTML = `
1260
+ <div style="margin-bottom: 4px;">尺寸: ${getDisplaySize(params)}</div>
1261
  <div style="margin-bottom: 4px;">步数: ${params.num_inference_steps} | 引导: ${params.guidance_scale}</div>
1262
  <div style="margin-bottom: 8px; font-size: 12px; line-height: 1.3; word-break: break-all;">${params.prompt}</div>
1263
  `;
 
2042
 
2043
  modalImg.src = imageUrl;
2044
  if (params) {
2045
+ modalInfo.textContent = `${getDisplaySize(params)} | ${params.model}`;
2046
  } else {
2047
  modalInfo.textContent = '';
2048
  }
server.js CHANGED
@@ -67,16 +67,24 @@ app.use(helmet.contentSecurityPolicy({
67
  directives: {
68
  "default-src": ["'self'"],
69
  // Allow SPA inline scripts but forbid inline event attributes per CSP3
 
70
  "script-src": ["'self'", "'unsafe-inline'"],
71
  "script-src-attr": ["'none'"],
72
  // Permit external stylesheet from Baomitu CDN via style-src-elem
 
 
73
  "style-src": ["'self'", "'unsafe-inline'"],
74
  "style-src-elem": ["'self'", "'unsafe-inline'", "https://lib.baomitu.com"],
 
75
  "img-src": ["'self'", "data:", "blob:"],
76
  // Allow Baomitu icon fonts
77
  "font-src": ["'self'", "data:", "https://lib.baomitu.com"],
78
  // Allow CSS sourcemap requests to Baomitu CDN
79
  "connect-src": ["'self'", "https://image.chutes.ai", "https://lib.baomitu.com"],
 
 
 
 
80
  "media-src": ["'self'", "data:", "blob:"],
81
  "frame-src": ["'self'", "https://huggingface.co", "https://*.huggingface.co", "https://*.hf.space"],
82
  "frame-ancestors": ["'self'", "https://huggingface.co", "https://*.huggingface.co", "https://*.hf.space"]
@@ -317,6 +325,11 @@ app.post('/api/generate', async (req, res) => {
317
  const parsed = parseFloat(raw);
318
  return Number.isNaN(parsed) ? 7.5 : parsed;
319
  })(), 0, 20),
 
 
 
 
 
320
  num_inference_steps: clamp(parseInt(body.num_inference_steps ?? (body.input_args ? body.input_args.num_inference_steps : 25), 10) || 25, 1, 100),
321
  // Seed: support top-level or input_args.seed; if missing/null -> null (omit from payload)
322
  seed: (() => {
@@ -456,23 +469,16 @@ app.post('/api/generate', async (req, res) => {
456
  };
457
  const variantA = {
458
  model: targetModel,
459
- input_args: isHidream
460
- ? { ...inputExtras, ...commonArgs }
461
- : { ...inputExtras, ...commonArgs, width: flat.width, height: flat.height }
462
  };
463
 
464
- const variantB = isHidream
465
- ? {
466
- model: targetModel,
467
- input_args: { ...inputExtras, ...commonArgs }
468
- }
469
- : {
470
- model: targetModel,
471
- ...topLevelExtras,
472
- ...commonArgs,
473
- width: flat.width,
474
- height: flat.height
475
- };
476
 
477
  // Hidream-specific flat payload expected by chutes-hidream endpoint (no input_args)
478
  const variantHidreamFlat = isHidream ? {
@@ -621,70 +627,70 @@ app.post('/api/generate', async (req, res) => {
621
  }
622
 
623
  let result;
624
-
625
- // If the model prefers minimal payload, choose ordering per model.
626
- // For hunyuan-image-3: nested-minimal (input_args with size) FIRST to ensure size is honored.
627
- // For Z-Image-Turbo: use dedicated nested payload FIRST
628
- if (preferMinimal) {
629
- const isHunyuan =
630
- (typeof flat.model === 'string' && flat.model.toLowerCase() === 'hunyuan-image-3') ||
631
- /hunyuan-image-3/i.test(generateUrl) ||
632
- /hunyuan-image-3/i.test(String(targetModel || ''));
633
-
634
- if (isZImageTurbo) {
635
- // Z-Image-Turbo: try minimal payload first (as shown in curl example)
636
- try {
637
- result = await tryCall(variantZImageMinimal, 'z-image-turbo-minimal', generateUrl);
638
- } catch (e0) {}
639
- } else if (isHunyuan) {
640
- try {
641
- result = await tryCall(variantFlatMinimal, 'flat-minimal', generateUrl);
642
- } catch (e0) {}
643
- } else {
644
  try {
645
- result = await tryCall(variantFlatMinimal, 'flat-minimal', generateUrl);
646
- } catch (e0) {}
647
- if (!result) {
648
- try {
649
- result = await tryCall(variantCMinimal, 'nested-minimal', generateUrl);
650
- } catch (e1) {}
651
- }
652
  }
653
- }
654
-
655
- // Only try full payload if minimal didn't succeed
656
- if (!result) {
657
- let lastError = null;
658
-
 
 
 
 
659
  if (!result) {
660
- try { result = await tryCall(variantA, 'nested', generateUrl); }
661
- catch (e1) { lastError = e1; }
 
662
  }
663
-
 
 
 
 
664
  if (!result) {
665
- try { result = await tryCall(variantB, 'flat', generateUrl); }
666
- catch (e2) { lastError = e2; }
667
- }
668
-
669
- // qwen-image-edit official flat payload (no model)
670
- if (!result && isQwenEdit && variantQwenFlat) {
671
- try { result = await tryCall(variantQwenFlat, 'qwen-flat', generateUrl); }
672
- catch (eQ) { lastError = eQ; }
673
  }
 
674
 
675
- // hidream official flat payload (no model)
676
- if (!result && isHidream && variantHidreamFlat) {
677
- try { result = await tryCall(variantHidreamFlat, 'hidream-flat', generateUrl); }
678
- catch (eH) { lastError = eH; }
679
- }
680
 
681
- // Z-Image-Turbo nested payload - fallback if not tried in minimal mode
682
- if (!result && isZImageTurbo && variantZImageNested && !preferMinimal) {
683
- try { result = await tryCall(variantZImageNested, 'z-image-turbo-nested', generateUrl); }
684
- catch (eZ) { lastError = eZ; }
685
- }
686
 
687
- if (!result) {
 
688
  // Auto fallback to another model when capacity/infrastructure errors (disabled when NO_FALLBACK=true)
689
  const capacityCodes = ['UPSTREAM_CAPACITY_EXHAUSTED','UPSTREAM_NO_INSTANCES','UPSTREAM_INFRASTRUCTURE','UPSTREAM_BAD_GATEWAY'];
690
  if (AUTO_FALLBACK && !NO_FALLBACK && lastError && capacityCodes.includes(lastError.code || '')) {
@@ -739,14 +745,13 @@ app.post('/api/generate', async (req, res) => {
739
  }
740
  }
741
 
742
- const status = (lastError && lastError.status) || 502;
743
- return res.status(status).json({
744
- ok: false,
745
- error: (lastError && (lastError.hint || lastError.message)) || 'Upstream error',
746
- code: (lastError && lastError.code) || 'UPSTREAM_ERROR',
747
- upstream_model: targetModel
748
- });
749
- }
750
  }
751
 
752
  return res.json({
 
67
  directives: {
68
  "default-src": ["'self'"],
69
  // Allow SPA inline scripts but forbid inline event attributes per CSP3
70
+ // Allow SPA inline scripts but forbid inline event attributes per CSP3
71
  "script-src": ["'self'", "'unsafe-inline'"],
72
  "script-src-attr": ["'none'"],
73
  // Permit external stylesheet from Baomitu CDN via style-src-elem
74
+ "script-src-attr": ["'none'"],
75
+ // Permit external stylesheet from Baomitu CDN via style-src-elem
76
  "style-src": ["'self'", "'unsafe-inline'"],
77
  "style-src-elem": ["'self'", "'unsafe-inline'", "https://lib.baomitu.com"],
78
+ "style-src-elem": ["'self'", "'unsafe-inline'", "https://lib.baomitu.com"],
79
  "img-src": ["'self'", "data:", "blob:"],
80
  // Allow Baomitu icon fonts
81
  "font-src": ["'self'", "data:", "https://lib.baomitu.com"],
82
  // Allow CSS sourcemap requests to Baomitu CDN
83
  "connect-src": ["'self'", "https://image.chutes.ai", "https://lib.baomitu.com"],
84
+ // Allow Baomitu icon fonts
85
+ "font-src": ["'self'", "data:", "https://lib.baomitu.com"],
86
+ // Allow CSS sourcemap requests to Baomitu CDN
87
+ "connect-src": ["'self'", "https://image.chutes.ai", "https://lib.baomitu.com"],
88
  "media-src": ["'self'", "data:", "blob:"],
89
  "frame-src": ["'self'", "https://huggingface.co", "https://*.huggingface.co", "https://*.hf.space"],
90
  "frame-ancestors": ["'self'", "https://huggingface.co", "https://*.huggingface.co", "https://*.hf.space"]
 
325
  const parsed = parseFloat(raw);
326
  return Number.isNaN(parsed) ? 7.5 : parsed;
327
  })(), 0, 20),
328
+ guidance_scale: clamp((() => {
329
+ const raw = body.guidance_scale ?? (body.input_args ? body.input_args.guidance_scale : undefined);
330
+ const parsed = parseFloat(raw);
331
+ return Number.isNaN(parsed) ? 7.5 : parsed;
332
+ })(), 0, 20),
333
  num_inference_steps: clamp(parseInt(body.num_inference_steps ?? (body.input_args ? body.input_args.num_inference_steps : 25), 10) || 25, 1, 100),
334
  // Seed: support top-level or input_args.seed; if missing/null -> null (omit from payload)
335
  seed: (() => {
 
469
  };
470
  const variantA = {
471
  model: targetModel,
472
+ input_args: { ...inputExtras, ...commonArgs, width: flat.width, height: flat.height }
 
 
473
  };
474
 
475
+ const variantB = {
476
+ model: targetModel,
477
+ ...topLevelExtras,
478
+ ...commonArgs,
479
+ width: flat.width,
480
+ height: flat.height
481
+ };
 
 
 
 
 
482
 
483
  // Hidream-specific flat payload expected by chutes-hidream endpoint (no input_args)
484
  const variantHidreamFlat = isHidream ? {
 
627
  }
628
 
629
  let result;
630
+ let lastError = null;
631
+
632
+ const isHunyuan =
633
+ (typeof flat.model === 'string' && flat.model.toLowerCase() === 'hunyuan-image-3') ||
634
+ /hunyuan-image-3/i.test(generateUrl) ||
635
+ /hunyuan-image-3/i.test(String(targetModel || ''));
636
+
637
+ // Model-specific payloads FIRST - try the correct format before generic fallbacks
638
+ if (isHidream) {
639
+ // HiDream: uses resolution instead of width/height
640
+ try {
641
+ result = await tryCall(variantHidreamFlat, 'hidream-flat', generateUrl);
642
+ } catch (eH) { lastError = eH; }
643
+ } else if (isZImageTurbo) {
644
+ // Z-Image-Turbo: try minimal payload first (as shown in curl example)
645
+ try {
646
+ result = await tryCall(variantZImageMinimal, 'z-image-turbo-minimal', generateUrl);
647
+ } catch (e0) { lastError = e0; }
648
+ // Fallback to nested payload
649
+ if (!result && variantZImageNested) {
650
  try {
651
+ result = await tryCall(variantZImageNested, 'z-image-turbo-nested', generateUrl);
652
+ } catch (eZ) { lastError = eZ; }
 
 
 
 
 
653
  }
654
+ } else if (isQwenEdit) {
655
+ // Qwen-image-edit: try official flat payload first
656
+ try {
657
+ result = await tryCall(variantQwenFlat, 'qwen-flat', generateUrl);
658
+ } catch (eQ) { lastError = eQ; }
659
+ } else if (isHunyuan) {
660
+ // Hunyuan: uses size instead of width/height
661
+ try {
662
+ result = await tryCall(variantFlatMinimal, 'flat-minimal', generateUrl);
663
+ } catch (e0) { lastError = e0; }
664
  if (!result) {
665
+ try {
666
+ result = await tryCall(variantCMinimal, 'nested-minimal', generateUrl);
667
+ } catch (e1) { lastError = e1; }
668
  }
669
+ } else if (preferMinimal) {
670
+ // Other models with minimal preference
671
+ try {
672
+ result = await tryCall(variantFlatMinimal, 'flat-minimal', generateUrl);
673
+ } catch (e0) { lastError = e0; }
674
  if (!result) {
675
+ try {
676
+ result = await tryCall(variantCMinimal, 'nested-minimal', generateUrl);
677
+ } catch (e1) { lastError = e1; }
 
 
 
 
 
678
  }
679
+ }
680
 
681
+ // Generic fallback payloads - only try if model-specific didn't succeed
682
+ if (!result) {
683
+ try { result = await tryCall(variantA, 'nested', generateUrl); }
684
+ catch (e1) { lastError = e1; }
685
+ }
686
 
687
+ if (!result) {
688
+ try { result = await tryCall(variantB, 'flat', generateUrl); }
689
+ catch (e2) { lastError = e2; }
690
+ }
 
691
 
692
+ // Final fallback logic
693
+ if (!result) {
694
  // Auto fallback to another model when capacity/infrastructure errors (disabled when NO_FALLBACK=true)
695
  const capacityCodes = ['UPSTREAM_CAPACITY_EXHAUSTED','UPSTREAM_NO_INSTANCES','UPSTREAM_INFRASTRUCTURE','UPSTREAM_BAD_GATEWAY'];
696
  if (AUTO_FALLBACK && !NO_FALLBACK && lastError && capacityCodes.includes(lastError.code || '')) {
 
745
  }
746
  }
747
 
748
+ const status = (lastError && lastError.status) || 502;
749
+ return res.status(status).json({
750
+ ok: false,
751
+ error: (lastError && (lastError.hint || lastError.message)) || 'Upstream error',
752
+ code: (lastError && lastError.code) || 'UPSTREAM_ERROR',
753
+ upstream_model: targetModel
754
+ });
 
755
  }
756
 
757
  return res.json({