Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
if TYPE_CHECKING:
from transformers import GenerationConfig


imported = importlib.import_module("transformers")
ready = imported is not None and hasattr(imported, "GenerationConfig")
try:
imported = importlib.import_module("transformers")
ready = imported is not None and hasattr(imported, "GenerationConfig")
except (ImportError, ModuleNotFoundError):
ready = False


class HuggingFacePromptExecutionSettings(PromptExecutionSettings):
Expand All @@ -27,10 +29,12 @@ class HuggingFacePromptExecutionSettings(PromptExecutionSettings):

def get_generation_config(self) -> "GenerationConfig":
"""Get the generation config."""
from transformers import GenerationConfig

if not ready:
raise ImportError("transformers is not installed.")
raise ImportError(
"transformers is not installed. Please install it with `pip install semantic-kernel[hugging_face]`."
)

from transformers import GenerationConfig

return GenerationConfig(
**self.model_dump(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@
else:
from typing_extensions import override # pragma: no cover

import torch
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, Pipeline, TextIteratorStreamer, pipeline
try:
import torch
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, Pipeline, TextIteratorStreamer, pipeline

ready = True
except (ImportError, ModuleNotFoundError):
ready = False

from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import HuggingFacePromptExecutionSettings
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
Expand All @@ -35,7 +40,7 @@ def __init__(
self,
model: Any,
tokenizer: Any,
device: torch.device,
device: Any,
output_key: str,
prefix: str,
default_call_kwargs: dict[str, Any],
Expand Down Expand Up @@ -180,6 +185,12 @@ def __init__(

Note that this model will be downloaded from the Hugging Face model hub.
"""
if not ready:
raise ImportError(
"torch and transformers are not installed. "
"Please install them with `pip install semantic-kernel[hugging_face]`."
)

generator: _Seq2SeqGenerator | Pipeline
if task in {"summarization", "text2text-generation"}:
generator = _create_seq2seq_generator(ai_model_id, task, device, model_kwargs, pipeline_kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
else:
from typing_extensions import override # pragma: no cover

import torch
try:
import torch

torch_installed = True
except (ImportError, ModuleNotFoundError):
torch_installed = False
from numpy import ndarray

from semantic_kernel.connectors.ai.embedding_generator_base import EmbeddingGeneratorBase
Expand Down Expand Up @@ -48,6 +53,11 @@ def __init__(

Note that this model will be downloaded from the Hugging Face model hub.
"""
if not torch_installed:
raise ImportError(
"torch is not installed. Please install it with `pip install semantic-kernel[hugging_face]`."
)

from sentence_transformers import SentenceTransformer

resolved_device = f"cuda:{device}" if device >= 0 and torch.cuda.is_available() else "cpu"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def __init__(self, ai_model_path: str, **kwargs) -> None:
ServiceInitializationError: When model cannot be loaded
"""
if not ready:
raise ImportError("onnxruntime-genai is not installed.")
raise ImportError(
"onnxruntime-genai is not installed. Please install it with `pip install semantic-kernel[onnx]`."
)
try:
json_gen_ai_config = os.path.join(ai_model_path + "/genai_config.json")
with open(json_gen_ai_config) as file:
Expand Down Expand Up @@ -86,8 +88,7 @@ async def _generate_next_token_async(

while not generator.is_done():
generator.generate_next_token()
new_token_choices = [self.tokenizer_stream.decode(token) for token in generator.get_next_tokens()]
yield new_token_choices
yield [self.tokenizer_stream.decode(token) for token in generator.get_next_tokens()]
del generator
except Exception as ex:
raise ServiceInvalidResponseError("Failed Inference with ONNX", ex) from ex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ def __init__(

except ImportError as exc:
raise ServiceInitializationError(
"Could not import chromadb python package. Please install it with `pip install chromadb`."
(
"Could not import chromadb python package. "
"Please install it with `pip install semantic-kernel[chroma]`."
)
) from exc

if client_settings:
Expand Down
119 changes: 119 additions & 0 deletions python/tests/unit/connectors/test_optional_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright (c) Microsoft. All rights reserved.

import sys
from unittest.mock import MagicMock, patch

import pytest

from semantic_kernel.exceptions import ServiceInitializationError


def test_hugging_face_settings_instantiation_without_transformers():
"""HuggingFacePromptExecutionSettings can be imported and instantiated without transformers installed."""
from semantic_kernel.connectors.ai.hugging_face import HuggingFacePromptExecutionSettings

settings = HuggingFacePromptExecutionSettings(
max_new_tokens=100,
temperature=0.7,
)
assert settings.max_new_tokens == 100
assert settings.temperature == 0.7


def test_hugging_face_settings_get_generation_config_missing_dependency():
"""Calling get_generation_config without transformers produces an actionable error naming the install extra."""
from semantic_kernel.connectors.ai.hugging_face import (
HuggingFacePromptExecutionSettings,
hf_prompt_execution_settings,
)

settings = HuggingFacePromptExecutionSettings()

with patch.object(hf_prompt_execution_settings, "ready", False):
with pytest.raises(ImportError) as exc_info:
settings.get_generation_config()

err_msg = str(exc_info.value)
assert "transformers is not installed" in err_msg
assert "semantic-kernel[hugging_face]" in err_msg


def test_hugging_face_settings_get_generation_config_available():
"""When transformers is available, get_generation_config constructs the GenerationConfig."""
from semantic_kernel.connectors.ai.hugging_face import (
HuggingFacePromptExecutionSettings,
hf_prompt_execution_settings,
)

mock_gen_config_cls = MagicMock()
mock_transformers = MagicMock()
mock_transformers.GenerationConfig = mock_gen_config_cls

settings = HuggingFacePromptExecutionSettings(temperature=0.8, top_p=0.9)

with (
patch.object(hf_prompt_execution_settings, "ready", True),
patch.dict(sys.modules, {"transformers": mock_transformers}),
):
config = settings.get_generation_config()
assert config == mock_gen_config_cls.return_value
mock_gen_config_cls.assert_called_once()


def test_hugging_face_text_completion_missing_dependency():
"""HuggingFaceTextCompletion raises an actionable ImportError naming semantic-kernel[hugging_face]."""
from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextCompletion
from semantic_kernel.connectors.ai.hugging_face.services import hf_text_completion

with patch.object(hf_text_completion, "ready", False):
with pytest.raises(ImportError) as exc_info:
HuggingFaceTextCompletion(ai_model_id="test_model")

err_msg = str(exc_info.value)
assert "torch and transformers are not installed" in err_msg
assert "semantic-kernel[hugging_face]" in err_msg


def test_hugging_face_text_embedding_missing_dependency():
"""HuggingFaceTextEmbedding raises an actionable ImportError naming semantic-kernel[hugging_face]."""
from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextEmbedding
from semantic_kernel.connectors.ai.hugging_face.services import hf_text_embedding

with patch.object(hf_text_embedding, "torch_installed", False):
with pytest.raises(ImportError) as exc_info:
HuggingFaceTextEmbedding(ai_model_id="test_model")

err_msg = str(exc_info.value)
assert "torch is not installed" in err_msg
assert "semantic-kernel[hugging_face]" in err_msg


def test_onnx_gen_ai_missing_dependency():
"""OnnxGenAICompletionBase raises an actionable ImportError naming semantic-kernel[onnx]."""
from semantic_kernel.connectors.ai.onnx.services import onnx_gen_ai_completion_base
from semantic_kernel.connectors.ai.onnx.services.onnx_gen_ai_completion_base import (
OnnxGenAICompletionBase,
)

with patch.object(onnx_gen_ai_completion_base, "ready", False):
with pytest.raises(ImportError) as exc_info:
OnnxGenAICompletionBase(ai_model_path="dummy_path")

err_msg = str(exc_info.value)
assert "onnxruntime-genai is not installed" in err_msg
assert "semantic-kernel[onnx]" in err_msg


def test_chroma_memory_store_missing_dependency():
"""ChromaMemoryStore raises ServiceInitializationError naming semantic-kernel[chroma]."""
from semantic_kernel.connectors.memory_stores.chroma.chroma_memory_store import (
ChromaMemoryStore,
)

with patch.dict(sys.modules, {"chromadb": None, "chromadb.config": None}):
with pytest.raises(ServiceInitializationError) as exc_info:
ChromaMemoryStore()

err_msg = str(exc_info.value)
assert "chromadb" in err_msg
assert "semantic-kernel[chroma]" in err_msg
Loading