Compare commits

...

2 Commits

3 changed files with 69 additions and 16 deletions

View File

@ -2617,7 +2617,7 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
{ {
role: "system", role: "system",
content: content:
"Ты помощник для генерации тегов. Проанализируй текст и предложи 3-8 релевантных тегов на русском языке через запятую. Теги должны быть краткими (1-3 слова). Избегай общих слов типа 'текст', 'заметка', 'информация'. Верни ТОЛЬКО теги через запятую, без знаков #, без нумерации, без точек. Пример: работа, проект, задачи, дедлайн", "Ты помощник для генерации тегов. Проанализируй текст и предложи 3-8 релевантных тегов на русском языке через запятую. Теги должны быть краткими (1-3 слова) и написаны БЕЗ ПРОБЕЛОВ - слова объединяются в одно (например: списокПокупок, работаНадПроектом, важныеЗадачи). Избегай общих слов типа 'текст', 'заметка', 'информация'. Верни ТОЛЬКО теги через запятую, без знаков #, без нумерации, без точек. Пример: работа, проект, задачи, дедлайн, списокПокупок",
}, },
{ {
role: "user", role: "user",
@ -2625,7 +2625,7 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
}, },
], ],
temperature: 0.7, temperature: 0.7,
max_tokens: 200, max_tokens: 500, // Увеличено для моделей с reasoning tokens
}; };
const requestData = JSON.stringify(requestBody); const requestData = JSON.stringify(requestBody);
@ -2693,6 +2693,26 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
responseData.choices[0].message responseData.choices[0].message
) { ) {
tagsText = responseData.choices[0].message.content || ""; tagsText = responseData.choices[0].message.content || "";
// Если content пустой, пробуем извлечь из reasoning (для моделей с reasoning tokens)
if (!tagsText && responseData.choices[0].message.reasoning) {
const reasoning = responseData.choices[0].message.reasoning;
// Пытаемся найти теги в reasoning тексте
// Ищем паттерны типа "теги: ..." или просто список тегов через запятую
const tagsMatch = reasoning.match(/(?:теги?|tags?)[:\s]+([^.\n]+)/i);
if (tagsMatch && tagsMatch[1]) {
tagsText = tagsMatch[1].trim();
} else {
// Если не нашли паттерн, пробуем взять последнюю строку reasoning
const lines = reasoning.split('\n').filter(l => l.trim());
if (lines.length > 0) {
const lastLine = lines[lines.length - 1];
// Проверяем, похоже ли на список тегов (содержит запятые и короткие слова)
if (lastLine.includes(',') && lastLine.length < 200) {
tagsText = lastLine.trim();
}
}
}
}
} }
// Альтернативный формат (некоторые API) // Альтернативный формат (некоторые API)
else if ( else if (
@ -2729,6 +2749,14 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
firstChoice.content || firstChoice.content ||
firstChoice.message?.text || firstChoice.message?.text ||
""; "";
// Fallback на reasoning
if (!tagsText && firstChoice.message?.reasoning) {
const reasoning = firstChoice.message.reasoning;
const tagsMatch = reasoning.match(/(?:теги?|tags?)[:\s]+([^.\n]+)/i);
if (tagsMatch && tagsMatch[1]) {
tagsText = tagsMatch[1].trim();
}
}
} }
} }
@ -2830,19 +2858,38 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
console.log("Теги до обработки:", tags); console.log("Теги до обработки:", tags);
// Функция для объединения слов в теге (убирает пробелы и делает camelCase)
const normalizeTag = (tag) => {
// Убираем # в начале и конце
tag = tag.replace(/^#+\s*/, "").replace(/\s*#+$/, "");
// Убираем точки, дефисы в начале/конце если они есть
tag = tag.replace(/^[.\-\s]+|[.\-\s]+$/g, "");
// Убираем кавычки если есть
tag = tag.replace(/^["']+|["']+$/g, "");
tag = tag.trim();
// Если есть пробелы, объединяем слова в camelCase
if (tag.includes(" ")) {
const words = tag.split(/\s+/).filter(w => w.length > 0);
if (words.length > 0) {
// Первое слово в нижнем регистре, остальные с заглавной первой буквой
const firstWord = words[0].toLowerCase();
const restWords = words.slice(1).map(word => {
if (word.length === 0) return "";
return word[0].toUpperCase() + word.slice(1).toLowerCase();
});
tag = firstWord + restWords.join("");
}
}
return tag;
};
// Обрабатываем теги // Обрабатываем теги
tags = tags tags = tags
.map((tag) => tag.trim()) .map((tag) => tag.trim())
.filter((tag) => tag.length > 0) .filter((tag) => tag.length > 0)
.map((tag) => { .map((tag) => normalizeTag(tag))
// Убираем # в начале и конце
tag = tag.replace(/^#+\s*/, "").replace(/\s*#+$/, "");
// Убираем точки, дефисы в начале/конце если они есть
tag = tag.replace(/^[.\-\s]+|[.\-\s]+$/g, "");
// Убираем кавычки если есть
tag = tag.replace(/^["']+|["']+$/g, "");
return tag.trim();
})
.filter((tag) => { .filter((tag) => {
// Фильтруем слишком короткие и слишком длинные теги // Фильтруем слишком короткие и слишком длинные теги
return tag.length > 0 && tag.length <= 50; return tag.length > 0 && tag.length <= 50;

View File

@ -82,7 +82,7 @@ define(['./workbox-47da91e0'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "/index.html", "url": "/index.html",
"revision": "0.q2r0i2lj5ro" "revision": "0.5s0rolua70o"
}], { }], {
"ignoreURLParametersMatching": [/^utm_/, /^fbclid$/] "ignoreURLParametersMatching": [/^utm_/, /^fbclid$/]
}); });

View File

@ -729,10 +729,16 @@ const SettingsPage: React.FC = () => {
<span className="toggle-text-main">Включить функции ИИ</span> <span className="toggle-text-main">Включить функции ИИ</span>
<span className="toggle-text-desc"> <span className="toggle-text-desc">
{checkAiSettingsFilled() ? ( {checkAiSettingsFilled() ? (
<ul style={{ margin: "8px 0 0 20px", padding: 0 }}> <div>
<li>Улучшение текста заметок</li> <p style={{ margin: "8px 0 4px 0" }}>
<li>Объединение заметок</li> Активирует использование искусственного интеллекта для работы с заметками:
</ul> </p>
<ul style={{ margin: "4px 0 0 20px", padding: 0 }}>
<li>Улучшение и доработка текста заметок</li>
<li>Автоматическая генерация тегов для заметок</li>
<li>Умное объединение нескольких заметок</li>
</ul>
</div>
) : ( ) : (
"Сначала заполните API Key, Base URL и Модель ниже" "Сначала заполните API Key, Base URL и Модель ниже"
)} )}