eab478bdc8
- log_info_generate.go: add nil guard in InjectTieredBillingInfo - billing_expr_request.go: merge headers instead of replacing - go.mod: remove incorrect // indirect on expr-lang/expr - ToolPriceSettings.jsx: add null check in syncToVisual - tool_billing.go: fix PricePer1K for image_generation (per-call, not per-1K) - utils.jsx: add minute() to time condition regex - useUsageLogsData.jsx: pass displayMode to renderTieredModelPrice - AGENTS.md, CLAUDE.md: fix Rule 6/7 ordering - relay-gemini.go: add TEXT modality case in CandidatesTokensDetails
92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package helper
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/dto"
|
|
"github.com/QuantumNous/new-api/pkg/billingexpr"
|
|
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func ResolveIncomingBillingExprRequestInput(c *gin.Context, info *relaycommon.RelayInfo) (billingexpr.RequestInput, error) {
|
|
if info != nil && info.BillingRequestInput != nil {
|
|
input := cloneRequestInput(*info.BillingRequestInput)
|
|
merged := cloneStringMap(info.RequestHeaders)
|
|
for k, v := range input.Headers {
|
|
merged[k] = v
|
|
}
|
|
input.Headers = merged
|
|
return input, nil
|
|
}
|
|
|
|
input := billingexpr.RequestInput{}
|
|
if info != nil {
|
|
input.Headers = cloneStringMap(info.RequestHeaders)
|
|
}
|
|
|
|
bodyBytes, err := readIncomingBillingExprBody(c)
|
|
if err != nil {
|
|
return billingexpr.RequestInput{}, err
|
|
}
|
|
input.Body = bodyBytes
|
|
return input, nil
|
|
}
|
|
|
|
func BuildBillingExprRequestInputFromRequest(request dto.Request, headers map[string]string) (billingexpr.RequestInput, error) {
|
|
input := billingexpr.RequestInput{
|
|
Headers: cloneStringMap(headers),
|
|
}
|
|
if request == nil {
|
|
return input, nil
|
|
}
|
|
|
|
bodyBytes, err := common.Marshal(request)
|
|
if err != nil {
|
|
return billingexpr.RequestInput{}, err
|
|
}
|
|
input.Body = bodyBytes
|
|
return input, nil
|
|
}
|
|
|
|
func readIncomingBillingExprBody(c *gin.Context) ([]byte, error) {
|
|
if c == nil || c.Request == nil || !isJSONContentType(c.Request.Header.Get("Content-Type")) {
|
|
return nil, nil
|
|
}
|
|
storage, err := common.GetBodyStorage(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return storage.Bytes()
|
|
}
|
|
|
|
func cloneRequestInput(src billingexpr.RequestInput) billingexpr.RequestInput {
|
|
input := billingexpr.RequestInput{
|
|
Headers: cloneStringMap(src.Headers),
|
|
}
|
|
if len(src.Body) > 0 {
|
|
input.Body = append([]byte(nil), src.Body...)
|
|
}
|
|
return input
|
|
}
|
|
|
|
func isJSONContentType(contentType string) bool {
|
|
contentType = strings.ToLower(strings.TrimSpace(contentType))
|
|
return strings.HasPrefix(contentType, "application/json")
|
|
}
|
|
|
|
func cloneStringMap(src map[string]string) map[string]string {
|
|
if len(src) == 0 {
|
|
return map[string]string{}
|
|
}
|
|
dst := make(map[string]string, len(src))
|
|
for key, value := range src {
|
|
if strings.TrimSpace(key) == "" {
|
|
continue
|
|
}
|
|
dst[key] = value
|
|
}
|
|
return dst
|
|
}
|