From 27c1ae8c95167f6bfc7ef896c433d8bf6b66b83e Mon Sep 17 00:00:00 2001 From: Thibault Jaigu Date: Fri, 25 Sep 2026 13:11:45 +0100 Subject: [PATCH] feat(ai): add Requesty provider --- .../app-expo/src/components/onboarding/steps/AIPage.tsx | 1 + .../app-expo/src/screens/settings/ai/EndpointEditor.tsx | 1 + packages/app/src/components/onboarding/steps/AIPage.tsx | 1 + packages/app/src/components/settings/AISettings.tsx | 1 + packages/core/src/types/chat.ts | 1 + packages/core/src/utils/api.test.ts | 8 ++++++++ packages/core/src/utils/api.ts | 9 +++++++++ 7 files changed, 22 insertions(+) diff --git a/packages/app-expo/src/components/onboarding/steps/AIPage.tsx b/packages/app-expo/src/components/onboarding/steps/AIPage.tsx index d16844d7b..ad2c47721 100644 --- a/packages/app-expo/src/components/onboarding/steps/AIPage.tsx +++ b/packages/app-expo/src/components/onboarding/steps/AIPage.tsx @@ -36,6 +36,7 @@ const PROVIDER_OPTIONS: { id: AIProviderType; name: string }[] = [ { id: "ollama", name: "Ollama" }, { id: "lmstudio", name: "LM Studio" }, { id: "openrouter", name: "OpenRouter" }, + { id: "requesty", name: "Requesty" }, { id: "siliconflow", name: "SiliconFlow" }, { id: "custom", name: "Custom" }, ]; diff --git a/packages/app-expo/src/screens/settings/ai/EndpointEditor.tsx b/packages/app-expo/src/screens/settings/ai/EndpointEditor.tsx index 07b5e6472..1992e2c17 100644 --- a/packages/app-expo/src/screens/settings/ai/EndpointEditor.tsx +++ b/packages/app-expo/src/screens/settings/ai/EndpointEditor.tsx @@ -32,6 +32,7 @@ const PROVIDERS: { id: AIProviderType; label: string }[] = [ { id: "ollama", label: "Ollama" }, { id: "lmstudio", label: "LM Studio" }, { id: "openrouter", label: "OpenRouter" }, + { id: "requesty", label: "Requesty" }, { id: "siliconflow", label: "SiliconFlow" }, { id: "moonshot", label: "Moonshot" }, { id: "zhipu", label: "智谱 GLM" }, diff --git a/packages/app/src/components/onboarding/steps/AIPage.tsx b/packages/app/src/components/onboarding/steps/AIPage.tsx index 6e31f59d8..72a730aa3 100644 --- a/packages/app/src/components/onboarding/steps/AIPage.tsx +++ b/packages/app/src/components/onboarding/steps/AIPage.tsx @@ -28,6 +28,7 @@ const PROVIDER_OPTIONS: { id: AIProviderType; name: string }[] = [ { id: "ollama", name: "Ollama" }, { id: "lmstudio", name: "LM Studio" }, { id: "openrouter", name: "OpenRouter" }, + { id: "requesty", name: "Requesty" }, { id: "siliconflow", name: "SiliconFlow" }, { id: "custom", name: "Custom" }, ]; diff --git a/packages/app/src/components/settings/AISettings.tsx b/packages/app/src/components/settings/AISettings.tsx index f406c09af..8d57d9f78 100644 --- a/packages/app/src/components/settings/AISettings.tsx +++ b/packages/app/src/components/settings/AISettings.tsx @@ -41,6 +41,7 @@ function useProviderOptions(): { value: AIProviderType; label: string }[] { { value: "ollama", label: "Ollama" }, { value: "lmstudio", label: "LM Studio" }, { value: "openrouter", label: "OpenRouter" }, + { value: "requesty", label: "Requesty" }, { value: "siliconflow", label: "SiliconFlow" }, { value: "moonshot", label: "Moonshot (Kimi)" }, { value: "zhipu", label: t("settings.ai_provider_zhipu") }, diff --git a/packages/core/src/types/chat.ts b/packages/core/src/types/chat.ts index 22c1668ee..cce960c3e 100644 --- a/packages/core/src/types/chat.ts +++ b/packages/core/src/types/chat.ts @@ -131,6 +131,7 @@ export type AIProviderType = | "ollama" | "lmstudio" | "openrouter" + | "requesty" | "siliconflow" | "moonshot" | "zhipu" diff --git a/packages/core/src/utils/api.test.ts b/packages/core/src/utils/api.test.ts index a97fb4b45..4fd797b40 100644 --- a/packages/core/src/utils/api.test.ts +++ b/packages/core/src/utils/api.test.ts @@ -65,6 +65,8 @@ describe("AI API URL helpers", () => { it("respects providers that should not auto-append /v1", () => { expect(getDefaultBaseUrl("atlascloud")).toBe("https://api.atlascloud.ai/v1"); expect(resolveProviderBaseUrl("atlascloud")).toBe("https://api.atlascloud.ai/v1"); + expect(getDefaultBaseUrl("requesty")).toBe("https://router.requesty.ai/v1"); + expect(resolveProviderBaseUrl("requesty")).toBe("https://router.requesty.ai/v1"); expect(resolveProviderBaseUrl("anthropic", "https://api.anthropic.com")).toBe( "https://api.anthropic.com", ); @@ -78,6 +80,7 @@ describe("AI API URL helpers", () => { "https://api.openai.com/v1/models", ); expect(buildProviderModelsUrl("atlascloud")).toBe("https://api.atlascloud.ai/v1/models"); + expect(buildProviderModelsUrl("requesty")).toBe("https://router.requesty.ai/v1/models"); expect(buildProviderModelsUrl("ollama", "http://localhost:11434")).toBe( "http://localhost:11434/api/tags", ); @@ -90,6 +93,11 @@ describe("AI API URL helpers", () => { expect(detectProviderFromUrl("https://api.atlascloud.ai/v1")).toBe("atlascloud"); }); + it("detects Requesty endpoints", () => { + expect(detectProviderFromUrl("https://router.requesty.ai/v1")).toBe("requesty"); + expect(detectProviderFromUrl("https://router.eu.requesty.ai/v1")).toBe("requesty"); + }); + describe("ensureUrlProtocol / scheme-less inputs", () => { it("prepends https:// when a remote URL has no scheme", () => { expect(ensureUrlProtocol("api.openai.com/v1")).toBe("https://api.openai.com/v1"); diff --git a/packages/core/src/utils/api.ts b/packages/core/src/utils/api.ts index 26d4d1142..f53154441 100644 --- a/packages/core/src/utils/api.ts +++ b/packages/core/src/utils/api.ts @@ -89,6 +89,14 @@ export const PROVIDER_CONFIGS: Record = { placeholder: "https://openrouter.ai/api/v1", keyPlaceholder: "sk-or-...", }, + requesty: { + id: "requesty", + name: "Requesty", + defaultBaseUrl: "https://router.requesty.ai/v1", + needsV1Suffix: false, + placeholder: "https://router.requesty.ai/v1", + keyPlaceholder: "rqsty-...", + }, siliconflow: { id: "siliconflow", name: "SiliconFlow", @@ -552,6 +560,7 @@ export function detectProviderFromUrl(url: string): string { if (urlLower.includes("localhost:11434")) return "ollama"; if (urlLower.includes("localhost:1234")) return "lmstudio"; if (urlLower.includes("openrouter.ai")) return "openrouter"; + if (urlLower.includes("requesty.ai")) return "requesty"; if (urlLower.includes("siliconflow.cn")) return "siliconflow"; if (urlLower.includes("moonshot.cn")) return "moonshot"; if (urlLower.includes("bigmodel.cn")) return "zhipu";