src / toolsProvider.ts
import * as cheerio from 'cheerio';
import { tool, ToolsProviderController, text } from '@lmstudio/sdk';
import { z } from 'zod';
/**
* Плагин для извлечения очищенного текста с веб-страниц.
*/
export async function toolsProvider(ctl: ToolsProviderController) {
const fetchAndCleanWebTextTool = tool({
name: 'fetch_and_clean_web_text_webscrapping_ru',
description: text`
Fetches a URL, removes HTML noise (nav, footer, scripts, ads), and returns the main text content.
Useful for extracting the primary article content from a webpage for summarization.
`,
parameters: {
url: z.string().url().describe('The full URL of the page to scrape.'),
},
implementation: async ({ url }, { warn }) => {
try {
const response = await fetch(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
},
signal: AbortSignal.timeout(10000),
});
if (!response.ok) {
return `Error: Failed to fetch page. Status: ${response.status} ${response.statusText}`;
}
const html = await response.text();
const $ = cheerio.load(html);
// 1. Удаляем весь «шум» (технические теги и блоки навигации/рекламы)
$('script, style, nav, footer, header, iframe, noscript, .ads, .sidebar, #sidebar, .menu, .footer, .header, .nav, .breadcrumbs').remove();
// 2. Пытаемся найти основной контент.
const mainContent = $('article, main, .content, #main, .post-content, .article-body, .tm-article-body').first();
// Если специализированный блок не найден, берем всё тело страницы
const textSource = mainContent.length > 0 ? mainContent : $('body');
// 3. Извлекаем текст и очищаем от лишних пробелов и пустых строк
let cleanText = textSource.text()
.replace(/\\s+/g, ' ') // Заменяем любые группы пробелов/переносов на один пробел
.trim();
if (!cleanText || cleanText.length < 10) {
return 'Error: No readable content found on the page. The site might be protected or empty.';
}
// 4. Возвращаем строго строку.
return `SOURCE URL: ${url}\n\n--- CLEANED CONTENT ---\n\n${cleanText}`;
} catch (error: any) {
warn(`Scraping error for ${url}: ${error.message}`);
return `Error: An unexpected error occurred while scraping: ${error.message}`;
}
},
});
return [fetchAndCleanWebTextTool];
}
src / toolsProvider.ts
import * as cheerio from 'cheerio';
import { tool, ToolsProviderController, text } from '@lmstudio/sdk';
import { z } from 'zod';
/**
* Плагин для извлечения очищенного текста с веб-страниц.
*/
export async function toolsProvider(ctl: ToolsProviderController) {
const fetchAndCleanWebTextTool = tool({
name: 'fetch_and_clean_web_text_webscrapping_ru',
description: text`
Fetches a URL, removes HTML noise (nav, footer, scripts, ads), and returns the main text content.
Useful for extracting the primary article content from a webpage for summarization.
`,
parameters: {
url: z.string().url().describe('The full URL of the page to scrape.'),
},
implementation: async ({ url }, { warn }) => {
try {
const response = await fetch(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
},
signal: AbortSignal.timeout(10000),
});
if (!response.ok) {
return `Error: Failed to fetch page. Status: ${response.status} ${response.statusText}`;
}
const html = await response.text();
const $ = cheerio.load(html);
// 1. Удаляем весь «шум» (технические теги и блоки навигации/рекламы)
$('script, style, nav, footer, header, iframe, noscript, .ads, .sidebar, #sidebar, .menu, .footer, .header, .nav, .breadcrumbs').remove();
// 2. Пытаемся найти основной контент.
const mainContent = $('article, main, .content, #main, .post-content, .article-body, .tm-article-body').first();
// Если специализированный блок не найден, берем всё тело страницы
const textSource = mainContent.length > 0 ? mainContent : $('body');
// 3. Извлекаем текст и очищаем от лишних пробелов и пустых строк
let cleanText = textSource.text()
.replace(/\\s+/g, ' ') // Заменяем любые группы пробелов/переносов на один пробел
.trim();
if (!cleanText || cleanText.length < 10) {
return 'Error: No readable content found on the page. The site might be protected or empty.';
}
// 4. Возвращаем строго строку.
return `SOURCE URL: ${url}\n\n--- CLEANED CONTENT ---\n\n${cleanText}`;
} catch (error: any) {
warn(`Scraping error for ${url}: ${error.message}`);
return `Error: An unexpected error occurred while scraping: ${error.message}`;
}
},
});
return [fetchAndCleanWebTextTool];
}