Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Inference Client] Factorize inference payload build #2601

Merged
merged 14 commits into from
Oct 15, 2024
188 changes: 55 additions & 133 deletions src/huggingface_hub/inference/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
_get_unsupported_text_generation_kwargs,
_import_numpy,
_open_as_binary,
_prepare_payload,
_set_unsupported_text_generation_kwargs,
_stream_chat_completion_response,
_stream_text_generation_response,
Expand Down Expand Up @@ -364,18 +365,8 @@ def audio_classification(
```
"""
parameters = {"function_to_apply": function_to_apply, "top_k": top_k}
if all(parameter is None for parameter in parameters.values()):
# if no parameters are provided, send audio as raw data
data = audio
payload: Optional[Dict[str, Any]] = None
else:
# Or some parameters are provided -> send audio as base64 encoded string
data = None
payload = {"inputs": _b64_encode(audio)}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, data=data, model=model, task="audio-classification")
payload = _prepare_payload(audio, parameters=parameters, expect_binary=True)
response = self.post(**payload, model=model, task="audio-classification")
return AudioClassificationOutputElement.parse_obj_as_list(response)

def audio_to_audio(
Expand Down Expand Up @@ -988,7 +979,7 @@ def document_question_answering(
[DocumentQuestionAnsweringOutputElement(answer='us-001', end=16, score=0.9999666213989258, start=16, words=None)]
```
"""
payload: Dict[str, Any] = {"question": question, "image": _b64_encode(image)}
inputs: Dict[str, Any] = {"question": question, "image": _b64_encode(image)}
parameters = {
"doc_stride": doc_stride,
"handle_impossible_answer": handle_impossible_answer,
Expand All @@ -999,10 +990,8 @@ def document_question_answering(
"top_k": top_k,
"word_boxes": word_boxes,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, model=model, task="document-question-answering")
payload = _prepare_payload(inputs, parameters=parameters)
response = self.post(**payload, model=model, task="document-question-answering")
return DocumentQuestionAnsweringOutputElement.parse_obj_as_list(response)

def feature_extraction(
Expand Down Expand Up @@ -1060,17 +1049,14 @@ def feature_extraction(
[ 0.28552425, -0.928395 , -1.2077185 , ..., 0.76810825, -2.1069427 , 0.6236161 ]], dtype=float32)
```
"""
payload: Dict = {"inputs": text}
parameters = {
"normalize": normalize,
"prompt_name": prompt_name,
"truncate": truncate,
"truncation_direction": truncation_direction,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, model=model, task="feature-extraction")
payload = _prepare_payload(text, parameters=parameters)
response = self.post(**payload, model=model, task="feature-extraction")
np = _import_numpy()
return np.array(_bytes_to_dict(response), dtype="float32")

Expand Down Expand Up @@ -1119,12 +1105,9 @@ def fill_mask(
]
```
"""
payload: Dict = {"inputs": text}
parameters = {"targets": targets, "top_k": top_k}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, model=model, task="fill-mask")
payload = _prepare_payload(text, parameters=parameters)
response = self.post(**payload, model=model, task="fill-mask")
return FillMaskOutputElement.parse_obj_as_list(response)

def image_classification(
Expand Down Expand Up @@ -1166,19 +1149,8 @@ def image_classification(
```
"""
parameters = {"function_to_apply": function_to_apply, "top_k": top_k}

if all(parameter is None for parameter in parameters.values()):
data = image
payload: Optional[Dict[str, Any]] = None

else:
data = None
payload = {"inputs": _b64_encode(image)}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value

response = self.post(json=payload, data=data, model=model, task="image-classification")
payload = _prepare_payload(image, parameters=parameters, expect_binary=True)
response = self.post(**payload, model=model, task="image-classification")
return ImageClassificationOutputElement.parse_obj_as_list(response)

def image_segmentation(
Expand Down Expand Up @@ -1237,18 +1209,8 @@ def image_segmentation(
"subtask": subtask,
"threshold": threshold,
}
if all(parameter is None for parameter in parameters.values()):
# if no parameters are provided, the image can be raw bytes, an image file, or URL to an online image
data = image
payload: Optional[Dict[str, Any]] = None
else:
# if parameters are provided, the image needs to be a base64-encoded string
data = None
payload = {"inputs": _b64_encode(image)}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, data=data, model=model, task="image-segmentation")
payload = _prepare_payload(image, parameters=parameters, expect_binary=True)
response = self.post(**payload, model=model, task="image-segmentation")
output = ImageSegmentationOutputElement.parse_obj_as_list(response)
for item in output:
item.mask = _b64_to_image(item.mask) # type: ignore [assignment]
Expand Down Expand Up @@ -1323,19 +1285,8 @@ def image_to_image(
"guidance_scale": guidance_scale,
**kwargs,
}
if all(parameter is None for parameter in parameters.values()):
# Either only an image to send => send as raw bytes
data = image
payload: Optional[Dict[str, Any]] = None
else:
# if parameters are provided, the image needs to be a base64-encoded string
data = None
payload = {"inputs": _b64_encode(image)}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value

response = self.post(json=payload, data=data, model=model, task="image-to-image")
payload = _prepare_payload(image, parameters=parameters, expect_binary=True)
response = self.post(**payload, model=model, task="image-to-image")
return _bytes_to_image(response)

def image_to_text(self, image: ContentT, *, model: Optional[str] = None) -> ImageToTextOutput:
Expand Down Expand Up @@ -1493,25 +1444,15 @@ def object_detection(
```py
>>> from huggingface_hub import InferenceClient
>>> client = InferenceClient()
>>> client.object_detection("people.jpg"):
>>> client.object_detection("people.jpg")
[ObjectDetectionOutputElement(score=0.9486683011054993, label='person', box=ObjectDetectionBoundingBox(xmin=59, ymin=39, xmax=420, ymax=510)), ...]
```
"""
parameters = {
"threshold": threshold,
}
if all(parameter is None for parameter in parameters.values()):
# if no parameters are provided, the image can be raw bytes, an image file, or URL to an online image
data = image
payload: Optional[Dict[str, Any]] = None
else:
# if parameters are provided, the image needs to be a base64-encoded string
data = None
payload = {"inputs": _b64_encode(image)}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, data=data, model=model, task="object-detection")
payload = _prepare_payload(image, parameters=parameters, expect_binary=True)
response = self.post(**payload, model=model, task="object-detection")
return ObjectDetectionOutputElement.parse_obj_as_list(response)

def question_answering(
Expand Down Expand Up @@ -1587,12 +1528,10 @@ def question_answering(
"max_seq_len": max_seq_len,
"top_k": top_k,
}
payload: Dict[str, Any] = {"question": question, "context": context}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
inputs: Dict[str, Any] = {"question": question, "context": context}
payload = _prepare_payload(inputs, parameters=parameters)
response = self.post(
json=payload,
**payload,
model=model,
task="question-answering",
)
Expand Down Expand Up @@ -1700,19 +1639,14 @@ def summarization(
SummarizationOutput(generated_text="The Eiffel tower is one of the most famous landmarks in the world....")
```
"""
payload: Dict[str, Any] = {"inputs": text}
if parameters is not None:
payload["parameters"] = parameters
else:
if parameters is None:
parameters = {
"clean_up_tokenization_spaces": clean_up_tokenization_spaces,
"generate_parameters": generate_parameters,
"truncation": truncation,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, model=model, task="summarization")
payload = _prepare_payload(text, parameters=parameters)
response = self.post(**payload, model=model, task="summarization")
return SummarizationOutput.parse_obj_as_list(response)[0]

def table_question_answering(
Expand Down Expand Up @@ -1757,15 +1691,13 @@ def table_question_answering(
TableQuestionAnsweringOutputElement(answer='36542', coordinates=[[0, 1]], cells=['36542'], aggregator='AVERAGE')
```
"""
payload: Dict[str, Any] = {
inputs = {
"query": query,
"table": table,
}

if parameters is not None:
payload["parameters"] = parameters
payload = _prepare_payload(inputs, parameters=parameters)
response = self.post(
json=payload,
**payload,
model=model,
task="table-question-answering",
)
Expand Down Expand Up @@ -1813,7 +1745,11 @@ def tabular_classification(self, table: Dict[str, Any], *, model: Optional[str]
["5", "5", "5"]
```
"""
response = self.post(json={"table": table}, model=model, task="tabular-classification")
response = self.post(
json={"table": table},
model=model,
task="tabular-classification",
)
return _bytes_to_list(response)

def tabular_regression(self, table: Dict[str, Any], *, model: Optional[str] = None) -> List[float]:
Expand Down Expand Up @@ -1899,15 +1835,16 @@ def text_classification(
]
```
"""
payload: Dict[str, Any] = {"inputs": text}
parameters = {
"function_to_apply": function_to_apply,
"top_k": top_k,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, model=model, task="text-classification")
payload = _prepare_payload(text, parameters=parameters)
response = self.post(
**payload,
model=model,
task="text-classification",
)
return TextClassificationOutputElement.parse_obj_as_list(response)[0] # type: ignore [return-value]

@overload
Expand Down Expand Up @@ -2481,7 +2418,7 @@ def text_to_image(
>>> image.save("better_astronaut.png")
```
"""
payload = {"inputs": prompt}

parameters = {
"negative_prompt": negative_prompt,
"height": height,
Expand All @@ -2493,10 +2430,8 @@ def text_to_image(
"seed": seed,
**kwargs,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value # type: ignore
response = self.post(json=payload, model=model, task="text-to-image")
payload = _prepare_payload(prompt, parameters=parameters)
response = self.post(**payload, model=model, task="text-to-image")
return _bytes_to_image(response)

def text_to_speech(
Expand Down Expand Up @@ -2599,7 +2534,6 @@ def text_to_speech(
>>> Path("hello_world.flac").write_bytes(audio)
```
"""
payload: Dict[str, Any] = {"inputs": text}
parameters = {
"do_sample": do_sample,
"early_stopping": early_stopping,
Expand All @@ -2618,10 +2552,8 @@ def text_to_speech(
"typical_p": typical_p,
"use_cache": use_cache,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, model=model, task="text-to-speech")
payload = _prepare_payload(text, parameters=parameters)
response = self.post(**payload, model=model, task="text-to-speech")
return response

def token_classification(
Expand Down Expand Up @@ -2683,17 +2615,15 @@ def token_classification(
]
```
"""
payload: Dict[str, Any] = {"inputs": text}

parameters = {
"aggregation_strategy": aggregation_strategy,
"ignore_labels": ignore_labels,
"stride": stride,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
payload = _prepare_payload(text, parameters=parameters)
response = self.post(
json=payload,
**payload,
model=model,
task="token-classification",
)
Expand Down Expand Up @@ -2769,18 +2699,15 @@ def translation(

if src_lang is None and tgt_lang is not None:
raise ValueError("You cannot specify `tgt_lang` without specifying `src_lang`.")
payload: Dict[str, Any] = {"inputs": text}
parameters = {
"src_lang": src_lang,
"tgt_lang": tgt_lang,
"clean_up_tokenization_spaces": clean_up_tokenization_spaces,
"truncation": truncation,
"generate_parameters": generate_parameters,
}
for key, value in parameters.items():
if value is not None:
payload.setdefault("parameters", {})[key] = value
response = self.post(json=payload, model=model, task="translation")
payload = _prepare_payload(text, parameters=parameters)
response = self.post(**payload, model=model, task="translation")
return TranslationOutput.parse_obj_as_list(response)[0]

def visual_question_answering(
Expand Down Expand Up @@ -2924,12 +2851,9 @@ def zero_shot_classification(
parameters = {"candidate_labels": labels, "multi_label": multi_label}
if hypothesis_template is not None:
parameters["hypothesis_template"] = hypothesis_template

payload = _prepare_payload(text, parameters=parameters)
response = self.post(
json={
"inputs": text,
"parameters": parameters,
},
**payload,
task="zero-shot-classification",
model=model,
)
Expand Down Expand Up @@ -2986,13 +2910,11 @@ def zero_shot_image_classification(
if len(labels) < 2:
raise ValueError("You must specify at least 2 classes to compare.")

payload = {
"inputs": {"image": _b64_encode(image), "candidateLabels": ",".join(labels)},
}
if hypothesis_template is not None:
payload.setdefault("parameters", {})["hypothesis_template"] = hypothesis_template
inputs = {"image": _b64_encode(image), "candidateLabels": ",".join(labels)}
parameters = {"hypothesis_template": hypothesis_template} if hypothesis_template is not None else None
hanouticelina marked this conversation as resolved.
Show resolved Hide resolved
payload = _prepare_payload(inputs, parameters=parameters)
response = self.post(
json=payload,
**payload,
model=model,
task="zero-shot-image-classification",
)
Expand Down
Loading
Loading