mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-10-30 11:06:56 +00:00
A plugin change can leave a task model without a usable price: no price is set, the price uses usage the plugin no longer provides, or the plugin no longer offers the model. The gateway now lists these models. The list follows the plugins currently serving, so a gateway update that changes a built-in plugin shows up too. - Uploading, installing from the marketplace, activating or enabling a plugin, or deleting its active version, lists the models it cannot charge in the upload dialog or in the notice after the action. - Model pricing marks each of them "Price needs attention" with the reason, and a "Price status" filter lists them. A plugin model that was never priced now appears in the table too. The badge replaces "Task pricing not configured". - The sidebar shows how many models need attention on System Settings, Billing & Payment and Model Pricing, for super administrators. - GET /api/option/model_pricing/issues returns the list. It is root only; access tokens need the option:read scope. - Prices kept for a switched-off plugin are not reported. They apply again when the plugin is switched back on.
91 lines
2.6 KiB
Go
91 lines
2.6 KiB
Go
package controller
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/model"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func GetModelPricingConfig(c *gin.Context) {
|
|
snapshot, err := model.GetModelPricingSnapshot(c.QueryArray("model"))
|
|
if err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
common.ApiSuccess(c, snapshot)
|
|
}
|
|
|
|
func GetModelPricingIssues(c *gin.Context) {
|
|
issues, err := model.GetModelPricingIssues("")
|
|
if err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
common.ApiSuccess(c, issues)
|
|
}
|
|
|
|
func PreviewModelPricingConversion(c *gin.Context) {
|
|
var request struct {
|
|
ModelName string `json:"model_name"`
|
|
Pricing model.PricingValues `json:"pricing"`
|
|
}
|
|
if err := common.DecodeJson(c.Request.Body, &request); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()})
|
|
return
|
|
}
|
|
preview, err := model.PreviewModelPricingConversion(request.ModelName, request.Pricing)
|
|
if err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
common.ApiSuccess(c, preview)
|
|
}
|
|
|
|
func PreviewModelPricing(c *gin.Context) {
|
|
var request struct {
|
|
ModelName string `json:"model_name"`
|
|
Pricing model.PricingValues `json:"pricing"`
|
|
}
|
|
if err := common.DecodeJson(c.Request.Body, &request); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()})
|
|
return
|
|
}
|
|
preview, err := model.PreviewModelPricing(request.ModelName, request.Pricing)
|
|
if err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
common.ApiSuccess(c, model.ModelPricingDescription{
|
|
Effective: preview,
|
|
CacheWriteMode: model.ResolveCacheWriteMode(request.ModelName, request.Pricing),
|
|
BillingDetails: model.ResolveLegacyBillingDetails(request.ModelName, preview, request.Pricing),
|
|
})
|
|
}
|
|
|
|
func UpdateModelPricingConfig(c *gin.Context) {
|
|
var request struct {
|
|
Changes []model.ModelPricingChange `json:"changes"`
|
|
}
|
|
if err := common.DecodeJson(c.Request.Body, &request); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": err.Error()})
|
|
return
|
|
}
|
|
if err := model.UpdateModelPricing(request.Changes); err != nil {
|
|
status := http.StatusBadRequest
|
|
if errors.Is(err, model.ErrModelPricingConflict) {
|
|
status = http.StatusConflict
|
|
}
|
|
c.JSON(status, gin.H{"success": false, "message": err.Error()})
|
|
return
|
|
}
|
|
names := make([]string, 0, len(request.Changes))
|
|
for _, change := range request.Changes {
|
|
names = append(names, change.ModelName)
|
|
}
|
|
recordManageAudit(c, "model.pricing.update", map[string]any{"models": names})
|
|
common.ApiSuccess(c, gin.H{"updated_models": names})
|
|
}
|