refactor: single image provider — routerai gpt-5-image-mini only
- config: убраны imageBaseUrl/imageFallbackBaseUrl/imageModel (старые провайдеры) Остались только routeraiBaseUrl, routeraiApiKey, routeraiModel - covers.js: единственная цепочка routerai→retry→local SVG Убраны generateCoverViaImageGenerations, ViaResponses (aiprimetech), ViaImagesEndpoint generateCoverViaRouterAI: убран quality параметр (routerai игнорирует) - postImages.js: убраны Nyxos/Aiguoguo, убраны isHD/imgQuality/tryNyxos - aiUsage.js: реальные цены из статистики routerai.ru: gpt-5-image-mini ~₽2.72, всегда 4175 image tokens (high quality) - index.js: лог показывает routerai вместо старого aiguoguo
This commit is contained in:
+25
-36
@@ -271,19 +271,20 @@ async function generateCoverViaImagesEndpoint({ prompt }) {
|
||||
* Более стабильный чем /responses, поддерживает gpt-image-1-mini/gpt-image-2.
|
||||
*/
|
||||
/**
|
||||
* RouterAI — стабильный провайдер через /responses + image_generation tool.
|
||||
* quality: 'low' для постов TG (₽0.25), 'medium' для обложек/VK (₽0.84)
|
||||
* RouterAI /responses + gpt-5-image-mini.
|
||||
* Единственный провайдер картинок. Цена: ~₽2.72/картинка.
|
||||
* quality параметр routerai игнорирует — всегда high (4175 image tokens).
|
||||
*/
|
||||
async function generateCoverViaRouterAI({ prompt, quality = 'medium' }) {
|
||||
async function generateCoverViaRouterAI({ prompt }) {
|
||||
const base = config.ai.routeraiBaseUrl;
|
||||
const key = config.ai.routeraiApiKey;
|
||||
const model = config.ai.routeraiImageModel || 'openai/gpt-5-image-mini';
|
||||
const model = config.ai.routeraiModel || 'openai/gpt-5-image-mini';
|
||||
const started = Date.now();
|
||||
try {
|
||||
const res = await axios.post(`${base}/responses`, {
|
||||
model,
|
||||
input: `Use the image_generation tool to create this illustration. Only call the tool, no text.\n\n${prompt.slice(0, 3000)}`,
|
||||
tools: [{ type: 'image_generation', quality }],
|
||||
tools: [{ type: 'image_generation' }],
|
||||
tool_choice: { type: 'image_generation' },
|
||||
}, {
|
||||
headers: { Authorization: `Bearer ${key}` },
|
||||
@@ -430,43 +431,31 @@ async function generateCover({ articleId, title, tags = [], channelId = null })
|
||||
const prompt = buildCoverPrompt({ title, tags, articleId, channelStyle, rubric: selectedRubric });
|
||||
|
||||
let img;
|
||||
let usedPath = 'images-generations';
|
||||
|
||||
// Цепочка: 1) aiguoguo /images/generations (2 попытки) → 2) aiprimetech /responses → 3) legacy → 4) local SVG
|
||||
// Единственный провайдер картинок: routerai /responses + gpt-5-image-mini
|
||||
// Цена: ~₽2.72/картинка (4175 image tokens, high quality, quality param не работает)
|
||||
try {
|
||||
try {
|
||||
img = await generateCoverViaRouterAI({ prompt });
|
||||
} catch (err) {
|
||||
const status = err.response?.status;
|
||||
// Ретрай при 5xx
|
||||
if (!status || (status >= 500 && status < 600)) {
|
||||
console.warn(`[Cover] routerai attempt 1 failed (${status||'timeout'}), retry in 10s...`);
|
||||
await new Promise(r => setTimeout(r, 10_000));
|
||||
try {
|
||||
img = await generateCoverViaImageGenerations({ prompt });
|
||||
} catch (err) {
|
||||
// Ретрай только при временных ошибках провайдера (5xx)
|
||||
const status = err.response?.status;
|
||||
if (status >= 500 && status < 600) {
|
||||
console.warn(`[Cover] /images/generations ${status} — retry in 12s...`);
|
||||
await new Promise(r => setTimeout(r, 12000));
|
||||
img = await generateCoverViaImageGenerations({ prompt });
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[Cover] /images/generations failed: ${(err.response?.data?.error?.message || err.message).slice(0, 150)}`);
|
||||
try {
|
||||
img = await generateCoverViaResponses({ prompt });
|
||||
usedPath = 'responses';
|
||||
img = await generateCoverViaRouterAI({ prompt });
|
||||
} catch (err2) {
|
||||
console.warn(`[Cover] /responses failed: ${(err2.response?.data?.error?.message || err2.message).slice(0, 150)}`);
|
||||
try {
|
||||
img = await generateCoverViaImagesEndpoint({ prompt });
|
||||
usedPath = 'images-legacy';
|
||||
} catch (err3) {
|
||||
console.warn(`[Cover] legacy failed: ${(err3.response?.data?.error?.message || err3.message).slice(0, 150)}`);
|
||||
throw new Error('all_external_failed');
|
||||
}
|
||||
console.warn(`[Cover] routerai attempt 2 failed: ${(err2.response?.data?.error?.message || err2.message).slice(0, 150)}`);
|
||||
img = null;
|
||||
}
|
||||
} else {
|
||||
console.warn(`[Cover] routerai failed (${status}): ${(err.response?.data?.error?.message || err.message).slice(0, 150)}`);
|
||||
img = null;
|
||||
}
|
||||
} catch (outerErr) {
|
||||
// Все внешние API упали — local SVG
|
||||
console.log(`[Cover] article=${articleId} → local SVG (all external APIs unavailable)`);
|
||||
}
|
||||
if (!img) {
|
||||
// routerai недоступен — fallback на local SVG
|
||||
console.log(`[Cover] article=${articleId} → local SVG (routerai unavailable)`);
|
||||
const localUrl = await localGen.generateLocalCover({ articleId, title, category: tags?.[0] || '' });
|
||||
await query('UPDATE articles SET cover_url=$1, updated_at=NOW() WHERE id=$2', [localUrl, articleId]);
|
||||
return localUrl;
|
||||
|
||||
Reference in New Issue
Block a user