( docsUrls: string[], orgId: number, isDocusaurus: boolean, ignoreLines: string[], )
| 17 | }); |
| 18 | |
| 19 | export async function embedDocsFromUrl( |
| 20 | docsUrls: string[], |
| 21 | orgId: number, |
| 22 | isDocusaurus: boolean, |
| 23 | ignoreLines: string[], |
| 24 | ): Promise<DocChunkInsert[]> { |
| 25 | // Split the links into chunks of 10 to avoid overloading the API |
| 26 | const docUrlChunks = []; |
| 27 | for (let i = 0; i < docsUrls.length; i += 10) { |
| 28 | docUrlChunks.push(docsUrls.slice(i, i + 10)); |
| 29 | } |
| 30 | // Iterate over the collected links |
| 31 | const out: DocChunkInsert[][][] = []; |
| 32 | for (const docUrlChunk of docUrlChunks) { |
| 33 | out.push( |
| 34 | await Promise.all( |
| 35 | docUrlChunk.map(async (url) => { |
| 36 | console.log("Loading documents from " + url); |
| 37 | // Initialize an APIReferenceLoader with the option to scrape visible content |
| 38 | let loader = getDocsLoader(url, isDocusaurus); |
| 39 | // Load the raw text from the web page |
| 40 | const raw_documents = await loader.load(); |
| 41 | const title = (/<title>(.*)<\/title>/.exec( |
| 42 | raw_documents[0].pageContent, |
| 43 | ) ?? [])[1]; |
| 44 | console.log(`Title: ${title}`); |
| 45 | |
| 46 | // Convert the HTML to Markdown |
| 47 | const md = turndownService.turndown( |
| 48 | // pageContent always starts with added <title> tag, so this must be removed |
| 49 | // (there shouldn't be other <title> tags in there since it's the page body that's returned) |
| 50 | raw_documents[0].pageContent.replace(/<title>(.*)<\/title>/, ""), |
| 51 | ); |
| 52 | console.log("md", md); |
| 53 | |
| 54 | // Extract headings and subheadings from the Markdown |
| 55 | const headerToSection = splitTextByHeaders(md); |
| 56 | |
| 57 | // For each heading, split the text into chunks by newline, filter out useless chunks, and split into 3s |
| 58 | const doc_chunks = Object.entries(headerToSection) |
| 59 | .map(([header, section]) => { |
| 60 | // If a line is just the title, skip it |
| 61 | if (!section.replaceAll(title, "").trim()) return []; |
| 62 | // Split the documents into text chunks |
| 63 | const lines = splitIntoTextChunks(section, [ |
| 64 | title, |
| 65 | ...ignoreLines, |
| 66 | ]); |
| 67 | |
| 68 | const chunkGroups = lines |
| 69 | // Remove null values (at end of array) e.g. ["text", null, null] -> ["text"] |
| 70 | .map((line, idx) => |
| 71 | [line, lines[idx + 1], lines[idx + 2]].filter( |
| 72 | (item) => item !== null, |
| 73 | ), |
| 74 | ) |
| 75 | // Slice to remove the last 2 chunk groups which are shorter than the rest |
| 76 | // Max to ensure we still embed very short doc pages |
nothing calls this directly
no test coverage detected