Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web-evals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"scripts": {
"lint": "next lint --max-warnings 0",
"check-types": "tsc -b",
"dev": "scripts/check-services.sh && next dev",
"dev": "scripts/check-services.sh && next dev -p 3446",
"format": "prettier --write src",
"build": "next build",
"start": "next start",
Expand Down
2 changes: 1 addition & 1 deletion packages/evals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ By default, the evals system uses the following ports:

- **PostgreSQL**: 5433 (external) → 5432 (internal)
- **Redis**: 6380 (external) → 6379 (internal)
- **Web Service**: 3446 (external) → 3000 (internal)
- **Web Service**: 3446 (external) → 3446 (internal)

These ports are configured to avoid conflicts with other services that might be running on the standard PostgreSQL (5432) and Redis (6379) ports.

Expand Down
2 changes: 1 addition & 1 deletion packages/evals/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ services:
context: ../../
dockerfile: packages/evals/Dockerfile.web
ports:
- "${EVALS_WEB_PORT:-3446}:3000"
- "${EVALS_WEB_PORT:-3446}:3446"
environment:
- HOST_EXECUTION_METHOD=docker
volumes:
Expand Down
5 changes: 2 additions & 3 deletions packages/evals/scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ build_extension() {
echo "🔨 Building the Roo Code extension..."
pnpm -w vsix -- --out ../bin/roo-code-$(git rev-parse --short HEAD).vsix || exit 1
code --install-extension ../../bin/roo-code-$(git rev-parse --short HEAD).vsix || exit 1
cd evals
}

check_docker_services() {
Expand Down Expand Up @@ -377,7 +376,7 @@ fi

echo -e "\n🚀 You're ready to rock and roll! \n"

if ! nc -z localhost 3000; then
if ! nc -z localhost 3446; then
read -p "🌐 Would you like to start the evals web app? (Y/n): " start_evals

if [[ "$start_evals" =~ ^[Yy]|^$ ]]; then
Expand All @@ -386,5 +385,5 @@ if ! nc -z localhost 3000; then
echo "💡 You can start it anytime with 'pnpm --filter @roo-code/web-evals dev'."
fi
else
echo "👟 The evals web app is running at http://localhost:3000 (or http://localhost:3446 if using Docker)"
echo "👟 The evals web app is running at http://localhost:3446"
fi
45 changes: 20 additions & 25 deletions src/api/providers/__tests__/roo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,12 @@ vitest.mock("openai", () => {
return {
[Symbol.asyncIterator]: async function* () {
yield {
choices: [
{
delta: { content: "Test response" },
index: 0,
},
],
choices: [{ delta: { content: "Test response" }, index: 0 }],
usage: null,
}
yield {
choices: [
{
delta: {},
index: 0,
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 5,
total_tokens: 15,
},
choices: [{ delta: {}, index: 0 }],
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
}
},
}
Expand All @@ -73,6 +59,7 @@ const mockHasInstance = vitest.fn()
// Create mock functions that we can control
const mockGetSessionTokenFn = vitest.fn()
const mockHasInstanceFn = vitest.fn()
const mockOnFn = vitest.fn()

vitest.mock("@roo-code/cloud", () => ({
CloudService: {
Expand All @@ -82,6 +69,8 @@ vitest.mock("@roo-code/cloud", () => ({
authService: {
getSessionToken: () => mockGetSessionTokenFn(),
},
on: vitest.fn(),
off: vitest.fn(),
}
},
},
Expand Down Expand Up @@ -409,11 +398,18 @@ describe("RooHandler", () => {
it("should handle undefined auth service gracefully", () => {
mockHasInstanceFn.mockReturnValue(true)
// Mock CloudService with undefined authService
const originalGetter = Object.getOwnPropertyDescriptor(CloudService, "instance")?.get
const originalGetSessionToken = mockGetSessionTokenFn.getMockImplementation()

// Temporarily make authService return undefined
mockGetSessionTokenFn.mockImplementation(() => undefined)

try {
Object.defineProperty(CloudService, "instance", {
get: () => ({ authService: undefined }),
get: () => ({
authService: undefined,
on: vitest.fn(),
off: vitest.fn(),
}),
configurable: true,
})

Expand All @@ -424,12 +420,11 @@ describe("RooHandler", () => {
const handler = new RooHandler(mockOptions)
expect(handler).toBeInstanceOf(RooHandler)
} finally {
// Always restore original getter, even if test fails
if (originalGetter) {
Object.defineProperty(CloudService, "instance", {
get: originalGetter,
configurable: true,
})
// Restore original mock implementation
if (originalGetSessionToken) {
mockGetSessionTokenFn.mockImplementation(originalGetSessionToken)
} else {
mockGetSessionTokenFn.mockReturnValue("test-session-token")
}
}
})
Expand Down
42 changes: 36 additions & 6 deletions src/api/providers/roo.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"

import { rooDefaultModelId, rooModels, type RooModelId } from "@roo-code/types"
import { AuthState, rooDefaultModelId, rooModels, type RooModelId } from "@roo-code/types"
import { CloudService } from "@roo-code/cloud"

import type { ApiHandlerOptions } from "../../shared/api"
import { ApiStream } from "../transform/stream"

import type { ApiHandlerCreateMessageMetadata } from "../index"
import { DEFAULT_HEADERS } from "./constants"
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"

export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
private authStateListener?: (state: { state: AuthState }) => void

constructor(options: ApiHandlerOptions) {
// Get the session token if available, but don't throw if not.
// The server will handle authentication errors and return appropriate status codes.
let sessionToken = ""
let sessionToken: string | undefined = undefined

if (CloudService.hasInstance()) {
sessionToken = CloudService.instance.authService?.getSessionToken() || ""
sessionToken = CloudService.instance.authService?.getSessionToken()
}

// Always construct the handler, even without a valid token.
Expand All @@ -25,11 +27,39 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
...options,
providerName: "Roo Code Cloud",
baseURL: process.env.ROO_CODE_PROVIDER_URL ?? "https://api.roocode.com/proxy/v1",
apiKey: sessionToken || "unauthenticated", // Use a placeholder if no token
apiKey: sessionToken || "unauthenticated", // Use a placeholder if no token.
defaultProviderModelId: rooDefaultModelId,
providerModels: rooModels,
defaultTemperature: 0.7,
})

if (CloudService.hasInstance()) {
const cloudService = CloudService.instance

this.authStateListener = (state: { state: AuthState }) => {
if (state.state === "active-session") {
this.client = new OpenAI({
baseURL: this.baseURL,
apiKey: cloudService.authService?.getSessionToken() ?? "unauthenticated",
defaultHeaders: DEFAULT_HEADERS,
})
} else if (state.state === "logged-out") {
this.client = new OpenAI({
baseURL: this.baseURL,
apiKey: "unauthenticated",
defaultHeaders: DEFAULT_HEADERS,
})
}
}

cloudService.on("auth-state-changed", this.authStateListener)
}
}

dispose() {
if (this.authStateListener && CloudService.hasInstance()) {
CloudService.instance.off("auth-state-changed", this.authStateListener)
}
}

override async *createMessage(
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export async function activate(context: vscode.ExtensionContext) {
// Add to subscriptions for proper cleanup on deactivate.
context.subscriptions.push(cloudService)

// Trigger initial cloud profile sync now that CloudService is ready
// Trigger initial cloud profile sync now that CloudService is ready.
try {
await provider.initializeCloudProfileSyncWhenReady()
} catch (error) {
Expand Down