Skip to content

Vector example with @orama/plugin-embeddings not working #925

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
diego-betto opened this issue Apr 17, 2025 · 4 comments
Open

Vector example with @orama/plugin-embeddings not working #925

diego-betto opened this issue Apr 17, 2025 · 4 comments

Comments

@diego-betto
Copy link

Describe the bug

Hi,

it seems that the second example, the one using @orama/plugin-embeddings does not work as expected.

From what I can see:

  • the first import is missing insert and search
  • then the search throws an error
    file:///..../node_modules/@orama/orama/dist/esm/methods/search-vector.js:15
      const vectorIndex = orama.data.index.vectorIndexes[vector.property];
    
    TypeError: Cannot read properties of undefined (reading 'property')
    
    Maybe we need to add a check for vector is being defined here or use another import.

Also check the first example. It imports remove‘ and searchVector’ but doesn't use them. It's not a big deal, but I think it's better to remove them or add some code that uses them for better understanding.

Regards,
Diego

To Reproduce

Just try the example with @orama/plugin-embeddings

Expected behavior

To run without issues

Environment Info

OS: Ubuntu 24.04.2 LTS
Node: v22.14.0

Packages
    "@orama/orama": "^3.1.6",
    "@orama/plugin-embeddings": "^3.1.6",
    "@tensorflow/tfjs-node": "^4.22.0"

Affected areas

Initialization, Search

Additional context

No response

@lights0123
Copy link

Just ran into this! It's a problem with Orama's bundler. beforeSearch is an async function:

async beforeSearch<T extends AnyOrama>(_db: AnyOrama, params: SearchParams<T, TypedDocument<any>>) {

Orama switches into a special mode when it detects that hook as an async function:

const needAsync = hooks.some(isAsyncFunction)

That's done by looking at the function's constructor:

return func?.constructor?.name === 'AsyncFunction'

But...

Orama's bundler converts it away from an async function! If you look at the code published to npm, you'll see that beforeSearch is defined as (prettified):

beforeSearch: function t(t, i2) {
  return r(function () {
    var r2, t2, a3, l2, c2;
    return n(this, function (n2) {
      // omitted
    });
  })();
}

This is not an async function, because it was transformed by the bundler!

@lights0123
Copy link

As a workaround, you can do this:

pluginEmbeddings({
	embeddings: {
		// Property used to store generated embeddings. Must be defined in the schema.
		defaultProperty: 'embeddings',
		onInsert: {
			// Generate embeddings at insert-time.
			// Turn off if you're inserting documents with embeddings already generated.
			generate: true,
			// Properties to use for generating embeddings at insert time.
			// These properties will be concatenated and used to generate embeddings.
			properties: ['contents'],
			verbose: true,
		},
	},
}).then(async (p) => {
        // shouldn't need this but there's a mistake in the TypeScript types
	const plugin = await p;
	// the fix
	plugin.beforeSearch.constructor = (async () => {}).constructor;
	plugin.beforeInsert.constructor = (async () => {}).constructor;
	// now use it
});

@micheleriva
Copy link
Member

micheleriva commented Apr 25, 2025

Hey @lights0123 that's a nice catch... any suggestion for a fix at the bundler level?

@lights0123
Copy link

lights0123 commented Apr 25, 2025

Async functions are part of ES7, and your bundler tsup reads tsconfig.json so it should be as simple as changing this line to ES7:

You'll also need to add to your documentation that users should configure their bundlers in the same way, as they typically re-process files. This drops support for IE11.


However, I think it would be better to just re-write some logic to look at the return type of the function—I don't really like that async () => {} works while () => new Promise(() => {}) doesn't. You could do that by replacing

const needAsync = hooks.some(isAsyncFunction)
if (needAsync) {
return (async () => {
for (const hook of hooks) {
await hook(db, params, language)
}
})()
} else {
for (const hook of hooks) {
hook(db, params, language)
}
}

with (untested):

  let promise: Promise<void> | undefined;

  for (const hook of hooks) {
    if (promise) {
      // If we already have a promise chain, extend it
      // by running the current hook after previous promises resolve
      promise = promise.then(() => hook(db, params, language));
    } else {
      // First hook or no promises yet
      const result = hook(db, params, language);
      if (typeof result?.then === 'function') {
        // Start a promise chain
        promise = result;
      }
    }
  }

  return promise;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants