From cf10e48598d337867af99147df991b929743b884 Mon Sep 17 00:00:00 2001 From: Denis Kokorev Date: Thu, 17 Jul 2025 14:16:44 +0200 Subject: [PATCH 1/4] fix: safely import transformers.pipelines.Conversation to support transformers>=4.48.0 --- .../mlserver_huggingface/codecs/utils.py | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/runtimes/huggingface/mlserver_huggingface/codecs/utils.py b/runtimes/huggingface/mlserver_huggingface/codecs/utils.py index ede599b2d..87a666b41 100644 --- a/runtimes/huggingface/mlserver_huggingface/codecs/utils.py +++ b/runtimes/huggingface/mlserver_huggingface/codecs/utils.py @@ -4,14 +4,21 @@ import base64 import numpy as np from PIL import Image, ImageChops -from transformers.pipelines import Conversation from mlserver.codecs.json import JSONEncoderWithArray IMAGE_PREFIX = "data:image/" DEFAULT_IMAGE_FORMAT = "PNG" -class HuggingfaceJSONEncoder(JSONEncoderWithArray): +def get_conversation_class(): + try: + from transformers.pipelines import Conversation + return Conversation + except ImportError: + return None + +Conversation = get_conversation_class() +class HuggingfaceJSONEncoder(JSONEncoderWithArray): def default(self, obj): if isinstance(obj, Image.Image): buf = io.BytesIO() @@ -24,7 +31,7 @@ def default(self, obj): + ";base64," + base64.b64encode(buf.getvalue()).decode() ) - elif isinstance(obj, Conversation): + elif Conversation and isinstance(obj, Conversation): return { "uuid": str(obj.uuid), "past_user_inputs": obj.past_user_inputs, @@ -66,7 +73,7 @@ def do(cls, raw): @classmethod def convert_conversation(cls, d: Dict[str, Any]): - if set(d.keys()) == conversation_keys: + if Conversation and set(d.keys()) == conversation_keys: return Conversation( text=d["new_user_input"], conversation_id=d["uuid"], @@ -83,8 +90,12 @@ def convert_dict(cls, d: Dict[str, Any]): tmp = {} for k, v in d.items(): if isinstance(v, dict): - if set(v.keys()) == conversation_keys: - tmp[k] = Conversation(text=v["new_user_input"]) + if set(d.keys()) == conversation_keys: + tmp[k] = ( + Conversation(text=v["new_user_input"]) + if Conversation + else v + ) else: tmp[k] = cls.convert_dict(v) elif isinstance(v, list): From b690816a8f833faf67ec4ec1b3168a2744ec8b08 Mon Sep 17 00:00:00 2001 From: Denis Kokorev Date: Thu, 17 Jul 2025 14:21:42 +0200 Subject: [PATCH 2/4] chore: add missing newlines in utils.py --- runtimes/huggingface/mlserver_huggingface/codecs/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtimes/huggingface/mlserver_huggingface/codecs/utils.py b/runtimes/huggingface/mlserver_huggingface/codecs/utils.py index 87a666b41..068c11693 100644 --- a/runtimes/huggingface/mlserver_huggingface/codecs/utils.py +++ b/runtimes/huggingface/mlserver_huggingface/codecs/utils.py @@ -18,6 +18,8 @@ def get_conversation_class(): return None Conversation = get_conversation_class() + + class HuggingfaceJSONEncoder(JSONEncoderWithArray): def default(self, obj): if isinstance(obj, Image.Image): From 259a94cec6e242ba2aa385d4d7b936913e21515c Mon Sep 17 00:00:00 2001 From: Denis Kokorev Date: Thu, 17 Jul 2025 15:48:27 +0200 Subject: [PATCH 3/4] fix: safely import transformers.pipelines.Conversation in codecs.conversation.py --- .../mlserver_huggingface/codecs/conversation.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py b/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py index f8bf04494..0731e540d 100644 --- a/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py +++ b/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py @@ -3,7 +3,10 @@ from mlserver.types import RequestInput, ResponseOutput, Parameters from transformers.pipelines import Conversation from mlserver.codecs.lists import is_list_of -from .utils import json_decode, json_encode +from .utils import json_decode, json_encode, get_conversation_class + + +Conversation = get_conversation_class() @register_input_codec @@ -16,12 +19,14 @@ class HuggingfaceConversationCodec(InputCodec): @classmethod def can_encode(cls, payload: Any) -> bool: - return is_list_of(payload, Conversation) + return Conversation is not None and is_list_of(payload, Conversation) @classmethod def encode_output( - cls, name: str, payload: List[Conversation], use_bytes: bool = True, **kwargs + cls, name: str, payload: List[Any], use_bytes: bool = True, **kwargs ) -> ResponseOutput: + if Conversation is None: + raise ImportError("transformers.pipelines.Conversation is not available.") encoded = [json_encode(item, use_bytes=use_bytes) for item in payload] shape = [len(encoded), 1] return ResponseOutput( @@ -41,8 +46,10 @@ def decode_output(cls, response_output: ResponseOutput) -> List[Any]: @classmethod def encode_input( - cls, name: str, payload: List[Conversation], use_bytes: bool = True, **kwargs + cls, name: str, payload: List[Any], use_bytes: bool = True, **kwargs ) -> RequestInput: + if Conversation is None: + raise ImportError("transformers.pipelines.Conversation is not available.") output = cls.encode_output(name, payload, use_bytes) return RequestInput( name=output.name, @@ -55,6 +62,6 @@ def encode_input( ) @classmethod - def decode_input(cls, request_input: RequestInput) -> List[Conversation]: + def decode_input(cls, request_input: RequestInput) -> List[Any]: packed = request_input.data return [json_decode(item) for item in packed] From 8e48713c72eef20c11ce29f3a7f89c984b2dfe0e Mon Sep 17 00:00:00 2001 From: Denis Kokorev Date: Thu, 17 Jul 2025 15:50:08 +0200 Subject: [PATCH 4/4] fix: remove the unsafe import --- runtimes/huggingface/mlserver_huggingface/codecs/conversation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py b/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py index 0731e540d..34db48c8e 100644 --- a/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py +++ b/runtimes/huggingface/mlserver_huggingface/codecs/conversation.py @@ -1,7 +1,6 @@ from typing import List, Any from mlserver.codecs.base import InputCodec, register_input_codec from mlserver.types import RequestInput, ResponseOutput, Parameters -from transformers.pipelines import Conversation from mlserver.codecs.lists import is_list_of from .utils import json_decode, json_encode, get_conversation_class