Skip to content

Commit a8cf3e1

Browse files
author
catlog22
committed
feat: Refactor embedding pool sidebar rendering and always load semantic dependencies status
1 parent 0515ef6 commit a8cf3e1

File tree

2 files changed

+147
-77
lines changed

2 files changed

+147
-77
lines changed

ccw/src/templates/dashboard-js/views/api-settings.js

Lines changed: 144 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,13 @@ async function toggleProviderExclusion(providerId) {
189189

190190
// Re-render the discovered providers section
191191
renderDiscoveredProviders();
192+
193+
// Update sidebar summary
194+
const sidebarContainer = document.querySelector('.api-settings-sidebar .embedding-pool-sidebar-summary');
195+
if (sidebarContainer && sidebarContainer.parentElement) {
196+
sidebarContainer.parentElement.innerHTML = renderEmbeddingPoolSidebar();
197+
if (window.lucide) lucide.createIcons();
198+
}
192199
}
193200

194201
// ========== Provider Management ==========
@@ -991,9 +998,7 @@ async function renderApiSettings() {
991998
'<i data-lucide="plus"></i> ' + t('apiSettings.addEndpoint') +
992999
'</button>';
9931000
} else if (activeSidebarTab === 'embedding-pool') {
994-
sidebarContentHtml = '<div class="embedding-pool-sidebar-info" style="padding: 1rem; color: var(--text-secondary); font-size: 0.875rem;">' +
995-
'<p>' + t('apiSettings.embeddingPoolDesc') + '</p>' +
996-
'</div>';
1001+
sidebarContentHtml = renderEmbeddingPoolSidebar();
9971002
} else if (activeSidebarTab === 'cache') {
9981003
sidebarContentHtml = '<div class="cache-sidebar-info" style="padding: 1rem; color: var(--text-secondary); font-size: 0.875rem;">' +
9991004
'<p>' + t('apiSettings.cacheTabHint') + '</p>' +
@@ -2533,6 +2538,72 @@ function generateKeyId() {
25332538

25342539
// ========== Embedding Pool Management ==========
25352540

2541+
/**
2542+
* Render embedding pool sidebar summary
2543+
*/
2544+
function renderEmbeddingPoolSidebar() {
2545+
if (!embeddingPoolConfig) {
2546+
return '<div class="embedding-pool-sidebar-info" style="padding: 1rem;">' +
2547+
'<div style="text-align: center; color: hsl(var(--muted-foreground)); font-size: 0.875rem;">' +
2548+
'<p>' + t('apiSettings.embeddingPoolDesc') + '</p>' +
2549+
'</div>' +
2550+
'</div>';
2551+
}
2552+
2553+
const enabled = embeddingPoolConfig.enabled || false;
2554+
const targetModel = embeddingPoolConfig.targetModel || '';
2555+
const strategy = embeddingPoolConfig.strategy || 'round_robin';
2556+
const excludedIds = embeddingPoolConfig.excludedProviderIds || [];
2557+
2558+
// Count total providers/keys
2559+
let totalProviders = embeddingPoolDiscoveredProviders.length;
2560+
let totalKeys = 0;
2561+
let activeProviders = 0;
2562+
2563+
embeddingPoolDiscoveredProviders.forEach(function(p) {
2564+
totalKeys += p.apiKeys?.length || 1;
2565+
if (excludedIds.indexOf(p.providerId) === -1) {
2566+
activeProviders++;
2567+
}
2568+
});
2569+
2570+
const strategyLabels = {
2571+
'round_robin': t('codexlens.strategyRoundRobin') || 'Round Robin',
2572+
'latency_aware': t('codexlens.strategyLatency') || 'Latency-Aware',
2573+
'weighted_random': t('codexlens.strategyWeighted') || 'Weighted Random'
2574+
};
2575+
2576+
return '<div class="embedding-pool-sidebar-summary" style="padding: 1rem; display: flex; flex-direction: column; gap: 1rem;">' +
2577+
'<div style="padding: 1rem; background: hsl(var(--muted) / 0.3); border-radius: 0.5rem;">' +
2578+
'<h4 style="margin: 0 0 0.75rem 0; font-size: 0.875rem; font-weight: 600; color: hsl(var(--foreground));">' +
2579+
t('apiSettings.embeddingPool') +
2580+
'</h4>' +
2581+
'<div style="display: flex; flex-direction: column; gap: 0.5rem; font-size: 0.75rem; color: hsl(var(--muted-foreground));">' +
2582+
'<div style="display: flex; align-items: center; gap: 0.5rem; padding: 0.375rem 0; border-bottom: 1px solid hsl(var(--border));">' +
2583+
'<i data-lucide="' + (enabled ? 'check-circle' : 'x-circle') + '" style="width: 14px; height: 14px; color: ' + (enabled ? 'hsl(var(--success))' : 'hsl(var(--muted-foreground))') + ';"></i>' +
2584+
'<span>' + (enabled ? (t('common.enabled') || 'Enabled') : (t('common.disabled') || 'Disabled')) + '</span>' +
2585+
'</div>' +
2586+
(enabled && targetModel ?
2587+
'<div style="display: flex; flex-direction: column; gap: 0.25rem; padding: 0.375rem 0; border-bottom: 1px solid hsl(var(--border));">' +
2588+
'<span style="font-weight: 500; color: hsl(var(--foreground));">' + t('apiSettings.targetModel') + '</span>' +
2589+
'<code style="font-size: 0.6875rem; color: hsl(var(--primary)); word-break: break-all;">' + targetModel + '</code>' +
2590+
'</div>' : '') +
2591+
(enabled ?
2592+
'<div style="display: flex; flex-direction: column; gap: 0.25rem; padding: 0.375rem 0; border-bottom: 1px solid hsl(var(--border));">' +
2593+
'<span style="font-weight: 500; color: hsl(var(--foreground));">' + t('apiSettings.strategy') + '</span>' +
2594+
'<span>' + (strategyLabels[strategy] || strategy) + '</span>' +
2595+
'</div>' : '') +
2596+
(enabled && totalProviders > 0 ?
2597+
'<div style="display: flex; flex-direction: column; gap: 0.25rem; padding: 0.375rem 0;">' +
2598+
'<span style="font-weight: 500; color: hsl(var(--foreground));">' + t('apiSettings.discoveredProviders') + '</span>' +
2599+
'<span>' + activeProviders + ' / ' + totalProviders + ' providers (' + totalKeys + ' keys)</span>' +
2600+
'</div>' : '') +
2601+
'</div>' +
2602+
'</div>' +
2603+
'</div>';
2604+
}
2605+
2606+
25362607
/**
25372608
* Render embedding pool main panel
25382609
*/
@@ -2567,47 +2638,56 @@ async function renderEmbeddingPoolMainPanel() {
25672638
'<p class="panel-subtitle">' + t('apiSettings.embeddingPoolDesc') + '</p>' +
25682639
'</div>' +
25692640

2570-
// Enable/Disable Toggle
2571-
'<div class="settings-section">' +
2572-
'<div class="section-header">' +
2573-
'<h3>' + t('apiSettings.poolEnabled') + '</h3>' +
2641+
// Enable/Disable Toggle Card
2642+
'<div class="settings-section" style="padding: 1.25rem; background: hsl(var(--muted) / 0.3); border-radius: 0.75rem;">' +
2643+
'<div class="section-header" style="border: none; padding: 0;">' +
2644+
'<div style="display: flex; align-items: center; gap: 0.5rem;">' +
2645+
'<i data-lucide="power" style="width: 1rem; height: 1rem; color: hsl(var(--primary));"></i>' +
2646+
'<h3 style="margin: 0;">' + t('apiSettings.poolEnabled') + '</h3>' +
2647+
'</div>' +
25742648
'<label class="toggle-switch">' +
25752649
'<input type="checkbox" id="embedding-pool-enabled" ' + (enabled ? 'checked' : '') + ' onchange="onEmbeddingPoolEnabledChange(this.checked)" />' +
25762650
'<span class="toggle-track"><span class="toggle-thumb"></span></span>' +
25772651
'</label>' +
25782652
'</div>' +
25792653
'</div>' +
25802654

2581-
// Configuration Form
2655+
// Configuration Form Card
25822656
'<div class="settings-section" id="embedding-pool-config" style="' + (enabled ? '' : 'display: none;') + '">' +
2583-
'<div class="form-group">' +
2584-
'<label for="embedding-pool-target-model">' + t('apiSettings.targetModel') + '</label>' +
2585-
'<select id="embedding-pool-target-model" class="cli-input" onchange="onTargetModelChange(this.value)">' +
2586-
modelOptionsHtml +
2587-
'</select>' +
2588-
'</div>' +
2589-
2590-
'<div class="form-group">' +
2591-
'<label for="embedding-pool-strategy">' + t('apiSettings.strategy') + '</label>' +
2592-
'<select id="embedding-pool-strategy" class="cli-input">' +
2593-
'<option value="round_robin"' + (strategy === 'round_robin' ? ' selected' : '') + '>Round Robin</option>' +
2594-
'<option value="latency_aware"' + (strategy === 'latency_aware' ? ' selected' : '') + '>Latency Aware</option>' +
2595-
'<option value="weighted_random"' + (strategy === 'weighted_random' ? ' selected' : '') + '>Weighted Random</option>' +
2596-
'</select>' +
2597-
'</div>' +
2598-
2599-
'<div class="form-group">' +
2600-
'<label for="embedding-pool-cooldown">' + t('apiSettings.defaultCooldown') + '</label>' +
2601-
'<input type="number" id="embedding-pool-cooldown" class="cli-input" value="' + defaultCooldown + '" min="1" />' +
2657+
'<h3 style="margin: 0 0 1rem 0; font-size: 1rem; font-weight: 600;">' + t('apiSettings.configuration') || 'Configuration' + '</h3>' +
2658+
2659+
// Model and Strategy in Grid
2660+
'<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem;">' +
2661+
'<div class="form-group" style="margin: 0;">' +
2662+
'<label for="embedding-pool-target-model">' + t('apiSettings.targetModel') + '</label>' +
2663+
'<select id="embedding-pool-target-model" class="cli-input" onchange="onTargetModelChange(this.value)">' +
2664+
modelOptionsHtml +
2665+
'</select>' +
2666+
'</div>' +
2667+
'<div class="form-group" style="margin: 0;">' +
2668+
'<label for="embedding-pool-strategy">' + t('apiSettings.strategy') + '</label>' +
2669+
'<select id="embedding-pool-strategy" class="cli-input">' +
2670+
'<option value="round_robin"' + (strategy === 'round_robin' ? ' selected' : '') + '>Round Robin</option>' +
2671+
'<option value="latency_aware"' + (strategy === 'latency_aware' ? ' selected' : '') + '>Latency Aware</option>' +
2672+
'<option value="weighted_random"' + (strategy === 'weighted_random' ? ' selected' : '') + '>Weighted Random</option>' +
2673+
'</select>' +
2674+
'</div>' +
26022675
'</div>' +
26032676

2604-
'<div class="form-group">' +
2605-
'<label for="embedding-pool-concurrent">' + t('apiSettings.defaultConcurrent') + '</label>' +
2606-
'<input type="number" id="embedding-pool-concurrent" class="cli-input" value="' + defaultMaxConcurrentPerKey + '" min="1" />' +
2677+
// Cooldown and Concurrent in Grid
2678+
'<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">' +
2679+
'<div class="form-group" style="margin: 0;">' +
2680+
'<label for="embedding-pool-cooldown">' + t('apiSettings.defaultCooldown') + ' (s)</label>' +
2681+
'<input type="number" id="embedding-pool-cooldown" class="cli-input" value="' + defaultCooldown + '" min="1" />' +
2682+
'</div>' +
2683+
'<div class="form-group" style="margin: 0;">' +
2684+
'<label for="embedding-pool-concurrent">' + t('apiSettings.defaultConcurrent') + '</label>' +
2685+
'<input type="number" id="embedding-pool-concurrent" class="cli-input" value="' + defaultMaxConcurrentPerKey + '" min="1" />' +
2686+
'</div>' +
26072687
'</div>' +
26082688

26092689
// Discovered Providers Section
2610-
'<div id="discovered-providers-section"></div>' +
2690+
'<div id="discovered-providers-section" style="margin-top: 1.5rem;"></div>' +
26112691

26122692
'<div class="form-actions">' +
26132693
'<button class="btn btn-primary" onclick="saveEmbeddingPoolConfig()">' +
@@ -2675,32 +2755,43 @@ function renderDiscoveredProviders() {
26752755
totalKeys += p.apiKeys?.length || 1;
26762756
});
26772757

2678-
let providersHtml = '<div class="discovered-providers-list">' +
2679-
'<div class="discovered-providers-header">' +
2680-
'<h4>' + t('apiSettings.discoveredProviders') + '</h4>' +
2681-
'<span class="provider-count">' + totalProviders + ' providers, ' + totalKeys + ' keys</span>' +
2682-
'</div>';
2758+
let providersHtml = '<div>' +
2759+
'<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem;">' +
2760+
'<h4 style="margin: 0; font-size: 0.9375rem; font-weight: 600;">' + t('apiSettings.discoveredProviders') + '</h4>' +
2761+
'<span style="font-size: 0.75rem; color: hsl(var(--muted-foreground)); padding: 0.25rem 0.625rem; background: hsl(var(--muted) / 0.5); border-radius: 9999px;">' +
2762+
totalProviders + ' providers, ' + totalKeys + ' keys' +
2763+
'</span>' +
2764+
'</div>' +
2765+
'<div style="display: flex; flex-direction: column; gap: 0.75rem;">';
26832766

2684-
embeddingPoolDiscoveredProviders.forEach(function(provider) {
2767+
embeddingPoolDiscoveredProviders.forEach(function(provider, index) {
26852768
const isExcluded = excludedIds.indexOf(provider.providerId) > -1;
2686-
const icon = isExcluded ? 'x-circle' : 'check-circle';
26872769
const keyCount = provider.apiKeys?.length || 1;
2688-
const keyInfo = keyCount > 1 ? ' (' + keyCount + ' keys)' : '';
2689-
2690-
providersHtml += '<div class="discovered-provider-item' + (isExcluded ? ' excluded' : '') + '">' +
2691-
'<div class="discovered-provider-info">' +
2692-
'<i data-lucide="' + icon + '" class="provider-icon' + (isExcluded ? ' excluded' : '') + '"></i>' +
2693-
'<div>' +
2694-
'<div class="discovered-provider-name">' + escapeHtml(provider.providerName) + '</div>' +
2695-
'<div class="discovered-provider-keys">' + provider.modelName + keyInfo + '</div>' +
2696-
'</div>' +
2697-
'</div>' +
2698-
'<div class="discovered-provider-actions">' +
2699-
'<button class="btn btn-sm ' + (isExcluded ? 'btn-primary' : 'btn-outline') + '" onclick="toggleProviderExclusion(\'' + provider.providerId + '\')">' +
2700-
(isExcluded ? t('common.include') : t('apiSettings.excludeProvider')) +
2701-
'</button>' +
2770+
2771+
// Get provider icon
2772+
let providerIcon = 'server';
2773+
if (provider.providerName.toLowerCase().includes('openai')) providerIcon = 'brain';
2774+
else if (provider.providerName.toLowerCase().includes('modelscope')) providerIcon = 'cpu';
2775+
else if (provider.providerName.toLowerCase().includes('azure')) providerIcon = 'cloud';
2776+
2777+
providersHtml += '<div style="border: 1px solid hsl(var(--border)); border-radius: 0.5rem; padding: 1rem; ' +
2778+
(isExcluded ? 'opacity: 0.5; background: hsl(var(--muted) / 0.3);' : 'background: hsl(var(--card));') + '">' +
2779+
'<div style="display: flex; align-items: center; justify-between; margin-bottom: 0.75rem;">' +
2780+
'<div style="display: flex; align-items: center; gap: 0.75rem;">' +
2781+
'<div style="width: 2rem; height: 2rem; border-radius: 0.375rem; background: ' + (isExcluded ? 'hsl(var(--muted))' : 'hsl(var(--success) / 0.1)') + '; color: ' + (isExcluded ? 'hsl(var(--muted-foreground))' : 'hsl(var(--success))') + '; display: flex; align-items: center; justify-content: center;">' +
2782+
'<i data-lucide="' + providerIcon + '" style="width: 1.125rem; height: 1.125rem;"></i>' +
2783+
'</div>' +
2784+
'<div>' +
2785+
'<div style="font-weight: 500; font-size: 0.875rem; color: hsl(var(--foreground));">' + escapeHtml(provider.providerName) + '</div>' +
2786+
'<div style="font-size: 0.75rem; color: hsl(var(--muted-foreground));">' + provider.modelName + ' · ' + keyCount + ' key' + (keyCount > 1 ? 's' : '') + '</div>' +
2787+
'</div>' +
2788+
'</div>' +
2789+
'<button class="btn btn-sm ' + (isExcluded ? 'btn-primary' : 'btn-outline') + '" onclick="toggleProviderExclusion(\'' + provider.providerId + '\')" style="flex-shrink: 0;">' +
2790+
'<i data-lucide="' + (isExcluded ? 'plus' : 'x') + '" style="width: 0.875rem; height: 0.875rem;"></i> ' +
2791+
(isExcluded ? t('common.include') : t('apiSettings.excludeProvider')) +
2792+
'</button>' +
27022793
'</div>' +
2703-
'</div>';
2794+
'</div>';
27042795
});
27052796

27062797
providersHtml += '</div>';

ccw/src/templates/dashboard-js/views/codexlens-manager.js

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,16 +1947,9 @@ async function renderCodexLensManager() {
19471947
// Wait for LiteLLM config before loading semantic deps (it may need provider info)
19481948
await litellmPromise;
19491949

1950-
// Load semantic deps status (skip if we already have it from dashboard-init)
1951-
if (!dashboardData?.semantic) {
1952-
loadSemanticDepsStatus();
1953-
} else {
1954-
// Use cached semantic status from dashboard-init
1955-
var semanticContainer = document.getElementById('semanticDepsStatus');
1956-
if (semanticContainer && dashboardData.semantic) {
1957-
updateSemanticDepsUI(semanticContainer, dashboardData.semantic);
1958-
}
1959-
}
1950+
// Always load semantic deps status - it needs GPU detection and device list
1951+
// which are not included in the aggregated endpoint
1952+
loadSemanticDepsStatus();
19601953

19611954
loadModelList();
19621955

@@ -1970,20 +1963,6 @@ async function renderCodexLensManager() {
19701963
}
19711964
}
19721965

1973-
/**
1974-
* Update semantic deps UI from cached data
1975-
*/
1976-
function updateSemanticDepsUI(container, semanticData) {
1977-
if (!container) return;
1978-
1979-
if (semanticData.available) {
1980-
container.innerHTML = '<div class="flex items-center gap-2 text-success"><i data-lucide="check-circle" class="w-4 h-4"></i><span>' + (semanticData.backend || 'Ready') + '</span></div>';
1981-
} else {
1982-
container.innerHTML = '<div class="flex items-center gap-2 text-muted-foreground"><i data-lucide="circle-dashed" class="w-4 h-4"></i><span>' + t('codexlens.notInstalled') + '</span></div>';
1983-
}
1984-
if (window.lucide) lucide.createIcons();
1985-
}
1986-
19871966
/**
19881967
* Build CodexLens Manager page content
19891968
*/

0 commit comments

Comments
 (0)