mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
feat(codemode): rank whole-word path matches above substring matches in search (#50275)
This commit is contained in:
@@ -192,6 +192,8 @@ export type Prepared<R = never> = {
|
||||
|
||||
export type SearchEntry = {
|
||||
readonly description: ToolDescription
|
||||
/** The path split into words, so `zones` matches `get_zones` as a word rather than as a substring of `timezones`. */
|
||||
readonly pathWords: ReadonlyArray<string>
|
||||
readonly searchText: string
|
||||
}
|
||||
|
||||
@@ -247,6 +249,7 @@ const makeSearchTool = (searchIndex: ReadonlyArray<SearchEntry>): Tool => ({
|
||||
(total, forms) =>
|
||||
total +
|
||||
(forms.some((form) => path === form || path.endsWith(`.${form}`)) ? 20 : 0) +
|
||||
(forms.some((form) => entry.pathWords.includes(form)) ? 12 : 0) +
|
||||
(forms.some((form) => path.includes(form)) ? 8 : 0) +
|
||||
(forms.some((form) => description.includes(form)) ? 4 : 0) +
|
||||
(forms.some((form) => entry.searchText.includes(form)) ? 2 : 0),
|
||||
@@ -281,6 +284,7 @@ export const searchSignature = (() => {
|
||||
|
||||
const toSearchEntry = <R>(visible: VisibleTool<R>): SearchEntry => ({
|
||||
description: describeTool(visible),
|
||||
pathWords: tokenize(visible.path),
|
||||
searchText: [
|
||||
visible.path,
|
||||
visible.tool.description,
|
||||
|
||||
@@ -1039,6 +1039,32 @@ describe("CodeMode public contract", () => {
|
||||
}
|
||||
})
|
||||
|
||||
test("a query term that is a whole word of the path outranks a substring of it", async () => {
|
||||
const simple = (description: string) =>
|
||||
Tool.make({
|
||||
description,
|
||||
input: Schema.Struct({}),
|
||||
output: Schema.String,
|
||||
execute: () => Effect.succeed("ok"),
|
||||
})
|
||||
const runtime = CodeMode.make({
|
||||
tools: {
|
||||
// Declared so that alphabetical order would put the substring match first.
|
||||
cloudflare: { get_timezones: simple("List timezones"), get_zones: simple("List zones") },
|
||||
},
|
||||
})
|
||||
|
||||
const ranked = await Effect.runPromise(runtime.execute(`return search({ query: "zones" })`))
|
||||
expect(ranked.ok).toBe(true)
|
||||
if (ranked.ok) {
|
||||
const value = ranked.value as { items: Array<{ path: string }> }
|
||||
expect(value.items.map((item) => item.path)).toStrictEqual([
|
||||
"tools.cloudflare.get_zones",
|
||||
"tools.cloudflare.get_timezones",
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
test("a plural query term matches singular-only tool text", async () => {
|
||||
const simple = (description: string) =>
|
||||
Tool.make({
|
||||
|
||||
Reference in New Issue
Block a user