claude-code-workflow 6.2.7 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +16 -1
- package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +11 -4
- package/.claude/workflows/cli-templates/protocols/write-protocol.md +10 -75
- package/.claude/workflows/cli-tools-usage.md +14 -24
- package/.codex/AGENTS.md +51 -1
- package/.codex/prompts/compact.md +378 -0
- package/.gemini/GEMINI.md +57 -20
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +21 -8
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts +2 -0
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +129 -8
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/hook.d.ts.map +1 -1
- package/ccw/dist/commands/hook.js +3 -2
- package/ccw/dist/commands/hook.js.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.d.ts +180 -0
- package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -0
- package/ccw/dist/config/litellm-api-config-manager.js +770 -0
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -0
- package/ccw/dist/config/provider-models.d.ts +73 -0
- package/ccw/dist/config/provider-models.d.ts.map +1 -0
- package/ccw/dist/config/provider-models.js +172 -0
- package/ccw/dist/config/provider-models.js.map +1 -0
- package/ccw/dist/core/cache-manager.d.ts.map +1 -1
- package/ccw/dist/core/cache-manager.js +3 -5
- package/ccw/dist/core/cache-manager.js.map +1 -1
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
- package/ccw/dist/core/dashboard-generator.js +3 -1
- package/ccw/dist/core/dashboard-generator.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +169 -0
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.js +234 -18
- package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.js +30 -32
- package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.d.ts +21 -0
- package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-api-routes.js +780 -0
- package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts +20 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.js +85 -0
- package/ccw/dist/core/routes/litellm-routes.js.map +1 -0
- package/ccw/dist/core/routes/mcp-routes.js +2 -2
- package/ccw/dist/core/routes/mcp-routes.js.map +1 -1
- package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/status-routes.js +39 -0
- package/ccw/dist/core/routes/status-routes.js.map +1 -1
- package/ccw/dist/core/routes/system-routes.js +1 -1
- package/ccw/dist/core/routes/system-routes.js.map +1 -1
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +15 -1
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/mcp-server/index.js +1 -1
- package/ccw/dist/mcp-server/index.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +82 -0
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -0
- package/ccw/dist/tools/claude-cli-tools.js +216 -0
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -0
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor.js +76 -14
- package/ccw/dist/tools/cli-executor.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts +9 -2
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +114 -9
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/context-cache-store.d.ts +136 -0
- package/ccw/dist/tools/context-cache-store.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache-store.js +256 -0
- package/ccw/dist/tools/context-cache-store.js.map +1 -0
- package/ccw/dist/tools/context-cache.d.ts +56 -0
- package/ccw/dist/tools/context-cache.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache.js +294 -0
- package/ccw/dist/tools/context-cache.js.map +1 -0
- package/ccw/dist/tools/core-memory.d.ts.map +1 -1
- package/ccw/dist/tools/core-memory.js +33 -19
- package/ccw/dist/tools/core-memory.js.map +1 -1
- package/ccw/dist/tools/index.d.ts.map +1 -1
- package/ccw/dist/tools/index.js +2 -0
- package/ccw/dist/tools/index.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts +85 -0
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-client.js +188 -0
- package/ccw/dist/tools/litellm-client.js.map +1 -0
- package/ccw/dist/tools/litellm-executor.d.ts +34 -0
- package/ccw/dist/tools/litellm-executor.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-executor.js +192 -0
- package/ccw/dist/tools/litellm-executor.js.map +1 -0
- package/ccw/dist/tools/pattern-parser.d.ts +55 -0
- package/ccw/dist/tools/pattern-parser.d.ts.map +1 -0
- package/ccw/dist/tools/pattern-parser.js +237 -0
- package/ccw/dist/tools/pattern-parser.js.map +1 -0
- package/ccw/dist/tools/smart-search.d.ts +1 -0
- package/ccw/dist/tools/smart-search.d.ts.map +1 -1
- package/ccw/dist/tools/smart-search.js +117 -41
- package/ccw/dist/tools/smart-search.js.map +1 -1
- package/ccw/dist/types/litellm-api-config.d.ts +294 -0
- package/ccw/dist/types/litellm-api-config.d.ts.map +1 -0
- package/ccw/dist/types/litellm-api-config.js +8 -0
- package/ccw/dist/types/litellm-api-config.js.map +1 -0
- package/ccw/src/cli.ts +258 -244
- package/ccw/src/commands/cli.ts +153 -9
- package/ccw/src/commands/hook.ts +3 -2
- package/ccw/src/config/.litellm-api-config-manager.ts.2025-12-23T11-57-43-727Z.bak +441 -0
- package/ccw/src/config/litellm-api-config-manager.ts +1012 -0
- package/ccw/src/config/provider-models.ts +222 -0
- package/ccw/src/core/cache-manager.ts +292 -294
- package/ccw/src/core/dashboard-generator.ts +3 -1
- package/ccw/src/core/routes/cli-routes.ts +192 -0
- package/ccw/src/core/routes/codexlens-routes.ts +241 -19
- package/ccw/src/core/routes/hooks-routes.ts +399 -405
- package/ccw/src/core/routes/litellm-api-routes.ts +930 -0
- package/ccw/src/core/routes/litellm-routes.ts +107 -0
- package/ccw/src/core/routes/mcp-routes.ts +1271 -1271
- package/ccw/src/core/routes/status-routes.ts +51 -0
- package/ccw/src/core/routes/system-routes.ts +1 -1
- package/ccw/src/core/server.ts +15 -1
- package/ccw/src/mcp-server/index.ts +1 -1
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +44 -0
- package/ccw/src/templates/dashboard-css/31-api-settings.css +2265 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +15 -8
- package/ccw/src/templates/dashboard-js/components/cli-status.js +323 -9
- package/ccw/src/templates/dashboard-js/components/navigation.js +329 -313
- package/ccw/src/templates/dashboard-js/i18n.js +583 -1
- package/ccw/src/templates/dashboard-js/views/api-settings.js +3362 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +199 -24
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +1265 -27
- package/ccw/src/templates/dashboard.html +840 -831
- package/ccw/src/tools/claude-cli-tools.ts +300 -0
- package/ccw/src/tools/cli-executor.ts +83 -14
- package/ccw/src/tools/codex-lens.ts +146 -9
- package/ccw/src/tools/context-cache-store.ts +368 -0
- package/ccw/src/tools/context-cache.ts +393 -0
- package/ccw/src/tools/core-memory.ts +33 -19
- package/ccw/src/tools/index.ts +2 -0
- package/ccw/src/tools/litellm-client.ts +246 -0
- package/ccw/src/tools/litellm-executor.ts +241 -0
- package/ccw/src/tools/pattern-parser.ts +329 -0
- package/ccw/src/tools/smart-search.ts +142 -41
- package/ccw/src/types/litellm-api-config.ts +402 -0
- package/ccw-litellm/README.md +180 -0
- package/ccw-litellm/pyproject.toml +35 -0
- package/ccw-litellm/src/ccw_litellm/__init__.py +47 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/cli.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/cli.py +108 -0
- package/ccw-litellm/src/ccw_litellm/clients/__init__.py +12 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +251 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +165 -0
- package/ccw-litellm/src/ccw_litellm/config/__init__.py +22 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/loader.py +316 -0
- package/ccw-litellm/src/ccw_litellm/config/models.py +130 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +14 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +52 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +45 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +378 -23
- package/codex-lens/src/codexlens/cli/embedding_manager.py +660 -56
- package/codex-lens/src/codexlens/cli/model_manager.py +31 -18
- package/codex-lens/src/codexlens/cli/output.py +12 -1
- package/codex-lens/src/codexlens/config.py +93 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +6 -2
- package/codex-lens/src/codexlens/search/hybrid_search.py +44 -21
- package/codex-lens/src/codexlens/search/ranking.py +1 -1
- package/codex-lens/src/codexlens/semantic/__init__.py +42 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/base.py +61 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +43 -20
- package/codex-lens/src/codexlens/semantic/embedder.py +60 -13
- package/codex-lens/src/codexlens/semantic/factory.py +98 -0
- package/codex-lens/src/codexlens/semantic/gpu_support.py +225 -3
- package/codex-lens/src/codexlens/semantic/litellm_embedder.py +144 -0
- package/codex-lens/src/codexlens/semantic/rotational_embedder.py +434 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +33 -8
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/path_mapper.py +27 -1
- package/package.json +15 -5
- package/.codex/prompts.zip +0 -0
- package/ccw/package.json +0 -65
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import { dirname, join as pathJoin } from 'path';
|
|
3
|
+
// Get current module path for package-relative lookups
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = dirname(__filename);
|
|
6
|
+
// Package root: routes -> core -> src -> ccw -> package root
|
|
7
|
+
const PACKAGE_ROOT = pathJoin(__dirname, '..', '..', '..', '..');
|
|
8
|
+
import { getAllProviders, getProvider, addProvider, updateProvider, deleteProvider, getAllEndpoints, getEndpoint, addEndpoint, updateEndpoint, deleteEndpoint, getDefaultEndpoint, setDefaultEndpoint, getGlobalCacheSettings, updateGlobalCacheSettings, loadLiteLLMApiConfig, saveLiteLLMYamlConfig, generateLiteLLMYamlConfig, getCodexLensEmbeddingRotation, updateCodexLensEmbeddingRotation, getEmbeddingProvidersForRotation, generateRotationEndpoints, syncCodexLensConfig, getEmbeddingPoolConfig, updateEmbeddingPoolConfig, discoverProvidersForModel, } from '../../config/litellm-api-config-manager.js';
|
|
9
|
+
import { getContextCacheStore } from '../../tools/context-cache-store.js';
|
|
10
|
+
import { getLiteLLMClient } from '../../tools/litellm-client.js';
|
|
11
|
+
// Cache for ccw-litellm status check
|
|
12
|
+
let ccwLitellmStatusCache = {
|
|
13
|
+
data: null,
|
|
14
|
+
timestamp: 0,
|
|
15
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
16
|
+
};
|
|
17
|
+
// Clear cache (call after install)
|
|
18
|
+
export function clearCcwLitellmStatusCache() {
|
|
19
|
+
ccwLitellmStatusCache.data = null;
|
|
20
|
+
ccwLitellmStatusCache.timestamp = 0;
|
|
21
|
+
}
|
|
22
|
+
const PROVIDER_MODELS = {
|
|
23
|
+
openai: [
|
|
24
|
+
{ id: 'gpt-4-turbo', name: 'GPT-4 Turbo', provider: 'openai', description: '128K context' },
|
|
25
|
+
{ id: 'gpt-4', name: 'GPT-4', provider: 'openai', description: '8K context' },
|
|
26
|
+
{ id: 'gpt-3.5-turbo', name: 'GPT-3.5 Turbo', provider: 'openai', description: '16K context' },
|
|
27
|
+
],
|
|
28
|
+
anthropic: [
|
|
29
|
+
{ id: 'claude-3-opus-20240229', name: 'Claude 3 Opus', provider: 'anthropic', description: '200K context' },
|
|
30
|
+
{ id: 'claude-3-sonnet-20240229', name: 'Claude 3 Sonnet', provider: 'anthropic', description: '200K context' },
|
|
31
|
+
{ id: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', provider: 'anthropic', description: '200K context' },
|
|
32
|
+
],
|
|
33
|
+
google: [
|
|
34
|
+
{ id: 'gemini-pro', name: 'Gemini Pro', provider: 'google', description: '32K context' },
|
|
35
|
+
{ id: 'gemini-pro-vision', name: 'Gemini Pro Vision', provider: 'google', description: '16K context' },
|
|
36
|
+
],
|
|
37
|
+
ollama: [
|
|
38
|
+
{ id: 'llama2', name: 'Llama 2', provider: 'ollama', description: 'Local model' },
|
|
39
|
+
{ id: 'mistral', name: 'Mistral', provider: 'ollama', description: 'Local model' },
|
|
40
|
+
],
|
|
41
|
+
azure: [],
|
|
42
|
+
mistral: [
|
|
43
|
+
{ id: 'mistral-large-latest', name: 'Mistral Large', provider: 'mistral', description: '32K context' },
|
|
44
|
+
{ id: 'mistral-medium-latest', name: 'Mistral Medium', provider: 'mistral', description: '32K context' },
|
|
45
|
+
],
|
|
46
|
+
deepseek: [
|
|
47
|
+
{ id: 'deepseek-chat', name: 'DeepSeek Chat', provider: 'deepseek', description: '64K context' },
|
|
48
|
+
{ id: 'deepseek-coder', name: 'DeepSeek Coder', provider: 'deepseek', description: '64K context' },
|
|
49
|
+
],
|
|
50
|
+
custom: [],
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Handle LiteLLM API routes
|
|
54
|
+
* @returns true if route was handled, false otherwise
|
|
55
|
+
*/
|
|
56
|
+
export async function handleLiteLLMApiRoutes(ctx) {
|
|
57
|
+
const { pathname, url, req, res, initialPath, handlePostRequest, broadcastToClients } = ctx;
|
|
58
|
+
// ===========================
|
|
59
|
+
// Provider Management Routes
|
|
60
|
+
// ===========================
|
|
61
|
+
// GET /api/litellm-api/providers - List all providers
|
|
62
|
+
if (pathname === '/api/litellm-api/providers' && req.method === 'GET') {
|
|
63
|
+
try {
|
|
64
|
+
const providers = getAllProviders(initialPath);
|
|
65
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
66
|
+
res.end(JSON.stringify({ providers, count: providers.length }));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
70
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
// POST /api/litellm-api/providers - Create provider
|
|
75
|
+
if (pathname === '/api/litellm-api/providers' && req.method === 'POST') {
|
|
76
|
+
handlePostRequest(req, res, async (body) => {
|
|
77
|
+
const providerData = body;
|
|
78
|
+
if (!providerData.name || !providerData.type || !providerData.apiKey) {
|
|
79
|
+
return { error: 'Provider name, type, and apiKey are required', status: 400 };
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const provider = addProvider(initialPath, providerData);
|
|
83
|
+
broadcastToClients({
|
|
84
|
+
type: 'LITELLM_PROVIDER_CREATED',
|
|
85
|
+
payload: { provider, timestamp: new Date().toISOString() }
|
|
86
|
+
});
|
|
87
|
+
return { success: true, provider };
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
return { error: err.message, status: 500 };
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
// GET /api/litellm-api/providers/:id - Get provider by ID
|
|
96
|
+
const providerGetMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)$/);
|
|
97
|
+
if (providerGetMatch && req.method === 'GET') {
|
|
98
|
+
const providerId = providerGetMatch[1];
|
|
99
|
+
try {
|
|
100
|
+
const provider = getProvider(initialPath, providerId);
|
|
101
|
+
if (!provider) {
|
|
102
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
103
|
+
res.end(JSON.stringify({ error: 'Provider not found' }));
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
107
|
+
res.end(JSON.stringify(provider));
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
111
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
// PUT /api/litellm-api/providers/:id - Update provider
|
|
116
|
+
const providerUpdateMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)$/);
|
|
117
|
+
if (providerUpdateMatch && req.method === 'PUT') {
|
|
118
|
+
const providerId = providerUpdateMatch[1];
|
|
119
|
+
handlePostRequest(req, res, async (body) => {
|
|
120
|
+
const updates = body;
|
|
121
|
+
try {
|
|
122
|
+
const provider = updateProvider(initialPath, providerId, updates);
|
|
123
|
+
broadcastToClients({
|
|
124
|
+
type: 'LITELLM_PROVIDER_UPDATED',
|
|
125
|
+
payload: { provider, timestamp: new Date().toISOString() }
|
|
126
|
+
});
|
|
127
|
+
return { success: true, provider };
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
return { error: err.message, status: 404 };
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
// DELETE /api/litellm-api/providers/:id - Delete provider
|
|
136
|
+
const providerDeleteMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)$/);
|
|
137
|
+
if (providerDeleteMatch && req.method === 'DELETE') {
|
|
138
|
+
const providerId = providerDeleteMatch[1];
|
|
139
|
+
try {
|
|
140
|
+
const success = deleteProvider(initialPath, providerId);
|
|
141
|
+
if (!success) {
|
|
142
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
143
|
+
res.end(JSON.stringify({ error: 'Provider not found' }));
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
broadcastToClients({
|
|
147
|
+
type: 'LITELLM_PROVIDER_DELETED',
|
|
148
|
+
payload: { providerId, timestamp: new Date().toISOString() }
|
|
149
|
+
});
|
|
150
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
151
|
+
res.end(JSON.stringify({ success: true, message: 'Provider deleted' }));
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
155
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
// POST /api/litellm-api/providers/:id/test - Test provider connection
|
|
160
|
+
const providerTestMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)\/test$/);
|
|
161
|
+
if (providerTestMatch && req.method === 'POST') {
|
|
162
|
+
const providerId = providerTestMatch[1];
|
|
163
|
+
try {
|
|
164
|
+
const provider = getProvider(initialPath, providerId);
|
|
165
|
+
if (!provider) {
|
|
166
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
167
|
+
res.end(JSON.stringify({ success: false, error: 'Provider not found' }));
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
if (!provider.enabled) {
|
|
171
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
172
|
+
res.end(JSON.stringify({ success: false, error: 'Provider is disabled' }));
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
// Test connection using litellm client
|
|
176
|
+
const client = getLiteLLMClient();
|
|
177
|
+
const available = await client.isAvailable();
|
|
178
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
179
|
+
res.end(JSON.stringify({ success: available, provider: provider.type }));
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
183
|
+
res.end(JSON.stringify({ success: false, error: err.message }));
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
// ===========================
|
|
188
|
+
// Endpoint Management Routes
|
|
189
|
+
// ===========================
|
|
190
|
+
// GET /api/litellm-api/endpoints - List all endpoints
|
|
191
|
+
if (pathname === '/api/litellm-api/endpoints' && req.method === 'GET') {
|
|
192
|
+
try {
|
|
193
|
+
const endpoints = getAllEndpoints(initialPath);
|
|
194
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
195
|
+
res.end(JSON.stringify({ endpoints, count: endpoints.length }));
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
199
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
// POST /api/litellm-api/endpoints - Create endpoint
|
|
204
|
+
if (pathname === '/api/litellm-api/endpoints' && req.method === 'POST') {
|
|
205
|
+
handlePostRequest(req, res, async (body) => {
|
|
206
|
+
const endpointData = body;
|
|
207
|
+
if (!endpointData.id || !endpointData.name || !endpointData.providerId || !endpointData.model) {
|
|
208
|
+
return { error: 'Endpoint id, name, providerId, and model are required', status: 400 };
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const endpoint = addEndpoint(initialPath, endpointData);
|
|
212
|
+
broadcastToClients({
|
|
213
|
+
type: 'LITELLM_ENDPOINT_CREATED',
|
|
214
|
+
payload: { endpoint, timestamp: new Date().toISOString() }
|
|
215
|
+
});
|
|
216
|
+
return { success: true, endpoint };
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
return { error: err.message, status: 500 };
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
// GET /api/litellm-api/endpoints/:id - Get endpoint by ID
|
|
225
|
+
const endpointGetMatch = pathname.match(/^\/api\/litellm-api\/endpoints\/([^/]+)$/);
|
|
226
|
+
if (endpointGetMatch && req.method === 'GET') {
|
|
227
|
+
const endpointId = endpointGetMatch[1];
|
|
228
|
+
try {
|
|
229
|
+
const endpoint = getEndpoint(initialPath, endpointId);
|
|
230
|
+
if (!endpoint) {
|
|
231
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
232
|
+
res.end(JSON.stringify({ error: 'Endpoint not found' }));
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
236
|
+
res.end(JSON.stringify(endpoint));
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
240
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
241
|
+
}
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
// PUT /api/litellm-api/endpoints/:id - Update endpoint
|
|
245
|
+
const endpointUpdateMatch = pathname.match(/^\/api\/litellm-api\/endpoints\/([^/]+)$/);
|
|
246
|
+
if (endpointUpdateMatch && req.method === 'PUT') {
|
|
247
|
+
const endpointId = endpointUpdateMatch[1];
|
|
248
|
+
handlePostRequest(req, res, async (body) => {
|
|
249
|
+
const updates = body;
|
|
250
|
+
try {
|
|
251
|
+
const endpoint = updateEndpoint(initialPath, endpointId, updates);
|
|
252
|
+
broadcastToClients({
|
|
253
|
+
type: 'LITELLM_ENDPOINT_UPDATED',
|
|
254
|
+
payload: { endpoint, timestamp: new Date().toISOString() }
|
|
255
|
+
});
|
|
256
|
+
return { success: true, endpoint };
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
return { error: err.message, status: 404 };
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
// DELETE /api/litellm-api/endpoints/:id - Delete endpoint
|
|
265
|
+
const endpointDeleteMatch = pathname.match(/^\/api\/litellm-api\/endpoints\/([^/]+)$/);
|
|
266
|
+
if (endpointDeleteMatch && req.method === 'DELETE') {
|
|
267
|
+
const endpointId = endpointDeleteMatch[1];
|
|
268
|
+
try {
|
|
269
|
+
const success = deleteEndpoint(initialPath, endpointId);
|
|
270
|
+
if (!success) {
|
|
271
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
272
|
+
res.end(JSON.stringify({ error: 'Endpoint not found' }));
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
broadcastToClients({
|
|
276
|
+
type: 'LITELLM_ENDPOINT_DELETED',
|
|
277
|
+
payload: { endpointId, timestamp: new Date().toISOString() }
|
|
278
|
+
});
|
|
279
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
280
|
+
res.end(JSON.stringify({ success: true, message: 'Endpoint deleted' }));
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
284
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
285
|
+
}
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
// ===========================
|
|
289
|
+
// Model Discovery Routes
|
|
290
|
+
// ===========================
|
|
291
|
+
// GET /api/litellm-api/models/:providerType - Get available models for provider type
|
|
292
|
+
const modelsMatch = pathname.match(/^\/api\/litellm-api\/models\/([^/]+)$/);
|
|
293
|
+
if (modelsMatch && req.method === 'GET') {
|
|
294
|
+
const providerType = modelsMatch[1];
|
|
295
|
+
try {
|
|
296
|
+
const models = PROVIDER_MODELS[providerType];
|
|
297
|
+
if (!models) {
|
|
298
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
299
|
+
res.end(JSON.stringify({ error: 'Provider type not found' }));
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
303
|
+
res.end(JSON.stringify({ providerType, models, count: models.length }));
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
307
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
// ===========================
|
|
312
|
+
// Cache Management Routes
|
|
313
|
+
// ===========================
|
|
314
|
+
// GET /api/litellm-api/cache/stats - Get cache statistics
|
|
315
|
+
if (pathname === '/api/litellm-api/cache/stats' && req.method === 'GET') {
|
|
316
|
+
try {
|
|
317
|
+
const cacheStore = getContextCacheStore();
|
|
318
|
+
const stats = cacheStore.getStatus();
|
|
319
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
320
|
+
res.end(JSON.stringify(stats));
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
324
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
325
|
+
}
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
// POST /api/litellm-api/cache/clear - Clear cache
|
|
329
|
+
if (pathname === '/api/litellm-api/cache/clear' && req.method === 'POST') {
|
|
330
|
+
try {
|
|
331
|
+
const cacheStore = getContextCacheStore();
|
|
332
|
+
const result = cacheStore.clear();
|
|
333
|
+
broadcastToClients({
|
|
334
|
+
type: 'LITELLM_CACHE_CLEARED',
|
|
335
|
+
payload: { removed: result.removed, timestamp: new Date().toISOString() }
|
|
336
|
+
});
|
|
337
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
338
|
+
res.end(JSON.stringify({ success: true, removed: result.removed }));
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
342
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
343
|
+
}
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
// ===========================
|
|
347
|
+
// Config Management Routes
|
|
348
|
+
// ===========================
|
|
349
|
+
// GET /api/litellm-api/config - Get full config
|
|
350
|
+
if (pathname === '/api/litellm-api/config' && req.method === 'GET') {
|
|
351
|
+
try {
|
|
352
|
+
const config = loadLiteLLMApiConfig(initialPath);
|
|
353
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
354
|
+
res.end(JSON.stringify(config));
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
358
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
359
|
+
}
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
// PUT /api/litellm-api/config/cache - Update global cache settings
|
|
363
|
+
if (pathname === '/api/litellm-api/config/cache' && req.method === 'PUT') {
|
|
364
|
+
handlePostRequest(req, res, async (body) => {
|
|
365
|
+
const settings = body;
|
|
366
|
+
try {
|
|
367
|
+
updateGlobalCacheSettings(initialPath, settings);
|
|
368
|
+
const updatedSettings = getGlobalCacheSettings(initialPath);
|
|
369
|
+
broadcastToClients({
|
|
370
|
+
type: 'LITELLM_CACHE_SETTINGS_UPDATED',
|
|
371
|
+
payload: { settings: updatedSettings, timestamp: new Date().toISOString() }
|
|
372
|
+
});
|
|
373
|
+
return { success: true, settings: updatedSettings };
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
return { error: err.message, status: 500 };
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
// PUT /api/litellm-api/config/default-endpoint - Set default endpoint
|
|
382
|
+
if (pathname === '/api/litellm-api/config/default-endpoint' && req.method === 'PUT') {
|
|
383
|
+
handlePostRequest(req, res, async (body) => {
|
|
384
|
+
const { endpointId } = body;
|
|
385
|
+
try {
|
|
386
|
+
setDefaultEndpoint(initialPath, endpointId);
|
|
387
|
+
const defaultEndpoint = getDefaultEndpoint(initialPath);
|
|
388
|
+
broadcastToClients({
|
|
389
|
+
type: 'LITELLM_DEFAULT_ENDPOINT_UPDATED',
|
|
390
|
+
payload: { endpointId, defaultEndpoint, timestamp: new Date().toISOString() }
|
|
391
|
+
});
|
|
392
|
+
return { success: true, defaultEndpoint };
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
return { error: err.message, status: 500 };
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
// ===========================
|
|
401
|
+
// Config Sync Routes
|
|
402
|
+
// ===========================
|
|
403
|
+
// POST /api/litellm-api/config/sync - Sync UI config to ccw_litellm YAML config
|
|
404
|
+
if (pathname === '/api/litellm-api/config/sync' && req.method === 'POST') {
|
|
405
|
+
try {
|
|
406
|
+
const yamlPath = saveLiteLLMYamlConfig(initialPath);
|
|
407
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
408
|
+
res.end(JSON.stringify({
|
|
409
|
+
success: true,
|
|
410
|
+
message: 'Config synced to ccw_litellm',
|
|
411
|
+
yamlPath,
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
416
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
417
|
+
}
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
// GET /api/litellm-api/config/yaml-preview - Preview YAML config without saving
|
|
421
|
+
if (pathname === '/api/litellm-api/config/yaml-preview' && req.method === 'GET') {
|
|
422
|
+
try {
|
|
423
|
+
const yamlConfig = generateLiteLLMYamlConfig(initialPath);
|
|
424
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
425
|
+
res.end(JSON.stringify({
|
|
426
|
+
success: true,
|
|
427
|
+
config: yamlConfig,
|
|
428
|
+
}));
|
|
429
|
+
}
|
|
430
|
+
catch (err) {
|
|
431
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
432
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
// ===========================
|
|
437
|
+
// CCW-LiteLLM Package Management
|
|
438
|
+
// ===========================
|
|
439
|
+
// GET /api/litellm-api/ccw-litellm/status - Check ccw-litellm installation status
|
|
440
|
+
// Supports ?refresh=true to bypass cache
|
|
441
|
+
if (pathname === '/api/litellm-api/ccw-litellm/status' && req.method === 'GET') {
|
|
442
|
+
const forceRefresh = url.searchParams.get('refresh') === 'true';
|
|
443
|
+
// Check cache first (unless force refresh)
|
|
444
|
+
if (!forceRefresh && ccwLitellmStatusCache.data &&
|
|
445
|
+
Date.now() - ccwLitellmStatusCache.timestamp < ccwLitellmStatusCache.ttl) {
|
|
446
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
447
|
+
res.end(JSON.stringify(ccwLitellmStatusCache.data));
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
// Async check - use pip show for more reliable detection
|
|
451
|
+
try {
|
|
452
|
+
const { exec } = await import('child_process');
|
|
453
|
+
const { promisify } = await import('util');
|
|
454
|
+
const execAsync = promisify(exec);
|
|
455
|
+
let result = { installed: false };
|
|
456
|
+
// Method 1: Try pip show ccw-litellm (most reliable)
|
|
457
|
+
try {
|
|
458
|
+
const { stdout } = await execAsync('pip show ccw-litellm', {
|
|
459
|
+
timeout: 10000,
|
|
460
|
+
windowsHide: true,
|
|
461
|
+
shell: true,
|
|
462
|
+
});
|
|
463
|
+
// Parse version from pip show output
|
|
464
|
+
const versionMatch = stdout.match(/Version:\s*(.+)/i);
|
|
465
|
+
if (versionMatch) {
|
|
466
|
+
result = { installed: true, version: versionMatch[1].trim() };
|
|
467
|
+
console.log(`[ccw-litellm status] Found via pip show: ${result.version}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
catch (pipErr) {
|
|
471
|
+
console.log('[ccw-litellm status] pip show failed, trying python import...');
|
|
472
|
+
// Method 2: Fallback to Python import
|
|
473
|
+
const pythonExecutables = ['python', 'python3', 'py'];
|
|
474
|
+
for (const pythonExe of pythonExecutables) {
|
|
475
|
+
try {
|
|
476
|
+
// Use simpler Python code without complex quotes
|
|
477
|
+
const { stdout } = await execAsync(`${pythonExe} -c "import ccw_litellm; print(ccw_litellm.__version__)"`, {
|
|
478
|
+
timeout: 5000,
|
|
479
|
+
windowsHide: true,
|
|
480
|
+
shell: true,
|
|
481
|
+
});
|
|
482
|
+
const version = stdout.trim();
|
|
483
|
+
if (version) {
|
|
484
|
+
result = { installed: true, version };
|
|
485
|
+
console.log(`[ccw-litellm status] Found with ${pythonExe}: ${version}`);
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
catch (err) {
|
|
490
|
+
result.error = err.message;
|
|
491
|
+
console.log(`[ccw-litellm status] ${pythonExe} failed:`, result.error.substring(0, 100));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Update cache
|
|
496
|
+
ccwLitellmStatusCache = {
|
|
497
|
+
data: result,
|
|
498
|
+
timestamp: Date.now(),
|
|
499
|
+
ttl: 5 * 60 * 1000,
|
|
500
|
+
};
|
|
501
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
502
|
+
res.end(JSON.stringify(result));
|
|
503
|
+
}
|
|
504
|
+
catch (err) {
|
|
505
|
+
const errorResult = { installed: false, error: err.message };
|
|
506
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
507
|
+
res.end(JSON.stringify(errorResult));
|
|
508
|
+
}
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
// ===========================
|
|
512
|
+
// CodexLens Embedding Rotation Routes
|
|
513
|
+
// ===========================
|
|
514
|
+
// GET /api/litellm-api/codexlens/rotation - Get rotation config
|
|
515
|
+
if (pathname === '/api/litellm-api/codexlens/rotation' && req.method === 'GET') {
|
|
516
|
+
try {
|
|
517
|
+
const rotationConfig = getCodexLensEmbeddingRotation(initialPath);
|
|
518
|
+
const availableProviders = getEmbeddingProvidersForRotation(initialPath);
|
|
519
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
520
|
+
res.end(JSON.stringify({
|
|
521
|
+
rotationConfig: rotationConfig || null,
|
|
522
|
+
availableProviders,
|
|
523
|
+
}));
|
|
524
|
+
}
|
|
525
|
+
catch (err) {
|
|
526
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
527
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
528
|
+
}
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
// PUT /api/litellm-api/codexlens/rotation - Update rotation config
|
|
532
|
+
if (pathname === '/api/litellm-api/codexlens/rotation' && req.method === 'PUT') {
|
|
533
|
+
handlePostRequest(req, res, async (body) => {
|
|
534
|
+
const rotationConfig = body;
|
|
535
|
+
try {
|
|
536
|
+
const { syncResult } = updateCodexLensEmbeddingRotation(initialPath, rotationConfig || undefined);
|
|
537
|
+
broadcastToClients({
|
|
538
|
+
type: 'CODEXLENS_ROTATION_UPDATED',
|
|
539
|
+
payload: { rotationConfig, syncResult, timestamp: new Date().toISOString() }
|
|
540
|
+
});
|
|
541
|
+
return { success: true, rotationConfig, syncResult };
|
|
542
|
+
}
|
|
543
|
+
catch (err) {
|
|
544
|
+
return { error: err.message, status: 500 };
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
// GET /api/litellm-api/codexlens/rotation/endpoints - Get generated rotation endpoints
|
|
550
|
+
if (pathname === '/api/litellm-api/codexlens/rotation/endpoints' && req.method === 'GET') {
|
|
551
|
+
try {
|
|
552
|
+
const endpoints = generateRotationEndpoints(initialPath);
|
|
553
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
554
|
+
res.end(JSON.stringify({
|
|
555
|
+
endpoints,
|
|
556
|
+
count: endpoints.length,
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
catch (err) {
|
|
560
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
561
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
562
|
+
}
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
// POST /api/litellm-api/codexlens/rotation/sync - Manually sync rotation config to CodexLens
|
|
566
|
+
if (pathname === '/api/litellm-api/codexlens/rotation/sync' && req.method === 'POST') {
|
|
567
|
+
try {
|
|
568
|
+
const syncResult = syncCodexLensConfig(initialPath);
|
|
569
|
+
if (syncResult.success) {
|
|
570
|
+
broadcastToClients({
|
|
571
|
+
type: 'CODEXLENS_CONFIG_SYNCED',
|
|
572
|
+
payload: { ...syncResult, timestamp: new Date().toISOString() }
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
576
|
+
res.end(JSON.stringify(syncResult));
|
|
577
|
+
}
|
|
578
|
+
catch (err) {
|
|
579
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
580
|
+
res.end(JSON.stringify({ success: false, message: err.message }));
|
|
581
|
+
}
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
// ===========================
|
|
585
|
+
// Embedding Pool Routes (New Generic API)
|
|
586
|
+
// ===========================
|
|
587
|
+
// GET /api/litellm-api/embedding-pool - Get pool config and available models
|
|
588
|
+
if (pathname === '/api/litellm-api/embedding-pool' && req.method === 'GET') {
|
|
589
|
+
try {
|
|
590
|
+
const poolConfig = getEmbeddingPoolConfig(initialPath);
|
|
591
|
+
// Get list of all available embedding models from all providers
|
|
592
|
+
const config = loadLiteLLMApiConfig(initialPath);
|
|
593
|
+
const availableModels = [];
|
|
594
|
+
const modelMap = new Map();
|
|
595
|
+
for (const provider of config.providers) {
|
|
596
|
+
if (!provider.enabled || !provider.embeddingModels)
|
|
597
|
+
continue;
|
|
598
|
+
for (const model of provider.embeddingModels) {
|
|
599
|
+
if (!model.enabled)
|
|
600
|
+
continue;
|
|
601
|
+
const key = model.id;
|
|
602
|
+
if (modelMap.has(key)) {
|
|
603
|
+
modelMap.get(key).providers.push(provider.name);
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
modelMap.set(key, {
|
|
607
|
+
modelId: model.id,
|
|
608
|
+
modelName: model.name,
|
|
609
|
+
providers: [provider.name],
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
availableModels.push(...Array.from(modelMap.values()));
|
|
615
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
616
|
+
res.end(JSON.stringify({
|
|
617
|
+
poolConfig: poolConfig || null,
|
|
618
|
+
availableModels,
|
|
619
|
+
}));
|
|
620
|
+
}
|
|
621
|
+
catch (err) {
|
|
622
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
623
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
624
|
+
}
|
|
625
|
+
return true;
|
|
626
|
+
}
|
|
627
|
+
// PUT /api/litellm-api/embedding-pool - Update pool config
|
|
628
|
+
if (pathname === '/api/litellm-api/embedding-pool' && req.method === 'PUT') {
|
|
629
|
+
handlePostRequest(req, res, async (body) => {
|
|
630
|
+
const poolConfig = body;
|
|
631
|
+
try {
|
|
632
|
+
const { syncResult } = updateEmbeddingPoolConfig(initialPath, poolConfig || undefined);
|
|
633
|
+
broadcastToClients({
|
|
634
|
+
type: 'EMBEDDING_POOL_UPDATED',
|
|
635
|
+
payload: { poolConfig, syncResult, timestamp: new Date().toISOString() }
|
|
636
|
+
});
|
|
637
|
+
return { success: true, poolConfig, syncResult };
|
|
638
|
+
}
|
|
639
|
+
catch (err) {
|
|
640
|
+
return { error: err.message, status: 500 };
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
// GET /api/litellm-api/embedding-pool/discover/:model - Preview auto-discovery results
|
|
646
|
+
const discoverMatch = pathname.match(/^\/api\/litellm-api\/embedding-pool\/discover\/([^/]+)$/);
|
|
647
|
+
if (discoverMatch && req.method === 'GET') {
|
|
648
|
+
const targetModel = decodeURIComponent(discoverMatch[1]);
|
|
649
|
+
try {
|
|
650
|
+
const discovered = discoverProvidersForModel(initialPath, targetModel);
|
|
651
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
652
|
+
res.end(JSON.stringify({
|
|
653
|
+
targetModel,
|
|
654
|
+
discovered,
|
|
655
|
+
count: discovered.length,
|
|
656
|
+
}));
|
|
657
|
+
}
|
|
658
|
+
catch (err) {
|
|
659
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
660
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
661
|
+
}
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
// POST /api/litellm-api/ccw-litellm/install - Install ccw-litellm package
|
|
665
|
+
if (pathname === '/api/litellm-api/ccw-litellm/install' && req.method === 'POST') {
|
|
666
|
+
handlePostRequest(req, res, async () => {
|
|
667
|
+
try {
|
|
668
|
+
const { spawn } = await import('child_process');
|
|
669
|
+
const path = await import('path');
|
|
670
|
+
const fs = await import('fs');
|
|
671
|
+
// Try to find ccw-litellm package in distribution
|
|
672
|
+
const possiblePaths = [
|
|
673
|
+
path.join(initialPath, 'ccw-litellm'),
|
|
674
|
+
path.join(initialPath, '..', 'ccw-litellm'),
|
|
675
|
+
path.join(process.cwd(), 'ccw-litellm'),
|
|
676
|
+
path.join(PACKAGE_ROOT, 'ccw-litellm'), // npm package internal path
|
|
677
|
+
];
|
|
678
|
+
let packagePath = '';
|
|
679
|
+
for (const p of possiblePaths) {
|
|
680
|
+
const pyproject = path.join(p, 'pyproject.toml');
|
|
681
|
+
if (fs.existsSync(pyproject)) {
|
|
682
|
+
packagePath = p;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (!packagePath) {
|
|
687
|
+
// Try pip install from PyPI as fallback
|
|
688
|
+
return new Promise((resolve) => {
|
|
689
|
+
const proc = spawn('pip', ['install', 'ccw-litellm'], { shell: true, timeout: 300000 });
|
|
690
|
+
let output = '';
|
|
691
|
+
let error = '';
|
|
692
|
+
proc.stdout?.on('data', (data) => { output += data.toString(); });
|
|
693
|
+
proc.stderr?.on('data', (data) => { error += data.toString(); });
|
|
694
|
+
proc.on('close', (code) => {
|
|
695
|
+
if (code === 0) {
|
|
696
|
+
// Clear status cache after successful installation
|
|
697
|
+
clearCcwLitellmStatusCache();
|
|
698
|
+
resolve({ success: true, message: 'ccw-litellm installed from PyPI' });
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
resolve({ success: false, error: error || 'Installation failed' });
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
proc.on('error', (err) => resolve({ success: false, error: err.message }));
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
// Install from local package
|
|
708
|
+
return new Promise((resolve) => {
|
|
709
|
+
const proc = spawn('pip', ['install', '-e', packagePath], { shell: true, timeout: 300000 });
|
|
710
|
+
let output = '';
|
|
711
|
+
let error = '';
|
|
712
|
+
proc.stdout?.on('data', (data) => { output += data.toString(); });
|
|
713
|
+
proc.stderr?.on('data', (data) => { error += data.toString(); });
|
|
714
|
+
proc.on('close', (code) => {
|
|
715
|
+
if (code === 0) {
|
|
716
|
+
// Clear status cache after successful installation
|
|
717
|
+
clearCcwLitellmStatusCache();
|
|
718
|
+
// Broadcast installation event
|
|
719
|
+
broadcastToClients({
|
|
720
|
+
type: 'CCW_LITELLM_INSTALLED',
|
|
721
|
+
payload: { timestamp: new Date().toISOString() }
|
|
722
|
+
});
|
|
723
|
+
resolve({ success: true, message: 'ccw-litellm installed successfully', path: packagePath });
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
resolve({ success: false, error: error || output || 'Installation failed' });
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
proc.on('error', (err) => resolve({ success: false, error: err.message }));
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
catch (err) {
|
|
733
|
+
return { success: false, error: err.message };
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
// POST /api/litellm-api/ccw-litellm/uninstall - Uninstall ccw-litellm package
|
|
739
|
+
if (pathname === '/api/litellm-api/ccw-litellm/uninstall' && req.method === 'POST') {
|
|
740
|
+
handlePostRequest(req, res, async () => {
|
|
741
|
+
try {
|
|
742
|
+
const { spawn } = await import('child_process');
|
|
743
|
+
return new Promise((resolve) => {
|
|
744
|
+
const proc = spawn('pip', ['uninstall', '-y', 'ccw-litellm'], { shell: true, timeout: 120000 });
|
|
745
|
+
let output = '';
|
|
746
|
+
let error = '';
|
|
747
|
+
proc.stdout?.on('data', (data) => { output += data.toString(); });
|
|
748
|
+
proc.stderr?.on('data', (data) => { error += data.toString(); });
|
|
749
|
+
proc.on('close', (code) => {
|
|
750
|
+
// Clear status cache after uninstallation attempt
|
|
751
|
+
clearCcwLitellmStatusCache();
|
|
752
|
+
if (code === 0) {
|
|
753
|
+
broadcastToClients({
|
|
754
|
+
type: 'CCW_LITELLM_UNINSTALLED',
|
|
755
|
+
payload: { timestamp: new Date().toISOString() }
|
|
756
|
+
});
|
|
757
|
+
resolve({ success: true, message: 'ccw-litellm uninstalled successfully' });
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
// Check if package was not installed
|
|
761
|
+
if (error.includes('not installed') || output.includes('not installed')) {
|
|
762
|
+
resolve({ success: true, message: 'ccw-litellm was not installed' });
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
resolve({ success: false, error: error || output || 'Uninstallation failed' });
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
proc.on('error', (err) => resolve({ success: false, error: err.message }));
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
catch (err) {
|
|
773
|
+
return { success: false, error: err.message };
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
//# sourceMappingURL=litellm-api-routes.js.map
|