Files
hermes-agent/apps/desktop/src/store/provider-wait.test.ts

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)
})
})