Skip to content

Commit cd5304c

Browse files
fix(ai-gateway): fall back to default when routing override is cleared (#2790)
* fix(ai-gateway): fall back to default when routing override is cleared When an admin clears the Vercel routing override the admin router persists `{vercel_routing_percentage: null, ...}` to Redis. The hot path parsed that with a non-nullable schema and threw; `createCachedFetch` then kept serving the last cached numeric value, so a worker that had cached a non-null override would never pick up the intended default. Accept `null` in `GatewayPercentageSchema` and treat it as 'use `DEFAULT_VERCEL_PERCENTAGE`' in the fetcher. * refactor: move gateway-config into ai-gateway folder * style: wrap long import after rename --------- Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
1 parent c4836fa commit cd5304c

5 files changed

Lines changed: 39 additions & 7 deletions

File tree

apps/web/src/app/admin/gateway/RoutingContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { toast } from 'sonner';
77
import { Button } from '@/components/ui/button';
88
import { Input } from '@/components/ui/input';
99
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
10-
import { DEFAULT_VERCEL_PERCENTAGE } from '@/lib/gateway-config';
10+
import { DEFAULT_VERCEL_PERCENTAGE } from '@/lib/ai-gateway/gateway-config';
1111

1212
export function RoutingContent() {
1313
const trpc = useTRPC();
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, test, expect } from '@jest/globals';
2+
import { GatewayPercentageSchema } from './gateway-config';
3+
4+
describe('GatewayPercentageSchema', () => {
5+
test('accepts a numeric percentage', () => {
6+
expect(GatewayPercentageSchema.parse({ vercel_routing_percentage: 25 })).toEqual({
7+
vercel_routing_percentage: 25,
8+
});
9+
});
10+
11+
test('accepts null (written when an admin clears the override)', () => {
12+
expect(GatewayPercentageSchema.parse({ vercel_routing_percentage: null })).toEqual({
13+
vercel_routing_percentage: null,
14+
});
15+
});
16+
17+
test('rejects out-of-range values', () => {
18+
expect(() => GatewayPercentageSchema.parse({ vercel_routing_percentage: 101 })).toThrow();
19+
expect(() => GatewayPercentageSchema.parse({ vercel_routing_percentage: -1 })).toThrow();
20+
});
21+
});

apps/web/src/lib/gateway-config.ts renamed to apps/web/src/lib/ai-gateway/gateway-config.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,15 @@ export const DEFAULT_GATEWAY_CONFIG: GatewayConfig = {
2020
updated_by_email: null,
2121
};
2222

23-
/** Schema for parsing just the percentage from Redis (used on the hot path). */
23+
/**
24+
* Schema for parsing just the percentage from Redis (used on the hot path).
25+
*
26+
* `vercel_routing_percentage` is nullable because clearing the override in
27+
* the admin UI persists an explicit `null`. Callers should treat `null` as
28+
* "no override, use DEFAULT_VERCEL_PERCENTAGE".
29+
*/
2430
export const GatewayPercentageSchema = z.object({
25-
vercel_routing_percentage: vercelRoutingPercentage,
31+
vercel_routing_percentage: vercelRoutingPercentage.nullable(),
2632
});
2733

2834
/** Schema for the admin set-mutation input. */

apps/web/src/lib/ai-gateway/providers/vercel/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,20 @@ import { isReasoningExplicitlyDisabled } from '@/lib/ai-gateway/providers/openro
1616
import { mapModelIdToVercel } from '@/lib/ai-gateway/providers/vercel/mapModelIdToVercel';
1717
import { redisGet } from '@/lib/redis';
1818
import { createCachedFetch } from '@/lib/cached-fetch';
19-
import { GatewayPercentageSchema, DEFAULT_VERCEL_PERCENTAGE } from '@/lib/gateway-config';
19+
import {
20+
GatewayPercentageSchema,
21+
DEFAULT_VERCEL_PERCENTAGE,
22+
} from '@/lib/ai-gateway/gateway-config';
2023
import { VERCEL_ROUTING_REDIS_KEY } from '@/lib/redis-keys';
2124
import { getRandomNumber } from '@/lib/ai-gateway/getRandomNumber';
2225
import { getVercelModels } from '@/lib/ai-gateway/providers/gateway-models-cache';
2326

2427
const getVercelRoutingPercentage = createCachedFetch(
2528
async () => {
2629
const raw = await redisGet(VERCEL_ROUTING_REDIS_KEY);
27-
return GatewayPercentageSchema.parse(JSON.parse(raw ?? 'null')).vercel_routing_percentage;
30+
if (!raw) return DEFAULT_VERCEL_PERCENTAGE;
31+
const { vercel_routing_percentage } = GatewayPercentageSchema.parse(JSON.parse(raw));
32+
return vercel_routing_percentage ?? DEFAULT_VERCEL_PERCENTAGE;
2833
},
2934
10_000,
3035
DEFAULT_VERCEL_PERCENTAGE

apps/web/src/routers/admin/gateway-config-router.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import {
44
GatewayConfigSchema,
55
GatewayConfigInputSchema,
66
DEFAULT_GATEWAY_CONFIG,
7-
} from '@/lib/gateway-config';
7+
} from '@/lib/ai-gateway/gateway-config';
88
import { VERCEL_ROUTING_REDIS_KEY } from '@/lib/redis-keys';
9-
import type { GatewayConfig } from '@/lib/gateway-config';
9+
import type { GatewayConfig } from '@/lib/ai-gateway/gateway-config';
1010
import { TRPCError } from '@trpc/server';
1111

1212
async function readConfig(): Promise<GatewayConfig> {

0 commit comments

Comments
 (0)