Skip to content

Commit ef2be7c

Browse files
committed
fix more
1 parent abbb8fd commit ef2be7c

File tree

5 files changed

+79
-144
lines changed

5 files changed

+79
-144
lines changed

src/api/providers/archgw.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { RouterProvider } from "./router-provider"
2121
*/
2222
export class ArchGwHandler extends RouterProvider implements SingleCompletionHandler {
2323
preferenceConfig?: string // Declare the property
24+
archgwUsePreferences: boolean
2425

2526
constructor(options: ApiHandlerOptions) {
2627
console.log("ArchGwHandler constructor called with options:", options)
@@ -34,6 +35,7 @@ export class ArchGwHandler extends RouterProvider implements SingleCompletionHan
3435
defaultModelInfo: archgwDefaultModelInfo,
3536
})
3637
this.preferenceConfig = options.archgwPreferenceConfig // Store the new parameter
38+
this.archgwUsePreferences = options.archgwUsePreferences || false // Store the preference flag
3739
}
3840

3941
override async *createMessage(
@@ -61,7 +63,7 @@ export class ArchGwHandler extends RouterProvider implements SingleCompletionHan
6163
},
6264
}
6365

64-
if (this.preferenceConfig) {
66+
if (this.archgwUsePreferences && this.preferenceConfig) {
6567
if (!requestOptions.metadata) {
6668
requestOptions.metadata = {}
6769
}

webview-ui/src/components/settings/providers/ArchGw.tsx

Lines changed: 39 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { useCallback, useState, useEffect, useRef } from "react"
1+
import { useCallback, useState, useEffect, useRef, useMemo } from "react"
22
import { VSCodeTextArea, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
33
import { Checkbox } from "vscrui"
44

5-
import * as yaml from "yaml"
6-
75
import { type ProviderSettings, type OrganizationAllowList, archgwDefaultModelId } from "@roo-code/types"
86

97
import { RouterName } from "@roo/api"
@@ -23,10 +21,17 @@ type ArchGwProps = {
2321
organizationAllowList: OrganizationAllowList
2422
}
2523

24+
import { validateArchGwPreferenceConfig } from "@src/utils/validate"
25+
2626
export const ArchGw = ({ apiConfiguration, setApiConfigurationField, organizationAllowList }: ArchGwProps) => {
2727
const { t } = useAppTranslation()
2828

29-
// No local default; always pull from URL
29+
const validateArchGwPreferences = useMemo(() => {
30+
const { archgwPreferenceConfig } = apiConfiguration
31+
return archgwPreferenceConfig
32+
? validateArchGwPreferenceConfig(archgwPreferenceConfig)
33+
: { isValid: true, errorMessage: undefined }
34+
}, [apiConfiguration])
3035

3136
const { routerModels } = useExtensionState()
3237
const [refreshStatus, setRefreshStatus] = useState<"idle" | "loading" | "success" | "error">("idle")
@@ -36,8 +41,6 @@ export const ArchGw = ({ apiConfiguration, setApiConfigurationField, organizatio
3641
const [archgwBaseUrlSelected, setArchgwBaseUrlSelected] = useState(!!apiConfiguration?.archgwBaseUrl)
3742
const [archgwUsePreferencesSelected, setArchgwUsePreferences] = useState(!!apiConfiguration?.archgwUsePreferences)
3843

39-
const [archgwPreferenceConfigError, setArchgwPreferenceConfigError] = useState<string | null>(null)
40-
4144
useEffect(() => {
4245
const handleMessage = (event: MessageEvent<ExtensionMessage>) => {
4346
const message = event.data
@@ -77,48 +80,6 @@ export const ArchGw = ({ apiConfiguration, setApiConfigurationField, organizatio
7780
[setApiConfigurationField],
7881
)
7982

80-
const handleArchgwPreferenceConfigInput = useCallback(
81-
(event: any) => {
82-
const value = event?.target?.value ?? ""
83-
try {
84-
// Only validate if not empty
85-
if (value.trim() !== "") {
86-
const parsed = yaml.parse(value)
87-
if (!Array.isArray(parsed)) {
88-
throw new Error(
89-
t("settings:providers.archgwPreferenceConfig.schemaError", {
90-
error: "YAML must be a list of objects with 'name', 'model' and 'usage'.",
91-
}),
92-
)
93-
}
94-
for (const item of parsed) {
95-
if (
96-
typeof item !== "object" ||
97-
typeof item.name !== "string" ||
98-
typeof item.model !== "string" ||
99-
typeof item.usage !== "string"
100-
) {
101-
throw new Error(
102-
t("settings:providers.archgwPreferenceConfig.schemaError", {
103-
error: "Each item must have 'name', 'model' and 'usage' as strings.",
104-
}),
105-
)
106-
}
107-
}
108-
}
109-
setArchgwPreferenceConfigError(null)
110-
setApiConfigurationField("archgwPreferenceConfig", value)
111-
} catch (err: any) {
112-
setArchgwPreferenceConfigError(
113-
t("settings:providers.archgwPreferenceConfig.yamlError", {
114-
error: err.message || String(err),
115-
}),
116-
)
117-
}
118-
},
119-
[setApiConfigurationField, t],
120-
)
121-
12283
const handleRefreshModels = useCallback(() => {
12384
archgwErrorJustReceived.current = false // Reset flag on new refresh action
12485
setRefreshStatus("loading")
@@ -217,20 +178,46 @@ export const ArchGw = ({ apiConfiguration, setApiConfigurationField, organizatio
217178

218179
setApiConfigurationField("archgwUsePreferences", checked)
219180
}}>
220-
{t("settings:providers.usePreferenceModel1")}
181+
{t("settings:providers.usePreferenceBasedRouting")}
221182
</Checkbox>
222183

223184
{archgwUsePreferencesSelected && (
224185
<>
225186
<div className="text-sm text-vscode-foreground">{t("settings:providers.routingConfig")}</div>
226187
<VSCodeTextArea
227188
value={apiConfiguration?.archgwPreferenceConfig || ""}
228-
onInput={handleArchgwPreferenceConfigInput}
189+
onInput={(e) =>
190+
setApiConfigurationField("archgwPreferenceConfig", (e.target as HTMLInputElement).value)
191+
}
229192
className="w-full font-mono text-sm"
230193
resize="vertical"
231194
/>
232-
{archgwPreferenceConfigError && (
233-
<div className="text-sm text-vscode-errorForeground mt-1">{archgwPreferenceConfigError}</div>
195+
196+
<div className="text-sm text-vscode-descriptionForeground -mt-2">
197+
{t("settings:providers.archgwPreferenceConfigUse")}
198+
<div>
199+
<pre style={{ whiteSpace: "pre-wrap" }}>
200+
{`- name: code generation
201+
model: openai/gpt-4.1
202+
usage: generating new code snippets
203+
- name: code understanding
204+
model: openai/gpt-4o-mini
205+
usage: understand and explain existing code snippets`}
206+
</pre>
207+
</div>
208+
{t("settings:providers.archgwPreferenceConfigDesc")}
209+
</div>
210+
211+
{!validateArchGwPreferences.isValid ? (
212+
<div className="text-sm text-vscode-errorForeground mt-2">
213+
{validateArchGwPreferences.errorMessage || t("settings:providers.invalidRoutingConfig")}
214+
</div>
215+
) : (
216+
validateArchGwPreferences.errorMessage && (
217+
<div className="text-sm text-vscode-errorForeground mt-2">
218+
{validateArchGwPreferences.errorMessage}
219+
</div>
220+
)
234221
)}
235222
</>
236223
)}

webview-ui/src/components/settings/providers/__tests__/ArchGw.spec.tsx

Lines changed: 0 additions & 90 deletions
This file was deleted.

webview-ui/src/i18n/locales/en/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
"nameExists": "A profile with this name already exists",
145145
"deleteProfile": "Delete Profile",
146146
"invalidArnFormat": "Invalid ARN format. Please check the examples above.",
147+
"invalidRoutingConfig": "Invalid routing config format.",
147148
"enterNewName": "Enter new name",
148149
"addProfile": "Add Profile",
149150
"renameProfile": "Rename Profile",
@@ -156,13 +157,15 @@
156157
"vscodeLmDescription": " The VS Code Language Model API allows you to run models provided by other VS Code extensions (including but not limited to GitHub Copilot). The easiest way to get started is to install the Copilot and Copilot Chat extensions from the VS Code Marketplace.",
157158
"awsCustomArnUse": "Enter a valid Amazon Bedrock ARN for the model you want to use. Format examples:",
158159
"awsCustomArnDesc": "Make sure the region in the ARN matches your selected AWS Region above.",
160+
"archgwPreferenceConfigUse": "Enter a valid YAML configuration for Arch Gateway preference-based routing. The configuration should include the route name, model ID and the usage preferences. For example:",
161+
"archgwPreferenceConfigDesc": "Make sure the model ID exists in your arch gateway configuration.",
159162
"openRouterApiKey": "OpenRouter API Key",
160163
"getOpenRouterApiKey": "Get OpenRouter API Key",
161164
"apiKeyStorageNotice": "API keys are stored securely in VSCode's Secret Storage",
162165
"glamaApiKey": "Glama API Key",
163166
"getGlamaApiKey": "Get Glama API Key",
164167
"useCustomBaseUrl": "Use custom base URL",
165-
"usePreferenceModel1": "Use preference based routing",
168+
"usePreferenceBasedRouting": "Use preference based routing",
166169
"routingConfig": "Routing Config",
167170
"archgwPreferenceConfig": {
168171
"schemaError": "Invalid routing config schema. Error: {{error}}",

webview-ui/src/utils/validate.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { ProviderSettings, OrganizationAllowList } from "@roo-code/types"
44

55
import { isRouterName, RouterModels } from "@roo/api"
66

7+
import * as yaml from "yaml"
8+
79
export function validateApiConfiguration(
810
apiConfiguration: ProviderSettings,
911
routerModels?: RouterModels,
@@ -305,3 +307,34 @@ export function validateApiConfigurationExcludingModelErrors(
305307
// skip model validation errors as they'll be shown in the model selector
306308
return undefined
307309
}
310+
311+
export function validateArchGwPreferenceConfig(archgwPreferenceConfig: string) {
312+
try {
313+
// Only validate if not empty
314+
if (archgwPreferenceConfig.trim() !== "") {
315+
const parsed = yaml.parse(archgwPreferenceConfig)
316+
if (!Array.isArray(parsed)) {
317+
return {
318+
isValid: false,
319+
errorMessage: "YAML must be a list of objects with 'name', 'model' and 'usage'.",
320+
}
321+
}
322+
for (const item of parsed) {
323+
if (
324+
typeof item !== "object" ||
325+
typeof item.name !== "string" ||
326+
typeof item.model !== "string" ||
327+
typeof item.usage !== "string"
328+
) {
329+
return {
330+
isValid: false,
331+
errorMessage: "Each item must have 'name', 'model' and 'usage' as strings.",
332+
}
333+
}
334+
}
335+
}
336+
} catch (err: any) {
337+
return { isValid: false, errorMessage: err.message || String(err) }
338+
}
339+
return { isValid: true, errorMessage: undefined }
340+
}

0 commit comments

Comments
 (0)