Skip to content

Commit f5ef211

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@c2a1983d38883380db7cdccedfe7b9a1048c6023
1 parent 5182f32 commit f5ef211

5 files changed

Lines changed: 45 additions & 34 deletions

File tree

bun.lock

Lines changed: 2 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/src/__tests__/freebuff-models.test.ts

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,9 @@ describe('freebuff model availability', () => {
252252
expect(all[all.length - 1]).toBe(FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID)
253253
// And it is nobody's starting pick — a default has to be joinable at every
254254
// hour, which Pro is not.
255-
expect(DEFAULT_FREEBUFF_MODEL_ID).not.toBe(FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID)
255+
expect(DEFAULT_FREEBUFF_MODEL_ID).not.toBe(
256+
FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID,
257+
)
256258
expect(DEFAULT_FREEBUFF_WEB_MODEL_ID).not.toBe(
257259
FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID,
258260
)
@@ -265,7 +267,7 @@ describe('freebuff model availability', () => {
265267
* because the hazard is a notice being ADDED back rather than an existing one
266268
* being wrong — and because both notices that used to live here expired
267269
* without anyone noticing (each claimed V4 Flash was the better default;
268-
* Flash then became premium and started closing for ten hours a day).
270+
* Flash then became premium and started closing during peak hours).
269271
*
270272
* It matters more than copy: migrateSupersededFreebuffModelPreference
271273
* rewrites a SAVED pick on every load, so a supersedes notice silently moves
@@ -300,17 +302,6 @@ describe('freebuff model availability', () => {
300302
)
301303
})
302304

303-
/**
304-
* The peak-hours swap, as one test because it is one decision. V4 Pro used to
305-
* close 00:00-10:00 UTC and Flash stayed open; on 2026-08-21 that inverted,
306-
* because Pro moved to a flat-priced lane while Flash is still served by
307-
* lanes that double at peak.
308-
*
309-
* Asserted as a PAIR deliberately. Either row alone looks arbitrary, and the
310-
* dangerous state is both closed at once — which is what a copy-paste of the
311-
* old availability onto the new row would produce, closing the catalog's two
312-
* strongest models for ten hours a day.
313-
*/
314305
/**
315306
* THE invariant, restated as what it has always really been: V4 Pro and V4
316307
* Flash are never closed at the same time.
@@ -343,7 +334,10 @@ describe('freebuff model availability', () => {
343334
isFreebuffSessionModelAvailable(FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID, peak),
344335
).toBe(true)
345336
expect(
346-
isFreebuffSessionModelAvailable(FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID, peak),
337+
isFreebuffSessionModelAvailable(
338+
FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID,
339+
peak,
340+
),
347341
).toBe(true)
348342
})
349343

@@ -815,14 +809,14 @@ describe('freebuff model availability', () => {
815809
includeGodOnly: true,
816810
}),
817811
).toBe(true)
818-
expect(
819-
isFreebuffWebGodOnlyModelId(FREEBUFF_GPT_5_6_LUNA_ES_MODEL_ID),
820-
).toBe(true)
812+
expect(isFreebuffWebGodOnlyModelId(FREEBUFF_GPT_5_6_LUNA_ES_MODEL_ID)).toBe(
813+
true,
814+
)
821815
// A premium model absent from every pool lands in the unmetered Standard
822816
// set instead — this must be true, or it isn't in SOME pool.
823-
expect(
824-
isFreebuffWebPremiumModelId(FREEBUFF_GPT_5_6_LUNA_ES_MODEL_ID),
825-
).toBe(true)
817+
expect(isFreebuffWebPremiumModelId(FREEBUFF_GPT_5_6_LUNA_ES_MODEL_ID)).toBe(
818+
true,
819+
)
826820
expect(FREEBUFF_STANDARD_MODEL_IDS).not.toContain(
827821
FREEBUFF_GPT_5_6_LUNA_ES_MODEL_ID,
828822
)

common/src/__tests__/freebuff-peak-hours.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,12 @@ describe('the expensive window', () => {
6363
)
6464
})
6565
})
66+
67+
test.each(['2026-08-29T02:00:00Z', '2026-08-30T02:00:00Z'])(
68+
'%s uses weekend rates in Beijing',
69+
(instant) => {
70+
const date = new Date(instant)
71+
expect(deepseekPricingWindow(date)).toBe('off-peak')
72+
expect(isDeepSeekExpensiveWindow(date)).toBe(false)
73+
},
74+
)

common/src/constants/freebuff-peak-hours.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
/**
1414
* Peak hours, from api-docs.deepseek.com/quick_start/pricing (read 2026-08-16):
1515
* "Peak hours are 01:00 - 04:00 and 06:00 - 10:00 UTC (all other hours are
16-
* off-peak)." Every rate doubles inside them.
16+
* off-peak)." These windows apply Monday-Friday Beijing time; weekends are
17+
* always off-peak.
1718
*
1819
* Half-open [start, end): 04:00:00 UTC itself is already off-peak. TWO
1920
* disjoint windows, not one — the 04:00-06:00 gap between them is exactly what
@@ -28,6 +29,11 @@ export const DEEPSEEK_PEAK_HOUR_RANGES_UTC: ReadonlyArray<
2829

2930
export type DeepSeekPricingWindow = 'peak' | 'off-peak'
3031

32+
function isBeijingWeekend(at: Date): boolean {
33+
const beijingDay = new Date(at.getTime() + 8 * 60 * 60 * 1000).getUTCDay()
34+
return beijingDay === 0 || beijingDay === 6
35+
}
36+
3137
/**
3238
* Which rate card applies at `at`.
3339
*
@@ -36,6 +42,7 @@ export type DeepSeekPricingWindow = 'peak' | 'off-peak'
3642
* ceiling), and a hidden `new Date()` would make both untestable.
3743
*/
3844
export function deepseekPricingWindow(at: Date): DeepSeekPricingWindow {
45+
if (isBeijingWeekend(at)) return 'off-peak'
3946
const hour = at.getUTCHours()
4047
const peak = DEEPSEEK_PEAK_HOUR_RANGES_UTC.some(
4148
([startHour, endHour]) => hour >= startHour && hour < endHour,
@@ -58,8 +65,8 @@ export function deepseekPricingWindow(at: Date): DeepSeekPricingWindow {
5865
export const DEEPSEEK_EXPENSIVE_WINDOW_LEAD_HOURS = 1
5966

6067
/**
61-
* The single window in which DeepSeek is at its most expensive, [start, end)
62-
* UTC — 00:00 to 10:00, which is 5pm to 3am Pacific.
68+
* The single weekday window in which DeepSeek is at its most expensive,
69+
* [start, end) UTC — 00:00 to 10:00, which is 5pm to 3am Pacific.
6370
*
6471
* DERIVED from DEEPSEEK_PEAK_HOUR_RANGES_UTC rather than written down, so it
6572
* cannot drift the day DeepSeek moves its hours.
@@ -79,6 +86,7 @@ export const DEEPSEEK_EXPENSIVE_WINDOW_UTC: readonly [number, number] = [
7986
/** Whether `at` falls in the window above. Half-open like the peak check, so
8087
* the closing hour is already outside it. */
8188
export function isDeepSeekExpensiveWindow(at: Date): boolean {
89+
if (isBeijingWeekend(at)) return false
8290
const [start, end] = DEEPSEEK_EXPENSIVE_WINDOW_UTC
8391
const hour = at.getUTCHours()
8492
return hour >= start && hour < end

common/src/constants/freebuff-subscriptions.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ import {
2424
*/
2525

2626
/** Models a subscription's pooled allowance can be spent on. */
27-
export const FREEBUFF_SUBSCRIPTION_MODEL_IDS: readonly string[] = Object.freeze([
28-
FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID,
29-
FREEBUFF_GPT_5_6_LUNA_MODEL_ID,
30-
FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID,
31-
FREEBUFF_KIMI_K3_ECO_MODEL_ID,
32-
])
27+
export const FREEBUFF_SUBSCRIPTION_MODEL_IDS: readonly string[] = Object.freeze(
28+
[
29+
FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID,
30+
FREEBUFF_GPT_5_6_LUNA_MODEL_ID,
31+
FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID,
32+
FREEBUFF_KIMI_K3_ECO_MODEL_ID,
33+
],
34+
)
3335

3436
/**
3537
* The expensive half of the pool, sub-capped within each day.
@@ -181,7 +183,7 @@ export function freebuffSubscriptionTierDisclaimers(
181183
'Adds to your free sessions rather than replacing them',
182184
]
183185
if (tier.deepseekPeakHoursExcluded) {
184-
out.push('DeepSeek models are unavailable during peak hours')
186+
out.push('DeepSeek models are unavailable during weekday peak hours')
185187
}
186188
return out
187189
}

0 commit comments

Comments
 (0)