|
|
@@ -40,10 +40,10 @@ class RequestThrottler {
|
|
|
getWaitTime(key: string): number {
|
|
|
const request = this.requests.get(key);
|
|
|
if (!request) return 0;
|
|
|
-
|
|
|
+
|
|
|
const now = Date.now();
|
|
|
if (now >= request.resetTime) return 0;
|
|
|
-
|
|
|
+
|
|
|
return request.resetTime - now;
|
|
|
}
|
|
|
}
|
|
|
@@ -58,18 +58,18 @@ function debounce<T extends (...args: any[]) => any>(
|
|
|
immediate?: boolean
|
|
|
): (...args: Parameters<T>) => void {
|
|
|
let timeout: NodeJS.Timeout | null = null;
|
|
|
-
|
|
|
+
|
|
|
return function executedFunction(...args: Parameters<T>) {
|
|
|
const later = () => {
|
|
|
timeout = null;
|
|
|
if (!immediate) func(...args);
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
const callNow = immediate && !timeout;
|
|
|
-
|
|
|
+
|
|
|
if (timeout) clearTimeout(timeout);
|
|
|
timeout = setTimeout(later, wait);
|
|
|
-
|
|
|
+
|
|
|
if (callNow) func(...args);
|
|
|
};
|
|
|
}
|
|
|
@@ -295,7 +295,7 @@ class ApiClient {
|
|
|
}
|
|
|
|
|
|
const endpoints = ['/queue/status', '/health', '/status', '/'];
|
|
|
-
|
|
|
+
|
|
|
for (const endpoint of endpoints) {
|
|
|
try {
|
|
|
const response = await fetch(`${this.getBaseUrl()}${endpoint}`, {
|
|
|
@@ -309,7 +309,7 @@ class ApiClient {
|
|
|
|
|
|
if (response.ok) {
|
|
|
const data = await response.json();
|
|
|
-
|
|
|
+
|
|
|
// For queue status, consider it healthy if it returns valid structure
|
|
|
if (endpoint === '/queue/status' && data.queue) {
|
|
|
const result = {
|
|
|
@@ -320,7 +320,7 @@ class ApiClient {
|
|
|
cache.set(cacheKey, result, 10000); // Cache for 10 seconds
|
|
|
return result;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// For other health endpoints
|
|
|
const healthStatus: HealthStatus = {
|
|
|
status: 'ok',
|
|
|
@@ -424,14 +424,14 @@ class ApiClient {
|
|
|
}
|
|
|
|
|
|
const result = await this.request<JobInfo>(`/queue/job/${jobId}`);
|
|
|
-
|
|
|
+
|
|
|
// Cache job status for a short time
|
|
|
if (result.status === 'processing' || result.status === 'queued') {
|
|
|
cache.set(cacheKey, result, 2000); // Cache for 2 seconds for active jobs
|
|
|
} else {
|
|
|
cache.set(cacheKey, result, 10000); // Cache for 10 seconds for completed jobs
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
@@ -494,7 +494,7 @@ class ApiClient {
|
|
|
async cancelJob(jobId: string): Promise<void> {
|
|
|
// Clear job status cache when cancelling
|
|
|
cache.delete(`job_status_${jobId}`);
|
|
|
-
|
|
|
+
|
|
|
return this.request<void>('/queue/cancel', {
|
|
|
method: 'POST',
|
|
|
body: JSON.stringify({ job_id: jobId }),
|
|
|
@@ -510,31 +510,31 @@ class ApiClient {
|
|
|
}
|
|
|
|
|
|
const response = await this.request<{ queue: QueueStatus }>('/queue/status');
|
|
|
-
|
|
|
+
|
|
|
// Cache queue status based on current activity
|
|
|
- const hasActiveJobs = response.queue.jobs.some(job =>
|
|
|
+ const hasActiveJobs = response.queue.jobs.some(job =>
|
|
|
job.status === 'processing' || job.status === 'queued'
|
|
|
);
|
|
|
-
|
|
|
+
|
|
|
// Cache for shorter time if there are active jobs
|
|
|
const cacheTime = hasActiveJobs ? 1000 : 5000; // 1 second for active, 5 seconds for idle
|
|
|
cache.set(cacheKey, response.queue, cacheTime);
|
|
|
-
|
|
|
+
|
|
|
return response.queue;
|
|
|
}
|
|
|
|
|
|
async clearQueue(): Promise<void> {
|
|
|
// Clear all related caches
|
|
|
cache.delete('queue_status');
|
|
|
-
|
|
|
+
|
|
|
return this.request<void>('/queue/clear', {
|
|
|
method: 'POST',
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// Model management
|
|
|
- async getModels(type?: string, loaded?: boolean): Promise<ModelInfo[]> {
|
|
|
- const cacheKey = `models_${type || 'all'}_${loaded ? 'loaded' : 'all'}`;
|
|
|
+ async getModels(type?: string, loaded?: boolean, page: number = 1, limit: number = 50, search?: string): Promise<{ models: ModelInfo[]; pagination: any; statistics: any }> {
|
|
|
+ const cacheKey = `models_${type || 'all'}_${loaded ? 'loaded' : 'all'}_${page}_${limit}_${search || 'all'}`;
|
|
|
const cachedResult = cache.get(cacheKey);
|
|
|
if (cachedResult) {
|
|
|
return cachedResult;
|
|
|
@@ -544,22 +544,65 @@ class ApiClient {
|
|
|
const params = [];
|
|
|
if (type && type !== 'loaded') params.push(`type=${type}`);
|
|
|
if (type === 'loaded' || loaded) params.push('loaded=true');
|
|
|
- // Request a high limit to get all models (default is 50)
|
|
|
- params.push('limit=1000');
|
|
|
+ params.push(`page=${page}`);
|
|
|
+ params.push(`limit=${limit}`);
|
|
|
+ if (search) params.push(`search=${encodeURIComponent(search)}`);
|
|
|
+
|
|
|
+ // Add include_metadata for additional information
|
|
|
+ params.push('include_metadata=true');
|
|
|
+
|
|
|
if (params.length > 0) endpoint += '?' + params.join('&');
|
|
|
|
|
|
- const response = await this.request<{ models: ModelInfo[] }>(endpoint);
|
|
|
+ const response = await this.request<{
|
|
|
+ models: ModelInfo[];
|
|
|
+ pagination: {
|
|
|
+ page: number;
|
|
|
+ limit: number;
|
|
|
+ total_count: number;
|
|
|
+ total_pages: number;
|
|
|
+ has_next: boolean;
|
|
|
+ has_prev: boolean
|
|
|
+ };
|
|
|
+ statistics: any;
|
|
|
+ }>(endpoint);
|
|
|
+
|
|
|
const models = response.models.map(model => ({
|
|
|
...model,
|
|
|
id: model.sha256_short || model.name,
|
|
|
size: model.file_size || model.size,
|
|
|
path: model.path || model.name,
|
|
|
}));
|
|
|
-
|
|
|
+
|
|
|
+ const result = {
|
|
|
+ models,
|
|
|
+ pagination: response.pagination,
|
|
|
+ statistics: response.statistics || {}
|
|
|
+ };
|
|
|
+
|
|
|
// Cache models for 30 seconds as they don't change frequently
|
|
|
- cache.set(cacheKey, models, 30000);
|
|
|
-
|
|
|
- return models;
|
|
|
+ cache.set(cacheKey, result, 30000);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get all models (for backward compatibility)
|
|
|
+ async getAllModels(type?: string, loaded?: boolean): Promise<ModelInfo[]> {
|
|
|
+ const allModels: ModelInfo[] = [];
|
|
|
+ let page = 1;
|
|
|
+ const limit = 100;
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ const response = await this.getModels(type, loaded, page, limit);
|
|
|
+ allModels.push(...response.models);
|
|
|
+
|
|
|
+ if (!response.pagination.has_next) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ page++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return allModels;
|
|
|
}
|
|
|
|
|
|
async getModelInfo(modelId: string): Promise<ModelInfo> {
|
|
|
@@ -577,7 +620,7 @@ class ApiClient {
|
|
|
async loadModel(modelId: string): Promise<void> {
|
|
|
// Clear model cache when loading
|
|
|
cache.delete(`model_info_${modelId}`);
|
|
|
-
|
|
|
+
|
|
|
return this.request<void>(`/models/${modelId}/load`, {
|
|
|
method: 'POST',
|
|
|
});
|
|
|
@@ -586,7 +629,7 @@ class ApiClient {
|
|
|
async unloadModel(modelId: string): Promise<void> {
|
|
|
// Clear model cache when unloading
|
|
|
cache.delete(`model_info_${modelId}`);
|
|
|
-
|
|
|
+
|
|
|
return this.request<void>(`/models/${modelId}/unload`, {
|
|
|
method: 'POST',
|
|
|
});
|
|
|
@@ -595,7 +638,7 @@ class ApiClient {
|
|
|
async scanModels(): Promise<void> {
|
|
|
// Clear all model caches when scanning
|
|
|
cache.clear();
|
|
|
-
|
|
|
+
|
|
|
return this.request<void>('/models/refresh', {
|
|
|
method: 'POST',
|
|
|
});
|