Przeglądaj źródła

Bare metal evals fixes (#8224)

Co-authored-by: Roo Code <[email protected]>
Chris Estreich 3 miesięcy temu
rodzic
commit
0e1b23d09c

+ 1 - 1
apps/web-evals/package.json

@@ -5,7 +5,7 @@
 	"scripts": {
 	"scripts": {
 		"lint": "next lint --max-warnings 0",
 		"lint": "next lint --max-warnings 0",
 		"check-types": "tsc -b",
 		"check-types": "tsc -b",
-		"dev": "scripts/check-services.sh && next dev",
+		"dev": "scripts/check-services.sh && next dev -p 3446",
 		"format": "prettier --write src",
 		"format": "prettier --write src",
 		"build": "next build",
 		"build": "next build",
 		"start": "next start",
 		"start": "next start",

+ 1 - 1
packages/evals/README.md

@@ -95,7 +95,7 @@ By default, the evals system uses the following ports:
 
 
 - **PostgreSQL**: 5433 (external) → 5432 (internal)
 - **PostgreSQL**: 5433 (external) → 5432 (internal)
 - **Redis**: 6380 (external) → 6379 (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.
 These ports are configured to avoid conflicts with other services that might be running on the standard PostgreSQL (5432) and Redis (6379) ports.
 
 

+ 1 - 1
packages/evals/docker-compose.yml

@@ -52,7 +52,7 @@ services:
             context: ../../
             context: ../../
             dockerfile: packages/evals/Dockerfile.web
             dockerfile: packages/evals/Dockerfile.web
         ports:
         ports:
-            - "${EVALS_WEB_PORT:-3446}:3000"
+            - "${EVALS_WEB_PORT:-3446}:3446"
         environment:
         environment:
             - HOST_EXECUTION_METHOD=docker
             - HOST_EXECUTION_METHOD=docker
         volumes:
         volumes:

+ 2 - 3
packages/evals/scripts/setup.sh

@@ -12,7 +12,6 @@ build_extension() {
   echo "🔨 Building the Roo Code extension..."
   echo "🔨 Building the Roo Code extension..."
   pnpm -w vsix -- --out ../bin/roo-code-$(git rev-parse --short HEAD).vsix || exit 1
   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
   code --install-extension ../../bin/roo-code-$(git rev-parse --short HEAD).vsix || exit 1
-  cd evals
 }
 }
 
 
 check_docker_services() {
 check_docker_services() {
@@ -377,7 +376,7 @@ fi
 
 
 echo -e "\n🚀 You're ready to rock and roll! \n"
 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
   read -p "🌐 Would you like to start the evals web app? (Y/n): " start_evals
 
 
   if [[ "$start_evals" =~ ^[Yy]|^$ ]]; then
   if [[ "$start_evals" =~ ^[Yy]|^$ ]]; then
@@ -386,5 +385,5 @@ if ! nc -z localhost 3000; then
     echo "💡 You can start it anytime with 'pnpm --filter @roo-code/web-evals dev'."
     echo "💡 You can start it anytime with 'pnpm --filter @roo-code/web-evals dev'."
   fi
   fi
 else
 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
 fi

+ 20 - 25
src/api/providers/__tests__/roo.spec.ts

@@ -36,26 +36,12 @@ vitest.mock("openai", () => {
 						return {
 						return {
 							[Symbol.asyncIterator]: async function* () {
 							[Symbol.asyncIterator]: async function* () {
 								yield {
 								yield {
-									choices: [
-										{
-											delta: { content: "Test response" },
-											index: 0,
-										},
-									],
+									choices: [{ delta: { content: "Test response" }, index: 0 }],
 									usage: null,
 									usage: null,
 								}
 								}
 								yield {
 								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 },
 								}
 								}
 							},
 							},
 						}
 						}
@@ -73,6 +59,7 @@ const mockHasInstance = vitest.fn()
 // Create mock functions that we can control
 // Create mock functions that we can control
 const mockGetSessionTokenFn = vitest.fn()
 const mockGetSessionTokenFn = vitest.fn()
 const mockHasInstanceFn = vitest.fn()
 const mockHasInstanceFn = vitest.fn()
+const mockOnFn = vitest.fn()
 
 
 vitest.mock("@roo-code/cloud", () => ({
 vitest.mock("@roo-code/cloud", () => ({
 	CloudService: {
 	CloudService: {
@@ -82,6 +69,8 @@ vitest.mock("@roo-code/cloud", () => ({
 				authService: {
 				authService: {
 					getSessionToken: () => mockGetSessionTokenFn(),
 					getSessionToken: () => mockGetSessionTokenFn(),
 				},
 				},
+				on: vitest.fn(),
+				off: vitest.fn(),
 			}
 			}
 		},
 		},
 	},
 	},
@@ -409,11 +398,18 @@ describe("RooHandler", () => {
 		it("should handle undefined auth service gracefully", () => {
 		it("should handle undefined auth service gracefully", () => {
 			mockHasInstanceFn.mockReturnValue(true)
 			mockHasInstanceFn.mockReturnValue(true)
 			// Mock CloudService with undefined authService
 			// 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 {
 			try {
 				Object.defineProperty(CloudService, "instance", {
 				Object.defineProperty(CloudService, "instance", {
-					get: () => ({ authService: undefined }),
+					get: () => ({
+						authService: undefined,
+						on: vitest.fn(),
+						off: vitest.fn(),
+					}),
 					configurable: true,
 					configurable: true,
 				})
 				})
 
 
@@ -424,12 +420,11 @@ describe("RooHandler", () => {
 				const handler = new RooHandler(mockOptions)
 				const handler = new RooHandler(mockOptions)
 				expect(handler).toBeInstanceOf(RooHandler)
 				expect(handler).toBeInstanceOf(RooHandler)
 			} finally {
 			} 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")
 				}
 				}
 			}
 			}
 		})
 		})

+ 36 - 6
src/api/providers/roo.ts

@@ -1,22 +1,24 @@
 import { Anthropic } from "@anthropic-ai/sdk"
 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 { CloudService } from "@roo-code/cloud"
 
 
 import type { ApiHandlerOptions } from "../../shared/api"
 import type { ApiHandlerOptions } from "../../shared/api"
 import { ApiStream } from "../transform/stream"
 import { ApiStream } from "../transform/stream"
 
 
 import type { ApiHandlerCreateMessageMetadata } from "../index"
 import type { ApiHandlerCreateMessageMetadata } from "../index"
+import { DEFAULT_HEADERS } from "./constants"
 import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
 import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
 
 
 export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
 export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
+	private authStateListener?: (state: { state: AuthState }) => void
+
 	constructor(options: ApiHandlerOptions) {
 	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()) {
 		if (CloudService.hasInstance()) {
-			sessionToken = CloudService.instance.authService?.getSessionToken() || ""
+			sessionToken = CloudService.instance.authService?.getSessionToken()
 		}
 		}
 
 
 		// Always construct the handler, even without a valid token.
 		// Always construct the handler, even without a valid token.
@@ -25,11 +27,39 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
 			...options,
 			...options,
 			providerName: "Roo Code Cloud",
 			providerName: "Roo Code Cloud",
 			baseURL: process.env.ROO_CODE_PROVIDER_URL ?? "https://api.roocode.com/proxy/v1",
 			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,
 			defaultProviderModelId: rooDefaultModelId,
 			providerModels: rooModels,
 			providerModels: rooModels,
 			defaultTemperature: 0.7,
 			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(
 	override async *createMessage(

+ 1 - 1
src/extension.ts

@@ -194,7 +194,7 @@ export async function activate(context: vscode.ExtensionContext) {
 	// Add to subscriptions for proper cleanup on deactivate.
 	// Add to subscriptions for proper cleanup on deactivate.
 	context.subscriptions.push(cloudService)
 	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 {
 	try {
 		await provider.initializeCloudProfileSyncWhenReady()
 		await provider.initializeCloudProfileSyncWhenReady()
 	} catch (error) {
 	} catch (error) {