fix: getChannel arg order, postImages via Nyxos /images/generations
generate.js: getChannel(userId, channelId) → getChannel(channelId, userId) channels.js: getChannel alias → getFullChannel postImages.js: убран /responses + gpt-5.5 (не работал на aiprimetech), заменён на Nyxos /images/generations с fallback на aiguoguo
This commit is contained in:
@@ -50,7 +50,7 @@ router.post('/transform', async (req, res) => {
|
||||
if (!channelId || !originalPost || !action) {
|
||||
return res.status(400).json({ error: 'channelId, originalPost, action required' });
|
||||
}
|
||||
const channel = await channelsSvc.getChannel(userId, channelId);
|
||||
const channel = await channelsSvc.getChannel(channelId, userId);
|
||||
if (!channel) return res.status(404).json({ error: 'Channel not found' });
|
||||
|
||||
const ai = require('../services/ai');
|
||||
@@ -69,7 +69,7 @@ router.post('/post-image', async (req, res) => {
|
||||
const userId = parseInt(req.headers['x-user-id']) || null;
|
||||
if (!channelId || !post) return res.status(400).json({ error: 'channelId and post required' });
|
||||
|
||||
const channel = await channelsSvc.getChannel(userId, channelId);
|
||||
const channel = await channelsSvc.getChannel(channelId, userId);
|
||||
if (!channel) return res.status(404).json({ error: 'Channel not found' });
|
||||
|
||||
const { generatePostImage } = require('../services/postImages');
|
||||
@@ -99,7 +99,7 @@ router.post('/topics-ideas', async (req, res) => {
|
||||
const { channelId, count = 7 } = req.body;
|
||||
const userId = parseInt(req.headers['x-user-id']) || null;
|
||||
if (!channelId) return res.status(400).json({ error: 'channelId required' });
|
||||
const channel = await channelsSvc.getChannel(userId, channelId);
|
||||
const channel = await channelsSvc.getChannel(channelId, userId);
|
||||
if (!channel) return res.status(404).json({ error: 'Channel not found' });
|
||||
|
||||
const ai = require('../services/ai');
|
||||
@@ -118,7 +118,7 @@ router.post('/from-url', async (req, res) => {
|
||||
const userId = parseInt(req.headers['x-user-id']) || null;
|
||||
if (!channelId || !url) return res.status(400).json({ error: 'channelId and url required' });
|
||||
|
||||
const channel = await channelsSvc.getChannel(userId, channelId);
|
||||
const channel = await channelsSvc.getChannel(channelId, userId);
|
||||
if (!channel) return res.status(404).json({ error: 'Channel not found' });
|
||||
|
||||
const { generateFromUrl } = require('../services/fromUrl');
|
||||
|
||||
@@ -185,6 +185,7 @@ async function deleteChannel(channelId, userId) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getChannel: getFullChannel, // алиас для обратной совместимости
|
||||
getFullChannel,
|
||||
listChannels,
|
||||
createChannel,
|
||||
|
||||
+34
-49
@@ -85,61 +85,46 @@ ${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.`;
|
||||
|
||||
const model = config.ai.imageModelViaResponses || 'gpt-5.2';
|
||||
const wrappedInput = `Use the image_generation tool to create the following illustration. Do not write any text response, only call the tool.\n\n${prompt}`;
|
||||
// Используем Nyxos /images/generations (первичный провайдер)
|
||||
// с fallback на aiguoguo — тот же путь что для обложек статей
|
||||
const model = config.ai.imageModel || 'gpt-image-2';
|
||||
|
||||
const started = Date.now();
|
||||
let res;
|
||||
try {
|
||||
res = await axios.post(
|
||||
`${config.ai.baseUrl}/responses`,
|
||||
{
|
||||
model,
|
||||
input: wrappedInput,
|
||||
tools: [{ type: 'image_generation' }],
|
||||
tool_choice: { type: 'image_generation' },
|
||||
},
|
||||
{
|
||||
headers: { Authorization: `Bearer ${config.ai.apiKey}` },
|
||||
timeout: 300_000,
|
||||
async function tryProvider(baseUrl, apiKey) {
|
||||
const started = Date.now();
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${baseUrl}/images/generations`,
|
||||
{ model, prompt: prompt.slice(0, 4000), n: 1, size: '1024x1024', response_format: 'url' },
|
||||
{ headers: { Authorization: `Bearer ${apiKey}` }, timeout: 120_000 }
|
||||
);
|
||||
const item = res.data?.data?.[0];
|
||||
if (!item) throw new Error('No image data');
|
||||
aiUsage.log({ provider: aiUsage.providerFromBaseUrl(baseUrl), requestType: 'image', model, 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: aiUsage.providerFromBaseUrl(baseUrl), requestType: 'image', model, imageCount: 1, meta: { channel_id: channel.id }, succeeded: false, errorMessage: (err.response?.data?.error?.message || err.message || '').slice(0, 500) }).catch(() => {});
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
let bytes;
|
||||
try {
|
||||
bytes = await tryProvider(config.ai.imageBaseUrl, config.ai.imageApiKey);
|
||||
} catch (err) {
|
||||
aiUsage.log({
|
||||
provider: aiUsage.providerFromBaseUrl(config.ai.baseUrl),
|
||||
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;
|
||||
const status = err.response?.status;
|
||||
if (!status || status >= 500) {
|
||||
console.warn('[postImages] primary failed, trying fallback...');
|
||||
bytes = await tryProvider(config.ai.imageFallbackBaseUrl, config.ai.imageFallbackApiKey);
|
||||
} else { throw err; }
|
||||
}
|
||||
|
||||
const output = res.data?.output || [];
|
||||
const imgCall = output.find(o => o.type === 'image_generation_call');
|
||||
if (!imgCall?.result) {
|
||||
aiUsage.log({
|
||||
provider: aiUsage.providerFromBaseUrl(config.ai.baseUrl),
|
||||
requestType: 'image_via_responses', model, imageCount: 1,
|
||||
meta: { channel_id: channel.id },
|
||||
durationMs: Date.now() - started, succeeded: false,
|
||||
errorMessage: 'No image generated',
|
||||
}).catch(() => {});
|
||||
throw new Error('No image generated');
|
||||
}
|
||||
|
||||
aiUsage.log({
|
||||
provider: aiUsage.providerFromBaseUrl(config.ai.baseUrl),
|
||||
requestType: 'image_via_responses', model, imageCount: 1,
|
||||
requestId: res.data?.id,
|
||||
meta: { channel_id: channel.id },
|
||||
durationMs: Date.now() - started, succeeded: true,
|
||||
}).catch(() => {});
|
||||
|
||||
const bytes = Buffer.from(imgCall.result, 'base64');
|
||||
const tsKey = `post-${channel.id}-${Date.now()}`;
|
||||
const ext = imgCall.output_format || 'png';
|
||||
const ext = 'png';
|
||||
|
||||
// Оптимизация через sharp если есть
|
||||
let publicUrl;
|
||||
|
||||
Reference in New Issue
Block a user