@@ -1789,10 +1789,13 @@ const loadNnueModel = async (
17891789 storage : StockfishModelStorage ,
17901790 timeoutMs : number ,
17911791 onNetworkFetchStart ?: ( ) => void ,
1792+ forceRefresh = false ,
17921793) : Promise < ArrayBuffer > => {
1793- const cachedModel = await storage . getModel ( modelUrl )
1794- if ( cachedModel ) {
1795- return cachedModel
1794+ if ( ! forceRefresh ) {
1795+ const cachedModel = await storage . getModel ( modelUrl )
1796+ if ( cachedModel ) {
1797+ return cachedModel
1798+ }
17961799 }
17971800
17981801 onNetworkFetchStart ?.( )
@@ -1808,16 +1811,27 @@ const loadNnueModel = async (
18081811 return buffer
18091812}
18101813
1814+ const shouldRetryNnueLoad = ( error : unknown ) : boolean => {
1815+ if ( error instanceof RangeError ) {
1816+ return true
1817+ }
1818+
1819+ if ( ! ( error instanceof Error ) ) {
1820+ return false
1821+ }
1822+
1823+ return (
1824+ / o f f s e t i s o u t o f b o u n d s / i. test ( error . message ) ||
1825+ / c o u l d n o t a l l o c a t e / i. test ( error . message ) ||
1826+ / b y t e s \? / i. test ( error . message )
1827+ )
1828+ }
1829+
18111830const setupStockfish = async (
18121831 onPhaseChange ?: ( phase : StockfishInitPhase ) => void ,
18131832) : Promise < StockfishWeb > => {
1814- onPhaseChange ?.( 'loading-module' )
18151833 // eslint-disable-next-line @typescript-eslint/no-explicit-any
18161834 const makeModule : any = await import ( 'lila-stockfish-web/sf17-79.js' )
1817- const instance : StockfishWeb = await makeModule . default ( {
1818- wasmMemory : sharedWasmMemory ( 2560 ) ,
1819- locateFile : ( name : string ) => `/stockfish/${ name } ` ,
1820- } )
18211835
18221836 // NNUE weights served via raw.githubusercontent.com permalink (CORS + COEP compatible).
18231837 // Override with NEXT_PUBLIC_STOCKFISH_NNUE_BASE_URL for self-hosted deployments.
@@ -1826,31 +1840,84 @@ const setupStockfish = async (
18261840 'https://raw.githubusercontent.com/CSSLab/maia-platform-frontend/e23a50e/public/stockfish'
18271841 const storage = new StockfishModelStorage ( )
18281842 await storage . requestPersistentStorage ( )
1829-
1830- const nnue0Url = `${ nnueBaseUrl } /${ instance . getRecommendedNnue ( 0 ) } `
1831- const nnue1Url = `${ nnueBaseUrl } /${ instance . getRecommendedNnue ( 1 ) } `
18321843 const timeoutMs = getNnueFetchTimeoutMs ( )
1833- let downloadStarted = false
1844+ let nnueUrls : [ string , string ] | null = null
1845+
1846+ const createInstance = async ( ) : Promise < StockfishWeb > => {
1847+ onPhaseChange ?.( 'loading-module' )
1848+ return makeModule . default ( {
1849+ wasmMemory : sharedWasmMemory ( 2560 ) ,
1850+ locateFile : ( name : string ) => `/stockfish/${ name } ` ,
1851+ } )
1852+ }
1853+
1854+ const loadWeightsIntoInstance = async (
1855+ instance : StockfishWeb ,
1856+ forceRefresh = false ,
1857+ ) : Promise < void > => {
1858+ if ( ! nnueUrls ) {
1859+ throw new Error ( 'Stockfish recommended NNUE URLs were not initialized' )
1860+ }
1861+
1862+ let downloadStarted = false
18341863
1835- try {
18361864 onPhaseChange ?.( 'checking-cache' )
18371865 const buffers = await Promise . all ( [
1838- loadNnueModel ( nnue0Url , storage , timeoutMs , ( ) => {
1839- if ( ! downloadStarted ) {
1840- downloadStarted = true
1841- onPhaseChange ?.( 'downloading-nnue' )
1842- }
1843- } ) ,
1844- loadNnueModel ( nnue1Url , storage , timeoutMs , ( ) => {
1845- if ( ! downloadStarted ) {
1846- downloadStarted = true
1847- onPhaseChange ?.( 'downloading-nnue' )
1848- }
1849- } ) ,
1866+ loadNnueModel (
1867+ nnueUrls [ 0 ] ,
1868+ storage ,
1869+ timeoutMs ,
1870+ ( ) => {
1871+ if ( ! downloadStarted ) {
1872+ downloadStarted = true
1873+ onPhaseChange ?.( 'downloading-nnue' )
1874+ }
1875+ } ,
1876+ forceRefresh ,
1877+ ) ,
1878+ loadNnueModel (
1879+ nnueUrls [ 1 ] ,
1880+ storage ,
1881+ timeoutMs ,
1882+ ( ) => {
1883+ if ( ! downloadStarted ) {
1884+ downloadStarted = true
1885+ onPhaseChange ?.( 'downloading-nnue' )
1886+ }
1887+ } ,
1888+ forceRefresh ,
1889+ ) ,
18501890 ] )
1891+
18511892 onPhaseChange ?.( 'loading-nnue' )
18521893 instance . setNnueBuffer ( new Uint8Array ( buffers [ 0 ] ) , 0 )
18531894 instance . setNnueBuffer ( new Uint8Array ( buffers [ 1 ] ) , 1 )
1895+ }
1896+
1897+ try {
1898+ let instance = await createInstance ( )
1899+ nnueUrls = [
1900+ `${ nnueBaseUrl } /${ instance . getRecommendedNnue ( 0 ) } ` ,
1901+ `${ nnueBaseUrl } /${ instance . getRecommendedNnue ( 1 ) } ` ,
1902+ ]
1903+
1904+ try {
1905+ await loadWeightsIntoInstance ( instance )
1906+ } catch ( error ) {
1907+ if ( ! shouldRetryNnueLoad ( error ) ) {
1908+ throw error
1909+ }
1910+
1911+ console . warn (
1912+ 'Stockfish NNUE load failed; clearing cached weights and retrying once.' ,
1913+ error ,
1914+ )
1915+ await Promise . all ( nnueUrls . map ( ( url ) => storage . deleteModel ( url ) ) )
1916+
1917+ instance = await createInstance ( )
1918+ await loadWeightsIntoInstance ( instance , true )
1919+ }
1920+
18541921 return instance
18551922 } catch ( error ) {
18561923 console . error ( 'Failed to load NNUE models:' , error )
0 commit comments