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