feat: ai完成接入deepseek

This commit is contained in:
ccnetcore
2025-06-25 00:05:00 +08:00
parent 04c2b246f6
commit 8eea510583
4 changed files with 145 additions and 9 deletions

View File

@@ -0,0 +1,118 @@
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenAI.Chat;
using Yi.Framework.AiHub.Application.Contracts.Options;
using Yi.Framework.AiHub.Domain.Extensions;
namespace Yi.Framework.AiHub.Domain.AiChat.Impl;
public class AzureRestChatService : IChatService
{
private readonly AiChatModelOptions _options;
public AzureRestChatService(IOptions<AiGateWayOptions> options)
{
this._options = options.Value.Chats[nameof(AzureRestChatService)];
}
public async IAsyncEnumerable<string> CompleteChatAsync(string modelId, List<ChatMessage> messages,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
// 设置API URL
var apiUrl = $"{_options.Endpoint}models/chat/completions";
var ss = messages.Select(x => new
{
role = x.GetRoleAsString(),
content = x.Content.FirstOrDefault()?.Text
}).ToList();
// 准备请求内容
var requestBody = new
{
messages = messages.Select(x => new
{
role = x.GetRoleAsString(),
content = x.Content.FirstOrDefault()?.Text
}).ToList(),
stream = true,
max_tokens = 2048,
temperature = 0.8,
top_p = 0.1,
presence_penalty = 0,
frequency_penalty = 0,
model = modelId
};
// 序列化请求内容为JSON
string jsonBody = JsonConvert.SerializeObject(requestBody);
using var httpClient = new HttpClient();
// 设置请求头
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_options.ApiKey}");
// 其他头信息如Content-Type在StringContent中设置
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
// 发送POST请求
HttpResponseMessage response = await httpClient.PostAsync(apiUrl, content, cancellationToken);
// 确认响应成功
response.EnsureSuccessStatusCode();
// 读取响应内容
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
// 从流中读取数据并输出到控制台
using var streamReader = new System.IO.StreamReader(responseStream);
string line;
while ((line = await streamReader.ReadLineAsync(cancellationToken)) != null)
{
var result = GetContent(line);
if (result is not null)
{
yield return result;
}
}
}
private string? GetContent(string line)
{
if (string.IsNullOrWhiteSpace(line))
return null;
string prefix = "data: ";
line = line.Substring(prefix.Length);
try
{
// 解析为JObject
var jsonObj = JObject.Parse(line);
var content = jsonObj["choices"][0]["delta"]["content"].ToString();
return content;
// // 判断choices是否存在且是数组并且有元素
// if (jsonObj.TryGetValue("choices", out var choicesToken) && choicesToken is JArray choicesArray &&
// choicesArray.Count > 0)
// {
// var firstChoice = choicesArray[0] as JObject;
// // 判断delta字段是否存在
// if (firstChoice.TryGetValue("delta", out var deltaToken))
// {
// // 获取content字段
// if (deltaToken.Type == JTokenType.Object && ((JObject)deltaToken).TryGetValue("content", out var contentToken))
// {
// return contentToken.ToString();
// }
// }
// }
}
catch (Exception)
{
// 解析失败
return null;
}
return null;
}
}

View File

@@ -0,0 +1,21 @@
using System.Reflection;
using OpenAI.Chat;
namespace Yi.Framework.AiHub.Domain.Extensions;
public static class ChatMessageExtensions
{
public static string GetRoleAsString(this ChatMessage message)
{
var type = message.GetType();
var propertyInfo = type.GetProperty("Role", BindingFlags.NonPublic | BindingFlags.Instance);
if (propertyInfo != null)
{
var value = propertyInfo.GetValue(message) as ChatMessageRole?;
return value.ToString().ToLower();
}
return string.Empty;
}
}

View File

@@ -27,6 +27,7 @@ namespace Yi.Framework.AiHub.Domain
services.AddKeyedTransient<IChatService, AzureChatService>(nameof(AzureChatService)); services.AddKeyedTransient<IChatService, AzureChatService>(nameof(AzureChatService));
services.AddKeyedTransient<IChatService, AzureRestChatService>(nameof(AzureRestChatService));
} }
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)

View File

@@ -17,22 +17,18 @@ namespace Yi.Abp.Application
[DependsOn( [DependsOn(
typeof(YiAbpApplicationContractsModule), typeof(YiAbpApplicationContractsModule),
typeof(YiAbpDomainModule), typeof(YiAbpDomainModule),
typeof(YiFrameworkRbacApplicationModule), typeof(YiFrameworkRbacApplicationModule),
typeof(YiFrameworkBbsApplicationModule), typeof(YiFrameworkBbsApplicationModule),
typeof(YiFrameworkDigitalCollectiblesApplicationModule), typeof(YiFrameworkDigitalCollectiblesApplicationModule),
typeof(YiFrameworkChatHubApplicationModule), typeof(YiFrameworkChatHubApplicationModule),
typeof(YiFrameworkStockApplicationModule), typeof(YiFrameworkStockApplicationModule),
typeof(YiFrameworkAiHubApplicationModule), typeof(YiFrameworkAiHubApplicationModule),
typeof(YiFrameworkTenantManagementApplicationModule), typeof(YiFrameworkTenantManagementApplicationModule),
typeof(YiFrameworkCodeGenApplicationModule), typeof(YiFrameworkCodeGenApplicationModule),
typeof (YiFrameworkSettingManagementApplicationModule), typeof(YiFrameworkSettingManagementApplicationModule),
typeof(YiFrameworkDddApplicationModule) typeof(YiFrameworkDddApplicationModule)
)] )]
public class YiAbpApplicationModule : AbpModule public class YiAbpApplicationModule : AbpModule
{ {
} }
} }