feat: publish sanitized custom.97 source
This commit is contained in:
1022 files changed
+69903
-19207
No files matched your search
+70
-19
@@ -13,7 +13,9 @@ import (
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/pkg/billingexpr"
|
||||
"github.com/QuantumNous/new-api/pkg/jsplugin"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -23,10 +25,10 @@ import (
|
||||
// 实际扣费已由 BillingSession(PreConsumeBilling + SettleBilling)完成。
|
||||
func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo, task *model.Task) {
|
||||
tokenName := c.GetString("token_name")
|
||||
logContent := fmt.Sprintf("操作 %s", info.Action)
|
||||
logContent := []*common.Message{common.NewMessage("Action {{action}}", map[string]any{"action": info.Action})}
|
||||
// 支持任务仅按次计费
|
||||
if common.StringsContains(constant.TaskPricePatches, info.OriginModelName) {
|
||||
logContent = fmt.Sprintf("%s,按次计费", logContent)
|
||||
logContent = append(logContent, common.NewMessage("Billed per request"))
|
||||
} else {
|
||||
var contents []string
|
||||
if otherRatios := info.PriceData.OtherRatios(); len(otherRatios) > 0 {
|
||||
@@ -42,12 +44,15 @@ func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo, task *model
|
||||
}
|
||||
}
|
||||
if len(contents) > 0 {
|
||||
logContent = fmt.Sprintf("%s, 计算参数:%s", logContent, strings.Join(contents, ", "))
|
||||
logContent = append(logContent, common.NewMessage("Billing parameters: {{params}}", map[string]any{"params": strings.Join(contents, ", ")}))
|
||||
}
|
||||
}
|
||||
other := model.NewLogOther()
|
||||
other.SetPublic("is_task", true)
|
||||
other.SetPublic("request_path", c.Request.URL.Path)
|
||||
if taskDeliveredInline(c, task) {
|
||||
other.SetPublic("task_sync", true)
|
||||
}
|
||||
other.SetPublic("model_price", info.PriceData.ModelPrice)
|
||||
if info.PriceData.ModelRatio > 0 {
|
||||
other.SetPublic("model_ratio", info.PriceData.ModelRatio)
|
||||
@@ -67,6 +72,9 @@ func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo, task *model
|
||||
if len(snap.UsageFacts) > 0 {
|
||||
other.SetPublic("usage_facts", snap.UsageFacts)
|
||||
}
|
||||
setTaskImageCount(other, snap.UsageFacts["image_count"])
|
||||
} else {
|
||||
setTaskImageCount(other, info.PriceData.OtherRatios()["image_count"])
|
||||
}
|
||||
appendTaskLogInfo(task, other)
|
||||
attachQuotaSaturation(c, info, other)
|
||||
@@ -93,7 +101,7 @@ func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo, task *model
|
||||
func resolveTokenKey(ctx context.Context, tokenId int, taskID string) string {
|
||||
token, err := model.GetTokenById(tokenId)
|
||||
if err != nil {
|
||||
logger.LogWarn(ctx, fmt.Sprintf("获取令牌 key 失败 (tokenId=%d, task=%s): %s", tokenId, taskID, err.Error()))
|
||||
logger.LogWarn(ctx, common.LogText("failed to get token key (tokenId=%d, task=%s): %s", tokenId, taskID, err.Error()))
|
||||
return ""
|
||||
}
|
||||
return token.Key
|
||||
@@ -132,13 +140,15 @@ func taskAdjustTokenQuota(ctx context.Context, task *model.Task, delta int) {
|
||||
err = model.IncreaseTokenQuota(task.PrivateData.TokenId, tokenKey, -delta)
|
||||
}
|
||||
if err != nil {
|
||||
logger.LogWarn(ctx, fmt.Sprintf("调整令牌额度失败 (delta=%d, task=%s): %s", delta, task.TaskID, err.Error()))
|
||||
logger.LogWarn(ctx, common.LogText("failed to adjust token quota (delta=%d, task=%s): %s", delta, task.TaskID, err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// taskBillingOther 从 task 的 BillingContext 构建日志 Other 字段。
|
||||
func taskBillingOther(task *model.Task) *model.LogOther {
|
||||
other := model.NewLogOther()
|
||||
// 前端列表按 is_task 选择任务档位渲染分支,缺失会把差额/退款日志误判成普通 relay 动态计费
|
||||
other.SetPublic("is_task", true)
|
||||
if bc := task.PrivateData.BillingContext; bc != nil {
|
||||
other.SetPublic("model_price", bc.ModelPrice)
|
||||
if bc.ModelRatio > 0 {
|
||||
@@ -148,7 +158,7 @@ func taskBillingOther(task *model.Task) *model.LogOther {
|
||||
if priceData := taskBillingContextPriceData(bc); priceData != nil {
|
||||
for k, v := range priceData.OtherRatios() {
|
||||
if !other.SetPublic(k, v) {
|
||||
common.SysError("task billing other ratio key rejected: " + k)
|
||||
common.SysError(common.LogText("task billing other ratio key rejected: %s", k))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +169,9 @@ func taskBillingOther(task *model.Task) *model.LogOther {
|
||||
if len(snap.UsageFacts) > 0 {
|
||||
other.SetPublic("usage_facts", snap.UsageFacts)
|
||||
}
|
||||
setTaskImageCount(other, snap.UsageFacts["image_count"])
|
||||
} else if priceData := taskBillingContextPriceData(bc); priceData != nil {
|
||||
setTaskImageCount(other, priceData.OtherRatios()["image_count"])
|
||||
}
|
||||
}
|
||||
props := task.Properties
|
||||
@@ -170,6 +183,35 @@ func taskBillingOther(task *model.Task) *model.LogOther {
|
||||
return other
|
||||
}
|
||||
|
||||
// setTaskImageCount publishes the billed image quantity of an image task as
|
||||
// other.image_count, the same field the HTTP image relay writes, so the log
|
||||
// detail shows one "billable image count" regardless of the serving path. The
|
||||
// value is a host-validated count fact (at most dto.MaxImageN), never a quota.
|
||||
func setTaskImageCount(other *model.LogOther, value any) {
|
||||
count, ok := value.(float64)
|
||||
if !ok || count < 0 || count > float64(dto.MaxImageN) {
|
||||
return
|
||||
}
|
||||
other.SetPublic("image_count", common.QuotaRound(count))
|
||||
}
|
||||
|
||||
// taskDeliveredInline reports whether the submitting HTTP request itself
|
||||
// returns the task deliverable: the upstream completed immediately, or the
|
||||
// request came through the synchronous OpenAI Images protocol, where the host
|
||||
// waits for an asynchronous upstream task before answering. The usage log
|
||||
// presents such requests as synchronous instead of as asynchronous jobs.
|
||||
func taskDeliveredInline(c *gin.Context, task *model.Task) bool {
|
||||
if task != nil && (task.Status == model.TaskStatusSuccess || task.Status == model.TaskStatusFailure) {
|
||||
return true
|
||||
}
|
||||
pinnedValue, exists := c.Get(jsplugin.ContextKeyPinnedEndpoint)
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
pinned, ok := pinnedValue.(jsplugin.PinnedEndpoint)
|
||||
return ok && pinned.Protocol == jsplugin.ProtocolOpenAIImage
|
||||
}
|
||||
|
||||
func appendTaskLogInfo(task *model.Task, other *model.LogOther) {
|
||||
if task == nil || other == nil {
|
||||
return
|
||||
@@ -177,6 +219,11 @@ func appendTaskLogInfo(task *model.Task, other *model.LogOther) {
|
||||
if task.TaskID != "" {
|
||||
other.SetPublic("task_id", task.TaskID)
|
||||
}
|
||||
if task.PrivateData.ResultDiscarded {
|
||||
// The result was delivered inline and the upstream snapshot was not
|
||||
// persisted, so no artifact can be retrieved for this task.
|
||||
other.SetPublic("result_discarded", true)
|
||||
}
|
||||
if task.PrivateData.Execution != nil {
|
||||
AppendTaskPluginAuditInfo(other, task.PrivateData.Execution.TaskPlugin)
|
||||
}
|
||||
@@ -221,7 +268,7 @@ func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) bool
|
||||
|
||||
// 1. 退还资金来源(钱包或订阅)
|
||||
if err := taskAdjustFunding(task, -quota); err != nil {
|
||||
logger.LogWarn(ctx, fmt.Sprintf("退还资金来源失败 task %s: %s", task.TaskID, err.Error()))
|
||||
logger.LogWarn(ctx, common.LogText("failed to refund the funding source, task %s: %s", task.TaskID, err.Error()))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -239,7 +286,6 @@ func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) bool
|
||||
model.RecordTaskBillingLog(model.RecordTaskBillingLogParams{
|
||||
UserId: task.UserId,
|
||||
LogType: model.LogTypeRefund,
|
||||
Content: "",
|
||||
ChannelId: task.ChannelId,
|
||||
ModelName: taskModelName(task),
|
||||
Quota: quota,
|
||||
@@ -252,16 +298,16 @@ func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) bool
|
||||
// 回写失败必须显式告警,避免漏掉潜在的重复退款风险。
|
||||
task.Quota = 0
|
||||
if err := task.UpdateQuota(); err != nil {
|
||||
logger.LogError(ctx, fmt.Sprintf("退款成功但清除 task quota 失败 task %s: %s", task.TaskID, err.Error()))
|
||||
logger.LogError(ctx, common.LogText("refund succeeded but clearing the task quota failed, task %s: %s", task.TaskID, err.Error()))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RecalculateTaskQuota 通用的异步差额结算。
|
||||
// actualQuota 是任务完成后的实际应扣额度,与预扣额度 (task.Quota) 做差额结算。
|
||||
// reason 用于日志记录(例如 "token重算" 或 "adaptor调整")。
|
||||
// reason 写入日志内容(例如按 token 重算或 adaptor 调整)。
|
||||
// clamps 可选:若计算 actualQuota 时发生额度饱和,将其记入日志 admin_info(仅管理员可见)。
|
||||
func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int, reason string, clamps ...*common.QuotaClamp) {
|
||||
func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int, reason *common.Message, clamps ...*common.QuotaClamp) {
|
||||
if actualQuota < 0 {
|
||||
return
|
||||
}
|
||||
@@ -269,22 +315,22 @@ func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int
|
||||
quotaDelta := actualQuota - preConsumedQuota
|
||||
|
||||
if quotaDelta == 0 {
|
||||
logger.LogInfo(ctx, fmt.Sprintf("任务 %s 预扣费准确(%s,%s)",
|
||||
task.TaskID, logger.LogQuota(actualQuota), reason))
|
||||
logger.LogInfo(ctx, common.LogText("task %s pre-consumed quota matches the actual quota (%s, %s)",
|
||||
task.TaskID, logger.LogQuota(actualQuota), reason.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
logger.LogInfo(ctx, fmt.Sprintf("任务 %s 差额结算:delta=%s(实际:%s,预扣:%s,%s)",
|
||||
logger.LogInfo(ctx, common.LogText("task %s settles the difference: delta=%s (actual %s, pre-consumed %s, %s)",
|
||||
task.TaskID,
|
||||
logger.LogQuota(quotaDelta),
|
||||
logger.LogQuota(actualQuota),
|
||||
logger.LogQuota(preConsumedQuota),
|
||||
reason,
|
||||
reason.Error(),
|
||||
))
|
||||
|
||||
// 调整资金来源
|
||||
if err := taskAdjustFunding(task, quotaDelta); err != nil {
|
||||
logger.LogError(ctx, fmt.Sprintf("差额结算资金调整失败 task %s: %s", task.TaskID, err.Error()))
|
||||
logger.LogError(ctx, common.LogText("failed to adjust funding for the difference, task %s: %s", task.TaskID, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -293,7 +339,7 @@ func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int
|
||||
|
||||
task.Quota = actualQuota
|
||||
if err := task.UpdateQuota(); err != nil {
|
||||
logger.LogError(ctx, fmt.Sprintf("差额结算回写 quota 失败 task %s: %s", task.TaskID, err.Error()))
|
||||
logger.LogError(ctx, common.LogText("failed to write back the settled task quota, task %s: %s", task.TaskID, err.Error()))
|
||||
}
|
||||
|
||||
// 提交阶段已经累计过一次请求;结算阶段只调整最终用量。
|
||||
@@ -319,7 +365,7 @@ func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int
|
||||
model.RecordTaskBillingLog(model.RecordTaskBillingLogParams{
|
||||
UserId: task.UserId,
|
||||
LogType: logType,
|
||||
Content: reason,
|
||||
Content: []*common.Message{reason},
|
||||
ChannelId: task.ChannelId,
|
||||
ModelName: taskModelName(task),
|
||||
Quota: logQuota,
|
||||
@@ -383,7 +429,12 @@ func RecalculateTaskQuotaByTokens(ctx context.Context, task *model.Task, totalTo
|
||||
// 计算实际应扣费额度: totalTokens * modelRatio * groupRatio * otherMultiplier(饱和转换,防止溢出成负数)
|
||||
actualQuota, clamp := common.QuotaFromFloatChecked(float64(totalTokens) * modelRatio * finalGroupRatio * otherMultiplier)
|
||||
|
||||
reason := fmt.Sprintf("token重算:tokens=%d, modelRatio=%.2f, groupRatio=%.2f, otherMultiplier=%.4f", totalTokens, modelRatio, finalGroupRatio, otherMultiplier)
|
||||
reason := common.NewMessage("Recalculated from tokens: tokens {{tokens}}, model ratio {{model_ratio}}, group ratio {{group_ratio}}, other multiplier {{other_multiplier}}", map[string]any{
|
||||
"tokens": totalTokens,
|
||||
"model_ratio": fmt.Sprintf("%.2f", modelRatio),
|
||||
"group_ratio": fmt.Sprintf("%.2f", finalGroupRatio),
|
||||
"other_multiplier": fmt.Sprintf("%.4f", otherMultiplier),
|
||||
})
|
||||
RecalculateTaskQuota(ctx, task, actualQuota, reason, clamp)
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user