Skip to content
Closed
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
3 changes: 3 additions & 0 deletions packages/ghost-benchmarks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
results/**
dist/
dist/**
5 changes: 5 additions & 0 deletions packages/ghost-benchmarks/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Test case files with intentional syntax errors and formatting
test-cases/**
**/__test_cases__/**
dist/**
node_modules/**
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const arr = [1, 2, 3, 4<<<AUTOCOMPLETE_HERE>>>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const arr = [1, 2, 3,
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('test')
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class Animal {
constructor(name) {
this.name = name;
}
}

class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class Animal
def initialize(name)
@name = name
end
end

class Dog < Animal
def initialize(name, breed)
super(name)
@breed = breed
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from "vitest"
import { AutoTriggerStrategyTester } from "./auto-trigger-strategy.js"
import { CURSOR_MARKER } from "../services/ghost/ghostConstants.js"
import { AutoTriggerStrategyTester } from "../../src/test-llm-autocompletion/auto-trigger-strategy.js"
import { CURSOR_MARKER } from "../../src/services/ghost/ghostConstants.js"

// Mock LLMClient to avoid needing API keys in tests
vi.mock("./llm-client.js", () => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { LLMClient } from "./llm-client.js"
import { AutoTriggerStrategy } from "../services/ghost/strategies/AutoTriggerStrategy.js"
import { GhostSuggestionContext } from "../services/ghost/types.js"
import { MockTextDocument } from "../services/mocking/MockTextDocument.js"
import { CURSOR_MARKER } from "../services/ghost/ghostConstants.js"
import { GhostStreamingParser } from "../services/ghost/GhostStreamingParser.js"
import * as vscode from "vscode"
import { AutoTriggerStrategy } from "../../src/services/ghost/strategies/AutoTriggerStrategy.js"
import { GhostSuggestionContext } from "../../src/services/ghost/types.js"
import { MockTextDocument } from "../../src/services/mocking/MockTextDocument.js"
import { CURSOR_MARKER } from "../../src/services/ghost/ghostConstants.js"
import { GhostStreamingParser } from "../../src/services/ghost/GhostStreamingParser.js"
import * as vscode from "./mock-vscode.js"

export class AutoTriggerStrategyTester {
private llmClient: LLMClient
Expand Down
61 changes: 61 additions & 0 deletions packages/ghost-benchmarks/esbuild.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import esbuild from "esbuild"
import path from "path"
import { fileURLToPath } from "url"

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const config = {
entryPoints: ["runner.ts"],
bundle: true,
platform: "node",
target: "node20",
format: "esm",
outfile: "dist/runner.js",
external: [
// Keep these as external dependencies
"electron",
"tiktoken", // Has WASM dependencies that can't be bundled
"tree-sitter-wasms", // WASM files
"web-tree-sitter", // WASM files
"openai",
"dotenv",
"@anthropic-ai/sdk",
],
alias: {
// Map workspace dependencies to actual packages
"@roo-code/telemetry": path.resolve(__dirname, "../telemetry/src"),
"@roo-code/types": path.resolve(__dirname, "../types/src"),
"@roo-code/ipc": path.resolve(__dirname, "../ipc/src"),
"@roo-code/cloud": path.resolve(__dirname, "../cloud/src"),
// Mock vscode module
vscode: path.resolve(__dirname, "mock-vscode.ts"),
},
plugins: [],
define: {},
resolveExtensions: [".ts", ".js", ".json"],
sourcemap: true,
minify: false, // Keep readable for debugging
logLevel: "info",
mainFields: ["module", "main"],
conditions: ["import", "require", "node"],
}

// Build function
async function build() {
try {
console.log("🔨 Building Ghost Benchmarks with esbuild...")
await esbuild.build(config)
console.log("✅ Build completed successfully!")
} catch (error) {
console.error("❌ Build failed:", error)
process.exit(1)
}
}

// Run build if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
build()
}

export { config, build }
8 changes: 8 additions & 0 deletions packages/ghost-benchmarks/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { config } from "@roo-code/config-eslint/base"

export default [
...config,
{
ignores: ["dist/**", "**/test-cases/**"],
},
]
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import OpenAI from "openai"
import { config } from "dotenv"
import { DEFAULT_HEADERS } from "../api/providers/constants.js"
import { DEFAULT_HEADERS } from "../../src/api/providers/constants.js"

config()

Expand Down Expand Up @@ -35,24 +35,40 @@ export class LLMClient {
this.provider = process.env.LLM_PROVIDER || "kilocode"
this.model = process.env.LLM_MODEL || "mistralai/codestral-2508"

if (this.provider !== "kilocode") {
throw new Error(`Only kilocode provider is supported. Got: ${this.provider}`)
if (this.provider !== "kilocode" && this.provider !== "openrouter") {
throw new Error(`Only kilocode and openrouter providers are supported. Got: ${this.provider}`)
}

if (!process.env.KILOCODE_API_KEY) {
// Validate API keys based on provider
if (this.provider === "kilocode" && !process.env.KILOCODE_API_KEY) {
throw new Error("KILOCODE_API_KEY is required for Kilocode provider")
}

const baseUrl = getKiloBaseUriFromToken(process.env.KILOCODE_API_KEY)
if (this.provider === "openrouter" && !process.env.OPENROUTER_API_KEY) {
throw new Error("OPENROUTER_API_KEY is required for OpenRouter provider")
}

this.openai = new OpenAI({
baseURL: `${baseUrl}/api/openrouter/`,
apiKey: process.env.KILOCODE_API_KEY,
defaultHeaders: {
...DEFAULT_HEADERS,
"X-KILOCODE-TESTER": "SUPPRESS",
},
})
// Configure OpenAI client based on provider
if (this.provider === "kilocode") {
const baseUrl = getKiloBaseUriFromToken(process.env.KILOCODE_API_KEY)
this.openai = new OpenAI({
baseURL: `${baseUrl}/api/openrouter/`,
apiKey: process.env.KILOCODE_API_KEY,
defaultHeaders: {
...DEFAULT_HEADERS,
"X-KILOCODE-TESTER": "SUPPRESS",
},
})
} else if (this.provider === "openrouter") {
this.openai = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY,
defaultHeaders: {
...DEFAULT_HEADERS,
"HTTP-Referer": "https://kilocode.ai",
},
})
}
}

async sendPrompt(systemPrompt: string, userPrompt: string): Promise<LLMResponse> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,26 @@ export class Uri {
get fsPath(): string {
return this.path
}

with(change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri {
return new Uri(
change.scheme !== undefined ? change.scheme : this.scheme,
change.authority !== undefined ? change.authority : this.authority,
change.path !== undefined ? change.path : this.path,
change.query !== undefined ? change.query : this.query,
change.fragment !== undefined ? change.fragment : this.fragment,
)
}

toJSON(): any {
return {
scheme: this.scheme,
authority: this.authority,
path: this.path,
query: this.query,
fragment: this.fragment,
}
}
}

export class Selection extends Range {
Expand Down
41 changes: 41 additions & 0 deletions packages/ghost-benchmarks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@kilocode/ghost-benchmarks",
"version": "1.0.0",
"description": "Benchmarking system for Ghost autocomplete functionality",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
"ghost-benchmark": "./dist/cli/benchmark-cli.cjs"
},
"scripts": {
"build": "node esbuild.config.js",
"dev": "node esbuild.config.js --watch",
"test": "npm run build && node dist/runner.js",
"test:verbose": "npm run build && node dist/runner.js --verbose",
"test:watch": "vitest --watch",
"lint": "eslint *.ts",
"clean": "rm -rf dist"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.51.0",
"openai": "^5.12.2",
"dotenv": "^16.4.5",
"diff": "^5.1.0",
"web-tree-sitter": "^0.20.8"
},
"devDependencies": {
"@roo-code/config-eslint": "workspace:^",
"@types/node": "^20.0.0",
"esbuild": "^0.25.0",
"eslint": "^9.27.0",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -324,22 +324,4 @@ async function main() {
}
}

// Check for required environment variables
function checkEnvironment() {
const provider = process.env.LLM_PROVIDER || "kilocode"

if (provider !== "kilocode") {
console.error(`\n❌ Error: Only kilocode provider is supported. Got: ${provider}`)
process.exit(1)
}

if (!process.env.KILOCODE_API_KEY) {
console.error(`\n❌ Error: KILOCODE_API_KEY is not set`)
console.log("\nPlease create a .env file with your API credentials.")
console.log("Example: KILOCODE_API_KEY=your-api-key-here\n")
process.exit(1)
}
}

checkEnvironment()
main().catch(console.error)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from "fs"
import path from "path"
import { fileURLToPath } from "url"
import { CURSOR_MARKER } from "../services/ghost/ghostConstants.js"
import { CURSOR_MARKER } from "../../src/services/ghost/ghostConstants.js"

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
Expand Down Expand Up @@ -44,20 +44,27 @@ function parseTestCaseFile(filePath: string): { description: string; input: stri
}

function loadTestCases(): Category[] {
if (!fs.existsSync(TEST_CASES_DIR)) {
// Use the correct test cases directory for bundled execution
const testCasesPath = fs.existsSync(TEST_CASES_DIR) ? TEST_CASES_DIR : path.join(process.cwd(), "test-cases")

if (!fs.existsSync(testCasesPath)) {
console.error("Error: Test cases directory not found at:", testCasesPath)
return []
}

return loadFromDirectory(testCasesPath)
}

function loadFromDirectory(testCasesDir: string): Category[] {
const categories: Category[] = []
const categoryDirs = fs.readdirSync(TEST_CASES_DIR, { withFileTypes: true })
const categoryDirs = fs.readdirSync(testCasesDir, { withFileTypes: true })

for (const categoryDir of categoryDirs) {
if (!categoryDir.isDirectory()) continue

const categoryName = categoryDir.name
const categoryPath = path.join(TEST_CASES_DIR, categoryName)
const categoryPath = path.join(testCasesDir, categoryName)
const testCaseFiles = fs.readdirSync(categoryPath).filter((f) => f.endsWith(".txt"))

const testCases: CategoryTestCase[] = []

for (const testCaseFile of testCaseFiles) {
Expand Down
28 changes: 28 additions & 0 deletions packages/ghost-benchmarks/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"lib": ["ES2022", "esnext.disposable", "DOM"],
"moduleResolution": "Bundler",
"baseUrl": ".",
"strict": false,
"noImplicitAny": false,
"strictNullChecks": false,
"strictPropertyInitialization": false,
"noImplicitReturns": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": false,
"resolveJsonModule": true,
"declaration": true,
"outDir": "./dist",
"noEmit": false,
"useUnknownInCatchVariables": false,
"types": ["node"]
},
"include": ["./*.ts", "./test-cases/**/*.txt"],
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
}
15 changes: 15 additions & 0 deletions packages/ghost-benchmarks/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineConfig } from "vitest/config"

export default defineConfig({
test: {
globals: true,
environment: "node",
include: ["src/**/*.{test,spec}.{js,ts}"],
exclude: ["node_modules", "dist"],
},
resolve: {
alias: {
"@": new URL("./src", import.meta.url).pathname,
},
},
})
Loading
Loading