176 lines
4.8 KiB
Go
176 lines
4.8 KiB
Go
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"
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
type AiProxyApi struct{}
|
||
|
||
var aiProxyService = service.ServiceGroupApp.AppServiceGroup.AiProxyService
|
||
var aiApiKeyService = service.ServiceGroupApp.AppServiceGroup.AiApiKeyService
|
||
|
||
// ChatCompletions OpenAI兼容的聊天补全接口
|
||
// @Tags AiProxy
|
||
// @Summary 聊天补全(OpenAI兼容)
|
||
// @accept application/json
|
||
// @Produce application/json
|
||
// @Param data body request.ChatCompletionRequest true "聊天请求"
|
||
// @Success 200 {object} response.ChatCompletionResponse "聊天响应"
|
||
// @Router /v1/chat/completions [post]
|
||
func (a *AiProxyApi) ChatCompletions(c *gin.Context) {
|
||
var req request.ChatCompletionRequest
|
||
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.ProcessChatCompletionStream(c, &req)
|
||
return
|
||
}
|
||
|
||
// 处理普通响应
|
||
resp, err := aiProxyService.ProcessChatCompletion(c.Request.Context(), &req)
|
||
if err != nil {
|
||
global.GVA_LOG.Error("处理聊天请求失败!", zap.Error(err))
|
||
response.FailWithMessage(err.Error(), c)
|
||
return
|
||
}
|
||
|
||
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)
|
||
}
|