104 lines
4.7 KiB
JavaScript
104 lines
4.7 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.toolDefinition = exports.SearchMessagesInputSchema = void 0;
|
|
exports.toolHandler = toolHandler;
|
|
const zod_1 = require("zod");
|
|
const date_fns_1 = require("date-fns");
|
|
const locale_1 = require("date-fns/locale");
|
|
// Input schema for the search_messages tool
|
|
exports.SearchMessagesInputSchema = zod_1.z.object({
|
|
query: zod_1.z.string().describe('The natural language query to search for messages.'),
|
|
threadId: zod_1.z.string().optional().describe('Optional: The ID of the thread to search within. If not provided, searches the current active thread.'),
|
|
topK: zod_1.z.number().int().min(1).default(5).optional().describe('Optional: The maximum number of similar messages to return. Defaults to 5.'),
|
|
minScore: zod_1.z.number().min(0).max(1).default(0.6).optional().describe('Optional: The minimum similarity score (0-1) for results to be included. Defaults to 0.6.'),
|
|
});
|
|
// Tool definition for MCP
|
|
exports.toolDefinition = {
|
|
name: 'search_messages',
|
|
description: 'Semantically searches for relevant messages in the conversation history based on a natural language query.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
query: { type: 'string', description: 'The natural language query to search for messages.' },
|
|
threadId: { type: 'string', description: 'Optional: The ID of the thread to search within. If not provided, searches the current active thread.' },
|
|
topK: { type: 'number', description: 'Optional: The maximum number of similar messages to return. Defaults to 5.' },
|
|
minScore: { type: 'number', description: 'Optional: The minimum similarity score (0-1) for results to be included. Defaults to 0.6.' },
|
|
},
|
|
required: ['query'],
|
|
},
|
|
};
|
|
// Tool handler function
|
|
async function toolHandler(toolInvocation, threadManager) {
|
|
const parsedInput = exports.SearchMessagesInputSchema.safeParse(toolInvocation.input);
|
|
if (!parsedInput.success) {
|
|
return {
|
|
message: `Invalid input for search_messages: ${parsedInput.error.errors.map(e => e.message).join(', ')}`,
|
|
isError: true,
|
|
};
|
|
}
|
|
const { query, threadId, topK, minScore } = parsedInput.data;
|
|
try {
|
|
// Determine the thread ID to search within
|
|
let targetThreadId = threadId;
|
|
if (!targetThreadId) {
|
|
const activeThread = await threadManager.getCurrentThread();
|
|
if (activeThread.thread) {
|
|
targetThreadId = activeThread.thread.id;
|
|
}
|
|
else {
|
|
return {
|
|
message: 'No active thread found and no threadId provided. Cannot perform search.',
|
|
isError: true,
|
|
};
|
|
}
|
|
}
|
|
// Perform the search
|
|
const results = await threadManager.messagesDAOInstance.searchSimilar(query, {
|
|
threadId: targetThreadId,
|
|
topK,
|
|
minScore,
|
|
});
|
|
// Format the results for display
|
|
const formatted = results.map(msg => ({
|
|
id: msg.id,
|
|
role: msg.role,
|
|
content: msg.content.substring(0, 200) + (msg.content.length > 200 ? '...' : ''),
|
|
timestamp: (0, date_fns_1.formatDistanceToNow)(msg.timestamp, { locale: locale_1.zhCN, addSuffix: true }),
|
|
score: msg.score.toFixed(3),
|
|
threadId: msg.threadId
|
|
}));
|
|
const message = formatSearchResults(query, formatted);
|
|
return {
|
|
message,
|
|
info: JSON.stringify({
|
|
resultCount: results.length,
|
|
avgScore: results.length > 0 ? (results.reduce((sum, r) => sum + r.score, 0) / results.length).toFixed(3) : '0.000'
|
|
}, null, 2) // 格式化为可读的 JSON 字符串
|
|
};
|
|
}
|
|
catch (error) {
|
|
return {
|
|
message: `Error performing search: ${error.message}`,
|
|
isError: true,
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Formats search results into a human-readable Markdown string.
|
|
*/
|
|
function formatSearchResults(query, results) {
|
|
if (results.length === 0) {
|
|
return `🔍 未找到与 "${query}" 相关的消息。`;
|
|
}
|
|
let output = `### 🔍 搜索结果: "${query}"\n\n`;
|
|
output += `找到 ${results.length} 条相关消息:\n\n`;
|
|
results.forEach((result, index) => {
|
|
const icon = result.role === 'user' ? '👤' : '🤖';
|
|
const shortId = result.id.substring(0, 8);
|
|
output += `**${index + 1}. ${icon} ${result.role}** (相似度: ${result.score}) - ID: \`${shortId}\`\n`;
|
|
output += `🕒 ${result.timestamp}\n`;
|
|
output += `> ${result.content}\n\n`;
|
|
});
|
|
return output;
|
|
}
|