Fix FAL.AI models list endpoint (#4236) The FAL.AI endpoint seems to have switched to a paginated format where the results are nested under an `items` key. This updates the endpoint handler to fetch all pages and extract the model options correctly.

b45477578ffd3c135a9a4745fa3cf38af0db76f1

Jenner La Fave <jenner@jfave.com>

Signed
1 files changed, +22 -6Showing whitespace changes
src/endpoints/stable-diffusion.js+22 -6
@@ -1160,6 +1160,12 @@ const falai = express.Router();
1160falai.post('/models', async (_request, response) => {1160falai.post('/models', async (_request, response) => {
1161 try {1161 try {
1162 const modelsUrl = new URL('https://fal.ai/api/models?categories=text-to-image');1162 const modelsUrl = new URL('https://fal.ai/api/models?categories=text-to-image');
1163 let page = 1;
1164 let modelsResponse;
1165 let models = [];
1166
1167 do {
1168 modelsUrl.searchParams.set('page', page.toString());
1163 const result = await fetch(modelsUrl);1169 const result = await fetch(modelsUrl);
11641170
1165 if (!result.ok) {1171 if (!result.ok) {
@@ -1167,21 +1173,31 @@ falai.post('/models', async (_request, response) => {
1167 throw new Error('FAL.AI request failed.');1173 throw new Error('FAL.AI request failed.');
1168 }1174 }
11691175
1170 const data = await result.json();
11711176
1172 if (!Array.isArray(data)) {1177 modelsResponse = await result.json();
1178 if (!('items' in modelsResponse) || !Array.isArray(modelsResponse.items)) {
1173 console.warn('FAL.AI returned invalid data.');1179 console.warn('FAL.AI returned invalid data.');
1174 throw new Error('FAL.AI request failed.');1180 throw new Error('FAL.AI request failed.');
1175 }1181 }
11761182
1177 const models = data1183 models = models.concat(
1178 .filter(x => !x.title.toLowerCase().includes('inpainting') &&1184 modelsResponse.items.filter(
1185 x => (
1186 !x.title.toLowerCase().includes('inpainting') &&
1179 !x.title.toLowerCase().includes('control') &&1187 !x.title.toLowerCase().includes('control') &&
1180 !x.title.toLowerCase().includes('upscale') &&1188 !x.title.toLowerCase().includes('upscale') &&
1181 !x.title.toLowerCase().includes('lora'))1189 !x.title.toLowerCase().includes('lora')
1190 ),
1191 ),
1192 );
1193
1194 page = modelsResponse.page + 1;
1195 } while (modelsResponse != null && page < modelsResponse.pages);
1196
1197 const modelOptions = models
1182 .sort((a, b) => a.title.localeCompare(b.title))1198 .sort((a, b) => a.title.localeCompare(b.title))
1183 .map(x => ({ value: x.modelUrl.split('fal-ai/')[1], text: x.title }));1199 .map(x => ({ value: x.modelUrl.split('fal-ai/')[1], text: x.title }));
1184 return response.send(models);1200 return response.send(modelOptions);
1185 } catch (error) {1201 } catch (error) {
1186 console.error(error);1202 console.error(error);
1187 return response.sendStatus(500);1203 return response.sendStatus(500);