From bec1ca32015bab3a8556f78c1eedf3621447417b Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Fri, 28 Nov 2025 07:49:22 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20feat:=20add=20mux-gateway=20prov?= =?UTF-8?q?ider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new mux-gateway provider that uses createGateway from the AI SDK. Uses couponCode as the API key with hardcoded baseURL pointing to https://gateway.mux.coder.com/api/v1/ai-gateway/v1/ai - Add importMuxGateway function and registry entry - Add mux-gateway handler in createModel() - Add couponCode field in settings UI (no apiKey/baseUrl) - Auto-add default models on first couponCode setup: - anthropic/claude-sonnet-4-5-20250514 - anthropic/claude-opus-4-5-20250514 - openai/gpt-5.1 - openai/gpt-5.1-codex _Generated with mux_ --- .../Settings/sections/ProvidersSection.tsx | 14 ++++++++++ src/browser/components/Settings/types.ts | 2 ++ src/common/constants/providers.ts | 9 +++++++ src/node/services/aiService.ts | 19 ++++++++++++++ src/node/services/ipcMain.ts | 26 +++++++++++++++++++ 5 files changed, 70 insertions(+) diff --git a/src/browser/components/Settings/sections/ProvidersSection.tsx b/src/browser/components/Settings/sections/ProvidersSection.tsx index ac18481e8..6315544d9 100644 --- a/src/browser/components/Settings/sections/ProvidersSection.tsx +++ b/src/browser/components/Settings/sections/ProvidersSection.tsx @@ -44,6 +44,13 @@ function getProviderFields(provider: ProviderName): FieldConfig[] { ]; } + // Mux Gateway only needs couponCode + if (provider === "mux-gateway") { + return [ + { key: "couponCode", label: "Coupon Code", placeholder: "Enter coupon code", type: "secret" }, + ]; + } + // Default for most providers return [ { key: "apiKey", label: "API Key", placeholder: "Enter API key", type: "secret" }, @@ -138,6 +145,11 @@ export function ProvidersSection() { ); } + // For Mux Gateway, check couponCodeSet + if (provider === "mux-gateway") { + return providerConfig.couponCodeSet ?? false; + } + // For other providers, check apiKeySet return providerConfig.apiKeySet ?? false; }; @@ -153,6 +165,8 @@ export function ProvidersSection() { if (fieldConfig.type === "secret") { // For apiKey, we have apiKeySet from the sanitized config if (field === "apiKey") return config[provider]?.apiKeySet ?? false; + // For couponCode (mux-gateway), check couponCodeSet + if (field === "couponCode") return config[provider]?.couponCodeSet ?? false; // For other secrets, check if the field exists in the raw config // Since we don't expose secret values, we assume they're not set if undefined const providerConfig = config[provider] as Record | undefined; diff --git a/src/browser/components/Settings/types.ts b/src/browser/components/Settings/types.ts index 831d991d0..e4eb33bd7 100644 --- a/src/browser/components/Settings/types.ts +++ b/src/browser/components/Settings/types.ts @@ -16,6 +16,8 @@ export interface ProviderConfigDisplay { bearerTokenSet?: boolean; accessKeyIdSet?: boolean; secretAccessKeySet?: boolean; + // Mux Gateway-specific fields + couponCodeSet?: boolean; // Allow additional fields for extensibility [key: string]: unknown; } diff --git a/src/common/constants/providers.ts b/src/common/constants/providers.ts index 309b0def0..2650cf274 100644 --- a/src/common/constants/providers.ts +++ b/src/common/constants/providers.ts @@ -55,6 +55,13 @@ export async function importBedrock() { return import("@ai-sdk/amazon-bedrock"); } +/** + * Dynamically import the Gateway provider from the AI SDK + */ +export async function importMuxGateway() { + return import("ai"); +} + /** * Centralized provider registry mapping provider names to their import functions * @@ -76,6 +83,7 @@ export const PROVIDER_REGISTRY = { ollama: importOllama, openrouter: importOpenRouter, bedrock: importBedrock, + "mux-gateway": importMuxGateway, } as const; /** @@ -99,6 +107,7 @@ export const PROVIDER_DISPLAY_NAMES: Record = { ollama: "Ollama", openrouter: "OpenRouter", bedrock: "Amazon Bedrock", + "mux-gateway": "Mux Gateway", }; /** diff --git a/src/node/services/aiService.ts b/src/node/services/aiService.ts index 4c59caa61..b9010fd36 100644 --- a/src/node/services/aiService.ts +++ b/src/node/services/aiService.ts @@ -656,6 +656,25 @@ export class AIService extends EventEmitter { return Ok(provider(modelId)); } + // Handle Mux Gateway provider + if (providerName === "mux-gateway") { + // Mux Gateway uses couponCode as the API key + const couponCode = providerConfig.couponCode; + if (typeof couponCode !== "string" || !couponCode) { + return Err({ + type: "api_key_not_found", + provider: providerName, + }); + } + + const { createGateway } = await PROVIDER_REGISTRY["mux-gateway"](); + const gateway = createGateway({ + apiKey: couponCode, + baseURL: "https://gateway.mux.coder.com/api/v1/ai-gateway/v1/ai", + }); + return Ok(gateway(modelId)); + } + return Err({ type: "provider_not_supported", provider: providerName, diff --git a/src/node/services/ipcMain.ts b/src/node/services/ipcMain.ts index 53c16cb1b..99922cbdd 100644 --- a/src/node/services/ipcMain.ts +++ b/src/node/services/ipcMain.ts @@ -1467,6 +1467,14 @@ export class IpcMain { // Load current providers config or create empty const providersConfig = this.config.loadProvidersConfig() ?? {}; + // Track if this is first time setting couponCode for mux-gateway + const isFirstMuxGatewayCoupon = + provider === "mux-gateway" && + keyPath.length === 1 && + keyPath[0] === "couponCode" && + value !== "" && + !providersConfig[provider]?.couponCode; + // Ensure provider exists if (!providersConfig[provider]) { providersConfig[provider] = {}; @@ -1492,6 +1500,19 @@ export class IpcMain { } } + // Add default models when setting up mux-gateway for the first time + if (isFirstMuxGatewayCoupon) { + const providerConfig = providersConfig[provider] as Record; + if (!providerConfig.models || (providerConfig.models as string[]).length === 0) { + providerConfig.models = [ + "anthropic/claude-sonnet-4-5-20250514", + "anthropic/claude-opus-4-5-20250514", + "openai/gpt-5.1", + "openai/gpt-5.1-codex", + ]; + } + } + // Save updated config this.config.saveProvidersConfig(providersConfig); @@ -1562,6 +1583,11 @@ export class IpcMain { providerData.secretAccessKeySet = !!providerConfig.secretAccessKey; } + // Mux Gateway-specific fields + if (provider === "mux-gateway") { + providerData.couponCodeSet = !!providerConfig.couponCode; + } + sanitized[provider] = providerData; } return sanitized;