feat(ai): add conversation history search

This commit is contained in:
Lan-zk authored and GitHub committed 2026-08-13 16:04:11 +08:00
1 parent 1c89373035
commit 7a01a440b8
11 files changed
+117 -2

No files matched your search

@@ -0,0 +1,46 @@
import { describe, expect, it } from "vitest";
import { buildAiConversationSearchIndex, filterAiConversationSearchIndex } from "@/lib/ai/aiConversationSearch";
const conversations = [
{
id: "mysql-users",
title: "Repair user records",
connectionName: "dev-mysql",
database: "accounts",
messages: [
{ role: "user", content: "How can I find duplicate email addresses?" },
{ role: "assistant", content: "Use a GROUP BY query with HAVING COUNT(*) > 1." },
],
},
{
id: "postgres-orders",
title: "订单排查",
connectionName: "prod-postgres",
database: "commerce",
messages: [{ role: "user", content: "查询昨天失败的订单" }],
},
];
describe("AI conversation search index", () => {
const index = buildAiConversationSearchIndex(conversations);
it("finds history by title, connection, database, or message text without case sensitivity", () => {
expect(filterAiConversationSearchIndex(index, " REPAIR ").map((conversation) => conversation.id)).toEqual(["mysql-users"]);
expect(filterAiConversationSearchIndex(index, "POSTGRES").map((conversation) => conversation.id)).toEqual(["postgres-orders"]);
expect(filterAiConversationSearchIndex(index, "ACCOUNTS").map((conversation) => conversation.id)).toEqual(["mysql-users"]);
expect(filterAiConversationSearchIndex(index, "duplicate EMAIL").map((conversation) => conversation.id)).toEqual(["mysql-users"]);
expect(filterAiConversationSearchIndex(index, "失败的订单").map((conversation) => conversation.id)).toEqual(["postgres-orders"]);
});
it("restores the complete history when the search is cleared", () => {
expect(filterAiConversationSearchIndex(index, " ")).toEqual(conversations);
});
it("returns an empty history when no conversation matches", () => {
expect(filterAiConversationSearchIndex(index, "oracle")).toEqual([]);
});
it("precomputes normalized searchable text once per conversation", () => {
expect(index.map((entry) => entry.searchText)).toEqual(["repair user records\ndev-mysql\naccounts\nhow can i find duplicate email addresses?\nuse a group by query with having count(*) > 1.", "订单排查\nprod-postgres\ncommerce\n查询昨天失败的订单"]);
});
});
@@ -0,0 +1,25 @@
export interface AiConversationSearchItem {
title: string;
connectionName: string;
database: string;
messages: readonly { content: string }[];
}
export interface AiConversationSearchEntry<T extends AiConversationSearchItem> {
conversation: T;
searchText: string;
}
export function buildAiConversationSearchIndex<T extends AiConversationSearchItem>(conversations: readonly T[]): AiConversationSearchEntry<T>[] {
return conversations.map((conversation) => ({
conversation,
searchText: [conversation.title, conversation.connectionName, conversation.database, ...conversation.messages.map((message) => message.content)].join("\n").toLowerCase(),
}));
}
export function filterAiConversationSearchIndex<T extends AiConversationSearchItem>(index: readonly AiConversationSearchEntry<T>[], query: string): T[] {
const normalizedQuery = query.trim().toLowerCase();
if (!normalizedQuery) return index.map((entry) => entry.conversation);
return index.filter((entry) => entry.searchText.includes(normalizedQuery)).map((entry) => entry.conversation);
}