Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions controller/pricing.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,16 +303,22 @@ func buildWebsitePublicGroupPricingPayload(
description = group
}
usableGroup := map[string]string{group: description}
visiblePricing := filterHiddenPricingModels(filterPricingByUsableGroups(pricing, usableGroup))

return gin.H{
"success": true,
"data": filterHiddenPricingModels(filterPricingByUsableGroups(pricing, usableGroup)),
"vendors": vendors,
"group_ratio": map[string]float64{group: ratio},
"success": true,
"data": visiblePricing,
"vendors": vendors,
"group_ratio": map[string]float64{group: ratio},
// Per-model overrides beat the flat group ratio during billing
// (ratio_setting.GetEffectiveGroupRatio), so the public payload has to
// carry them too — otherwise a model priced below the group ratio is
// quoted higher than it is actually charged.
"group_model_ratio": filterGroupModelRatioByUsableGroupsAndModels(ratio_setting.GetGroupModelRatioCopy(), usableGroup, visiblePricing),
"usable_group": usableGroup,
"supported_endpoint": supportedEndpoint,
"auto_groups": autoGroups,
"pricing_version": "website-public-plg-v1",
"pricing_version": "website-public-plg-v2",
}
}

Expand Down
48 changes: 47 additions & 1 deletion controller/pricing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,11 @@ func TestBuildWebsitePublicGroupPricingPayloadIncludesHiddenPLGOnly(t *testing.T
],
"vendors": null,
"group_ratio": {"plg": 0.9},
"group_model_ratio": {},
"usable_group": {"plg": "plg"},
"supported_endpoint": null,
"auto_groups": null,
"pricing_version": "website-public-plg-v1"
"pricing_version": "website-public-plg-v2"
}`, string(body))
}

Expand Down Expand Up @@ -278,3 +279,48 @@ func TestFilterHiddenPricingModelsDoesNotMutateInput(t *testing.T) {
require.Len(t, pricing, 2)
require.Equal(t, "gpt-4o", pricing[0].ModelName)
}

func withGroupModelRatio(t *testing.T, value string) {
t.Helper()
previous := ratio_setting.GroupModelRatio2JSONString()
t.Cleanup(func() {
_ = ratio_setting.UpdateGroupModelRatioByJSONString(previous)
})
require.NoError(t, ratio_setting.UpdateGroupModelRatioByJSONString(value))
}

// The public PLG payload must expose per-model group ratios. Without them the
// website falls back to the flat plg ratio and quotes a price the user does
// not actually pay when a model is configured cheaper for plg.
func TestBuildWebsitePublicGroupPricingPayloadExposesGroupModelRatio(t *testing.T) {
withGroupModelRatio(t, `{"plg":{"glm-5":0.6,"hidden-elsewhere":0.5},"vip":{"glm-5":0.4}}`)

pricing := []model.Pricing{
{ModelName: "glm-5", EnableGroup: []string{"plg"}},
{ModelName: "gpt-4o", EnableGroup: []string{"plg"}},
}

payload := buildWebsitePublicGroupPricingPayload(pricing, nil, nil, nil, "plg", 0.9)

groupModelRatio, ok := payload["group_model_ratio"].(map[string]map[string]float64)
require.True(t, ok, "group_model_ratio must be present")
require.Equal(t, map[string]map[string]float64{
"plg": {"glm-5": 0.6},
}, groupModelRatio)
}

func TestBuildWebsitePublicGroupPricingPayloadOmitsHiddenModelRatios(t *testing.T) {
withGroupModelRatio(t, `{"plg":{"glm-5":0.6,"secret-model":0.3}}`)
withHiddenPricingModels(t, "secret-model")

pricing := []model.Pricing{
{ModelName: "glm-5", EnableGroup: []string{"plg"}},
{ModelName: "secret-model", EnableGroup: []string{"plg"}},
}

payload := buildWebsitePublicGroupPricingPayload(pricing, nil, nil, nil, "plg", 0.9)

require.Equal(t, map[string]map[string]float64{
"plg": {"glm-5": 0.6},
}, payload["group_model_ratio"])
}
73 changes: 73 additions & 0 deletions website/src/lib/group-model-ratio-pricing.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { describe, expect, test } from "bun:test";
import { buildRowsForModels } from "./home-models";
import {
buildEffectiveGroupRatio,
formatModelPrice,
getBestGroupRatio,
type PricingData,
type PricingModel,
} from "./pricing";

const VENDORS: PricingData["vendors"] = [{ id: 1, name: "Zhipu" }];

// A domestic model billed cheaper than the flat plg ratio: GroupModelRatio
// gives plg/glm-5 a 0.6 override while the plg group ratio is 0.9.
const GROUP_RATIO = { plg: 0.9 };
const GROUP_MODEL_RATIO = { plg: { "glm-5": 0.6 } };

const GLM: PricingModel = {
model_name: "glm-5",
vendor_id: 1,
quota_type: 0,
model_ratio: 0.3, // official $0.60 / 1M input
completion_ratio: 1,
enable_groups: ["plg"],
};

// No per-model override: falls back to the flat plg group ratio.
const GPT: PricingModel = {
model_name: "gpt-4.1-mini",
vendor_id: 1,
quota_type: 0,
model_ratio: 0.2, // official $0.40 / 1M input
completion_ratio: 1,
enable_groups: ["plg"],
};

function enrich(model: PricingModel): PricingModel {
return {
...model,
group_ratio: buildEffectiveGroupRatio(model, GROUP_RATIO, GROUP_MODEL_RATIO),
};
}

describe("per-model group ratio takes precedence over the flat group ratio", () => {
test("buildEffectiveGroupRatio applies the model override", () => {
expect(buildEffectiveGroupRatio(GLM, GROUP_RATIO, GROUP_MODEL_RATIO)).toEqual({ plg: 0.6 });
expect(buildEffectiveGroupRatio(GPT, GROUP_RATIO, GROUP_MODEL_RATIO)).toEqual({ plg: 0.9 });
});

test("getBestGroupRatio prefers the model override", () => {
expect(getBestGroupRatio(enrich(GLM), GROUP_RATIO)).toBe(0.6);
expect(getBestGroupRatio(enrich(GPT), GROUP_RATIO)).toBe(0.9);
});

test("formatModelPrice quotes the overridden ratio, not the group ratio", () => {
// 0.3 x 2 x 0.6 = 0.36. Using the flat 0.9 would over-quote at $0.54.
expect(formatModelPrice(enrich(GLM), "input")).toBe("$0.36");
expect(formatModelPrice(enrich(GPT), "input")).toBe("$0.36");
});

test("directory rows quote the overridden ratio", () => {
const rows = buildRowsForModels([enrich(GLM), enrich(GPT)], VENDORS, GROUP_RATIO);

expect(rows[0].official).toBe("$0.6");
expect(rows[0].discounted).toBe("$0.36"); // 0.6 x 0.6, not 0.6 x 0.9
expect(rows[1].official).toBe("$0.4");
expect(rows[1].discounted).toBe("$0.36"); // 0.4 x 0.9
});

test("an unenriched model still falls back to the flat group ratio", () => {
expect(getBestGroupRatio(GLM, GROUP_RATIO)).toBe(0.9);
});
});