feat: sync deployed v1.0.0-rc.38-custom.42
Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
1 parent
aa6c3f50f4
commit
ceddfd3d6b
828 files changed
+122253
-13702
No files matched your search
@@ -0,0 +1,111 @@
|
||||
package billingexpr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/expr-lang/expr/ast"
|
||||
"github.com/expr-lang/expr/parser"
|
||||
)
|
||||
|
||||
// DetectionCostBound proves a conservative cost ceiling rather than sampling
|
||||
// a few token counts. Both conditional branches are bounded, including prices
|
||||
// selected by time/request rules. Unsupported expressions fail closed.
|
||||
func DetectionCostBound(expression string, input, output float64) (float64, error) {
|
||||
_, body := ParseExprVersion(expression)
|
||||
tree, err := parser.Parse(body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var bound func(ast.Node) (float64, error)
|
||||
unsupported := errors.New("detection pricing cannot be safely bounded")
|
||||
bound = func(node ast.Node) (float64, error) {
|
||||
switch n := node.(type) {
|
||||
case *ast.IntegerNode:
|
||||
if n.Value >= 0 {
|
||||
return float64(n.Value), nil
|
||||
}
|
||||
case *ast.FloatNode:
|
||||
if n.Value >= 0 {
|
||||
return n.Value, nil
|
||||
}
|
||||
case *ast.IdentifierNode:
|
||||
switch n.Value {
|
||||
case "p", "len", "cr", "cc", "cc1h":
|
||||
return input, nil
|
||||
case "c":
|
||||
return output, nil
|
||||
case "img", "img_cr", "img_o", "ai", "ao":
|
||||
return 0, nil
|
||||
case "image_count":
|
||||
return 1, nil
|
||||
}
|
||||
case *ast.BinaryNode:
|
||||
a, e := bound(n.Left)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
b, e := bound(n.Right)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
switch n.Operator {
|
||||
case "+":
|
||||
return a + b, nil
|
||||
case "*":
|
||||
return a * b, nil
|
||||
case "/":
|
||||
switch n.Right.(type) {
|
||||
case *ast.IntegerNode, *ast.FloatNode:
|
||||
if b > 0 {
|
||||
return a / b, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.ConditionalNode:
|
||||
a, e := bound(n.Exp1)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
b, e := bound(n.Exp2)
|
||||
return max(a, b), e
|
||||
case *ast.CallNode:
|
||||
name, ok := n.Callee.(*ast.IdentifierNode)
|
||||
if !ok {
|
||||
return 0, unsupported
|
||||
}
|
||||
if name.Value == "tier" && len(n.Arguments) == 2 {
|
||||
return bound(n.Arguments[1])
|
||||
}
|
||||
if name.Value == "fixed" && len(n.Arguments) == 1 {
|
||||
v, e := bound(n.Arguments[0])
|
||||
return v * 1_000_000, e
|
||||
}
|
||||
if name.Value == "ceil" && len(n.Arguments) == 1 {
|
||||
v, e := bound(n.Arguments[0])
|
||||
return math.Ceil(v), e
|
||||
}
|
||||
if name.Value == "floor" && len(n.Arguments) == 1 {
|
||||
v, e := bound(n.Arguments[0])
|
||||
return math.Floor(v), e
|
||||
}
|
||||
if name.Value == "min" || name.Value == "max" {
|
||||
v := float64(0)
|
||||
for _, arg := range n.Arguments {
|
||||
x, e := bound(arg)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
v = max(v, x)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return 0, unsupported
|
||||
}
|
||||
value, err := bound(tree.Node)
|
||||
if math.IsNaN(value) || math.IsInf(value, 0) {
|
||||
return 0, unsupported
|
||||
}
|
||||
return value, err
|
||||
}
|
||||
Reference in new issue
Block a user