feat: 支持codex

This commit is contained in:
ccnetcore
2025-12-11 01:17:31 +08:00
parent 53aa575ad4
commit 433d616b9b
10 changed files with 531 additions and 36 deletions

View File

@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Domain.AiGateWay;
@@ -18,6 +19,7 @@ using Yi.Framework.AiHub.Domain.Shared.Dtos.Anthropic;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi.Embeddings;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi.Images;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi.Responses;
using Yi.Framework.AiHub.Domain.Shared.Enums;
using Yi.Framework.Core.Extensions;
using Yi.Framework.SqlSugarCore.Abstractions;
@@ -546,9 +548,9 @@ public class AiGateWayManager : DomainService
var chatService =
LazyServiceProvider.GetRequiredKeyedService<IAnthropicChatCompletionService>(modelDescribe.HandlerName);
var data = await chatService.ChatCompletionsAsync(modelDescribe, request, cancellationToken);
data.SupplementalMultiplier(modelDescribe.Multiplier);
if (userId is not null)
{
await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
@@ -660,6 +662,183 @@ public class AiGateWayManager : DomainService
}
}
/// <summary>
/// OpenAi 响应-非流式-缓存处理
/// </summary>
/// <param name="httpContext"></param>
/// <param name="request"></param>
/// <param name="userId"></param>
/// <param name="sessionId"></param>
/// <param name="tokenId"></param>
/// <param name="cancellationToken"></param>
public async Task OpenAiResponsesAsyncForStatisticsAsync(HttpContext httpContext,
OpenAiResponsesInput request,
Guid? userId = null,
Guid? sessionId = null,
Guid? tokenId = null,
CancellationToken cancellationToken = default)
{
//todo 1
// _specialCompatible.AnthropicCompatible(request);
var response = httpContext.Response;
// 设置响应头,声明是 json
//response.ContentType = "application/json; charset=UTF-8";
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Response, request.Model);
var chatService =
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
var data = await chatService.ResponsesAsync(modelDescribe, request, cancellationToken);
//todo 2
// data.SupplementalMultiplier(modelDescribe.Multiplier);
//todo 3
// if (userId is not null)
// {
// await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
// new MessageInputDto
// {
// Content = sessionId is null ? "不予存储" : request.Messages?.FirstOrDefault()?.Content ?? string.Empty,
// ModelId = request.Model,
// TokenUsage = data.TokenUsage,
// }, tokenId);
//
// await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
// new MessageInputDto
// {
// Content = sessionId is null ? "不予存储" : data.content?.FirstOrDefault()?.text,
// ModelId = request.Model,
// TokenUsage = data.TokenUsage
// }, tokenId);
//
// await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.TokenUsage, tokenId);
//
// // 扣减尊享token包用量
// var totalTokens = data.TokenUsage.TotalTokens ?? 0;
// if (totalTokens > 0)
// {
// await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
// }
// }
await response.WriteAsJsonAsync(data, cancellationToken);
}
/// <summary>
/// OpenAi 响应-流式
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async IAsyncEnumerable<(string, dynamic?)> OpenAiResponsesAsync(
OpenAiResponsesInput request,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
//todo cc
// _specialCompatible.AnthropicCompatible(request);
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Response, request.Model);
var chatService =
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
await foreach (var result in chatService.ResponsesStreamAsync(modelDescribe, request, cancellationToken))
{
//todo 倍率
// result.Item2.SupplementalMultiplier(modelDescribe.Multiplier);
yield return result;
}
}
/// <summary>
/// OpenAi响应-流式-缓存处理
/// </summary>
/// <param name="httpContext"></param>
/// <param name="request"></param>
/// <param name="userId"></param>
/// <param name="sessionId"></param>
/// <param name="tokenId">Token IdWeb端传null或Guid.Empty</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task OpenAiResponsesStreamForStatisticsAsync(
HttpContext httpContext,
OpenAiResponsesInput request,
Guid? userId = null,
Guid? sessionId = null,
Guid? tokenId = null,
CancellationToken cancellationToken = default)
{
var response = httpContext.Response;
// 设置响应头,声明是 SSE 流
response.ContentType = "text/event-stream;charset=utf-8;";
response.Headers.TryAdd("Cache-Control", "no-cache");
response.Headers.TryAdd("Connection", "keep-alive");
var completeChatResponse = OpenAiResponsesAsync(request, cancellationToken);
ThorUsageResponse? tokenUsage = null;
try
{
await foreach (var responseResult in completeChatResponse)
{
//message_start是为了保底机制
if (responseResult.Item1.Contains("response.completed"))
{
JObject obj = JObject.FromObject(responseResult.Item2);
int inputTokens = (int?)obj["response"]?["usage"]?["input_tokens"] ?? 0;
int outputTokens = (int?)obj["response"]?["usage"]?["output_tokens"] ?? 0;
tokenUsage = new ThorUsageResponse
{
PromptTokens = inputTokens,
InputTokens = inputTokens,
OutputTokens = outputTokens,
CompletionTokens = outputTokens,
TotalTokens = inputTokens+outputTokens,
};
}
await WriteAsEventStreamDataAsync(httpContext, responseResult.Item1, responseResult.Item2,
cancellationToken);
}
}
catch (Exception e)
{
_logger.LogError(e, $"Ai响应异常");
var errorContent = $"响应Ai异常异常信息\n当前Ai模型{request.Model}\n异常信息{e.Message}\n异常堆栈:{e}";
throw new UserFriendlyException(errorContent);
}
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
new MessageInputDto
{
Content = "不予存储" ,
ModelId = request.Model,
TokenUsage = tokenUsage,
}, tokenId);
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
new MessageInputDto
{
Content = "不予存储" ,
ModelId = request.Model,
TokenUsage = tokenUsage
}, tokenId);
await _usageStatisticsManager.SetUsageAsync(userId, request.Model, tokenUsage, tokenId);
// 扣减尊享token包用量
if (userId.HasValue && tokenUsage is not null)
{
var totalTokens = tokenUsage.TotalTokens ?? 0;
if (tokenUsage.TotalTokens > 0)
{
await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
}
}
}
#region Anthropic格式Http响应
private static readonly byte[] EventPrefix = "event: "u8.ToArray();
@@ -675,7 +854,6 @@ public class AiGateWayManager : DomainService
string @event,
T value,
CancellationToken cancellationToken = default)
where T : class
{
var response = context.Response;
var bodyStream = response.Body;