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",
content:
"Ты помощник для генерации тегов. Проанализируй текст и предложи 3-8 релевантных тегов на русском языке через запятую. Теги должны быть краткими (1-3 слова). Избегай общих слов типа 'текст', 'заметка', 'информация'. Верни ТОЛЬКО теги через запятую, без знаков #, без нумерации, без точек. Пример: работа, проект, задачи, дедлайн",
"Ты помощник для генерации тегов. Проанализируй текст и предложи 3-8 релевантных тегов на русском языке через запятую. Теги должны быть краткими (1-3 слова) и написаны БЕЗ ПРОБЕЛОВ - слова объединяются в одно (например: списокПокупок, работаНадПроектом, важныеЗадачи). Избегай общих слов типа 'текст', 'заметка', 'информация'. Верни ТОЛЬКО теги через запятую, без знаков #, без нумерации, без точек. Пример: работа, проект, задачи, дедлайн, списокПокупок",
},
{
role: "user",
@ -2625,7 +2625,7 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
},
],
temperature: 0.7,
max_tokens: 200,
max_tokens: 500, // Увеличено для моделей с reasoning tokens
};
const requestData = JSON.stringify(requestBody);
@ -2693,6 +2693,26 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
responseData.choices[0].message
) {
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)
else if (
@ -2729,6 +2749,14 @@ app.post("/api/ai/generate-tags", requireApiAuth, async (req, res) => {
firstChoice.content ||
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);
// Функция для объединения слов в теге (убирает пробелы и делает 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
.map((tag) => tag.trim())
.filter((tag) => tag.length > 0)
.map((tag) => {
// Убираем # в начале и конце
tag = tag.replace(/^#+\s*/, "").replace(/\s*#+$/, "");
// Убираем точки, дефисы в начале/конце если они есть
tag = tag.replace(/^[.\-\s]+|[.\-\s]+$/g, "");
// Убираем кавычки если есть
tag = tag.replace(/^["']+|["']+$/g, "");
return tag.trim();
})
.map((tag) => normalizeTag(tag))
.filter((tag) => {
// Фильтруем слишком короткие и слишком длинные теги
return tag.length > 0 && tag.length <= 50;

View File

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

View File

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