Преглед на файлове

feat: db filed price zero null (#168)

zijiren преди 8 месеца
родител
ревизия
55a3509f26
променени са 8 файла, в които са добавени 307 реда и са изтрити 36 реда
  1. 9 9
      core/common/consume/consume.go
  2. 91 1
      core/docs/docs.go
  3. 91 1
      core/docs/swagger.json
  4. 63 1
      core/docs/swagger.yaml
  5. 21 21
      core/model/log.go
  6. 29 0
      core/model/utils.go
  7. 1 1
      core/relay/controller/edits.go
  8. 2 2
      core/relay/controller/image.go

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

@@ -123,7 +123,7 @@ func CalculateAmount(
 		if code != http.StatusOK {
 			return 0
 		}
-		return modelPrice.PerRequestPrice
+		return float64(modelPrice.PerRequestPrice)
 	}
 
 	inputTokens := usage.InputTokens
@@ -138,33 +138,33 @@ func CalculateAmount(
 	}
 
 	outputTokens := usage.OutputTokens
-	outputPrice := modelPrice.OutputPrice
+	outputPrice := float64(modelPrice.OutputPrice)
 	outputPriceUnit := modelPrice.GetOutputPriceUnit()
 	if usage.ReasoningTokens != 0 && modelPrice.ThinkingModeOutputPrice != 0 {
-		outputPrice = modelPrice.ThinkingModeOutputPrice
+		outputPrice = float64(modelPrice.ThinkingModeOutputPrice)
 		if modelPrice.ThinkingModeOutputPriceUnit != 0 {
-			outputPriceUnit = modelPrice.ThinkingModeOutputPriceUnit
+			outputPriceUnit = int64(modelPrice.ThinkingModeOutputPriceUnit)
 		}
 	}
 
 	inputAmount := decimal.NewFromInt(int64(inputTokens)).
-		Mul(decimal.NewFromFloat(modelPrice.InputPrice)).
+		Mul(decimal.NewFromFloat(float64(modelPrice.InputPrice))).
 		Div(decimal.NewFromInt(modelPrice.GetInputPriceUnit()))
 
 	imageInputAmount := decimal.NewFromInt(int64(usage.ImageInputTokens)).
-		Mul(decimal.NewFromFloat(modelPrice.ImageInputPrice)).
+		Mul(decimal.NewFromFloat(float64(modelPrice.ImageInputPrice))).
 		Div(decimal.NewFromInt(modelPrice.GetImageInputPriceUnit()))
 
 	cachedAmount := decimal.NewFromInt(int64(usage.CachedTokens)).
-		Mul(decimal.NewFromFloat(modelPrice.CachedPrice)).
+		Mul(decimal.NewFromFloat(float64(modelPrice.CachedPrice))).
 		Div(decimal.NewFromInt(modelPrice.GetCachedPriceUnit()))
 
 	cacheCreationAmount := decimal.NewFromInt(int64(usage.CacheCreationTokens)).
-		Mul(decimal.NewFromFloat(modelPrice.CacheCreationPrice)).
+		Mul(decimal.NewFromFloat(float64(modelPrice.CacheCreationPrice))).
 		Div(decimal.NewFromInt(modelPrice.GetCacheCreationPriceUnit()))
 
 	webSearchAmount := decimal.NewFromInt(int64(usage.WebSearchCount)).
-		Mul(decimal.NewFromFloat(modelPrice.WebSearchPrice)).
+		Mul(decimal.NewFromFloat(float64(modelPrice.WebSearchPrice))).
 		Div(decimal.NewFromInt(modelPrice.GetWebSearchPriceUnit()))
 
 	outputAmount := decimal.NewFromInt(int64(outputTokens)).

+ 91 - 1
core/docs/docs.go

@@ -6914,6 +6914,86 @@ const docTemplate = `{
                 }
             }
         },
+        "/v1/images/edits": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "ImagesEdits",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "relay"
+                ],
+                "summary": "ImagesEdits",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Prompt",
+                        "name": "prompt",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "Model",
+                        "name": "model",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "file",
+                        "description": "Images",
+                        "name": "image",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "Optional Aiproxy-Channel header",
+                        "name": "Aiproxy-Channel",
+                        "in": "header"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.SttJSONResponse"
+                        },
+                        "headers": {
+                            "X-RateLimit-Limit-Requests": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Limit-Requests"
+                            },
+                            "X-RateLimit-Limit-Tokens": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Limit-Tokens"
+                            },
+                            "X-RateLimit-Remaining-Requests": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Remaining-Requests"
+                            },
+                            "X-RateLimit-Remaining-Tokens": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Remaining-Tokens"
+                            },
+                            "X-RateLimit-Reset-Requests": {
+                                "type": "string",
+                                "description": "X-RateLimit-Reset-Requests"
+                            },
+                            "X-RateLimit-Reset-Tokens": {
+                                "type": "string",
+                                "description": "X-RateLimit-Reset-Tokens"
+                            }
+                        }
+                    }
+                }
+            }
+        },
         "/v1/images/generations": {
             "post": {
                 "security": [
@@ -7798,6 +7878,9 @@ const docTemplate = `{
                 "output_tokens": {
                     "type": "integer"
                 },
+                "reasoning_tokens": {
+                    "type": "integer"
+                },
                 "total_tokens": {
                     "type": "integer"
                 },
@@ -8768,7 +8851,7 @@ const docTemplate = `{
                         }
                     ]
                 },
-                "outputTokens": {
+                "output_tokens": {
                     "description": "The number of image tokens in the output image.",
                     "type": "integer"
                 },
@@ -9153,6 +9236,13 @@ const docTemplate = `{
                 "per_request_price": {
                     "type": "number"
                 },
+                "thinking_mode_output_price": {
+                    "description": "when ThinkingModeOutputPrice and ReasoningTokens are not 0, OutputPrice and OutputPriceUnit will be overwritten",
+                    "type": "number"
+                },
+                "thinking_mode_output_price_unit": {
+                    "type": "integer"
+                },
                 "web_search_price": {
                     "type": "number"
                 },

+ 91 - 1
core/docs/swagger.json

@@ -6905,6 +6905,86 @@
                 }
             }
         },
+        "/v1/images/edits": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "ImagesEdits",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "relay"
+                ],
+                "summary": "ImagesEdits",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Prompt",
+                        "name": "prompt",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "Model",
+                        "name": "model",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "file",
+                        "description": "Images",
+                        "name": "image",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "Optional Aiproxy-Channel header",
+                        "name": "Aiproxy-Channel",
+                        "in": "header"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/model.SttJSONResponse"
+                        },
+                        "headers": {
+                            "X-RateLimit-Limit-Requests": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Limit-Requests"
+                            },
+                            "X-RateLimit-Limit-Tokens": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Limit-Tokens"
+                            },
+                            "X-RateLimit-Remaining-Requests": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Remaining-Requests"
+                            },
+                            "X-RateLimit-Remaining-Tokens": {
+                                "type": "integer",
+                                "description": "X-RateLimit-Remaining-Tokens"
+                            },
+                            "X-RateLimit-Reset-Requests": {
+                                "type": "string",
+                                "description": "X-RateLimit-Reset-Requests"
+                            },
+                            "X-RateLimit-Reset-Tokens": {
+                                "type": "string",
+                                "description": "X-RateLimit-Reset-Tokens"
+                            }
+                        }
+                    }
+                }
+            }
+        },
         "/v1/images/generations": {
             "post": {
                 "security": [
@@ -7789,6 +7869,9 @@
                 "output_tokens": {
                     "type": "integer"
                 },
+                "reasoning_tokens": {
+                    "type": "integer"
+                },
                 "total_tokens": {
                     "type": "integer"
                 },
@@ -8759,7 +8842,7 @@
                         }
                     ]
                 },
-                "outputTokens": {
+                "output_tokens": {
                     "description": "The number of image tokens in the output image.",
                     "type": "integer"
                 },
@@ -9144,6 +9227,13 @@
                 "per_request_price": {
                     "type": "number"
                 },
+                "thinking_mode_output_price": {
+                    "description": "when ThinkingModeOutputPrice and ReasoningTokens are not 0, OutputPrice and OutputPriceUnit will be overwritten",
+                    "type": "number"
+                },
+                "thinking_mode_output_price_unit": {
+                    "type": "integer"
+                },
                 "web_search_price": {
                     "type": "number"
                 },

+ 63 - 1
core/docs/swagger.yaml

@@ -356,6 +356,8 @@ definitions:
         type: integer
       output_tokens:
         type: integer
+      reasoning_tokens:
+        type: integer
       total_tokens:
         type: integer
       web_search_count:
@@ -1033,7 +1035,7 @@ definitions:
         allOf:
         - $ref: '#/definitions/model.ImageInputTokensDetails'
         description: The input tokens detailed information for the image generation.
-      outputTokens:
+      output_tokens:
         description: The number of image tokens in the output image.
         type: integer
       total_tokens:
@@ -1311,6 +1313,12 @@ definitions:
         type: integer
       per_request_price:
         type: number
+      thinking_mode_output_price:
+        description: when ThinkingModeOutputPrice and ReasoningTokens are not 0, OutputPrice
+          and OutputPriceUnit will be overwritten
+        type: number
+      thinking_mode_output_price_unit:
+        type: integer
       web_search_price:
         type: number
       web_search_price_unit:
@@ -5770,6 +5778,60 @@ paths:
       summary: Embeddings
       tags:
       - relay
+  /v1/images/edits:
+    post:
+      description: ImagesEdits
+      parameters:
+      - description: Prompt
+        in: formData
+        name: prompt
+        required: true
+        type: string
+      - description: Model
+        in: formData
+        name: model
+        required: true
+        type: string
+      - description: Images
+        in: formData
+        name: image
+        required: true
+        type: file
+      - description: Optional Aiproxy-Channel header
+        in: header
+        name: Aiproxy-Channel
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          headers:
+            X-RateLimit-Limit-Requests:
+              description: X-RateLimit-Limit-Requests
+              type: integer
+            X-RateLimit-Limit-Tokens:
+              description: X-RateLimit-Limit-Tokens
+              type: integer
+            X-RateLimit-Remaining-Requests:
+              description: X-RateLimit-Remaining-Requests
+              type: integer
+            X-RateLimit-Remaining-Tokens:
+              description: X-RateLimit-Remaining-Tokens
+              type: integer
+            X-RateLimit-Reset-Requests:
+              description: X-RateLimit-Reset-Requests
+              type: string
+            X-RateLimit-Reset-Tokens:
+              description: X-RateLimit-Reset-Tokens
+              type: string
+          schema:
+            $ref: '#/definitions/model.SttJSONResponse'
+      security:
+      - ApiKeyAuth: []
+      summary: ImagesEdits
+      tags:
+      - relay
   /v1/images/generations:
     post:
       description: ImagesGenerations

+ 21 - 21
core/model/log.go

@@ -39,69 +39,69 @@ func (d *RequestDetail) BeforeSave(_ *gorm.DB) (err error) {
 }
 
 type Price struct {
-	PerRequestPrice float64 `json:"per_request_price,omitempty"`
+	PerRequestPrice ZeroNullFloat64 `json:"per_request_price,omitempty"`
 
-	InputPrice     float64 `json:"input_price,omitempty"`
-	InputPriceUnit int64   `json:"input_price_unit,omitempty"`
+	InputPrice     ZeroNullFloat64 `json:"input_price,omitempty"`
+	InputPriceUnit ZeroNullInt64   `json:"input_price_unit,omitempty"`
 
-	ImageInputPrice     float64 `json:"image_input_price,omitempty"`
-	ImageInputPriceUnit int64   `json:"image_input_price_unit,omitempty"`
+	ImageInputPrice     ZeroNullFloat64 `json:"image_input_price,omitempty"`
+	ImageInputPriceUnit ZeroNullInt64   `json:"image_input_price_unit,omitempty"`
 
-	OutputPrice     float64 `json:"output_price,omitempty"`
-	OutputPriceUnit int64   `json:"output_price_unit,omitempty"`
+	OutputPrice     ZeroNullFloat64 `json:"output_price,omitempty"`
+	OutputPriceUnit ZeroNullInt64   `json:"output_price_unit,omitempty"`
 
 	// when ThinkingModeOutputPrice and ReasoningTokens are not 0, OutputPrice and OutputPriceUnit will be overwritten
-	ThinkingModeOutputPrice     float64 `json:"thinking_mode_output_price,omitempty"`
-	ThinkingModeOutputPriceUnit int64   `json:"thinking_mode_output_price_unit,omitempty"`
+	ThinkingModeOutputPrice     ZeroNullFloat64 `json:"thinking_mode_output_price,omitempty"`
+	ThinkingModeOutputPriceUnit ZeroNullInt64   `json:"thinking_mode_output_price_unit,omitempty"`
 
-	CachedPrice     float64 `json:"cached_price,omitempty"`
-	CachedPriceUnit int64   `json:"cached_price_unit,omitempty"`
+	CachedPrice     ZeroNullFloat64 `json:"cached_price,omitempty"`
+	CachedPriceUnit ZeroNullInt64   `json:"cached_price_unit,omitempty"`
 
-	CacheCreationPrice     float64 `json:"cache_creation_price,omitempty"`
-	CacheCreationPriceUnit int64   `json:"cache_creation_price_unit,omitempty"`
+	CacheCreationPrice     ZeroNullFloat64 `json:"cache_creation_price,omitempty"`
+	CacheCreationPriceUnit ZeroNullInt64   `json:"cache_creation_price_unit,omitempty"`
 
-	WebSearchPrice     float64 `json:"web_search_price,omitempty"`
-	WebSearchPriceUnit int64   `json:"web_search_price_unit,omitempty"`
+	WebSearchPrice     ZeroNullFloat64 `json:"web_search_price,omitempty"`
+	WebSearchPriceUnit ZeroNullInt64   `json:"web_search_price_unit,omitempty"`
 }
 
 func (p *Price) GetInputPriceUnit() int64 {
 	if p.InputPriceUnit > 0 {
-		return p.InputPriceUnit
+		return int64(p.InputPriceUnit)
 	}
 	return PriceUnit
 }
 
 func (p *Price) GetImageInputPriceUnit() int64 {
 	if p.ImageInputPriceUnit > 0 {
-		return p.ImageInputPriceUnit
+		return int64(p.ImageInputPriceUnit)
 	}
 	return PriceUnit
 }
 
 func (p *Price) GetOutputPriceUnit() int64 {
 	if p.OutputPriceUnit > 0 {
-		return p.OutputPriceUnit
+		return int64(p.OutputPriceUnit)
 	}
 	return PriceUnit
 }
 
 func (p *Price) GetCachedPriceUnit() int64 {
 	if p.CachedPriceUnit > 0 {
-		return p.CachedPriceUnit
+		return int64(p.CachedPriceUnit)
 	}
 	return PriceUnit
 }
 
 func (p *Price) GetCacheCreationPriceUnit() int64 {
 	if p.CacheCreationPriceUnit > 0 {
-		return p.CacheCreationPriceUnit
+		return int64(p.CacheCreationPriceUnit)
 	}
 	return PriceUnit
 }
 
 func (p *Price) GetWebSearchPriceUnit() int64 {
 	if p.WebSearchPriceUnit > 0 {
-		return p.WebSearchPriceUnit
+		return int64(p.WebSearchPriceUnit)
 	}
 	return PriceUnit
 }

+ 29 - 0
core/model/utils.go

@@ -46,6 +46,35 @@ func IgnoreNotFound(err error) error {
 	return err
 }
 
+type ZeroNullFloat64 float64
+
+func (znf ZeroNullFloat64) Value() (driver.Value, error) {
+	if znf == 0 {
+		return nil, nil
+	}
+	return float64(znf), nil
+}
+
+func (znf *ZeroNullFloat64) Scan(value any) error {
+	if value == nil {
+		*znf = 0
+		return nil
+	}
+	switch v := value.(type) {
+	case int:
+		*znf = ZeroNullFloat64(v)
+	case int64:
+		*znf = ZeroNullFloat64(v)
+	case float32:
+		*znf = ZeroNullFloat64(v)
+	case float64:
+		*znf = ZeroNullFloat64(v)
+	default:
+		return fmt.Errorf("unsupported type: %T", v)
+	}
+	return nil
+}
+
 type ZeroNullInt64 int64
 
 func (zni ZeroNullInt64) Value() (driver.Value, error) {

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

@@ -24,7 +24,7 @@ func GetImagesEditsRequestPrice(c *gin.Context, mc *model.ModelConfig) (model.Pr
 		InputPriceUnit:      mc.Price.InputPriceUnit,
 		ImageInputPrice:     mc.Price.ImageInputPrice,
 		ImageInputPriceUnit: mc.Price.ImageInputPriceUnit,
-		OutputPrice:         imageCostPrice,
+		OutputPrice:         model.ZeroNullFloat64(imageCostPrice),
 		OutputPriceUnit:     mc.Price.OutputPriceUnit,
 	}, nil
 }

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

@@ -28,7 +28,7 @@ func getImagesRequest(c *gin.Context) (*relaymodel.ImageRequest, error) {
 func GetImagesOutputPrice(modelConfig *model.ModelConfig, size string, quality string) (float64, bool) {
 	switch {
 	case len(modelConfig.ImagePrices) == 0 && len(modelConfig.ImageQualityPrices) == 0:
-		return modelConfig.Price.OutputPrice, true
+		return float64(modelConfig.Price.OutputPrice), true
 	case len(modelConfig.ImageQualityPrices) != 0:
 		price, ok := modelConfig.ImageQualityPrices[size][quality]
 		return price, ok
@@ -57,7 +57,7 @@ func GetImagesRequestPrice(c *gin.Context, mc *model.ModelConfig) (model.Price,
 		InputPriceUnit:      mc.Price.InputPriceUnit,
 		ImageInputPrice:     mc.Price.ImageInputPrice,
 		ImageInputPriceUnit: mc.Price.ImageInputPriceUnit,
-		OutputPrice:         imageCostPrice,
+		OutputPrice:         model.ZeroNullFloat64(imageCostPrice),
 		OutputPriceUnit:     mc.Price.OutputPriceUnit,
 	}, nil
 }