mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-13 09:13:48 +00:00
Reworked the Hugging Face provider (#944)
This commit is contained in:
1 parent
a7228c7782
commit
f14c69b938
28 files changed
+1039
-93
No files matched your search
@@ -1,43 +1,158 @@
|
||||
namespace AIStudio.Provider.HuggingFace;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Provider.HuggingFace;
|
||||
|
||||
public static class HFInferenceProviderExtensions
|
||||
{
|
||||
public static string Endpoints(this HFInferenceProvider provider, Model model) => provider switch
|
||||
{
|
||||
HFInferenceProvider.CEREBRAS => "cerebras/v1/",
|
||||
HFInferenceProvider.NEBIUS_AI_STUDIO => "nebius/v1/",
|
||||
HFInferenceProvider.SAMBANOVA => "sambanova/v1/",
|
||||
HFInferenceProvider.NOVITA => "novita/v3/openai/",
|
||||
HFInferenceProvider.HYPERBOLIC => "hyperbolic/v1/",
|
||||
HFInferenceProvider.TOGETHER_AI => "together/v1/",
|
||||
HFInferenceProvider.FIREWORKS => "fireworks-ai/inference/v1/",
|
||||
HFInferenceProvider.HF_INFERENCE_API => $"hf-inference/models/{model.ToString()}/v1/",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(HFInferenceProviderExtensions).Namespace, nameof(HFInferenceProviderExtensions));
|
||||
|
||||
/// <summary>
|
||||
/// The slug Hugging Face uses for this inference provider.
|
||||
/// </summary>
|
||||
/// <param name="provider">The inference provider.</param>
|
||||
/// <returns>The slug, or an empty string for the routing strategies, which name no provider.</returns>
|
||||
public static string EndpointsId(this HFInferenceProvider provider) => provider switch
|
||||
{
|
||||
HFInferenceProvider.BASETEN => "baseten",
|
||||
HFInferenceProvider.CEREBRAS => "cerebras",
|
||||
HFInferenceProvider.NEBIUS_AI_STUDIO => "nebius",
|
||||
HFInferenceProvider.SAMBANOVA => "sambanova",
|
||||
HFInferenceProvider.COHERE => "cohere",
|
||||
HFInferenceProvider.DEEPINFRA => "deepinfra",
|
||||
HFInferenceProvider.FEATHERLESS_AI => "featherless-ai",
|
||||
HFInferenceProvider.FIREWORKS => "fireworks-ai",
|
||||
HFInferenceProvider.GROQ => "groq",
|
||||
HFInferenceProvider.NOVITA => "novita",
|
||||
HFInferenceProvider.HYPERBOLIC => "hyperbolic",
|
||||
HFInferenceProvider.NSCALE => "nscale",
|
||||
HFInferenceProvider.OVHCLOUD => "ovhcloud",
|
||||
HFInferenceProvider.PUBLIC_AI => "publicai",
|
||||
HFInferenceProvider.SCALEWAY => "scaleway",
|
||||
HFInferenceProvider.TOGETHER_AI => "together",
|
||||
HFInferenceProvider.FIREWORKS => "fireworks",
|
||||
HFInferenceProvider.HF_INFERENCE_API => "hf-inference",
|
||||
HFInferenceProvider.ZAI => "zai-org",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The suffix which tells the router where to send the request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The router serves every provider through one endpoint. Which provider answers is decided by
|
||||
/// a suffix on the model name, e.g. "google/gemma-4-31B-it:novita". Without a suffix, the router
|
||||
/// picks the fastest provider itself.
|
||||
/// </remarks>
|
||||
/// <param name="provider">The inference provider.</param>
|
||||
/// <returns>The suffix including its colon, or an empty string when the router should choose.</returns>
|
||||
public static string ModelSuffix(this HFInferenceProvider provider) => provider switch
|
||||
{
|
||||
HFInferenceProvider.NONE or HFInferenceProvider.AUTOMATIC => string.Empty,
|
||||
|
||||
HFInferenceProvider.CHEAPEST => ":cheapest",
|
||||
HFInferenceProvider.PREFERRED => ":preferred",
|
||||
|
||||
_ => $":{provider.EndpointsId()}",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether this inference provider serves models to chat with.
|
||||
/// </summary>
|
||||
/// <param name="provider">The inference provider.</param>
|
||||
/// <returns>True, when the provider serves chat models.</returns>
|
||||
public static bool SupportsChat(this HFInferenceProvider provider) => provider is not HFInferenceProvider.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this inference provider creates embeddings for us.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Embeddings are a much shorter story than chatting. The router serves them nowhere near its
|
||||
/// own endpoint, only through the route of a provider, and only two of those answer the
|
||||
/// OpenAI-compatible form we send. The routing strategies are out by their nature: without a
|
||||
/// named provider there is no route to address.
|
||||
/// </remarks>
|
||||
/// <param name="provider">The inference provider.</param>
|
||||
/// <returns>True, when we can create embeddings through this provider.</returns>
|
||||
public static bool SupportsEmbeddings(this HFInferenceProvider provider) => provider is HFInferenceProvider.TOGETHER_AI or HFInferenceProvider.DEEPINFRA;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this inference provider transcribes audio for us.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The same two providers as for embeddings, and for the same reason: transcription lives on a
|
||||
/// provider's own route, and only these two answer the OpenAI-compatible form there. Others do
|
||||
/// transcribe for Hugging Face, but not in a shape we could send an audio file to: fal-ai and
|
||||
/// Replicate both turn the request down with "Model not supported by provider".
|
||||
/// </remarks>
|
||||
/// <param name="provider">The inference provider.</param>
|
||||
/// <returns>True, when we can transcribe audio through this provider.</returns>
|
||||
public static bool SupportsTranscription(this HFInferenceProvider provider) => provider is HFInferenceProvider.TOGETHER_AI or HFInferenceProvider.DEEPINFRA;
|
||||
|
||||
/// <summary>
|
||||
/// The base URL of the provider's own OpenAI-compatible route.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only chatting goes through the router's own endpoint. Everything else has to address the
|
||||
/// provider directly, and they do not agree on where their OpenAI-compatible API sits: DeepInfra
|
||||
/// keeps it below an additional "openai" segment, and answers the path without it with
|
||||
/// "Not allowed to POST /v1/embeddings for provider deepinfra".
|
||||
/// </remarks>
|
||||
/// <param name="provider">The inference provider.</param>
|
||||
/// <returns>The base URL, or an empty string when the provider has no such route.</returns>
|
||||
public static string ProviderBaseURL(this HFInferenceProvider provider) => provider switch
|
||||
{
|
||||
HFInferenceProvider.TOGETHER_AI => "https://router.huggingface.co/together/v1/",
|
||||
HFInferenceProvider.DEEPINFRA => "https://router.huggingface.co/deepinfra/v1/openai/",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Removes the routing suffix from a model, if it carries one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The suffix says where a request goes, not what the model is. Everything asking what a model
|
||||
/// can do has to look at the bare name: "google/gemma-4-31B-it:novita" is the same model as
|
||||
/// "google/gemma-4-31B-it", and a name detection which never heard of the suffix would miss it.
|
||||
/// Model IDs on the hub are written as "org/model" and carry no colon of their own, so the last
|
||||
/// colon always starts the suffix.
|
||||
/// </remarks>
|
||||
/// <param name="model">The model as it is configured.</param>
|
||||
/// <returns>The model without its routing suffix.</returns>
|
||||
public static Model WithoutRoutingSuffix(this Model model)
|
||||
{
|
||||
var separatorIndex = model.Id.LastIndexOf(':');
|
||||
return separatorIndex is -1 ? model : model with { Id = model.Id[..separatorIndex] };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value to filter the Hugging Face model catalog by.
|
||||
/// </summary>
|
||||
/// <param name="provider">The inference provider.</param>
|
||||
/// <returns>The provider slug, or "all" when no particular provider was chosen.</returns>
|
||||
public static string CatalogFilter(this HFInferenceProvider provider)
|
||||
{
|
||||
var slug = provider.EndpointsId();
|
||||
return string.IsNullOrEmpty(slug) ? "all" : slug;
|
||||
}
|
||||
|
||||
public static string ToName(this HFInferenceProvider provider) => provider switch
|
||||
{
|
||||
HFInferenceProvider.AUTOMATIC => TB("Automatic: the fastest provider"),
|
||||
HFInferenceProvider.CHEAPEST => TB("Automatic: the cheapest provider"),
|
||||
HFInferenceProvider.PREFERRED => TB("Automatic: your preferred order"),
|
||||
|
||||
HFInferenceProvider.BASETEN => "Baseten",
|
||||
HFInferenceProvider.CEREBRAS => "Cerebras",
|
||||
HFInferenceProvider.NEBIUS_AI_STUDIO => "Nebius AI Studio",
|
||||
HFInferenceProvider.SAMBANOVA => "Sambanova",
|
||||
HFInferenceProvider.NOVITA => "Novita",
|
||||
HFInferenceProvider.HYPERBOLIC => "Hyperbolic",
|
||||
HFInferenceProvider.TOGETHER_AI => "Together AI",
|
||||
HFInferenceProvider.COHERE => "Cohere",
|
||||
HFInferenceProvider.DEEPINFRA => "DeepInfra",
|
||||
HFInferenceProvider.FEATHERLESS_AI => "Featherless AI",
|
||||
HFInferenceProvider.FIREWORKS => "Fireworks AI",
|
||||
HFInferenceProvider.HF_INFERENCE_API => "Hugging Face Inference API",
|
||||
HFInferenceProvider.GROQ => "Groq",
|
||||
HFInferenceProvider.NOVITA => "Novita",
|
||||
HFInferenceProvider.NSCALE => "Nscale",
|
||||
HFInferenceProvider.OVHCLOUD => "OVHcloud",
|
||||
HFInferenceProvider.PUBLIC_AI => "Public AI",
|
||||
HFInferenceProvider.SCALEWAY => "Scaleway",
|
||||
HFInferenceProvider.TOGETHER_AI => "Together AI",
|
||||
HFInferenceProvider.ZAI => "Z.ai",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user