Skip to content

perf: stop inlining the model snapshot and build scripts into every page - #387

Merged
sabrinaaquino merged 2 commits into
mainfrom
perf/trim-per-page-payload
Aug 3, 2026
Merged

perf: stop inlining the model snapshot and build scripts into every page#387
sabrinaaquino merged 2 commits into
mainfrom
perf/trim-per-page-payload

Conversation

@sabrinaaquino

@sabrinaaquino sabrinaaquino commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Mintlify injects every .js file in the content directory into every page, and one line of model-search.js — the STATIC_MODELS literal — was 125KB of model catalog. It was being inlined into all ~2,000 page variants, including the majority that never display model data. Because the HTML is served Cache-Control: no-store, it was re-downloaded on every navigation instead of cached.

This PR moves that data to a cacheable file and stops publishing the build scripts. It also fixes two broken links found while investigating.

Scope check: this trims ~17KB per page over the wire (13.5% gzipped). It is real but modest, and it is not a fix for the slow docs loads. That is dominated by 13–24s cold-render TTFB at the origin (see Not addressed). Treat this as payload hygiene plus a JS error fix, not a page-speed fix.

Changes

  • Move the model snapshot out of model-search.js into data/static-models.json, fetched on demand. model-search.js drops from 265KB to 143KB, and the snapshot is served with Cache-Control: public, max-age=1800 instead of being embedded in no-store HTML.
  • Stop publishing the build scripts. scripts/*.js are build-time Node scripts with #!/usr/bin/env node shebangs that were being shipped to browsers. Added a .mintignore.
  • Repoint the pipeline. scripts/update-static-models.js writes the JSON snapshot instead of regex-patching model-search.js, scripts/generate-pricing-static.js reads from it, and the hourly sync workflow diffs/commits the new path.

The loader degrades safely: if the snapshot can't be fetched it falls back to the live models API, the same source the page refreshes from a moment later anyway.

This also fixes two uncaught JS errors on every page

Browser testing showed production currently throws these on every page load:

ReferenceError: require is not defined
SyntaxError: Identifier 'fs' has already been declared

That's the two injected build scripts: both start with const fs = require('fs'), so the second redeclares fs and neither can call require in a browser. Both errors are gone on the preview.

Also fixed

  • Buy Credits led to a 404. The pricing card on about-venice pointed at https://venice.ai/settings/billing, which returns a real Next.js 404, in all nine locales. Now points at https://venice.ai/settings/api, which is what the rest of the docs already use for buying credits.
  • Stale llms.txt link to overview/getting-started, a page that no longer exists and only resolved via redirect. Now points at getting-started/quick-start.

Static models are already current — the hourly job synced them in f84b849, and the extracted JSON is byte-identical to what the updater produces.

Verification

Payload, preview vs. production

Page Gzipped (what users download) Uncompressed
/it/overview/beta-models 125,022 → 108,097 (−16,925, 13.5%) 761,319 → 598,463 (−162,856)
/overview/about-venice 126,926 → 109,946 (−16,980, 13.4%) 802,265 → 634,025 (−168,240)

The gzipped column is the number that matters; the uncompressed column only shows where the bytes went.

Headless Chrome, every component that reads the snapshot

Each page loaded on both sites and item counts compared. Identical on preview and production:

Page Component Prod Preview
/models/overview model browser 294 294
/overview/pricing pricing tables 118 118
/overview/beta-models beta models 61 61
/overview/deprecations deprecation table 3 3
/overview/deprecations traits list 7 7
/guides/features/prompt-caching cache pricing 75 75
/guides/features/reasoning-models reasoning models 86 86
/models/text-to-speech voice picker 357 357

Fully-rendered pricing page layout is identical to the pixel on both (12468.2px / 1018.25px / 6490px for the chat, embedding and image sections).

  • /data/static-models.json returns 200 application/json, Cache-Control: public, max-age=1800, and is requested on every model-driven page
  • /scripts/*.js now return 404 — no longer published or injected
  • Two uncaught JS errors present on every production page are gone
  • Model catalog absent from page HTML; only the fetch URL remains
  • settings/billing gone from about-venice, replaced by settings/api
  • node scripts/update-static-models.js reports the snapshot up to date, confirming a lossless extraction
  • node scripts/generate-pricing-static.js regenerates overview/pricing.mdx byte-identically from the new path

Remaining console noise (a 403 and a Reddit pixel CORS failure) is present on production too and is unrelated to this change.

Not addressed

  • The actual cause of slow docs loads: 13–24s cold-render TTFB. Warm pages serve in ~0.3s, and the edge cache holds ~70 minutes, so intermittent visitors hit a cold render most of the time. This PR does not move that number meaningfully.
  • Cache-Control: no-store on HTML, which prevents browsers caching pages at all, even on immediate re-visit.
  • style.css inlined twice by Mintlify (~133KB uncompressed per page), once as a <style> tag and once in the flight payload.

The last three are Mintlify platform behavior and likely need a support conversation rather than a repo change.

Mintlify injects every .js file in the content directory into every page, so
model-search.js was carried on all ~2,000 page variants. Because the HTML is
served with Cache-Control: no-store, that payload was re-downloaded on every
navigation rather than cached.

- Move the 125KB STATIC_MODELS literal into data/static-models.json and load it
  on demand, falling back to the live models API if the snapshot is
  unavailable. Cuts model-search.js from 265KB to 143KB.
- Add .mintignore so the build-time Node scripts in scripts/ are no longer
  published and injected into pages.
- Point the sync workflow and pricing generator at the new snapshot path.

Also fixes the Buy Credits CTA, which pointed at venice.ai/settings/billing
(a 404) in all nine locales, and a stale llms.txt link to the removed
overview/getting-started page.
@mintlify

mintlify Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
veniceai 🟢 Ready View Preview Aug 3, 2026, 7:47 PM

The hourly snapshot job rewrites the STATIC_MODELS line in model-search.js,
which this branch removes, so main conflicts with it on every sync.

Resolved by keeping this branch's loader in model-search.js and carrying
main's newer model data into data/static-models.json, so no snapshot updates
are lost. overview/pricing.mdx regenerates byte-identically from it.
@sabrinaaquino
sabrinaaquino merged commit 9369431 into main Aug 3, 2026
3 checks passed
@sabrinaaquino
sabrinaaquino deleted the perf/trim-per-page-payload branch August 3, 2026 20:56
sabrinaaquino added a commit that referenced this pull request Aug 5, 2026
The three cards were hand-written, so they rotted: the page still advertised
Kimi K2.6 and Claude Opus 4.7 well after both were superseded, and #387 left
behind empty stats divs when it stripped the inlined model data.

scripts/generate-popular-models.js now renders the grid from
data/static-models.json, which the hourly workflow already refreshes, so the
name, provider, context window, privacy tier, and model ID always match the
catalog. Which models are featured stays an editorial choice, so the ids are
pinned; if a pinned id leaves the catalog the newest model from the same
provider takes the slot rather than leaving a card for a model nobody can call.

Cards now carry the provider logo. The SVGs in images/icons/models are authored
with fill="currentColor", so they are painted through a CSS mask the way the
model catalog avatars are, instead of via an img tag that would render nothing.

Restoring the model ID to each card also makes the section subtitle true again,
so it goes back to telling readers to pass the ID as their model parameter.
sabrinaaquino added a commit that referenced this pull request Aug 5, 2026
…catalog (#389)

* docs: tighten landing page copy and drop em dash

Rewrite the endpoint card descriptions so they add information instead of
restating the chips directly beneath them, and give the hero and Endpoints
subtitles distinct jobs rather than both ending in "behind one API key".

The Popular models subtitle told readers to use the model ID as their model
parameter, but the cards stopped rendering IDs in #387, so it now points at
the full catalog instead.

* docs: lead the landing hero with models and tools

OpenAI compatibility is a migration detail rather than the reason to pick
Venice, so the hero now leads with private, unrestricted access to the leading
models. The subtitle adds the tool surface (web search, document parsing, MCP)
so the page reads as more than a model gateway.

Compatibility is still called out in the meta description and on the Chat
Completions card, where developers evaluating a switch will look for it.

* docs: generate Popular models cards from the live snapshot

The three cards were hand-written, so they rotted: the page still advertised
Kimi K2.6 and Claude Opus 4.7 well after both were superseded, and #387 left
behind empty stats divs when it stripped the inlined model data.

scripts/generate-popular-models.js now renders the grid from
data/static-models.json, which the hourly workflow already refreshes, so the
name, provider, context window, privacy tier, and model ID always match the
catalog. Which models are featured stays an editorial choice, so the ids are
pinned; if a pinned id leaves the catalog the newest model from the same
provider takes the slot rather than leaving a card for a model nobody can call.

Cards now carry the provider logo. The SVGs in images/icons/models are authored
with fill="currentColor", so they are painted through a CSS mask the way the
model catalog avatars are, instead of via an img tag that would render nothing.

Restoring the model ID to each card also makes the section subtitle true again,
so it goes back to telling readers to pass the ID as their model parameter.

* docs: name the modalities in the landing hero

The hero now states the full surface in one line rather than splitting the
models claim and the modality list across the tagline and subtitle. The
subtitle keeps the tool surface, which nothing else above the fold covers.

Moving "behind one API key" into the tagline meant the Endpoints subtitle was
repeating it, so that subtitle switches to what is actually shared across
modalities: auth, error codes, and rate limit headers.

* docs: generate the catalog count and stop printing the hero claim twice

The banner claimed 250+ models while the catalog held 293 live ones, and any
hand-picked replacement would drift the same way. The count now comes from the
same snapshot as the cards, rounded down to the nearest 10 so the claim is
always true rather than aspirational. That puts the banner at "290+ models"
today, and it promotes itself to "300+" once the catalog gets there without
anyone editing the page.

Mintlify renders frontmatter `description` as a visible subtitle under the H1,
not just as metadata, so tightening it to match the hero put the same sentence
on screen twice. The page now carries og:description instead, which keeps the
social card text without repeating the hero. Search engines will build the
snippet from the hero line, which says the same thing.

* docs: give the landing page a description distinct from the hero

Restores the visible subtitle, and with it the meta description, but pointed at
what the page is for rather than repeating the hero's product claim. og:description
keeps the claim for social cards.

* docs: rebuild the landing page Agents section around real integrations

The three Agents cards described categories rather than products and all
pointed at anchors on the AI Agents hub. Split them into agent apps, coding
agents, and MCP and skills, with one card per integration linking straight
to its own guide.

Each card carries the project's own brand logo. Marks that ship as single
colour artwork get flipped in dark mode via an is-mono class, so the
monochrome and full colour logos both read on either theme.

Also folds the section eyebrow labels into the headings, drops the hero
subtitle, and renames the Search and RAG section to Tools.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant