Răsfoiți Sursa

feat: usage int64 file replace to empty null typed (#166)

zijiren 8 luni în urmă
părinte
comite
c33e452932

+ 6 - 6
core/common/consume/consume.go

@@ -139,27 +139,27 @@ func CalculateAmount(
 		inputTokens -= usage.CacheCreationTokens
 	}
 
-	inputAmount := decimal.NewFromInt(inputTokens).
+	inputAmount := decimal.NewFromInt(int64(inputTokens)).
 		Mul(decimal.NewFromFloat(modelPrice.InputPrice)).
 		Div(decimal.NewFromInt(modelPrice.GetInputPriceUnit()))
 
-	imageInputAmount := decimal.NewFromInt(usage.ImageInputTokens).
+	imageInputAmount := decimal.NewFromInt(int64(usage.ImageInputTokens)).
 		Mul(decimal.NewFromFloat(modelPrice.ImageInputPrice)).
 		Div(decimal.NewFromInt(modelPrice.GetImageInputPriceUnit()))
 
-	cachedAmount := decimal.NewFromInt(usage.CachedTokens).
+	cachedAmount := decimal.NewFromInt(int64(usage.CachedTokens)).
 		Mul(decimal.NewFromFloat(modelPrice.CachedPrice)).
 		Div(decimal.NewFromInt(modelPrice.GetCachedPriceUnit()))
 
-	cacheCreationAmount := decimal.NewFromInt(usage.CacheCreationTokens).
+	cacheCreationAmount := decimal.NewFromInt(int64(usage.CacheCreationTokens)).
 		Mul(decimal.NewFromFloat(modelPrice.CacheCreationPrice)).
 		Div(decimal.NewFromInt(modelPrice.GetCacheCreationPriceUnit()))
 
-	webSearchAmount := decimal.NewFromInt(usage.WebSearchCount).
+	webSearchAmount := decimal.NewFromInt(int64(usage.WebSearchCount)).
 		Mul(decimal.NewFromFloat(modelPrice.WebSearchPrice)).
 		Div(decimal.NewFromInt(modelPrice.GetWebSearchPriceUnit()))
 
-	outputAmount := decimal.NewFromInt(outputTokens).
+	outputAmount := decimal.NewFromInt(int64(outputTokens)).
 		Mul(decimal.NewFromFloat(modelPrice.OutputPrice)).
 		Div(decimal.NewFromInt(modelPrice.GetOutputPriceUnit()))
 

+ 7 - 7
core/model/log.go

@@ -103,13 +103,13 @@ func (p *Price) GetWebSearchPriceUnit() int64 {
 }
 
 type Usage struct {
-	InputTokens         int64 `json:"input_tokens,omitempty"`
-	ImageInputTokens    int64 `json:"image_input_tokens,omitempty"`
-	OutputTokens        int64 `json:"output_tokens,omitempty"`
-	CachedTokens        int64 `json:"cached_tokens,omitempty"`
-	CacheCreationTokens int64 `json:"cache_creation_tokens,omitempty"`
-	TotalTokens         int64 `json:"total_tokens,omitempty"`
-	WebSearchCount      int64 `json:"web_search_count,omitempty"`
+	InputTokens         ZeroNullInt64 `json:"input_tokens,omitempty"`
+	ImageInputTokens    ZeroNullInt64 `json:"image_input_tokens,omitempty"`
+	OutputTokens        ZeroNullInt64 `json:"output_tokens,omitempty"`
+	CachedTokens        ZeroNullInt64 `json:"cached_tokens,omitempty"`
+	CacheCreationTokens ZeroNullInt64 `json:"cache_creation_tokens,omitempty"`
+	TotalTokens         ZeroNullInt64 `json:"total_tokens,omitempty"`
+	WebSearchCount      ZeroNullInt64 `json:"web_search_count,omitempty"`
 }
 
 func (u *Usage) Add(other *Usage) {

+ 11 - 10
core/model/summary.go

@@ -3,6 +3,7 @@ package model
 import (
 	"cmp"
 	"errors"
+	"fmt"
 	"slices"
 	"time"
 
@@ -33,34 +34,34 @@ type SummaryData struct {
 func (d *SummaryData) buildUpdateData(tableName string) map[string]any {
 	data := map[string]any{}
 	if d.RequestCount > 0 {
-		data["request_count"] = gorm.Expr(tableName+".request_count + ?", d.RequestCount)
+		data["request_count"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.request_count, 0) + ?", tableName), d.RequestCount)
 	}
 	if d.UsedAmount > 0 {
-		data["used_amount"] = gorm.Expr(tableName+".used_amount + ?", d.UsedAmount)
+		data["used_amount"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.used_amount, 0) + ?", tableName), d.UsedAmount)
 	}
 	if d.ExceptionCount > 0 {
-		data["exception_count"] = gorm.Expr(tableName+".exception_count + ?", d.ExceptionCount)
+		data["exception_count"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.exception_count, 0) + ?", tableName), d.ExceptionCount)
 	}
 	if d.Usage.InputTokens > 0 {
-		data["input_tokens"] = gorm.Expr(tableName+".input_tokens + ?", d.Usage.InputTokens)
+		data["input_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.input_tokens, 0) + ?", tableName), d.Usage.InputTokens)
 	}
 	if d.Usage.ImageInputTokens > 0 {
-		data["image_input_tokens"] = gorm.Expr(tableName+".image_input_tokens + ?", d.Usage.ImageInputTokens)
+		data["image_input_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.image_input_tokens, 0) + ?", tableName), d.Usage.ImageInputTokens)
 	}
 	if d.Usage.OutputTokens > 0 {
-		data["output_tokens"] = gorm.Expr(tableName+".output_tokens + ?", d.Usage.OutputTokens)
+		data["output_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.output_tokens, 0) + ?", tableName), d.Usage.OutputTokens)
 	}
 	if d.Usage.TotalTokens > 0 {
-		data["total_tokens"] = gorm.Expr(tableName+".total_tokens + ?", d.Usage.TotalTokens)
+		data["total_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.total_tokens, 0) + ?", tableName), d.Usage.TotalTokens)
 	}
 	if d.Usage.CachedTokens > 0 {
-		data["cached_tokens"] = gorm.Expr(tableName+".cached_tokens + ?", d.Usage.CachedTokens)
+		data["cached_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.cached_tokens, 0) + ?", tableName), d.Usage.CachedTokens)
 	}
 	if d.Usage.CacheCreationTokens > 0 {
-		data["cache_creation_tokens"] = gorm.Expr(tableName+".cache_creation_tokens + ?", d.Usage.CacheCreationTokens)
+		data["cache_creation_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.cache_creation_tokens, 0) + ?", tableName), d.Usage.CacheCreationTokens)
 	}
 	if d.Usage.WebSearchCount > 0 {
-		data["web_search_count"] = gorm.Expr(tableName+".web_search_count + ?", d.Usage.WebSearchCount)
+		data["web_search_count"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.web_search_count, 0) + ?", tableName), d.Usage.WebSearchCount)
 	}
 	return data
 }

+ 2 - 2
core/relay/adaptor/ali/image.go

@@ -99,8 +99,8 @@ func ImageHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.
 	c.Writer.WriteHeader(resp.StatusCode)
 	_, _ = c.Writer.Write(jsonResponse)
 	return &model.Usage{
-		OutputTokens: int64(len(jsonResponse)),
-		TotalTokens:  int64(len(jsonResponse)),
+		OutputTokens: model.ZeroNullInt64(len(jsonResponse)),
+		TotalTokens:  model.ZeroNullInt64(len(jsonResponse)),
 	}, nil
 }
 

+ 2 - 2
core/relay/adaptor/ali/rerank.go

@@ -96,8 +96,8 @@ func RerankHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 		}
 	} else {
 		usage = &model.Usage{
-			InputTokens: rerankResponse.Usage.TotalTokens,
-			TotalTokens: rerankResponse.Usage.TotalTokens,
+			InputTokens: model.ZeroNullInt64(rerankResponse.Usage.TotalTokens),
+			TotalTokens: model.ZeroNullInt64(rerankResponse.Usage.TotalTokens),
 		}
 	}
 

+ 4 - 4
core/relay/adaptor/ali/stt-realtime.go

@@ -211,13 +211,13 @@ func STTDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 			}
 			continue
 		case "task-finished":
-			usage.InputTokens = msg.Payload.Usage.Characters
-			usage.TotalTokens = msg.Payload.Usage.Characters
+			usage.InputTokens = model.ZeroNullInt64(msg.Payload.Usage.Characters)
+			usage.TotalTokens = model.ZeroNullInt64(msg.Payload.Usage.Characters)
 			c.JSON(http.StatusOK, gin.H{
 				"text": output.String(),
 				"usage": relaymodel.Usage{
-					PromptTokens: usage.InputTokens,
-					TotalTokens:  usage.TotalTokens,
+					PromptTokens: int64(usage.InputTokens),
+					TotalTokens:  int64(usage.TotalTokens),
 				},
 			})
 			return usage, nil

+ 2 - 2
core/relay/adaptor/ali/tts.go

@@ -218,8 +218,8 @@ func TTSDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 			case "result-generated":
 				continue
 			case "task-finished":
-				usage.InputTokens = msg.Payload.Usage.Characters
-				usage.TotalTokens = msg.Payload.Usage.Characters
+				usage.InputTokens = model.ZeroNullInt64(msg.Payload.Usage.Characters)
+				usage.TotalTokens = model.ZeroNullInt64(msg.Payload.Usage.Characters)
 				return usage, nil
 			case "task-failed":
 				return usage, openai.ErrorWrapperWithMessage(msg.Header.ErrorMessage, msg.Header.ErrorCode, http.StatusInternalServerError)

+ 4 - 4
core/relay/adaptor/anthropic/main.go

@@ -193,8 +193,8 @@ func StreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*model.Us
 				}
 				usage.Add(response.Usage)
 				if usage.PromptTokens == 0 {
-					usage.PromptTokens = m.RequestUsage.InputTokens
-					usage.TotalTokens += m.RequestUsage.InputTokens
+					usage.PromptTokens = int64(m.RequestUsage.InputTokens)
+					usage.TotalTokens += int64(m.RequestUsage.InputTokens)
 				}
 				response.Usage = usage
 				responseText.Reset()
@@ -217,9 +217,9 @@ func StreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*model.Us
 
 	if usage == nil {
 		usage = &relaymodel.Usage{
-			PromptTokens:     m.RequestUsage.InputTokens,
+			PromptTokens:     int64(m.RequestUsage.InputTokens),
 			CompletionTokens: openai.CountTokenText(responseText.String(), m.OriginModel),
-			TotalTokens:      m.RequestUsage.InputTokens + openai.CountTokenText(responseText.String(), m.OriginModel),
+			TotalTokens:      int64(m.RequestUsage.InputTokens) + openai.CountTokenText(responseText.String(), m.OriginModel),
 		}
 	}
 

+ 5 - 5
core/relay/adaptor/anthropic/openai.go

@@ -415,7 +415,7 @@ func Response2OpenAI(meta *meta.Meta, claudeResponse *Response) *relaymodel.Text
 		},
 	}
 	if fullTextResponse.Usage.PromptTokens == 0 {
-		fullTextResponse.Usage.PromptTokens = meta.RequestUsage.InputTokens
+		fullTextResponse.Usage.PromptTokens = int64(meta.RequestUsage.InputTokens)
 	}
 	fullTextResponse.Usage.TotalTokens = fullTextResponse.Usage.PromptTokens + fullTextResponse.Usage.CompletionTokens
 	return &fullTextResponse
@@ -470,8 +470,8 @@ func OpenAIStreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*mo
 			}
 			usage.Add(response.Usage)
 			if usage.PromptTokens == 0 {
-				usage.PromptTokens = m.RequestUsage.InputTokens
-				usage.TotalTokens += m.RequestUsage.InputTokens
+				usage.PromptTokens = int64(m.RequestUsage.InputTokens)
+				usage.TotalTokens += int64(m.RequestUsage.InputTokens)
 			}
 			response.Usage = usage
 			responseText.Reset()
@@ -493,9 +493,9 @@ func OpenAIStreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*mo
 
 	if usage == nil {
 		usage = &relaymodel.Usage{
-			PromptTokens:     m.RequestUsage.InputTokens,
+			PromptTokens:     int64(m.RequestUsage.InputTokens),
 			CompletionTokens: openai.CountTokenText(responseText.String(), m.OriginModel),
-			TotalTokens:      m.RequestUsage.InputTokens + openai.CountTokenText(responseText.String(), m.OriginModel),
+			TotalTokens:      int64(m.RequestUsage.InputTokens) + openai.CountTokenText(responseText.String(), m.OriginModel),
 		}
 		_ = render.ObjectData(c, &relaymodel.ChatCompletionsStreamResponse{
 			ID:      openai.ChatCompletionID(),

+ 4 - 7
core/relay/adaptor/aws/claude/main.go

@@ -204,9 +204,6 @@ func StreamHandler(m *meta.Meta, c *gin.Context) (*model.Usage, *relaymodel.Erro
 				}
 				return usage.ToModelUsage(), err
 			}
-			if response == nil {
-				continue
-			}
 			if response != nil {
 				switch {
 				case response.Usage != nil:
@@ -215,8 +212,8 @@ func StreamHandler(m *meta.Meta, c *gin.Context) (*model.Usage, *relaymodel.Erro
 					}
 					usage.Add(response.Usage)
 					if usage.PromptTokens == 0 {
-						usage.PromptTokens = m.RequestUsage.InputTokens
-						usage.TotalTokens += m.RequestUsage.InputTokens
+						usage.PromptTokens = int64(m.RequestUsage.InputTokens)
+						usage.TotalTokens += int64(m.RequestUsage.InputTokens)
 					}
 					response.Usage = usage
 					responseText.Reset()
@@ -242,9 +239,9 @@ func StreamHandler(m *meta.Meta, c *gin.Context) (*model.Usage, *relaymodel.Erro
 
 	if usage == nil {
 		usage = &relaymodel.Usage{
-			PromptTokens:     m.RequestUsage.InputTokens,
+			PromptTokens:     int64(m.RequestUsage.InputTokens),
 			CompletionTokens: openai.CountTokenText(responseText.String(), m.OriginModel),
-			TotalTokens:      m.RequestUsage.InputTokens + openai.CountTokenText(responseText.String(), m.OriginModel),
+			TotalTokens:      int64(m.RequestUsage.InputTokens) + openai.CountTokenText(responseText.String(), m.OriginModel),
 		}
 		_ = render.ObjectData(c, &relaymodel.ChatCompletionsStreamResponse{
 			ID:      openai.ChatCompletionID(),

+ 2 - 2
core/relay/adaptor/baidu/image.go

@@ -40,8 +40,8 @@ func ImageHandler(_ *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usa
 	}
 
 	usage := &model.Usage{
-		InputTokens: int64(len(imageResponse.Data)),
-		TotalTokens: int64(len(imageResponse.Data)),
+		InputTokens: model.ZeroNullInt64(len(imageResponse.Data)),
+		TotalTokens: model.ZeroNullInt64(len(imageResponse.Data)),
 	}
 
 	if imageResponse.Error != nil && imageResponse.Error.ErrorMsg != "" {

+ 2 - 2
core/relay/adaptor/coze/main.go

@@ -143,7 +143,7 @@ func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 
 	render.Done(c)
 
-	return openai.ResponseText2Usage(responseText.String(), meta.ActualModel, meta.RequestUsage.InputTokens).ToModelUsage(), nil
+	return openai.ResponseText2Usage(responseText.String(), meta.ActualModel, int64(meta.RequestUsage.InputTokens)).ToModelUsage(), nil
 }
 
 func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, *relaymodel.ErrorWithStatusCode) {
@@ -178,5 +178,5 @@ func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage
 	if len(fullTextResponse.Choices) > 0 {
 		responseText = fullTextResponse.Choices[0].Message.StringContent()
 	}
-	return openai.ResponseText2Usage(responseText, meta.ActualModel, meta.RequestUsage.InputTokens).ToModelUsage(), nil
+	return openai.ResponseText2Usage(responseText, meta.ActualModel, int64(meta.RequestUsage.InputTokens)).ToModelUsage(), nil
 }

+ 2 - 2
core/relay/adaptor/doc2x/pdf.go

@@ -349,8 +349,8 @@ func handleParsePdfResponse(meta *meta.Meta, c *gin.Context, response *StatusRes
 	}
 
 	return &model.Usage{
-		InputTokens: pages,
-		TotalTokens: pages,
+		InputTokens: model.ZeroNullInt64(pages),
+		TotalTokens: model.ZeroNullInt64(pages),
 	}, nil
 }
 

+ 1 - 1
core/relay/adaptor/doubao/main.go

@@ -140,7 +140,7 @@ func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Respons
 			usage, err = openai.Handler(meta, c, resp, newHandlerPreHandler(&websearchCount))
 		}
 		if usage != nil {
-			usage.WebSearchCount += websearchCount
+			usage.WebSearchCount += model.ZeroNullInt64(websearchCount)
 		}
 	default:
 		return openai.DoResponse(meta, c, resp)

+ 1 - 1
core/relay/adaptor/gemini/main.go

@@ -604,7 +604,7 @@ func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 	}
 
 	usage := relaymodel.Usage{
-		PromptTokens: meta.RequestUsage.InputTokens,
+		PromptTokens: int64(meta.RequestUsage.InputTokens),
 	}
 
 	for scanner.Scan() {

+ 2 - 2
core/relay/adaptor/jina/rerank.go

@@ -44,8 +44,8 @@ func RerankHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 	if usage.PromptTokens == 0 && usage.TotalTokens != 0 {
 		usage.PromptTokens = usage.TotalTokens
 	} else if usage.PromptTokens == 0 {
-		usage.PromptTokens = meta.RequestUsage.InputTokens
-		usage.TotalTokens = meta.RequestUsage.InputTokens
+		usage.PromptTokens = int64(meta.RequestUsage.InputTokens)
+		usage.TotalTokens = int64(meta.RequestUsage.InputTokens)
 	}
 	modelUsage := usage.ToModelUsage()
 	_, err = node.SetAny("meta", map[string]any{

+ 2 - 2
core/relay/adaptor/minimax/tts.go

@@ -146,7 +146,7 @@ func TTSHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Us
 
 	usageCharacters := meta.RequestUsage.InputTokens
 	if result.ExtraInfo.UsageCharacters > 0 {
-		usageCharacters = result.ExtraInfo.UsageCharacters
+		usageCharacters = model.ZeroNullInt64(result.ExtraInfo.UsageCharacters)
 	}
 
 	return &model.Usage{
@@ -185,7 +185,7 @@ func ttsStreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*mo
 			continue
 		}
 		if result.ExtraInfo.UsageCharacters > 0 {
-			usageCharacters = result.ExtraInfo.UsageCharacters
+			usageCharacters = model.ZeroNullInt64(result.ExtraInfo.UsageCharacters)
 		}
 
 		audioBytes, err := hex.DecodeString(result.Data.Audio)

+ 11 - 7
core/relay/adaptor/openai/main.go

@@ -186,7 +186,11 @@ func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response, preHand
 	}
 
 	if usage == nil || (usage.TotalTokens == 0 && responseText.Len() > 0) {
-		usage = ResponseText2Usage(responseText.String(), meta.ActualModel, meta.RequestUsage.InputTokens)
+		usage = ResponseText2Usage(
+			responseText.String(),
+			meta.ActualModel,
+			int64(meta.RequestUsage.InputTokens),
+		)
 		_ = render.ObjectData(c, &relaymodel.ChatCompletionsStreamResponse{
 			ID:      ChatCompletionID(),
 			Model:   meta.OriginModel,
@@ -196,8 +200,8 @@ func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response, preHand
 			Usage:   usage,
 		})
 	} else if usage.TotalTokens != 0 && usage.PromptTokens == 0 { // some channels don't return prompt tokens & completion tokens
-		usage.PromptTokens = meta.RequestUsage.InputTokens
-		usage.CompletionTokens = usage.TotalTokens - meta.RequestUsage.InputTokens
+		usage.PromptTokens = int64(meta.RequestUsage.InputTokens)
+		usage.CompletionTokens = usage.TotalTokens - int64(meta.RequestUsage.InputTokens)
 	}
 
 	render.Done(c)
@@ -389,17 +393,17 @@ func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response, preHandler Pr
 			completionTokens += CountTokenText(choice.Message.StringContent(), meta.ActualModel)
 		}
 		usage = &relaymodel.Usage{
-			PromptTokens:     meta.RequestUsage.InputTokens,
+			PromptTokens:     int64(meta.RequestUsage.InputTokens),
 			CompletionTokens: completionTokens,
-			TotalTokens:      meta.RequestUsage.InputTokens + completionTokens,
+			TotalTokens:      int64(meta.RequestUsage.InputTokens) + completionTokens,
 		}
 		_, err = node.Set("usage", ast.NewAny(usage))
 		if err != nil {
 			return usage.ToModelUsage(), ErrorWrapper(err, "set_usage_failed", http.StatusInternalServerError)
 		}
 	} else if usage.TotalTokens != 0 && usage.PromptTokens == 0 { // some channels don't return prompt tokens & completion tokens
-		usage.PromptTokens = meta.RequestUsage.InputTokens
-		usage.CompletionTokens = usage.TotalTokens - meta.RequestUsage.InputTokens
+		usage.PromptTokens = int64(meta.RequestUsage.InputTokens)
+		usage.CompletionTokens = usage.TotalTokens - int64(meta.RequestUsage.InputTokens)
 		_, err = node.Set("usage", ast.NewAny(usage))
 		if err != nil {
 			return usage.ToModelUsage(), ErrorWrapper(err, "set_usage_failed", http.StatusInternalServerError)

+ 4 - 4
core/relay/adaptor/openai/rerank.go

@@ -66,11 +66,11 @@ func RerankHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 		}, nil
 	}
 	if rerankResponse.Meta.Tokens.InputTokens <= 0 {
-		rerankResponse.Meta.Tokens.InputTokens = meta.RequestUsage.InputTokens
+		rerankResponse.Meta.Tokens.InputTokens = int64(meta.RequestUsage.InputTokens)
 	}
 	return &model.Usage{
-		InputTokens:  rerankResponse.Meta.Tokens.InputTokens,
-		OutputTokens: rerankResponse.Meta.Tokens.OutputTokens,
-		TotalTokens:  rerankResponse.Meta.Tokens.InputTokens + rerankResponse.Meta.Tokens.OutputTokens,
+		InputTokens:  model.ZeroNullInt64(rerankResponse.Meta.Tokens.InputTokens),
+		OutputTokens: model.ZeroNullInt64(rerankResponse.Meta.Tokens.OutputTokens),
+		TotalTokens:  model.ZeroNullInt64(rerankResponse.Meta.Tokens.InputTokens + rerankResponse.Meta.Tokens.OutputTokens),
 	}, nil
 }

+ 1 - 1
core/relay/adaptor/openai/stt.go

@@ -113,7 +113,7 @@ func STTHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Us
 	}
 	var promptTokens int64
 	if meta.RequestUsage.InputTokens > 0 {
-		promptTokens = meta.RequestUsage.InputTokens
+		promptTokens = int64(meta.RequestUsage.InputTokens)
 	} else {
 		promptTokens = CountTokenText(text, meta.ActualModel)
 	}

+ 2 - 2
core/relay/adaptor/siliconflow/adaptor.go

@@ -38,8 +38,8 @@ func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Respons
 	case mode.AudioSpeech:
 		size := c.Writer.Size()
 		usage = &model.Usage{
-			OutputTokens: int64(size),
-			TotalTokens:  int64(size),
+			OutputTokens: model.ZeroNullInt64(size),
+			TotalTokens:  model.ZeroNullInt64(size),
 		}
 	}
 	return usage, nil

+ 4 - 1
core/relay/controller/anthropic.go

@@ -18,6 +18,9 @@ func GetAnthropicRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usage
 	}
 
 	return model.Usage{
-		InputTokens: openai.CountTokenMessages(textRequest.Messages, textRequest.Model),
+		InputTokens: model.ZeroNullInt64(openai.CountTokenMessages(
+			textRequest.Messages,
+			textRequest.Model,
+		)),
 	}, nil
 }

+ 4 - 1
core/relay/controller/chat.go

@@ -18,6 +18,9 @@ func GetChatRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usage, err
 	}
 
 	return model.Usage{
-		InputTokens: openai.CountTokenMessages(textRequest.Messages, textRequest.Model),
+		InputTokens: model.ZeroNullInt64(openai.CountTokenMessages(
+			textRequest.Messages,
+			textRequest.Model,
+		)),
 	}, nil
 }

+ 4 - 1
core/relay/controller/completions.go

@@ -18,6 +18,9 @@ func GetCompletionsRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usa
 	}
 
 	return model.Usage{
-		InputTokens: openai.CountTokenInput(textRequest.Prompt, textRequest.Model),
+		InputTokens: model.ZeroNullInt64(openai.CountTokenInput(
+			textRequest.Prompt,
+			textRequest.Model,
+		)),
 	}, nil
 }

+ 6 - 3
core/relay/controller/edits.go

@@ -47,8 +47,11 @@ func GetImagesEditsRequestUsage(c *gin.Context, mc *model.ModelConfig) (model.Us
 	}
 
 	return model.Usage{
-		InputTokens:      openai.CountTokenInput(prompt, mc.Model),
-		ImageInputTokens: images,
-		OutputTokens:     int64(n),
+		InputTokens: model.ZeroNullInt64(openai.CountTokenInput(
+			prompt,
+			mc.Model,
+		)),
+		ImageInputTokens: model.ZeroNullInt64(images),
+		OutputTokens:     model.ZeroNullInt64(n),
 	}, nil
 }

+ 4 - 1
core/relay/controller/embed.go

@@ -18,6 +18,9 @@ func GetEmbedRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usage, er
 	}
 
 	return model.Usage{
-		InputTokens: openai.CountTokenInput(textRequest.Input, textRequest.Model),
+		InputTokens: model.ZeroNullInt64(openai.CountTokenInput(
+			textRequest.Input,
+			textRequest.Model,
+		)),
 	}, nil
 }

+ 5 - 2
core/relay/controller/image.go

@@ -69,7 +69,10 @@ func GetImagesRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usage, e
 	}
 
 	return model.Usage{
-		InputTokens:  openai.CountTokenInput(imageRequest.Prompt, imageRequest.Model),
-		OutputTokens: int64(imageRequest.N),
+		InputTokens: model.ZeroNullInt64(openai.CountTokenInput(
+			imageRequest.Prompt,
+			imageRequest.Model,
+		)),
+		OutputTokens: model.ZeroNullInt64(imageRequest.N),
 	}, nil
 }

+ 1 - 1
core/relay/controller/rerank.go

@@ -46,6 +46,6 @@ func GetRerankRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usage, e
 		return model.Usage{}, err
 	}
 	return model.Usage{
-		InputTokens: rerankPromptTokens(rerankRequest),
+		InputTokens: model.ZeroNullInt64(rerankPromptTokens(rerankRequest)),
 	}, nil
 }

+ 1 - 1
core/relay/controller/stt.go

@@ -33,7 +33,7 @@ func GetSTTRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usage, erro
 	log.Data["duration"] = durationInt
 
 	return model.Usage{
-		InputTokens: durationInt,
+		InputTokens: model.ZeroNullInt64(durationInt),
 	}, nil
 }
 

+ 1 - 1
core/relay/controller/tts.go

@@ -19,6 +19,6 @@ func GetTTSRequestUsage(c *gin.Context, _ *model.ModelConfig) (model.Usage, erro
 	}
 
 	return model.Usage{
-		InputTokens: int64(utf8.RuneCountInString(ttsRequest.Input)),
+		InputTokens: model.ZeroNullInt64(utf8.RuneCountInString(ttsRequest.Input)),
 	}, nil
 }

+ 6 - 6
core/relay/model/chat.go

@@ -22,14 +22,14 @@ func (u *Usage) ToModelUsage() *model.Usage {
 		return nil
 	}
 	usage := &model.Usage{
-		InputTokens:    u.PromptTokens,
-		OutputTokens:   u.CompletionTokens,
-		TotalTokens:    u.TotalTokens,
-		WebSearchCount: u.WebSearchCount,
+		InputTokens:    model.ZeroNullInt64(u.PromptTokens),
+		OutputTokens:   model.ZeroNullInt64(u.CompletionTokens),
+		TotalTokens:    model.ZeroNullInt64(u.TotalTokens),
+		WebSearchCount: model.ZeroNullInt64(u.WebSearchCount),
 	}
 	if u.PromptTokensDetails != nil {
-		usage.CachedTokens = u.PromptTokensDetails.CachedTokens
-		usage.CacheCreationTokens = u.PromptTokensDetails.CacheCreationTokens
+		usage.CachedTokens = model.ZeroNullInt64(u.PromptTokensDetails.CachedTokens)
+		usage.CacheCreationTokens = model.ZeroNullInt64(u.PromptTokensDetails.CacheCreationTokens)
 	}
 	return usage
 }

+ 4 - 4
core/relay/model/image.go

@@ -45,10 +45,10 @@ type ImageUsage struct {
 
 func (i *ImageUsage) ToModelUsage() *model.Usage {
 	return &model.Usage{
-		InputTokens:      i.InputTokens,
-		ImageInputTokens: i.InputTokensDetails.ImageTokens,
-		OutputTokens:     i.OutputTokens,
-		TotalTokens:      i.TotalTokens,
+		InputTokens:      model.ZeroNullInt64(i.InputTokens),
+		ImageInputTokens: model.ZeroNullInt64(i.InputTokensDetails.ImageTokens),
+		OutputTokens:     model.ZeroNullInt64(i.OutputTokens),
+		TotalTokens:      model.ZeroNullInt64(i.TotalTokens),
 	}
 }