88 lines
3.5 KiB
TypeScript
88 lines
3.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { parseModelLoadWait, providerWaitText } from './provider-wait'
|
|
|
|
// The load-notice string is minted by the backend
|
|
// (agent/chat_completion_helpers._managed_local_load_notice) and parsed
|
|
// here — these tests pin the desktop side of that cross-language contract
|
|
// (the backend pins its side in tests/hermes_cli/test_load_progress.py).
|
|
describe('providerWaitText', () => {
|
|
it('accepts the managed-local load frame', () => {
|
|
const frame = '⏳ loading Qwen3.6-35B-A3B-UD-Q4_K_M into memory — 42% (responses start once the model is loaded)'
|
|
|
|
expect(providerWaitText(frame)).toBe(frame)
|
|
})
|
|
|
|
it('still accepts classic wait frames and rejects spinner noise', () => {
|
|
expect(providerWaitText('⏳ waiting on local-model — 30s with no output yet')).not.toBe('')
|
|
expect(providerWaitText('◉_◉ cogitating...')).toBe('')
|
|
})
|
|
|
|
it('accepts the near-deadline update minted by agent/chat_completion_wait_notice.wait_notice_text', () => {
|
|
// Exact output of wait_notice_text('gpt-5.5', 290, 'first_event', ('TTFB', 10)); rejecting it
|
|
// left the status row on the stale first notice until the reconnect.
|
|
const frame =
|
|
'⏳ still waiting on gpt-5.5 — 290s waiting for the first provider event (auto-reconnect: TTFB watchdog in 10s)'
|
|
|
|
expect(providerWaitText(frame)).toBe(frame)
|
|
})
|
|
})
|
|
|
|
describe('providerWaitText accepts retry and auto-recovery frames', () => {
|
|
// Minted by agent/turn_recovery.py (retry backoff) and
|
|
// agent/turn_recovery_autorecover.ladder_notice (outage ladder). Rejecting
|
|
// them left a bare timer for minutes while the backend slept between retries.
|
|
it.each([
|
|
'⏳ rate limited — resets in 2m, retrying in 30s (attempt 2/3)',
|
|
'⏳ provider overloaded — resets in 1m, retrying in 15s (attempt 1/3)',
|
|
'⏳ waiting on provider — retrying in 6s (attempt 1/3)',
|
|
'⏳ Provider temporarily unavailable — retrying automatically in 18s (cycle 1/5); press Esc to stop'
|
|
])('%s', frame => {
|
|
expect(providerWaitText(frame)).toBe(frame)
|
|
})
|
|
})
|
|
|
|
describe('parseModelLoadWait', () => {
|
|
it('extracts model and percent from a load frame', () => {
|
|
expect(
|
|
parseModelLoadWait(
|
|
'⏳ loading Qwen3.6-35B-A3B-UD-Q4_K_M into memory — 42% (responses start once the model is loaded)'
|
|
)
|
|
).toEqual({ kind: 'load', model: 'Qwen3.6-35B-A3B-UD-Q4_K_M', percent: 42 })
|
|
})
|
|
|
|
it('extracts the percent from a prefill frame', () => {
|
|
expect(parseModelLoadWait('⚙ processing prompt — 31%')).toEqual({
|
|
kind: 'prefill',
|
|
model: '',
|
|
percent: 31
|
|
})
|
|
})
|
|
|
|
it('parses a percentless prefill frame with a null percent (no fake bar)', () => {
|
|
expect(parseModelLoadWait('⚙ processing prompt')).toEqual({
|
|
kind: 'prefill',
|
|
model: '',
|
|
percent: null
|
|
})
|
|
})
|
|
|
|
it('returns null for every other wait frame', () => {
|
|
expect(parseModelLoadWait('⏳ waiting on qwen — 30s with no output yet')).toBeNull()
|
|
expect(parseModelLoadWait('⚠ no output from provider for 900s — reconnecting...')).toBeNull()
|
|
expect(parseModelLoadWait('')).toBeNull()
|
|
})
|
|
|
|
it('clamps out-of-range percents', () => {
|
|
expect(parseModelLoadWait('⏳ loading m into memory — 999%')?.percent).toBe(100)
|
|
})
|
|
})
|
|
|
|
describe('providerWaitText accepts prefill frames', () => {
|
|
it('passes the ⚙ processing-prompt frame through', () => {
|
|
const frame = '⚙ processing prompt — 31%'
|
|
|
|
expect(providerWaitText(frame)).toBe(frame)
|
|
})
|
|
})
|