Bladeren bron

refactor: improve channel base URL handling and enhance RelayInfo logging

CaIon 6 maanden geleden
bovenliggende
commit
7f1f368065
5 gewijzigde bestanden met toevoegingen van 92 en 10 verwijderingen
  1. 7 5
      controller/channel-test.go
  2. 3 3
      controller/relay.go
  3. 5 1
      model/channel.go
  4. 76 0
      relay/common/relay_info.go
  5. 1 1
      service/pre_consume_quota.go

+ 7 - 5
controller/channel-test.go

@@ -144,7 +144,9 @@ func testChannel(channel *model.Channel, testModel string) testResult {
 		}
 	}
 
-	err = helper.ModelMappedHelper(c, info, nil)
+	info.InitChannelMeta(c)
+
+	err = helper.ModelMappedHelper(c, info, request)
 	if err != nil {
 		return testResult{
 			context:     c,
@@ -166,10 +168,10 @@ func testChannel(channel *model.Channel, testModel string) testResult {
 		}
 	}
 
-	// 创建一个用于日志的 info 副本,移除 ApiKey
-	logInfo := *info
-	logInfo.ApiKey = ""
-	common.SysLog(fmt.Sprintf("testing channel %d with model %s , info %+v ", channel.Id, testModel, logInfo))
+	//// 创建一个用于日志的 info 副本,移除 ApiKey
+	//logInfo := info
+	//logInfo.ApiKey = ""
+	common.SysLog(fmt.Sprintf("testing channel %d with model %s , info %+v ", channel.Id, testModel, info.ToString()))
 
 	priceData, err := helper.ModelPriceHelper(c, info, 0, request.GetTokenCountMeta())
 	if err != nil {

+ 3 - 3
controller/relay.go

@@ -133,13 +133,13 @@ func Relay(c *gin.Context, relayFormat types.RelayFormat) {
 		return
 	}
 
-	preConsumedQuota, newApiErr := service.PreConsumeQuota(c, priceData.ShouldPreConsumedQuota, relayInfo)
-	if newApiErr != nil {
+	preConsumedQuota, newAPIError := service.PreConsumeQuota(c, priceData.ShouldPreConsumedQuota, relayInfo)
+	if newAPIError != nil {
 		return
 	}
 
 	defer func() {
-		if newApiErr != nil {
+		if newAPIError != nil {
 			service.ReturnPreConsumedQuota(c, relayInfo, preConsumedQuota)
 		}
 	}()

+ 5 - 1
model/channel.go

@@ -406,7 +406,11 @@ func (channel *Channel) GetBaseURL() string {
 	if channel.BaseURL == nil {
 		return ""
 	}
-	return *channel.BaseURL
+	url := *channel.BaseURL
+	if url == "" {
+		url = constant.ChannelBaseURLs[channel.Type]
+	}
+	return url
 }
 
 func (channel *Channel) GetModelMapping() string {

+ 76 - 0
relay/common/relay_info.go

@@ -2,6 +2,7 @@ package common
 
 import (
 	"errors"
+	"fmt"
 	"one-api/common"
 	"one-api/constant"
 	"one-api/dto"
@@ -155,6 +156,81 @@ func (info *RelayInfo) InitChannelMeta(c *gin.Context) {
 	info.ChannelMeta = channelMeta
 }
 
+func (info *RelayInfo) ToString() string {
+	if info == nil {
+		return "RelayInfo<nil>"
+	}
+
+	// Basic info
+	b := &strings.Builder{}
+	fmt.Fprintf(b, "RelayInfo{ ")
+	fmt.Fprintf(b, "RelayFormat: %s, ", info.RelayFormat)
+	fmt.Fprintf(b, "RelayMode: %d, ", info.RelayMode)
+	fmt.Fprintf(b, "IsStream: %t, ", info.IsStream)
+	fmt.Fprintf(b, "IsPlayground: %t, ", info.IsPlayground)
+	fmt.Fprintf(b, "RequestURLPath: %q, ", info.RequestURLPath)
+	fmt.Fprintf(b, "OriginModelName: %q, ", info.OriginModelName)
+	fmt.Fprintf(b, "PromptTokens: %d, ", info.PromptTokens)
+	fmt.Fprintf(b, "ShouldIncludeUsage: %t, ", info.ShouldIncludeUsage)
+	fmt.Fprintf(b, "DisablePing: %t, ", info.DisablePing)
+	fmt.Fprintf(b, "SendResponseCount: %d, ", info.SendResponseCount)
+	fmt.Fprintf(b, "FinalPreConsumedQuota: %d, ", info.FinalPreConsumedQuota)
+
+	// User & token info (mask secrets)
+	fmt.Fprintf(b, "User{ Id: %d, Email: %q, Group: %q, UsingGroup: %q, Quota: %d }, ",
+		info.UserId, info.UserEmail, info.UserGroup, info.UsingGroup, info.UserQuota)
+	fmt.Fprintf(b, "Token{ Id: %d, Unlimited: %t, Key: ***masked*** }, ", info.TokenId, info.TokenUnlimited)
+
+	// Time info
+	latencyMs := info.FirstResponseTime.Sub(info.StartTime).Milliseconds()
+	fmt.Fprintf(b, "Timing{ Start: %s, FirstResponse: %s, LatencyMs: %d }, ",
+		info.StartTime.Format(time.RFC3339Nano), info.FirstResponseTime.Format(time.RFC3339Nano), latencyMs)
+
+	// Audio / realtime
+	if info.InputAudioFormat != "" || info.OutputAudioFormat != "" || len(info.RealtimeTools) > 0 || info.AudioUsage {
+		fmt.Fprintf(b, "Realtime{ AudioUsage: %t, InFmt: %q, OutFmt: %q, Tools: %d }, ",
+			info.AudioUsage, info.InputAudioFormat, info.OutputAudioFormat, len(info.RealtimeTools))
+	}
+
+	// Reasoning
+	if info.ReasoningEffort != "" {
+		fmt.Fprintf(b, "ReasoningEffort: %q, ", info.ReasoningEffort)
+	}
+
+	// Price data (non-sensitive)
+	if info.PriceData.UsePrice {
+		fmt.Fprintf(b, "PriceData{ %s }, ", info.PriceData.ToSetting())
+	}
+
+	// Channel metadata (mask ApiKey)
+	if info.ChannelMeta != nil {
+		cm := info.ChannelMeta
+		fmt.Fprintf(b, "ChannelMeta{ Type: %d, Id: %d, IsMultiKey: %t, MultiKeyIndex: %d, BaseURL: %q, ApiType: %d, ApiVersion: %q, Organization: %q, CreateTime: %d, UpstreamModelName: %q, IsModelMapped: %t, SupportStreamOptions: %t, ApiKey: ***masked*** }, ",
+			cm.ChannelType, cm.ChannelId, cm.ChannelIsMultiKey, cm.ChannelMultiKeyIndex, cm.ChannelBaseUrl, cm.ApiType, cm.ApiVersion, cm.Organization, cm.ChannelCreateTime, cm.UpstreamModelName, cm.IsModelMapped, cm.SupportStreamOptions)
+	}
+
+	// Responses usage info (non-sensitive)
+	if info.ResponsesUsageInfo != nil && len(info.ResponsesUsageInfo.BuiltInTools) > 0 {
+		fmt.Fprintf(b, "ResponsesTools{ ")
+		first := true
+		for name, tool := range info.ResponsesUsageInfo.BuiltInTools {
+			if !first {
+				fmt.Fprintf(b, ", ")
+			}
+			first = false
+			if tool != nil {
+				fmt.Fprintf(b, "%s: calls=%d", name, tool.CallCount)
+			} else {
+				fmt.Fprintf(b, "%s: calls=0", name)
+			}
+		}
+		fmt.Fprintf(b, " }, ")
+	}
+
+	fmt.Fprintf(b, "}")
+	return b.String()
+}
+
 // 定义支持流式选项的通道类型
 var streamSupportedChannels = map[int]bool{
 	constant.ChannelTypeOpenAI:     true,

+ 1 - 1
service/pre_consume_quota.go

@@ -37,7 +37,7 @@ func PreConsumeQuota(c *gin.Context, preConsumedQuota int, relayInfo *relaycommo
 		return 0, types.NewErrorWithStatusCode(errors.New("user quota is not enough"), types.ErrorCodeInsufficientUserQuota, http.StatusForbidden, types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
 	}
 	if userQuota-preConsumedQuota < 0 {
-		return 0, types.NewErrorWithStatusCode(fmt.Errorf("pre-consume quota failed, user quota: %s, need quota: %s", logger.FormatQuota(userQuota), logger.FormatQuota(preConsumedQuota)), types.ErrorCodeInsufficientUserQuota, http.StatusForbidden, types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
+		return 0, types.NewErrorWithStatusCode(fmt.Errorf("预扣费额度失败, 用户剩余额度: %s, 需要预扣费额度: %s", logger.FormatQuota(userQuota), logger.FormatQuota(preConsumedQuota)), types.ErrorCodeInsufficientUserQuota, http.StatusForbidden, types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
 	}
 
 	trustQuota := common.GetTrustQuota()