Skip to content

Commit 8a590f3

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@4f4f9a6cd4e9c7aa793a32fcdc8438ced2a0f5a2
1 parent 1459aef commit 8a590f3

3 files changed

Lines changed: 93 additions & 0 deletions

File tree

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ import {
6262
isFreebuffPremiumModelId,
6363
isFreebuffSessionModelAllowedForAccessTier,
6464
isFreebuffSessionModelAvailable,
65+
freebuffModelUnavailableWindow,
66+
FREEBUFF_DEPLOYMENT_HOURS_LABEL,
6567
isFreebuffSessionModelId,
6668
isFreebuffTracedModelId,
6769
isFreebuffWebDeemphasizedModelId,
@@ -1823,3 +1825,45 @@ describe('Muse Spark rate-limit fallback', () => {
18231825
expect(MUSE_SPARK_FALLBACK_AFTER_MS).toBe(10_000)
18241826
})
18251827
})
1828+
1829+
describe('the unavailability window matches the reason for the closure', () => {
1830+
/**
1831+
* Both refusal sites gate on isFreebuffSessionModelAvailable, which covers
1832+
* `deployment_hours` AND `off_peak_only`, and both hardcoded the
1833+
* deployment-hours label. So V4 Flash -- closed 5pm-3am Pacific for DeepSeek's
1834+
* peak pricing -- told users it was "available 9am ET-5pm PT every day":
1835+
* a different window, for a different reason, in two timezones at once.
1836+
*/
1837+
const peak = new Date('2026-08-25T08:00:00Z')
1838+
1839+
test('a peak-closed model is told when it comes BACK, not our staffing hours', () => {
1840+
const window = freebuffModelUnavailableWindow(
1841+
FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID,
1842+
peak,
1843+
)
1844+
expect(window).toContain('again at')
1845+
// The bug: the staffing label has nothing to do with peak pricing.
1846+
expect(window).not.toBe(FREEBUFF_DEPLOYMENT_HOURS_LABEL)
1847+
expect(window).not.toContain('ET')
1848+
})
1849+
1850+
/**
1851+
* No model carries `deployment_hours` today -- the catalog is `always` and
1852+
* `off_peak_only` only -- so the staffing label is reachable from the
1853+
* LIMITED-OFFER branch and not from this one. The resolver still returns it
1854+
* as the default rather than inventing a window for a closure it does not
1855+
* recognise, which is why this asserts the DEFAULT rather than a model that
1856+
* would have to be invented to test it.
1857+
*/
1858+
test('an unrecognised closure falls back to the staffing label, not a guess', () => {
1859+
expect(freebuffModelUnavailableWindow('mimo/mimo-v2.5', peak)).toBe(
1860+
FREEBUFF_DEPLOYMENT_HOURS_LABEL,
1861+
)
1862+
})
1863+
1864+
test('it reads as a sentence in the template that renders it', () => {
1865+
// freebuffSession.ts renders `${model} is available ${availableHours}.`
1866+
const s = `X is available ${freebuffModelUnavailableWindow(FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID, peak)}.`
1867+
expect(s).toMatch(/^X is available again at .+\.$/)
1868+
})
1869+
})

common/src/constants/freebuff-models.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from '../util/zoned-time'
77
import {
88
deepSeekExpensiveWindowEndsAt,
9+
formatDeepSeekExpensiveWindowReturn,
910
isDeepSeekExpensiveWindow,
1011
} from './freebuff-peak-hours'
1112
import { mimoModels } from './model-config'
@@ -2920,6 +2921,27 @@ function isAvailableAt(
29202921
return isFreebuffDeploymentHours(now)
29212922
}
29222923

2924+
/**
2925+
* The window to quote when `model` is refused for being unavailable.
2926+
*
2927+
* Derived from the model's OWN availability instead of assumed. Both refusal
2928+
* sites in free-session/public-api.ts gate on isFreebuffSessionModelAvailable,
2929+
* which covers `deployment_hours` AND `off_peak_only`, but both hardcoded the
2930+
* deployment-hours label -- so a model closed for DeepSeek's peak window was
2931+
* quoted our staffing hours, a different window entirely.
2932+
*/
2933+
export function freebuffModelUnavailableWindow(
2934+
id: string,
2935+
now: Date = new Date(),
2936+
): string {
2937+
const model =
2938+
SUPPORTED_FREEBUFF_MODELS.find((candidate) => candidate.id === id) ??
2939+
getFreebuffWebModel(id)
2940+
return model.availability === 'off_peak_only'
2941+
? formatDeepSeekExpensiveWindowReturn(now)
2942+
: FREEBUFF_DEPLOYMENT_HOURS_LABEL
2943+
}
2944+
29232945
export function isFreebuffSessionModelAvailable(
29242946
id: string,
29252947
now: Date = new Date(),

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,30 @@ export function formatDeepSeekExpensiveWindowLocal(
124124
const [start, end] = DEEPSEEK_EXPENSIVE_WINDOW_UTC
125125
return `${atUtcHour(start)}${atUtcHour(end)}`
126126
}
127+
128+
/**
129+
* What to tell a user whose model is shut for the peak window.
130+
*
131+
* Phrased as WHEN IT COMES BACK rather than as a range, because that is the
132+
* question being asked. A range makes the reader do arithmetic against a clock
133+
* they cannot see -- and the range this replaced was not even the right window:
134+
* `model_unavailable` hardcoded FREEBUFF_DEPLOYMENT_HOURS_LABEL ("9am ET-5pm PT
135+
* every day"), which describes OUR STAFFING hours for limited-offer models and
136+
* has nothing to do with DeepSeek's peak pricing. A model closed 5pm-3am
137+
* Pacific was telling people it was open 9am-5pm.
138+
*
139+
* Local time, and one timezone. The old label mixed two ("9am ET-5pm PT"),
140+
* which cannot be read as an interval by anyone.
141+
*/
142+
export function formatDeepSeekExpensiveWindowReturn(
143+
on: Date = new Date(),
144+
timeZone?: string,
145+
): string {
146+
const ends = deepSeekExpensiveWindowEndsAt(on)
147+
const fmt = new Intl.DateTimeFormat(undefined, {
148+
hour: 'numeric',
149+
minute: '2-digit',
150+
...(timeZone ? { timeZone } : {}),
151+
})
152+
return `again at ${fmt.format(ends)}`
153+
}

0 commit comments

Comments
 (0)