change file type and fix agents

This commit is contained in:
Дания
2025-06-14 02:01:19 +03:00
parent 39a62818e9
commit a7be793608
7 changed files with 157 additions and 181 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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];
}; };

View File

@@ -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;

View File

@@ -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);
}
} }

View 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;

View File

@@ -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;