qwen-api-proxy 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +49 -0
- package/LICENSE +21 -0
- package/README.md +2054 -0
- package/bin/qwen-api-proxy.js +414 -0
- package/index.js +444 -0
- package/package.json +85 -0
- package/src/Authorization.txt +17 -0
- package/src/AvailableModels.txt +26 -0
- package/src/api/chat.js +1392 -0
- package/src/api/chatHistory.js +344 -0
- package/src/api/fileUpload.js +182 -0
- package/src/api/imageGeneration.js +459 -0
- package/src/api/modelMapping.js +274 -0
- package/src/api/routes.js +2160 -0
- package/src/api/tokenManager.js +382 -0
- package/src/browser/auth.js +171 -0
- package/src/browser/browser.js +233 -0
- package/src/browser/session.js +134 -0
- package/src/config.js +116 -0
- package/src/logger/index.js +89 -0
- package/src/utils/accountSetup.js +153 -0
- package/src/utils/botSettings.js +231 -0
- package/src/utils/permissionChecker.js +205 -0
- package/src/utils/prompt.js +11 -0
- package/src/utils/proxy.js +255 -0
- package/src/utils/telegramBot.js +2977 -0
- package/src/utils/telegramNotifier.js +94 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
// imageGeneration.js - Модуль для генерации изображений через Qwen Image API
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { logInfo, logError, logDebug } from '../logger/index.js';
|
|
4
|
+
import { sendMessage } from './chat.js';
|
|
5
|
+
import { uploadFileToQwen } from './fileUpload.js';
|
|
6
|
+
import { IMAGE_GENERATION_MODE, DASHSCOPE_API_KEY } from '../config.js';
|
|
7
|
+
|
|
8
|
+
const DASHSCOPE_API_BASE = 'https://dashscope-intl.aliyuncs.com/api/v1';
|
|
9
|
+
|
|
10
|
+
// Модели для генерации изображений
|
|
11
|
+
const IMAGE_GENERATION_MODELS = [
|
|
12
|
+
'qwen-image-max',
|
|
13
|
+
'qwen-image-plus',
|
|
14
|
+
'qwen-image',
|
|
15
|
+
'wan2.6-t2i',
|
|
16
|
+
'wan2.5-t2i-preview',
|
|
17
|
+
'wan2.2-t2i-flash'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Генерация изображения по текстовому описанию
|
|
22
|
+
* @param {string} prompt - Текстовое описание изображения
|
|
23
|
+
* @param {string} model - Модель для генерации
|
|
24
|
+
* @param {object} options - Дополнительные параметры
|
|
25
|
+
* @returns {Promise<object>} - Результат генерации
|
|
26
|
+
*/
|
|
27
|
+
export async function generateImage(prompt, model = 'qwen-image-plus', options = {}) {
|
|
28
|
+
// Определяем режим генерации
|
|
29
|
+
const mode = IMAGE_GENERATION_MODE;
|
|
30
|
+
|
|
31
|
+
if (mode === 'browser') {
|
|
32
|
+
logInfo('🎨 Генерация изображения через browser mode...');
|
|
33
|
+
return generateImageViaBrowser(prompt, model, options);
|
|
34
|
+
} else {
|
|
35
|
+
logInfo('🎨 Генерация изображения через DashScope API...');
|
|
36
|
+
return generateImageViaDashScope(prompt, model, options);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Генерация изображения через DashScope API (прямой вызов)
|
|
42
|
+
*/
|
|
43
|
+
async function generateImageViaDashScope(prompt, model = 'qwen-image-plus', options = {}) {
|
|
44
|
+
const apiKey = process.env.DASHSCOPE_API_KEY;
|
|
45
|
+
|
|
46
|
+
if (!apiKey) {
|
|
47
|
+
logError('API ключ DASHSCOPE_API_KEY не установлен');
|
|
48
|
+
return {
|
|
49
|
+
error: 'API ключ DASHSCOPE_API_KEY не установлен. Пожалуйста, настройте переменную окружения.'
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
logInfo(`Генерация изображения через ${model}...`);
|
|
55
|
+
logDebug(`Prompt: ${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}`);
|
|
56
|
+
|
|
57
|
+
const payload = {
|
|
58
|
+
model: model,
|
|
59
|
+
input: {
|
|
60
|
+
prompt: prompt,
|
|
61
|
+
negative_prompt: options.negativePrompt || ' '
|
|
62
|
+
},
|
|
63
|
+
parameters: {
|
|
64
|
+
size: options.size || '1024*1024',
|
|
65
|
+
n: options.n || 1,
|
|
66
|
+
prompt_extend: options.promptExtend !== false,
|
|
67
|
+
watermark: options.watermark || false
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Если есть изображение для image-to-image
|
|
72
|
+
if (options.imagePath) {
|
|
73
|
+
logInfo(`📸 Image-to-image mode: загружаем файл ${options.imagePath}`);
|
|
74
|
+
|
|
75
|
+
// Загружаем файл в Qwen и получаем URL
|
|
76
|
+
const uploadResult = await uploadFileToQwen(options.imagePath);
|
|
77
|
+
|
|
78
|
+
// Проверяем успешность загрузки
|
|
79
|
+
if (!uploadResult || uploadResult.success === false) {
|
|
80
|
+
const errorMsg = uploadResult?.error || 'Unknown error';
|
|
81
|
+
logError(`❌ Ошибка загрузки файла: ${errorMsg}`);
|
|
82
|
+
throw new Error(`Не удалось загрузить изображение: ${errorMsg}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (uploadResult.file_url || uploadResult.url) {
|
|
86
|
+
const fileUrl = uploadResult.file_url || uploadResult.url;
|
|
87
|
+
logInfo(`✅ Файл загружен: ${fileUrl}`);
|
|
88
|
+
payload.input.image_url = fileUrl;
|
|
89
|
+
} else {
|
|
90
|
+
logError('❌ URL файла не найден в результате загрузки:', uploadResult);
|
|
91
|
+
throw new Error('Не удалось получить URL загруженного изображения');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Асинхронный запрос для Wan моделей
|
|
96
|
+
const isWanModel = model.startsWith('wan');
|
|
97
|
+
const endpoint = isWanModel
|
|
98
|
+
? `${DASHSCOPE_API_BASE}/services/aigc/text2image/image-synthesis`
|
|
99
|
+
: `${DASHSCOPE_API_BASE}/services/aigc/text2image/image-synthesis`;
|
|
100
|
+
|
|
101
|
+
const response = await axios.post(endpoint, payload, {
|
|
102
|
+
headers: {
|
|
103
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
104
|
+
'Content-Type': 'application/json',
|
|
105
|
+
'X-DashScope-Async': isWanModel ? 'enable' : undefined
|
|
106
|
+
},
|
|
107
|
+
timeout: 120000
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const data = response.data;
|
|
111
|
+
|
|
112
|
+
// Асинхронный режим - получаем task_id и опрашиваем статус
|
|
113
|
+
if (data.output?.task_id) {
|
|
114
|
+
logInfo(`Задача создана: ${data.output.task_id}`);
|
|
115
|
+
return await pollTaskStatus(data.output.task_id, apiKey);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Синхронный режим - сразу получаем результат
|
|
119
|
+
if (data.output?.results && data.output.results.length > 0) {
|
|
120
|
+
const imageUrl = data.output.results[0].url;
|
|
121
|
+
logInfo(`Изображение сгенерировано: ${imageUrl}`);
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
imageUrl: imageUrl,
|
|
125
|
+
taskId: data.output.task_id,
|
|
126
|
+
model: model,
|
|
127
|
+
prompt: prompt
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
error: 'Неожиданный формат ответа от API',
|
|
133
|
+
rawData: data
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
} catch (error) {
|
|
137
|
+
logError('Ошибка при генерации изображения', error);
|
|
138
|
+
return {
|
|
139
|
+
error: error.response?.data?.message || error.message || 'Неизвестная ошибка'
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Генерация изображения через браузер (аналогично генерации текста)
|
|
146
|
+
* Использует Qwen Chat API с chat_type='t2i'
|
|
147
|
+
*/
|
|
148
|
+
async function generateImageViaBrowser(prompt, model = 'qwen-image-plus', options = {}) {
|
|
149
|
+
try {
|
|
150
|
+
logInfo(`🖼️ Browser mode: генерация изображения через ${model}...`);
|
|
151
|
+
logDebug(`Prompt: ${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}`);
|
|
152
|
+
|
|
153
|
+
// Подготавливаем файлы если есть imagePath
|
|
154
|
+
let files = null;
|
|
155
|
+
if (options.imagePath) {
|
|
156
|
+
logInfo(`📸 Image-to-image mode: загружаем файл ${options.imagePath}`);
|
|
157
|
+
|
|
158
|
+
// Загружаем файл в Qwen и получаем URL
|
|
159
|
+
const uploadResult = await uploadFileToQwen(options.imagePath);
|
|
160
|
+
|
|
161
|
+
// Проверяем успешность загрузки
|
|
162
|
+
if (!uploadResult || uploadResult.success === false) {
|
|
163
|
+
const errorMsg = uploadResult?.error || 'Unknown error';
|
|
164
|
+
logError(`❌ Ошибка загрузки файла: ${errorMsg}`);
|
|
165
|
+
throw new Error(`Не удалось загрузить изображение: ${errorMsg}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (uploadResult.file_url || uploadResult.url) {
|
|
169
|
+
const fileUrl = uploadResult.file_url || uploadResult.url;
|
|
170
|
+
logInfo(`✅ Файл загружен: ${fileUrl}`);
|
|
171
|
+
// Формат файла для API: { url: '...' }
|
|
172
|
+
files = [{ url: fileUrl }];
|
|
173
|
+
} else {
|
|
174
|
+
logError('❌ URL файла не найден в результате загрузки:', uploadResult);
|
|
175
|
+
throw new Error('Не удалось получить URL загруженного изображения');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Используем sendMessage с chatType='t2i' (text-to-image)
|
|
180
|
+
const result = await sendMessage(
|
|
181
|
+
prompt,
|
|
182
|
+
model,
|
|
183
|
+
null, // chatId - будет создан автоматически
|
|
184
|
+
null, // parentId
|
|
185
|
+
files, // files - изображение для image-to-image
|
|
186
|
+
null, // tools
|
|
187
|
+
null, // toolChoice
|
|
188
|
+
null, // systemMessage
|
|
189
|
+
't2i', // chatType - text to image
|
|
190
|
+
options.size || '1024*1024', // size
|
|
191
|
+
true // waitForCompletion
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Проверяем результат
|
|
195
|
+
if (result.error) {
|
|
196
|
+
logError('❌ Ошибка генерации изображения (browser mode)', result.error);
|
|
197
|
+
return {
|
|
198
|
+
error: result.error,
|
|
199
|
+
details: result.details || 'Browser mode generation failed'
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Извлекаем URL изображения из ответа
|
|
204
|
+
let imageUrl = null;
|
|
205
|
+
|
|
206
|
+
// Проверяем разные форматы ответа
|
|
207
|
+
if (result.imageUrl) {
|
|
208
|
+
imageUrl = result.imageUrl;
|
|
209
|
+
} else if (result.choices?.[0]?.message?.content) {
|
|
210
|
+
const content = result.choices[0].message.content;
|
|
211
|
+
// Если контент - это URL изображения
|
|
212
|
+
if (content.startsWith('http') || content.startsWith('data:')) {
|
|
213
|
+
imageUrl = content;
|
|
214
|
+
} else {
|
|
215
|
+
// Пытаемся извлечь URL из markdown или JSON
|
|
216
|
+
const urlMatch = content.match(/\[(?:Generated Image)?\]\(([^)]+)\)/);
|
|
217
|
+
if (urlMatch) {
|
|
218
|
+
imageUrl = urlMatch[1];
|
|
219
|
+
} else {
|
|
220
|
+
// Пробуем распарсить как JSON
|
|
221
|
+
try {
|
|
222
|
+
const parsed = JSON.parse(content);
|
|
223
|
+
imageUrl = parsed.url || parsed.image_url || parsed.imageUrl;
|
|
224
|
+
} catch {
|
|
225
|
+
// Не JSON, используем как есть
|
|
226
|
+
logWarn('⚠️ Ответ не содержит явного URL изображения');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!imageUrl) {
|
|
233
|
+
logError('❌ URL изображения не найден в ответе');
|
|
234
|
+
logDebug('Response:', JSON.stringify(result, null, 2));
|
|
235
|
+
|
|
236
|
+
// Логируем структуру ошибки для отладки
|
|
237
|
+
if (result.error) logDebug('result.error exists:', JSON.stringify(result.error));
|
|
238
|
+
if (result.errorBody) logDebug('result.errorBody exists:', result.errorBody.substring(0, 200));
|
|
239
|
+
if (result.details) logDebug('result.details exists:', result.details.substring(0, 200));
|
|
240
|
+
|
|
241
|
+
// Проверяем, есть ли в ответе реальная ошибка
|
|
242
|
+
let errorMessage = 'Image URL not found in response';
|
|
243
|
+
|
|
244
|
+
// Проверяем формат ошибки API (прямое поле error)
|
|
245
|
+
if (result.error) {
|
|
246
|
+
// API вернул ошибку
|
|
247
|
+
if (result.error.code) {
|
|
248
|
+
errorMessage = `API Error: ${result.error.code}`;
|
|
249
|
+
if (result.error.details) {
|
|
250
|
+
errorMessage += ` - ${result.error.details}`;
|
|
251
|
+
}
|
|
252
|
+
} else if (typeof result.error === 'string') {
|
|
253
|
+
errorMessage = result.error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Проверяем errorBody (JSON строка с ошибкой)
|
|
257
|
+
else if (result.errorBody) {
|
|
258
|
+
try {
|
|
259
|
+
const errorData = JSON.parse(result.errorBody);
|
|
260
|
+
if (errorData.error) {
|
|
261
|
+
if (errorData.error.code) {
|
|
262
|
+
errorMessage = `API Error: ${errorData.error.code}`;
|
|
263
|
+
if (errorData.error.details) {
|
|
264
|
+
errorMessage += ` - ${errorData.error.details}`;
|
|
265
|
+
}
|
|
266
|
+
} else if (typeof errorData.error === 'string') {
|
|
267
|
+
errorMessage = errorData.error;
|
|
268
|
+
}
|
|
269
|
+
} else if (errorData.code) {
|
|
270
|
+
// Код ошибки на верхнем уровне
|
|
271
|
+
errorMessage = `API Error: ${errorData.code}`;
|
|
272
|
+
if (errorData.detail || errorData.details) {
|
|
273
|
+
errorMessage += ` - ${errorData.detail || errorData.details}`;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
// Не JSON, используем как есть
|
|
278
|
+
if (typeof result.errorBody === 'string') {
|
|
279
|
+
errorMessage = result.errorBody.substring(0, 200);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Проверяем details (JSON строка с ошибкой из handleApiError)
|
|
284
|
+
else if (result.details && typeof result.details === 'string') {
|
|
285
|
+
try {
|
|
286
|
+
const errorData = JSON.parse(result.details);
|
|
287
|
+
if (errorData.error) {
|
|
288
|
+
if (errorData.error.code) {
|
|
289
|
+
errorMessage = `API Error: ${errorData.error.code}`;
|
|
290
|
+
if (errorData.error.details) {
|
|
291
|
+
errorMessage += ` - ${errorData.error.details}`;
|
|
292
|
+
}
|
|
293
|
+
} else if (typeof errorData.error === 'string') {
|
|
294
|
+
errorMessage = errorData.error;
|
|
295
|
+
}
|
|
296
|
+
} else if (errorData.code) {
|
|
297
|
+
// Код ошибки на верхнем уровне
|
|
298
|
+
errorMessage = `API Error: ${errorData.code}`;
|
|
299
|
+
if (errorData.detail || errorData.details) {
|
|
300
|
+
errorMessage += ` - ${errorData.detail || errorData.details}`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} catch {
|
|
304
|
+
// Не JSON, используем как есть
|
|
305
|
+
errorMessage = result.details.substring(0, 200);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Проверяем формат ошибки в choices
|
|
309
|
+
else if (result.choices?.[0]?.message?.content) {
|
|
310
|
+
const content = result.choices[0].message.content;
|
|
311
|
+
// Пытаемся распарсить как JSON для поиска ошибки
|
|
312
|
+
try {
|
|
313
|
+
const parsed = JSON.parse(content);
|
|
314
|
+
if (parsed.error) {
|
|
315
|
+
if (parsed.error.code) {
|
|
316
|
+
errorMessage = `API Error: ${parsed.error.code}`;
|
|
317
|
+
if (parsed.error.details) {
|
|
318
|
+
errorMessage += ` - ${parsed.error.details}`;
|
|
319
|
+
}
|
|
320
|
+
} else if (typeof parsed.error === 'string') {
|
|
321
|
+
errorMessage = parsed.error;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
} catch {
|
|
325
|
+
// Не JSON, оставляем стандартное сообщение
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
error: errorMessage,
|
|
331
|
+
rawResponse: result
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
logInfo(`✅ Изображение сгенерировано (browser mode): ${imageUrl}`);
|
|
336
|
+
return {
|
|
337
|
+
success: true,
|
|
338
|
+
imageUrl: imageUrl,
|
|
339
|
+
model: model,
|
|
340
|
+
prompt: prompt,
|
|
341
|
+
chatId: result.chatId,
|
|
342
|
+
parentId: result.parentId
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
} catch (error) {
|
|
346
|
+
logError('❌ Ошибка при генерации изображения (browser mode)', error);
|
|
347
|
+
return {
|
|
348
|
+
error: error.message || 'Unknown error in browser mode'
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Опрос статуса задачи генерации изображения
|
|
355
|
+
* @param {string} taskId - ID задачи
|
|
356
|
+
* @param {string} apiKey - API ключ
|
|
357
|
+
* @returns {Promise<object>} - Результат генерации
|
|
358
|
+
*/
|
|
359
|
+
async function pollTaskStatus(taskId, apiKey) {
|
|
360
|
+
const maxAttempts = 60;
|
|
361
|
+
const pollInterval = 2000; // 2 секунды
|
|
362
|
+
|
|
363
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
364
|
+
try {
|
|
365
|
+
const response = await axios.get(
|
|
366
|
+
`${DASHSCOPE_API_BASE}/tasks/${taskId}`,
|
|
367
|
+
{
|
|
368
|
+
headers: {
|
|
369
|
+
'Authorization': `Bearer ${apiKey}`
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const task = response.data;
|
|
375
|
+
const taskStatus = task.output?.task_status;
|
|
376
|
+
|
|
377
|
+
logDebug(`Статус задачи ${taskId}: ${taskStatus} (попытка ${attempt + 1}/${maxAttempts})`);
|
|
378
|
+
|
|
379
|
+
if (taskStatus === 'SUCCEEDED') {
|
|
380
|
+
const imageUrl = task.output?.results?.[0]?.url;
|
|
381
|
+
if (imageUrl) {
|
|
382
|
+
logInfo(`Изображение сгенерировано: ${imageUrl}`);
|
|
383
|
+
return {
|
|
384
|
+
success: true,
|
|
385
|
+
imageUrl: imageUrl,
|
|
386
|
+
taskId: taskId,
|
|
387
|
+
model: task.input?.model || 'unknown'
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
return { error: 'Изображение не найдено в результате' };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (taskStatus === 'FAILED' || taskStatus === 'CANCELLED') {
|
|
394
|
+
return {
|
|
395
|
+
error: `Задача завершена со статусом: ${taskStatus}`,
|
|
396
|
+
message: task.output?.message || 'Неизвестная ошибка'
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// PENDING или RUNNING - продолжаем опрос
|
|
401
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
402
|
+
|
|
403
|
+
} catch (error) {
|
|
404
|
+
logError(`Ошибка при опросе задачи ${taskId}`, error);
|
|
405
|
+
if (attempt === maxAttempts - 1) {
|
|
406
|
+
return { error: `Ошибка опроса: ${error.message}` };
|
|
407
|
+
}
|
|
408
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return { error: 'Превышено время ожидания генерации изображения' };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Получить список доступных моделей генерации изображений
|
|
417
|
+
* @returns {string[]} - Список моделей
|
|
418
|
+
*/
|
|
419
|
+
export function getAvailableImageModels() {
|
|
420
|
+
return IMAGE_GENERATION_MODELS;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Проверка доступности API генерации изображений
|
|
425
|
+
* @returns {Promise<boolean>} - Статус доступности
|
|
426
|
+
*/
|
|
427
|
+
export async function checkImageApiAvailability() {
|
|
428
|
+
const mode = IMAGE_GENERATION_MODE;
|
|
429
|
+
|
|
430
|
+
// Browser mode всегда доступен (если браузер работает)
|
|
431
|
+
if (mode === 'browser') {
|
|
432
|
+
logDebug('🖼️ Browser mode: проверка через статус браузера');
|
|
433
|
+
const { getBrowserContext, getAuthenticationStatus } = await import('../browser/browser.js');
|
|
434
|
+
const browserContext = getBrowserContext();
|
|
435
|
+
const isAuthenticated = getAuthenticationStatus();
|
|
436
|
+
return !!(browserContext && isAuthenticated);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// DashScope mode: проверяем API ключ
|
|
440
|
+
const apiKey = DASHSCOPE_API_KEY;
|
|
441
|
+
|
|
442
|
+
if (!apiKey) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
try {
|
|
447
|
+
// Простой тестовый запрос для проверки API
|
|
448
|
+
await axios.get(`${DASHSCOPE_API_BASE}/models`, {
|
|
449
|
+
headers: {
|
|
450
|
+
'Authorization': `Bearer ${apiKey}`
|
|
451
|
+
},
|
|
452
|
+
timeout: 5000
|
|
453
|
+
});
|
|
454
|
+
return true;
|
|
455
|
+
} catch (error) {
|
|
456
|
+
logDebug(`API генерации изображений недоступен: ${error.message}`);
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
}
|