diff --git a/src/renderer/src/settings/pages/model/__tests__/preset-brands.test.ts b/src/renderer/src/settings/pages/model/__tests__/preset-brands.test.ts index 5ead2c72..102859a6 100644 --- a/src/renderer/src/settings/pages/model/__tests__/preset-brands.test.ts +++ b/src/renderer/src/settings/pages/model/__tests__/preset-brands.test.ts @@ -1,9 +1,9 @@ /** - * 47 个内置预设各自该显示**谁**的 logo —— 一张钉死的对照表。 + * 48 个内置预设各自该显示**谁**的 logo —— 一张钉死的对照表。 * * **为什么值得单独一个文件:** `brands.test.ts` 守的是规则表本身自洽 * (每个牌子有样本、顺序不打架),但它不知道「添加供应商」目录里到底摆着 - * 哪 47 个名字。而两张表是**分开演进**的:presets.ts 加一家、或者给某家改个 + * 哪 48 个名字。而两张表是**分开演进**的:presets.ts 加一家、或者给某家改个 * 更好听的名字,`brands.ts` 一个字都不用动就能悄悄错掉一个 logo。 * * ★★ 这不是假想。这张表第一次跑出来时,43 家里 **13 家没有图标、3 家挂着 @@ -24,8 +24,8 @@ import { resolveBrand, type Brand } from '../../../../components/brand/brands' /** * `null` = 我们**故意**不给它图标,退回首字母。 - * 三家都是 lobehub 根本没收字形的(逐个在 `@lobehub/icons-static-svg/icons/` - * 下找过):OhMyGPT、LocalAI,以及 llama.cpp —— 最后这个是 ggerganov 的 + * 四家都是 lobehub 根本没收字形的(逐个在 `@lobehub/icons-static-svg/icons/` + * 下找过):Requesty、OhMyGPT、LocalAI,以及 llama.cpp —— 最后这个是 ggerganov 的 * 独立项目,不是 Meta 的东西。 * * ★ 编一个「差不多的」图标比没有图标更糟:用户看见 Meta 的 ∞ 会以为 @@ -82,6 +82,7 @@ const EXPECTED: Readonly> = { // `brands.ts` 的 `^routin(-plan)?$` 是给用户改名之后兜底的第二条路。 'routin-plan': 'routin', openrouter: 'openrouter', + requesty: null, siliconflow: 'siliconcloud', 'siliconflow-intl': 'siliconcloud', together: 'together', @@ -124,10 +125,10 @@ describe('内置预设的品牌图标', () => { * 「null 只有这么几家」:某天规则表改窄了让一堆家一起掉进 null, * 这里会红,而界面上只会安静地多出几个字母。 */ - it('没有图标的就是这三家,不多不少', () => { + it('没有图标的就是这四家,不多不少', () => { const blank = PROVIDER_PRESETS.filter((p) => resolveBrand(p.name, p.id) === null).map( (p) => p.id ) - expect(blank).toEqual(['ohmygpt', 'llamacpp', 'localai']) + expect(blank).toEqual(['requesty', 'ohmygpt', 'llamacpp', 'localai']) }) }) diff --git a/src/shared/domain/__tests__/presets.test.ts b/src/shared/domain/__tests__/presets.test.ts index 652a1a90..ddc7c258 100644 --- a/src/shared/domain/__tests__/presets.test.ts +++ b/src/shared/domain/__tests__/presets.test.ts @@ -254,6 +254,7 @@ describe('预设表 × joinUpstreamUrl:拼出来的 URL 没有畸形', () => { ['groq', 'openai-chat', 'https://api.groq.com/openai/v1/chat/completions'], ['fireworks', 'anthropic', 'https://api.fireworks.ai/inference/v1/messages'], ['openrouter', 'anthropic', 'https://openrouter.ai/api/v1/messages'], + ['requesty', 'anthropic', 'https://router.requesty.ai/v1/messages'], ['kimi-coding', 'anthropic', 'https://api.kimi.com/coding/v1/messages'], ['kimi-coding', 'openai-chat', 'https://api.kimi.com/coding/v1/chat/completions'], ['routin-plan', 'openai-responses', 'https://api.routin.ai/plan/v1/responses'], @@ -344,12 +345,12 @@ describe('预设表 · 模型列表', () => { }) }) - it('实测免鉴权的那四家标了 modelListPublic', () => { + it('实测免鉴权的那五家标了 modelListPublic', () => { const publics = PROVIDER_PRESETS.filter((p) => p.endpoints.some((e) => e.modelListPublic === true) ).map((p) => p.id) expect(new Set(publics)).toEqual( - new Set(['openrouter', 'deepinfra', 'opencode-go', 'ollama-cloud']) + new Set(['openrouter', 'requesty', 'deepinfra', 'opencode-go', 'ollama-cloud']) ) }) }) diff --git a/src/shared/domain/baseurl.ts b/src/shared/domain/baseurl.ts index b5e3efc1..e3ecdf65 100644 --- a/src/shared/domain/baseurl.ts +++ b/src/shared/domain/baseurl.ts @@ -46,7 +46,7 @@ export function joinUpstreamUrl(baseUrl: string, path: string): string { * * | 形状 | 厂商 | * |---|---| - * | `/v1` | OpenAI、Moonshot、MiniMax、混元、StepFun、百川、Mistral、xAI、Together、硅基流动 | + * | `/v1` | OpenAI、Moonshot、MiniMax、混元、StepFun、百川、Mistral、xAI、Together、硅基流动、Requesty | * | 别的前缀 | Groq `/openai/v1`、Fireworks `/inference/v1`、**DeepInfra `/v1/openai`**、OpenRouter `/api/v1`、**智谱 `/api/paas/v4`**、**火山 `/api/v3`**、百炼 `/compatible-mode/v1`、**千帆 `/v2`**、Cohere `/compatibility/v1` | * | 尾斜杠 | **Gemini `/v1beta/openai/`** | * diff --git a/src/shared/domain/presets.ts b/src/shared/domain/presets.ts index 26cdcbff..de3555f6 100644 --- a/src/shared/domain/presets.ts +++ b/src/shared/domain/presets.ts @@ -881,6 +881,24 @@ export const PROVIDER_PRESETS: readonly ProviderPreset[] = [ notes: '模型 ID 带 vendor/ 前缀。模型列表免鉴权就能拉 —— 没填 key 也能先看有哪些模型。', verification: 'probed' }, + { + id: 'requesty', + name: 'Requesty', + category: 'aggregator', + endpoints: [ + oa('https://router.requesty.ai/v1', true, true), + resp('https://router.requesty.ai/v1', true), + anth('https://router.requesty.ai') + ], + docsUrl: 'https://docs.requesty.ai', + apiKeyUrl: 'https://app.requesty.ai/api-keys', + suggestedModels: ['claude-fable-5.1', 'kimi-k3', 'glm-5.3'], + notes: + '模型 ID 可用 vendor/model 形式(如 openai/gpt-4o-mini),也可用托管路由的短名(如 claude-fable-5.1)。' + + '模型列表免鉴权就能拉,没填 key 也能先看有哪些模型。' + + '欧盟区把地址换成 router.eu.requesty.ai 即可,同一个 key 通用。', + verification: 'probed' + }, { id: 'siliconflow', name: '硅基流动(国内)',