Skip to content
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

The "path" argument must be of type string or an instance of URL. Received undefined #916

Open
1 of 5 tasks
firdausai opened this issue Sep 1, 2024 · 2 comments
Open
1 of 5 tasks
Labels
bug Something isn't working

Comments

@firdausai
Copy link

System Info

"@xenova/transformers": "^2.17.2"
Electron (Obsidian.md)

Environment/Platform

  • Website/web-app
  • Browser extension
  • Server-side (e.g., Node.js, Deno, Bun)
  • Desktop app (e.g., Electron)
  • Other (e.g., VSCode extension)

Description

I am trying to build an obsidian.md plugin, where I want to load a local model (or hosted on huggingface is fine too) using transformer.js. I went through several issues.

  1. Current obsidian.md plugin template uses ES2018, but transformer.js need at least ES2020 due to BigInt. I believe I solved that by changing the target build.
  2. After changing the target, the error changed to The "path" argument must be of type string or an instance of URL. Received undefined.

Not sure if its a pure transformer.js or it is caused something by changing the target build.

Code in question:

[main.ts]

import { pipeline } from '@xenova/transformers';

export default class MyPlugin extends Plugin {
   ...

   const classifier = await pipeline('sentiment-analysis');       // Error starts here
   const output = await classifier('I love transformers!');

   ...
}

I also tried other pipeline value like below, but the error is still the same:

const extractor = await pipeline('feature-extraction', 'Xenova/bert-base-uncased', { revision: 'default' });
const output = await extractor('This is a simple test.');

Reproduction

  1. git clone https://github.com/obsidianmd/obsidian-sample-plugin.git
  2. add const classifier = await pipeline('sentiment-analysis'); const output = await classifier('I love transformers!'); in MyPlugin class.
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, WorkspaceLeaf } from 'obsidian';
import { ExampleView, VIEW_TYPE_EXAMPLE } from "./view";
import { pipeline } from '@xenova/transformers';

// Remember to rename these classes and interfaces!

interface MyPluginSettings {
	mySetting: string;
}

const DEFAULT_SETTINGS: MyPluginSettings = {
	mySetting: 'default'
}

export default class MyPlugin extends Plugin {
	settings: MyPluginSettings;

	async onload() {

		console.log("foo asd")

		const classifier = await pipeline('sentiment-analysis');
		const output = await classifier('I love transformers!');

		console.log("output")
		console.log(output)

		this.registerView(
      VIEW_TYPE_EXAMPLE,
      (leaf) => new ExampleView(leaf)
    );

		await this.loadSettings();

		// This creates an icon in the left ribbon.
		const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
			// Called when the user clicks the icon.
			new Notice('This is a notice!');
		});

		this.addRibbonIcon("brain", "Open Plugin", () => {
      this.activateView();
    });
		
		// Perform additional things with the ribbon
		ribbonIconEl.addClass('my-plugin-ribbon-class');

		// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
		const statusBarItemEl = this.addStatusBarItem();
		statusBarItemEl.setText('Status Bar Text');

		// This adds a simple command that can be triggered anywhere
		this.addCommand({
			id: 'open-sample-modal-simple',
			name: 'Open sample modal (simple)',
			callback: () => {
				new SampleModal(this.app).open();
			}
		});
		// This adds an editor command that can perform some operation on the current editor instance
		this.addCommand({
			id: 'sample-editor-command',
			name: 'Sample editor command',
			editorCallback: (editor: Editor, view: MarkdownView) => {
				console.log(editor.getSelection());
				editor.replaceSelection('Sample Editor Command');
			}
		});
		// This adds a complex command that can check whether the current state of the app allows execution of the command
		this.addCommand({
			id: 'open-sample-modal-complex',
			name: 'Open sample modal (complex)',
			checkCallback: (checking: boolean) => {
				// Conditions to check
				const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
				if (markdownView) {
					// If checking is true, we're simply "checking" if the command can be run.
					// If checking is false, then we want to actually perform the operation.
					if (!checking) {
						new SampleModal(this.app).open();
					}

					// This command will only show up in Command Palette when the check function returns true
					return true;
				}
			}
		});

		// This adds a settings tab so the user can configure various aspects of the plugin
		this.addSettingTab(new SampleSettingTab(this.app, this));

		// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
		// Using this function will automatically remove the event listener when this plugin is disabled.
		this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
			console.log('click', evt);
		});

		// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
		this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
	}

	async activateView() {
    const { workspace } = this.app;

    let leaf: WorkspaceLeaf | null = null;
    const leaves = workspace.getLeavesOfType(VIEW_TYPE_EXAMPLE);

    if (leaves.length > 0) {
      // A leaf with our view already exists, use that
      leaf = leaves[0];
    } else {
      // Our view could not be found in the workspace, create a new leaf
      // in the right sidebar for it
      leaf = workspace.getRightLeaf(false);
      await leaf!.setViewState({ type: VIEW_TYPE_EXAMPLE, active: true });
    }

    // "Reveal" the leaf in case it is in a collapsed sidebar
    workspace.revealLeaf(leaf!);
  }

	onunload() {
		console.log("onunload")

	}

	async loadSettings() {
		this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
	}

	async saveSettings() {
		await this.saveData(this.settings);
	}
}

class SampleModal extends Modal {
	constructor(app: App) {
		super(app);
	}

	onOpen() {
		const {contentEl} = this;
		contentEl.setText('Woah!');
	}

	onClose() {
		const {contentEl} = this;
		contentEl.empty();
	}
}

class SampleSettingTab extends PluginSettingTab {
	plugin: MyPlugin;

	constructor(app: App, plugin: MyPlugin) {
		super(app, plugin);
		this.plugin = plugin;
	}

	display(): void {
		const {containerEl} = this;

		containerEl.empty();

		new Setting(containerEl)
			.setName('Setting #1')
			.setDesc('It\'s a secret')
			.addText(text => text
				.setPlaceholder('Enter your secret')
				.setValue(this.plugin.settings.mySetting)
				.onChange(async (value) => {
					this.plugin.settings.mySetting = value;
					await this.plugin.saveSettings();
				}));
	}
}
@firdausai firdausai added the bug Something isn't working label Sep 1, 2024
@firdausai
Copy link
Author

I have pinpoint the problem to esbuild. This line of code is the issue

@xenova/transformers/src/env.js

const __dirname = RUNNING_LOCALLY
    ? path.dirname(path.dirname(url.fileURLToPath(import.meta.url)))
    : './';

the current esbuild config change import.meta.url into import_meta.url. However, this was fixed by changing some setting as per this github discussion.

https://github.com/evanw/esbuild/commit/ac97be74c3b5453740f84fc40098ccae95cde19f

...

    The `import.meta` syntax is now passed through unmodified when the target is `es2020` or newer and the output format is `esm`. This lets you use features such as `import.meta.url` in those situations.

This worked and it doesn't change the variable anymore. However, since I changed the output format, I come across a new error, which prevents me from using import and require me to use required() when importing a module. So far, only transformers/src/env.js amd transformers/src/utils/hub.js used import, so I change them manually in the output dir (not ideal, but I just want to see if it would work). It did work, but now I ran into another issue and have this error Cannot use 'import.meta' outside a module. This is where I am still stuck.

@gyagp
Copy link

gyagp commented Sep 2, 2024

Please refer microsoft/onnxruntime#20165 (comment). More discussion there is welcome. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants