Skip to content

Commit e5bdf00

Browse files
julien-cMishigkrampstudio
authored
Local apps (huggingface#659)
Co-authored-by: Mishig <[email protected]> Co-authored-by: Bertrand CHEVRIER <[email protected]>
1 parent 30d02eb commit e5bdf00

File tree

9 files changed

+200
-36
lines changed

9 files changed

+200
-36
lines changed

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ We use `pnpm` as our package manager. You need to use it, eg `pnpm install` inst
88

99
If you want to format the whole codebase, you can do `pnpm -r format` at the root.
1010

11-
Otherwise, we avoid runtime dependencies unless they're strictly needed. For example, our only dependency is `hash-wasm`, and it's only in the browser context and when uploaded files are > 10MB.
11+
Other than that, we avoid runtime dependencies unless they're strictly needed. For example, our only dependency is `hash-wasm`, and it's only in the browser context and when uploaded files are > 10MB.
1212

1313
## Pull requests
1414

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ This is a collection of JS libraries to interact with the Hugging Face API, with
5252
- [@huggingface/inference](packages/inference/README.md): Use Inference Endpoints (dedicated) and Inference API (serverless) to make calls to 100,000+ Machine Learning models
5353
- [@huggingface/hub](packages/hub/README.md): Interact with huggingface.co to create or delete repos and commit / download files
5454
- [@huggingface/agents](packages/agents/README.md): Interact with HF models through a natural language interface
55+
- [@huggingface/gguf](packages/gguf/README.md): A GGUF parser that works on remotely hosted files.
56+
- [@huggingface/tasks](packages/tasks/README.md): The definition files and source-of-truth for the Hub's main primitives like pipeline tasks, model libraries, etc.
57+
5558

5659

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

packages/tasks/README.md

+12-14
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
# Tasks
22

3-
This package contains data used for https://huggingface.co/tasks.
3+
This package contains the definition files (written in Typescript) for the huggingface.co hub's:
4+
5+
- **pipeline types** (a.k.a. **task types**) - used to determine which widget to display on the model page, and which inference API to run.
6+
- **default widget inputs** - when they aren't provided in the model card.
7+
- definitions and UI elements for **model libraries** (and soon for **dataset libraries**).
8+
9+
Please add any missing ones to these definitions by opening a PR. Thanks 🔥
410

5-
## Philosophy behind Tasks
11+
⚠️ The hub's definitive doc is at https://huggingface.co/docs/hub.
12+
13+
## Definition of Tasks
14+
15+
This package also contains data used to define https://huggingface.co/tasks.
616

717
The Task pages are made to lower the barrier of entry to understand a task that can be solved with machine learning and use or train a model to accomplish it. It's a collaborative documentation effort made to help out software developers, social scientists, or anyone with no background in machine learning that is interested in understanding how machine learning models can be used to solve a problem.
818

@@ -19,16 +29,4 @@ We have a [`dataset`](https://huggingface.co/datasets/huggingfacejs/tasks) that
1929

2030
This might seem overwhelming, but you don't necessarily need to add all of these in one pull request or on your own, you can simply contribute one section. Feel free to ask for help whenever you need.
2131

22-
## Other data
23-
24-
This package contains the definition files (written in Typescript) for the huggingface.co hub's:
25-
26-
- **pipeline types** a.k.a. **task types** (used to determine which widget to display on the model page, and which inference API to run)
27-
- **default widget inputs** (when they aren't provided in the model card)
28-
- definitions and UI elements for **model libraries** (and soon for **dataset libraries**).
29-
30-
Please add to any of those definitions by opening a PR. Thanks 🔥
31-
32-
⚠️ The hub's definitive doc is at https://huggingface.co/docs/hub.
33-
3432
## Feedback (feature requests, bugs, etc.) is super welcome 💙💚💛💜♥️🧡

packages/tasks/src/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,6 @@ export { SPECIAL_TOKENS_ATTRIBUTES } from "./tokenizer-data";
4545

4646
import * as snippets from "./snippets";
4747
export { snippets };
48+
49+
export { LOCAL_APPS } from "./local-apps";
50+
export type { LocalApp, LocalAppKey } from "./local-apps";

packages/tasks/src/local-apps.ts

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import type { ModelData } from "./model-data";
2+
import type { PipelineType } from "./pipelines";
3+
4+
/**
5+
* Elements configurable by a local app.
6+
*/
7+
export type LocalApp = {
8+
/**
9+
* Name that appears in buttons
10+
*/
11+
prettyLabel: string;
12+
/**
13+
* Link to get more info about a local app (website etc)
14+
*/
15+
docsUrl: string;
16+
/**
17+
* main category of app
18+
*/
19+
mainTask: PipelineType;
20+
/**
21+
* Whether to display a pill "macOS-only"
22+
*/
23+
macOSOnly?: boolean;
24+
25+
comingSoon?: boolean;
26+
/**
27+
* IMPORTANT: function to figure out whether to display the button on a model page's main "Use this model" dropdown.
28+
*/
29+
displayOnModelPage: (model: ModelData) => boolean;
30+
} & (
31+
| {
32+
/**
33+
* If the app supports deeplink, URL to open.
34+
*/
35+
deeplink: (model: ModelData) => URL;
36+
}
37+
| {
38+
/**
39+
* And if not (mostly llama.cpp), snippet to copy/paste in your terminal
40+
*/
41+
snippet: (model: ModelData) => string;
42+
}
43+
);
44+
45+
function isGgufModel(model: ModelData) {
46+
return model.tags.includes("gguf");
47+
}
48+
49+
const snippetLlamacpp = (model: ModelData): string => {
50+
return `./main \
51+
--hf-repo ${model.id} \
52+
-m file.gguf \
53+
-p "I believe the meaning of life is " -n 128`;
54+
};
55+
56+
/**
57+
* Add your new local app here.
58+
*
59+
* This is open to new suggestions and awesome upcoming apps.
60+
*
61+
* /!\ IMPORTANT
62+
*
63+
* If possible, you need to support deeplinks and be as cross-platform as possible.
64+
*
65+
* Ping the HF team if we can help with anything!
66+
*/
67+
export const LOCAL_APPS = {
68+
"llama.cpp": {
69+
prettyLabel: "llama.cpp",
70+
docsUrl: "https://github.com/ggerganov/llama.cpp",
71+
mainTask: "text-generation",
72+
displayOnModelPage: isGgufModel,
73+
snippet: snippetLlamacpp,
74+
},
75+
lmstudio: {
76+
prettyLabel: "LM Studio",
77+
docsUrl: "https://lmstudio.ai",
78+
mainTask: "text-generation",
79+
displayOnModelPage: isGgufModel,
80+
deeplink: (model) => new URL(`lmstudio://open_from_hf?model=${model.id}`),
81+
},
82+
jan: {
83+
prettyLabel: "Jan",
84+
docsUrl: "https://jan.ai",
85+
mainTask: "text-generation",
86+
displayOnModelPage: isGgufModel,
87+
deeplink: (model) => new URL(`jan://open_from_hf?model=${model.id}`),
88+
},
89+
faraday: {
90+
prettyLabel: "Faraday",
91+
docsUrl: "https://faraday.dev",
92+
mainTask: "text-generation",
93+
macOSOnly: true,
94+
displayOnModelPage: isGgufModel,
95+
deeplink: (model) => new URL(`faraday://open_from_hf?model=${model.id}`),
96+
},
97+
drawthings: {
98+
prettyLabel: "Draw Things",
99+
docsUrl: "https://drawthings.ai",
100+
mainTask: "text-to-image",
101+
macOSOnly: true,
102+
/**
103+
* random function, will need to refine the actual conditions:
104+
*/
105+
displayOnModelPage: (model) => model.tags.includes("textual_inversion"),
106+
deeplink: (model) => new URL(`drawthings://open_from_hf?model=${model.id}`),
107+
},
108+
diffusionbee: {
109+
prettyLabel: "DiffusionBee",
110+
docsUrl: "https://diffusionbee.com",
111+
mainTask: "text-to-image",
112+
macOSOnly: true,
113+
comingSoon: true,
114+
displayOnModelPage: (model) => model.library_name === "diffusers" && model.pipeline_tag === "text-to-image",
115+
deeplink: (model) => new URL(`diffusionbee://open_from_hf?model=${model.id}`),
116+
},
117+
} satisfies Record<string, LocalApp>;
118+
119+
export type LocalAppKey = keyof typeof LOCAL_APPS;

packages/tasks/src/model-data.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ export interface ModelData {
2525
* id of model (e.g. 'user/repo_name')
2626
*/
2727
id: string;
28-
/**
29-
* Kept for backward compatibility
30-
*/
31-
modelId?: string;
3228
/**
3329
* Whether or not to enable inference widget for this model
3430
*/
@@ -84,7 +80,7 @@ export interface ModelData {
8480
/**
8581
* all the model tags
8682
*/
87-
tags?: string[];
83+
tags: string[];
8884
/**
8985
* transformers-specific info to display in the code sample.
9086
*/

packages/tasks/src/model-libraries-snippets.ts

+15-15
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ predictions = predictor.predict_json(predictor_input)`,
3333
];
3434

3535
export const allennlp = (model: ModelData): string[] => {
36-
if (model.tags?.includes("question-answering")) {
36+
if (model.tags.includes("question-answering")) {
3737
return allennlpQuestionAnswering(model);
3838
}
3939
return allennlpUnknown(model);
@@ -85,11 +85,11 @@ pipeline.load_textual_inversion("${model.id}")`,
8585
];
8686

8787
export const diffusers = (model: ModelData): string[] => {
88-
if (model.tags?.includes("controlnet")) {
88+
if (model.tags.includes("controlnet")) {
8989
return diffusers_controlnet(model);
90-
} else if (model.tags?.includes("lora")) {
90+
} else if (model.tags.includes("lora")) {
9191
return diffusers_lora(model);
92-
} else if (model.tags?.includes("textual_inversion")) {
92+
} else if (model.tags.includes("textual_inversion")) {
9393
return diffusers_textual_inversion(model);
9494
} else {
9595
return diffusers_default(model);
@@ -118,9 +118,9 @@ text, *_ = model(speech)[0]`,
118118
const espnetUnknown = () => [`unknown model type (must be text-to-speech or automatic-speech-recognition)`];
119119

120120
export const espnet = (model: ModelData): string[] => {
121-
if (model.tags?.includes("text-to-speech")) {
121+
if (model.tags.includes("text-to-speech")) {
122122
return espnetTTS(model);
123-
} else if (model.tags?.includes("automatic-speech-recognition")) {
123+
} else if (model.tags.includes("automatic-speech-recognition")) {
124124
return espnetASR(model);
125125
}
126126
return espnetUnknown();
@@ -228,7 +228,7 @@ inference.crop("file.wav", excerpt)`,
228228
];
229229

230230
export const pyannote_audio = (model: ModelData): string[] => {
231-
if (model.tags?.includes("pyannote-audio-pipeline")) {
231+
if (model.tags.includes("pyannote-audio-pipeline")) {
232232
return pyannote_audio_pipeline(model);
233233
}
234234
return pyannote_audio_model(model);
@@ -258,9 +258,9 @@ model = TFAutoModel.from_pretrained("${model.id}")
258258
];
259259

260260
export const tensorflowtts = (model: ModelData): string[] => {
261-
if (model.tags?.includes("text-to-mel")) {
261+
if (model.tags.includes("text-to-mel")) {
262262
return tensorflowttsTextToMel(model);
263-
} else if (model.tags?.includes("mel-to-wav")) {
263+
} else if (model.tags.includes("mel-to-wav")) {
264264
return tensorflowttsMelToWav(model);
265265
}
266266
return tensorflowttsUnknown(model);
@@ -309,7 +309,7 @@ model = joblib.load(
309309
};
310310

311311
export const sklearn = (model: ModelData): string[] => {
312-
if (model.tags?.includes("skops")) {
312+
if (model.tags.includes("skops")) {
313313
const skopsmodelFile = model.config?.sklearn?.model?.file;
314314
const skopssaveFormat = model.config?.sklearn?.model_format;
315315
if (!skopsmodelFile) {
@@ -413,7 +413,7 @@ export const transformers = (model: ModelData): string[] => {
413413
if (!info) {
414414
return [`# ⚠️ Type of model unknown`];
415415
}
416-
const remote_code_snippet = model.tags?.includes(TAG_CUSTOM_CODE) ? ", trust_remote_code=True" : "";
416+
const remote_code_snippet = model.tags.includes(TAG_CUSTOM_CODE) ? ", trust_remote_code=True" : "";
417417

418418
let autoSnippet: string;
419419
if (info.processor) {
@@ -564,7 +564,7 @@ model = create_model(${model.id})`,
564564
export const nemo = (model: ModelData): string[] => {
565565
let command: string[] | undefined = undefined;
566566
// Resolve the tag to a nemo domain/sub-domain
567-
if (model.tags?.includes("automatic-speech-recognition")) {
567+
if (model.tags.includes("automatic-speech-recognition")) {
568568
command = nemoDomainResolver("ASR", model);
569569
}
570570

@@ -605,11 +605,11 @@ wav = model.generate(descriptions) # generates 3 samples.`,
605605
];
606606

607607
export const audiocraft = (model: ModelData): string[] => {
608-
if (model.tags?.includes("musicgen")) {
608+
if (model.tags.includes("musicgen")) {
609609
return musicgen(model);
610-
} else if (model.tags?.includes("audiogen")) {
610+
} else if (model.tags.includes("audiogen")) {
611611
return audiogen(model);
612-
} else if (model.tags?.includes("magnet")) {
612+
} else if (model.tags.includes("magnet")) {
613613
return magnet(model);
614614
} else {
615615
return [`# Type of model unknown.`];

packages/widgets/src/lib/components/InferenceWidget/InferenceWidget.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
};
8181
8282
$: widgetComponent =
83-
model.pipeline_tag === "text-generation" && model.tags?.includes("conversational")
83+
model.pipeline_tag === "text-generation" && model.tags.includes("conversational")
8484
? (ConversationalWidget as typeof SvelteComponent)
8585
: model.pipeline_tag && model.pipeline_tag in WIDGET_COMPONENTS
8686
? WIDGET_COMPONENTS[model.pipeline_tag as keyof typeof WIDGET_COMPONENTS]

0 commit comments

Comments
 (0)