ваше сообщение коммита
This commit is contained in:
@@ -69,59 +69,99 @@ async function getAIResponse(message, language = 'ru') {
|
|||||||
// Функция для обработки гостевых сообщений после аутентификации
|
// Функция для обработки гостевых сообщений после аутентификации
|
||||||
async function processGuestMessages(userId, guestId) {
|
async function processGuestMessages(userId, guestId) {
|
||||||
try {
|
try {
|
||||||
console.log(`Starting to process guest messages for user ${userId} with guestId ${guestId}`);
|
console.log(`Processing guest messages for user ${userId} with guest ID ${guestId}`);
|
||||||
|
|
||||||
// Получаем все гостевые сообщения
|
// Получаем все гостевые сообщения
|
||||||
const guestMessages = await db.query(
|
const guestMessagesResult = await db.query(
|
||||||
`SELECT m.id, m.content, m.conversation_id, m.metadata, m.created_at
|
'SELECT * FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC',
|
||||||
FROM messages m
|
|
||||||
WHERE m.metadata->>'guest_id' = $1
|
|
||||||
ORDER BY m.created_at ASC`,
|
|
||||||
[guestId]
|
[guestId]
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(`Found ${guestMessages.rows.length} guest messages to process`);
|
if (guestMessagesResult.rows.length === 0) {
|
||||||
|
console.log('No guest messages found');
|
||||||
// Обновляем user_id для всех бесед с гостевыми сообщениями
|
return { success: true, message: 'No guest messages found' };
|
||||||
await db.query(
|
|
||||||
`UPDATE conversations c
|
|
||||||
SET user_id = $1
|
|
||||||
WHERE id IN (
|
|
||||||
SELECT DISTINCT conversation_id
|
|
||||||
FROM messages m
|
|
||||||
WHERE m.metadata->>'guest_id' = $2
|
|
||||||
)`,
|
|
||||||
[userId, guestId]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Обрабатываем каждое гостевое сообщение
|
|
||||||
for (const msg of guestMessages.rows) {
|
|
||||||
console.log(`Processing guest message ${msg.id}: ${msg.content}`);
|
|
||||||
|
|
||||||
// Получаем язык из метаданных
|
|
||||||
const metadata = typeof msg.metadata === 'string' ? JSON.parse(msg.metadata) : msg.metadata;
|
|
||||||
const language = metadata?.language || 'ru';
|
|
||||||
|
|
||||||
// Получаем ответ от AI
|
|
||||||
console.log(`Getting AI response for message ${msg.id} in ${language}`);
|
|
||||||
const aiResponse = await aiAssistant.getResponse(msg.content, language);
|
|
||||||
|
|
||||||
// Сохраняем ответ AI в ту же беседу
|
|
||||||
await db.query(
|
|
||||||
`INSERT INTO messages
|
|
||||||
(conversation_id, sender_type, content, channel, created_at)
|
|
||||||
VALUES ($1, 'assistant', $2, 'chat', NOW())`,
|
|
||||||
[msg.conversation_id, aiResponse]
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`Saved AI response for message ${msg.id}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Successfully processed all guest messages for user ${userId}`);
|
const guestMessages = guestMessagesResult.rows;
|
||||||
return true;
|
console.log(`Found ${guestMessages.length} guest messages`);
|
||||||
|
|
||||||
|
// Создаем новый диалог для этих сообщений
|
||||||
|
const firstMessage = guestMessages[0];
|
||||||
|
const title = firstMessage.content.length > 30
|
||||||
|
? `${firstMessage.content.substring(0, 30)}...`
|
||||||
|
: firstMessage.content;
|
||||||
|
|
||||||
|
const newConversationResult = await db.query(
|
||||||
|
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
|
||||||
|
[userId, title]
|
||||||
|
);
|
||||||
|
|
||||||
|
const conversation = newConversationResult.rows[0];
|
||||||
|
console.log('Created new conversation for guest messages:', conversation);
|
||||||
|
|
||||||
|
// Обрабатываем каждое гостевое сообщение
|
||||||
|
for (const guestMessage of guestMessages) {
|
||||||
|
console.log(`Processing guest message ID ${guestMessage.id}: ${guestMessage.content}`);
|
||||||
|
|
||||||
|
// Сохраняем сообщение пользователя
|
||||||
|
const userMessageResult = await db.query(
|
||||||
|
`INSERT INTO messages
|
||||||
|
(conversation_id, content, sender_type, role, guest_message_id, channel, created_at)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
RETURNING *`,
|
||||||
|
[
|
||||||
|
conversation.id,
|
||||||
|
guestMessage.content,
|
||||||
|
'user',
|
||||||
|
'user',
|
||||||
|
guestMessage.id,
|
||||||
|
'web',
|
||||||
|
guestMessage.created_at
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Saved user message with ID ${userMessageResult.rows[0].id}`);
|
||||||
|
|
||||||
|
// Получаем ответ от ИИ
|
||||||
|
console.log('Getting AI response for:', guestMessage.content);
|
||||||
|
const language = guestMessage.language || 'auto';
|
||||||
|
const aiResponse = await aiAssistant.getResponse(guestMessage.content, language);
|
||||||
|
console.log('AI response received:', aiResponse);
|
||||||
|
|
||||||
|
// Сохраняем ответ от ИИ
|
||||||
|
const aiMessageResult = await db.query(
|
||||||
|
`INSERT INTO messages
|
||||||
|
(conversation_id, content, sender_type, role, guest_message_id, channel, created_at)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
RETURNING *`,
|
||||||
|
[
|
||||||
|
conversation.id,
|
||||||
|
aiResponse,
|
||||||
|
'assistant',
|
||||||
|
'assistant',
|
||||||
|
guestMessage.id,
|
||||||
|
'web',
|
||||||
|
new Date()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Saved AI response with ID ${aiMessageResult.rows[0].id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем гостевые сообщения, так как они уже обработаны
|
||||||
|
await db.query('DELETE FROM guest_messages WHERE guest_id = $1', [guestId]);
|
||||||
|
console.log('Deleted processed guest messages');
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Processed ${guestMessages.length} guest messages`,
|
||||||
|
conversationId: conversation.id
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error processing guest messages:', error);
|
console.error('Error processing guest messages:', error);
|
||||||
return false;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,55 +197,82 @@ router.post('/guest-message', async (req, res) => {
|
|||||||
|
|
||||||
// Маршрут для обычных сообщений (для аутентифицированных пользователей)
|
// Маршрут для обычных сообщений (для аутентифицированных пользователей)
|
||||||
router.post('/message', requireAuth, async (req, res) => {
|
router.post('/message', requireAuth, async (req, res) => {
|
||||||
|
const { message, conversationId, language = 'auto' } = req.body;
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
return res.status(400).json({ error: 'Message is required' });
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { message, language } = req.body;
|
console.log('Processing message:', { message, conversationId, language, userId: req.session.userId });
|
||||||
const userId = req.session.userId;
|
const userId = req.session.userId;
|
||||||
|
|
||||||
// Используем методы из aiAssistant вместо прямого обращения к vectorStore
|
let conversation;
|
||||||
const similarDocs = await aiAssistant.findSimilarDocuments(message);
|
|
||||||
const aiResponse = await aiAssistant.getResponse(message, language);
|
|
||||||
|
|
||||||
// Создаем новую беседу или получаем существующую
|
// Если указан ID диалога, проверяем его существование и принадлежность пользователю
|
||||||
const conversationResult = await db.query(
|
if (conversationId) {
|
||||||
`INSERT INTO conversations (user_id, created_at)
|
const conversationResult = await db.query(
|
||||||
VALUES ($1, NOW())
|
'SELECT * FROM conversations WHERE id = $1 AND user_id = $2',
|
||||||
RETURNING id`,
|
[conversationId, userId]
|
||||||
[userId]
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const conversationId = conversationResult.rows[0].id;
|
if (conversationResult.rows.length === 0) {
|
||||||
|
return res.status(404).json({ error: 'Conversation not found or access denied' });
|
||||||
|
}
|
||||||
|
|
||||||
|
conversation = conversationResult.rows[0];
|
||||||
|
console.log('Using existing conversation:', conversation);
|
||||||
|
} else {
|
||||||
|
// Создаем новый диалог
|
||||||
|
const title = message.length > 30 ? `${message.substring(0, 30)}...` : message;
|
||||||
|
|
||||||
|
const newConversationResult = await db.query(
|
||||||
|
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
|
||||||
|
[userId, title]
|
||||||
|
);
|
||||||
|
|
||||||
|
conversation = newConversationResult.rows[0];
|
||||||
|
console.log('Created new conversation:', conversation);
|
||||||
|
}
|
||||||
|
|
||||||
// Сохраняем сообщение пользователя
|
// Сохраняем сообщение пользователя
|
||||||
await db.query(
|
console.log('Saving user message');
|
||||||
|
const userMessageResult = await db.query(
|
||||||
`INSERT INTO messages
|
`INSERT INTO messages
|
||||||
(conversation_id, sender_type, content, channel, metadata, created_at)
|
(conversation_id, content, sender_type, role, tokens_used, channel, created_at)
|
||||||
VALUES ($1, 'user', $2, 'chat', $3, NOW())`,
|
VALUES
|
||||||
[
|
($1, $2, $3, $4, $5, $6, $7)
|
||||||
conversationId,
|
RETURNING *`,
|
||||||
message,
|
[conversation.id, message, 'user', 'user', 0, 'web', new Date()]
|
||||||
JSON.stringify({ language: language || 'ru' })
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Сохраняем ответ AI
|
// Получаем ответ от ИИ
|
||||||
await db.query(
|
console.log('Getting AI response');
|
||||||
|
const aiResponse = await aiAssistant.getResponse(message, language);
|
||||||
|
console.log('AI response received:', aiResponse);
|
||||||
|
|
||||||
|
// Сохраняем ответ от ИИ
|
||||||
|
console.log('Saving AI response');
|
||||||
|
const aiMessageResult = await db.query(
|
||||||
`INSERT INTO messages
|
`INSERT INTO messages
|
||||||
(conversation_id, sender_type, content, channel, metadata, created_at)
|
(conversation_id, content, sender_type, role, tokens_used, channel, created_at)
|
||||||
VALUES ($1, 'assistant', $2, 'chat', $3, NOW())`,
|
VALUES
|
||||||
[
|
($1, $2, $3, $4, $5, $6, $7)
|
||||||
conversationId,
|
RETURNING *`,
|
||||||
aiResponse,
|
[conversation.id, aiResponse, 'assistant', 'assistant', 0, 'web', new Date()]
|
||||||
JSON.stringify({ language: language || 'ru' })
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({
|
const response = {
|
||||||
success: true,
|
success: true,
|
||||||
message: aiResponse
|
userMessage: userMessageResult.rows[0],
|
||||||
});
|
aiMessage: aiMessageResult.rows[0],
|
||||||
|
conversation
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error processing message:', error);
|
console.error('Error processing message:', error);
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({ error: 'Error processing message' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -232,35 +299,79 @@ router.get('/history', async (req, res) => {
|
|||||||
id: req.sessionID,
|
id: req.sessionID,
|
||||||
userId: req.session.userId,
|
userId: req.session.userId,
|
||||||
address: req.session.address,
|
address: req.session.address,
|
||||||
authenticated: req.session.authenticated
|
authenticated: req.session.authenticated,
|
||||||
|
guestId: req.session.guestId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!req.session.authenticated || !req.session.userId) {
|
|
||||||
return res.status(401).json({ error: 'Unauthorized' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const limit = parseInt(req.query.limit) || 50;
|
const limit = parseInt(req.query.limit) || 50;
|
||||||
const offset = parseInt(req.query.offset) || 0;
|
const offset = parseInt(req.query.offset) || 0;
|
||||||
|
|
||||||
// Получаем сообщения с пагинацией
|
let messages = [];
|
||||||
const result = await db.query(
|
let total = 0;
|
||||||
`SELECT
|
|
||||||
m.id,
|
// Если пользователь аутентифицирован, получаем его сообщения
|
||||||
m.content,
|
if (req.session.authenticated && req.session.userId) {
|
||||||
m.sender_type as role,
|
const countResult = await db.query(
|
||||||
m.created_at,
|
`SELECT COUNT(*) as total FROM messages m
|
||||||
c.user_id
|
JOIN conversations c ON m.conversation_id = c.id
|
||||||
FROM messages m
|
WHERE c.user_id = $1`,
|
||||||
JOIN conversations c ON m.conversation_id = c.id
|
[req.session.userId]
|
||||||
WHERE c.user_id = $1
|
);
|
||||||
ORDER BY m.created_at DESC
|
total = parseInt(countResult.rows[0].total) || 0;
|
||||||
LIMIT $2 OFFSET $3`,
|
|
||||||
[req.session.userId, limit, offset]
|
const result = await db.query(
|
||||||
);
|
`SELECT
|
||||||
|
m.id,
|
||||||
|
m.content,
|
||||||
|
m.sender_type,
|
||||||
|
m.role,
|
||||||
|
m.created_at,
|
||||||
|
c.user_id,
|
||||||
|
c.id as conversation_id
|
||||||
|
FROM messages m
|
||||||
|
JOIN conversations c ON m.conversation_id = c.id
|
||||||
|
WHERE c.user_id = $1
|
||||||
|
ORDER BY m.created_at ASC
|
||||||
|
LIMIT $2 OFFSET $3`,
|
||||||
|
[req.session.userId, limit, offset]
|
||||||
|
);
|
||||||
|
|
||||||
|
messages = result.rows;
|
||||||
|
console.log(`Found ${messages.length} messages for authenticated user`);
|
||||||
|
}
|
||||||
|
// Если есть guestId, получаем гостевые сообщения
|
||||||
|
else if (req.session.guestId) {
|
||||||
|
const countResult = await db.query(
|
||||||
|
`SELECT COUNT(*) as total FROM guest_messages
|
||||||
|
WHERE guest_id = $1`,
|
||||||
|
[req.session.guestId]
|
||||||
|
);
|
||||||
|
total = parseInt(countResult.rows[0].total) || 0;
|
||||||
|
|
||||||
|
const result = await db.query(
|
||||||
|
`SELECT
|
||||||
|
id,
|
||||||
|
content,
|
||||||
|
'user' as sender_type,
|
||||||
|
'user' as role,
|
||||||
|
created_at,
|
||||||
|
guest_id as user_id,
|
||||||
|
NULL as conversation_id
|
||||||
|
FROM guest_messages
|
||||||
|
WHERE guest_id = $1
|
||||||
|
ORDER BY created_at ASC
|
||||||
|
LIMIT $2 OFFSET $3`,
|
||||||
|
[req.session.guestId, limit, offset]
|
||||||
|
);
|
||||||
|
|
||||||
|
messages = result.rows;
|
||||||
|
console.log(`Found ${messages.length} guest messages`);
|
||||||
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
messages: result.rows.reverse()
|
messages: messages,
|
||||||
|
total: total
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -316,30 +427,49 @@ router.post('/link-guest-messages', requireAuth, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем наличие гостевых сообщений
|
// Проверяем наличие гостевых сообщений
|
||||||
const guestMessages = await db.query(
|
const guestMessagesCheck = await db.query(
|
||||||
'SELECT EXISTS(SELECT 1 FROM guest_messages WHERE guest_id = $1)',
|
'SELECT EXISTS(SELECT 1 FROM guest_messages WHERE guest_id = $1)',
|
||||||
[guestId]
|
[guestId]
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('Guest messages check:', guestMessages.rows[0]);
|
console.log('Guest messages check:', guestMessagesCheck.rows[0]);
|
||||||
|
|
||||||
if (!guestMessages.rows[0].exists) {
|
if (!guestMessagesCheck.rows[0].exists) {
|
||||||
console.log('No guest messages found for guestId:', guestId);
|
console.log('No guest messages found for guestId:', guestId);
|
||||||
return res.json({ success: true, message: 'No guest messages to link' });
|
return res.json({ success: true, message: 'No guest messages to link' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Связываем сообщения
|
try {
|
||||||
console.log('Calling link_guest_messages function');
|
// Обрабатываем гостевые сообщения для получения ответов от AI
|
||||||
await db.query('SELECT link_guest_messages($1, $2)', [userId, guestId]);
|
console.log('Processing guest messages to get AI responses');
|
||||||
|
const result = await processGuestMessages(userId, guestId);
|
||||||
|
console.log('Guest messages processed:', result);
|
||||||
|
|
||||||
// Очищаем guestId из сессии после связывания
|
// Очищаем guestId из сессии после связывания
|
||||||
delete req.session.guestId;
|
req.session.guestId = null;
|
||||||
|
await req.session.save();
|
||||||
|
|
||||||
console.log('Messages linked successfully');
|
console.log('Messages linked and processed successfully');
|
||||||
res.json({ success: true });
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Guest messages linked and processed',
|
||||||
|
result
|
||||||
|
});
|
||||||
|
} catch (processError) {
|
||||||
|
console.error('Error processing guest messages:', processError);
|
||||||
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Error processing guest messages',
|
||||||
|
details: processError.message
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error linking guest messages:', error);
|
console.error('Error linking guest messages:', error);
|
||||||
res.status(500).json({ success: false, error: 'Internal server error' });
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Internal server error',
|
||||||
|
details: error.message
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -395,4 +525,28 @@ router.delete('/message/:id', requireAuth, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Маршрут для проверки и инициализации сессии
|
||||||
|
router.get('/check-session', async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Если у пользователя нет guestId, создаем его
|
||||||
|
if (!req.session.guestId) {
|
||||||
|
req.session.guestId = crypto.randomBytes(16).toString('hex');
|
||||||
|
await req.session.save();
|
||||||
|
console.log('Created new guestId:', req.session.guestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
guestId: req.session.guestId,
|
||||||
|
isAuthenticated: req.session.authenticated || false
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking session:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Internal server error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ const { ChatOllama } = require('@langchain/ollama');
|
|||||||
const { HNSWLib } = require('@langchain/community/vectorstores/hnswlib');
|
const { HNSWLib } = require('@langchain/community/vectorstores/hnswlib');
|
||||||
const { OpenAIEmbeddings } = require('@langchain/openai');
|
const { OpenAIEmbeddings } = require('@langchain/openai');
|
||||||
const logger = require('../utils/logger');
|
const logger = require('../utils/logger');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
class AIAssistant {
|
class AIAssistant {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -18,7 +19,10 @@ class AIAssistant {
|
|||||||
return new ChatOllama({
|
return new ChatOllama({
|
||||||
baseUrl: this.baseUrl,
|
baseUrl: this.baseUrl,
|
||||||
model: this.defaultModel,
|
model: this.defaultModel,
|
||||||
system: systemPrompt
|
system: systemPrompt,
|
||||||
|
temperature: 0.7,
|
||||||
|
maxTokens: 1000,
|
||||||
|
timeout: 30000 // 30 секунд таймаут
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,25 +35,38 @@ class AIAssistant {
|
|||||||
// Основной метод для получения ответа
|
// Основной метод для получения ответа
|
||||||
async getResponse(message, language = 'auto') {
|
async getResponse(message, language = 'auto') {
|
||||||
try {
|
try {
|
||||||
|
console.log('getResponse called with:', { message, language });
|
||||||
|
|
||||||
// Определяем язык, если не указан явно
|
// Определяем язык, если не указан явно
|
||||||
const detectedLanguage = language === 'auto'
|
const detectedLanguage = language === 'auto'
|
||||||
? this.detectLanguage(message)
|
? this.detectLanguage(message)
|
||||||
: language;
|
: language;
|
||||||
|
|
||||||
const chat = this.createChat(detectedLanguage);
|
console.log('Detected language:', detectedLanguage);
|
||||||
|
|
||||||
|
// Сначала пробуем прямой API запрос
|
||||||
try {
|
try {
|
||||||
// Пробуем получить ответ через ChatOllama
|
console.log('Trying direct API request...');
|
||||||
|
const response = await this.fallbackRequest(message, detectedLanguage);
|
||||||
|
console.log('Direct API response received:', response);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in direct API request:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если прямой запрос не удался, пробуем через ChatOllama
|
||||||
|
const chat = this.createChat(detectedLanguage);
|
||||||
|
try {
|
||||||
|
console.log('Sending request to ChatOllama...');
|
||||||
const response = await chat.invoke(message);
|
const response = await chat.invoke(message);
|
||||||
|
console.log('ChatOllama response:', response);
|
||||||
return response.content;
|
return response.content;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error using ChatOllama:', error);
|
console.error('Error using ChatOllama:', error);
|
||||||
|
throw error;
|
||||||
// Пробуем альтернативный метод через прямой API
|
|
||||||
return await this.fallbackRequest(message, detectedLanguage);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in getResponse:', error);
|
console.error('Error in getResponse:', error);
|
||||||
return "Извините, я не смог обработать ваш запрос. Пожалуйста, попробуйте позже.";
|
return "Извините, я не смог обработать ваш запрос. Пожалуйста, попробуйте позже.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,12 +74,13 @@ class AIAssistant {
|
|||||||
// Альтернативный метод запроса через прямой API
|
// Альтернативный метод запроса через прямой API
|
||||||
async fallbackRequest(message, language) {
|
async fallbackRequest(message, language) {
|
||||||
try {
|
try {
|
||||||
logger.info('Using fallback request method');
|
console.log('Using fallback request method with:', { message, language });
|
||||||
|
|
||||||
const systemPrompt = language === 'ru'
|
const systemPrompt = language === 'ru'
|
||||||
? 'Вы - полезный ассистент. Отвечайте на русском языке.'
|
? 'Вы - полезный ассистент. Отвечайте на русском языке.'
|
||||||
: 'You are a helpful assistant. Respond in English.';
|
: 'You are a helpful assistant. Respond in English.';
|
||||||
|
|
||||||
|
console.log('Sending request to Ollama API...');
|
||||||
const response = await fetch(`${this.baseUrl}/api/generate`, {
|
const response = await fetch(`${this.baseUrl}/api/generate`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -70,14 +88,23 @@ class AIAssistant {
|
|||||||
model: this.defaultModel,
|
model: this.defaultModel,
|
||||||
prompt: message,
|
prompt: message,
|
||||||
system: systemPrompt,
|
system: systemPrompt,
|
||||||
stream: false
|
stream: false,
|
||||||
|
options: {
|
||||||
|
temperature: 0.7,
|
||||||
|
num_predict: 1000
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
console.log('Ollama API response:', data);
|
||||||
return data.response;
|
return data.response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in fallback request:', error);
|
console.error('Error in fallback request:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,11 +160,10 @@ input, textarea {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-left: 110px;
|
margin-left: 190px; /* 40px + 110px (sidebar) + 40px (button) */
|
||||||
|
margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */
|
||||||
transition: margin 0.3s ease;
|
transition: margin 0.3s ease;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
|
||||||
margin-left: 110px;
|
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -172,16 +171,16 @@ input, textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-expanded ~ .main-content {
|
.sidebar-expanded ~ .main-content {
|
||||||
margin-left: 325px;
|
margin-left: 190px; /* 40px + 110px (sidebar) + 40px (button) */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Стили для адаптации основного содержимого при скрытии правой панели */
|
/* Стили для адаптации основного содержимого при скрытии правой панели */
|
||||||
.main-content.no-right-sidebar {
|
.main-content.no-right-sidebar {
|
||||||
margin-right: 0;
|
margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content:not(.no-right-sidebar) {
|
.main-content:not(.no-right-sidebar) {
|
||||||
margin-right: 300px;
|
margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Стили для контейнера чата */
|
/* Стили для контейнера чата */
|
||||||
@@ -960,7 +959,7 @@ input, textarea {
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
margin-bottom: 40px; /* Увеличиваем отступ после заголовка */
|
margin-bottom: 24px; /* Уменьшенный отступ после заголовка */
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,7 +993,8 @@ input, textarea {
|
|||||||
/* Стили для основного контента */
|
/* Стили для основного контента */
|
||||||
.content-container {
|
.content-container {
|
||||||
padding: 20px 15px;
|
padding: 20px 15px;
|
||||||
margin-right: 300px; /* Учитываем ширину правой панели */
|
margin-right: 40px; /* Одинаковый отступ справа */
|
||||||
|
margin-left: 40px; /* Одинаковый отступ слева */
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
<div class="chat-container">
|
<div class="chat-container">
|
||||||
<div class="chat-messages" ref="messagesContainer">
|
<div class="chat-messages" ref="messagesContainer">
|
||||||
<div v-for="message in messages" :key="message.id"
|
<div v-for="message in messages" :key="message.id"
|
||||||
:class="['message', message.role === 'assistant' ? 'ai-message' : 'user-message']">
|
:class="['message', message.sender_type === 'assistant' || message.role === 'assistant' ? 'ai-message' : 'user-message']">
|
||||||
<div class="message-content" v-html="formatMessage(message.content)"></div>
|
<div class="message-content" v-html="formatMessage(message.content)"></div>
|
||||||
<div class="message-time">{{ formatTime(message.timestamp || message.created_at) }}</div>
|
<div class="message-time">{{ formatTime(message.timestamp || message.created_at) }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -213,6 +213,7 @@ const auth = useAuth();
|
|||||||
const isAuthenticated = computed(() => auth.isAuthenticated.value);
|
const isAuthenticated = computed(() => auth.isAuthenticated.value);
|
||||||
const isConnecting = ref(false);
|
const isConnecting = ref(false);
|
||||||
const messages = ref([]);
|
const messages = ref([]);
|
||||||
|
const hasUserSentMessage = ref(localStorage.getItem('hasUserSentMessage') === 'true');
|
||||||
const newMessage = ref('');
|
const newMessage = ref('');
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const messagesContainer = ref(null);
|
const messagesContainer = ref(null);
|
||||||
@@ -432,15 +433,18 @@ const handleMessage = async (text) => {
|
|||||||
|
|
||||||
if (!isAuthenticated.value) {
|
if (!isAuthenticated.value) {
|
||||||
// Сохраняем в таблицу guest_messages
|
// Сохраняем в таблицу guest_messages
|
||||||
|
console.log('Sending guest message:', messageContent);
|
||||||
const response = await api.post('/api/chat/guest-message', {
|
const response = await api.post('/api/chat/guest-message', {
|
||||||
message: messageContent,
|
message: messageContent,
|
||||||
language: userLanguage.value
|
language: userLanguage.value
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
|
console.log('Guest message sent:', response.data);
|
||||||
const userMessage = {
|
const userMessage = {
|
||||||
id: response.data.messageId,
|
id: response.data.messageId,
|
||||||
content: messageContent,
|
content: messageContent,
|
||||||
|
sender_type: 'user',
|
||||||
role: 'user',
|
role: 'user',
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
};
|
};
|
||||||
@@ -450,57 +454,65 @@ const handleMessage = async (text) => {
|
|||||||
messages.value.push({
|
messages.value.push({
|
||||||
id: Date.now() + 1,
|
id: Date.now() + 1,
|
||||||
content: 'Для получения ответа от ассистента, пожалуйста, авторизуйтесь одним из способов в правой панели.',
|
content: 'Для получения ответа от ассистента, пожалуйста, авторизуйтесь одним из способов в правой панели.',
|
||||||
|
sender_type: 'assistant',
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Устанавливаем флаг отправки сообщения
|
// Прокручиваем к последнему сообщению
|
||||||
if (!hasUserSentMessage.value) {
|
await nextTick();
|
||||||
hasUserSentMessage.value = true;
|
scrollToBottom();
|
||||||
localStorage.setItem('hasUserSentMessage', 'true');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.error || 'Ошибка при отправке сообщения');
|
throw new Error(response.data.error || 'Ошибка при отправке сообщения');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Для авторизованного пользователя сохраняем в messages
|
// Отправляем сообщение аутентифицированного пользователя
|
||||||
|
console.log('Sending authenticated message:', messageContent);
|
||||||
const response = await api.post('/api/chat/message', {
|
const response = await api.post('/api/chat/message', {
|
||||||
message: messageContent,
|
message: messageContent,
|
||||||
language: userLanguage.value
|
language: userLanguage.value
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
const message = {
|
console.log('Authenticated message sent:', response.data);
|
||||||
id: response.data.messageId,
|
// Добавляем сообщение пользователя
|
||||||
content: messageContent,
|
messages.value.push({
|
||||||
|
id: response.data.userMessage.id,
|
||||||
|
content: response.data.userMessage.content,
|
||||||
|
sender_type: 'user',
|
||||||
role: 'user',
|
role: 'user',
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: response.data.userMessage.created_at
|
||||||
hasResponse: true
|
});
|
||||||
};
|
|
||||||
messages.value.push(message);
|
|
||||||
|
|
||||||
const aiMessage = {
|
// Добавляем ответ ассистента
|
||||||
id: response.data.aiMessageId,
|
messages.value.push({
|
||||||
content: response.data.message,
|
id: response.data.aiMessage.id,
|
||||||
|
content: response.data.aiMessage.content,
|
||||||
|
sender_type: 'assistant',
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
timestamp: new Date().toISOString()
|
timestamp: response.data.aiMessage.created_at
|
||||||
};
|
});
|
||||||
messages.value.push(aiMessage);
|
|
||||||
|
// Прокручиваем к последнему сообщению
|
||||||
|
await nextTick();
|
||||||
|
scrollToBottom();
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.error || 'Ошибка при отправке сообщения');
|
throw new Error(response.data.error || 'Ошибка при отправке сообщения');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
scrollToBottom();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error sending message:', error);
|
console.error('Error sending message:', error);
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
id: Date.now(),
|
id: Date.now() + 1,
|
||||||
content: error.message || 'Произошла ошибка при отправке сообщения. Пожалуйста, попробуйте еще раз.',
|
content: 'Произошла ошибка при отправке сообщения. Пожалуйста, попробуйте позже.',
|
||||||
|
sender_type: 'assistant',
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Прокручиваем к последнему сообщению
|
||||||
|
await nextTick();
|
||||||
|
scrollToBottom();
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
@@ -631,10 +643,12 @@ const scrollToBottom = () => {
|
|||||||
|
|
||||||
// Загрузка сообщений
|
// Загрузка сообщений
|
||||||
const loadMoreMessages = async () => {
|
const loadMoreMessages = async () => {
|
||||||
if (!isAuthenticated.value) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isLoadingMore.value = true;
|
isLoadingMore.value = true;
|
||||||
|
console.log('Fetching chat history...');
|
||||||
|
|
||||||
|
// Всегда запрашиваем историю, так как на сервере проверяется наличие
|
||||||
|
// userId или guestId в сессии и возвращаются соответствующие сообщения
|
||||||
const response = await api.get('/api/chat/history', {
|
const response = await api.get('/api/chat/history', {
|
||||||
params: {
|
params: {
|
||||||
limit: limit.value,
|
limit: limit.value,
|
||||||
@@ -642,18 +656,39 @@ const loadMoreMessages = async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.success) {
|
console.log('Chat history response:', response.data);
|
||||||
const newMessages = response.data.messages.map(msg => ({
|
|
||||||
id: msg.id,
|
|
||||||
content: msg.content,
|
|
||||||
role: msg.role || (msg.sender_type === 'assistant' ? 'assistant' : 'user'),
|
|
||||||
timestamp: msg.created_at,
|
|
||||||
showAuthOptions: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
messages.value = [...messages.value, ...newMessages];
|
if (response.data.success) {
|
||||||
|
const newMessages = response.data.messages.map(msg => {
|
||||||
|
console.log('Processing message:', msg);
|
||||||
|
return {
|
||||||
|
id: msg.id,
|
||||||
|
content: msg.content,
|
||||||
|
sender_type: msg.sender_type || (msg.role === 'assistant' ? 'assistant' : 'user'),
|
||||||
|
role: msg.role || (msg.sender_type === 'assistant' ? 'assistant' : 'user'),
|
||||||
|
timestamp: msg.created_at,
|
||||||
|
showAuthOptions: false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Processed messages:', newMessages);
|
||||||
|
|
||||||
|
// Объединяем сообщения и сортируем их по timestamp
|
||||||
|
const allMessages = [...messages.value, ...newMessages];
|
||||||
|
allMessages.sort((a, b) => {
|
||||||
|
const timeA = new Date(a.timestamp || a.created_at).getTime();
|
||||||
|
const timeB = new Date(b.timestamp || b.created_at).getTime();
|
||||||
|
return timeA - timeB;
|
||||||
|
});
|
||||||
|
|
||||||
|
messages.value = allMessages;
|
||||||
|
console.log('Updated messages array:', messages.value);
|
||||||
hasMoreMessages.value = response.data.total > messages.value.length;
|
hasMoreMessages.value = response.data.total > messages.value.length;
|
||||||
offset.value += newMessages.length;
|
offset.value += newMessages.length;
|
||||||
|
|
||||||
|
// Прокручиваем к последнему сообщению
|
||||||
|
await nextTick();
|
||||||
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading chat history:', error);
|
console.error('Error loading chat history:', error);
|
||||||
@@ -663,23 +698,18 @@ const loadMoreMessages = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Загружаем сообщения при изменении аутентификации
|
// Загружаем сообщения при изменении аутентификации
|
||||||
watch(() => isAuthenticated.value, async (newValue) => {
|
watch(() => isAuthenticated.value, async (newValue, oldValue) => {
|
||||||
if (newValue) {
|
// Если пользователь только что авторизовался
|
||||||
messages.value = [];
|
if (newValue && !oldValue) {
|
||||||
offset.value = 0;
|
|
||||||
hasMoreMessages.value = true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Сначала загружаем историю из messages
|
|
||||||
await loadMoreMessages();
|
|
||||||
|
|
||||||
// Связываем гостевые сообщения (копируем из guest_messages в messages)
|
// Связываем гостевые сообщения (копируем из guest_messages в messages)
|
||||||
await api.post('/api/chat/link-guest-messages');
|
await api.post('/api/chat/link-guest-messages');
|
||||||
console.log('Guest messages linked to authenticated user');
|
console.log('Guest messages linked to authenticated user');
|
||||||
|
|
||||||
// Перезагружаем сообщения, чтобы получить все, включая перенесенные
|
// Перезагружаем все сообщения
|
||||||
messages.value = [];
|
messages.value = [];
|
||||||
offset.value = 0;
|
offset.value = 0;
|
||||||
|
hasMoreMessages.value = true;
|
||||||
await loadMoreMessages();
|
await loadMoreMessages();
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
@@ -687,10 +717,12 @@ watch(() => isAuthenticated.value, async (newValue) => {
|
|||||||
} catch (linkError) {
|
} catch (linkError) {
|
||||||
console.error('Error linking guest messages:', linkError);
|
console.error('Error linking guest messages:', linkError);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!newValue && oldValue) {
|
||||||
|
// Если пользователь вышел из системы, загружаем только гостевые сообщения
|
||||||
messages.value = [];
|
messages.value = [];
|
||||||
offset.value = 0;
|
offset.value = 0;
|
||||||
hasMoreMessages.value = true;
|
hasMoreMessages.value = true;
|
||||||
|
await loadMoreMessages(); // Загрузит гостевые сообщения, если они есть
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -751,8 +783,14 @@ const disconnectWallet = async () => {
|
|||||||
auth.telegramId = null;
|
auth.telegramId = null;
|
||||||
auth.email = null;
|
auth.email = null;
|
||||||
|
|
||||||
// Перезагружаем страницу для сброса состояния
|
// Загружаем только гостевые сообщения после выхода
|
||||||
window.location.reload();
|
messages.value = [];
|
||||||
|
offset.value = 0;
|
||||||
|
hasMoreMessages.value = true;
|
||||||
|
await loadMoreMessages();
|
||||||
|
|
||||||
|
// НЕ перезагружаем страницу, чтобы не потерять историю сообщений
|
||||||
|
// window.location.reload();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error disconnecting wallet:', error);
|
console.error('Error disconnecting wallet:', error);
|
||||||
}
|
}
|
||||||
@@ -792,13 +830,32 @@ const formatMessage = (text) => {
|
|||||||
return DOMPurify.sanitize(rawHtml);
|
return DOMPurify.sanitize(rawHtml);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Инициализация состояния правой панели при загрузке
|
// Функция для проверки наличия гостевых сообщений
|
||||||
onMounted(() => {
|
const checkGuestMessages = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/api/chat/check-session');
|
||||||
|
console.log('Session check response:', response.data);
|
||||||
|
|
||||||
|
// После инициализации сессии загружаем сообщения
|
||||||
|
if (!isAuthenticated.value) {
|
||||||
|
// Если пользователь не авторизован, попробуем загрузить гостевые сообщения
|
||||||
|
await loadMoreMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking guest messages:', error);
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Инициализация состояния при загрузке
|
||||||
|
onMounted(async () => {
|
||||||
// Загружаем состояние правой панели из localStorage
|
// Загружаем состояние правой панели из localStorage
|
||||||
const savedSidebarState = localStorage.getItem('showWalletSidebar');
|
const savedSidebarState = localStorage.getItem('showWalletSidebar');
|
||||||
if (savedSidebarState !== null) {
|
if (savedSidebarState !== null) {
|
||||||
showWalletSidebar.value = savedSidebarState === 'true';
|
showWalletSidebar.value = savedSidebarState === 'true';
|
||||||
} else {
|
} else {
|
||||||
// По умолчанию правая панель скрыта
|
// По умолчанию правая панель скрыта
|
||||||
showWalletSidebar.value = false;
|
showWalletSidebar.value = false;
|
||||||
}
|
}
|
||||||
@@ -807,6 +864,7 @@ onMounted(() => {
|
|||||||
if (messagesContainer.value) {
|
if (messagesContainer.value) {
|
||||||
messagesContainer.value.addEventListener('scroll', handleScroll);
|
messagesContainer.value.addEventListener('scroll', handleScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Auth state on mount:', {
|
console.log('Auth state on mount:', {
|
||||||
isAuthenticated: auth.isAuthenticated.value,
|
isAuthenticated: auth.isAuthenticated.value,
|
||||||
authType: auth.authType.value,
|
authType: auth.authType.value,
|
||||||
@@ -814,9 +872,12 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Проверяем статус авторизации
|
// Проверяем статус авторизации
|
||||||
auth.checkAuth();
|
await auth.checkAuth();
|
||||||
|
|
||||||
// Обновляем баланс при монтировании и изменении аутентификации
|
// Проверяем наличие гостевых сообщений и инициализируем сессию
|
||||||
|
await checkGuestMessages();
|
||||||
|
|
||||||
|
// Обновляем баланс при монтировании если авторизован
|
||||||
if (auth.isAuthenticated.value) {
|
if (auth.isAuthenticated.value) {
|
||||||
updateBalances();
|
updateBalances();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user