Skip to content

Instantly share code, notes, and snippets.

@PaulieScanlon
Created May 27, 2025 15:22
Show Gist options
  • Save PaulieScanlon/d58e380cc197ebb5af2fc6c809412786 to your computer and use it in GitHub Desktop.
Save PaulieScanlon/d58e380cc197ebb5af2fc6c809412786 to your computer and use it in GitHub Desktop.
Grabs Vercel Model Capabilities and creates a modelData array for mastra.ai docs.
import * as cheerio from "cheerio";
const apiKeyMap = {
"xAI Grok": "XAI_API_KEY",
OpenAI: "OPENAI_API_KEY",
Anthropic: "ANTHROPIC_API_KEY",
Mistral: "MISTRAL_API_KEY",
"Google Generative AI": "GOOGLE_API_KEY",
"Google Vertex": "GOOGLE_VERTEX_API_KEY",
DeepSeek: "DEEPSEEK_API_KEY",
Cerebras: "CEREBRAS_API_KEY",
Groq: "GROQ_API_KEY",
Vercel: "VERCEL_API_KEY"
};
function parseBool(cell) {
const val = cell.toLowerCase();
if (val.includes("<check")) return true;
if (val.includes("<cross")) return false;
return val === "yes";
}
// Helper: Get first word lowercased for provider URL slug
function slugifyProviderFirstWord(name) {
const firstWord = name.trim().split(/\s+/)[0];
return firstWord.toLowerCase();
}
function getApiKey(provider) {
// Extract plain text from markdown link [Name](url)
const plainProvider = provider.replace(/\[([^\]]+)\]\([^)]+\)/, "$1").trim();
// Find matching key ignoring case
const key = Object.keys(apiKeyMap).find((k) => k.toLowerCase() === plainProvider.toLowerCase());
return key ? apiKeyMap[key] : "";
}
(async () => {
const url = "https://raw.githubusercontent.com/vercel/ai/refs/heads/main/content/docs/02-foundations/02-providers-and-models.mdx";
const res = await fetch(url);
const mdx = await res.text();
const sectionRegex = /## Model Capabilities\s+([\s\S]*?)(?=\n## |\n$)/;
const match = sectionRegex.exec(mdx);
if (!match) {
console.error("❌ Could not find the Model Capabilities section");
return;
}
const tableMarkdown = match[1].trim();
const tableMatch = tableMarkdown.match(/\|(.+\|)+\n\|[-|:\s]+\|\n([\s\S]+?)(?=\n\n|\n$)/);
if (!tableMatch) {
console.error("❌ Could not find a markdown table in the section");
return;
}
const headerRow = tableMatch[0].split("\n")[0];
const dataRows = tableMatch[0].split("\n").slice(2);
const htmlTable = `
<table>
<thead>
<tr>${headerRow
.split("|")
.filter(Boolean)
.map((c) => `<th>${c.trim()}</th>`)
.join("")}</tr>
</thead>
<tbody>
${dataRows
.map((row) => {
const cells = row.split("|").filter(Boolean);
return `<tr>${cells.map((c) => `<td>${c.trim()}</td>`).join("")}</tr>`;
})
.join("")}
</tbody>
</table>
`;
const $ = cheerio.load(htmlTable);
const modelData = [];
$("tbody tr").each(function () {
const tds = $(this).find("td");
const rawProvider = tds.eq(0).html() || "";
const provider = rawProvider.replace(/\[([^\]]+)\]\([^)]+\)/, "$1").trim();
let model = tds.eq(1).text().trim();
model = model.replace(/`/g, "");
const imageInput = parseBool(tds.eq(2).html() || "");
const objectGeneration = parseBool(tds.eq(3).html() || "");
const toolUsage = parseBool(tds.eq(4).html() || "");
const toolStreaming = parseBool(tds.eq(5).html() || "");
const apiKey = getApiKey(provider);
const providerSlug = slugifyProviderFirstWord(provider);
modelData.push({
provider,
providerUrl: `https://sdk.vercel.ai/providers/ai-sdk-providers/${providerSlug}`,
model,
imageInput,
objectGeneration,
toolUsage,
toolStreaming,
apiKey
});
});
console.log("const modelData =", JSON.stringify(modelData, null, 2));
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment