@@ -18,6 +18,9 @@ import {
1818
1919@Discord ( )
2020export class AiChat {
21+ private static readonly MAX_RETRIES = 5 ;
22+ private static readonly BASE_DELAY = 1000 ; // 1 second
23+
2124 @On ( )
2225 async messageCreate (
2326 [ message ] : ArgsOf < "messageCreate" > ,
@@ -63,60 +66,82 @@ export class AiChat {
6366 const userContext = await this . getUserContext ( message . author . id , message ) ;
6467 const fullMessage = `${ userMsg } ${ replyContext } ${ userContext } ` ;
6568
66- try {
67- const messageImages = await makeImageParts ( message ) ;
68- const allImages = [ ...messageImages , ...repliedImages ] ;
69-
70- // Create user message with context
71- const userMessage : ModelMessage =
72- allImages . length > 0
73- ? {
74- role : "user" ,
75- content : [
76- { type : "text" , text : fullMessage } ,
77- ...allImages . map ( ( url ) => ( {
78- type : "image" as const ,
79- image : url ,
80- } ) ) ,
81- ] ,
82- }
83- : {
84- role : "user" ,
85- content : fullMessage ,
86- } ;
87-
88- // Add user message to history
89- messages . push ( userMessage ) ;
90-
91- const { text, steps } = await generateText ( {
92- model : google ( "gemini-2.5-flash" ) ,
93- system : AI_SYSTEM_PROMPT ,
94- messages : [ ...messages ] ,
95- tools : TOOLS ,
96- } ) ;
97-
98- messages . push ( { role : "assistant" , content : text ?. trim ( ) } ) ;
99-
100- // Trim history if too long
101- if ( messages . length > MAX_MESSAGES_PER_CHANNEL ) {
102- messages . splice ( 0 , messages . length - MAX_MESSAGES_PER_CHANNEL ) ;
103- }
69+ // Retry logic with exponential backoff
70+ let lastError : Error | null = null ;
71+
72+ for ( let attempt = 0 ; attempt < AiChat . MAX_RETRIES ; attempt ++ ) {
73+ try {
74+ const messageImages = await makeImageParts ( message ) ;
75+ const allImages = [ ...messageImages , ...repliedImages ] ;
76+
77+ // Create user message with context
78+ const userMessage : ModelMessage =
79+ allImages . length > 0
80+ ? {
81+ role : "user" ,
82+ content : [
83+ { type : "text" , text : fullMessage } ,
84+ ...allImages . map ( ( url ) => ( {
85+ type : "image" as const ,
86+ image : url ,
87+ } ) ) ,
88+ ] ,
89+ }
90+ : {
91+ role : "user" ,
92+ content : fullMessage ,
93+ } ;
94+
95+ // Add user message to history
96+ messages . push ( userMessage ) ;
97+
98+ const { text, steps } = await generateText ( {
99+ model : google ( "gemini-2.5-flash" ) ,
100+ system : AI_SYSTEM_PROMPT ,
101+ messages : [ ...messages ] ,
102+ tools : TOOLS ,
103+ } ) ;
104+
105+ messages . push ( { role : "assistant" , content : text ?. trim ( ) } ) ;
106+
107+ // Trim history if too long
108+ if ( messages . length > MAX_MESSAGES_PER_CHANNEL ) {
109+ messages . splice ( 0 , messages . length - MAX_MESSAGES_PER_CHANNEL ) ;
110+ }
104111
105- channelMessages . set ( message . channel . id , messages ) ;
106-
107- const gifUrl = this . extractGifFromSteps ( steps ) ;
108- await message . reply ( {
109- content : text ?. trim ( ) ,
110- files : gifUrl
111- ? [ { attachment : gifUrl , name : "reaction.gif" } ]
112- : undefined ,
113- } ) ;
114- } catch ( err ) {
115- error ( "AI error:" , err ) ;
116- await message . reply (
117- "Something went wrong while thinking. Try again later!"
118- ) ;
112+ channelMessages . set ( message . channel . id , messages ) ;
113+
114+ const gifUrl = this . extractGifFromSteps ( steps ) ;
115+ await message . reply ( {
116+ content : text ?. trim ( ) ,
117+ files : gifUrl
118+ ? [ { attachment : gifUrl , name : "reaction.gif" } ]
119+ : undefined ,
120+ } ) ;
121+
122+ // Success - exit retry loop
123+ return ;
124+ } catch ( err ) {
125+ lastError = err as Error ;
126+ error ( `AI error (attempt ${ attempt + 1 } /${ AiChat . MAX_RETRIES } ):` , err ) ;
127+
128+ // Don't wait after the last attempt
129+ if ( attempt < AiChat . MAX_RETRIES - 1 ) {
130+ const delay = AiChat . BASE_DELAY * Math . pow ( 2 , attempt ) ;
131+ console . log ( `Retrying in ${ delay } ms...` ) ;
132+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
133+ }
134+ }
119135 }
136+
137+ // All retries failed
138+ error (
139+ `All ${ AiChat . MAX_RETRIES } AI attempts failed. Last error:` ,
140+ lastError
141+ ) ;
142+ await message . reply (
143+ "Something went wrong while thinking. Try again later!"
144+ ) ;
120145 }
121146
122147 private async getUserContext (
0 commit comments