Merge branch 'RooCodeInc:main' into main

This commit is contained in:
Murilo Pires 2025-07-08 16:54:20 -03:00 committed by GitHub
commit 080b61b086
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 1168 additions and 488 deletions

3
.gitattributes vendored
View file

@ -1,3 +1,6 @@
demo.gif filter=lfs diff=lfs merge=lfs -text
assets/docs/demo.gif filter=lfs diff=lfs merge=lfs -text
src/assets/docs/demo.gif filter=lfs diff=lfs merge=lfs -text
# Test snapshot files - mark as linguist-generated to exclude from GitHub language statistics
*.snap linguist-generated=true

View file

@ -628,7 +628,7 @@ describe("importExport", () => {
codebaseIndexEmbedderBaseUrl: "http://localhost:11434", // Wrong URL from Ollama
// OpenAI Compatible settings are now stored directly in codebaseIndexConfig
codebaseIndexOpenAiCompatibleBaseUrl: "https://custom-openai-api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1536,
codebaseIndexEmbedderModelDimension: 1536,
},
}
@ -673,7 +673,7 @@ describe("importExport", () => {
codebaseIndexEmbedderBaseUrl: "",
// OpenAI Compatible settings are now stored directly in codebaseIndexConfig
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 768,
codebaseIndexEmbedderModelDimension: 768,
},
}
@ -688,9 +688,7 @@ describe("importExport", () => {
const exportedData = (safeWriteJson as Mock).mock.calls[0][1]
// Settings are now exported as-is from codebaseIndexConfig
expect(
exportedData.globalSettings.codebaseIndexConfig.codebaseIndexOpenAiCompatibleModelDimension,
).toBe(768)
expect(exportedData.globalSettings.codebaseIndexConfig.codebaseIndexEmbedderModelDimension).toBe(768)
expect(exportedData.globalSettings.codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl).toBe(
"https://api.example.com/v1",
)
@ -731,7 +729,7 @@ describe("importExport", () => {
codebaseIndexEmbedderBaseUrl: "http://localhost:11434", // Wrong URL from Ollama
// OpenAI Compatible settings are now stored directly in codebaseIndexConfig
codebaseIndexOpenAiCompatibleBaseUrl: "https://openai-compatible.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1536,
codebaseIndexEmbedderModelDimension: 1536,
},
}
@ -749,9 +747,7 @@ describe("importExport", () => {
expect(exportedData.globalSettings.codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl).toBe(
"https://openai-compatible.example.com/v1",
)
expect(
exportedData.globalSettings.codebaseIndexConfig.codebaseIndexOpenAiCompatibleModelDimension,
).toBe(1536)
expect(exportedData.globalSettings.codebaseIndexConfig.codebaseIndexEmbedderModelDimension).toBe(1536)
// The generic embedder base URL is still there
expect(exportedData.globalSettings.codebaseIndexConfig.codebaseIndexEmbedderBaseUrl).toBe(
"http://localhost:11434",
@ -921,7 +917,6 @@ describe("importExport", () => {
codebaseIndexEmbedderModelDimension: 1536,
// OpenAI Compatible settings are now stored directly here
codebaseIndexOpenAiCompatibleBaseUrl: "https://imported-url.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1536,
},
},
})
@ -956,7 +951,7 @@ describe("importExport", () => {
expect.objectContaining({
codebaseIndexConfig: expect.objectContaining({
codebaseIndexOpenAiCompatibleBaseUrl: "https://imported-url.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1536,
codebaseIndexEmbedderModelDimension: 1536,
}),
}),
)
@ -1105,7 +1100,6 @@ describe("importExport", () => {
codebaseIndexEmbedderModelDimension: testModelDimension,
// OpenAI Compatible settings are now stored directly in codebaseIndexConfig
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: testModelDimension,
},
}
@ -1160,7 +1154,7 @@ describe("importExport", () => {
// Step 9: Verify that the model dimension was preserved exactly in global settings
const importedGlobalSettings = mockContextProxy.setValues.mock.calls[0][0]
expect(importedGlobalSettings.codebaseIndexConfig?.codebaseIndexOpenAiCompatibleModelDimension).toBe(
expect(importedGlobalSettings.codebaseIndexConfig?.codebaseIndexEmbedderModelDimension).toBe(
testModelDimension,
)
expect(importedGlobalSettings.codebaseIndexConfig?.codebaseIndexOpenAiCompatibleBaseUrl).toBe(
@ -1198,7 +1192,7 @@ describe("importExport", () => {
codebaseIndexEmbedderBaseUrl: "https://api.example.com/v1",
// OpenAI Compatible settings are now stored directly in codebaseIndexConfig
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: testModelDimension, // 0 is a valid value
codebaseIndexEmbedderModelDimension: testModelDimension, // 0 is a valid value
},
}
@ -1219,7 +1213,7 @@ describe("importExport", () => {
// Verify the exported data includes the model dimension even when it's 0
const exportedData = (safeWriteJson as Mock).mock.calls[0][1]
expect(exportedData.globalSettings.codebaseIndexConfig.codebaseIndexOpenAiCompatibleModelDimension).toBe(0)
expect(exportedData.globalSettings.codebaseIndexConfig.codebaseIndexEmbedderModelDimension).toBe(0)
// Test import roundtrip
const exportedFileContent = JSON.stringify(exportedData)
@ -1247,7 +1241,7 @@ describe("importExport", () => {
// Verify that model dimension 0 was preserved in global settings
const setValuesCall = mockContextProxy.setValues.mock.calls[0][0]
expect(setValuesCall.codebaseIndexConfig?.codebaseIndexOpenAiCompatibleModelDimension).toBe(0)
expect(setValuesCall.codebaseIndexConfig?.codebaseIndexEmbedderModelDimension).toBe(0)
})
it("should handle missing model dimension gracefully", async () => {

View file

@ -5,7 +5,7 @@ import { TodoItem, TodoStatus } from "@roo-code/types"
*/
export function formatReminderSection(todoList?: TodoItem[]): string {
if (!todoList || todoList.length === 0) {
return ""
return "You have not created a todo list yet. Create one with `update_todo_list` if your task is complicated or involves multiple steps."
}
const statusMap: Record<TodoStatus, string> = {
pending: "Pending",

View file

@ -549,13 +549,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -611,13 +611,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -617,13 +617,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -554,13 +554,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -549,13 +549,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -605,13 +605,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -549,13 +549,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -637,13 +637,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -549,13 +549,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -605,13 +605,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -617,13 +617,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -549,13 +549,19 @@ Mode-specific Instructions:
2. You should also ask the user clarifying questions to get a better understanding of the task.
3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.
3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:
- Specific and actionable
- Listed in logical execution order
- Focused on a single, well-defined outcome
- Clear enough that another mode could execute it independently
4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.
5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.
**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**
6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.
**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**
Rules:
# Rules from .clinerules-architect:

View file

@ -0,0 +1,88 @@
import { describe, it, expect } from "vitest"
import { getPromptComponent } from "../system"
import type { CustomModePrompts } from "@roo-code/types"
describe("getPromptComponent", () => {
it("should return undefined for empty objects", () => {
const customModePrompts: CustomModePrompts = {
architect: {},
}
const result = getPromptComponent(customModePrompts, "architect")
expect(result).toBeUndefined()
})
it("should return the component for objects with any properties", () => {
const customModePrompts: CustomModePrompts = {
architect: {
foo: "bar",
baz: 123,
} as any,
}
const result = getPromptComponent(customModePrompts, "architect")
expect(result).toEqual({ foo: "bar", baz: 123 })
})
it("should return undefined for missing mode", () => {
const customModePrompts: CustomModePrompts = {}
const result = getPromptComponent(customModePrompts, "architect")
expect(result).toBeUndefined()
})
it("should return undefined when customModePrompts is undefined", () => {
const result = getPromptComponent(undefined, "architect")
expect(result).toBeUndefined()
})
it.each([
["roleDefinition", { roleDefinition: "Test role" }],
["customInstructions", { customInstructions: "Test instructions" }],
["whenToUse", { whenToUse: "Test when to use" }],
["description", { description: "Test description" }],
])("should return the component when it has %s", (property, component) => {
const customModePrompts: CustomModePrompts = {
architect: component,
}
const result = getPromptComponent(customModePrompts, "architect")
expect(result).toEqual(component)
})
it("should return the component when it has multiple properties", () => {
const customModePrompts: CustomModePrompts = {
architect: {
roleDefinition: "Test role",
customInstructions: "Test instructions",
whenToUse: "Test when to use",
description: "Test description",
},
}
const result = getPromptComponent(customModePrompts, "architect")
expect(result).toEqual({
roleDefinition: "Test role",
customInstructions: "Test instructions",
whenToUse: "Test when to use",
description: "Test description",
})
})
it("should return the component when it has both relevant and irrelevant properties", () => {
const customModePrompts: CustomModePrompts = {
architect: {
roleDefinition: "Test role",
foo: "bar",
baz: 123,
} as any,
}
const result = getPromptComponent(customModePrompts, "architect")
expect(result).toEqual({
roleDefinition: "Test role",
foo: "bar",
baz: 123,
})
})
})

View file

@ -6,6 +6,7 @@ import type { ModeConfig, PromptComponent, CustomModePrompts, TodoItem } from "@
import { Mode, modes, defaultModeSlug, getModeBySlug, getGroupName, getModeSelection } from "../../shared/modes"
import { DiffStrategy } from "../../shared/tools"
import { formatLanguage } from "../../shared/language"
import { isEmpty } from "../../utils/object"
import { McpHub } from "../../services/mcp/McpHub"
import { CodeIndexManager } from "../../services/code-index/manager"
@ -26,6 +27,19 @@ import {
markdownFormattingSection,
} from "./sections"
// Helper function to get prompt component, filtering out empty objects
export function getPromptComponent(
customModePrompts: CustomModePrompts | undefined,
mode: string,
): PromptComponent | undefined {
const component = customModePrompts?.[mode]
// Return undefined if component is empty
if (isEmpty(component)) {
return undefined
}
return component
}
async function generatePrompt(
context: vscode.ExtensionContext,
cwd: string,
@ -129,13 +143,6 @@ export const SYSTEM_PROMPT = async (
throw new Error("Extension context is required for generating system prompt")
}
const getPromptComponent = (value: unknown) => {
if (typeof value === "object" && value !== null) {
return value as PromptComponent
}
return undefined
}
// Try to load custom system prompt from file
const variablesForPrompt: PromptVariables = {
workspace: cwd,
@ -147,7 +154,7 @@ export const SYSTEM_PROMPT = async (
const fileCustomSystemPrompt = await loadSystemPromptFile(cwd, mode, variablesForPrompt)
// Check if it's a custom mode
const promptComponent = getPromptComponent(customModePrompts?.[mode])
const promptComponent = getPromptComponent(customModePrompts, mode)
// Get full mode config from custom modes or fall back to built-in modes
const currentMode = getModeBySlug(mode, customModes) || modes.find((m) => m.slug === mode) || modes[0]

View file

@ -1967,8 +1967,8 @@ export const webviewMessageHandler = async (
codebaseIndexEmbedderProvider: settings.codebaseIndexEmbedderProvider,
codebaseIndexEmbedderBaseUrl: settings.codebaseIndexEmbedderBaseUrl,
codebaseIndexEmbedderModelId: settings.codebaseIndexEmbedderModelId,
codebaseIndexEmbedderModelDimension: settings.codebaseIndexEmbedderModelDimension, // Generic dimension
codebaseIndexOpenAiCompatibleBaseUrl: settings.codebaseIndexOpenAiCompatibleBaseUrl,
codebaseIndexOpenAiCompatibleModelDimension: settings.codebaseIndexOpenAiCompatibleModelDimension,
codebaseIndexSearchMaxResults: settings.codebaseIndexSearchMaxResults,
codebaseIndexSearchMinScore: settings.codebaseIndexSearchMinScore,
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "URL base no vàlida. Comproveu la vostra configuració d'URL.",
"invalidModel": "Model no vàlid. Comproveu la vostra configuració de model.",
"invalidResponse": "Resposta no vàlida del servei d'incrustació. Comproveu la vostra configuració."
},
"serviceFactory": {
"openAiConfigMissing": "Falta la configuració d'OpenAI per crear l'embedder",
"ollamaConfigMissing": "Falta la configuració d'Ollama per crear l'embedder",
"openAiCompatibleConfigMissing": "Falta la configuració compatible amb OpenAI per crear l'embedder",
"geminiConfigMissing": "Falta la configuració de Gemini per crear l'embedder",
"invalidEmbedderType": "Tipus d'embedder configurat no vàlid: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "No s'ha pogut determinar la dimensió del vector per al model '{{modelId}}' amb el proveïdor '{{provider}}'. Assegura't que la 'Dimensió d'incrustació' estigui configurada correctament als paràmetres del proveïdor compatible amb OpenAI.",
"vectorDimensionNotDetermined": "No s'ha pogut determinar la dimensió del vector per al model '{{modelId}}' amb el proveïdor '{{provider}}'. Comprova els perfils del model o la configuració.",
"qdrantUrlMissing": "Falta l'URL de Qdrant per crear l'emmagatzematge de vectors",
"codeIndexingNotConfigured": "No es poden crear serveis: La indexació de codi no està configurada correctament"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "Ungültige Basis-URL. Bitte überprüfe deine URL-Konfiguration.",
"invalidModel": "Ungültiges Modell. Bitte überprüfe deine Modellkonfiguration.",
"invalidResponse": "Ungültige Antwort vom Embedder-Dienst. Bitte überprüfe deine Konfiguration."
},
"serviceFactory": {
"openAiConfigMissing": "OpenAI-Konfiguration fehlt für die Erstellung des Embedders",
"ollamaConfigMissing": "Ollama-Konfiguration fehlt für die Erstellung des Embedders",
"openAiCompatibleConfigMissing": "OpenAI-kompatible Konfiguration fehlt für die Erstellung des Embedders",
"geminiConfigMissing": "Gemini-Konfiguration fehlt für die Erstellung des Embedders",
"invalidEmbedderType": "Ungültiger Embedder-Typ konfiguriert: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Konnte die Vektordimension für Modell '{{modelId}}' mit Anbieter '{{provider}}' nicht bestimmen. Stelle sicher, dass die 'Embedding-Dimension' in den OpenAI-kompatiblen Anbietereinstellungen korrekt eingestellt ist.",
"vectorDimensionNotDetermined": "Konnte die Vektordimension für Modell '{{modelId}}' mit Anbieter '{{provider}}' nicht bestimmen. Überprüfe die Modellprofile oder Konfiguration.",
"qdrantUrlMissing": "Qdrant-URL fehlt für die Erstellung des Vektorspeichers",
"codeIndexingNotConfigured": "Kann keine Dienste erstellen: Code-Indizierung ist nicht richtig konfiguriert"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "Invalid base URL. Please check your URL configuration.",
"invalidModel": "Invalid model. Please check your model configuration.",
"invalidResponse": "Invalid response from embedder service. Please check your configuration."
},
"serviceFactory": {
"openAiConfigMissing": "OpenAI configuration missing for embedder creation",
"ollamaConfigMissing": "Ollama configuration missing for embedder creation",
"openAiCompatibleConfigMissing": "OpenAI Compatible configuration missing for embedder creation",
"geminiConfigMissing": "Gemini configuration missing for embedder creation",
"invalidEmbedderType": "Invalid embedder type configured: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Could not determine vector dimension for model '{{modelId}}' with provider '{{provider}}'. Please ensure the 'Embedding Dimension' is correctly set in the OpenAI-Compatible provider settings.",
"vectorDimensionNotDetermined": "Could not determine vector dimension for model '{{modelId}}' with provider '{{provider}}'. Check model profiles or configuration.",
"qdrantUrlMissing": "Qdrant URL missing for vector store creation",
"codeIndexingNotConfigured": "Cannot create services: Code indexing is not properly configured"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "URL base no válida. Comprueba la configuración de tu URL.",
"invalidModel": "Modelo no válido. Comprueba la configuración de tu modelo.",
"invalidResponse": "Respuesta no válida del servicio de embedder. Comprueba tu configuración."
},
"serviceFactory": {
"openAiConfigMissing": "Falta la configuración de OpenAI para crear el incrustador",
"ollamaConfigMissing": "Falta la configuración de Ollama para crear el incrustador",
"openAiCompatibleConfigMissing": "Falta la configuración compatible con OpenAI para crear el incrustador",
"geminiConfigMissing": "Falta la configuración de Gemini para crear el incrustador",
"invalidEmbedderType": "Tipo de incrustador configurado inválido: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "No se pudo determinar la dimensión del vector para el modelo '{{modelId}}' con el proveedor '{{provider}}'. Asegúrate de que la 'Dimensión de incrustación' esté configurada correctamente en los ajustes del proveedor compatible con OpenAI.",
"vectorDimensionNotDetermined": "No se pudo determinar la dimensión del vector para el modelo '{{modelId}}' con el proveedor '{{provider}}'. Verifica los perfiles del modelo o la configuración.",
"qdrantUrlMissing": "Falta la URL de Qdrant para crear el almacén de vectores",
"codeIndexingNotConfigured": "No se pueden crear servicios: La indexación de código no está configurada correctamente"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "URL de base invalide. Veuillez vérifier votre configuration d'URL.",
"invalidModel": "Modèle invalide. Veuillez vérifier votre configuration de modèle.",
"invalidResponse": "Réponse invalide du service d'embedder. Veuillez vérifier votre configuration."
},
"serviceFactory": {
"openAiConfigMissing": "Configuration OpenAI manquante pour la création de l'embedder",
"ollamaConfigMissing": "Configuration Ollama manquante pour la création de l'embedder",
"openAiCompatibleConfigMissing": "Configuration compatible OpenAI manquante pour la création de l'embedder",
"geminiConfigMissing": "Configuration Gemini manquante pour la création de l'embedder",
"invalidEmbedderType": "Type d'embedder configuré invalide : {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Impossible de déterminer la dimension du vecteur pour le modèle '{{modelId}}' avec le fournisseur '{{provider}}'. Assure-toi que la 'Dimension d'embedding' est correctement définie dans les paramètres du fournisseur compatible OpenAI.",
"vectorDimensionNotDetermined": "Impossible de déterminer la dimension du vecteur pour le modèle '{{modelId}}' avec le fournisseur '{{provider}}'. Vérifie les profils du modèle ou la configuration.",
"qdrantUrlMissing": "URL Qdrant manquante pour la création du stockage de vecteurs",
"codeIndexingNotConfigured": "Impossible de créer les services : L'indexation du code n'est pas correctement configurée"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "अमान्य बेस यूआरएल। कृपया अपनी यूआरएल कॉन्फ़िगरेशन जांचें।",
"invalidModel": "अमान्य मॉडल। कृपया अपनी मॉडल कॉन्फ़िगरेशन जांचें।",
"invalidResponse": "एम्बेडर सेवा से अमान्य प्रतिक्रिया। कृपया अपनी कॉन्फ़िगरेशन जांचें।"
},
"serviceFactory": {
"openAiConfigMissing": "एम्बेडर बनाने के लिए OpenAI कॉन्फ़िगरेशन गायब है",
"ollamaConfigMissing": "एम्बेडर बनाने के लिए Ollama कॉन्फ़िगरेशन गायब है",
"openAiCompatibleConfigMissing": "एम्बेडर बनाने के लिए OpenAI संगत कॉन्फ़िगरेशन गायब है",
"geminiConfigMissing": "एम्बेडर बनाने के लिए Gemini कॉन्फ़िगरेशन गायब है",
"invalidEmbedderType": "अमान्य एम्बेडर प्रकार कॉन्फ़िगर किया गया: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "प्रदाता '{{provider}}' के साथ मॉडल '{{modelId}}' के लिए वेक्टर आयाम निर्धारित नहीं कर सका। कृपया सुनिश्चित करें कि OpenAI-संगत प्रदाता सेटिंग्स में 'एम्बेडिंग आयाम' सही तरीके से सेट है।",
"vectorDimensionNotDetermined": "प्रदाता '{{provider}}' के साथ मॉडल '{{modelId}}' के लिए वेक्टर आयाम निर्धारित नहीं कर सका। मॉडल प्रोफ़ाइल या कॉन्फ़िगरेशन की जांच करें।",
"qdrantUrlMissing": "वेक्टर स्टोर बनाने के लिए Qdrant URL गायब है",
"codeIndexingNotConfigured": "सेवाएं नहीं बना सकते: कोड इंडेक्सिंग ठीक से कॉन्फ़िगर नहीं है"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "URL dasar tidak valid. Silakan periksa konfigurasi URL Anda.",
"invalidModel": "Model tidak valid. Silakan periksa konfigurasi model Anda.",
"invalidResponse": "Respons tidak valid dari layanan embedder. Silakan periksa konfigurasi Anda."
},
"serviceFactory": {
"openAiConfigMissing": "Konfigurasi OpenAI tidak ada untuk membuat embedder",
"ollamaConfigMissing": "Konfigurasi Ollama tidak ada untuk membuat embedder",
"openAiCompatibleConfigMissing": "Konfigurasi yang kompatibel dengan OpenAI tidak ada untuk membuat embedder",
"geminiConfigMissing": "Konfigurasi Gemini tidak ada untuk membuat embedder",
"invalidEmbedderType": "Tipe embedder yang dikonfigurasi tidak valid: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Tidak dapat menentukan dimensi vektor untuk model '{{modelId}}' dengan penyedia '{{provider}}'. Pastikan 'Dimensi Embedding' diatur dengan benar di pengaturan penyedia yang kompatibel dengan OpenAI.",
"vectorDimensionNotDetermined": "Tidak dapat menentukan dimensi vektor untuk model '{{modelId}}' dengan penyedia '{{provider}}'. Periksa profil model atau konfigurasi.",
"qdrantUrlMissing": "URL Qdrant tidak ada untuk membuat penyimpanan vektor",
"codeIndexingNotConfigured": "Tidak dapat membuat layanan: Pengindeksan kode tidak dikonfigurasi dengan benar"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "URL di base non valido. Controlla la configurazione del tuo URL.",
"invalidModel": "Modello non valido. Controlla la configurazione del tuo modello.",
"invalidResponse": "Risposta non valida dal servizio embedder. Controlla la tua configurazione."
},
"serviceFactory": {
"openAiConfigMissing": "Configurazione OpenAI mancante per la creazione dell'embedder",
"ollamaConfigMissing": "Configurazione Ollama mancante per la creazione dell'embedder",
"openAiCompatibleConfigMissing": "Configurazione compatibile con OpenAI mancante per la creazione dell'embedder",
"geminiConfigMissing": "Configurazione Gemini mancante per la creazione dell'embedder",
"invalidEmbedderType": "Tipo di embedder configurato non valido: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Impossibile determinare la dimensione del vettore per il modello '{{modelId}}' con il provider '{{provider}}'. Assicurati che la 'Dimensione di embedding' sia impostata correttamente nelle impostazioni del provider compatibile con OpenAI.",
"vectorDimensionNotDetermined": "Impossibile determinare la dimensione del vettore per il modello '{{modelId}}' con il provider '{{provider}}'. Controlla i profili del modello o la configurazione.",
"qdrantUrlMissing": "URL Qdrant mancante per la creazione dello storage vettoriale",
"codeIndexingNotConfigured": "Impossibile creare i servizi: L'indicizzazione del codice non è configurata correttamente"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "無効なベースURLです。URL構成を確認してください。",
"invalidModel": "無効なモデルです。モデル構成を確認してください。",
"invalidResponse": "エンベッダーサービスからの無効な応答です。設定を確認してください。"
},
"serviceFactory": {
"openAiConfigMissing": "エンベッダー作成のためのOpenAI設定がありません",
"ollamaConfigMissing": "エンベッダー作成のためのOllama設定がありません",
"openAiCompatibleConfigMissing": "エンベッダー作成のためのOpenAI互換設定がありません",
"geminiConfigMissing": "エンベッダー作成のためのGemini設定がありません",
"invalidEmbedderType": "無効なエンベッダータイプが設定されています: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "プロバイダー '{{provider}}' のモデル '{{modelId}}' の埋め込み次元を決定できませんでした。OpenAI互換プロバイダー設定で「埋め込み次元」が正しく設定されていることを確認してください。",
"vectorDimensionNotDetermined": "プロバイダー '{{provider}}' のモデル '{{modelId}}' の埋め込み次元を決定できませんでした。モデルプロファイルまたは設定を確認してください。",
"qdrantUrlMissing": "ベクターストア作成のためのQdrant URLがありません",
"codeIndexingNotConfigured": "サービスを作成できません: コードインデックスが正しく設定されていません"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "잘못된 기본 URL입니다. URL 구성을 확인하세요.",
"invalidModel": "잘못된 모델입니다. 모델 구성을 확인하세요.",
"invalidResponse": "임베더 서비스에서 잘못된 응답이 왔습니다. 구성을 확인하세요."
},
"serviceFactory": {
"openAiConfigMissing": "임베더 생성을 위한 OpenAI 구성이 누락되었습니다",
"ollamaConfigMissing": "임베더 생성을 위한 Ollama 구성이 누락되었습니다",
"openAiCompatibleConfigMissing": "임베더 생성을 위한 OpenAI 호환 구성이 누락되었습니다",
"geminiConfigMissing": "임베더 생성을 위한 Gemini 구성이 누락되었습니다",
"invalidEmbedderType": "잘못된 임베더 유형이 구성되었습니다: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "프로바이더 '{{provider}}'의 모델 '{{modelId}}'에 대한 벡터 차원을 결정할 수 없습니다. OpenAI 호환 프로바이더 설정에서 '임베딩 차원'이 올바르게 설정되어 있는지 확인하세요.",
"vectorDimensionNotDetermined": "프로바이더 '{{provider}}'의 모델 '{{modelId}}'에 대한 벡터 차원을 결정할 수 없습니다. 모델 프로필 또는 구성을 확인하세요.",
"qdrantUrlMissing": "벡터 저장소 생성을 위한 Qdrant URL이 누락되었습니다",
"codeIndexingNotConfigured": "서비스를 생성할 수 없습니다: 코드 인덱싱이 올바르게 구성되지 않았습니다"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "Ongeldige basis-URL. Controleer je URL-configuratie.",
"invalidModel": "Ongeldig model. Controleer je modelconfiguratie.",
"invalidResponse": "Ongeldige reactie van embedder-service. Controleer je configuratie."
},
"serviceFactory": {
"openAiConfigMissing": "OpenAI-configuratie ontbreekt voor het maken van embedder",
"ollamaConfigMissing": "Ollama-configuratie ontbreekt voor het maken van embedder",
"openAiCompatibleConfigMissing": "OpenAI-compatibele configuratie ontbreekt voor het maken van embedder",
"geminiConfigMissing": "Gemini-configuratie ontbreekt voor het maken van embedder",
"invalidEmbedderType": "Ongeldig embedder-type geconfigureerd: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Kan de vectordimensie voor model '{{modelId}}' met provider '{{provider}}' niet bepalen. Zorg ervoor dat de 'Embedding Dimensie' correct is ingesteld in de OpenAI-compatibele provider-instellingen.",
"vectorDimensionNotDetermined": "Kan de vectordimensie voor model '{{modelId}}' met provider '{{provider}}' niet bepalen. Controleer modelprofielen of configuratie.",
"qdrantUrlMissing": "Qdrant URL ontbreekt voor het maken van vectoropslag",
"codeIndexingNotConfigured": "Kan geen services maken: Code-indexering is niet correct geconfigureerd"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "Nieprawidłowy podstawowy adres URL. Sprawdź konfigurację adresu URL.",
"invalidModel": "Nieprawidłowy model. Sprawdź konfigurację modelu.",
"invalidResponse": "Nieprawidłowa odpowiedź z usługi embedder. Sprawdź swoją konfigurację."
},
"serviceFactory": {
"openAiConfigMissing": "Brak konfiguracji OpenAI do utworzenia embeddera",
"ollamaConfigMissing": "Brak konfiguracji Ollama do utworzenia embeddera",
"openAiCompatibleConfigMissing": "Brak konfiguracji kompatybilnej z OpenAI do utworzenia embeddera",
"geminiConfigMissing": "Brak konfiguracji Gemini do utworzenia embeddera",
"invalidEmbedderType": "Skonfigurowano nieprawidłowy typ embeddera: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Nie można określić wymiaru wektora dla modelu '{{modelId}}' z dostawcą '{{provider}}'. Upewnij się, że 'Wymiar osadzania' jest poprawnie ustawiony w ustawieniach dostawcy kompatybilnego z OpenAI.",
"vectorDimensionNotDetermined": "Nie można określić wymiaru wektora dla modelu '{{modelId}}' z dostawcą '{{provider}}'. Sprawdź profile modelu lub konfigurację.",
"qdrantUrlMissing": "Brak adresu URL Qdrant do utworzenia magazynu wektorów",
"codeIndexingNotConfigured": "Nie można utworzyć usług: Indeksowanie kodu nie jest poprawnie skonfigurowane"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "URL base inválida. Verifique sua configuração de URL.",
"invalidModel": "Modelo inválido. Verifique a configuração do seu modelo.",
"invalidResponse": "Resposta inválida do serviço de embedder. Verifique sua configuração."
},
"serviceFactory": {
"openAiConfigMissing": "Configuração do OpenAI ausente para criação do embedder",
"ollamaConfigMissing": "Configuração do Ollama ausente para criação do embedder",
"openAiCompatibleConfigMissing": "Configuração compatível com OpenAI ausente para criação do embedder",
"geminiConfigMissing": "Configuração do Gemini ausente para criação do embedder",
"invalidEmbedderType": "Tipo de embedder configurado inválido: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Não foi possível determinar a dimensão do vetor para o modelo '{{modelId}}' com o provedor '{{provider}}'. Certifique-se de que a 'Dimensão de Embedding' esteja configurada corretamente nas configurações do provedor compatível com OpenAI.",
"vectorDimensionNotDetermined": "Não foi possível determinar a dimensão do vetor para o modelo '{{modelId}}' com o provedor '{{provider}}'. Verifique os perfis do modelo ou a configuração.",
"qdrantUrlMissing": "URL do Qdrant ausente para criação do armazenamento de vetores",
"codeIndexingNotConfigured": "Não é possível criar serviços: A indexação de código não está configurada corretamente"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "Неверный базовый URL. Проверьте конфигурацию URL.",
"invalidModel": "Неверная модель. Проверьте конфигурацию модели.",
"invalidResponse": "Неверный ответ от службы embedder. Проверьте вашу конфигурацию."
},
"serviceFactory": {
"openAiConfigMissing": "Отсутствует конфигурация OpenAI для создания эмбеддера",
"ollamaConfigMissing": "Отсутствует конфигурация Ollama для создания эмбеддера",
"openAiCompatibleConfigMissing": "Отсутствует конфигурация, совместимая с OpenAI, для создания эмбеддера",
"geminiConfigMissing": "Отсутствует конфигурация Gemini для создания эмбеддера",
"invalidEmbedderType": "Настроен недопустимый тип эмбеддера: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Не удалось определить размерность вектора для модели '{{modelId}}' с провайдером '{{provider}}'. Убедитесь, что 'Размерность эмбеддинга' правильно установлена в настройках провайдера, совместимого с OpenAI.",
"vectorDimensionNotDetermined": "Не удалось определить размерность вектора для модели '{{modelId}}' с провайдером '{{provider}}'. Проверьте профили модели или конфигурацию.",
"qdrantUrlMissing": "Отсутствует URL Qdrant для создания векторного хранилища",
"codeIndexingNotConfigured": "Невозможно создать сервисы: Индексация кода не настроена должным образом"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "Geçersiz temel URL. Lütfen URL yapılandırmanızı kontrol edin.",
"invalidModel": "Geçersiz model. Lütfen model yapılandırmanızı kontrol edin.",
"invalidResponse": "Embedder hizmetinden geçersiz yanıt. Lütfen yapılandırmanızı kontrol edin."
},
"serviceFactory": {
"openAiConfigMissing": "Gömücü oluşturmak için OpenAI yapılandırması eksik",
"ollamaConfigMissing": "Gömücü oluşturmak için Ollama yapılandırması eksik",
"openAiCompatibleConfigMissing": "Gömücü oluşturmak için OpenAI uyumlu yapılandırması eksik",
"geminiConfigMissing": "Gömücü oluşturmak için Gemini yapılandırması eksik",
"invalidEmbedderType": "Geçersiz gömücü türü yapılandırıldı: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "'{{provider}}' sağlayıcısı ile '{{modelId}}' modeli için vektör boyutu belirlenemedi. OpenAI uyumlu sağlayıcı ayarlarında 'Gömme Boyutu'nun doğru ayarlandığından emin ol.",
"vectorDimensionNotDetermined": "'{{provider}}' sağlayıcısı ile '{{modelId}}' modeli için vektör boyutu belirlenemedi. Model profillerini veya yapılandırmayı kontrol et.",
"qdrantUrlMissing": "Vektör deposu oluşturmak için Qdrant URL'si eksik",
"codeIndexingNotConfigured": "Hizmetler oluşturulamıyor: Kod indeksleme düzgün yapılandırılmamış"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "URL cơ sở không hợp lệ. Vui lòng kiểm tra cấu hình URL của bạn.",
"invalidModel": "Mô hình không hợp lệ. Vui lòng kiểm tra cấu hình mô hình của bạn.",
"invalidResponse": "Phản hồi không hợp lệ từ dịch vụ embedder. Vui lòng kiểm tra cấu hình của bạn."
},
"serviceFactory": {
"openAiConfigMissing": "Thiếu cấu hình OpenAI để tạo embedder",
"ollamaConfigMissing": "Thiếu cấu hình Ollama để tạo embedder",
"openAiCompatibleConfigMissing": "Thiếu cấu hình tương thích OpenAI để tạo embedder",
"geminiConfigMissing": "Thiếu cấu hình Gemini để tạo embedder",
"invalidEmbedderType": "Loại embedder được cấu hình không hợp lệ: {{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "Không thể xác định kích thước vector cho mô hình '{{modelId}}' với nhà cung cấp '{{provider}}'. Hãy đảm bảo 'Kích thước Embedding' được cài đặt đúng trong cài đặt nhà cung cấp tương thích OpenAI.",
"vectorDimensionNotDetermined": "Không thể xác định kích thước vector cho mô hình '{{modelId}}' với nhà cung cấp '{{provider}}'. Kiểm tra hồ sơ mô hình hoặc cấu hình.",
"qdrantUrlMissing": "Thiếu URL Qdrant để tạo kho lưu trữ vector",
"codeIndexingNotConfigured": "Không thể tạo dịch vụ: Lập chỉ mục mã không được cấu hình đúng cách"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "基础 URL 无效。请检查您的 URL 配置。",
"invalidModel": "模型无效。请检查您的模型配置。",
"invalidResponse": "嵌入服务响应无效。请检查您的配置。"
},
"serviceFactory": {
"openAiConfigMissing": "创建嵌入器缺少 OpenAI 配置",
"ollamaConfigMissing": "创建嵌入器缺少 Ollama 配置",
"openAiCompatibleConfigMissing": "创建嵌入器缺少 OpenAI 兼容配置",
"geminiConfigMissing": "创建嵌入器缺少 Gemini 配置",
"invalidEmbedderType": "配置的嵌入器类型无效:{{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "无法确定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量维度。请确保在 OpenAI 兼容提供商设置中正确设置了「嵌入维度」。",
"vectorDimensionNotDetermined": "无法确定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量维度。请检查模型配置文件或配置。",
"qdrantUrlMissing": "创建向量存储缺少 Qdrant URL",
"codeIndexingNotConfigured": "无法创建服务:代码索引未正确配置"
}
}

View file

@ -37,5 +37,16 @@
"invalidBaseUrl": "無效的基礎 URL。請檢查您的 URL 組態。",
"invalidModel": "無效的模型。請檢查您的模型組態。",
"invalidResponse": "內嵌服務回應無效。請檢查您的組態。"
},
"serviceFactory": {
"openAiConfigMissing": "建立嵌入器缺少 OpenAI 設定",
"ollamaConfigMissing": "建立嵌入器缺少 Ollama 設定",
"openAiCompatibleConfigMissing": "建立嵌入器缺少 OpenAI 相容設定",
"geminiConfigMissing": "建立嵌入器缺少 Gemini 設定",
"invalidEmbedderType": "設定的嵌入器類型無效:{{embedderProvider}}",
"vectorDimensionNotDeterminedOpenAiCompatible": "無法確定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量維度。請確保在 OpenAI 相容提供商設定中正確設定了「嵌入維度」。",
"vectorDimensionNotDetermined": "無法確定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量維度。請檢查模型設定檔或設定。",
"qdrantUrlMissing": "建立向量儲存缺少 Qdrant URL",
"codeIndexingNotConfigured": "無法建立服務:程式碼索引未正確設定"
}
}

View file

@ -135,7 +135,7 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexEmbedderBaseUrl: "",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1024,
codebaseIndexEmbedderModelDimension: 1024,
}
mockContextProxy.getGlobalState.mockImplementation((key: string) => {
if (key === "codebaseIndexConfig") return mockGlobalState
@ -153,12 +153,12 @@ describe("CodeIndexConfigManager", () => {
isConfigured: true,
embedderProvider: "openai-compatible",
modelId: "custom-model",
modelDimension: 1024,
openAiOptions: { openAiNativeApiKey: "" },
ollamaOptions: { ollamaBaseUrl: "" },
openAiCompatibleOptions: {
baseUrl: "https://api.example.com/v1",
apiKey: "test-openai-compatible-key",
modelDimension: 1024,
},
qdrantUrl: "http://qdrant.local",
qdrantApiKey: "test-qdrant-key",
@ -213,7 +213,7 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexEmbedderBaseUrl: "",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: "invalid-dimension", // Invalid type
codebaseIndexEmbedderModelDimension: "invalid-dimension", // Invalid type
}
mockContextProxy.getGlobalState.mockImplementation((key: string) => {
if (key === "codebaseIndexConfig") return mockGlobalState
@ -231,13 +231,14 @@ describe("CodeIndexConfigManager", () => {
isConfigured: true,
embedderProvider: "openai-compatible",
modelId: "custom-model",
modelDimension: undefined, // Invalid dimension is converted to undefined
openAiOptions: { openAiNativeApiKey: "" },
ollamaOptions: { ollamaBaseUrl: "" },
openAiCompatibleOptions: {
baseUrl: "https://api.example.com/v1",
apiKey: "test-openai-compatible-key",
modelDimension: "invalid-dimension",
},
geminiOptions: undefined,
qdrantUrl: "http://qdrant.local",
qdrantApiKey: "test-qdrant-key",
searchMinScore: 0.4,
@ -533,7 +534,7 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexEmbedderProvider: "openai-compatible",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1024,
codebaseIndexEmbedderModelDimension: 1024,
}
}
return undefined
@ -554,7 +555,7 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexEmbedderProvider: "openai-compatible",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 2048,
codebaseIndexEmbedderModelDimension: 2048,
}
}
return undefined
@ -573,10 +574,10 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexQdrantUrl: "http://qdrant.local",
codebaseIndexEmbedderProvider: "openai-compatible",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexEmbedderModelDimension: 1024,
}
}
if (key === "codebaseIndexOpenAiCompatibleBaseUrl") return "https://api.example.com/v1"
if (key === "codebaseIndexOpenAiCompatibleModelDimension") return 1024
return undefined
})
setupSecretMocks({
@ -594,11 +595,11 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexQdrantUrl: "http://qdrant.local",
codebaseIndexEmbedderProvider: "openai-compatible",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexEmbedderModelDimension: 1024,
codebaseIndexSearchMinScore: 0.5, // Changed unrelated setting
}
}
if (key === "codebaseIndexOpenAiCompatibleBaseUrl") return "https://api.example.com/v1"
if (key === "codebaseIndexOpenAiCompatibleModelDimension") return 1024
return undefined
})
@ -637,7 +638,7 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexEmbedderProvider: "openai-compatible",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1024,
codebaseIndexEmbedderModelDimension: 1024,
}
}
return undefined
@ -657,7 +658,7 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexEmbedderProvider: "openai-compatible",
codebaseIndexEmbedderModelId: "custom-model",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
codebaseIndexOpenAiCompatibleModelDimension: 1024,
codebaseIndexEmbedderModelDimension: 1024,
}
}
return undefined
@ -808,10 +809,10 @@ describe("CodeIndexConfigManager", () => {
codebaseIndexQdrantUrl: "http://qdrant.local",
codebaseIndexEmbedderProvider: "openai-compatible",
codebaseIndexEmbedderModelId: "nomic-embed-code",
codebaseIndexOpenAiCompatibleBaseUrl: "https://api.example.com/v1",
// No codebaseIndexSearchMinScore
}
}
if (key === "codebaseIndexOpenAiCompatibleBaseUrl") return "https://api.example.com/v1"
return undefined
})
mockContextProxy.getSecret.mockImplementation((key: string) => {

View file

@ -146,7 +146,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow("OpenAI configuration missing for embedder creation")
expect(() => factory.createEmbedder()).toThrow("serviceFactory.openAiConfigMissing")
})
it("should throw error when Ollama base URL is missing", () => {
@ -161,7 +161,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow("Ollama configuration missing for embedder creation")
expect(() => factory.createEmbedder()).toThrow("serviceFactory.ollamaConfigMissing")
})
it("should pass model ID to OpenAI Compatible embedder when using OpenAI Compatible provider", () => {
@ -224,9 +224,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow(
"OpenAI Compatible configuration missing for embedder creation",
)
expect(() => factory.createEmbedder()).toThrow("serviceFactory.openAiCompatibleConfigMissing")
})
it("should throw error when OpenAI Compatible API key is missing", () => {
@ -242,9 +240,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow(
"OpenAI Compatible configuration missing for embedder creation",
)
expect(() => factory.createEmbedder()).toThrow("serviceFactory.openAiCompatibleConfigMissing")
})
it("should throw error when OpenAI Compatible options are missing", () => {
@ -257,9 +253,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow(
"OpenAI Compatible configuration missing for embedder creation",
)
expect(() => factory.createEmbedder()).toThrow("serviceFactory.openAiCompatibleConfigMissing")
})
it("should create GeminiEmbedder when using Gemini provider", () => {
@ -290,7 +284,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow("Gemini configuration missing for embedder creation")
expect(() => factory.createEmbedder()).toThrow("serviceFactory.geminiConfigMissing")
})
it("should throw error when Gemini options are missing", () => {
@ -302,7 +296,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow("Gemini configuration missing for embedder creation")
expect(() => factory.createEmbedder()).toThrow("serviceFactory.geminiConfigMissing")
})
it("should throw error for invalid embedder provider", () => {
@ -314,7 +308,7 @@ describe("CodeIndexServiceFactory", () => {
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow("Invalid embedder type configured: invalid-provider")
expect(() => factory.createEmbedder()).toThrow("serviceFactory.invalidEmbedderType")
})
})
@ -406,8 +400,10 @@ describe("CodeIndexServiceFactory", () => {
const testConfig = {
embedderProvider: "openai-compatible",
modelId: testModelId,
modelDimension: manualDimension,
openAiCompatibleOptions: {
modelDimension: manualDimension,
baseUrl: "https://api.example.com/v1",
apiKey: "test-api-key",
},
qdrantUrl: "http://localhost:6333",
qdrantApiKey: "test-key",
@ -463,8 +459,10 @@ describe("CodeIndexServiceFactory", () => {
const testConfig = {
embedderProvider: "openai-compatible",
modelId: testModelId,
modelDimension: 0, // Invalid dimension
openAiCompatibleOptions: {
modelDimension: 0, // Invalid dimension
baseUrl: "https://api.example.com/v1",
apiKey: "test-api-key",
},
qdrantUrl: "http://localhost:6333",
qdrantApiKey: "test-key",
@ -474,7 +472,7 @@ describe("CodeIndexServiceFactory", () => {
// Act & Assert
expect(() => factory.createVectorStore()).toThrow(
"Could not determine vector dimension for model 'custom-model' with provider 'openai-compatible'. Please ensure the 'Embedding Dimension' is correctly set in the OpenAI-Compatible provider settings.",
"serviceFactory.vectorDimensionNotDeterminedOpenAiCompatible",
)
})
@ -496,7 +494,7 @@ describe("CodeIndexServiceFactory", () => {
// Act & Assert
expect(() => factory.createVectorStore()).toThrow(
"Could not determine vector dimension for model 'unknown-model' with provider 'openai-compatible'. Please ensure the 'Embedding Dimension' is correctly set in the OpenAI-Compatible provider settings.",
"serviceFactory.vectorDimensionNotDeterminedOpenAiCompatible",
)
})
@ -560,9 +558,7 @@ describe("CodeIndexServiceFactory", () => {
mockGetModelDimension.mockReturnValue(undefined)
// Act & Assert
expect(() => factory.createVectorStore()).toThrow(
"Could not determine vector dimension for model 'unknown-model' with provider 'openai'. Check model profiles or configuration.",
)
expect(() => factory.createVectorStore()).toThrow("serviceFactory.vectorDimensionNotDetermined")
})
it("should throw error when Qdrant URL is missing", () => {
@ -577,7 +573,7 @@ describe("CodeIndexServiceFactory", () => {
mockGetModelDimension.mockReturnValue(1536)
// Act & Assert
expect(() => factory.createVectorStore()).toThrow("Qdrant URL missing for vector store creation")
expect(() => factory.createVectorStore()).toThrow("serviceFactory.qdrantUrlMissing")
})
})
@ -747,7 +743,7 @@ describe("CodeIndexServiceFactory", () => {
await expect(async () => {
const embedder = factory.createEmbedder()
await factory.validateEmbedder(embedder)
}).rejects.toThrow("OpenAI configuration missing for embedder creation")
}).rejects.toThrow("serviceFactory.openAiConfigMissing")
})
it("should return error for unknown embedder provider", async () => {
@ -760,7 +756,7 @@ describe("CodeIndexServiceFactory", () => {
// Act & Assert
// This should throw when trying to create the embedder
expect(() => factory.createEmbedder()).toThrow("Invalid embedder type configured: unknown-provider")
expect(() => factory.createEmbedder()).toThrow("serviceFactory.invalidEmbedderType")
})
})
})

View file

@ -13,9 +13,10 @@ export class CodeIndexConfigManager {
private isEnabled: boolean = false
private embedderProvider: EmbedderProvider = "openai"
private modelId?: string
private modelDimension?: number
private openAiOptions?: ApiHandlerOptions
private ollamaOptions?: ApiHandlerOptions
private openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number }
private openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
private geminiOptions?: { apiKey: string }
private qdrantUrl?: string = "http://localhost:6333"
private qdrantApiKey?: string
@ -65,9 +66,6 @@ export class CodeIndexConfigManager {
// Fix: Read OpenAI Compatible settings from the correct location within codebaseIndexConfig
const openAiCompatibleBaseUrl = codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl ?? ""
const openAiCompatibleApiKey = this.contextProxy?.getSecret("codebaseIndexOpenAiCompatibleApiKey") ?? ""
const openAiCompatibleModelDimension = codebaseIndexConfig.codebaseIndexOpenAiCompatibleModelDimension as
| number
| undefined
const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
// Update instance variables with configuration
@ -76,6 +74,23 @@ export class CodeIndexConfigManager {
this.qdrantApiKey = qdrantApiKey ?? ""
this.searchMinScore = codebaseIndexSearchMinScore
this.searchMaxResults = codebaseIndexSearchMaxResults
// Validate and set model dimension
const rawDimension = codebaseIndexConfig.codebaseIndexEmbedderModelDimension
if (rawDimension !== undefined && rawDimension !== null) {
const dimension = Number(rawDimension)
if (!isNaN(dimension) && dimension > 0) {
this.modelDimension = dimension
} else {
console.warn(
`Invalid codebaseIndexEmbedderModelDimension value: ${rawDimension}. Must be a positive number.`,
)
this.modelDimension = undefined
}
} else {
this.modelDimension = undefined
}
this.openAiOptions = { openAiNativeApiKey: openAiKey }
// Set embedder provider with support for openai-compatible
@ -100,7 +115,6 @@ export class CodeIndexConfigManager {
? {
baseUrl: openAiCompatibleBaseUrl,
apiKey: openAiCompatibleApiKey,
modelDimension: openAiCompatibleModelDimension,
}
: undefined
@ -117,6 +131,7 @@ export class CodeIndexConfigManager {
isConfigured: boolean
embedderProvider: EmbedderProvider
modelId?: string
modelDimension?: number
openAiOptions?: ApiHandlerOptions
ollamaOptions?: ApiHandlerOptions
openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
@ -133,11 +148,11 @@ export class CodeIndexConfigManager {
configured: this.isConfigured(),
embedderProvider: this.embedderProvider,
modelId: this.modelId,
modelDimension: this.modelDimension,
openAiKey: this.openAiOptions?.openAiNativeApiKey ?? "",
ollamaBaseUrl: this.ollamaOptions?.ollamaBaseUrl ?? "",
openAiCompatibleBaseUrl: this.openAiCompatibleOptions?.baseUrl ?? "",
openAiCompatibleApiKey: this.openAiCompatibleOptions?.apiKey ?? "",
openAiCompatibleModelDimension: this.openAiCompatibleOptions?.modelDimension,
geminiApiKey: this.geminiOptions?.apiKey ?? "",
qdrantUrl: this.qdrantUrl ?? "",
qdrantApiKey: this.qdrantApiKey ?? "",
@ -158,6 +173,7 @@ export class CodeIndexConfigManager {
isConfigured: this.isConfigured(),
embedderProvider: this.embedderProvider,
modelId: this.modelId,
modelDimension: this.modelDimension,
openAiOptions: this.openAiOptions,
ollamaOptions: this.ollamaOptions,
openAiCompatibleOptions: this.openAiCompatibleOptions,
@ -225,7 +241,7 @@ export class CodeIndexConfigManager {
const prevOllamaBaseUrl = prev?.ollamaBaseUrl ?? ""
const prevOpenAiCompatibleBaseUrl = prev?.openAiCompatibleBaseUrl ?? ""
const prevOpenAiCompatibleApiKey = prev?.openAiCompatibleApiKey ?? ""
const prevOpenAiCompatibleModelDimension = prev?.openAiCompatibleModelDimension
const prevModelDimension = prev?.modelDimension
const prevGeminiApiKey = prev?.geminiApiKey ?? ""
const prevQdrantUrl = prev?.qdrantUrl ?? ""
const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
@ -257,7 +273,7 @@ export class CodeIndexConfigManager {
const currentOllamaBaseUrl = this.ollamaOptions?.ollamaBaseUrl ?? ""
const currentOpenAiCompatibleBaseUrl = this.openAiCompatibleOptions?.baseUrl ?? ""
const currentOpenAiCompatibleApiKey = this.openAiCompatibleOptions?.apiKey ?? ""
const currentOpenAiCompatibleModelDimension = this.openAiCompatibleOptions?.modelDimension
const currentModelDimension = this.modelDimension
const currentGeminiApiKey = this.geminiOptions?.apiKey ?? ""
const currentQdrantUrl = this.qdrantUrl ?? ""
const currentQdrantApiKey = this.qdrantApiKey ?? ""
@ -277,11 +293,9 @@ export class CodeIndexConfigManager {
return true
}
// Check for OpenAI Compatible modelDimension changes
if (this.embedderProvider === "openai-compatible" || prevProvider === "openai-compatible") {
if (prevOpenAiCompatibleModelDimension !== currentOpenAiCompatibleModelDimension) {
return true
}
// Check for model dimension changes (generic for all providers)
if (prevModelDimension !== currentModelDimension) {
return true
}
if (prevQdrantUrl !== currentQdrantUrl || prevQdrantApiKey !== currentQdrantApiKey) {
@ -332,6 +346,7 @@ export class CodeIndexConfigManager {
isConfigured: this.isConfigured(),
embedderProvider: this.embedderProvider,
modelId: this.modelId,
modelDimension: this.modelDimension,
openAiOptions: this.openAiOptions,
ollamaOptions: this.ollamaOptions,
openAiCompatibleOptions: this.openAiCompatibleOptions,
@ -381,6 +396,14 @@ export class CodeIndexConfigManager {
return this.modelId
}
/**
* Gets the current model dimension being used for embeddings.
* Returns the explicitly configured dimension or undefined if not set.
*/
public get currentModelDimension(): number | undefined {
return this.modelDimension
}
/**
* Gets the configured minimum search score based on user setting, model-specific threshold, or fallback.
* Priority: 1) User setting, 2) Model-specific threshold, 3) Default DEFAULT_SEARCH_MIN_SCORE constant.

View file

@ -9,9 +9,10 @@ export interface CodeIndexConfig {
isConfigured: boolean
embedderProvider: EmbedderProvider
modelId?: string
modelDimension?: number // Generic dimension property for all providers
openAiOptions?: ApiHandlerOptions
ollamaOptions?: ApiHandlerOptions
openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number }
openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
geminiOptions?: { apiKey: string }
qdrantUrl?: string
qdrantApiKey?: string
@ -27,11 +28,11 @@ export type PreviousConfigSnapshot = {
configured: boolean
embedderProvider: EmbedderProvider
modelId?: string
modelDimension?: number // Generic dimension property
openAiKey?: string
ollamaBaseUrl?: string
openAiCompatibleBaseUrl?: string
openAiCompatibleApiKey?: string
openAiCompatibleModelDimension?: number
geminiApiKey?: string
qdrantUrl?: string
qdrantApiKey?: string

View file

@ -10,6 +10,7 @@ import { ICodeParser, IEmbedder, IFileWatcher, IVectorStore } from "./interfaces
import { CodeIndexConfigManager } from "./config-manager"
import { CacheManager } from "./cache-manager"
import { Ignore } from "ignore"
import { t } from "../../i18n"
/**
* Factory class responsible for creating and configuring code indexing service dependencies.
@ -33,7 +34,7 @@ export class CodeIndexServiceFactory {
const apiKey = config.openAiOptions?.openAiNativeApiKey
if (!apiKey) {
throw new Error("OpenAI configuration missing for embedder creation")
throw new Error(t("embeddings:serviceFactory.openAiConfigMissing"))
}
return new OpenAiEmbedder({
...config.openAiOptions,
@ -41,7 +42,7 @@ export class CodeIndexServiceFactory {
})
} else if (provider === "ollama") {
if (!config.ollamaOptions?.ollamaBaseUrl) {
throw new Error("Ollama configuration missing for embedder creation")
throw new Error(t("embeddings:serviceFactory.ollamaConfigMissing"))
}
return new CodeIndexOllamaEmbedder({
...config.ollamaOptions,
@ -49,7 +50,7 @@ export class CodeIndexServiceFactory {
})
} else if (provider === "openai-compatible") {
if (!config.openAiCompatibleOptions?.baseUrl || !config.openAiCompatibleOptions?.apiKey) {
throw new Error("OpenAI Compatible configuration missing for embedder creation")
throw new Error(t("embeddings:serviceFactory.openAiCompatibleConfigMissing"))
}
return new OpenAICompatibleEmbedder(
config.openAiCompatibleOptions.baseUrl,
@ -58,12 +59,14 @@ export class CodeIndexServiceFactory {
)
} else if (provider === "gemini") {
if (!config.geminiOptions?.apiKey) {
throw new Error("Gemini configuration missing for embedder creation")
throw new Error(t("embeddings:serviceFactory.geminiConfigMissing"))
}
return new GeminiEmbedder(config.geminiOptions.apiKey)
}
throw new Error(`Invalid embedder type configured: ${config.embedderProvider}`)
throw new Error(
t("embeddings:serviceFactory.invalidEmbedderType", { embedderProvider: config.embedderProvider }),
)
}
/**
@ -96,33 +99,29 @@ export class CodeIndexServiceFactory {
let vectorSize: number | undefined
if (provider === "openai-compatible") {
if (config.openAiCompatibleOptions?.modelDimension && config.openAiCompatibleOptions.modelDimension > 0) {
vectorSize = config.openAiCompatibleOptions.modelDimension
} else {
// Fallback if not provided or invalid in openAiCompatibleOptions
vectorSize = getModelDimension(provider, modelId)
}
// First check if a manual dimension is provided (works for all providers)
if (config.modelDimension && config.modelDimension > 0) {
vectorSize = config.modelDimension
} else if (provider === "gemini") {
// Gemini's text-embedding-004 has a fixed dimension of 768
vectorSize = 768
} else {
// Fall back to model-specific dimension from profiles
vectorSize = getModelDimension(provider, modelId)
}
if (vectorSize === undefined) {
let errorMessage = `Could not determine vector dimension for model '${modelId}' with provider '${provider}'. `
if (vectorSize === undefined || vectorSize <= 0) {
if (provider === "openai-compatible") {
errorMessage += `Please ensure the 'Embedding Dimension' is correctly set in the OpenAI-Compatible provider settings.`
throw new Error(
t("embeddings:serviceFactory.vectorDimensionNotDeterminedOpenAiCompatible", { modelId, provider }),
)
} else {
errorMessage += `Check model profiles or configuration.`
throw new Error(t("embeddings:serviceFactory.vectorDimensionNotDetermined", { modelId, provider }))
}
throw new Error(errorMessage)
}
if (!config.qdrantUrl) {
// This check remains important
throw new Error("Qdrant URL missing for vector store creation")
throw new Error(t("embeddings:serviceFactory.qdrantUrlMissing"))
}
// Assuming constructor is updated: new QdrantVectorStore(workspacePath, url, vectorSize, apiKey?)
@ -170,7 +169,7 @@ export class CodeIndexServiceFactory {
fileWatcher: IFileWatcher
} {
if (!this.configManager.isFeatureConfigured) {
throw new Error("Cannot create services: Code indexing is not properly configured")
throw new Error(t("embeddings:serviceFactory.codeIndexingNotConfigured"))
}
const embedder = this.createEmbedder()

View file

@ -241,8 +241,8 @@ export interface WebviewMessage {
codebaseIndexEmbedderProvider: "openai" | "ollama" | "openai-compatible" | "gemini"
codebaseIndexEmbedderBaseUrl?: string
codebaseIndexEmbedderModelId: string
codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers
codebaseIndexOpenAiCompatibleBaseUrl?: string
codebaseIndexOpenAiCompatibleModelDimension?: number
codebaseIndexSearchMaxResults?: number
codebaseIndexSearchMinScore?: number

View file

@ -0,0 +1,97 @@
import { describe, it, expect } from "vitest"
import { getModeSelection, modes } from "../modes"
import type { PromptComponent } from "@roo-code/types"
describe("getModeSelection with empty promptComponent", () => {
it("should use built-in mode instructions when promptComponent is undefined", () => {
const architectMode = modes.find((m) => m.slug === "architect")!
// Test with undefined promptComponent (which is what getPromptComponent returns for empty objects)
const result = getModeSelection("architect", undefined, [])
// Should use built-in mode values
expect(result.roleDefinition).toBe(architectMode.roleDefinition)
expect(result.baseInstructions).toBe(architectMode.customInstructions)
expect(result.baseInstructions).toContain("Do some information gathering")
})
it("should use built-in mode instructions when promptComponent is null", () => {
const debugMode = modes.find((m) => m.slug === "debug")!
// Test with null promptComponent
const result = getModeSelection("debug", null as any, [])
// Should use built-in mode values
expect(result.roleDefinition).toBe(debugMode.roleDefinition)
expect(result.baseInstructions).toBe(debugMode.customInstructions)
expect(result.baseInstructions).toContain("Reflect on 5-7 different possible sources")
})
it("should use promptComponent when it has actual content", () => {
// Test with promptComponent that has actual content
const validPromptComponent: PromptComponent = {
roleDefinition: "Custom role",
customInstructions: "Custom instructions",
}
const result = getModeSelection("architect", validPromptComponent, [])
// Should use promptComponent values
expect(result.roleDefinition).toBe("Custom role")
expect(result.baseInstructions).toBe("Custom instructions")
})
it("should merge promptComponent with built-in mode when it has partial content", () => {
const architectMode = modes.find((m) => m.slug === "architect")!
// Test with promptComponent that only has customInstructions
const partialPromptComponent: PromptComponent = {
customInstructions: "Only custom instructions",
}
const result = getModeSelection("architect", partialPromptComponent, [])
// Should merge: use promptComponent's customInstructions but fall back to built-in roleDefinition
expect(result.roleDefinition).toBe(architectMode.roleDefinition) // Falls back to built-in
expect(result.baseInstructions).toBe("Only custom instructions") // Uses promptComponent
})
it("should merge promptComponent with built-in mode when it only has roleDefinition", () => {
const debugMode = modes.find((m) => m.slug === "debug")!
// Test with promptComponent that only has roleDefinition
const partialPromptComponent: PromptComponent = {
roleDefinition: "Custom debug role",
}
const result = getModeSelection("debug", partialPromptComponent, [])
// Should merge: use promptComponent's roleDefinition but fall back to built-in customInstructions
expect(result.roleDefinition).toBe("Custom debug role") // Uses promptComponent
expect(result.baseInstructions).toBe(debugMode.customInstructions) // Falls back to built-in
})
it("should handle promptComponent with both roleDefinition and customInstructions", () => {
// Test with promptComponent that has both properties
const fullPromptComponent: PromptComponent = {
roleDefinition: "Full custom role",
customInstructions: "Full custom instructions",
}
const result = getModeSelection("architect", fullPromptComponent, [])
// Should use promptComponent values for both
expect(result.roleDefinition).toBe("Full custom role")
expect(result.baseInstructions).toBe("Full custom instructions")
})
it("should fall back to default mode when built-in mode is not found", () => {
const defaultMode = modes[0] // First mode is the default
// Test with non-existent mode
const partialPromptComponent: PromptComponent = {
customInstructions: "Custom instructions for unknown mode",
}
const result = getModeSelection("non-existent-mode", partialPromptComponent, [])
// Should merge with default mode
expect(result.roleDefinition).toBe(defaultMode.roleDefinition) // Falls back to default mode
expect(result.baseInstructions).toBe("Custom instructions for unknown mode") // Uses promptComponent
})
})

View file

@ -443,10 +443,11 @@ describe("getModeSelection", () => {
expect(selection.baseInstructions).toBe(newCustomMode.customInstructions)
})
test("should return empty strings if slug does not exist in custom, prompt, or built-in modes", () => {
test("should fall back to default mode if slug does not exist in custom, prompt, or built-in modes", () => {
const selection = getModeSelection("non-existent-mode", undefined, customModesList)
expect(selection.roleDefinition).toBe("")
expect(selection.baseInstructions).toBe("")
const defaultMode = modes[0] // First mode is the default
expect(selection.roleDefinition).toBe(defaultMode.roleDefinition)
expect(selection.baseInstructions).toBe(defaultMode.customInstructions || "")
})
test("customMode's properties are used if customMode exists, ignoring promptComponent's properties", () => {

View file

@ -72,7 +72,7 @@ export const modes: readonly ModeConfig[] = [
description: "Plan and design before implementation",
groups: ["read", ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], "browser", "mcp"],
customInstructions:
"1. Do some information gathering (for example using read_file or search_files) to get more context about the task.\n\n2. You should also ask the user clarifying questions to get a better understanding of the task.\n\n3. Once you've gained more context about the user's request, you should create a detailed plan for how to accomplish the task. Include Mermaid diagrams if they help make your plan clearer.\n\n4. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.\n\n5. Use the switch_mode tool to request that the user switch to another mode to implement the solution.\n\n**IMPORTANT: Do not provide time estimates for how long tasks will take to complete. Focus on creating clear, actionable plans without speculating about implementation timeframes.**",
"1. Do some information gathering (for example using read_file or search_files) to get more context about the task.\n\n2. You should also ask the user clarifying questions to get a better understanding of the task.\n\n3. Once you've gained more context about the user's request, break down the task into clear, actionable steps and create a todo list using the `update_todo_list` tool. Each todo item should be:\n - Specific and actionable\n - Listed in logical execution order\n - Focused on a single, well-defined outcome\n - Clear enough that another mode could execute it independently\n\n4. As you gather more information or discover new requirements, update the todo list to reflect the current understanding of what needs to be accomplished.\n\n5. Ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and refine the todo list. Include Mermaid diagrams if they help clarify complex workflows or system architecture.\n\n6. Use the switch_mode tool to request that the user switch to another mode to implement the solution.\n\n**IMPORTANT: Focus on creating clear, actionable todo lists rather than lengthy markdown documents. Use the todo list as your primary planning tool to track and organize the work that needs to be done.**",
},
{
slug: "code",
@ -183,23 +183,29 @@ export function findModeBySlug(slug: string, modes: readonly ModeConfig[] | unde
/**
* Get the mode selection based on the provided mode slug, prompt component, and custom modes.
* If a custom mode is found, it takes precedence over the built-in modes.
* If no custom mode is found, the built-in mode is used.
* If no custom mode is found, the built-in mode is used with partial merging from promptComponent.
* If neither is found, the default mode is used.
*/
export function getModeSelection(mode: string, promptComponent?: PromptComponent, customModes?: ModeConfig[]) {
const customMode = findModeBySlug(mode, customModes)
const builtInMode = findModeBySlug(mode, modes)
const modeToUse = customMode || promptComponent || builtInMode
// If we have a custom mode, use it entirely
if (customMode) {
return {
roleDefinition: customMode.roleDefinition || "",
baseInstructions: customMode.customInstructions || "",
description: customMode.description || "",
}
}
const roleDefinition = modeToUse?.roleDefinition || ""
const baseInstructions = modeToUse?.customInstructions || ""
const description = (customMode || builtInMode)?.description || ""
// Otherwise, use built-in mode as base and merge with promptComponent
const baseMode = builtInMode || modes[0] // fallback to default mode
return {
roleDefinition,
baseInstructions,
description,
roleDefinition: promptComponent?.roleDefinition || baseMode.roleDefinition || "",
baseInstructions: promptComponent?.customInstructions || baseMode.customInstructions || "",
description: baseMode.description || "",
}
}

View file

@ -0,0 +1,38 @@
import { describe, it, expect } from "vitest"
import { isEmpty } from "../object"
describe("isEmpty", () => {
describe("should return true for empty values", () => {
it.each([
["empty object", {}],
["empty array", []],
["null", null],
["undefined", undefined],
["string", "string"],
["number", 123],
["boolean true", true],
["boolean false", false],
])("%s", (_, value) => {
expect(isEmpty(value)).toBe(true)
})
})
describe("should return false for non-empty values", () => {
it.each([
["object with properties", { a: 1 }],
["object with multiple properties", { a: 1, b: 2 }],
["array with one item", [1]],
["array with multiple items", [1, 2, 3]],
])("%s", (_, value) => {
expect(isEmpty(value)).toBe(false)
})
})
it("should handle objects with null prototype", () => {
const obj = Object.create(null)
expect(isEmpty(obj)).toBe(true)
obj.prop = "value"
expect(isEmpty(obj)).toBe(false)
})
})

18
src/utils/object.ts Normal file
View file

@ -0,0 +1,18 @@
/**
* Check if an object is empty (has no own enumerable properties)
* @param obj The object to check
* @returns true if the object is empty, false otherwise
*/
export function isEmpty(obj: unknown): boolean {
if (!obj || typeof obj !== "object") {
return true
}
// Check if it's an array
if (Array.isArray(obj)) {
return obj.length === 0
}
// Check if it's an object with no own properties
return Object.keys(obj).length === 0
}

View file

@ -943,10 +943,11 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"resize-none",
"overflow-x-hidden",
"overflow-y-auto",
"pr-2",
"pr-9",
"flex-none flex-grow",
"z-[2]",
"scrollbar-none",
"scrollbar-hide",
)}
onScroll={() => updateHighlights()}
/>
@ -961,24 +962,31 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
</Button>
)}
<div className="absolute top-1 right-1 z-30">
<IconButton
iconClass={isEnhancingPrompt ? "codicon-loading" : "codicon-sparkle"}
title={t("chat:enhancePrompt")}
disabled={sendingDisabled}
isLoading={isEnhancingPrompt}
onClick={handleEnhancePrompt}
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
/>
</div>
<div className="absolute bottom-1 right-1 z-30">
<IconButton
iconClass="codicon-send"
title={t("chat:sendMessage")}
disabled={sendingDisabled}
onClick={onSend}
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
/>
</div>
{!inputValue && (
<div
className={cn(
"absolute",
"left-2",
"flex",
"gap-2",
"text-xs",
"text-descriptionForeground",
"pointer-events-none",
"z-25",
"bottom-1.5",
"pr-2",
"transition-opacity",
"duration-200",
"ease-in-out",
"opacity-70",
)}>
className="absolute left-2 z-30 pr-9 flex items-center h-8"
style={{ bottom: "0.25rem", color: "var(--vscode-tab-inactiveForeground)" }}>
{placeholderBottomText}
</div>
)}
@ -1134,25 +1142,12 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0")}>
{codebaseIndexConfig?.codebaseIndexEnabled && <IndexingStatusDot />}
<IconButton
iconClass={isEnhancingPrompt ? "codicon-loading" : "codicon-sparkle"}
title={t("chat:enhancePrompt")}
disabled={sendingDisabled}
isLoading={isEnhancingPrompt}
onClick={handleEnhancePrompt}
/>
<IconButton
iconClass="codicon-device-camera"
title={t("chat:addImages")}
disabled={shouldDisableImages}
onClick={onSelectImages}
/>
<IconButton
iconClass="codicon-send"
title={t("chat:sendMessage")}
disabled={sendingDisabled}
onClick={onSend}
/>
</div>
</div>
</div>

View file

@ -51,6 +51,7 @@ interface LocalCodeIndexSettings {
codebaseIndexEmbedderProvider: EmbedderProvider
codebaseIndexEmbedderBaseUrl?: string
codebaseIndexEmbedderModelId: string
codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers
codebaseIndexSearchMaxResults?: number
codebaseIndexSearchMinScore?: number
@ -59,7 +60,6 @@ interface LocalCodeIndexSettings {
codeIndexQdrantApiKey?: string
codebaseIndexOpenAiCompatibleBaseUrl?: string
codebaseIndexOpenAiCompatibleApiKey?: string
codebaseIndexOpenAiCompatibleModelDimension?: number
codebaseIndexGeminiApiKey?: string
}
@ -72,6 +72,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
const { codebaseIndexConfig, codebaseIndexModels } = useExtensionState()
const [open, setOpen] = useState(false)
const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false)
const [isSetupSettingsOpen, setIsSetupSettingsOpen] = useState(false)
const [indexingStatus, setIndexingStatus] = useState<IndexingStatus>(externalIndexingStatus)
@ -85,13 +86,13 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
codebaseIndexEmbedderProvider: "openai",
codebaseIndexEmbedderBaseUrl: "",
codebaseIndexEmbedderModelId: "",
codebaseIndexEmbedderModelDimension: undefined,
codebaseIndexSearchMaxResults: CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS,
codebaseIndexSearchMinScore: CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE,
codeIndexOpenAiKey: "",
codeIndexQdrantApiKey: "",
codebaseIndexOpenAiCompatibleBaseUrl: "",
codebaseIndexOpenAiCompatibleApiKey: "",
codebaseIndexOpenAiCompatibleModelDimension: undefined,
codebaseIndexGeminiApiKey: "",
})
@ -115,6 +116,8 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
codebaseIndexEmbedderProvider: codebaseIndexConfig.codebaseIndexEmbedderProvider || "openai",
codebaseIndexEmbedderBaseUrl: codebaseIndexConfig.codebaseIndexEmbedderBaseUrl || "",
codebaseIndexEmbedderModelId: codebaseIndexConfig.codebaseIndexEmbedderModelId || "",
codebaseIndexEmbedderModelDimension:
codebaseIndexConfig.codebaseIndexEmbedderModelDimension || undefined,
codebaseIndexSearchMaxResults:
codebaseIndexConfig.codebaseIndexSearchMaxResults ?? CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS,
codebaseIndexSearchMinScore:
@ -123,8 +126,6 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
codeIndexQdrantApiKey: "",
codebaseIndexOpenAiCompatibleBaseUrl: codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl || "",
codebaseIndexOpenAiCompatibleApiKey: "",
codebaseIndexOpenAiCompatibleModelDimension:
codebaseIndexConfig.codebaseIndexOpenAiCompatibleModelDimension || undefined,
codebaseIndexGeminiApiKey: "",
}
setInitialSettings(settings)
@ -304,7 +305,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>{children}</PopoverTrigger>
<PopoverContent
className="w-[calc(100vw-32px)] max-w-[450px] max-h-[80vh] overflow-y-auto p-4"
className="w-[calc(100vw-32px)] max-w-[450px] max-h-[80vh] overflow-y-auto p-0"
align="end"
alignOffset={0}
side="bottom"
@ -312,9 +313,11 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
collisionPadding={16}
avoidCollisions={true}
container={portalContainer}>
<div className="mb-4">
<h3 className="text-base font-medium mb-2">{t("settings:codeIndex.title")}</h3>
<p className="text-sm text-vscode-descriptionForeground">
<div className="p-3 border-b border-vscode-dropdown-border cursor-default">
<div className="flex flex-row items-center gap-1 p-0 mt-0 mb-1 w-full">
<h4 className="m-0 pb-2 flex-1">{t("settings:codeIndex.title")}</h4>
</div>
<p className="my-0 pr-4 text-sm w-full">
<Trans i18nKey="settings:codeIndex.description">
<VSCodeLink
href={buildDocLink("features/experimental/codebase-indexing", "settings")}
@ -324,7 +327,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
</p>
</div>
<div className="space-y-4">
<div className="p-4">
{/* Status Section */}
<div className="space-y-2">
<h4 className="text-sm font-medium">{t("settings:codeIndex.statusTitle")}</h4>
@ -357,235 +360,297 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
)}
</div>
{/* Embedder Provider Section */}
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.embedderProviderLabel")}</label>
<Select
value={currentSettings.codebaseIndexEmbedderProvider}
onValueChange={(value: EmbedderProvider) =>
updateSetting("codebaseIndexEmbedderProvider", value)
}>
<SelectTrigger className="w-full">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="openai">{t("settings:codeIndex.openaiProvider")}</SelectItem>
<SelectItem value="ollama">{t("settings:codeIndex.ollamaProvider")}</SelectItem>
<SelectItem value="openai-compatible">
{t("settings:codeIndex.openaiCompatibleProvider")}
</SelectItem>
<SelectItem value="gemini">{t("settings:codeIndex.geminiProvider")}</SelectItem>
</SelectContent>
</Select>
</div>
{/* Setup Settings Disclosure */}
<div className="mt-4">
<button
onClick={() => setIsSetupSettingsOpen(!isSetupSettingsOpen)}
className="flex items-center text-xs text-vscode-foreground hover:text-vscode-textLink-foreground focus:outline-none"
aria-expanded={isSetupSettingsOpen}>
<span
className={`codicon codicon-${isSetupSettingsOpen ? "chevron-down" : "chevron-right"} mr-1`}></span>
<span className="text-base font-semibold">{t("settings:codeIndex.setupConfigLabel")}</span>
</button>
{/* Provider-specific settings */}
{currentSettings.codebaseIndexEmbedderProvider === "openai" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.openAiKeyLabel")}</label>
<VSCodeTextField
type="password"
value={currentSettings.codeIndexOpenAiKey || ""}
onInput={(e: any) => updateSetting("codeIndexOpenAiKey", e.target.value)}
placeholder={t("settings:codeIndex.openAiKeyPlaceholder")}
className="w-full"
/>
{isSetupSettingsOpen && (
<div className="mt-4 space-y-4">
{/* Embedder Provider Section */}
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.embedderProviderLabel")}
</label>
<Select
value={currentSettings.codebaseIndexEmbedderProvider}
onValueChange={(value: EmbedderProvider) =>
updateSetting("codebaseIndexEmbedderProvider", value)
}>
<SelectTrigger className="w-full">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="openai">
{t("settings:codeIndex.openaiProvider")}
</SelectItem>
<SelectItem value="ollama">
{t("settings:codeIndex.ollamaProvider")}
</SelectItem>
<SelectItem value="openai-compatible">
{t("settings:codeIndex.openaiCompatibleProvider")}
</SelectItem>
<SelectItem value="gemini">
{t("settings:codeIndex.geminiProvider")}
</SelectItem>
</SelectContent>
</Select>
</div>
{/* Provider-specific settings */}
{currentSettings.codebaseIndexEmbedderProvider === "openai" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.openAiKeyLabel")}
</label>
<VSCodeTextField
type="password"
value={currentSettings.codeIndexOpenAiKey || ""}
onInput={(e: any) =>
updateSetting("codeIndexOpenAiKey", e.target.value)
}
placeholder={t("settings:codeIndex.openAiKeyPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.modelLabel")}
</label>
<VSCodeDropdown
value={currentSettings.codebaseIndexEmbedderModelId}
onChange={(e: any) =>
updateSetting("codebaseIndexEmbedderModelId", e.target.value)
}
className="w-full">
<VSCodeOption value="">
{t("settings:codeIndex.selectModel")}
</VSCodeOption>
{getAvailableModels().map((modelId) => {
const model =
codebaseIndexModels?.[
currentSettings.codebaseIndexEmbedderProvider
]?.[modelId]
return (
<VSCodeOption key={modelId} value={modelId}>
{modelId}{" "}
{model
? t("settings:codeIndex.modelDimensions", {
dimension: model.dimension,
})
: ""}
</VSCodeOption>
)
})}
</VSCodeDropdown>
</div>
</>
)}
{currentSettings.codebaseIndexEmbedderProvider === "ollama" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.ollamaBaseUrlLabel")}
</label>
<VSCodeTextField
value={currentSettings.codebaseIndexEmbedderBaseUrl || ""}
onInput={(e: any) =>
updateSetting("codebaseIndexEmbedderBaseUrl", e.target.value)
}
placeholder={t("settings:codeIndex.ollamaUrlPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.modelLabel")}
</label>
<VSCodeDropdown
value={currentSettings.codebaseIndexEmbedderModelId}
onChange={(e: any) =>
updateSetting("codebaseIndexEmbedderModelId", e.target.value)
}
className="w-full">
<VSCodeOption value="">
{t("settings:codeIndex.selectModel")}
</VSCodeOption>
{getAvailableModels().map((modelId) => {
const model =
codebaseIndexModels?.[
currentSettings.codebaseIndexEmbedderProvider
]?.[modelId]
return (
<VSCodeOption key={modelId} value={modelId}>
{modelId}{" "}
{model
? t("settings:codeIndex.modelDimensions", {
dimension: model.dimension,
})
: ""}
</VSCodeOption>
)
})}
</VSCodeDropdown>
</div>
</>
)}
{currentSettings.codebaseIndexEmbedderProvider === "openai-compatible" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.openAiCompatibleBaseUrlLabel")}
</label>
<VSCodeTextField
value={currentSettings.codebaseIndexOpenAiCompatibleBaseUrl || ""}
onInput={(e: any) =>
updateSetting(
"codebaseIndexOpenAiCompatibleBaseUrl",
e.target.value,
)
}
placeholder={t("settings:codeIndex.openAiCompatibleBaseUrlPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.openAiCompatibleApiKeyLabel")}
</label>
<VSCodeTextField
type="password"
value={currentSettings.codebaseIndexOpenAiCompatibleApiKey || ""}
onInput={(e: any) =>
updateSetting("codebaseIndexOpenAiCompatibleApiKey", e.target.value)
}
placeholder={t("settings:codeIndex.openAiCompatibleApiKeyPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.modelLabel")}
</label>
<VSCodeTextField
value={currentSettings.codebaseIndexEmbedderModelId || ""}
onInput={(e: any) =>
updateSetting("codebaseIndexEmbedderModelId", e.target.value)
}
placeholder={t("settings:codeIndex.modelPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.modelDimensionLabel")}
</label>
<VSCodeTextField
value={
currentSettings.codebaseIndexEmbedderModelDimension?.toString() ||
""
}
onInput={(e: any) => {
const value = e.target.value ? parseInt(e.target.value) : undefined
updateSetting("codebaseIndexEmbedderModelDimension", value)
}}
placeholder={t("settings:codeIndex.modelDimensionPlaceholder")}
className="w-full"
/>
</div>
</>
)}
{currentSettings.codebaseIndexEmbedderProvider === "gemini" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.geminiApiKeyLabel")}
</label>
<VSCodeTextField
type="password"
value={currentSettings.codebaseIndexGeminiApiKey || ""}
onInput={(e: any) =>
updateSetting("codebaseIndexGeminiApiKey", e.target.value)
}
placeholder={t("settings:codeIndex.geminiApiKeyPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.modelLabel")}
</label>
<VSCodeDropdown
value={currentSettings.codebaseIndexEmbedderModelId}
onChange={(e: any) =>
updateSetting("codebaseIndexEmbedderModelId", e.target.value)
}
className="w-full">
<VSCodeOption value="">
{t("settings:codeIndex.selectModel")}
</VSCodeOption>
{getAvailableModels().map((modelId) => {
const model =
codebaseIndexModels?.[
currentSettings.codebaseIndexEmbedderProvider
]?.[modelId]
return (
<VSCodeOption key={modelId} value={modelId}>
{modelId}{" "}
{model
? t("settings:codeIndex.modelDimensions", {
dimension: model.dimension,
})
: ""}
</VSCodeOption>
)
})}
</VSCodeDropdown>
</div>
</>
)}
{/* Qdrant Settings */}
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.qdrantUrlLabel")}
</label>
<VSCodeTextField
value={currentSettings.codebaseIndexQdrantUrl || ""}
onInput={(e: any) => updateSetting("codebaseIndexQdrantUrl", e.target.value)}
placeholder={t("settings:codeIndex.qdrantUrlPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.qdrantApiKeyLabel")}
</label>
<VSCodeTextField
type="password"
value={currentSettings.codeIndexQdrantApiKey || ""}
onInput={(e: any) => updateSetting("codeIndexQdrantApiKey", e.target.value)}
placeholder={t("settings:codeIndex.qdrantApiKeyPlaceholder")}
className="w-full"
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.modelLabel")}</label>
<VSCodeDropdown
value={currentSettings.codebaseIndexEmbedderModelId}
onChange={(e: any) => updateSetting("codebaseIndexEmbedderModelId", e.target.value)}
className="w-full">
<VSCodeOption value="">{t("settings:codeIndex.selectModel")}</VSCodeOption>
{getAvailableModels().map((modelId) => {
const model =
codebaseIndexModels?.[currentSettings.codebaseIndexEmbedderProvider]?.[
modelId
]
return (
<VSCodeOption key={modelId} value={modelId}>
{modelId}{" "}
{model
? t("settings:codeIndex.modelDimensions", {
dimension: model.dimension,
})
: ""}
</VSCodeOption>
)
})}
</VSCodeDropdown>
</div>
</>
)}
{currentSettings.codebaseIndexEmbedderProvider === "ollama" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.ollamaBaseUrlLabel")}
</label>
<VSCodeTextField
value={currentSettings.codebaseIndexEmbedderBaseUrl || ""}
onInput={(e: any) => updateSetting("codebaseIndexEmbedderBaseUrl", e.target.value)}
placeholder={t("settings:codeIndex.ollamaUrlPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.modelLabel")}</label>
<VSCodeDropdown
value={currentSettings.codebaseIndexEmbedderModelId}
onChange={(e: any) => updateSetting("codebaseIndexEmbedderModelId", e.target.value)}
className="w-full">
<VSCodeOption value="">{t("settings:codeIndex.selectModel")}</VSCodeOption>
{getAvailableModels().map((modelId) => {
const model =
codebaseIndexModels?.[currentSettings.codebaseIndexEmbedderProvider]?.[
modelId
]
return (
<VSCodeOption key={modelId} value={modelId}>
{modelId}{" "}
{model
? t("settings:codeIndex.modelDimensions", {
dimension: model.dimension,
})
: ""}
</VSCodeOption>
)
})}
</VSCodeDropdown>
</div>
</>
)}
{currentSettings.codebaseIndexEmbedderProvider === "openai-compatible" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.openAiCompatibleBaseUrlLabel")}
</label>
<VSCodeTextField
value={currentSettings.codebaseIndexOpenAiCompatibleBaseUrl || ""}
onInput={(e: any) =>
updateSetting("codebaseIndexOpenAiCompatibleBaseUrl", e.target.value)
}
placeholder={t("settings:codeIndex.openAiCompatibleBaseUrlPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.openAiCompatibleApiKeyLabel")}
</label>
<VSCodeTextField
type="password"
value={currentSettings.codebaseIndexOpenAiCompatibleApiKey || ""}
onInput={(e: any) =>
updateSetting("codebaseIndexOpenAiCompatibleApiKey", e.target.value)
}
placeholder={t("settings:codeIndex.openAiCompatibleApiKeyPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.modelLabel")}</label>
<VSCodeTextField
value={currentSettings.codebaseIndexEmbedderModelId || ""}
onInput={(e: any) => updateSetting("codebaseIndexEmbedderModelId", e.target.value)}
placeholder={t("settings:codeIndex.modelPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.modelDimensionLabel")}
</label>
<VSCodeTextField
value={
currentSettings.codebaseIndexOpenAiCompatibleModelDimension?.toString() || ""
}
onInput={(e: any) => {
const value = e.target.value ? parseInt(e.target.value) : undefined
updateSetting("codebaseIndexOpenAiCompatibleModelDimension", value)
}}
placeholder={t("settings:codeIndex.modelDimensionPlaceholder")}
className="w-full"
/>
</div>
</>
)}
{currentSettings.codebaseIndexEmbedderProvider === "gemini" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.geminiApiKeyLabel")}
</label>
<VSCodeTextField
type="password"
value={currentSettings.codebaseIndexGeminiApiKey || ""}
onInput={(e: any) => updateSetting("codebaseIndexGeminiApiKey", e.target.value)}
placeholder={t("settings:codeIndex.geminiApiKeyPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.modelLabel")}</label>
<VSCodeDropdown
value={currentSettings.codebaseIndexEmbedderModelId}
onChange={(e: any) => updateSetting("codebaseIndexEmbedderModelId", e.target.value)}
className="w-full">
<VSCodeOption value="">{t("settings:codeIndex.selectModel")}</VSCodeOption>
{getAvailableModels().map((modelId) => {
const model =
codebaseIndexModels?.[currentSettings.codebaseIndexEmbedderProvider]?.[
modelId
]
return (
<VSCodeOption key={modelId} value={modelId}>
{modelId}{" "}
{model
? t("settings:codeIndex.modelDimensions", {
dimension: model.dimension,
})
: ""}
</VSCodeOption>
)
})}
</VSCodeDropdown>
</div>
</>
)}
{/* Qdrant Settings */}
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.qdrantUrlLabel")}</label>
<VSCodeTextField
value={currentSettings.codebaseIndexQdrantUrl || ""}
onInput={(e: any) => updateSetting("codebaseIndexQdrantUrl", e.target.value)}
placeholder={t("settings:codeIndex.qdrantUrlPlaceholder")}
className="w-full"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">{t("settings:codeIndex.qdrantApiKeyLabel")}</label>
<VSCodeTextField
type="password"
value={currentSettings.codeIndexQdrantApiKey || ""}
onInput={(e: any) => updateSetting("codeIndexQdrantApiKey", e.target.value)}
placeholder={t("settings:codeIndex.qdrantApiKeyPlaceholder")}
className="w-full"
/>
)}
</div>
{/* Advanced Settings Disclosure */}
@ -596,11 +661,13 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
aria-expanded={isAdvancedSettingsOpen}>
<span
className={`codicon codicon-${isAdvancedSettingsOpen ? "chevron-down" : "chevron-right"} mr-1`}></span>
<span>{t("settings:codeIndex.advancedConfigLabel")}</span>
<span className="text-base font-semibold">
{t("settings:codeIndex.advancedConfigLabel")}
</span>
</button>
{isAdvancedSettingsOpen && (
<div className="mt-4 space-y-4 pl-4">
<div className="mt-4 space-y-4">
{/* Search Score Threshold Slider */}
<div className="space-y-2">
<div className="flex items-center gap-2">
@ -693,7 +760,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
</div>
{/* Action Buttons */}
<div className="flex items-center justify-between gap-2 pt-2">
<div className="flex items-center justify-between gap-2 pt-6">
<div className="flex gap-2">
{(indexingStatus.systemStatus === "Error" || indexingStatus.systemStatus === "Standby") && (
<VSCodeButton

View file

@ -109,13 +109,17 @@ export const IndexingStatusDot: React.FC<IndexingStatusDotProps> = ({ className
onMouseEnter={handleMouseEnterButton}
onMouseLeave={handleMouseLeaveButton}
className={cn(
"flex items-center justify-center w-7 h-7 rounded-md",
"bg-transparent hover:bg-vscode-list-hoverBackground",
"cursor-pointer transition-all duration-200",
"opacity-85 hover:opacity-100 relative",
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-md min-w-[28px] min-h-[28px]",
"opacity-85 text-vscode-foreground",
"transition-all duration-150",
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
className,
)}
aria-label={getTooltipText()}>
{/* Status dot */}
<span
className={cn(
"inline-block w-2 h-2 rounded-full relative z-10 transition-colors duration-200",

View file

@ -35,20 +35,32 @@ export const ShareButton = ({ item, disabled = false }: ShareButtonProps) => {
const { t } = useTranslation()
const { sharingEnabled, cloudIsAuthenticated, cloudUserInfo } = useExtensionState()
const wasUnauthenticatedRef = useRef(false)
const initiatedAuthFromThisButtonRef = useRef(false)
// Track authentication state changes to auto-open popover after login
useEffect(() => {
if (!cloudIsAuthenticated || !sharingEnabled) {
wasUnauthenticatedRef.current = true
} else if (wasUnauthenticatedRef.current && cloudIsAuthenticated && sharingEnabled) {
// User just authenticated, send telemetry, close modal, and open the popover
telemetryClient.capture(TelemetryEventName.ACCOUNT_CONNECT_SUCCESS)
setConnectModalOpen(false)
setShareDropdownOpen(true)
// Only open dropdown if auth was initiated from this button
if (initiatedAuthFromThisButtonRef.current) {
// User just authenticated from this share button, send telemetry, close modal, and open the popover
telemetryClient.capture(TelemetryEventName.ACCOUNT_CONNECT_SUCCESS)
setConnectModalOpen(false)
setShareDropdownOpen(true)
initiatedAuthFromThisButtonRef.current = false // Reset the flag
}
wasUnauthenticatedRef.current = false
}
}, [cloudIsAuthenticated, sharingEnabled])
// Cleanup effect to reset flag on unmount
useEffect(() => {
return () => {
initiatedAuthFromThisButtonRef.current = false
}
}, [])
// Listen for share success messages from the extension
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
@ -92,6 +104,8 @@ export const ShareButton = ({ item, disabled = false }: ShareButtonProps) => {
// Send telemetry for connect to cloud action
telemetryClient.capture(TelemetryEventName.SHARE_CONNECT_TO_CLOUD_CLICKED)
// Mark that authentication was initiated from this button
initiatedAuthFromThisButtonRef.current = true
vscode.postMessage({ type: "rooCloudSignIn" })
setShareDropdownOpen(false)
setConnectModalOpen(false)

View file

@ -265,7 +265,7 @@ describe("TaskActions", () => {
expect(screen.queryByText("Connect to Cloud")).not.toBeInTheDocument()
})
it("automatically opens popover when user becomes authenticated", () => {
it("does not automatically open popover when user becomes authenticated from elsewhere", () => {
// Start with unauthenticated state
mockUseExtensionState.mockReturnValue({
sharingEnabled: false,
@ -277,7 +277,7 @@ describe("TaskActions", () => {
// Verify popover is not open initially
expect(screen.queryByText("Share with Organization")).not.toBeInTheDocument()
// Simulate user becoming authenticated
// Simulate user becoming authenticated (e.g., from AccountView)
mockUseExtensionState.mockReturnValue({
sharingEnabled: true,
cloudIsAuthenticated: true,
@ -288,7 +288,47 @@ describe("TaskActions", () => {
rerender(<TaskActions item={mockItem} buttonsDisabled={false} />)
// Verify popover automatically opens and shows sharing options
// Verify popover does NOT automatically open when auth happens from elsewhere
expect(screen.queryByText("Share with Organization")).not.toBeInTheDocument()
expect(screen.queryByText("Share Publicly")).not.toBeInTheDocument()
})
it("automatically opens popover when user authenticates from share button", () => {
// Start with unauthenticated state
mockUseExtensionState.mockReturnValue({
sharingEnabled: false,
cloudIsAuthenticated: false,
} as any)
const { rerender } = render(<TaskActions item={mockItem} buttonsDisabled={false} />)
// Click share button to open connect modal
const buttons = screen.getAllByRole("button")
const shareButton = buttons.find((btn) => btn.querySelector(".codicon-link"))
expect(shareButton).toBeDefined()
fireEvent.click(shareButton!)
// Click connect button to initiate authentication
const connectButton = screen.getByText("Connect")
fireEvent.click(connectButton)
// Verify rooCloudSignIn message was sent
expect(mockPostMessage).toHaveBeenCalledWith({
type: "rooCloudSignIn",
})
// Simulate user becoming authenticated after clicking connect from share button
mockUseExtensionState.mockReturnValue({
sharingEnabled: true,
cloudIsAuthenticated: true,
cloudUserInfo: {
organizationName: "Test Organization",
},
} as any)
rerender(<TaskActions item={mockItem} buttonsDisabled={false} />)
// Verify popover automatically opens when auth was initiated from share button
expect(screen.getByText("Share with Organization")).toBeInTheDocument()
expect(screen.getByText("Share Publicly")).toBeInTheDocument()
})

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "URL base d'Ollama",
"qdrantApiKeyLabel": "Clau API de Qdrant",
"qdrantApiKeyPlaceholder": "Introduïu la vostra clau API de Qdrant (opcional)",
"setupConfigLabel": "Configuració",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -72,6 +72,7 @@
"qdrantKeyLabel": "Qdrant-Schlüssel:",
"qdrantApiKeyLabel": "Qdrant API-Schlüssel",
"qdrantApiKeyPlaceholder": "Gib deinen Qdrant API-Schlüssel ein (optional)",
"setupConfigLabel": "Einrichtung",
"startIndexingButton": "Start",
"clearIndexDataButton": "Index löschen",
"unsavedSettingsMessage": "Bitte speichere deine Einstellungen, bevor du den Indexierungsprozess startest.",
@ -481,7 +482,7 @@
},
"commandDelay": {
"label": "Terminal-Befehlsverzögerung",
"description": "Verzögerung in Millisekunden, die nach der Befehlsausführung hinzugefügt wird. Die Standardeinstellung von 0 deaktiviert die Verzögerung vollständig. Dies kann dazu beitragen, dass die Befehlsausgabe in Terminals mit Timing-Problemen vollständig erfasst wird. In den meisten Terminals wird dies durch Setzen von `PROMPT_COMMAND='sleep N'` implementiert, und Powershell fügt `start-sleep` am Ende jedes Befehls hinzu. Ursprünglich war dies eine Lösung für VSCode-Bug#237208 und ist möglicherweise nicht mehr erforderlich. <0>Mehr erfahren</0>"
"description": "Verzögerung in Millisekunden, die nach der Befehlsausführung hinzugefügt wird. Die Standardeinstellung von 0 deaktiviert die Verzögerung vollständig. Dies kann dazu beitragen, dass die Befehlsausgabe in Terminals mit Timing-Problemen vollständig erfasst wird. In den meisten Terminals wird dies durch Setzen von `PROMPT_COMMAND='sleep N'` und Powershell fügt `start-sleep` am Ende jedes Befehls hinzu. Ursprünglich war dies eine Lösung für VSCode-Bug#237208 und ist möglicherweise nicht mehr erforderlich. <0>Mehr erfahren</0>"
},
"compressProgressBar": {
"label": "Fortschrittsbalken-Ausgabe komprimieren",
@ -501,72 +502,72 @@
},
"zshP10k": {
"label": "Powerlevel10k-Integration aktivieren",
"description": "Wenn aktiviert, wird POWERLEVEL9K_TERM_SHELL_INTEGRATION=true gesetzt, um die Shell-Integrationsfunktionen von Powerlevel10k zu aktivieren. <0>Mehr erfahren</0>"
"description": "Wenn aktiviert, wird POWERLEVEL9K_INSTANT_PROMPT=quiet gesetzt, um die Powerlevel10k-Integration zu aktivieren. Dies kann die Leistung verbessern, indem der Prompt sofort angezeigt wird. <0>Mehr erfahren</0>"
},
"zdotdir": {
"label": "ZDOTDIR-Behandlung aktivieren",
"description": "Erstellt bei Aktivierung ein temporäres Verzeichnis für ZDOTDIR, um die zsh-Shell-Integration korrekt zu handhaben. Dies stellt sicher, dass die VSCode-Shell-Integration mit zsh funktioniert und dabei deine zsh-Konfiguration erhalten bleibt. <0>Mehr erfahren</0>"
"label": "ZDOTDIR Handhabung aktivieren",
"description": "Wenn aktiviert, wird ein temporäres Verzeichnis für ZDOTDIR erstellt, um die Zsh-Shell-Integration ordnungsgemäß zu handhaben. Dies stellt sicher, dass die VSCode-Shell-Integration mit Zsh korrekt funktioniert, während deine Zsh-Konfiguration erhalten bleibt. <0>Mehr erfahren</0>"
},
"inheritEnv": {
"label": "Umgebungsvariablen übernehmen",
"description": "Wenn aktiviert, übernimmt das Terminal Umgebungsvariablen vom übergeordneten VSCode-Prozess, wie z.B. in Benutzerprofilen definierte Shell-Integrationseinstellungen. Dies schaltet direkt die globale VSCode-Einstellung `terminal.integrated.inheritEnv` um. <0>Mehr erfahren</0>"
"label": "Umgebungsvariablen erben",
"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>"
}
},
"advanced": {
"diff": {
"label": "Bearbeitung durch Diffs aktivieren",
"description": "Wenn aktiviert, kann Roo Dateien schneller bearbeiten und lehnt automatisch gekürzte vollständige Dateischreibvorgänge ab. Funktioniert am besten mit dem neuesten Claude 3.7 Sonnet-Modell.",
"description": "Wenn aktiviert, kann Roo Dateien schneller bearbeiten und lehnt automatisch abgeschnittene vollständige Dateischreibvorgänge ab. Funktioniert am besten mit dem neuesten Claude 3.7 Sonnet-Modell.",
"strategy": {
"label": "Diff-Strategie",
"options": {
"standard": "Standard (Einzelner Block)",
"multiBlock": "Experimentell: Multi-Block-Diff",
"unified": "Experimentell: Vereinheitlichter Diff"
"standard": "Standard (Einzelblock)",
"multiBlock": "Experimentell: Mehrblock-Diff",
"unified": "Experimentell: Einheitliches Diff"
},
"descriptions": {
"standard": "Die Standard-Diff-Strategie wendet Änderungen auf einen einzelnen Codeblock gleichzeitig an.",
"unified": "Die vereinheitlichte Diff-Strategie verwendet mehrere Ansätze zum Anwenden von Diffs und wählt den besten Ansatz aus.",
"multiBlock": "Die Multi-Block-Diff-Strategie ermöglicht die Aktualisierung mehrerer Codeblöcke in einer Datei in einer Anfrage."
"standard": "Die Standard-Diff-Strategie wendet Änderungen jeweils auf einen einzelnen Codeblock an.",
"unified": "Die einheitliche Diff-Strategie wendet mehrere Ansätze zur Anwendung von Diffs an und wählt den besten Ansatz.",
"multiBlock": "Die Mehrblock-Diff-Strategie ermöglicht das Aktualisieren mehrerer Codeblöcke in einer Datei in einer Anfrage."
}
},
"matchPrecision": {
"label": "Übereinstimmungsgenauigkeit",
"description": "Dieser Schieberegler steuert, wie genau Codeabschnitte beim Anwenden von Diffs übereinstimmen müssen. Niedrigere Werte ermöglichen flexiblere Übereinstimmungen, erhöhen aber das Risiko falscher Ersetzungen. Verwende Werte unter 100% mit äußerster Vorsicht."
"label": "Übereinstimmungspräzision",
"description": "Dieser Schieberegler steuert, wie genau Codeabschnitte bei der Anwendung von Diffs übereinstimmen müssen. Niedrigere Werte ermöglichen eine flexiblere Übereinstimmung, erhöhen aber das Risiko falscher Ersetzungen. Verwenden Sie Werte unter 100 % mit äußerster Vorsicht."
}
}
},
"experimental": {
"DIFF_STRATEGY_UNIFIED": {
"name": "Experimentelle einheitliche Diff-Strategie verwenden",
"description": "Aktiviert die experimentelle einheitliche Diff-Strategie. Diese Strategie könnte die Anzahl der durch Modellfehler verursachten Wiederholungen reduzieren, kann aber unerwartetes Verhalten oder falsche Bearbeitungen verursachen. Nur aktivieren, wenn du die Risiken verstehst und bereit bist, alle Änderungen sorgfältig zu überprüfen."
"description": "Aktiviere die experimentelle einheitliche Diff-Strategie. Diese Strategie könnte die Anzahl der durch Modellfehler verursachten Wiederholungsversuche reduzieren, kann aber zu unerwartetem Verhalten oder falschen Bearbeitungen führen. Aktiviere sie nur, wenn du die Risiken verstehst und bereit bist, alle Änderungen sorgfältig zu überprüfen."
},
"SEARCH_AND_REPLACE": {
"name": "Experimentelles Such- und Ersetzungswerkzeug verwenden",
"description": "Aktiviert das experimentelle Such- und Ersetzungswerkzeug, das Roo ermöglicht, mehrere Instanzen eines Suchbegriffs in einer Anfrage zu ersetzen."
"description": "Aktiviere das experimentelle Such- und Ersetzungswerkzeug, mit dem Roo mehrere Instanzen eines Suchbegriffs in einer Anfrage ersetzen kann."
},
"INSERT_BLOCK": {
"name": "Experimentelles Inhalts-Einfüge-Werkzeug verwenden",
"description": "Aktiviert das experimentelle Inhalts-Einfüge-Werkzeug, das Roo ermöglicht, Inhalte an bestimmten Zeilennummern einzufügen, ohne einen Diff erstellen zu müssen."
"name": "Experimentelles Inhalts-Einfügewerkzeug verwenden",
"description": "Aktiviere das experimentelle Inhalts-Einfügewerkzeug, mit dem Roo Inhalte an bestimmten Zeilennummern einfügen kann, ohne einen Diff erstellen zu müssen."
},
"POWER_STEERING": {
"name": "Experimentellen \"Servolenkung\"-Modus verwenden",
"description": "Wenn aktiviert, wird Roo das Modell häufiger an die Details seiner aktuellen Modusdefinition erinnern. Dies führt zu einer stärkeren Einhaltung von Rollendefinitionen und benutzerdefinierten Anweisungen, verwendet aber mehr Tokens pro Nachricht."
},
"MULTI_SEARCH_AND_REPLACE": {
"name": "Experimentelles Multi-Block-Diff-Werkzeug verwenden",
"description": "Wenn aktiviert, verwendet Roo das Multi-Block-Diff-Werkzeug. Dies versucht, mehrere Codeblöcke in der Datei in einer Anfrage zu aktualisieren."
"name": "Experimentellen \"Power Steering\"-Modus verwenden",
"description": "Wenn aktiviert, erinnert Roo das Modell häufiger an die Details seiner aktuellen Modusdefinition. Dies führt zu einer stärkeren Einhaltung von Rollendefinitionen und benutzerdefinierten Anweisungen, verbraucht aber mehr Token pro Nachricht."
},
"CONCURRENT_FILE_READS": {
"name": "Gleichzeitiges Lesen von Dateien aktivieren",
"description": "Wenn aktiviert, kann Roo mehrere Dateien in einer einzigen Anfrage lesen. Wenn deaktiviert, muss Roo Dateien nacheinander lesen. Das Deaktivieren kann helfen, wenn du mit weniger leistungsfähigen Modellen arbeitest oder mehr Kontrolle über den Dateizugriff möchtest."
"description": "Wenn aktiviert, kann Roo mehrere Dateien in einer einzigen Anfrage lesen. Wenn deaktiviert, muss Roo Dateien einzeln lesen. Das Deaktivieren kann hilfreich sein, wenn mit weniger fähigen Modellen gearbeitet wird oder wenn du mehr Kontrolle über den Dateizugriff haben möchtest."
},
"MULTI_SEARCH_AND_REPLACE": {
"name": "Experimentelles Multi-Block-Diff-Tool verwenden",
"description": "Wenn aktiviert, wird Roo das Multi-Block-Diff-Tool verwenden. Dies wird versuchen, mehrere Codeblöcke in der Datei in einer Anfrage zu aktualisieren."
},
"MARKETPLACE": {
"name": "Marketplace aktivieren",
"description": "Wenn aktiviert, kannst du MCP und benutzerdefinierte Modi aus dem Marketplace installieren und verwalten."
"name": "Marktplatz aktivieren",
"description": "Wenn aktiviert, können Sie MCPs und benutzerdefinierte Modi aus dem Marketplace installieren."
},
"MULTI_FILE_APPLY_DIFF": {
"name": "Gleichzeitige Dateibearbeitungen aktivieren",
"description": "Wenn aktiviert, kann Roo mehrere Dateien in einer einzigen Anfrage bearbeiten. Wenn deaktiviert, muss Roo Dateien einzeln bearbeiten. Das Deaktivieren kann helfen, wenn du mit weniger fähigen Modellen arbeitest oder mehr Kontrolle über Dateiänderungen haben möchtest."
"description": "Wenn aktiviert, kann Roo mehrere Dateien in einer einzigen Anfrage bearbeiten. Wenn deaktiviert, muss Roo Dateien einzeln bearbeiten. Das Deaktivieren kann hilfreich sein, wenn mit weniger fähigen Modellen gearbeitet wird oder wenn du mehr Kontrolle über Dateiänderungen haben möchtest."
}
},
"promptCaching": {
@ -575,16 +576,16 @@
},
"temperature": {
"useCustom": "Benutzerdefinierte Temperatur verwenden",
"description": "Steuert die Zufälligkeit in den Antworten des Modells.",
"description": "Steuert die Zufälligkeit der Modellantworten.",
"rangeDescription": "Höhere Werte machen die Ausgabe zufälliger, niedrigere Werte machen sie deterministischer."
},
"modelInfo": {
"supportsImages": "Unterstützt Bilder",
"noImages": "Unterstützt keine Bilder",
"supportsComputerUse": "Unterstützt Computer-Nutzung",
"noComputerUse": "Unterstützt keine Computer-Nutzung",
"supportsPromptCache": "Unterstützt Prompt-Caching",
"noPromptCache": "Unterstützt kein Prompt-Caching",
"supportsComputerUse": "Unterstützt Computernutzung",
"noComputerUse": "Unterstützt keine Computernutzung",
"supportsPromptCache": "Unterstützt Prompt-Cache",
"noPromptCache": "Unterstützt keinen Prompt-Cache",
"maxOutput": "Maximale Ausgabe",
"inputPrice": "Eingabepreis",
"outputPrice": "Ausgabepreis",
@ -592,27 +593,27 @@
"cacheWritesPrice": "Cache-Schreibpreis",
"enableStreaming": "Streaming aktivieren",
"enableR1Format": "R1-Modellparameter aktivieren",
"enableR1FormatTips": "Muss aktiviert werden, wenn R1-Modelle wie QWQ verwendet werden, um 400-Fehler zu vermeiden",
"enableR1FormatTips": "Muss bei Verwendung von R1-Modellen wie QWQ aktiviert werden, um 400er-Fehler zu vermeiden",
"useAzure": "Azure verwenden",
"azureApiVersion": "Azure API-Version festlegen",
"gemini": {
"freeRequests": "* Kostenlos bis zu {{count}} Anfragen pro Minute. Danach hängt die Abrechnung von der Prompt-Größe ab.",
"pricingDetails": "Weitere Informationen finden Sie in den Preisdetails.",
"pricingDetails": "Weitere Informationen finden Sie unter Preisdetails.",
"billingEstimate": "* Die Abrechnung ist eine Schätzung - die genauen Kosten hängen von der Prompt-Größe ab."
}
},
"modelPicker": {
"automaticFetch": "Die Erweiterung ruft automatisch die neueste Liste der verfügbaren Modelle von <serviceLink>{{serviceName}}</serviceLink> ab. Wenn du dir nicht sicher bist, welches Modell du wählen sollst, funktioniert Roo Code am besten mit <defaultModelLink>{{defaultModelId}}</defaultModelLink>. Du kannst auch nach \"free\" suchen, um derzeit verfügbare kostenlose Optionen zu finden.",
"automaticFetch": "Die Erweiterung ruft automatisch die neueste Liste der auf <serviceLink>{{serviceName}}</serviceLink> verfügbaren Modelle ab. Wenn du dir nicht sicher bist, welches Modell du wählen sollst, funktioniert Roo Code am besten mit <defaultModelLink>{{defaultModelId}}</defaultModelLink>. Du kannst auch versuchen, nach \"kostenlos\" zu suchen, um die derzeit verfügbaren kostenlosen Optionen zu finden.",
"label": "Modell",
"searchPlaceholder": "Suchen",
"noMatchFound": "Keine Übereinstimmung gefunden",
"useCustomModel": "Benutzerdefiniert verwenden: {{modelId}}"
},
"footer": {
"feedback": "Wenn du Fragen oder Feedback hast, kannst du gerne ein Issue auf <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> öffnen oder <redditLink>reddit.com/r/RooCode</redditLink> oder <discordLink>discord.gg/roocode</discordLink> beitreten",
"feedback": "Wenn du Fragen oder Feedback hast, kannst du gerne ein Issue auf <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> eröffnen oder <redditLink>reddit.com/r/RooCode</redditLink> oder <discordLink>discord.gg/roocode</discordLink> beitreten",
"telemetry": {
"label": "Anonyme Fehler- und Nutzungsberichte zulassen",
"description": "Helfen Sie, Roo Code zu verbessern, indem Sie anonyme Nutzungsdaten und Fehlerberichte senden. Es werden niemals Code, Prompts oder persönliche Informationen gesendet. Weitere Details finden Sie in unserer Datenschutzrichtlinie."
"description": "Hilf mit, Roo Code zu verbessern, indem du anonyme Nutzungsdaten und Fehlerberichte sendest. Es werden niemals Code, Prompts oder persönliche Informationen gesendet (es sei denn, du verbindest dich mit Roo Code Cloud). Weitere Einzelheiten findest du in unserer <privacyLink>Datenschutzrichtlinie</privacyLink>."
},
"settings": {
"import": "Importieren",
@ -621,19 +622,19 @@
}
},
"thinkingBudget": {
"maxTokens": "Maximale Tokens",
"maxThinkingTokens": "Maximale Thinking-Tokens"
"maxTokens": "Max Tokens",
"maxThinkingTokens": "Max Thinking Tokens"
},
"validation": {
"apiKey": "Du musst einen gültigen API-Schlüssel angeben.",
"awsRegion": "Du musst eine Region für Amazon Bedrock auswählen.",
"awsRegion": "Du musst eine Region für die Verwendung mit Amazon Bedrock auswählen.",
"googleCloud": "Du musst eine gültige Google Cloud Projekt-ID und Region angeben.",
"modelId": "Du musst eine gültige Modell-ID angeben.",
"modelSelector": "Du musst einen gültigen Modell-Selektor angeben.",
"openAi": "Du musst eine gültige Basis-URL, API-Schlüssel und Modell-ID angeben.",
"modelSelector": "Du musst einen gültigen Modellselektor angeben.",
"openAi": "Du musst eine gültige Basis-URL, einen API-Schlüssel und eine Modell-ID angeben.",
"arn": {
"invalidFormat": "Ungültiges ARN-Format. Bitte überprüfen Sie die Formatanforderungen.",
"regionMismatch": "Warnung: Die Region in deiner ARN ({{arnRegion}}) stimmt nicht mit deiner ausgewählten Region ({{region}}) überein. Dies kann zu Zugriffsproblemen führen. Der Provider wird die Region aus der ARN verwenden."
"invalidFormat": "Ungültiges ARN-Format. Bitte überprüfe die Formatanforderungen.",
"regionMismatch": "Warnung: Die Region in deiner ARN ({{arnRegion}}) stimmt nicht mit deiner ausgewählten Region ({{region}}) überein. Dies kann zu Zugriffsproblemen führen. Der Anbieter wird die Region aus der ARN verwenden."
},
"modelAvailability": "Die von dir angegebene Modell-ID ({{modelId}}) ist nicht verfügbar. Bitte wähle ein anderes Modell.",
"providerNotAllowed": "Anbieter '{{provider}}' ist von deiner Organisation nicht erlaubt",
@ -646,7 +647,7 @@
"accessKey": "Zugriffsschlüssel eingeben...",
"secretKey": "Geheimschlüssel eingeben...",
"sessionToken": "Sitzungstoken eingeben...",
"credentialsJson": "Anmeldedaten-JSON eingeben...",
"credentialsJson": "Anmeldeinformationen JSON eingeben...",
"keyFilePath": "Schlüsseldateipfad eingeben...",
"projectId": "Projekt-ID eingeben...",
"customArn": "ARN eingeben (z.B. arn:aws:bedrock:us-east-1:123456789012:foundation-model/my-model)",
@ -674,5 +675,5 @@
"useCustomArn": "Benutzerdefinierte ARN verwenden..."
},
"includeMaxOutputTokens": "Maximale Ausgabe-Tokens einbeziehen",
"includeMaxOutputTokensDescription": "Sende den Parameter für maximale Ausgabe-Tokens in API-Anfragen. Einige Anbieter unterstützen dies möglicherweise nicht."
"includeMaxOutputTokensDescription": "Senden Sie den Parameter für maximale Ausgabe-Tokens in API-Anfragen. Einige Anbieter unterstützen dies möglicherweise nicht."
}

View file

@ -72,6 +72,7 @@
"qdrantKeyLabel": "Qdrant Key:",
"qdrantApiKeyLabel": "Qdrant API Key",
"qdrantApiKeyPlaceholder": "Enter your Qdrant API key (optional)",
"setupConfigLabel": "Setup",
"advancedConfigLabel": "Advanced Configuration",
"searchMinScoreLabel": "Search Score Threshold",
"searchMinScoreDescription": "Minimum similarity score (0.0-1.0) required for search results. Lower values return more results but may be less relevant. Higher values return fewer but more relevant results.",

View file

@ -72,6 +72,7 @@
"qdrantKeyLabel": "Clave de Qdrant:",
"qdrantApiKeyLabel": "Clave API de Qdrant",
"qdrantApiKeyPlaceholder": "Introduce tu clave API de Qdrant (opcional)",
"setupConfigLabel": "Configuración",
"startIndexingButton": "Iniciar",
"clearIndexDataButton": "Borrar índice",
"unsavedSettingsMessage": "Por favor guarda tus ajustes antes de iniciar el proceso de indexación.",

View file

@ -72,6 +72,7 @@
"qdrantKeyLabel": "Clé Qdrant :",
"qdrantApiKeyLabel": "Clé API Qdrant",
"qdrantApiKeyPlaceholder": "Entrez votre clé API Qdrant (optionnel)",
"setupConfigLabel": "Configuration",
"startIndexingButton": "Démarrer",
"clearIndexDataButton": "Effacer l'index",
"unsavedSettingsMessage": "Merci d'enregistrer tes paramètres avant de démarrer le processus d'indexation.",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Ollama आधार URL",
"qdrantApiKeyLabel": "Qdrant API कुंजी",
"qdrantApiKeyPlaceholder": "अपनी Qdrant API कुंजी दर्ज करें (वैकल्पिक)",
"setupConfigLabel": "सेटअप",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "URL Dasar Ollama",
"qdrantApiKeyLabel": "Kunci API Qdrant",
"qdrantApiKeyPlaceholder": "Masukkan kunci API Qdrant kamu (opsional)",
"setupConfigLabel": "Pengaturan",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "URL base Ollama",
"qdrantApiKeyLabel": "Chiave API Qdrant",
"qdrantApiKeyPlaceholder": "Inserisci la tua chiave API Qdrant (opzionale)",
"setupConfigLabel": "Impostazione",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Ollama ベースURL",
"qdrantApiKeyLabel": "Qdrant APIキー",
"qdrantApiKeyPlaceholder": "Qdrant APIキーを入力オプション",
"setupConfigLabel": "設定",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Ollama 기본 URL",
"qdrantApiKeyLabel": "Qdrant API 키",
"qdrantApiKeyPlaceholder": "Qdrant API 키를 입력하세요 (선택사항)",
"setupConfigLabel": "설정",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Ollama Basis-URL",
"qdrantApiKeyLabel": "Qdrant API-sleutel",
"qdrantApiKeyPlaceholder": "Voer je Qdrant API-sleutel in (optioneel)",
"setupConfigLabel": "Instellen",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Bazowy URL Ollama",
"qdrantApiKeyLabel": "Klucz API Qdrant",
"qdrantApiKeyPlaceholder": "Wprowadź swój klucz API Qdrant (opcjonalnie)",
"setupConfigLabel": "Konfiguracja",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "URL Base do Ollama",
"qdrantApiKeyLabel": "Chave da API Qdrant",
"qdrantApiKeyPlaceholder": "Insira sua chave da API Qdrant (opcional)",
"setupConfigLabel": "Configuração",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Базовый URL Ollama",
"qdrantApiKeyLabel": "API-ключ Qdrant",
"qdrantApiKeyPlaceholder": "Введите ваш API-ключ Qdrant (необязательно)",
"setupConfigLabel": "Настройка",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Ollama Temel URL",
"qdrantApiKeyLabel": "Qdrant API Anahtarı",
"qdrantApiKeyPlaceholder": "Qdrant API anahtarınızı girin (isteğe bağlı)",
"setupConfigLabel": "Kurulum",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "URL cơ sở Ollama",
"qdrantApiKeyLabel": "Khóa API Qdrant",
"qdrantApiKeyPlaceholder": "Nhập khóa API Qdrant của bạn (tùy chọn)",
"setupConfigLabel": "Cài đặt",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",

View file

@ -72,6 +72,7 @@
"qdrantKeyLabel": "Qdrant 密钥:",
"qdrantApiKeyLabel": "Qdrant API 密钥",
"qdrantApiKeyPlaceholder": "输入你的 Qdrant API 密钥(可选)",
"setupConfigLabel": "设置",
"startIndexingButton": "开始",
"clearIndexDataButton": "清除索引",
"unsavedSettingsMessage": "请先保存设置再开始索引过程。",

View file

@ -81,6 +81,7 @@
"ollamaBaseUrlLabel": "Ollama 基礎 URL",
"qdrantApiKeyLabel": "Qdrant API 金鑰",
"qdrantApiKeyPlaceholder": "輸入您的 Qdrant API 金鑰(選用)",
"setupConfigLabel": "設定",
"ollamaUrlPlaceholder": "http://localhost:11434",
"openAiCompatibleBaseUrlPlaceholder": "https://api.example.com",
"modelDimensionPlaceholder": "1536",