Skip to content

Conversation

@arabot777
Copy link
Contributor

What’s in this PR
This PR adds wavespeed AI as a new inference provider for conversational tasks.

@arabot777
Copy link
Contributor Author

Related to huggingface/huggingface.js#1424

@HuggingFaceDocBuilderDev

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@hanouticelina
Copy link
Contributor

@bot /style

@github-actions
Copy link
Contributor

github-actions bot commented Oct 27, 2025

Style bot fixed some files and pushed the changes.

Copy link
Contributor

@hanouticelina hanouticelina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @arabot777 for the PR! 🤗
could you please update this line

response = provider_helper.get_response(response)
and pass request_parameters into get_response():

response = provider_helper.get_response(response, request_parameters)

also, could you add Wavespeed to the Inference providers table in the documentation here?

I left a few comments to avoid duplicating the payload dictionary construction and use built-in types!

def _prepare_payload_as_dict(
self,
inputs: Any,
parameters: Dict,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parameters: Dict,
parameters: dict,


def get_response(
self,
response: Union[bytes, Dict],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
response: Union[bytes, Dict],
response: Union[bytes, dict],

def __init__(self, task: str):
super().__init__(provider="wavespeed", base_url="https://api.wavespeed.ai", task=task)

def _prepare_headers(self, headers: Dict, api_key: str) -> Dict:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use built-in types everywhere

Suggested change
def _prepare_headers(self, headers: Dict, api_key: str) -> Dict:
def _prepare_headers(self, headers: dict, api_key: str) -> dict:

inputs: Any,
parameters: Dict,
provider_mapping_info: InferenceProviderMapping,
) -> Optional[Dict]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
) -> Optional[Dict]:
) -> Optional[dict]:

Comment on lines 104 to 114
class WavespeedAITextToVideoTask(WavespeedAITask):
def __init__(self):
super().__init__("text-to-video")

def _prepare_payload_as_dict(
self,
inputs: Any,
parameters: Dict,
provider_mapping_info: InferenceProviderMapping,
) -> Optional[Dict]:
return {"prompt": inputs, **filter_none(parameters)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WavespeedAITextToVideoTask can inherit from WavespeedAITextToImageTask since it's the same payload preparation when the input modality is text

Suggested change
class WavespeedAITextToVideoTask(WavespeedAITask):
def __init__(self):
super().__init__("text-to-video")
def _prepare_payload_as_dict(
self,
inputs: Any,
parameters: Dict,
provider_mapping_info: InferenceProviderMapping,
) -> Optional[Dict]:
return {"prompt": inputs, **filter_none(parameters)}
class WavespeedAITextToVideoTask(WavespeedAITextToImageTask):
def __init__(self):
WavespeedAITask.__init__(self, "text-to-video")

Comment on lines 150 to 180
class WavespeedAIImageToVideoTask(WavespeedAITask):
def __init__(self):
super().__init__("image-to-video")

def _prepare_payload_as_dict(
self,
inputs: Any,
parameters: Dict,
provider_mapping_info: InferenceProviderMapping,
) -> Optional[Dict]:
# Convert inputs to image (URL or base64)
if isinstance(inputs, str) and inputs.startswith(("http://", "https://")):
image = inputs
elif isinstance(inputs, str):
# If input is a file path, read it first
with open(inputs, "rb") as f:
file_content = f.read()
image_b64 = base64.b64encode(file_content).decode("utf-8")
image = f"data:image/jpeg;base64,{image_b64}"
else:
# If input is binary data
image_b64 = base64.b64encode(inputs).decode("utf-8")
image = f"data:image/jpeg;base64,{image_b64}"

# Extract prompt from parameters if present
prompt = parameters.pop("prompt", None)
payload = {"image": image, **filter_none(parameters)}
if prompt is not None:
payload["prompt"] = prompt

return payload
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the payload is built the same for both image-to-image and image-to-video

Suggested change
class WavespeedAIImageToVideoTask(WavespeedAITask):
def __init__(self):
super().__init__("image-to-video")
def _prepare_payload_as_dict(
self,
inputs: Any,
parameters: Dict,
provider_mapping_info: InferenceProviderMapping,
) -> Optional[Dict]:
# Convert inputs to image (URL or base64)
if isinstance(inputs, str) and inputs.startswith(("http://", "https://")):
image = inputs
elif isinstance(inputs, str):
# If input is a file path, read it first
with open(inputs, "rb") as f:
file_content = f.read()
image_b64 = base64.b64encode(file_content).decode("utf-8")
image = f"data:image/jpeg;base64,{image_b64}"
else:
# If input is binary data
image_b64 = base64.b64encode(inputs).decode("utf-8")
image = f"data:image/jpeg;base64,{image_b64}"
# Extract prompt from parameters if present
prompt = parameters.pop("prompt", None)
payload = {"image": image, **filter_none(parameters)}
if prompt is not None:
payload["prompt"] = prompt
return payload
class WavespeedAIImageToVideoTask(WavespeedAIImageToImageTask):
def __init__(self):
WavespeedAITask.__init__(self, "image-to-video")

@arabot777
Copy link
Contributor Author

@hanouticelina thanks for the review! I've addressed all your feedback:
Ready for another review!

Copy link
Contributor

@hanouticelina hanouticelina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

almost ready to be merged! 🤗 thanks for the iteration @arabot777

Comment on lines 24 to 28
def _prepare_headers(self, headers: dict, api_key: str) -> dict:
headers = super()._prepare_headers(headers, api_key)
if not api_key.startswith("hf_"):
headers["Authorization"] = f"Bearer {api_key}"
return headers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to redefine _prepare_headers, TaskProviderHelper. _prepare_headers already sets the bearer prefix in the authorization header

Suggested change
def _prepare_headers(self, headers: dict, api_key: str) -> dict:
headers = super()._prepare_headers(headers, api_key)
if not api_key.startswith("hf_"):
headers["Authorization"] = f"Bearer {api_key}"
return headers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hanouticelina Done! Thanks for the thorough review!

@hanouticelina
Copy link
Contributor

@bot /style

@github-actions
Copy link
Contributor

github-actions bot commented Oct 28, 2025

Style bot fixed some files and pushed the changes.

Copy link
Contributor

@hanouticelina hanouticelina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, thank you @arabot777 for the contribution!

@hanouticelina hanouticelina merged commit 7e86848 into huggingface:main Oct 28, 2025
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants