mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-03-03 00:00:58 +08:00
feat: 新增向量嵌入服务支持
新增SiliconFlow向量嵌入服务实现,支持文本向量化功能: - 新增ITextEmbeddingService接口和SiliconFlowTextEmbeddingService实现 - 新增EmbeddingCreateRequest/Response等向量相关DTO - 在AiGateWayManager中新增EmbeddingForStatisticsAsync方法 - 在OpenApiService中新增向量生成API接口 - 扩展ModelTypeEnum枚举支持Embedding类型 - 优化ThorChatMessage的Content属性处理逻辑
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -10,6 +11,7 @@ using Newtonsoft.Json.Serialization;
|
||||
using Volo.Abp.Domain.Services;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi.Embeddings;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi.Images;
|
||||
using Yi.Framework.AiHub.Domain.AiGateWay;
|
||||
using Yi.Framework.AiHub.Domain.AiGateWay.Exceptions;
|
||||
@@ -276,19 +278,20 @@ public class AiGateWayManager : DomainService
|
||||
/// <param name="request"></param>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public async Task CreateImageForStatisticsAsync(HttpContext context,Guid? userId,Guid? sessionId, ImageCreateRequest request)
|
||||
public async Task CreateImageForStatisticsAsync(HttpContext context, Guid? userId, Guid? sessionId,
|
||||
ImageCreateRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = request.Model;
|
||||
if (string.IsNullOrEmpty(model)) model = "dall-e-2";
|
||||
|
||||
|
||||
var modelDescribe = await GetModelAsync(model);
|
||||
|
||||
|
||||
// 获取渠道指定的实现类型的服务
|
||||
var imageService =
|
||||
LazyServiceProvider.GetRequiredKeyedService<IImageService>(modelDescribe.HandlerName);
|
||||
|
||||
|
||||
var response = await imageService.CreateImage(request, modelDescribe);
|
||||
|
||||
if (response.Error != null || response.Results.Count == 0)
|
||||
@@ -297,7 +300,7 @@ public class AiGateWayManager : DomainService
|
||||
}
|
||||
|
||||
await context.Response.WriteAsJsonAsync(response);
|
||||
|
||||
|
||||
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
@@ -322,4 +325,110 @@ public class AiGateWayManager : DomainService
|
||||
throw new UserFriendlyException(errorContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 向量生成
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
/// <exception cref="BusinessException"></exception>
|
||||
public async Task EmbeddingForStatisticsAsync(HttpContext context, Guid? userId, Guid? sessionId,
|
||||
ThorEmbeddingInput input)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (input == null) throw new Exception("模型校验异常");
|
||||
|
||||
using var embedding =
|
||||
Activity.Current?.Source.StartActivity("向量模型调用");
|
||||
|
||||
var modelDescribe = await GetModelAsync(input.Model);
|
||||
|
||||
// 获取渠道指定的实现类型的服务
|
||||
var embeddingService =
|
||||
LazyServiceProvider.GetRequiredKeyedService<ITextEmbeddingService>(modelDescribe.HandlerName);
|
||||
|
||||
var embeddingCreateRequest = new EmbeddingCreateRequest
|
||||
{
|
||||
Model = input.Model,
|
||||
EncodingFormat = input.EncodingFormat
|
||||
};
|
||||
|
||||
//dto进行转换,支持多种格式
|
||||
if (input.Input is JsonElement str)
|
||||
{
|
||||
if (str.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var inputString = str.EnumerateArray().Select(x => x.ToString()).ToArray();
|
||||
embeddingCreateRequest.InputAsList = inputString.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Input,输入格式错误,非string或Array类型");
|
||||
}
|
||||
}
|
||||
else if (input.Input is string strInput)
|
||||
{
|
||||
embeddingCreateRequest.Input = strInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Input,输入格式错误,未找到类型");
|
||||
}
|
||||
|
||||
|
||||
var stream =
|
||||
await embeddingService.EmbeddingAsync(embeddingCreateRequest, modelDescribe, context.RequestAborted);
|
||||
|
||||
var usage = new ThorUsageResponse()
|
||||
{
|
||||
InputTokens = stream.Usage?.InputTokens ?? 0,
|
||||
CompletionTokens = 0,
|
||||
TotalTokens = stream.Usage?.InputTokens ?? 0
|
||||
};
|
||||
await context.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
input.Model,
|
||||
stream.Data,
|
||||
stream.Error,
|
||||
stream.ObjectTypeName,
|
||||
Usage = usage
|
||||
});
|
||||
|
||||
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
Content = string.Empty,
|
||||
ModelId = input.Model,
|
||||
TokenUsage = usage,
|
||||
});
|
||||
|
||||
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
Content = string.Empty,
|
||||
ModelId = input.Model,
|
||||
TokenUsage = usage
|
||||
});
|
||||
|
||||
await _usageStatisticsManager.SetUsageAsync(userId, input.Model, usage);
|
||||
}
|
||||
catch (ThorRateLimitException)
|
||||
{
|
||||
context.Response.StatusCode = 429;
|
||||
}
|
||||
catch (UnauthorizedAccessException e)
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var errorContent = $"嵌入Ai异常,异常信息:\n当前Ai模型:{input.Model}\n异常信息:{e.Message}\n异常堆栈:{e}";
|
||||
throw new UserFriendlyException(errorContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user