Skip to content

Commit 6745c8f

Browse files
authored
Merge branch 'RooCodeInc:main' into main
2 parents 74fd8b4 + 0f994fc commit 6745c8f

File tree

25 files changed

+199
-81
lines changed

25 files changed

+199
-81
lines changed

src/services/checkpoints/excludes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const getBuildArtifactPatterns = () => [
1111
".next/",
1212
".nuxt/",
1313
".sass-cache/",
14+
".terraform/",
15+
".terragrunt-cache/",
1416
".vs/",
1517
".vscode/",
1618
"Pods/",

src/services/code-index/embedders/ollama.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { withValidationErrorHandling, sanitizeErrorMessage } from "../shared/val
77
import { TelemetryService } from "@roo-code/telemetry"
88
import { TelemetryEventName } from "@roo-code/types"
99

10+
// Timeout constants for Ollama API requests
11+
const OLLAMA_EMBEDDING_TIMEOUT_MS = 60000 // 60 seconds for embedding requests
12+
const OLLAMA_VALIDATION_TIMEOUT_MS = 30000 // 30 seconds for validation requests
13+
1014
/**
1115
* Implements the IEmbedder interface using a local Ollama instance.
1216
*/
@@ -61,7 +65,7 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
6165

6266
// Add timeout to prevent indefinite hanging
6367
const controller = new AbortController()
64-
const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout
68+
const timeoutId = setTimeout(() => controller.abort(), OLLAMA_EMBEDDING_TIMEOUT_MS)
6569

6670
const response = await fetch(url, {
6771
method: "POST",
@@ -140,7 +144,7 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
140144

141145
// Add timeout to prevent indefinite hanging
142146
const controller = new AbortController()
143-
const timeoutId = setTimeout(() => controller.abort(), 5000) // 5 second timeout
147+
const timeoutId = setTimeout(() => controller.abort(), OLLAMA_VALIDATION_TIMEOUT_MS)
144148

145149
const modelsResponse = await fetch(modelsUrl, {
146150
method: "GET",
@@ -197,7 +201,7 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
197201

198202
// Add timeout for test request too
199203
const testController = new AbortController()
200-
const testTimeoutId = setTimeout(() => testController.abort(), 5000)
204+
const testTimeoutId = setTimeout(() => testController.abort(), OLLAMA_VALIDATION_TIMEOUT_MS)
201205

202206
const testResponse = await fetch(testUrl, {
203207
method: "POST",

webview-ui/src/components/settings/ApiOptions.tsx

Lines changed: 99 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from "react"
22
import { convertHeadersToObject } from "./utils/headers"
33
import { useDebounce } from "react-use"
44
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
5+
import { ExternalLinkIcon } from "@radix-ui/react-icons"
56

67
import {
78
type ProviderName,
@@ -31,8 +32,22 @@ import { useAppTranslation } from "@src/i18n/TranslationContext"
3132
import { useRouterModels } from "@src/components/ui/hooks/useRouterModels"
3233
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
3334
import { useExtensionState } from "@src/context/ExtensionStateContext"
35+
import {
36+
useOpenRouterModelProviders,
37+
OPENROUTER_DEFAULT_PROVIDER_NAME,
38+
} from "@src/components/ui/hooks/useOpenRouterModelProviders"
3439
import { filterProviders, filterModels } from "./utils/organizationFilters"
35-
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SearchableSelect } from "@src/components/ui"
40+
import {
41+
Select,
42+
SelectTrigger,
43+
SelectValue,
44+
SelectContent,
45+
SelectItem,
46+
SearchableSelect,
47+
Collapsible,
48+
CollapsibleTrigger,
49+
CollapsibleContent,
50+
} from "@src/components/ui"
3651

3752
import {
3853
Anthropic,
@@ -121,6 +136,7 @@ const ApiOptions = ({
121136
)
122137

123138
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
139+
const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false)
124140

125141
const handleInputChange = useCallback(
126142
<K extends keyof ProviderSettings, E>(
@@ -141,12 +157,20 @@ const ApiOptions = ({
141157

142158
const { data: routerModels, refetch: refetchRouterModels } = useRouterModels()
143159

160+
const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
161+
enabled:
162+
!!apiConfiguration?.openRouterModelId &&
163+
routerModels?.openrouter &&
164+
Object.keys(routerModels.openrouter).length > 1 &&
165+
apiConfiguration.openRouterModelId in routerModels.openrouter,
166+
})
167+
144168
// Update `apiModelId` whenever `selectedModelId` changes.
145169
useEffect(() => {
146-
if (selectedModelId) {
170+
if (selectedModelId && apiConfiguration.apiModelId !== selectedModelId) {
147171
setApiConfigurationField("apiModelId", selectedModelId)
148172
}
149-
}, [selectedModelId, setApiConfigurationField])
173+
}, [selectedModelId, setApiConfigurationField, apiConfiguration.apiModelId])
150174

151175
// Debounced refresh model updates, only executed 250ms after the user
152176
// stops typing.
@@ -534,30 +558,78 @@ const ApiOptions = ({
534558
/>
535559

536560
{!fromWelcomeView && (
537-
<>
538-
<DiffSettingsControl
539-
diffEnabled={apiConfiguration.diffEnabled}
540-
fuzzyMatchThreshold={apiConfiguration.fuzzyMatchThreshold}
541-
onChange={(field, value) => setApiConfigurationField(field, value)}
542-
/>
543-
<TemperatureControl
544-
value={apiConfiguration.modelTemperature}
545-
onChange={handleInputChange("modelTemperature", noTransform)}
546-
maxValue={2}
547-
/>
548-
<RateLimitSecondsControl
549-
value={apiConfiguration.rateLimitSeconds || 0}
550-
onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)}
551-
/>
552-
<ConsecutiveMistakeLimitControl
553-
value={
554-
apiConfiguration.consecutiveMistakeLimit !== undefined
555-
? apiConfiguration.consecutiveMistakeLimit
556-
: DEFAULT_CONSECUTIVE_MISTAKE_LIMIT
557-
}
558-
onChange={(value) => setApiConfigurationField("consecutiveMistakeLimit", value)}
559-
/>
560-
</>
561+
<Collapsible open={isAdvancedSettingsOpen} onOpenChange={setIsAdvancedSettingsOpen}>
562+
<CollapsibleTrigger className="flex items-center gap-1 w-full cursor-pointer hover:opacity-80 mb-2">
563+
<span className={`codicon codicon-chevron-${isAdvancedSettingsOpen ? "down" : "right"}`}></span>
564+
<span className="font-medium">{t("settings:advancedSettings.title")}</span>
565+
</CollapsibleTrigger>
566+
<CollapsibleContent className="space-y-3">
567+
<DiffSettingsControl
568+
diffEnabled={apiConfiguration.diffEnabled}
569+
fuzzyMatchThreshold={apiConfiguration.fuzzyMatchThreshold}
570+
onChange={(field, value) => setApiConfigurationField(field, value)}
571+
/>
572+
<TemperatureControl
573+
value={apiConfiguration.modelTemperature}
574+
onChange={handleInputChange("modelTemperature", noTransform)}
575+
maxValue={2}
576+
/>
577+
<RateLimitSecondsControl
578+
value={apiConfiguration.rateLimitSeconds || 0}
579+
onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)}
580+
/>
581+
<ConsecutiveMistakeLimitControl
582+
value={
583+
apiConfiguration.consecutiveMistakeLimit !== undefined
584+
? apiConfiguration.consecutiveMistakeLimit
585+
: DEFAULT_CONSECUTIVE_MISTAKE_LIMIT
586+
}
587+
onChange={(value) => setApiConfigurationField("consecutiveMistakeLimit", value)}
588+
/>
589+
{selectedProvider === "openrouter" &&
590+
openRouterModelProviders &&
591+
Object.keys(openRouterModelProviders).length > 0 && (
592+
<div>
593+
<div className="flex items-center gap-1">
594+
<label className="block font-medium mb-1">
595+
{t("settings:providers.openRouter.providerRouting.title")}
596+
</label>
597+
<a href={`https://openrouter.ai/${selectedModelId}/providers`}>
598+
<ExternalLinkIcon className="w-4 h-4" />
599+
</a>
600+
</div>
601+
<Select
602+
value={
603+
apiConfiguration?.openRouterSpecificProvider ||
604+
OPENROUTER_DEFAULT_PROVIDER_NAME
605+
}
606+
onValueChange={(value) =>
607+
setApiConfigurationField("openRouterSpecificProvider", value)
608+
}>
609+
<SelectTrigger className="w-full">
610+
<SelectValue placeholder={t("settings:common.select")} />
611+
</SelectTrigger>
612+
<SelectContent>
613+
<SelectItem value={OPENROUTER_DEFAULT_PROVIDER_NAME}>
614+
{OPENROUTER_DEFAULT_PROVIDER_NAME}
615+
</SelectItem>
616+
{Object.entries(openRouterModelProviders).map(([value, { label }]) => (
617+
<SelectItem key={value} value={value}>
618+
{label}
619+
</SelectItem>
620+
))}
621+
</SelectContent>
622+
</Select>
623+
<div className="text-sm text-vscode-descriptionForeground mt-1">
624+
{t("settings:providers.openRouter.providerRouting.description")}{" "}
625+
<a href="https://openrouter.ai/docs/features/provider-routing">
626+
{t("settings:providers.openRouter.providerRouting.learnMore")}.
627+
</a>
628+
</div>
629+
</div>
630+
)}
631+
</CollapsibleContent>
632+
</Collapsible>
561633
)}
562634
</div>
563635
)

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,15 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
218218
return prevState
219219
}
220220

221-
setChangeDetected(true)
221+
const previousValue = prevState.apiConfiguration?.[field]
222+
223+
// Don't treat initial sync from undefined to a defined value as a user change
224+
// This prevents the dirty state when the component initializes and auto-syncs the model ID
225+
const isInitialSync = previousValue === undefined && value !== undefined
226+
227+
if (!isInitialSync) {
228+
setChangeDetected(true)
229+
}
222230
return { ...prevState, apiConfiguration: { ...prevState.apiConfiguration, [field]: value } }
223231
})
224232
},

webview-ui/src/components/settings/__tests__/ApiOptions.spec.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ vi.mock("@/components/ui", () => ({
100100
</select>
101101
</div>
102102
),
103+
// Add Collapsible components
104+
Collapsible: ({ children, open }: any) => (
105+
<div className="collapsible-mock" data-open={open}>
106+
{children}
107+
</div>
108+
),
109+
CollapsibleTrigger: ({ children, className, onClick }: any) => (
110+
<div className={`collapsible-trigger-mock ${className || ""}`} onClick={onClick}>
111+
{children}
112+
</div>
113+
),
114+
CollapsibleContent: ({ children, className }: any) => (
115+
<div className={`collapsible-content-mock ${className || ""}`}>{children}</div>
116+
),
103117
}))
104118

105119
vi.mock("../TemperatureControl", () => ({

webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,20 @@ vi.mock("@/components/ui", () => ({
179179
{children}
180180
</button>
181181
),
182+
// Add Collapsible components
183+
Collapsible: ({ children, open }: any) => (
184+
<div className="collapsible-mock" data-open={open}>
185+
{children}
186+
</div>
187+
),
188+
CollapsibleTrigger: ({ children, className, onClick }: any) => (
189+
<div className={`collapsible-trigger-mock ${className || ""}`} onClick={onClick}>
190+
{children}
191+
</div>
192+
),
193+
CollapsibleContent: ({ children, className }: any) => (
194+
<div className={`collapsible-content-mock ${className || ""}`}>{children}</div>
195+
),
182196
}))
183197

184198
// Mock window.postMessage to trigger state hydration

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

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,14 @@ import { useCallback, useState } from "react"
22
import { Trans } from "react-i18next"
33
import { Checkbox } from "vscrui"
44
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
5-
import { ExternalLinkIcon } from "@radix-ui/react-icons"
65

76
import { type ProviderSettings, type OrganizationAllowList, openRouterDefaultModelId } from "@roo-code/types"
87

98
import type { RouterModels } from "@roo/api"
109

1110
import { useAppTranslation } from "@src/i18n/TranslationContext"
1211
import { getOpenRouterAuthUrl } from "@src/oauth/urls"
13-
import {
14-
useOpenRouterModelProviders,
15-
OPENROUTER_DEFAULT_PROVIDER_NAME,
16-
} from "@src/components/ui/hooks/useOpenRouterModelProviders"
1712
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
18-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
1913

2014
import { inputEventTransform, noTransform } from "../transforms"
2115

@@ -37,7 +31,6 @@ export const OpenRouter = ({
3731
apiConfiguration,
3832
setApiConfigurationField,
3933
routerModels,
40-
selectedModelId,
4134
uriScheme,
4235
fromWelcomeView,
4336
organizationAllowList,
@@ -58,14 +51,6 @@ export const OpenRouter = ({
5851
[setApiConfigurationField],
5952
)
6053

61-
const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
62-
enabled:
63-
!!apiConfiguration?.openRouterModelId &&
64-
routerModels?.openrouter &&
65-
Object.keys(routerModels.openrouter).length > 1 &&
66-
apiConfiguration.openRouterModelId in routerModels.openrouter,
67-
})
68-
6954
return (
7055
<>
7156
<VSCodeTextField
@@ -139,41 +124,6 @@ export const OpenRouter = ({
139124
organizationAllowList={organizationAllowList}
140125
errorMessage={modelValidationError}
141126
/>
142-
{openRouterModelProviders && Object.keys(openRouterModelProviders).length > 0 && (
143-
<div>
144-
<div className="flex items-center gap-1">
145-
<label className="block font-medium mb-1">
146-
{t("settings:providers.openRouter.providerRouting.title")}
147-
</label>
148-
<a href={`https://openrouter.ai/${selectedModelId}/providers`}>
149-
<ExternalLinkIcon className="w-4 h-4" />
150-
</a>
151-
</div>
152-
<Select
153-
value={apiConfiguration?.openRouterSpecificProvider || OPENROUTER_DEFAULT_PROVIDER_NAME}
154-
onValueChange={(value) => setApiConfigurationField("openRouterSpecificProvider", value)}>
155-
<SelectTrigger className="w-full">
156-
<SelectValue placeholder={t("settings:common.select")} />
157-
</SelectTrigger>
158-
<SelectContent>
159-
<SelectItem value={OPENROUTER_DEFAULT_PROVIDER_NAME}>
160-
{OPENROUTER_DEFAULT_PROVIDER_NAME}
161-
</SelectItem>
162-
{Object.entries(openRouterModelProviders).map(([value, { label }]) => (
163-
<SelectItem key={value} value={value}>
164-
{label}
165-
</SelectItem>
166-
))}
167-
</SelectContent>
168-
</Select>
169-
<div className="text-sm text-vscode-descriptionForeground mt-1">
170-
{t("settings:providers.openRouter.providerRouting.description")}{" "}
171-
<a href="https://openrouter.ai/docs/features/provider-routing">
172-
{t("settings:providers.openRouter.providerRouting.learnMore")}.
173-
</a>
174-
</div>
175-
</div>
176-
)}
177127
</>
178128
)
179129
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,9 @@
542542
"description": "Quan està habilitat, el terminal hereta les variables d'entorn del procés pare de VSCode, com ara la configuració d'integració del shell definida al perfil d'usuari. Això commuta directament la configuració global de VSCode `terminal.integrated.inheritEnv`. <0>Més informació</0>"
543543
}
544544
},
545+
"advancedSettings": {
546+
"title": "Configuració avançada"
547+
},
545548
"advanced": {
546549
"diff": {
547550
"label": "Habilitar edició mitjançant diffs",

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,9 @@
542542
"description": "Wenn aktiviert, erbt das Terminal Umgebungsvariablen aus dem übergeordneten Prozess von VSCode, wie z.B. benutzerdefinierte Shell-Integrationseinstellungen. Dies schaltet direkt die globale VSCode-Einstellung `terminal.integrated.inheritEnv` um. <0>Mehr erfahren</0>"
543543
}
544544
},
545+
"advancedSettings": {
546+
"title": "Erweiterte Einstellungen"
547+
},
545548
"advanced": {
546549
"diff": {
547550
"label": "Bearbeitung durch Diffs aktivieren",

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,9 @@
542542
"description": "When enabled, the terminal will inherit environment variables from VSCode's parent process, such as user-profile-defined shell integration settings. This directly toggles VSCode global setting `terminal.integrated.inheritEnv`. <0>Learn more</0>"
543543
}
544544
},
545+
"advancedSettings": {
546+
"title": "Advanced settings"
547+
},
545548
"advanced": {
546549
"diff": {
547550
"label": "Enable editing through diffs",

0 commit comments

Comments
 (0)