Skip to content

Commit 9139342

Browse files
committed
fix: Handled exception error
1 parent f08546c commit 9139342

File tree

5 files changed

+74
-64
lines changed

5 files changed

+74
-64
lines changed

src/Everywhere/Chat/ChatService.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ private async Task ProcessUserChatMessageAsync(
223223
}
224224
finally
225225
{
226+
analyzingContextMessage.FinishedAt = DateTimeOffset.UtcNow;
226227
analyzingContextMessage.IsBusy = false;
227228
}
228229

@@ -528,10 +529,10 @@ private async Task GenerateAsync(
528529
}
529530
catch (Exception e)
530531
{
531-
var chatRequestException = ChatRequestException.Parse(e);
532+
e = ChatRequestException.Parse(e);
532533
activity?.SetStatus(ActivityStatusCode.Error, e.Message.Trim());
533-
assistantChatMessage.ErrorMessageKey = chatRequestException.GetFriendlyMessage();
534-
logger.LogError(chatRequestException, "Error generating chat response");
534+
assistantChatMessage.ErrorMessageKey = e.GetFriendlyMessage();
535+
logger.LogError(e, "Error generating chat response");
535536
}
536537
finally
537538
{

src/Everywhere/Common/HandledException.cs

Lines changed: 65 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -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

248246
internal 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

src/Everywhere/I18N/Strings.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,10 +870,10 @@ My intelligence comes from powerful Large language models (LLMs), which are prov
870870
<value>API key is missing or invalid.</value>
871871
</data>
872872
<data name="KernelRequestException_QuotaExceeded" xml:space="preserve">
873-
<value>You have exceeded your API usage quota.</value>
873+
<value>Exceeded the API usage quota, or lacks permission to access the current model.</value>
874874
</data>
875875
<data name="KernelRequestException_RateLimit" xml:space="preserve">
876-
<value>You have exceeded the request rate limit. Please try again later.</value>
876+
<value>Rate limit exceeded. Please try again later.</value>
877877
</data>
878878
<data name="KernelRequestException_EndpointNotReachable" xml:space="preserve">
879879
<value>Service endpoint is not reachable. Please check your network connection.</value>

src/Everywhere/I18N/Strings.zh-hans.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@
851851
<value>模型配置不正确。模型提供商或 ID 可能缺失或无效。</value>
852852
</data>
853853
<data name="KernelRequestException_QuotaExceeded" xml:space="preserve">
854-
<value>您已超出 API 使用配额。</value>
854+
<value>超出 API 使用配额,或者没有访问当前模型的权限。</value>
855855
</data>
856856
<data name="KernelRequestException_RateLimit" xml:space="preserve">
857857
<value>请求频率过高,请稍后重试。</value>

src/Everywhere/ViewModels/WelcomeViewModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using CommunityToolkit.Mvvm.ComponentModel;
33
using CommunityToolkit.Mvvm.Input;
44
using Everywhere.AI;
5+
using Everywhere.Common;
56
using Everywhere.Configuration;
67
using Microsoft.Extensions.Logging;
78
using Microsoft.SemanticKernel;
@@ -159,6 +160,7 @@ await kernelMixin.ChatCompletionService.GetChatMessageContentAsync(
159160
}
160161
catch (Exception ex)
161162
{
163+
ex = ChatRequestException.Parse(ex);
162164
_logger.LogError(
163165
ex,
164166
"Failed to validate API key for provider {ProviderId} and model {ModelId}",

0 commit comments

Comments
 (0)