diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesInput.cs
index 2e17d351..8adf388a 100644
--- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesInput.cs
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesInput.cs
@@ -1,4 +1,5 @@
-using System.Text.Json.Serialization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi.Responses;
@@ -7,14 +8,14 @@ public class OpenAiResponsesInput
[JsonPropertyName("stream")] public bool? Stream { get; set; }
[JsonPropertyName("model")] public string Model { get; set; }
- [JsonPropertyName("input")] public dynamic Input { get; set; }
+ [JsonPropertyName("input")] public JsonElement Input { get; set; }
[JsonPropertyName("max_output_tokens")]
public int? MaxOutputTokens { get; set; }
- [JsonPropertyName("max_tool_calls")] public dynamic? MaxToolCalls { get; set; }
+ [JsonPropertyName("max_tool_calls")] public JsonElement? MaxToolCalls { get; set; }
[JsonPropertyName("instructions")] public string? Instructions { get; set; }
- [JsonPropertyName("metadata")] public dynamic? Metadata { get; set; }
+ [JsonPropertyName("metadata")] public JsonElement? Metadata { get; set; }
[JsonPropertyName("parallel_tool_calls")]
public bool? ParallelToolCalls { get; set; }
@@ -22,24 +23,24 @@ public class OpenAiResponsesInput
[JsonPropertyName("previous_response_id")]
public string? PreviousResponseId { get; set; }
- [JsonPropertyName("prompt")] public dynamic? Prompt { get; set; }
+ [JsonPropertyName("prompt")] public JsonElement? Prompt { get; set; }
[JsonPropertyName("prompt_cache_key")] public string? PromptCacheKey { get; set; }
[JsonPropertyName("prompt_cache_retention")]
public string? PromptCacheRetention { get; set; }
- [JsonPropertyName("reasoning")] public dynamic? Reasoning { get; set; }
+ [JsonPropertyName("reasoning")] public JsonElement? Reasoning { get; set; }
[JsonPropertyName("safety_identifier")]
public string? SafetyIdentifier { get; set; }
[JsonPropertyName("service_tier")] public string? ServiceTier { get; set; }
[JsonPropertyName("store")] public bool? Store { get; set; }
- [JsonPropertyName("stream_options")] public dynamic? StreamOptions { get; set; }
+ [JsonPropertyName("stream_options")] public JsonElement? StreamOptions { get; set; }
[JsonPropertyName("temperature")] public decimal? Temperature { get; set; }
- [JsonPropertyName("text")] public dynamic? Text { get; set; }
- [JsonPropertyName("tool_choice")] public dynamic? ToolChoice { get; set; }
- [JsonPropertyName("tools")] public dynamic? Tools { get; set; }
+ [JsonPropertyName("text")] public JsonElement? Text { get; set; }
+ [JsonPropertyName("tool_choice")] public JsonElement? ToolChoice { get; set; }
+ [JsonPropertyName("tools")] public JsonElement? Tools { get; set; }
[JsonPropertyName("top_logprobs")] public int? TopLogprobs { get; set; }
[JsonPropertyName("top_p")] public decimal? TopP { get; set; }
[JsonPropertyName("truncation")] public string? Truncation { get; set; }
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesOutput.cs
index 6ddcc5d6..31e6c4ca 100644
--- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesOutput.cs
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/OpenAi/Responses/OpenAiResponsesOutput.cs
@@ -52,6 +52,18 @@ public class OpenAiResponsesOutput
public dynamic? User { get; set; }
[JsonPropertyName("metadata")]
public dynamic? Metadata { get; set; }
+
+ public void SupplementalMultiplier(decimal multiplier)
+ {
+ if (this.Usage is not null)
+ {
+ this.Usage.InputTokens =
+ (int)Math.Round((this.Usage?.InputTokens ?? 0) * multiplier);
+
+ this.Usage.OutputTokens =
+ (int)Math.Round((this.Usage?.OutputTokens ?? 0) * multiplier);
+ }
+ }
}
public class OpenAiResponsesUsageOutput
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Extensions/JsonElementExtensions.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Extensions/JsonElementExtensions.cs
new file mode 100644
index 00000000..7c5b6e53
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Extensions/JsonElementExtensions.cs
@@ -0,0 +1,279 @@
+using System.Text.Json;
+
+namespace Yi.Framework.AiHub.Domain.Shared.Extensions;
+
+public static class JsonElementExtensions
+{
+ #region 路径访问
+
+ ///
+ /// 链式获取深层属性,支持对象属性和数组索引
+ ///
+ ///
+ /// root.GetPath("user", "addresses", 0, "city")
+ ///
+ public static JsonElement? GetPath(this JsonElement element, params object[] path)
+ {
+ JsonElement current = element;
+
+ foreach (var key in path)
+ {
+ switch (key)
+ {
+ case string propertyName:
+ if (current.ValueKind != JsonValueKind.Object ||
+ !current.TryGetProperty(propertyName, out current))
+ return null;
+ break;
+
+ case int index:
+ if (current.ValueKind != JsonValueKind.Array ||
+ index < 0 || index >= current.GetArrayLength())
+ return null;
+ current = current[index];
+ break;
+
+ default:
+ return null;
+ }
+ }
+
+ return current;
+ }
+
+ ///
+ /// 安全获取对象属性
+ ///
+ public static JsonElement? Get(this JsonElement element, string propertyName)
+ {
+ if (element.ValueKind == JsonValueKind.Object &&
+ element.TryGetProperty(propertyName, out var value))
+ return value;
+ return null;
+ }
+
+ ///
+ /// 安全获取数组元素
+ ///
+ public static JsonElement? Get(this JsonElement element, int index)
+ {
+ if (element.ValueKind == JsonValueKind.Array &&
+ index >= 0 && index < element.GetArrayLength())
+ return element[index];
+ return null;
+ }
+
+ ///
+ /// 链式安全获取对象属性
+ ///
+ public static JsonElement? Get(this JsonElement? element, string propertyName)
+ => element?.Get(propertyName);
+
+ ///
+ /// 链式安全获取数组元素
+ ///
+ public static JsonElement? Get(this JsonElement? element, int index)
+ => element?.Get(index);
+
+ #endregion
+
+ #region 取值方法(带默认值)
+
+ public static string? GetString(this JsonElement? element, string? defaultValue = null)
+ => element?.ValueKind == JsonValueKind.String ? element.Value.GetString() : defaultValue;
+
+ public static int GetInt(this JsonElement? element, int defaultValue = 0)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetInt32() : defaultValue;
+
+ public static long GetLong(this JsonElement? element, long defaultValue = 0)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetInt64() : defaultValue;
+
+ public static double GetDouble(this JsonElement? element, double defaultValue = 0)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetDouble() : defaultValue;
+
+ public static decimal GetDecimal(this JsonElement? element, decimal defaultValue = 0)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetDecimal() : defaultValue;
+
+ public static bool GetBool(this JsonElement? element, bool defaultValue = false)
+ => element?.ValueKind is JsonValueKind.True or JsonValueKind.False
+ ? element.Value.GetBoolean()
+ : defaultValue;
+
+ public static DateTime GetDateTime(this JsonElement? element, DateTime defaultValue = default)
+ => element?.ValueKind == JsonValueKind.String && element.Value.TryGetDateTime(out var dt)
+ ? dt
+ : defaultValue;
+
+ public static Guid GetGuid(this JsonElement? element, Guid defaultValue = default)
+ => element?.ValueKind == JsonValueKind.String && element.Value.TryGetGuid(out var guid)
+ ? guid
+ : defaultValue;
+
+ #endregion
+
+ #region 可空取值方法
+
+ public static int? GetIntOrNull(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetInt32() : null;
+
+ public static long? GetLongOrNull(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetInt64() : null;
+
+ public static double? GetDoubleOrNull(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetDouble() : null;
+
+ public static decimal? GetDecimalOrNull(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Number ? element.Value.GetDecimal() : null;
+
+ public static bool? GetBoolOrNull(this JsonElement? element)
+ => element?.ValueKind is JsonValueKind.True or JsonValueKind.False
+ ? element.Value.GetBoolean()
+ : null;
+
+ public static DateTime? GetDateTimeOrNull(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.String && element.Value.TryGetDateTime(out var dt)
+ ? dt
+ : null;
+
+ public static Guid? GetGuidOrNull(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.String && element.Value.TryGetGuid(out var guid)
+ ? guid
+ : null;
+
+ #endregion
+
+ #region 数组操作
+
+ ///
+ /// 安全获取数组,不存在返回空数组
+ ///
+ public static IEnumerable GetArray(this JsonElement? element)
+ {
+ if (element?.ValueKind == JsonValueKind.Array)
+ {
+ foreach (var item in element.Value.EnumerateArray())
+ yield return item;
+ }
+ }
+
+ ///
+ /// 获取数组长度
+ ///
+ public static int GetArrayLength(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Array ? element.Value.GetArrayLength() : 0;
+
+ ///
+ /// 数组转 List
+ ///
+ public static List ToStringList(this JsonElement? element)
+ => element.GetArray().Select(e => e.GetString()).ToList();
+
+ public static List ToIntList(this JsonElement? element)
+ => element.GetArray()
+ .Where(e => e.ValueKind == JsonValueKind.Number)
+ .Select(e => e.GetInt32())
+ .ToList();
+
+ #endregion
+
+ #region 对象操作
+
+ ///
+ /// 安全枚举对象属性
+ ///
+ public static IEnumerable GetProperties(this JsonElement? element)
+ {
+ if (element?.ValueKind == JsonValueKind.Object)
+ {
+ foreach (var prop in element.Value.EnumerateObject())
+ yield return prop;
+ }
+ }
+
+ ///
+ /// 获取所有属性名
+ ///
+ public static IEnumerable GetPropertyNames(this JsonElement? element)
+ => element.GetProperties().Select(p => p.Name);
+
+ ///
+ /// 判断是否包含某属性
+ ///
+ public static bool HasProperty(this JsonElement? element, string propertyName)
+ => element?.ValueKind == JsonValueKind.Object &&
+ element.Value.TryGetProperty(propertyName, out _);
+
+ #endregion
+
+ #region 类型判断
+
+ public static bool IsNull(this JsonElement? element)
+ => element == null || element.Value.ValueKind == JsonValueKind.Null;
+
+ public static bool IsNullOrUndefined(this JsonElement? element)
+ => element == null || element.Value.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined;
+
+ public static bool IsObject(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Object;
+
+ public static bool IsArray(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Array;
+
+ public static bool IsString(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.String;
+
+ public static bool IsNumber(this JsonElement? element)
+ => element?.ValueKind == JsonValueKind.Number;
+
+ public static bool IsBool(this JsonElement? element)
+ => element?.ValueKind is JsonValueKind.True or JsonValueKind.False;
+
+ public static bool Exists(this JsonElement? element)
+ => element != null && element.Value.ValueKind != JsonValueKind.Undefined;
+
+ #endregion
+
+ #region 反序列化
+
+ ///
+ /// 反序列化为指定类型
+ ///
+ public static T? Deserialize(this JsonElement? element, JsonSerializerOptions? options = null)
+ => element.HasValue ? element.Value.Deserialize(options) : default;
+
+ ///
+ /// 反序列化为指定类型,带默认值
+ ///
+ public static T Deserialize(this JsonElement? element, T defaultValue, JsonSerializerOptions? options = null)
+ => element.HasValue ? element.Value.Deserialize(options) ?? defaultValue : defaultValue;
+
+ #endregion
+
+ #region 转换为字典/动态类型
+
+ ///
+ /// 转换为 Dictionary
+ ///
+ public static Dictionary? ToDictionary(this JsonElement? element)
+ {
+ if (element?.ValueKind != JsonValueKind.Object)
+ return null;
+
+ var dict = new Dictionary();
+ foreach (var prop in element.Value.EnumerateObject())
+ dict[prop.Name] = prop.Value;
+ return dict;
+ }
+
+ #endregion
+
+ #region 原始值
+
+ ///
+ /// 获取原始 JSON 字符串
+ ///
+ public static string? GetRawText(this JsonElement? element)
+ => element?.GetRawText();
+
+ #endregion
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/IOpenAiResponseService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/IOpenAiResponseService.cs
index ab719e86..04cc9672 100644
--- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/IOpenAiResponseService.cs
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/IOpenAiResponseService.cs
@@ -1,4 +1,5 @@
-using Yi.Framework.AiHub.Domain.Shared.Dtos;
+using System.Text.Json;
+using Yi.Framework.AiHub.Domain.Shared.Dtos;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi.Responses;
namespace Yi.Framework.AiHub.Domain.AiGateWay;
@@ -12,7 +13,7 @@ public interface IOpenAiResponseService
///
///
///
- public IAsyncEnumerable<(string, dynamic?)> ResponsesStreamAsync(AiModelDescribe aiModelDescribe,
+ public IAsyncEnumerable<(string, JsonElement?)> ResponsesStreamAsync(AiModelDescribe aiModelDescribe,
OpenAiResponsesInput input,
CancellationToken cancellationToken);
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorCustomOpenAI/Chats/OpenAiResponseService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorCustomOpenAI/Chats/OpenAiResponseService.cs
index 51d248fc..975c28a1 100644
--- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorCustomOpenAI/Chats/OpenAiResponseService.cs
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorCustomOpenAI/Chats/OpenAiResponseService.cs
@@ -13,7 +13,7 @@ namespace Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorCustomOpenAI.Chats;
public class OpenAiResponseService(ILogger logger,IHttpClientFactory httpClientFactory):IOpenAiResponseService
{
- public async IAsyncEnumerable<(string, dynamic?)> ResponsesStreamAsync(AiModelDescribe options, OpenAiResponsesInput input,
+ public async IAsyncEnumerable<(string, JsonElement?)> ResponsesStreamAsync(AiModelDescribe options, OpenAiResponsesInput input,
CancellationToken cancellationToken)
{
using var openai =
@@ -73,7 +73,7 @@ public class OpenAiResponseService(ILogger logger,IHttpCl
data = line[OpenAIConstant.Data.Length..].Trim();
- var result = JsonSerializer.Deserialize(data,
+ var result = JsonSerializer.Deserialize(data,
ThorJsonSerializer.DefaultOptions);
yield return (eventType, result);
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs
index d0346507..2ecf4320 100644
--- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs
@@ -21,6 +21,7 @@ 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.AiHub.Domain.Shared.Extensions;
using Yi.Framework.Core.Extensions;
using Yi.Framework.SqlSugarCore.Abstractions;
using JsonSerializer = System.Text.Json.JsonSerializer;
@@ -91,30 +92,7 @@ public class AiGateWayManager : DomainService
return aiModelDescribe;
}
-
- ///
- /// 聊天完成-流式
- ///
- ///
- ///
- ///
- public async IAsyncEnumerable CompleteChatStreamAsync(
- ThorChatCompletionsRequest request,
- [EnumeratorCancellation] CancellationToken cancellationToken)
- {
- _specialCompatible.Compatible(request);
- var modelDescribe = await GetModelAsync(ModelApiTypeEnum.OpenAi, request.Model);
- var chatService =
- LazyServiceProvider.GetRequiredKeyedService(modelDescribe.HandlerName);
-
- await foreach (var result in chatService.CompleteChatStreamAsync(modelDescribe, request, cancellationToken))
- {
- result.SupplementalMultiplier(modelDescribe.Multiplier);
- yield return result;
- }
- }
-
-
+
///
/// 聊天完成-非流式
///
@@ -176,6 +154,7 @@ public class AiGateWayManager : DomainService
await response.WriteAsJsonAsync(data, cancellationToken);
}
+
///
/// 聊天完成-缓存处理
///
@@ -201,8 +180,12 @@ public class AiGateWayManager : DomainService
response.Headers.TryAdd("Connection", "keep-alive");
- var gateWay = LazyServiceProvider.GetRequiredService();
- var completeChatResponse = gateWay.CompleteChatStreamAsync(request, cancellationToken);
+ _specialCompatible.Compatible(request);
+ var modelDescribe = await GetModelAsync(ModelApiTypeEnum.OpenAi, request.Model);
+ var chatService =
+ LazyServiceProvider.GetRequiredKeyedService(modelDescribe.HandlerName);
+
+ var completeChatResponse = chatService.CompleteChatStreamAsync(modelDescribe,request, cancellationToken);
var tokenUsage = new ThorUsageResponse();
//缓存队列算法
@@ -244,6 +227,7 @@ public class AiGateWayManager : DomainService
{
await foreach (var data in completeChatResponse)
{
+ data.SupplementalMultiplier(modelDescribe.Multiplier);
if (data.Usage is not null && (data.Usage.CompletionTokens > 0 || data.Usage.OutputTokens > 0))
{
tokenUsage = data.Usage;
@@ -316,8 +300,8 @@ public class AiGateWayManager : DomainService
}
}
}
-
-
+
+
///
/// 图片生成
///
@@ -385,8 +369,8 @@ public class AiGateWayManager : DomainService
throw new UserFriendlyException(errorContent);
}
}
-
-
+
+
///
/// 向量生成
///
@@ -498,30 +482,7 @@ public class AiGateWayManager : DomainService
throw new UserFriendlyException(errorContent);
}
}
-
-
- ///
- /// Anthropic聊天完成-流式
- ///
- ///
- ///
- ///
- public async IAsyncEnumerable<(string, AnthropicStreamDto?)> AnthropicCompleteChatStreamAsync(
- AnthropicInput request,
- [EnumeratorCancellation] CancellationToken cancellationToken)
- {
- _specialCompatible.AnthropicCompatible(request);
- var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Claude, request.Model);
- var chatService =
- LazyServiceProvider.GetRequiredKeyedService(modelDescribe.HandlerName);
-
- await foreach (var result in chatService.StreamChatCompletionsAsync(modelDescribe, request, cancellationToken))
- {
- result.Item2.SupplementalMultiplier(modelDescribe.Multiplier);
- yield return result;
- }
- }
-
+
///
/// Anthropic聊天完成-非流式
@@ -582,6 +543,7 @@ public class AiGateWayManager : DomainService
await response.WriteAsJsonAsync(data, cancellationToken);
}
+
///
/// Anthropic聊天完成-缓存处理
///
@@ -605,16 +567,20 @@ public class AiGateWayManager : DomainService
response.ContentType = "text/event-stream;charset=utf-8;";
response.Headers.TryAdd("Cache-Control", "no-cache");
response.Headers.TryAdd("Connection", "keep-alive");
-
-
- var gateWay = LazyServiceProvider.GetRequiredService();
- var completeChatResponse = gateWay.AnthropicCompleteChatStreamAsync(request, cancellationToken);
+
+ _specialCompatible.AnthropicCompatible(request);
+ var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Claude, request.Model);
+ var chatService =
+ LazyServiceProvider.GetRequiredKeyedService(modelDescribe.HandlerName);
+
+ var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe,request, cancellationToken);
ThorUsageResponse? tokenUsage = null;
StringBuilder backupSystemContent = new StringBuilder();
try
{
await foreach (var responseResult in completeChatResponse)
{
+ responseResult.Item2.SupplementalMultiplier(modelDescribe.Multiplier);
//message_start是为了保底机制
if (responseResult.Item1.Contains("message_delta") || responseResult.Item1.Contains("message_start"))
{
@@ -679,7 +645,6 @@ public class AiGateWayManager : DomainService
Guid? tokenId = null,
CancellationToken cancellationToken = default)
{
- //todo 1
// _specialCompatible.AnthropicCompatible(request);
var response = httpContext.Response;
// 设置响应头,声明是 json
@@ -689,69 +654,47 @@ public class AiGateWayManager : DomainService
var chatService =
LazyServiceProvider.GetRequiredKeyedService(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);
- // }
- // }
+
+ data.SupplementalMultiplier(modelDescribe.Multiplier);
+
+ var tokenUsage= new ThorUsageResponse
+ {
+ InputTokens = data.Usage.InputTokens,
+ OutputTokens = data.Usage.OutputTokens,
+ TotalTokens = data.Usage.InputTokens + data.Usage.OutputTokens,
+ };
+ if (userId is not null)
+ {
+ await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
+ new MessageInputDto
+ {
+ Content = "不予存储",
+ ModelId = request.Model,
+ TokenUsage = tokenUsage,
+ }, tokenId);
+
+ await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
+ new MessageInputDto
+ {
+ Content = "不予存储",
+ ModelId = request.Model,
+ TokenUsage = tokenUsage
+ }, tokenId);
+
+ await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage, tokenId);
+
+ // 扣减尊享token包用量
+ var totalTokens = tokenUsage.TotalTokens ?? 0;
+ if (totalTokens > 0)
+ {
+ await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
+ }
+ }
await response.WriteAsJsonAsync(data, cancellationToken);
}
- ///
- /// OpenAi 响应-流式
- ///
- ///
- ///
- ///
- 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(modelDescribe.HandlerName);
-
- await foreach (var result in chatService.ResponsesStreamAsync(modelDescribe, request, cancellationToken))
- {
- //todo 倍率
- // result.Item2.SupplementalMultiplier(modelDescribe.Multiplier);
- yield return result;
- }
- }
-
-
///
/// OpenAi响应-流式-缓存处理
///
@@ -776,7 +719,11 @@ public class AiGateWayManager : DomainService
response.Headers.TryAdd("Cache-Control", "no-cache");
response.Headers.TryAdd("Connection", "keep-alive");
- var completeChatResponse = OpenAiResponsesAsync(request, cancellationToken);
+ var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Response, request.Model);
+ var chatService =
+ LazyServiceProvider.GetRequiredKeyedService(modelDescribe.HandlerName);
+
+ var completeChatResponse = chatService.ResponsesStreamAsync(modelDescribe,request, cancellationToken);
ThorUsageResponse? tokenUsage = null;
try
{
@@ -785,12 +732,14 @@ public class AiGateWayManager : DomainService
//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;
+ var obj = responseResult.Item2!.Value;
+ int inputTokens = obj.GetPath("response","usage","input_tokens").GetInt();
+ int outputTokens = obj.GetPath("response","usage","output_tokens").GetInt();
+ inputTokens=Convert.ToInt32(inputTokens * modelDescribe.Multiplier);
+ outputTokens=Convert.ToInt32(outputTokens * modelDescribe.Multiplier);
tokenUsage = new ThorUsageResponse
{
- PromptTokens = inputTokens,
+ PromptTokens =inputTokens,
InputTokens = inputTokens,
OutputTokens = outputTokens,
CompletionTokens = outputTokens,
@@ -839,7 +788,7 @@ public class AiGateWayManager : DomainService
}
- #region Anthropic格式Http响应
+ #region 流式传输格式Http响应
private static readonly byte[] EventPrefix = "event: "u8.ToArray();
private static readonly byte[] DataPrefix = "data: "u8.ToArray();