@@ -117,7 +117,6 @@ public enum KernelRequestExceptionType
117117 OperationCancelled ,
118118}
119119
120-
121120/// <summary>
122121/// Represents errors that occur during requests to LLM providers.
123122/// This class normalizes various provider-specific exceptions into a unified format.
@@ -158,35 +157,25 @@ public ChatRequestException(
158157 {
159158 OriginalException = originalException ;
160159 ExceptionType = type ;
161- FriendlyMessageKey = customFriendlyMessageKey ?? new DynamicResourceKey ( type switch
162- {
163- KernelRequestExceptionType . InvalidConfiguration => LocaleKey . KernelRequestException_InvalidConfiguration ,
164- KernelRequestExceptionType . InvalidApiKey => LocaleKey . KernelRequestException_InvalidApiKey ,
165- KernelRequestExceptionType . QuotaExceeded => LocaleKey . KernelRequestException_QuotaExceeded ,
166- KernelRequestExceptionType . RateLimit => LocaleKey . KernelRequestException_RateLimit ,
167- KernelRequestExceptionType . EndpointNotReachable => LocaleKey . KernelRequestException_EndpointNotReachable ,
168- KernelRequestExceptionType . InvalidEndpoint => LocaleKey . KernelRequestException_InvalidEndpoint ,
169- KernelRequestExceptionType . EmptyResponse => LocaleKey . KernelRequestException_EmptyResponse ,
170- KernelRequestExceptionType . FeatureNotSupport => LocaleKey . KernelRequestException_FeatureNotSupport ,
171- KernelRequestExceptionType . Timeout => LocaleKey . KernelRequestException_Timeout ,
172- KernelRequestExceptionType . NetworkError => LocaleKey . KernelRequestException_NetworkError ,
173- KernelRequestExceptionType . ServiceUnavailable => LocaleKey . KernelRequestException_ServiceUnavailable ,
174- KernelRequestExceptionType . OperationCancelled => LocaleKey . KernelRequestException_OperationCancelled ,
175- _ => LocaleKey . KernelRequestException_Unknown ,
176- } ) ;
160+ FriendlyMessageKey = customFriendlyMessageKey ?? new DynamicResourceKey (
161+ type switch
162+ {
163+ KernelRequestExceptionType . InvalidConfiguration => LocaleKey . KernelRequestException_InvalidConfiguration ,
164+ KernelRequestExceptionType . InvalidApiKey => LocaleKey . KernelRequestException_InvalidApiKey ,
165+ KernelRequestExceptionType . QuotaExceeded => LocaleKey . KernelRequestException_QuotaExceeded ,
166+ KernelRequestExceptionType . RateLimit => LocaleKey . KernelRequestException_RateLimit ,
167+ KernelRequestExceptionType . EndpointNotReachable => LocaleKey . KernelRequestException_EndpointNotReachable ,
168+ KernelRequestExceptionType . InvalidEndpoint => LocaleKey . KernelRequestException_InvalidEndpoint ,
169+ KernelRequestExceptionType . EmptyResponse => LocaleKey . KernelRequestException_EmptyResponse ,
170+ KernelRequestExceptionType . FeatureNotSupport => LocaleKey . KernelRequestException_FeatureNotSupport ,
171+ KernelRequestExceptionType . Timeout => LocaleKey . KernelRequestException_Timeout ,
172+ KernelRequestExceptionType . NetworkError => LocaleKey . KernelRequestException_NetworkError ,
173+ KernelRequestExceptionType . ServiceUnavailable => LocaleKey . KernelRequestException_ServiceUnavailable ,
174+ KernelRequestExceptionType . OperationCancelled => LocaleKey . KernelRequestException_OperationCancelled ,
175+ _ => LocaleKey . KernelRequestException_Unknown ,
176+ } ) ;
177177 }
178178
179- private static readonly List < IExceptionParser > Parsers =
180- [
181- new GoogleApiExceptionParser ( ) ,
182- new ClientResultExceptionParser ( ) ,
183- new HttpRequestExceptionParser ( ) ,
184- new OllamaExceptionParser ( ) ,
185- new HttpOperationExceptionParser ( ) ,
186- new GeneralExceptionParser ( ) ,
187- new HttpStatusCodeParser ( ) ,
188- ] ;
189-
190179 /// <summary>
191180 /// Parses a generic <see cref="Exception"/> into a <see cref="ChatRequestException"/>.
192181 /// </summary>
@@ -197,14 +186,13 @@ public ChatRequestException(
197186 public static ChatRequestException Parse ( Exception exception , string ? modelProviderId = null , string ? modelId = null )
198187 {
199188 var context = new ExceptionParsingContext ( exception ) ;
200-
201- foreach ( var parser in Parsers )
202- {
203- if ( parser . TryParse ( context ) )
204- {
205- break ;
206- }
207- }
189+ new ParserChain < ClientResultExceptionParser ,
190+ ParserChain < GoogleApiExceptionParser ,
191+ ParserChain < HttpRequestExceptionParser ,
192+ ParserChain < OllamaExceptionParser ,
193+ ParserChain < HttpOperationExceptionParser ,
194+ GeneralExceptionParser > > > > > ( ) . TryParse ( ref context ) ;
195+ new HttpStatusCodeParser ( ) . TryParse ( ref context ) ;
208196
209197 return new ChatRequestException (
210198 originalException : exception ,
@@ -238,7 +226,17 @@ public ChatFunctionCallException(Exception originalException, DynamicResourceKey
238226
239227#region Exception Parsers
240228
241- internal class ExceptionParsingContext ( Exception exception )
229+ internal readonly struct ParserChain < T1 , T2 > : IExceptionParser
230+ where T1 : struct , IExceptionParser
231+ where T2 : struct , IExceptionParser
232+ {
233+ public bool TryParse ( ref ExceptionParsingContext context )
234+ {
235+ return default ( T1 ) . TryParse ( ref context ) || default ( T2 ) . TryParse ( ref context ) ;
236+ }
237+ }
238+
239+ internal ref struct ExceptionParsingContext ( Exception exception )
242240{
243241 public Exception Exception { get ; } = exception ;
244242 public KernelRequestExceptionType ? ExceptionType { get ; set ; }
@@ -247,12 +245,12 @@ internal class ExceptionParsingContext(Exception exception)
247245
248246internal interface IExceptionParser
249247{
250- bool TryParse ( ExceptionParsingContext context ) ;
248+ bool TryParse ( ref ExceptionParsingContext context ) ;
251249}
252250
253- internal class ClientResultExceptionParser : IExceptionParser
251+ internal struct ClientResultExceptionParser : IExceptionParser
254252{
255- public bool TryParse ( ExceptionParsingContext context )
253+ public bool TryParse ( ref ExceptionParsingContext context )
256254 {
257255 if ( context . Exception is not ClientResultException clientResult )
258256 {
@@ -271,9 +269,9 @@ public bool TryParse(ExceptionParsingContext context)
271269 }
272270}
273271
274- internal class GoogleApiExceptionParser : IExceptionParser
272+ internal struct GoogleApiExceptionParser : IExceptionParser
275273{
276- public bool TryParse ( ExceptionParsingContext context )
274+ public bool TryParse ( ref ExceptionParsingContext context )
277275 {
278276 if ( context . Exception is not GoogleApiException googleApi )
279277 {
@@ -305,9 +303,9 @@ public bool TryParse(ExceptionParsingContext context)
305303 }
306304}
307305
308- internal class HttpRequestExceptionParser : IExceptionParser
306+ internal readonly struct HttpRequestExceptionParser : IExceptionParser
309307{
310- public bool TryParse ( ExceptionParsingContext context )
308+ public bool TryParse ( ref ExceptionParsingContext context )
311309 {
312310 if ( context . Exception is not HttpRequestException httpRequest )
313311 {
@@ -331,9 +329,9 @@ public bool TryParse(ExceptionParsingContext context)
331329 }
332330}
333331
334- internal class OllamaExceptionParser : IExceptionParser
332+ internal readonly struct OllamaExceptionParser : IExceptionParser
335333{
336- public bool TryParse ( ExceptionParsingContext context )
334+ public bool TryParse ( ref ExceptionParsingContext context )
337335 {
338336 if ( context . Exception is not OllamaException ollama )
339337 {
@@ -354,9 +352,9 @@ public bool TryParse(ExceptionParsingContext context)
354352 }
355353}
356354
357- internal class HttpOperationExceptionParser : IExceptionParser
355+ internal readonly struct HttpOperationExceptionParser : IExceptionParser
358356{
359- public bool TryParse ( ExceptionParsingContext context )
357+ public bool TryParse ( ref ExceptionParsingContext context )
360358 {
361359 if ( context . Exception is not HttpOperationException httpOperation )
362360 {
@@ -367,9 +365,9 @@ public bool TryParse(ExceptionParsingContext context)
367365 }
368366}
369367
370- internal class GeneralExceptionParser : IExceptionParser
368+ internal readonly struct GeneralExceptionParser : IExceptionParser
371369{
372- public bool TryParse ( ExceptionParsingContext context )
370+ public bool TryParse ( ref ExceptionParsingContext context )
373371 {
374372 context . ExceptionType = context . Exception switch
375373 {
@@ -384,9 +382,9 @@ public bool TryParse(ExceptionParsingContext context)
384382 }
385383}
386384
387- internal class HttpStatusCodeParser : IExceptionParser
385+ internal readonly struct HttpStatusCodeParser : IExceptionParser
388386{
389- public bool TryParse ( ExceptionParsingContext context )
387+ public bool TryParse ( ref ExceptionParsingContext context )
390388 {
391389 if ( context . ExceptionType . HasValue || ! context . StatusCode . HasValue )
392390 {
@@ -395,10 +393,10 @@ public bool TryParse(ExceptionParsingContext context)
395393
396394 context . ExceptionType = context . StatusCode switch
397395 {
398- HttpStatusCode . BadRequest => KernelRequestExceptionType . InvalidConfiguration ,
396+ HttpStatusCode . BadRequest => ParseException ( context . Exception . Message , KernelRequestExceptionType . InvalidConfiguration ) ,
399397 HttpStatusCode . Unauthorized => KernelRequestExceptionType . InvalidApiKey ,
400398 HttpStatusCode . PaymentRequired => KernelRequestExceptionType . QuotaExceeded ,
401- HttpStatusCode . Forbidden => ParseForbiddenException ( context . Exception . Message ) ,
399+ HttpStatusCode . Forbidden => ParseException ( context . Exception . Message , KernelRequestExceptionType . InvalidApiKey ) ,
402400 HttpStatusCode . NotFound => KernelRequestExceptionType . InvalidConfiguration ,
403401 HttpStatusCode . Conflict => KernelRequestExceptionType . InvalidConfiguration ,
404402 HttpStatusCode . UnprocessableEntity => KernelRequestExceptionType . InvalidConfiguration ,
@@ -413,10 +411,12 @@ public bool TryParse(ExceptionParsingContext context)
413411 return context . ExceptionType . HasValue ;
414412 }
415413
416- private static KernelRequestExceptionType ParseForbiddenException ( string message )
414+ private static KernelRequestExceptionType ParseException ( string message , KernelRequestExceptionType fallback )
417415 {
418416 if ( message . Contains ( "quota" , StringComparison . OrdinalIgnoreCase ) ||
419- message . Contains ( "limit" , StringComparison . OrdinalIgnoreCase ) )
417+ message . Contains ( "limit" , StringComparison . OrdinalIgnoreCase ) ||
418+ message . Contains ( "exceeded" , StringComparison . OrdinalIgnoreCase ) ||
419+ message . Contains ( "organization" , StringComparison . OrdinalIgnoreCase ) )
420420 {
421421 return KernelRequestExceptionType . QuotaExceeded ;
422422 }
@@ -428,9 +428,16 @@ private static KernelRequestExceptionType ParseForbiddenException(string message
428428 return KernelRequestExceptionType . InvalidApiKey ;
429429 }
430430
431+ if ( message . Contains ( "model" , StringComparison . OrdinalIgnoreCase ) ||
432+ message . Contains ( "not found" , StringComparison . OrdinalIgnoreCase ) ||
433+ message . Contains ( "invalid" , StringComparison . OrdinalIgnoreCase ) )
434+ {
435+ return KernelRequestExceptionType . InvalidConfiguration ;
436+ }
437+
431438 // Default for 403 Forbidden if no specific keywords are found
432- return KernelRequestExceptionType . InvalidApiKey ;
439+ return fallback ;
433440 }
434441}
435442
436- #endregion
443+ #endregion
0 commit comments