From c2315ea8949a4e1cc3b3d467902e0bf5f3fa48dc Mon Sep 17 00:00:00 2001 From: Adhityan K V Date: Mon, 30 Dec 2024 19:06:10 +0100 Subject: [PATCH] feat: added working image loader --- docs/components/data-sources/image.mdx | 31 ++++++++++ docs/components/data-sources/overview.mdx | 1 + docs/mint.json | 1 + examples/image/assets/test.jpg | Bin 0 -> 52816 bytes examples/image/eslint.config.js | 3 + examples/image/package.json | 11 ++++ examples/image/project.json | 57 ++++++++++++++++++ examples/image/src/main.ts | 17 ++++++ examples/image/tsconfig.app.json | 9 +++ examples/image/tsconfig.json | 17 ++++++ loaders/embedjs-loader-image/package.json | 3 +- .../embedjs-loader-image/src/image-loader.ts | 15 +++-- package-lock.json | 7 +++ scripts/publish-via-nx.js | 14 +++++ 14 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 docs/components/data-sources/image.mdx create mode 100644 examples/image/assets/test.jpg create mode 100644 examples/image/eslint.config.js create mode 100644 examples/image/package.json create mode 100644 examples/image/project.json create mode 100644 examples/image/src/main.ts create mode 100644 examples/image/tsconfig.app.json create mode 100644 examples/image/tsconfig.json diff --git a/docs/components/data-sources/image.mdx b/docs/components/data-sources/image.mdx new file mode 100644 index 00000000..bdaa2e44 --- /dev/null +++ b/docs/components/data-sources/image.mdx @@ -0,0 +1,31 @@ +--- +title: '🖼️ Image' +--- + +You can load most images using an LLM to describe the contents of the image. This LLM by default is the one used with the application, +but a special LLM can also be specified for the content description. To load images, follow the steps below. + +## Install Image addon + +```bash +npm install @llm-tools/embedjs-loader-image +``` + +## Usage + +### Load from a local file + +```ts +import { RAGApplicationBuilder } from '@llm-tools/embedjs'; +import { ImageLoader } from '@llm-tools/embedjs-loader-image'; +import { OpenAiEmbeddings } from '@llm-tools/embedjs-openai'; +import { HNSWDb } from '@llm-tools/embedjs-hnswlib'; + +const app = await new RAGApplicationBuilder() +.setModel(SIMPLE_MODELS.OPENAI_GPT4_O) +.setEmbeddingModel(new OpenAiEmbeddings()) +.setVectorDatabase(new HNSWDb()) +.build(); + +app.addLoader(new ImageLoader({ filePathOrUrl: '/path/to/file.jpeg' })) +``` \ No newline at end of file diff --git a/docs/components/data-sources/overview.mdx b/docs/components/data-sources/overview.mdx index 8c826508..3c63ecd3 100644 --- a/docs/components/data-sources/overview.mdx +++ b/docs/components/data-sources/overview.mdx @@ -22,6 +22,7 @@ We handle the complexity of loading unstructured data from these data sources, a + diff --git a/docs/mint.json b/docs/mint.json index 56164fc9..07e00dba 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -84,6 +84,7 @@ "components/data-sources/markdown", "components/data-sources/xml", "components/data-sources/directory", + "components/data-sources/image", "components/data-sources/custom" ] } diff --git a/examples/image/assets/test.jpg b/examples/image/assets/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3bdea555206c79a23301dfde8fe9b03cc2f4a4e2 GIT binary patch literal 52816 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 I*ng(}8?y8)Jpcdz literal 0 HcmV?d00001 diff --git a/examples/image/eslint.config.js b/examples/image/eslint.config.js new file mode 100644 index 00000000..e2a15f2a --- /dev/null +++ b/examples/image/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from '../../eslint.config.js'; + +export default [...baseConfig]; diff --git a/examples/image/package.json b/examples/image/package.json new file mode 100644 index 00000000..2609813b --- /dev/null +++ b/examples/image/package.json @@ -0,0 +1,11 @@ +{ + "name": "@llm-tools/embedjs-examples-image", + "version": "0.1.1", + "type": "module", + "dependencies": { + "dotenv": "^16.4.7" + }, + "scripts": { + "start": "nx run examples-image:serve" + } +} diff --git a/examples/image/project.json b/examples/image/project.json new file mode 100644 index 00000000..11fc57ea --- /dev/null +++ b/examples/image/project.json @@ -0,0 +1,57 @@ +{ + "name": "examples-image", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "examples/image/src", + "projectType": "application", + "tags": [], + "targets": { + "build": { + "executor": "@nx/esbuild:esbuild", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "development", + "options": { + "platform": "node", + "outputPath": "dist/examples/image", + "format": ["esm"], + "bundle": true, + "main": "examples/image/src/main.ts", + "tsConfig": "examples/image/tsconfig.app.json", + "generatePackageJson": false, + "esbuildOptions": { + "sourcemap": true, + "outExtension": { + ".js": ".js" + } + } + }, + "configurations": { + "development": {}, + "production": { + "esbuildOptions": { + "sourcemap": false, + "outExtension": { + ".js": ".js" + } + } + } + } + }, + "serve": { + "executor": "@nx/js:node", + "defaultConfiguration": "development", + "dependsOn": ["build"], + "options": { + "buildTarget": "examples-image:build", + "runBuildTargetDependencies": true + }, + "configurations": { + "development": { + "buildTarget": "examples-image:build:development" + }, + "production": { + "buildTarget": "examples-image:build:production" + } + } + } + } +} diff --git a/examples/image/src/main.ts b/examples/image/src/main.ts new file mode 100644 index 00000000..5a546a5b --- /dev/null +++ b/examples/image/src/main.ts @@ -0,0 +1,17 @@ +import 'dotenv/config'; +import path from 'node:path'; +import { RAGApplicationBuilder, SIMPLE_MODELS } from '@llm-tools/embedjs'; +import { ImageLoader } from '@llm-tools/embedjs-loader-image'; +import { OpenAiEmbeddings } from '@llm-tools/embedjs-openai'; +import { HNSWDb } from '@llm-tools/embedjs-hnswlib'; + +const ragApplication = await new RAGApplicationBuilder() + .setModel(SIMPLE_MODELS.OPENAI_GPT4_O) + .setEmbeddingModel(new OpenAiEmbeddings()) + .setVectorDatabase(new HNSWDb()) + .build(); + +const imagePath = path.resolve('./examples/image/assets/test.jpg'); +await ragApplication.addLoader(new ImageLoader({ filePathOrUrl: imagePath })); + +await ragApplication.query('How does deep learning relate to artifical intelligence'); diff --git a/examples/image/tsconfig.app.json b/examples/image/tsconfig.app.json new file mode 100644 index 00000000..5a8c35da --- /dev/null +++ b/examples/image/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/examples/image/tsconfig.json b/examples/image/tsconfig.json new file mode 100644 index 00000000..c60cf5c2 --- /dev/null +++ b/examples/image/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ], + "compilerOptions": { + "esModuleInterop": true, + "target": "ES2022", + "lib": ["ES2022", "ES2022.Object"], + "module": "NodeNext", + "moduleResolution": "nodenext" + } +} diff --git a/loaders/embedjs-loader-image/package.json b/loaders/embedjs-loader-image/package.json index a2f66cab..faa42582 100644 --- a/loaders/embedjs-loader-image/package.json +++ b/loaders/embedjs-loader-image/package.json @@ -9,7 +9,8 @@ "debug": "^4.4.0", "md5": "^2.3.0", "mime": "^4.0.6", - "stream-mime-type": "^2.0.0" + "stream-mime-type": "^2.0.0", + "exifremove": "^1.0.1" }, "type": "module", "main": "./src/index.js", diff --git a/loaders/embedjs-loader-image/src/image-loader.ts b/loaders/embedjs-loader-image/src/image-loader.ts index 491a1b2f..7f5e669c 100644 --- a/loaders/embedjs-loader-image/src/image-loader.ts +++ b/loaders/embedjs-loader-image/src/image-loader.ts @@ -1,11 +1,12 @@ import { HumanMessage } from '@langchain/core/messages'; import { getMimeType } from 'stream-mime-type'; import createDebugMessages from 'debug'; +import exifremove from 'exifremove'; import fs from 'node:fs'; import md5 from 'md5'; import { BaseLoader, BaseModel } from '@llm-tools/embedjs-interfaces'; -import { cleanString, contentTypeToMimeType, getSafe, isValidURL, streamToString } from '@llm-tools/embedjs-utils'; +import { cleanString, contentTypeToMimeType, getSafe, isValidURL, streamToBuffer } from '@llm-tools/embedjs-utils'; export class ImageLoader extends BaseLoader<{ type: 'ImageLoader' }> { private readonly debug = createDebugMessages('embedjs:loader:ImageLoader'); @@ -60,9 +61,11 @@ export class ImageLoader extends BaseLoader<{ type: 'ImageLoader' }> { } this.debug(`Image stream detected type '${this.mime}'`); - const text = this.isUrl - ? (await getSafe(this.filePathOrUrl, { format: 'text' })).body - : await streamToString(fs.createReadStream(this.filePathOrUrl)); + const buffer = this.isUrl + ? (await getSafe(this.filePathOrUrl, { format: 'buffer' })).body + : await streamToBuffer(fs.createReadStream(this.filePathOrUrl)); + + const plainImageBuffer = exifremove.remove(buffer); const message = new HumanMessage({ content: [ @@ -73,7 +76,7 @@ export class ImageLoader extends BaseLoader<{ type: 'ImageLoader' }> { { type: 'image_url', image_url: { - url: `data:${this.mime};base64,${btoa(text)}`, + url: `data:${this.mime};base64,${plainImageBuffer.toString('base64')}`, }, }, ], @@ -81,7 +84,7 @@ export class ImageLoader extends BaseLoader<{ type: 'ImageLoader' }> { this.debug('Asking LLM to describe image'); const response = await this.captionModel.simpleQuery([message]); - this.debug('LLM describes image as', response.result); + this.debug('LLM describes image as: ', response.result); yield { pageContent: cleanString(response.result), diff --git a/package-lock.json b/package-lock.json index 0407ecff..dd19c991 100644 --- a/package-lock.json +++ b/package-lock.json @@ -346,6 +346,7 @@ "@llm-tools/embedjs-interfaces": "0.1.27", "@llm-tools/embedjs-utils": "0.1.27", "debug": "^4.4.0", + "exifremove": "^1.0.1", "md5": "^2.3.0", "mime": "^4.0.6", "stream-mime-type": "^2.0.0" @@ -12728,6 +12729,12 @@ "node": ">=0.8.x" } }, + "node_modules/exifremove": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exifremove/-/exifremove-1.0.1.tgz", + "integrity": "sha512-a4VrNsSgpKBo9dHFsRi098XYm/X8dHP2NPA4N/3WAIxInQ8VHOfic/F8uAMIlWk4QRurTFwClFUb0QkMj4nqHg==", + "license": "MIT" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", diff --git a/scripts/publish-via-nx.js b/scripts/publish-via-nx.js index 273f604f..4751e4bd 100644 --- a/scripts/publish-via-nx.js +++ b/scripts/publish-via-nx.js @@ -12,6 +12,19 @@ function abs(relativePath) { return resolve(dirname(fileURLToPath(import.meta.url)), relativePath); } +/** + * @param {string} version - The version to update the root package to + * @param {boolean} dryRun - Whether to perform a dry run or not + */ +async function updateRootPackageVersion(version, dryRun) { + const absPath = abs('..'); + console.log(`Updating root package at path '${absPath}' to version '${version}' ${dryRun ? '[dry run]' : ''}`); + const pkgJson = await PackageJson.load(absPath); + pkgJson.update({ version }); + + if (!dryRun) await pkgJson.save(); +} + /** * @param {pkgName} pkgName - The name of the package to update * @param {string} version - The version to update the package to @@ -76,6 +89,7 @@ async function createRelease(dryRun, version, makeGitCommit) { } console.log('Updating projects actual version to match NX computed values'); + await updateRootPackageVersion(newVersion, dryRun); for await (const [pkgName, { newVersion }] of Object.entries(projectsVersionData)) { if (newVersion !== null) await updatePackageVersion(pkgName, newVersion, versionMap, dryRun); else console.log(`Skipping '${pkgName}' version update as it's already up to date`);