Spaces:
Running
Running
import type { ProviderEntry, ProviderFetcher } from './types'; | |
interface HFRouterModel { | |
id: string; | |
object: string; | |
created: number; | |
owned_by: string; | |
providers?: HFRouterProvider[]; | |
} | |
interface HFRouterProvider { | |
provider: string; | |
status?: "live" | "offline" | "staging" | "deprecated"; | |
context_length?: number; | |
pricing?: { | |
input: number; // cents per million tokens | |
output: number; // cents per million tokens | |
}; | |
supports_tools?: boolean; | |
supports_structured_output?: boolean; | |
} | |
export class HuggingFaceRouterFetcher implements ProviderFetcher { | |
name = 'huggingface-router'; | |
async fetchModels(): Promise<ProviderEntry[]> { | |
try { | |
const response = await fetch('https://router.huggingface.co/v1/models'); | |
if (!response.ok) { | |
throw new Error(`HTTP ${response.status}: ${response.statusText}`); | |
} | |
const data = await response.json() as { data: HFRouterModel[] }; | |
return this.normalizeModels(data.data); | |
} catch (error) { | |
console.error('Failed to fetch HuggingFace router models:', error); | |
throw error; | |
} | |
} | |
private normalizeModels(models: HFRouterModel[]): ProviderEntry[] { | |
const entries: ProviderEntry[] = []; | |
for (const model of models) { | |
if (!model.providers) continue; | |
for (const provider of model.providers) { | |
const entry: ProviderEntry = { | |
provider: this.normalizeProviderName(provider.provider), | |
model_id: model.id, | |
owned_by: model.owned_by, | |
created: model.created, | |
}; | |
// Set status | |
if (provider.status) { | |
entry.status = provider.status === "staging" ? "offline" : provider.status; | |
} | |
// Convert pricing from cents to dollars per million tokens | |
if (provider.pricing) { | |
entry.pricing = { | |
input: provider.pricing.input / 100, // cents to dollars | |
output: provider.pricing.output / 100, // cents to dollars | |
}; | |
} | |
// Copy context length | |
if (provider.context_length) { | |
entry.context_length = provider.context_length; | |
} | |
// Copy capability flags | |
if (provider.supports_tools !== undefined) { | |
entry.supports_tools = provider.supports_tools; | |
} | |
if (provider.supports_structured_output !== undefined) { | |
entry.supports_structured_output = provider.supports_structured_output; | |
} | |
entries.push(entry); | |
} | |
} | |
return entries; | |
} | |
private normalizeProviderName(providerName: string): string { | |
// Map HF router provider names to our standard names | |
const providerMap: Record<string, string> = { | |
'featherless-ai': 'featherless', | |
'fireworks-ai': 'fireworks', | |
'hf-inference': 'huggingface', | |
// Keep others as-is | |
}; | |
return providerMap[providerName] || providerName; | |
} | |
} | |
// Helper function to extract HF router data from a model | |
export function extractHFRouterData(model: any): Map<string, ProviderEntry> { | |
const providerMap = new Map<string, ProviderEntry>(); | |
if (!model.providers || !Array.isArray(model.providers)) { | |
return providerMap; | |
} | |
for (const provider of model.providers) { | |
if (!provider.provider) continue; | |
const entry: ProviderEntry = { | |
provider: provider.provider, | |
}; | |
// Set status | |
if (provider.status) { | |
entry.status = provider.status === "staging" ? "offline" : provider.status; | |
} | |
// Convert pricing from cents to dollars if needed | |
if (provider.pricing) { | |
// Check if pricing is already in dollars (values < 100 likely dollars) | |
const needsConversion = provider.pricing.input >= 100 || provider.pricing.output >= 100; | |
entry.pricing = { | |
input: needsConversion ? provider.pricing.input / 100 : provider.pricing.input, | |
output: needsConversion ? provider.pricing.output / 100 : provider.pricing.output, | |
}; | |
} | |
// Copy other fields | |
if (provider.context_length) { | |
entry.context_length = provider.context_length; | |
} | |
if (provider.supports_tools !== undefined) { | |
entry.supports_tools = provider.supports_tools; | |
} | |
if (provider.supports_structured_output !== undefined) { | |
entry.supports_structured_output = provider.supports_structured_output; | |
} | |
providerMap.set(provider.provider, entry); | |
} | |
return providerMap; | |
} |