Skip to content

Commit 4b0157d

Browse files
committed
reproduction (reproduced)
1 parent 17d66c5 commit 4b0157d

3 files changed

Lines changed: 732 additions & 0 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
type GatewayPricing = {
2+
input?: string;
3+
output?: string;
4+
prompt?: string;
5+
completion?: string;
6+
input_cache_read?: string;
7+
};
8+
9+
type GatewayModel = {
10+
id: string;
11+
owned_by?: string;
12+
max_tokens?: number;
13+
pricing?: GatewayPricing;
14+
};
15+
16+
type GatewayEndpoint = {
17+
provider_name: string;
18+
max_completion_tokens?: number;
19+
pricing?: GatewayPricing;
20+
};
21+
22+
type GatewayEndpointsResponse = {
23+
data: {
24+
id: string;
25+
endpoints: GatewayEndpoint[];
26+
};
27+
};
28+
29+
const gatewayModelsUrl = 'https://ai-gateway.vercel.sh/v1/models';
30+
const gatewayModelPageBaseUrl = 'https://vercel.com/ai-gateway/models';
31+
32+
async function fetchJson<T>(url: string): Promise<T> {
33+
const response = await fetch(url);
34+
35+
if (!response.ok) {
36+
throw new Error(`GET ${url} failed with ${response.status}`);
37+
}
38+
39+
return response.json() as Promise<T>;
40+
}
41+
42+
async function fetchText(url: string): Promise<string> {
43+
const response = await fetch(url);
44+
45+
if (!response.ok) {
46+
throw new Error(`GET ${url} failed with ${response.status}`);
47+
}
48+
49+
return response.text();
50+
}
51+
52+
function stripHtml(html: string): string {
53+
return html
54+
.replace(/<script[\s\S]*?<\/script>/gi, ' ')
55+
.replace(/<style[\s\S]*?<\/style>/gi, ' ')
56+
.replace(/<[^>]+>/g, ' ')
57+
.replace(/&nbsp;/g, ' ')
58+
.replace(/&amp;/g, '&')
59+
.replace(/&#x27;/g, "'")
60+
.replace(/&quot;/g, '"')
61+
.replace(/\s+/g, ' ')
62+
.trim();
63+
}
64+
65+
function hasPrice(
66+
pricing: GatewayPricing | undefined,
67+
input: string,
68+
output: string,
69+
): boolean {
70+
return (
71+
(pricing?.input === input || pricing?.prompt === input) &&
72+
(pricing?.output === output || pricing?.completion === output)
73+
);
74+
}
75+
76+
async function main() {
77+
const failures: string[] = [];
78+
const modelsResponse = await fetchJson<{ data: GatewayModel[] }>(
79+
gatewayModelsUrl,
80+
);
81+
const thinkingModelId = 'deepseek/deepseek-v3.2-thinking';
82+
const thinkingModel = modelsResponse.data.find(
83+
model => model.id === thinkingModelId,
84+
);
85+
86+
if (thinkingModel == null) {
87+
throw new Error(
88+
`${thinkingModelId} was not present in ${gatewayModelsUrl}`,
89+
);
90+
}
91+
92+
const endpointsUrl = `${gatewayModelsUrl}/${thinkingModelId}/endpoints`;
93+
const endpointsResponse =
94+
await fetchJson<GatewayEndpointsResponse>(endpointsUrl);
95+
const endpointProviders = endpointsResponse.data.endpoints.map(
96+
endpoint => endpoint.provider_name,
97+
);
98+
const bedrockEndpoint = endpointsResponse.data.endpoints.find(
99+
endpoint => endpoint.provider_name === 'bedrock',
100+
);
101+
102+
const pageUrl = `${gatewayModelPageBaseUrl}/deepseek-v3.2-thinking`;
103+
const pageText = stripHtml(await fetchText(pageUrl));
104+
105+
const pageClaimsDeepSeekProvider = pageText.includes(
106+
'The Thinking variant and standard V3.2 are accessible through AI Gateway under the deepseek provider',
107+
);
108+
if (pageClaimsDeepSeekProvider && !endpointProviders.includes('deepseek')) {
109+
failures.push(
110+
`${pageUrl} says the Thinking variant is available under the deepseek provider, but ${endpointsUrl} only lists providers: ${endpointProviders.join(
111+
', ',
112+
)}.`,
113+
);
114+
}
115+
116+
if (
117+
bedrockEndpoint != null &&
118+
hasPrice(thinkingModel.pricing, '0.00000062', '0.00000185') &&
119+
hasPrice(bedrockEndpoint.pricing, '0.00000062', '0.00000185') &&
120+
!('pricing_provider' in thinkingModel)
121+
) {
122+
failures.push(
123+
`${gatewayModelsUrl} exposes aggregate pricing for ${thinkingModelId} as Bedrock-like $0.62/M input and $1.85/M output, but the model object does not identify Bedrock as the aggregate pricing source.`,
124+
);
125+
}
126+
127+
if (
128+
thinkingModel.max_tokens === 8000 &&
129+
pageText.includes('generates up to 163K tokens') &&
130+
pageText.includes('The output token budget extends to 163K tokens')
131+
) {
132+
failures.push(
133+
`${gatewayModelsUrl} reports max_tokens=8000 for ${thinkingModelId}, while ${pageUrl} says the model generates up to 163K tokens and has a 163K output token budget.`,
134+
);
135+
}
136+
137+
if (failures.length > 0) {
138+
throw new Error(
139+
`AI Gateway DeepSeek V3.2 catalog metadata is inconsistent:\n- ${failures.join(
140+
'\n- ',
141+
)}`,
142+
);
143+
}
144+
145+
console.log(
146+
'AI Gateway DeepSeek V3.2 catalog metadata is internally consistent.',
147+
);
148+
}
149+
150+
main().catch(error => {
151+
console.error(error);
152+
process.exit(1);
153+
});

0 commit comments

Comments
 (0)