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:
Ник (Claude)
2026-06-11 15:44:33 +03:00
parent 08086650fc
commit fe7af0b3b5
5 changed files with 56 additions and 97 deletions
+12 -36
View File
@@ -85,62 +85,38 @@ ${style.image_prompt_instructions ? `\nChannel visual guidelines: ${style.image_
Composition: 16:9 wide format, balanced, suitable for social media.
Strictly: no text, no letters, no logos, no faces of real people.`;
// RouterAI /responses (primary) → Nyxos /images/generations (fallback)
// standard: gpt-5-image-mini + low quality (₽0.20/картинка)
// hd: gpt-5.4-image-2 + medium quality (₽2.96/картинка) — для текста на картинках
const isHD = channel.image_quality === 'hd';
const model = isHD
? 'openai/gpt-5.4-image-2'
: (config.ai.routeraiImageModel || 'openai/gpt-5-image-mini');
const imgQuality = isHD ? 'medium' : 'low';
// Единственный провайдер: routerai /responses + gpt-5-image-mini
// Цена: ~₽2.72/картинка. quality параметр не работает, всегда high.
const model = config.ai.routeraiModel || 'openai/gpt-5-image-mini';
async function tryRouterAI() {
async function generateViaRouterAI() {
const started = Date.now();
try {
const res = await axios.post(`${config.ai.routeraiBaseUrl}/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: imgQuality }],
tools: [{ type: 'image_generation' }],
tool_choice: { type: 'image_generation' },
}, { headers: { Authorization: `Bearer ${config.ai.routeraiApiKey}` }, timeout: 120_000 });
const imgCall = (res.data?.output || []).find(o => o.type === 'image_generation_call');
if (!imgCall?.result) throw new Error('No image in RouterAI response');
aiUsage.log({ provider: 'routerai', requestType: 'image_via_responses', model, quality: imgQuality, imageCount: 1, meta: { channel_id: channel.id }, durationMs: Date.now()-started, succeeded: true }).catch(() => {});
aiUsage.log({ provider: 'routerai', requestType: 'image_via_responses', model, imageCount: 1, meta: { channel_id: channel.id }, durationMs: Date.now()-started, succeeded: true }).catch(() => {});
return Buffer.from(imgCall.result, 'base64');
} catch (err) {
aiUsage.log({ provider: 'routerai', requestType: 'image_via_responses', model, quality: imgQuality, imageCount: 1, meta: { channel_id: channel.id }, durationMs: Date.now()-started, succeeded: false, errorMessage: (err.response?.data?.error?.message || err.message || '').slice(0, 500) }).catch(() => {});
throw err;
}
}
async function tryNyxos() {
const m = config.ai.imageModel || 'gpt-image-2';
const started = Date.now();
try {
const res = await axios.post(`${config.ai.imageFallbackBaseUrl}/images/generations`,
{ model: m, prompt: prompt.slice(0, 4000), n: 1, size: '1024x1024', response_format: 'url' },
{ headers: { Authorization: `Bearer ${config.ai.imageFallbackApiKey}` }, timeout: 90_000 }
);
const item = res.data?.data?.[0];
if (!item) throw new Error('No image data');
aiUsage.log({ provider: 'nyxos', requestType: 'image', model: m, imageCount: 1, meta: { channel_id: channel.id }, succeeded: true }).catch(() => {});
if (item.url) { const r = await axios.get(item.url, { responseType: 'arraybuffer', timeout: 60_000 }); return Buffer.from(r.data); }
if (item.b64_json) return Buffer.from(item.b64_json, 'base64');
throw new Error('No url or b64_json');
} catch (err) {
aiUsage.log({ provider: 'nyxos', requestType: 'image', model: m, imageCount: 1, meta: { channel_id: channel.id }, succeeded: false, errorMessage: (err.response?.data?.error?.message || err.message || '').slice(0, 500) }).catch(() => {});
aiUsage.log({ provider: 'routerai', requestType: 'image_via_responses', model, imageCount: 1, meta: { channel_id: channel.id }, durationMs: Date.now()-started, succeeded: false, errorMessage: (err.response?.data?.error?.message || err.message || '').slice(0, 500) }).catch(() => {});
throw err;
}
}
let bytes;
try {
bytes = await tryRouterAI();
bytes = await generateViaRouterAI();
} catch (err) {
const status = err.response?.status;
if (!status || status >= 500) {
console.warn('[postImages] RouterAI failed, trying Nyxos fallback...');
bytes = await tryNyxos();
if (!status || (status >= 500 && status < 600)) {
console.warn(`[postImages] routerai attempt 1 failed (${status||'timeout'}), retry in 10s...`);
await new Promise(r => setTimeout(r, 10_000));
bytes = await generateViaRouterAI(); // бросит если снова упадёт
} else { throw err; }
}