diff --git a/eslint-rules/throw-error-baseline.json b/eslint-rules/throw-error-baseline.json index 0536a9d7..e95bfe53 100644 --- a/eslint-rules/throw-error-baseline.json +++ b/eslint-rules/throw-error-baseline.json @@ -220,13 +220,13 @@ "src/shared/claude-extension-setup.ts:247", "src/shared/claude-extension-setup.ts:257", "src/shared/claude-extension-setup.ts:317", - "src/shared/provider-preset-catalog.ts:325", - "src/shared/provider-preset-catalog.ts:328", - "src/shared/provider-preset-catalog.ts:337", - "src/shared/provider-preset-catalog.ts:340", - "src/shared/provider-preset-catalog.ts:343", - "src/shared/provider-preset-catalog.ts:348", + "src/shared/provider-preset-catalog.ts:339", + "src/shared/provider-preset-catalog.ts:342", "src/shared/provider-preset-catalog.ts:351", + "src/shared/provider-preset-catalog.ts:354", + "src/shared/provider-preset-catalog.ts:357", + "src/shared/provider-preset-catalog.ts:362", + "src/shared/provider-preset-catalog.ts:365", "src/shared/toml-object.ts:23", "src/targets/codex-adapter.ts:103", "src/targets/codex-adapter.ts:275", diff --git a/src/shared/provider-preset-catalog.ts b/src/shared/provider-preset-catalog.ts index b2072f2d..5880588a 100644 --- a/src/shared/provider-preset-catalog.ts +++ b/src/shared/provider-preset-catalog.ts @@ -25,6 +25,7 @@ export const PROVIDER_PRESET_IDS = [ 'ollama-cloud', 'novita', 'fireworks', + 'requesty', ] as const; export type ProviderPresetId = (typeof PROVIDER_PRESET_IDS)[number]; @@ -297,6 +298,19 @@ const RAW_PROVIDER_PRESET_DEFINITIONS: readonly ProviderPresetDefinition[] = [ requiresApiKey: true, badge: 'Anthropic-compatible', }, + { + id: 'requesty', + name: 'Requesty', + description: 'OpenAI-compatible LLM gateway (provider/model naming, e.g. openai/gpt-4o-mini)', + baseUrl: 'https://router.requesty.ai/v1', + defaultProfileName: 'requesty', + defaultModel: 'openai/gpt-4o-mini', + apiKeyPlaceholder: 'rqsty-sk-...', + apiKeyHint: 'Create an API key at app.requesty.ai/api-keys', + category: 'alternative', + requiresApiKey: true, + badge: 'OpenAI-compatible', + }, ]; function clonePresetDefinition(preset: ProviderPresetDefinition): ProviderPresetDefinition { diff --git a/tests/unit/api/provider-presets-requesty.test.ts b/tests/unit/api/provider-presets-requesty.test.ts new file mode 100644 index 00000000..5716c613 --- /dev/null +++ b/tests/unit/api/provider-presets-requesty.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from 'bun:test'; +import { getPresetById, isValidPresetId } from '../../../src/api/services/provider-presets'; +import { PROVIDER_PRESET_IDS } from '../../../src/shared/provider-preset-catalog'; + +describe('provider-presets-requesty', () => { + it('resolves requesty preset id', () => { + const preset = getPresetById('requesty'); + expect(preset?.id).toBe('requesty'); + expect(preset?.baseUrl).toBe('https://router.requesty.ai/v1'); + expect(preset?.defaultProfileName).toBe('requesty'); + }); + + it('registers requesty in PROVIDER_PRESET_IDS', () => { + expect(PROVIDER_PRESET_IDS).toContain('requesty'); + }); + + it('uses the OpenAI-compatible base URL with a /v1 suffix', () => { + const preset = getPresetById('requesty'); + expect(preset?.baseUrl).toBe('https://router.requesty.ai/v1'); + expect(preset?.baseUrl.endsWith('/v1')).toBe(true); + }); + + it('pins a provider/model default (openai/gpt-4o-mini)', () => { + const preset = getPresetById('requesty'); + expect(preset?.defaultModel).toBe('openai/gpt-4o-mini'); + }); + + it('validates requesty preset requires an API key', () => { + const preset = getPresetById('requesty'); + expect(preset?.requiresApiKey).toBe(true); + }); + + it('is a plain (non-featured) alternative provider', () => { + const preset = getPresetById('requesty'); + expect(preset?.category).toBe('alternative'); + expect(preset?.featured).toBeUndefined(); + }); + + it('treats requesty as a valid preset id', () => { + expect(isValidPresetId('requesty')).toBe(true); + }); + + it('handles whitespace in requesty preset id', () => { + const preset = getPresetById(' requesty '); + expect(preset?.id).toBe('requesty'); + }); + + it('handles uppercase requesty preset id', () => { + const preset = getPresetById('REQUESTY'); + expect(preset?.id).toBe('requesty'); + }); + + it('does not resolve partial or invalid requesty ids', () => { + expect(getPresetById('requesty-invalid')).toBeUndefined(); + expect(isValidPresetId('requesty-invalid')).toBe(false); + }); +});