🎨 优化模型配置 && 新增apikey功能 && 完善通用接口

This commit is contained in:
2026-03-03 17:13:24 +08:00
parent 2714e63d2a
commit 7dae1a6e2b
46 changed files with 3063 additions and 278 deletions

View File

@@ -1,11 +1,13 @@
package app
import (
"net/http"
"git.echol.cn/loser/ai_proxy/server/global"
"git.echol.cn/loser/ai_proxy/server/model/app"
"git.echol.cn/loser/ai_proxy/server/model/app/request"
"git.echol.cn/loser/ai_proxy/server/model/common/response"
"git.echol.cn/loser/ai_proxy/server/service"
"git.echol.cn/loser/ai_proxy/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
@@ -13,11 +15,11 @@ import (
type AiProxyApi struct{}
var aiProxyService = service.ServiceGroupApp.AppServiceGroup.AiProxyService
var aiApiKeyService = service.ServiceGroupApp.AppServiceGroup.AiApiKeyService
// ChatCompletions OpenAI兼容的聊天补全接口
// @Tags AiProxy
// @Summary 聊天补全OpenAI兼容
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.ChatCompletionRequest true "聊天请求"
@@ -31,16 +33,46 @@ func (a *AiProxyApi) ChatCompletions(c *gin.Context) {
return
}
userId := utils.GetUserID(c)
// 获取 API Key 信息
apiKeyInfo, exists := c.Get("ai_api_key")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未找到API密钥信息"})
return
}
apiKey := apiKeyInfo.(*app.AiApiKey)
// 验证模型权限
if req.Model != "" && !aiApiKeyService.CheckModelPermission(apiKey, req.Model) {
c.JSON(http.StatusForbidden, gin.H{
"error": gin.H{
"message": "该API密钥无权使用此模型: " + req.Model,
"type": "invalid_request_error",
"code": "model_not_allowed",
},
})
return
}
// 验证预设权限
if req.PresetName != "" && !aiApiKeyService.CheckPresetPermission(apiKey, req.PresetName) {
c.JSON(http.StatusForbidden, gin.H{
"error": gin.H{
"message": "该API密钥无权使用此预设: " + req.PresetName,
"type": "invalid_request_error",
"code": "preset_not_allowed",
},
})
return
}
// 处理流式响应
if req.Stream {
aiProxyService.ProcessChatCompletionStream(c, userId, &req)
aiProxyService.ProcessChatCompletionStream(c, &req)
return
}
// 处理普通响应
resp, err := aiProxyService.ProcessChatCompletion(c.Request.Context(), userId, &req)
resp, err := aiProxyService.ProcessChatCompletion(c.Request.Context(), &req)
if err != nil {
global.GVA_LOG.Error("处理聊天请求失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
@@ -49,3 +81,95 @@ func (a *AiProxyApi) ChatCompletions(c *gin.Context) {
c.JSON(200, resp)
}
// ListModels 获取可用模型列表
// @Tags AiProxy
// @Summary 获取可用模型列表OpenAI兼容
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.ModelListResponse "模型列表"
// @Router /v1/models [get]
func (a *AiProxyApi) ListModels(c *gin.Context) {
// 获取 API Key 信息
apiKeyInfo, exists := c.Get("ai_api_key")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未找到API密钥信息"})
return
}
apiKey := apiKeyInfo.(*app.AiApiKey)
// 获取可用模型列表
models, err := aiProxyService.GetAvailableModels(apiKey)
if err != nil {
global.GVA_LOG.Error("获取模型列表失败!", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(200, models)
}
// ClaudeMessages Claude API兼容的消息接口
// @Tags AiProxy
// @Summary 消息接口Claude兼容
// @accept application/json
// @Produce application/json
// @Param data body request.ClaudeMessageRequest true "消息请求"
// @Success 200 {object} response.ClaudeMessageResponse "消息响应"
// @Router /v1/messages [post]
func (a *AiProxyApi) ClaudeMessages(c *gin.Context) {
var req request.ClaudeMessageRequest
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 获取 API Key 信息
apiKeyInfo, exists := c.Get("ai_api_key")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未找到API密钥信息"})
return
}
apiKey := apiKeyInfo.(*app.AiApiKey)
// 验证模型权限
if req.Model != "" && !aiApiKeyService.CheckModelPermission(apiKey, req.Model) {
c.JSON(http.StatusForbidden, gin.H{
"error": gin.H{
"message": "该API密钥无权使用此模型: " + req.Model,
"type": "invalid_request_error",
"code": "model_not_allowed",
},
})
return
}
// 验证预设权限
if req.PresetName != "" && !aiApiKeyService.CheckPresetPermission(apiKey, req.PresetName) {
c.JSON(http.StatusForbidden, gin.H{
"error": gin.H{
"message": "该API密钥无权使用此预设: " + req.PresetName,
"type": "invalid_request_error",
"code": "preset_not_allowed",
},
})
return
}
// 处理流式响应
if req.Stream {
aiProxyService.ProcessClaudeMessageStream(c, &req)
return
}
// 处理普通响应
resp, err := aiProxyService.ProcessClaudeMessage(c.Request.Context(), &req)
if err != nil {
global.GVA_LOG.Error("处理Claude消息请求失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
c.JSON(200, resp)
}