From acdfbb46443476d86cec21e9198e2d8544fedd01 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Mon, 22 Dec 2025 17:42:26 +0800 Subject: [PATCH] feat: Enhance CodexLens with GPU support and semantic status improvements - Added accelerator and providers fields to SemanticStatus interface. - Updated checkSemanticStatus function to retrieve ONNX providers and accelerator type. - Introduced detectGpuSupport function to identify available GPU modes (CUDA, DirectML). - Modified installSemantic function to support GPU acceleration modes and clean up ONNX Runtime installations. - Updated package requirements in PKG-INFO for semantic-gpu and semantic-directml extras. - Added new source files for GPU support and enrichment functionalities. - Updated tests to cover new features and ensure comprehensive testing. --- .mcp.json | 7 +- ccw/src/core/routes/codexlens-routes.ts | 40 +- .../dashboard-js/components/mcp-manager.js | 19 +- ccw/src/templates/dashboard-js/i18n.js | 158 +++- .../dashboard-js/views/codexlens-manager.js | 819 +++++++++++++++++- ccw/src/tools/codex-lens.ts | 220 ++++- codex-lens/src/codex_lens.egg-info/PKG-INFO | 15 +- .../src/codex_lens.egg-info/SOURCES.txt | 20 +- .../src/codex_lens.egg-info/requires.txt | 15 +- 9 files changed, 1215 insertions(+), 98 deletions(-) diff --git a/.mcp.json b/.mcp.json index 11f76ecd..1f0b7db7 100644 --- a/.mcp.json +++ b/.mcp.json @@ -9,11 +9,8 @@ "env": {} }, "ccw-tools": { - "command": "npx", - "args": [ - "-y", - "ccw-mcp" - ], + "command": "ccw-mcp", + "args": [], "env": { "CCW_ENABLED_TOOLS": "write_file,edit_file,smart_search,core_memory" } diff --git a/ccw/src/core/routes/codexlens-routes.ts b/ccw/src/core/routes/codexlens-routes.ts index ff1754ed..63047d91 100644 --- a/ccw/src/core/routes/codexlens-routes.ts +++ b/ccw/src/core/routes/codexlens-routes.ts @@ -10,11 +10,12 @@ import { executeCodexLens, checkSemanticStatus, installSemantic, + detectGpuSupport, uninstallCodexLens, cancelIndexing, isIndexingInProgress } from '../../tools/codex-lens.js'; -import type { ProgressInfo } from '../../tools/codex-lens.js'; +import type { ProgressInfo, GpuMode } from '../../tools/codex-lens.js'; export interface RouteContext { pathname: string; @@ -343,7 +344,7 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise } try { - const result = await executeCodexLens(['config-set', '--key', 'index_dir', '--value', index_dir, '--json']); + const result = await executeCodexLens(['config', 'set', 'index_dir', index_dir, '--json']); if (result.success) { return { success: true, message: 'Configuration updated successfully' }; } else { @@ -668,16 +669,43 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise } - // API: CodexLens Semantic Search Install (fastembed, ONNX-based, ~200MB) + // API: Detect GPU support for semantic search + if (pathname === '/api/codexlens/gpu/detect' && req.method === 'GET') { + try { + const gpuInfo = await detectGpuSupport(); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ success: true, ...gpuInfo })); + } catch (err) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ success: false, error: err.message })); + } + return true; + } + + // API: CodexLens Semantic Search Install (with GPU mode support) if (pathname === '/api/codexlens/semantic/install' && req.method === 'POST') { - handlePostRequest(req, res, async () => { + handlePostRequest(req, res, async (body) => { try { - const result = await installSemantic(); + // Get GPU mode from request body, default to 'cpu' + const gpuMode: GpuMode = body?.gpuMode || 'cpu'; + const validModes: GpuMode[] = ['cpu', 'cuda', 'directml']; + + if (!validModes.includes(gpuMode)) { + return { success: false, error: `Invalid GPU mode: ${gpuMode}. Valid modes: ${validModes.join(', ')}`, status: 400 }; + } + + const result = await installSemantic(gpuMode); if (result.success) { const status = await checkSemanticStatus(); + const modeDescriptions = { + cpu: 'CPU (ONNX Runtime)', + cuda: 'NVIDIA CUDA GPU', + directml: 'Windows DirectML GPU' + }; return { success: true, - message: 'Semantic search installed successfully (fastembed)', + message: `Semantic search installed successfully with ${modeDescriptions[gpuMode]}`, + gpuMode, ...status }; } else { diff --git a/ccw/src/templates/dashboard-js/components/mcp-manager.js b/ccw/src/templates/dashboard-js/components/mcp-manager.js index d9309bae..f4bbf1ae 100644 --- a/ccw/src/templates/dashboard-js/components/mcp-manager.js +++ b/ccw/src/templates/dashboard-js/components/mcp-manager.js @@ -946,20 +946,15 @@ function setCcwProjectRootToCurrent() { } // Build CCW Tools config with selected tools -// Uses isWindowsPlatform from state.js to generate platform-appropriate commands +// Uses globally installed ccw-mcp command (from claude-code-workflow package) function buildCcwToolsConfig(selectedTools, pathConfig = {}) { const { projectRoot, allowedDirs } = pathConfig; - // Windows requires 'cmd /c' wrapper to execute npx - // Other platforms (macOS, Linux) can run npx directly - const config = isWindowsPlatform - ? { - command: "cmd", - args: ["/c", "npx", "-y", "ccw-mcp"] - } - : { - command: "npx", - args: ["-y", "ccw-mcp"] - }; + // Use globally installed ccw-mcp command directly + // Requires: npm install -g claude-code-workflow + const config = { + command: "ccw-mcp", + args: [] + }; // Add env if not all tools or not default 4 core tools const coreTools = ['write_file', 'edit_file', 'codex_lens', 'smart_search']; diff --git a/ccw/src/templates/dashboard-js/i18n.js b/ccw/src/templates/dashboard-js/i18n.js index ef99167c..69d18269 100644 --- a/ccw/src/templates/dashboard-js/i18n.js +++ b/ccw/src/templates/dashboard-js/i18n.js @@ -23,6 +23,9 @@ const i18n = { 'common.loading': 'Loading...', 'common.error': 'Error', 'common.success': 'Success', + 'common.retry': 'Retry', + 'common.refresh': 'Refresh', + 'common.minutes': 'minutes', // Header 'header.project': 'Project:', @@ -277,8 +280,17 @@ const i18n = { 'codexlens.installDeps': 'Install Dependencies', 'codexlens.installDepsPrompt': 'Would you like to install them now? (This may take a few minutes)\n\nClick "Cancel" to create FTS index only.', 'codexlens.installingDeps': 'Installing dependencies...', + 'codexlens.installingMode': 'Installing with', 'codexlens.depsInstalled': 'Dependencies installed successfully', 'codexlens.depsInstallFailed': 'Failed to install dependencies', + + // GPU Mode Selection + 'codexlens.selectGpuMode': 'Select acceleration mode', + 'codexlens.cpuModeDesc': 'Standard CPU processing', + 'codexlens.directmlModeDesc': 'Windows GPU (NVIDIA/AMD/Intel)', + 'codexlens.cudaModeDesc': 'NVIDIA GPU (requires CUDA Toolkit)', + 'common.recommended': 'Recommended', + 'common.unavailable': 'Unavailable', 'codexlens.modelManagement': 'Model Management', 'codexlens.loadingModels': 'Loading models...', 'codexlens.downloadModel': 'Download', @@ -293,6 +305,35 @@ const i18n = { 'codexlens.modelListError': 'Failed to load models', 'codexlens.noModelsAvailable': 'No models available', + // Model Download Progress + 'codexlens.downloadingModel': 'Downloading', + 'codexlens.connectingToHuggingFace': 'Connecting to Hugging Face...', + 'codexlens.downloadTimeEstimate': 'Estimated time', + 'codexlens.manualDownloadHint': 'Manual download', + 'codexlens.downloadingModelFiles': 'Downloading model files...', + 'codexlens.downloadingWeights': 'Downloading model weights...', + 'codexlens.downloadingTokenizer': 'Downloading tokenizer...', + 'codexlens.verifyingModel': 'Verifying model...', + 'codexlens.finalizingDownload': 'Finalizing...', + 'codexlens.downloadComplete': 'Download complete!', + 'codexlens.downloadFailed': 'Download failed', + 'codexlens.manualDownloadOptions': 'Manual download options', + 'codexlens.cliDownload': 'CLI', + 'codexlens.huggingfaceDownload': 'Hugging Face', + 'codexlens.downloadCanceled': 'Download canceled', + + // Manual Download Guide + 'codexlens.manualDownloadGuide': 'Manual Download Guide', + 'codexlens.cliMethod': 'Command Line (Recommended)', + 'codexlens.cliMethodDesc': 'Run in terminal with progress display:', + 'codexlens.pythonMethod': 'Python Script', + 'codexlens.pythonMethodDesc': 'Pre-download model using Python:', + 'codexlens.hfHubMethod': 'Hugging Face Hub CLI', + 'codexlens.hfHubMethodDesc': 'Download using huggingface-cli with resume support:', + 'codexlens.modelLinks': 'Direct Model Links', + 'codexlens.cacheLocation': 'Model Storage Location', + 'common.copied': 'Copied to clipboard', + // CodexLens Indexing Progress 'codexlens.indexing': 'Indexing', 'codexlens.indexingDesc': 'Building code index for workspace', @@ -302,6 +343,43 @@ const i18n = { 'codexlens.indexSuccess': 'Index created successfully', 'codexlens.indexFailed': 'Indexing failed', + // CodexLens Install + 'codexlens.installDesc': 'Python-based code indexing engine', + 'codexlens.whatWillBeInstalled': 'What will be installed:', + 'codexlens.pythonVenv': 'Python virtual environment', + 'codexlens.pythonVenvDesc': 'Isolated Python environment', + 'codexlens.codexlensPackage': 'CodexLens package', + 'codexlens.codexlensPackageDesc': 'Code indexing and search engine', + 'codexlens.sqliteFtsDesc': 'Full-text search database', + 'codexlens.installLocation': 'Installation Location', + 'codexlens.installTime': 'First installation may take 2-3 minutes to download and setup Python packages.', + 'codexlens.startingInstall': 'Starting installation...', + 'codexlens.installing': 'Installing...', + 'codexlens.creatingVenv': 'Creating virtual environment...', + 'codexlens.installingPip': 'Installing pip packages...', + 'codexlens.installingPackage': 'Installing CodexLens package...', + 'codexlens.settingUpDeps': 'Setting up Python dependencies...', + 'codexlens.installComplete': 'Installation complete!', + 'codexlens.installSuccess': 'CodexLens installed successfully!', + 'codexlens.installNow': 'Install Now', + 'codexlens.accelerator': 'Accelerator', + + // CodexLens Uninstall + 'codexlens.uninstall': 'Uninstall', + 'codexlens.uninstallDesc': 'Remove CodexLens and all data', + 'codexlens.whatWillBeRemoved': 'What will be removed:', + 'codexlens.removeVenv': 'Virtual environment at ~/.codexlens/venv', + 'codexlens.removeData': 'All CodexLens indexed data and databases', + 'codexlens.removeConfig': 'Configuration and semantic search models', + 'codexlens.removing': 'Removing files...', + 'codexlens.uninstalling': 'Uninstalling...', + 'codexlens.removingVenv': 'Removing virtual environment...', + 'codexlens.removingData': 'Deleting indexed data...', + 'codexlens.removingConfig': 'Cleaning up configuration...', + 'codexlens.finalizing': 'Finalizing removal...', + 'codexlens.uninstallComplete': 'Uninstallation complete!', + 'codexlens.uninstallSuccess': 'CodexLens uninstalled successfully!', + // Index Manager 'index.manager': 'Index Manager', 'index.projects': 'Projects', @@ -1358,7 +1436,10 @@ const i18n = { 'common.loading': '加载中...', 'common.error': '错误', 'common.success': '成功', - + 'common.retry': '重试', + 'common.refresh': '刷新', + 'common.minutes': '分钟', + // Header 'header.project': '项目:', 'header.recentProjects': '最近项目', @@ -1612,8 +1693,17 @@ const i18n = { 'codexlens.installDeps': '安装依赖', 'codexlens.installDepsPrompt': '是否立即安装?(可能需要几分钟)\n\n点击"取消"将只创建 FTS 索引。', 'codexlens.installingDeps': '安装依赖中...', + 'codexlens.installingMode': '正在安装', 'codexlens.depsInstalled': '依赖安装成功', 'codexlens.depsInstallFailed': '依赖安装失败', + + // GPU 模式选择 + 'codexlens.selectGpuMode': '选择加速模式', + 'codexlens.cpuModeDesc': '标准 CPU 处理', + 'codexlens.directmlModeDesc': 'Windows GPU(NVIDIA/AMD/Intel)', + 'codexlens.cudaModeDesc': 'NVIDIA GPU(需要 CUDA Toolkit)', + 'common.recommended': '推荐', + 'common.unavailable': '不可用', 'codexlens.modelManagement': '模型管理', 'codexlens.loadingModels': '加载模型中...', 'codexlens.downloadModel': '下载', @@ -1628,6 +1718,35 @@ const i18n = { 'codexlens.modelListError': '加载模型列表失败', 'codexlens.noModelsAvailable': '没有可用模型', + // 模型下载进度 + 'codexlens.downloadingModel': '正在下载', + 'codexlens.connectingToHuggingFace': '正在连接 Hugging Face...', + 'codexlens.downloadTimeEstimate': '预计时间', + 'codexlens.manualDownloadHint': '手动下载', + 'codexlens.downloadingModelFiles': '正在下载模型文件...', + 'codexlens.downloadingWeights': '正在下载模型权重...', + 'codexlens.downloadingTokenizer': '正在下载分词器...', + 'codexlens.verifyingModel': '正在验证模型...', + 'codexlens.finalizingDownload': '正在完成...', + 'codexlens.downloadComplete': '下载完成!', + 'codexlens.downloadFailed': '下载失败', + 'codexlens.manualDownloadOptions': '手动下载选项', + 'codexlens.cliDownload': '命令行', + 'codexlens.huggingfaceDownload': 'Hugging Face', + 'codexlens.downloadCanceled': '下载已取消', + + // 手动下载指南 + 'codexlens.manualDownloadGuide': '手动下载指南', + 'codexlens.cliMethod': '命令行(推荐)', + 'codexlens.cliMethodDesc': '在终端运行,显示下载进度:', + 'codexlens.pythonMethod': 'Python 脚本', + 'codexlens.pythonMethodDesc': '使用 Python 预下载模型:', + 'codexlens.hfHubMethod': 'Hugging Face Hub CLI', + 'codexlens.hfHubMethodDesc': '使用 huggingface-cli 下载,支持断点续传:', + 'codexlens.modelLinks': '模型直链', + 'codexlens.cacheLocation': '模型存储位置', + 'common.copied': '已复制到剪贴板', + // CodexLens 索引进度 'codexlens.indexing': '索引中', 'codexlens.indexingDesc': '正在为工作区构建代码索引', @@ -1637,6 +1756,43 @@ const i18n = { 'codexlens.indexSuccess': '索引创建成功', 'codexlens.indexFailed': '索引失败', + // CodexLens 安装 + 'codexlens.installDesc': '基于 Python 的代码索引引擎', + 'codexlens.whatWillBeInstalled': '将安装的内容:', + 'codexlens.pythonVenv': 'Python 虚拟环境', + 'codexlens.pythonVenvDesc': '隔离的 Python 环境', + 'codexlens.codexlensPackage': 'CodexLens 包', + 'codexlens.codexlensPackageDesc': '代码索引和搜索引擎', + 'codexlens.sqliteFtsDesc': '全文搜索数据库', + 'codexlens.installLocation': '安装位置', + 'codexlens.installTime': '首次安装可能需要 2-3 分钟下载和配置 Python 包。', + 'codexlens.startingInstall': '正在启动安装...', + 'codexlens.installing': '安装中...', + 'codexlens.creatingVenv': '正在创建虚拟环境...', + 'codexlens.installingPip': '正在安装 pip 包...', + 'codexlens.installingPackage': '正在安装 CodexLens 包...', + 'codexlens.settingUpDeps': '正在配置 Python 依赖...', + 'codexlens.installComplete': '安装完成!', + 'codexlens.installSuccess': 'CodexLens 安装成功!', + 'codexlens.installNow': '立即安装', + 'codexlens.accelerator': '加速器', + + // CodexLens 卸载 + 'codexlens.uninstall': '卸载', + 'codexlens.uninstallDesc': '移除 CodexLens 及所有数据', + 'codexlens.whatWillBeRemoved': '将被移除的内容:', + 'codexlens.removeVenv': '虚拟环境 ~/.codexlens/venv', + 'codexlens.removeData': '所有 CodexLens 索引数据和数据库', + 'codexlens.removeConfig': '配置文件和语义搜索模型', + 'codexlens.removing': '正在删除文件...', + 'codexlens.uninstalling': '正在卸载...', + 'codexlens.removingVenv': '正在删除虚拟环境...', + 'codexlens.removingData': '正在删除索引数据...', + 'codexlens.removingConfig': '正在清理配置文件...', + 'codexlens.finalizing': '正在完成卸载...', + 'codexlens.uninstallComplete': '卸载完成!', + 'codexlens.uninstallSuccess': 'CodexLens 卸载成功!', + // 索引管理器 'index.manager': '索引管理器', 'index.projects': '项目数', diff --git a/ccw/src/templates/dashboard-js/views/codexlens-manager.js b/ccw/src/templates/dashboard-js/views/codexlens-manager.js index 74169108..79d994ca 100644 --- a/ccw/src/templates/dashboard-js/views/codexlens-manager.js +++ b/ccw/src/templates/dashboard-js/views/codexlens-manager.js @@ -126,10 +126,10 @@ function buildCodexLensConfigContent(config) { '' + - '' - : '') + '' + @@ -335,6 +335,26 @@ function initCodexLensConfigEvents(currentConfig) { // SEMANTIC DEPENDENCIES MANAGEMENT // ============================================================ +// Store detected GPU info +var detectedGpuInfo = null; + +/** + * Detect GPU support + */ +async function detectGpuSupport() { + try { + var response = await fetch('/api/codexlens/gpu/detect'); + var result = await response.json(); + if (result.success) { + detectedGpuInfo = result; + return result; + } + } catch (err) { + console.error('GPU detection failed:', err); + } + return { mode: 'cpu', available: ['cpu'], info: 'CPU only' }; +} + /** * Load semantic dependencies status */ @@ -343,24 +363,58 @@ async function loadSemanticDepsStatus() { if (!container) return; try { + // Detect GPU support in parallel + var gpuPromise = detectGpuSupport(); var response = await fetch('/api/codexlens/semantic/status'); var result = await response.json(); + var gpuInfo = await gpuPromise; if (result.available) { - container.innerHTML = - '
' + - '' + - '' + t('codexlens.semanticInstalled') + '' + - '(' + (result.backend || 'fastembed') + ')' + - '
'; - } else { + // Build accelerator badge + var accelerator = result.accelerator || 'CPU'; + var acceleratorIcon = 'cpu'; + var acceleratorClass = 'bg-muted text-muted-foreground'; + + if (accelerator === 'CUDA') { + acceleratorIcon = 'zap'; + acceleratorClass = 'bg-green-500/20 text-green-600'; + } else if (accelerator === 'DirectML') { + acceleratorIcon = 'gpu-card'; + acceleratorClass = 'bg-blue-500/20 text-blue-600'; + } else if (accelerator === 'ROCm') { + acceleratorIcon = 'flame'; + acceleratorClass = 'bg-red-500/20 text-red-600'; + } + container.innerHTML = '
' + + '
' + + '' + + '' + t('codexlens.semanticInstalled') + '' + + '(' + (result.backend || 'fastembed') + ')' + + '
' + + '
' + + '' + + '' + + accelerator + + '' + + (result.providers && result.providers.length > 0 + ? '' + result.providers.join(', ') + '' + : '') + + '
' + + '
'; + } else { + // Build GPU mode options + var gpuOptions = buildGpuModeSelector(gpuInfo); + + container.innerHTML = + '
' + '
' + '' + '' + t('codexlens.semanticNotInstalled') + '' + '
' + - '' + '
'; @@ -373,21 +427,120 @@ async function loadSemanticDepsStatus() { } /** - * Install semantic dependencies + * Build GPU mode selector HTML */ -async function installSemanticDeps() { +function buildGpuModeSelector(gpuInfo) { + var modes = [ + { + id: 'cpu', + label: 'CPU', + desc: t('codexlens.cpuModeDesc') || 'Standard CPU processing', + icon: 'cpu', + available: true + }, + { + id: 'directml', + label: 'DirectML', + desc: t('codexlens.directmlModeDesc') || 'Windows GPU (NVIDIA/AMD/Intel)', + icon: 'gpu-card', + available: gpuInfo.available.includes('directml'), + recommended: gpuInfo.mode === 'directml' + }, + { + id: 'cuda', + label: 'CUDA', + desc: t('codexlens.cudaModeDesc') || 'NVIDIA GPU (requires CUDA Toolkit)', + icon: 'zap', + available: gpuInfo.available.includes('cuda'), + recommended: gpuInfo.mode === 'cuda' + } + ]; + + var html = + '
' + + '
' + + '' + + (t('codexlens.selectGpuMode') || 'Select acceleration mode') + + '
' + + '
' + + ' ' + gpuInfo.info + + '
' + + '
'; + + modes.forEach(function(mode) { + var isDisabled = !mode.available; + var isRecommended = mode.recommended; + var isDefault = mode.id === gpuInfo.mode; + + html += + ''; + }); + + html += + '
' + + '
'; + + return html; +} + +/** + * Get selected GPU mode + */ +function getSelectedGpuMode() { + var selected = document.querySelector('input[name="gpuMode"]:checked'); + return selected ? selected.value : 'cpu'; +} + +/** + * Install semantic dependencies with GPU mode + */ +async function installSemanticDepsWithGpu() { var container = document.getElementById('semanticDepsStatus'); if (!container) return; + var gpuMode = getSelectedGpuMode(); + var modeLabels = { + cpu: 'CPU', + cuda: 'NVIDIA CUDA', + directml: 'DirectML' + }; + container.innerHTML = - '
' + t('codexlens.installingDeps') + '
'; + '
' + + '
' + + '
' + + '' + t('codexlens.installingDeps') + '' + + '
' + + '
' + + (t('codexlens.installingMode') || 'Installing with') + ': ' + modeLabels[gpuMode] + + '
' + + '
'; try { - var response = await fetch('/api/codexlens/semantic/install', { method: 'POST' }); + var response = await fetch('/api/codexlens/semantic/install', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ gpuMode: gpuMode }) + }); var result = await response.json(); if (result.success) { - showRefreshToast(t('codexlens.depsInstalled'), 'success'); + showRefreshToast(t('codexlens.depsInstalled') + ' (' + modeLabels[gpuMode] + ')', 'success'); await loadSemanticDepsStatus(); await loadModelList(); } else { @@ -400,10 +553,164 @@ async function installSemanticDeps() { } } +/** + * Install semantic dependencies (legacy, defaults to CPU) + */ +async function installSemanticDeps() { + await installSemanticDepsWithGpu(); +} + // ============================================================ // MODEL MANAGEMENT // ============================================================ +/** + * Build manual download guide HTML + */ +function buildManualDownloadGuide() { + var modelData = [ + { profile: 'code', name: 'jinaai/jina-embeddings-v2-base-code', size: '~150 MB' }, + { profile: 'fast', name: 'BAAI/bge-small-en-v1.5', size: '~80 MB' }, + { profile: 'balanced', name: 'mixedbread-ai/mxbai-embed-large-v1', size: '~600 MB' }, + { profile: 'multilingual', name: 'intfloat/multilingual-e5-large', size: '~1 GB' } + ]; + + var html = + '
' + + '' + + '' + + '
'; + + return html; +} + +/** + * Toggle manual download guide visibility + */ +function toggleManualDownloadGuide() { + var content = document.getElementById('manualDownloadContent'); + var chevron = document.getElementById('manualDownloadChevron'); + + if (content && chevron) { + content.classList.toggle('hidden'); + chevron.style.transform = content.classList.contains('hidden') ? '' : 'rotate(90deg)'; + } +} + +/** + * Copy text to clipboard + */ +function copyToClipboard(text) { + navigator.clipboard.writeText(text).then(function() { + showRefreshToast(t('common.copied') || 'Copied to clipboard', 'success'); + }).catch(function(err) { + console.error('Failed to copy:', err); + }); +} + /** * Load model list */ @@ -476,6 +783,10 @@ async function loadModelList() { }); html += ''; + + // Add manual download guide section + html += buildManualDownloadGuide(); + container.innerHTML = html; if (window.lucide) lucide.createIcons(); } catch (err) { @@ -485,18 +796,94 @@ async function loadModelList() { } /** - * Download model + * Download model with progress simulation and manual download info */ async function downloadModel(profile) { var modelCard = document.getElementById('model-' + profile); if (!modelCard) return; var originalHTML = modelCard.innerHTML; + + // Get model info for size estimation + var modelSizes = { + 'fast': { size: 80, time: '1-2' }, + 'code': { size: 150, time: '2-5' }, + 'multilingual': { size: 1000, time: '5-15' }, + 'balanced': { size: 600, time: '3-10' } + }; + + var modelInfo = modelSizes[profile] || { size: 100, time: '2-5' }; + + // Show detailed download UI with progress simulation modelCard.innerHTML = - '
' + - '' + t('codexlens.downloading') + '' + + '
' + + '
' + + '
' + + '' + (t('codexlens.downloadingModel') || 'Downloading') + ' ' + profile + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '' + (t('codexlens.connectingToHuggingFace') || 'Connecting to Hugging Face...') + '' + + '~' + modelInfo.size + ' MB' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + (t('codexlens.downloadTimeEstimate') || 'Estimated time') + ': ' + modelInfo.time + ' ' + (t('common.minutes') || 'minutes') + '' + + '
' + + '
' + + '' + + '' + (t('codexlens.manualDownloadHint') || 'Manual download') + ': codexlens model-download ' + profile + '' + + '
' + + '
' + + '' + '
'; + if (window.lucide) lucide.createIcons(); + + // Start progress simulation + var progressBar = document.getElementById('model-progress-' + profile); + var statusText = document.getElementById('model-status-' + profile); + var simulatedProgress = 0; + var progressInterval = null; + var downloadAborted = false; + + // Store abort controller for cancellation + window['modelDownloadAbort_' + profile] = function() { + downloadAborted = true; + if (progressInterval) clearInterval(progressInterval); + }; + + // Simulate progress based on model size + var progressStages = [ + { percent: 10, msg: t('codexlens.downloadingModelFiles') || 'Downloading model files...' }, + { percent: 30, msg: t('codexlens.downloadingWeights') || 'Downloading model weights...' }, + { percent: 60, msg: t('codexlens.downloadingTokenizer') || 'Downloading tokenizer...' }, + { percent: 80, msg: t('codexlens.verifyingModel') || 'Verifying model...' }, + { percent: 95, msg: t('codexlens.finalizingDownload') || 'Finalizing...' } + ]; + + var stageIndex = 0; + var baseInterval = Math.max(2000, modelInfo.size * 30); // Slower for larger models + + progressInterval = setInterval(function() { + if (downloadAborted) return; + + if (stageIndex < progressStages.length) { + var stage = progressStages[stageIndex]; + simulatedProgress = stage.percent; + if (progressBar) progressBar.style.width = simulatedProgress + '%'; + if (statusText) statusText.textContent = stage.msg; + stageIndex++; + } + }, baseInterval); + try { var response = await fetch('/api/codexlens/models/download', { method: 'POST', @@ -504,20 +891,99 @@ async function downloadModel(profile) { body: JSON.stringify({ profile: profile }) }); + // Clear simulation + if (progressInterval) clearInterval(progressInterval); + + if (downloadAborted) { + modelCard.innerHTML = originalHTML; + if (window.lucide) lucide.createIcons(); + return; + } + var result = await response.json(); if (result.success) { + // Show completion + if (progressBar) progressBar.style.width = '100%'; + if (statusText) statusText.textContent = t('codexlens.downloadComplete') || 'Download complete!'; + showRefreshToast(t('codexlens.modelDownloaded') + ': ' + profile, 'success'); - await loadModelList(); + + // Refresh model list after short delay + setTimeout(function() { + loadModelList(); + }, 500); } else { showRefreshToast(t('codexlens.modelDownloadFailed') + ': ' + result.error, 'error'); - modelCard.innerHTML = originalHTML; - if (window.lucide) lucide.createIcons(); + showModelDownloadError(modelCard, profile, result.error, originalHTML); } } catch (err) { + if (progressInterval) clearInterval(progressInterval); showRefreshToast(t('common.error') + ': ' + err.message, 'error'); - modelCard.innerHTML = originalHTML; - if (window.lucide) lucide.createIcons(); + showModelDownloadError(modelCard, profile, err.message, originalHTML); + } + + // Cleanup abort function + delete window['modelDownloadAbort_' + profile]; +} + +/** + * Show model download error with manual download instructions + */ +function showModelDownloadError(modelCard, profile, error, originalHTML) { + var modelNames = { + 'fast': 'BAAI/bge-small-en-v1.5', + 'code': 'jinaai/jina-embeddings-v2-base-code', + 'multilingual': 'intfloat/multilingual-e5-large', + 'balanced': 'mixedbread-ai/mxbai-embed-large-v1' + }; + + var modelName = modelNames[profile] || profile; + var hfUrl = 'https://huggingface.co/' + modelName; + + modelCard.innerHTML = + '
' + + '
' + + '' + + '
' + + '
' + (t('codexlens.downloadFailed') || 'Download failed') + '
' + + '
' + error + '
' + + '
' + + '
' + + '
' + + '
' + (t('codexlens.manualDownloadOptions') || 'Manual download options') + ':
' + + '
' + + '
' + + '1.' + + '' + (t('codexlens.cliDownload') || 'CLI') + ': codexlens model-download ' + profile + '' + + '
' + + '
' + + '2.' + + '' + (t('codexlens.huggingfaceDownload') || 'Hugging Face') + ': ' + modelName + '' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '
' + + '
'; + + if (window.lucide) lucide.createIcons(); +} + +/** + * Cancel model download + */ +function cancelModelDownload(profile) { + if (window['modelDownloadAbort_' + profile]) { + window['modelDownloadAbort_' + profile](); + showRefreshToast(t('codexlens.downloadCanceled') || 'Download canceled', 'info'); + loadModelList(); } } @@ -876,16 +1342,315 @@ async function cancelCodexLensIndexing() { /** * Install CodexLens + * Note: Uses CodexLens-specific install wizard from cli-status.js + * which calls /api/codexlens/bootstrap (Python venv), not the generic + * CLI install that uses npm install -g (NPM packages) */ -function installCodexLens() { - openCliInstallWizard('codexlens'); +function installCodexLensFromManager() { + // Use the CodexLens-specific install wizard from cli-status.js + if (typeof openCodexLensInstallWizard === 'function') { + openCodexLensInstallWizard(); + } else { + // Fallback: inline install wizard if cli-status.js not loaded + showCodexLensInstallDialog(); + } +} + +/** + * Fallback install dialog when cli-status.js is not loaded + */ +function showCodexLensInstallDialog() { + var modal = document.createElement('div'); + modal.id = 'codexlensInstallModalFallback'; + modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50'; + modal.innerHTML = + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '

' + t('codexlens.installCodexLens') + '

' + + '

' + t('codexlens.installDesc') + '

' + + '
' + + '
' + + '
' + + '
' + + '

' + t('codexlens.whatWillBeInstalled') + '

' + + '
    ' + + '
  • ' + + '' + + '' + t('codexlens.pythonVenv') + ' - ' + t('codexlens.pythonVenvDesc') + '' + + '
  • ' + + '
  • ' + + '' + + '' + t('codexlens.codexlensPackage') + ' - ' + t('codexlens.codexlensPackageDesc') + '' + + '
  • ' + + '
  • ' + + '' + + 'SQLite FTS5 - ' + t('codexlens.sqliteFtsDesc') + '' + + '
  • ' + + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '

' + t('codexlens.installLocation') + '

' + + '

~/.codexlens/venv

' + + '

' + t('codexlens.installTime') + '

' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '' + + '' + + '
' + + '
'; + + document.body.appendChild(modal); + if (window.lucide) lucide.createIcons(); +} + +function closeCodexLensInstallDialogFallback() { + var modal = document.getElementById('codexlensInstallModalFallback'); + if (modal) modal.remove(); +} + +async function startCodexLensInstallFallback() { + var progressDiv = document.getElementById('codexlensInstallProgressFallback'); + var installBtn = document.getElementById('codexlensInstallBtnFallback'); + var statusText = document.getElementById('codexlensInstallStatusFallback'); + var progressBar = document.getElementById('codexlensInstallProgressBarFallback'); + + progressDiv.classList.remove('hidden'); + installBtn.disabled = true; + installBtn.innerHTML = '' + t('codexlens.installing') + ''; + + var stages = [ + { progress: 10, text: t('codexlens.creatingVenv') }, + { progress: 30, text: t('codexlens.installingPip') }, + { progress: 50, text: t('codexlens.installingPackage') }, + { progress: 70, text: t('codexlens.settingUpDeps') }, + { progress: 90, text: t('codexlens.finalizing') } + ]; + + var currentStage = 0; + var progressInterval = setInterval(function() { + if (currentStage < stages.length) { + statusText.textContent = stages[currentStage].text; + progressBar.style.width = stages[currentStage].progress + '%'; + currentStage++; + } + }, 1500); + + try { + var response = await fetch('/api/codexlens/bootstrap', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}) + }); + + clearInterval(progressInterval); + var result = await response.json(); + + if (result.success) { + progressBar.style.width = '100%'; + statusText.textContent = t('codexlens.installComplete'); + + setTimeout(function() { + closeCodexLensInstallDialogFallback(); + showRefreshToast(t('codexlens.installSuccess'), 'success'); + // Refresh the page to update status + if (typeof loadCodexLensStatus === 'function') { + loadCodexLensStatus().then(function() { + if (typeof renderCodexLensManager === 'function') renderCodexLensManager(); + }); + } else { + location.reload(); + } + }, 1000); + } else { + statusText.textContent = t('common.error') + ': ' + result.error; + progressBar.classList.add('bg-destructive'); + installBtn.disabled = false; + installBtn.innerHTML = ' ' + t('common.retry'); + if (window.lucide) lucide.createIcons(); + } + } catch (err) { + clearInterval(progressInterval); + statusText.textContent = t('common.error') + ': ' + err.message; + progressBar.classList.add('bg-destructive'); + installBtn.disabled = false; + installBtn.innerHTML = ' ' + t('common.retry'); + if (window.lucide) lucide.createIcons(); + } } /** * Uninstall CodexLens + * Note: Uses CodexLens-specific uninstall wizard from cli-status.js + * which calls /api/codexlens/uninstall (Python venv), not the generic + * CLI uninstall that uses /api/cli/uninstall (NPM packages) */ -function uninstallCodexLens() { - openCliUninstallWizard('codexlens'); +function uninstallCodexLensFromManager() { + // Use the CodexLens-specific uninstall wizard from cli-status.js + if (typeof openCodexLensUninstallWizard === 'function') { + openCodexLensUninstallWizard(); + } else { + // Fallback: inline uninstall wizard if cli-status.js not loaded + showCodexLensUninstallDialog(); + } +} + +/** + * Fallback uninstall dialog when cli-status.js is not loaded + */ +function showCodexLensUninstallDialog() { + var modal = document.createElement('div'); + modal.id = 'codexlensUninstallModalFallback'; + modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50'; + modal.innerHTML = + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '

' + t('codexlens.uninstall') + '

' + + '

' + t('codexlens.uninstallDesc') + '

' + + '
' + + '
' + + '
' + + '
' + + '

' + t('codexlens.whatWillBeRemoved') + '

' + + '
    ' + + '
  • ' + + '' + + '' + t('codexlens.removeVenv') + '' + + '
  • ' + + '
  • ' + + '' + + '' + t('codexlens.removeData') + '' + + '
  • ' + + '
  • ' + + '' + + '' + t('codexlens.removeConfig') + '' + + '
  • ' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '' + + '' + + '
' + + '
'; + + document.body.appendChild(modal); + if (window.lucide) lucide.createIcons(); +} + +function closeCodexLensUninstallDialogFallback() { + var modal = document.getElementById('codexlensUninstallModalFallback'); + if (modal) modal.remove(); +} + +async function startCodexLensUninstallFallback() { + var progressDiv = document.getElementById('codexlensUninstallProgressFallback'); + var uninstallBtn = document.getElementById('codexlensUninstallBtnFallback'); + var statusText = document.getElementById('codexlensUninstallStatusFallback'); + var progressBar = document.getElementById('codexlensUninstallProgressBarFallback'); + + progressDiv.classList.remove('hidden'); + uninstallBtn.disabled = true; + uninstallBtn.innerHTML = '' + t('codexlens.uninstalling') + ''; + + var stages = [ + { progress: 25, text: t('codexlens.removingVenv') }, + { progress: 50, text: t('codexlens.removingData') }, + { progress: 75, text: t('codexlens.removingConfig') }, + { progress: 90, text: t('codexlens.finalizing') } + ]; + + var currentStage = 0; + var progressInterval = setInterval(function() { + if (currentStage < stages.length) { + statusText.textContent = stages[currentStage].text; + progressBar.style.width = stages[currentStage].progress + '%'; + currentStage++; + } + }, 500); + + try { + var response = await fetch('/api/codexlens/uninstall', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}) + }); + + clearInterval(progressInterval); + var result = await response.json(); + + if (result.success) { + progressBar.style.width = '100%'; + statusText.textContent = t('codexlens.uninstallComplete'); + + setTimeout(function() { + closeCodexLensUninstallDialogFallback(); + showRefreshToast(t('codexlens.uninstallSuccess'), 'success'); + // Refresh the page to update status + if (typeof loadCodexLensStatus === 'function') { + loadCodexLensStatus().then(function() { + if (typeof renderCodexLensManager === 'function') renderCodexLensManager(); + }); + } else { + location.reload(); + } + }, 1000); + } else { + statusText.textContent = t('common.error') + ': ' + result.error; + progressBar.classList.add('bg-destructive'); + uninstallBtn.disabled = false; + uninstallBtn.innerHTML = ' ' + t('common.retry'); + if (window.lucide) lucide.createIcons(); + } + } catch (err) { + clearInterval(progressInterval); + statusText.textContent = t('common.error') + ': ' + err.message; + progressBar.classList.add('bg-destructive'); + uninstallBtn.disabled = false; + uninstallBtn.innerHTML = ' ' + t('common.retry'); + if (window.lucide) lucide.createIcons(); + } } /** diff --git a/ccw/src/tools/codex-lens.ts b/ccw/src/tools/codex-lens.ts index 84d2ccbe..46cfe9ff 100644 --- a/ccw/src/tools/codex-lens.ts +++ b/ccw/src/tools/codex-lens.ts @@ -75,6 +75,8 @@ interface ReadyStatus { interface SemanticStatus { available: boolean; backend?: string; + accelerator?: string; + providers?: string[]; error?: string; } @@ -190,18 +192,39 @@ async function checkSemanticStatus(): Promise { return { available: false, error: 'CodexLens not installed' }; } - // Check semantic module availability + // Check semantic module availability and accelerator info return new Promise((resolve) => { const checkCode = ` import sys +import json try: from codexlens.semantic import SEMANTIC_AVAILABLE, SEMANTIC_BACKEND - if SEMANTIC_AVAILABLE: - print(f"available:{SEMANTIC_BACKEND}") - else: - print("unavailable") + result = {"available": SEMANTIC_AVAILABLE, "backend": SEMANTIC_BACKEND if SEMANTIC_AVAILABLE else None} + + # Get ONNX providers for accelerator info + try: + import onnxruntime + providers = onnxruntime.get_available_providers() + result["providers"] = providers + + # Determine accelerator type + if "CUDAExecutionProvider" in providers or "TensorrtExecutionProvider" in providers: + result["accelerator"] = "CUDA" + elif "DmlExecutionProvider" in providers: + result["accelerator"] = "DirectML" + elif "CoreMLExecutionProvider" in providers: + result["accelerator"] = "CoreML" + elif "ROCMExecutionProvider" in providers: + result["accelerator"] = "ROCm" + else: + result["accelerator"] = "CPU" + except: + result["providers"] = [] + result["accelerator"] = "CPU" + + print(json.dumps(result)) except Exception as e: - print(f"error:{e}") + print(json.dumps({"available": False, "error": str(e)})) `; const child = spawn(VENV_PYTHON, ['-c', checkCode], { stdio: ['ignore', 'pipe', 'pipe'], @@ -220,12 +243,16 @@ except Exception as e: child.on('close', (code) => { const output = stdout.trim(); - if (output.startsWith('available:')) { - const backend = output.split(':')[1]; - resolve({ available: true, backend }); - } else if (output === 'unavailable') { - resolve({ available: false, error: 'Semantic dependencies not installed' }); - } else { + try { + const result = JSON.parse(output); + resolve({ + available: result.available || false, + backend: result.backend, + accelerator: result.accelerator || 'CPU', + providers: result.providers || [], + error: result.error + }); + } catch { resolve({ available: false, error: output || stderr || 'Unknown error' }); } }); @@ -237,10 +264,66 @@ except Exception as e: } /** - * Install semantic search dependencies (fastembed, ONNX-based, ~200MB) + * GPU acceleration mode for semantic search + */ +type GpuMode = 'cpu' | 'cuda' | 'directml'; + +/** + * Detect available GPU acceleration + * @returns Detected GPU mode and info + */ +async function detectGpuSupport(): Promise<{ mode: GpuMode; available: GpuMode[]; info: string }> { + const available: GpuMode[] = ['cpu']; + let detectedInfo = 'CPU only'; + + // Check for NVIDIA GPU (CUDA) + try { + if (process.platform === 'win32') { + execSync('nvidia-smi', { stdio: 'pipe' }); + available.push('cuda'); + detectedInfo = 'NVIDIA GPU detected (CUDA available)'; + } else { + execSync('which nvidia-smi', { stdio: 'pipe' }); + available.push('cuda'); + detectedInfo = 'NVIDIA GPU detected (CUDA available)'; + } + } catch { + // NVIDIA not available + } + + // On Windows, DirectML is always available if DirectX 12 is supported + if (process.platform === 'win32') { + try { + // Check for DirectX 12 support via dxdiag or registry + // DirectML works on most modern Windows 10/11 systems + available.push('directml'); + if (available.includes('cuda')) { + detectedInfo = 'NVIDIA GPU detected (CUDA & DirectML available)'; + } else { + detectedInfo = 'DirectML available (Windows GPU acceleration)'; + } + } catch { + // DirectML check failed + } + } + + // Recommend best available mode + let recommendedMode: GpuMode = 'cpu'; + if (process.platform === 'win32' && available.includes('directml')) { + recommendedMode = 'directml'; // DirectML is easier on Windows (no CUDA toolkit needed) + } else if (available.includes('cuda')) { + recommendedMode = 'cuda'; + } + + return { mode: recommendedMode, available, info: detectedInfo }; +} + +/** + * Install semantic search dependencies with optional GPU acceleration + * @param gpuMode - GPU acceleration mode: 'cpu', 'cuda', or 'directml' * @returns Bootstrap result */ -async function installSemantic(): Promise { +async function installSemantic(gpuMode: GpuMode = 'cpu'): Promise { // First ensure CodexLens is installed const venvStatus = await checkVenvStatus(); if (!venvStatus.ready) { @@ -252,42 +335,106 @@ async function installSemantic(): Promise { ? join(CODEXLENS_VENV, 'Scripts', 'pip.exe') : join(CODEXLENS_VENV, 'bin', 'pip'); - return new Promise((resolve) => { - console.log('[CodexLens] Installing semantic search dependencies (fastembed)...'); - console.log('[CodexLens] Using ONNX-based fastembed backend (~200MB)'); + // IMPORTANT: Uninstall all onnxruntime variants first to prevent conflicts + // Having multiple onnxruntime packages causes provider detection issues + const onnxVariants = ['onnxruntime', 'onnxruntime-gpu', 'onnxruntime-directml']; + console.log(`[CodexLens] Cleaning up existing ONNX Runtime packages...`); - const child = spawn(pipPath, ['install', 'numpy>=1.24', 'fastembed>=0.2'], { + for (const pkg of onnxVariants) { + try { + execSync(`"${pipPath}" uninstall ${pkg} -y`, { stdio: 'pipe' }); + console.log(`[CodexLens] Removed ${pkg}`); + } catch { + // Package not installed, ignore + } + } + + // Build package list based on GPU mode + const packages = ['numpy>=1.24', 'fastembed>=0.5', 'hnswlib>=0.8.0']; + + let modeDescription = 'CPU (ONNX Runtime)'; + let onnxPackage = 'onnxruntime>=1.18.0'; // Default CPU + + if (gpuMode === 'cuda') { + onnxPackage = 'onnxruntime-gpu>=1.18.0'; + modeDescription = 'NVIDIA CUDA GPU acceleration'; + } else if (gpuMode === 'directml') { + onnxPackage = 'onnxruntime-directml>=1.18.0'; + modeDescription = 'Windows DirectML GPU acceleration'; + } + + return new Promise((resolve) => { + console.log(`[CodexLens] Installing semantic search dependencies...`); + console.log(`[CodexLens] Mode: ${modeDescription}`); + console.log(`[CodexLens] ONNX Runtime: ${onnxPackage}`); + console.log(`[CodexLens] Packages: ${packages.join(', ')}`); + + // Install ONNX Runtime first with force-reinstall to ensure clean state + const installOnnx = spawn(pipPath, ['install', '--force-reinstall', onnxPackage], { stdio: ['ignore', 'pipe', 'pipe'], - timeout: 600000, // 10 minutes for potential model download + timeout: 600000, // 10 minutes for GPU packages }); - let stdout = ''; - let stderr = ''; + let onnxStdout = ''; + let onnxStderr = ''; - child.stdout.on('data', (data) => { - stdout += data.toString(); - // Log progress + installOnnx.stdout.on('data', (data) => { + onnxStdout += data.toString(); const line = data.toString().trim(); - if (line.includes('Downloading') || line.includes('Installing') || line.includes('Collecting')) { + if (line.includes('Downloading') || line.includes('Installing')) { console.log(`[CodexLens] ${line}`); } }); - child.stderr.on('data', (data) => { - stderr += data.toString(); + installOnnx.stderr.on('data', (data) => { + onnxStderr += data.toString(); }); - child.on('close', (code) => { - if (code === 0) { - console.log('[CodexLens] Semantic dependencies installed successfully'); - resolve({ success: true }); - } else { - resolve({ success: false, error: `Installation failed: ${stderr || stdout}` }); + installOnnx.on('close', (onnxCode) => { + if (onnxCode !== 0) { + resolve({ success: false, error: `Failed to install ${onnxPackage}: ${onnxStderr || onnxStdout}` }); + return; } + + console.log(`[CodexLens] ${onnxPackage} installed successfully`); + + // Now install remaining packages + const child = spawn(pipPath, ['install', ...packages], { + stdio: ['ignore', 'pipe', 'pipe'], + timeout: 600000, + }); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (data) => { + stdout += data.toString(); + const line = data.toString().trim(); + if (line.includes('Downloading') || line.includes('Installing') || line.includes('Collecting')) { + console.log(`[CodexLens] ${line}`); + } + }); + + child.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + child.on('close', (code) => { + if (code === 0) { + console.log(`[CodexLens] Semantic dependencies installed successfully (${gpuMode} mode)`); + resolve({ success: true, message: `Installed with ${modeDescription}` }); + } else { + resolve({ success: false, error: `Installation failed: ${stderr || stdout}` }); + } + }); + + child.on('error', (err) => { + resolve({ success: false, error: `Failed to run pip: ${err.message}` }); + }); }); - child.on('error', (err) => { - resolve({ success: false, error: `Failed to run pip: ${err.message}` }); + installOnnx.on('error', (err) => { + resolve({ success: false, error: `Failed to install ONNX Runtime: ${err.message}` }); }); }); } @@ -1126,7 +1273,8 @@ function isIndexingInProgress(): boolean { export type { ProgressInfo, ExecuteOptions }; // Export for direct usage -export { ensureReady, executeCodexLens, checkVenvStatus, bootstrapVenv, checkSemanticStatus, installSemantic, uninstallCodexLens, cancelIndexing, isIndexingInProgress }; +export { ensureReady, executeCodexLens, checkVenvStatus, bootstrapVenv, checkSemanticStatus, installSemantic, detectGpuSupport, uninstallCodexLens, cancelIndexing, isIndexingInProgress }; +export type { GpuMode }; // Backward-compatible export for tests export const codexLensTool = { diff --git a/codex-lens/src/codex_lens.egg-info/PKG-INFO b/codex-lens/src/codex_lens.egg-info/PKG-INFO index 8f62e446..d11786d3 100644 --- a/codex-lens/src/codex_lens.egg-info/PKG-INFO +++ b/codex-lens/src/codex_lens.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: codex-lens -Version: 0.1.0 +Version: 0.2.0 Summary: CodexLens multi-modal code analysis platform Author: CodexLens contributors License: MIT @@ -17,7 +17,18 @@ Requires-Dist: tree-sitter-typescript>=0.23 Requires-Dist: pathspec>=0.11 Provides-Extra: semantic Requires-Dist: numpy>=1.24; extra == "semantic" -Requires-Dist: fastembed>=0.2; extra == "semantic" +Requires-Dist: fastembed>=0.5; extra == "semantic" +Requires-Dist: hnswlib>=0.8.0; extra == "semantic" +Provides-Extra: semantic-gpu +Requires-Dist: numpy>=1.24; extra == "semantic-gpu" +Requires-Dist: fastembed>=0.5; extra == "semantic-gpu" +Requires-Dist: hnswlib>=0.8.0; extra == "semantic-gpu" +Requires-Dist: onnxruntime-gpu>=1.18.0; extra == "semantic-gpu" +Provides-Extra: semantic-directml +Requires-Dist: numpy>=1.24; extra == "semantic-directml" +Requires-Dist: fastembed>=0.5; extra == "semantic-directml" +Requires-Dist: hnswlib>=0.8.0; extra == "semantic-directml" +Requires-Dist: onnxruntime-directml>=1.18.0; extra == "semantic-directml" Provides-Extra: encoding Requires-Dist: chardet>=5.0; extra == "encoding" Provides-Extra: full diff --git a/codex-lens/src/codex_lens.egg-info/SOURCES.txt b/codex-lens/src/codex_lens.egg-info/SOURCES.txt index ff8cce11..a14bfc8f 100644 --- a/codex-lens/src/codex_lens.egg-info/SOURCES.txt +++ b/codex-lens/src/codex_lens.egg-info/SOURCES.txt @@ -11,8 +11,11 @@ src/codexlens/entities.py src/codexlens/errors.py src/codexlens/cli/__init__.py src/codexlens/cli/commands.py +src/codexlens/cli/embedding_manager.py src/codexlens/cli/model_manager.py src/codexlens/cli/output.py +src/codexlens/indexing/__init__.py +src/codexlens/indexing/symbol_extractor.py src/codexlens/parsers/__init__.py src/codexlens/parsers/encoding.py src/codexlens/parsers/factory.py @@ -20,15 +23,16 @@ src/codexlens/parsers/tokenizer.py src/codexlens/parsers/treesitter_parser.py src/codexlens/search/__init__.py src/codexlens/search/chain_search.py +src/codexlens/search/enrichment.py src/codexlens/search/hybrid_search.py src/codexlens/search/query_parser.py src/codexlens/search/ranking.py src/codexlens/semantic/__init__.py +src/codexlens/semantic/ann_index.py src/codexlens/semantic/chunker.py src/codexlens/semantic/code_extractor.py src/codexlens/semantic/embedder.py -src/codexlens/semantic/graph_analyzer.py -src/codexlens/semantic/llm_enhancer.py +src/codexlens/semantic/gpu_support.py src/codexlens/semantic/vector_store.py src/codexlens/storage/__init__.py src/codexlens/storage/dir_index.py @@ -42,38 +46,38 @@ src/codexlens/storage/sqlite_utils.py src/codexlens/storage/migrations/__init__.py src/codexlens/storage/migrations/migration_001_normalize_keywords.py src/codexlens/storage/migrations/migration_002_add_token_metadata.py -src/codexlens/storage/migrations/migration_003_code_relationships.py src/codexlens/storage/migrations/migration_004_dual_fts.py src/codexlens/storage/migrations/migration_005_cleanup_unused_fields.py -tests/test_chain_search_engine.py +tests/test_ann_index.py tests/test_cli_hybrid_search.py tests/test_cli_output.py tests/test_code_extractor.py tests/test_config.py tests/test_dual_fts.py tests/test_encoding.py +tests/test_enrichment.py tests/test_entities.py tests/test_errors.py tests/test_file_cache.py -tests/test_graph_analyzer.py -tests/test_graph_cli.py -tests/test_graph_storage.py tests/test_hybrid_chunker.py tests/test_hybrid_search_e2e.py tests/test_incremental_indexing.py -tests/test_llm_enhancer.py tests/test_parser_integration.py tests/test_parsers.py tests/test_performance_optimizations.py +tests/test_pure_vector_search.py tests/test_query_parser.py +tests/test_result_grouping.py tests/test_rrf_fusion.py tests/test_schema_cleanup_migration.py +tests/test_search_comparison.py tests/test_search_comprehensive.py tests/test_search_full_coverage.py tests/test_search_performance.py tests/test_semantic.py tests/test_semantic_search.py tests/test_storage.py +tests/test_symbol_extractor.py tests/test_token_chunking.py tests/test_token_storage.py tests/test_tokenizer.py diff --git a/codex-lens/src/codex_lens.egg-info/requires.txt b/codex-lens/src/codex_lens.egg-info/requires.txt index 4e8e9412..fa253403 100644 --- a/codex-lens/src/codex_lens.egg-info/requires.txt +++ b/codex-lens/src/codex_lens.egg-info/requires.txt @@ -15,4 +15,17 @@ tiktoken>=0.5.0 [semantic] numpy>=1.24 -fastembed>=0.2 +fastembed>=0.5 +hnswlib>=0.8.0 + +[semantic-directml] +numpy>=1.24 +fastembed>=0.5 +hnswlib>=0.8.0 +onnxruntime-directml>=1.18.0 + +[semantic-gpu] +numpy>=1.24 +fastembed>=0.5 +hnswlib>=0.8.0 +onnxruntime-gpu>=1.18.0