Files
Yi.Admin/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiChat/Impl/AzureRestChatService.cs

155 lines
5.0 KiB
C#
Raw Normal View History

2025-06-25 00:05:00 +08:00
using System.Runtime.CompilerServices;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenAI.Chat;
using Yi.Framework.AiHub.Domain.Extensions;
2025-06-25 17:12:09 +08:00
using Yi.Framework.AiHub.Domain.Shared.Dtos;
2025-06-25 00:05:00 +08:00
namespace Yi.Framework.AiHub.Domain.AiChat.Impl;
public class AzureRestChatService : IChatService
{
2025-06-25 17:12:09 +08:00
public AzureRestChatService()
2025-06-25 00:05:00 +08:00
{
}
2025-06-27 22:13:26 +08:00
public async IAsyncEnumerable<CompleteChatResponse> CompleteChatAsync(AiModelDescribe aiModelDescribe,
List<ChatMessage> messages,
2025-06-25 00:05:00 +08:00
[EnumeratorCancellation] CancellationToken cancellationToken)
{
// 设置API URL
2025-06-26 17:54:52 +08:00
var apiUrl = $"{aiModelDescribe.Endpoint}";
2025-06-25 00:05:00 +08:00
// 准备请求内容
var requestBody = new
{
messages = messages.Select(x => new
{
role = x.GetRoleAsString(),
content = x.Content.FirstOrDefault()?.Text
}).ToList(),
stream = true,
2025-06-30 21:58:34 +08:00
// max_tokens = 2048,
2025-06-28 23:07:32 +08:00
// temperature = 0.8,
// top_p = 0.1,
2025-06-26 17:54:52 +08:00
// presence_penalty = 0,
// frequency_penalty = 0,
2025-06-25 17:12:09 +08:00
model = aiModelDescribe.ModelId
2025-06-25 00:05:00 +08:00
};
// 序列化请求内容为JSON
string jsonBody = JsonConvert.SerializeObject(requestBody);
2025-07-01 16:11:41 +08:00
using var httpClient = new HttpClient()
{
//10分钟超时
Timeout = TimeSpan.FromSeconds(600)
};
2025-06-25 00:05:00 +08:00
// 设置请求头
2025-06-25 17:12:09 +08:00
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {aiModelDescribe.ApiKey}");
2025-06-25 00:05:00 +08:00
// 其他头信息如Content-Type在StringContent中设置
2025-06-25 17:12:09 +08:00
2025-06-25 00:23:00 +08:00
// 构造 POST 请求
var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
2025-06-25 00:05:00 +08:00
2025-06-25 00:23:00 +08:00
// 设置请求内容(示例)
2025-06-25 17:12:09 +08:00
request.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
2025-06-25 00:05:00 +08:00
// 发送POST请求
2025-06-25 17:12:09 +08:00
HttpResponseMessage response =
await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
2025-06-28 23:07:32 +08:00
if (!response.IsSuccessStatusCode)
{
throw new UserFriendlyException($"当前模型不可用:{aiModelDescribe.ModelId},状态码:{response.StatusCode},原因:{response.ReasonPhrase}");
}
2025-06-25 00:05:00 +08:00
// 确认响应成功
2025-06-28 23:07:32 +08:00
// response.EnsureSuccessStatusCode();
2025-06-25 00:05:00 +08:00
// 读取响应内容
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
// 从流中读取数据并输出到控制台
2025-06-25 17:12:09 +08:00
using var streamReader = new StreamReader(responseStream);
2025-06-27 22:13:26 +08:00
while (await streamReader.ReadLineAsync(cancellationToken) is { } line)
2025-06-25 00:05:00 +08:00
{
2025-06-27 22:13:26 +08:00
var result = new CompleteChatResponse();
try
2025-06-25 00:05:00 +08:00
{
2025-06-27 22:13:26 +08:00
var jsonObj = MapToJObject(line);
2025-06-27 22:49:08 +08:00
if (jsonObj is not null)
2025-06-27 22:13:26 +08:00
{
2025-06-27 22:49:08 +08:00
var content = GetContent(jsonObj);
var tokenUsage = GetTokenUsage(jsonObj);
result = new CompleteChatResponse
{
TokenUsage = tokenUsage,
IsFinish = tokenUsage is not null,
Content = content
};
}
2025-06-25 00:05:00 +08:00
}
2025-06-27 22:13:26 +08:00
catch (Exception e)
{
Console.WriteLine("解析失败");
}
yield return result;
2025-06-25 00:05:00 +08:00
}
}
2025-06-27 22:13:26 +08:00
private JObject? MapToJObject(string line)
2025-06-25 00:05:00 +08:00
{
2025-06-27 22:49:08 +08:00
if (line == "data: [DONE]"||string.IsNullOrWhiteSpace(line) )
{
return null;
}
2025-06-25 00:05:00 +08:00
if (string.IsNullOrWhiteSpace(line))
return null;
string prefix = "data: ";
line = line.Substring(prefix.Length);
2025-06-27 22:13:26 +08:00
return JObject.Parse(line);
}
2025-06-25 00:05:00 +08:00
2025-06-27 22:13:26 +08:00
private string? GetContent(JObject? jsonObj)
{
var contentToken = jsonObj.SelectToken("choices[0].delta.content");
if (contentToken != null && contentToken.Type != JTokenType.Null)
2025-06-25 00:05:00 +08:00
{
2025-06-27 22:13:26 +08:00
return contentToken.ToString();
2025-06-25 00:05:00 +08:00
}
2025-06-27 22:13:26 +08:00
return null;
}
private TokenUsage? GetTokenUsage(JObject? jsonObj)
{
var usage = jsonObj.SelectToken("usage");
if (usage is not null && usage.Type != JTokenType.Null)
2025-06-25 00:05:00 +08:00
{
2025-06-27 22:49:08 +08:00
var result = new TokenUsage();
var completionTokens = usage["completion_tokens"];
if (completionTokens is not null && completionTokens.Type != JTokenType.Null)
2025-06-27 22:13:26 +08:00
{
2025-06-27 22:49:08 +08:00
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>();
}
2025-06-27 22:13:26 +08:00
return result;
2025-06-25 00:05:00 +08:00
}
2025-06-27 22:13:26 +08:00
return null;
2025-06-25 00:05:00 +08:00
}
}