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:
catlog22
2025-12-24 18:01:26 +08:00
parent dfca4d60ee
commit e3e61bcae9
13 changed files with 575 additions and 107 deletions

View File

@@ -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;

View File

@@ -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 }));