chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1 @@
# Copyright (c) Microsoft. All rights reserved.
@@ -0,0 +1,157 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
import sys
from collections.abc import AsyncGenerator
from threading import Thread
from typing import Any, ClassVar, Literal
if sys.version_info >= (3, 12):
from typing import override # pragma: no cover
else:
from typing_extensions import override # pragma: no cover
import torch
from transformers import AutoTokenizer, TextIteratorStreamer, pipeline
from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import HuggingFacePromptExecutionSettings
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.connectors.ai.text_completion_client_base import TextCompletionClientBase
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
from semantic_kernel.exceptions import ServiceInvalidExecutionSettingsError, ServiceResponseException
from semantic_kernel.utils.telemetry.model_diagnostics.decorators import (
trace_streaming_text_completion,
trace_text_completion,
)
logger: logging.Logger = logging.getLogger(__name__)
class HuggingFaceTextCompletion(TextCompletionClientBase):
"""Hugging Face text completion service."""
MODEL_PROVIDER_NAME: ClassVar[str] = "huggingface"
task: Literal["summarization", "text-generation", "text2text-generation"]
device: str
generator: Any
def __init__(
self,
ai_model_id: str,
task: str | None = "text2text-generation",
device: int = -1,
service_id: str | None = None,
model_kwargs: dict[str, Any] | None = None,
pipeline_kwargs: dict[str, Any] | None = None,
) -> None:
"""Initializes a new instance of the HuggingFaceTextCompletion class.
Args:
ai_model_id (str): Hugging Face model card string, see
https://huggingface.co/models
device (int): Device to run the model on, defaults to CPU, 0+ for GPU,
-- None if using device_map instead. (If both device and device_map
are specified, device overrides device_map. If unintended,
it can lead to unexpected behavior.) (optional)
service_id (str): Service ID for the AI service. (optional)
task (str): Model completion task type, options are:
- summarization: takes a long text and returns a shorter summary.
- text-generation: takes incomplete text and returns a set of completion candidates.
- text2text-generation (default): takes an input prompt and returns a completion.
text2text-generation is the default as it behaves more like GPT-3+. (optional)
model_kwargs (dict[str, Any]): Additional dictionary of keyword arguments
passed along to the model's `from_pretrained(..., **model_kwargs)` function. (optional)
pipeline_kwargs (dict[str, Any]): Additional keyword arguments passed along
to the specific pipeline init (see the documentation for the corresponding pipeline class
for possible values). (optional)
Note that this model will be downloaded from the Hugging Face model hub.
"""
generator = pipeline(
task=task, # type: ignore[arg-type]
model=ai_model_id,
device=device,
model_kwargs=model_kwargs,
**pipeline_kwargs or {},
)
resolved_device = f"cuda:{device}" if device >= 0 and torch.cuda.is_available() else "cpu"
super().__init__(
service_id=service_id or ai_model_id,
ai_model_id=ai_model_id,
task=task,
device=resolved_device,
generator=generator,
)
# region Overriding base class methods
# Override from AIServiceClientBase
@override
def get_prompt_execution_settings_class(self) -> type["PromptExecutionSettings"]:
return HuggingFacePromptExecutionSettings
@override
@trace_text_completion(MODEL_PROVIDER_NAME)
async def _inner_get_text_contents(
self,
prompt: str,
settings: "PromptExecutionSettings",
) -> list[TextContent]:
if not isinstance(settings, HuggingFacePromptExecutionSettings):
settings = self.get_prompt_execution_settings_from_settings(settings)
assert isinstance(settings, HuggingFacePromptExecutionSettings) # nosec
try:
results = self.generator(prompt, **settings.prepare_settings_dict())
except Exception as e:
raise ServiceResponseException("Hugging Face completion failed") from e
if isinstance(results, list):
return [self._create_text_content(results, result) for result in results]
return [self._create_text_content(results, results)]
@override
@trace_streaming_text_completion(MODEL_PROVIDER_NAME)
async def _inner_get_streaming_text_contents(
self,
prompt: str,
settings: "PromptExecutionSettings",
) -> AsyncGenerator[list[StreamingTextContent], Any]:
if not isinstance(settings, HuggingFacePromptExecutionSettings):
settings = self.get_prompt_execution_settings_from_settings(settings)
assert isinstance(settings, HuggingFacePromptExecutionSettings) # nosec
if settings.num_return_sequences > 1:
raise ServiceInvalidExecutionSettingsError(
"HuggingFace TextIteratorStreamer does not stream multiple responses in a parsable format."
" If you need multiple responses, please use the complete method.",
)
try:
streamer = TextIteratorStreamer(AutoTokenizer.from_pretrained(self.ai_model_id))
# See https://github.com/huggingface/transformers/blob/main/src/transformers/generation/streamers.py#L159
thread = Thread(
target=self.generator, args={prompt}, kwargs=settings.prepare_settings_dict(streamer=streamer)
)
thread.start()
for new_text in streamer:
yield [
StreamingTextContent(
choice_index=0, inner_content=new_text, text=new_text, ai_model_id=self.ai_model_id
)
]
thread.join()
except Exception as e:
raise ServiceResponseException("Hugging Face completion failed") from e
# endregion
def _create_text_content(self, response: Any, candidate: dict[str, str]) -> TextContent:
return TextContent(
inner_content=response,
ai_model_id=self.ai_model_id,
text=candidate["summary_text" if self.task == "summarization" else "generated_text"],
)
@@ -0,0 +1,88 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
import sys
from typing import TYPE_CHECKING, Any
if sys.version_info >= (3, 12):
from typing import override # pragma: no cover
else:
from typing_extensions import override # pragma: no cover
import torch
from numpy import ndarray
from semantic_kernel.connectors.ai.embedding_generator_base import EmbeddingGeneratorBase
from semantic_kernel.exceptions import ServiceResponseException
from semantic_kernel.utils.feature_stage_decorator import experimental
if TYPE_CHECKING:
from torch import Tensor
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
logger: logging.Logger = logging.getLogger(__name__)
@experimental
class HuggingFaceTextEmbedding(EmbeddingGeneratorBase):
"""Hugging Face text embedding service."""
device: str
generator: Any
def __init__(
self,
ai_model_id: str,
device: int = -1,
service_id: str | None = None,
) -> None:
"""Initializes a new instance of the HuggingFaceTextEmbedding class.
Args:
ai_model_id (str): Hugging Face model card string, see
https://huggingface.co/sentence-transformers
device (int): Device to run the model on, -1 for CPU, 0+ for GPU. (optional)
service_id (str): Service ID for the model. (optional)
Note that this model will be downloaded from the Hugging Face model hub.
"""
from sentence_transformers import SentenceTransformer
resolved_device = f"cuda:{device}" if device >= 0 and torch.cuda.is_available() else "cpu"
super().__init__(
ai_model_id=ai_model_id,
service_id=service_id or ai_model_id,
device=resolved_device,
generator=SentenceTransformer( # type: ignore
model_name_or_path=ai_model_id,
device=resolved_device,
),
)
@override
async def generate_embeddings(
self,
texts: list[str],
settings: "PromptExecutionSettings | None" = None,
**kwargs: Any,
) -> ndarray:
try:
logger.info(f"Generating embeddings for {len(texts)} texts.")
return self.generator.encode(sentences=texts, convert_to_numpy=True, **kwargs)
except Exception as e:
raise ServiceResponseException("Hugging Face embeddings failed", e) from e
@override
async def generate_raw_embeddings(
self,
texts: list[str],
settings: "PromptExecutionSettings | None" = None,
**kwargs: Any,
) -> "list[Tensor] | ndarray | Tensor":
try:
logger.info(f"Generating raw embeddings for {len(texts)} texts.")
return self.generator.encode(sentences=texts, **kwargs)
except Exception as e:
raise ServiceResponseException("Hugging Face embeddings failed", e) from e