Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

205 lines
5.2 KiB
Go

package export
import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"math"
"reflect"
"sort"
"strconv"
"strings"
"unicode/utf16"
)
func EffectivePricingDigest(rows []EffectivePricingRow) (string, error) {
canonical, err := canonicalPricingJSON(canonicalPricingRows(rows))
if err != nil {
return "", fmt.Errorf("canonical pricing digest: %w", err)
}
sum := sha256.Sum256(canonical)
return "sha256:" + fmt.Sprintf("%x", sum), nil
}
func canonicalPricingRows(rows []EffectivePricingRow) map[string]any {
copied := make([]EffectivePricingRow, len(rows))
copy(copied, rows)
sort.SliceStable(copied, func(i, j int) bool {
return canonicalPricingRowLess(copied[i], copied[j])
})
out := make([]any, 0, len(copied))
for _, row := range copied {
var updatedAt any
if row.Rates.UpdatedAt != nil {
updatedAt = row.Rates.UpdatedAt.UTC().Format(jsonTimeLayout)
}
out = append(out, map[string]any{
"cache_read_per_mtok": row.Rates.CacheReadPerMTok,
"cache_write_per_mtok": row.Rates.CacheWritePerMTok,
"input_per_mtok": row.Rates.InputPerMTok,
"model_pattern": row.ModelPattern,
"output_per_mtok": row.Rates.OutputPerMTok,
"source": string(row.Rates.Source),
"updated_at": updatedAt,
})
}
return map[string]any{"rows": out}
}
const jsonTimeLayout = "2006-01-02T15:04:05Z07:00"
func canonicalPricingJSON(v any) ([]byte, error) {
var b bytes.Buffer
if err := writeCanonicalJSON(&b, reflect.ValueOf(v)); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func writeCanonicalJSON(b *bytes.Buffer, v reflect.Value) error {
if !v.IsValid() {
b.WriteString("null")
return nil
}
if v.Kind() == reflect.Interface || v.Kind() == reflect.Pointer {
if v.IsNil() {
b.WriteString("null")
return nil
}
return writeCanonicalJSON(b, v.Elem())
}
switch v.Kind() {
case reflect.Map:
if v.Type().Key().Kind() != reflect.String {
return fmt.Errorf("canonical JSON only supports string map keys")
}
keys := v.MapKeys()
sort.Slice(keys, func(i, j int) bool {
return utf16Less(keys[i].String(), keys[j].String())
})
b.WriteByte('{')
for i, key := range keys {
if i > 0 {
b.WriteByte(',')
}
writeJSONString(b, key.String())
b.WriteByte(':')
if err := writeCanonicalJSON(b, v.MapIndex(key)); err != nil {
return err
}
}
b.WriteByte('}')
case reflect.Slice, reflect.Array:
b.WriteByte('[')
for i := 0; i < v.Len(); i++ {
if i > 0 {
b.WriteByte(',')
}
if err := writeCanonicalJSON(b, v.Index(i)); err != nil {
return err
}
}
b.WriteByte(']')
case reflect.String:
writeJSONString(b, v.String())
case reflect.Bool:
if v.Bool() {
b.WriteString("true")
} else {
b.WriteString("false")
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
b.WriteString(strconv.FormatInt(v.Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
b.WriteString(strconv.FormatUint(v.Uint(), 10))
case reflect.Float32, reflect.Float64:
f := v.Float()
if math.IsNaN(f) || math.IsInf(f, 0) {
return fmt.Errorf("canonical JSON cannot encode non-finite number")
}
b.WriteString(formatCanonicalJSONFloat(f, v.Type().Bits()))
default:
return fmt.Errorf("canonical JSON unsupported type %s", v.Type())
}
return nil
}
func writeJSONString(b *bytes.Buffer, s string) {
var encoded bytes.Buffer
enc := json.NewEncoder(&encoded)
enc.SetEscapeHTML(false)
_ = enc.Encode(s)
b.Write(bytes.TrimSpace(encoded.Bytes()))
}
func canonicalPricingRowLess(a, b EffectivePricingRow) bool {
aValues := canonicalPricingRowSortValues(a)
bValues := canonicalPricingRowSortValues(b)
for i := range aValues {
if aValues[i] != bValues[i] {
return aValues[i] < bValues[i]
}
}
return false
}
func canonicalPricingRowSortValues(row EffectivePricingRow) []string {
updatedAt := ""
if row.Rates.UpdatedAt != nil {
updatedAt = row.Rates.UpdatedAt.UTC().Format(jsonTimeLayout)
}
return []string{
row.ModelPattern,
string(row.Rates.Source),
formatCanonicalJSONFloat(row.Rates.InputPerMTok, 64),
formatCanonicalJSONFloat(row.Rates.OutputPerMTok, 64),
formatCanonicalJSONFloat(row.Rates.CacheWritePerMTok, 64),
formatCanonicalJSONFloat(row.Rates.CacheReadPerMTok, 64),
updatedAt,
}
}
func formatCanonicalJSONFloat(f float64, bits int) string {
if f == 0 {
return "0"
}
abs := math.Abs(f)
if abs >= 1e21 || abs < 1e-6 {
return normalizeCanonicalExponent(strconv.FormatFloat(f, 'e', -1, bits))
}
return strconv.FormatFloat(f, 'f', -1, bits)
}
func normalizeCanonicalExponent(s string) string {
mantissa, exponent, ok := strings.Cut(s, "e")
if !ok {
return s
}
sign := ""
switch {
case strings.HasPrefix(exponent, "+"):
sign = "+"
exponent = strings.TrimPrefix(exponent, "+")
case strings.HasPrefix(exponent, "-"):
sign = "-"
exponent = strings.TrimPrefix(exponent, "-")
}
exponent = strings.TrimLeft(exponent, "0")
if exponent == "" {
exponent = "0"
}
return mantissa + "e" + sign + exponent
}
func utf16Less(a, b string) bool {
au := utf16.Encode([]rune(a))
bu := utf16.Encode([]rune(b))
for i := 0; i < len(au) && i < len(bu); i++ {
if au[i] != bu[i] {
return au[i] < bu[i]
}
}
return len(au) < len(bu)
}