Skip to content

Commit 6df74d6

Browse files
authored
fix(docs): publish full LLM text as a static asset (#8769)
* fix(docs): publish full LLM text as a static asset * fix(docs): regenerate full text during development
1 parent 350b016 commit 6df74d6

7 files changed

Lines changed: 125 additions & 36 deletions

File tree

‎apps/docs/.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,7 @@ next-env.d.ts
4141

4242
# fumadocs generates .source dirs anywhere a source.config sits
4343
**/.source/
44+
45+
# Build-generated documentation asset
46+
/public/llms-full.txt
47+
/public/.llms-full-*/

‎apps/docs/app/llms-full.txt/route.ts‎

Lines changed: 0 additions & 33 deletions
This file was deleted.

‎apps/docs/next.config.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ const config: NextConfig = {
1616
webpackMemoryOptimizations: true,
1717
webpackBuildWorker: true,
1818
},
19+
async headers() {
20+
return [
21+
{
22+
source: '/llms-full.txt',
23+
headers: [
24+
{ key: 'Content-Type', value: 'text/plain; charset=utf-8' },
25+
{ key: 'Cache-Control', value: 'public, max-age=0, s-maxage=86400, must-revalidate' },
26+
],
27+
},
28+
]
29+
},
1930
async redirects() {
2031
return DOCS_REDIRECTS
2132
},

‎apps/docs/package.json‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@
44
"private": true,
55
"license": "Apache-2.0",
66
"scripts": {
7-
"dev": "bun run dev:cache:cap && next dev --port 3001",
7+
"dev": "bun run dev:cache:cap && bun run llms-full:generate && bunx concurrently --kill-others --names Next,LLMs \"next dev --port 3001\" \"bun scripts/watch-llms-full.ts\"",
88
"dev:cache:cap": "bun run ../../scripts/prune-turbopack-cache.ts",
99
"dev:clean": "rm -rf .next/dev/cache",
10-
"build": "fumadocs-mdx && NODE_OPTIONS='--max-old-space-size=8192' next build",
10+
"build": "bun run llms-full:generate && NODE_OPTIONS='--max-old-space-size=8192' next build",
1111
"start": "next start",
1212
"postinstall": "fumadocs-mdx",
1313
"test": "vitest run",
1414
"type-check": "fumadocs-mdx && tsc --noEmit",
1515
"lint": "biome check --write --unsafe .",
1616
"lint:check": "biome check .",
1717
"format": "biome format --write .",
18-
"format:check": "biome format ."
18+
"format:check": "biome format .",
19+
"llms-full:generate": "fumadocs-mdx && bun scripts/generate-llms-full.ts"
1920
},
2021
"dependencies": {
2122
"@sim/db": "workspace:*",
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { createWriteStream } from 'node:fs'
2+
import { mkdtemp, rename, rm } from 'node:fs/promises'
3+
import path from 'node:path'
4+
import { Readable } from 'node:stream'
5+
import { pipeline } from 'node:stream/promises'
6+
import { createLogger } from '@sim/logger'
7+
import { compareStrings } from '@sim/utils/string'
8+
import { plugin } from 'bun'
9+
import { createMdxPlugin } from 'fumadocs-mdx/bun'
10+
11+
const MAX_LLM_TEXT_BYTES = 64 * 1024 * 1024
12+
const logger = createLogger('DocsLlmsFullAsset')
13+
14+
await plugin(createMdxPlugin({ disableMetaFile: true }))
15+
16+
const { source } = await import('@/lib/source')
17+
const { getLLMText } = await import('@/lib/llms')
18+
const publicDirectory = path.resolve('public')
19+
const temporaryDirectory = await mkdtemp(path.join(publicDirectory, '.llms-full-'))
20+
const temporaryFile = path.join(temporaryDirectory, 'llms-full.txt')
21+
22+
async function* generateText(): AsyncGenerator<string> {
23+
let hasContent = false
24+
let bytes = 0
25+
const pages = [...source.getPages()].sort((left, right) => compareStrings(left.url, right.url))
26+
for (const page of pages) {
27+
if (!page?.data || !page.url) continue
28+
const text = await getLLMText(page)
29+
if (!text) continue
30+
const chunk = `${hasContent ? '\n\n---\n\n' : ''}${text}`
31+
bytes += Buffer.byteLength(chunk)
32+
if (bytes > MAX_LLM_TEXT_BYTES) {
33+
throw new Error('Full documentation text exceeds the static asset size budget')
34+
}
35+
yield chunk
36+
hasContent = true
37+
}
38+
if (!hasContent) throw new Error('No documentation text was generated')
39+
}
40+
41+
try {
42+
await pipeline(Readable.from(generateText()), createWriteStream(temporaryFile))
43+
await rename(temporaryFile, path.join(publicDirectory, 'llms-full.txt'))
44+
logger.info('Generated full documentation static asset')
45+
} finally {
46+
await rm(temporaryDirectory, { recursive: true, force: true })
47+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { watch } from 'node:fs'
2+
import { createLogger } from '@sim/logger'
3+
import { spawn } from 'bun'
4+
import { OPENAPI_SPEC_FILES } from '@/lib/openapi-specs'
5+
6+
const logger = createLogger('DocsLlmsFullWatcher')
7+
const WATCHED_ROOT_FILES = new Set(['source.config.ts', ...OPENAPI_SPEC_FILES])
8+
9+
let debounceTimer: ReturnType<typeof setTimeout> | undefined
10+
let pending = false
11+
let running = false
12+
13+
async function regenerate(): Promise<void> {
14+
running = true
15+
try {
16+
while (pending) {
17+
pending = false
18+
const child = spawn(['bun', 'run', 'llms-full:generate'], {
19+
stdin: 'ignore',
20+
stdout: 'inherit',
21+
stderr: 'inherit',
22+
})
23+
const exitCode = await child.exited
24+
if (exitCode !== 0) {
25+
logger.warn('Documentation asset generation failed; retaining the previous asset', {
26+
exitCode,
27+
})
28+
}
29+
}
30+
} finally {
31+
running = false
32+
}
33+
}
34+
35+
function scheduleGeneration(): void {
36+
pending = true
37+
if (running) return
38+
if (debounceTimer) clearTimeout(debounceTimer)
39+
debounceTimer = setTimeout(() => {
40+
debounceTimer = undefined
41+
void regenerate()
42+
}, 250)
43+
}
44+
45+
for (const directory of ['content', 'lib']) {
46+
watch(directory, { recursive: true }, scheduleGeneration)
47+
}
48+
49+
watch('.', (_, changedPath) => {
50+
if (changedPath && WATCHED_ROOT_FILES.has(changedPath.toString())) scheduleGeneration()
51+
})

‎apps/docs/turbo.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": ["//"],
3+
"tasks": {
4+
"build": {
5+
"outputs": ["$TURBO_EXTENDS$", "public/llms-full.txt"]
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)