mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
Merge branch 'main' into cte/benchmark-monorepo
This commit is contained in:
commit
9164e5e203
22 changed files with 253 additions and 230 deletions
|
|
@ -2,12 +2,6 @@ import * as assert from "assert"
|
|||
import * as vscode from "vscode"
|
||||
|
||||
suite("Roo Code Extension", () => {
|
||||
test("OPENROUTER_API_KEY environment variable is set", () => {
|
||||
if (!process.env.OPENROUTER_API_KEY) {
|
||||
assert.fail("OPENROUTER_API_KEY environment variable is not set")
|
||||
}
|
||||
})
|
||||
|
||||
test("Commands should be registered", async () => {
|
||||
const expectedCommands = [
|
||||
"roo-cline.plusButtonClicked",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import * as vscode from "vscode"
|
|||
|
||||
import type { RooCodeAPI } from "../../../src/exports/roo-code"
|
||||
|
||||
import { waitUntilReady } from "./utils"
|
||||
import { waitFor } from "./utils"
|
||||
|
||||
declare global {
|
||||
var api: RooCodeAPI
|
||||
|
|
@ -18,18 +18,25 @@ export async function run() {
|
|||
throw new Error("Extension not found")
|
||||
}
|
||||
|
||||
// Activate the extension if it's not already active.
|
||||
const api = extension.isActive ? extension.exports : await extension.activate()
|
||||
|
||||
// TODO: We might want to support a "free" model out of the box so
|
||||
// contributors can run the tests locally without having to pay.
|
||||
await api.setConfiguration({
|
||||
apiProvider: "openrouter",
|
||||
apiProvider: "openrouter" as const,
|
||||
openRouterApiKey: process.env.OPENROUTER_API_KEY!,
|
||||
openRouterModelId: "anthropic/claude-3.5-sonnet",
|
||||
openRouterModelId: "google/gemini-2.0-flash-001",
|
||||
openRouterModelInfo: {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 1000000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.1,
|
||||
outputPrice: 0.4,
|
||||
thinking: false,
|
||||
},
|
||||
})
|
||||
|
||||
await waitUntilReady({ api })
|
||||
await vscode.commands.executeCommand("roo-cline.SidebarProvider.focus")
|
||||
await waitFor(() => api.isReady())
|
||||
|
||||
// Expose the API to the tests.
|
||||
globalThis.api = api
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ suite("Roo Code Modes", () => {
|
|||
|
||||
let messages: ClineMessage[] = []
|
||||
|
||||
api.on("message", ({ message }) => messages.push(message))
|
||||
api.on("message", ({ message }) => {
|
||||
if (message.type === "say" && message.partial === false) {
|
||||
messages.push(message)
|
||||
}
|
||||
})
|
||||
|
||||
const switchModesTaskId = await api.startNewTask({
|
||||
configuration: { mode: "Code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
|
||||
|
|
@ -31,22 +35,25 @@ suite("Roo Code Modes", () => {
|
|||
* Grade the response.
|
||||
*/
|
||||
|
||||
const gradePrompt =
|
||||
`Given this prompt: ${switchModesPrompt} grade the response from 1 to 10 in the format of "Grade: (1-10)": ` +
|
||||
messages
|
||||
.filter(({ type }) => type === "say")
|
||||
.map(({ text }) => text ?? "")
|
||||
.join("\n")
|
||||
const response = messages
|
||||
.filter(({ type, say, partial }) => say === "text")
|
||||
.map(({ text }) => text ?? "")
|
||||
.join("\n")
|
||||
|
||||
const gradePrompt = `Given this prompt: ${switchModesPrompt} grade the response from 1 to 10 in the format of "Grade: (1-10)". For example: Grade 7\n\nResponse: ${response}`
|
||||
|
||||
messages = []
|
||||
|
||||
const gradeTaskId = await api.startNewTask({ configuration: { mode: "Ask" }, text: gradePrompt })
|
||||
await waitUntilCompleted({ api, taskId: gradeTaskId, timeout: 60_000 })
|
||||
await waitUntilCompleted({ api, taskId: gradeTaskId })
|
||||
|
||||
const completion = messages.find(({ type, say, partial }) => say === "completion_result" && partial === false)
|
||||
const completion = messages.find(({ type, say, partial }) => say === "completion_result")
|
||||
const match = completion?.text?.match(/Grade: (\d+)/)
|
||||
const score = parseInt(match?.[1] ?? "0")
|
||||
assert.ok(score >= 7 && score <= 10, `Grade must be between 7 and 10 - ${completion?.text}`)
|
||||
assert.ok(
|
||||
score >= 7 && score <= 10,
|
||||
`Grade must be between 7 and 10. DEBUG: score = ${score}, completion = ${completion?.text}`,
|
||||
)
|
||||
|
||||
await api.cancelCurrentTask()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,11 +8,17 @@ suite("Roo Code Subtasks", () => {
|
|||
test("Should handle subtask cancellation and resumption correctly", async () => {
|
||||
const api = globalThis.api
|
||||
|
||||
const messages: ClineMessage[] = []
|
||||
api.on("message", ({ message }) => messages.push(message))
|
||||
const messages: Record<string, ClineMessage[]> = {}
|
||||
|
||||
api.on("message", ({ taskId, message }) => {
|
||||
if (message.type === "say" && message.partial === false) {
|
||||
messages[taskId] = messages[taskId] || []
|
||||
messages[taskId].push(message)
|
||||
}
|
||||
})
|
||||
|
||||
await api.setConfiguration({
|
||||
mode: "Code",
|
||||
mode: "ask",
|
||||
alwaysAllowModeSwitch: true,
|
||||
alwaysAllowSubtasks: true,
|
||||
autoApprovalEnabled: true,
|
||||
|
|
@ -34,7 +40,7 @@ suite("Roo Code Subtasks", () => {
|
|||
// Wait for the subtask to be spawned and then cancel it.
|
||||
api.on("taskSpawned", (_, childTaskId) => (spawnedTaskId = childTaskId))
|
||||
await waitFor(() => !!spawnedTaskId)
|
||||
await sleep(2_000) // Give the task a chance to start and populate the history.
|
||||
await sleep(1_000) // Give the task a chance to start and populate the history.
|
||||
await api.cancelCurrentTask()
|
||||
|
||||
// Wait a bit to ensure any task resumption would have happened.
|
||||
|
|
@ -42,15 +48,11 @@ suite("Roo Code Subtasks", () => {
|
|||
|
||||
// The parent task should not have resumed yet, so we shouldn't see
|
||||
// "Parent task resumed".
|
||||
// assert.ok(
|
||||
// getMessage({
|
||||
// api,
|
||||
// taskId: parentTaskId,
|
||||
// include: "Parent task resumed",
|
||||
// exclude: "You are the parent task",
|
||||
// }) === undefined,
|
||||
// "Parent task should not have resumed after subtask cancellation",
|
||||
// )
|
||||
assert.ok(
|
||||
messages[parentTaskId].find(({ type, text }) => type === "say" && text === "Parent task resumed") ===
|
||||
undefined,
|
||||
"Parent task should not have resumed after subtask cancellation",
|
||||
)
|
||||
|
||||
// Start a new task with the same message as the subtask.
|
||||
const anotherTaskId = await api.startNewTask({ text: childPrompt })
|
||||
|
|
@ -60,15 +62,11 @@ suite("Roo Code Subtasks", () => {
|
|||
await sleep(2_000)
|
||||
|
||||
// The parent task should still not have resumed.
|
||||
// assert.ok(
|
||||
// getMessage({
|
||||
// api,
|
||||
// taskId: parentTaskId,
|
||||
// include: "Parent task resumed",
|
||||
// exclude: "You are the parent task",
|
||||
// }) === undefined,
|
||||
// "Parent task should not have resumed after subtask cancellation",
|
||||
// )
|
||||
assert.ok(
|
||||
messages[parentTaskId].find(({ type, text }) => type === "say" && text === "Parent task resumed") ===
|
||||
undefined,
|
||||
"Parent task should not have resumed after subtask cancellation",
|
||||
)
|
||||
|
||||
// Clean up - cancel all tasks.
|
||||
await api.clearCurrentTask()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,12 @@ suite("Roo Code Task", () => {
|
|||
const api = globalThis.api
|
||||
|
||||
const messages: ClineMessage[] = []
|
||||
api.on("message", ({ message }) => messages.push(message))
|
||||
|
||||
api.on("message", ({ message }) => {
|
||||
if (message.type === "say" && message.partial === false) {
|
||||
messages.push(message)
|
||||
}
|
||||
})
|
||||
|
||||
const taskId = await api.startNewTask({
|
||||
configuration: { mode: "Ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
|
||||
|
|
@ -18,7 +23,7 @@ suite("Roo Code Task", () => {
|
|||
|
||||
await waitUntilCompleted({ api, taskId })
|
||||
|
||||
const completion = messages.find(({ type, say, partial }) => say === "completion_result" && partial === false)
|
||||
const completion = messages.find(({ say, partial }) => say === "completion_result")
|
||||
|
||||
assert.ok(
|
||||
completion?.text?.includes("My name is Roo"),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ type WaitForOptions = {
|
|||
|
||||
export const waitFor = (
|
||||
condition: (() => Promise<boolean>) | (() => boolean),
|
||||
{ timeout = 60_000, interval = 250 }: WaitForOptions = {},
|
||||
{ timeout = 30_000, interval = 250 }: WaitForOptions = {},
|
||||
) => {
|
||||
let timeoutId: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
|
|
@ -41,15 +41,6 @@ export const waitFor = (
|
|||
])
|
||||
}
|
||||
|
||||
type WaitUntilReadyOptions = WaitForOptions & {
|
||||
api: RooCodeAPI
|
||||
}
|
||||
|
||||
export const waitUntilReady = async ({ api, ...options }: WaitUntilReadyOptions) => {
|
||||
await vscode.commands.executeCommand("roo-cline.SidebarProvider.focus")
|
||||
await waitFor(() => api.isReady(), options)
|
||||
}
|
||||
|
||||
type WaitUntilAbortedOptions = WaitForOptions & {
|
||||
api: RooCodeAPI
|
||||
taskId: string
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
"benchmark/**",
|
||||
"src/activate/**",
|
||||
"src/exports/**",
|
||||
"src/schemas/**",
|
||||
"src/schemas/ipc.ts",
|
||||
"src/extension.ts",
|
||||
"scripts/**"
|
||||
],
|
||||
|
|
|
|||
162
package-lock.json
generated
162
package-lock.json
generated
|
|
@ -8302,44 +8302,75 @@
|
|||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/bare-events": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz",
|
||||
"integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==",
|
||||
"version": "2.5.4",
|
||||
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz",
|
||||
"integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/bare-fs": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz",
|
||||
"integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.2.tgz",
|
||||
"integrity": "sha512-S5mmkMesiduMqnz51Bfh0Et9EX0aTCJxhsI4bvzFFLs8Z1AV8RDHadfY5CyLwdoLHgXbNBEN1gQcbEtGwuvixw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bare-events": "^2.0.0",
|
||||
"bare-path": "^2.0.0",
|
||||
"bare-stream": "^2.0.0"
|
||||
"bare-events": "^2.5.4",
|
||||
"bare-path": "^3.0.0",
|
||||
"bare-stream": "^2.6.4"
|
||||
},
|
||||
"engines": {
|
||||
"bare": ">=1.16.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bare-buffer": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bare-buffer": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/bare-os": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz",
|
||||
"integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==",
|
||||
"optional": true
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz",
|
||||
"integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"bare": ">=1.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bare-path": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz",
|
||||
"integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
|
||||
"integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bare-os": "^2.1.0"
|
||||
"bare-os": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/bare-stream": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.4.2.tgz",
|
||||
"integrity": "sha512-XZ4ln/KV4KT+PXdIWTKjsLY+quqCaEtqqtgGJVPw9AoM73By03ij64YjepK0aQvHSWDb6AfAZwqKaFu68qkrdA==",
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz",
|
||||
"integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"streamx": "^2.20.0"
|
||||
"streamx": "^2.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bare-buffer": "*",
|
||||
"bare-events": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bare-buffer": {
|
||||
"optional": true
|
||||
},
|
||||
"bare-events": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
|
|
@ -16664,84 +16695,6 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/glob": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
|
||||
"integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^4.0.1",
|
||||
"minimatch": "^10.0.0",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/jackspeak": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz",
|
||||
"integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/lru-cache": {
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
|
||||
"integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/minimatch": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
||||
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/path-scurry": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
|
||||
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.37.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz",
|
||||
|
|
@ -17885,16 +17838,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tar-fs": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz",
|
||||
"integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz",
|
||||
"integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^3.1.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bare-fs": "^2.1.1",
|
||||
"bare-path": "^2.1.0"
|
||||
"bare-fs": "^4.0.1",
|
||||
"bare-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tar-stream": {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,20 @@ import { ApiHandlerOptions } from "../../../shared/api"
|
|||
|
||||
// Mock the AWS SDK
|
||||
jest.mock("@aws-sdk/client-bedrock-runtime", () => {
|
||||
const mockResponse = {
|
||||
output: {
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
text: "Test response",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
output: new TextEncoder().encode(JSON.stringify({ content: "Test response" })),
|
||||
})
|
||||
return Promise.resolve(mockResponse)
|
||||
})
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -399,14 +399,20 @@ describe("AwsBedrockHandler", () => {
|
|||
})
|
||||
})
|
||||
|
||||
//response.output.message.content[0].text
|
||||
|
||||
describe("completePrompt", () => {
|
||||
it("should complete prompt successfully", async () => {
|
||||
const mockResponse = {
|
||||
output: new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
content: "Test response",
|
||||
}),
|
||||
),
|
||||
output: {
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
text: "Test response",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockResolvedValue(mockResponse)
|
||||
|
|
@ -450,7 +456,9 @@ describe("AwsBedrockHandler", () => {
|
|||
|
||||
it("should handle invalid response format", async () => {
|
||||
const mockResponse = {
|
||||
output: new TextEncoder().encode("invalid json"),
|
||||
output: {
|
||||
message: {},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockResolvedValue(mockResponse)
|
||||
|
|
@ -464,9 +472,16 @@ describe("AwsBedrockHandler", () => {
|
|||
|
||||
it("should handle empty response", async () => {
|
||||
const mockResponse = {
|
||||
output: new TextEncoder().encode(JSON.stringify({})),
|
||||
output: {
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
text: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockResolvedValue(mockResponse)
|
||||
handler["client"] = {
|
||||
send: mockSend,
|
||||
|
|
@ -486,11 +501,15 @@ describe("AwsBedrockHandler", () => {
|
|||
})
|
||||
|
||||
const mockResponse = {
|
||||
output: new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
content: "Test response",
|
||||
}),
|
||||
),
|
||||
output: {
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
text: "Test response",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockResolvedValue(mockResponse)
|
||||
|
|
@ -519,11 +538,15 @@ describe("AwsBedrockHandler", () => {
|
|||
})
|
||||
|
||||
const mockResponse = {
|
||||
output: new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
content: "Test response",
|
||||
}),
|
||||
),
|
||||
output: {
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
text: "Test response",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockResolvedValue(mockResponse)
|
||||
|
|
@ -552,13 +575,16 @@ describe("AwsBedrockHandler", () => {
|
|||
})
|
||||
|
||||
const mockResponse = {
|
||||
output: new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
content: "Test response",
|
||||
}),
|
||||
),
|
||||
output: {
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
text: "Test response",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockResolvedValue(mockResponse)
|
||||
handler["client"] = {
|
||||
send: mockSend,
|
||||
|
|
@ -585,11 +611,15 @@ describe("AwsBedrockHandler", () => {
|
|||
})
|
||||
|
||||
const mockResponse = {
|
||||
output: new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
content: "Test response",
|
||||
}),
|
||||
),
|
||||
output: {
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
text: "Test response",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const mockSend = jest.fn().mockResolvedValue(mockResponse)
|
||||
|
|
|
|||
|
|
@ -665,13 +665,14 @@ Please check:
|
|||
const command = new ConverseCommand(payload)
|
||||
const response = await this.client.send(command)
|
||||
|
||||
if (response.output && response.output instanceof Uint8Array) {
|
||||
if (
|
||||
response?.output?.message?.content &&
|
||||
response.output.message.content.length > 0 &&
|
||||
response.output.message.content[0].text &&
|
||||
response.output.message.content[0].text.trim().length > 0
|
||||
) {
|
||||
try {
|
||||
const outputStr = new TextDecoder().decode(response.output)
|
||||
const output = JSON.parse(outputStr)
|
||||
if (output.content) {
|
||||
return output.content
|
||||
}
|
||||
return response.output.message.content[0].text
|
||||
} catch (parseError) {
|
||||
logger.error("Failed to parse Bedrock response", {
|
||||
ctx: "bedrock",
|
||||
|
|
|
|||
|
|
@ -17,20 +17,31 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
private readonly context: vscode.ExtensionContext
|
||||
private readonly ipc?: IpcServer
|
||||
private readonly taskMap = new Map<string, ClineProvider>()
|
||||
private readonly log: (...args: unknown[]) => void
|
||||
|
||||
constructor(outputChannel: vscode.OutputChannel, provider: ClineProvider, socketPath?: string) {
|
||||
constructor(
|
||||
outputChannel: vscode.OutputChannel,
|
||||
provider: ClineProvider,
|
||||
socketPath?: string,
|
||||
enableLogging = false,
|
||||
) {
|
||||
super()
|
||||
|
||||
this.outputChannel = outputChannel
|
||||
this.sidebarProvider = provider
|
||||
this.context = provider.context
|
||||
|
||||
this.log = enableLogging
|
||||
? (...args: unknown[]) => {
|
||||
outputChannelLog(this.outputChannel, ...args)
|
||||
console.log(args)
|
||||
}
|
||||
: () => {}
|
||||
|
||||
this.registerListeners(this.sidebarProvider)
|
||||
|
||||
if (socketPath) {
|
||||
const ipc = (this.ipc = new IpcServer(socketPath, (...args: unknown[]) =>
|
||||
outputChannelLog(this.outputChannel, ...args),
|
||||
))
|
||||
const ipc = (this.ipc = new IpcServer(socketPath, this.log))
|
||||
|
||||
ipc.listen()
|
||||
this.log(`[API] ipc server started: socketPath=${socketPath}, pid=${process.pid}, ppid=${process.ppid}`)
|
||||
|
|
@ -156,11 +167,6 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
return this.sidebarProvider.viewLaunched
|
||||
}
|
||||
|
||||
public log(message: string) {
|
||||
this.outputChannel.appendLine(message)
|
||||
console.log(`${message}\n`)
|
||||
}
|
||||
|
||||
private registerListeners(provider: ClineProvider) {
|
||||
provider.on("clineCreated", (cline) => {
|
||||
cline.on("taskStarted", () => {
|
||||
|
|
|
|||
|
|
@ -76,10 +76,4 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
|
|||
* Returns true if the API is ready to use.
|
||||
*/
|
||||
isReady(): boolean
|
||||
|
||||
/**
|
||||
* Logs a message to the output channel.
|
||||
* @param message The message to log.
|
||||
*/
|
||||
log(message: string): void
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export class IpcServer extends EventEmitter<IpcServerEvents> {
|
|||
this.emit(IpcMessageType.TaskCommand, payload.clientId, payload.data)
|
||||
break
|
||||
default:
|
||||
throw new Error(`[server#onMessage] unhandled payload: ${JSON.stringify(payload)}`)
|
||||
this.log(`[server#onMessage] unhandled payload: ${JSON.stringify(payload)}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
src/exports/roo-code.d.ts
vendored
5
src/exports/roo-code.d.ts
vendored
|
|
@ -575,11 +575,6 @@ interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
|
|||
* Returns true if the API is ready to use.
|
||||
*/
|
||||
isReady(): boolean
|
||||
/**
|
||||
* Logs a message to the output channel.
|
||||
* @param message The message to log.
|
||||
*/
|
||||
log(message: string): void
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -119,7 +119,9 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
vscode.commands.executeCommand("roo-cline.activationCompleted")
|
||||
|
||||
// Implements the `RooCodeAPI` interface.
|
||||
return new API(outputChannel, provider, process.env.ROO_CODE_IPC_SOCKET_PATH)
|
||||
const socketPath = process.env.ROO_CODE_IPC_SOCKET_PATH
|
||||
const enableLogging = typeof socketPath === "string"
|
||||
return new API(outputChannel, provider, socketPath, enableLogging)
|
||||
}
|
||||
|
||||
// This method is called when your extension is deactivated
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ interface ChatTextAreaProps {
|
|||
inputValue: string
|
||||
setInputValue: (value: string) => void
|
||||
textAreaDisabled: boolean
|
||||
selectApiConfigDisabled: boolean
|
||||
placeholderText: string
|
||||
selectedImages: string[]
|
||||
setSelectedImages: React.Dispatch<React.SetStateAction<string[]>>
|
||||
|
|
@ -50,6 +51,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
inputValue,
|
||||
setInputValue,
|
||||
textAreaDisabled,
|
||||
selectApiConfigDisabled,
|
||||
placeholderText,
|
||||
selectedImages,
|
||||
setSelectedImages,
|
||||
|
|
@ -975,7 +977,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
<div className={cn("flex-1", "min-w-0", "overflow-hidden")}>
|
||||
<SelectDropdown
|
||||
value={currentConfigId}
|
||||
disabled={textAreaDisabled}
|
||||
disabled={selectApiConfigDisabled}
|
||||
title={t("chat:selectApiConfig")}
|
||||
placeholder={displayName} // Always show the current name
|
||||
options={[
|
||||
|
|
|
|||
|
|
@ -1346,6 +1346,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
inputValue={inputValue}
|
||||
setInputValue={setInputValue}
|
||||
textAreaDisabled={textAreaDisabled}
|
||||
selectApiConfigDisabled={textAreaDisabled && clineAsk !== "api_req_failed"}
|
||||
placeholderText={placeholderText}
|
||||
selectedImages={selectedImages}
|
||||
setSelectedImages={setSelectedImages}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ describe("ChatTextArea", () => {
|
|||
setInputValue: jest.fn(),
|
||||
onSend: jest.fn(),
|
||||
textAreaDisabled: false,
|
||||
selectApiConfigDisabled: false,
|
||||
onSelectImages: jest.fn(),
|
||||
shouldDisableImages: false,
|
||||
placeholderText: "Type a message...",
|
||||
|
|
@ -408,4 +409,21 @@ describe("ChatTextArea", () => {
|
|||
expect(setInputValue).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("selectApiConfig", () => {
|
||||
// Helper function to get the API config dropdown
|
||||
const getApiConfigDropdown = () => {
|
||||
return screen.getByTitle("chat:selectApiConfig")
|
||||
}
|
||||
it("should be enabled independently of textAreaDisabled", () => {
|
||||
render(<ChatTextArea {...defaultProps} textAreaDisabled={true} selectApiConfigDisabled={false} />)
|
||||
const apiConfigDropdown = getApiConfigDropdown()
|
||||
expect(apiConfigDropdown).not.toHaveAttribute("disabled")
|
||||
})
|
||||
it("should be disabled when selectApiConfigDisabled is true", () => {
|
||||
render(<ChatTextArea {...defaultProps} textAreaDisabled={true} selectApiConfigDisabled={true} />)
|
||||
const apiConfigDropdown = getApiConfigDropdown()
|
||||
expect(apiConfigDropdown).toHaveAttribute("disabled")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
|||
: `${t("history:enterSelectionMode")}`
|
||||
}>
|
||||
<span
|
||||
className={`codicon ${isSelectionMode ? "codicon-check-all" : "codicon-checklist"}`}
|
||||
className={`codicon ${isSelectionMode ? "codicon-check-all" : "codicon-checklist"} mr-1`}
|
||||
/>
|
||||
{isSelectionMode ? t("history:exitSelection") : t("history:selectionMode")}
|
||||
</VSCodeButton>
|
||||
|
|
|
|||
|
|
@ -39,22 +39,30 @@ export const PROVIDERS = [
|
|||
{ value: "human-relay", label: "Human Relay" },
|
||||
].sort((a, b) => a.label.localeCompare(b.label))
|
||||
|
||||
//This list alpha sorted and updated April 2, 2025 to include any region that supported 1 or
|
||||
//more models shown at https://docs.aws.amazon.com/bedrock/latest/userguide/models-regions.html
|
||||
|
||||
export const AWS_REGIONS = [
|
||||
{ value: "us-east-1", label: "us-east-1" },
|
||||
{ value: "us-east-2", label: "us-east-2" },
|
||||
{ value: "us-west-2", label: "us-west-2" },
|
||||
{ value: "ap-south-1", label: "ap-south-1" },
|
||||
{ value: "ap-northeast-1", label: "ap-northeast-1" },
|
||||
{ value: "ap-northeast-2", label: "ap-northeast-2" },
|
||||
{ value: "ap-south-1", label: "ap-south-1" },
|
||||
{ value: "ap-southeast-1", label: "ap-southeast-1" },
|
||||
{ value: "ap-southeast-2", label: "ap-southeast-2" },
|
||||
{ value: "ca-central-1", label: "ca-central-1" },
|
||||
{ value: "eu-central-1", label: "eu-central-1" },
|
||||
{ value: "eu-central-2", label: "eu-central-2" },
|
||||
{ value: "eu-north-1", label: "eu-north-1" },
|
||||
{ value: "eu-south-1", label: "eu-south-1" },
|
||||
{ value: "eu-south-2", label: "eu-south-2" },
|
||||
{ value: "eu-west-1", label: "eu-west-1" },
|
||||
{ value: "eu-west-2", label: "eu-west-2" },
|
||||
{ value: "eu-west-3", label: "eu-west-3" },
|
||||
{ value: "sa-east-1", label: "sa-east-1" },
|
||||
{ value: "us-east-1", label: "us-east-1" },
|
||||
{ value: "us-east-2", label: "us-east-2" },
|
||||
{ value: "us-gov-east-1", label: "us-gov-east-1" },
|
||||
{ value: "us-gov-west-1", label: "us-gov-west-1" },
|
||||
{ value: "us-west-2", label: "us-west-2" },
|
||||
]
|
||||
|
||||
export const VERTEX_REGIONS = [
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
|
|||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenu open={open} onOpenChange={setOpen} modal={false}>
|
||||
<DropdownMenuTrigger
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
|
|
@ -106,7 +106,7 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
|
|||
onEscapeKeyDown={() => setOpen(false)}
|
||||
onInteractOutside={() => setOpen(false)}
|
||||
container={portalContainer}
|
||||
className={contentClassName}>
|
||||
className={cn("overflow-y-auto max-h-[80vh]", contentClassName)}>
|
||||
{options.map((option, index) => {
|
||||
if (option.type === DropdownOptionType.SEPARATOR) {
|
||||
return <DropdownMenuSeparator key={`sep-${index}`} />
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue