From 606395ca8b8b1c762b3d014a547f1d11a7f7cc65 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Mar 2026 22:01:10 +0000 Subject: [PATCH 1/2] refactor: clean up codebase, remove dead code and improve readability - Remove dead genCommand() and editPrompt() from index.ts, extract resolveModelConfig() helper to eliminate duplicated config/model logic - Remove all commented-out code across the codebase - Remove unused imports (google, ollama, openai, anthropic, etc from ai.ts) - Remove dead getDefaultModelId() function from ai.ts - Remove dead iscancelled() function from config.ts - Remove duplicate getDetailedHardwareInfo() (identical to getHardwareInfo()) - Remove legacy evaluateGeneration() and commented-out batch eval block - Deduplicate levenshteinDistance and withRetry by exporting from eval.ts and importing in eval-improvements.ts - Simplify getDefaultModel() with data-driven env var lookup - Simplify generateTextWithModel() with spread syntax - Simplify GPU fallback in hardware.ts (identical branches consolidated) - Extract shell history logic into appendToShellHistory() helper - Fix unnecessary type assertion, redundant awaits, and unused variables - Fix inconsistent cancel handling (symbol check -> isCancel pattern) All changes verified: typecheck, lint, and build pass cleanly. --- bun.lock | 1 + src/bench_models.ts | 2 +- src/commands/config.ts | 10 --- src/helpers/hardware.ts | 43 +--------- src/index.ts | 96 ++++------------------ src/lib/ai.ts | 132 +++++++++++-------------------- src/lib/basic.eval.ts | 2 +- src/lib/ci-eval.ts | 2 +- src/lib/config.ts | 28 ++----- src/lib/enhanced-eval-example.ts | 2 +- src/lib/eval-improvements.ts | 49 +----------- src/lib/eval.ts | 68 +--------------- src/lib/example.eval.ts | 6 +- src/lib/llm-judge.eval.ts | 4 +- src/test-improved-prompt.ts | 2 +- src/utils.ts | 44 +++++------ 16 files changed, 107 insertions(+), 384 deletions(-) diff --git a/bun.lock b/bun.lock index df5a45c..34dde14 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "lazyshell", diff --git a/src/bench_models.ts b/src/bench_models.ts index 079e1c9..c7be500 100644 --- a/src/bench_models.ts +++ b/src/bench_models.ts @@ -2,7 +2,7 @@ import fs from 'fs/promises'; import path from 'path'; import chalk from 'chalk'; import { performance } from 'perf_hooks'; -import { getBenchmarkModels, generateBenchmarkText, Command, generateCommandStruct, ModelConfig } from './lib/ai'; +import { getBenchmarkModels, Command, generateCommandStruct, ModelConfig } from './lib/ai'; import { spinner } from '@clack/prompts'; // Get the models to benchmark from our AI library diff --git a/src/commands/config.ts b/src/commands/config.ts index 7eea6f4..80f7154 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -102,8 +102,6 @@ async function showCurrentConfig(config: Config) { } async function editProvider(config: Config) { - // console.log(chalk.blue('🔄 Change Provider')); - const newProvider = await promptProvider(); config.provider = newProvider; config.model = SUPPORTED_PROVIDERS[newProvider].defaultModel; @@ -130,8 +128,6 @@ async function editProvider(config: Config) { } async function editApiKey(config: Config) { - // console.log(chalk.blue('🔑 Update API Key')); - const providerInfo = SUPPORTED_PROVIDERS[config.provider]; // Handle providers that don't support API keys at all @@ -159,8 +155,6 @@ async function editApiKey(config: Config) { } async function editModel(config: Config) { - // console.log(chalk.blue('🤖 Change Model')); - const currentModel = config.model || SUPPORTED_PROVIDERS[config.provider].defaultModel; const newModel = await input({ @@ -185,8 +179,6 @@ async function editModel(config: Config) { } async function editBaseUrl(config: Config) { - // console.log(chalk.blue('🌐 Change Base URL')); - const providerInfo = SUPPORTED_PROVIDERS[config.provider]; // Check if the provider supports custom base URL @@ -220,8 +212,6 @@ async function editBaseUrl(config: Config) { } async function resetConfiguration() { - // console.log(chalk.blue('🔄 Reset Configuration')); - const confirmed = await confirm({ message: chalk.yellow('Are you sure you want to reset your configuration? This will delete your current settings.'), }); diff --git a/src/helpers/hardware.ts b/src/helpers/hardware.ts index 2a7b3f9..a6cdad4 100644 --- a/src/helpers/hardware.ts +++ b/src/helpers/hardware.ts @@ -9,7 +9,6 @@ export interface HardwareInfo { async function getGpuInfo(): Promise { try { - // Try to dynamically import systeminformation const si = await import('systeminformation').catch(() => null); if (si) { @@ -18,12 +17,10 @@ async function getGpuInfo(): Promise { const gpu = graphics.controllers[0]; let gpuInfo = gpu.model || gpu.name || 'Unknown GPU'; - // Add vendor if available and not already included if (gpu.vendor && !gpuInfo.toLowerCase().includes(gpu.vendor.toLowerCase())) { gpuInfo = `${gpu.vendor} ${gpuInfo}`; } - // Add VRAM info if available if (gpu.vram && gpu.vram > 0) { gpuInfo += ` (${gpu.vram}MB VRAM)`; } @@ -31,24 +28,11 @@ async function getGpuInfo(): Promise { return gpuInfo.trim(); } } - } catch (error) { - console.warn('Failed to get GPU info from systeminformation:', error); + } catch { // Fall through to basic detection } - // Fallback: Basic GPU detection based on platform - const platform = os.platform(); - - switch (platform) { - case 'linux': - return 'GPU info unavailable (install systeminformation for detailed info)'; - case 'darwin': - return 'GPU info unavailable (install systeminformation for detailed info)'; - case 'win32': - return 'GPU info unavailable (install systeminformation for detailed info)'; - default: - return 'Unknown GPU'; - } + return 'GPU info unavailable (install systeminformation for detailed info)'; } export async function getHardwareInfo(): Promise { @@ -59,28 +43,7 @@ export async function getHardwareInfo(): Promise { const gpu = await getGpuInfo(); return { cpu, memory, arch, gpu }; - } catch (error) { - console.error('Error getting hardware info:', error); - return { - cpu: 'Unknown CPU', - memory: 'Unknown Memory', - arch: 'Unknown Architecture', - gpu: 'Unknown GPU', - }; - } -} - -// Async version for detailed hardware info including GPU -export async function getDetailedHardwareInfo(): Promise { - try { - const cpu = os.cpus()[0]?.model || 'Unknown CPU'; - const memory = `${Math.round(os.totalmem() / 1024 / 1024 / 1024)} GB`; - const arch = os.arch(); - const gpu = await getGpuInfo(); - - return { cpu, memory, arch, gpu }; - } catch (error) { - console.error('Error getting detailed hardware info:', error); + } catch { return { cpu: 'Unknown CPU', memory: 'Unknown Memory', diff --git a/src/index.ts b/src/index.ts index 502858d..e3d97a9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,93 +2,54 @@ import { Command } from 'commander'; import { select, text as input, spinner, outro, isCancel, cancel, intro } from '@clack/prompts'; import chalk from 'chalk'; import { info, print, runCommand, printWrapped } from './utils'; -import { generateCommand, generateCommandStruct, getDefaultModel, getModelFromConfig } from './lib/ai'; +import { generateCommandStruct, getDefaultModel, getModelFromConfig, type ModelConfig } from './lib/ai'; import { getOrInitializeConfig } from './lib/config'; - import { showConfigUI } from './commands/config'; -// Conditional clipboard functionality async function copyToClipboard(text: string): Promise { try { - // Only import and use clipboard on supported platforms - const os = require('os'); - const platform = os.platform(); - - // Skip clipboard on Android (which shows up as linux) and potentially problematic ARM systems - // Check for Android-specific indicators - const isAndroid = process.env.ANDROID_ROOT || process.env.ANDROID_DATA || platform === 'android'; - - // Skip if it's Android or if we're on ARM and the environment looks mobile + const isAndroid = process.env.ANDROID_ROOT || process.env.ANDROID_DATA; if (isAndroid) { return false; } - // Try to import the clipboard module const { Clipboard } = await import('@napi-rs/clipboard'); const clipboard = new Clipboard(); clipboard.setText(text); return true; } catch { - // Silently fail if clipboard is not available return false; } } -async function genCommand(prompt: string) { - // Get configuration first +async function resolveModelConfig(): Promise { const config = await getOrInitializeConfig(); if (!config) { console.error(chalk.red('Failed to initialize configuration. Exiting.')); process.exit(1); } - let modelConfig; try { - modelConfig = getModelFromConfig(config); + return getModelFromConfig(config); } catch (error) { - await print(chalk.red(`Configuration error: ${error}`)); - await print(chalk.yellow('Falling back to environment variables...')); - modelConfig = getDefaultModel(); - } - - // console log the model being used - // console.log(chalk.blue(`Using model: ${modelConfig.provider}/${modelConfig.modelId}`)); - - // Show spinner while generating command - const spin = spinner(); - spin.start('Loading...'); - try { - const result = await generateCommand(prompt); - spin.stop('Command generated successfully!'); - return { text: result }; - } catch (error) { - spin.stop(chalk.red('Failed to generate command')); - throw error; + console.error(chalk.red(`Configuration error: ${error}`)); + console.log(chalk.yellow('Falling back to environment variables...')); + return getDefaultModel(); } } -async function editPrompt(command: string): Promise { - // For now, just return the command as is - // In a real implementation, this would open an editor - const editedCommand = await input({ - message: 'How would you like to edit the command?', - placeholder: command, - }); - if (isCancel(editedCommand)) { - cancel('Editing cancelled.'); - process.exit(0); - } - return editedCommand; -} - async function refineCommand(currentPrompt: string, command: string): Promise { const refineText = await input({ message: 'How would you like to refine the command?', placeholder: '', }); - // Combine the original prompt with the refinement - return `former prompt:${currentPrompt} its command is ${command} refining prompt: ${refineText as string}`; + if (isCancel(refineText)) { + cancel('Refinement cancelled.'); + process.exit(0); + } + + return `former prompt:${currentPrompt} its command is ${command} refining prompt: ${refineText}`; } const program = new Command(); @@ -105,26 +66,7 @@ program while (shouldContinue) { try { - // const result = await genCommand(currentPrompt); - // const command = result.text.trim(); - - // Get configuration and use it for command generation - const config = await getOrInitializeConfig(); - if (!config) { - console.error(chalk.red('Failed to initialize configuration. Exiting.')); - process.exit(1); - } - - let modelConfig; - try { - modelConfig = getModelFromConfig(config); - } catch (error) { - console.error(chalk.red(`Configuration error: ${error}`)); - console.log(chalk.yellow('Falling back to environment variables...')); - modelConfig = getDefaultModel(); - } - - // console.log(chalk.blue(`Using model: ${modelConfig.provider}/${modelConfig.modelId}`)); + const modelConfig = await resolveModelConfig(); const spin = spinner(); spin.start('Loading...'); @@ -132,11 +74,10 @@ program spin.stop('Your command: '); const command = result.command.trim(); - await print(`${command}`); + await print(command); if ('explanation' in result && result.explanation) { await info('Explanation:'); - const lineWidth = 80; - await printWrapped(`${result.explanation.trim()}`, lineWidth); + await printWrapped(result.explanation.trim(), 80); } const clipboardSuccess = await copyToClipboard(command); @@ -148,7 +89,6 @@ program message: 'Run command?', options: [ { label: '✅ Yes', value: 'execute' }, - // { name: '✏️ Edit command', value: 'edit' }, { label: '🔧 Refine', value: 'refine' }, { label: '❌ Cancel', value: 'cancel' }, ], @@ -164,9 +104,6 @@ program runCommand(command); shouldContinue = false; break; - // case 'edit': - // currentPrompt = await editPrompt(command); - // break; case 'refine': currentPrompt = await refineCommand(currentPrompt, command); break; @@ -180,6 +117,7 @@ program } } }); + program .command('config') .description('Open configuration UI') diff --git a/src/lib/ai.ts b/src/lib/ai.ts index 40ca7c5..86550b4 100644 --- a/src/lib/ai.ts +++ b/src/lib/ai.ts @@ -1,28 +1,22 @@ import { generateObject, generateText, type LanguageModel } from 'ai'; -import { google } from '@ai-sdk/google'; -import { ollama } from 'ollama-ai-provider'; -import { openai } from '@ai-sdk/openai'; -import { anthropic } from '@ai-sdk/anthropic'; -import { createOpenAICompatible } from '@ai-sdk/openai-compatible'; import os from 'os'; import z from 'zod'; import type { Config, ProviderKey } from './config'; import dedent from 'dedent'; import { getDistroPackageManager } from '../helpers/package-manager'; import { getHardwareInfo, type HardwareInfo } from '../helpers/hardware'; +import { getModelFromRegistry } from './provider-registry'; -// System information interface export interface SystemInfo { platform: string; release: string; - distro?: string; // Only for Linux - packageManager?: string; // Only for Linux + distro?: string; + packageManager?: string; type: string; arch: string; - hardware?: HardwareInfo; // Hardware info for Linux systems + hardware?: HardwareInfo; } -// Model configuration interface export interface ModelConfig { provider: string; modelId: string; @@ -31,14 +25,12 @@ export interface ModelConfig { maxRetries?: number | undefined; } -// Text generation options -export interface GenerationOptions { +interface GenerationOptions { temperature?: number; systemPrompt?: string; maxTokens?: number; } -// Simple function to get Linux distro name from /etc/os-release function getLinuxDistro(): string | undefined { try { const osRelease = require('fs').readFileSync('/etc/os-release', 'utf8'); @@ -49,7 +41,6 @@ function getLinuxDistro(): string | undefined { } } -// Get system information for context export async function getSystemInfo(): Promise { const sysinfo: SystemInfo = { platform: os.platform(), @@ -58,61 +49,41 @@ export async function getSystemInfo(): Promise { arch: os.arch(), }; if (os.platform() === 'linux') { - sysinfo.distro = getLinuxDistro(); // Get Linux distribution name simply + sysinfo.distro = getLinuxDistro(); sysinfo.packageManager = getDistroPackageManager(); sysinfo.hardware = await getHardwareInfo(); } return sysinfo; } -import { ProviderRegistry, getModelFromRegistry } from './provider-registry'; - -// Get model based on configuration export function getModelFromConfig(config: Config): ModelConfig { return getModelFromRegistry(config.provider, config.model, config.baseUrl, config.apiKey); } -// Get default model ID for a provider -function getDefaultModelId(provider: ProviderKey): string { - const defaultModels: Record = { - groq: 'llama-3.3-70b-versatile', - google: 'gemini-2.0-flash-lite', - openrouter: 'google/gemini-2.0-flash-001', - anthropic: 'claude-3-5-haiku-latest', - openai: 'gpt-4o-mini', - ollama: 'llama3.2', - mistral: 'devstral-small-2505', - lmstudio: 'deepseek/deepseek-r1-0528-qwen3-8b', - openaiCompatible: 'gpt-3.5-turbo', - }; - - return defaultModels[provider]; -} - -// Get available model based on environment variables (legacy function) export function getDefaultModel(): ModelConfig { - if (process.env.GROQ_API_KEY) { - return getModelFromRegistry('groq'); - } else if (process.env.GOOGLE_GENERATIVE_AI_API_KEY) { - return getModelFromRegistry('google'); - } else if (process.env.OPENROUTER_API_KEY) { - return getModelFromRegistry('openrouter'); - } else if (process.env.ANTHROPIC_API_KEY) { - return getModelFromRegistry('anthropic'); - } else if (process.env.OPENAI_API_KEY) { - return getModelFromRegistry('openai'); - } else { - try { - return getModelFromRegistry('ollama'); - } catch (error) { - throw new Error( - 'No API key found. Please set either GROQ_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, OPENROUTER_API_KEY, ANTHROPIC_API_KEY, or OPENAI_API_KEY. Or setup Ollama/LM Studio' - ); + const envProviderMap: [string, ProviderKey][] = [ + ['GROQ_API_KEY', 'groq'], + ['GOOGLE_GENERATIVE_AI_API_KEY', 'google'], + ['OPENROUTER_API_KEY', 'openrouter'], + ['ANTHROPIC_API_KEY', 'anthropic'], + ['OPENAI_API_KEY', 'openai'], + ]; + + for (const [envVar, provider] of envProviderMap) { + if (process.env[envVar]) { + return getModelFromRegistry(provider); } } + + try { + return getModelFromRegistry('ollama'); + } catch { + throw new Error( + 'No API key found. Please set either GROQ_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, OPENROUTER_API_KEY, ANTHROPIC_API_KEY, or OPENAI_API_KEY. Or setup Ollama/LM Studio' + ); + } } -// Get predefined models for benchmarking export function getBenchmarkModels(): Record { return { 'or-devstral': getModelFromRegistry('openrouter', 'mistralai/devstral-small:free').model, @@ -125,30 +96,22 @@ export function getBenchmarkModels(): Record { }; } -// Generate text with a model -export async function generateTextWithModel(model: LanguageModel, prompt: string, options: GenerationOptions = {}) { +async function generateTextWithModel(model: LanguageModel, prompt: string, options: GenerationOptions = {}) { const { temperature = 0, systemPrompt, maxTokens } = options; - const generateOptions: any = { + return generateText({ model, temperature, prompt, - }; - - if (systemPrompt) { - generateOptions.system = systemPrompt; - } - - if (maxTokens) { - generateOptions.maxTokens = maxTokens; - } - - return await generateText(generateOptions); + ...(systemPrompt && { system: systemPrompt }), + ...(maxTokens && { maxTokens }), + }); } const osInfo = await getSystemInfo(); const pwd = process.cwd(); const currentShell = os.platform() == 'win32' ? 'powershell' : process.env.SHELL || os.userInfo().shell || 'unknown'; + const systemPrompt = dedent`You are an expert system administrator and command-line specialist. SYSTEM CONTEXT: @@ -229,29 +192,24 @@ export async function generateCommandStruct( explanation: boolean = true ): Promise { const modelConf = modelConfig || getDefaultModel(); - let zShema; - if (explanation) { - zShema = zCmdExp; - } else { - zShema = zCmd; - } + const schema = explanation ? zCmdExp : zCmd; + try { const result = await generateObject({ model: modelConf.model, system: systemPrompt, - schema: zShema, + schema, prompt, temperature: modelConf.temperature || 0.1, maxRetries: modelConf.maxRetries || undefined, }); return result.object; - } catch (error) { + } catch { const result = await generateCommand(prompt, modelConf); return { command: result, explanation: '' }; } } -// Generate command using the default model with system admin context export async function generateCommand(prompt: string, modelConfig?: ModelConfig): Promise { const finalModelConfig = modelConfig || getDefaultModel(); @@ -263,7 +221,6 @@ export async function generateCommand(prompt: string, modelConfig?: ModelConfig) return result.text.trim(); } -// Generate text for benchmarking with simple system prompt export async function generateBenchmarkText(model: LanguageModel, prompt: string): Promise { const result = await generateTextWithModel(model, prompt, { temperature: 0, @@ -273,24 +230,23 @@ export async function generateBenchmarkText(model: LanguageModel, prompt: string return result.text.trim(); } -// Export model instances for direct use export const models = { - groq: (modelId: string = 'llama-3.3-70b-versatile', baseUrl?: string, apiKey?: string) => + groq: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('groq', modelId, baseUrl, apiKey).model, - google: (modelId: string = 'gemini-2.0-flash-lite', baseUrl?: string, apiKey?: string) => + google: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('google', modelId, baseUrl, apiKey).model, - openrouter: (modelId: string = 'google/gemini-2.0-flash-001', baseUrl?: string, apiKey?: string) => + openrouter: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('openrouter', modelId, baseUrl, apiKey).model, - anthropic: (modelId: string = 'claude-3-5-haiku-latest', baseUrl?: string, apiKey?: string) => + anthropic: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('anthropic', modelId, baseUrl, apiKey).model, - openai: (modelId: string = 'gpt-4o-mini', baseUrl?: string, apiKey?: string) => + openai: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('openai', modelId, baseUrl, apiKey).model, - ollama: (modelId: string = 'llama3.2', baseUrl?: string, apiKey?: string) => + ollama: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('ollama', modelId, baseUrl, apiKey).model, - mistral: (modelId: string = 'devstral-small-2505', baseUrl?: string, apiKey?: string) => + mistral: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('mistral', modelId, baseUrl, apiKey).model, - lmstudio: (modelId: string = 'deepseek/deepseek-r1-0528-qwen3-8b', baseUrl?: string, apiKey?: string) => + lmstudio: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('lmstudio', modelId, baseUrl, apiKey).model, - openaiCompatible: (modelId: string = 'gpt-3.5-turbo', baseUrl?: string, apiKey?: string) => + openaiCompatible: (modelId?: string, baseUrl?: string, apiKey?: string) => getModelFromRegistry('openaiCompatible', modelId, baseUrl, apiKey).model, }; diff --git a/src/lib/basic.eval.ts b/src/lib/basic.eval.ts index 273ee9d..9547528 100644 --- a/src/lib/basic.eval.ts +++ b/src/lib/basic.eval.ts @@ -1,4 +1,4 @@ -import { generateCommand, generateBenchmarkText, getDefaultModel, models } from './ai'; +import { generateCommand } from './ai'; import { eval, ExactMatch, Contains, Levenshtein, LLMJudge, createLLMJudge } from './eval'; async function main() { diff --git a/src/lib/ci-eval.ts b/src/lib/ci-eval.ts index f3471e9..07978e0 100644 --- a/src/lib/ci-eval.ts +++ b/src/lib/ci-eval.ts @@ -1,5 +1,5 @@ import { generateCommand, getDefaultModel, models, ModelConfig } from './ai'; -import { eval as runEval, LLMJudge, createLLMJudge, EvalSummary } from './eval'; +import { eval as runEval, createLLMJudge, EvalSummary } from './eval'; const judgeModelConf: ModelConfig = { model: models.groq('qwen-qwq-32b'), diff --git a/src/lib/config.ts b/src/lib/config.ts index 49ce05f..01cd7a9 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -26,7 +26,6 @@ export const SUPPORTED_PROVIDERS = { envVar: 'OPENROUTER_API_KEY', defaultModel: 'google/gemini-2.0-flash-001', defaultBaseUrl: 'https://openrouter.ai/api/v1', - // defaultModel: 'google/gemma-3-27b-it:free', }, anthropic: { name: 'Anthropic Claude', @@ -186,10 +185,6 @@ export async function promptProvider(): Promise { process.exit(0); } - if (typeof provider === 'symbol') { - throw new Error('Provider selection cancelled'); - } - return provider; } @@ -265,8 +260,9 @@ export async function promptApiKey(provider: ProviderKey): Promise { if (config && validateConfig(config)) { return config; - } else { - console.log(chalk.yellow('Configuration exists but is incomplete or invalid.')); - return await initializeConfig(); } - } else { - console.log(chalk.yellow('No configuration found.')); - return await initializeConfig(); + console.log(chalk.yellow('Configuration exists but is incomplete or invalid.')); + return initializeConfig(); } + + console.log(chalk.yellow('No configuration found.')); + return initializeConfig(); } /** @@ -378,15 +373,8 @@ export function getApiKeyFromEnv(provider: ProviderKey): string | undefined { * Get the effective API key (config first, then environment) */ export function getEffectiveApiKey(config: Config): string | undefined { - // Use config API key if available if (config.apiKey) { return config.apiKey; } - - // Fall back to environment variable return getApiKeyFromEnv(config.provider); } - -function iscancelled(provider: string | symbol) { - throw new Error('Function not implemented.'); -} diff --git a/src/lib/enhanced-eval-example.ts b/src/lib/enhanced-eval-example.ts index a3cffe2..00fc508 100644 --- a/src/lib/enhanced-eval-example.ts +++ b/src/lib/enhanced-eval-example.ts @@ -1,4 +1,4 @@ -import { generateCommand, generateCommandStruct, getDefaultModel, models, ModelConfig } from './ai'; +import { generateCommandStruct, getDefaultModel, models, ModelConfig } from './ai'; import { eval as runEnhancedEval, EnhancedExactMatch, diff --git a/src/lib/eval-improvements.ts b/src/lib/eval-improvements.ts index d1d5e1a..c03c311 100644 --- a/src/lib/eval-improvements.ts +++ b/src/lib/eval-improvements.ts @@ -1,6 +1,7 @@ import z from 'zod'; import { generateObject } from 'ai'; import { models, type ModelConfig } from './ai'; +import { levenshteinDistance, withRetry } from './eval'; import fs from 'fs/promises'; import path from 'path'; @@ -121,7 +122,7 @@ export const CommandSafety: EnhancedScorer = { description: 'Evaluates command safety and potential risks', category: 'security', weight: 2.0, // Higher weight for safety - score: (input: any, output: string, expected: any): ScorerResult => { + score: (input: any, output: string, _expected: any): ScorerResult => { const command = String(output); const dangerousPatterns = [ /rm\s+-rf\s+\//, // rm -rf / @@ -265,51 +266,6 @@ function calculateSimilarity(str1: string, str2: string): number { return (longer.length - distance) / longer.length; } -function levenshteinDistance(str1: string, str2: string): number { - const matrix = Array(str2.length + 1) - .fill(null) - .map(() => Array(str1.length + 1).fill(null)); - - for (let i = 0; i <= str1.length; i++) { - matrix[0][i] = i; - } - - for (let j = 0; j <= str2.length; j++) { - matrix[j][0] = j; - } - - for (let j = 1; j <= str2.length; j++) { - for (let i = 1; i <= str1.length; i++) { - const substitutionCost = str1[i - 1] === str2[j - 1] ? 0 : 1; - matrix[j][i] = Math.min(matrix[j][i - 1] + 1, matrix[j - 1][i] + 1, matrix[j - 1][i - 1] + substitutionCost); - } - } - - return matrix[str2.length][str1.length]; -} - -async function withRetry(fn: () => Promise, maxRetries: number = 3, baseDelayMs: number = 1000): Promise { - for (let attempt = 0; attempt < maxRetries; attempt++) { - try { - return await fn(); - } catch (error) { - const isRateLimitError = - error && - (String(error).toLowerCase().includes('rate limit') || - String(error).toLowerCase().includes('too many requests')); - - if (!isRateLimitError || attempt === maxRetries - 1) { - throw error; - } - - const delayMs = baseDelayMs * Math.pow(2, attempt); - console.log(`Rate limited, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`); - await new Promise(resolve => setTimeout(resolve, delayMs)); - } - } - throw new Error('Max retries exceeded'); -} - // Statistics Utilities function calculateStatistics(scores: number[]): { @@ -347,7 +303,6 @@ export async function runEnhancedEval => { + score: async (input: any, output: any, _expected?: any): Promise => { try { return await withRetry( async () => { - // Optional delay for conservative rate limiting - // if (delayMs > 0) { - // await createDelay(delayMs); - // } - const model = modelConfig || judgeModelConf; const prompt = `You are an expert evaluator. Please rate the following output based on ${criteria}. @@ -146,8 +141,7 @@ export const LLMJudge = createLLMJudge('LLMJudge'); export const LLMJudgeNoDelay = createLLMJudge('LLMJudgeNoDelay', undefined, undefined, 0); export const LLMJudgeFast = createLLMJudge('LLMJudgeFast', undefined, undefined, 500); -// Helper function for Levenshtein distance -function levenshteinDistance(str1: string, str2: string): number { +export function levenshteinDistance(str1: string, str2: string): number { const matrix = Array(str2.length + 1) .fill(null) .map(() => Array(str1.length + 1).fill(null)); @@ -179,7 +173,6 @@ export function createDelay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } -// Rate limiting with exponential backoff export async function withRetry( fn: () => Promise, maxRetries: number = 3, @@ -311,61 +304,4 @@ export async function runEval( }; } -// Export with the desired interface name export { runEval as eval }; - -// 4. Legacy compatibility (for existing code) - -export const zEvaluationInput = z.object({ - originalPrompt: z.string(), - generatedOutput: z.string(), - referenceOutput: z.string().optional(), -}); -export type EvaluationInput = z.infer; - -export const zEvaluationResult = z.object({ - score: z.number().min(1).max(5), - explanation: z.string(), -}); -export type EvaluationResult = z.infer; - -// Legacy evaluation function (kept for backward compatibility) -export async function evaluateGeneration( - input: EvaluationInput, - evaluatorModelConfig?: any -): Promise { - // This is a simplified version for backward compatibility - // In a real implementation, you might want to use the new eval system - throw new Error('Legacy evaluateGeneration is deprecated. Use the new eval() function instead.'); -} - -// Example of how you might batch evaluate (optional for now) -/* -export async function batchEvaluateGenerations( - inputs: EvaluationInput[], - evaluatorModelConfig?: ModelConfig -): Promise { - const results: EvaluationResult[] = []; - for (const input of inputs) { - try { - const result = await evaluateGeneration(input, evaluatorModelConfig); - results.push(result); - } catch (error) { - console.error(`Failed to evaluate input: ${JSON.stringify(input)}`, error); - // Decide how to handle errors, e.g., push a specific error result or skip - results.push({ - score: 0, // Indicate error - explanation: `Evaluation failed: ${error instanceof Error ? error.message : String(error)}`, - }); - } - } - return results; -} -*/ - -// Utility to get a specific evaluator model (if needed, or rely on ai.ts) -// export function getEvaluatorModel(config?: Config): ModelConfig { -// // Potentially use a specific model known for good evaluation, or a cost-effective one -// // For now, reuses the logic from ai.ts -// return getDefaultModel(); // Or a more specific configuration -// } diff --git a/src/lib/example.eval.ts b/src/lib/example.eval.ts index bd21a27..161febb 100644 --- a/src/lib/example.eval.ts +++ b/src/lib/example.eval.ts @@ -2,7 +2,7 @@ import { eval, Levenshtein, ExactMatch, Contains } from './eval'; // Example 1: Simple string concatenation task async function basicStringExample() { - return await eval('Basic String Concatenation', { + return eval('Basic String Concatenation', { // Test data function data: async () => { return [ @@ -22,7 +22,7 @@ async function basicStringExample() { // Example 2: Command generation evaluation async function commandGenerationExample() { - return await eval('Command Generation', { + return eval('Command Generation', { data: () => [ { input: 'list all files in current directory', @@ -56,7 +56,7 @@ async function commandGenerationExample() { // Example 3: LLM-based evaluation (you would replace this with actual LLM calls) async function llmExample() { - return await eval('LLM Generation', { + return eval('LLM Generation', { data: async () => [ { input: 'Explain recursion', diff --git a/src/lib/llm-judge.eval.ts b/src/lib/llm-judge.eval.ts index dfe0f2f..bdff3d6 100644 --- a/src/lib/llm-judge.eval.ts +++ b/src/lib/llm-judge.eval.ts @@ -1,5 +1,5 @@ -import { generateCommand, getDefaultModel, models, ModelConfig } from './ai'; -import { eval, LLMJudge, createLLMJudge } from './eval'; +import { generateCommand, models, ModelConfig } from './ai'; +import { eval, createLLMJudge } from './eval'; const judgeModelConf: ModelConfig = { model: models.groq('qwen-qwq-32b'), diff --git a/src/test-improved-prompt.ts b/src/test-improved-prompt.ts index e786890..6a5ef2a 100644 --- a/src/test-improved-prompt.ts +++ b/src/test-improved-prompt.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { generateCommand, generateCommandStruct, getDefaultModel } from './lib/ai'; +import { generateCommandStruct } from './lib/ai'; import chalk from 'chalk'; async function testImprovedPrompt() { diff --git a/src/utils.ts b/src/utils.ts index 6d5703f..08f0682 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -17,55 +17,51 @@ export function runCommand(command: string) { if (stderr) { console.error(`stderr: ${stderr}`); } + }); + + appendToShellHistory(command); + + childProcess.stdout?.on('data', data => { + process.stdout.write(data); + }); - // console.log(`stdout: ${stdout}`); + childProcess.stderr?.on('data', data => { + process.stderr.write(data); }); +} - // Determine user shell and add command to appropriate history file +function appendToShellHistory(command: string) { const shell = process.env.SHELL?.split('/').pop() || ''; - let historyFilePath = ''; + let historyFilePath: string; + let entry: string; if (shell === 'zsh') { historyFilePath = path.join(os.homedir(), '.zsh_history'); - // ZSH history format includes timestamps - fs.appendFileSync(historyFilePath, `: ${Math.floor(Date.now() / 1000)}:0;${command}\n`); + entry = `: ${Math.floor(Date.now() / 1000)}:0;${command}\n`; } else if (shell === 'bash') { historyFilePath = path.join(os.homedir(), '.bash_history'); - fs.appendFileSync(historyFilePath, `${command}\n`); + entry = `${command}\n`; } else { - // Default to sh history format historyFilePath = path.join(os.homedir(), '.sh_history'); - fs.appendFileSync(historyFilePath, `${command}\n`); + entry = `${command}\n`; } - // Handle process output in real-time - childProcess.stdout?.on('data', data => { - process.stdout.write(data); - }); - - childProcess.stderr?.on('data', data => { - process.stderr.write(data); - }); + fs.appendFileSync(historyFilePath, entry); } export async function print(msg: string) { await stream.message( (function* () { - yield `${msg}`; + yield msg; })() ); } -/** - * Print text with line wrapping - * @param msg - The message to print - * @param lineWidth - Maximum characters per line (default: 80) - */ export async function printWrapped(msg: string, lineWidth: number = 80) { const wrappedMsg = wrapText(msg, lineWidth); await stream.message( (function* () { - yield `${wrappedMsg}`; + yield wrappedMsg; })() ); } @@ -73,7 +69,7 @@ export async function printWrapped(msg: string, lineWidth: number = 80) { export async function info(msg: string) { await stream.info( (function* () { - yield `${msg}`; + yield msg; })() ); } From c8cd7689ab5f11cc51dbc7669e094f4254e43a02 Mon Sep 17 00:00:00 2001 From: Oussama Bernou <96293508+bernoussama@users.noreply.github.com> Date: Wed, 18 Mar 2026 22:18:53 +0000 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/helpers/hardware.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helpers/hardware.ts b/src/helpers/hardware.ts index a6cdad4..f25d45e 100644 --- a/src/helpers/hardware.ts +++ b/src/helpers/hardware.ts @@ -28,7 +28,8 @@ async function getGpuInfo(): Promise { return gpuInfo.trim(); } } - } catch { + } catch (error) { + console.warn('Failed to get GPU info from systeminformation:', error); // Fall through to basic detection }