mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-30 17:14:40 +08:00
fix(provider-usage): address review feedback
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { Schema } from "effect"
|
||||
|
||||
const NumberField = Schema.Finite
|
||||
const IntegerField = Schema.Int
|
||||
// Matches the cloud schema: remaining percent is a 0-100 share of the base quota; boosts scale it separately.
|
||||
const PercentField = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0)).check(Schema.isLessThanOrEqualTo(100))
|
||||
|
||||
export const ModelRemains = Schema.Struct({
|
||||
model_name: Schema.String,
|
||||
@@ -10,18 +11,16 @@ export const ModelRemains = Schema.Struct({
|
||||
start_time: Schema.optional(IntegerField),
|
||||
end_time: Schema.optional(IntegerField),
|
||||
remains_time: Schema.optional(IntegerField),
|
||||
interval_boost_permill: Schema.optional(IntegerField),
|
||||
interval_boost_permille: Schema.optional(IntegerField),
|
||||
current_interval_remaining_percent: Schema.optional(NumberField),
|
||||
current_interval_remaining_percent: Schema.optional(PercentField),
|
||||
current_interval_status: Schema.optional(IntegerField),
|
||||
current_weekly_total_count: Schema.optional(IntegerField),
|
||||
current_weekly_usage_count: Schema.optional(IntegerField),
|
||||
weekly_start_time: Schema.optional(IntegerField),
|
||||
weekly_end_time: Schema.optional(IntegerField),
|
||||
weekly_remains_time: Schema.optional(IntegerField),
|
||||
weekly_boost_permill: Schema.optional(IntegerField),
|
||||
weekly_boost_permille: Schema.optional(IntegerField),
|
||||
current_weekly_remaining_percent: Schema.optional(NumberField),
|
||||
current_weekly_remaining_percent: Schema.optional(PercentField),
|
||||
current_weekly_status: Schema.optional(IntegerField),
|
||||
}).annotate({ identifier: "MiniMaxModelRemains" })
|
||||
export type ModelRemains = typeof ModelRemains.Type
|
||||
|
||||
@@ -131,9 +131,7 @@ function window(
|
||||
const start = weekly ? row.weekly_start_time : row.start_time
|
||||
const end = weekly ? row.weekly_end_time : row.end_time
|
||||
const remains = weekly ? row.weekly_remains_time : row.remains_time
|
||||
const boost = weekly
|
||||
? (row.weekly_boost_permille ?? row.weekly_boost_permill)
|
||||
: (row.interval_boost_permille ?? row.interval_boost_permill)
|
||||
const boost = weekly ? row.weekly_boost_permille : row.interval_boost_permille
|
||||
const span = duration(start, end)
|
||||
const base = {
|
||||
id: `${row.model_name}-${kind}`,
|
||||
@@ -148,7 +146,8 @@ function window(
|
||||
if (percent !== undefined) {
|
||||
const factor = boost !== undefined && boost > 0 ? boost / 1000 : 1
|
||||
const cap = 100 * factor
|
||||
const remaining = percent * factor
|
||||
// The status flag is authoritative: an exhausted window has zero remaining even when the percent field lags.
|
||||
const remaining = status === 2 ? 0 : percent * factor
|
||||
return {
|
||||
...base,
|
||||
unit: factor === 1 ? "percent" : "standard_units",
|
||||
@@ -161,14 +160,16 @@ function window(
|
||||
}
|
||||
|
||||
if (total !== undefined && total > 0 && count !== undefined && count >= 0) {
|
||||
// Despite the name, MiniMax's *_usage_count fields report the remaining quota, not the consumed amount.
|
||||
const remaining = status === 2 ? 0 : count
|
||||
return {
|
||||
...base,
|
||||
unit: "count",
|
||||
orientation: "count",
|
||||
used: Math.max(0, total - count),
|
||||
remaining: count,
|
||||
used: Math.max(0, total - remaining),
|
||||
remaining,
|
||||
limit: total,
|
||||
state: status === 2 || count === 0 ? "exhausted" : "active",
|
||||
state: status === 2 || remaining === 0 ? "exhausted" : "active",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ describe("MiniMax usage normalization", () => {
|
||||
native({
|
||||
current_weekly_remaining_percent: 100,
|
||||
current_weekly_status: 1,
|
||||
weekly_boost_permill: 1500,
|
||||
weekly_boost_permille: 1500,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
@@ -132,6 +132,187 @@ describe("MiniMax usage normalization", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("MiniMax usage window calculations", () => {
|
||||
test("clamps used at zero when the remaining count exceeds the total", () => {
|
||||
const item = normalize(
|
||||
native({
|
||||
current_interval_total_count: 1500,
|
||||
current_interval_usage_count: 1600,
|
||||
current_interval_status: 1,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
|
||||
expect(item.windows[0]).toMatchObject({
|
||||
orientation: "count",
|
||||
remaining: 1600,
|
||||
used: 0,
|
||||
limit: 1500,
|
||||
state: "active",
|
||||
})
|
||||
})
|
||||
|
||||
test("marks count windows exhausted when nothing remains or the status flags it", () => {
|
||||
const drained = normalize(
|
||||
native({
|
||||
current_interval_total_count: 1500,
|
||||
current_interval_usage_count: 0,
|
||||
current_interval_status: 1,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
expect(drained.windows[0]).toMatchObject({ remaining: 0, used: 1500, state: "exhausted" })
|
||||
|
||||
const flagged = normalize(
|
||||
native({
|
||||
current_interval_total_count: 1500,
|
||||
current_interval_usage_count: 800,
|
||||
current_interval_status: 2,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
expect(flagged.windows[0]).toMatchObject({ remaining: 0, used: 1500, state: "exhausted" })
|
||||
})
|
||||
|
||||
test("treats an exhausted status as authoritative over lagging percent fields", () => {
|
||||
const item = normalize(native({ current_interval_remaining_percent: 12, current_interval_status: 2 }), options)
|
||||
|
||||
expect(item.windows[0]).toMatchObject({ remaining: 0, used: 100, limit: 100, state: "exhausted" })
|
||||
})
|
||||
|
||||
test("reads weekly count windows from the weekly fields", () => {
|
||||
const item = normalize(
|
||||
native({
|
||||
current_weekly_total_count: 6000,
|
||||
current_weekly_usage_count: 4500,
|
||||
current_weekly_status: 1,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
|
||||
expect(item.windows).toHaveLength(1)
|
||||
expect(item.windows[0]).toMatchObject({
|
||||
id: "general-weekly",
|
||||
orientation: "count",
|
||||
remaining: 4500,
|
||||
used: 1500,
|
||||
limit: 6000,
|
||||
period: { unit: "week", value: 1 },
|
||||
})
|
||||
})
|
||||
|
||||
test("rejects out-of-range percent values like the cloud schema does", () => {
|
||||
expect(() => native({ current_interval_remaining_percent: 150 })).toThrow()
|
||||
expect(() => native({ current_weekly_remaining_percent: -1 })).toThrow()
|
||||
})
|
||||
|
||||
test("accepts the permille boost spelling and falls back to plain percent without a boost", () => {
|
||||
const boosted = normalize(
|
||||
native({
|
||||
current_interval_remaining_percent: 50,
|
||||
current_interval_status: 1,
|
||||
interval_boost_permille: 2000,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
expect(boosted.windows[0]).toMatchObject({
|
||||
unit: "standard_units",
|
||||
orientation: "amount",
|
||||
remaining: 100,
|
||||
used: 100,
|
||||
limit: 200,
|
||||
})
|
||||
|
||||
const plain = normalize(native({ current_interval_remaining_percent: 50, current_interval_status: 1 }), options)
|
||||
expect(plain.windows[0]).toMatchObject({
|
||||
unit: "percent",
|
||||
orientation: "remaining_percent",
|
||||
remaining: 50,
|
||||
used: 50,
|
||||
limit: 100,
|
||||
})
|
||||
})
|
||||
|
||||
test("marks percent windows exhausted when nothing remains", () => {
|
||||
const item = normalize(native({ current_interval_remaining_percent: 0, current_interval_status: 1 }), options)
|
||||
|
||||
expect(item.windows[0]).toMatchObject({ remaining: 0, used: 100, state: "exhausted" })
|
||||
})
|
||||
|
||||
test("derives the period from the window span when it is a round unit", () => {
|
||||
const start = 1_781_827_200_000
|
||||
const item = normalize(
|
||||
native({
|
||||
current_interval_remaining_percent: 80,
|
||||
current_interval_status: 1,
|
||||
start_time: start,
|
||||
end_time: start + 14 * 24 * 60 * 60 * 1000,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
|
||||
expect(item.windows[0]?.period).toEqual({ unit: "week", value: 2 })
|
||||
expect(item.windows[0]?.durationMs).toBe(14 * 24 * 60 * 60 * 1000)
|
||||
})
|
||||
|
||||
test("omits the period when the span is not a round hour, day, or week", () => {
|
||||
const start = 1_781_827_200_000
|
||||
const item = normalize(
|
||||
native({
|
||||
current_interval_remaining_percent: 80,
|
||||
current_interval_status: 1,
|
||||
start_time: start,
|
||||
end_time: start + 90 * 60 * 1000,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
|
||||
expect(item.windows[0]?.period).toBeUndefined()
|
||||
expect(item.windows[0]?.durationMs).toBe(90 * 60 * 1000)
|
||||
})
|
||||
|
||||
test("omits the duration and period when the window timestamps are inverted", () => {
|
||||
const start = 1_781_827_200_000
|
||||
const item = normalize(
|
||||
native({
|
||||
current_interval_remaining_percent: 80,
|
||||
current_interval_status: 1,
|
||||
start_time: start,
|
||||
end_time: start,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
|
||||
expect(item.windows[0]?.durationMs).toBeUndefined()
|
||||
expect(item.windows[0]?.period).toBeUndefined()
|
||||
})
|
||||
|
||||
test("falls back to remains_time when end_time is missing or zero", () => {
|
||||
const item = normalize(
|
||||
native({
|
||||
current_interval_remaining_percent: 80,
|
||||
current_interval_status: 1,
|
||||
end_time: 0,
|
||||
remains_time: 3_600_000,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
expect(item.windows[0]?.resetAt).toBe("2026-06-19T01:00:00.000Z")
|
||||
|
||||
const none = normalize(native({ current_interval_remaining_percent: 80, current_interval_status: 1 }), options)
|
||||
expect(none.windows[0]?.resetAt).toBeUndefined()
|
||||
})
|
||||
|
||||
test("omits windows with no usage signals and keeps status-only windows as unknown", () => {
|
||||
const empty = normalize(native({}), options)
|
||||
expect(empty.windows).toEqual([])
|
||||
|
||||
const item = normalize(native({ current_interval_status: 1 }), options)
|
||||
expect(item.windows).toHaveLength(1)
|
||||
expect(item.windows[0]).toMatchObject({ unit: "unknown", orientation: "amount", state: "unknown" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("MiniMax usage transport and detection", () => {
|
||||
test("uses fixed hosts and ignores configured base URLs", async () => {
|
||||
const fn = mock(() => Promise.resolve(response({ base_resp: { status_code: 0 }, model_remains: [] })))
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
.provider-usage-section-heading {
|
||||
margin-bottom: 12px;
|
||||
/* Optically align with the text inside the cards (1px border + 8px card padding) */
|
||||
padding: 0 9px;
|
||||
}
|
||||
|
||||
.provider-usage-section-heading h3 {
|
||||
@@ -61,6 +63,11 @@
|
||||
border-left: 2px solid var(--icon-warning-base);
|
||||
}
|
||||
|
||||
/* Align the action button label with the card content; small buttons have 12px inline padding */
|
||||
.provider-usage-list [data-slot="card-actions"] {
|
||||
margin-left: -12px;
|
||||
}
|
||||
|
||||
.provider-usage-loading {
|
||||
display: grid;
|
||||
min-height: 92px;
|
||||
|
||||
Reference in New Issue
Block a user