feat: enhance tiered billing functionality and UI components
- Introduced new fields for billing mode and expression in the Pricing model. - Implemented dynamic pricing breakdown component to display tiered billing details. - Updated various components to support and render tiered billing information. - Enhanced pricing calculation logic to accommodate dynamic pricing scenarios. - Added tests for new billing expression functionalities and UI components.
This commit is contained in:
@@ -931,3 +931,55 @@ func TestTimeFunctions_MonthDayPattern(t *testing.T) {
|
||||
t.Errorf("cost = %f, want 1000 or 500", cost)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Image and audio token tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestImageTokenVariable(t *testing.T) {
|
||||
exprStr := `tier("base", p * 2 + c * 10 + img * 5)`
|
||||
cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000, C: 500, Img: 200})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 1000*2 + 500*10 + 200*5 = 2000 + 5000 + 1000 = 8000
|
||||
if math.Abs(cost-8000) > 1e-6 {
|
||||
t.Errorf("cost = %f, want 8000", cost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAudioTokenVariables(t *testing.T) {
|
||||
exprStr := `tier("base", p * 2 + c * 10 + ai * 50 + ao * 100)`
|
||||
cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000, C: 500, AI: 100, AO: 50})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 1000*2 + 500*10 + 100*50 + 50*100 = 2000 + 5000 + 5000 + 5000 = 17000
|
||||
if math.Abs(cost-17000) > 1e-6 {
|
||||
t.Errorf("cost = %f, want 17000", cost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageAudioAliases(t *testing.T) {
|
||||
exprStr := `tier("base", prompt_tokens * 1 + image_tokens * 3 + audio_input_tokens * 5 + audio_output_tokens * 10)`
|
||||
cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 100, Img: 50, AI: 20, AO: 10})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 100*1 + 50*3 + 20*5 + 10*10 = 100 + 150 + 100 + 100 = 450
|
||||
if math.Abs(cost-450) > 1e-6 {
|
||||
t.Errorf("cost = %f, want 450", cost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageAudioZero(t *testing.T) {
|
||||
exprStr := `tier("base", p * 2 + img * 5 + ai * 50 + ao * 100)`
|
||||
cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// img, ai, ao default to 0
|
||||
if math.Abs(cost-2000) > 1e-6 {
|
||||
t.Errorf("cost = %f, want 2000", cost)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,12 @@ var compileEnvPrototype = map[string]interface{}{
|
||||
"cache_read_tokens": float64(0),
|
||||
"cache_create_tokens": float64(0),
|
||||
"cache_create_1h_tokens": float64(0),
|
||||
"img": float64(0),
|
||||
"ai": float64(0),
|
||||
"ao": float64(0),
|
||||
"image_tokens": float64(0),
|
||||
"audio_input_tokens": float64(0),
|
||||
"audio_output_tokens": float64(0),
|
||||
"tier": func(string, float64) float64 { return 0 },
|
||||
"header": func(string) string { return "" },
|
||||
"param": func(string) interface{} { return nil },
|
||||
|
||||
@@ -62,6 +62,12 @@ func runProgram(prog *vm.Program, params TokenParams, request RequestInput) (flo
|
||||
"cache_read_tokens": params.CR,
|
||||
"cache_create_tokens": params.CC,
|
||||
"cache_create_1h_tokens": params.CC1h,
|
||||
"img": params.Img,
|
||||
"ai": params.AI,
|
||||
"ao": params.AO,
|
||||
"image_tokens": params.Img,
|
||||
"audio_input_tokens": params.AI,
|
||||
"audio_output_tokens": params.AO,
|
||||
"tier": func(name string, value float64) float64 {
|
||||
trace.MatchedTier = name
|
||||
trace.Cost = value
|
||||
|
||||
@@ -14,11 +14,14 @@ type RequestInput struct {
|
||||
// Fields beyond P and C are optional — when absent they default to 0,
|
||||
// which means cache-unaware expressions keep working unchanged.
|
||||
type TokenParams struct {
|
||||
P float64 // prompt tokens
|
||||
C float64 // completion tokens
|
||||
P float64 // prompt tokens (text)
|
||||
C float64 // completion tokens (text)
|
||||
CR float64 // cache read (hit) tokens
|
||||
CC float64 // cache creation tokens (5-min TTL for Claude, generic for others)
|
||||
CC1h float64 // cache creation tokens — 1-hour TTL (Claude only)
|
||||
Img float64 // image input tokens
|
||||
AI float64 // audio input tokens
|
||||
AO float64 // audio output tokens
|
||||
}
|
||||
|
||||
// TraceResult holds side-channel info captured by the tier() function
|
||||
|
||||
Reference in New Issue
Block a user