Skip to content

Commit 42198b5

Browse files
authored
Add script for generating llms.txt and llms-full.txt (#222)
* Add script for generating llms.txt and llms-full.txt * Use the title instead of the filename to identify links * Don't validate llms links * Force .html extension on llms.txt links * Declare the pageheader string pattern as a variable * Add page headers to header text as a clue in llms-full.txt
1 parent bac2ead commit 42198b5

File tree

7 files changed

+163
-3
lines changed

7 files changed

+163
-3
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ docs/.vitepress/dist/
99
docs/.vitepress/.temp
1010
.firebase/
1111

12+
docs/public/llms.txt
13+
docs/public/llms-full.txt
14+
1215
/indexes/content-files/
1316

1417
yarn-error.log

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ pnpm-lock.yaml
1414
/docs/public/img
1515
/docs/public/api3-whitepaper-v1.0.3.pdf
1616

17+
docs/public/llms.txt
18+
docs/public/llms-full.txt
19+
1720
# assets
1821
/docs/reference/ois/latest/assets
1922
/docs/reference/airnode/latest/assets

docs/dapps/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ Api3 provides data feeds and pays dApps for using them.
1313

1414
2. Api3 enables the Oracle Extractable Value (OEV) resulting from the usage of these data feeds to be captured, and pays it to the respective dApps in the form of [OEV Rewards.](#oev-rewards)
1515

16+
::: info 💡 Tip
17+
18+
For quick reference, you can copy-paste [`llms-full.txt`](https://docs.api3.org/llms-full.txt) to your choice of AI assistant.
19+
20+
:::
21+
1622
## Api3 Market
1723

1824
Liquidity is increasingly shifting to newly launched L2 networks, and dApps that are able to branch out to these more quickly are at a significant competitive advantage.

docs/oev-searchers/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ bound by rules enforced on-chain. The auction winner must pay the announced
2828
amount, which in return allows them to perform the oracle update and capture
2929
profitable opportunities.
3030

31+
::: info 💡 Tip
32+
33+
For quick reference, you can copy-paste [`llms-full.txt`](https://docs.api3.org/llms-full.txt) to your choice of AI assistant.
34+
35+
:::
36+
3137
## Practical example
3238

3339
Imagine an overcollateralized lending platform that uses Api3 price feeds.

libs/link-validator-ignore.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@
77
"https://orbitlending.io/",
88
"https://www.coinbase.com/blog/introducing-the-coinbase-price-oracle",
99
"https://x.com",
10-
"https://blastscan.io/address/0x5b0cf2b36a65a6BB085D501B971e4c102B9Cd473#readProxyContract#F17"
10+
"https://blastscan.io/address/0x5b0cf2b36a65a6BB085D501B971e4c102B9Cd473#readProxyContract#F17",
11+
"https://docs.api3.org/llms.txt",
12+
"https://docs.api3.org/llms-full.txt"
1113
]

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
"packageManager": "[email protected]",
77
"resolutions": {},
88
"scripts": {
9-
"docs:dev": "pnpm vitepress dev docs",
10-
"docs:build": "pnpm vitepress build docs;",
9+
"docs:dev": "pnpm generate-llms-files && pnpm vitepress dev docs",
10+
"docs:build": "pnpm generate-llms-files && pnpm vitepress build docs;",
1111
"docs:serve": "vitepress serve docs --port 8082",
1212
"format": "prettier --write --cache \"./**/*.{js,vue,md,json,yaml}\" --log-level silent",
1313
"format:check": "prettier --check --cache \"./**/*.{js,vue,md,json,yaml}\"",
14+
"generate-llms-files": "node scripts/generate-llms-files.js",
1415
"prepare": "husky",
1516
"firebase:emulator": "pnpm docs:build; firebase emulators:start"
1617
},

scripts/generate-llms-files.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const dappsSidebar = require('../docs/dapps/sidebar.js');
4+
const oevSearchersSidebar = require('../docs/oev-searchers/sidebar.js');
5+
6+
const docsDir = path.join(__dirname, '..', 'docs');
7+
const staticDir = path.join(__dirname, '..', 'docs', 'public');
8+
const llmsTxtPath = path.join(staticDir, 'llms.txt');
9+
const llmsFullTxtPath = path.join(staticDir, 'llms-full.txt');
10+
11+
function getMarkdownFiles(items) {
12+
let files = [];
13+
for (const item of items) {
14+
if (item.link) {
15+
files.push(item.link);
16+
}
17+
if (item.items) {
18+
files = files.concat(getMarkdownFiles(item.items));
19+
}
20+
}
21+
return files;
22+
}
23+
24+
function generateLlmsTxt() {
25+
let content = '# Api3 Docs\n\n';
26+
content += `> Api3 Docs helps developers build dApps using Api3 data feeds and searchers recapture oracle extractable value (OEV).\n\n`;
27+
28+
const sidebars = {
29+
dapps: dappsSidebar,
30+
'oev-searchers': oevSearchersSidebar,
31+
};
32+
33+
for (const key in sidebars) {
34+
const sidebar = sidebars[key];
35+
if (sidebar) {
36+
content += `## ${key}\n\n`;
37+
const files = getMarkdownFiles(sidebar);
38+
for (const file of files) {
39+
const filePath = path.join(docsDir, `${file}.md`);
40+
if (fs.existsSync(filePath)) {
41+
const fileContent = fs.readFileSync(filePath, 'utf-8');
42+
const lines = fileContent.split('\n');
43+
let title = path.basename(file, '.md');
44+
for (const line of lines) {
45+
if (line.startsWith('title: ')) {
46+
title = line.substring('title: '.length);
47+
break;
48+
}
49+
}
50+
const url = `https://docs.api3.org${file}`;
51+
content += `- [${title}](${url.replace(/\/$/, '/index')}.html)\n`;
52+
} else {
53+
const indexPath = path.join(docsDir, file, 'index.md');
54+
if (fs.existsSync(indexPath)) {
55+
const fileContent = fs.readFileSync(indexPath, 'utf-8');
56+
const lines = fileContent.split('\n');
57+
let title = path.basename(file);
58+
for (const line of lines) {
59+
if (line.startsWith('title: ')) {
60+
title = line.substring('title: '.length);
61+
break;
62+
}
63+
}
64+
const url = `https://docs.api3.org${file}`;
65+
content += `- [${title}](${url.replace(/\/$/, '/index.html')})\n`;
66+
}
67+
}
68+
}
69+
content += '\n';
70+
}
71+
}
72+
73+
fs.writeFileSync(llmsTxtPath, content);
74+
console.log(`Successfully created ${llmsTxtPath}`);
75+
}
76+
77+
function generateLlmsFullTxt() {
78+
const llmsTxtContent = fs.readFileSync(llmsTxtPath, 'utf-8');
79+
const links = llmsTxtContent.match(/- \[(.*?)\]\((.*?)\)/g);
80+
let fullContent = '';
81+
const pageHeader = '<PageHeader/>\n\n';
82+
83+
if (links) {
84+
for (const link of links) {
85+
const match = link.match(/- \[(.*?)\]\((.*?)\)/);
86+
if (match) {
87+
const url = match[2]
88+
.replace('https://docs.api3.org', '')
89+
.replace('.html', '.md');
90+
const filePath = path.join(docsDir, url);
91+
if (fs.existsSync(filePath)) {
92+
let fileContent = fs.readFileSync(filePath, 'utf-8');
93+
const lines = fileContent.split('\n');
94+
let pageHeaderValue = '';
95+
for (const line of lines) {
96+
if (line.startsWith('pageHeader: ')) {
97+
pageHeaderValue = line.substring('pageHeader: '.length);
98+
break;
99+
}
100+
}
101+
102+
const pageHeaderIndex = fileContent.indexOf(pageHeader);
103+
if (pageHeaderIndex === -1) {
104+
throw new Error(`Could not find PageHeader in ${filePath}`);
105+
}
106+
fileContent = fileContent.substring(
107+
pageHeaderIndex + pageHeader.length
108+
);
109+
const titleMatch = fileContent.match(/# (.*)/);
110+
if (titleMatch && pageHeaderValue) {
111+
fileContent = fileContent.replace(
112+
/# (.*)/,
113+
`# ${titleMatch[1]} (${pageHeaderValue})`
114+
);
115+
}
116+
fullContent += fileContent + '\n\n';
117+
}
118+
}
119+
}
120+
}
121+
122+
fs.writeFileSync(llmsFullTxtPath, fullContent);
123+
console.log(`Successfully created ${llmsFullTxtPath}`);
124+
}
125+
126+
function main() {
127+
try {
128+
if (!fs.existsSync(staticDir)) {
129+
fs.mkdirSync(staticDir, { recursive: true });
130+
}
131+
generateLlmsTxt();
132+
generateLlmsFullTxt();
133+
} catch (err) {
134+
console.error('Error creating llms files:', err);
135+
process.exit(1);
136+
}
137+
}
138+
139+
main();

0 commit comments

Comments
 (0)