mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat(tool-search): expand dynamic group activation
This commit is contained in:
@@ -334,6 +334,17 @@ export function writeCachedCatalog(cachePath: string, catalog: ToolCatalog): voi
|
||||
renameSync(temporaryPath, cachePath);
|
||||
}
|
||||
|
||||
const GROUP_QUERY_ALIASES: Record<string, string[]> = {
|
||||
"context-execution": ["context mode", "run command", "test build logs", "执行命令", "运行测试", "分析日志"],
|
||||
"ssh-connection": ["ssh connect", "remote server connection", "连接服务器", "远程连接"],
|
||||
"ssh-remote-shell": ["ssh shell", "remote command", "remote terminal", "远程命令", "远程终端"],
|
||||
"ssh-remote-files": ["ssh file", "remote read write edit", "远程文件", "远程读写"],
|
||||
"ssh-remote-search": ["ssh search", "remote find grep", "远程搜索", "远程查找"],
|
||||
"chrome-navigation": ["chrome browser navigation", "browser tabs pages", "浏览器导航", "网页导航"],
|
||||
"chrome-interaction": ["chrome browser interaction", "click type form", "浏览器交互", "点击输入"],
|
||||
"chrome-debugging": ["chrome browser debugging", "console network screenshot", "浏览器调试", "控制台网络"],
|
||||
};
|
||||
|
||||
function queryTerms(value: string): Set<string> {
|
||||
const normalized = value.toLowerCase();
|
||||
const terms = new Set(normalized.match(/[\p{L}\p{N}]+/gu) ?? []);
|
||||
@@ -343,24 +354,92 @@ function queryTerms(value: string): Set<string> {
|
||||
return terms;
|
||||
}
|
||||
|
||||
export function rankGroups(catalog: ToolCatalog, query: string): Array<{ group: GroupCard; score: number }> {
|
||||
const querySet = queryTerms(query);
|
||||
const GENERIC_QUERY_TERMS = new Set([
|
||||
"edit",
|
||||
"execute",
|
||||
"fetch",
|
||||
"file",
|
||||
"files",
|
||||
"find",
|
||||
"get",
|
||||
"list",
|
||||
"read",
|
||||
"run",
|
||||
"search",
|
||||
"show",
|
||||
"tool",
|
||||
"tools",
|
||||
"web",
|
||||
"write",
|
||||
"列出",
|
||||
"写入",
|
||||
"工具",
|
||||
"执行",
|
||||
"搜索",
|
||||
"文件",
|
||||
"显示",
|
||||
"查找",
|
||||
"获取",
|
||||
"编辑",
|
||||
"网页",
|
||||
"读取",
|
||||
"运行",
|
||||
]);
|
||||
|
||||
function addTermWeights(target: Map<string, number>, querySet: Set<string>, value: string, weight: number): void {
|
||||
const terms = queryTerms(value);
|
||||
for (const term of querySet) {
|
||||
if (terms.has(term)) target.set(term, Math.max(target.get(term) ?? 0, weight));
|
||||
}
|
||||
}
|
||||
|
||||
export interface RankedGroup {
|
||||
group: GroupCard;
|
||||
score: number;
|
||||
matchedTerms: string[];
|
||||
}
|
||||
|
||||
export function rankGroups(catalog: ToolCatalog, query: string): RankedGroup[] {
|
||||
const normalizedQuery = query.trim().toLowerCase();
|
||||
const querySet = queryTerms(normalizedQuery);
|
||||
return catalog.groups
|
||||
.map((group) => {
|
||||
const cards = catalog.tools.filter((tool) => tool.primaryGroup === group.id);
|
||||
const searchable = [
|
||||
group.id,
|
||||
group.title,
|
||||
group.summary,
|
||||
...group.useWhen,
|
||||
...group.avoidWhen,
|
||||
...cards.flatMap((tool) => [tool.name, tool.summary, ...tool.useWhen, ...tool.keywords]),
|
||||
].join(" ");
|
||||
const terms = queryTerms(searchable);
|
||||
let score = 0;
|
||||
for (const term of querySet) if (terms.has(term)) score += term.length > 1 ? 3 : 1;
|
||||
if (group.id === query.trim().toLowerCase()) score += 100;
|
||||
return { group, score };
|
||||
const aliases = GROUP_QUERY_ALIASES[group.id] ?? [];
|
||||
const termWeights = new Map<string, number>();
|
||||
addTermWeights(termWeights, querySet, group.id, 8);
|
||||
addTermWeights(termWeights, querySet, group.title, 6);
|
||||
addTermWeights(termWeights, querySet, group.summary, 2);
|
||||
addTermWeights(termWeights, querySet, group.useWhen.join(" "), 3);
|
||||
addTermWeights(termWeights, querySet, group.avoidWhen.join(" "), 1);
|
||||
addTermWeights(termWeights, querySet, aliases.join(" "), 5);
|
||||
for (const card of cards) {
|
||||
addTermWeights(termWeights, querySet, card.name, 10);
|
||||
addTermWeights(termWeights, querySet, card.summary, 2);
|
||||
addTermWeights(termWeights, querySet, card.useWhen.join(" "), 3);
|
||||
addTermWeights(termWeights, querySet, card.keywords.join(" "), 5);
|
||||
}
|
||||
let score = [...termWeights].reduce(
|
||||
(total, [term, weight]) => total + (term.length > 1 ? 3 : 1) * weight,
|
||||
0,
|
||||
);
|
||||
if (cards.some((card) => normalizedQuery === card.name.toLowerCase())) score += 800;
|
||||
if (group.id === normalizedQuery) score += 1_000;
|
||||
if (aliases.some((alias) => alias.toLowerCase() === normalizedQuery)) score += 500;
|
||||
return { group, score, matchedTerms: [...termWeights.keys()] };
|
||||
})
|
||||
.sort((left, right) => right.score - left.score || left.group.id.localeCompare(right.group.id));
|
||||
}
|
||||
|
||||
/** Avoid auto-activating a generic, weak, or ambiguous natural-language match. */
|
||||
export function selectConfidentGroup(ranked: RankedGroup[], _query: string): GroupCard | undefined {
|
||||
const [first, second] = ranked;
|
||||
if (!first || first.score < 24) return undefined;
|
||||
if (first.score >= 500) return first.group;
|
||||
const informativeTerms = first.matchedTerms.filter(
|
||||
(term) => term.length > 1 && !GENERIC_QUERY_TERMS.has(term),
|
||||
);
|
||||
if (informativeTerms.length === 0) return undefined;
|
||||
if (second && first.score - second.score < 12) return undefined;
|
||||
return first.group;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user