Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / shared / lib / assistant / checkHardwareForAssistant.ts
blob0f3eba4b6e10b94e5200f650090708da72515b89
1 import { isBrave, isFirefox, isSafari } from '@proton/shared/lib/helpers/browser';
3 type HardwareSpecs = {
4     deviceMemory: any;
5     platform: string;
6     webGlRenderer: string;
7     webGlVendor: string;
8 };
10 export type GpuAssessmentResult =
11     | 'ok' // seems like WebGPU should work
12     | 'noWebGpu' // we cannot load WebGPU
13     | 'noWebGpuFirefox' // we cannot load WebGPU and we specifically know it's because the user uses Firefox
14     | 'noWebGpuSafari' // we cannot load WebGPU and we specifically know it's because the user uses Safari
15     | 'insufficientRam' // total ram can't hold the model in memory, and swapping would give terrible perfs
16     | 'macPreM1' // Mac detected, but it seems to be an older Intel CPU that cannot run LLMs
17     | 'noShaderF16' // this GPU is lacking newer features that are required for LLM text generation
18     | 'noShaderF16Brave' // in some cases Brave won't report shader-f16 despite the hardware supporting it
19     | 'maxBufferSizeTooLow'; // this GPU is likely underpowered
21 // TODO Fix the any
22 const isBlacklisted = (specs: HardwareSpecs, adapter: any): GpuAssessmentResult | null => {
23     // Returns null if not blacklisted, else returns a reason why it was blacklisted
25     // Check FP16 support.
26     // Some graphics cards like GTX 1060 have enough RAM (6GB) but lack FP16 support, so we must blacklist them.
27     if (!adapter.features.has('shader-f16')) {
28         console.error("Feature 'shader-f16' is not supported by this GPU adapter, but is required to run the LLM.");
29         if (isBrave()) {
30             console.error(
31                 "It looks like you're running Brave. In some cases, Brave may not expose the 'shader-f16' feature. " +
32                     'Consider trying in another Chromium browser.'
33             );
34             return 'noShaderF16Brave';
35         } else {
36             return 'noShaderF16';
37         }
38     }
40     // Unlike what the name says, 'MacIntel' appears for both older Intel CPUs and newer Apple CPUs (M1 and later).
41     let isMac = specs.platform === 'MacIntel';
42     if (isMac) {
43         // The following criterion on maxBufferSize was chosen not because of the impact on the LLM, but because
44         // based on hardware data analysis on a bunch of machines, it was a good differentiator of pre- vs post-M1.
45         const macPostM1 = adapter.limits.maxBufferSize >= 4294967292;
46         if (!macPostM1) {
47             console.error('Mac with Intel chips are not sufficiently powerful for LLM text generation.');
48             return 'macPreM1';
49         }
50     } else {
51         // We lack sufficient data to distinguish Windows/Linux machines that have decent hardware, but we've seen at
52         // least one Windows configuration where the maxBufferSize was < 4294967292 yet the LLM was working well.
53         // I'm putting this criterion for now, but it's likely inexact and will have to evolve over time.
54         const maxBufferSizeTooLow = adapter.limits.maxBufferSize < 2147483644;
55         if (maxBufferSizeTooLow) {
56             console.error(`maxBufferSize = ${adapter.limits.maxBufferSize} could be too low to run the LLM`);
57             return 'maxBufferSizeTooLow';
58         }
59     }
61     return null; // ok
64 export const checkHardwareForAssistant = async (): Promise<GpuAssessmentResult> => {
65     const canvas = document.createElement('canvas');
67     // Gather specs
68     let webGlRenderer: string | undefined;
69     let webGlVendor: string | undefined;
70     if (canvas) {
71         const gl = canvas.getContext('webgl');
72         if (!gl) {
73             return 'noWebGpu'; // no WebGL really, but it doesn't change the conclusion
74         }
75         webGlRenderer = gl.getParameter(gl.RENDERER);
76         webGlVendor = gl.getParameter(gl.VENDOR);
77     }
78     const specs: HardwareSpecs = {
79         platform: navigator.platform,
80         // @ts-ignore
81         deviceMemory: navigator.deviceMemory || null,
82         webGlRenderer: webGlRenderer || '',
83         webGlVendor: webGlVendor || '',
84     };
86     // Test if there's enough memory
87     // ...except for Brave, which under-reports the device memory
88     // https://github.com/brave/brave-browser/issues/1157
89     if (!isBrave()) {
90         if (specs.deviceMemory !== null && specs.deviceMemory < 8) {
91             console.error('This machine reports RAM under 8GB which may be too low to run the LLM.');
92             return 'insufficientRam';
93         }
94     }
96     // Test if we can load webgpu
97     try {
98         const navigator = globalThis.navigator as any;
99         // TODO Fix the any
100         const adapter: any | undefined = await navigator.gpu?.requestAdapter();
101         if (!adapter) {
102             console.error('WebGPU is not available.');
103             if (isFirefox()) {
104                 return 'noWebGpuFirefox';
105             } else if (isSafari()) {
106                 return 'noWebGpuSafari';
107             } else {
108                 return 'noWebGpu';
109             }
110         }
111         // Test if system is not blacklisted
112         const reason = isBlacklisted(specs, adapter);
113         if (reason) {
114             return reason;
115         }
116     } catch (e) {
117         console.error(e);
118         if (isFirefox()) {
119             return 'noWebGpuFirefox';
120         } else if (isSafari()) {
121             return 'noWebGpuSafari';
122         } else {
123             return 'noWebGpu';
124         }
125     }
127     return 'ok';