Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

# modelparams.dev

> An open, community-maintained catalog of model parameters.
> An open, community-maintained catalog of LLM API parameters.

[![npm version](https://img.shields.io/npm/v/modelparams.svg)](https://www.npmjs.com/package/modelparams)
[![npm downloads](https://img.shields.io/npm/dm/modelparams.svg)](https://www.npmjs.com/package/modelparams)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Every parameter each AI model accepts, in one place. Inspired by [models.dev](https://github.com/anomalyco/models.dev); we use it at [Manifest](https://manifest.build/).
Every API parameter each AI model accepts, in one place: `temperature`, `top_p`, `max_tokens` and the rest of the request body, with defaults, ranges and gating conditions. Not weight counts — see [model parameters vs. API parameters](https://modelparams.dev/model-parameters-vs-api-parameters). Inspired by [models.dev](https://github.com/anomalyco/models.dev); we use it at [Manifest](https://manifest.build/).

## TypeScript

Expand Down
8 changes: 8 additions & 0 deletions src/build/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { SITE_URL } from "../data/site.js";
import { buildRobotsTxt } from "../data/robots.js";
import {
API_PATH,
DISAMBIGUATION_PATH,
GLOSSARY_PATH,
modelPagePath,
parameterPagePath,
Expand All @@ -32,6 +33,7 @@ import { bundleClientScript, compileStyles, copyStaticAssets } from "./assets.js
import { gitLastmodMap, modelLastmod, newestLastmod } from "./lastmod.js";
import { renderIndex } from "./render.js";
import { renderApiPage } from "./render-api.js";
import { renderDisambiguationPage } from "./render-disambiguation.js";
import { renderGlossaryPage } from "./render-glossary.js";
import { renderModelPage } from "./render-model.js";
import { defaultSummary,renderParameterPage, rangeSummary } from "./render-parameter.js";
Expand Down Expand Up @@ -75,6 +77,7 @@ async function writeRobotsAndSitemap(models: Model[]): Promise<void> {
const entries: { path: string; priority: string; lastmod: string }[] = [
{ path: "/", priority: "1.0", lastmod: freshest(models) },
{ path: GLOSSARY_PATH, priority: "0.7", lastmod: freshest(models) },
{ path: DISAMBIGUATION_PATH, priority: "0.6", lastmod: freshest(models) },
{ path: API_PATH, priority: "0.5", lastmod: freshest(models) },
...uniqueProviders(models).map((provider) => ({
path: providerPagePath(provider),
Expand Down Expand Up @@ -123,6 +126,11 @@ async function writeHtmlPages(models: Model[]): Promise<void> {
}

await fs.writeFile(path.join(DIST_DIR, "glossary.html"), await renderGlossaryPage(models), "utf8");
await fs.writeFile(
path.join(DIST_DIR, `${DISAMBIGUATION_PATH.replace(/^\//, "")}.html`),
await renderDisambiguationPage(models),
"utf8",
);
await fs.writeFile(path.join(DIST_DIR, "api.html"), await renderApiPage(models), "utf8");
await fs.writeFile(path.join(DIST_DIR, "404.html"), await renderNotFoundPage(models), "utf8");
}
Expand Down
14 changes: 12 additions & 2 deletions src/build/og.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { buildParameterIndex, type ParameterDetail } from "../data/parameters.js
import { CLIENT_DIR, DIST_ASSETS_DIR, DIST_DIR } from "../data/paths.js";
import {
API_PATH,
DISAMBIGUATION_PATH,
GLOSSARY_PATH,
modelPagePath,
ogImagePath,
Expand Down Expand Up @@ -68,7 +69,7 @@ export function providerCard(provider: string, models: Model[]): OgCard {
const paths = paramPaths(models);
return {
eyebrow: "Provider",
headline: `${providerLabel(provider)} model parameters`,
headline: `${providerLabel(provider)} API parameters`,
subline: `${models.length} model${models.length === 1 ? "" : "s"} · ${paths.length} parameters tracked`,
chips: paths.slice(0, CHIP_LIMIT),
};
Expand All @@ -87,11 +88,19 @@ export function parameterCard(detail: ParameterDetail, facts: string[]): OgCard
export function glossaryCard(parameterCount: number): OgCard {
return {
eyebrow: "Glossary",
headline: "LLM parameter glossary",
headline: "LLM API parameter glossary",
subline: `${parameterCount} parameters defined, with defaults and ranges`,
};
}

export function disambiguationCard(): OgCard {
return {
eyebrow: "Explainer",
headline: "Model parameters vs. API parameters",
subline: "Weight count or request settings — which one you're looking for",
};
}

export function apiCard(modelCount: number): OgCard {
return {
eyebrow: "Documentation",
Expand Down Expand Up @@ -140,6 +149,7 @@ export async function writeOgImages(
await writeCard("/", home);
await writeCard(GLOSSARY_PATH, glossaryCard(details.length));
await writeCard(API_PATH, apiCard(models.length));
await writeCard(DISAMBIGUATION_PATH, disambiguationCard());

for (const provider of providers) {
const providerModels = models.filter((model) => model.provider === provider);
Expand Down
40 changes: 40 additions & 0 deletions src/build/render-disambiguation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import path from "node:path";
import ejs from "ejs";
import { disambiguationFaq } from "../data/disambiguation.js";
import { VIEWS_DIR } from "../data/paths.js";
import { SITE_NAME, SITE_URL } from "../data/site.js";
import { DISAMBIGUATION_PATH, GLOSSARY_PATH, absolute, ogImagePath } from "../data/urls.js";
import { type Model } from "../schema/model.js";
import { buildDisambiguationStructuredData } from "./structured-data.js";
import { hubLinks, renderShell, viewHelpers } from "./render.js";

const TITLE = `Model parameters vs. API parameters · ${SITE_NAME}`;

// Names both senses in the snippet. Someone searching for a weight count should
// be able to tell from the SERP that this page answers them and that the rest of
// the site won't.
const DESCRIPTION =
"A model's parameter count is its trained weights. API parameters are the request settings like temperature and top_p. Which one you want, and where to find it.";

export async function renderDisambiguationPage(allModels: Model[]): Promise<string> {
const faqs = disambiguationFaq(allModels.length);
const body = await ejs.renderFile(path.join(VIEWS_DIR, "disambiguation.ejs"), {
helpers: viewHelpers,
faqs,
modelCount: allModels.length,
glossaryPath: GLOSSARY_PATH,
});

return renderShell(
{
title: TITLE,
description: DESCRIPTION,
canonicalUrl: absolute(SITE_URL, DISAMBIGUATION_PATH),
ogImage: ogImagePath(DISAMBIGUATION_PATH),
ogType: "article",
structuredData: buildDisambiguationStructuredData(faqs, DESCRIPTION, SITE_URL),
providerHubs: hubLinks(allModels),
},
body,
);
}
4 changes: 2 additions & 2 deletions src/build/render-glossary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { type Model } from "../schema/model.js";
import { buildGlossaryStructuredData } from "./structured-data.js";
import { hubLinks, renderShell, viewHelpers } from "./render.js";

const GLOSSARY_TITLE = `LLM parameter glossary · ${SITE_NAME}`;
const GLOSSARY_TITLE = `LLM API parameter glossary · ${SITE_NAME}`;

const GLOSSARY_DESCRIPTION =
"Every LLM API parameter defined: what temperature, top_p, max_tokens and reasoning effort do. Open any parameter for its default and range on every model.";

function glossaryIntro(groups: GlossaryGroup[]): string {
const total = groups.reduce((sum, groupItem) => sum + groupItem.entries.length, 0);
return `${total} parameters appear across the catalog. This page defines each one, grouped by what it controls. Open any parameter for the full breakdown — its default, range, and conditions on every model that accepts it. Definitions come from the same community-maintained data as the JSON API.`;
return `${total} API parameters appear across the catalog. This page defines each one, grouped by what it controls. Open any parameter for the full breakdown — its default, range, and conditions on every model that accepts it. Definitions come from the same community-maintained data as the JSON API.`;
}

export async function renderGlossaryPage(allModels: Model[]): Promise<string> {
Expand Down
18 changes: 14 additions & 4 deletions src/build/render-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { groupParams } from "../data/group.js";
import { VIEWS_DIR } from "../data/paths.js";
import { SITE_NAME, SITE_URL } from "../data/site.js";
import {
DISAMBIGUATION_PATH,
absolute,
modelJsonPath,
modelPagePath,
Expand All @@ -27,15 +28,23 @@ export function modelPageTitle(model: Model): string {
// separate URLs, so a fallback without it would give them the same title.
const variant = model.authType === "subscription" ? " (subscription)" : "";
const who = `${modelFullLabel(model)}${variant}`;
return fitTitle([`${who} parameters · ${SITE_NAME}`, `${who} parameters`, who]);
// "API" survives every fallback, including the shortest. Bare "<model>
// parameters" is what people type when they want a weight count, and a title
// that matches it pulls the page into the wrong result set — so the site name
// and then the provider prefix go first, and the qualifier never does.
return fitTitle([
`${who} API parameters · ${SITE_NAME}`,
`${who} API parameters`,
`${modelLabel(model)}${variant} API parameters`,
]);
}

const PARAM_TAIL = ". Type, default, range, and gating conditions for each.";

export function modelPageDescription(model: Model): string {
const who = `${modelFullLabel(model)}${authNote(model)}`;
if (model.params.length === 0) {
return `${who}: no parameters documented yet. Browse the open catalog of model parameters on ${SITE_NAME}.`;
return `${who}: no API parameters documented yet. Browse the open catalog of LLM API parameters on ${SITE_NAME}.`;
}
const count = `${model.params.length} API parameter${model.params.length === 1 ? "" : "s"}`;
const head = `All ${count} for ${who}`;
Expand Down Expand Up @@ -87,13 +96,13 @@ export function modelParamProse(model: Model): ParamProseGroup[] {
export function modelIntro(model: Model): string {
const who = modelFullLabel(model);
if (model.params.length === 0) {
return `No parameters are documented yet for ${who}. The data is community-maintained, so this page fills in as entries land.`;
return `No API parameters are documented yet for ${who}. The data is community-maintained, so this page fills in as entries land.`;
}
const access =
model.authType === "subscription"
? " when you reach it through a subscription rather than an API key"
: "";
return `These are the parameters ${SITE_NAME} tracks for ${who}${access}. Each row gives the type, default, valid range or values, and the conditions that gate it. It's the same data the JSON API serves.`;
return `These are the API parameters ${SITE_NAME} tracks for ${who}${access} — the settings you send in a request. Each row gives the type, default, valid range or values, and the conditions that gate it. It's the same data the JSON API serves.`;
}

export async function renderModelPage(model: Model, allModels: Model[]): Promise<string> {
Expand All @@ -116,6 +125,7 @@ export async function renderModelPage(model: Model, allModels: Model[]): Promise
modelJson: JSON.stringify({ $schema: "https://modelparams.dev/api/v1/schema.json", ...model }, null, 2),
isSubscription: model.authType === "subscription",
proseGroups: modelParamProse(model),
disambiguationPath: DISAMBIGUATION_PATH,
});

const description = modelPageDescription(model);
Expand Down
8 changes: 5 additions & 3 deletions src/build/render-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ import { DESCRIPTION_MAX, fitDescription, sampleList } from "./meta.js";
import { hubLinks, renderShell, viewHelpers } from "./render.js";

export function providerPageTitle(provider: string): string {
return `${providerLabel(provider)} model parameters · ${SITE_NAME}`;
return `${providerLabel(provider)} API parameters · ${SITE_NAME}`;
}

const PARAM_TAIL = ". Type, default, range, and gating conditions for each.";

export function providerPageDescription(provider: string, models: Model[]): string {
const count = `${models.length} ${providerLabel(provider)} model${models.length === 1 ? "" : "s"}`;
const head = `Parameters for ${count}`;
const head = `API parameters for ${count}`;
const sample = sampleList(
sampleParams(models),
DESCRIPTION_MAX - head.length - PARAM_TAIL.length - 2,
);
// Every candidate keeps "API parameters" via `head`; none may fall back to a
// bare "Parameters for …", which reads as weight count.
return fitDescription([
`${head}: ${sample}${PARAM_TAIL}`,
`${head}${PARAM_TAIL}`,
Expand All @@ -39,7 +41,7 @@ function sampleParams(models: Model[]): string[] {

export function providerIntro(provider: string, models: Model[]): string {
const count = `${models.length} ${providerLabel(provider)} model${models.length === 1 ? "" : "s"}`;
return `${SITE_NAME} tracks parameters for ${count}. Open a model to see its full set: the type, default, valid range or values, and the conditions that gate each parameter.`;
return `${SITE_NAME} tracks API parameters for ${count}. Open a model to see its full set: the type, default, valid range or values, and the conditions that gate each parameter.`;
}

export async function renderProviderPage(
Expand Down
13 changes: 7 additions & 6 deletions src/build/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,16 @@ export interface RenderOptions {
/**
* Brand-first homepage title. Interior pages read "<page> · modelparams.dev";
* the homepage inverts that so the brand leads, which is what a branded search
* ("modelparams") and a link in a feed both want to see first. "LLM parameters"
* is the head term people actually type — "model parameters" reads as weights
* to an ML audience, so the descriptive half says LLM. The live count carries
* the scale that makes the result worth clicking.
* ("modelparams") and a link in a feed both want to see first. The descriptive
* half says "LLM API parameters" in full: "model parameters" and even "LLM
* parameters" both read as weight count to a general audience, and API is the
* one word that weight-count queries never contain. The live count carries the
* scale that makes the result worth clicking.
*/
export function homeTitle(modelCount: number): string {
return fitTitle([
`${SITE_NAME} LLM Parameters for ${modelCount} Models`,
`${SITE_NAME} LLM Parameters`,
`${SITE_NAME} · LLM API Parameters for ${modelCount} Models`,
`${SITE_NAME} · LLM API Parameters`,
]);
}

Expand Down
Loading
Loading