Internationalizes error messages and console logs.

This commit is contained in:
uxname committed 2025-06-02 23:13:46 +03:00
1 parent fa126651de
commit c7735cc6f9
4 files changed
+50 -57

No files matched your search

+32 -35
View File
@@ -41,7 +41,7 @@ export class TelegramBot {
const message = const message =
status === 'started' status === 'started'
? `<b>${stepName}</b> ${emoji}` ? `<b>${stepName}</b> ${emoji}`
: `<b>${stepName}</b> ${emoji}\n\n<pre>${result?.substring(0, 3000) || 'Готово'}</pre>`; : `<b>${stepName}</b> ${emoji}\n\n<pre>${result?.substring(0, 3000) || 'Done'}</pre>`;
await this.sendMessage(chatId, message); await this.sendMessage(chatId, message);
}; };
@@ -50,7 +50,7 @@ export class TelegramBot {
private setupHandlers(): void { private setupHandlers(): void {
this.bot.start((ctx) => { this.bot.start((ctx) => {
ctx.reply( ctx.reply(
'Привет! Я бот для работы с AI пайплайнами. Просто отправь мне сообщение, и я обработаю его через пайплайн.', 'Hello! I am a bot for working with AI pipelines. Just send me a message and I will process it through the pipeline.',
); );
}); });
@@ -61,50 +61,49 @@ export class TelegramBot {
try { try {
const userPipeline: Pipeline = { const userPipeline: Pipeline = {
description: 'Криптокошелёк анализатор для Liquidswap', description: 'Crypto wallet analyzer for Liquidswap',
systemPrompt: `Вы — профессиональный криптоаналитик и портфельный менеджер. systemPrompt: `You are a professional crypto analyst and portfolio manager.
Анализируйте криптовалютный рынок, используя актуальные веб-данные. Analyze the cryptocurrency market using up-to-date web data.
Проведите анализ портфеля пользователя, адаптируясь к широкому (рыночные тренды, новости) или ограниченному (только портфель) набору данных. Conduct a portfolio analysis, adapting to either a broad (market trends, news) or limited (portfolio only) dataset.
Если данные портфеля отсутствуют, запросите их. Охватите по возможности: If portfolio data is missing, request it. Cover the following aspects if possible:
- Технический анализ (тренды, волатильность, индикаторы). - Technical analysis (trends, volatility, indicators).
- Фундаментальный анализ (новости, проекты). - Fundamental analysis (news, projects).
- Риски (корреляции, ликвидность, регуляторные факторы). - Risks (correlations, liquidity, regulatory factors).
- Диверсификацию и оптимизацию. - Diversification and optimization.
Предоставьте рекомендации по улучшению портфеля. Provide recommendations for portfolio improvement.
Формат ответа: отчет с разделами "Текущая ситуация на рынке", "Анализ портфеля", "Риски", "Рекомендации". Response format: a report with sections "Current Market Situation", "Portfolio Analysis", "Risks", "Recommendations".
Тон профессиональный, без сложного жаргона. Maintain a professional tone without complex jargon.
Если данных недостаточно, задайте уточняющие вопросы. If data is insufficient, ask clarifying questions.
Обеспечьте точность ≥ 95%, избегая неподтвержденных предположений.`, Ensure accuracy ≥ 95%, avoiding unsubstantiated assumptions.`,
steps: [ steps: [
{ {
name: 'Обработка запроса', name: 'Request Processing',
prompt: userMessage, prompt: userMessage,
}, },
{ {
name: 'Анализ', name: 'Analysis',
prompt: `Я хочу грамотно распределить свой криптовалютный портфель, используя данные DeFi-протокола Liquidswap. prompt: `I want to properly allocate my cryptocurrency portfolio using data from the Liquidswap DeFi protocol.
Пожалуйста, предоставьте рекомендации по распределению активов, Please provide asset allocation recommendations based on data from the following tools:
основываясь на данных от инструментов:
\`get_tokens\`, \`get_tokens\`,
\`get_pools\`, \`get_pools\`,
\`get_pools_historical_aprs\` (за 5 дней), \`get_pools_historical_aprs\` (last 5 days),
\`get_pools_historical_tvls\` (за 5 дней), \`get_pools_historical_tvls\` (last 5 days),
\`get_balances_by_address\` (мой адрес Aptos, если потребуется, запросите его) и \`multi_tool_use.parallel\`. \`get_balances_by_address\` (my Aptos address, request it if needed) and \`multi_tool_use.parallel\`.
Затем напиши доступную информацию об указанном адресе.`, Then provide the available information about the specified address.`,
}, },
{ {
name: 'Форматирование ответа', name: 'Response Formatting',
prompt: `Отлично, теперь используя эмодзи давай сделаем из этих данных telegram пост. prompt: `Great, now let's create a Telegram post from this data using emojis.
Сначала кратко напиши простыми словами что следовало бы сделать пользователю, First, briefly explain in simple terms what the user should do,
затем напиши подробную детальную информацию. then provide detailed information.
В конце поста должен быть текст: "${process.env.PROMO_TEXT}"`, The post should end with the text: "${process.env.PROMO_TEXT}"`,
}, },
], ],
}; };
this.activePipelines.set(chatId, { pipeline: userPipeline, chatId }); this.activePipelines.set(chatId, { pipeline: userPipeline, chatId });
await ctx.sendChatAction('typing'); await ctx.sendChatAction('typing');
await ctx.reply('🚀 Запускаю анализ...'); await ctx.reply('🚀 Starting analysis...');
const results = await this.pipelineExecutor.executeChain({ const results = await this.pipelineExecutor.executeChain({
pipeline: userPipeline, pipeline: userPipeline,
@@ -131,13 +130,11 @@ export class TelegramBot {
console.log(`Sending response to chat ${chatId}:`, trimmedResult); console.log(`Sending response to chat ${chatId}:`, trimmedResult);
await ctx.replyWithHTML(trimmedResult); await ctx.replyWithHTML(trimmedResult);
} else { } else {
await ctx.reply( await ctx.reply('Failed to process the request. Please try again.');
'Не удалось обработать запрос. Пожалуйста, попробуйте еще раз.',
);
} }
} catch (error) { } catch (error) {
console.error('Error processing message:', error); console.error('Error processing message:', error);
await ctx.reply('Произошла ошибка при обработке вашего запроса.'); await ctx.reply('An error occurred while processing your request.');
} }
}); });
+5 -11
View File
@@ -9,33 +9,27 @@ async function main() {
const mcpServerUrl = process.env.MCP_SERVER_URL; const mcpServerUrl = process.env.MCP_SERVER_URL;
if (!telegramToken) { if (!telegramToken) {
console.error( console.error('Error: TELEGRAM_BOT_TOKEN environment variable is not set.');
'Ошибка: Переменная окружения TELEGRAM_BOT_TOKEN не установлена.',
);
process.exit(1); process.exit(1);
} }
if (!openAIApiKey) { if (!openAIApiKey) {
console.error( console.error('Error: OPENAI_API_KEY environment variable is not set.');
'Ошибка: Переменная окружения OPENAI_API_KEY не установлена.',
);
process.exit(1); process.exit(1);
} }
if (!mcpServerUrl) { if (!mcpServerUrl) {
console.error( console.error('Error: MCP_SERVER_URL environment variable is not set.');
'Ошибка: Переменная окружения MCP_SERVER_URL не установлена.',
);
process.exit(1); process.exit(1);
} }
const bot = new TelegramBot(telegramToken, mcpServerUrl); const bot = new TelegramBot(telegramToken, mcpServerUrl);
bot.launch(); bot.launch();
console.log('Бот запущен и готов к работе!'); console.log('Bot is up and running!');
} }
main().catch((error) => { main().catch((error) => {
console.error('Произошла ошибка:', error); console.error('An error occurred:', error);
process.exit(1); process.exit(1);
}); });
+2 -2
View File
@@ -98,7 +98,7 @@ export class PipelineExecutor {
for (let i = 0; i < totalSteps; i++) { for (let i = 0; i < totalSteps; i++) {
const { name, prompt } = steps[i]!; const { name, prompt } = steps[i]!;
const stepName = name || 'Без названия'; const stepName = name || 'Untitled';
const isLastStep = i === totalSteps - 1; const isLastStep = i === totalSteps - 1;
try { try {
@@ -120,7 +120,7 @@ export class PipelineExecutor {
} }
} catch (error) { } catch (error) {
console.error(`Error in step "${stepName}":`, error); console.error(`Error in step "${stepName}":`, error);
const errorMessage = `❌ Ошибка на шаге "${stepName}": ${error instanceof Error ? error.message : String(error)}`; const errorMessage = `❌ Error in step "${stepName}": ${error instanceof Error ? error.message : String(error)}`;
if (options.onStepComplete) { if (options.onStepComplete) {
await options.onStepComplete(stepName, errorMessage, isLastStep); await options.onStepComplete(stepName, errorMessage, isLastStep);
} }
+11 -9
View File
@@ -24,10 +24,10 @@ export class McpToolsService {
version: '1.0.0', version: '1.0.0',
}); });
await this.mcpClient.connect(this.transport); await this.mcpClient.connect(this.transport);
console.log(`[MCP Service] Подключено к MCP серверу: ${this.serverUrl}`); console.log(`[MCP Service] Connected to MCP server: ${this.serverUrl}`);
} catch (error) { } catch (error) {
console.error( console.error(
`[MCP Service] Ошибка подключения к MCP серверу ${this.serverUrl}:`, `[MCP Service] Error connecting to MCP server ${this.serverUrl}:`,
error, error,
); );
throw error; throw error;
@@ -39,13 +39,15 @@ export class McpToolsService {
await this.mcpClient.close(); await this.mcpClient.close();
this.mcpClient = null; this.mcpClient = null;
this.transport = null; this.transport = null;
console.log(`[MCP Service] Отключено от MCP сервера: ${this.serverUrl}`); console.log(
`[MCP Service] Disconnected from MCP server: ${this.serverUrl}`,
);
} }
} }
async getLangchainTools(): Promise<DynamicStructuredTool[]> { async getLangchainTools(): Promise<DynamicStructuredTool[]> {
if (!this.mcpClient) { if (!this.mcpClient) {
throw new Error('MCP клиент не подключен. Сначала вызовите .connect()'); throw new Error('MCP client is not connected. Call .connect() first');
} }
const mcpTools: McpTool[] = (await this.mcpClient.listTools()).tools; const mcpTools: McpTool[] = (await this.mcpClient.listTools()).tools;
@@ -89,7 +91,7 @@ export class McpToolsService {
schema: zodSchema, schema: zodSchema,
func: async (args: Record<string, unknown>) => { func: async (args: Record<string, unknown>) => {
console.log( console.log(
`[MCP Tool Call] Вызов инструмента MCP: ${mcpTool.name} с аргументами:`, `[MCP Tool Call] Calling MCP tool: ${mcpTool.name} with arguments:`,
args, args,
); );
const result = await this.mcpClient!.callTool({ const result = await this.mcpClient!.callTool({
@@ -103,7 +105,7 @@ export class McpToolsService {
.map((c) => (c as TextContent).text || '') .map((c) => (c as TextContent).text || '')
.join('\n'); .join('\n');
throw new Error( throw new Error(
`Ошибка выполнения инструмента ${mcpTool.name}: ${errorContent}`, `Error executing tool ${mcpTool.name}: ${errorContent}`,
); );
} }
@@ -114,9 +116,9 @@ export class McpToolsService {
return (result.content as any[]) return (result.content as any[])
.map((c) => { .map((c) => {
if (c.type === 'text') return (c as TextContent).text; if (c.type === 'text') return (c as TextContent).text;
if (c.type === 'image') return `[Изображение: ${c.mimeType}]`; if (c.type === 'image') return `[Image: ${c.mimeType}]`;
if (c.type === 'audio') return `[Аудио: ${c.mimeType}]`; if (c.type === 'audio') return `[Audio: ${c.mimeType}]`;
if (c.type === 'resource') return `[Ресурс: ${c.uri}]`; if (c.type === 'resource') return `[Resource: ${c.uri}]`;
return JSON.stringify(c); return JSON.stringify(c);
}) })
.join('\n'); .join('\n');