mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
feat: Enhance LiteLLM integration and CLI management
- Added token estimation and batching functionality in LiteLLMEmbedder to handle large text inputs efficiently. - Updated embed method to support max_tokens_per_batch parameter for better API call management. - Introduced new API routes for managing custom CLI endpoints, including GET, POST, PUT, and DELETE methods. - Enhanced CLI history component to support source directory context for native session content. - Improved error handling and logging in various components for better debugging and user feedback. - Added internationalization support for new API endpoint features in the i18n module. - Updated CodexLens CLI commands to allow for concurrent API calls with a max_workers option. - Enhanced embedding manager to track model information and handle embeddings generation more robustly. - Added entry points for CLI commands in the package configuration.
This commit is contained in:
@@ -38,7 +38,9 @@ import {
|
||||
saveClaudeCliTools,
|
||||
updateClaudeToolEnabled,
|
||||
updateClaudeCacheSettings,
|
||||
getClaudeCliToolsInfo
|
||||
getClaudeCliToolsInfo,
|
||||
addClaudeCustomEndpoint,
|
||||
removeClaudeCustomEndpoint
|
||||
} from '../../tools/claude-cli-tools.js';
|
||||
|
||||
export interface RouteContext {
|
||||
@@ -211,6 +213,93 @@ export async function handleCliRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
// API: Get all custom endpoints
|
||||
if (pathname === '/api/cli/endpoints' && req.method === 'GET') {
|
||||
try {
|
||||
const config = loadClaudeCliTools(initialPath);
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ endpoints: config.customEndpoints || [] }));
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: (err as Error).message }));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: Add/Update custom endpoint
|
||||
if (pathname === '/api/cli/endpoints' && req.method === 'POST') {
|
||||
handlePostRequest(req, res, async (body: unknown) => {
|
||||
try {
|
||||
const { id, name, enabled } = body as { id: string; name: string; enabled: boolean };
|
||||
if (!id || !name) {
|
||||
return { error: 'id and name are required', status: 400 };
|
||||
}
|
||||
const config = addClaudeCustomEndpoint(initialPath, { id, name, enabled: enabled !== false });
|
||||
|
||||
broadcastToClients({
|
||||
type: 'CLI_ENDPOINT_UPDATED',
|
||||
payload: { endpoint: { id, name, enabled }, timestamp: new Date().toISOString() }
|
||||
});
|
||||
|
||||
return { success: true, endpoints: config.customEndpoints };
|
||||
} catch (err) {
|
||||
return { error: (err as Error).message, status: 500 };
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: Update custom endpoint enabled status
|
||||
if (pathname.match(/^\/api\/cli\/endpoints\/[^/]+$/) && req.method === 'PUT') {
|
||||
const endpointId = pathname.split('/').pop() || '';
|
||||
handlePostRequest(req, res, async (body: unknown) => {
|
||||
try {
|
||||
const { enabled, name } = body as { enabled?: boolean; name?: string };
|
||||
const config = loadClaudeCliTools(initialPath);
|
||||
const endpoint = config.customEndpoints.find(e => e.id === endpointId);
|
||||
|
||||
if (!endpoint) {
|
||||
return { error: 'Endpoint not found', status: 404 };
|
||||
}
|
||||
|
||||
if (typeof enabled === 'boolean') endpoint.enabled = enabled;
|
||||
if (name) endpoint.name = name;
|
||||
|
||||
saveClaudeCliTools(initialPath, config);
|
||||
|
||||
broadcastToClients({
|
||||
type: 'CLI_ENDPOINT_UPDATED',
|
||||
payload: { endpoint, timestamp: new Date().toISOString() }
|
||||
});
|
||||
|
||||
return { success: true, endpoint };
|
||||
} catch (err) {
|
||||
return { error: (err as Error).message, status: 500 };
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: Delete custom endpoint
|
||||
if (pathname.match(/^\/api\/cli\/endpoints\/[^/]+$/) && req.method === 'DELETE') {
|
||||
const endpointId = pathname.split('/').pop() || '';
|
||||
try {
|
||||
const config = removeClaudeCustomEndpoint(initialPath, endpointId);
|
||||
|
||||
broadcastToClients({
|
||||
type: 'CLI_ENDPOINT_DELETED',
|
||||
payload: { endpointId, timestamp: new Date().toISOString() }
|
||||
});
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ success: true, endpoints: config.customEndpoints }));
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: (err as Error).message }));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: CLI Execution History
|
||||
if (pathname === '/api/cli/history') {
|
||||
const projectPath = url.searchParams.get('path') || initialPath;
|
||||
|
||||
@@ -529,27 +529,38 @@ export async function handleLiteLLMApiRoutes(ctx: RouteContext): Promise<boolean
|
||||
// GET /api/litellm-api/ccw-litellm/status - Check ccw-litellm installation status
|
||||
if (pathname === '/api/litellm-api/ccw-litellm/status' && req.method === 'GET') {
|
||||
try {
|
||||
const { spawn } = await import('child_process');
|
||||
const result = await new Promise<{ installed: boolean; version?: string }>((resolve) => {
|
||||
const proc = spawn('python', ['-c', 'import ccw_litellm; print(ccw_litellm.__version__ if hasattr(ccw_litellm, "__version__") else "installed")'], {
|
||||
shell: true,
|
||||
timeout: 10000
|
||||
});
|
||||
const { execSync } = await import('child_process');
|
||||
|
||||
let output = '';
|
||||
proc.stdout?.on('data', (data) => { output += data.toString(); });
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve({ installed: true, version: output.trim() || 'unknown' });
|
||||
} else {
|
||||
resolve({ installed: false });
|
||||
// Try multiple Python executables
|
||||
const pythonExecutables = ['python', 'python3', 'py'];
|
||||
// Use single quotes inside Python code for Windows compatibility
|
||||
const pythonCode = "import ccw_litellm; print(getattr(ccw_litellm, '__version__', 'installed'))";
|
||||
|
||||
let installed = false;
|
||||
let version = '';
|
||||
let lastError = '';
|
||||
|
||||
for (const pythonExe of pythonExecutables) {
|
||||
try {
|
||||
const output = execSync(`${pythonExe} -c "${pythonCode}"`, {
|
||||
encoding: 'utf-8',
|
||||
timeout: 10000,
|
||||
windowsHide: true
|
||||
});
|
||||
version = output.trim();
|
||||
if (version) {
|
||||
installed = true;
|
||||
console.log(`[ccw-litellm status] Found with ${pythonExe}: ${version}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
proc.on('error', () => resolve({ installed: false }));
|
||||
});
|
||||
} catch (err) {
|
||||
lastError = (err as Error).message;
|
||||
console.log(`[ccw-litellm status] ${pythonExe} failed:`, lastError.substring(0, 100));
|
||||
}
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(result));
|
||||
res.end(JSON.stringify(installed ? { installed: true, version } : { installed: false, error: lastError }));
|
||||
} catch (err) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ installed: false, error: (err as Error).message }));
|
||||
|
||||
@@ -33,9 +33,13 @@ async function loadCliHistory(options = {}) {
|
||||
}
|
||||
|
||||
// Load native session content for a specific execution
|
||||
async function loadNativeSessionContent(executionId) {
|
||||
async function loadNativeSessionContent(executionId, sourceDir) {
|
||||
try {
|
||||
const url = `/api/cli/native-session?path=${encodeURIComponent(projectPath)}&id=${encodeURIComponent(executionId)}`;
|
||||
// If sourceDir provided, use it to build the correct path
|
||||
const basePath = sourceDir && sourceDir !== '.'
|
||||
? projectPath + '/' + sourceDir
|
||||
: projectPath;
|
||||
const url = `/api/cli/native-session?path=${encodeURIComponent(basePath)}&id=${encodeURIComponent(executionId)}`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) return null;
|
||||
return await response.json();
|
||||
@@ -133,9 +137,12 @@ function renderCliHistory() {
|
||||
</span>`
|
||||
: '';
|
||||
|
||||
// Escape sourceDir for use in onclick
|
||||
const sourceDirEscaped = exec.sourceDir ? exec.sourceDir.replace(/'/g, "\\'") : '';
|
||||
|
||||
return `
|
||||
<div class="cli-history-item ${hasNative ? 'has-native' : ''}">
|
||||
<div class="cli-history-item-content" onclick="showExecutionDetail('${exec.id}')">
|
||||
<div class="cli-history-item-content" onclick="showExecutionDetail('${exec.id}', '${sourceDirEscaped}')">
|
||||
<div class="cli-history-item-header">
|
||||
<span class="cli-tool-tag cli-tool-${exec.tool}">${exec.tool.toUpperCase()}</span>
|
||||
<span class="cli-mode-tag">${exec.mode || 'analysis'}</span>
|
||||
@@ -154,14 +161,14 @@ function renderCliHistory() {
|
||||
</div>
|
||||
<div class="cli-history-actions">
|
||||
${hasNative ? `
|
||||
<button class="btn-icon" onclick="event.stopPropagation(); showNativeSessionDetail('${exec.id}')" title="View Native Session">
|
||||
<button class="btn-icon" onclick="event.stopPropagation(); showNativeSessionDetail('${exec.id}', '${sourceDirEscaped}')" title="View Native Session">
|
||||
<i data-lucide="file-json" class="w-3.5 h-3.5"></i>
|
||||
</button>
|
||||
` : ''}
|
||||
<button class="btn-icon" onclick="event.stopPropagation(); showExecutionDetail('${exec.id}')" title="View Details">
|
||||
<button class="btn-icon" onclick="event.stopPropagation(); showExecutionDetail('${exec.id}', '${sourceDirEscaped}')" title="View Details">
|
||||
<i data-lucide="eye" class="w-3.5 h-3.5"></i>
|
||||
</button>
|
||||
<button class="btn-icon btn-danger" onclick="event.stopPropagation(); confirmDeleteExecution('${exec.id}')" title="Delete">
|
||||
<button class="btn-icon btn-danger" onclick="event.stopPropagation(); confirmDeleteExecution('${exec.id}', '${sourceDirEscaped}')" title="Delete">
|
||||
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -650,9 +657,9 @@ async function copyConcatenatedPrompt(executionId) {
|
||||
/**
|
||||
* Show native session detail modal with full conversation content
|
||||
*/
|
||||
async function showNativeSessionDetail(executionId) {
|
||||
async function showNativeSessionDetail(executionId, sourceDir) {
|
||||
// Load native session content
|
||||
const nativeSession = await loadNativeSessionContent(executionId);
|
||||
const nativeSession = await loadNativeSessionContent(executionId, sourceDir);
|
||||
|
||||
if (!nativeSession) {
|
||||
showRefreshToast('Native session not found', 'error');
|
||||
|
||||
@@ -228,6 +228,11 @@ const i18n = {
|
||||
'cli.codexLensDescFull': 'Full-text code search engine',
|
||||
'cli.semanticDesc': 'AI-powered code understanding',
|
||||
'cli.semanticDescFull': 'Natural language code search',
|
||||
'cli.apiEndpoints': 'API Endpoints',
|
||||
'cli.configured': 'configured',
|
||||
'cli.addToCli': 'Add to CLI',
|
||||
'cli.enabled': 'Enabled',
|
||||
'cli.disabled': 'Disabled',
|
||||
|
||||
// CodexLens Configuration
|
||||
'codexlens.config': 'CodexLens Configuration',
|
||||
@@ -378,6 +383,8 @@ const i18n = {
|
||||
'codexlens.indexComplete': 'Index complete',
|
||||
'codexlens.indexSuccess': 'Index created successfully',
|
||||
'codexlens.indexFailed': 'Indexing failed',
|
||||
'codexlens.embeddingsFailed': 'Embeddings generation failed',
|
||||
'codexlens.ftsSuccessEmbeddingsFailed': 'FTS index created, but embeddings failed',
|
||||
|
||||
// CodexLens Install
|
||||
'codexlens.installDesc': 'Python-based code indexing engine',
|
||||
@@ -1880,6 +1887,11 @@ const i18n = {
|
||||
'cli.codexLensDescFull': '全文代码搜索引擎',
|
||||
'cli.semanticDesc': 'AI 驱动的代码理解',
|
||||
'cli.semanticDescFull': '自然语言代码搜索',
|
||||
'cli.apiEndpoints': 'API 端点',
|
||||
'cli.configured': '已配置',
|
||||
'cli.addToCli': '添加到 CLI',
|
||||
'cli.enabled': '已启用',
|
||||
'cli.disabled': '已禁用',
|
||||
|
||||
// CodexLens 配置
|
||||
'codexlens.config': 'CodexLens 配置',
|
||||
@@ -2031,6 +2043,8 @@ const i18n = {
|
||||
'codexlens.indexComplete': '索引完成',
|
||||
'codexlens.indexSuccess': '索引创建成功',
|
||||
'codexlens.indexFailed': '索引失败',
|
||||
'codexlens.embeddingsFailed': '嵌入生成失败',
|
||||
'codexlens.ftsSuccessEmbeddingsFailed': 'FTS 索引已创建,但嵌入生成失败',
|
||||
|
||||
// CodexLens 安装
|
||||
'codexlens.installDesc': '基于 Python 的代码索引引擎',
|
||||
|
||||
@@ -2739,8 +2739,11 @@ function toggleKeyVisibility(btn) {
|
||||
*/
|
||||
async function checkCcwLitellmStatus() {
|
||||
try {
|
||||
console.log('[API Settings] Checking ccw-litellm status...');
|
||||
var response = await fetch('/api/litellm-api/ccw-litellm/status');
|
||||
console.log('[API Settings] Status response:', response.status);
|
||||
var status = await response.json();
|
||||
console.log('[API Settings] ccw-litellm status:', status);
|
||||
window.ccwLitellmStatus = status;
|
||||
return status;
|
||||
} catch (e) {
|
||||
|
||||
@@ -59,6 +59,91 @@ async function loadCcwEndpointTools() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== LiteLLM API Endpoints ==========
|
||||
var litellmApiEndpoints = [];
|
||||
var cliCustomEndpoints = [];
|
||||
|
||||
async function loadLitellmApiEndpoints() {
|
||||
try {
|
||||
var response = await fetch('/api/litellm-api/config');
|
||||
if (!response.ok) throw new Error('Failed to load LiteLLM endpoints');
|
||||
var data = await response.json();
|
||||
litellmApiEndpoints = data.endpoints || [];
|
||||
window.litellmApiConfig = data;
|
||||
return litellmApiEndpoints;
|
||||
} catch (err) {
|
||||
console.error('Failed to load LiteLLM endpoints:', err);
|
||||
litellmApiEndpoints = [];
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCliCustomEndpoints() {
|
||||
try {
|
||||
var response = await fetch('/api/cli/endpoints');
|
||||
if (!response.ok) throw new Error('Failed to load CLI custom endpoints');
|
||||
var data = await response.json();
|
||||
cliCustomEndpoints = data.endpoints || [];
|
||||
return cliCustomEndpoints;
|
||||
} catch (err) {
|
||||
console.error('Failed to load CLI custom endpoints:', err);
|
||||
cliCustomEndpoints = [];
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleEndpointEnabled(endpointId, enabled) {
|
||||
try {
|
||||
var response = await fetch('/api/cli/endpoints/' + endpointId, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ enabled: enabled })
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to update endpoint');
|
||||
var data = await response.json();
|
||||
if (data.success) {
|
||||
// Update local state
|
||||
var idx = cliCustomEndpoints.findIndex(function(e) { return e.id === endpointId; });
|
||||
if (idx >= 0) {
|
||||
cliCustomEndpoints[idx].enabled = enabled;
|
||||
}
|
||||
showRefreshToast((enabled ? 'Enabled' : 'Disabled') + ' endpoint: ' + endpointId, 'success');
|
||||
}
|
||||
return data;
|
||||
} catch (err) {
|
||||
showRefreshToast('Failed to update endpoint: ' + err.message, 'error');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function syncEndpointToCliTools(endpoint) {
|
||||
try {
|
||||
var response = await fetch('/api/cli/endpoints', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: endpoint.id,
|
||||
name: endpoint.name,
|
||||
enabled: true
|
||||
})
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to sync endpoint');
|
||||
var data = await response.json();
|
||||
if (data.success) {
|
||||
cliCustomEndpoints = data.endpoints;
|
||||
showRefreshToast('Endpoint synced to CLI tools: ' + endpoint.id, 'success');
|
||||
renderToolsSection();
|
||||
}
|
||||
return data;
|
||||
} catch (err) {
|
||||
showRefreshToast('Failed to sync endpoint: ' + err.message, 'error');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
window.toggleEndpointEnabled = toggleEndpointEnabled;
|
||||
window.syncEndpointToCliTools = syncEndpointToCliTools;
|
||||
|
||||
// ========== CLI Tool Configuration ==========
|
||||
async function loadCliToolConfig() {
|
||||
try {
|
||||
@@ -322,7 +407,9 @@ async function renderCliManager() {
|
||||
loadCliToolStatus(),
|
||||
loadCodexLensStatus(),
|
||||
loadCcwInstallations(),
|
||||
loadCcwEndpointTools()
|
||||
loadCcwEndpointTools(),
|
||||
loadLitellmApiEndpoints(),
|
||||
loadCliCustomEndpoints()
|
||||
]);
|
||||
|
||||
container.innerHTML = '<div class="status-manager">' +
|
||||
@@ -487,6 +574,51 @@ function renderToolsSection() {
|
||||
'</div>';
|
||||
}
|
||||
|
||||
// API Endpoints section
|
||||
var apiEndpointsHtml = '';
|
||||
if (litellmApiEndpoints.length > 0) {
|
||||
var endpointItems = litellmApiEndpoints.map(function(endpoint) {
|
||||
// Check if endpoint is synced to CLI tools
|
||||
var cliEndpoint = cliCustomEndpoints.find(function(e) { return e.id === endpoint.id; });
|
||||
var isSynced = !!cliEndpoint;
|
||||
var isEnabled = cliEndpoint ? cliEndpoint.enabled : false;
|
||||
|
||||
// Find provider info
|
||||
var provider = (window.litellmApiConfig?.providers || []).find(function(p) { return p.id === endpoint.providerId; });
|
||||
var providerName = provider ? provider.name : endpoint.providerId;
|
||||
|
||||
return '<div class="tool-item ' + (isSynced && isEnabled ? 'available' : 'unavailable') + '">' +
|
||||
'<div class="tool-item-left">' +
|
||||
'<span class="tool-status-dot ' + (isSynced && isEnabled ? 'status-available' : 'status-unavailable') + '"></span>' +
|
||||
'<div class="tool-item-info">' +
|
||||
'<div class="tool-item-name">' + endpoint.id + ' <span class="tool-type-badge">API</span></div>' +
|
||||
'<div class="tool-item-desc">' + endpoint.model + ' (' + providerName + ')</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="tool-item-right">' +
|
||||
(isSynced
|
||||
? '<label class="toggle-switch" onclick="event.stopPropagation()">' +
|
||||
'<input type="checkbox" ' + (isEnabled ? 'checked' : '') + ' onchange="toggleEndpointEnabled(\'' + endpoint.id + '\', this.checked); renderToolsSection();">' +
|
||||
'<span class="toggle-slider"></span>' +
|
||||
'</label>'
|
||||
: '<button class="btn-sm btn-primary" onclick="event.stopPropagation(); syncEndpointToCliTools({id: \'' + endpoint.id + '\', name: \'' + endpoint.name + '\'})">' +
|
||||
'<i data-lucide="plus" class="w-3 h-3"></i> ' + (t('cli.addToCli') || 'Add to CLI') +
|
||||
'</button>') +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
|
||||
apiEndpointsHtml = '<div class="tools-subsection" style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border);">' +
|
||||
'<div class="section-header-left" style="margin-bottom: 0.5rem;">' +
|
||||
'<h4 style="font-size: 0.875rem; font-weight: 600; display: flex; align-items: center; gap: 0.5rem;">' +
|
||||
'<i data-lucide="cloud" class="w-4 h-4"></i> ' + (t('cli.apiEndpoints') || 'API Endpoints') +
|
||||
'</h4>' +
|
||||
'<span class="section-count">' + litellmApiEndpoints.length + ' ' + (t('cli.configured') || 'configured') + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="tools-list">' + endpointItems + '</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
container.innerHTML = '<div class="section-header">' +
|
||||
'<div class="section-header-left">' +
|
||||
'<h3><i data-lucide="terminal" class="w-4 h-4"></i> ' + t('cli.tools') + '</h3>' +
|
||||
@@ -500,7 +632,8 @@ function renderToolsSection() {
|
||||
toolsHtml +
|
||||
codexLensHtml +
|
||||
semanticHtml +
|
||||
'</div>';
|
||||
'</div>' +
|
||||
apiEndpointsHtml;
|
||||
|
||||
if (window.lucide) lucide.createIcons();
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ async function loadSemanticDepsStatus() {
|
||||
acceleratorIcon = 'zap';
|
||||
acceleratorClass = 'bg-green-500/20 text-green-600';
|
||||
} else if (accelerator === 'DirectML') {
|
||||
acceleratorIcon = 'gpu-card';
|
||||
acceleratorIcon = 'cpu';
|
||||
acceleratorClass = 'bg-blue-500/20 text-blue-600';
|
||||
} else if (accelerator === 'ROCm') {
|
||||
acceleratorIcon = 'flame';
|
||||
@@ -450,7 +450,7 @@ function buildGpuModeSelector(gpuInfo) {
|
||||
id: 'directml',
|
||||
label: 'DirectML',
|
||||
desc: t('codexlens.directmlModeDesc') || 'Windows GPU (NVIDIA/AMD/Intel)',
|
||||
icon: 'gpu-card',
|
||||
icon: 'cpu',
|
||||
available: gpuInfo.available.includes('directml'),
|
||||
recommended: gpuInfo.mode === 'directml'
|
||||
},
|
||||
@@ -1331,7 +1331,15 @@ async function startCodexLensIndexing(indexType, embeddingModel, embeddingBacken
|
||||
|
||||
// Check if completed successfully (WebSocket might have already reported)
|
||||
if (result.success) {
|
||||
handleIndexComplete(true, t('codexlens.indexComplete'));
|
||||
// For vector index, check if embeddings were actually generated
|
||||
var embeddingsResult = result.result && result.result.embeddings;
|
||||
if (indexType === 'vector' && embeddingsResult && !embeddingsResult.generated) {
|
||||
// FTS succeeded but embeddings failed - show partial success
|
||||
var errorMsg = embeddingsResult.error || t('codexlens.embeddingsFailed');
|
||||
handleIndexComplete(false, t('codexlens.ftsSuccessEmbeddingsFailed') || 'FTS index created, but embeddings failed: ' + errorMsg);
|
||||
} else {
|
||||
handleIndexComplete(true, t('codexlens.indexComplete'));
|
||||
}
|
||||
} else if (!result.success) {
|
||||
handleIndexComplete(false, result.error || t('common.unknownError'));
|
||||
}
|
||||
|
||||
@@ -275,11 +275,22 @@ interface SearchResult {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
interface ModelInfo {
|
||||
model_profile?: string;
|
||||
model_name?: string;
|
||||
embedding_dim?: number;
|
||||
backend?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
interface IndexStatus {
|
||||
indexed: boolean;
|
||||
has_embeddings: boolean;
|
||||
file_count?: number;
|
||||
embeddings_coverage_percent?: number;
|
||||
total_chunks?: number;
|
||||
model_info?: ModelInfo;
|
||||
warning?: string;
|
||||
}
|
||||
|
||||
@@ -320,6 +331,18 @@ async function checkIndexStatus(path: string = '.'): Promise<IndexStatus> {
|
||||
const embeddingsData = status.embeddings || {};
|
||||
const embeddingsCoverage = embeddingsData.coverage_percent || 0;
|
||||
const has_embeddings = embeddingsCoverage >= 50; // Threshold: 50%
|
||||
const totalChunks = embeddingsData.total_chunks || 0;
|
||||
|
||||
// Extract model info if available
|
||||
const modelInfoData = embeddingsData.model_info;
|
||||
const modelInfo: ModelInfo | undefined = modelInfoData ? {
|
||||
model_profile: modelInfoData.model_profile,
|
||||
model_name: modelInfoData.model_name,
|
||||
embedding_dim: modelInfoData.embedding_dim,
|
||||
backend: modelInfoData.backend,
|
||||
created_at: modelInfoData.created_at,
|
||||
updated_at: modelInfoData.updated_at,
|
||||
} : undefined;
|
||||
|
||||
let warning: string | undefined;
|
||||
if (!indexed) {
|
||||
@@ -335,6 +358,8 @@ async function checkIndexStatus(path: string = '.'): Promise<IndexStatus> {
|
||||
has_embeddings,
|
||||
file_count: status.total_files,
|
||||
embeddings_coverage_percent: embeddingsCoverage,
|
||||
total_chunks: totalChunks,
|
||||
model_info: modelInfo,
|
||||
warning,
|
||||
};
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user