UNPKG

@langchain/community

Version:
1 lines • 11.7 kB
{"version":3,"file":"obsidian.cjs","names":["BaseDocumentLoader","filePath: string","content: string","yaml","fields: Record<string, string>","metadata: Record<string, unknown>","result: Record<string, unknown>","documents: Document[]","metadata: Document[\"metadata\"]","Document","DirectoryLoader","directoryPath: string","options?: ObsidianFileLoaderOptions","UnknownHandling"],"sources":["../../../src/document_loaders/fs/obsidian.ts"],"sourcesContent":["import type { basename as BasenameT } from \"node:path\";\nimport type { readFile as ReadFileT, stat as StatT } from \"node:fs/promises\";\nimport yaml from \"js-yaml\";\nimport { Document } from \"@langchain/core/documents\";\nimport { getEnv } from \"@langchain/core/utils/env\";\nimport { BaseDocumentLoader } from \"@langchain/core/document_loaders/base\";\nimport {\n DirectoryLoader,\n UnknownHandling,\n} from \"@langchain/classic/document_loaders/fs/directory\";\n\nexport type FrontMatter = {\n title?: string;\n description?: string;\n tags?: string[] | string;\n [key: string]: unknown;\n};\n\nexport interface ObsidianFileLoaderOptions {\n encoding?: BufferEncoding;\n collectMetadata?: boolean;\n}\n\n/**\n * Represents a loader for Obsidian markdown files. This loader extends the BaseDocumentLoader\n * and provides functionality to parse and extract metadata, tags, and dataview fields from\n * Obsidian markdown files.\n */\nclass ObsidianFileLoader extends BaseDocumentLoader {\n private filePath: string;\n\n private encoding: BufferEncoding;\n\n private collectMetadata: boolean;\n\n /**\n * Initializes a new instance of the ObsidianFileLoader class.\n * @param filePath The path to the Obsidian markdown file.\n * @param encoding The character encoding to use when reading the file. Defaults to 'utf-8'.\n * @param collectMetadata Determines whether metadata should be collected from the file. Defaults to true.\n */\n constructor(\n filePath: string,\n {\n encoding = \"utf-8\",\n collectMetadata = true,\n }: ObsidianFileLoaderOptions = {}\n ) {\n super();\n this.filePath = filePath;\n this.encoding = encoding;\n this.collectMetadata = collectMetadata;\n }\n\n private static FRONT_MATTER_REGEX = /^---\\n(.*?)\\n---\\n/s;\n\n /**\n * Parses the YAML front matter from the given content string.\n * @param content The string content of the markdown file.\n * @returns An object representing the parsed front matter.\n */\n private parseFrontMatter(content: string): FrontMatter {\n if (!this.collectMetadata) {\n return {};\n }\n\n const match = content.match(ObsidianFileLoader.FRONT_MATTER_REGEX);\n if (!match) {\n return {};\n }\n\n try {\n const frontMatter = yaml.load(match[1]) as FrontMatter;\n if (frontMatter.tags && typeof frontMatter.tags === \"string\") {\n frontMatter.tags = frontMatter.tags.split(\", \");\n }\n\n return frontMatter;\n } catch {\n console.warn(\"Encountered non-yaml frontmatter\");\n return {};\n }\n }\n\n /**\n * Removes YAML front matter from the given content string.\n * @param content The string content of the markdown file.\n * @returns The content string with the front matter removed.\n */\n private removeFrontMatter(content: string): string {\n if (!this.collectMetadata) {\n return content;\n }\n\n return content.replace(ObsidianFileLoader.FRONT_MATTER_REGEX, \"\");\n }\n\n private static TAG_REGEX = /(?:\\s|^)#([a-zA-Z_][\\w/-]*)/g;\n\n /**\n * Parses Obsidian-style tags from the given content string.\n * @param content The string content of the markdown file.\n * @returns A set of parsed tags.\n */\n private parseObsidianTags(content: string): Set<string> {\n if (!this.collectMetadata) {\n return new Set();\n }\n\n const matches = content.matchAll(ObsidianFileLoader.TAG_REGEX);\n const tags = new Set<string>();\n for (const match of matches) {\n tags.add(match[1]);\n }\n\n return tags;\n }\n\n private static DATAVIEW_LINE_REGEX = /^\\s*(\\w+)::\\s*(.*)$/gm;\n\n private static DATAVIEW_INLINE_BRACKET_REGEX = /\\[(\\w+)::\\s*(.*)\\]/gm;\n\n private static DATAVIEW_INLINE_PAREN_REGEX = /\\((\\w+)::\\s*(.*)\\)/gm;\n\n /**\n * Parses dataview fields from the given content string.\n * @param content The string content of the markdown file.\n * @returns A record object containing key-value pairs of dataview fields.\n */\n private parseObsidianDataviewFields(content: string): Record<string, string> {\n if (!this.collectMetadata) {\n return {};\n }\n\n const fields: Record<string, string> = {};\n const lineMatches = content.matchAll(\n ObsidianFileLoader.DATAVIEW_LINE_REGEX\n );\n for (const [, key, value] of lineMatches) {\n fields[key] = value;\n }\n\n const bracketMatches = content.matchAll(\n ObsidianFileLoader.DATAVIEW_INLINE_BRACKET_REGEX\n );\n for (const [, key, value] of bracketMatches) {\n fields[key] = value;\n }\n\n const parenMatches = content.matchAll(\n ObsidianFileLoader.DATAVIEW_INLINE_PAREN_REGEX\n );\n for (const [, key, value] of parenMatches) {\n fields[key] = value;\n }\n\n return fields;\n }\n\n /**\n * Converts metadata to a format compatible with Langchain.\n * @param metadata The metadata object to convert.\n * @returns A record object containing key-value pairs of Langchain-compatible metadata.\n */\n private toLangchainCompatibleMetadata(metadata: Record<string, unknown>) {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (typeof value === \"string\" || typeof value === \"number\") {\n result[key] = value;\n } else {\n result[key] = JSON.stringify(value);\n }\n }\n return result;\n }\n\n /**\n * It loads the Obsidian file, parses it, and returns a `Document` instance.\n * @returns An array of `Document` instances to comply with the BaseDocumentLoader interface.\n */\n public async load(): Promise<Document[]> {\n const documents: Document[] = [];\n\n const { basename, readFile, stat } = await ObsidianFileLoader.imports();\n const fileName = basename(this.filePath);\n const stats = await stat(this.filePath);\n let content = await readFile(this.filePath, this.encoding);\n\n const frontMatter = this.parseFrontMatter(content);\n const tags = this.parseObsidianTags(content);\n const dataviewFields = this.parseObsidianDataviewFields(content);\n content = this.removeFrontMatter(content);\n\n const metadata: Document[\"metadata\"] = {\n source: fileName,\n path: this.filePath,\n created: stats.birthtimeMs,\n lastModified: stats.mtimeMs,\n lastAccessed: stats.atimeMs,\n ...this.toLangchainCompatibleMetadata(frontMatter),\n ...dataviewFields,\n };\n\n if (tags.size || frontMatter.tags) {\n metadata.tags = Array.from(\n new Set([...tags, ...(frontMatter.tags ?? [])])\n ).join(\",\");\n }\n\n documents.push(\n new Document({\n pageContent: content,\n metadata,\n })\n );\n\n return documents;\n }\n\n /**\n * Imports the necessary functions from the `node:path` and\n * `node:fs/promises` modules. It is used to dynamically import the\n * functions when needed. If the import fails, it throws an error\n * indicating that the modules failed to load.\n * @returns A promise that resolves to an object containing the imported functions.\n */\n static async imports(): Promise<{\n basename: typeof BasenameT;\n readFile: typeof ReadFileT;\n stat: typeof StatT;\n }> {\n try {\n const { basename } = await import(\"node:path\");\n const { readFile, stat } = await import(\"node:fs/promises\");\n return { basename, readFile, stat };\n } catch (e) {\n console.error(e);\n throw new Error(\n `Failed to load fs/promises. ObsidianFileLoader available only on environment 'node'. It appears you are running environment '${getEnv()}'. See https://<link to docs> for alternatives.`\n );\n }\n }\n}\n\n/**\n * Represents a loader for directories containing Obsidian markdown files. This loader extends\n * the DirectoryLoader and provides functionality to load and parse '.md' files with YAML frontmatter,\n * Obsidian tags, and Dataview fields.\n */\nexport class ObsidianLoader extends DirectoryLoader {\n /**\n * Initializes a new instance of the ObsidianLoader class.\n * @param directoryPath The path to the directory containing Obsidian markdown files.\n * @param encoding The character encoding to use when reading files. Defaults to 'utf-8'.\n * @param collectMetadata Determines whether metadata should be collected from the files. Defaults to true.\n */\n constructor(directoryPath: string, options?: ObsidianFileLoaderOptions) {\n super(\n directoryPath,\n {\n \".md\": (filePath) => new ObsidianFileLoader(filePath, options),\n },\n true,\n UnknownHandling.Ignore\n );\n }\n}\n"],"mappings":"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"}