Spaces:
Sleeping
pr3 (#3)
Browse files- 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>
- data/models.json +2 -9
- public/index.html +13 -4
- server.js +83 -78
|
@@ -1,22 +1,15 @@
|
|
| 1 |
[
|
|
|
|
| 2 |
{ "id": "JuggernautXL", "name": "JuggernautXL", "free": true },
|
| 3 |
-
{ "id": "
|
| 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" },
|
|
@@ -1052,7 +1052,7 @@
|
|
| 1052 |
}
|
| 1053 |
|
| 1054 |
function genRandomSeed() {
|
| 1055 |
-
return Math.floor(Math.random() *
|
| 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
|
| 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
|
| 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
|
| 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 |
}
|
|
@@ -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:
|
| 460 |
-
? { ...inputExtras, ...commonArgs }
|
| 461 |
-
: { ...inputExtras, ...commonArgs, width: flat.width, height: flat.height }
|
| 462 |
};
|
| 463 |
|
| 464 |
-
const variantB =
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
:
|
| 470 |
-
|
| 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 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
try {
|
| 645 |
-
result = await tryCall(
|
| 646 |
-
} catch (
|
| 647 |
-
if (!result) {
|
| 648 |
-
try {
|
| 649 |
-
result = await tryCall(variantCMinimal, 'nested-minimal', generateUrl);
|
| 650 |
-
} catch (e1) {}
|
| 651 |
-
}
|
| 652 |
}
|
| 653 |
-
}
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 659 |
if (!result) {
|
| 660 |
-
try {
|
| 661 |
-
|
|
|
|
| 662 |
}
|
| 663 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 664 |
if (!result) {
|
| 665 |
-
try {
|
| 666 |
-
|
| 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 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
}
|
| 686 |
|
| 687 |
-
|
|
|
|
| 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 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 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({
|