-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmodel-detector.ts
More file actions
278 lines (252 loc) · 7.97 KB
/
Copy pathmodel-detector.ts
File metadata and controls
278 lines (252 loc) · 7.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
// Device capability detection for AI model selection
// Detects WebGPU support, memory, and recommends the best backend
import { probeActualLimits, type GPUTierConfig } from './gpu-limits';
import { selectOptimalModel, type ModelRecommendation } from './model-selector';
export interface DeviceCapabilities {
hasWebGPU: boolean;
gpuTier: 'none' | 'low' | 'medium' | 'high';
gpuConfig: GPUTierConfig | null;
estimatedMemoryGB: number;
hasSufficientMemory: boolean;
recommendedBackend: 'huggingface' | 'webllm-gpu' | 'wllama-cpu';
recommendedModel: string;
modelRecommendation: ModelRecommendation | null;
canUseWebLLM: boolean;
}
/**
* Check if WebGPU is available in the browser
* Now uses probeActualLimits for more detailed detection
*/
export async function checkWebGPU(): Promise<boolean> {
const gpuConfig = await probeActualLimits();
return gpuConfig !== null;
}
/**
* Estimate GPU tier based on actual GPU limits
* Now uses probeActualLimits for accurate detection
*/
async function estimateGPUTier(): Promise<'none' | 'low' | 'medium' | 'high'> {
const gpuConfig = await probeActualLimits();
if (!gpuConfig) return 'none';
// Map GPUTierConfig tier to our tier system
switch (gpuConfig.tier) {
case 'discrete':
return 'high';
case 'integrated':
return 'medium';
case 'mobile':
return 'low';
default:
return 'none';
}
}
/**
* Estimate device memory (RAM) in GB
*/
function estimateMemory(): number {
try {
// @ts-ignore - deviceMemory is experimental
if (navigator.deviceMemory) {
// @ts-ignore
return navigator.deviceMemory as number;
}
// Fallback: estimate based on hardware concurrency (rough heuristic)
const cores = navigator.hardwareConcurrency || 2;
if (cores >= 8) return 8;
if (cores >= 4) return 4;
return 2;
} catch (error) {
console.warn('Memory estimation failed, assuming 4GB');
return 4;
}
}
/**
* Check if device has sufficient memory for WebLLM
* Minimum: 4GB for small models, 8GB+ recommended for larger models
*/
function checkSufficientMemory(memoryGB: number): boolean {
return memoryGB >= 4;
}
/**
* Recommend the best model based on device capabilities
* Now uses intelligent model selector
*/
function recommendModel(capabilities: Partial<DeviceCapabilities>): string {
const { hasWebGPU, estimatedMemoryGB, gpuConfig } = capabilities;
const recommendation = selectOptimalModel(
estimatedMemoryGB || 4,
hasWebGPU || false,
gpuConfig || null
);
console.log(`📊 Model recommendation: ${recommendation.displayName} (${recommendation.reason})`);
return recommendation.modelName;
}
/**
* Recommend the best AI backend based on device capabilities
* IMPORTANT: This does NOT check if HuggingFace API key is available.
* The recommendation is based purely on device capabilities.
* HuggingFace backend will only be used if the user explicitly provides an API key.
*/
function recommendBackend(
hasWebGPU: boolean,
gpuTier: 'none' | 'low' | 'medium' | 'high',
hasSufficientMemory: boolean
): 'huggingface' | 'webllm-gpu' | 'wllama-cpu' {
// If WebGPU is available and memory is sufficient, use WebLLM with GPU
if (hasWebGPU && hasSufficientMemory && (gpuTier === 'medium' || gpuTier === 'high')) {
return 'webllm-gpu';
}
// CRITICAL: If no WebGPU or insufficient memory, ALWAYS fallback to Wllama CPU
// This handles:
// - Desktop without GPU (uses wllama)
// - Mobile with low memory (uses wllama with small model)
// - Any device where WebGPU is not available
console.log('⚠️ No WebGPU available or insufficient memory, using Wllama (CPU/WASM)');
return 'wllama-cpu';
}
/**
* Detect all device capabilities and return recommendations
*/
export async function detectDeviceCapabilities(): Promise<DeviceCapabilities> {
console.log('🔍 Detecting device capabilities...');
const gpuConfig = await probeActualLimits();
const hasWebGPU = gpuConfig !== null;
const gpuTier = await estimateGPUTier();
const estimatedMemoryGB = estimateMemory();
const hasSufficientMemory = checkSufficientMemory(estimatedMemoryGB);
// Get model recommendation
const modelRecommendation = selectOptimalModel(
estimatedMemoryGB,
hasWebGPU,
gpuConfig
);
const capabilities: DeviceCapabilities = {
hasWebGPU,
gpuTier,
gpuConfig,
estimatedMemoryGB,
hasSufficientMemory,
recommendedBackend: 'wllama-cpu', // will be set below
recommendedModel: modelRecommendation.modelName,
modelRecommendation,
canUseWebLLM: hasWebGPU && hasSufficientMemory,
};
capabilities.recommendedBackend = recommendBackend(
hasWebGPU,
gpuTier,
hasSufficientMemory
);
console.log('📊 Device capabilities:', {
WebGPU: hasWebGPU ? '✅' : '❌',
'GPU Tier': gpuTier,
'GPU Config': gpuConfig ? `${gpuConfig.tier} (${Math.round(gpuConfig.maxBufferSize / 1024 / 1024)}MB buffer)` : 'N/A',
'Memory (GB)': estimatedMemoryGB,
'Sufficient Memory': hasSufficientMemory ? '✅' : '❌',
'Recommended Backend': capabilities.recommendedBackend,
'Recommended Model': `${modelRecommendation.displayName} (${modelRecommendation.size})`,
'Reason': modelRecommendation.reason,
});
return capabilities;
}
/**
* Get a human-readable description of the backend
*/
export function getBackendDescription(
backend: 'huggingface' | 'webllm-gpu' | 'webllm-cpu'
): string {
switch (backend) {
case 'huggingface':
return '🚀 HuggingFace API (rápido, requiere conexión)';
case 'webllm-gpu':
return '⚡ WebLLM con GPU (rápido, funciona offline)';
case 'webllm-cpu':
return '🐢 WebLLM con CPU (lento, funciona offline)';
}
}
/**
* Check if a specific model is available for WebLLM
* All WebLLM models end with '-MLC' suffix
*/
export function isWebLLMModel(modelName: string): boolean {
// All WebLLM models end with '-MLC' or '-MLC-1k' or similar patterns
return modelName.includes('-MLC');
}
/**
* Get list of available WebLLM models with metadata
*/
export interface WebLLMModelInfo {
name: string;
displayName: string;
size: string;
description: string;
minMemoryGB: number;
recommended: boolean;
}
export function getAvailableWebLLMModels(): WebLLMModelInfo[] {
return [
{
name: 'Llama-3.2-1B-Instruct-q4f16_1-MLC',
displayName: 'Llama 3.2 1B (Recomendado)',
size: '~700 MB',
description: 'Rápido y eficiente. Meta Llama 3.2',
minMemoryGB: 2,
recommended: true,
},
{
name: 'Llama-3.2-3B-Instruct-q4f16_1-MLC',
displayName: 'Llama 3.2 3B',
size: '~1.6 GB',
description: 'Mejor calidad. Meta Llama 3.2',
minMemoryGB: 4,
recommended: true,
},
{
name: 'Phi-3.5-mini-instruct-q4f16_1-MLC',
displayName: 'Phi-3.5 Mini',
size: '~1.9 GB',
description: 'Versátil y potente. Microsoft Phi-3.5',
minMemoryGB: 6,
recommended: true,
},
{
name: 'Qwen2.5-1.5B-Instruct-q4f16_1-MLC',
displayName: 'Qwen 2.5 1.5B',
size: '~900 MB',
description: 'Equilibrado. Alibaba Qwen 2.5',
minMemoryGB: 3,
recommended: true,
},
{
name: 'TinyLlama-1.1B-Chat-v1.0-q4f16_1-MLC',
displayName: 'TinyLlama 1.1B',
size: '~550 MB',
description: 'Muy ligero, calidad básica',
minMemoryGB: 2,
recommended: false,
},
{
name: 'Qwen2.5-0.5B-Instruct-q4f16_1-MLC',
displayName: 'Qwen 2.5 0.5B',
size: '~300 MB',
description: 'Modelo pequeño, rápido en CPU',
minMemoryGB: 2,
recommended: false,
},
{
name: 'SmolLM2-360M-Instruct-q4f16_1-MLC',
displayName: 'SmolLM2 360M',
size: '~200 MB',
description: 'Ultraligero para dispositivos limitados',
minMemoryGB: 1,
recommended: false,
},
{
name: 'SmolLM2-135M-Instruct-q0f16-MLC',
displayName: 'SmolLM2 135M (Mínimo)',
size: '~135 MB',
description: 'El más pequeño disponible',
minMemoryGB: 1,
recommended: false,
},
];
}