PluginProbe
BotWriter – AI Writer & SEO Content Generator / trunk
BotWriter – AI Writer & SEO Content Generator vtrunk
3.4.10 3.4.9 3.4.8 3.4.7 3.4.6 3.4.4 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 3.3.0 3.2.8 trunk 1.3.0 1.3.1 1.3.2 1.3.3 All 51 releases
botwriter / includes / image-models.php

image-models.php in BotWriter – AI Writer & SEO Content Generator trunk, at includes/image-models.php

340 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * BotWriter Image Models Manager
4 *
5 * Handles loading/storing image provider models from a dedicated JSON catalog.
6 *
7 * @package BotWriter
8 */
9
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 if (!defined('BOTWRITER_IMAGE_MODELS_OPTION')) {
15 define('BOTWRITER_IMAGE_MODELS_OPTION', 'botwriter_image_models_data');
16 }
17
18 /**
19 * Fallback catalog used when JSON file is unavailable.
20 *
21 * @return array
22 */
23 function botwriter_get_image_models_fallback_catalog() {
24 return array(
25 'version' => '1.0.0',
26 'updated' => gmdate('Y-m-d'),
27 'providers' => array(
28 'dalle' => array(
29 'default' => 'gpt-image-1',
30 'groups' => array(
31 'GPT Image' => array(
32 'gpt-image-1' => 'GPT Image 1',
33 ),
34 ),
35 'all_models' => array(),
36 ),
37 'gemini' => array(
38 'default' => 'gemini-3.1-flash-lite-image',
39 'groups' => array(
40 'Gemini Image (Stable)' => array(
41 'gemini-3.1-flash-lite-image' => 'Gemini 3.1 Flash Lite Image',
42 'gemini-3.1-flash-image' => 'Gemini 3.1 Flash Image',
43 'gemini-3-pro-image' => 'Gemini 3 Pro Image',
44 'gemini-2.5-flash-image' => 'Gemini 2.5 Flash Image (legacy)',
45 ),
46 'Legacy aliases (auto-migrated)' => array(
47 'gemini-3.1-flash-image-preview' => 'Gemini 3.1 Flash Image Preview (legacy alias)',
48 'gemini-3-pro-image-preview' => 'Gemini 3 Pro Image Preview (legacy alias)',
49 'gemini-2.5-flash-image-preview' => 'Gemini 2.5 Flash Image Preview (legacy alias)',
50 ),
51 ),
52 'all_models' => array(),
53 ),
54 'fal' => array(
55 'default' => 'fal-ai/flux-pro/v1.1',
56 'groups' => array(
57 'Fal.ai' => array(
58 'fal-ai/flux-pro/v1.1' => 'Flux Pro v1.1',
59 ),
60 ),
61 'all_models' => array(),
62 ),
63 'replicate' => array(
64 'default' => 'black-forest-labs/flux-1.1-pro',
65 'groups' => array(
66 'Replicate' => array(
67 'black-forest-labs/flux-1.1-pro' => 'Flux 1.1 Pro',
68 ),
69 ),
70 'all_models' => array(),
71 ),
72 'stability' => array(
73 'default' => 'sd3.5-large-turbo',
74 'groups' => array(
75 'Stability' => array(
76 'sd3.5-large-turbo' => 'SD 3.5 Large Turbo',
77 ),
78 ),
79 'all_models' => array(),
80 ),
81 'cloudflare' => array(
82 'default' => 'flux-1-schnell',
83 'groups' => array(
84 'Cloudflare' => array(
85 'flux-1-schnell' => 'FLUX.1 Schnell',
86 ),
87 ),
88 'all_models' => array(),
89 ),
90 'stockphoto' => array(
91 'default' => 'random',
92 'groups' => array(
93 'Stock Photo' => array(
94 'random' => 'Random (auto rotate)',
95 'pixabay' => 'Pixabay',
96 'pexels' => 'Pexels',
97 'unsplash' => 'Unsplash',
98 'openverse' => 'Openverse',
99 ),
100 ),
101 'all_models' => array(),
102 ),
103 'none' => array(
104 'default' => 'none',
105 'groups' => array(
106 'Disabled' => array(
107 'none' => 'No Image Generation',
108 ),
109 ),
110 'all_models' => array(),
111 ),
112 ),
113 );
114 }
115
116 /**
117 * Load default image models from JSON file.
118 *
119 * @return array
120 */
121 function botwriter_load_default_image_models() {
122 $json_file = plugin_dir_path(dirname(__FILE__)) . 'image-models-default.json';
123
124 if (!file_exists($json_file)) {
125 return botwriter_get_image_models_fallback_catalog();
126 }
127
128 $json_content = file_get_contents($json_file);
129 if ($json_content === false) {
130 return botwriter_get_image_models_fallback_catalog();
131 }
132
133 $models_data = json_decode($json_content, true);
134 if (json_last_error() !== JSON_ERROR_NONE || !is_array($models_data)) {
135 return botwriter_get_image_models_fallback_catalog();
136 }
137
138 if (empty($models_data['providers']) || !is_array($models_data['providers'])) {
139 return botwriter_get_image_models_fallback_catalog();
140 }
141
142 return $models_data;
143 }
144
145 /**
146 * Get persisted image models data, initialized from JSON if needed.
147 *
148 * @return array
149 */
150 function botwriter_get_image_models_data() {
151 $models_data = get_option(BOTWRITER_IMAGE_MODELS_OPTION);
152
153 if (empty($models_data) || !is_array($models_data) || empty($models_data['providers'])) {
154 $models_data = botwriter_load_default_image_models();
155 update_option(BOTWRITER_IMAGE_MODELS_OPTION, $models_data);
156 }
157
158 return $models_data;
159 }
160
161 /**
162 * Reset image models to default catalog from JSON.
163 *
164 * @return bool True on success.
165 */
166 function botwriter_reset_image_models_to_default() {
167 delete_option(BOTWRITER_IMAGE_MODELS_OPTION);
168
169 $models_data = botwriter_get_image_models_data();
170 return is_array($models_data) && !empty($models_data['providers']);
171 }
172
173 /**
174 * Get grouped image models for a provider.
175 *
176 * @param string $provider Provider slug.
177 * @return array
178 */
179 function botwriter_get_provider_image_models($provider) {
180 $provider = sanitize_key((string) $provider);
181 $models_data = botwriter_get_image_models_data();
182
183 if (empty($models_data['providers'][$provider]['groups']) || !is_array($models_data['providers'][$provider]['groups'])) {
184 return array();
185 }
186
187 return $models_data['providers'][$provider]['groups'];
188 }
189
190 /**
191 * Get a flattened map of image models for a provider.
192 *
193 * @param string $provider Provider slug.
194 * @return array
195 */
196 function botwriter_get_provider_image_models_flat($provider) {
197 $grouped = botwriter_get_provider_image_models($provider);
198 if (empty($grouped) || !is_array($grouped)) {
199 return array();
200 }
201
202 $flat = array();
203 foreach ($grouped as $group_models) {
204 if (!is_array($group_models)) {
205 continue;
206 }
207
208 foreach ($group_models as $model_id => $model_label) {
209 $flat[(string) $model_id] = (string) $model_label;
210 }
211 }
212
213 return $flat;
214 }
215
216 /**
217 * Get provider default image model.
218 *
219 * @param string $provider Provider slug.
220 * @return string
221 */
222 function botwriter_get_provider_default_image_model($provider) {
223 $provider = sanitize_key((string) $provider);
224 $models_data = botwriter_get_image_models_data();
225
226 if (!empty($models_data['providers'][$provider]['default'])) {
227 return (string) $models_data['providers'][$provider]['default'];
228 }
229
230 return '';
231 }
232
233 /**
234 * Normalize an image model against the provider catalog.
235 *
236 * @param string $provider Provider slug.
237 * @param string $model Model value to normalize.
238 * @return string
239 */
240 function botwriter_normalize_image_model($provider, $model) {
241 $provider = sanitize_key((string) $provider);
242 $default_model = (string) botwriter_get_provider_default_image_model($provider);
243
244 if ($provider === 'stockphoto') {
245 $model = sanitize_key((string) $model);
246 $valid_stock_models = array('pixabay', 'pexels', 'unsplash', 'openverse', 'random');
247
248 if (in_array($model, $valid_stock_models, true)) {
249 return $model;
250 }
251
252 return 'random';
253 }
254 if ($provider === 'none') {
255 return 'none';
256 }
257
258 if ($default_model === '') {
259 $default_model = 'gpt-image-1';
260 }
261
262 $model = trim((string) $model);
263 if ($model === '') {
264 return $default_model;
265 }
266
267 if ($provider === 'gemini') {
268 $gemini_aliases = array(
269 'gemini-2.0-flash-preview-image-generation' => 'gemini-2.5-flash-image',
270 'gemini-2.0-flash-exp-image-generation' => 'gemini-2.5-flash-image',
271 'gemini-2.0-flash-image' => 'gemini-2.5-flash-image',
272 'gemini-2.5-flash-image-preview' => 'gemini-2.5-flash-image',
273 'gemini-3.1-flash-lite-image-preview' => 'gemini-3.1-flash-lite-image',
274 'gemini-3.1-flash-image-preview' => 'gemini-3.1-flash-image',
275 'gemini-3-pro-image-preview' => 'gemini-3-pro-image',
276 );
277
278 $model_lc = strtolower($model);
279 if (isset($gemini_aliases[$model_lc])) {
280 $model = $gemini_aliases[$model_lc];
281 } else {
282 $model = $model_lc;
283 }
284 }
285
286 if ($provider === 'stability') {
287 $model_lc = strtolower($model);
288 $stability_aliases = array(
289 'sd3-large-turbo' => 'sd3.5-large-turbo',
290 'sd3-large' => 'sd3.5-large',
291 'sd3-medium' => 'sd3.5-medium',
292 );
293
294 if (isset($stability_aliases[$model_lc])) {
295 $model = $stability_aliases[$model_lc];
296 }
297 }
298
299 if ($provider === 'cloudflare') {
300 $model_lc = strtolower($model);
301 $cloudflare_aliases = array(
302 '@cf/black-forest-labs/flux-1-schnell' => 'flux-1-schnell',
303 '@cf/black-forest-labs/flux-2-dev' => 'flux-2-dev',
304 '@cf/black-forest-labs/flux-2-klein-4b' => 'flux-2-klein-4b',
305 '@cf/black-forest-labs/flux-2-klein-9b' => 'flux-2-klein-9b',
306 '@cf/leonardo/lucid-origin' => 'lucid-origin',
307 '@cf/leonardo/phoenix-1.0' => 'phoenix-1.0',
308 '@cf/bytedance/stable-diffusion-xl-lightning' => 'stable-diffusion-xl-lightning',
309 '@cf/stabilityai/stable-diffusion-xl-base-1.0' => 'stable-diffusion-xl-base-1.0',
310 '@cf/lykon/dreamshaper-8-lcm' => 'dreamshaper-8-lcm',
311 );
312
313 if (isset($cloudflare_aliases[$model_lc])) {
314 $model = $cloudflare_aliases[$model_lc];
315 }
316 }
317
318 $valid_models = array_keys(botwriter_get_provider_image_models_flat($provider));
319 if (!empty($valid_models) && in_array($model, $valid_models, true)) {
320 return $model;
321 }
322
323 return $default_model;
324 }
325
326 /**
327 * Return option name used to store the model for an image provider.
328 *
329 * @param string $provider Provider slug.
330 * @return string
331 */
332 function botwriter_get_image_model_option_name($provider) {
333 $provider = sanitize_key((string) $provider);
334 if ($provider === 'gemini') {
335 return 'botwriter_gemini_image_model';
336 }
337
338 return "botwriter_{$provider}_model";
339 }
340