refactor: 清理 AI 客户端死代码并统一错误处理
- 删除未使用的 OpenAiCompatibleClient,DTO 抽离到 ai_contracts.cs - 新增 AiHttpResponseGuard 统一 LLM/VLM 的 HTTP 错误处理: 错误响应带响应体(截断 2000 字符)、空响应与无效 JSON 显式抛 InvalidDataException - 空 catch 补全精确异常类型(IOException/UnauthorizedAccessException/JsonException) - 修正 Program.cs 数据库初始化注释 - 新增 AI 客户端错误处理单元测试
This commit is contained in:
@@ -40,7 +40,7 @@ public sealed class DeepSeekClient(HttpClient http, IConfiguration config)
|
||||
httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
|
||||
|
||||
using var response = await _http.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||
response.EnsureSuccessStatusCode();
|
||||
await AiHttpResponseGuard.ThrowIfFailedAsync(response, "DeepSeek", ct);
|
||||
|
||||
using var stream = await response.Content.ReadAsStreamAsync(ct);
|
||||
using var reader = new StreamReader(stream);
|
||||
@@ -76,11 +76,9 @@ public sealed class DeepSeekClient(HttpClient http, IConfiguration config)
|
||||
var json = JsonSerializer.Serialize(request, _jsonOptions);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _http.PostAsync("chat/completions", content, ct);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync(ct);
|
||||
return JsonSerializer.Deserialize<ChatCompletionResponse>(body, _jsonOptions)!;
|
||||
using var response = await _http.PostAsync("chat/completions", content, ct);
|
||||
var body = await AiHttpResponseGuard.ReadSuccessBodyAsync(response, "DeepSeek", ct);
|
||||
return AiHttpResponseGuard.DeserializeRequired<ChatCompletionResponse>(body, _jsonOptions, "DeepSeek");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,13 +123,45 @@ public sealed class VisionClient(HttpClient http, IConfiguration config)
|
||||
|
||||
var json = JsonSerializer.Serialize(request, _jsonOptions);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
var response = await _http.PostAsync("chat/completions", content, ct);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorBody = await response.Content.ReadAsStringAsync(ct);
|
||||
throw new HttpRequestException($"VLM API {response.StatusCode}: {errorBody}");
|
||||
}
|
||||
var body = await response.Content.ReadAsStringAsync(ct);
|
||||
return JsonSerializer.Deserialize<ChatCompletionResponse>(body, _jsonOptions)!;
|
||||
using var response = await _http.PostAsync("chat/completions", content, ct);
|
||||
var body = await AiHttpResponseGuard.ReadSuccessBodyAsync(response, "VLM", ct);
|
||||
return AiHttpResponseGuard.DeserializeRequired<ChatCompletionResponse>(body, _jsonOptions, "VLM");
|
||||
}
|
||||
}
|
||||
|
||||
internal static class AiHttpResponseGuard
|
||||
{
|
||||
private const int MaxErrorBodyLength = 2000;
|
||||
|
||||
public static async Task ThrowIfFailedAsync(HttpResponseMessage response, string provider, CancellationToken ct)
|
||||
{
|
||||
if (response.IsSuccessStatusCode) return;
|
||||
var body = await response.Content.ReadAsStringAsync(ct);
|
||||
throw new HttpRequestException($"{provider} API {(int)response.StatusCode} ({response.StatusCode}): {Truncate(body)}");
|
||||
}
|
||||
|
||||
public static async Task<string> ReadSuccessBodyAsync(HttpResponseMessage response, string provider, CancellationToken ct)
|
||||
{
|
||||
await ThrowIfFailedAsync(response, provider, ct);
|
||||
var body = await response.Content.ReadAsStringAsync(ct);
|
||||
if (string.IsNullOrWhiteSpace(body))
|
||||
throw new InvalidDataException($"{provider} API返回了空响应");
|
||||
return body;
|
||||
}
|
||||
|
||||
public static T DeserializeRequired<T>(string body, JsonSerializerOptions options, string provider)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(body, options)
|
||||
?? throw new InvalidDataException($"{provider} API响应内容为空");
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
throw new InvalidDataException($"{provider} API响应格式无效", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string Truncate(string value) =>
|
||||
value.Length <= MaxErrorBodyLength ? value : value[..MaxErrorBodyLength] + "...";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user