|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { api } from './api-client.js'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class ModelsClient { |
|
|
constructor() { |
|
|
this.models = []; |
|
|
this.healthRegistry = []; |
|
|
this.lastUpdate = null; |
|
|
this.statusCache = null; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getModelsSummary() { |
|
|
try { |
|
|
console.log('[ModelsClient] Fetching models summary from /api/models/summary'); |
|
|
const response = await api.get('/models/summary'); |
|
|
|
|
|
|
|
|
if (!response) { |
|
|
throw new Error('Empty response from /api/models/summary'); |
|
|
} |
|
|
|
|
|
|
|
|
if (response.fallback === true || (response.ok === false && !response.summary)) { |
|
|
console.warn('[ModelsClient] Received fallback or error response:', response); |
|
|
|
|
|
} |
|
|
|
|
|
this.models = []; |
|
|
this.healthRegistry = response.health_registry || []; |
|
|
this.lastUpdate = new Date(); |
|
|
this.statusCache = response; |
|
|
|
|
|
|
|
|
if (response.categories && typeof response.categories === 'object') { |
|
|
for (const [category, categoryModels] of Object.entries(response.categories)) { |
|
|
if (Array.isArray(categoryModels)) { |
|
|
categoryModels.forEach(model => { |
|
|
if (model && typeof model === 'object') { |
|
|
this.models.push({ |
|
|
...model, |
|
|
category |
|
|
}); |
|
|
} |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const summary = response.summary || {}; |
|
|
console.log('[ModelsClient] Models summary loaded:', { |
|
|
total: summary.total_models || 0, |
|
|
loaded: summary.loaded_models || 0, |
|
|
failed: summary.failed_models || 0, |
|
|
categories: Object.keys(response.categories || {}).length, |
|
|
healthEntries: this.healthRegistry.length |
|
|
}); |
|
|
|
|
|
return response; |
|
|
} catch (error) { |
|
|
const safeError = error || new Error('Unknown error'); |
|
|
console.error('[ModelsClient] Failed to get models summary:', safeError); |
|
|
console.error('[ModelsClient] Error details:', { |
|
|
message: safeError?.message || 'Unknown error', |
|
|
stack: safeError?.stack || 'No stack trace', |
|
|
name: safeError?.name || 'Error' |
|
|
}); |
|
|
|
|
|
|
|
|
return { |
|
|
ok: false, |
|
|
error: safeError?.message || 'Unknown error', |
|
|
fallback: true, |
|
|
summary: { |
|
|
total_models: 0, |
|
|
loaded_models: 0, |
|
|
failed_models: 0, |
|
|
hf_mode: 'error', |
|
|
transformers_available: false |
|
|
}, |
|
|
categories: {}, |
|
|
health_registry: [], |
|
|
timestamp: new Date().toISOString() |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getModelsStatus() { |
|
|
try { |
|
|
console.log('[ModelsClient] Fetching models status from /api/models/status'); |
|
|
const response = await api.getModelsStatus(); |
|
|
|
|
|
|
|
|
if (!response) { |
|
|
throw new Error('Empty response from /api/models/status'); |
|
|
} |
|
|
|
|
|
|
|
|
console.log('[ModelsClient] Models status loaded:', { |
|
|
success: response.success, |
|
|
loaded: response.models_loaded || 0, |
|
|
failed: response.models_failed || 0, |
|
|
hf_mode: response.hf_mode || 'unknown' |
|
|
}); |
|
|
|
|
|
return response; |
|
|
} catch (error) { |
|
|
const safeError = error || new Error('Unknown error'); |
|
|
console.error('[ModelsClient] Failed to get models status:', safeError); |
|
|
console.error('[ModelsClient] Error details:', { |
|
|
message: safeError?.message || 'Unknown error', |
|
|
stack: safeError?.stack || 'No stack trace', |
|
|
name: safeError?.name || 'Error' |
|
|
}); |
|
|
|
|
|
|
|
|
return { |
|
|
success: false, |
|
|
status: 'error', |
|
|
status_message: `Error retrieving model status: ${safeError?.message || 'Unknown error'}`, |
|
|
error: safeError?.message || 'Unknown error', |
|
|
models_loaded: 0, |
|
|
models_failed: 0, |
|
|
hf_mode: 'unknown', |
|
|
transformers_available: false, |
|
|
fallback: true, |
|
|
timestamp: new Date().toISOString() |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getHealthRegistry() { |
|
|
try { |
|
|
const summary = await this.getModelsSummary(); |
|
|
const registry = summary?.health_registry || []; |
|
|
console.log(`[ModelsClient] Health registry loaded: ${registry.length} entries`); |
|
|
return registry; |
|
|
} catch (error) { |
|
|
const safeError = error || new Error('Unknown error'); |
|
|
console.error('[ModelsClient] Failed to get health registry:', safeError?.message || 'Unknown error'); |
|
|
return []; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async testModel(modelKey, text) { |
|
|
try { |
|
|
return await api.testModel(modelKey, text); |
|
|
} catch (error) { |
|
|
const safeError = error || new Error('Unknown error'); |
|
|
console.error(`Failed to test model ${modelKey}:`, safeError); |
|
|
|
|
|
return { |
|
|
success: false, |
|
|
error: safeError?.message || 'Unknown error', |
|
|
model: modelKey, |
|
|
result: { |
|
|
sentiment: 'neutral', |
|
|
score: 0.5, |
|
|
confidence: 0.5 |
|
|
}, |
|
|
fallback: true |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async analyzeSentiment(text, mode = 'crypto', modelKey = null) { |
|
|
try { |
|
|
return await api.analyzeSentiment(text, mode, modelKey); |
|
|
} catch (error) { |
|
|
const safeError = error || new Error('Unknown error'); |
|
|
console.error('Failed to analyze sentiment:', safeError); |
|
|
|
|
|
return { |
|
|
success: false, |
|
|
error: safeError?.message || 'Unknown error', |
|
|
sentiment: 'neutral', |
|
|
score: 0.5, |
|
|
confidence: 0.5, |
|
|
model: modelKey || 'fallback', |
|
|
fallback: true |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getModel(key) { |
|
|
return this.models.find(m => m.key === key); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getModelsByCategory(category) { |
|
|
return this.models.filter(m => m.category === category); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getLoadedModels() { |
|
|
return this.models.filter(m => m.loaded); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getFailedModels() { |
|
|
return this.models.filter(m => m.status === 'unavailable' || m.error_count > 0); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getHealthyModels() { |
|
|
return this.models.filter(m => m.status === 'healthy'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
formatModelStatus(model) { |
|
|
const statusIcons = { |
|
|
'healthy': '✓', |
|
|
'degraded': '⚠', |
|
|
'unavailable': '✗', |
|
|
'unknown': '?' |
|
|
}; |
|
|
|
|
|
const statusColors = { |
|
|
'healthy': '#22c55e', |
|
|
'degraded': '#f59e0b', |
|
|
'unavailable': '#ef4444', |
|
|
'unknown': '#64748b' |
|
|
}; |
|
|
|
|
|
return { |
|
|
icon: statusIcons[model.status] || '?', |
|
|
color: statusColors[model.status] || '#64748b', |
|
|
text: model.status || 'unknown' |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getCategoryStats() { |
|
|
const stats = {}; |
|
|
|
|
|
this.models.forEach(model => { |
|
|
const cat = model.category || 'other'; |
|
|
if (!stats[cat]) { |
|
|
stats[cat] = { |
|
|
total: 0, |
|
|
loaded: 0, |
|
|
healthy: 0, |
|
|
degraded: 0, |
|
|
unavailable: 0 |
|
|
}; |
|
|
} |
|
|
|
|
|
stats[cat].total++; |
|
|
if (model.loaded) stats[cat].loaded++; |
|
|
if (model.status === 'healthy') stats[cat].healthy++; |
|
|
if (model.status === 'degraded') stats[cat].degraded++; |
|
|
if (model.status === 'unavailable') stats[cat].unavailable++; |
|
|
}); |
|
|
|
|
|
return stats; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getSummaryStats() { |
|
|
if (this.statusCache && this.statusCache.summary) { |
|
|
return this.statusCache.summary; |
|
|
} |
|
|
|
|
|
return { |
|
|
total_models: this.models.length, |
|
|
loaded_models: this.getLoadedModels().length, |
|
|
failed_models: this.getFailedModels().length, |
|
|
hf_mode: 'unknown', |
|
|
transformers_available: false |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async refresh() { |
|
|
console.log('[ModelsClient] Force refreshing models data...'); |
|
|
|
|
|
|
|
|
try { |
|
|
if (api && typeof api.clearCacheEntry === 'function') { |
|
|
api.clearCacheEntry('/models/summary'); |
|
|
api.clearCacheEntry('/models/status'); |
|
|
console.log('[ModelsClient] Cleared API cache for models endpoints'); |
|
|
} else if (api && typeof api.clearCache === 'function') { |
|
|
|
|
|
api.clearCache(); |
|
|
console.log('[ModelsClient] Cleared all API cache'); |
|
|
} |
|
|
} catch (e) { |
|
|
console.warn('[ModelsClient] Failed to clear cache:', e); |
|
|
} |
|
|
|
|
|
|
|
|
this.statusCache = null; |
|
|
this.models = []; |
|
|
this.healthRegistry = []; |
|
|
this.lastUpdate = null; |
|
|
|
|
|
|
|
|
return await this.getModelsSummary(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
isStale(maxAge = 60000) { |
|
|
if (!this.lastUpdate) return true; |
|
|
return (Date.now() - this.lastUpdate.getTime()) > maxAge; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const modelsClient = new ModelsClient(); |
|
|
export default modelsClient; |
|
|
|
|
|
console.log('[ModelsClient] Initialized'); |
|
|
|
|
|
|