Exclusion mechanism

This commit is contained in:
Bruno Bergher 2026-01-11 08:13:45 +00:00
parent 07ab2c1295
commit f4abc8725a
5 changed files with 228 additions and 1 deletions

View file

@ -0,0 +1,54 @@
// npx vitest run src/hooks/__tests__/useSettingsSearch.exclusions.spec.ts
import { renderHook } from "@testing-library/react"
import { useSettingsSearch } from "../useSettingsSearch"
// Mock react-i18next
vi.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}))
// Mock settings data to include excluded paths and normal settings
vi.mock("@/i18n/locales/en/settings.json", () => ({
default: {
modelInfo: {
inputPrice: "Input price",
outputPrice: "Output price",
},
validation: {
apiKey: "You must provide a valid API key",
},
browser: {
enable: {
label: "Enable browser tool",
description: "Allows Roo to use a browser",
},
},
},
}))
describe("useSettingsSearch - exclusions", () => {
it("does not return excluded modelInfo entries", () => {
const { result } = renderHook(() => useSettingsSearch("price"))
const modelInfoResults = result.current.filter((r) => r.id.startsWith("modelInfo."))
expect(modelInfoResults).toHaveLength(0)
})
it("does not return excluded validation entries", () => {
const { result } = renderHook(() => useSettingsSearch("api key"))
const validationResults = result.current.filter((r) => r.id.startsWith("validation."))
expect(validationResults).toHaveLength(0)
})
it("still returns actionable settings", () => {
const { result } = renderHook(() => useSettingsSearch("browser"))
const browserResult = result.current.find((r) => r.id === "browser.enable")
expect(browserResult).toBeDefined()
})
})

View file

@ -1,6 +1,35 @@
import { parseSettingsI18nKeys, type SectionName, sectionNames } from "../parseSettingsI18nKeys"
describe("parseSettingsI18nKeys", () => {
it("should exclude display-only and helper entries", () => {
const translations = {
modelInfo: {
inputPrice: "Input price",
},
validation: {
apiKey: "You must provide an API key",
},
placeholders: {
apiKey: "Enter API Key",
},
browser: {
enable: {
label: "Enable browser tool",
},
},
}
const results = parseSettingsI18nKeys(translations)
// Excluded categories
expect(results.find((r) => r.id.startsWith("modelInfo."))).toBeUndefined()
expect(results.find((r) => r.id.startsWith("validation."))).toBeUndefined()
expect(results.find((r) => r.id.startsWith("placeholders."))).toBeUndefined()
// Included actionable setting
expect(results.find((r) => r.id === "browser.enable")).toBeDefined()
})
describe("basic parsing functionality", () => {
it("should parse settings with label property", () => {
const translations = {

View file

@ -0,0 +1,55 @@
import { describe, it, expect } from "vitest"
import { getExclusionReason, shouldExcludeFromSearch } from "../settingsSearchExclusions"
describe("settingsSearchExclusions", () => {
describe("shouldExcludeFromSearch", () => {
it("excludes modelInfo display fields", () => {
expect(shouldExcludeFromSearch("modelInfo.inputPrice")).toBe(true)
expect(shouldExcludeFromSearch("modelInfo.outputPrice")).toBe(true)
expect(shouldExcludeFromSearch("modelInfo.contextWindow")).toBe(true)
})
it("excludes validation messages", () => {
expect(shouldExcludeFromSearch("validation.apiKey")).toBe(true)
expect(shouldExcludeFromSearch("validation.modelId")).toBe(true)
})
it("excludes placeholders", () => {
expect(shouldExcludeFromSearch("placeholders.apiKey")).toBe(true)
expect(shouldExcludeFromSearch("placeholders.baseUrl")).toBe(true)
})
it("excludes custom model pricing", () => {
expect(shouldExcludeFromSearch("providers.customModel.pricing.input")).toBe(true)
expect(shouldExcludeFromSearch("providers.customModel.pricing.output")).toBe(true)
})
it("excludes service tier display-only entries", () => {
expect(shouldExcludeFromSearch("serviceTier.columns.tier")).toBe(true)
expect(shouldExcludeFromSearch("serviceTier.pricingTableTitle")).toBe(true)
})
it("does not exclude actionable settings", () => {
expect(shouldExcludeFromSearch("browser.enable")).toBe(false)
expect(shouldExcludeFromSearch("providers.apiProvider")).toBe(false)
expect(shouldExcludeFromSearch("terminal.outputLineLimit")).toBe(false)
})
it("does not exclude settings that merely contain keywords", () => {
expect(shouldExcludeFromSearch("providers.enablePromptCaching")).toBe(false)
})
})
describe("getExclusionReason", () => {
it("returns reason for excluded ids", () => {
const reason = getExclusionReason("modelInfo.inputPrice")
expect(reason).toBeDefined()
expect(reason?.length).toBeGreaterThan(0)
})
it("returns undefined for included ids", () => {
expect(getExclusionReason("browser.enable")).toBeUndefined()
})
})
})

View file

@ -1,3 +1,5 @@
import { shouldExcludeFromSearch } from "./settingsSearchExclusions"
/**
* Utility for parsing i18n translation structure to extract searchable settings information.
*
@ -384,5 +386,7 @@ export function parseSettingsI18nKeys(
}
}
return results
const filteredResults = results.filter((setting) => !shouldExcludeFromSearch(setting.id))
return filteredResults
}

View file

@ -0,0 +1,85 @@
export interface ExclusionRule {
/** Pattern to match against a parsed setting id */
pattern: string | RegExp
/** Human-readable reason for exclusion */
reason: string
/** Example ids matched by this rule */
examples?: string[]
}
const SEARCH_EXCLUSIONS: ExclusionRule[] = [
{
pattern: /^modelInfo\./,
reason: "Model information is display-only and not configurable",
examples: ["modelInfo.inputPrice", "modelInfo.outputPrice", "modelInfo.contextWindow"],
},
{
pattern: /^providers\.customModel\.pricing\./,
reason: "Custom model pricing fields are display-focused and should not clutter search",
examples: ["providers.customModel.pricing.input", "providers.customModel.pricing.output"],
},
{
pattern: /^validation\./,
reason: "Validation messages are error text, not settings",
examples: ["validation.apiKey", "validation.modelId"],
},
{
pattern: /^placeholders\./,
reason: "Placeholder text is helper content, not a setting",
examples: ["placeholders.apiKey", "placeholders.baseUrl"],
},
{
pattern: /^defaults\./,
reason: "Default value descriptions are informational only",
examples: ["defaults.ollamaUrl", "defaults.lmStudioUrl"],
},
{
pattern: /^labels\./,
reason: "Generic labels are helper text, not settings",
examples: ["labels.customArn", "labels.useCustomArn"],
},
{
pattern: /^thinkingBudget\./,
reason: "Thinking budget entries are display-only",
examples: ["thinkingBudget.maxTokens", "thinkingBudget.maxThinkingTokens"],
},
{
pattern: /^serviceTier\.columns\./,
reason: "Service tier column headers are display-only",
examples: ["serviceTier.columns.tier", "serviceTier.columns.input"],
},
{
pattern: /^serviceTier\.pricingTableTitle$/,
reason: "Service tier table title is display-only",
examples: ["serviceTier.pricingTableTitle"],
},
{
pattern: /^modelPicker\.simplifiedExplanation$/,
reason: "Model picker helper text is informational",
examples: ["modelPicker.simplifiedExplanation"],
},
{
pattern: /^modelInfo\.gemini\.(freeRequests|pricingDetails|billingEstimate)$/,
reason: "Gemini pricing notes are display-only",
examples: ["modelInfo.gemini.freeRequests", "modelInfo.gemini.pricingDetails"],
},
]
function matchesRule(settingId: string, rule: ExclusionRule): boolean {
if (typeof rule.pattern === "string") {
return settingId === rule.pattern
}
return rule.pattern.test(settingId)
}
export function shouldExcludeFromSearch(settingId: string): boolean {
return SEARCH_EXCLUSIONS.some((rule) => matchesRule(settingId, rule))
}
export function getExclusionReason(settingId: string): string | undefined {
const rule = SEARCH_EXCLUSIONS.find((candidate) => matchesRule(settingId, candidate))
return rule?.reason
}
export { SEARCH_EXCLUSIONS }