diff --git a/src/main/config-health.ts b/src/main/config-health.ts index 72bfdb217..0fb3bfe04 100644 --- a/src/main/config-health.ts +++ b/src/main/config-health.ts @@ -563,6 +563,7 @@ const DRIFT_FIELDS: ReadonlyArray<{ label: "ANTHROPIC_API_KEY (.env)", }, { source: "env", field: "OPENAI_API_KEY", label: "OPENAI_API_KEY (.env)" }, + { source: "env", field: "EVOLINK_API_KEY", label: "EVOLINK_API_KEY (.env)" }, { source: "env", field: "DEEPSEEK_API_KEY", diff --git a/src/main/default-models.ts b/src/main/default-models.ts index df4acb08a..12a8eab94 100644 --- a/src/main/default-models.ts +++ b/src/main/default-models.ts @@ -42,6 +42,14 @@ const DEFAULT_MODELS: DefaultModel[] = [ baseUrl: "", }, + // ── EvoLink (OpenAI-compatible gateway) ──────────────────────────── + { + name: "EvoLink GPT-5.2", + provider: "custom", + model: "gpt-5.2", + baseUrl: "https://direct.evolink.ai/v1", + }, + // ── Ollama Cloud ───────────────────────────────────────────────── { name: "glm-5.1", @@ -93,7 +101,6 @@ const DEFAULT_MODELS: DefaultModel[] = [ model: "Qwen/Qwen3-235B-A22B-Instruct-2507", baseUrl: "", }, - ]; export default DEFAULT_MODELS; diff --git a/src/main/hermes.ts b/src/main/hermes.ts index 6f618af06..c2ce9b168 100644 --- a/src/main/hermes.ts +++ b/src/main/hermes.ts @@ -2137,6 +2137,7 @@ function sendMessageViaCli( "OPENAI_API_KEY", "OLLAMA_API_KEY", "AIMLAPI_API_KEY", + "EVOLINK_API_KEY", "ANTHROPIC_API_KEY", "GROQ_API_KEY", "DEEPSEEK_API_KEY", diff --git a/src/main/installer.ts b/src/main/installer.ts index 469112228..0dbe32a8b 100644 --- a/src/main/installer.ts +++ b/src/main/installer.ts @@ -305,6 +305,7 @@ const PROVIDER_ENV_KEYS: Record = { openai: "OPENAI_API_KEY", "ollama-cloud": "OLLAMA_API_KEY", aimlapi: "AIMLAPI_API_KEY", + evolink: "EVOLINK_API_KEY", google: "GOOGLE_API_KEY", xai: "XAI_API_KEY", groq: "GROQ_API_KEY", @@ -341,6 +342,7 @@ const URL_TO_ENV_KEY: Array<[RegExp, string]> = [ [/openai\.com/i, "OPENAI_API_KEY"], [/(^|\/\/|\.)ollama\.com(?=\/|:|$)/i, "OLLAMA_API_KEY"], [/api\.aimlapi\.com/i, "AIMLAPI_API_KEY"], + [/direct\.evolink\.ai/i, "EVOLINK_API_KEY"], [/huggingface\.co/i, "HF_TOKEN"], [/api\.groq\.com/i, "GROQ_API_KEY"], [/api\.deepseek\.com/i, "DEEPSEEK_API_KEY"], diff --git a/src/renderer/src/constants.ts b/src/renderer/src/constants.ts index af82fe61a..707188958 100644 --- a/src/renderer/src/constants.ts +++ b/src/renderer/src/constants.ts @@ -139,6 +139,18 @@ export const PROVIDERS = { baseUrl: "https://api.aimlapi.com/v1", needsKey: true, }, + { + id: "evolink", + name: "EvoLink", + desc: "OpenAI-compatible gateway", + tag: "", + envKey: "EVOLINK_API_KEY", + url: "https://evolink.ai/dashboard/keys", + placeholder: "sk-...", + configProvider: "custom", + baseUrl: "https://direct.evolink.ai/v1", + needsKey: true, + }, { id: "openai", name: "constants.openaiName", @@ -492,6 +504,12 @@ export const SETTINGS_SECTIONS: SectionDef[] = [ type: "password", hint: "constants.aimlapiHint", }, + { + key: "EVOLINK_API_KEY", + label: "EvoLink API key", + type: "password", + hint: "https://direct.evolink.ai/v1", + }, { key: "ANTHROPIC_API_KEY", label: "constants.anthropicApiKey", diff --git a/src/shared/url-key-map.ts b/src/shared/url-key-map.ts index 26ffae269..f21c608d5 100644 --- a/src/shared/url-key-map.ts +++ b/src/shared/url-key-map.ts @@ -28,6 +28,7 @@ export const URL_KEY_MAP: ReadonlyArray = [ { pattern: /openai\.com/i, envKey: "OPENAI_API_KEY" }, { pattern: /ollama\.com/i, envKey: "OLLAMA_API_KEY" }, { pattern: /api\.aimlapi\.com/i, envKey: "AIMLAPI_API_KEY" }, + { pattern: /direct\.evolink\.ai/i, envKey: "EVOLINK_API_KEY" }, { pattern: /huggingface\.co/i, envKey: "HF_TOKEN" }, { pattern: /api\.groq\.com/i, envKey: "GROQ_API_KEY" }, { pattern: /api\.deepseek\.com/i, envKey: "DEEPSEEK_API_KEY" }, diff --git a/tests/constants.test.ts b/tests/constants.test.ts index 8bf586aa3..80e5e78f5 100644 --- a/tests/constants.test.ts +++ b/tests/constants.test.ts @@ -63,6 +63,17 @@ describe("PROVIDERS", () => { } }); + it("configures EvoLink through the custom OpenAI-compatible endpoint path", () => { + expect( + PROVIDERS.setup.find((entry) => entry.id === "evolink"), + ).toMatchObject({ + envKey: "EVOLINK_API_KEY", + configProvider: "custom", + baseUrl: "https://direct.evolink.ai/v1", + needsKey: true, + }); + }); + it("no duplicate option values", () => { const values = PROVIDERS.options.map((o) => o.value); expect(new Set(values).size).toBe(values.length); @@ -188,6 +199,7 @@ describe("SETTINGS_SECTIONS", () => { expect(allKeys).toContain("XAI_API_KEY"); expect(allKeys).toContain("XIAOMI_API_KEY"); expect(allKeys).toContain("AIMLAPI_API_KEY"); + expect(allKeys).toContain("EVOLINK_API_KEY"); }); it("includes existing keys (backward compat)", () => { diff --git a/tests/default-models.test.ts b/tests/default-models.test.ts new file mode 100644 index 000000000..59518b3bd --- /dev/null +++ b/tests/default-models.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from "vitest"; +import DEFAULT_MODELS from "../src/main/default-models"; + +describe("DEFAULT_MODELS", () => { + it("seeds EvoLink through the custom OpenAI-compatible endpoint path", () => { + expect(DEFAULT_MODELS).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: "EvoLink GPT-5.2", + provider: "custom", + model: "gpt-5.2", + baseUrl: "https://direct.evolink.ai/v1", + }), + ]), + ); + }); +}); diff --git a/tests/install-gate-providers.test.ts b/tests/install-gate-providers.test.ts index 2c25add7c..781395b45 100644 --- a/tests/install-gate-providers.test.ts +++ b/tests/install-gate-providers.test.ts @@ -19,6 +19,7 @@ describe("expectedEnvKeyForModel — provider-name lookup", () => { ["openai", "OPENAI_API_KEY"], ["ollama-cloud", "OLLAMA_API_KEY"], ["aimlapi", "AIMLAPI_API_KEY"], + ["evolink", "EVOLINK_API_KEY"], ["google", "GOOGLE_API_KEY"], ["xai", "XAI_API_KEY"], ["deepseek", "DEEPSEEK_API_KEY"], // the specific provider from issue #236 @@ -59,12 +60,15 @@ describe("expectedEnvKeyForModel — URL fallback for custom/auto providers", () expect( expectedEnvKeyForModel("custom", "https://openrouter.ai/api/v1"), ).toBe("OPENROUTER_API_KEY"); - expect( - expectedEnvKeyForModel("custom", "https://ollama.com/v1"), - ).toBe("OLLAMA_API_KEY"); + expect(expectedEnvKeyForModel("custom", "https://ollama.com/v1")).toBe( + "OLLAMA_API_KEY", + ); expect(expectedEnvKeyForModel("custom", "https://api.aimlapi.com/v1")).toBe( "AIMLAPI_API_KEY", ); + expect( + expectedEnvKeyForModel("custom", "https://direct.evolink.ai/v1"), + ).toBe("EVOLINK_API_KEY"); expect( expectedEnvKeyForModel("custom", "https://api.xiaomimimo.com/v1"), ).toBe("XIAOMI_API_KEY"); diff --git a/tests/url-key-map.test.ts b/tests/url-key-map.test.ts index 86d820fd6..ed5c68aa4 100644 --- a/tests/url-key-map.test.ts +++ b/tests/url-key-map.test.ts @@ -25,6 +25,7 @@ describe("URL_KEY_MAP", () => { "https://api.anthropic.com/v1": "ANTHROPIC_API_KEY", "https://api.openai.com/v1": "OPENAI_API_KEY", "https://api.aimlapi.com/v1": "AIMLAPI_API_KEY", + "https://direct.evolink.ai/v1": "EVOLINK_API_KEY", "https://huggingface.co/api": "HF_TOKEN", "https://api.groq.com/openai/v1": "GROQ_API_KEY", "https://api.deepseek.com/v1": "DEEPSEEK_API_KEY",