feat: 新增统一流式转发与统计能力,支持多API类型

新增统一流式处理机制,支持 Completions、Anthropic Messages、OpenAI Responses、Gemini GenerateContent 四种 API 的原封不动 SSE 转发
统一处理 token 用量统计、倍率计算、尊享包扣费与消息记录
新增统一发送接口 ai-chat/unified/send,支持从请求体自动解析模型 ID
提升多模型流式接入的一致性与扩展性
This commit is contained in:
ccnetcore
2026-01-10 00:22:57 +08:00
parent 62b26bc2a4
commit 87518af562
2 changed files with 455 additions and 0 deletions

View File

@@ -25,6 +25,7 @@ using Yi.Framework.AiHub.Domain.Entities.Model;
using Yi.Framework.AiHub.Domain.Extensions;
using Yi.Framework.AiHub.Domain.Managers;
using Yi.Framework.AiHub.Domain.Shared.Consts;
using System.Text.Json;
using Yi.Framework.AiHub.Domain.Shared.Dtos;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.Shared.Enums;
@@ -270,4 +271,89 @@ public class AiChatService : ApplicationService
var data = await _agentStoreRepository.GetFirstAsync(x => x.SessionId == sessionId);
return data?.Store;
}
/// <summary>
/// 统一发送消息 - 支持4种API类型
/// </summary>
/// <param name="apiType">API类型枚举</param>
/// <param name="input">原始请求体JsonElement</param>
/// <param name="modelId">模型IDGemini格式需要从URL传入</param>
/// <param name="sessionId">会话ID</param>
/// <param name="cancellationToken"></param>
[HttpPost("ai-chat/unified/send")]
public async Task PostUnifiedSendAsync(
[FromQuery] ModelApiTypeEnum apiType,
[FromBody] JsonElement input,
[FromQuery] string modelId,
[FromQuery] Guid? sessionId,
CancellationToken cancellationToken)
{
// 从请求体中提取模型ID如果未从URL传入
if (string.IsNullOrEmpty(modelId))
{
modelId = ExtractModelIdFromRequest(apiType, input);
}
// 除了免费模型,其他的模型都要校验
if (modelId != FreeModelId)
{
if (CurrentUser.IsAuthenticated)
{
await _aiBlacklistManager.VerifiyAiBlacklist(CurrentUser.GetId());
if (!CurrentUser.IsAiVip())
{
throw new UserFriendlyException("该模型需要VIP用户才能使用请购买VIP后重新登录重试");
}
}
else
{
throw new UserFriendlyException("未登录用户只能使用未加速的DeepSeek-R1请登录后重试");
}
}
// 如果是尊享包服务,需要校验是否尊享包足够
if (CurrentUser.IsAuthenticated)
{
var isPremium = await _modelManager.IsPremiumModelAsync(modelId);
if (isPremium)
{
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId());
if (availableTokens <= 0)
{
throw new UserFriendlyException("尊享token包用量不足请先购买尊享token包");
}
}
}
// 调用统一流式处理
await _aiGateWayManager.UnifiedStreamForStatisticsAsync(
_httpContextAccessor.HttpContext!,
apiType,
input,
modelId,
CurrentUser.Id,
sessionId,
null,
CancellationToken.None);
}
/// <summary>
/// 从请求体中提取模型ID
/// </summary>
private string ExtractModelIdFromRequest(ModelApiTypeEnum apiType, JsonElement input)
{
try
{
if (input.TryGetProperty("model", out var modelProperty))
{
return modelProperty.GetString() ?? string.Empty;
}
}
catch
{
// 忽略解析错误
}
throw new UserFriendlyException("无法从请求中获取模型ID请在URL参数中指定modelId");
}
}