183 lines
5.4 KiB
TypeScript
183 lines
5.4 KiB
TypeScript
// src/tools/chat/history.ts
|
|
// Copyright (C) 2025 DTP Technologies, LLC
|
|
// All Rights Reserved
|
|
|
|
import type { ToolDefinition } from "../../lib/ai-client.js";
|
|
import type { IUser } from "../../models/user.js";
|
|
|
|
import {
|
|
DtpTool,
|
|
type ToolArguments,
|
|
type ToolContext,
|
|
} from "../../lib/tool.js";
|
|
import VectorStoreService from "../../services/vector-store.js";
|
|
import { ChatSessionMode } from "@/models/chat-session.js";
|
|
|
|
const COLLECTION_NAME = "chat-history";
|
|
|
|
export class ChatHistoryTool extends DtpTool {
|
|
get name(): string {
|
|
return "ChatHistoryTool";
|
|
}
|
|
get slug(): string {
|
|
return "chat-history";
|
|
}
|
|
get metadata() {
|
|
return {
|
|
name: this.definition.function.name || "chat_history",
|
|
category: "chat",
|
|
tags: ["history", "memory", "semantic", "search", "context"],
|
|
modes: [
|
|
ChatSessionMode.Plan,
|
|
ChatSessionMode.Build,
|
|
ChatSessionMode.Test,
|
|
ChatSessionMode.Ship,
|
|
ChatSessionMode.Develop,
|
|
],
|
|
};
|
|
}
|
|
|
|
public definition: ToolDefinition = {
|
|
type: "function",
|
|
function: {
|
|
name: "chat_history",
|
|
description:
|
|
"Search your conversation history with this user using semantic search. Use this tool to recall past discussions, find relevant context, or reference previous topics. The search uses semantic similarity, so describe what you're looking for in natural language. Returns up to 5 most relevant conversation turns with their content and relevance scores.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
query: {
|
|
type: "string",
|
|
description:
|
|
"A natural language description of what you're looking for in the conversation history. Example: 'discussions about API design' or 'when we talked about database schema'",
|
|
},
|
|
session_id: {
|
|
type: "string",
|
|
description:
|
|
"Optional: Limit search to a specific session ID. If omitted, searches across all sessions with this user.",
|
|
},
|
|
},
|
|
required: ["query"],
|
|
},
|
|
},
|
|
};
|
|
|
|
public async execute(
|
|
context: ToolContext,
|
|
args: ToolArguments,
|
|
): Promise<string> {
|
|
const { query, session_id } = args;
|
|
const user = context.session.user as IUser;
|
|
const userId = user._id.toHexString();
|
|
|
|
if (!query) {
|
|
return JSON.stringify({
|
|
success: false,
|
|
error: "MISSING_QUERY",
|
|
message: "The 'query' parameter is required.",
|
|
hint: 'Provide a natural language description of what you\'re looking for. Example: {"query": "discussions about authentication"}',
|
|
});
|
|
}
|
|
|
|
if (typeof query !== "string") {
|
|
return JSON.stringify({
|
|
success: false,
|
|
error: "INVALID_QUERY_TYPE",
|
|
message: `The 'query' parameter must be a string, but received ${typeof query}.`,
|
|
hint: "Ensure you pass a string value for the query parameter.",
|
|
});
|
|
}
|
|
|
|
if (query.trim().length < 3) {
|
|
return JSON.stringify({
|
|
success: false,
|
|
error: "QUERY_TOO_SHORT",
|
|
message:
|
|
"The query is too short. Please provide at least 3 characters.",
|
|
hint: "Use more descriptive terms to get better search results.",
|
|
});
|
|
}
|
|
|
|
if (
|
|
session_id !== undefined &&
|
|
session_id !== null &&
|
|
typeof session_id !== "string"
|
|
) {
|
|
return JSON.stringify({
|
|
success: false,
|
|
error: "INVALID_SESSION_ID_TYPE",
|
|
message: `The 'session_id' parameter must be a string if provided, but received ${typeof session_id}.`,
|
|
hint: "Either omit session_id or provide it as a string.",
|
|
});
|
|
}
|
|
|
|
this.log.debug("Searching chat history", {
|
|
userId,
|
|
sessionId: session_id,
|
|
query: query.trim(),
|
|
});
|
|
|
|
try {
|
|
const filter: Record<string, unknown> = { userId };
|
|
|
|
if (
|
|
session_id &&
|
|
typeof session_id === "string" &&
|
|
session_id.trim().length > 0
|
|
) {
|
|
filter.sessionId = session_id.trim();
|
|
}
|
|
|
|
const results = await VectorStoreService.search(
|
|
userId,
|
|
COLLECTION_NAME,
|
|
query.trim(),
|
|
5,
|
|
filter,
|
|
);
|
|
|
|
if (results.length === 0) {
|
|
return JSON.stringify({
|
|
success: true,
|
|
count: 0,
|
|
message: "No relevant history found for your query.",
|
|
hint: "Try using different keywords or broader search terms. If this is a new conversation, there may not be any history yet.",
|
|
});
|
|
}
|
|
|
|
const formattedResults = results.map((r) => ({
|
|
id: r.id,
|
|
content: r.content,
|
|
relevance: Math.round(r.score * 100) / 100,
|
|
}));
|
|
|
|
return JSON.stringify({
|
|
success: true,
|
|
count: results.length,
|
|
results: formattedResults,
|
|
message: `Found ${results.length} relevant conversation turn(s).`,
|
|
});
|
|
} catch (error) {
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : String(error);
|
|
const errorStack = error instanceof Error ? error.stack : undefined;
|
|
|
|
this.log.error("Failed to search chat history", {
|
|
userId,
|
|
query,
|
|
error: errorMessage,
|
|
stack: errorStack,
|
|
});
|
|
|
|
return JSON.stringify({
|
|
success: false,
|
|
error: "SEARCH_FAILED",
|
|
message: `Failed to search history: ${errorMessage}`,
|
|
hint: "This may be a temporary issue. Try again with a simpler query, or proceed without historical context.",
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
export default new ChatHistoryTool();
|