From 6d47fa0b3bb39b28dda80cade6e68d28976c2c0b Mon Sep 17 00:00:00 2001 From: Thibault Jaigu Date: Thu, 23 Jul 2026 13:53:32 +0100 Subject: [PATCH] feat: add Requesty as an OpenAI-compatible provider --- src/components/settings/provider-presets.tsx | 18 +++++++++ src/lib/provider-catalog.ts | 40 ++++++++++++++++++++ src/lib/provider-icon-rule.ts | 5 +++ 3 files changed, 63 insertions(+) diff --git a/src/components/settings/provider-presets.tsx b/src/components/settings/provider-presets.tsx index d726871b..9299472f 100644 --- a/src/components/settings/provider-presets.tsx +++ b/src/components/settings/provider-presets.tsx @@ -25,6 +25,22 @@ import Ollama from "@lobehub/icons/es/Ollama"; import OpenAI from "@lobehub/icons/es/OpenAI"; import XAI from "@lobehub/icons/es/XAI"; +// Requesty has no brand icon in @lobehub/icons yet, so mirror the approach the +// model-selector already uses for the `requesty` provider and render its logo +// from models.dev. Same `size` prop shape as the lobehub icon components so the +// two icon maps below can treat it uniformly. +function RequestyIcon({ size = 18 }: { size?: number }): ReactNode { + return ( + Requesty + ); +} + // --------------------------------------------------------------------------- // Brand icon resolver // --------------------------------------------------------------------------- @@ -36,6 +52,7 @@ import XAI from "@lobehub/icons/es/XAI"; */ const ICON_BY_KEY: Record = { openrouter: , + requesty: , zhipu: , kimi: , moonshot: , @@ -92,6 +109,7 @@ function resolveIcon(iconKey: string): ReactNode { const ICON_MAP: Record = { anthropic: , openrouter: , + requesty: , zhipu: , kimi: , moonshot: , diff --git a/src/lib/provider-catalog.ts b/src/lib/provider-catalog.ts index 2efe170c..d0bcbe07 100644 --- a/src/lib/provider-catalog.ts +++ b/src/lib/provider-catalog.ts @@ -720,6 +720,46 @@ export const VENDOR_PRESETS: VendorPreset[] = [ }, }, + // ── Requesty ── + // Requesty is an OpenAI-compatible model router (like OpenRouter, but its + // gateway speaks the OpenAI Chat Completions wire protocol at + // `https://router.requesty.ai/v1`). It therefore rides the generic + // `openai-compatible` path — CodePilot / Codex runtimes via the AI SDK chat + // transport — rather than OpenRouter's Anthropic-skin `protocol: 'openrouter'`. + // Because the base URL ends in `/v1` and the preset is not catalog-only, the + // "搜索并添加模型" dialog reaches Requesty's `/v1/models` through the generic + // reliable-provider probe (canReliablyFetchModels / canSearchUpstreamModels), + // so no dedicated catalog fetcher is needed. `provider/model` naming matches + // OpenRouter. `defaultModels` is an intentional alias-only bootstrap (a few + // popular SKUs); user-added models are normal usage, not drift — so no + // `fixedCatalog`/`modelDiscoveryMode` gate. + { + key: 'requesty', + name: 'Requesty', + description: 'Use Requesty to access multiple models', + descriptionZh: '通过 Requesty 访问多种模型', + protocol: 'openai-compatible', + authStyle: 'api_key', + baseUrl: 'https://router.requesty.ai/v1', + defaultEnvOverrides: {}, + defaultModels: [ + { modelId: 'openai/gpt-4o-mini', displayName: 'GPT-4o mini', role: 'default', capabilities: { toolUse: true, vision: true } }, + { modelId: 'openai/gpt-4o', displayName: 'GPT-4o', capabilities: { toolUse: true, vision: true } }, + { modelId: 'anthropic/claude-sonnet-4-5', displayName: 'Claude Sonnet 4.5', capabilities: { toolUse: true, vision: true, reasoning: true } }, + { modelId: 'google/gemini-2.5-flash', displayName: 'Gemini 2.5 Flash', capabilities: { toolUse: true, vision: true } }, + { modelId: 'deepseek/deepseek-chat', displayName: 'DeepSeek Chat', capabilities: { toolUse: true } }, + ], + defaultRoleModels: { default: 'openai/gpt-4o-mini' }, + fields: ['api_key'], + iconKey: 'requesty', + meta: { + apiKeyUrl: 'https://app.requesty.ai/api-keys', + docsUrl: 'https://docs.requesty.ai', + pricingUrl: 'https://app.requesty.ai/router/list', + billingModel: 'pay_as_you_go', + }, + }, + // ── Zhipu GLM (China) ── { key: 'glm-cn', diff --git a/src/lib/provider-icon-rule.ts b/src/lib/provider-icon-rule.ts index 5d92cab5..f969667f 100644 --- a/src/lib/provider-icon-rule.ts +++ b/src/lib/provider-icon-rule.ts @@ -15,6 +15,7 @@ export type ProviderIconKey = | "openrouter" + | "requesty" | "zhipu" | "kimi" | "moonshot" @@ -39,6 +40,10 @@ export function getProviderIconKey(name: string, baseUrl: string): ProviderIconK const url = baseUrl.toLowerCase(); if (lower.includes("openrouter")) return "openrouter"; + // Requesty — OpenAI-compatible model router. Scoped to brand-unique + // fragments (`requesty.ai` host / `requesty` name) so it can't steal + // another vendor's icon. + if (url.includes("requesty.ai") || lower.includes("requesty")) return "requesty"; // OpenCode Go: the provider name carries a protocol suffix — "OpenCode Go // (OpenAI)" / "(Anthropic)". Match BEFORE the openai / anthropic name // matchers below, which would otherwise steal the wrong brand logo