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

@@ -9,11 +9,13 @@ using Newtonsoft.Json.Serialization;
using OpenAI.Chat;
using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Application.Contracts.Dtos;
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.AiChat;
using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.AiHub.Domain.Entities.Model;
using Yi.Framework.AiHub.Domain.Shared.Dtos;
using Yi.Framework.SqlSugarCore.Abstractions;
using Usage = Yi.Framework.AiHub.Application.Contracts.Dtos.Usage;
namespace Yi.Framework.AiHub.Domain.Managers;
@@ -107,7 +109,7 @@ public class AiGateWayManager : DomainService
await using var writer = new StreamWriter(response.Body, Encoding.UTF8, leaveOpen: true);
var modelDescribe = await GetModelAsync(modelId);
var chatService = LazyServiceProvider.GetRequiredKeyedService<IChatService>(modelDescribe.HandlerName);
var output = await chatService.CompleteChatAsync(modelDescribe, messages, cancellationToken);
var data = await chatService.CompleteChatAsync(modelDescribe, messages, cancellationToken);
if (userId is not null)
{
await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
@@ -115,22 +117,23 @@ public class AiGateWayManager : DomainService
{
Content = messages.LastOrDefault().Content.FirstOrDefault()?.Text ?? string.Empty,
ModelId = modelId,
TokenUsage = output.TokenUsage,
TokenUsage = data.TokenUsage,
});
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
new MessageInputDto
{
Content = output.Content,
Content = data.Content,
ModelId = modelId,
TokenUsage = output.TokenUsage
TokenUsage = data.TokenUsage
});
await _usageStatisticsManager.SetUsageAsync(userId.Value, modelId, output.TokenUsage.InputTokenCount,
output.TokenUsage.OutputTokenCount);
await _usageStatisticsManager.SetUsageAsync(userId.Value, modelId, data.TokenUsage.InputTokenCount,
data.TokenUsage.OutputTokenCount);
}
var body = JsonConvert.SerializeObject(output, new JsonSerializerSettings
var result = MapToChatCompletions(modelId, data.Content);
var body = JsonConvert.SerializeObject(result, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
@@ -261,9 +264,9 @@ public class AiGateWayManager : DomainService
}
private SendMessageOutputDto MapToMessage(string modelId, string content)
private SendMessageStreamOutputDto MapToMessage(string modelId, string content)
{
var output = new SendMessageOutputDto
var output = new SendMessageStreamOutputDto
{
Id = "chatcmpl-BotYP3BlN5T4g9YPnW0fBSBvKzXdd",
Object = "chat.completion.chunk",
@@ -338,4 +341,75 @@ public class AiGateWayManager : DomainService
return output;
}
private ChatCompletionsOutput MapToChatCompletions(string modelId, string content)
{
return new ChatCompletionsOutput
{
Id = "resp_67ccd2bed1ec8190b14f964abc0542670bb6a6b452d3795b",
Object = "response",
CreatedAt = 1741476542,
Status = "completed",
Error = null,
IncompleteDetails = null,
Instructions = null,
MaxOutputTokens = null,
Model = modelId,
Output = new List<Output>()
{
new Output
{
Type = "message",
Id = "msg_67ccd2bf17f0819081ff3bb2cf6508e60bb6a6b452d3795b",
Status = "completed",
Role = "assistant",
Content = new List<Content>
{
new Content
{
Type = "output_text",
Text = content,
Annotations = new List<object>()
}
}
}
},
ParallelToolCalls = true,
PreviousResponseId = null,
Reasoning = new Reasoning
{
Effort = null,
Summary = null
},
Store = true,
Temperature = 0,
Text = new Text
{
Format = new Format
{
Type = "text"
}
},
ToolChoice = "auto",
Tools = new List<object>(),
TopP = 1.0,
Truncation = "disabled",
Usage = new Application.Contracts.Dtos.OpenAi.Usage
{
InputTokens = 0,
InputTokensDetails = new InputTokensDetails
{
CachedTokens = 0
},
OutputTokens = 0,
OutputTokensDetails = new OutputTokensDetails
{
ReasoningTokens = 0
},
TotalTokens = 0
},
User = null,
Metadata = null
};
}
}