Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/components/settings/provider-presets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<img
alt="Requesty"
src="https://models.dev/logos/requesty.svg"
width={size}
height={size}
className="dark:invert"
/>
);
}

// ---------------------------------------------------------------------------
// Brand icon resolver
// ---------------------------------------------------------------------------
Expand All @@ -36,6 +52,7 @@ import XAI from "@lobehub/icons/es/XAI";
*/
const ICON_BY_KEY: Record<ProviderIconKey, ReactNode> = {
openrouter: <OpenRouter size={18} />,
requesty: <RequestyIcon size={18} />,
zhipu: <Zhipu size={18} />,
kimi: <Kimi size={18} />,
moonshot: <Moonshot size={18} />,
Expand Down Expand Up @@ -92,6 +109,7 @@ function resolveIcon(iconKey: string): ReactNode {
const ICON_MAP: Record<string, ReactNode> = {
anthropic: <Anthropic size={18} />,
openrouter: <OpenRouter size={18} />,
requesty: <RequestyIcon size={18} />,
zhipu: <Zhipu size={18} />,
kimi: <Kimi size={18} />,
moonshot: <Moonshot size={18} />,
Expand Down
40 changes: 40 additions & 0 deletions src/lib/provider-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
5 changes: 5 additions & 0 deletions src/lib/provider-icon-rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

export type ProviderIconKey =
| "openrouter"
| "requesty"
| "zhipu"
| "kimi"
| "moonshot"
Expand All @@ -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
Expand Down
Loading