diff --git a/README.md b/README.md index d01422e..95a1b83 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ In January 2025, Hugging Face announced [Inference Providers](https://huggingfac This repo contains a [mapping](src/models.ts) of Replicate models to Hugging Face models, so we can tell Hugging Face when to display a Replicate inference widget on its model pages. -This repo also includes tooling to keep those mappings up to date using Hugging Face's Model Mappings API. +This repo also includes tooling to keep those mappings up to date using Hugging Face's [Model Mappings API](https://huggingface.co/docs/inference-providers/register-as-a-provider#3-model-mapping-api). ## Adding a new model @@ -24,6 +24,25 @@ Add your new model to the `inferenceModels` array, like so: To see allowable values for `task`, refer to [huggingface.co/tasks](https://huggingface.co/tasks). +## Adding a new tag mapping + +To add a new tag mapping, edit [src/tags.ts](src/tags.ts) + +Add your new tag to the `inferenceTags` array, like so: + +```typescript +{ + type: 'tag-filter', + task: 'text-to-image', + tags: ['flux', 'lora'], + providerModel: 'black-forest-labs/flux-dev-lora', + adapterType: 'lora', + status: 'live', +} +``` + +Tag mappings allow you to map multiple Hugging Face models to a single Replicate model based on their tags. This is useful when you have a single Replicate model that can handle multiple variations of a model (like different LoRA adapters). + ## Updating model mappings This repo uses a [GitHub Actions workflow](.github/workflows/sync.yml) to keep the model mappings up to date, as well as model warm/cold states. diff --git a/src/hf.ts b/src/hf.ts index 32a5d2d..e220b20 100644 --- a/src/hf.ts +++ b/src/hf.ts @@ -1,10 +1,21 @@ -interface MappingItem { +interface ModelMappingItem { task: string; hfModel: string; providerModel: string; status?: 'live' | 'staging'; } +interface TagFilterMappingItem { + task: string; + providerModel: string; + status?: 'live' | 'staging'; + type: 'tag-filter'; + tags: string[]; + adapterType: 'lora'; +} + +type MappingItem = ModelMappingItem | TagFilterMappingItem; + interface StatusUpdateRequest { hfModel: string; status: 'live' | 'staging'; @@ -43,12 +54,18 @@ class HFInferenceProviderClient { if (!response.ok) { const errorText = await response.text(); - throw new Error(`Request failed: ${response.status} ${response.statusText} - ${errorText}`); + const error = new Error(`Request failed: ${response.status} ${response.statusText} - ${errorText}`); + if (response.status !== 409) { + console.error('Request error:', error); + } + throw error; } return await response.json(); } catch (error) { - console.error('Request error:', error); + if (error instanceof Error && !error.message.includes('409 Conflict')) { + console.error('Request error:', error); + } throw error; } } diff --git a/src/index.ts b/src/index.ts index 7233ac6..ed81225 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import { type InferenceModel, inferenceModels } from './models.js'; +import { type InferenceTag, inferenceTags } from './tags.js'; import HFInferenceProviderClient from './hf.js'; @@ -37,7 +38,6 @@ console.log("\n\nExisting HF model IDs:"); console.log(existingHFModelIds); const newMappings = replicateModels.filter(model => !existingHFModelIds.includes(model.hfModel)); - const existingMappings = replicateModels.filter(model => existingHFModelIds.includes(model.hfModel)); if (newMappings.length > 0) { @@ -63,4 +63,27 @@ if (existingMappings.length > 0) { console.log("\n\nNo existing mappings to update."); } +// Handle tag mappings +console.log("\n\nReplicate tag mappings:"); +console.log(inferenceTags); + +// Register tag mappings +if (inferenceTags.length > 0) { + console.log("\n\nAdding tag mappings:"); + for (const tag of inferenceTags) { + console.log(`${tag.tags.join(', ')} - ${tag.status ?? 'live'}`); + try { + await hf.registerMappingItem(tag); + } catch (error) { + if (error instanceof Error && error.message.includes('409 Conflict')) { + console.log(`Skipping existing mapping for tags: ${tag.tags.join(', ')}`); + continue; + } + throw error; + } + } +} else { + console.log("\n\nNo tag mappings to add."); +} + console.log("\n\nDone!"); \ No newline at end of file diff --git a/src/tags.ts b/src/tags.ts new file mode 100644 index 0000000..8acb6e7 --- /dev/null +++ b/src/tags.ts @@ -0,0 +1,19 @@ +export interface InferenceTag { + type: 'tag-filter'; + task: string; + tags: string[]; + providerModel: string; + adapterType: 'lora'; + status?: 'live' | 'staging'; +} + +export const inferenceTags: InferenceTag[] = [ + { + type: 'tag-filter', + task: 'text-to-image', + tags: ['flux', 'lora'], + providerModel: 'black-forest-labs/flux-dev-lora', + adapterType: 'lora', + status: 'live', + } +];