mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-08 03:07:53 +00:00
feat: add configurable timeout settings for Ollama embedder
- Add timeout configuration options for Ollama embedding and validation operations - Default to 30s for embedding requests and 10s for validation requests - Add UI components in CodeIndexPopover for timeout configuration with validation - Update configuration interfaces and type definitions to support timeout settings - Integrate timeout settings into existing configuration management pipeline - Add comprehensive test coverage for timeout functionality - Add translation support for timeout settings Fixes #5733
This commit is contained in:
parent
8a3dcfb593
commit
7647fd168f
10 changed files with 175 additions and 9 deletions
|
|
@ -34,6 +34,9 @@ export const codebaseIndexConfigSchema = z.object({
|
|||
// OpenAI Compatible specific fields
|
||||
codebaseIndexOpenAiCompatibleBaseUrl: z.string().optional(),
|
||||
codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(),
|
||||
// Ollama timeout settings for codebase indexing
|
||||
codebaseIndexOllamaEmbeddingTimeoutMs: z.number().int().min(1000).max(300000).optional(),
|
||||
codebaseIndexOllamaValidationTimeoutMs: z.number().int().min(1000).max(60000).optional(),
|
||||
})
|
||||
|
||||
export type CodebaseIndexConfig = z.infer<typeof codebaseIndexConfigSchema>
|
||||
|
|
@ -62,6 +65,9 @@ export const codebaseIndexProviderSchema = z.object({
|
|||
codebaseIndexOpenAiCompatibleApiKey: z.string().optional(),
|
||||
codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(),
|
||||
codebaseIndexGeminiApiKey: z.string().optional(),
|
||||
// Ollama timeout settings for codebase indexing
|
||||
codebaseIndexOllamaEmbeddingTimeoutMs: z.number().int().min(1000).max(300000).optional(),
|
||||
codebaseIndexOllamaValidationTimeoutMs: z.number().int().min(1000).max(60000).optional(),
|
||||
})
|
||||
|
||||
export type CodebaseIndexProvider = z.infer<typeof codebaseIndexProviderSchema>
|
||||
|
|
|
|||
|
|
@ -1966,6 +1966,20 @@ export const webviewMessageHandler = async (
|
|||
codebaseIndexSearchMinScore: settings.codebaseIndexSearchMinScore,
|
||||
}
|
||||
|
||||
// Save Ollama timeout settings to global state
|
||||
if (settings.codebaseIndexOllamaEmbeddingTimeoutMs !== undefined) {
|
||||
await updateGlobalState(
|
||||
"codebaseIndexOllamaEmbeddingTimeoutMs",
|
||||
settings.codebaseIndexOllamaEmbeddingTimeoutMs,
|
||||
)
|
||||
}
|
||||
if (settings.codebaseIndexOllamaValidationTimeoutMs !== undefined) {
|
||||
await updateGlobalState(
|
||||
"codebaseIndexOllamaValidationTimeoutMs",
|
||||
settings.codebaseIndexOllamaValidationTimeoutMs,
|
||||
)
|
||||
}
|
||||
|
||||
// Save global state first
|
||||
await updateGlobalState("codebaseIndexConfig", globalStateConfig)
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,32 @@ describe("CodeIndexServiceFactory", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("should pass timeout parameters to Ollama embedder when configured", () => {
|
||||
// Arrange
|
||||
const testModelId = "nomic-embed-text:latest"
|
||||
const testConfig = {
|
||||
embedderProvider: "ollama",
|
||||
modelId: testModelId,
|
||||
ollamaOptions: {
|
||||
ollamaBaseUrl: "http://localhost:11434",
|
||||
embeddingTimeoutMs: 45000,
|
||||
validationTimeoutMs: 15000,
|
||||
},
|
||||
}
|
||||
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
|
||||
|
||||
// Act
|
||||
factory.createEmbedder()
|
||||
|
||||
// Assert
|
||||
expect(MockedCodeIndexOllamaEmbedder).toHaveBeenCalledWith({
|
||||
ollamaBaseUrl: "http://localhost:11434",
|
||||
ollamaModelId: testModelId,
|
||||
embeddingTimeoutMs: 45000,
|
||||
validationTimeoutMs: 15000,
|
||||
})
|
||||
})
|
||||
|
||||
it("should throw error when OpenAI API key is missing", () => {
|
||||
// Arrange
|
||||
const testConfig = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ApiHandlerOptions } from "../../shared/api"
|
||||
import { ContextProxy } from "../../core/config/ContextProxy"
|
||||
import { EmbedderProvider } from "./interfaces/manager"
|
||||
import { CodeIndexConfig, PreviousConfigSnapshot } from "./interfaces/config"
|
||||
import { CodeIndexConfig, PreviousConfigSnapshot, OllamaConfigOptions } from "./interfaces/config"
|
||||
import { DEFAULT_SEARCH_MIN_SCORE, DEFAULT_MAX_SEARCH_RESULTS } from "./constants"
|
||||
import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "../../shared/embeddingModels"
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ export class CodeIndexConfigManager {
|
|||
private modelId?: string
|
||||
private modelDimension?: number
|
||||
private openAiOptions?: ApiHandlerOptions
|
||||
private ollamaOptions?: ApiHandlerOptions
|
||||
private ollamaOptions?: OllamaConfigOptions
|
||||
private openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
|
||||
private geminiOptions?: { apiKey: string }
|
||||
private qdrantUrl?: string = "http://localhost:6333"
|
||||
|
|
@ -68,6 +68,10 @@ export class CodeIndexConfigManager {
|
|||
const openAiCompatibleApiKey = this.contextProxy?.getSecret("codebaseIndexOpenAiCompatibleApiKey") ?? ""
|
||||
const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
|
||||
|
||||
// Get Ollama timeout settings from global state
|
||||
const ollamaEmbeddingTimeoutMs = this.contextProxy?.getGlobalState("codebaseIndexOllamaEmbeddingTimeoutMs")
|
||||
const ollamaValidationTimeoutMs = this.contextProxy?.getGlobalState("codebaseIndexOllamaValidationTimeoutMs")
|
||||
|
||||
// Update instance variables with configuration
|
||||
this.codebaseIndexEnabled = codebaseIndexEnabled ?? true
|
||||
this.qdrantUrl = codebaseIndexQdrantUrl
|
||||
|
|
@ -108,6 +112,9 @@ export class CodeIndexConfigManager {
|
|||
|
||||
this.ollamaOptions = {
|
||||
ollamaBaseUrl: codebaseIndexEmbedderBaseUrl,
|
||||
ollamaModelId: codebaseIndexEmbedderModelId,
|
||||
embeddingTimeoutMs: ollamaEmbeddingTimeoutMs,
|
||||
validationTimeoutMs: ollamaValidationTimeoutMs,
|
||||
}
|
||||
|
||||
this.openAiCompatibleOptions =
|
||||
|
|
@ -132,7 +139,7 @@ export class CodeIndexConfigManager {
|
|||
modelId?: string
|
||||
modelDimension?: number
|
||||
openAiOptions?: ApiHandlerOptions
|
||||
ollamaOptions?: ApiHandlerOptions
|
||||
ollamaOptions?: OllamaConfigOptions
|
||||
openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
|
||||
geminiOptions?: { apiKey: string }
|
||||
qdrantUrl?: string
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ import { t } from "../../../i18n"
|
|||
import { withValidationErrorHandling, sanitizeErrorMessage } from "../shared/validation-helpers"
|
||||
import { TelemetryService } from "@roo-code/telemetry"
|
||||
import { TelemetryEventName } from "@roo-code/types"
|
||||
import { OllamaConfigOptions } from "../interfaces/config"
|
||||
|
||||
export interface OllamaEmbedderOptions extends ApiHandlerOptions {
|
||||
embeddingTimeoutMs?: number
|
||||
validationTimeoutMs?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the IEmbedder interface using a local Ollama instance.
|
||||
|
|
@ -13,11 +19,16 @@ import { TelemetryEventName } from "@roo-code/types"
|
|||
export class CodeIndexOllamaEmbedder implements IEmbedder {
|
||||
private readonly baseUrl: string
|
||||
private readonly defaultModelId: string
|
||||
private readonly embeddingTimeoutMs: number
|
||||
private readonly validationTimeoutMs: number
|
||||
|
||||
constructor(options: ApiHandlerOptions) {
|
||||
constructor(options: OllamaEmbedderOptions) {
|
||||
// Ensure ollamaBaseUrl and ollamaModelId exist on ApiHandlerOptions or add defaults
|
||||
this.baseUrl = options.ollamaBaseUrl || "http://localhost:11434"
|
||||
this.defaultModelId = options.ollamaModelId || "nomic-embed-text:latest"
|
||||
// Default timeouts: 30s for embedding (3x original), 10s for validation (2x original)
|
||||
this.embeddingTimeoutMs = options.embeddingTimeoutMs || 30000
|
||||
this.validationTimeoutMs = options.validationTimeoutMs || 10000
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,7 +72,7 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
|
|||
|
||||
// Add timeout to prevent indefinite hanging
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.embeddingTimeoutMs)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
|
|
@ -140,7 +151,7 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
|
|||
|
||||
// Add timeout to prevent indefinite hanging
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000) // 5 second timeout
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.validationTimeoutMs)
|
||||
|
||||
const modelsResponse = await fetch(modelsUrl, {
|
||||
method: "GET",
|
||||
|
|
@ -197,7 +208,7 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
|
|||
|
||||
// Add timeout for test request too
|
||||
const testController = new AbortController()
|
||||
const testTimeoutId = setTimeout(() => testController.abort(), 5000)
|
||||
const testTimeoutId = setTimeout(() => testController.abort(), this.validationTimeoutMs)
|
||||
|
||||
const testResponse = await fetch(testUrl, {
|
||||
method: "POST",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
import { ApiHandlerOptions } from "../../../shared/api" // Adjust path if needed
|
||||
import { EmbedderProvider } from "./manager"
|
||||
|
||||
// Interface for Ollama-specific options including timeout configuration
|
||||
export interface OllamaConfigOptions {
|
||||
ollamaBaseUrl?: string
|
||||
ollamaModelId?: string
|
||||
embeddingTimeoutMs?: number
|
||||
validationTimeoutMs?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration state for the code indexing feature
|
||||
*/
|
||||
|
|
@ -10,7 +18,7 @@ export interface CodeIndexConfig {
|
|||
modelId?: string
|
||||
modelDimension?: number // Generic dimension property for all providers
|
||||
openAiOptions?: ApiHandlerOptions
|
||||
ollamaOptions?: ApiHandlerOptions
|
||||
ollamaOptions?: OllamaConfigOptions
|
||||
openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
|
||||
geminiOptions?: { apiKey: string }
|
||||
qdrantUrl?: string
|
||||
|
|
|
|||
|
|
@ -247,6 +247,10 @@ export interface WebviewMessage {
|
|||
codebaseIndexSearchMaxResults?: number
|
||||
codebaseIndexSearchMinScore?: number
|
||||
|
||||
// Ollama timeout settings
|
||||
codebaseIndexOllamaEmbeddingTimeoutMs?: number
|
||||
codebaseIndexOllamaValidationTimeoutMs?: number
|
||||
|
||||
// Secret settings
|
||||
codeIndexOpenAiKey?: string
|
||||
codeIndexQdrantApiKey?: string
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ interface LocalCodeIndexSettings {
|
|||
codebaseIndexSearchMaxResults?: number
|
||||
codebaseIndexSearchMinScore?: number
|
||||
|
||||
// Ollama timeout settings
|
||||
codebaseIndexOllamaEmbeddingTimeoutMs?: number
|
||||
codebaseIndexOllamaValidationTimeoutMs?: number
|
||||
|
||||
// Secret settings (start empty, will be loaded separately)
|
||||
codeIndexOpenAiKey?: string
|
||||
codeIndexQdrantApiKey?: string
|
||||
|
|
@ -160,6 +164,8 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
codebaseIndexEmbedderModelDimension: undefined,
|
||||
codebaseIndexSearchMaxResults: CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS,
|
||||
codebaseIndexSearchMinScore: CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE,
|
||||
codebaseIndexOllamaEmbeddingTimeoutMs: 30000, // 30 seconds default
|
||||
codebaseIndexOllamaValidationTimeoutMs: 10000, // 10 seconds default
|
||||
codeIndexOpenAiKey: "",
|
||||
codeIndexQdrantApiKey: "",
|
||||
codebaseIndexOpenAiCompatibleBaseUrl: "",
|
||||
|
|
@ -193,6 +199,10 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
codebaseIndexConfig.codebaseIndexSearchMaxResults ?? CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS,
|
||||
codebaseIndexSearchMinScore:
|
||||
codebaseIndexConfig.codebaseIndexSearchMinScore ?? CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE,
|
||||
codebaseIndexOllamaEmbeddingTimeoutMs:
|
||||
codebaseIndexConfig.codebaseIndexOllamaEmbeddingTimeoutMs ?? 30000,
|
||||
codebaseIndexOllamaValidationTimeoutMs:
|
||||
codebaseIndexConfig.codebaseIndexOllamaValidationTimeoutMs ?? 10000,
|
||||
codeIndexOpenAiKey: "",
|
||||
codeIndexQdrantApiKey: "",
|
||||
codebaseIndexOpenAiCompatibleBaseUrl: codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl || "",
|
||||
|
|
@ -743,6 +753,77 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Ollama Timeout Settings */}
|
||||
<div className="space-y-4 mt-4 p-3 border border-vscode-dropdown-border rounded">
|
||||
<h5 className="text-sm font-medium mb-2">
|
||||
{t("settings:codeIndex.ollamaTimeoutSettings")}
|
||||
</h5>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium">
|
||||
{t("settings:codeIndex.ollamaEmbeddingTimeoutLabel")}
|
||||
</label>
|
||||
<StandardTooltip
|
||||
content={t(
|
||||
"settings:codeIndex.ollamaEmbeddingTimeoutDescription",
|
||||
)}>
|
||||
<span className="codicon codicon-info text-xs text-vscode-descriptionForeground cursor-help" />
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
<VSCodeTextField
|
||||
value={
|
||||
currentSettings.codebaseIndexOllamaEmbeddingTimeoutMs?.toString() ||
|
||||
"30000"
|
||||
}
|
||||
onInput={(e: any) => {
|
||||
const value = parseInt(e.target.value) || 30000
|
||||
updateSetting(
|
||||
"codebaseIndexOllamaEmbeddingTimeoutMs",
|
||||
Math.max(1000, Math.min(300000, value)),
|
||||
)
|
||||
}}
|
||||
placeholder="30000"
|
||||
className="w-full"
|
||||
/>
|
||||
<p className="text-xs text-vscode-descriptionForeground">
|
||||
{t("settings:codeIndex.ollamaEmbeddingTimeoutHelp")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium">
|
||||
{t("settings:codeIndex.ollamaValidationTimeoutLabel")}
|
||||
</label>
|
||||
<StandardTooltip
|
||||
content={t(
|
||||
"settings:codeIndex.ollamaValidationTimeoutDescription",
|
||||
)}>
|
||||
<span className="codicon codicon-info text-xs text-vscode-descriptionForeground cursor-help" />
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
<VSCodeTextField
|
||||
value={
|
||||
currentSettings.codebaseIndexOllamaValidationTimeoutMs?.toString() ||
|
||||
"10000"
|
||||
}
|
||||
onInput={(e: any) => {
|
||||
const value = parseInt(e.target.value) || 10000
|
||||
updateSetting(
|
||||
"codebaseIndexOllamaValidationTimeoutMs",
|
||||
Math.max(1000, Math.min(60000, value)),
|
||||
)
|
||||
}}
|
||||
placeholder="10000"
|
||||
className="w-full"
|
||||
/>
|
||||
<p className="text-xs text-vscode-descriptionForeground">
|
||||
{t("settings:codeIndex.ollamaValidationTimeoutHelp")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -223,6 +223,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||
codebaseIndexEmbedderModelId: "",
|
||||
codebaseIndexSearchMaxResults: undefined,
|
||||
codebaseIndexSearchMinScore: undefined,
|
||||
codebaseIndexOllamaEmbeddingTimeoutMs: 30000,
|
||||
codebaseIndexOllamaValidationTimeoutMs: 10000,
|
||||
},
|
||||
codebaseIndexModels: { ollama: {}, openai: {} },
|
||||
alwaysAllowUpdateTodoList: true,
|
||||
|
|
|
|||
|
|
@ -119,7 +119,14 @@
|
|||
"ollamaBaseUrlRequired": "Ollama base URL is required",
|
||||
"baseUrlRequired": "Base URL is required",
|
||||
"modelDimensionMinValue": "Model dimension must be greater than 0"
|
||||
}
|
||||
},
|
||||
"ollamaTimeoutSettings": "Ollama Timeout Settings",
|
||||
"ollamaEmbeddingTimeoutLabel": "Embedding Timeout (ms)",
|
||||
"ollamaEmbeddingTimeoutDescription": "Maximum time to wait for embedding requests to complete. Increase this value if you experience timeout errors with large code blocks.",
|
||||
"ollamaEmbeddingTimeoutHelp": "Range: 1000-300000ms (1-300 seconds). Default: 30000ms (30 seconds)",
|
||||
"ollamaValidationTimeoutLabel": "Validation Timeout (ms)",
|
||||
"ollamaValidationTimeoutDescription": "Maximum time to wait for model validation requests to complete. This is used to test if the Ollama model is available.",
|
||||
"ollamaValidationTimeoutHelp": "Range: 1000-60000ms (1-60 seconds). Default: 10000ms (10 seconds)"
|
||||
},
|
||||
"autoApprove": {
|
||||
"description": "Allow Roo to automatically perform operations without requiring approval. Enable these settings only if you fully trust the AI and understand the associated security risks.",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue