Improve provider UX and DeepSeek fallback handling

---
Build: pass | Tests: pass — 68 passed (3 files)

---
Build: pass | Tests: pass — Tests  2035 passed (2035)

---
Build: pass | Tests: pass — Tests  2035 passed (2035)
This commit is contained in:
Operator & Codex 2026-04-29 01:22:24 +02:00
parent d4cbafc88f
commit 8f87bc86a7
2 changed files with 50 additions and 1 deletions

View file

@ -108,6 +108,32 @@ describe('parseProviderCapError', () => {
expect(parsed?.until.getTime()).toBe(now.getTime() + 300 * 1000);
});
it('detects DeepSeek insufficient balance as a long cooldown', () => {
const now = new Date('2026-04-29T01:00:00Z');
const parsed = parseProviderCapError(
'{"error":{"code":"insufficient_balance","message":"Balance not enough"}}',
now,
300,
);
expect(parsed?.provider).toBe('deepseek');
expect(parsed?.until.getTime()).toBe(
now.getTime() + 24 * 60 * 60 * 1000,
);
expect(parsed?.reason).toContain('insufficient_balance');
});
it('detects DeepSeek rate limits with the default cooldown', () => {
const now = new Date('2026-04-29T01:00:00Z');
const parsed = parseProviderCapError(
'{"error":{"code":"rate_limit_exceeded","message":"Too many requests"}}',
now,
300,
);
expect(parsed?.provider).toBe('deepseek');
expect(parsed?.until.getTime()).toBe(now.getTime() + 300 * 1000);
expect(parsed?.reason).toBe('DeepSeek 429 rate limit');
});
it('returns null for unrelated errors', () => {
expect(parseProviderCapError('connection refused')).toBeNull();
expect(parseProviderCapError('429 Too Many Requests')).toBeNull();

View file

@ -111,7 +111,7 @@ describe('command context', () => {
});
describe('augmentPickerModels', () => {
it('adds deepseek-chat for the DeepSeek picker when catalog only has v4 models', () => {
it('adds deepseek-chat for the DeepSeek picker and sorts stable above experimental', () => {
const models = augmentPickerModels('deepseek', [
{
id: 'deepseek-v4-flash',
@ -140,6 +140,11 @@ describe('augmentPickerModels', () => {
'deepseek-v4-flash',
'deepseek-v4-pro',
]);
expect(models.map((m) => m.name)).toEqual([
'✓ DeepSeek Chat',
'⚠ DeepSeek V4 Flash',
'⚠ DeepSeek V4 Pro',
]);
});
it('does not duplicate deepseek-chat when it is already present', () => {
@ -158,6 +163,24 @@ describe('augmentPickerModels', () => {
expect(models).toHaveLength(1);
expect(models[0]?.id).toBe('deepseek-chat');
expect(models[0]?.name).toBe('✓ DeepSeek Chat');
});
it('hides deprecated deepseek-reasoner from the picker', () => {
const models = augmentPickerModels('deepseek', [
{
id: 'deepseek-reasoner',
provider: 'deepseek',
parent: null,
name: 'deepseek-reasoner',
context_length: null,
pricing_in: null,
pricing_out: null,
free_tier: false,
},
]);
expect(models.map((m) => m.id)).toEqual(['deepseek-chat']);
});
it('leaves non-DeepSeek providers unchanged', () => {