Refactor: Remove redundant comments and simplify code

This commit is contained in:
uxname committed 2025-05-24 16:07:39 +03:00
1 parent c348b4902e
commit da064fcb66
1 file changed
+17 -38
+17 -38
View File
@@ -7,16 +7,15 @@ import {
import { Runnable } from '@langchain/core/runnables'; import { Runnable } from '@langchain/core/runnables';
import { DynamicStructuredTool } from '@langchain/core/tools'; import { DynamicStructuredTool } from '@langchain/core/tools';
import { ChatOpenAI } from '@langchain/openai'; import { ChatOpenAI } from '@langchain/openai';
import dotenv from 'dotenv'; // Импортируем dotenv для загрузки переменных окружения import dotenv from 'dotenv';
import Handlebars from 'handlebars'; import Handlebars from 'handlebars';
import { z } from 'zod'; import { z } from 'zod';
// MCP SDK imports
import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { Tool as McpTool, TextContent } from '@modelcontextprotocol/sdk/types'; import { Tool as McpTool, TextContent } from '@modelcontextprotocol/sdk/types';
dotenv.config(); // Загружаем переменные окружения из .env файла dotenv.config();
interface PipelineStep { interface PipelineStep {
name: string; name: string;
@@ -29,9 +28,6 @@ interface Pipeline {
steps: PipelineStep[]; steps: PipelineStep[];
} }
/**
* Сервис для взаимодействия с MCP сервером и предоставления инструментов LangChain.
*/
class McpToolsService { class McpToolsService {
private mcpClient: Client | null = null; private mcpClient: Client | null = null;
private transport: SSEClientTransport | null = null; private transport: SSEClientTransport | null = null;
@@ -41,12 +37,9 @@ class McpToolsService {
this.serverUrl = serverUrl; this.serverUrl = serverUrl;
} }
/**
* Устанавливает соединение с MCP сервером.
*/
async connect(): Promise<void> { async connect(): Promise<void> {
if (this.mcpClient && this.transport) { if (this.mcpClient && this.transport) {
return; // Уже подключено return;
} }
try { try {
this.transport = new SSEClientTransport(new URL(this.serverUrl)); this.transport = new SSEClientTransport(new URL(this.serverUrl));
@@ -65,9 +58,6 @@ class McpToolsService {
} }
} }
/**
* Закрывает соединение с MCP сервером.
*/
async disconnect(): Promise<void> { async disconnect(): Promise<void> {
if (this.mcpClient) { if (this.mcpClient) {
await this.mcpClient.close(); await this.mcpClient.close();
@@ -77,10 +67,6 @@ class McpToolsService {
} }
} }
/**
* Получает список инструментов с MCP сервера и преобразует их в формат LangChain.
* @returns Массив инструментов LangChain.
*/
async getLangchainTools(): Promise<DynamicStructuredTool[]> { async getLangchainTools(): Promise<DynamicStructuredTool[]> {
if (!this.mcpClient) { if (!this.mcpClient) {
throw new Error('MCP клиент не подключен. Сначала вызовите .connect()'); throw new Error('MCP клиент не подключен. Сначала вызовите .connect()');
@@ -90,8 +76,6 @@ class McpToolsService {
const langchainTools: DynamicStructuredTool[] = []; const langchainTools: DynamicStructuredTool[] = [];
for (const mcpTool of mcpTools) { for (const mcpTool of mcpTools) {
// Простая конвертация JSON Schema в Zod schema.
// Для сложных схем рекомендуется использовать специализированную библиотеку (например, json-schema-to-zod).
const properties: Record<string, z.ZodTypeAny> = {}; const properties: Record<string, z.ZodTypeAny> = {};
for (const key in mcpTool.inputSchema.properties) { for (const key in mcpTool.inputSchema.properties) {
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // biome-ignore lint/suspicious/noExplicitAny: <explanation>
@@ -108,11 +92,9 @@ class McpToolsService {
schemaType = z.boolean(); schemaType = z.boolean();
break; break;
case 'array': case 'array':
// Для массивов без явного типа элементов, используем z.array(z.any())
schemaType = z.array(z.any()); schemaType = z.array(z.any());
break; break;
case 'object': case 'object':
// Для объектов без явной вложенной схемы, используем z.object({})
schemaType = z.object({}); schemaType = z.object({});
break; break;
default: default:
@@ -149,7 +131,6 @@ class McpToolsService {
); );
} }
// Предпочтительно возвращаем структурированный контент, если он есть
if (result.structuredContent) { if (result.structuredContent) {
return result.structuredContent; return result.structuredContent;
} }
@@ -160,7 +141,7 @@ class McpToolsService {
if (c.type === 'image') return `[Изображение: ${c.mimeType}]`; if (c.type === 'image') return `[Изображение: ${c.mimeType}]`;
if (c.type === 'audio') return `[Аудио: ${c.mimeType}]`; if (c.type === 'audio') return `[Аудио: ${c.mimeType}]`;
if (c.type === 'resource') return `[Ресурс: ${c.uri}]`; if (c.type === 'resource') return `[Ресурс: ${c.uri}]`;
return JSON.stringify(c); // Запасной вариант для других типов return JSON.stringify(c);
}) })
.join('\n'); .join('\n');
}, },
@@ -181,8 +162,8 @@ class PipelineExecutor {
private async _getOrLoadTools(): Promise<DynamicStructuredTool[]> { private async _getOrLoadTools(): Promise<DynamicStructuredTool[]> {
if (!this.cachedTools) { if (!this.cachedTools) {
await this.mcpToolsService.connect(); // Устанавливаем соединение с MCP сервером await this.mcpToolsService.connect();
this.cachedTools = await this.mcpToolsService.getLangchainTools(); // Получаем инструменты LangChain this.cachedTools = await this.mcpToolsService.getLangchainTools();
} }
return this.cachedTools; return this.cachedTools;
} }
@@ -225,7 +206,9 @@ class PipelineExecutor {
const variables = options.variables ?? {}; const variables = options.variables ?? {};
const systemPrompts = options.systemPrompts ?? []; const systemPrompts = options.systemPrompts ?? [];
const chain = await this.initChain(options.openAIApiKey); const chain = await this.initChain(options.openAIApiKey);
const initialMessages = this.buildMessages( // Инициализируем массив сообщений, который будет накапливаться
const messages: (HumanMessage | SystemMessage | AIMessage | ToolMessage)[] =
this.buildMessages(
[options.pipeline.systemPrompt, ...systemPrompts], [options.pipeline.systemPrompt, ...systemPrompts],
variables, variables,
); );
@@ -233,17 +216,15 @@ class PipelineExecutor {
for (const { prompt } of options.pipeline.steps) { for (const { prompt } of options.pipeline.steps) {
const renderedPrompt = this.renderPrompt(prompt, variables); const renderedPrompt = this.renderPrompt(prompt, variables);
const currentMessages: ( // Добавляем текущий промпт пользователя в общий массив сообщений
| HumanMessage messages.push(new HumanMessage(renderedPrompt));
| SystemMessage
| AIMessage
| ToolMessage
)[] = [...initialMessages, new HumanMessage(renderedPrompt)];
const finalResponse = await this._resolveToolCalls( const finalResponse = await this._resolveToolCalls(
chain, chain,
currentMessages, messages, // Передаем накопительный массив сообщений
); );
// Добавляем ответ ИИ в общий массив сообщений для сохранения контекста
messages.push(finalResponse);
results.push(finalResponse?.content.toString() ?? '<empty>'); results.push(finalResponse?.content.toString() ?? '<empty>');
} }
return results; return results;
@@ -260,7 +241,7 @@ class PipelineExecutor {
for (const toolCall of response.tool_calls) { for (const toolCall of response.tool_calls) {
try { try {
const tools = await this._getOrLoadTools(); // Получаем LangChain-совместимые инструменты const tools = await this._getOrLoadTools();
const tool = tools.find((t) => t.name === toolCall.name); const tool = tools.find((t) => t.name === toolCall.name);
if (!tool) { if (!tool) {
@@ -276,12 +257,11 @@ class PipelineExecutor {
continue; continue;
} }
// Выполняем функцию LangChain инструмента, которая внутри вызывает MCP сервер
const toolResult = await tool.func(toolCall.args); const toolResult = await tool.func(toolCall.args);
messages.push( messages.push(
new ToolMessage({ new ToolMessage({
tool_call_id: toolCall.id!, tool_call_id: toolCall.id!,
content: JSON.stringify(toolResult), // LangChain ожидает строковый контент здесь content: JSON.stringify(toolResult),
}), }),
); );
} catch (error) { } catch (error) {
@@ -305,7 +285,6 @@ class PipelineExecutor {
async function main() { async function main() {
const openAIApiKey = process.env.OPENAI_API_KEY; const openAIApiKey = process.env.OPENAI_API_KEY;
// Используйте переменную окружения для URL сервера MCP, с запасным вариантом по умолчанию
const mcpServerUrl = const mcpServerUrl =
process.env.MCP_SERVER_URL || 'https://santiment-mcp.dev.mind-dev.com/sse'; process.env.MCP_SERVER_URL || 'https://santiment-mcp.dev.mind-dev.com/sse';
@@ -316,7 +295,7 @@ async function main() {
process.exit(1); process.exit(1);
} }
const pipelineExecutor = new PipelineExecutor(mcpServerUrl); // Передаем URL сервера const pipelineExecutor = new PipelineExecutor(mcpServerUrl);
const testPipeline: Pipeline = { const testPipeline: Pipeline = {
description: 'Тестовый пайплайн', description: 'Тестовый пайплайн',