using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Volo.Abp.Application.Services; using Volo.Abp.Users; using Yi.Framework.AiHub.Domain.Entities; 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 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.Enums; using Yi.Framework.Rbac.Application.Contracts.IServices; using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.AiHub.Application.Services; public class OpenApiService : ApplicationService { private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; private readonly TokenManager _tokenManager; private readonly AiGateWayManager _aiGateWayManager; private readonly ISqlSugarRepository _aiModelRepository; private readonly AiBlacklistManager _aiBlacklistManager; private readonly IAccountService _accountService; private readonly PremiumPackageManager _premiumPackageManager; public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger logger, TokenManager tokenManager, AiGateWayManager aiGateWayManager, ISqlSugarRepository aiModelRepository, AiBlacklistManager aiBlacklistManager, IAccountService accountService, PremiumPackageManager premiumPackageManager) { _httpContextAccessor = httpContextAccessor; _logger = logger; _tokenManager = tokenManager; _aiGateWayManager = aiGateWayManager; _aiModelRepository = aiModelRepository; _aiBlacklistManager = aiBlacklistManager; _accountService = accountService; _premiumPackageManager = premiumPackageManager; } /// /// 对话 /// /// /// [HttpPost("openApi/v1/chat/completions")] public async Task ChatCompletionsAsync([FromBody] ThorChatCompletionsRequest input, CancellationToken cancellationToken) { //前面都是校验,后面才是真正的调用 var httpContext = this._httpContextAccessor.HttpContext; var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext)); await _aiBlacklistManager.VerifiyAiBlacklist(userId); //如果是尊享包服务,需要校验是是否尊享包足够 if (PremiumPackageConst.ModeIds.Contains(input.Model)) { // 检查尊享token包用量 var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId); if (availableTokens <= 0) { throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); } } //ai网关代理httpcontext if (input.Stream == true) { await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId, null, cancellationToken); } else { await _aiGateWayManager.CompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId, null, cancellationToken); } } /// /// 图片生成 /// /// /// [HttpPost("openApi/v1/images/generations")] public async Task ImagesGenerationsAsync([FromBody] ImageCreateRequest input, CancellationToken cancellationToken) { var httpContext = this._httpContextAccessor.HttpContext; Intercept(httpContext); var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext)); await _aiBlacklistManager.VerifiyAiBlacklist(userId); await _aiGateWayManager.CreateImageForStatisticsAsync(httpContext, userId, null, input); } /// /// 向量生成 /// /// /// [HttpPost("openApi/v1/embeddings")] public async Task EmbeddingAsync([FromBody] ThorEmbeddingInput input, CancellationToken cancellationToken) { var httpContext = this._httpContextAccessor.HttpContext; Intercept(httpContext); var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext)); await _aiBlacklistManager.VerifiyAiBlacklist(userId); await _aiGateWayManager.EmbeddingForStatisticsAsync(httpContext, userId, null, input); } /// /// 获取模型列表 /// /// [HttpGet("openApi/v1/models")] public async Task ModelsAsync() { var data = await _aiModelRepository._DbQueryable .Where(x => x.ModelType == ModelTypeEnum.Chat) .OrderByDescending(x => x.OrderNum) .Select(x => new ModelsDataDto { Id = x.ModelId, @object = "model", Created = DateTime.Now.ToUnixTimeSeconds(), OwnedBy = "organization-owner", Type = x.ModelId }).ToListAsync(); return new ModelsListDto() { Data = data }; } /// /// Anthropic对话(尊享服务专用) /// /// /// [HttpPost("openApi/v1/messages")] public async Task MessagesAsync([FromBody] AnthropicInput input, CancellationToken cancellationToken) { //前面都是校验,后面才是真正的调用 var httpContext = this._httpContextAccessor.HttpContext; var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext)); await _aiBlacklistManager.VerifiyAiBlacklist(userId); // 验证用户是否为VIP var userInfo = await _accountService.GetAsync(null, null, userId); if (userInfo == null) { throw new UserFriendlyException("用户信息不存在"); } // 检查是否为VIP(使用RoleCodes判断) if (!userInfo.RoleCodes.Contains(AiHubConst.VipRole) && userInfo.User.UserName != "cc") { throw new UserFriendlyException("该接口为尊享服务专用,需要VIP权限才能使用"); } // 检查尊享token包用量 var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId); if (availableTokens <= 0) { throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); } //ai网关代理httpcontext if (input.Stream) { await _aiGateWayManager.AnthropicCompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId, null, cancellationToken); } else { await _aiGateWayManager.AnthropicCompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId, null, cancellationToken); } } #region 私有 private string? GetTokenByHttpContext(HttpContext httpContext) { // 优先从 x-api-key 获取 string apiKeyHeader = httpContext.Request.Headers["x-api-key"]; if (!string.IsNullOrWhiteSpace(apiKeyHeader)) { return apiKeyHeader.Trim(); } // 再检查 Authorization 头 string authHeader = httpContext.Request.Headers["Authorization"]; if (!string.IsNullOrWhiteSpace(authHeader) && authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { return authHeader.Substring("Bearer ".Length).Trim(); } return null; } private void Intercept(HttpContext httpContext) { if (httpContext.Request.Host.Value == "yxai.chat") { throw new UserFriendlyException("当前海外站点不支持大流量接口,请使用转发站点:https://ai.ccnetcore.com"); } } #endregion }