fix: validate vercel gateway model token metadata

This commit is contained in:
Peter Steinberger
2026-05-28 17:59:42 -04:00
parent 423531df50
commit 39db00f896
2 changed files with 52 additions and 6 deletions

View File

@@ -3,6 +3,7 @@ import { readProviderJsonArrayFieldResponse } from "openclaw/plugin-sdk/provider
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
import { asPositiveSafeInteger } from "openclaw/plugin-sdk/string-coerce-runtime";
export const VERCEL_AI_GATEWAY_PROVIDER_ID = "vercel-ai-gateway";
export const VERCEL_AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh";
@@ -151,13 +152,13 @@ function buildDiscoveredModelDefinition(
const fallback = getStaticFallbackModel(id);
const contextWindow =
typeof model.context_window === "number" && Number.isFinite(model.context_window)
? model.context_window
: (fallback?.contextWindow ?? VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW);
asPositiveSafeInteger(model.context_window) ??
fallback?.contextWindow ??
VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW;
const maxTokens =
typeof model.max_tokens === "number" && Number.isFinite(model.max_tokens)
? model.max_tokens
: (fallback?.maxTokens ?? VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS);
asPositiveSafeInteger(model.max_tokens) ??
fallback?.maxTokens ??
VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS;
const normalizedCost = normalizeCost(model.pricing);
return {

View File

@@ -12,6 +12,8 @@ import {
discoverVercelAiGatewayModels,
getStaticVercelAiGatewayModelCatalog,
VERCEL_AI_GATEWAY_BASE_URL,
VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW,
VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS,
} from "./api.js";
import {
buildStaticVercelAiGatewayProvider,
@@ -93,4 +95,47 @@ describe("vercel ai gateway provider catalog", () => {
});
}
});
it("falls back from malformed live token metadata", async () => {
fetchWithSsrFGuardMock.mockResolvedValueOnce({
response: {
ok: true,
status: 200,
json: async () => ({
data: [
{
id: "anthropic/claude-opus-4.6",
name: "Claude Opus 4.6",
context_window: -1,
max_tokens: 128_000.5,
tags: ["vision", "reasoning"],
},
{
id: "custom/provider-model",
name: "Custom model",
context_window: Number.POSITIVE_INFINITY,
max_tokens: 0,
tags: ["reasoning"],
},
],
}),
},
release: async () => {},
});
await withLiveDiscovery(async () => {
const models = await discoverVercelAiGatewayModels();
expect(models[0]).toMatchObject({
id: "anthropic/claude-opus-4.6",
contextWindow: 1_000_000,
maxTokens: 128_000,
});
expect(models[1]).toMatchObject({
id: "custom/provider-model",
contextWindow: VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW,
maxTokens: VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS,
});
});
});
});