forked from ilkerzg/fal-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodels-new.js
More file actions
207 lines (182 loc) · 6.14 KB
/
models-new.js
File metadata and controls
207 lines (182 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/**
* ===== SCALABLE FAL AI MODEL LOADER =====
*
* This module provides efficient loading and management of FAL AI model configurations
* from JSON files. It implements caching for performance and provides various utility
* functions for model discovery and organization.
*
* Key Features:
* - Dynamic model loading from JSON configuration files
* - In-memory caching for improved performance
* - Model categorization and grouping
* - Shared parameter analysis across models
* - Graceful error handling for missing or corrupt files
* - Cache refresh capabilities for runtime updates
*
* Architecture:
* - Models are stored as individual JSON files in the ./models directory
* - Each filename (without .json) becomes the model key/identifier
* - Configurations are cached in memory after first load
* - Cache can be refreshed when model files are updated
*
* @author ilkerzg
* @version 0.0.1
*/
import fs from 'fs-extra';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const modelsDir = path.join(__dirname, 'models');
// ===== CACHING SYSTEM =====
/**
* In-memory cache for loaded model configurations.
* This prevents repeated file system access and improves performance.
*/
let modelsCache = null;
/**
* Load all model definitions from JSON files in the models directory
*
* This function scans the models directory for JSON files and loads each one as a model
* configuration. It implements caching to avoid repeated file system operations and
* provides graceful error handling for individual file loading failures.
*
* The function uses filenames (without .json extension) as model keys, allowing for
* easy model identification and retrieval. Failed file loads are logged as warnings
* but don't prevent other models from loading successfully.
*
* @returns {Promise<Object>} Object with model keys as properties and model configurations as values:
* - Key: filename without .json extension (e.g., 'flux-pro-kontext')
* - Value: parsed JSON model configuration object
*
* @throws {Error} If models directory is inaccessible or no valid JSON files found
*
* @example
* const models = await loadModels();
* console.log(Object.keys(models)); // ['flux-pro-kontext', 'sdxl-turbo', ...]
* const fluxModel = models['flux-pro-kontext'];
*/
export const loadModels = async () => {
if (modelsCache) {
return modelsCache;
}
try {
// Read all JSON files from models directory
const files = await fs.readdir(modelsDir);
const jsonFiles = files.filter(file => file.endsWith('.json'));
if (jsonFiles.length === 0) {
throw new Error('No model JSON files found in models directory');
}
const models = {};
// Load each JSON file
for (const file of jsonFiles) {
const filePath = path.join(modelsDir, file);
const modelKey = path.basename(file, '.json'); // Use filename as key
try {
const modelData = await fs.readJson(filePath);
models[modelKey] = modelData;
} catch (error) {
console.warn(`Warning: Failed to load model from ${file}:`, error.message);
}
}
modelsCache = models;
return models;
} catch (error) {
throw new Error(`Failed to load models: ${error.message}`);
}
};
/**
* Get model by its key
* @param {string} modelKey - The model key
* @returns {Object|null} Model definition or null if not found
*/
export const getModelById = async (modelKey) => {
const models = await loadModels();
return models[modelKey] || null;
};
/**
* Get all models as an array with keys
* @returns {Array} Array of models with key property
*/
export const getAllModels = async () => {
const models = await loadModels();
return Object.entries(models).map(([key, model]) => ({
key,
...model
}));
};
/**
* Get models grouped by category
* @returns {Object} Object with categories as keys and arrays of models as values
*/
export const getModelsByCategory = async () => {
const models = await loadModels();
const categories = {};
Object.entries(models).forEach(([key, model]) => {
if (!categories[model.category]) {
categories[model.category] = [];
}
categories[model.category].push({ key, ...model });
});
return categories;
};
/**
* Helper function to find shared parameters between multiple models
* @param {Array} models - Array of model objects
* @returns {Object} Object describing shared parameters
*/
export const getSharedParameters = (models) => {
if (!models || models.length === 0) return {};
// Find shared aspect ratios across all models
const sharedAspectRatios = models.reduce((common, model) => {
if (!model.supportedAspectRatios) return common;
return common.filter(ratio => model.supportedAspectRatios.includes(ratio));
}, models[0]?.supportedAspectRatios || []);
// Find shared formats across all models
const sharedFormats = models.reduce((common, model) => {
if (!model.supportedFormats) return common;
return common.filter(format => model.supportedFormats.includes(format));
}, models[0]?.supportedFormats || []);
const sharedParams = {};
// Add aspect ratio if shared
if (sharedAspectRatios.length > 0) {
sharedParams.aspect_ratio = {
type: 'string',
options: sharedAspectRatios,
default: sharedAspectRatios[0]
};
}
// Add output format if shared
if (sharedFormats.length > 0) {
sharedParams.output_format = {
type: 'string',
options: sharedFormats,
default: sharedFormats[0]
};
}
// Add num_images (all models support this, but limit to max 4 per API call)
sharedParams.num_images = {
type: 'integer',
min: 1,
max: 4, // Maximum 4 images per single API call
default: 1
};
return sharedParams;
};
/**
* Refresh models cache - useful when models are updated
*/
export const refreshModelsCache = () => {
modelsCache = null;
};
/**
* Get list of available model files
* @returns {Array} Array of model filenames
*/
export const getAvailableModelFiles = async () => {
try {
const files = await fs.readdir(modelsDir);
return files.filter(file => file.endsWith('.json'));
} catch (error) {
return [];
}
};