| |
| |
| |
| |
|
|
| 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'); |
|
|
|
|