修复无法选择供应商问题
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/backend/.idea
|
/backend/.idea
|
||||||
/bin
|
/bin
|
||||||
|
.aider*
|
||||||
|
|||||||
@@ -399,15 +399,15 @@ func (h *APIHandler) GetRequestLogStatsHandler(c *gin.Context) {
|
|||||||
func (h *APIHandler) ClearRequestLogsHandler(c *gin.Context) {
|
func (h *APIHandler) ClearRequestLogsHandler(c *gin.Context) {
|
||||||
// 获取查询参数
|
// 获取查询参数
|
||||||
olderThan := c.Query("older_than") // 清空多少天前的日志
|
olderThan := c.Query("older_than") // 清空多少天前的日志
|
||||||
|
|
||||||
// 先查询当前总记录数(包括软删除的记录)
|
// 先查询当前总记录数(包括软删除的记录)
|
||||||
var totalCountBefore int64
|
var totalCountBefore int64
|
||||||
h.DB.Unscoped().Model(&models.RequestLog{}).Count(&totalCountBefore)
|
h.DB.Unscoped().Model(&models.RequestLog{}).Count(&totalCountBefore)
|
||||||
|
|
||||||
// 查询活跃记录数(不包括软删除的记录)
|
// 查询活跃记录数(不包括软删除的记录)
|
||||||
var activeCountBefore int64
|
var activeCountBefore int64
|
||||||
h.DB.Model(&models.RequestLog{}).Count(&activeCountBefore)
|
h.DB.Model(&models.RequestLog{}).Count(&activeCountBefore)
|
||||||
|
|
||||||
var startTime time.Time
|
var startTime time.Time
|
||||||
if olderThan != "" {
|
if olderThan != "" {
|
||||||
// 解析天数
|
// 解析天数
|
||||||
@@ -416,12 +416,12 @@ func (h *APIHandler) ClearRequestLogsHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid older_than parameter, must be a number representing days"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid older_than parameter, must be a number representing days"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if days < 0 {
|
if days < 0 {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "older_than must be 0 or greater"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "older_than must be 0 or greater"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是0天,要删除所有日志,不需要时间限制
|
// 如果是0天,要删除所有日志,不需要时间限制
|
||||||
if days == 0 {
|
if days == 0 {
|
||||||
// 使用Unscoped进行硬删除,真正删除记录而非软删除
|
// 使用Unscoped进行硬删除,真正删除记录而非软删除
|
||||||
@@ -430,81 +430,80 @@ func (h *APIHandler) ClearRequestLogsHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear logs"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear logs"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行VACUUM来回收数据库空间
|
// 执行VACUUM来回收数据库空间
|
||||||
vacuumError := ""
|
vacuumError := ""
|
||||||
if err := h.DB.Exec("VACUUM").Error; err != nil {
|
if err := h.DB.Exec("VACUUM").Error; err != nil {
|
||||||
vacuumError = fmt.Sprintf("VACUUM failed: %v", err)
|
vacuumError = fmt.Sprintf("VACUUM failed: %v", err)
|
||||||
fmt.Printf("Warning: %s\n", vacuumError)
|
fmt.Printf("Warning: %s\n", vacuumError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询删除后的记录数
|
// 查询删除后的记录数
|
||||||
var totalCountAfter int64
|
var totalCountAfter int64
|
||||||
h.DB.Unscoped().Model(&models.RequestLog{}).Count(&totalCountAfter)
|
h.DB.Unscoped().Model(&models.RequestLog{}).Count(&totalCountAfter)
|
||||||
|
|
||||||
// 返回详细的删除信息
|
// 返回详细的删除信息
|
||||||
response := gin.H{
|
response := gin.H{
|
||||||
"message": "All logs cleared successfully with hard delete and vacuum",
|
"message": "All logs cleared successfully with hard delete and vacuum",
|
||||||
"deleted_count": result.RowsAffected,
|
"deleted_count": result.RowsAffected,
|
||||||
"older_than_days": olderThan,
|
"older_than_days": olderThan,
|
||||||
"total_count_before": totalCountBefore,
|
"total_count_before": totalCountBefore,
|
||||||
"active_count_before": activeCountBefore,
|
"active_count_before": activeCountBefore,
|
||||||
"total_count_after": totalCountAfter,
|
"total_count_after": totalCountAfter,
|
||||||
"hard_delete": true,
|
"hard_delete": true,
|
||||||
"vacuum_executed": vacuumError == "",
|
"vacuum_executed": vacuumError == "",
|
||||||
}
|
}
|
||||||
if vacuumError != "" {
|
if vacuumError != "" {
|
||||||
response["vacuum_error"] = vacuumError
|
response["vacuum_error"] = vacuumError
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime = time.Now().AddDate(0, 0, -days)
|
startTime = time.Now().AddDate(0, 0, -days)
|
||||||
} else {
|
} else {
|
||||||
// 如果没有指定天数,默认清空30天前的日志
|
// 如果没有指定天数,默认清空30天前的日志
|
||||||
startTime = time.Now().AddDate(0, 0, -30)
|
startTime = time.Now().AddDate(0, 0, -30)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用Unscoped执行硬删除操作,真正删除记录而非软删除
|
// 使用Unscoped执行硬删除操作,真正删除记录而非软删除
|
||||||
result := h.DB.Unscoped().Where("created_at < ?", startTime).Delete(&models.RequestLog{})
|
result := h.DB.Unscoped().Where("created_at < ?", startTime).Delete(&models.RequestLog{})
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear logs"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear logs"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行VACUUM来回收数据库空间
|
// 执行VACUUM来回收数据库空间
|
||||||
vacuumError := ""
|
vacuumError := ""
|
||||||
if err := h.DB.Exec("VACUUM").Error; err != nil {
|
if err := h.DB.Exec("VACUUM").Error; err != nil {
|
||||||
vacuumError = fmt.Sprintf("VACUUM failed: %v", err)
|
vacuumError = fmt.Sprintf("VACUUM failed: %v", err)
|
||||||
fmt.Printf("Warning: %s\n", vacuumError)
|
fmt.Printf("Warning: %s\n", vacuumError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询删除后的记录数
|
// 查询删除后的记录数
|
||||||
var totalCountAfter int64
|
var totalCountAfter int64
|
||||||
h.DB.Unscoped().Model(&models.RequestLog{}).Count(&totalCountAfter)
|
h.DB.Unscoped().Model(&models.RequestLog{}).Count(&totalCountAfter)
|
||||||
|
|
||||||
// 返回详细的删除信息
|
// 返回详细的删除信息
|
||||||
response := gin.H{
|
response := gin.H{
|
||||||
"message": "Logs cleared successfully with hard delete and vacuum",
|
"message": "Logs cleared successfully with hard delete and vacuum",
|
||||||
"deleted_count": result.RowsAffected,
|
"deleted_count": result.RowsAffected,
|
||||||
"older_than_days": olderThan,
|
"older_than_days": olderThan,
|
||||||
"cutoff_time": startTime,
|
"cutoff_time": startTime,
|
||||||
"total_count_before": totalCountBefore,
|
"total_count_before": totalCountBefore,
|
||||||
"active_count_before": activeCountBefore,
|
"active_count_before": activeCountBefore,
|
||||||
"total_count_after": totalCountAfter,
|
"total_count_after": totalCountAfter,
|
||||||
"hard_delete": true,
|
"hard_delete": true,
|
||||||
"vacuum_executed": vacuumError == "",
|
"vacuum_executed": vacuumError == "",
|
||||||
}
|
}
|
||||||
if vacuumError != "" {
|
if vacuumError != "" {
|
||||||
response["vacuum_error"] = vacuumError
|
response["vacuum_error"] = vacuumError
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// GetLogCleanerStatusHandler 获取日志清理器状态
|
// GetLogCleanerStatusHandler 获取日志清理器状态
|
||||||
func (h *APIHandler) GetLogCleanerStatusHandler(c *gin.Context) {
|
func (h *APIHandler) GetLogCleanerStatusHandler(c *gin.Context) {
|
||||||
if h.LogCleaner == nil {
|
if h.LogCleaner == nil {
|
||||||
@@ -542,21 +541,20 @@ func (h *APIHandler) ForceLogCleanupHandler(c *gin.Context) {
|
|||||||
|
|
||||||
// 返回清理报告
|
// 返回清理报告
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"message": "Manual log cleanup completed",
|
"message": "Manual log cleanup completed",
|
||||||
"execute_time": report.ExecuteTime.Format("2006-01-02 15:04:05"),
|
"execute_time": report.ExecuteTime.Format("2006-01-02 15:04:05"),
|
||||||
"duration": report.Duration.String(),
|
"duration": report.Duration.String(),
|
||||||
"deleted_count": report.DeletedCount,
|
"deleted_count": report.DeletedCount,
|
||||||
"total_count_before": report.TotalCountBefore,
|
"total_count_before": report.TotalCountBefore,
|
||||||
"active_count_before": report.ActiveCountBefore,
|
"active_count_before": report.ActiveCountBefore,
|
||||||
"total_count_after": report.TotalCountAfter,
|
"total_count_after": report.TotalCountAfter,
|
||||||
"cutoff_time": report.CutoffTime.Format("2006-01-02 15:04:05"),
|
"cutoff_time": report.CutoffTime.Format("2006-01-02 15:04:05"),
|
||||||
"vacuum_duration": report.VacuumDuration.String(),
|
"vacuum_duration": report.VacuumDuration.String(),
|
||||||
"vacuum_error": report.VacuumError,
|
"vacuum_error": report.VacuumError,
|
||||||
"success": report.Success,
|
"success": report.Success,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============ API Key 管理相关处理器 ============
|
// ============ API Key 管理相关处理器 ============
|
||||||
|
|
||||||
// APIKeyListResponse API Key列表响应
|
// APIKeyListResponse API Key列表响应
|
||||||
@@ -569,13 +567,13 @@ type APIKeyListResponse struct {
|
|||||||
// GetAPIKeysHandler 获取所有API Key列表
|
// GetAPIKeysHandler 获取所有API Key列表
|
||||||
func (h *APIHandler) GetAPIKeysHandler(c *gin.Context) {
|
func (h *APIHandler) GetAPIKeysHandler(c *gin.Context) {
|
||||||
var apiKeys []models.APIKey
|
var apiKeys []models.APIKey
|
||||||
|
|
||||||
// 查询所有API Key
|
// 查询所有API Key
|
||||||
if err := h.DB.Order("created_at DESC").Find(&apiKeys).Error; err != nil {
|
if err := h.DB.Order("created_at DESC").Find(&apiKeys).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch API keys"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch API keys"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为响应格式
|
// 转换为响应格式
|
||||||
response := make([]APIKeyListResponse, len(apiKeys))
|
response := make([]APIKeyListResponse, len(apiKeys))
|
||||||
for i, key := range apiKeys {
|
for i, key := range apiKeys {
|
||||||
@@ -585,7 +583,7 @@ func (h *APIHandler) GetAPIKeysHandler(c *gin.Context) {
|
|||||||
CreatedAt: key.CreatedAt,
|
CreatedAt: key.CreatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"api_keys": response,
|
"api_keys": response,
|
||||||
"total": len(response),
|
"total": len(response),
|
||||||
@@ -600,36 +598,36 @@ type CreateAPIKeyRequest struct {
|
|||||||
// CreateAPIKeyHandler 创建新的API Key
|
// CreateAPIKeyHandler 创建新的API Key
|
||||||
func (h *APIHandler) CreateAPIKeyHandler(c *gin.Context) {
|
func (h *APIHandler) CreateAPIKeyHandler(c *gin.Context) {
|
||||||
var req CreateAPIKeyRequest
|
var req CreateAPIKeyRequest
|
||||||
|
|
||||||
// 解析请求
|
// 解析请求
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format: " + err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format: " + err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证Key不为空
|
// 验证Key不为空
|
||||||
if req.Key == "" {
|
if req.Key == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "API key cannot be empty"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "API key cannot be empty"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查Key是否已存在
|
// 检查Key是否已存在
|
||||||
var existingKey models.APIKey
|
var existingKey models.APIKey
|
||||||
if err := h.DB.Where("key = ?", req.Key).First(&existingKey).Error; err == nil {
|
if err := h.DB.Where("key = ?", req.Key).First(&existingKey).Error; err == nil {
|
||||||
c.JSON(http.StatusConflict, gin.H{"error": "API key already exists"})
|
c.JSON(http.StatusConflict, gin.H{"error": "API key already exists"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新的API Key
|
// 创建新的API Key
|
||||||
newAPIKey := models.APIKey{
|
newAPIKey := models.APIKey{
|
||||||
Key: req.Key,
|
Key: req.Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.DB.Create(&newAPIKey).Error; err != nil {
|
if err := h.DB.Create(&newAPIKey).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create API key"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create API key"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回创建的API Key
|
// 返回创建的API Key
|
||||||
c.JSON(http.StatusCreated, gin.H{
|
c.JSON(http.StatusCreated, gin.H{
|
||||||
"message": "API key created successfully",
|
"message": "API key created successfully",
|
||||||
@@ -649,7 +647,7 @@ func (h *APIHandler) DeleteAPIKeyHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "API key ID is required"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "API key ID is required"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找API Key
|
// 查找API Key
|
||||||
var apiKey models.APIKey
|
var apiKey models.APIKey
|
||||||
if err := h.DB.First(&apiKey, keyID).Error; err != nil {
|
if err := h.DB.First(&apiKey, keyID).Error; err != nil {
|
||||||
@@ -660,13 +658,13 @@ func (h *APIHandler) DeleteAPIKeyHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to find API key"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to find API key"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除API Key
|
// 删除API Key
|
||||||
if err := h.DB.Delete(&apiKey).Error; err != nil {
|
if err := h.DB.Delete(&apiKey).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete API key"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete API key"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"message": "API key deleted successfully",
|
"message": "API key deleted successfully",
|
||||||
"id": apiKey.ID,
|
"id": apiKey.ID,
|
||||||
|
|||||||
@@ -77,17 +77,7 @@ func (h *APIHandler) ChatCompletions(c *gin.Context) {
|
|||||||
if messagesRaw, ok := jsonBody["messages"].([]interface{}); ok {
|
if messagesRaw, ok := jsonBody["messages"].([]interface{}); ok {
|
||||||
for _, msgRaw := range messagesRaw {
|
for _, msgRaw := range messagesRaw {
|
||||||
if msgMap, ok := msgRaw.(map[string]interface{}); ok {
|
if msgMap, ok := msgRaw.(map[string]interface{}); ok {
|
||||||
if reasoning, exists := msgMap["reasoning"]; exists {
|
delete(msgMap, "reasoning")
|
||||||
// 如果 reasoning 是对象,提取 content 字段作为字符串
|
|
||||||
if reasoningMap, ok := reasoning.(map[string]interface{}); ok {
|
|
||||||
if reasoningContent, ok := reasoningMap["content"].(string); ok {
|
|
||||||
msgMap["reasoning"] = reasoningContent
|
|
||||||
} else {
|
|
||||||
// 如果无法提取 content,移除 reasoning 字段
|
|
||||||
delete(msgMap, "reasoning")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,8 +128,11 @@ func (h *APIHandler) ChatCompletions(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("接收到请求, 开始使用 %s 请求后端, 请求处理花费: %v", backendModel.Name, time.Since(requestTimestamp))
|
||||||
|
|
||||||
// 构建后端API URL
|
// 构建后端API URL
|
||||||
backendURL := backendModel.Provider.BaseURL + "/v1/chat/completions"
|
backendURL := backendModel.Provider.BaseURL + "/v1/chat/completions"
|
||||||
|
forwardRequestTimestamp := time.Now()
|
||||||
|
|
||||||
// 转发请求到后端
|
// 转发请求到后端
|
||||||
resp, err := forwardRequest(backendURL, backendModel.Provider.ApiKey, requestBody, c.GetHeader("User-Agent"))
|
resp, err := forwardRequest(backendURL, backendModel.Provider.ApiKey, requestBody, c.GetHeader("User-Agent"))
|
||||||
@@ -153,18 +146,22 @@ func (h *APIHandler) ChatCompletions(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// 记录响应时间
|
// 记录响应时间
|
||||||
responseTimestamp := time.Now()
|
responseTimestamp := time.Now()
|
||||||
|
log.Printf("后端模型已经回应: %s, 后端响应时间: %v", backendModel.Name, time.Since(forwardRequestTimestamp))
|
||||||
|
|
||||||
// 获取API Key ID
|
// 获取API Key ID
|
||||||
apiKeyID := getAPIKeyID(c)
|
apiKeyID := getAPIKeyID(c)
|
||||||
|
|
||||||
// 处理流式响应
|
// 处理流式响应
|
||||||
if isStream {
|
if isStream {
|
||||||
|
streamTimestamp := time.Now()
|
||||||
|
log.Printf("后端模型开始流送: %s", backendModel.Name)
|
||||||
handleStreamingResponse(c, resp, requestTimestamp, responseTimestamp,
|
handleStreamingResponse(c, resp, requestTimestamp, responseTimestamp,
|
||||||
apiKeyID, modelName, backendModel, requestTokenCount,
|
apiKeyID, modelName, backendModel, requestTokenCount,
|
||||||
string(requestBody), h.DB)
|
string(requestBody), h.DB)
|
||||||
|
log.Printf("流送请求处理完成, 模型: %s, 流送处理时间: %v", backendModel.Name, time.Since(streamTimestamp))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,9 +177,6 @@ func (h *APIHandler) ChatCompletions(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Backend Response Status: %s", resp.Status)
|
|
||||||
log.Printf("Backend Response Body: %s", string(responseBody))
|
|
||||||
|
|
||||||
// 计算响应token数
|
// 计算响应token数
|
||||||
responseTokenCount := extractResponseTokenCount(responseBody)
|
responseTokenCount := extractResponseTokenCount(responseBody)
|
||||||
|
|
||||||
@@ -195,6 +189,9 @@ func (h *APIHandler) ChatCompletions(c *gin.Context) {
|
|||||||
requestTimestamp, responseTimestamp, requestTokenCount, responseTokenCount,
|
requestTimestamp, responseTimestamp, requestTokenCount, responseTokenCount,
|
||||||
cost, string(requestBody), string(responseBody))
|
cost, string(requestBody), string(responseBody))
|
||||||
logger.LogRequest(h.DB, logEntry)
|
logger.LogRequest(h.DB, logEntry)
|
||||||
|
|
||||||
|
// 记录处理时间
|
||||||
|
log.Printf("请求处理完成, 模型: %s, 总时间: %v", backendModel.Name, time.Since(requestTimestamp))
|
||||||
|
|
||||||
// 复制响应头并返回响应
|
// 复制响应头并返回响应
|
||||||
copyResponseHeaders(c, resp)
|
copyResponseHeaders(c, resp)
|
||||||
@@ -225,25 +222,6 @@ func (h *APIHandler) ResponsesCompletions(c *gin.Context) {
|
|||||||
// 从 map 中提取需要的字段
|
// 从 map 中提取需要的字段
|
||||||
modelName, _ := jsonBody["model"].(string)
|
modelName, _ := jsonBody["model"].(string)
|
||||||
|
|
||||||
// 处理 input 中的 reasoning 字段,将对象转换为字符串
|
|
||||||
if inputRaw, ok := jsonBody["input"].([]interface{}); ok {
|
|
||||||
for _, msgRaw := range inputRaw {
|
|
||||||
if msgMap, ok := msgRaw.(map[string]interface{}); ok {
|
|
||||||
if reasoning, exists := msgMap["reasoning"]; exists {
|
|
||||||
// 如果 reasoning 是对象,提取 content 字段作为字符串
|
|
||||||
if reasoningMap, ok := reasoning.(map[string]interface{}); ok {
|
|
||||||
if reasoningContent, ok := reasoningMap["content"].(string); ok {
|
|
||||||
msgMap["reasoning"] = reasoningContent
|
|
||||||
} else {
|
|
||||||
// 如果无法提取 content,移除 reasoning 字段
|
|
||||||
delete(msgMap, "reasoning")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取 input 并转换为计算 token 所需的格式
|
// 提取 input 并转换为计算 token 所需的格式
|
||||||
var messages []billing.ChatCompletionMessage
|
var messages []billing.ChatCompletionMessage
|
||||||
if inputRaw, ok := jsonBody["input"].([]interface{}); ok {
|
if inputRaw, ok := jsonBody["input"].([]interface{}); ok {
|
||||||
@@ -290,6 +268,8 @@ func (h *APIHandler) ResponsesCompletions(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("接收到请求, 开始使用: %s", backendModel.Name)
|
||||||
|
|
||||||
// 构建后端API URL
|
// 构建后端API URL
|
||||||
backendURL := backendModel.Provider.BaseURL + "/v1/responses"
|
backendURL := backendModel.Provider.BaseURL + "/v1/responses"
|
||||||
|
|
||||||
@@ -308,6 +288,7 @@ func (h *APIHandler) ResponsesCompletions(c *gin.Context) {
|
|||||||
|
|
||||||
// 记录响应时间
|
// 记录响应时间
|
||||||
responseTimestamp := time.Now()
|
responseTimestamp := time.Now()
|
||||||
|
log.Printf("后端模型已经回应: %s, 后端响应时间: %v", backendModel.Name, responseTimestamp.Sub(requestTimestamp))
|
||||||
|
|
||||||
// 读取响应体
|
// 读取响应体
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
@@ -336,6 +317,10 @@ func (h *APIHandler) ResponsesCompletions(c *gin.Context) {
|
|||||||
requestTimestamp, responseTimestamp, requestTokenCount, responseTokenCount,
|
requestTimestamp, responseTimestamp, requestTokenCount, responseTokenCount,
|
||||||
cost, string(requestBody), string(responseBody))
|
cost, string(requestBody), string(responseBody))
|
||||||
logger.LogRequest(h.DB, logEntry)
|
logger.LogRequest(h.DB, logEntry)
|
||||||
|
|
||||||
|
// 记录处理时间
|
||||||
|
processingTime := responseTimestamp.Sub(requestTimestamp)
|
||||||
|
log.Printf("请求处理完成, 模型: %s, 处理时间: %v", backendModel.Name, processingTime)
|
||||||
|
|
||||||
// 复制响应头并返回响应
|
// 复制响应头并返回响应
|
||||||
copyResponseHeaders(c, resp)
|
copyResponseHeaders(c, resp)
|
||||||
@@ -419,6 +404,8 @@ func (h *APIHandler) Embeddings(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("接收到请求, 开始使用: %s", backendModel.Name)
|
||||||
|
|
||||||
// 构建后端API URL
|
// 构建后端API URL
|
||||||
backendURL := backendModel.Provider.BaseURL + "/v1/embeddings"
|
backendURL := backendModel.Provider.BaseURL + "/v1/embeddings"
|
||||||
|
|
||||||
@@ -437,6 +424,7 @@ func (h *APIHandler) Embeddings(c *gin.Context) {
|
|||||||
|
|
||||||
// 记录响应时间
|
// 记录响应时间
|
||||||
responseTimestamp := time.Now()
|
responseTimestamp := time.Now()
|
||||||
|
log.Printf("后端模型已经回应: %s, 后端响应时间: %v", backendModel.Name, responseTimestamp.Sub(requestTimestamp))
|
||||||
|
|
||||||
// 读取响应体
|
// 读取响应体
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
@@ -450,9 +438,6 @@ func (h *APIHandler) Embeddings(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Backend Response Status: %s", resp.Status)
|
|
||||||
log.Printf("Backend Response Body: %s", string(responseBody))
|
|
||||||
|
|
||||||
// 从响应中提取 token 使用量
|
// 从响应中提取 token 使用量
|
||||||
responseTokenCount := extractEmbeddingsTokenCount(responseBody, requestTokenCount)
|
responseTokenCount := extractEmbeddingsTokenCount(responseBody, requestTokenCount)
|
||||||
|
|
||||||
@@ -468,6 +453,10 @@ func (h *APIHandler) Embeddings(c *gin.Context) {
|
|||||||
requestTimestamp, responseTimestamp, requestTokenCount, responseTokenCount,
|
requestTimestamp, responseTimestamp, requestTokenCount, responseTokenCount,
|
||||||
cost, string(requestBody), string(responseBody))
|
cost, string(requestBody), string(responseBody))
|
||||||
logger.LogRequest(h.DB, logEntry)
|
logger.LogRequest(h.DB, logEntry)
|
||||||
|
|
||||||
|
// 记录处理时间
|
||||||
|
processingTime := responseTimestamp.Sub(requestTimestamp)
|
||||||
|
log.Printf("请求处理完成, 模型: %s, 处理时间: %v", backendModel.Name, processingTime)
|
||||||
|
|
||||||
// 复制响应头并返回响应
|
// 复制响应头并返回响应
|
||||||
copyResponseHeaders(c, resp)
|
copyResponseHeaders(c, resp)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ type AppConfig struct {
|
|||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Environment string `json:"environment"`
|
Environment string `json:"environment"`
|
||||||
LogLevel string `json:"log_level"`
|
LogLevel string `json:"log_level"`
|
||||||
|
LogInDB bool `json:"log_in_db"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig 默认配置
|
// DefaultConfig 默认配置
|
||||||
@@ -51,10 +52,10 @@ func DefaultConfig() *Config {
|
|||||||
Host: "0.0.0.0",
|
Host: "0.0.0.0",
|
||||||
},
|
},
|
||||||
LogCleaner: scheduler.LogCleanerConfig{
|
LogCleaner: scheduler.LogCleanerConfig{
|
||||||
Enabled: true, // 默认启用自动清理
|
Enabled: true, // 默认启用自动清理
|
||||||
ExecuteTime: "02:00", // 凌晨2点执行
|
ExecuteTime: "02:00", // 凌晨2点执行
|
||||||
RetentionDays: 7, // 保留7天的日志
|
RetentionDays: 7, // 保留7天的日志
|
||||||
CheckInterval: 5, // 每5分钟检查一次
|
CheckInterval: 5, // 每5分钟检查一次
|
||||||
},
|
},
|
||||||
App: AppConfig{
|
App: AppConfig{
|
||||||
Name: "AI Gateway",
|
Name: "AI Gateway",
|
||||||
@@ -165,4 +166,4 @@ func (c *Config) Print() {
|
|||||||
log.Printf(" 📱 应用名称: %s v%s", c.App.Name, c.App.Version)
|
log.Printf(" 📱 应用名称: %s v%s", c.App.Name, c.App.Version)
|
||||||
log.Printf(" 🌍 运行环境: %s", c.App.Environment)
|
log.Printf(" 🌍 运行环境: %s", c.App.Environment)
|
||||||
log.Printf(" 📝 日志级别: %s", c.App.LogLevel)
|
log.Printf(" 📝 日志级别: %s", c.App.LogLevel)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,21 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var saveRequestLog = false
|
||||||
|
|
||||||
// LogRequest 异步记录API请求日志
|
// LogRequest 异步记录API请求日志
|
||||||
func LogRequest(db *gorm.DB, logEntry *models.RequestLog) {
|
func LogRequest(db *gorm.DB, logEntry *models.RequestLog) {
|
||||||
|
if !saveRequestLog {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := db.Create(logEntry).Error; err != nil {
|
if err := db.Create(logEntry).Error; err != nil {
|
||||||
log.Printf("Failed to save request log: %v", err)
|
log.Printf("Failed to save request log: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetSaveRequestLog(save bool) {
|
||||||
|
saveRequestLog = save
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"ai-gateway/api"
|
"ai-gateway/api"
|
||||||
"ai-gateway/internal/config"
|
"ai-gateway/internal/config"
|
||||||
"ai-gateway/internal/db"
|
"ai-gateway/internal/db"
|
||||||
|
"ai-gateway/internal/logger"
|
||||||
"ai-gateway/internal/middleware"
|
"ai-gateway/internal/middleware"
|
||||||
"ai-gateway/internal/scheduler"
|
"ai-gateway/internal/scheduler"
|
||||||
"log"
|
"log"
|
||||||
@@ -28,6 +29,8 @@ func main() {
|
|||||||
cfg = config.DefaultConfig()
|
cfg = config.DefaultConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.SetSaveRequestLog(cfg.App.LogInDB)
|
||||||
|
|
||||||
// 初始化数据库连接(使用固定数据库路径)
|
// 初始化数据库连接(使用固定数据库路径)
|
||||||
database, err := db.InitDB(config.DatabasePath)
|
database, err := db.InitDB(config.DatabasePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -41,9 +41,12 @@ const VirtualModelForm = ({ model, onSave, onCancel }) => {
|
|||||||
}, [model]);
|
}, [model]);
|
||||||
|
|
||||||
const handleBackendModelChange = (index, field, value) => {
|
const handleBackendModelChange = (index, field, value) => {
|
||||||
const newBackendModels = [...backendModels];
|
// 使用函数式setState来确保获取最新的状态
|
||||||
newBackendModels[index] = { ...newBackendModels[index], [field]: value };
|
setBackendModels(prevModels => {
|
||||||
setBackendModels(newBackendModels);
|
const newBackendModels = [...prevModels];
|
||||||
|
newBackendModels[index] = { ...newBackendModels[index], [field]: value };
|
||||||
|
return newBackendModels;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addBackendModel = () => {
|
const addBackendModel = () => {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default defineConfig({
|
|||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:8080', // 后端服务器地址
|
target: 'http://localhost:8080', // 后端服务器地址
|
||||||
// target: 'http://10.1.39.104:9130',
|
// target: 'http://82.156.222.20:6233',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user