Skip to content

Commit 3ed8540

Browse files
committed
refactor(experiments): improve type safety for experiment configuration
Change ExperimentId type to be value-based rather than key-based Make experiment record types more strict with proper typing Pass full experiment config object instead of single boolean flag Update type definitions and usages across codebase
1 parent bb84d79 commit 3ed8540

File tree

6 files changed

+28
-24
lines changed

6 files changed

+28
-24
lines changed

src/core/Cline.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ import { OpenRouterHandler } from "../api/providers/openrouter"
6161
import { McpHub } from "../services/mcp/McpHub"
6262
import crypto from "crypto"
6363
import { insertGroups } from "./diff/insert-groups"
64-
import { EXPERIMENT_IDS } from "../shared/experiments"
64+
import { EXPERIMENT_IDS, experiments as Experiments } from "../shared/experiments"
6565

6666
const cwd =
6767
vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution
@@ -117,7 +117,7 @@ export class Cline {
117117
task?: string | undefined,
118118
images?: string[] | undefined,
119119
historyItem?: HistoryItem | undefined,
120-
experimentalDiffStrategy: boolean = false,
120+
experiments?: Record<string, boolean>,
121121
) {
122122
if (!task && !images && !historyItem) {
123123
throw new Error("Either historyItem or task/images must be provided")
@@ -139,7 +139,7 @@ export class Cline {
139139
}
140140

141141
// Initialize diffStrategy based on current state
142-
this.updateDiffStrategy(experimentalDiffStrategy)
142+
this.updateDiffStrategy(Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.DIFF_STRATEGY))
143143

144144
if (task || images) {
145145
this.startTask(task, images)

src/core/webview/ClineProvider.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
experimentConfigs,
4646
experiments as Experiments,
4747
experimentDefault,
48+
ExperimentId,
4849
} from "../../shared/experiments"
4950
import { CustomSupportPrompts, supportPrompt } from "../../shared/support-prompt"
5051

@@ -360,7 +361,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
360361
task,
361362
images,
362363
undefined,
363-
Experiments.isEnabled(experiments, EXPERIMENT_IDS.DIFF_STRATEGY),
364+
experiments,
364365
)
365366
}
366367

@@ -388,7 +389,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
388389
undefined,
389390
undefined,
390391
historyItem,
391-
Experiments.isEnabled(experiments, EXPERIMENT_IDS.DIFF_STRATEGY),
392+
experiments,
392393
)
393394
}
394395

@@ -1222,7 +1223,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
12221223
const updatedExperiments = {
12231224
...((await this.getGlobalState("experiments")) ?? experimentDefault),
12241225
...message.values,
1225-
}
1226+
} as Record<ExperimentId, boolean>
12261227

12271228
await this.updateGlobalState("experiments", updatedExperiments)
12281229

@@ -2132,7 +2133,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
21322133
this.getGlobalState("enhancementApiConfigId") as Promise<string | undefined>,
21332134
this.getGlobalState("autoApprovalEnabled") as Promise<boolean | undefined>,
21342135
this.customModesManager.getCustomModes(),
2135-
this.getGlobalState("experiments") as Promise<Record<string, boolean> | undefined>,
2136+
this.getGlobalState("experiments") as Promise<Record<ExperimentId, boolean> | undefined>,
21362137
])
21372138

21382139
let apiProvider: ApiProvider

src/core/webview/__tests__/ClineProvider.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ describe("ClineProvider", () => {
639639
"Test task",
640640
undefined,
641641
undefined,
642-
false,
642+
experimentDefault,
643643
)
644644
})
645645
test("handles mode-specific custom instructions updates", async () => {

src/shared/ExtensionMessage.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { McpServer } from "./mcp"
66
import { GitCommit } from "../utils/git"
77
import { Mode, CustomModePrompts, ModeConfig } from "./modes"
88
import { CustomSupportPrompts } from "./support-prompt"
9+
import { ExperimentId } from "./experiments"
910

1011
export interface LanguageModelChatSelector {
1112
vendor?: string
@@ -108,7 +109,7 @@ export interface ExtensionState {
108109
mode: Mode
109110
modeApiConfigs?: Record<Mode, string>
110111
enhancementApiConfigId?: string
111-
experiments: Record<string, boolean> // Map of experiment IDs to their enabled state
112+
experiments: Record<ExperimentId, boolean> // Map of experiment IDs to their enabled state
112113
autoApprovalEnabled?: boolean
113114
customModes: ModeConfig[]
114115
toolRequirements?: Record<string, boolean> // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled)

src/shared/experiments.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
export interface ExperimentConfig {
2-
id: string
3-
name: string
4-
description: string
5-
enabled: boolean
6-
}
7-
81
export const EXPERIMENT_IDS = {
92
DIFF_STRATEGY: "experimentalDiffStrategy",
103
SEARCH_AND_REPLACE: "search_and_replace",
114
INSERT_BLOCK: "insert_code_block",
125
} as const
136

14-
export type ExperimentId = keyof typeof EXPERIMENT_IDS
7+
export type ExperimentKey = keyof typeof EXPERIMENT_IDS
8+
export type ExperimentId = valueof<typeof EXPERIMENT_IDS>
9+
10+
export interface ExperimentConfig {
11+
id: ExperimentId
12+
name: string
13+
description: string
14+
enabled: boolean
15+
}
16+
17+
type valueof<X> = X[keyof X]
1518

16-
export const experimentConfigsMap: Record<ExperimentId, ExperimentConfig> = {
19+
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
1720
DIFF_STRATEGY: {
1821
id: EXPERIMENT_IDS.DIFF_STRATEGY,
1922
name: "Use experimental unified diff strategy",
@@ -42,13 +45,13 @@ export const experimentConfigsMap: Record<ExperimentId, ExperimentConfig> = {
4245
export const experimentConfigs = Object.values(experimentConfigsMap)
4346
export const experimentDefault = Object.fromEntries(
4447
Object.entries(experimentConfigsMap).map(([_, config]) => [config.id, config.enabled]),
45-
)
48+
) as Record<ExperimentId, boolean>
4649

4750
export const experiments = {
48-
get: (id: ExperimentId): ExperimentConfig | undefined => {
51+
get: (id: ExperimentKey): ExperimentConfig | undefined => {
4952
return experimentConfigsMap[id]
5053
},
51-
isEnabled: (experimentsConfig: Record<string, boolean>, id: string): boolean => {
54+
isEnabled: (experimentsConfig: Record<ExperimentId, boolean>, id: ExperimentId): boolean => {
5255
return experimentsConfig[id] ?? experimentDefault[id]
5356
},
5457
} as const

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { McpServer } from "../../../src/shared/mcp"
1616
import { checkExistKey } from "../../../src/shared/checkExistApiConfig"
1717
import { Mode, CustomModePrompts, defaultModeSlug, defaultPrompts, ModeConfig } from "../../../src/shared/modes"
1818
import { CustomSupportPrompts } from "../../../src/shared/support-prompt"
19-
import { experimentDefault } from "../../../src/shared/experiments"
19+
import { experimentDefault, ExperimentId } from "../../../src/shared/experiments"
2020

2121
export interface ExtensionStateContextType extends ExtensionState {
2222
didHydrateState: boolean
@@ -64,8 +64,7 @@ export interface ExtensionStateContextType extends ExtensionState {
6464
setCustomSupportPrompts: (value: CustomSupportPrompts) => void
6565
enhancementApiConfigId?: string
6666
setEnhancementApiConfigId: (value: string) => void
67-
experiments: Record<string, boolean>
68-
setExperimentEnabled: (id: string, enabled: boolean) => void
67+
setExperimentEnabled: (id: ExperimentId, enabled: boolean) => void
6968
setAutoApprovalEnabled: (value: boolean) => void
7069
handleInputChange: (field: keyof ApiConfiguration) => (event: any) => void
7170
customModes: ModeConfig[]

0 commit comments

Comments
 (0)