@@ -124,8 +124,7 @@ class Ruixen {
124124 constructor ( ) {
125125 guardianStore . subscribe ( ( g ) => {
126126 this . apiKey = g . apiKey || '' ;
127- // Hard-enforce free model to avoid non-free calls
128- this . model = 'openai/gpt-oss-120b:free' ;
127+ this . model = g . model || 'openai/gpt-oss-120b:free' ;
129128 this . analyzer = g . apiKey && g . apiKeyValid ? new AIAnalyzer ( g . apiKey , this . model ) : null ;
130129 } ) ;
131130 }
@@ -175,12 +174,7 @@ class Ruixen {
175174
176175 async analyzeWeeklyCloud ( pet : PetPanelData ) : Promise < string | null > {
177176 if ( ! this . analyzer ) return null ;
178- try {
179- return await this . analyzer . analyzeWeeklySummary ( pet ) ;
180- } catch ( e ) {
181- console . error ( 'Weekly cloud analysis failed:' , e ) ;
182- return null ;
183- }
177+ return this . tryWeeklyModelsSequentially ( pet ) ;
184178 }
185179
186180 private runQueue ( ) {
@@ -292,6 +286,7 @@ class Ruixen {
292286 healthConcerns : concerns ,
293287 recommendations : recs ,
294288 nextCheckupSuggestion : undefined ,
289+ modelUsed : 'offline' ,
295290 } ;
296291 }
297292
@@ -414,11 +409,17 @@ class Ruixen {
414409 } catch ( e ) {
415410 const msg = String ( ( e as Error ) ?. message || e ) ;
416411 const is429 = / 4 2 9 | R a t e l i m i t e x c e e d e d / i. test ( msg ) ;
412+ const providerUpstream = / t e m p o r a r i l y r a t e - l i m i t e d u p s t r e a m / i. test ( msg ) ;
417413 if ( ! is429 ) {
418414 console . warn ( 'Model attempt failed (non-429), aborting fallbacks:' , model , e ) ;
419415 return this . offlineHeuristic ( pet , entry ) ;
420416 }
421- console . warn ( 'Model rate-limited, trying next free model:' , model ) ;
417+ console . warn (
418+ providerUpstream
419+ ? 'Model upstream-limited, trying next free model:'
420+ : 'Model rate-limited, trying next free model:' ,
421+ model
422+ ) ;
422423 if ( i === this . fallbackModels . length - 1 ) {
423424 const today = new Date ( ) . toDateString ( ) ;
424425 setExhausted ( today ) ;
@@ -430,6 +431,32 @@ class Ruixen {
430431 }
431432 return this . offlineHeuristic ( pet , entry ) ;
432433 }
434+
435+ private async tryWeeklyModelsSequentially ( pet : PetPanelData ) : Promise < string | null > {
436+ if ( ! this . analyzer ) return null ;
437+ for ( let i = 0 ; i < this . fallbackModels . length ; i ++ ) {
438+ const model = this . fallbackModels [ i ] ;
439+ try {
440+ const text = await this . analyzer . analyzeWeeklySummary ( pet , model ) ;
441+ return `(${ model } )\n${ text } ` ;
442+ } catch ( e ) {
443+ const msg = String ( ( e as Error ) ?. message || e ) ;
444+ const is429 = / 4 2 9 | R a t e l i m i t e x c e e d e d / i. test ( msg ) ;
445+ if ( ! is429 ) {
446+ console . warn ( 'Weekly model failed (non-429), aborting fallbacks:' , model , e ) ;
447+ return null ;
448+ }
449+ if ( i === this . fallbackModels . length - 1 ) {
450+ const today = new Date ( ) . toDateString ( ) ;
451+ setExhausted ( today ) ;
452+ saveDailyCount ( { date : today , count : DAILY_FREE_LIMIT } ) ;
453+ this . dailyUsage . set ( DAILY_FREE_LIMIT ) ;
454+ return null ;
455+ }
456+ }
457+ }
458+ return null ;
459+ }
433460}
434461
435462export const ruixen = new Ruixen ( ) ;
@@ -444,6 +471,10 @@ export const ruixenHelpers = {
444471 analyzeWeeklyCloud ( pet : PetPanelData ) {
445472 return ruixen . analyzeWeeklyCloud ( pet ) ;
446473 } ,
474+ setModel ( model : string ) {
475+ guardianStore . update ( ( g ) => ( { ...g , model } as any ) ) ;
476+ return model ;
477+ } ,
447478 weeklySummary ( pet : PetPanelData ) {
448479 return ruixen . weeklySummary ( pet ) ;
449480 } ,
0 commit comments