diff --git a/server/src/services/native-runtime/runner-api-catalog.ts b/server/src/services/native-runtime/runner-api-catalog.ts index d52681af90d..d90ad882b9c 100644 --- a/server/src/services/native-runtime/runner-api-catalog.ts +++ b/server/src/services/native-runtime/runner-api-catalog.ts @@ -34,8 +34,12 @@ const SYNONYMS: Record = { blocker: "dependencies", blockers: "dependencies", files: "attachments", }; +// Function words carry no resource meaning; scoring them let any summary that +// happens to say "for" or "the" outrank the operation a query names. +const SEARCH_FUNCTION_WORDS = new Set(["a", "an", "the", "for", "of", "to", "in", "on", "at", "by", "with", "and", "or", "from"]); + function words(text: string): string[] { - return text.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean) + return text.toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length > 0 && !SEARCH_FUNCTION_WORDS.has(word)) .flatMap((word) => [word, ...(SYNONYMS[word] ? [SYNONYMS[word]] : [])]) .map((word) => word.length > 3 && word.endsWith("s") ? word.slice(0, -1) : word); } diff --git a/server/src/services/native-runtime/runner-api.test.ts b/server/src/services/native-runtime/runner-api.test.ts index b08b92c611d..4c11333fe52 100644 --- a/server/src/services/native-runtime/runner-api.test.ts +++ b/server/src/services/native-runtime/runner-api.test.ts @@ -202,6 +202,18 @@ describe("runner API catalog", () => { expect(operation.authorization.actor).toBeTruthy(); }); + it("keeps natural-language function words from hiding the requested API resource", () => { + expect(searchRunnerApi({ query: "list comments for an issue" }).results[0]?.operationId) + .toBe("GET /api/issues/{id}/comments"); + for (const [query, operation] of [ + ["queued comments for an issue", "GET /api/issues/{id}/queued-comments"], + ["activity for an issue", "GET /api/issues/{id}/activity"], + ["runs for an issue", "GET /api/issues/{id}/runs"], + ["case for task", "GET /api/issues/{issueId}/cases"], + ]) expect(searchRunnerApi({ query }).results.map(row => row.operationId)).toContain(operation); + expect(searchRunnerApi({ query: "for an the" }).results).toEqual([]); + }); + it("ranks natural language, explains dedicated alternatives, and supports exact lookup", () => { expect( searchRunnerApi({ query: "create project" }).results.map(