├── .eslintrc ├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── commit.config.js ├── jest.config.json ├── package.json ├── renovate.json ├── src ├── HuggingFace.ts └── index.ts ├── test ├── HuggingFace.test.ts ├── cats.png ├── cheetah.png └── sample1.flac ├── tsconfig.json └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es2021": true, 4 | "node": true, 5 | "jest": true 6 | }, 7 | "extends": ["standard", "plugin:typescript-sort-keys/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": 13, 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "indent": "off", 16 | "space-before-function-paren": "off", 17 | "no-unused-vars": "warn" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: linesofcodedev 2 | custom: ['https://www.paypal.me/TimMikeladze'] 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main CI workflow 2 | 3 | on: [push] 4 | 5 | jobs: 6 | run-ci: 7 | name: Run CI 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 10 10 | env: 11 | HF_API_KEY: ${{ secrets.HF_API_KEY }} 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Set up Node 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 18 23 | 24 | - name: Install dependencies (with cache) 25 | uses: bahmutov/npm-install@v1 26 | 27 | - name: Check types 28 | run: yarn type-check 29 | 30 | - name: Check linting 31 | run: yarn lint 32 | 33 | - name: Test 34 | run: yarn test:ci 35 | 36 | - name: Build 37 | run: yarn build 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist 5 | .idea 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | yarn lint-staged 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact = true 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | *.lock 4 | *.snap 5 | .prettierignore 6 | .env.template 7 | .gitignore 8 | .husky/** 9 | .npmrc 10 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "none" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 TimMikeladze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ❗ IMPORTANT: This package has been merged into [Huggingface.js](https://github.com/huggingface/huggingface.js). Read the latest docs [here](https://github.com/huggingface/huggingface.js/blob/main/packages/inference/README.md). 2 | 3 | # 🤗 Hugging Face Inference API 4 | 5 | A Typescript powered wrapper for the Hugging Face Inference API. Learn more about the Inference API at [Hugging Face](https://huggingface.co/docs/api-inference/index). 6 | 7 | ## Install 8 | 9 | ```console 10 | npm install huggingface 11 | 12 | yarn add huggingface 13 | 14 | pnpm add huggingface 15 | ``` 16 | 17 | ## Usage 18 | 19 | ❗**Important note:** Using an API key is optional to get started (simply provide a random string), however you will be rate limited eventually. Join [Hugging Face](https://huggingface.co/join) and then visit [access tokens](https://huggingface.co/settings/tokens) to generate your API key. 20 | 21 | ### Basic examples 22 | 23 | ```typescript 24 | import HuggingFace from 'huggingface' 25 | 26 | const hf = new HuggingFace('your api key') 27 | 28 | // Natural Language 29 | 30 | await hf.fillMask({ 31 | model: 'bert-base-uncased', 32 | inputs: '[MASK] world!' 33 | }) 34 | 35 | await hf.summarization({ 36 | model: 'facebook/bart-large-cnn', 37 | inputs: 38 | 'The tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey building, and the tallest structure in Paris. 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, a title it held for 41 years until the Chrysler Building in New York City was finished in 1930.', 39 | parameters: { 40 | max_length: 100 41 | } 42 | }) 43 | 44 | await hf.questionAnswer({ 45 | model: 'deepset/roberta-base-squad2', 46 | inputs: { 47 | question: 'What is the capital of France?', 48 | context: 'The capital of France is Paris.' 49 | } 50 | }) 51 | 52 | await hf.tableQuestionAnswer({ 53 | model: 'google/tapas-base-finetuned-wtq', 54 | inputs: { 55 | query: 'How many stars does the transformers repository have?', 56 | table: { 57 | Repository: ['Transformers', 'Datasets', 'Tokenizers'], 58 | Stars: ['36542', '4512', '3934'], 59 | Contributors: ['651', '77', '34'], 60 | 'Programming language': ['Python', 'Python', 'Rust, Python and NodeJS'] 61 | } 62 | } 63 | }) 64 | 65 | await hf.textClassification({ 66 | model: 'distilbert-base-uncased-finetuned-sst-2-english', 67 | inputs: 'I like you. I love you.' 68 | }) 69 | 70 | await hf.textGeneration({ 71 | model: 'gpt2', 72 | inputs: 'The answer to the universe is' 73 | }) 74 | 75 | await hf.tokenClassification({ 76 | model: 'dbmdz/bert-large-cased-finetuned-conll03-english', 77 | inputs: 'My name is Sarah Jessica Parker but you can call me Jessica' 78 | }) 79 | 80 | await hf.translation({ 81 | model: 't5-base', 82 | inputs: 'My name is Wolfgang and I live in Berlin' 83 | }) 84 | 85 | await hf.zeroShotClassification({ 86 | model: 'facebook/bart-large-mnli', 87 | inputs: [ 88 | 'Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!' 89 | ], 90 | parameters: { candidate_labels: ['refund', 'legal', 'faq'] } 91 | }) 92 | 93 | await hf.conversational({ 94 | model: 'microsoft/DialoGPT-large', 95 | inputs: { 96 | past_user_inputs: ['Which movie is the best ?'], 97 | generated_responses: ['It is Die Hard for sure.'], 98 | text: 'Can you explain why ?' 99 | } 100 | }) 101 | 102 | await hf.featureExtraction({ 103 | model: 'sentence-transformers/paraphrase-xlm-r-multilingual-v1', 104 | inputs: { 105 | source_sentence: 'That is a happy person', 106 | sentences: [ 107 | 'That is a happy dog', 108 | 'That is a very happy person', 109 | 'Today is a sunny day' 110 | ] 111 | } 112 | }) 113 | 114 | // Audio 115 | 116 | await hf.automaticSpeechRecognition({ 117 | model: 'facebook/wav2vec2-large-960h-lv60-self', 118 | data: readFileSync('test/sample1.flac') 119 | }) 120 | 121 | await hf.audioClassification({ 122 | model: 'superb/hubert-large-superb-er', 123 | data: readFileSync('test/sample1.flac') 124 | }) 125 | 126 | // Computer Vision 127 | 128 | await hf.imageClassification({ 129 | data: readFileSync('test/cheetah.png'), 130 | model: 'google/vit-base-patch16-224' 131 | }) 132 | 133 | await hf.objectDetection({ 134 | data: readFileSync('test/cats.png'), 135 | model: 'facebook/detr-resnet-50' 136 | }) 137 | 138 | await hf.imageSegmentation({ 139 | data: readFileSync('test/cats.png'), 140 | model: 'facebook/detr-resnet-50-panoptic' 141 | }) 142 | ``` 143 | 144 | ## Supported APIs 145 | 146 | ### Natural Language Processing 147 | 148 | - [x] Fill mask 149 | - [x] Summarization 150 | - [x] Question answering 151 | - [x] Table question answering 152 | - [x] Text classification 153 | - [x] Text generation 154 | - [x] Text2Text generation 155 | - [x] Token classification 156 | - [x] Named entity recognition 157 | - [x] Translation 158 | - [x] Zero-shot classification 159 | - [x] Conversational 160 | - [x] Feature extraction 161 | 162 | ### Audio 163 | 164 | - [x] Automatic speech recognition 165 | - [x] Audio classification 166 | 167 | ### Computer Vision 168 | 169 | - [x] Image classification 170 | - [x] Object detection 171 | - [x] Image segmentation 172 | 173 | ## Running tests 174 | 175 | ```console 176 | HF_API_KEY="your api key" yarn test 177 | ``` 178 | 179 | ## Options 180 | 181 | ```typescript 182 | export declare class HuggingFace { 183 | private readonly apiKey 184 | private readonly defaultOptions 185 | constructor(apiKey: string, defaultOptions?: Options) 186 | /** 187 | * Tries to fill in a hole with a missing word (token to be precise). That’s the base task for BERT models. 188 | */ 189 | fillMask(args: FillMaskArgs, options?: Options): Promise 190 | /** 191 | * 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. 192 | */ 193 | summarization( 194 | args: SummarizationArgs, 195 | options?: Options 196 | ): Promise 197 | /** 198 | * Want to have a nice know-it-all bot that can answer any question?. Recommended model: deepset/roberta-base-squad2 199 | */ 200 | questionAnswer( 201 | args: QuestionAnswerArgs, 202 | options?: Options 203 | ): Promise 204 | /** 205 | * 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. 206 | */ 207 | tableQuestionAnswer( 208 | args: TableQuestionAnswerArgs, 209 | options?: Options 210 | ): Promise 211 | /** 212 | * Usually used for sentiment-analysis this will output the likelihood of classes of an input. Recommended model: distilbert-base-uncased-finetuned-sst-2-english 213 | */ 214 | textClassification( 215 | args: TextClassificationArgs, 216 | options?: Options 217 | ): Promise 218 | /** 219 | * 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). 220 | */ 221 | textGeneration( 222 | args: TextGenerationArgs, 223 | options?: Options 224 | ): Promise 225 | /** 226 | * 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 227 | */ 228 | tokenClassification( 229 | args: TokenClassificationArgs, 230 | options?: Options 231 | ): Promise 232 | /** 233 | * This task is well known to translate text from one language to another. Recommended model: Helsinki-NLP/opus-mt-ru-en. 234 | */ 235 | translation( 236 | args: TranslationArgs, 237 | options?: Options 238 | ): Promise 239 | /** 240 | * 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. 241 | */ 242 | zeroShotClassification( 243 | args: ZeroShotClassificationArgs, 244 | options?: Options 245 | ): Promise 246 | /** 247 | * This task corresponds to any chatbot like structure. Models tend to have shorter max_length, so please check with caution when using a given model if you need long range dependency or not. Recommended model: microsoft/DialoGPT-large. 248 | * 249 | */ 250 | conversational( 251 | args: ConversationalArgs, 252 | options?: Options 253 | ): Promise 254 | /** 255 | * This task reads some text and outputs raw float values, that are usually consumed as part of a semantic database/semantic search. 256 | */ 257 | featureExtraction( 258 | args: FeatureExtractionArgs, 259 | options?: Options 260 | ): Promise 261 | /** 262 | * This task reads some audio input and outputs the said words within the audio files. 263 | * Recommended model (english language): facebook/wav2vec2-large-960h-lv60-self 264 | */ 265 | automaticSpeechRecognition( 266 | args: AutomaticSpeechRecognitionArgs, 267 | options?: Options 268 | ): Promise 269 | /** 270 | * This task reads some audio input and outputs the likelihood of classes. 271 | * Recommended model: superb/hubert-large-superb-er 272 | */ 273 | audioClassification( 274 | args: AudioClassificationArgs, 275 | options?: Options 276 | ): Promise 277 | /** 278 | * This task reads some image input and outputs the likelihood of classes. 279 | * Recommended model: google/vit-base-patch16-224 280 | */ 281 | imageClassification( 282 | args: ImageClassificationArgs, 283 | options?: Options 284 | ): Promise 285 | /** 286 | * This task reads some image input and outputs the likelihood of classes & bounding boxes of detected objects. 287 | * Recommended model: facebook/detr-resnet-50 288 | */ 289 | objectDetection( 290 | args: ObjectDetectionArgs, 291 | options?: Options 292 | ): Promise 293 | /** 294 | * This task reads some image input and outputs the likelihood of classes & bounding boxes of detected objects. 295 | * Recommended model: facebook/detr-resnet-50-panoptic 296 | */ 297 | imageSegmentation( 298 | args: ImageSegmentationArgs, 299 | options?: Options 300 | ): Promise 301 | request( 302 | args: Args & { 303 | data?: any 304 | }, 305 | options?: Options & { 306 | binary?: boolean 307 | } 308 | ): Promise 309 | private static toArray 310 | } 311 | 312 | export declare type Options = { 313 | /** 314 | * (Default: false). Boolean to use GPU instead of CPU for inference (requires Startup plan at least). 315 | */ 316 | use_gpu?: boolean 317 | /** 318 | * (Default: true). Boolean. There is a cache layer on the inference API to speedup requests we have already seen. Most models can use those results as is as models are deterministic (meaning the results will be the same anyway). However if you use a non deterministic model, you can set this parameter to prevent the caching mechanism from being used resulting in a real new query. 319 | */ 320 | use_cache?: boolean 321 | /** 322 | * (Default: false) Boolean. If the model is not ready, wait for it instead of receiving 503. It limits the number of requests required to get your inference done. It is advised to only set this flag to true after receiving a 503 error as it will limit hanging in your application to known places. 323 | */ 324 | wait_for_model?: boolean 325 | /** 326 | * (Default: true) Boolean. If a request 503s and wait_for_model is set to false, the request will be retried with the same parameters but with wait_for_model set to true. 327 | */ 328 | retry_on_error?: boolean 329 | } 330 | export declare type Args = { 331 | model: string 332 | } 333 | export declare type FillMaskArgs = Args & { 334 | inputs: string 335 | } 336 | export declare type FillMaskReturn = { 337 | /** 338 | * The probability for this token. 339 | */ 340 | score: number 341 | /** 342 | * The id of the token 343 | */ 344 | token: number 345 | /** 346 | * The string representation of the token 347 | */ 348 | token_str: string 349 | /** 350 | * The actual sequence of tokens that ran against the model (may contain special tokens) 351 | */ 352 | sequence: string 353 | }[] 354 | export declare type SummarizationArgs = Args & { 355 | /** 356 | * A string to be summarized 357 | */ 358 | inputs: string 359 | parameters?: { 360 | /** 361 | * (Default: None). Integer to define the minimum length in tokens of the output summary. 362 | */ 363 | min_length?: number 364 | /** 365 | * (Default: None). Integer to define the maximum length in tokens of the output summary. 366 | */ 367 | max_length?: number 368 | /** 369 | * (Default: None). Integer to define the top tokens considered within the sample operation to create new text. 370 | */ 371 | top_k?: number 372 | /** 373 | * (Default: None). Float to define the tokens that are within the sample operation of text generation. Add tokens in the sample for more probable to least probable until the sum of the probabilities is greater than top_p. 374 | */ 375 | top_p?: number 376 | /** 377 | * (Default: 1.0). Float (0.0-100.0). The temperature of the sampling operation. 1 means regular sampling, 0 means always take the highest score, 100.0 is getting closer to uniform probability. 378 | */ 379 | temperature?: number 380 | /** 381 | * (Default: None). Float (0.0-100.0). The more a token is used within generation the more it is penalized to not be picked in successive generation passes. 382 | */ 383 | repetition_penalty?: number 384 | /** 385 | * (Default: None). Float (0-120.0). The amount of time in seconds that the query should take maximum. Network can cause some overhead so it will be a soft limit. 386 | */ 387 | max_time?: number 388 | } 389 | } 390 | export declare type SummarizationReturn = { 391 | /** 392 | * The string after translation 393 | */ 394 | summary_text: string 395 | } 396 | export declare type QuestionAnswerArgs = Args & { 397 | inputs: { 398 | question: string 399 | context: string 400 | } 401 | } 402 | export declare type QuestionAnswerReturn = { 403 | /** 404 | * A string that’s the answer within the text. 405 | */ 406 | answer: string 407 | /** 408 | * A float that represents how likely that the answer is correct 409 | */ 410 | score: number 411 | /** 412 | * The index (string wise) of the start of the answer within context. 413 | */ 414 | start: number 415 | /** 416 | * The index (string wise) of the stop of the answer within context. 417 | */ 418 | end: number 419 | } 420 | export declare type TableQuestionAnswerArgs = Args & { 421 | inputs: { 422 | /** 423 | * The query in plain text that you want to ask the table 424 | */ 425 | query: string 426 | /** 427 | * A table of data represented as a dict of list where entries are headers and the lists are all the values, all lists must have the same size. 428 | */ 429 | table: Record 430 | } 431 | } 432 | export declare type TableQuestionAnswerReturn = { 433 | /** 434 | * The plaintext answer 435 | */ 436 | answer: string 437 | /** 438 | * a list of coordinates of the cells referenced in the answer 439 | */ 440 | coordinates: number[][] 441 | /** 442 | * A list of coordinates of the cells contents 443 | */ 444 | cells: string[] 445 | /** 446 | * The aggregator used to get the answer 447 | */ 448 | aggregator: string 449 | } 450 | export declare type TextClassificationArgs = Args & { 451 | /** 452 | * A string to be classified 453 | */ 454 | inputs: string 455 | } 456 | export declare type TextClassificationReturn = { 457 | /** 458 | * The label for the class (model specific) 459 | */ 460 | label: string 461 | /** 462 | * A floats that represents how likely is that the text belongs to this class. 463 | */ 464 | score: number 465 | }[] 466 | export declare type TextGenerationArgs = Args & { 467 | /** 468 | * A string to be generated from 469 | */ 470 | inputs: string 471 | parameters?: { 472 | /** 473 | * (Default: None). Integer to define the top tokens considered within the sample operation to create new text. 474 | */ 475 | top_k?: number 476 | /** 477 | * (Default: None). Float to define the tokens that are within the sample operation of text generation. Add tokens in the sample for more probable to least probable until the sum of the probabilities is greater than top_p. 478 | */ 479 | top_p?: number 480 | /** 481 | * (Default: 1.0). Float (0.0-100.0). The temperature of the sampling operation. 1 means regular sampling, 0 means always take the highest score, 100.0 is getting closer to uniform probability. 482 | */ 483 | temperature?: number 484 | /** 485 | * (Default: None). Float (0.0-100.0). The more a token is used within generation the more it is penalized to not be picked in successive generation passes. 486 | */ 487 | repetition_penalty?: number 488 | /** 489 | * (Default: None). Int (0-250). The amount of new tokens to be generated, this does not include the input length it is a estimate of the size of generated text you want. Each new tokens slows down the request, so look for balance between response times and length of text generated. 490 | */ 491 | max_new_tokens?: number 492 | /** 493 | * (Default: None). Float (0-120.0). The amount of time in seconds that the query should take maximum. Network can cause some overhead so it will be a soft limit. Use that in combination with max_new_tokens for best results. 494 | */ 495 | max_time?: number 496 | /** 497 | * (Default: True). Bool. If set to False, the return results will not contain the original query making it easier for prompting. 498 | */ 499 | return_full_text?: boolean 500 | /** 501 | * (Default: 1). Integer. The number of proposition you want to be returned. 502 | */ 503 | num_return_sequences?: number 504 | /** 505 | * (Optional: True). Bool. Whether or not to use sampling, use greedy decoding otherwise. 506 | */ 507 | do_sample?: boolean 508 | } 509 | } 510 | export declare type TextGenerationReturn = { 511 | /** 512 | * The continuated string 513 | */ 514 | generated_text: string 515 | } 516 | export declare type TokenClassificationArgs = Args & { 517 | /** 518 | * A string to be classified 519 | */ 520 | inputs: string 521 | parameters?: { 522 | /** 523 | * (Default: simple). There are several aggregation strategies: 524 | * 525 | * none: Every token gets classified without further aggregation. 526 | * 527 | * simple: Entities are grouped according to the default schema (B-, I- tags get merged when the tag is similar). 528 | * 529 | * first: Same as the simple strategy except words cannot end up with different tags. Words will use the tag of the first token when there is ambiguity. 530 | * 531 | * average: Same as the simple strategy except words cannot end up with different tags. Scores are averaged across tokens and then the maximum label is applied. 532 | * 533 | * max: Same as the simple strategy except words cannot end up with different tags. Word entity will be the token with the maximum score. 534 | */ 535 | aggregation_strategy?: 'none' | 'simple' | 'first' | 'average' | 'max' 536 | } 537 | } 538 | export declare type TokenClassificationReturnValue = { 539 | /** 540 | * The type for the entity being recognized (model specific). 541 | */ 542 | entity_group: string 543 | /** 544 | * How likely the entity was recognized. 545 | */ 546 | score: number 547 | /** 548 | * The string that was captured 549 | */ 550 | word: string 551 | /** 552 | * The offset stringwise where the answer is located. Useful to disambiguate if word occurs multiple times. 553 | */ 554 | start: number 555 | /** 556 | * The offset stringwise where the answer is located. Useful to disambiguate if word occurs multiple times. 557 | */ 558 | end: number 559 | } 560 | export declare type TokenClassificationReturn = TokenClassificationReturnValue[] 561 | export declare type TranslationArgs = Args & { 562 | /** 563 | * A string to be translated 564 | */ 565 | inputs: string 566 | } 567 | export declare type TranslationReturn = { 568 | /** 569 | * The string after translation 570 | */ 571 | translation_text: string 572 | } 573 | export declare type ZeroShotClassificationArgs = Args & { 574 | /** 575 | * a string or list of strings 576 | */ 577 | inputs: string | string[] 578 | parameters: { 579 | /** 580 | * a list of strings that are potential classes for inputs. (max 10 candidate_labels, for more, simply run multiple requests, results are going to be misleading if using too many candidate_labels anyway. If you want to keep the exact same, you can simply run multi_label=True and do the scaling on your end. 581 | */ 582 | candidate_labels: string[] 583 | /** 584 | * (Default: false) Boolean that is set to True if classes can overlap 585 | */ 586 | multi_label?: boolean 587 | } 588 | } 589 | export declare type ZeroShotClassificationReturnValue = { 590 | sequence: string 591 | labels: string[] 592 | scores: number[] 593 | } 594 | export declare type ZeroShotClassificationReturn = 595 | ZeroShotClassificationReturnValue[] 596 | export declare type ConversationalArgs = Args & { 597 | inputs: { 598 | /** 599 | * The last input from the user in the conversation. 600 | */ 601 | text: string 602 | /** 603 | * A list of strings corresponding to the earlier replies from the model. 604 | */ 605 | generated_responses?: string[] 606 | /** 607 | * A list of strings corresponding to the earlier replies from the user. Should be of the same length of generated_responses. 608 | */ 609 | past_user_inputs?: string[] 610 | } 611 | parameters?: { 612 | /** 613 | * (Default: None). Integer to define the minimum length in tokens of the output summary. 614 | */ 615 | min_length?: number 616 | /** 617 | * (Default: None). Integer to define the maximum length in tokens of the output summary. 618 | */ 619 | max_length?: number 620 | /** 621 | * (Default: None). Integer to define the top tokens considered within the sample operation to create new text. 622 | */ 623 | top_k?: number 624 | /** 625 | * (Default: None). Float to define the tokens that are within the sample operation of text generation. Add tokens in the sample for more probable to least probable until the sum of the probabilities is greater than top_p. 626 | */ 627 | top_p?: number 628 | /** 629 | * (Default: 1.0). Float (0.0-100.0). The temperature of the sampling operation. 1 means regular sampling, 0 means always take the highest score, 100.0 is getting closer to uniform probability. 630 | */ 631 | temperature?: number 632 | /** 633 | * (Default: None). Float (0.0-100.0). The more a token is used within generation the more it is penalized to not be picked in successive generation passes. 634 | */ 635 | repetition_penalty?: number 636 | /** 637 | * (Default: None). Float (0-120.0). The amount of time in seconds that the query should take maximum. Network can cause some overhead so it will be a soft limit. 638 | */ 639 | max_time?: number 640 | } 641 | } 642 | export declare type ConversationalReturn = { 643 | generated_text: string 644 | conversation: { 645 | generated_responses: string[] 646 | past_user_inputs: string[] 647 | } 648 | warnings: string[] 649 | } 650 | export declare type FeatureExtractionArgs = Args & { 651 | /** 652 | * The inputs vary based on the model. For example when using sentence-transformers/paraphrase-xlm-r-multilingual-v1 the inputs will look like this: 653 | * 654 | * inputs: { 655 | * "source_sentence": "That is a happy person", 656 | * "sentences": ["That is a happy dog", "That is a very happy person", "Today is a sunny day"] 657 | */ 658 | inputs: Record | Record[] 659 | } 660 | /** 661 | * Returned values are a list of floats, or a list of list of floats (depending on if you sent a string or a list of string, and if the automatic reduction, usually mean_pooling for instance was applied for you or not. This should be explained on the model's README. 662 | */ 663 | export declare type FeatureExtractionReturn = (number | number[])[] 664 | export declare type ImageClassificationArgs = Args & { 665 | /** 666 | * Binary image data 667 | */ 668 | data: any 669 | } 670 | export declare type ImageClassificationReturnValue = { 671 | /** 672 | * The label for the class (model specific) 673 | */ 674 | score: number 675 | /** 676 | * A float that represents how likely it is that the image file belongs to this class. 677 | */ 678 | label: string 679 | } 680 | export declare type ImageClassificationReturn = ImageClassificationReturnValue[] 681 | export declare type ObjectDetectionArgs = Args & { 682 | /** 683 | * Binary image data 684 | */ 685 | data: any 686 | } 687 | export declare type ObjectDetectionReturnValue = { 688 | /** 689 | * A float that represents how likely it is that the detected object belongs to the given class. 690 | */ 691 | score: number 692 | /** 693 | * The label for the class (model specific) of a detected object. 694 | */ 695 | label: string 696 | /** 697 | * A dict (with keys [xmin,ymin,xmax,ymax]) representing the bounding box of a detected object. 698 | */ 699 | box: { 700 | xmin: number 701 | ymin: number 702 | xmax: number 703 | ymax: number 704 | } 705 | } 706 | export declare type ObjectDetectionReturn = ObjectDetectionReturnValue[] 707 | export declare type ImageSegmentationArgs = Args & { 708 | /** 709 | * Binary image data 710 | */ 711 | data: any 712 | } 713 | export declare type ImageSegmentationReturnValue = { 714 | /** 715 | * A float that represents how likely it is that the detected object belongs to the given class. 716 | */ 717 | score: number 718 | /** 719 | * The label for the class (model specific) of a segment. 720 | */ 721 | label: string 722 | /** 723 | * A str (base64 str of a single channel black-and-white img) representing the mask of a segment. 724 | */ 725 | mask: string 726 | } 727 | export declare type ImageSegmentationReturn = ImageSegmentationReturnValue[] 728 | export declare type AutomaticSpeechRecognitionArgs = Args & { 729 | /** 730 | * Binary audio data 731 | */ 732 | data: any 733 | } 734 | export declare type AutomaticSpeechRecognitionReturn = { 735 | /** 736 | * The text that was recognized from the audio 737 | */ 738 | text: string 739 | } 740 | export declare type AudioClassificationArgs = Args & { 741 | /** 742 | * Binary audio data 743 | */ 744 | data: any 745 | } 746 | export declare type AudioClassificationReturnValue = { 747 | /** 748 | * The label for the class (model specific) 749 | */ 750 | label: string 751 | /** 752 | * A float that represents how likely it is that the audio file belongs to this class. 753 | */ 754 | score: number 755 | } 756 | export declare type AudioClassificationReturn = AudioClassificationReturnValue[] 757 | ``` 758 | -------------------------------------------------------------------------------- /commit.config.js: -------------------------------------------------------------------------------- 1 | import { GitEmoji } from 'commit-it' 2 | 3 | export default { 4 | plugins: [ 5 | new GitEmoji({ 6 | askForShortDescription: false, 7 | commitBodyRequired: false 8 | }) 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "ts-jest/presets/default-esm", 3 | "testEnvironment": "node", 4 | "testPathIgnorePatterns": ["/node_modules/"], 5 | "testMatch": ["**/?(*.)+(spec|test).[jt]s?(x)"], 6 | "extensionsToTreatAsEsm": [".ts"], 7 | "transform": { 8 | "^.+\\.tsx?$": [ 9 | "ts-jest", 10 | { 11 | "isolatedModules": true, 12 | "useESM": true 13 | } 14 | ] 15 | }, 16 | "testTimeout": 60000 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "huggingface", 3 | "version": "1.4.0", 4 | "license": "MIT", 5 | "author": "Tim Mikeladze ", 6 | "description": "Typescript wrapper for the Hugging Face Inference API", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/TimMikeladze/huggingface.git" 10 | }, 11 | "keywords": [ 12 | "hugging face", 13 | "hugging face typescript", 14 | "huggingface", 15 | "huggingface-inference-api", 16 | "huggingface-inference-api-typescript", 17 | "inference", 18 | "ai" 19 | ], 20 | "engines": { 21 | "node": ">=14" 22 | }, 23 | "files": [ 24 | "./dist" 25 | ], 26 | "source": "src/index.ts", 27 | "types": "dist/index.d.ts", 28 | "type": "module", 29 | "exports": { 30 | "require": "./dist/index.cjs", 31 | "default": "./dist/index.modern.js" 32 | }, 33 | "main": "./dist/index.cjs", 34 | "module": "./dist/index.module.js", 35 | "unpkg": "./dist/index.umd.js", 36 | "scripts": { 37 | "dev": "microbundle watch src/{index}.ts --target node -f modern", 38 | "build": "rm -rf dist && microbundle src/{index}.ts", 39 | "lint": "eslint --fix \"{src,test}/**/*.+(ts|tsx|js|jsx)\" && prettier --write .", 40 | "test": "yarn node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", 41 | "test:ci": "yarn test --ci --coverage", 42 | "type-check": "tsc", 43 | "release": "release-it", 44 | "commit": "commit-it" 45 | }, 46 | "release-it": { 47 | "git": { 48 | "commitMessage": "🔖 | v${version}" 49 | }, 50 | "github": { 51 | "release": true 52 | }, 53 | "npm": { 54 | "publish": false 55 | } 56 | }, 57 | "lint-staged": { 58 | "**/*.{ts,js,jsx,tsx}": "eslint --fix", 59 | "*": "prettier --write" 60 | }, 61 | "husky": { 62 | "hooks": { 63 | "pre-commit": "lint-staged" 64 | } 65 | }, 66 | "devDependencies": { 67 | "@types/jest": "29.4.0", 68 | "@types/node": "18.13.0", 69 | "@typescript-eslint/eslint-plugin": "5.52.0", 70 | "@typescript-eslint/parser": "5.52.0", 71 | "commit-it": "0.0.11", 72 | "dotenv": "16.0.3", 73 | "eslint": "8.34.0", 74 | "eslint-config-standard": "17.0.0", 75 | "eslint-plugin-import": "2.27.5", 76 | "eslint-plugin-n": "15.6.1", 77 | "eslint-plugin-node": "11.1.0", 78 | "eslint-plugin-promise": "6.1.1", 79 | "eslint-plugin-typescript-sort-keys": "2.1.0", 80 | "husky": "8.0.3", 81 | "jest": "29.4.3", 82 | "lint-staged": "13.1.2", 83 | "microbundle": "0.15.1", 84 | "prettier": "2.8.4", 85 | "release-it": "15.6.0", 86 | "ts-jest": "29.0.5", 87 | "typescript": "4.9.5" 88 | }, 89 | "dependencies": { 90 | "isomorphic-unfetch": "4.0.2" 91 | }, 92 | "resolutions": {} 93 | } 94 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "stabilityDays": 3, 4 | "timezone": "America/Los_Angeles", 5 | "schedule": ["on the first day of the month"], 6 | "packageRules": [ 7 | { 8 | "matchPackagePatterns": ["*"], 9 | "matchUpdateTypes": ["minor", "patch"], 10 | "groupName": "all non-major dependencies", 11 | "groupSlug": "all-minor-patch" 12 | } 13 | ], 14 | "ignoreDeps": [] 15 | } 16 | -------------------------------------------------------------------------------- /src/HuggingFace.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-unfetch' 2 | 3 | export type Options = { 4 | /** 5 | * (Default: true) Boolean. If a request 503s and wait_for_model is set to false, the request will be retried with the same parameters but with wait_for_model set to true. 6 | */ 7 | retry_on_error?: boolean 8 | /** 9 | * (Default: true). Boolean. There is a cache layer on the inference API to speedup requests we have already seen. Most models can use those results as is as models are deterministic (meaning the results will be the same anyway). However if you use a non deterministic model, you can set this parameter to prevent the caching mechanism from being used resulting in a real new query. 10 | */ 11 | use_cache?: boolean 12 | /** 13 | * (Default: false). Boolean to use GPU instead of CPU for inference (requires Startup plan at least). 14 | */ 15 | use_gpu?: boolean 16 | 17 | /** 18 | * (Default: false) Boolean. If the model is not ready, wait for it instead of receiving 503. It limits the number of requests required to get your inference done. It is advised to only set this flag to true after receiving a 503 error as it will limit hanging in your application to known places. 19 | */ 20 | wait_for_model?: boolean 21 | } 22 | 23 | export type Args = { 24 | model: string 25 | } 26 | 27 | export type FillMaskArgs = Args & { 28 | inputs: string 29 | } 30 | 31 | export type FillMaskReturn = { 32 | /** 33 | * The probability for this token. 34 | */ 35 | score: number 36 | /** 37 | * The actual sequence of tokens that ran against the model (may contain special tokens) 38 | */ 39 | sequence: string 40 | /** 41 | * The id of the token 42 | */ 43 | token: number 44 | /** 45 | * The string representation of the token 46 | */ 47 | token_str: string 48 | }[] 49 | 50 | export type SummarizationArgs = Args & { 51 | /** 52 | * A string to be summarized 53 | */ 54 | inputs: string 55 | parameters?: { 56 | /** 57 | * (Default: None). Integer to define the maximum length in tokens of the output summary. 58 | */ 59 | max_length?: number 60 | /** 61 | * (Default: None). Float (0-120.0). The amount of time in seconds that the query should take maximum. Network can cause some overhead so it will be a soft limit. 62 | */ 63 | max_time?: number 64 | /** 65 | * (Default: None). Integer to define the minimum length in tokens of the output summary. 66 | */ 67 | min_length?: number 68 | /** 69 | * (Default: None). Float (0.0-100.0). The more a token is used within generation the more it is penalized to not be picked in successive generation passes. 70 | */ 71 | repetition_penalty?: number 72 | /** 73 | * (Default: 1.0). Float (0.0-100.0). The temperature of the sampling operation. 1 means regular sampling, 0 means always take the highest score, 100.0 is getting closer to uniform probability. 74 | */ 75 | temperature?: number 76 | /** 77 | * (Default: None). Integer to define the top tokens considered within the sample operation to create new text. 78 | */ 79 | top_k?: number 80 | /** 81 | * (Default: None). Float to define the tokens that are within the sample operation of text generation. Add tokens in the sample for more probable to least probable until the sum of the probabilities is greater than top_p. 82 | */ 83 | top_p?: number 84 | } 85 | } 86 | 87 | export type SummarizationReturn = { 88 | /** 89 | * The string after translation 90 | */ 91 | summary_text: string 92 | } 93 | 94 | export type QuestionAnswerArgs = Args & { 95 | inputs: { 96 | context: string 97 | question: string 98 | } 99 | } 100 | 101 | export type QuestionAnswerReturn = { 102 | /** 103 | * A string that’s the answer within the text. 104 | */ 105 | answer: string 106 | /** 107 | * The index (string wise) of the stop of the answer within context. 108 | */ 109 | end: number 110 | /** 111 | * A float that represents how likely that the answer is correct 112 | */ 113 | score: number 114 | /** 115 | * The index (string wise) of the start of the answer within context. 116 | */ 117 | start: number 118 | } 119 | 120 | export type TableQuestionAnswerArgs = Args & { 121 | inputs: { 122 | /** 123 | * The query in plain text that you want to ask the table 124 | */ 125 | query: string 126 | /** 127 | * A table of data represented as a dict of list where entries are headers and the lists are all the values, all lists must have the same size. 128 | */ 129 | table: Record 130 | } 131 | } 132 | 133 | export type TableQuestionAnswerReturn = { 134 | /** 135 | * The aggregator used to get the answer 136 | */ 137 | aggregator: string 138 | /** 139 | * The plaintext answer 140 | */ 141 | answer: string 142 | /** 143 | * A list of coordinates of the cells contents 144 | */ 145 | cells: string[] 146 | /** 147 | * a list of coordinates of the cells referenced in the answer 148 | */ 149 | coordinates: number[][] 150 | } 151 | 152 | export type TextClassificationArgs = Args & { 153 | /** 154 | * A string to be classified 155 | */ 156 | inputs: string 157 | } 158 | 159 | export type TextClassificationReturn = { 160 | /** 161 | * The label for the class (model specific) 162 | */ 163 | label: string 164 | /** 165 | * A floats that represents how likely is that the text belongs to this class. 166 | */ 167 | score: number 168 | }[] 169 | 170 | export type TextGenerationArgs = Args & { 171 | /** 172 | * A string to be generated from 173 | */ 174 | inputs: string 175 | parameters?: { 176 | /** 177 | * (Optional: True). Bool. Whether or not to use sampling, use greedy decoding otherwise. 178 | */ 179 | do_sample?: boolean 180 | /** 181 | * (Default: None). Int (0-250). The amount of new tokens to be generated, this does not include the input length it is a estimate of the size of generated text you want. Each new tokens slows down the request, so look for balance between response times and length of text generated. 182 | */ 183 | max_new_tokens?: number 184 | /** 185 | * (Default: None). Float (0-120.0). The amount of time in seconds that the query should take maximum. Network can cause some overhead so it will be a soft limit. Use that in combination with max_new_tokens for best results. 186 | */ 187 | max_time?: number 188 | /** 189 | * (Default: 1). Integer. The number of proposition you want to be returned. 190 | */ 191 | num_return_sequences?: number 192 | /** 193 | * (Default: None). Float (0.0-100.0). The more a token is used within generation the more it is penalized to not be picked in successive generation passes. 194 | */ 195 | repetition_penalty?: number 196 | /** 197 | * (Default: True). Bool. If set to False, the return results will not contain the original query making it easier for prompting. 198 | */ 199 | return_full_text?: boolean 200 | /** 201 | * (Default: 1.0). Float (0.0-100.0). The temperature of the sampling operation. 1 means regular sampling, 0 means always take the highest score, 100.0 is getting closer to uniform probability. 202 | */ 203 | temperature?: number 204 | /** 205 | * (Default: None). Integer to define the top tokens considered within the sample operation to create new text. 206 | */ 207 | top_k?: number 208 | /** 209 | * (Default: None). Float to define the tokens that are within the sample operation of text generation. Add tokens in the sample for more probable to least probable until the sum of the probabilities is greater than top_p. 210 | */ 211 | top_p?: number 212 | } 213 | } 214 | 215 | export type TextGenerationReturn = { 216 | /** 217 | * The continuated string 218 | */ 219 | generated_text: string 220 | } 221 | 222 | export type TokenClassificationArgs = Args & { 223 | /** 224 | * A string to be classified 225 | */ 226 | inputs: string 227 | parameters?: { 228 | /** 229 | * (Default: simple). There are several aggregation strategies: 230 | * 231 | * none: Every token gets classified without further aggregation. 232 | * 233 | * simple: Entities are grouped according to the default schema (B-, I- tags get merged when the tag is similar). 234 | * 235 | * first: Same as the simple strategy except words cannot end up with different tags. Words will use the tag of the first token when there is ambiguity. 236 | * 237 | * average: Same as the simple strategy except words cannot end up with different tags. Scores are averaged across tokens and then the maximum label is applied. 238 | * 239 | * max: Same as the simple strategy except words cannot end up with different tags. Word entity will be the token with the maximum score. 240 | */ 241 | aggregation_strategy?: 'none' | 'simple' | 'first' | 'average' | 'max' 242 | } 243 | } 244 | 245 | export type TokenClassificationReturnValue = { 246 | /** 247 | * The offset stringwise where the answer is located. Useful to disambiguate if word occurs multiple times. 248 | */ 249 | end: number 250 | /** 251 | * The type for the entity being recognized (model specific). 252 | */ 253 | entity_group: string 254 | /** 255 | * How likely the entity was recognized. 256 | */ 257 | score: number 258 | /** 259 | * The offset stringwise where the answer is located. Useful to disambiguate if word occurs multiple times. 260 | */ 261 | start: number 262 | /** 263 | * The string that was captured 264 | */ 265 | word: string 266 | } 267 | 268 | export type TokenClassificationReturn = TokenClassificationReturnValue[] 269 | 270 | export type TranslationArgs = Args & { 271 | /** 272 | * A string to be translated 273 | */ 274 | inputs: string 275 | } 276 | 277 | export type TranslationReturn = { 278 | /** 279 | * The string after translation 280 | */ 281 | translation_text: string 282 | } 283 | 284 | export type ZeroShotClassificationArgs = Args & { 285 | /** 286 | * a string or list of strings 287 | */ 288 | inputs: string | string[] 289 | parameters: { 290 | /** 291 | * a list of strings that are potential classes for inputs. (max 10 candidate_labels, for more, simply run multiple requests, results are going to be misleading if using too many candidate_labels anyway. If you want to keep the exact same, you can simply run multi_label=True and do the scaling on your end. 292 | */ 293 | candidate_labels: string[] 294 | /** 295 | * (Default: false) Boolean that is set to True if classes can overlap 296 | */ 297 | multi_label?: boolean 298 | } 299 | } 300 | 301 | export type ZeroShotClassificationReturnValue = { 302 | labels: string[] 303 | scores: number[] 304 | sequence: string 305 | } 306 | 307 | export type ZeroShotClassificationReturn = ZeroShotClassificationReturnValue[] 308 | 309 | export type ConversationalArgs = Args & { 310 | inputs: { 311 | /** 312 | * A list of strings corresponding to the earlier replies from the model. 313 | */ 314 | generated_responses?: string[] 315 | /** 316 | * A list of strings corresponding to the earlier replies from the user. Should be of the same length of generated_responses. 317 | */ 318 | past_user_inputs?: string[] 319 | /** 320 | * The last input from the user in the conversation. 321 | */ 322 | text: string 323 | } 324 | parameters?: { 325 | /** 326 | * (Default: None). Integer to define the maximum length in tokens of the output summary. 327 | */ 328 | max_length?: number 329 | /** 330 | * (Default: None). Float (0-120.0). The amount of time in seconds that the query should take maximum. Network can cause some overhead so it will be a soft limit. 331 | */ 332 | max_time?: number 333 | /** 334 | * (Default: None). Integer to define the minimum length in tokens of the output summary. 335 | */ 336 | min_length?: number 337 | /** 338 | * (Default: None). Float (0.0-100.0). The more a token is used within generation the more it is penalized to not be picked in successive generation passes. 339 | */ 340 | repetition_penalty?: number 341 | /** 342 | * (Default: 1.0). Float (0.0-100.0). The temperature of the sampling operation. 1 means regular sampling, 0 means always take the highest score, 100.0 is getting closer to uniform probability. 343 | */ 344 | temperature?: number 345 | /** 346 | * (Default: None). Integer to define the top tokens considered within the sample operation to create new text. 347 | */ 348 | top_k?: number 349 | /** 350 | * (Default: None). Float to define the tokens that are within the sample operation of text generation. Add tokens in the sample for more probable to least probable until the sum of the probabilities is greater than top_p. 351 | */ 352 | top_p?: number 353 | } 354 | } 355 | 356 | export type ConversationalReturn = { 357 | conversation: { 358 | generated_responses: string[] 359 | past_user_inputs: string[] 360 | } 361 | generated_text: string 362 | warnings: string[] 363 | } 364 | 365 | export type FeatureExtractionArgs = Args & { 366 | /** 367 | * The inputs vary based on the model. For example when using sentence-transformers/paraphrase-xlm-r-multilingual-v1 the inputs will look like this: 368 | * 369 | * inputs: { 370 | * "source_sentence": "That is a happy person", 371 | * "sentences": ["That is a happy dog", "That is a very happy person", "Today is a sunny day"] 372 | */ 373 | inputs: Record | Record[] 374 | } 375 | 376 | /** 377 | * Returned values are a list of floats, or a list of list of floats (depending on if you sent a string or a list of string, and if the automatic reduction, usually mean_pooling for instance was applied for you or not. This should be explained on the model's README. 378 | */ 379 | export type FeatureExtractionReturn = (number | number[])[] 380 | 381 | export type ImageClassificationArgs = Args & { 382 | /** 383 | * Binary image data 384 | */ 385 | data: any 386 | } 387 | 388 | export type ImageClassificationReturnValue = { 389 | /** 390 | * A float that represents how likely it is that the image file belongs to this class. 391 | */ 392 | label: string 393 | /** 394 | * The label for the class (model specific) 395 | */ 396 | score: number 397 | } 398 | 399 | export type ImageClassificationReturn = ImageClassificationReturnValue[] 400 | 401 | export type ObjectDetectionArgs = Args & { 402 | /** 403 | * Binary image data 404 | */ 405 | data: any 406 | } 407 | 408 | export type ObjectDetectionReturnValue = { 409 | /** 410 | * A dict (with keys [xmin,ymin,xmax,ymax]) representing the bounding box of a detected object. 411 | */ 412 | box: { 413 | xmax: number 414 | xmin: number 415 | ymax: number 416 | ymin: number 417 | } 418 | /** 419 | * The label for the class (model specific) of a detected object. 420 | */ 421 | label: string 422 | 423 | /** 424 | * A float that represents how likely it is that the detected object belongs to the given class. 425 | */ 426 | score: number 427 | } 428 | 429 | export type ObjectDetectionReturn = ObjectDetectionReturnValue[] 430 | 431 | export type ImageSegmentationArgs = Args & { 432 | /** 433 | * Binary image data 434 | */ 435 | data: any 436 | } 437 | 438 | export type ImageSegmentationReturnValue = { 439 | /** 440 | * The label for the class (model specific) of a segment. 441 | */ 442 | label: string 443 | /** 444 | * A str (base64 str of a single channel black-and-white img) representing the mask of a segment. 445 | */ 446 | mask: string 447 | /** 448 | * A float that represents how likely it is that the detected object belongs to the given class. 449 | */ 450 | score: number 451 | } 452 | 453 | export type ImageSegmentationReturn = ImageSegmentationReturnValue[] 454 | 455 | export type AutomaticSpeechRecognitionArgs = Args & { 456 | /** 457 | * Binary audio data 458 | */ 459 | data: any 460 | } 461 | 462 | export type AutomaticSpeechRecognitionReturn = { 463 | /** 464 | * The text that was recognized from the audio 465 | */ 466 | text: string 467 | } 468 | 469 | export type AudioClassificationArgs = Args & { 470 | /** 471 | * Binary audio data 472 | */ 473 | data: any 474 | } 475 | 476 | export type AudioClassificationReturnValue = { 477 | /** 478 | * The label for the class (model specific) 479 | */ 480 | label: string 481 | 482 | /** 483 | * A float that represents how likely it is that the audio file belongs to this class. 484 | */ 485 | score: number 486 | } 487 | 488 | export type AudioClassificationReturn = AudioClassificationReturnValue[] 489 | 490 | export class HuggingFace { 491 | private readonly apiKey: string 492 | private readonly defaultOptions: Options 493 | 494 | constructor(apiKey: string, defaultOptions: Options = {}) { 495 | this.apiKey = apiKey 496 | this.defaultOptions = defaultOptions 497 | } 498 | 499 | /** 500 | * Tries to fill in a hole with a missing word (token to be precise). That’s the base task for BERT models. 501 | */ 502 | public async fillMask( 503 | args: FillMaskArgs, 504 | options?: Options 505 | ): Promise { 506 | return this.request(args, options) 507 | } 508 | 509 | /** 510 | * 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. 511 | */ 512 | public async summarization( 513 | args: SummarizationArgs, 514 | options?: Options 515 | ): Promise { 516 | return (await this.request(args, options))?.[0] 517 | } 518 | 519 | /** 520 | * Want to have a nice know-it-all bot that can answer any question?. Recommended model: deepset/roberta-base-squad2 521 | */ 522 | public async questionAnswer( 523 | args: QuestionAnswerArgs, 524 | options?: Options 525 | ): Promise { 526 | return await this.request(args, options) 527 | } 528 | 529 | /** 530 | * 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. 531 | */ 532 | public async tableQuestionAnswer( 533 | args: TableQuestionAnswerArgs, 534 | options?: Options 535 | ): Promise { 536 | return await this.request(args, options) 537 | } 538 | 539 | /** 540 | * Usually used for sentiment-analysis this will output the likelihood of classes of an input. Recommended model: distilbert-base-uncased-finetuned-sst-2-english 541 | */ 542 | public async textClassification( 543 | args: TextClassificationArgs, 544 | options?: Options 545 | ): Promise { 546 | return (await this.request(args, options))?.[0] 547 | } 548 | 549 | /** 550 | * 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). 551 | */ 552 | public async textGeneration( 553 | args: TextGenerationArgs, 554 | options?: Options 555 | ): Promise { 556 | return (await this.request(args, options))?.[0] 557 | } 558 | 559 | /** 560 | * 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 561 | */ 562 | public async tokenClassification( 563 | args: TokenClassificationArgs, 564 | options?: Options 565 | ): Promise { 566 | return HuggingFace.toArray(await this.request(args, options)) 567 | } 568 | 569 | /** 570 | * This task is well known to translate text from one language to another. Recommended model: Helsinki-NLP/opus-mt-ru-en. 571 | */ 572 | public async translation( 573 | args: TranslationArgs, 574 | options?: Options 575 | ): Promise { 576 | return (await this.request(args, options))?.[0] 577 | } 578 | 579 | /** 580 | * 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. 581 | */ 582 | public async zeroShotClassification( 583 | args: ZeroShotClassificationArgs, 584 | options?: Options 585 | ): Promise { 586 | return HuggingFace.toArray(await this.request(args, options)) 587 | } 588 | 589 | /** 590 | * This task corresponds to any chatbot like structure. Models tend to have shorter max_length, so please check with caution when using a given model if you need long range dependency or not. Recommended model: microsoft/DialoGPT-large. 591 | * 592 | */ 593 | public async conversational( 594 | args: ConversationalArgs, 595 | options?: Options 596 | ): Promise { 597 | return await this.request(args, options) 598 | } 599 | 600 | /** 601 | * This task reads some text and outputs raw float values, that are usually consumed as part of a semantic database/semantic search. 602 | */ 603 | public async featureExtraction( 604 | args: FeatureExtractionArgs, 605 | options?: Options 606 | ): Promise { 607 | return await this.request(args, options) 608 | } 609 | 610 | /** 611 | * This task reads some audio input and outputs the said words within the audio files. 612 | * Recommended model (english language): facebook/wav2vec2-large-960h-lv60-self 613 | */ 614 | public async automaticSpeechRecognition( 615 | args: AutomaticSpeechRecognitionArgs, 616 | options?: Options 617 | ): Promise { 618 | return await this.request(args, { 619 | ...options, 620 | binary: true 621 | }) 622 | } 623 | 624 | /** 625 | * This task reads some audio input and outputs the likelihood of classes. 626 | * Recommended model: superb/hubert-large-superb-er 627 | */ 628 | public async audioClassification( 629 | args: AudioClassificationArgs, 630 | options?: Options 631 | ): Promise { 632 | return await this.request(args, { 633 | ...options, 634 | binary: true 635 | }) 636 | } 637 | 638 | /** 639 | * This task reads some image input and outputs the likelihood of classes. 640 | * Recommended model: google/vit-base-patch16-224 641 | */ 642 | public async imageClassification( 643 | args: ImageClassificationArgs, 644 | options?: Options 645 | ): Promise { 646 | return await this.request(args, { 647 | ...options, 648 | binary: true 649 | }) 650 | } 651 | 652 | /** 653 | * This task reads some image input and outputs the likelihood of classes & bounding boxes of detected objects. 654 | * Recommended model: facebook/detr-resnet-50 655 | */ 656 | public async objectDetection( 657 | args: ObjectDetectionArgs, 658 | options?: Options 659 | ): Promise { 660 | return await this.request(args, { 661 | ...options, 662 | binary: true 663 | }) 664 | } 665 | 666 | /** 667 | * This task reads some image input and outputs the likelihood of classes & bounding boxes of detected objects. 668 | * Recommended model: facebook/detr-resnet-50-panoptic 669 | */ 670 | public async imageSegmentation( 671 | args: ImageSegmentationArgs, 672 | options?: Options 673 | ): Promise { 674 | return await this.request(args, { 675 | ...options, 676 | binary: true 677 | }) 678 | } 679 | 680 | public async request( 681 | args: Args & { data?: any }, 682 | options?: Options & { 683 | binary?: boolean 684 | } 685 | ): Promise { 686 | const mergedOptions = { ...this.defaultOptions, ...options } 687 | const { model, ...otherArgs } = args 688 | const response = await fetch( 689 | `https://api-inference.huggingface.co/models/${model}`, 690 | { 691 | headers: { Authorization: `Bearer ${this.apiKey}` }, 692 | method: 'POST', 693 | body: options?.binary 694 | ? args.data 695 | : JSON.stringify({ 696 | ...otherArgs, 697 | options: mergedOptions 698 | }) 699 | } 700 | ) 701 | 702 | if ( 703 | mergedOptions.retry_on_error !== false && 704 | response.status === 503 && 705 | !mergedOptions.wait_for_model 706 | ) { 707 | return this.request(args, { 708 | ...mergedOptions, 709 | wait_for_model: true 710 | }) 711 | } 712 | 713 | const res = await response.json() 714 | if (res.error) { 715 | throw new Error(res.error) 716 | } 717 | return res 718 | } 719 | 720 | private static toArray(obj: any): any[] { 721 | if (Array.isArray(obj)) { 722 | return obj 723 | } 724 | return [obj] 725 | } 726 | } 727 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { HuggingFace } from './HuggingFace' 2 | export * from './HuggingFace' 3 | export default HuggingFace 4 | -------------------------------------------------------------------------------- /test/HuggingFace.test.ts: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' // 3 minute 2 | 3 | import HuggingFace from '../src' 4 | import { readFileSync } from 'fs' 5 | 6 | jest.setTimeout(60000 * 3) 7 | 8 | describe('HuggingFace', () => { 9 | // Individual tests can be ran without providing an api key, however running all tests without an api key will result in rate limiting error. 10 | const hf = new HuggingFace(process.env.HF_API_KEY as string) 11 | it('throws error if model does not exist', () => { 12 | expect( 13 | hf.fillMask({ 14 | model: 'this-model-does-not-exist-123', 15 | inputs: '[MASK] world!' 16 | }) 17 | ).rejects.toThrowError('Model this-model-does-not-exist-123 does not exist') 18 | }) 19 | it('fillMask', async () => { 20 | expect( 21 | await hf.fillMask({ 22 | model: 'bert-base-uncased', 23 | inputs: '[MASK] world!' 24 | }) 25 | ).toEqual( 26 | expect.arrayContaining([ 27 | expect.objectContaining({ 28 | score: expect.any(Number), 29 | token: expect.any(Number), 30 | token_str: expect.any(String), 31 | sequence: expect.any(String) 32 | }) 33 | ]) 34 | ) 35 | }) 36 | it('summarization', async () => { 37 | expect( 38 | await hf.summarization({ 39 | model: 'facebook/bart-large-cnn', 40 | inputs: 41 | 'The tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey building, and the tallest structure in Paris. 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, a title it held for 41 years until the Chrysler Building in New York City was finished in 1930.', 42 | parameters: { 43 | max_length: 100 44 | } 45 | }) 46 | ).toEqual({ 47 | summary_text: 48 | '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.' 49 | }) 50 | }) 51 | it('questionAnswer', async () => { 52 | expect( 53 | await hf.questionAnswer({ 54 | model: 'deepset/roberta-base-squad2', 55 | inputs: { 56 | question: 'What is the capital of France?', 57 | context: 'The capital of France is Paris.' 58 | } 59 | }) 60 | ).toMatchObject({ 61 | answer: 'Paris', 62 | score: expect.any(Number), 63 | start: expect.any(Number), 64 | end: expect.any(Number) 65 | }) 66 | }) 67 | it('table question answer', async () => { 68 | expect( 69 | await hf.tableQuestionAnswer({ 70 | model: 'google/tapas-base-finetuned-wtq', 71 | inputs: { 72 | query: 'How many stars does the transformers repository have?', 73 | table: { 74 | Repository: ['Transformers', 'Datasets', 'Tokenizers'], 75 | Stars: ['36542', '4512', '3934'], 76 | Contributors: ['651', '77', '34'], 77 | 'Programming language': [ 78 | 'Python', 79 | 'Python', 80 | 'Rust, Python and NodeJS' 81 | ] 82 | } 83 | } 84 | }) 85 | ).toMatchObject({ 86 | answer: 'AVERAGE > 36542', 87 | coordinates: [[0, 1]], 88 | cells: ['36542'], 89 | aggregator: 'AVERAGE' 90 | }) 91 | }) 92 | it('textClassification', async () => { 93 | expect( 94 | await hf.textClassification({ 95 | model: 'distilbert-base-uncased-finetuned-sst-2-english', 96 | inputs: 'I like you. I love you.' 97 | }) 98 | ).toEqual( 99 | expect.arrayContaining([ 100 | expect.objectContaining({ 101 | label: expect.any(String), 102 | score: expect.any(Number) 103 | }) 104 | ]) 105 | ) 106 | }) 107 | it('textGeneration', async () => { 108 | expect( 109 | await hf.textGeneration({ 110 | model: 'gpt2', 111 | inputs: 'The answer to the universe is' 112 | }) 113 | ).toMatchObject({ 114 | generated_text: expect.any(String) 115 | }) 116 | }) 117 | it('tokenClassification', async () => { 118 | expect( 119 | await hf.tokenClassification({ 120 | model: 'dbmdz/bert-large-cased-finetuned-conll03-english', 121 | inputs: 'My name is Sarah Jessica Parker but you can call me Jessica' 122 | }) 123 | ).toEqual( 124 | expect.arrayContaining([ 125 | expect.objectContaining({ 126 | entity_group: expect.any(String), 127 | score: expect.any(Number), 128 | word: expect.any(String), 129 | start: expect.any(Number), 130 | end: expect.any(Number) 131 | }) 132 | ]) 133 | ) 134 | }) 135 | it('translation', async () => { 136 | expect( 137 | await hf.translation({ 138 | model: 't5-base', 139 | inputs: 'My name is Wolfgang and I live in Berlin' 140 | }) 141 | ).toMatchObject({ 142 | translation_text: 'Mein Name ist Wolfgang und ich lebe in Berlin' 143 | }) 144 | }) 145 | it('zeroShotClassification', async () => { 146 | expect( 147 | await hf.zeroShotClassification({ 148 | model: 'facebook/bart-large-mnli', 149 | inputs: [ 150 | 'Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!' 151 | ], 152 | parameters: { candidate_labels: ['refund', 'legal', 'faq'] } 153 | }) 154 | ).toEqual( 155 | expect.arrayContaining([ 156 | expect.objectContaining({ 157 | sequence: 158 | 'Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!', 159 | labels: ['refund', 'faq', 'legal'], 160 | scores: [0.877787709236145, 0.10522633045911789, 0.01698593981564045] 161 | }) 162 | ]) 163 | ) 164 | }) 165 | it('conversational', async () => { 166 | expect( 167 | await hf.conversational({ 168 | model: 'microsoft/DialoGPT-large', 169 | inputs: { 170 | past_user_inputs: ['Which movie is the best ?'], 171 | generated_responses: ['It is Die Hard for sure.'], 172 | text: 'Can you explain why ?' 173 | } 174 | }) 175 | ).toMatchObject({ 176 | generated_text: "It's the best movie ever.", 177 | conversation: { 178 | past_user_inputs: [ 179 | 'Which movie is the best ?', 180 | 'Can you explain why ?' 181 | ], 182 | generated_responses: [ 183 | 'It is Die Hard for sure.', 184 | "It's the best movie ever." 185 | ] 186 | }, 187 | warnings: [ 188 | 'Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.' 189 | ] 190 | }) 191 | }) 192 | it('featureExtraction', async () => { 193 | expect( 194 | await hf.featureExtraction({ 195 | model: 'sentence-transformers/paraphrase-xlm-r-multilingual-v1', 196 | inputs: { 197 | source_sentence: 'That is a happy person', 198 | sentences: [ 199 | 'That is a happy dog', 200 | 'That is a very happy person', 201 | 'Today is a sunny day' 202 | ] 203 | } 204 | }) 205 | ).toEqual([expect.any(Number), expect.any(Number), expect.any(Number)]) 206 | }) 207 | it('automaticSpeechRecognition', async () => { 208 | expect( 209 | await hf.automaticSpeechRecognition({ 210 | model: 'facebook/wav2vec2-large-960h-lv60-self', 211 | data: readFileSync('test/sample1.flac') 212 | }) 213 | ).toMatchObject({ 214 | text: "GOING ALONG SLUSHY COUNTRY ROADS AND SPEAKING TO DAMP AUDIENCES IN DRAUGHTY SCHOOLROOMS DAY AFTER DAY FOR A FORTNIGHT HE'LL HAVE TO PUT IN AN APPEARANCE AT SOME PLACE OF WORSHIP ON SUNDAY MORNING AND HE CAN COME TO US IMMEDIATELY AFTERWARDS" 215 | }) 216 | }) 217 | it('audioClassification', async () => { 218 | expect( 219 | await hf.audioClassification({ 220 | model: 'superb/hubert-large-superb-er', 221 | data: readFileSync('test/sample1.flac') 222 | }) 223 | ).toEqual( 224 | expect.arrayContaining([ 225 | expect.objectContaining({ 226 | score: expect.any(Number), 227 | label: expect.any(String) 228 | }) 229 | ]) 230 | ) 231 | }) 232 | it('imageClassification', async () => { 233 | expect( 234 | await hf.imageClassification({ 235 | data: readFileSync('test/cheetah.png'), 236 | model: 'google/vit-base-patch16-224' 237 | }) 238 | ).toEqual( 239 | expect.arrayContaining([ 240 | expect.objectContaining({ 241 | score: expect.any(Number), 242 | label: expect.any(String) 243 | }) 244 | ]) 245 | ) 246 | }) 247 | it('objectDetection', async () => { 248 | expect( 249 | await hf.imageClassification({ 250 | data: readFileSync('test/cats.png'), 251 | model: 'facebook/detr-resnet-50' 252 | }) 253 | ).toEqual( 254 | expect.arrayContaining([ 255 | expect.objectContaining({ 256 | score: expect.any(Number), 257 | label: expect.any(String), 258 | box: expect.objectContaining({ 259 | xmin: expect.any(Number), 260 | ymin: expect.any(Number), 261 | xmax: expect.any(Number), 262 | ymax: expect.any(Number) 263 | }) 264 | }) 265 | ]) 266 | ) 267 | }) 268 | xit('imageSegmentation', async () => { 269 | expect( 270 | await hf.imageClassification({ 271 | data: readFileSync('test/cats.png'), 272 | model: 'facebook/detr-resnet-50-panoptic' 273 | }) 274 | ).toEqual( 275 | expect.arrayContaining([ 276 | expect.objectContaining({ 277 | score: expect.any(Number), 278 | label: expect.any(String), 279 | mask: expect.any(String) 280 | }) 281 | ]) 282 | ) 283 | }) 284 | }) 285 | -------------------------------------------------------------------------------- /test/cats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimMikeladze/huggingface/d3dd95456467bb002c92724cffccc1a348e90d40/test/cats.png -------------------------------------------------------------------------------- /test/cheetah.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimMikeladze/huggingface/d3dd95456467bb002c92724cffccc1a348e90d40/test/cheetah.png -------------------------------------------------------------------------------- /test/sample1.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimMikeladze/huggingface/d3dd95456467bb002c92724cffccc1a348e90d40/test/sample1.flac -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "noEmit": true, 5 | "module": "ESNext", 6 | "target": "ESNext", 7 | "moduleResolution": "Node" 8 | } 9 | } 10 | --------------------------------------------------------------------------------