Skip to content

Commit 23b6375

Browse files
authored
🤖 feat: use couponCode as the only mux-gateway config field (#899)
Simplifies mux-gateway configuration to use only `couponCode` field. ## Changes - Add mux-gateway to `/providers set` command suggestions - Replace `voucher` with `couponCode` across UI, schema, and backend - Backwards compatible: falls back to legacy `voucher` field if present ## Usage ``` /providers set mux-gateway couponCode <value> ``` _Generated with `mux`_
1 parent 774fa25 commit 23b6375

File tree

6 files changed

+41
-28
lines changed

6 files changed

+41
-28
lines changed

src/browser/components/Settings/sections/ProvidersSection.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ function getProviderFields(provider: ProviderName): FieldConfig[] {
4646
];
4747
}
4848

49-
// Mux Gateway only needs voucher
49+
// Mux Gateway only needs couponCode
5050
if (provider === "mux-gateway") {
51-
return [{ key: "voucher", label: "Voucher", placeholder: "Enter voucher", type: "secret" }];
51+
return [
52+
{ key: "couponCode", label: "Coupon Code", placeholder: "Enter coupon code", type: "secret" },
53+
];
5254
}
5355

5456
// Default for most providers
@@ -102,8 +104,8 @@ export function ProvidersSection() {
102104
updateOptimistically(provider, { apiKeySet: editValue !== "" });
103105
} else if (field === "baseUrl") {
104106
updateOptimistically(provider, { baseUrl: editValue || undefined });
105-
} else if (field === "voucher") {
106-
updateOptimistically(provider, { voucherSet: editValue !== "" });
107+
} else if (field === "couponCode") {
108+
updateOptimistically(provider, { couponCodeSet: editValue !== "" });
107109
}
108110

109111
setEditingField(null);
@@ -122,8 +124,8 @@ export function ProvidersSection() {
122124
updateOptimistically(provider, { apiKeySet: false });
123125
} else if (field === "baseUrl") {
124126
updateOptimistically(provider, { baseUrl: undefined });
125-
} else if (field === "voucher") {
126-
updateOptimistically(provider, { voucherSet: false });
127+
} else if (field === "couponCode") {
128+
updateOptimistically(provider, { couponCodeSet: false });
127129
}
128130

129131
// Save in background
@@ -142,9 +144,9 @@ export function ProvidersSection() {
142144
return !!(aws.region ?? aws.bearerTokenSet ?? aws.accessKeyIdSet ?? aws.secretAccessKeySet);
143145
}
144146

145-
// For Mux Gateway, check voucherSet
147+
// For Mux Gateway, check couponCodeSet
146148
if (provider === "mux-gateway") {
147-
return providerConfig.voucherSet ?? false;
149+
return providerConfig.couponCodeSet ?? false;
148150
}
149151

150152
// For other providers, check apiKeySet
@@ -172,8 +174,8 @@ export function ProvidersSection() {
172174
if (fieldConfig.type === "secret") {
173175
// For apiKey, we have apiKeySet from the sanitized config
174176
if (field === "apiKey") return providerConfig.apiKeySet ?? false;
175-
// For voucher (mux-gateway), check voucherSet
176-
if (field === "voucher") return providerConfig.voucherSet ?? false;
177+
// For couponCode (mux-gateway), check couponCodeSet
178+
if (field === "couponCode") return providerConfig.couponCodeSet ?? false;
177179

178180
// For AWS secrets, check the aws nested object
179181
if (provider === "bedrock" && providerConfig.aws) {

src/browser/utils/slashCommands/registry.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ const DEFAULT_PROVIDER_NAMES: SuggestionDefinition[] = [
6060
key: "bedrock",
6161
description: "Amazon Bedrock provider (AWS)",
6262
},
63+
{
64+
key: "mux-gateway",
65+
description: "Mux Gateway provider",
66+
},
6367
];
6468

6569
const DEFAULT_PROVIDER_KEYS: Record<string, SuggestionDefinition[]> = {
@@ -111,6 +115,12 @@ const DEFAULT_PROVIDER_KEYS: Record<string, SuggestionDefinition[]> = {
111115
description: "AWS Secret Access Key (use with accessKeyId)",
112116
},
113117
],
118+
"mux-gateway": [
119+
{
120+
key: "couponCode",
121+
description: "Coupon code for Mux Gateway access",
122+
},
123+
],
114124
default: [
115125
{
116126
key: "apiKey",

src/common/orpc/schemas/api.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,16 @@ describe("ProviderConfigInfoSchema conformance", () => {
6565
expect(parsed.aws).toEqual(full.aws);
6666
});
6767

68-
it("preserves all ProviderConfigInfo fields (with voucherSet)", () => {
68+
it("preserves all ProviderConfigInfo fields (with couponCodeSet)", () => {
6969
const full: ProviderConfigInfo = {
7070
apiKeySet: true,
71-
voucherSet: true,
71+
couponCodeSet: true,
7272
};
7373

7474
const parsed = ProviderConfigInfoSchema.parse(full);
7575

7676
expect(parsed).toEqual(full);
77-
expect(parsed.voucherSet).toBe(true);
77+
expect(parsed.couponCodeSet).toBe(true);
7878
});
7979

8080
it("preserves all ProviderConfigInfo fields (full object with all optional fields)", () => {
@@ -89,7 +89,7 @@ describe("ProviderConfigInfoSchema conformance", () => {
8989
accessKeyIdSet: true,
9090
secretAccessKeySet: true,
9191
},
92-
voucherSet: true,
92+
couponCodeSet: true,
9393
};
9494

9595
const parsed = ProviderConfigInfoSchema.parse(full);
@@ -102,7 +102,7 @@ describe("ProviderConfigInfoSchema conformance", () => {
102102
expect(parsed.baseUrl).toBe(full.baseUrl);
103103
expect(parsed.models).toEqual(full.models);
104104
expect(parsed.aws).toEqual(full.aws);
105-
expect(parsed.voucherSet).toBe(full.voucherSet);
105+
expect(parsed.couponCodeSet).toBe(full.couponCodeSet);
106106
});
107107

108108
it("preserves ProvidersConfigMap with multiple providers", () => {
@@ -122,7 +122,7 @@ describe("ProviderConfigInfoSchema conformance", () => {
122122
},
123123
"mux-gateway": {
124124
apiKeySet: false,
125-
voucherSet: true,
125+
couponCodeSet: true,
126126
models: ["anthropic/claude-sonnet-4-5"],
127127
},
128128
};

src/common/orpc/schemas/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const ProviderConfigInfoSchema = z.object({
4949
/** AWS-specific fields (only present for bedrock provider) */
5050
aws: AWSCredentialStatusSchema.optional(),
5151
/** Mux Gateway-specific fields */
52-
voucherSet: z.boolean().optional(),
52+
couponCodeSet: z.boolean().optional(),
5353
});
5454

5555
export const ProvidersConfigMapSchema = z.record(z.string(), ProviderConfigInfoSchema);

src/node/services/aiService.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,9 +751,9 @@ export class AIService extends EventEmitter {
751751

752752
// Handle Mux Gateway provider
753753
if (providerName === "mux-gateway") {
754-
// Mux Gateway uses voucher as the API key (fallback to legacy couponCode)
755-
const voucher = providerConfig.voucher ?? providerConfig.couponCode;
756-
if (typeof voucher !== "string" || !voucher) {
754+
// Mux Gateway uses couponCode as the API key (fallback to legacy voucher)
755+
const couponCode = providerConfig.couponCode ?? providerConfig.voucher;
756+
if (typeof couponCode !== "string" || !couponCode) {
757757
return Err({
758758
type: "api_key_not_found",
759759
provider: providerName,
@@ -773,7 +773,7 @@ export class AIService extends EventEmitter {
773773
const gatewayBaseURL =
774774
providerConfig.baseURL ?? "https://gateway.mux.coder.com/api/v1/ai-gateway/v1/ai";
775775
const gateway = createGateway({
776-
apiKey: voucher,
776+
apiKey: couponCode,
777777
baseURL: gatewayBaseURL,
778778
fetch: fetchWithCacheControl,
779779
});

src/node/services/providerService.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@ export class ProviderService {
7373
};
7474
}
7575

76-
// Mux Gateway-specific fields
76+
// Mux Gateway-specific fields (check couponCode first, fallback to legacy voucher)
7777
if (provider === "mux-gateway") {
78-
providerInfo.voucherSet = !!(config as { voucher?: string }).voucher;
78+
const muxConfig = config as { couponCode?: string; voucher?: string };
79+
providerInfo.couponCodeSet = !!(muxConfig.couponCode ?? muxConfig.voucher);
7980
}
8081

8182
result[provider] = providerInfo;
@@ -111,13 +112,13 @@ export class ProviderService {
111112
// Load current providers config or create empty
112113
const providersConfig = this.config.loadProvidersConfig() ?? {};
113114

114-
// Track if this is first time setting voucher for mux-gateway
115-
const isFirstMuxGatewayVoucher =
115+
// Track if this is first time setting couponCode for mux-gateway
116+
const isFirstMuxGatewayCoupon =
116117
provider === "mux-gateway" &&
117118
keyPath.length === 1 &&
118-
keyPath[0] === "voucher" &&
119+
keyPath[0] === "couponCode" &&
119120
value !== "" &&
120-
!providersConfig[provider]?.voucher;
121+
!providersConfig[provider]?.couponCode;
121122

122123
// Ensure provider exists
123124
if (!providersConfig[provider]) {
@@ -145,7 +146,7 @@ export class ProviderService {
145146
}
146147

147148
// Add default models when setting up mux-gateway for the first time
148-
if (isFirstMuxGatewayVoucher) {
149+
if (isFirstMuxGatewayCoupon) {
149150
const providerConfig = providersConfig[provider] as Record<string, unknown>;
150151
if (!providerConfig.models || (providerConfig.models as string[]).length === 0) {
151152
providerConfig.models = [

0 commit comments

Comments
 (0)