Skip to content

Commit

Permalink
feat(core): attempt graceful fastembed import error handling, add tod…
Browse files Browse the repository at this point in the history
…o to try fastembed-web as fallback later (#1871)

This PR adds error handling for if the default embedder import fails. I
also added a TODO for trying to fallback to
[`fastembed-web`](https://www.npmjs.com/package/fastembed-web) later -
right now I can't try it cause our vercel deploys are failing for other
reasons

---------

Co-authored-by: Your Name <you@example.com>
  • Loading branch information
TylerBarnes and Your Name authored Feb 14, 2025
1 parent ce74a66 commit fe3dcb0
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/nasty-adults-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@mastra/core': patch
---

Add fastembed import error handling
67 changes: 57 additions & 10 deletions packages/core/src/vector/fastembed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,67 @@ function getModelCachePath() {
return cachedPath;
}

function unbundleableImport(name: string) {
const nonStaticallyAnalyzableName = `${name}?d=${Date.now()}`; // +? date to prevent esbuild from seeing this as statically analyzable and bundling this as a regular import.
return import(nonStaticallyAnalyzableName.split(`?`)[0]!); // remove the date to prevent module not found errors in cloud
}

let useFastEmbedWebFallback = false;

// Shared function to generate embeddings using fastembed
async function generateEmbeddings(values: string[], modelType: 'BGESmallENV15' | 'BGEBaseENV15') {
try {
// NOTE: This dynamic import janky dance is due to some runtimes not having support for the native bindings used by onnxruntime-node
// I made a fork of fastembed-js and published it as https://www.npmjs.com/package/fastembed-web
// The fork uses onnxruntime-web instead of the node package. Theoretically this fork has greater compatibility while also being less performant.
// If we need greater compat for this embedder we can do a dynamic import for fastembed-js and if the import fails we can do a second dynamic import for fastembed-web and try that
// I haven't done this yet because I got fastembed-js to work locally and in Mastra Cloud.
// If we need this to also work places like Netlify and Vercel and it's not working we will likely need to try using fastembed-web.
//
// Dynamically import fastembed only when this function is called
// this is to avoid importing fastembed in runtimes that don't support its native bindings
const fastEmbedImportPath = 'fastembed?d=' + Date.now(); // +? date to prevent esbuild from seeing this as statically analyzable and bundling this as a regular import.
const { EmbeddingModel, FlagEmbedding } = await import(fastEmbedImportPath.split(`?`)[0]!); // remove the date to prevent module not found errors in cloud
let mod: any;
const importErrors: Error[] = [];

if (useFastEmbedWebFallback) {
// TODO: see below
// mod = await unbundleableImport('fastembed-web');
} else {
try {
mod = await unbundleableImport('fastembed');
} catch (e) {
if (e instanceof Error) {
importErrors.push(e);
} else {
throw e;
}
// TODO: once vercel deploys are fixed, install fastembed-web and try it as a fallback
// try {
// mod = await unbundleableImport('fastembed-web');
// useFastEmbedWebFallback = true;
// } catch (e) {
// if (e instanceof Error) {
// importErrors.push(e);
// } else {
// throw e;
// }
// }
}
}

if (!mod) {
throw new Error(`${importErrors.map(e => e.message).join(`\n`)}
This runtime does not support fastembed-js, which is the default embedder in Mastra.
Scroll up to read import errors. These errors mean you can't use the default Mastra embedder on this hosting platform.
You can either use Mastra Cloud which supports the default embedder, or you can configure an alternate provider.
For example if you're using Memory:
import { openai } from "@ai-sdk/openai";
const memory = new Memory({
embedder: openai.embedding("text-embedding-3-small"), // <- doesn't have to be openai
})
Visit https://sdk.vercel.ai/docs/foundations/overview#embedding-models to find an alternate embedding provider
`);
}

const { FlagEmbedding, EmbeddingModel } = mod;

const model = await FlagEmbedding.init({
model: EmbeddingModel[modelType],
Expand All @@ -42,7 +89,7 @@ async function generateEmbeddings(values: string[], modelType: 'BGESmallENV15' |
for await (const result of embeddings) {
// result is an array of embeddings, one for each text in the batch
// We convert each Float32Array embedding to a regular number array
// @ts-ignore ? Date.now() import breaks types
// @ts-ignore unbundleableImport fn breaks types
allResults.push(...result.map(embedding => Array.from(embedding)));
}

Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fe3dcb0

Please sign in to comment.