Giter Club home page Giter Club logo

huggingface.js's Introduction


huggingface javascript library logo

// Programatically interact with the Hub

await createRepo({
  repo: {type: "model", name: "my-user/nlp-model"},
  credentials: {accessToken: HF_TOKEN}
});

await uploadFile({
  repo: "my-user/nlp-model",
  credentials: {accessToken: HF_TOKEN},
  // Can work with native File in browsers
  file: {
    path: "pytorch_model.bin",
    content: new Blob(...) 
  }
});

// Use hosted inference

await inference.translation({
  model: 't5-base',
  inputs: 'My name is Wolfgang and I live in Berlin'
})

await inference.textToImage({
  model: 'stabilityai/stable-diffusion-2',
  inputs: 'award winning high resolution photo of a giant tortoise/((ladybird)) hybrid, [trending on artstation]',
  parameters: {
    negative_prompt: 'blurry',
  }
})

// and much more…

Hugging Face JS libraries

This is a collection of JS libraries to interact with the Hugging Face API, with TS types included.

  • @huggingface/inference: Use Inference Endpoints (dedicated) and Inference API (serverless) to make calls to 100,000+ Machine Learning models
  • @huggingface/hub: Interact with huggingface.co to create or delete repos and commit / download files
  • @huggingface/agents: Interact with HF models through a natural language interface

We use modern features to avoid polyfills and dependencies, so the libraries will only work on modern browsers / Node.js >= 18 / Bun / Deno.

The libraries are still very young, please help us by opening issues!

Installation

From NPM

To install via NPM, you can download the libraries as needed:

npm install @huggingface/inference
npm install @huggingface/hub
npm install @huggingface/agents

Then import the libraries in your code:

import { HfInference } from "@huggingface/inference";
import { HfAgent } from "@huggingface/agents";
import { createRepo, commit, deleteRepo, listFiles } from "@huggingface/hub";
import type { RepoId, Credentials } from "@huggingface/hub";

From CDN or Static hosting

You can run our packages with vanilla JS, without any bundler, by using a CDN or static hosting. Using ES modules, i.e. <script type="module">, you can import the libraries in your code:

<script type="module">
    import { HfInference } from 'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]/+esm';
    import { createRepo, commit, deleteRepo, listFiles } from "https://cdn.jsdelivr.net/npm/@huggingface/[email protected]/+esm";
</script>

Deno

// esm.sh
import { HfInference } from "https://esm.sh/@huggingface/inference"
import { HfAgent } from "https://esm.sh/@huggingface/agents";

import { createRepo, commit, deleteRepo, listFiles } from "https://esm.sh/@huggingface/hub"
// or npm:
import { HfInference } from "npm:@huggingface/inference"
import { HfAgent } from "npm:@huggingface/agents";

import { createRepo, commit, deleteRepo, listFiles } from "npm:@huggingface/hub"

Usage examples

Get your HF access token in your account settings.

@huggingface/inference examples

import { HfInference } from "@huggingface/inference";

const HF_TOKEN = "hf_...";

const inference = new HfInference(HF_TOKEN);

// You can also omit "model" to use the recommended model for the task
await inference.translation({
  model: 't5-base',
  inputs: 'My name is Wolfgang and I live in Amsterdam'
})

await inference.textToImage({
  model: 'stabilityai/stable-diffusion-2',
  inputs: 'award winning high resolution photo of a giant tortoise/((ladybird)) hybrid, [trending on artstation]',
  parameters: {
    negative_prompt: 'blurry',
  }
})

await inference.imageToText({
  data: await (await fetch('https://picsum.photos/300/300')).blob(),
  model: 'nlpconnect/vit-gpt2-image-captioning',  
})

// Using your own dedicated inference endpoint: https://hf.co/docs/inference-endpoints/
const gpt2 = inference.endpoint('https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2');
const { generated_text } = await gpt2.textGeneration({inputs: 'The answer to the universe is'});

@huggingface/hub examples

import { createRepo, uploadFile, deleteFiles } from "@huggingface/hub";

const HF_TOKEN = "hf_...";

await createRepo({
  repo: "my-user/nlp-model", // or {type: "model", name: "my-user/nlp-test"},
  credentials: {accessToken: HF_TOKEN}
});

await uploadFile({
  repo: "my-user/nlp-model",
  credentials: {accessToken: HF_TOKEN},
  // Can work with native File in browsers
  file: {
    path: "pytorch_model.bin",
    content: new Blob(...) 
  }
});

await deleteFiles({
  repo: {type: "space", name: "my-user/my-space"}, // or "spaces/my-user/my-space"
  credentials: {accessToken: HF_TOKEN},
  paths: ["README.md", ".gitattributes"]
});

@huggingface/agents example

import {HfAgent, LLMFromHub, defaultTools} from '@huggingface/agents';

const HF_TOKEN = "hf_...";

const agent = new HfAgent(
  HF_TOKEN,
  LLMFromHub(HF_TOKEN),
  [...defaultTools]
);


// you can generate the code, inspect it and then run it
const code = await agent.generateCode("Draw a picture of a cat wearing a top hat. Then caption the picture and read it out loud.");
console.log(code);
const messages = await agent.evaluateCode(code)
console.log(messages); // contains the data

// or you can run the code directly, however you can't check that the code is safe to execute this way, use at your own risk.
const messages = await agent.run("Draw a picture of a cat wearing a top hat. Then caption the picture and read it out loud.")
console.log(messages); 

There are more features of course, check each library's README!

Formatting & testing

sudo corepack enable
pnpm install

pnpm -r format:check
pnpm -r lint:check
pnpm -r test

Building

pnpm -r build

This will generate ESM and CJS javascript files in packages/*/dist, eg packages/inference/dist/index.mjs.

huggingface.js's People

Contributors

arsalabangash avatar aschen avatar atiorh avatar bhavberi avatar coyotte508 avatar directrix01 avatar dongs0104 avatar dylanebert avatar gary149 avatar julien-c avatar kakulukian avatar kohsheen1234 avatar krampstudio avatar machineuser avatar merveenoyan avatar mishig25 avatar nebulaanish avatar nsarrazin avatar osanseviero avatar radames avatar riccardomusmeci avatar sbrandeis avatar simoninithomas avatar timmikeladze avatar tomaarsen avatar vaibhavs10 avatar vvmnnnkv avatar walshydev avatar wauplin avatar xenova avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

huggingface.js's Issues

Add Document Question Answer

Document Question Answering

const base64Image = await fetch("https://s3.amazonaws.com/moonup/production/uploads/1677559577886-6064e095abd8d3692e3e2ed6.png")
    .then(res => res.blob())
    .then(blob => {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onloadend = ()=> resolve(reader.result);
        reader.onerror = reject;
        reader.readAsDataURL(blob);
      });
    });


 const response = await fetch("https://api-inference.huggingface.co/models/impira/layoutlm-document-qa", {
    "headers": {
      "content-type": "application/json",
    },
    body: JSON.stringify({
      inputs: {
        question: "What is the invoice number?",
        image: base64Image.replace(/^data:.+;base64,/, '')
      }
    }),
    method: "POST"
  }).then(res => res.json());

Tracking integration for Speaker Verification

Tracking integration of task - Speaker Verification

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/adding-a-task

Relevant models:
https://huggingface.co/microsoft/wavlm-base-plus-sv

Relevant spaces:
https://huggingface.co/spaces/microsoft/wavlm-speaker-verification (inference API could look like this)

Tracking integration for Video to Video

Tracking integration of task - video-to-video

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/adding-a-task

Reinforcement-learning Task Page

This is the tracker for completion of task page for reinforcement-learning task. Link to task page is here: https://huggingface.co/tasks/reinforcement-learning You can contribute parts of a task or the whole task! (There's no single assignee to a task, these are wikipedia-like pages)
You can refer to huggingface/hub-docs#369 if you want to contribute! 🤗

List of items to contribute:

  • Task description
  • Subtasks (if there is any)
  • Most used dataset for the task
  • Most used model for the task
  • Metrics that are used to evaluate the task
  • Libraries used for the task
  • Use cases
  • Small snippet for inference that demonstrates the task
  • Task Schema
  • Model for Widget

Zero Shot Image Classification Task Page

This is the tracker for completion of task page for zero-shot-image-classification task. The page is here: https://huggingface.co/tasks/zero-shot-image-classification You can contribute parts of a task or the whole task! (There's no single assignee to a task, these are wikipedia-like pages)
You can refer to huggingface/hub-docs#369 if you want to contribute! 🤗

List of items to contribute:

  • Task description
  • About the Task section in the markdown part
  • Subtasks (if there is any)
  • Most used dataset for the task
  • Most used model for the task
  • Metrics that are used to evaluate the task
  • Libraries used for the task
  • Use cases
  • Small snippet for inference that demonstrates the task
  • Task Schema
  • Model for Widget

Unconditional-image-generation Task Page

This is the tracker for completion of task page for unconditional-image-generation task. You can contribute parts of a task or the whole task! (There's no single assignee to a task, these are wikipedia-like pages)
You can refer to huggingface/hub-docs#369 if you want to contribute! 🤗

Task page for unconditional-image-generation is here: https://huggingface.co/tasks/unconditional-image-generation

List of items to contribute:

  • Task description
  • Subtasks (if there is any)
  • Most used dataset for the task
  • Most used model for the task
  • Metrics that are used to evaluate the task
  • Libraries used for the task
  • Use cases
  • Small snippet for inference that demonstrates the task
  • Task Schema
  • Model for the Widget

Allow multilabel classification mode for widgets in the models repo

🚀 Feature request

  1. Enable multilabel classification mode and regression mode for the widgets in the model repo.
  2. Create the corresponding tags that can be read from the model card.

Motivation

Models for sequence classification by default support three modes: binary/multiclass classification, multilabel classification, and regression. However, the widgets in the model repository support only multiclass mode (where probabilities of classes sum to 1). This can be misleading for the users who taste the models using the widgets. For example, my model https://huggingface.co/cointegrated/rubert-tiny-toxicity, which is intended for multilabel classification, but the widget normalizes the predicted probabilities to sum to 1, which leads to confisuion of the potential users of the model.

Your contribution

If you show me where to start, I could start working on implementing this feature. However, currently I don't know what part of the Huggingface repository is responsible for widgets and underlying computations.

Tracking integration for image feature extraction

Tracking integration of task - Feature Extraction

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget #301

Integration guide: https://hf.co/docs/hub/adding-a-task

Tracking integration for `unconditional-image-generation`

Tracking integration of unconditional-image-generation

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a diffusers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a task page
  • Added a widget

Integration guide: https://hf.co/docs/hub/models-tasks

Add E2E browser tests

We have some divering codepaths depending on browser / node, and we only test on node. We should add browser tests.

Tracking integration for text-to-3D

Tracking integration of task - Text to 3D

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/models-tasks

icon:

<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none"><g clip-path="url(#a)"><path fill="#000" fill-rule="evenodd" d="M1.27 7.508h1.5v.75h-1.5a.75.75 0 0 1-.75-.75v-6a.75.75 0 0 1 .75-.75h6a.75.75 0 0 1 .75.75v1.5h-.75v-1.5h-6v6Zm3.375-1.125h-.75V3.758H2.77v-.75h3v.75H4.645v2.625Zm.904.302v2.187a.436.436 0 0 0 .217.376l2.049 1.193V8.018L5.549 6.685Zm2.966 3.756 2.05-1.193a.436.436 0 0 0 .216-.376V6.685L8.514 8.018v2.423Zm1.904-4.356L8.165 7.411 5.91 6.085l1.926-1.121a.653.653 0 0 1 .657 0l1.926 1.121Zm.91 3.352c.1-.172.151-.367.151-.565V6.544a1.135 1.135 0 0 0-.564-.98L8.846 4.36a1.353 1.353 0 0 0-1.362 0l-2.07 1.205a1.136 1.136 0 0 0-.565.981v2.327a1.136 1.136 0 0 0 .564.981l2.072 1.205a1.355 1.355 0 0 0 1.36 0l2.072-1.205c.172-.1.314-.244.413-.416Z" clip-rule="evenodd"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h12v12H0z"/></clipPath></defs></svg>

image

Feature-extraction Task Page

This is the tracker for completion of task page for feature-extraction task. The page is here: https://huggingface.co/tasks/feature-extraction You can contribute parts of a task or the whole task! (There's no single assignee to a task, these are wikipedia-like pages)
You can refer to huggingface/hub-docs#369 if you want to contribute! 🤗

List of items to contribute:

  • Task description
  • Task Schema
  • Subtasks (if there is any)
  • Most used dataset for the task
  • Most used model for the task
  • Metrics that are used to evaluate the task
  • Libraries used for the task
  • Use cases
  • Small snippet for inference that demonstrates the task
  • Model for the Widget

Tracking integration for Video Classification

Tracking integration of task - Video Classification

Example discussion - https://huggingface.co/keras-io/video-transformers/discussions/1#62e9694f1ee086f609b4a500

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/models-tasks

Image Feature Extraction Widget

Would have as input

  • Image

Would output:

  • Feature Vector

This should be similar in implementation to the image classification widget mixed with text feature extraction widget.

Tracking integration for Depth Estimation

Tracking integration of task - Depth Estimation

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/models-tasks

Tracking integration for textual entailment/NLI

Is your feature request related to a problem? Please describe.
The textual entailment / NLI task cannot be easily visualized through the Hosted inference API. Widget pipeline tags exist for most downstream tasks (e.g. 0-shot classification, QA, STS)

Describe the solution you'd like
A widget dedicated to textual entailment (e.g. outputting whether the hypothesis entails/contradicts/is neutral to the premise would be really nice.

Edited to follow new task format

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/adding-a-task

Add `text-to-image` API inference

hi @TimMikeladze it would be great to add text-to-image on the inference package.
I'm not sure if it's well documented on the docs, but it's widely available on all text-to-image models widgets on the hub,
for example https://huggingface.co/prompthero/openjourney?text=mdjrny-v4+style+a+flying+cat

Here a simple example request:

fetch(
  "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-2",
  {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "X-Wait-For-Model": "true" 
    },
    body: JSON.stringify({
      inputs:
        "award winning high resolution photo of a giant tortoise/((ladybird)) hybrid, [trending on artstation]",
      negative_prompt: "blurry",
    }),
  }
)
  .then((res) => res.blob())
  .then((blob) => {
    const tab = window.open((target = "_blank"));
    tab.location.href = window.URL.createObjectURL(blob);
  });

Text Classification in Hosted Inference API with Multiple Inputs

Some of the text classification tasks take two inputs just like similarity models, but since the problem is text classification, widget takes only one text, which confuses users on how to pass their text to model in hosted inference API. See this question in the forum.
Couple of other example models: Cross Encoder QNLI even though these models are based on similarity, they usually return entailment/not entailment
Cross Encoder MSMARCO Passage Ranking

Solution is letting user input as many text as they want (like similarity widget) yet keeping the class labels without having an additional pipeline. A similar widget is zero shot classification but it takes possible class names and not multiple text input.

maybe this is relevant for cc: @osanseviero

Add threshold parameter for object detection widget

Is your feature request related to a problem? Please describe.
Currently, the object detection widgets don't have a threshold parameter, so they just use the hardcoded one from the object detection pipeline, which is 0.9 as seen here.

However, with more and more object detection models coming, we definitely need an adjustable threshold button for the widget, as for instance now Deformable DETR checkpoints, which need a lower threshold to detect objects in a scene, simply say "no objects detected":

Screenshot 2022-10-17 at 08 40 55

Describe the solution you'd like
Adding a "threshold button" to the inference widgets. Perhaps make it possible to set a default threshold in the model card.

Maximize button not working properly on Hosted inference API block

Description:
I recently visited the Huggingface website and noticed that when I click on the maximize button on the Hosted inference API block, it maximizes, but the button still shows the maximize icon instead of switching to the minimize icon. This does not happen with any other blocks on the page.

Steps to Reproduce:

  1. Go to the Huggingface website
  2. Open any model
  3. Click on the maximize button under Hosted inference API block

Expected Results:
The maximize button should switch to the minimize button after it is clicked.

Actual Results:
The maximize button still shows the maximize after it is clicked.

Tracking integration for image similarity

Tracking integration of task - image similarity

Given that we have a tutorial notebook on image similarity and an upcoming blog post, and given the usefulness of the use case, it's time we added support for this task.

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/models-tasks

Tooling in its own package?

Should we put eslint + prettier config in a package for example?

Also, husky / lint-staged are not configured yet, I didn't research how to properly do it for a mono-repo

Zero-shot-classification Task Page

This is the tracker for completion of task page for zero-shot-classification task. The page is here: https://huggingface.co/tasks/zero-shot-classification You can contribute parts of a task or the whole task! (There's no single assignee to a task, these are wikipedia-like pages)
You can refer to huggingface/hub-docs#369 if you want to contribute! 🤗

List of items to contribute:

  • Task description
  • Subtasks (if there is any)
  • Most used dataset for the task
  • Most used model for the task
  • Metrics that are used to evaluate the task
  • Libraries used for the task
  • Use cases
  • Small snippet for inference that demonstrates the task
  • Task schema
  • Model for the widget

Tracking integration for Speaker diarization

Tracking integration of task - Speaker diarization (Who spoke when?)

Note that you're not expected to do all of the following steps. This PR helps track all the steps required to get a new task fully supported in the Hub 🔥

  • Integration with Inference API. Select at least one of the following:
    • Added a transformers pipeline
    • Added to Community Inference API for 3rd party library
    • Added to Community Inference API for generic
  • Added basic UI elements (icon, order specification, etc)
  • Added a widget

Integration guide: https://hf.co/docs/hub/adding-a-task

Relevant models: https://huggingface.co/anton-l/wav2vec2-base-superb-sd

Expected request with `Content-Type: application/json`

Update from this issue:

Hello! I was informed that huggingface has been folded into this repo. Therefore, I tried to reinvestigate the issue using this package (@huggingface/inference).

However, the response was still Unexpected token E in JSON at position 0 (which probably means Expected request with Content-Type: application/json again.)

Upon further investigation, this only occurs if I use google/flan-t5-xxl as a model, while gpt2 works perfectly fine.

Will adding 'Content-Type': 'application/json' to the request headers solve this problem while not breaking other use cases?

Streaming mode for the inference api

https://huggingface.co/docs/api-inference/parallelism#streaming

In order to maximize the speed of inference, instead of running many HTTP requests it will be more efficient to stream your data to the API. This will require the use of websockets on your end.

Important: A pro account is required to use and test streaming. I began a partial implementation to add streaming support several months ago. Leaving this patch below for future reference.

commit 035a2ecab05097c663887ebf12f6716d9bbac6aa
Author: TimMikeladze <[email protected]>
Date:   Thu Sep 22 17:27:14 2022 +0300

    First pass at streaming support

diff --git a/package.json b/package.json
index 7d93ea1..f8e3232 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,7 @@
   ],
   "devDependencies": {
     "@size-limit/preset-small-lib": "7.0.8",
+    "@types/ws": "8.5.3",
     "husky": "8.0.1",
     "size-limit": "7.0.8",
     "tsdx": "0.14.1",
@@ -68,6 +69,8 @@
     "node-notifier": ">=8.0.1"
   },
   "dependencies": {
-    "isomorphic-unfetch": "3.1.0"
+    "isomorphic-unfetch": "3.1.0",
+    "isomorphic-ws": "5.0.0",
+    "ws": "8.8.1"
   }
 }
diff --git a/src/HuggingFace.ts b/src/HuggingFace.ts
index 6909702..29f3684 100644
--- a/src/HuggingFace.ts
+++ b/src/HuggingFace.ts
@@ -1,6 +1,12 @@
 import fetch from 'isomorphic-unfetch';
+import WebSocket from 'ws';
 
 export type Options = {
+  /**
+   * (Default: `true`) If enabled, array arguments will be sent over a WebSocket connection and the response will be streamed back.
+   */
+  use_streaming?: boolean;
+
   /**
    * (Default: false). Boolean to use GPU instead of CPU for inference (requires Startup plan at least).
    */
@@ -21,7 +27,14 @@ export type Options = {
 };
 
 export type Args = {
+  /**
+   * The name of the HuggingFace model to use.
+   */
   model: string;
+  /**
+   * When `use_streaming` option is enabled this id will be included in each response to identify the request.
+   */
+  id?: string;
 };
 
 export type FillMaskArgs = Args & {
@@ -391,9 +404,9 @@ export class HuggingFace {
    * Tries to fill in a hole with a missing word (token to be precise). That’s the base task for BERT models.
    */
   public async fillMask(
-    args: FillMaskArgs,
+    args: FillMaskArgs | FillMaskArgs[],
     options?: Options
-  ): Promise<FillMaskReturn> {
+  ): Promise<FillMaskReturn | FillMaskReturn[]> {
     return this.request(args, options);
   }
 
@@ -401,19 +414,33 @@ export class HuggingFace {
    * This task is well known to summarize longer text into shorter text. Be careful, some models have a maximum length of input. That means that the summary cannot handle full books for instance. Be careful when choosing your model.
    */
   public async summarization(
-    args: SummarizationArgs,
+    args: SummarizationArgs | SummarizationArgs[],
     options?: Options
-  ): Promise<SummarizationReturn> {
+  ): Promise<SummarizationReturn | SummarizationReturn[]> {
     return (await this.request(args, options))?.[0];
   }
 
+  /**
+   * Want to have a nice know-it-all bot that can answer any question?. Recommended model: deepset/roberta-base-squad2
+   */
+  public async questionAnswer(
+    args: QuestionAnswerArgs[],
+    options?: Options
+  ): Promise<QuestionAnswerReturn[]>;
   /**
    * Want to have a nice know-it-all bot that can answer any question?. Recommended model: deepset/roberta-base-squad2
    */
   public async questionAnswer(
     args: QuestionAnswerArgs,
     options?: Options
-  ): Promise<QuestionAnswerReturn> {
+  ): Promise<QuestionAnswerReturn>;
+  /**
+   * Want to have a nice know-it-all bot that can answer any question?. Recommended model: deepset/roberta-base-squad2
+   */
+  public async questionAnswer(
+    args: QuestionAnswerArgs | QuestionAnswerArgs[],
+    options?: Options
+  ): Promise<QuestionAnswerReturn | QuestionAnswerReturn[]> {
     return await this.request(args, options);
   }
 
@@ -421,9 +448,9 @@ export class HuggingFace {
    * Don’t know SQL? Don’t want to dive into a large spreadsheet? Ask questions in plain english! Recommended model: google/tapas-base-finetuned-wtq.
    */
   public async tableQuestionAnswer(
-    args: TableQuestionAnswerArgs,
+    args: TableQuestionAnswerArgs | TableQuestionAnswerArgs[],
     options?: Options
-  ): Promise<TableQuestionAnswerReturn> {
+  ): Promise<TableQuestionAnswerReturn | TableQuestionAnswerReturn[]> {
     return await this.request(args, options);
   }
 
@@ -431,9 +458,9 @@ export class HuggingFace {
    * Usually used for sentiment-analysis this will output the likelihood of classes of an input. Recommended model: distilbert-base-uncased-finetuned-sst-2-english
    */
   public async textClassification(
-    args: TextClassificationArgs,
+    args: TextClassificationArgs | TextClassificationArgs[],
     options?: Options
-  ): Promise<TextClassificationReturn> {
+  ): Promise<TextClassificationReturn | TextClassificationReturn[]> {
     return await this.request(args, options);
   }
 
@@ -441,9 +468,9 @@ export class HuggingFace {
    * Use to continue text from a prompt. This is a very generic task. Recommended model: gpt2 (it’s a simple model, but fun to play with).
    */
   public async textGeneration(
-    args: TextGenerationArgs,
+    args: TextGenerationArgs | TextGenerationArgs[],
     options?: Options
-  ): Promise<TextGenerationReturn> {
+  ): Promise<TextGenerationReturn | TextGenerationReturn[]> {
     return (await this.request(args, options))?.[0];
   }
 
@@ -451,9 +478,9 @@ export class HuggingFace {
    * Usually used for sentence parsing, either grammatical, or Named Entity Recognition (NER) to understand keywords contained within text. Recommended model: dbmdz/bert-large-cased-finetuned-conll03-english
    */
   public async tokenClassification(
-    args: TokenClassificationArgs,
+    args: TokenClassificationArgs | TokenClassificationArgs[],
     options?: Options
-  ): Promise<TokenClassificationReturn> {
+  ): Promise<TokenClassificationReturn | TokenClassificationReturn[]> {
     return HuggingFace.toArray(await this.request(args, options));
   }
 
@@ -461,9 +488,9 @@ export class HuggingFace {
    * This task is well known to translate text from one language to another. Recommended model: Helsinki-NLP/opus-mt-ru-en.
    */
   public async translation(
-    args: TranslationArgs,
+    args: TranslationArgs | TranslationArgs[],
     options?: Options
-  ): Promise<TranslationReturn> {
+  ): Promise<TranslationReturn | TranslationReturn[]> {
     return (await this.request(args, options))?.[0];
   }
 
@@ -471,9 +498,9 @@ export class HuggingFace {
    * This task is super useful to try out classification with zero code, you simply pass a sentence/paragraph and the possible labels for that sentence, and you get a result. Recommended model: facebook/bart-large-mnli.
    */
   public async zeroShotClassification(
-    args: ZeroShotClassificationArgs,
+    args: ZeroShotClassificationArgs | ZeroShotClassificationArgs[],
     options?: Options
-  ): Promise<ZeroShotClassificationReturn> {
+  ): Promise<ZeroShotClassificationReturn | ZeroShotClassificationReturn[]> {
     return HuggingFace.toArray(await this.request(args, options));
   }
 
@@ -482,9 +509,9 @@ export class HuggingFace {
    *
    */
   public async conversational(
-    args: ConversationalArgs,
+    args: ConversationalArgs | ConversationalArgs[],
     options?: Options
-  ): Promise<ConversationalReturn> {
+  ): Promise<ConversationalReturn | ConversationalReturn[]> {
     return await this.request(args, options);
   }
 
@@ -492,43 +519,111 @@ export class HuggingFace {
    * This task reads some text and outputs raw float values, that are usually consumed as part of a semantic database/semantic search.
    */
   public async featureExtraction(
-    args: FeatureExtractionArgs,
+    args: FeatureExtractionArgs | FeatureExtractionArgs[],
     options?: Options
-  ): Promise<FeatureExtractionReturn> {
+  ): Promise<FeatureExtractionReturn | FeatureExtractionReturn[]> {
     return await this.request(args, options);
   }
 
-  public async request(args: Args, options?: Options): Promise<any> {
+  public async request(
+    args: Args | Args[],
+    options?: Options
+  ): Promise<any | any[]> {
     const mergedOptions = { ...this.defaultOptions, ...options };
-    const { model, ...otherArgs } = args;
-    const response = await fetch(
-      `https://api-inference.huggingface.co/models/${model}`,
-      {
-        headers: { Authorization: `Bearer ${this.apiKey}` },
-        method: 'POST',
-        body: JSON.stringify({
-          ...otherArgs,
-          options: mergedOptions,
-        }),
+
+    if (Array.isArray(args) && options?.use_streaming !== false) {
+      const models = new Set(args.map(x => x.model));
+
+      if (models.size > 1) {
+        throw new Error(
+          'You can only send use one model per request when the `use_streaming` option is enabled. Please group your requests by model.'
+        );
       }
-    );
-
-    if (
-      mergedOptions.retry_on_error !== false &&
-      response.status === 503 &&
-      !mergedOptions.wait_for_model
-    ) {
-      return this.request(args, {
-        ...mergedOptions,
-        wait_for_model: true,
+
+      const model = args[0].model;
+
+      const uniqueIds = args
+        .map(x => x.id)
+        .filter(x => x !== undefined && x !== null && x?.trim() !== '');
+
+      if (uniqueIds.length !== new Set(uniqueIds).size) {
+        throw new Error('Duplicate ids found in args');
+      }
+
+      const ws = new WebSocket(
+        `wss://api-inference.huggingface.co/bulk/stream/cpu/${model}`
+      );
+
+      // @ts-ignore
+      const responses: any[] = [];
+
+      // @ts-ignore
+      return new Promise((resolve, reject) => {
+        ws.on('open', () => {
+          ws.send(`Bearer ${this.apiKey}`, { binary: true });
+
+          for (const arg of args) {
+            ws.send(JSON.stringify(arg), { binary: true });
+          }
+        });
+
+        ws.on('message', (data: any) => {
+          console.log(Buffer.from(data).toString());
+          console.log(data);
+          // const message = JSON.parse(data);
+          // if (message.type == 'results') {
+          //   responses.push(message);
+          //   if (responses.length === args.length) {
+          //     ws.close();
+          //     resolve(responses);
+          //   }
+          // }
+        });
+        ws.on('error', message => {
+          console.log(message);
+          reject(message);
+        });
       });
-    }
+    } else {
+      const httpRequest = async (args: Args) => {
+        const { model, ...otherArgs } = args;
+
+        const response = await fetch(
+          `https://api-inference.huggingface.co/models/${model}`,
+          {
+            headers: { Authorization: `Bearer ${this.apiKey}` },
+            method: 'POST',
+            body: JSON.stringify({
+              ...otherArgs,
+              options: mergedOptions,
+            }),
+          }
+        );
+
+        if (
+          mergedOptions.retry_on_error !== false &&
+          response.status === 503 &&
+          !mergedOptions.wait_for_model
+        ) {
+          return this.request(args, {
+            ...mergedOptions,
+            wait_for_model: true,
+          });
+        }
+
+        const res = await response.json();
+        if (res.error) {
+          throw new Error(res.error);
+        }
+        return res;
+      };
+
+      if (Array.isArray(args)) {
+        return Promise.all(args.map(x => httpRequest(x)));
+      }
 
-    const res = await response.json();
-    if (res.error) {
-      throw new Error(res.error);
+      return httpRequest(args);
     }
-    return res;
   }
 
   private static toArray(obj: any): any[] {
diff --git a/test/HuggingFace.test.ts b/test/HuggingFace.test.ts
index ed7a592..da0a2bd 100644
--- a/test/HuggingFace.test.ts
+++ b/test/HuggingFace.test.ts
@@ -7,7 +7,7 @@ describe('HuggingFace', () => {
   // Individual tests can be ran without providing an api key, however running all tests without an api key will result in rate limiting error.
   let hf = new HuggingFace(process.env.HF_API_KEY as string);
 
-  it('throws error if model does not exist', () => {
+  xit('throws error if model does not exist', () => {
     expect(
       hf.fillMask({
         model: 'this-model-does-not-exist-123',
@@ -17,7 +17,24 @@ describe('HuggingFace', () => {
       `Model this-model-does-not-exist-123 does not exist`
     );
   });
-  it('fillMask', async () => {
+  xit('throws error if multiple models are provided and use_streaming is true', () => {
+    expect(
+      hf.fillMask([
+        {
+          model: 'this-model-does-not-exist-123',
+          inputs: '[MASK] world!',
+        },
+        {
+          model: 'this-model-also-does-not-exist-123',
+          inputs: '[MASK] world!',
+        },
+      ])
+    ).rejects.toThrowError(
+      `Model this-model-does-not-exist-123 does not exist`
+    );
+  });
+
+  xit('fillMask', async () => {
     expect(
       await hf.fillMask({
         model: 'bert-base-uncased',
@@ -34,7 +51,7 @@ describe('HuggingFace', () => {
       ])
     );
   });
-  it('summarization', async () => {
+  xit('summarization', async () => {
     expect(
       await hf.summarization({
         model: 'facebook/bart-large-cnn',
@@ -49,7 +66,7 @@ describe('HuggingFace', () => {
         'The tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey building. Its base is square, measuring 125 metres (410 ft) on each side. During its construction, the Eiffel Tower surpassed the Washington Monument to become the tallest man-made structure in the world.',
     });
   });
-  it('questionAnswer', async () => {
+  xit('questionAnswer', async () => {
     expect(
       await hf.questionAnswer({
         model: 'deepset/roberta-base-squad2',
@@ -65,7 +82,7 @@ describe('HuggingFace', () => {
       end: expect.any(Number),
     });
   });
-  it('table question answer', async () => {
+  xit('table question answer', async () => {
     expect(
       await hf.tableQuestionAnswer({
         model: 'google/tapas-base-finetuned-wtq',
@@ -90,7 +107,7 @@ describe('HuggingFace', () => {
       aggregator: 'AVERAGE',
     });
   });
-  it('textClassification', async () => {
+  xit('textClassification', async () => {
     expect(
       await hf.textClassification({
         model: 'distilbert-base-uncased-finetuned-sst-2-english',
@@ -105,7 +122,7 @@ describe('HuggingFace', () => {
       ])
     );
   });
-  it('textGeneration', async () => {
+  xit('textGeneration', async () => {
     expect(
       await hf.textGeneration({
         model: 'gpt2',
@@ -116,7 +133,7 @@ describe('HuggingFace', () => {
         'The answer to the universe is not a binary number that is at a certain point defined in our theory of time, but an infinite number of infinitely long points and points for which each of these points has the given form in our equation. If the given',
     });
   });
-  it(`tokenClassification`, async () => {
+  xit('tokenClassification', async () => {
     expect(
       await hf.tokenClassification({
         model: 'dbmdz/bert-large-cased-finetuned-conll03-english',
@@ -134,7 +151,7 @@ describe('HuggingFace', () => {
       ])
     );
   });
-  it(`translation`, async () => {
+  xit('translation', async () => {
     expect(
       await hf.translation({
         model: 'Helsinki-NLP/opus-mt-ru-en',
@@ -144,7 +161,7 @@ describe('HuggingFace', () => {
       translation_text: 'My name is Wolfgang and I live in Berlin.',
     });
   });
-  it(`zeroShotClassification`, async () => {
+  xit('zeroShotClassification', async () => {
     expect(
       await hf.zeroShotClassification({
         model: 'facebook/bart-large-mnli',
@@ -164,7 +181,7 @@ describe('HuggingFace', () => {
       ])
     );
   });
-  it(`conversational`, async () => {
+  xit('conversational', async () => {
     expect(
       await hf.conversational({
         model: 'microsoft/DialoGPT-large',
@@ -191,7 +208,7 @@ describe('HuggingFace', () => {
       ],
     });
   });
-  it(`featureExtraction`, async () => {
+  xit('featureExtraction', async () => {
     expect(
       await hf.featureExtraction({
         model: 'sentence-transformers/paraphrase-xlm-r-multilingual-v1',
@@ -206,4 +223,69 @@ describe('HuggingFace', () => {
       })
     ).toEqual([0.6623499393463135, 0.9382339715957642, 0.22963346540927887]);
   });
+
+  xit('use http for array input when use_streaming is false', async () => {
+    const res = await hf.questionAnswer(
+      [
+        {
+          model: 'deepset/roberta-base-squad2',
+          inputs: {
+            question: 'What is the capital of France?',
+            context: 'The capital of France is Paris.',
+          },
+        },
+        {
+          model: 'deepset/roberta-base-squad2',
+          inputs: {
+            question: 'What is the capital of England?',
+            context: 'The capital of England is London.',
+          },
+        },
+      ],
+      {
+        use_streaming: false,
+      }
+    );
+
+    expect(res).toHaveLength(2);
+
+    expect(res[0]).toEqual({
+      answer: 'Paris',
+      score: expect.any(Number),
+      start: expect.any(Number),
+      end: expect.any(Number),
+    });
+    expect(res[1]).toEqual({
+      answer: 'London',
+      score: expect.any(Number),
+      start: expect.any(Number),
+      end: expect.any(Number),
+    });
+  });
+
+  it('use websockets for array input when use_streaming is true', async () => {
+    const res = await hf.questionAnswer(
+      [
+        {
+          model: 'deepset/roberta-base-squad2',
+          inputs: {
+            question: 'What is the capital of France?',
+            context: 'The capital of France is Paris.',
+          },
+        },
+        {
+          model: 'deepset/roberta-base-squad2',
+          inputs: {
+            question: 'What is the capital of England?',
+            context: 'The capital of England is London.',
+          },
+        },
+      ],
+      {
+        use_streaming: true,
+      }
+    );
+
+    console.log(res);
+  });
 });
diff --git a/yarn.lock b/yarn.lock
index c7cfe8d..6033ed8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1404,6 +1404,13 @@
   resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
   integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
 
+"@types/[email protected]":
+  version "8.5.3"
+  resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
+  integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
+  dependencies:
+    "@types/node" "*"
+
 "@types/yargs-parser@*":
   version "21.0.0"
   resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
@@ -3842,6 +3849,11 @@ [email protected]:
     node-fetch "^2.6.1"
     unfetch "^4.2.0"
 
[email protected]:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf"
+  integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==
+
 isstream@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -6651,6 +6663,11 @@ [email protected]:
   dependencies:
     mkdirp "^0.5.1"
 
[email protected]:
+  version "8.8.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
+  integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
+
 ws@^7.0.0:
   version "7.5.8"
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a"


Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.