mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Python: allow default_headers configuration and users to pass in custom AzureOpenAI/OpenAI clients. (#3903)
### Motivation and Context Currently, users in Python have no ability to configure default_headers for the AzureOpenAI/OpenAI clients we use. Additionally, if a user wants to set up their own AzureOpenAI/OpenAI client, this isn't possible. This PR addresses #2895. <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> ### Description Allow users to set default_headers for Chat/Text/Embedding Completion classes for both OpenAI and AzureOpenAI. Additionally, allow users to pass in a customer OpenAI/AzureOpenAI client, if desired. Update tests accordingly. <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄 --------- Co-authored-by: Evan Mattson <[email protected]>
This commit is contained in:
co-authored by
Evan Mattson
parent
10e19986a8
commit
15f13b285d
Vendored
+6
-1
@@ -91,5 +91,10 @@
|
||||
},
|
||||
"java.debug.settings.onBuildFailureProceed": true,
|
||||
"java.compile.nullAnalysis.mode": "disabled",
|
||||
"dotnet.defaultSolution": "dotnet\\SK-dotnet.sln"
|
||||
"dotnet.defaultSolution": "dotnet\\SK-dotnet.sln",
|
||||
"python.testing.pytestArgs": [
|
||||
"python/tests"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true
|
||||
}
|
||||
@@ -3,3 +3,4 @@
|
||||
from typing import final
|
||||
|
||||
DEFAULT_AZURE_API_VERSION: final = "2023-05-15"
|
||||
USER_AGENT: final = "User-Agent"
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
|
||||
from logging import Logger
|
||||
from typing import Dict, Optional, Union, overload
|
||||
from typing import Dict, Mapping, Optional, Union, overload
|
||||
|
||||
from openai import AsyncAzureOpenAI
|
||||
from openai.lib.azure import AsyncAzureADTokenProvider
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.const import DEFAULT_AZURE_API_VERSION
|
||||
@@ -36,6 +37,7 @@ class AzureChatCompletion(
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -59,6 +61,8 @@ class AzureChatCompletion(
|
||||
The default value is "2023-05-15".
|
||||
ad_auth: Whether to use Azure Active Directory authentication. (Optional)
|
||||
The default value is False.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log: The logger instance to use. (Optional)
|
||||
logger: deprecated, use 'log' instead.
|
||||
"""
|
||||
@@ -72,6 +76,7 @@ class AzureChatCompletion(
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -93,10 +98,32 @@ class AzureChatCompletion(
|
||||
The default value is "2023-05-15".
|
||||
ad_auth: Whether to use Azure Active Directory authentication. (Optional)
|
||||
The default value is False.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log: The logger instance to use. (Optional)
|
||||
logger: deprecated, use 'log' instead.
|
||||
"""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
deployment_name: str,
|
||||
async_client: AsyncAzureOpenAI,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an AzureChatCompletion service.
|
||||
|
||||
Arguments:
|
||||
deployment_name: The name of the Azure deployment. This value
|
||||
will correspond to the custom name you chose for your deployment
|
||||
when you deployed a model. This value can be found under
|
||||
Resource Management > Deployments in the Azure portal or, alternatively,
|
||||
under Management > Deployments in Azure OpenAI Studio.
|
||||
async_client {AsyncAzureOpenAI} -- An existing client to use.
|
||||
log: The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
deployment_name: str,
|
||||
@@ -106,8 +133,10 @@ class AzureChatCompletion(
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
logger: Optional[Logger] = None,
|
||||
async_client: Optional[AsyncAzureOpenAI] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an AzureChatCompletion service.
|
||||
@@ -134,8 +163,11 @@ class AzureChatCompletion(
|
||||
The default value is "2023-05-15".
|
||||
ad_auth: Whether to use Azure Active Directory authentication. (Optional)
|
||||
The default value is False.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log: The logger instance to use. (Optional)
|
||||
logger: deprecated, use 'log' instead.
|
||||
async_client {Optional[AsyncAzureOpenAI]} -- An existing client to use. (Optional)
|
||||
"""
|
||||
if logger:
|
||||
logger.warning("The 'logger' argument is deprecated, use 'log' instead.")
|
||||
@@ -151,8 +183,10 @@ class AzureChatCompletion(
|
||||
api_key=api_key,
|
||||
ad_token=ad_token,
|
||||
ad_token_provider=ad_token_provider,
|
||||
default_headers=default_headers,
|
||||
log=log or logger,
|
||||
ai_model_type=OpenAIModelTypes.CHAT,
|
||||
async_client=async_client,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -163,7 +197,7 @@ class AzureChatCompletion(
|
||||
Arguments:
|
||||
settings: A dictionary of settings for the service.
|
||||
should contains keys: deployment_name, endpoint, api_key
|
||||
and optionally: api_version, ad_auth, log
|
||||
and optionally: api_version, ad_auth, default_headers, log
|
||||
"""
|
||||
return AzureChatCompletion(
|
||||
deployment_name=settings.get("deployment_name"),
|
||||
@@ -173,5 +207,6 @@ class AzureChatCompletion(
|
||||
api_key=settings.get("api_key"),
|
||||
ad_token=settings.get("ad_token"),
|
||||
ad_token_provider=settings.get("ad_token_provider"),
|
||||
default_headers=settings.get("default_headers"),
|
||||
log=settings.get("log"),
|
||||
)
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
import json
|
||||
from logging import Logger
|
||||
from typing import Any, Awaitable, Callable, Dict, Optional, Union
|
||||
from typing import Any, Awaitable, Callable, Dict, Mapping, Optional, Union
|
||||
|
||||
from openai import AsyncAzureOpenAI
|
||||
from pydantic import validate_call
|
||||
|
||||
from semantic_kernel.connectors.ai.ai_exception import AIException
|
||||
from semantic_kernel.connectors.ai.open_ai.const import DEFAULT_AZURE_API_VERSION
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
DEFAULT_AZURE_API_VERSION,
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.services.open_ai_handler import (
|
||||
OpenAIHandler,
|
||||
OpenAIModelTypes,
|
||||
@@ -31,56 +34,82 @@ class AzureOpenAIConfigBase(OpenAIHandler):
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[Callable[[], Union[str, Awaitable[str]]]] = None,
|
||||
default_headers: Union[Mapping[str, str], None] = None,
|
||||
log: Optional[Logger] = None,
|
||||
async_client: Optional[AsyncAzureOpenAI] = None,
|
||||
) -> None:
|
||||
# TODO: add SK user-agent here
|
||||
if not api_key and not ad_token and not ad_token_provider:
|
||||
raise AIException(
|
||||
AIException.ErrorCodes.InvalidConfiguration,
|
||||
"Please provide either api_key, ad_token or ad_token_provider",
|
||||
)
|
||||
if base_url:
|
||||
client = AsyncAzureOpenAI(
|
||||
base_url=str(base_url),
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
azure_ad_token=ad_token,
|
||||
azure_ad_token_provider=ad_token_provider,
|
||||
default_headers={"User-Agent": json.dumps(APP_INFO)}
|
||||
if APP_INFO
|
||||
else None,
|
||||
)
|
||||
else:
|
||||
if not endpoint:
|
||||
"""Internal class for configuring a connection to an Azure OpenAI service.
|
||||
|
||||
Arguments:
|
||||
deployment_name {str} -- Name of the deployment.
|
||||
ai_model_type {OpenAIModelTypes} -- The type of OpenAI model to deploy.
|
||||
endpoint {Optional[HttpsUrl]} -- The specific endpoint URL for the deployment. (Optional)
|
||||
base_url {Optional[HttpsUrl]} -- The base URL for Azure services. (Optional)
|
||||
api_version {str} -- Azure API version. Defaults to the defined DEFAULT_AZURE_API_VERSION.
|
||||
api_key {Optional[str]} -- API key for Azure services. (Optional)
|
||||
ad_token {Optional[str]} -- Azure AD token for authentication. (Optional)
|
||||
ad_token_provider {Optional[Callable[[], Union[str, Awaitable[str]]]]} -- A callable
|
||||
or coroutine function providing Azure AD tokens. (Optional)
|
||||
default_headers {Union[Mapping[str, str], None]} -- Default headers for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- Logger instance for logging purposes. (Optional)
|
||||
async_client {Optional[AsyncAzureOpenAI]} -- An existing client to use. (Optional)
|
||||
|
||||
The `validate_call` decorator is used with a configuration that allows arbitrary types.
|
||||
This is necessary for types like `HttpsUrl` and `OpenAIModelTypes`.
|
||||
"""
|
||||
# Merge APP_INFO into the headers if it exists
|
||||
merged_headers = default_headers.copy() if default_headers else {}
|
||||
if APP_INFO:
|
||||
merged_headers[USER_AGENT] = json.dumps(APP_INFO)
|
||||
|
||||
if not async_client:
|
||||
if not api_key and not ad_token and not ad_token_provider:
|
||||
raise AIException(
|
||||
AIException.ErrorCodes.InvalidConfiguration,
|
||||
"Please provide either base_url or endpoint",
|
||||
"Please provide either api_key, ad_token or ad_token_provider",
|
||||
)
|
||||
client = AsyncAzureOpenAI(
|
||||
azure_endpoint=endpoint,
|
||||
azure_deployment=deployment_name,
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
azure_ad_token=ad_token,
|
||||
azure_ad_token_provider=ad_token_provider,
|
||||
default_headers={"User-Agent": json.dumps(APP_INFO)}
|
||||
if APP_INFO
|
||||
else None,
|
||||
)
|
||||
if base_url:
|
||||
async_client = AsyncAzureOpenAI(
|
||||
base_url=str(base_url),
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
azure_ad_token=ad_token,
|
||||
azure_ad_token_provider=ad_token_provider,
|
||||
default_headers=merged_headers,
|
||||
)
|
||||
else:
|
||||
if not endpoint:
|
||||
raise AIException(
|
||||
AIException.ErrorCodes.InvalidConfiguration,
|
||||
"Please provide either base_url or endpoint",
|
||||
)
|
||||
async_client = AsyncAzureOpenAI(
|
||||
azure_endpoint=endpoint,
|
||||
azure_deployment=deployment_name,
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
azure_ad_token=ad_token,
|
||||
azure_ad_token_provider=ad_token_provider,
|
||||
default_headers=merged_headers,
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
ai_model_id=deployment_name,
|
||||
log=log,
|
||||
client=client,
|
||||
client=async_client,
|
||||
ai_model_type=ai_model_type,
|
||||
)
|
||||
|
||||
def to_dict(self) -> Dict[str, str]:
|
||||
client_settings = {
|
||||
"base_url": self.client.base_url,
|
||||
"base_url": str(self.client.base_url),
|
||||
"api_version": self.client._custom_query["api-version"],
|
||||
"api_key": self.client.api_key,
|
||||
"ad_token": self.client._azure_ad_token,
|
||||
"ad_token_provider": self.client._azure_ad_token_provider,
|
||||
"default_headers": {
|
||||
k: v for k, v in self.client.default_headers.items() if k != USER_AGENT
|
||||
},
|
||||
}
|
||||
base = self.model_dump(
|
||||
exclude={
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
|
||||
from logging import Logger
|
||||
from typing import Dict, Optional, overload
|
||||
from typing import Dict, Mapping, Optional, overload
|
||||
|
||||
from openai import AsyncAzureOpenAI
|
||||
from openai.lib.azure import AsyncAzureADTokenProvider
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.const import DEFAULT_AZURE_API_VERSION
|
||||
@@ -29,6 +30,7 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -50,6 +52,8 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
The default value is "2023-05-15".
|
||||
ad_auth: Whether to use Azure Active Directory authentication. (Optional)
|
||||
The default value is False.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log: The logger instance to use. (Optional)
|
||||
logger: deprecated, use 'log' instead.
|
||||
"""
|
||||
@@ -63,6 +67,7 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -84,10 +89,32 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
The default value is "2023-05-15".
|
||||
ad_auth: Whether to use Azure Active Directory authentication. (Optional)
|
||||
The default value is False.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log: The logger instance to use. (Optional)
|
||||
logger: deprecated, use 'log' instead.
|
||||
"""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
deployment_name: str,
|
||||
async_client: AsyncAzureOpenAI,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an AzureChatCompletion service.
|
||||
|
||||
Arguments:
|
||||
deployment_name: The name of the Azure deployment. This value
|
||||
will correspond to the custom name you chose for your deployment
|
||||
when you deployed a model. This value can be found under
|
||||
Resource Management > Deployments in the Azure portal or, alternatively,
|
||||
under Management > Deployments in Azure OpenAI Studio.
|
||||
async_client {AsyncAzureOpenAI} -- An existing client to use.
|
||||
log: The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
deployment_name: Optional[str] = None,
|
||||
@@ -97,8 +124,10 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
logger: Optional[Logger] = None,
|
||||
async_client: Optional[AsyncAzureOpenAI] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an AzureTextCompletion service.
|
||||
@@ -119,8 +148,11 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
The default value is "2023-03-15-preview".
|
||||
ad_auth: Whether to use Azure Active Directory authentication. (Optional)
|
||||
The default value is False.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log: The logger instance to use. (Optional)
|
||||
logger: deprecated, use 'log' instead.
|
||||
async_client {Optional[AsyncAzureOpenAI]} -- An existing client to use.
|
||||
"""
|
||||
if logger:
|
||||
logger.warning("The 'logger' argument is deprecated, use 'log' instead.")
|
||||
@@ -132,8 +164,10 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
api_key=api_key,
|
||||
ad_token=ad_token,
|
||||
ad_token_provider=ad_token_provider,
|
||||
default_headers=default_headers,
|
||||
log=log or logger,
|
||||
ai_model_type=OpenAIModelTypes.TEXT,
|
||||
async_client=async_client,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -155,5 +189,6 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
|
||||
api_key=settings["api_key"],
|
||||
ad_token=settings.get("ad_token"),
|
||||
ad_token_provider=settings.get("ad_token_provider"),
|
||||
default_headers=settings.get("default_headers"),
|
||||
log=settings.get("log"),
|
||||
)
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
|
||||
from logging import Logger
|
||||
from typing import Dict, Optional
|
||||
from typing import Dict, Mapping, Optional, overload
|
||||
|
||||
from openai import AsyncAzureOpenAI
|
||||
from openai.lib.azure import AsyncAzureADTokenProvider
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.const import DEFAULT_AZURE_API_VERSION
|
||||
@@ -21,16 +22,38 @@ from semantic_kernel.connectors.ai.open_ai.services.open_ai_text_embedding_base
|
||||
class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
|
||||
"""Azure Text Embedding class."""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
deployment_name: str,
|
||||
endpoint: str,
|
||||
async_client: AsyncAzureOpenAI,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an AzureChatCompletion service.
|
||||
|
||||
Arguments:
|
||||
deployment_name: The name of the Azure deployment. This value
|
||||
will correspond to the custom name you chose for your deployment
|
||||
when you deployed a model. This value can be found under
|
||||
Resource Management > Deployments in the Azure portal or, alternatively,
|
||||
under Management > Deployments in Azure OpenAI Studio.
|
||||
async_client {AsyncAzureOpenAI} -- An existing client to use.
|
||||
log: The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
deployment_name: str,
|
||||
endpoint: Optional[str] = None,
|
||||
api_version: str = DEFAULT_AZURE_API_VERSION,
|
||||
api_key: Optional[str] = None,
|
||||
ad_token: Optional[str] = None,
|
||||
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
logger: Optional[Logger] = None,
|
||||
async_client: Optional[AsyncAzureOpenAI] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an AzureTextEmbedding service.
|
||||
@@ -46,15 +69,20 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
|
||||
:param endpoint: The endpoint of the Azure deployment. This value
|
||||
can be found in the Keys & Endpoint section when examining
|
||||
your resource from the Azure portal.
|
||||
:param api_version: The API version to use. (Optional)
|
||||
The default value is "2023-05-15".
|
||||
:param api_key: The API key for the Azure deployment. This value can be
|
||||
found in the Keys & Endpoint section when examining your resource in
|
||||
the Azure portal. You can use either KEY1 or KEY2.
|
||||
:param api_version: The API version to use. (Optional)
|
||||
The default value is "2023-05-15".
|
||||
:param log: The logger instance to use. (Optional)
|
||||
:param logger: Deprecated, please use log instead. (Optional)
|
||||
:param ad_token : The Azure AD token for authentication. (Optional)
|
||||
:param ad_auth: Whether to use Azure Active Directory authentication.
|
||||
(Optional) The default value is False.
|
||||
:param default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
:param log: The logger instance to use. (Optional)
|
||||
:param logger: Deprecated, please use log instead. (Optional)
|
||||
:param async_client: An existing client to use. (Optional)
|
||||
|
||||
"""
|
||||
if logger:
|
||||
logger.warning("The 'logger' argument is deprecated, use 'log' instead.")
|
||||
@@ -65,8 +93,10 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
|
||||
api_key=api_key,
|
||||
ad_token=ad_token,
|
||||
ad_token_provider=ad_token_provider,
|
||||
default_headers=default_headers,
|
||||
log=log or logger,
|
||||
ai_model_type=OpenAIModelTypes.EMBEDDING,
|
||||
async_client=async_client,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -86,5 +116,6 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
|
||||
api_version=settings.get("api_version", DEFAULT_AZURE_API_VERSION),
|
||||
ad_token=settings.get("ad_token"),
|
||||
ad_token_provider=settings.get("ad_token_provider"),
|
||||
default_headers=settings.get("default_headers"),
|
||||
log=settings.get("log"),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from logging import Logger
|
||||
from typing import Dict, Optional
|
||||
from typing import Dict, Mapping, Optional, overload
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion_base import (
|
||||
OpenAIChatCompletionBase,
|
||||
@@ -22,11 +24,11 @@ class OpenAIChatCompletion(
|
||||
):
|
||||
"""OpenAI Chat completion class."""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: str,
|
||||
org_id: Optional[str] = None,
|
||||
async_client: AsyncOpenAI,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -35,19 +37,89 @@ class OpenAIChatCompletion(
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {str} -- OpenAI API key, see
|
||||
async_client {AsyncOpenAI} -- An existing client to use.
|
||||
log: The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: Optional[str] = None,
|
||||
org_id: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an OpenAIChatCompletion service.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {Optional[str]} -- OpenAI API key, see
|
||||
https://platform.openai.com/account/api-keys
|
||||
org_id {Optional[str]} -- OpenAI organization ID.
|
||||
This is usually optional unless your
|
||||
account belongs to multiple organizations.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an OpenAIChatCompletion service.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {Optional[str]} -- OpenAI API key, see
|
||||
https://platform.openai.com/account/api-keys
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: Optional[str] = None,
|
||||
org_id: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
async_client: Optional[AsyncOpenAI] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an OpenAIChatCompletion service.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {Optional[str]} -- OpenAI API key, see
|
||||
https://platform.openai.com/account/api-keys
|
||||
org_id {Optional[str]} -- OpenAI organization ID.
|
||||
This is usually optional unless your
|
||||
account belongs to multiple organizations.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- The logger instance to use. (Optional)
|
||||
async_client {Optional[AsyncOpenAI]} -- An existing client to use. (Optional)
|
||||
"""
|
||||
super().__init__(
|
||||
ai_model_id=ai_model_id,
|
||||
api_key=api_key,
|
||||
org_id=org_id,
|
||||
ai_model_type=OpenAIModelTypes.CHAT,
|
||||
default_headers=default_headers,
|
||||
log=log,
|
||||
async_client=async_client,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -63,5 +135,6 @@ class OpenAIChatCompletion(
|
||||
ai_model_id=settings["ai_model_id"],
|
||||
api_key=settings["api_key"],
|
||||
org_id=settings.get("org_id"),
|
||||
default_headers=settings.get("default_headers"),
|
||||
log=settings.get("log"),
|
||||
)
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import json
|
||||
from logging import Logger
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any, Dict, Mapping, Optional
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
from pydantic import Field, validate_call
|
||||
|
||||
from semantic_kernel.connectors.ai.ai_exception import AIException
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.services.open_ai_handler import (
|
||||
OpenAIHandler,
|
||||
)
|
||||
@@ -20,20 +25,53 @@ class OpenAIConfigBase(OpenAIHandler):
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str = Field(min_length=1),
|
||||
api_key: str = Field(min_length=1),
|
||||
api_key: Optional[str] = Field(min_length=1),
|
||||
ai_model_type: Optional[OpenAIModelTypes] = OpenAIModelTypes.CHAT,
|
||||
org_id: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
async_client: Optional[AsyncOpenAI] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
# TODO: add SK user-agent here
|
||||
client = AsyncOpenAI(
|
||||
api_key=api_key,
|
||||
organization=org_id,
|
||||
default_headers={"User-Agent": APP_INFO},
|
||||
)
|
||||
"""Initialize a client for OpenAI services.
|
||||
|
||||
This constructor sets up a client to interact with OpenAI's API, allowing for
|
||||
different types of AI model interactions, like chat or text completion.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model identifier. Must be non-empty.
|
||||
Default to a preset value.
|
||||
api_key {Optional[str]} -- OpenAI API key for authentication.
|
||||
Must be non-empty. (Optional)
|
||||
ai_model_type {Optional[OpenAIModelTypes]} -- The type of OpenAI
|
||||
model to interact with. Defaults to CHAT.
|
||||
org_id {Optional[str]} -- OpenAI organization ID. This is optional
|
||||
unless the account belongs to multiple organizations.
|
||||
default_headers {Optional[Mapping[str, str]]} -- Default headers
|
||||
for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- Logger instance for logging purposes. (Optional)
|
||||
|
||||
"""
|
||||
|
||||
# Merge APP_INFO into the headers if it exists
|
||||
merged_headers = default_headers.copy() if default_headers else {}
|
||||
if APP_INFO:
|
||||
merged_headers[USER_AGENT] = json.dumps(APP_INFO)
|
||||
|
||||
if not async_client:
|
||||
if not api_key:
|
||||
raise AIException(
|
||||
AIException.ErrorCodes.InvalidConfiguration,
|
||||
"Please provide an api_key",
|
||||
)
|
||||
async_client = AsyncOpenAI(
|
||||
api_key=api_key,
|
||||
organization=org_id,
|
||||
default_headers=merged_headers,
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
ai_model_id=ai_model_id,
|
||||
client=client,
|
||||
client=async_client,
|
||||
log=log,
|
||||
ai_model_type=ai_model_type,
|
||||
)
|
||||
@@ -44,6 +82,9 @@ class OpenAIConfigBase(OpenAIHandler):
|
||||
"""
|
||||
client_settings = {
|
||||
"api_key": self.client.api_key,
|
||||
"default_headers": {
|
||||
k: v for k, v in self.client.default_headers.items() if k != USER_AGENT
|
||||
},
|
||||
}
|
||||
if self.client.organization:
|
||||
client_settings["org_id"] = self.client.organization
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from logging import Logger
|
||||
from typing import Dict, Optional
|
||||
from typing import Dict, Mapping, Optional, overload
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.services.open_ai_config_base import (
|
||||
OpenAIConfigBase,
|
||||
@@ -17,11 +19,11 @@ from semantic_kernel.connectors.ai.open_ai.services.open_ai_text_completion_base
|
||||
class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
|
||||
"""OpenAI Text Completion class."""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: str,
|
||||
org_id: Optional[str] = None,
|
||||
async_client: AsyncOpenAI,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -30,19 +32,89 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {str} -- OpenAI API key, see
|
||||
https://platform.openai.com/account/api-keys
|
||||
async_client {AsyncOpenAI} -- An existing client to use.
|
||||
log: The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: Optional[str] = None,
|
||||
org_id: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an OpenAITextCompletion service.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {Optional[str]} -- OpenAI API key, see
|
||||
https://platform.openai.com/account/api-keys (Optional)
|
||||
org_id {Optional[str]} -- OpenAI organization ID.
|
||||
This is usually optional unless your
|
||||
account belongs to multiple organizations.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an OpenAITextCompletion service.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {Optional[str]} -- OpenAI API key, see
|
||||
https://platform.openai.com/account/api-keys (Optional)
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: Optional[str] = None,
|
||||
org_id: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
async_client: Optional[AsyncOpenAI] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an OpenAITextCompletion service.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
api_key {Optional[str]} -- OpenAI API key, see
|
||||
https://platform.openai.com/account/api-keys (Optional)
|
||||
org_id {Optional[str]} -- OpenAI organization ID.
|
||||
This is usually optional unless your
|
||||
account belongs to multiple organizations.
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- The logger instance to use. (Optional)
|
||||
async_client {Optional[AsyncOpenAI]} -- An existing client to use. (Optional)
|
||||
"""
|
||||
super().__init__(
|
||||
ai_model_id=ai_model_id,
|
||||
api_key=api_key,
|
||||
org_id=org_id,
|
||||
log=log,
|
||||
ai_model_type=OpenAIModelTypes.TEXT,
|
||||
default_headers=default_headers,
|
||||
async_client=async_client,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -58,5 +130,6 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
|
||||
ai_model_id=settings["ai_model_id"],
|
||||
api_key=settings["api_key"],
|
||||
org_id=settings.get("org_id"),
|
||||
default_headers=settings.get("default_headers"),
|
||||
log=settings.get("log"),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from logging import Logger
|
||||
from typing import Dict, Optional
|
||||
from typing import Dict, Mapping, Optional, overload
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.services.open_ai_config_base import (
|
||||
OpenAIConfigBase,
|
||||
@@ -17,12 +19,31 @@ from semantic_kernel.connectors.ai.open_ai.services.open_ai_text_embedding_base
|
||||
class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
|
||||
"""OpenAI Text Embedding class."""
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: str,
|
||||
org_id: Optional[str] = None,
|
||||
async_client: AsyncOpenAI,
|
||||
log: Optional[Logger] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize an OpenAITextEmbedding service.
|
||||
|
||||
Arguments:
|
||||
ai_model_id {str} -- OpenAI model name, see
|
||||
https://platform.openai.com/docs/models
|
||||
async_client {AsyncOpenAI} -- An existing client to use.
|
||||
log: The logger instance to use. (Optional)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ai_model_id: str,
|
||||
api_key: Optional[str] = None,
|
||||
org_id: Optional[str] = None,
|
||||
default_headers: Optional[Mapping[str, str]] = None,
|
||||
log: Optional[Logger] = None,
|
||||
async_client: Optional[AsyncOpenAI] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initializes a new instance of the OpenAITextCompletion class.
|
||||
@@ -35,13 +56,19 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
|
||||
org_id {Optional[str]} -- OpenAI organization ID.
|
||||
This is usually optional unless your
|
||||
account belongs to multiple organizations.
|
||||
default_headers {Optional[Mapping[str,str]]}: The default headers mapping of string keys to
|
||||
string values for HTTP requests. (Optional)
|
||||
log {Optional[Logger]} -- The logger instance to use. (Optional)
|
||||
async_client {Optional[AsyncOpenAI]} -- An existing client to use. (Optional)
|
||||
"""
|
||||
super().__init__(
|
||||
ai_model_id=ai_model_id,
|
||||
api_key=api_key,
|
||||
ai_model_type=OpenAIModelTypes.EMBEDDING,
|
||||
org_id=org_id,
|
||||
default_headers=default_headers,
|
||||
log=log,
|
||||
async_client=async_client,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -57,5 +84,6 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
|
||||
ai_model_id=settings["ai_model_id"],
|
||||
api_key=settings["api_key"],
|
||||
org_id=settings.get("org_id"),
|
||||
default_headers=settings.get("default_headers"),
|
||||
log=settings.get("log"),
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from logging import Logger
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
"""Base class for text completion AI services."""
|
||||
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from logging import Logger
|
||||
from typing import TYPE_CHECKING, AsyncGenerator, List, Optional, Union
|
||||
|
||||
@@ -13,6 +13,9 @@ from azure.search.documents.indexes.models import (
|
||||
)
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.memory.memory_record import MemoryRecord
|
||||
|
||||
SEARCH_FIELD_ID = "Id"
|
||||
@@ -83,7 +86,7 @@ def get_search_index_async_client(
|
||||
if azure_credential is None and token_credential is None:
|
||||
raise ValueError("Error: Azure Cognitive Search credentials not set.")
|
||||
|
||||
sk_headers = {"User-Agent": "Semantic-Kernel"}
|
||||
sk_headers = {USER_AGENT: "Semantic-Kernel"}
|
||||
|
||||
if azure_credential:
|
||||
return SearchIndexClient(
|
||||
|
||||
@@ -11,6 +11,9 @@ from openapi_core.exceptions import OpenAPIError
|
||||
from prance import ResolvingParser
|
||||
|
||||
from semantic_kernel import Kernel, SKContext
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.connectors.telemetry import HTTP_USER_AGENT
|
||||
from semantic_kernel.orchestration.sk_function_base import SKFunctionBase
|
||||
from semantic_kernel.skill_definition import sk_function, sk_function_context_parameter
|
||||
@@ -128,8 +131,8 @@ class RestApiOperation:
|
||||
processed_headers["Content-Type"] = content_type
|
||||
processed_payload = request_body
|
||||
|
||||
processed_headers["User-Agent"] = " ".join(
|
||||
(HTTP_USER_AGENT, processed_headers.get("User-Agent", ""))
|
||||
processed_headers[USER_AGENT] = " ".join(
|
||||
(HTTP_USER_AGENT, processed_headers.get(USER_AGENT, ""))
|
||||
).rstrip()
|
||||
|
||||
req = PreparedRestApiRequest(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from openai import AsyncAzureOpenAI
|
||||
from test_utils import retry
|
||||
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
@@ -49,6 +50,56 @@ async def test_azure_e2e_chat_completion_with_skill(
|
||||
assert len(output) < 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_e2e_chat_completion_with_skill_and_provided_client(
|
||||
setup_tldr_function_for_oai_models, get_aoai_config
|
||||
):
|
||||
kernel, sk_prompt, text_to_summarize = setup_tldr_function_for_oai_models
|
||||
|
||||
_, api_key, endpoint = get_aoai_config
|
||||
|
||||
if "Python_Integration_Tests" in os.environ:
|
||||
deployment_name = os.environ["AzureOpenAIChat__DeploymentName"]
|
||||
else:
|
||||
deployment_name = "gpt-35-turbo"
|
||||
|
||||
print("* Service: Azure OpenAI Chat Completion")
|
||||
print(f"* Endpoint: {endpoint}")
|
||||
print(f"* Deployment: {deployment_name}")
|
||||
|
||||
client = AsyncAzureOpenAI(
|
||||
azure_endpoint=endpoint,
|
||||
azure_deployment=deployment_name,
|
||||
api_key=api_key,
|
||||
api_version="2023-05-15",
|
||||
default_headers={"Test-User-X-ID": "test"},
|
||||
)
|
||||
|
||||
# Configure LLM service
|
||||
kernel.add_chat_service(
|
||||
"chat_completion",
|
||||
sk_oai.AzureChatCompletion(
|
||||
deployment_name=deployment_name,
|
||||
async_client=client,
|
||||
),
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_semantic_function(
|
||||
sk_prompt, max_tokens=200, temperature=0, top_p=0.5
|
||||
)
|
||||
|
||||
summary = await retry(
|
||||
lambda: kernel.run_async(tldr_function, input_str=text_to_summarize)
|
||||
)
|
||||
output = str(summary).strip()
|
||||
print(f"TLDR using input string: '{output}'")
|
||||
assert "First Law" not in output and (
|
||||
"human" in output or "Human" in output or "preserve" in output
|
||||
)
|
||||
assert len(output) < 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oai_chat_stream_service_with_skills(
|
||||
setup_tldr_function_for_oai_models, get_aoai_config
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from openai import AsyncAzureOpenAI
|
||||
from test_utils import retry
|
||||
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
@@ -51,6 +52,56 @@ async def test_azure_e2e_text_completion_with_skill(
|
||||
assert len(output) < 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_e2e_text_completion_with_skill_with_provided_client(
|
||||
setup_tldr_function_for_oai_models, get_aoai_config
|
||||
):
|
||||
kernel, sk_prompt, text_to_summarize = setup_tldr_function_for_oai_models
|
||||
|
||||
_, api_key, endpoint = get_aoai_config
|
||||
|
||||
if "Python_Integration_Tests" in os.environ:
|
||||
deployment_name = os.environ["AzureOpenAI__DeploymentName"]
|
||||
else:
|
||||
deployment_name = "text-davinci-003"
|
||||
|
||||
print("* Service: Azure OpenAI Text Completion")
|
||||
print(f"* Endpoint: {endpoint}")
|
||||
print(f"* Deployment: {deployment_name}")
|
||||
|
||||
client = AsyncAzureOpenAI(
|
||||
azure_endpoint=endpoint,
|
||||
azure_deployment=deployment_name,
|
||||
api_key=api_key,
|
||||
api_version="2023-05-15",
|
||||
default_headers={"Test-User-X-ID": "test"},
|
||||
)
|
||||
|
||||
# Configure LLM service
|
||||
kernel.add_text_completion_service(
|
||||
"text_completion",
|
||||
sk_oai.AzureTextCompletion(
|
||||
deployment_name=deployment_name,
|
||||
async_client=client,
|
||||
),
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_semantic_function(
|
||||
sk_prompt, max_tokens=200, temperature=0, top_p=0.5
|
||||
)
|
||||
|
||||
summary = await retry(
|
||||
lambda: kernel.run_async(tldr_function, input_str=text_to_summarize)
|
||||
)
|
||||
output = str(summary).strip()
|
||||
print(f"TLDR using input string: '{output}'")
|
||||
assert "First Law" not in output and (
|
||||
"human" in output or "Human" in output or "preserve" in output
|
||||
)
|
||||
assert len(output) < 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oai_text_stream_completion_with_skills(
|
||||
setup_tldr_function_for_oai_models, get_aoai_config
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import pytest
|
||||
from openai import AsyncOpenAI
|
||||
from test_utils import retry
|
||||
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
@@ -39,3 +40,44 @@ async def test_oai_chat_service_with_skills(
|
||||
"human" in output or "Human" in output or "preserve" in output
|
||||
)
|
||||
assert len(output) < 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oai_chat_service_with_skills_with_provided_client(
|
||||
setup_tldr_function_for_oai_models, get_oai_config
|
||||
):
|
||||
kernel, sk_prompt, text_to_summarize = setup_tldr_function_for_oai_models
|
||||
|
||||
api_key, org_id = get_oai_config
|
||||
|
||||
print("* Service: OpenAI Chat Completion")
|
||||
print("* Endpoint: OpenAI")
|
||||
print("* Model: gpt-3.5-turbo")
|
||||
|
||||
client = AsyncOpenAI(
|
||||
api_key=api_key,
|
||||
organization=org_id,
|
||||
)
|
||||
|
||||
kernel.add_chat_service(
|
||||
"chat-gpt",
|
||||
sk_oai.OpenAIChatCompletion(
|
||||
ai_model_id="gpt-3.5-turbo",
|
||||
async_client=client,
|
||||
),
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_semantic_function(
|
||||
sk_prompt, max_tokens=200, temperature=0, top_p=0.5
|
||||
)
|
||||
|
||||
summary = await retry(
|
||||
lambda: kernel.run_async(tldr_function, input_str=text_to_summarize)
|
||||
)
|
||||
output = str(summary).strip()
|
||||
print(f"TLDR using input string: '{output}'")
|
||||
assert "First Law" not in output and (
|
||||
"human" in output or "Human" in output or "preserve" in output
|
||||
)
|
||||
assert len(output) < 100
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import pytest
|
||||
from openai import AsyncOpenAI
|
||||
from test_utils import retry
|
||||
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
@@ -39,3 +40,44 @@ async def test_oai_text_completion_with_skills(
|
||||
"human" in output or "Human" in output or "preserve" in output
|
||||
)
|
||||
assert len(output) < 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oai_text_completion_with_skills_with_provided_client(
|
||||
setup_tldr_function_for_oai_models, get_oai_config
|
||||
):
|
||||
kernel, sk_prompt, text_to_summarize = setup_tldr_function_for_oai_models
|
||||
|
||||
api_key, org_id = get_oai_config
|
||||
|
||||
print("* Service: OpenAI Text Completion")
|
||||
print("* Endpoint: OpenAI")
|
||||
print("* Model: text-davinci-003")
|
||||
|
||||
client = AsyncOpenAI(
|
||||
api_key=api_key,
|
||||
organization=org_id,
|
||||
)
|
||||
|
||||
kernel.add_chat_service(
|
||||
"text-davinci-003",
|
||||
sk_oai.OpenAITextCompletion(
|
||||
ai_model_id="text-davinci-003",
|
||||
async_client=client,
|
||||
),
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_semantic_function(
|
||||
sk_prompt, max_tokens=200, temperature=0, top_p=0.5
|
||||
)
|
||||
|
||||
summary = await retry(
|
||||
lambda: kernel.run_async(tldr_function, input_str=text_to_summarize)
|
||||
)
|
||||
output = str(summary).strip()
|
||||
print(f"TLDR using input string: '{output}'")
|
||||
assert "First Law" not in output and (
|
||||
"human" in output or "Human" in output or "preserve" in output
|
||||
)
|
||||
assert len(output) < 100
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from openai import AsyncAzureOpenAI
|
||||
|
||||
import semantic_kernel as sk
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
@@ -40,6 +41,47 @@ async def test_azure_text_embedding_service(create_kernel, get_aoai_config):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_text_embedding_service_with_provided_client(
|
||||
create_kernel, get_aoai_config
|
||||
):
|
||||
kernel = create_kernel
|
||||
|
||||
_, api_key, endpoint = get_aoai_config
|
||||
|
||||
if "Python_Integration_Tests" in os.environ:
|
||||
deployment_name = os.environ["AzureOpenAIEmbeddings__DeploymentName"]
|
||||
else:
|
||||
deployment_name = "text-embedding-ada-002"
|
||||
|
||||
client = AsyncAzureOpenAI(
|
||||
azure_endpoint=endpoint,
|
||||
azure_deployment=deployment_name,
|
||||
api_key=api_key,
|
||||
api_version="2023-05-15",
|
||||
default_headers={"Test-User-X-ID": "test"},
|
||||
)
|
||||
|
||||
kernel.add_text_embedding_generation_service(
|
||||
"aoai-ada-2",
|
||||
sk_oai.AzureTextEmbedding(
|
||||
deployment_name=deployment_name,
|
||||
async_client=client,
|
||||
),
|
||||
)
|
||||
kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())
|
||||
|
||||
await kernel.memory.save_information_async(
|
||||
"test", id="info1", text="this is a test"
|
||||
)
|
||||
await kernel.memory.save_reference_async(
|
||||
"test",
|
||||
external_id="info1",
|
||||
text="this is a test",
|
||||
external_source_name="external source",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_batch_azure_embeddings(get_aoai_config):
|
||||
# Configure LLM service
|
||||
@@ -49,7 +91,7 @@ async def test_batch_azure_embeddings(get_aoai_config):
|
||||
deployment_name = os.environ["AzureOpenAIEmbeddings__DeploymentName"]
|
||||
|
||||
else:
|
||||
deployment_name = "text-embedding-ada-002"
|
||||
deployment_name = "ada-002"
|
||||
|
||||
embeddings_service = sk_oai.AzureTextEmbedding(
|
||||
deployment_name=deployment_name,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import pytest
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
import semantic_kernel as sk
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
@@ -27,3 +28,36 @@ async def test_oai_embedding_service(create_kernel, get_oai_config):
|
||||
text="this is a test",
|
||||
external_source_name="external source",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oai_embedding_service_with_provided_client(
|
||||
create_kernel, get_oai_config
|
||||
):
|
||||
kernel = create_kernel
|
||||
|
||||
api_key, org_id = get_oai_config
|
||||
|
||||
client = AsyncOpenAI(
|
||||
api_key=api_key,
|
||||
organization=org_id,
|
||||
)
|
||||
|
||||
kernel.add_text_embedding_generation_service(
|
||||
"oai-ada-2",
|
||||
sk_oai.OpenAITextEmbedding(
|
||||
ai_model_id="text-embedding-ada-002",
|
||||
async_client=client,
|
||||
),
|
||||
)
|
||||
kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())
|
||||
|
||||
await kernel.memory.save_information_async(
|
||||
"test", id="info1", text="this is a test"
|
||||
)
|
||||
await kernel.memory.save_reference_async(
|
||||
"test",
|
||||
external_id="info1",
|
||||
text="this is a test",
|
||||
external_source_name="external source",
|
||||
)
|
||||
|
||||
@@ -13,6 +13,9 @@ from semantic_kernel.connectors.ai.chat_completion_client_base import (
|
||||
ChatCompletionClientBase,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.chat_request_settings import ChatRequestSettings
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import (
|
||||
AzureChatCompletion,
|
||||
)
|
||||
@@ -47,19 +50,25 @@ def test_azure_chat_completion_init_base_url() -> None:
|
||||
api_version = "2023-03-15-preview"
|
||||
logger = Logger("test_logger")
|
||||
|
||||
# Test successful initialization
|
||||
# Custom header for testing
|
||||
default_headers = {"X-Unit-Test": "test-guid"}
|
||||
|
||||
azure_chat_completion = AzureChatCompletion(
|
||||
deployment_name=deployment_name,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
api_version=api_version,
|
||||
log=logger,
|
||||
default_headers=default_headers,
|
||||
)
|
||||
|
||||
assert azure_chat_completion.client is not None
|
||||
assert isinstance(azure_chat_completion.client, AsyncAzureOpenAI)
|
||||
assert azure_chat_completion.ai_model_id == deployment_name
|
||||
assert isinstance(azure_chat_completion, ChatCompletionClientBase)
|
||||
for key, value in default_headers.items():
|
||||
assert key in azure_chat_completion.client.default_headers
|
||||
assert azure_chat_completion.client.default_headers[key] == value
|
||||
|
||||
|
||||
def test_azure_chat_completion_init_with_empty_deployment_name() -> None:
|
||||
@@ -276,6 +285,7 @@ def test_azure_chat_completion_serialize() -> None:
|
||||
endpoint = "https://test-endpoint.com"
|
||||
api_key = "test_api_key"
|
||||
api_version = "2023-03-15-preview"
|
||||
default_headers = {"X-Test": "test"}
|
||||
logger = Logger("test_logger")
|
||||
|
||||
settings = {
|
||||
@@ -284,6 +294,7 @@ def test_azure_chat_completion_serialize() -> None:
|
||||
"api_key": api_key,
|
||||
"api_version": api_version,
|
||||
"log": logger,
|
||||
"default_headers": default_headers,
|
||||
}
|
||||
|
||||
azure_chat_completion = AzureChatCompletion.from_dict(settings)
|
||||
@@ -292,3 +303,12 @@ def test_azure_chat_completion_serialize() -> None:
|
||||
assert settings["endpoint"] in str(dumped_settings["base_url"])
|
||||
assert settings["deployment_name"] in str(dumped_settings["base_url"])
|
||||
assert settings["api_key"] == dumped_settings["api_key"]
|
||||
assert settings["api_version"] == dumped_settings["api_version"]
|
||||
|
||||
# Assert that the default header we added is present in the dumped_settings default headers
|
||||
for key, value in default_headers.items():
|
||||
assert key in dumped_settings["default_headers"]
|
||||
assert dumped_settings["default_headers"][key] == value
|
||||
|
||||
# Assert that the 'User-agent' header is not present in the dumped_settings default headers
|
||||
assert USER_AGENT not in dumped_settings["default_headers"]
|
||||
|
||||
@@ -40,6 +40,35 @@ def test_azure_text_completion_init() -> None:
|
||||
assert isinstance(azure_text_completion, TextCompletionClientBase)
|
||||
|
||||
|
||||
def test_azure_text_completion_init_with_custom_header() -> None:
|
||||
deployment_name = "test_deployment"
|
||||
endpoint = "https://test-endpoint.com"
|
||||
api_key = "test_api_key"
|
||||
api_version = "2023-03-15-preview"
|
||||
logger = Logger("test_logger")
|
||||
|
||||
# Custom header for testing
|
||||
default_headers = {"X-Unit-Test": "test-guid"}
|
||||
|
||||
# Test successful initialization
|
||||
azure_text_completion = AzureTextCompletion(
|
||||
deployment_name=deployment_name,
|
||||
endpoint=endpoint,
|
||||
api_key=api_key,
|
||||
api_version=api_version,
|
||||
log=logger,
|
||||
default_headers=default_headers,
|
||||
)
|
||||
|
||||
assert azure_text_completion.client is not None
|
||||
assert isinstance(azure_text_completion.client, AsyncAzureOpenAI)
|
||||
assert azure_text_completion.ai_model_id == deployment_name
|
||||
assert isinstance(azure_text_completion, TextCompletionClientBase)
|
||||
for key, value in default_headers.items():
|
||||
assert key in azure_text_completion.client.default_headers
|
||||
assert azure_text_completion.client.default_headers[key] == value
|
||||
|
||||
|
||||
def test_azure_text_completion_init_with_empty_deployment_name() -> None:
|
||||
# deployment_name = "test_deployment"
|
||||
endpoint = "https://test-endpoint.com"
|
||||
@@ -191,6 +220,7 @@ def test_azure_text_completion_serialize() -> None:
|
||||
endpoint = "https://test-endpoint.com"
|
||||
api_key = "test_api_key"
|
||||
api_version = "2023-03-15-preview"
|
||||
default_headers = {"X-Test": "test"}
|
||||
logger = Logger("test_logger")
|
||||
|
||||
settings = {
|
||||
@@ -199,6 +229,7 @@ def test_azure_text_completion_serialize() -> None:
|
||||
"api_key": api_key,
|
||||
"api_version": api_version,
|
||||
"log": logger,
|
||||
"default_headers": default_headers,
|
||||
}
|
||||
|
||||
azure_text_completion = AzureTextCompletion.from_dict(settings)
|
||||
@@ -207,3 +238,9 @@ def test_azure_text_completion_serialize() -> None:
|
||||
assert settings["endpoint"] in str(dumped_settings["base_url"])
|
||||
assert settings["deployment_name"] in str(dumped_settings["base_url"])
|
||||
assert settings["api_key"] == dumped_settings["api_key"]
|
||||
assert settings["api_version"] == dumped_settings["api_version"]
|
||||
|
||||
# Assert that the default header we added is present in the dumped_settings default headers
|
||||
for key, value in default_headers.items():
|
||||
assert key in dumped_settings["default_headers"]
|
||||
assert dumped_settings["default_headers"][key] == value
|
||||
|
||||
@@ -107,6 +107,38 @@ def test_azure_text_embedding_init_with_invalid_endpoint() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_azure_text_embedding_init_with_from_dict() -> None:
|
||||
deployment_name = "test_deployment"
|
||||
endpoint = "https://test-endpoint.com"
|
||||
api_key = "test_api_key"
|
||||
api_version = "2023-03-15-preview"
|
||||
logger = Logger("test_logger")
|
||||
default_headers = {"test_header": "test_value"}
|
||||
|
||||
settings = {
|
||||
"deployment_name": deployment_name,
|
||||
"endpoint": endpoint,
|
||||
"api_key": api_key,
|
||||
"api_version": api_version,
|
||||
"log": logger,
|
||||
"default_headers": default_headers,
|
||||
}
|
||||
|
||||
azure_text_embedding = AzureTextEmbedding.from_dict(settings=settings)
|
||||
|
||||
assert azure_text_embedding.client is not None
|
||||
assert isinstance(azure_text_embedding.client, AsyncAzureOpenAI)
|
||||
assert azure_text_embedding.ai_model_id == deployment_name
|
||||
assert isinstance(azure_text_embedding, EmbeddingGeneratorBase)
|
||||
assert endpoint in str(azure_text_embedding.client.base_url)
|
||||
assert azure_text_embedding.client.api_key == api_key
|
||||
|
||||
# Assert that the default header we added is present in the client's default headers
|
||||
for key, value in default_headers.items():
|
||||
assert key in azure_text_embedding.client.default_headers
|
||||
assert azure_text_embedding.client.default_headers[key] == value
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch.object(AsyncEmbeddings, "create", new_callable=AsyncMock)
|
||||
async def test_azure_text_embedding_calls_with_parameters(mock_create) -> None:
|
||||
|
||||
@@ -6,6 +6,9 @@ import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from semantic_kernel.connectors.ai import ChatCompletionClientBase
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion import (
|
||||
OpenAIChatCompletion,
|
||||
)
|
||||
@@ -27,6 +30,29 @@ def test_open_ai_chat_completion_init() -> None:
|
||||
assert isinstance(open_ai_chat_completion, ChatCompletionClientBase)
|
||||
|
||||
|
||||
def test_open_ai_chat_completion_init_with_default_header() -> None:
|
||||
ai_model_id = "test_model_id"
|
||||
api_key = "test_api_key"
|
||||
logger = Logger("test_logger")
|
||||
default_headers = {"X-Unit-Test": "test-guid"}
|
||||
|
||||
# Test successful initialization
|
||||
open_ai_chat_completion = OpenAIChatCompletion(
|
||||
ai_model_id=ai_model_id,
|
||||
api_key=api_key,
|
||||
log=logger,
|
||||
default_headers=default_headers,
|
||||
)
|
||||
|
||||
assert open_ai_chat_completion.ai_model_id == ai_model_id
|
||||
assert isinstance(open_ai_chat_completion, ChatCompletionClientBase)
|
||||
|
||||
# Assert that the default header we added is present in the client's default headers
|
||||
for key, value in default_headers.items():
|
||||
assert key in open_ai_chat_completion.client.default_headers
|
||||
assert open_ai_chat_completion.client.default_headers[key] == value
|
||||
|
||||
|
||||
def test_open_ai_chat_completion_init_with_empty_model_id() -> None:
|
||||
# ai_model_id = "test_model_id"
|
||||
api_key = "test_api_key"
|
||||
@@ -57,16 +83,25 @@ def test_open_ai_chat_completion_serialize() -> None:
|
||||
ai_model_id = "test_model_id"
|
||||
api_key = "test_api_key"
|
||||
logger = Logger("test_logger")
|
||||
default_headers = {"X-Unit-Test": "test-guid"}
|
||||
|
||||
settings = {
|
||||
"ai_model_id": ai_model_id,
|
||||
"api_key": api_key,
|
||||
"log": logger,
|
||||
"default_headers": default_headers,
|
||||
}
|
||||
|
||||
open_ai_chat_completion = OpenAIChatCompletion.from_dict(settings)
|
||||
dumped_settings = open_ai_chat_completion.to_dict()
|
||||
assert dumped_settings == settings
|
||||
assert dumped_settings["ai_model_id"] == ai_model_id
|
||||
assert dumped_settings["api_key"] == api_key
|
||||
# Assert that the default header we added is present in the dumped_settings default headers
|
||||
for key, value in default_headers.items():
|
||||
assert key in dumped_settings["default_headers"]
|
||||
assert dumped_settings["default_headers"][key] == value
|
||||
# Assert that the 'User-agent' header is not present in the dumped_settings default headers
|
||||
assert USER_AGENT not in dumped_settings["default_headers"]
|
||||
|
||||
|
||||
def test_open_ai_chat_completion_serialize_with_org_id() -> None:
|
||||
@@ -84,4 +119,8 @@ def test_open_ai_chat_completion_serialize_with_org_id() -> None:
|
||||
|
||||
open_ai_chat_completion = OpenAIChatCompletion.from_dict(settings)
|
||||
dumped_settings = open_ai_chat_completion.to_dict()
|
||||
assert dumped_settings == settings
|
||||
assert dumped_settings["ai_model_id"] == ai_model_id
|
||||
assert dumped_settings["api_key"] == api_key
|
||||
assert dumped_settings["org_id"] == org_id
|
||||
# Assert that the 'User-agent' header is not present in the dumped_settings default headers
|
||||
assert USER_AGENT not in dumped_settings["default_headers"]
|
||||
|
||||
@@ -27,6 +27,27 @@ def test_open_ai_text_completion_init() -> None:
|
||||
assert isinstance(open_ai_text_completion, TextCompletionClientBase)
|
||||
|
||||
|
||||
def test_open_ai_text_completion_init_with_default_header() -> None:
|
||||
ai_model_id = "test_model_id"
|
||||
api_key = "test_api_key"
|
||||
logger = Logger("test_logger")
|
||||
default_headers = {"X-Unit-Test": "test-guid"}
|
||||
|
||||
# Test successful initialization
|
||||
open_ai_text_completion = OpenAITextCompletion(
|
||||
ai_model_id=ai_model_id,
|
||||
api_key=api_key,
|
||||
log=logger,
|
||||
default_headers=default_headers,
|
||||
)
|
||||
|
||||
assert open_ai_text_completion.ai_model_id == ai_model_id
|
||||
assert isinstance(open_ai_text_completion, TextCompletionClientBase)
|
||||
for key, value in default_headers.items():
|
||||
assert key in open_ai_text_completion.client.default_headers
|
||||
assert open_ai_text_completion.client.default_headers[key] == value
|
||||
|
||||
|
||||
def test_open_ai_text_completion_init_with_empty_model_id() -> None:
|
||||
# ai_model_id = "test_model_id"
|
||||
api_key = "test_api_key"
|
||||
@@ -57,16 +78,23 @@ def test_open_ai_text_completion_serialize() -> None:
|
||||
ai_model_id = "test_model_id"
|
||||
api_key = "test_api_key"
|
||||
logger = Logger("test_logger")
|
||||
default_headers = {"X-Unit-Test": "test-guid"}
|
||||
|
||||
settings = {
|
||||
"ai_model_id": ai_model_id,
|
||||
"api_key": api_key,
|
||||
"log": logger,
|
||||
"default_headers": default_headers,
|
||||
}
|
||||
|
||||
open_ai_text_completion = OpenAITextCompletion.from_dict(settings)
|
||||
dumped_settings = open_ai_text_completion.to_dict()
|
||||
assert dumped_settings == settings
|
||||
assert dumped_settings["ai_model_id"] == ai_model_id
|
||||
assert dumped_settings["api_key"] == api_key
|
||||
# Assert that the default header we added is present in the dumped_settings default headers
|
||||
for key, value in default_headers.items():
|
||||
assert key in dumped_settings["default_headers"]
|
||||
assert dumped_settings["default_headers"][key] == value
|
||||
|
||||
|
||||
def test_open_ai_text_completion_serialize_with_org_id() -> None:
|
||||
@@ -84,4 +112,6 @@ def test_open_ai_text_completion_serialize_with_org_id() -> None:
|
||||
|
||||
open_ai_text_completion = OpenAITextCompletion.from_dict(settings)
|
||||
dumped_settings = open_ai_text_completion.to_dict()
|
||||
assert dumped_settings == settings
|
||||
assert dumped_settings["ai_model_id"] == ai_model_id
|
||||
assert dumped_settings["api_key"] == api_key
|
||||
assert dumped_settings["org_id"] == org_id
|
||||
|
||||
@@ -5,6 +5,9 @@ import pytest
|
||||
import yaml
|
||||
from openapi_core import Spec
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.connectors.openapi.sk_openapi import (
|
||||
OpenApiParser,
|
||||
OpenApiRunner,
|
||||
@@ -95,7 +98,7 @@ def test_prepare_request_with_path_params():
|
||||
headers={
|
||||
"Authorization": "Bearer abc123",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Semantic-Kernel",
|
||||
USER_AGENT: "Semantic-Kernel",
|
||||
},
|
||||
request_body={"title": "Buy milk", "completed": False},
|
||||
)
|
||||
@@ -134,7 +137,7 @@ def test_prepare_request_with_default_query_param():
|
||||
headers={
|
||||
"Authorization": "Bearer abc123",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Semantic-Kernel",
|
||||
USER_AGENT: "Semantic-Kernel",
|
||||
},
|
||||
request_body={"title": "Buy milk", "completed": False},
|
||||
)
|
||||
@@ -156,7 +159,7 @@ def test_prepare_request_with_default_header():
|
||||
method="PUT",
|
||||
url="http://example.com/todos/1",
|
||||
params={"completed": False},
|
||||
headers={"Content-Type": "application/json", "User-Agent": "Semantic-Kernel"},
|
||||
headers={"Content-Type": "application/json", USER_AGENT: "Semantic-Kernel"},
|
||||
request_body={"title": "Buy milk", "completed": False},
|
||||
)
|
||||
actual_request = put_operation.prepare_request(
|
||||
@@ -171,14 +174,14 @@ def test_prepare_request_with_default_header():
|
||||
def test_prepare_request_with_existing_user_agent():
|
||||
path_params = {"id": 1}
|
||||
query_params = {"completed": False}
|
||||
headers = {"User-Agent": "API/1.0 PythonBindings"}
|
||||
headers = {USER_AGENT: "API/1.0 PythonBindings"}
|
||||
request_body = {"title": "Buy milk", "completed": False}
|
||||
expected_request = PreparedRestApiRequest(
|
||||
method="PUT",
|
||||
url="http://example.com/todos/1",
|
||||
params={"completed": False},
|
||||
headers={
|
||||
"User-Agent": "Semantic-Kernel API/1.0 PythonBindings",
|
||||
USER_AGENT: "Semantic-Kernel API/1.0 PythonBindings",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
request_body={"title": "Buy milk", "completed": False},
|
||||
|
||||
Reference in New Issue
Block a user