mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Exclusion mechanism
This commit is contained in:
parent
07ab2c1295
commit
f4abc8725a
5 changed files with 228 additions and 1 deletions
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
85
webview-ui/src/utils/settingsSearchExclusions.ts
Normal file
85
webview-ui/src/utils/settingsSearchExclusions.ts
Normal 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 }
|
||||
Loading…
Add table
Reference in a new issue