feat: 支持非流式传输

This commit is contained in:
ccnetcore
2025-07-09 21:52:00 +08:00
parent 716c344780
commit c5a9b9a15f
9 changed files with 365 additions and 48 deletions

View File

@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -62,7 +63,8 @@ public class AzureRestChatService : IChatService
if (!response.IsSuccessStatusCode)
{
throw new UserFriendlyException($"当前模型不可用:{aiModelDescribe.ModelId},状态码:{response.StatusCode},原因:{response.ReasonPhrase}");
throw new UserFriendlyException(
$"当前模型不可用:{aiModelDescribe.ModelId},状态码:{response.StatusCode},原因:{response.ReasonPhrase}");
}
// 确认响应成功
// response.EnsureSuccessStatusCode();
@@ -76,11 +78,11 @@ public class AzureRestChatService : IChatService
var result = new CompleteChatResponse();
try
{
var jsonObj = MapToJObject(line);
var jsonObj = MapToStreamJObject(line);
if (jsonObj is not null)
{
var content = GetContent(jsonObj);
var tokenUsage = GetTokenUsage(jsonObj);
var content = GetStreamContent(jsonObj);
var tokenUsage = GetStreamTokenUsage(jsonObj);
result = new CompleteChatResponse
{
TokenUsage = tokenUsage,
@@ -98,28 +100,85 @@ public class AzureRestChatService : IChatService
}
}
public Task<CompleteChatResponse> CompleteChatAsync(AiModelDescribe aiModelDescribe, List<ChatMessage> messages, CancellationToken cancellationToken)
public async Task<CompleteChatResponse> CompleteChatAsync(AiModelDescribe aiModelDescribe,
List<ChatMessage> messages, CancellationToken cancellationToken)
{
throw new NotImplementedException("暂未实现");
// 设置API URL
var apiUrl = $"{aiModelDescribe.Endpoint}";
// 准备请求内容
var requestBody = new
{
messages = messages.Select(x => new
{
role = x.GetRoleAsString(),
content = x.Content.FirstOrDefault()?.Text
}).ToList(),
stream = false,
// max_tokens = 2048,
// temperature = 0.8,
// top_p = 0.1,
// presence_penalty = 0,
// frequency_penalty = 0,
model = aiModelDescribe.ModelId
};
// 序列化请求内容为JSON
string jsonBody = JsonConvert.SerializeObject(requestBody);
using var httpClient = new HttpClient()
{
//10分钟超时
Timeout = TimeSpan.FromSeconds(600)
};
// 设置请求头
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {aiModelDescribe.ApiKey}");
// 其他头信息如Content-Type在StringContent中设置
// 构造 POST 请求
var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
// 设置请求内容(示例)
request.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
// 发送POST请求
HttpResponseMessage response =
await httpClient.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
throw new UserFriendlyException(
$"当前模型不可用:{aiModelDescribe.ModelId},状态码:{response.StatusCode},原因:{response.ReasonPhrase}");
}
// 确认响应成功
// response.EnsureSuccessStatusCode();
// 读取响应内容
var responseStr = await response.Content.ReadAsStringAsync(cancellationToken);
var jObject = MapToJObject(responseStr);
var content = GetContent(jObject);
var usage = GetTokenUsage(jObject);
var result = new CompleteChatResponse
{
TokenUsage = usage,
IsFinish = true,
Content = content
};
return result;
}
private JObject? MapToJObject(string line)
private JObject? MapToJObject(string body)
{
if (line == "data: [DONE]"||string.IsNullOrWhiteSpace(line) )
{
if (string.IsNullOrWhiteSpace(body))
return null;
}
if (string.IsNullOrWhiteSpace(line))
return null;
string prefix = "data: ";
line = line.Substring(prefix.Length);
return JObject.Parse(line);
return JObject.Parse(body);
}
private string? GetContent(JObject? jsonObj)
{
var contentToken = jsonObj.SelectToken("choices[0].delta.content");
var contentToken = jsonObj.SelectToken("choices[0].message.content");
if (contentToken != null && contentToken.Type != JTokenType.Null)
{
return contentToken.ToString();
@@ -157,4 +216,60 @@ public class AzureRestChatService : IChatService
return null;
}
private JObject? MapToStreamJObject(string line)
{
if (line == "data: [DONE]" || string.IsNullOrWhiteSpace(line))
{
return null;
}
if (string.IsNullOrWhiteSpace(line))
return null;
string prefix = "data: ";
line = line.Substring(prefix.Length);
return JObject.Parse(line);
}
private string? GetStreamContent(JObject? jsonObj)
{
var contentToken = jsonObj.SelectToken("choices[0].delta.content");
if (contentToken != null && contentToken.Type != JTokenType.Null)
{
return contentToken.ToString();
}
return null;
}
private TokenUsage? GetStreamTokenUsage(JObject? jsonObj)
{
var usage = jsonObj.SelectToken("usage");
if (usage is not null && usage.Type != JTokenType.Null)
{
var result = new TokenUsage();
var completionTokens = usage["completion_tokens"];
if (completionTokens is not null && completionTokens.Type != JTokenType.Null)
{
result.OutputTokenCount = completionTokens.ToObject<int>();
}
var promptTokens = usage["prompt_tokens"];
if (promptTokens is not null && promptTokens.Type != JTokenType.Null)
{
result.InputTokenCount = promptTokens.ToObject<int>();
}
var totalTokens = usage["total_tokens"];
if (totalTokens is not null && totalTokens.Type != JTokenType.Null)
{
result.TotalTokenCount = totalTokens.ToObject<int>();
}
return result;
}
return null;
}
}