spa/.claude/skills/thread-manager/dist/tools/search-messages.js

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;
}