diff --git a/src/huggingface_hub/inference/_client.py b/src/huggingface_hub/inference/_client.py index 5e39dee55c..bf640addd5 100644 --- a/src/huggingface_hub/inference/_client.py +++ b/src/huggingface_hub/inference/_client.py @@ -130,7 +130,7 @@ class InferenceClient: Note: for better compatibility with OpenAI's client, `model` has been aliased as `base_url`. Those 2 arguments are mutually exclusive. If a URL is passed as `model` or `base_url` for chat completion, the `(/v1)/chat/completions` suffix path will be appended to the URL. provider (`str`, *optional*): - Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"featherless-ai"`, `"fireworks-ai"`, `"groq"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"nscale"`, `"openai"`, `"replicate"`, "sambanova"` or `"together"`. + Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"featherless-ai"`, `"fireworks-ai"`, `"groq"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"nscale"`, `"openai"`, `"replicate"`, `"sambanova"`, `"scaleway"` or `"together"`. Defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers. If model is a URL or `base_url` is passed, then `provider` is not used. token (`str`, *optional*): diff --git a/src/huggingface_hub/inference/_generated/_async_client.py b/src/huggingface_hub/inference/_generated/_async_client.py index 95eaf3e7e5..dc2a4c46ce 100644 --- a/src/huggingface_hub/inference/_generated/_async_client.py +++ b/src/huggingface_hub/inference/_generated/_async_client.py @@ -118,7 +118,7 @@ class AsyncInferenceClient: Note: for better compatibility with OpenAI's client, `model` has been aliased as `base_url`. Those 2 arguments are mutually exclusive. If a URL is passed as `model` or `base_url` for chat completion, the `(/v1)/chat/completions` suffix path will be appended to the URL. provider (`str`, *optional*): - Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"featherless-ai"`, `"fireworks-ai"`, `"groq"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"nscale"`, `"openai"`, `"replicate"`, "sambanova"` or `"together"`. + Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"featherless-ai"`, `"fireworks-ai"`, `"groq"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"nscale"`, `"openai"`, `"replicate"`, `"sambanova"`, `"scaleway"` or `"together"`. Defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers. If model is a URL or `base_url` is passed, then `provider` is not used. token (`str`, *optional*): diff --git a/src/huggingface_hub/inference/_providers/__init__.py b/src/huggingface_hub/inference/_providers/__init__.py index ec4866c30d..7c1e4b8074 100644 --- a/src/huggingface_hub/inference/_providers/__init__.py +++ b/src/huggingface_hub/inference/_providers/__init__.py @@ -38,6 +38,7 @@ from .openai import OpenAIConversationalTask from .replicate import ReplicateImageToImageTask, ReplicateTask, ReplicateTextToImageTask, ReplicateTextToSpeechTask from .sambanova import SambanovaConversationalTask, SambanovaFeatureExtractionTask +from .scaleway import ScalewayConversationalTask, ScalewayFeatureExtractionTask from .together import TogetherConversationalTask, TogetherTextGenerationTask, TogetherTextToImageTask @@ -60,6 +61,7 @@ "openai", "replicate", "sambanova", + "scaleway", "together", ] @@ -154,6 +156,10 @@ "conversational": SambanovaConversationalTask(), "feature-extraction": SambanovaFeatureExtractionTask(), }, + "scaleway": { + "conversational": ScalewayConversationalTask(), + "feature-extraction": ScalewayFeatureExtractionTask(), + }, "together": { "text-to-image": TogetherTextToImageTask(), "conversational": TogetherConversationalTask(), diff --git a/src/huggingface_hub/inference/_providers/_common.py b/src/huggingface_hub/inference/_providers/_common.py index 687464a934..2c47e88f22 100644 --- a/src/huggingface_hub/inference/_providers/_common.py +++ b/src/huggingface_hub/inference/_providers/_common.py @@ -33,6 +33,7 @@ "nscale": {}, "replicate": {}, "sambanova": {}, + "scaleway": {}, "together": {}, } diff --git a/src/huggingface_hub/inference/_providers/scaleway.py b/src/huggingface_hub/inference/_providers/scaleway.py new file mode 100644 index 0000000000..cfdd75416f --- /dev/null +++ b/src/huggingface_hub/inference/_providers/scaleway.py @@ -0,0 +1,28 @@ +from typing import Any, Dict, Optional, Union + +from huggingface_hub.inference._common import RequestParameters, _as_dict + +from ._common import BaseConversationalTask, InferenceProviderMapping, TaskProviderHelper, filter_none + + +class ScalewayConversationalTask(BaseConversationalTask): + def __init__(self): + super().__init__(provider="scaleway", base_url="https://api.scaleway.ai") + + +class ScalewayFeatureExtractionTask(TaskProviderHelper): + def __init__(self): + super().__init__(provider="scaleway", base_url="https://api.scaleway.ai", task="feature-extraction") + + def _prepare_route(self, mapped_model: str, api_key: str) -> str: + return "/v1/embeddings" + + def _prepare_payload_as_dict( + self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping + ) -> Optional[Dict]: + parameters = filter_none(parameters) + return {"input": inputs, "model": provider_mapping_info.provider_id, **parameters} + + def get_response(self, response: Union[bytes, Dict], request_params: Optional[RequestParameters] = None) -> Any: + embeddings = _as_dict(response)["data"] + return [embedding["embedding"] for embedding in embeddings] diff --git a/tests/test_inference_providers.py b/tests/test_inference_providers.py index 333eb57d33..5b876d27ca 100644 --- a/tests/test_inference_providers.py +++ b/tests/test_inference_providers.py @@ -50,6 +50,7 @@ ReplicateTextToSpeechTask, ) from huggingface_hub.inference._providers.sambanova import SambanovaConversationalTask, SambanovaFeatureExtractionTask +from huggingface_hub.inference._providers.scaleway import ScalewayConversationalTask, ScalewayFeatureExtractionTask from huggingface_hub.inference._providers.together import TogetherTextToImageTask from .testing_utils import assert_in_logs @@ -1077,6 +1078,75 @@ def test_prepare_url_conversational(self): assert url == "https://api.novita.ai/v3/openai/chat/completions" +class TestScalewayProvider: + def test_prepare_hf_url_conversational(self): + helper = ScalewayConversationalTask() + url = helper._prepare_url("hf_token", "username/repo_name") + assert url == "https://router.huggingface.co/scaleway/v1/chat/completions" + + def test_prepare_url_conversational(self): + helper = ScalewayConversationalTask() + url = helper._prepare_url("scw_token", "username/repo_name") + assert url == "https://api.scaleway.ai/v1/chat/completions" + + def test_prepare_payload_as_dict(self): + helper = ScalewayConversationalTask() + payload = helper._prepare_payload_as_dict( + [ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "Hello!"}, + ], + { + "max_tokens": 512, + "temperature": 0.15, + "top_p": 1, + "presence_penalty": 0, + "stream": True, + }, + InferenceProviderMapping( + provider="scaleway", + hf_model_id="meta-llama/Llama-3.1-8B-Instruct", + providerId="meta-llama/llama-3.1-8B-Instruct", + task="conversational", + status="live", + ), + ) + assert payload == { + "max_tokens": 512, + "messages": [ + {"content": "You are a helpful assistant", "role": "system"}, + {"role": "user", "content": "Hello!"}, + ], + "model": "meta-llama/llama-3.1-8B-Instruct", + "presence_penalty": 0, + "stream": True, + "temperature": 0.15, + "top_p": 1, + } + + def test_prepare_url_feature_extraction(self): + helper = ScalewayFeatureExtractionTask() + assert ( + helper._prepare_url("hf_token", "username/repo_name") + == "https://router.huggingface.co/scaleway/v1/embeddings" + ) + + def test_prepare_payload_as_dict_feature_extraction(self): + helper = ScalewayFeatureExtractionTask() + payload = helper._prepare_payload_as_dict( + "Example text to embed", + {"truncate": True}, + InferenceProviderMapping( + provider="scaleway", + hf_model_id="username/repo_name", + providerId="provider-id", + task="feature-extraction", + status="live", + ), + ) + assert payload == {"input": "Example text to embed", "model": "provider-id", "truncate": True} + + class TestNscaleProvider: def test_prepare_route_text_to_image(self): helper = NscaleTextToImageTask()