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,240 @@
# Copyright (c) Microsoft. All rights reserved.
import os
import sys
from typing import Annotated
if sys.version_info >= (3, 12):
from typing import override # pragma: no cover
else:
from typing_extensions import override # pragma: no cover
import pytest
from azure.ai.inference.aio import ChatCompletionsClient
from azure.identity import AzureCliCredential
from openai import AsyncAzureOpenAI
from semantic_kernel.connectors.ai.anthropic import AnthropicChatCompletion, AnthropicChatPromptExecutionSettings
from semantic_kernel.connectors.ai.azure_ai_inference import (
AzureAIInferenceChatCompletion,
AzureAIInferenceChatPromptExecutionSettings,
)
from semantic_kernel.connectors.ai.bedrock import BedrockChatCompletion, BedrockChatPromptExecutionSettings
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.connectors.ai.google.google_ai import GoogleAIChatCompletion, GoogleAIChatPromptExecutionSettings
from semantic_kernel.connectors.ai.mistral_ai import MistralAIChatCompletion, MistralAIChatPromptExecutionSettings
from semantic_kernel.connectors.ai.ollama import OllamaChatCompletion, OllamaChatPromptExecutionSettings
from semantic_kernel.connectors.ai.onnx import OnnxGenAIChatCompletion, OnnxGenAIPromptExecutionSettings, ONNXTemplate
from semantic_kernel.connectors.ai.open_ai import (
AzureChatCompletion,
AzureChatPromptExecutionSettings,
AzureOpenAISettings,
OpenAIChatCompletion,
OpenAIChatPromptExecutionSettings,
)
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.core_plugins.math_plugin import MathPlugin
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel import Kernel
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.utils.authentication.entra_id_authentication import get_entra_auth_token
from tests.integration.completions.completion_test_base import CompletionTestBase, ServiceType
from tests.utils import is_service_setup_for_testing
# Make sure all services are setup for before running the tests
# The following exceptions apply:
# 1. OpenAI and Azure OpenAI services are always setup for testing.
azure_openai_setup: bool = True
# 2. Bedrock services don't use API keys and model providers are tested individually,
# so no environment variables are required.
mistral_ai_setup: bool = is_service_setup_for_testing(
["MISTRALAI_API_KEY", "MISTRALAI_CHAT_MODEL_ID"], raise_if_not_set=False
) # We don't have a MistralAI deployment
# There is no single model in Ollama that supports both image and tool call in chat completion
# We are splitting the Ollama test into three services: chat, image, and tool call. The chat model
# can be any model that supports chat completion. Also, Ollama is only available on Linux runners in our pipeline.
ollama_setup: bool = is_service_setup_for_testing(["OLLAMA_CHAT_MODEL_ID"])
ollama_image_setup: bool = is_service_setup_for_testing(["OLLAMA_CHAT_MODEL_ID_IMAGE"])
ollama_tool_call_setup: bool = is_service_setup_for_testing(["OLLAMA_CHAT_MODEL_ID_TOOL_CALL"])
google_ai_setup: bool = is_service_setup_for_testing(["GOOGLE_AI_API_KEY", "GOOGLE_AI_GEMINI_MODEL_ID"])
vertex_ai_setup: bool = is_service_setup_for_testing([
"GOOGLE_AI_CLOUD_PROJECT_ID",
"GOOGLE_AI_GEMINI_MODEL_ID",
"GOOGLE_AI_CLOUD_REGION",
])
onnx_setup: bool = is_service_setup_for_testing(
["ONNX_GEN_AI_CHAT_MODEL_FOLDER"], raise_if_not_set=False
) # Tests are optional for ONNX
anthropic_setup: bool = is_service_setup_for_testing(["ANTHROPIC_API_KEY", "ANTHROPIC_CHAT_MODEL_ID"])
# A mock plugin that contains a function that returns a complex object.
class PersonDetails(KernelBaseModel):
id: str
name: str
age: int
class PersonSearchPlugin:
@kernel_function(name="SearchPerson", description="Search details of a person given their id.")
def search_person(
self, person_id: Annotated[str, "The person ID to search"]
) -> Annotated[PersonDetails, "The details of the person"]:
return PersonDetails(id=person_id, name="John Doe", age=42)
class ChatCompletionTestBase(CompletionTestBase):
"""Base class for testing completion services."""
@override
@pytest.fixture(
scope="function"
) # This needs to be scoped to function to avoid resources getting cleaned up after each test
def services(self) -> dict[str, tuple[ServiceType | None, type[PromptExecutionSettings] | None]]:
azure_openai_setup = True
credential = AzureCliCredential()
azure_openai_settings = AzureOpenAISettings()
endpoint = str(azure_openai_settings.endpoint)
deployment_name = azure_openai_settings.chat_deployment_name
ad_token = get_entra_auth_token(credential, azure_openai_settings.token_endpoint)
if not ad_token:
azure_openai_setup = False
api_version = azure_openai_settings.api_version
azure_custom_client = None
azure_ai_inference_client = None
if azure_openai_setup:
azure_custom_client = AzureChatCompletion(
async_client=AsyncAzureOpenAI(
azure_endpoint=endpoint,
azure_deployment=deployment_name,
azure_ad_token=ad_token,
api_version=api_version,
default_headers={"Test-User-X-ID": "test"},
),
)
assert deployment_name
azure_ai_inference_client = AzureAIInferenceChatCompletion(
ai_model_id=deployment_name,
client=ChatCompletionsClient(
endpoint=f"{endpoint.strip('/')}/openai/deployments/{deployment_name}",
credential=credential, # type: ignore
credential_scopes=["https://cognitiveservices.azure.com/.default"],
),
)
return {
"openai": (OpenAIChatCompletion(), OpenAIChatPromptExecutionSettings),
"azure": (
AzureChatCompletion(credential=credential) if azure_openai_setup else None,
AzureChatPromptExecutionSettings,
),
"azure_custom_client": (azure_custom_client, AzureChatPromptExecutionSettings),
"azure_ai_inference": (azure_ai_inference_client, AzureAIInferenceChatPromptExecutionSettings),
"anthropic": (AnthropicChatCompletion() if anthropic_setup else None, AnthropicChatPromptExecutionSettings),
"mistral_ai": (
MistralAIChatCompletion() if mistral_ai_setup else None,
MistralAIChatPromptExecutionSettings,
),
"ollama": (OllamaChatCompletion() if ollama_setup else None, OllamaChatPromptExecutionSettings),
"ollama_image": (
OllamaChatCompletion(ai_model_id=os.environ["OLLAMA_CHAT_MODEL_ID_IMAGE"])
if ollama_image_setup
else None,
OllamaChatPromptExecutionSettings,
),
"ollama_tool_call": (
OllamaChatCompletion(ai_model_id=os.environ["OLLAMA_CHAT_MODEL_ID_TOOL_CALL"])
if ollama_tool_call_setup
else None,
OllamaChatPromptExecutionSettings,
),
"google_ai": (GoogleAIChatCompletion() if google_ai_setup else None, GoogleAIChatPromptExecutionSettings),
"vertex_ai": (
GoogleAIChatCompletion(use_vertexai=True) if vertex_ai_setup else None,
GoogleAIChatPromptExecutionSettings,
),
"onnx_gen_ai": (
OnnxGenAIChatCompletion(template=ONNXTemplate.PHI3V) if onnx_setup else None,
OnnxGenAIPromptExecutionSettings,
),
"bedrock_amazon_nova": (
self._try_create_bedrock_chat_completion_client("amazon.nova-lite-v1:0"),
BedrockChatPromptExecutionSettings,
),
"bedrock_ai21labs": (
self._try_create_bedrock_chat_completion_client("ai21.jamba-1-5-mini-v1:0"),
BedrockChatPromptExecutionSettings,
),
"bedrock_anthropic_claude": (
self._try_create_bedrock_chat_completion_client("anthropic.claude-3-sonnet-20240229-v1:0"),
BedrockChatPromptExecutionSettings,
),
"bedrock_cohere_command": (
self._try_create_bedrock_chat_completion_client("cohere.command-r-v1:0"),
BedrockChatPromptExecutionSettings,
),
"bedrock_meta_llama": (
self._try_create_bedrock_chat_completion_client("meta.llama3-70b-instruct-v1:0"),
BedrockChatPromptExecutionSettings,
),
"bedrock_mistralai": (
self._try_create_bedrock_chat_completion_client("mistral.mistral-small-2402-v1:0"),
BedrockChatPromptExecutionSettings,
),
}
def setup(self, kernel: Kernel):
"""Setup the kernel with the completion service and function."""
kernel.add_plugin(MathPlugin(), plugin_name="math")
kernel.add_plugin(PersonSearchPlugin(), plugin_name="search")
async def get_chat_completion_response(
self,
kernel: Kernel,
service: ServiceType,
execution_settings: PromptExecutionSettings,
chat_history: ChatHistory,
stream: bool,
) -> ChatMessageContent | StreamingChatMessageContent | None:
"""Get response from the service
Args:
kernel (Kernel): Kernel instance.
service (ChatCompletionClientBase): Chat completion service.
execution_settings (PromptExecutionSettings): Execution settings.
input (str): Input string.
stream (bool): Stream flag.
"""
assert isinstance(service, ChatCompletionClientBase)
if not stream:
return await service.get_chat_message_content(
chat_history,
execution_settings,
kernel=kernel,
)
parts: list[StreamingChatMessageContent] = [
part
async for part in service.get_streaming_chat_message_content(
chat_history,
execution_settings,
kernel=kernel,
)
if part
]
if parts:
return sum(parts[1:], parts[0])
raise AssertionError("No response")
def _try_create_bedrock_chat_completion_client(self, model_id: str) -> BedrockChatCompletion | None:
try:
return BedrockChatCompletion(model_id=model_id)
except Exception as ex:
from conftest import logger
logger.warning(ex)
# Returning None so that the test that uses this service will be skipped
return None
@@ -0,0 +1,70 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
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.chat_message_content import ChatMessageContent
from semantic_kernel.kernel import Kernel
ServiceType = ChatCompletionClientBase | TextCompletionClientBase
class CompletionTestBase:
"""Base class for testing completion services."""
def services(self) -> dict[str, tuple["ServiceType", type[PromptExecutionSettings]]]:
"""Return completion services."""
raise NotImplementedError
async def test_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[str | ChatMessageContent | list[ChatMessageContent]],
kwargs: dict[str, Any],
) -> None:
"""Test completion service (Non-streaming).
Args:
kernel (Kernel): Kernel instance.
service_id (str): Service name.
services (dict[str, tuple[ServiceType, type[PromptExecutionSettings]]]): Completion services.
execution_settings_kwargs (dict[str, Any]): Execution settings keyword arguments.
inputs (list[str]): List of input strings.
kwargs (dict[str, Any]): Keyword arguments.
"""
raise NotImplementedError
async def test_streaming_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[str | ChatMessageContent | list[ChatMessageContent]],
kwargs: dict[str, Any],
):
"""Test completion service (Streaming).
Args:
kernel (Kernel): Kernel instance.
service_id (str): Service name.
services (dict[str, tuple[ServiceType, type[PromptExecutionSettings]]]): Completion services.
execution_settings_kwargs (dict[str, Any]): Execution settings keyword arguments.
inputs (list[str]): List of input strings.
kwargs (dict[str, Any]): Keyword arguments.
"""
raise NotImplementedError
def evaluate(self, test_target: Any, **kwargs):
"""Evaluate the response.
Args:
test_target (Any): Test target.
kwargs (dict[str, Any]): Keyword arguments.
"""
raise NotImplementedError
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from semantic_kernel.utils.logging import setup_logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
setup_logging()
@@ -0,0 +1,158 @@
# Copyright (c) Microsoft. All rights reserved.
import os
import time
from random import randint
import numpy as np
import pytest
import pytest_asyncio
from azure.identity import AzureCliCredential
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (
ApiKeyAuthentication,
AzureAISearchDataSource,
AzureAISearchDataSourceParameters,
DataSourceFieldsMapping,
ExtraBody,
)
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.function_result_content import FunctionResultContent
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel import Kernel
from semantic_kernel.memory.memory_record import MemoryRecord
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
try:
from semantic_kernel.connectors.memory_stores.azure_cognitive_search.azure_cognitive_search_memory_store import (
AzureCognitiveSearchMemoryStore,
)
azure_ai_search_installed = True
except ImportError:
azure_ai_search_installed = False
if os.environ.get("AZURE_COGNITIVE_SEARCH_ENDPOINT") and os.environ.get("AZURE_COGNITIVE_SEARCH_ADMIN_KEY"):
azure_ai_search_settings = True
else:
azure_ai_search_settings = False
pytestmark = pytest.mark.skipif(
not (azure_ai_search_installed and azure_ai_search_settings),
reason="Azure AI Search is not installed",
)
@pytest_asyncio.fixture
async def create_memory_store():
# Create an index and populate it with some data
collection = f"int-tests-chat-extensions-{randint(1000, 9999)}"
memory_store = AzureCognitiveSearchMemoryStore(vector_size=4)
await memory_store.create_collection(collection)
time.sleep(1)
try:
assert await memory_store.does_collection_exist(collection)
rec = MemoryRecord(
is_reference=False,
external_source_name=None,
id=None,
description="Emily and David's story.",
text="Emily and David, two passionate scientists, met during a research expedition to Antarctica. \
Bonded by their love for the natural world and shared curiosity, they uncovered a \
groundbreaking phenomenon in glaciology that could potentially reshape our understanding \
of climate change.",
additional_metadata=None,
embedding=np.array([0.2, 0.1, 0.2, 0.7]),
)
await memory_store.upsert(collection, rec)
time.sleep(1)
return collection, memory_store
except Exception as e:
await memory_store.delete_collection(collection)
raise e
@pytest_asyncio.fixture
async def create_with_data_chat_function(kernel: Kernel, create_memory_store):
collection, memory_store = create_memory_store
try:
# Load Azure OpenAI with data settings
search_endpoint = os.getenv("AZURE_COGNITIVE_SEARCH_ENDPOINT")
search_api_key = os.getenv("AZURE_COGNITIVE_SEARCH_ADMIN_KEY")
extra = ExtraBody(
data_sources=[
AzureAISearchDataSource(
parameters=AzureAISearchDataSourceParameters(
index_name=collection,
endpoint=search_endpoint,
authentication=ApiKeyAuthentication(key=search_api_key),
query_type="simple",
fields_mapping=DataSourceFieldsMapping(
title_field="Description",
content_fields=["Text"],
),
top_n_documents=1,
),
)
]
)
chat_service = AzureChatCompletion(service_id="chat-gpt-extensions", credential=AzureCliCredential())
kernel.add_service(chat_service)
prompt = "{{$chat_history}}{{$input}}"
exec_settings = PromptExecutionSettings(
service_id="chat-gpt-extensions",
extension_data={"max_tokens": 2000, "temperature": 0.7, "top_p": 0.8, "extra_body": extra},
)
prompt_template_config = PromptTemplateConfig(
template=prompt, description="Chat", execution_settings=exec_settings
)
# Create the semantic function
kernel.add_function(function_name="chat", plugin_name="plugin", prompt_template_config=prompt_template_config)
chat_function = kernel.get_function("plugin", "chat")
return chat_function, kernel, collection, memory_store
except Exception as e:
await memory_store.delete_collection(collection)
raise e
@pytestmark
async def test_azure_e2e_chat_completion_with_extensions(create_with_data_chat_function):
# Create an index and populate it with some data
chat_function, kernel, collection, memory_store = create_with_data_chat_function
chat_history = ChatHistory()
chat_history.add_user_message("A story about Emily and David...")
arguments = KernelArguments(input="who are Emily and David?", chat_history=chat_history)
# TODO: get streaming working for this test
use_streaming = False
try:
result: StreamingChatMessageContent = None
if use_streaming:
async for message in kernel.invoke_stream(chat_function, arguments):
result = message[0] if not result else result + message[0]
print(message, end="")
print(f"Answer using input string: '{result}'")
for item in result.items:
if isinstance(item, FunctionResultContent):
print(f"Content: {item.result}")
assert "two passionate scientists" in item.result
else:
result = await kernel.invoke(chat_function, arguments)
print(f"Answer using input string: '{result}'")
await memory_store.delete_collection(collection)
except Exception as e:
await memory_store.delete_collection(collection)
raise e
@@ -0,0 +1,347 @@
# Copyright (c) Microsoft. All rights reserved.
import os
import sys
from functools import partial
from typing import Any
if sys.version_info >= (3, 12):
from typing import override # pragma: no cover
else:
from typing_extensions import override # pragma: no cover
import pytest
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.contents import ChatHistory, ChatMessageContent, TextContent
from semantic_kernel.contents.image_content import ImageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from tests.integration.completions.chat_completion_test_base import (
ChatCompletionTestBase,
ollama_image_setup,
onnx_setup,
vertex_ai_setup,
)
from tests.integration.completions.completion_test_base import ServiceType
from tests.utils import retry
# Use the repo's own sample image via raw GitHub URL for URI-based tests.
# Previously this pointed to a 17.5 MB Wikimedia image that got blocked by
# Wikimedia's User-Agent policy (Phabricator T400119), causing Azure's
# server-side image fetcher to fail with HTTP 403.
IMAGE_TEST_URL = "https://raw.githubusercontent.com/microsoft/semantic-kernel/main/python/tests/assets/sample_image.jpg"
pytestmark = pytest.mark.parametrize(
"service_id, execution_settings_kwargs, inputs, kwargs",
[
pytest.param(
"openai",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent(uri=IMAGE_TEST_URL),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
id="openai_image_input_uri",
),
pytest.param(
"openai",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
id="openai_image_input_file",
),
pytest.param(
"azure",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent(uri=IMAGE_TEST_URL),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
id="azure_image_input_uri",
),
pytest.param(
"azure",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
id="azure_image_input_file",
),
pytest.param(
"onnx_gen_ai",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
marks=(
pytest.mark.skipif(not onnx_setup, reason="Need a Onnx Model setup"),
pytest.mark.onnx,
),
id="onnx_gen_ai_image_input_file",
),
pytest.param(
"azure_ai_inference",
{
"max_tokens": 256,
},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent(uri=IMAGE_TEST_URL),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
id="azure_ai_inference_image_input_uri",
),
pytest.param(
"azure_ai_inference",
{
"max_tokens": 256,
},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
id="azure_ai_inference_image_input_file",
),
pytest.param(
"google_ai",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(
role=AuthorRole.USER,
items=[TextContent(text="Where was it made? Make a guess if you are not sure.")],
),
],
{},
marks=[
pytest.mark.skip(reason="Skipping due to occasional throttling from Google AI."),
# pytest.mark.skipif(not google_ai_setup, reason="Google AI Environment Variables not set"),
],
id="google_ai_image_input_file",
),
pytest.param(
"vertex_ai",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(
role=AuthorRole.USER,
items=[TextContent(text="Where was it made? Make a guess if you are not sure.")],
),
],
{},
marks=pytest.mark.skipif(not vertex_ai_setup, reason="Vertex AI Environment Variables not set"),
id="vertex_ai_image_input_file",
),
pytest.param(
"ollama_image",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(
role=AuthorRole.USER,
items=[TextContent(text="Where was it made? Make a guess if you are not sure.")],
),
],
{},
marks=(
pytest.mark.skipif(not ollama_image_setup, reason="Ollama Environment Variables not set"),
pytest.mark.ollama,
),
id="ollama_image_input_file",
),
pytest.param(
"bedrock_anthropic_claude",
{},
[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="What is in this image?"),
ImageContent.from_image_path(
image_path=os.path.join(os.path.dirname(__file__), "../../", "assets/sample_image.jpg")
),
],
),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Where was it made?")]),
],
{},
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_anthropic_claude_image_input_file",
),
],
)
class TestChatCompletionWithImageInputTextOutput(ChatCompletionTestBase):
"""Test chat completion with image input and text output."""
@override
async def test_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[ChatMessageContent],
kwargs: dict[str, Any],
):
await self._test_helper(
kernel,
service_id,
services,
execution_settings_kwargs,
inputs,
False,
)
@override
async def test_streaming_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[ChatMessageContent],
kwargs: dict[str, Any],
):
await self._test_helper(
kernel,
service_id,
services,
execution_settings_kwargs,
inputs,
True,
)
@override
def evaluate(self, test_target: Any, **kwargs):
inputs = kwargs.get("inputs")
assert isinstance(inputs, list)
assert len(test_target) == len(inputs) * 2
for i in range(len(inputs)):
message = test_target[i * 2 + 1]
assert message.items, "No items in message"
assert len(message.items) == 1, "Unexpected number of items in message"
assert isinstance(message.items[0], TextContent), "Unexpected message item type"
assert message.items[0].text, "Empty message text"
async def _test_helper(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[ChatMessageContent],
stream: bool,
):
self.setup(kernel)
service, settings_type = services[service_id]
if service is None:
pytest.skip(f"Service {service_id} not set up")
history = ChatHistory()
for message in inputs:
history.add_message(message)
cmc: ChatMessageContent | None = await retry(
partial(
self.get_chat_completion_response,
kernel=kernel,
service=service,
execution_settings=settings_type(**execution_settings_kwargs),
chat_history=history,
stream=stream,
),
retries=5,
name="image_input",
)
if cmc:
history.add_message(cmc)
self.evaluate(history.messages, inputs=inputs)
@@ -0,0 +1,346 @@
# Copyright (c) Microsoft. All rights reserved.
import sys
from functools import partial
from typing import Any
import pytest
if sys.version_info >= (3, 12):
from typing import override # pragma: no cover
else:
from typing_extensions import override # pragma: no cover
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai import PromptExecutionSettings
from semantic_kernel.contents import AuthorRole, ChatHistory, ChatMessageContent, TextContent
from semantic_kernel.kernel_pydantic import KernelBaseModel
from tests.integration.completions.chat_completion_test_base import (
ChatCompletionTestBase,
anthropic_setup,
mistral_ai_setup,
ollama_setup,
onnx_setup,
vertex_ai_setup,
)
from tests.integration.completions.completion_test_base import ServiceType
from tests.utils import retry
class Step(KernelBaseModel):
explanation: str
output: str
class Reasoning(KernelBaseModel):
steps: list[Step]
final_answer: str
pytestmark = pytest.mark.parametrize(
"service_id, execution_settings_kwargs, inputs, kwargs",
[
# region OpenAI
pytest.param(
"openai",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
id="openai_text_input",
),
pytest.param(
"openai",
{"response_format": Reasoning},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
id="openai_json_schema_response_format",
),
# endregion
# region Azure
pytest.param(
"azure",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
id="azure_text_input",
),
pytest.param(
"azure_custom_client",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
id="azure_custom_client",
),
# endregion
# region Azure AI Inference
pytest.param(
"azure_ai_inference",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
id="azure_ai_inference_text_input",
),
# endregion
# region Anthropic
pytest.param(
"anthropic",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skipif(not anthropic_setup, reason="Anthropic Environment Variables not set"),
id="anthropic_text_input",
),
# endregion
# region Mistral AI
pytest.param(
"mistral_ai",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skipif(not mistral_ai_setup, reason="Mistral AI Environment Variables not set"),
id="mistral_ai_text_input",
),
# endregion
# region Ollama
pytest.param(
"ollama",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=(
pytest.mark.skipif(not ollama_setup, reason="Need local Ollama setup"),
pytest.mark.ollama,
),
id="ollama_text_input",
),
# endregion
# region Onnx Gen AI
pytest.param(
"onnx_gen_ai",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=(
pytest.mark.skipif(not onnx_setup, reason="Need a Onnx Model setup"),
pytest.mark.onnx,
),
id="onnx_gen_ai",
),
# endregion
# region Google AI
pytest.param(
"google_ai",
{"top_p": 0.9, "temperature": 0.7},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=[
pytest.mark.skip(reason="Skipping due to occasional throttling from Google AI."),
# pytest.mark.skipif(not google_ai_setup, reason="Need Google AI setup"),
],
id="google_ai_text_input",
),
# endregion
# region Vertex AI
pytest.param(
"vertex_ai",
{"top_p": 0.9, "temperature": 0.7},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skipif(not vertex_ai_setup, reason="Vertex AI Environment Variables not set"),
id="vertex_ai_text_input",
),
# endregion
# region Bedrock
pytest.param(
"bedrock_amazon_nova",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
id="bedrock_amazon_nova_text_input",
),
pytest.param(
"bedrock_ai21labs",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_ai21labs_text_input",
),
pytest.param(
"bedrock_anthropic_claude",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_anthropic_claude_text_input",
),
pytest.param(
"bedrock_cohere_command",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_cohere_command_text_input",
),
pytest.param(
"bedrock_meta_llama",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_meta_llama_text_input",
),
pytest.param(
"bedrock_mistralai",
{},
[
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="Hello")]),
ChatMessageContent(role=AuthorRole.USER, items=[TextContent(text="How are you today?")]),
],
{},
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_mistralai_text_input",
),
# endregion
],
)
class TestChatCompletion(ChatCompletionTestBase):
"""Test Chat Completions.
This only tests if the services can return text completions given text inputs.
"""
@override
async def test_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[ChatMessageContent],
kwargs: dict[str, Any],
):
await self._test_helper(
kernel,
service_id,
services,
execution_settings_kwargs,
inputs,
False,
)
@override
async def test_streaming_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[ChatMessageContent],
kwargs: dict[str, Any],
):
await self._test_helper(
kernel,
service_id,
services,
execution_settings_kwargs,
inputs,
True,
)
@override
def evaluate(self, test_target: Any, **kwargs):
inputs = kwargs.get("inputs")
assert isinstance(inputs, list)
assert len(test_target) == len(inputs) * 2
for i in range(len(inputs)):
message = test_target[i * 2 + 1]
assert message.items, "No items in message"
assert len(message.items) == 1, "Unexpected number of items in message"
assert isinstance(message.items[0], TextContent), "Unexpected message item type"
assert message.items[0].text, "Empty message text"
async def _test_helper(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[ChatMessageContent],
stream: bool,
):
self.setup(kernel)
service, settings_type = services[service_id]
if service is None:
pytest.skip(f"Service {service_id} not set up")
history = ChatHistory()
for message in inputs:
history.add_message(message)
cmc: ChatMessageContent | None = await retry(
partial(
self.get_chat_completion_response,
kernel=kernel,
service=service,
execution_settings=settings_type(**execution_settings_kwargs),
chat_history=history,
stream=stream,
),
retries=5,
name="get_chat_completion_response",
)
if cmc:
history.add_message(cmc)
self.evaluate(history.messages, inputs=inputs)
@@ -0,0 +1,76 @@
# Copyright (c) Microsoft. All rights reserved.
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.core_plugins.conversation_summary_plugin import ConversationSummaryPlugin
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
from tests.utils import retry
CHAT_TRANSCRIPT = """John: Hello, how are you?
Jane: I'm fine, thanks. How are you?
John: I'm doing well, writing some example code.
Jane: That's great! I'm writing some example code too.
John: What are you writing?
Jane: I'm writing a chatbot.
John: That's cool. I'm writing a chatbot too.
Jane: What language are you writing it in?
John: I'm writing it in C#.
Jane: I'm writing it in Python.
John: That's cool. I need to learn Python.
Jane: I need to learn C#.
John: Can I try out your chatbot?
Jane: Sure, here's the link.
John: Thanks!
Jane: You're welcome.
Jane: Look at this poem my chatbot wrote:
Jane: Roses are red
Jane: Violets are blue
Jane: I'm writing a chatbot
Jane: What about you?
John: That's cool. Let me see if mine will write a poem, too.
John: Here's a poem my chatbot wrote:
John: The singularity of the universe is a mystery.
Jane: You might want to try using a different model.
John: I'm using the GPT-2 model. That makes sense.
John: Here is a new poem after updating the model.
John: The universe is a mystery.
John: The universe is a mystery.
John: The universe is a mystery.
Jane: Sure, what's the problem?
John: Thanks for the help!
Jane: I'm now writing a bot to summarize conversations.
Jane: I have some bad news, we're only half way there.
John: Maybe there is a large piece of text we can use to generate a long conversation.
Jane: That's a good idea. Let me see if I can find one. Maybe Lorem Ipsum?
John: Yeah, that's a good idea."""
async def test_azure_summarize_conversation_using_plugin(kernel):
service_id = "text_completion"
execution_settings = PromptExecutionSettings(
service_id=service_id, max_tokens=ConversationSummaryPlugin._max_tokens, temperature=0.1, top_p=0.5
)
prompt_template_config = PromptTemplateConfig(
description="Given a section of a conversation transcript, summarize the part of the conversation.",
execution_settings={service_id: execution_settings},
)
kernel.add_service(sk_oai.OpenAIChatCompletion(service_id=service_id))
conversationSummaryPlugin = kernel.add_plugin(
ConversationSummaryPlugin(prompt_template_config), "conversationSummary"
)
arguments = KernelArguments(input=CHAT_TRANSCRIPT)
summary = await retry(
lambda: kernel.invoke(conversationSummaryPlugin["SummarizeConversation"], arguments), retries=5
)
output = str(summary).strip().lower()
print(output)
assert "john" in output and "jane" in output
assert len(output) < len(CHAT_TRANSCRIPT)
@@ -0,0 +1,345 @@
# Copyright (c) Microsoft. All rights reserved.
import sys
from functools import partial
from importlib import util
from typing import Any
if sys.version_info >= (3, 12):
from typing import override # pragma: no cover
else:
from typing_extensions import override # pragma: no cover
import pytest
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.bedrock import BedrockTextCompletion, BedrockTextPromptExecutionSettings
from semantic_kernel.connectors.ai.google.google_ai import GoogleAITextCompletion, GoogleAITextPromptExecutionSettings
from semantic_kernel.connectors.ai.hugging_face import HuggingFacePromptExecutionSettings, HuggingFaceTextCompletion
from semantic_kernel.connectors.ai.ollama import OllamaTextCompletion, OllamaTextPromptExecutionSettings
from semantic_kernel.connectors.ai.onnx import OnnxGenAIPromptExecutionSettings, OnnxGenAITextCompletion
from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion, OpenAITextPromptExecutionSettings
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 import StreamingTextContent, TextContent
from tests.integration.completions.completion_test_base import CompletionTestBase, ServiceType
from tests.utils import is_service_setup_for_testing, is_test_running_on_supported_platforms, retry
hugging_face_setup = util.find_spec("torch") is not None
ollama_setup: bool = is_service_setup_for_testing(["OLLAMA_TEXT_MODEL_ID"]) and is_test_running_on_supported_platforms([
"Linux"
])
google_ai_setup: bool = is_service_setup_for_testing(["GOOGLE_AI_API_KEY", "GOOGLE_AI_GEMINI_MODEL_ID"])
vertex_ai_setup: bool = is_service_setup_for_testing([
"GOOGLE_AI_CLOUD_PROJECT_ID",
"GOOGLE_AI_GEMINI_MODEL_ID",
"GOOGLE_AI_CLOUD_REGION",
])
onnx_setup: bool = is_service_setup_for_testing(
["ONNX_GEN_AI_TEXT_MODEL_FOLDER"], raise_if_not_set=False
) # Tests are optional for ONNX
pytestmark = pytest.mark.parametrize(
"service_id, execution_settings_kwargs, inputs, kwargs",
[
pytest.param(
"openai",
{},
["Repeat the word Hello once"],
{},
id="openai_text_completion",
),
pytest.param(
"hf_t2t",
{},
["translate English to Dutch: Hello"],
{},
id="huggingface_text_completion_translation",
),
pytest.param(
"hf_summ",
{},
[
"""Summarize: Whales are fully aquatic, open-ocean animals:
they can feed, mate, give birth, suckle and raise their young at sea.
Whales range in size from the 2.6 metres (8.5 ft) and 135 kilograms (298 lb)
dwarf sperm whale to the 29.9 metres (98 ft) and 190 tonnes (210 short tons) blue whale,
which is the largest known animal that has ever lived. The sperm whale is the largest
toothed predator on Earth. Several whale species exhibit sexual dimorphism,
in that the females are larger than males."""
],
{},
id="huggingface_text_completion_summarization",
),
pytest.param(
"hf_gen",
{},
["Hello, I like sleeping and "],
{},
id="huggingface_text_completion_generation",
),
pytest.param(
"ollama",
{},
["Repeat the word Hello once"],
{},
marks=(
pytest.mark.skip(
reason="Need local Ollama setup" if not ollama_setup else "Ollama responses are not always correct."
),
pytest.mark.ollama,
),
id="ollama_text_completion",
),
pytest.param(
"google_ai",
{},
["Repeat the word Hello once"],
{},
marks=[
pytest.mark.skip(reason="Skipping due to occasional throttling from Google AI."),
# pytest.mark.skipif(not google_ai_setup, reason="Need Google AI setup"),
],
id="google_ai_text_completion",
),
pytest.param(
"vertex_ai",
{},
["Repeat the word Hello once"],
{},
marks=pytest.mark.skipif(not vertex_ai_setup, reason="Need VertexAI setup"),
id="vertex_ai_text_completion",
),
pytest.param(
"onnx_gen_ai",
{},
["<|user|>Repeat the word Hello<|end|><|assistant|>"],
{},
marks=(
pytest.mark.skipif(not onnx_setup, reason="Need a Onnx Model setup"),
pytest.mark.onnx,
),
id="onnx_gen_ai_text_completion",
),
pytest.param(
"bedrock_anthropic_claude",
{},
["Repeat the word Hello once"],
{"streaming": False}, # Streaming is not supported for models from this provider
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_anthropic_claude_text_completion",
),
pytest.param(
"bedrock_cohere_command",
{},
["Repeat the word Hello once"],
{"streaming": False}, # Streaming is not supported for models from this provider
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_cohere_command_text_completion",
),
pytest.param(
"bedrock_ai21labs",
{},
["Repeat the word Hello once"],
{"streaming": False}, # Streaming is not supported for models from this provider
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_ai21labs_text_completion",
),
pytest.param(
"bedrock_meta_llama",
{},
["Repeat the word Hello once"],
{"streaming": False}, # Streaming is not supported for models from this provider
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_meta_llama_text_completion",
),
pytest.param(
"bedrock_mistralai",
{},
["Repeat the word Hello once"],
{"streaming": False}, # Streaming is not supported for models from this provider
marks=pytest.mark.skip(reason="Skipping due to occasional throttling from Bedrock."),
id="bedrock_mistralai_text_completion",
),
],
)
class TestTextCompletion(CompletionTestBase):
"""Test class for text completion"""
@override
@pytest.fixture(scope="class")
def services(self) -> dict[str, tuple[ServiceType | None, type[PromptExecutionSettings] | None]]:
"""Get the services to be tested"""
return {
"openai": (OpenAITextCompletion(), OpenAITextPromptExecutionSettings),
"ollama": (OllamaTextCompletion() if ollama_setup else None, OllamaTextPromptExecutionSettings),
"google_ai": (GoogleAITextCompletion() if google_ai_setup else None, GoogleAITextPromptExecutionSettings),
"vertex_ai": (
GoogleAITextCompletion(use_vertexai=True) if vertex_ai_setup else None,
GoogleAITextPromptExecutionSettings,
),
"hf_t2t": (
HuggingFaceTextCompletion(
service_id="patrickvonplaten/t5-tiny-random",
ai_model_id="patrickvonplaten/t5-tiny-random",
task="text2text-generation",
)
if hugging_face_setup
else None,
HuggingFacePromptExecutionSettings,
),
"hf_summ": (
HuggingFaceTextCompletion(
service_id="Falconsai/text_summarization",
ai_model_id="Falconsai/text_summarization",
task="summarization",
)
if hugging_face_setup
else None,
HuggingFacePromptExecutionSettings,
),
"hf_gen": (
HuggingFaceTextCompletion(
service_id="HuggingFaceM4/tiny-random-LlamaForCausalLM",
ai_model_id="HuggingFaceM4/tiny-random-LlamaForCausalLM",
task="text-generation",
)
if hugging_face_setup
else None,
HuggingFacePromptExecutionSettings,
),
"onnx_gen_ai": (
OnnxGenAITextCompletion() if onnx_setup else None,
OnnxGenAIPromptExecutionSettings,
),
# Amazon Bedrock supports models from multiple providers but requests to and responses from the models are
# inconsistent. So we need to test each model separately.
"bedrock_anthropic_claude": (
self._try_create_bedrock_text_completion_client("anthropic.claude-v2"),
BedrockTextPromptExecutionSettings,
),
"bedrock_cohere_command": (
self._try_create_bedrock_text_completion_client("cohere.command-text-v14"),
BedrockTextPromptExecutionSettings,
),
"bedrock_ai21labs": (
self._try_create_bedrock_text_completion_client("ai21.j2-mid-v1"),
BedrockTextPromptExecutionSettings,
),
"bedrock_meta_llama": (
self._try_create_bedrock_text_completion_client("meta.llama3-70b-instruct-v1:0"),
BedrockTextPromptExecutionSettings,
),
"bedrock_mistralai": (
self._try_create_bedrock_text_completion_client("mistral.mistral-7b-instruct-v0:2"),
BedrockTextPromptExecutionSettings,
),
}
async def get_text_completion_response(
self,
service: ServiceType,
execution_settings: PromptExecutionSettings,
prompt: str,
stream: bool,
) -> Any:
"""Get response from the service
Args:
kernel (Kernel): Kernel instance.
service (ChatCompletionClientBase): Chat completion service.
execution_settings (PromptExecutionSettings): Execution settings.
prompt (str): Input string.
stream (bool): Stream flag.
"""
assert isinstance(service, TextCompletionClientBase)
if stream:
response = service.get_streaming_text_content(
prompt=prompt,
settings=execution_settings,
)
parts: list[StreamingTextContent] = [part async for part in response if part is not None]
if parts:
return sum(parts[1:], parts[0])
raise AssertionError("No response")
return await service.get_text_content(
prompt=prompt,
settings=execution_settings,
)
return response
@override
async def test_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[str],
kwargs: dict[str, Any],
) -> None:
await self._test_helper(service_id, services, execution_settings_kwargs, inputs, False)
@override
async def test_streaming_completion(
self,
kernel: Kernel,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[str],
kwargs: dict[str, Any],
):
if "streaming" in kwargs and not kwargs["streaming"]:
pytest.skip("Skipping streaming test")
await self._test_helper(service_id, services, execution_settings_kwargs, inputs, True)
@override
def evaluate(self, test_target: Any, **kwargs):
print(test_target)
if isinstance(test_target, TextContent):
# Test is considered successful if the test_target is not empty
assert test_target.text, "Error: Empty test target"
return
raise AssertionError(f"Unexpected output: {test_target}, type: {type(test_target)}")
async def _test_helper(
self,
service_id: str,
services: dict[str, tuple[ServiceType, type[PromptExecutionSettings]]],
execution_settings_kwargs: dict[str, Any],
inputs: list[str],
stream: bool,
):
service, settings_type = services[service_id]
if not service:
pytest.skip(f"Setup not ready for {service_id if service_id else 'None'}")
for test_input in inputs:
response = await retry(
partial(
self.get_text_completion_response,
service=service,
execution_settings=settings_type(**execution_settings_kwargs),
prompt=test_input,
stream=stream,
),
retries=5,
name="text completions",
)
self.evaluate(response)
def _try_create_bedrock_text_completion_client(self, model_id: str) -> BedrockTextCompletion | None:
try:
return BedrockTextCompletion(model_id=model_id)
except Exception as ex:
from conftest import logger
logger.warning(ex)
# Returning None so that the test that uses this service will be skipped
return None