Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand Down
23 changes: 20 additions & 3 deletions src/hf.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;
}
}
Expand Down
25 changes: 24 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type InferenceModel, inferenceModels } from './models.js';
import { type InferenceTag, inferenceTags } from './tags.js';

import HFInferenceProviderClient from './hf.js';

Expand Down Expand Up @@ -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) {
Expand All @@ -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!");
19 changes: 19 additions & 0 deletions src/tags.ts
Original file line number Diff line number Diff line change
@@ -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',
}
];