change file type and fix agents
This commit is contained in:
@@ -15,7 +15,7 @@ const buildingsRouter = require('./buildings');
|
|||||||
const userApartmentsRouter = require('./user_apartments');
|
const userApartmentsRouter = require('./user_apartments');
|
||||||
const avatarRouter = require('./media');
|
const avatarRouter = require('./media');
|
||||||
const supportRouter = require('./supportApi');
|
const supportRouter = require('./supportApi');
|
||||||
const moderateRouter = require('./moderate.ts').default;
|
const moderateRouter = require('./moderate.js');
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.moderationText = void 0;
|
|
||||||
const node_https_1 = require("node:https");
|
|
||||||
const langchain_gigachat_1 = require("langchain-gigachat");
|
|
||||||
const zod_1 = require("zod");
|
|
||||||
const httpsAgent = new node_https_1.Agent({
|
|
||||||
rejectUnauthorized: false,
|
|
||||||
});
|
|
||||||
const llm = new langchain_gigachat_1.GigaChat({
|
|
||||||
credentials: process.env.GIGA_AUTH,
|
|
||||||
temperature: 0.2,
|
|
||||||
model: 'GigaChat-2',
|
|
||||||
httpsAgent,
|
|
||||||
});
|
|
||||||
// возвращаю комментарий + исправленное предложение + булево значение
|
|
||||||
const moderationLlm = llm.withStructuredOutput(zod_1.z.object({
|
|
||||||
comment: zod_1.z.string(),
|
|
||||||
fixedText: zod_1.z.string().optional(),
|
|
||||||
isApproved: zod_1.z.boolean(),
|
|
||||||
}));
|
|
||||||
const moderationText = async (title, body) => {
|
|
||||||
const prompt = `
|
|
||||||
Представь, что ты модерируешь предложения от жильцов многоквартирного дома (это личная инициатива по улучшения,
|
|
||||||
не имеющая отношения к Управляющей компании).
|
|
||||||
|
|
||||||
Заголовок: ${title}
|
|
||||||
Основной текст: ${body}
|
|
||||||
|
|
||||||
Твои задачи:
|
|
||||||
1. Проверь предложение и заголовок на спам.
|
|
||||||
2. Проверь, чтобы заголовок и текст были на одну тему.
|
|
||||||
3. Проверь само предложение пользователя на отсутствие грубой лексики и пошлостей.
|
|
||||||
4. Проверь грамматику.
|
|
||||||
5. Проверь на бессмысленность предложения. Оно не должно содержать только случайные символы.
|
|
||||||
6. Не должно быть рекламы, ссылок и т.д.
|
|
||||||
7. Проверь предложение на информативность, оно не должно быть слишком коротким.
|
|
||||||
8. Предложение должно быть в вежливой форме.
|
|
||||||
|
|
||||||
- Если все правила соблюдены, то предложение принимается!
|
|
||||||
|
|
||||||
Правила написания комментария:
|
|
||||||
- Если предложение отклоняется, верни комментарий со следующей формулировкой:
|
|
||||||
"Предложение отклонено. Причина: (укажи проблему)"
|
|
||||||
Правила написания fixedBody:
|
|
||||||
- Если предложение отклонено, то верни в поле "fixedBody" новый текст, который будет соответствовать правилам.
|
|
||||||
- Если предложение отклонено и содержит запрещённый контент (рекламу, личные данные), удали всю информацию,
|
|
||||||
которая противоречит правилам, и верни в только подходящий фрагмент, сохраняя общий смысл.
|
|
||||||
- Если текст не представляет никакой ценности, возврати в поле "fixedBody" правило,
|
|
||||||
по которому оно не прошло.
|
|
||||||
-Если предложение принимается, то ничего не возвращай в поле fixedBody.
|
|
||||||
`;
|
|
||||||
const result = await moderationLlm.invoke(prompt);
|
|
||||||
return [result.comment, result.fixedText, result.isApproved];
|
|
||||||
};
|
|
||||||
exports.moderationText = moderationText;
|
|
||||||
@@ -35,23 +35,33 @@ export const moderationText = async (title: string, body: string): Promise<[stri
|
|||||||
4. Проверь грамматику.
|
4. Проверь грамматику.
|
||||||
5. Проверь на бессмысленность предложения. Оно не должно содержать только случайные символы.
|
5. Проверь на бессмысленность предложения. Оно не должно содержать только случайные символы.
|
||||||
6. Не должно быть рекламы, ссылок и т.д.
|
6. Не должно быть рекламы, ссылок и т.д.
|
||||||
7. Проверь предложение на информативность, оно не должно быть слишком коротким.
|
7. Проверь предложение на информативность, предложение не может быть коротким, оно должно ясно отражжать суть инициативы.
|
||||||
8. Предложение должно быть в вежливой форме.
|
8. Предложение должно быть в вежливой форме.
|
||||||
|
|
||||||
- Если все правила соблюдены, то предложение принимается!
|
- Если все правила соблюдены, то предложение принимается!
|
||||||
|
|
||||||
|
- Если предложение отклонено, всегда пиши комментарий и fixedText!
|
||||||
|
|
||||||
Правила написания комментария:
|
Правила написания комментария:
|
||||||
- Если предложение отклоняется, верни комментарий со следующей формулировкой:
|
- Если предложение отклоняется, пиши комментарий со следующей формулировкой:
|
||||||
"Предложение отклонено. Причина: (укажи проблему)"
|
"Предложение отклонено. Причина: (укажи проблему)"
|
||||||
Правила написания fixedBody:
|
|
||||||
- Если предложение отклонено, то верни в поле "fixedBody" новый текст, который будет соответствовать правилам.
|
Правила написания fixedText:
|
||||||
|
- Если предложение отклонено, то верни в поле "fixedText" измененный текст, который будет соответствовать правилам.
|
||||||
- Если предложение отклонено и содержит запрещённый контент (рекламу, личные данные), удали всю информацию,
|
- Если предложение отклонено и содержит запрещённый контент (рекламу, личные данные), удали всю информацию,
|
||||||
которая противоречит правилам, и верни в только подходящий фрагмент, сохраняя общий смысл.
|
которая противоречит правилам, и верни в только подходящий фрагмент, сохраняя общий смысл.
|
||||||
- Если текст не представляет никакой ценности, возврати в поле "fixedBody" правило,
|
- Если текст не представляет никакой ценности, возврати в поле "fixedText" правило,
|
||||||
по которому оно не прошло.
|
по которому оно не прошло.
|
||||||
-Если предложение принимается, то ничего не возвращай в поле fixedBody.
|
-Если предложение принимается, то ничего не возвращай в поле fixedText.
|
||||||
`
|
`
|
||||||
|
|
||||||
const result = await moderationLlm.invoke(prompt);
|
const result = await moderationLlm.invoke(prompt);
|
||||||
|
console.log(result)
|
||||||
|
// Дополнительная проверка
|
||||||
|
if(!result.isApproved && result.comment.trim() === '' && result.fixedText.trim() === '') {
|
||||||
|
result.comment = 'Предложение отклонено. Причина: несоблюдение требований к оформлению или содержанию.',
|
||||||
|
result.fixedText = body
|
||||||
|
}
|
||||||
|
|
||||||
return [result.comment, result.fixedText, result.isApproved];
|
return [result.comment, result.fixedText, result.isApproved];
|
||||||
};
|
};
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.generatePicture = exports.llm = void 0;
|
|
||||||
const gigachat_1 = require("gigachat");
|
|
||||||
const node_https_1 = require("node:https");
|
|
||||||
const httpsAgent = new node_https_1.Agent({
|
|
||||||
rejectUnauthorized: false,
|
|
||||||
});
|
|
||||||
exports.llm = new gigachat_1.GigaChat({
|
|
||||||
credentials: process.env.GIGA_AUTH,
|
|
||||||
model: 'GigaChat-2',
|
|
||||||
httpsAgent,
|
|
||||||
});
|
|
||||||
const generatePicture = async (prompt) => {
|
|
||||||
const resp = await exports.llm.chat({
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "Ты — Василий Кандинский для жильцов многоквартирного дома"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: `Старайся передать атмосферу уюта и безопасности.
|
|
||||||
Нарисуй картинку подходящую для такого события: ${prompt}
|
|
||||||
В картинке не должно быть текста, только изображение.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
function_call: 'auto',
|
|
||||||
});
|
|
||||||
// Получение изображения по идентификатору
|
|
||||||
const detectedImage = (0, gigachat_1.detectImage)(resp.choices[0]?.message.content ?? '');
|
|
||||||
const image = await exports.llm.getImage(detectedImage?.uuid ?? '');
|
|
||||||
// Возвращаем содержимое изображения
|
|
||||||
return image.content;
|
|
||||||
};
|
|
||||||
exports.generatePicture = generatePicture;
|
|
||||||
@@ -3,6 +3,7 @@ import { Agent } from 'node:https';
|
|||||||
|
|
||||||
const httpsAgent = new Agent({
|
const httpsAgent = new Agent({
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
|
timeout: 60000
|
||||||
});
|
});
|
||||||
|
|
||||||
export const llm = new GigaChat({
|
export const llm = new GigaChat({
|
||||||
@@ -30,8 +31,19 @@ export const generatePicture = async (prompt: string) => {
|
|||||||
|
|
||||||
// Получение изображения по идентификатору
|
// Получение изображения по идентификатору
|
||||||
const detectedImage = detectImage(resp.choices[0]?.message.content ?? '');
|
const detectedImage = detectImage(resp.choices[0]?.message.content ?? '');
|
||||||
const image = await llm.getImage(detectedImage?.uuid ?? '');
|
|
||||||
|
if (!detectedImage?.uuid) {
|
||||||
|
throw new Error('Не удалось получить UUID изображения из ответа GigaChat');
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = await llm.getImage(detectedImage.uuid);
|
||||||
|
|
||||||
// Возвращаем содержимое изображения
|
// Возвращаем содержимое изображения, убеждаясь что это Buffer
|
||||||
return image.content;
|
if (Buffer.isBuffer(image.content)) {
|
||||||
|
return image.content;
|
||||||
|
} else if (typeof image.content === 'string') {
|
||||||
|
return Buffer.from(image.content, 'binary');
|
||||||
|
} else {
|
||||||
|
throw new Error('Unexpected image content type: ' + typeof image.content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
124
server/routers/kfu-m-24-1/sber_mobile/moderate.js
Normal file
124
server/routers/kfu-m-24-1/sber_mobile/moderate.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { moderationText } = require('./initiatives-ai-agents/moderation.ts');
|
||||||
|
const { generatePicture } = require('./initiatives-ai-agents/picture.ts');
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Обработчик для модерации текста
|
||||||
|
router.post('/moderate', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { title, body } = req.body;
|
||||||
|
if (!title || !body) {
|
||||||
|
res.status(400).json({ error: 'Заголовок и текст обязательны' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Запрос на модерацию:', { title: title.substring(0, 50), body: body.substring(0, 100) });
|
||||||
|
|
||||||
|
const [comment, fixedText, isApproved] = await moderationText(title, body);
|
||||||
|
|
||||||
|
console.log('Результат модерации получен:', { comment, fixedText: fixedText?.substring(0, 100), isApproved });
|
||||||
|
|
||||||
|
// Дополнительная проверка на стороне сервера
|
||||||
|
if (!isApproved && (!comment || comment.trim() === '')) {
|
||||||
|
console.warn('Обнаружен некорректный результат модерации - пустой комментарий при отклонении');
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
comment,
|
||||||
|
fixedText,
|
||||||
|
isApproved
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in moderation:', error);
|
||||||
|
res.status(500).json({ error: 'Внутренняя ошибка сервера', details: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обработчик для генерации изображений
|
||||||
|
router.post('/generate-image', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { prompt, userId } = req.body;
|
||||||
|
if (!prompt) {
|
||||||
|
res.status(400).json({ error: 'Необходимо указать запрос для генерации' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерируем изображение
|
||||||
|
const imageBuffer = await generatePicture(prompt);
|
||||||
|
|
||||||
|
//console.log('Изображение получено, размер буфера:', imageBuffer?.length || 0, 'байт');
|
||||||
|
if (!imageBuffer || imageBuffer.length === 0) {
|
||||||
|
res.status(500).json({ error: 'Получен пустой буфер изображения' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('Начинаем загрузку в Supabase Storage...');
|
||||||
|
|
||||||
|
// Получаем Supabase клиент и создаем имя файла
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const filename = `image_${userId || 'user'}_${timestamp}.jpg`;
|
||||||
|
|
||||||
|
let uploadResult;
|
||||||
|
let retries = 0;
|
||||||
|
const maxRetries = 5;
|
||||||
|
|
||||||
|
while (retries < maxRetries) {
|
||||||
|
try {
|
||||||
|
uploadResult = await supabase.storage
|
||||||
|
.from('images')
|
||||||
|
.upload(filename, imageBuffer, {
|
||||||
|
contentType: 'image/jpeg',
|
||||||
|
upsert: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadResult.error) {
|
||||||
|
break; // Успешная загрузка
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.warn(`Попытка загрузки ${retries + 1} неудачна:`, uploadResult.error);
|
||||||
|
retries++;
|
||||||
|
|
||||||
|
if (retries < maxRetries) {
|
||||||
|
// Ждем перед повторной попыткой
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000 * retries));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
//console.warn(`Попытка загрузки ${retries + 1} неудачна (исключение):`, error.message);
|
||||||
|
retries++;
|
||||||
|
|
||||||
|
if (retries < maxRetries) {
|
||||||
|
// Ждем перед повторной попыткой
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000 * retries));
|
||||||
|
} else {
|
||||||
|
throw error; // Перебрасываем ошибку после всех попыток
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadResult?.error) {
|
||||||
|
//console.error('Supabase storage error after all retries:', uploadResult.error);
|
||||||
|
res.status(500).json({ error: 'Ошибка при сохранении изображения после нескольких попыток' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('Изображение успешно загружено в Supabase Storage:', filename);
|
||||||
|
|
||||||
|
// Получаем публичный URL
|
||||||
|
const { data: urlData } = supabase.storage
|
||||||
|
.from('images')
|
||||||
|
.getPublicUrl(filename);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
imageUrl: urlData.publicUrl,
|
||||||
|
imagePath: filename
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
//console.error('Error in image generation:', error);
|
||||||
|
res.status(500).json({ error: 'Внутренняя ошибка сервера', details: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
|
||||||
import { moderationText } from './initiatives-ai-agents/moderation';
|
|
||||||
import { generatePicture } from './initiatives-ai-agents/picture';
|
|
||||||
|
|
||||||
const { getSupabaseClient } = require('./supabaseClient');
|
|
||||||
|
|
||||||
const router = Router();
|
|
||||||
|
|
||||||
// Обработчик для модерации текста
|
|
||||||
router.post('/moderate', async (req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const { title, body } = req.body;
|
|
||||||
if (!title || !body) {
|
|
||||||
res.status(400).json({ error: 'Заголовок и текст обязательны' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [comment, fixedText, isApproved] = await moderationText(title, body);
|
|
||||||
res.json({
|
|
||||||
comment,
|
|
||||||
fixedText,
|
|
||||||
isApproved
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Error in moderation:', error);
|
|
||||||
res.status(500).json({ error: 'Внутренняя ошибка сервера', details: error.message });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработчик для генерации изображений
|
|
||||||
router.post('/generate-image', async (req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const { prompt, userId } = req.body;
|
|
||||||
if (!prompt) {
|
|
||||||
res.status(400).json({ error: 'Необходимо указать запрос для генерации' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем изображение
|
|
||||||
const imageBuffer = await generatePicture(prompt);
|
|
||||||
|
|
||||||
// Получаем Supabase клиент
|
|
||||||
const supabase = getSupabaseClient();
|
|
||||||
|
|
||||||
// Генерируем уникальное имя файла
|
|
||||||
const timestamp = Date.now();
|
|
||||||
const filename = `image_${userId || 'user'}_${timestamp}.jpg`;
|
|
||||||
|
|
||||||
// Загружаем в Supabase
|
|
||||||
const { data, error } = await supabase.storage
|
|
||||||
.from('images')
|
|
||||||
.upload(filename, imageBuffer, {
|
|
||||||
contentType: 'image/jpeg',
|
|
||||||
upsert: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
res.status(500).json({ error: 'Ошибка при сохранении изображения' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем публичный URL изображения
|
|
||||||
const { data: urlData } = supabase.storage
|
|
||||||
.from('images')
|
|
||||||
.getPublicUrl(filename);
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
imageUrl: urlData.publicUrl,
|
|
||||||
imagePath: filename
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Error in image generation:', error);
|
|
||||||
res.status(500).json({ error: 'Внутренняя ошибка сервера', details: error.message });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
Reference in New Issue
Block a user