Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
70 lines
2.5 KiB
Go
70 lines
2.5 KiB
Go
package controller
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/model"
|
|
"github.com/QuantumNous/new-api/relaykit/dto"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func relayAttemptGuardResult(a model.RelayAttempt) string {
|
|
if a.GuardResult == "model_mismatch" {
|
|
return "mismatch_rejected"
|
|
}
|
|
if a.GuardResult == "unchecked" && a.ReturnedModel != "" && a.ReturnedModel == a.RequestedModel {
|
|
return "accepted"
|
|
}
|
|
if a.GuardResult == "accepted" && a.Number > 1 {
|
|
return "switched_success"
|
|
}
|
|
return a.GuardResult
|
|
}
|
|
|
|
// Attempt history is owner-scoped; channel identifiers are administrator-only.
|
|
func GetRelayAttempts(c *gin.Context) {
|
|
requestID := c.Param("request_id")
|
|
if requestID == "" || len(requestID) > 64 {
|
|
c.AbortWithStatus(400)
|
|
return
|
|
}
|
|
query := model.DB.Where("request_id = ?", requestID)
|
|
admin := c.GetInt("role") >= common.RoleAdminUser
|
|
if !admin {
|
|
query = query.Where("user_id = ?", c.GetInt("id"))
|
|
}
|
|
var attempts []model.RelayAttempt
|
|
if err := query.Order("number asc, id asc").Limit(100).Find(&attempts).Error; err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
rows := make([]gin.H, 0, len(attempts))
|
|
total := 0
|
|
pending := false
|
|
for _, a := range attempts {
|
|
var usage dto.Usage
|
|
_ = common.UnmarshalJsonStr(a.UsageJSON, &usage)
|
|
row := gin.H{"number": a.Number, "group": a.GroupName, "requested_model": a.RequestedModel, "returned_model": a.ReturnedModel, "guard_result": relayAttemptGuardResult(a), "usage_source": a.UsageSource, "state": a.State, "quota": a.Quota, "billing_source": a.Funding, "usage": usage}
|
|
row["charged"] = a.State == "settled" && a.Quota > 0
|
|
row["charge_explanation"] = "Each consumed attempt is charged independently, including rejected results."
|
|
var log model.Log
|
|
var other map[string]json.RawMessage
|
|
price := map[string]json.RawMessage{}
|
|
if common.UnmarshalJsonStr(a.LogJSON, &log) == nil && common.UnmarshalJsonStr(log.Other, &other) == nil {
|
|
for _, key := range []string{"group_ratio", "model_ratio", "completion_ratio", "cache_ratio", "model_price", "billing_mode", "expr_b64", "matched_tier", "billing_tokens", "billing_unit", "fixed_price"} {
|
|
if value, ok := other[key]; ok {
|
|
price[key] = value
|
|
}
|
|
}
|
|
}
|
|
row["price"] = price
|
|
if admin {
|
|
row["channel_id"] = a.ChannelID
|
|
}
|
|
rows = append(rows, row)
|
|
total += a.Quota
|
|
pending = pending || a.State != "settled"
|
|
}
|
|
common.ApiSuccess(c, gin.H{"attempts": rows, "total_quota": total, "pending": pending})
|
|
}
|