Python: upgraded all files to 310 plus format and removed from future (#6353)

### Motivation and Context

<!-- 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.
-->
Now that we only support 310 and up, we could unify all files, to use
the new style typing and annotations.

### Description

<!-- 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: -->

- [x] The code builds clean without any errors or warnings
- [x] 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
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄
This commit is contained in:
Eduard van Valkenburg
2024-05-21 16:33:08 +00:00
committed by GitHub
parent 2b96abfb2f
commit 81cdde24fc
169 changed files with 789 additions and 950 deletions
@@ -3,7 +3,7 @@
import asyncio
import os
from functools import reduce
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.function_call_behavior import FunctionCallBehavior
@@ -108,7 +108,7 @@ async def handle_streaming(
)
print("Mosscap:> ", end="")
streamed_chunks: List[StreamingChatMessageContent] = []
streamed_chunks: list[StreamingChatMessageContent] = []
async for message in response:
if not execution_settings.function_call_behavior.auto_invoke_kernel_functions and isinstance(
message[0], ChatMessageContent
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Any, Dict
from typing import Any
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai import PromptExecutionSettings
@@ -18,7 +18,7 @@ Read more about logit bias and how to configure output: https://help.openai.com/
"""
def _config_ban_tokens(settings: PromptExecutionSettings, keys: Dict[Any, Any]):
def _config_ban_tokens(settings: PromptExecutionSettings, keys: dict[Any, Any]):
# Map each token in the keys list to a bias value from -100 (a potential ban) to 100 (exclusive selection)
for k in keys:
# -100 to potentially ban all tokens in the list
@@ -3,7 +3,8 @@
import asyncio
import logging
import os
from typing import Any, Callable, Coroutine
from collections.abc import Callable, Coroutine
from typing import Any
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
from semantic_kernel.contents.chat_history import ChatHistory
@@ -1,6 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import asyncio
import datetime
@@ -1,6 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import asyncio
from typing import Annotated
@@ -1,9 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import os
from typing import Dict, Optional
import httpx
from aiohttp import ClientSession
@@ -47,7 +45,7 @@ class OpenAIAuthenticationProvider:
"""A Sample Authentication Provider for an OpenAI/OpenAPI plugin"""
def __init__(
self, oauth_values: Optional[Dict[str, Dict[str, str]]] = None, credentials: Optional[Dict[str, str]] = None
self, oauth_values: dict[str, dict[str, str]] | None = None, credentials: dict[str, str] | None = None
):
"""Initializes the OpenAIAuthenticationProvider."""
self.oauth_values = oauth_values or {}
@@ -145,7 +143,7 @@ async def main():
openai_spec_file = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "resources", "open_ai_plugins", "akv-openai.json"
)
with open(openai_spec_file, "r") as file:
with open(openai_spec_file) as file:
openai_spec = file.read()
http_client = httpx.AsyncClient()
@@ -1,11 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import sys
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from typing import Annotated
from semantic_kernel.functions.kernel_function_decorator import kernel_function
@@ -1,12 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import sys
from datetime import datetime, timedelta
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from typing import Annotated
from msgraph import GraphServiceClient
from msgraph.generated.models.booking_appointment import BookingAppointment
+1 -7
View File
@@ -1,17 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import sys
from typing import Annotated
from service_configurator import add_service
import semantic_kernel as sk
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from semantic_kernel.functions.kernel_function_decorator import kernel_function
@@ -1,10 +1,5 @@
import math
import sys
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from typing import Annotated
from semantic_kernel.functions.kernel_function_decorator import kernel_function
@@ -1,8 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, AsyncGenerator
from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING, Any
from semantic_kernel.services.ai_service_client_base import AIServiceClientBase
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, List
from typing import TYPE_CHECKING, Any
from semantic_kernel.services.ai_service_client_base import AIServiceClientBase
from semantic_kernel.utils.experimental_decorator import experimental_class
@@ -13,5 +13,5 @@ if TYPE_CHECKING:
@experimental_class
class EmbeddingGeneratorBase(AIServiceClientBase, ABC):
@abstractmethod
async def generate_embeddings(self, texts: List[str], **kwargs: Any) -> "ndarray":
async def generate_embeddings(self, texts: list[str], **kwargs: Any) -> "ndarray":
pass
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import TYPE_CHECKING, Callable, Literal
from collections.abc import Callable
from typing import TYPE_CHECKING, Literal
from pydantic.dataclasses import dataclass
@@ -1,6 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any, Dict, Iterable, List, Optional, Union
from collections.abc import Iterable
from typing import Any, Union
from pydantic import Field, model_validator
@@ -8,34 +9,34 @@ from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecut
from semantic_kernel.exceptions import ServiceInvalidExecutionSettingsError
# TODO: replace back with google types once pydantic issue is fixed.
MessagesOptions = List[Dict[str, Any]]
MessagesOptions = list[dict[str, Any]]
MessagePromptOption = Union[str, Dict[str, Any]]
MessagePromptOptions = Union[MessagePromptOption, List[MessagePromptOption]]
MessagePromptOption = Union[str, dict[str, Any]]
MessagePromptOptions = Union[MessagePromptOption, list[MessagePromptOption]]
ExampleOptions = Union[Dict[str, Any], List[Dict[str, Any]]]
ExampleOptions = Union[dict[str, Any], list[dict[str, Any]]]
class GooglePalmPromptExecutionSettings(PromptExecutionSettings):
ai_model_id: Optional[str] = Field(None, serialization_alias="model")
ai_model_id: str | None = Field(None, serialization_alias="model")
temperature: float = Field(0.0, ge=0.0, le=1.0)
top_p: float = 1.0
top_k: int = 1
candidate_count: int = Field(1, ge=1, le=8)
safety_settings: Optional[Dict[str, Any]] = None
prompt: Optional[MessagePromptOptions] = None
safety_settings: dict[str, Any] | None = None
prompt: MessagePromptOptions | None = None
class GooglePalmTextPromptExecutionSettings(GooglePalmPromptExecutionSettings):
max_output_tokens: int = Field(256, gt=0)
stop_sequences: Optional[Union[str, Iterable[str]]] = None
stop_sequences: str | Iterable[str] | None = None
class GooglePalmChatPromptExecutionSettings(GooglePalmPromptExecutionSettings):
messages: Optional[MessagesOptions] = None
examples: Optional[ExampleOptions] = None
context: Optional[str] = None
token_selection_biases: Optional[Dict[int, int]] = None
messages: MessagesOptions | None = None
examples: ExampleOptions | None = None
context: str | None = None
token_selection_biases: dict[int, int] | None = None
@model_validator(mode="after")
def validate_input(self):
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Annotated, Any, List, Tuple
from typing import Annotated, Any
import google.generativeai as palm
from google.generativeai.types import ChatResponse, MessageDict
@@ -76,7 +76,7 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
chat_history: ChatHistory,
settings: GooglePalmPromptExecutionSettings,
**kwargs: Any,
) -> List[ChatMessageContent]:
) -> list[ChatMessageContent]:
"""
This is the method that is called from the kernel to get a response from a chat-optimized LLM.
@@ -124,7 +124,7 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
async def get_streaming_chat_message_contents(
self,
messages: List[Tuple[str, str]],
messages: list[tuple[str, str]],
settings: GooglePalmPromptExecutionSettings,
**kwargs: Any,
):
@@ -134,7 +134,7 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
self,
prompt: str,
settings: GooglePalmPromptExecutionSettings,
) -> List[TextContent]:
) -> list[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Annotated, List
from typing import Annotated
import google.generativeai as palm
from google.generativeai.types import Completion
@@ -51,7 +51,7 @@ class GooglePalmTextCompletion(TextCompletionClientBase):
async def get_text_contents(
self, prompt: str, settings: GooglePalmTextPromptExecutionSettings
) -> List[TextContent]:
) -> list[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Annotated, Any, List
from typing import Annotated, Any
import google.generativeai as palm
from numpy import array, ndarray
@@ -48,7 +48,7 @@ class GooglePalmTextEmbedding(EmbeddingGeneratorBase):
)
super().__init__(ai_model_id=ai_model_id, api_key=api_key)
async def generate_embeddings(self, texts: List[str], **kwargs: Any) -> ndarray:
async def generate_embeddings(self, texts: list[str], **kwargs: Any) -> ndarray:
"""
Generates embeddings for a list of texts.
@@ -1,4 +1,4 @@
from typing import Any, Dict
from typing import Any
from transformers import GenerationConfig
@@ -25,7 +25,7 @@ class HuggingFacePromptExecutionSettings(PromptExecutionSettings):
)
)
def prepare_settings_dict(self, **kwargs) -> Dict[str, Any]:
def prepare_settings_dict(self, **kwargs) -> dict[str, Any]:
gen_config = self.get_generation_config()
settings = {
"generation_config": gen_config,
@@ -1,8 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from collections.abc import AsyncGenerator
from threading import Thread
from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Literal, Optional
from typing import TYPE_CHECKING, Any, Literal
import torch
from transformers import AutoTokenizer, TextIteratorStreamer, pipeline
@@ -27,11 +28,11 @@ class HuggingFaceTextCompletion(TextCompletionClientBase):
def __init__(
self,
ai_model_id: str,
task: Optional[str] = "text2text-generation",
device: Optional[int] = -1,
service_id: Optional[str] = None,
model_kwargs: Optional[Dict[str, Any]] = None,
pipeline_kwargs: Optional[Dict[str, Any]] = None,
task: str | None = "text2text-generation",
device: int | None = -1,
service_id: str | None = None,
model_kwargs: dict[str, Any] | None = None,
pipeline_kwargs: dict[str, Any] | None = None,
) -> None:
"""
Initializes a new instance of the HuggingFaceTextCompletion class.
@@ -77,7 +78,7 @@ class HuggingFaceTextCompletion(TextCompletionClientBase):
self,
prompt: str,
settings: HuggingFacePromptExecutionSettings,
) -> List[TextContent]:
) -> list[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -96,7 +97,7 @@ class HuggingFaceTextCompletion(TextCompletionClientBase):
return [self._create_text_content(results, result) for result in results]
return [self._create_text_content(results, results)]
def _create_text_content(self, response: Any, candidate: Dict[str, str]) -> TextContent:
def _create_text_content(self, response: Any, candidate: dict[str, str]) -> TextContent:
return TextContent(
inner_content=response,
ai_model_id=self.ai_model_id,
@@ -107,7 +108,7 @@ class HuggingFaceTextCompletion(TextCompletionClientBase):
self,
prompt: str,
settings: HuggingFacePromptExecutionSettings,
) -> AsyncGenerator[List[StreamingTextContent], Any]:
) -> AsyncGenerator[list[StreamingTextContent], Any]:
"""
Streams a text completion using a Hugging Face model.
Note that this method does not support multiple responses.
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Any, List, Optional
from typing import Any
import sentence_transformers
import torch
@@ -22,8 +22,8 @@ class HuggingFaceTextEmbedding(EmbeddingGeneratorBase):
def __init__(
self,
ai_model_id: str,
device: Optional[int] = -1,
service_id: Optional[str] = None,
device: int | None = -1,
service_id: str | None = None,
) -> None:
"""
Initializes a new instance of the HuggingFaceTextEmbedding class.
@@ -44,7 +44,7 @@ class HuggingFaceTextEmbedding(EmbeddingGeneratorBase):
generator=sentence_transformers.SentenceTransformer(model_name_or_path=ai_model_id, device=resolved_device),
)
async def generate_embeddings(self, texts: List[str], **kwargs: Any) -> ndarray:
async def generate_embeddings(self, texts: list[str], **kwargs: Any) -> ndarray:
"""
Generates embeddings for a list of texts.
@@ -1,6 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any, Dict, List, Literal, Optional
from typing import Any, Literal
from pydantic import Field
@@ -9,18 +9,18 @@ from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecut
class OllamaPromptExecutionSettings(PromptExecutionSettings):
ai_model_id: str = Field("", serialization_alias="model")
format: Optional[Literal["json"]] = None
options: Optional[Dict[str, Any]] = None
format: Literal["json"] | None = None
options: dict[str, Any] | None = None
stream: bool = False
class OllamaTextPromptExecutionSettings(OllamaPromptExecutionSettings):
prompt: Optional[str] = None
context: Optional[str] = None
system: Optional[str] = None
template: Optional[str] = None
prompt: str | None = None
context: str | None = None
system: str | None = None
template: str | None = None
raw: bool = False
class OllamaChatPromptExecutionSettings(OllamaPromptExecutionSettings):
messages: Optional[List[Dict[str, str]]] = None
messages: list[dict[str, str]] | None = None
@@ -2,7 +2,8 @@
import json
import logging
from typing import Any, AsyncGenerator, List, Optional
from collections.abc import AsyncGenerator
from typing import Any
import aiohttp
from pydantic import HttpUrl
@@ -33,14 +34,14 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase):
"""
url: HttpUrl = "http://localhost:11434/api/chat"
session: Optional[aiohttp.ClientSession] = None
session: aiohttp.ClientSession | None = None
async def get_chat_message_contents(
self,
chat_history: ChatHistory,
settings: OllamaChatPromptExecutionSettings,
**kwargs: Any,
) -> List[ChatMessageContent]:
) -> list[ChatMessageContent]:
"""
This is the method that is called from the kernel to get a response from a chat-optimized LLM.
@@ -75,7 +76,7 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase):
chat_history: ChatHistory,
settings: OllamaChatPromptExecutionSettings,
**kwargs: Any,
) -> AsyncGenerator[List[StreamingChatMessageContent], Any]:
) -> AsyncGenerator[list[StreamingChatMessageContent], Any]:
"""
Streams a text completion using a Ollama model.
Note that this method does not support multiple responses.
@@ -116,7 +117,7 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase):
self,
prompt: str,
settings: OllamaChatPromptExecutionSettings,
) -> List[TextContent]:
) -> list[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -147,7 +148,7 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase):
self,
prompt: str,
settings: OllamaChatPromptExecutionSettings,
) -> AsyncGenerator[List[StreamingTextContent], Any]:
) -> AsyncGenerator[list[StreamingTextContent], Any]:
"""
Streams a text completion using a Ollama model.
Note that this method does not support multiple responses.
@@ -2,7 +2,8 @@
import json
import logging
from typing import Any, AsyncGenerator, List, Optional
from collections.abc import AsyncGenerator
from typing import Any
import aiohttp
from pydantic import HttpUrl
@@ -28,13 +29,13 @@ class OllamaTextCompletion(TextCompletionClientBase):
"""
url: HttpUrl = "http://localhost:11434/api/generate"
session: Optional[aiohttp.ClientSession] = None
session: aiohttp.ClientSession | None = None
async def get_text_contents(
self,
prompt: str,
settings: OllamaTextPromptExecutionSettings,
) -> List[TextContent]:
) -> list[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -60,7 +61,7 @@ class OllamaTextCompletion(TextCompletionClientBase):
self,
prompt: str,
settings: OllamaTextPromptExecutionSettings,
) -> AsyncGenerator[List[StreamingTextContent], Any]:
) -> AsyncGenerator[list[StreamingTextContent], Any]:
"""
Streams a text completion using a Ollama model.
Note that this method does not support multiple responses,
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Any, List, Optional
from typing import Any
import aiohttp
from numpy import array, ndarray
@@ -27,9 +27,9 @@ class OllamaTextEmbedding(EmbeddingGeneratorBase):
"""
url: HttpUrl = "http://localhost:11434/api/embeddings"
session: Optional[aiohttp.ClientSession] = None
session: aiohttp.ClientSession | None = None
async def generate_embeddings(self, texts: List[str], **kwargs: Any) -> ndarray:
async def generate_embeddings(self, texts: list[str], **kwargs: Any) -> ndarray:
"""
Generates embeddings for a list of texts.
@@ -2,7 +2,7 @@
from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict
from typing import Any
from openai import BadRequestError
@@ -22,7 +22,7 @@ class ContentFilterResult:
severity: ContentFilterResultSeverity = ContentFilterResultSeverity.SAFE
@classmethod
def from_inner_error_result(cls, inner_error_results: Dict[str, Any]) -> "ContentFilterResult":
def from_inner_error_result(cls, inner_error_results: dict[str, Any]) -> "ContentFilterResult":
"""Creates a ContentFilterResult from the inner error results.
Arguments:
@@ -56,7 +56,7 @@ class ContentFilterAIException(ServiceContentFilterException):
content_filter_code: ContentFilterCodes
# The results of the different content filter checks.
content_filter_result: Dict[str, ContentFilterResult]
content_filter_result: dict[str, ContentFilterResult]
def __init__(
self,
@@ -1,10 +1,9 @@
import logging
from typing import Any, Dict, List, Literal, Optional, Union
from typing import Annotated, Any, Literal, Union
from pydantic import AliasGenerator, ConfigDict, Field
from pydantic.alias_generators import to_camel, to_snake
from pydantic.functional_validators import AfterValidator
from typing_extensions import Annotated
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (
OpenAIChatPromptExecutionSettings,
@@ -24,46 +23,46 @@ class AzureChatRequestBase(KernelBaseModel):
class ConnectionStringAuthentication(AzureChatRequestBase):
type: Annotated[Literal["ConnectionString", "connection_string"], AfterValidator(to_snake)] = "connection_string"
connection_string: Optional[str] = None
connection_string: str | None = None
class ApiKeyAuthentication(AzureChatRequestBase):
type: Annotated[Literal["APIKey", "api_key"], AfterValidator(to_snake)] = "api_key"
key: Optional[str] = None
key: str | None = None
class AzureEmbeddingDependency(AzureChatRequestBase):
type: Annotated[Literal["DeploymentName", "deployment_name"], AfterValidator(to_snake)] = "deployment_name"
deployment_name: Optional[str] = None
deployment_name: str | None = None
class DataSourceFieldsMapping(AzureChatRequestBase):
title_field: Optional[str] = None
url_field: Optional[str] = None
filepath_field: Optional[str] = None
content_fields: Optional[List[str]] = None
vector_fields: Optional[List[str]] = None
content_fields_separator: Optional[str] = "\n"
title_field: str | None = None
url_field: str | None = None
filepath_field: str | None = None
content_fields: list[str] | None = None
vector_fields: list[str] | None = None
content_fields_separator: str | None = "\n"
class AzureDataSourceParameters(AzureChatRequestBase):
index_name: str
index_language: Optional[str] = None
fields_mapping: Optional[DataSourceFieldsMapping] = None
in_scope: Optional[bool] = True
top_n_documents: Optional[int] = 5
semantic_configuration: Optional[str] = None
role_information: Optional[str] = None
filter: Optional[str] = None
index_language: str | None = None
fields_mapping: DataSourceFieldsMapping | None = None
in_scope: bool | None = True
top_n_documents: int | None = 5
semantic_configuration: str | None = None
role_information: str | None = None
filter: str | None = None
strictness: int = 3
embedding_dependency: Optional[AzureEmbeddingDependency] = None
embedding_dependency: AzureEmbeddingDependency | None = None
class AzureCosmosDBDataSourceParameters(AzureDataSourceParameters):
authentication: Optional[ConnectionStringAuthentication] = None
database_name: Optional[str] = None
container_name: Optional[str] = None
embedding_dependency_type: Optional[AzureEmbeddingDependency] = None
authentication: ConnectionStringAuthentication | None = None
database_name: str | None = None
container_name: str | None = None
embedding_dependency_type: AzureEmbeddingDependency | None = None
class AzureCosmosDBDataSource(AzureChatRequestBase):
@@ -72,11 +71,11 @@ class AzureCosmosDBDataSource(AzureChatRequestBase):
class AzureAISearchDataSourceParameters(AzureDataSourceParameters):
endpoint: Optional[str] = None
endpoint: str | None = None
query_type: Annotated[
Literal["simple", "semantic", "vector", "vectorSimpleHybrid", "vectorSemanticHybrid"], AfterValidator(to_snake)
] = "simple"
authentication: Optional[ApiKeyAuthentication] = None
authentication: ApiKeyAuthentication | None = None
class AzureAISearchDataSource(AzureChatRequestBase):
@@ -88,9 +87,9 @@ DataSource = Annotated[Union[AzureAISearchDataSource, AzureCosmosDBDataSource],
class ExtraBody(KernelBaseModel):
data_sources: Optional[List[DataSource]] = None
input_language: Optional[str] = Field(None, serialization_alias="inputLanguage")
output_language: Optional[str] = Field(None, serialization_alias="outputLanguage")
data_sources: list[DataSource] | None = None
input_language: str | None = Field(None, serialization_alias="inputLanguage")
output_language: str | None = Field(None, serialization_alias="outputLanguage")
def __getitem__(self, item):
return getattr(self, item)
@@ -99,5 +98,5 @@ class ExtraBody(KernelBaseModel):
class AzureChatPromptExecutionSettings(OpenAIChatPromptExecutionSettings):
"""Specific settings for the Azure OpenAI Chat Completion endpoint."""
response_format: Optional[str] = None
extra_body: Optional[Union[Dict[str, Any], ExtraBody]] = None
response_format: str | None = None
extra_body: dict[str, Any] | ExtraBody | None = None
@@ -1,8 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from typing import Any, Dict, List, Literal, Optional, Union
from typing import Any, Literal
from pydantic import Field, field_validator, model_validator
@@ -16,28 +15,28 @@ logger = logging.getLogger(__name__)
class OpenAIPromptExecutionSettings(PromptExecutionSettings):
"""Common request settings for (Azure) OpenAI services."""
ai_model_id: Optional[str] = Field(None, serialization_alias="model")
ai_model_id: str | None = Field(None, serialization_alias="model")
frequency_penalty: float = Field(0.0, ge=-2.0, le=2.0)
logit_bias: Dict[Union[str, int], float] = Field(default_factory=dict)
logit_bias: dict[str | int, float] = Field(default_factory=dict)
max_tokens: int = Field(256, gt=0)
number_of_responses: int = Field(1, ge=1, le=128, serialization_alias="n")
presence_penalty: float = Field(0.0, ge=-2.0, le=2.0)
seed: Optional[int] = None
stop: Optional[Union[str, List[str]]] = None
seed: int | None = None
stop: str | list[str] | None = None
stream: bool = False
temperature: float = Field(0.0, ge=0.0, le=2.0)
top_p: float = Field(1.0, ge=0.0, le=1.0)
user: Optional[str] = None
user: str | None = None
class OpenAITextPromptExecutionSettings(OpenAIPromptExecutionSettings):
"""Specific settings for the completions endpoint."""
prompt: Optional[str] = None
best_of: Optional[int] = Field(None, ge=1)
prompt: str | None = None
best_of: int | None = Field(None, ge=1)
echo: bool = False
logprobs: Optional[int] = Field(None, ge=0, le=5)
suffix: Optional[str] = None
logprobs: int | None = Field(None, ge=0, le=5)
suffix: str | None = None
@model_validator(mode="after")
def check_best_of_and_n(self) -> "OpenAITextPromptExecutionSettings":
@@ -58,17 +57,17 @@ class OpenAITextPromptExecutionSettings(OpenAIPromptExecutionSettings):
class OpenAIChatPromptExecutionSettings(OpenAIPromptExecutionSettings):
"""Specific settings for the Chat Completion endpoint."""
response_format: Optional[Dict[Literal["type"], Literal["text", "json_object"]]] = None
tools: Optional[List[Dict[str, Any]]] = Field(None, max_length=64)
tool_choice: Optional[str] = None
function_call: Optional[str] = None
functions: Optional[List[Dict[str, Any]]] = None
messages: Optional[List[Dict[str, Any]]] = None
function_call_behavior: Optional[FunctionCallBehavior] = Field(None, exclude=True)
response_format: dict[Literal["type"], Literal["text", "json_object"]] | None = None
tools: list[dict[str, Any]] | None = Field(None, max_length=64)
tool_choice: str | None = None
function_call: str | None = None
functions: list[dict[str, Any]] | None = None
messages: list[dict[str, Any]] | None = None
function_call_behavior: FunctionCallBehavior | None = Field(None, exclude=True)
@field_validator("functions", "function_call", mode="after")
@classmethod
def validate_function_call(cls, v: Optional[Union[str, List[Dict[str, Any]]]] = None):
def validate_function_call(cls, v: str | list[dict[str, Any]] | None = None):
if v is not None:
logger.warning(
"The function_call and functions parameters are deprecated. Please use the tool_choice and tools parameters instead." # noqa: E501
@@ -77,12 +76,12 @@ class OpenAIChatPromptExecutionSettings(OpenAIPromptExecutionSettings):
class OpenAIEmbeddingPromptExecutionSettings(PromptExecutionSettings):
input: Optional[Union[str, List[str], List[int], List[List[int]]]] = None
ai_model_id: Optional[str] = Field(None, serialization_alias="model")
encoding_format: Optional[Literal["float", "base64"]] = None
user: Optional[str] = None
extra_headers: Optional[Dict] = None
extra_query: Optional[Dict] = None
extra_body: Optional[Dict] = None
timeout: Optional[float] = None
dimensions: Optional[int] = Field(None, gt=0, le=3072)
input: str | list[str] | list[int] | list[list[int]] | None = None
ai_model_id: str | None = Field(None, serialization_alias="model")
encoding_format: Literal["float", "base64"] | None = None
user: str | None = None
extra_headers: dict | None = None
extra_query: dict | None = None
extra_body: dict | None = None
timeout: float | None = None
dimensions: int | None = Field(None, gt=0, le=3072)
@@ -1,8 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import logging
from collections.abc import Mapping
from copy import deepcopy
from typing import Any, Dict, Mapping, Optional, Union
from typing import Any
from uuid import uuid4
from openai import AsyncAzureOpenAI
@@ -120,7 +121,7 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
)
@classmethod
def from_dict(cls, settings: Dict[str, str]) -> "AzureChatCompletion":
def from_dict(cls, settings: dict[str, str]) -> "AzureChatCompletion":
"""
Initialize an Azure OpenAI service from a dictionary of settings.
@@ -148,7 +149,7 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
return AzureChatPromptExecutionSettings
def _create_chat_message_content(
self, response: ChatCompletion, choice: Choice, response_metadata: Dict[str, Any]
self, response: ChatCompletion, choice: Choice, response_metadata: dict[str, Any]
) -> ChatMessageContent:
"""Create a Azure chat message content object from a choice."""
content = super()._create_chat_message_content(response, choice, response_metadata)
@@ -158,7 +159,7 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
self,
chunk: ChatCompletionChunk,
choice: ChunkChoice,
chunk_metadata: Dict[str, Any],
chunk_metadata: dict[str, Any],
) -> "StreamingChatMessageContent":
"""Create a Azure streaming chat message content object from a choice."""
content = super()._create_streaming_chat_message_content(chunk, choice, chunk_metadata)
@@ -186,7 +187,7 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
content.items.insert(1, result)
return content
def _get_tool_message_from_chat_choice(self, choice: Union[Choice, ChunkChoice]) -> Optional[str]:
def _get_tool_message_from_chat_choice(self, choice: Choice | ChunkChoice) -> str | None:
"""Get the tool message from a choice."""
if isinstance(choice, Choice):
content = choice.message
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Awaitable, Callable, Dict, Mapping, Optional, Union
from collections.abc import Awaitable, Callable, Mapping
from openai import AsyncAzureOpenAI
from pydantic import ConfigDict, validate_call
@@ -23,15 +23,15 @@ class AzureOpenAIConfigBase(OpenAIHandler):
self,
deployment_name: str,
ai_model_type: OpenAIModelTypes,
endpoint: Optional[HttpsUrl] = None,
base_url: Optional[HttpsUrl] = None,
endpoint: HttpsUrl | None = None,
base_url: HttpsUrl | None = None,
api_version: str = DEFAULT_AZURE_API_VERSION,
service_id: Optional[str] = None,
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,
async_client: Optional[AsyncAzureOpenAI] = None,
service_id: str | None = None,
api_key: str | None = None,
ad_token: str | None = None,
ad_token_provider: Callable[[], str | Awaitable[str]] | None = None,
default_headers: Mapping[str, str] | None = None,
async_client: AsyncAzureOpenAI | None = None,
) -> None:
"""Internal class for configuring a connection to an Azure OpenAI service.
@@ -90,7 +90,7 @@ class AzureOpenAIConfigBase(OpenAIHandler):
args["service_id"] = service_id
super().__init__(**args)
def to_dict(self) -> Dict[str, str]:
def to_dict(self) -> dict[str, str]:
client_settings = {
"base_url": str(self.client.base_url),
"api_version": self.client._custom_query["api-version"],
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Mapping
from collections.abc import Mapping
from openai import AsyncAzureOpenAI
from openai.lib.azure import AsyncAzureADTokenProvider
@@ -2,7 +2,7 @@
import logging
from typing import Mapping
from collections.abc import Mapping
from openai import AsyncAzureOpenAI
from openai.lib.azure import AsyncAzureADTokenProvider
@@ -1,10 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import (
Dict,
Mapping,
)
from collections.abc import Mapping
from openai import AsyncOpenAI
from pydantic import ValidationError
@@ -77,7 +74,7 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
)
@classmethod
def from_dict(cls, settings: Dict[str, str]) -> "OpenAIChatCompletion":
def from_dict(cls, settings: dict[str, str]) -> "OpenAIChatCompletion":
"""
Initialize an Open AI service from a dictionary of settings.
@@ -2,9 +2,10 @@
import asyncio
import logging
from collections.abc import AsyncGenerator
from copy import copy
from functools import reduce
from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Optional, Union
from typing import TYPE_CHECKING, Any
from openai import AsyncStream
from openai.types.chat.chat_completion import ChatCompletion, Choice
@@ -73,7 +74,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
chat_history: ChatHistory,
settings: OpenAIChatPromptExecutionSettings,
**kwargs: Any,
) -> List["ChatMessageContent"]:
) -> list["ChatMessageContent"]:
"""Executes a chat completion request and returns the result.
Arguments:
@@ -150,7 +151,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
chat_history: ChatHistory,
settings: OpenAIChatPromptExecutionSettings,
**kwargs: Any,
) -> AsyncGenerator[List[StreamingChatMessageContent | None], Any]:
) -> AsyncGenerator[list[StreamingChatMessageContent | None], Any]:
"""Executes a streaming chat completion request and returns the result.
Arguments:
@@ -239,7 +240,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
self._update_settings(settings, chat_history, kernel=kernel)
def _chat_message_content_to_dict(self, message: "ChatMessageContent") -> Dict[str, Optional[str]]:
def _chat_message_content_to_dict(self, message: "ChatMessageContent") -> dict[str, str | None]:
msg = super()._chat_message_content_to_dict(message)
if message.role == "assistant":
if tool_calls := getattr(message, "tool_calls", None):
@@ -256,7 +257,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
# endregion
# region internal handlers
async def _send_chat_request(self, settings: OpenAIChatPromptExecutionSettings) -> List["ChatMessageContent"]:
async def _send_chat_request(self, settings: OpenAIChatPromptExecutionSettings) -> list["ChatMessageContent"]:
"""Send the chat request"""
response = await self._send_request(request_settings=settings)
response_metadata = self._get_metadata_from_chat_response(response)
@@ -284,7 +285,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
# region content creation
def _create_chat_message_content(
self, response: ChatCompletion, choice: Choice, response_metadata: Dict[str, Any]
self, response: ChatCompletion, choice: Choice, response_metadata: dict[str, Any]
) -> "ChatMessageContent":
"""Create a chat message content object from a choice."""
metadata = self._get_metadata_from_chat_choice(choice)
@@ -308,7 +309,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
self,
chunk: ChatCompletionChunk,
choice: ChunkChoice,
chunk_metadata: Dict[str, Any],
chunk_metadata: dict[str, Any],
) -> StreamingChatMessageContent | None:
"""Create a streaming chat message content object from a choice."""
metadata = self._get_metadata_from_chat_choice(choice)
@@ -328,7 +329,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
items=items,
)
def _get_metadata_from_chat_response(self, response: ChatCompletion) -> Dict[str, Any]:
def _get_metadata_from_chat_response(self, response: ChatCompletion) -> dict[str, Any]:
"""Get metadata from a chat response."""
return {
"id": response.id,
@@ -337,7 +338,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
"usage": getattr(response, "usage", None),
}
def _get_metadata_from_streaming_chat_response(self, response: ChatCompletionChunk) -> Dict[str, Any]:
def _get_metadata_from_streaming_chat_response(self, response: ChatCompletionChunk) -> dict[str, Any]:
"""Get metadata from a streaming chat response."""
return {
"id": response.id,
@@ -345,13 +346,13 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
"system_fingerprint": response.system_fingerprint,
}
def _get_metadata_from_chat_choice(self, choice: Union[Choice, ChunkChoice]) -> Dict[str, Any]:
def _get_metadata_from_chat_choice(self, choice: Choice | ChunkChoice) -> dict[str, Any]:
"""Get metadata from a chat choice."""
return {
"logprobs": getattr(choice, "logprobs", None),
}
def _get_tool_calls_from_chat_choice(self, choice: Union[Choice, ChunkChoice]) -> List[FunctionCallContent]:
def _get_tool_calls_from_chat_choice(self, choice: Choice | ChunkChoice) -> list[FunctionCallContent]:
"""Get tool calls from a chat choice."""
if isinstance(choice, Choice):
content = choice.message
@@ -369,7 +370,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
for tool in content.tool_calls
]
def _get_function_call_from_chat_choice(self, choice: Union[Choice, ChunkChoice]) -> List[FunctionCallContent]:
def _get_function_call_from_chat_choice(self, choice: Choice | ChunkChoice) -> list[FunctionCallContent]:
"""Get a function call from a chat choice."""
if isinstance(choice, Choice):
content = choice.message
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Dict, Mapping, Optional
from collections.abc import Mapping
from openai import AsyncOpenAI
from pydantic import ConfigDict, Field, validate_call
@@ -20,12 +20,12 @@ class OpenAIConfigBase(OpenAIHandler):
def __init__(
self,
ai_model_id: 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,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
async_client: Optional[AsyncOpenAI] = None,
api_key: str | None = Field(min_length=1),
ai_model_type: OpenAIModelTypes | None = OpenAIModelTypes.CHAT,
org_id: str | None = None,
service_id: str | None = None,
default_headers: Mapping[str, str] | None = None,
async_client: AsyncOpenAI | None = None,
) -> None:
"""Initialize a client for OpenAI services.
@@ -68,7 +68,7 @@ class OpenAIConfigBase(OpenAIHandler):
args["service_id"] = service_id
super().__init__(**args)
def to_dict(self) -> Dict[str, str]:
def to_dict(self) -> dict[str, str]:
"""
Create a dict of the service settings.
"""
@@ -2,7 +2,6 @@
import logging
from abc import ABC
from typing import List, Union
from numpy import array, ndarray
from openai import AsyncOpenAI, AsyncStream, BadRequestError
@@ -37,7 +36,7 @@ class OpenAIHandler(KernelBaseModel, ABC):
async def _send_request(
self,
request_settings: OpenAIPromptExecutionSettings,
) -> Union[ChatCompletion, Completion, AsyncStream[ChatCompletionChunk], AsyncStream[Completion]]:
) -> ChatCompletion | Completion | AsyncStream[ChatCompletionChunk] | AsyncStream[Completion]:
"""
Completes the given prompt. Returns a single string completion.
Cannot return multiple completions. Cannot return logprobs.
@@ -75,7 +74,7 @@ class OpenAIHandler(KernelBaseModel, ABC):
ex,
) from ex
async def _send_embedding_request(self, settings: OpenAIEmbeddingPromptExecutionSettings) -> List[ndarray]:
async def _send_embedding_request(self, settings: OpenAIEmbeddingPromptExecutionSettings) -> list[ndarray]:
try:
response = await self.client.embeddings.create(**settings.prepare_settings_dict())
self.store_usage(response)
@@ -2,7 +2,7 @@
import json
import logging
from typing import Dict, Mapping, Optional
from collections.abc import Mapping
from openai import AsyncOpenAI
from pydantic import ValidationError
@@ -29,9 +29,9 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
ai_model_id: str | None = None,
api_key: str | None = None,
org_id: str | None = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
async_client: Optional[AsyncOpenAI] = None,
service_id: str | None = None,
default_headers: Mapping[str, str] | None = None,
async_client: AsyncOpenAI | None = None,
env_file_path: str | None = None,
) -> None:
"""
@@ -75,7 +75,7 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
)
@classmethod
def from_dict(cls, settings: Dict[str, str]) -> "OpenAITextCompletion":
def from_dict(cls, settings: dict[str, str]) -> "OpenAITextCompletion":
"""
Initialize an Open AI service from a dictionary of settings.
@@ -1,7 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Union
from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING, Any
from openai import AsyncStream
from openai.types import Completion, CompletionChoice
@@ -35,7 +36,7 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
self,
prompt: str,
settings: "OpenAIPromptExecutionSettings",
) -> List["TextContent"]:
) -> list["TextContent"]:
"""Executes a completion request and returns the result.
Arguments:
@@ -58,8 +59,8 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
def _create_text_content(
self,
response: Completion,
choice: Union[CompletionChoice, ChatCompletionChoice],
response_metadata: Dict[str, Any],
choice: CompletionChoice | ChatCompletionChoice,
response_metadata: dict[str, Any],
) -> "TextContent":
"""Create a text content object from a choice."""
choice_metadata = self._get_metadata_from_text_choice(choice)
@@ -76,7 +77,7 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
self,
prompt: str,
settings: "OpenAIPromptExecutionSettings",
) -> AsyncGenerator[List["StreamingTextContent"], Any]:
) -> AsyncGenerator[list["StreamingTextContent"], Any]:
"""
Executes a completion request and streams the result.
Supports both chat completion and text completion.
@@ -108,7 +109,7 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
yield [self._create_streaming_text_content(chunk, choice, chunk_metadata) for choice in chunk.choices]
def _create_streaming_text_content(
self, chunk: Completion, choice: Union[CompletionChoice, ChatCompletionChunk], response_metadata: Dict[str, Any]
self, chunk: Completion, choice: CompletionChoice | ChatCompletionChunk, response_metadata: dict[str, Any]
) -> "StreamingTextContent":
"""Create a streaming text content object from a choice."""
choice_metadata = self._get_metadata_from_text_choice(choice)
@@ -122,7 +123,7 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
text=text,
)
def _get_metadata_from_text_response(self, response: Completion) -> Dict[str, Any]:
def _get_metadata_from_text_response(self, response: Completion) -> dict[str, Any]:
"""Get metadata from a completion response."""
return {
"id": response.id,
@@ -131,7 +132,7 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
"usage": response.usage,
}
def _get_metadata_from_streaming_text_response(self, response: Completion) -> Dict[str, Any]:
def _get_metadata_from_streaming_text_response(self, response: Completion) -> dict[str, Any]:
"""Get metadata from a streaming completion response."""
return {
"id": response.id,
@@ -139,7 +140,7 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
"system_fingerprint": response.system_fingerprint,
}
def _get_metadata_from_text_choice(self, choice: CompletionChoice) -> Dict[str, Any]:
def _get_metadata_from_text_choice(self, choice: CompletionChoice) -> dict[str, Any]:
"""Get metadata from a completion choice."""
return {
"logprobs": getattr(choice, "logprobs", None),
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Dict, Mapping, Optional
from collections.abc import Mapping
from openai import AsyncOpenAI
from pydantic import ValidationError
@@ -30,9 +30,9 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
ai_model_id: str,
api_key: str | None = None,
org_id: str | None = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
async_client: Optional[AsyncOpenAI] = None,
service_id: str | None = None,
default_headers: Mapping[str, str] | None = None,
async_client: AsyncOpenAI | None = None,
env_file_path: str | None = None,
) -> None:
"""
@@ -76,7 +76,7 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
)
@classmethod
def from_dict(cls, settings: Dict[str, str]) -> "OpenAITextEmbedding":
def from_dict(cls, settings: dict[str, str]) -> "OpenAITextEmbedding":
"""
Initialize an Open AI service from a dictionary of settings.
@@ -1,6 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any, List, Optional
from typing import Any
from numpy import array, ndarray
@@ -15,7 +15,7 @@ from semantic_kernel.utils.experimental_decorator import experimental_class
@experimental_class
class OpenAITextEmbeddingBase(OpenAIHandler, EmbeddingGeneratorBase):
async def generate_embeddings(self, texts: List[str], batch_size: Optional[int] = None, **kwargs: Any) -> ndarray:
async def generate_embeddings(self, texts: list[str], batch_size: int | None = None, **kwargs: Any) -> ndarray:
"""Generates embeddings for the given texts.
Arguments:
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import Any
@@ -56,7 +55,7 @@ class PromptExecutionSettings(KernelBaseModel):
by_alias=True,
)
def update_from_prompt_execution_settings(self, config: PromptExecutionSettings) -> None:
def update_from_prompt_execution_settings(self, config: "PromptExecutionSettings") -> None:
"""Update the prompt execution settings from a completion config."""
if config.service_id is not None:
self.service_id = config.service_id
@@ -65,7 +64,7 @@ class PromptExecutionSettings(KernelBaseModel):
self.unpack_extension_data()
@classmethod
def from_prompt_execution_settings(cls, config: PromptExecutionSettings) -> PromptExecutionSettings:
def from_prompt_execution_settings(cls, config: "PromptExecutionSettings") -> "PromptExecutionSettings":
"""Create a prompt execution settings from a completion config."""
config.pack_extension_data()
return cls(
@@ -1,8 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, AsyncGenerator
from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING, Any
from semantic_kernel.services.ai_service_client_base import AIServiceClientBase
@@ -1,5 +1,4 @@
import json
from typing import Dict, List, Optional
import aiohttp
@@ -26,7 +25,7 @@ class AstraClient:
keyspace_name: str,
embedding_dim: int,
similarity_function: str,
session: Optional[aiohttp.ClientSession] = None,
session: aiohttp.ClientSession | None = None,
):
self.astra_id = astra_id
self.astra_application_token = astra_application_token
@@ -45,7 +44,7 @@ class AstraClient:
}
self._session = session
async def _run_query(self, request_url: str, query: Dict):
async def _run_query(self, request_url: str, query: dict):
async with AsyncSession(self._session) as session:
async with session.post(request_url, data=json.dumps(query), headers=self.request_header) as response:
if response.status == 200:
@@ -74,8 +73,8 @@ class AstraClient:
async def create_collection(
self,
collection_name: str,
embedding_dim: Optional[int] = None,
similarity_function: Optional[str] = None,
embedding_dim: int | None = None,
similarity_function: str | None = None,
):
query = {
"createCollection": {
@@ -102,12 +101,12 @@ class AstraClient:
async def find_documents(
self,
collection_name: str,
filter: Optional[Dict] = None,
vector: Optional[List[float]] = None,
limit: Optional[int] = None,
include_vector: Optional[bool] = None,
include_similarity: Optional[bool] = None,
) -> List[Dict]:
filter: dict | None = None,
vector: list[float] | None = None,
limit: int | None = None,
include_vector: bool | None = None,
include_similarity: bool | None = None,
) -> list[dict]:
find_query = {}
if filter is not None:
@@ -132,17 +131,17 @@ class AstraClient:
result = await self._run_query(self._build_request_collection_url(collection_name), query)
return result["data"]["documents"]
async def insert_document(self, collection_name: str, document: Dict) -> str:
async def insert_document(self, collection_name: str, document: dict) -> str:
query = {"insertOne": {"document": document}}
result = await self._run_query(self._build_request_collection_url(collection_name), query)
return result["status"]["insertedIds"][0]
async def insert_documents(self, collection_name: str, documents: List[Dict]) -> List[str]:
async def insert_documents(self, collection_name: str, documents: list[dict]) -> list[str]:
query = {"insertMany": {"documents": documents}}
result = await self._run_query(self._build_request_collection_url(collection_name), query)
return result["status"]["insertedIds"]
async def update_document(self, collection_name: str, filter: Dict, update: Dict, upsert: bool = True) -> Dict:
async def update_document(self, collection_name: str, filter: dict, update: dict, upsert: bool = True) -> dict:
query = {
"findOneAndUpdate": {
"filter": filter,
@@ -153,7 +152,7 @@ class AstraClient:
result = await self._run_query(self._build_request_collection_url(collection_name), query)
return result["status"]
async def update_documents(self, collection_name: str, filter: Dict, update: Dict):
async def update_documents(self, collection_name: str, filter: dict, update: dict):
query = {
"updateMany": {
"filter": filter,
@@ -163,7 +162,7 @@ class AstraClient:
result = await self._run_query(self._build_request_collection_url(collection_name), query)
return result["status"]
async def delete_documents(self, collection_name: str, filter: Dict) -> int:
async def delete_documents(self, collection_name: str, filter: dict) -> int:
query = {"deleteMany": {"filter": filter}}
result = await self._run_query(self._build_request_collection_url(collection_name), query)
return result["status"]["deletedCount"]
@@ -2,7 +2,6 @@
import asyncio
import logging
from typing import List, Optional, Tuple
import aiohttp
from numpy import ndarray
@@ -99,7 +98,7 @@ class AstraDBMemoryStore(MemoryStoreBase):
session=self._session,
)
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
"""Gets the list of collections.
Returns:
@@ -110,8 +109,8 @@ class AstraDBMemoryStore(MemoryStoreBase):
async def create_collection(
self,
collection_name: str,
dimension_num: Optional[int] = None,
distance_type: Optional[str] = "cosine",
dimension_num: int | None = None,
distance_type: str | None = "cosine",
) -> None:
"""Creates a new collection in Astra if it does not exist.
@@ -179,7 +178,7 @@ class AstraDBMemoryStore(MemoryStoreBase):
return status["upsertedId"] if "upsertedId" in status else record._id
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""Upserts a batch of memory records into the data store. Does not guarantee that the collection exists.
If the record already exists, it will be updated.
If the record does not exist, it will be created.
@@ -217,8 +216,8 @@ class AstraDBMemoryStore(MemoryStoreBase):
return parse_payload(documents[0])
async def get_batch(
self, collection_name: str, keys: List[str], with_embeddings: bool = False
) -> List[MemoryRecord]:
self, collection_name: str, keys: list[str], with_embeddings: bool = False
) -> list[MemoryRecord]:
"""Gets a batch of records. Does not guarantee that the collection exists.
Arguments:
@@ -251,7 +250,7 @@ class AstraDBMemoryStore(MemoryStoreBase):
filter = {"_id": key}
await self._client.delete_documents(collection_name, filter)
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Removes a batch of records. Does not guarantee that the collection exists.
Arguments:
@@ -270,7 +269,7 @@ class AstraDBMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float = 0.0,
with_embedding: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Gets the nearest match to an embedding using cosine similarity.
Arguments:
collection_name {str} -- The name of the collection to get the nearest matches from.
@@ -297,7 +296,7 @@ class AstraDBMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float = 0.0,
with_embeddings: bool = False,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Gets the nearest matches to an embedding using cosine similarity.
Arguments:
collection_name {str} -- The name of the collection to get the nearest matches from.
@@ -1,5 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any, Dict
from typing import Any
import aiohttp
import numpy
@@ -18,11 +18,11 @@ class AsyncSession:
await self._session.close()
def build_payload(record: MemoryRecord) -> Dict[str, Any]:
def build_payload(record: MemoryRecord) -> dict[str, Any]:
"""
Builds a metadata payload to be sent to AstraDb from a MemoryRecord.
"""
payload: Dict[str, Any] = {}
payload: dict[str, Any] = {}
payload["$vector"] = record.embedding.tolist()
if record._text:
payload["text"] = record._text
@@ -33,7 +33,7 @@ def build_payload(record: MemoryRecord) -> Dict[str, Any]:
return payload
def parse_payload(document: Dict[str, Any]) -> MemoryRecord:
def parse_payload(document: dict[str, Any]) -> MemoryRecord:
"""
Parses a record from AstraDb into a MemoryRecord.
"""
@@ -3,7 +3,6 @@
import logging
import uuid
from inspect import isawaitable
from typing import List, Optional, Tuple
from azure.core.credentials import AzureKeyCredential, TokenCredential
from azure.core.exceptions import ResourceNotFoundError
@@ -101,8 +100,8 @@ class AzureCognitiveSearchMemoryStore(MemoryStoreBase):
async def create_collection(
self,
collection_name: str,
vector_config: Optional[HnswAlgorithmConfiguration] = None,
search_resource_encryption_key: Optional[SearchResourceEncryptionKey] = None,
vector_config: HnswAlgorithmConfiguration | None = None,
search_resource_encryption_key: SearchResourceEncryptionKey | None = None,
) -> None:
"""Creates a new collection if it does not exist.
@@ -166,7 +165,7 @@ class AzureCognitiveSearchMemoryStore(MemoryStoreBase):
await self._search_index_client.create_index(index)
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
"""Gets the list of collections.
Returns:
@@ -230,7 +229,7 @@ class AzureCognitiveSearchMemoryStore(MemoryStoreBase):
return result[0]
return None
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""Upsert a batch of records.
Arguments:
@@ -296,8 +295,8 @@ class AzureCognitiveSearchMemoryStore(MemoryStoreBase):
return dict_to_memory_record(search_result, with_embedding)
async def get_batch(
self, collection_name: str, keys: List[str], with_embeddings: bool = False
) -> List[MemoryRecord]:
self, collection_name: str, keys: list[str], with_embeddings: bool = False
) -> list[MemoryRecord]:
"""Gets a batch of records.
Arguments:
@@ -321,7 +320,7 @@ class AzureCognitiveSearchMemoryStore(MemoryStoreBase):
return search_results
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Removes a batch of records.
Arguments:
@@ -359,7 +358,7 @@ class AzureCognitiveSearchMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float = 0.0,
with_embedding: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Gets the nearest match to an embedding using vector configuration parameters.
Arguments:
@@ -392,7 +391,7 @@ class AzureCognitiveSearchMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float = 0.0,
with_embeddings: bool = False,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Gets the nearest matches to an embedding using vector configuration.
Parameters:
@@ -2,7 +2,6 @@
import base64
import os
from typing import List, Optional
from azure.core.credentials import AzureKeyCredential, TokenCredential
from azure.search.documents.indexes.aio import SearchIndexClient
@@ -23,10 +22,10 @@ SEARCH_FIELD_IS_REF = "IsReference"
def get_search_index_async_client(
search_endpoint: Optional[str] = None,
admin_key: Optional[str] = None,
azure_credential: Optional[AzureKeyCredential] = None,
token_credential: Optional[TokenCredential] = None,
search_endpoint: str | None = None,
admin_key: str | None = None,
azure_credential: AzureKeyCredential | None = None,
token_credential: TokenCredential | None = None,
):
"""Return a client for Azure Cognitive Search.
@@ -147,7 +146,7 @@ def get_index_schema(vector_size: int, vector_search_profile_name: str) -> list:
return search_fields
def get_field_selection(with_embeddings: bool) -> List[str]:
def get_field_selection(with_embeddings: bool) -> list[str]:
"""Get the list of fields to search and load.
Arguments:
@@ -1,7 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import List, Tuple
from numpy import ndarray
from pydantic import ValidationError
@@ -150,7 +149,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
"""
return await self.cosmosStore.create_collection(collection_name)
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
"""Gets the list of collections.
Returns:
@@ -167,7 +166,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
None
"""
return await self.cosmosStore.delete_collection(str())
return await self.cosmosStore.delete_collection("")
async def does_collection_exist(self, collection_name: str) -> bool:
"""Checks if a collection exists.
@@ -178,7 +177,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
bool -- True if the collection exists; otherwise, False.
"""
return await self.cosmosStore.does_collection_exist(str())
return await self.cosmosStore.does_collection_exist("")
async def upsert(self, collection_name: str, record: MemoryRecord) -> str:
"""Upsert a record.
@@ -190,9 +189,9 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
str -- The unique record id of the record.
"""
return await self.cosmosStore.upsert(str(), record)
return await self.cosmosStore.upsert("", record)
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""Upsert a batch of records.
Arguments:
@@ -202,7 +201,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
List[str] -- The unique database keys of the records.
"""
return await self.cosmosStore.upsert_batch(str(), records)
return await self.cosmosStore.upsert_batch("", records)
async def get(self, collection_name: str, key: str, with_embedding: bool) -> MemoryRecord:
"""Gets a record.
@@ -215,9 +214,9 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
MemoryRecord -- The record.
"""
return await self.cosmosStore.get(str(), key, with_embedding)
return await self.cosmosStore.get("", key, with_embedding)
async def get_batch(self, collection_name: str, keys: List[str], with_embeddings: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embeddings: bool) -> list[MemoryRecord]:
"""Gets a batch of records.
Arguments:
@@ -228,7 +227,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
List[MemoryRecord] -- The records.
"""
return await self.cosmosStore.get_batch(str(), keys, with_embeddings)
return await self.cosmosStore.get_batch("", keys, with_embeddings)
async def remove(self, collection_name: str, key: str) -> None:
"""Removes a record.
@@ -240,9 +239,9 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
None
"""
return await self.cosmosStore.remove(str(), key)
return await self.cosmosStore.remove("", key)
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Removes a batch of records.
Arguments:
@@ -252,7 +251,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
None
"""
return await self.cosmosStore.remove_batch(str(), keys)
return await self.cosmosStore.remove_batch("", keys)
async def get_nearest_matches(
self,
@@ -261,7 +260,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float,
with_embeddings: bool,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Gets the nearest matches to an embedding using vector configuration.
Parameters:
@@ -274,7 +273,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
List[Tuple[MemoryRecord, float]] -- The records and their relevance scores.
"""
return await self.cosmosStore.get_nearest_matches(str(), embedding, limit, min_relevance_score, with_embeddings)
return await self.cosmosStore.get_nearest_matches("", embedding, limit, min_relevance_score, with_embeddings)
async def get_nearest_match(
self,
@@ -282,7 +281,7 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float,
with_embedding: bool,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Gets the nearest match to an embedding using vector configuration parameters.
Arguments:
@@ -294,4 +293,4 @@ class AzureCosmosDBMemoryStore(MemoryStoreBase):
Returns:
Tuple[MemoryRecord, float] -- The record and the relevance score.
"""
return await self.cosmosStore.get_nearest_match(str(), embedding, min_relevance_score, with_embedding)
return await self.cosmosStore.get_nearest_match("", embedding, min_relevance_score, with_embedding)
@@ -2,7 +2,6 @@
from abc import ABC, abstractmethod
from typing import List, Tuple
from numpy import ndarray
@@ -18,7 +17,7 @@ class AzureCosmosDBStoreApi(ABC):
raise NotImplementedError
@abstractmethod
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
raise NotImplementedError
@abstractmethod
@@ -34,7 +33,7 @@ class AzureCosmosDBStoreApi(ABC):
raise NotImplementedError
@abstractmethod
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
raise NotImplementedError
@abstractmethod
@@ -42,7 +41,7 @@ class AzureCosmosDBStoreApi(ABC):
raise NotImplementedError
@abstractmethod
async def get_batch(self, collection_name: str, keys: List[str], with_embeddings: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embeddings: bool) -> list[MemoryRecord]:
raise NotImplementedError
@abstractmethod
@@ -50,7 +49,7 @@ class AzureCosmosDBStoreApi(ABC):
raise NotImplementedError
@abstractmethod
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
raise NotImplementedError
@abstractmethod
@@ -61,7 +60,7 @@ class AzureCosmosDBStoreApi(ABC):
limit: int,
min_relevance_score: float,
with_embeddings: bool,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
raise NotImplementedError
@abstractmethod
@@ -71,5 +70,5 @@ class AzureCosmosDBStoreApi(ABC):
embedding: ndarray,
min_relevance_score: float,
with_embedding: bool,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
raise NotImplementedError
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import json
from typing import Any, Dict, List, Tuple
from typing import Any
import numpy as np
@@ -117,7 +117,7 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
def _get_vector_index_ivf(
self, collection_name: str, kind: str, num_lists: int, similarity: str, dimensions: int
) -> Dict[str, Any]:
) -> dict[str, Any]:
command = {
"createIndexes": collection_name,
"indexes": [
@@ -137,7 +137,7 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
def _get_vector_index_hnsw(
self, collection_name: str, kind: str, m: int, ef_construction: int, similarity: str, dimensions: int
) -> Dict[str, Any]:
) -> dict[str, Any]:
command = {
"createIndexes": collection_name,
"indexes": [
@@ -156,7 +156,7 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
}
return command
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
return self.database.list_collection_names()
async def delete_collection(self, collection_name: str) -> None:
@@ -169,9 +169,9 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
result = await self.upsert_batch(collection_name, [record])
return result[0]
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
doc_ids: List[str] = []
cosmosRecords: List[dict] = []
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
doc_ids: list[str] = []
cosmosRecords: list[dict] = []
for record in records:
cosmosRecord: dict = {
"_id": record.id,
@@ -202,7 +202,7 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
timestamp=result.get("timestamp", None),
)
async def get_batch(self, collection_name: str, keys: List[str], with_embeddings: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embeddings: bool) -> list[MemoryRecord]:
if not with_embeddings:
results = self.collection.find({"_id": {"$in": keys}}, {"embedding": 0})
else:
@@ -223,7 +223,7 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
async def remove(self, collection_name: str, key: str) -> None:
self.collection.delete_one({"_id": key})
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
self.collection.delete_many({"_id": {"$in": keys}})
async def get_nearest_matches(
@@ -233,8 +233,8 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
limit: int,
min_relevance_score: float,
with_embeddings: bool,
) -> List[Tuple[MemoryRecord, float]]:
pipeline: List[dict[str, Any]] = []
) -> list[tuple[MemoryRecord, float]]:
pipeline: list[dict[str, Any]] = []
if self.kind == CosmosDBVectorSearchType.VECTOR_IVF:
pipeline = self._get_pipeline_vector_ivf(embedding.tolist(), limit)
elif self.kind == CosmosDBVectorSearchType.VECTOR_HNSW:
@@ -259,8 +259,8 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
nearest_results.append((result, aggResult["similarityScore"]))
return nearest_results
def _get_pipeline_vector_ivf(self, embeddings: List[float], k: int = 4) -> List[dict[str, Any]]:
pipeline: List[dict[str, Any]] = [
def _get_pipeline_vector_ivf(self, embeddings: list[float], k: int = 4) -> list[dict[str, Any]]:
pipeline: list[dict[str, Any]] = [
{
"$search": {
"cosmosSearch": {
@@ -281,9 +281,9 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
return pipeline
def _get_pipeline_vector_hnsw(
self, embeddings: List[float], k: int = 4, ef_search: int = 40
) -> List[dict[str, Any]]:
pipeline: List[dict[str, Any]] = [
self, embeddings: list[float], k: int = 4, ef_search: int = 40
) -> list[dict[str, Any]]:
pipeline: list[dict[str, Any]] = [
{
"$search": {
"cosmosSearch": {
@@ -309,7 +309,7 @@ class MongoStoreApi(AzureCosmosDBStoreApi):
embedding: np.ndarray,
min_relevance_score: float,
with_embedding: bool,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
nearest_results = await self.get_nearest_matches(
collection_name=collection_name,
embedding=embedding,
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import json
from typing import Any, List, Tuple
from typing import Any
import numpy as np
from azure.cosmos.aio import ContainerProxy, CosmosClient, DatabaseProxy
@@ -58,7 +58,7 @@ class AzureCosmosDBNoSQLMemoryStore(MemoryStoreBase):
vector_embedding_policy=self.vector_embedding_policy,
)
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
return [container["id"] async for container in self.database.list_containers()]
async def delete_collection(self, collection_name: str) -> None:
@@ -71,8 +71,8 @@ class AzureCosmosDBNoSQLMemoryStore(MemoryStoreBase):
result = await self.upsert_batch(collection_name, [record])
return result[0]
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
doc_ids: List[str] = []
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
doc_ids: list[str] = []
for record in records:
cosmosRecord: dict = {
"id": record.id,
@@ -99,7 +99,7 @@ class AzureCosmosDBNoSQLMemoryStore(MemoryStoreBase):
timestamp=item.get("timestamp", None),
)
async def get_batch(self, collection_name: str, keys: List[str], with_embeddings: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embeddings: bool) -> list[MemoryRecord]:
query = "SELECT * FROM c WHERE ARRAY_CONTAINS(@ids, c.id)"
parameters = [{"name": "@ids", "value": keys}]
@@ -120,13 +120,13 @@ class AzureCosmosDBNoSQLMemoryStore(MemoryStoreBase):
async def remove(self, collection_name: str, key: str) -> None:
await self.container.delete_item(key, partition_key=key)
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
for key in keys:
await self.container.delete_item(key, partition_key=key)
async def get_nearest_matches(
self, collection_name: str, embedding: ndarray, limit: int, min_relevance_score: float, with_embeddings: bool
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
embedding_key = self.vector_embedding_policy["vectorEmbeddings"][0]["path"][1:]
query = (
"SELECT TOP {} c.id, c.{}, c.text, c.description, c.metadata, "
@@ -155,7 +155,7 @@ class AzureCosmosDBNoSQLMemoryStore(MemoryStoreBase):
async def get_nearest_match(
self, collection_name: str, embedding: ndarray, min_relevance_score: float, with_embedding: bool
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
nearest_results = await self.get_nearest_matches(
collection_name=collection_name,
embedding=embedding,
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import TYPE_CHECKING, List, Optional, Tuple
from typing import TYPE_CHECKING, Optional
from numpy import array, ndarray
@@ -25,7 +25,7 @@ class ChromaMemoryStore(MemoryStoreBase):
def __init__(
self,
persist_directory: Optional[str] = None,
persist_directory: str | None = None,
client_settings: Optional["chromadb.config.Settings"] = None,
**kwargs,
) -> None:
@@ -93,7 +93,7 @@ class ChromaMemoryStore(MemoryStoreBase):
except ValueError:
return None
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
"""Gets the list of collections.
Returns:
@@ -159,7 +159,7 @@ class ChromaMemoryStore(MemoryStoreBase):
)
return record._key
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""Upserts a batch of records.
Arguments:
@@ -191,7 +191,7 @@ class ChromaMemoryStore(MemoryStoreBase):
f"Record with key '{key}' does not exist in collection '{collection_name}'"
) from exc
async def get_batch(self, collection_name: str, keys: List[str], with_embeddings: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embeddings: bool) -> list[MemoryRecord]:
"""Gets a batch of records.
Arguments:
@@ -224,7 +224,7 @@ class ChromaMemoryStore(MemoryStoreBase):
"""
await self.remove_batch(collection_name, [key])
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Removes a batch of records.
Arguments:
@@ -245,7 +245,7 @@ class ChromaMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float = 0.0,
with_embeddings: bool = True,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Gets the nearest matches to an embedding using cosine similarity.
Arguments:
@@ -312,7 +312,7 @@ class ChromaMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float = 0.0,
with_embedding: bool = True,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Gets the nearest match to an embedding using cosine similarity.
Arguments:
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING
from numpy import array, linalg, ndarray
@@ -25,7 +25,7 @@ def camel_to_snake(camel_str):
return snake_str
def query_results_to_records(results: "QueryResult", with_embedding: bool) -> List[MemoryRecord]:
def query_results_to_records(results: "QueryResult", with_embedding: bool) -> list[MemoryRecord]:
# if results has only one record, it will be a list instead of a nested list
# this is to make sure that results is always a nested list
# {'ids': ['test_id1'], 'embeddings': [[...]], 'documents': ['sample text1'], 'metadatas': [{...}]}
@@ -2,7 +2,7 @@
import logging
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from typing import Any
from numpy import array, expand_dims, ndarray
from pymilvus import Collection, CollectionSchema, DataType, FieldSchema, connections, utility
@@ -49,7 +49,7 @@ OUTPUT_FIELDS_WO_EMBEDDING = [
@experimental_function
def memoryrecord_to_milvus_dict(mem: MemoryRecord) -> Dict[str, Any]:
def memoryrecord_to_milvus_dict(mem: MemoryRecord) -> dict[str, Any]:
"""Convert a memoryrecord into a dict.
Args:
mem (MemoryRecord): MemoryRecord to convert.
@@ -69,7 +69,7 @@ def memoryrecord_to_milvus_dict(mem: MemoryRecord) -> Dict[str, Any]:
@experimental_function
def milvus_dict_to_memoryrecord(milvus_dict: Dict[str, Any]) -> MemoryRecord:
def milvus_dict_to_memoryrecord(milvus_dict: dict[str, Any]) -> MemoryRecord:
"""Convert Milvus search result dict into MemoryRecord.
Args:
@@ -96,7 +96,7 @@ def milvus_dict_to_memoryrecord(milvus_dict: Dict[str, Any]) -> MemoryRecord:
@experimental_function
def create_fields(dimensions: int) -> List[FieldSchema]:
def create_fields(dimensions: int) -> list[FieldSchema]:
return [
FieldSchema(
name=SEARCH_FIELD_ID,
@@ -147,7 +147,7 @@ class MilvusMemoryStore(MemoryStoreBase):
def __init__(
self,
uri: str = "http://localhost:19530",
token: Optional[str] = None,
token: str | None = None,
**kwargs,
) -> None:
"""MilvusMemoryStore allows for searching for records using Milvus/Zilliz Cloud.
@@ -164,13 +164,13 @@ class MilvusMemoryStore(MemoryStoreBase):
authentication is required. Defaults to None.
"""
connections.connect("default", uri=uri, token=token)
self.collections: Dict[str, Collection] = {}
self.collections: dict[str, Collection] = {}
async def create_collection(
self,
collection_name: str,
dimension_num: int = 1536,
distance_type: Optional[str] = "IP",
distance_type: str | None = "IP",
overwrite: bool = False,
consistency: str = "Session",
) -> None:
@@ -203,7 +203,7 @@ class MilvusMemoryStore(MemoryStoreBase):
async def get_collections(
self,
) -> List[str]:
) -> list[str]:
"""Return a list of present collections.
Returns:
@@ -211,7 +211,7 @@ class MilvusMemoryStore(MemoryStoreBase):
"""
return utility.list_collections()
async def delete_collection(self, collection_name: Optional[str] = None, all: bool = False) -> None:
async def delete_collection(self, collection_name: str | None = None, all: bool = False) -> None:
"""Delete the specified collection.
If all is True, all collections in the cluster will be removed.
@@ -258,7 +258,7 @@ class MilvusMemoryStore(MemoryStoreBase):
)
return res[0]
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord], batch_size=100) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord], batch_size=100) -> list[str]:
"""_summary_
Args:
@@ -302,7 +302,7 @@ class MilvusMemoryStore(MemoryStoreBase):
res = await self.get_batch(collection_name=collection_name, keys=[key], with_embeddings=with_embedding)
return res[0]
async def get_batch(self, collection_name: str, keys: List[str], with_embeddings: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embeddings: bool) -> list[MemoryRecord]:
"""Get the MemoryRecords corresponding to the keys
Args:
@@ -342,7 +342,7 @@ class MilvusMemoryStore(MemoryStoreBase):
"""
await self.remove_batch(collection_name=collection_name, keys=[key])
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Remove multiple records based on keys.
Args:
@@ -378,7 +378,7 @@ class MilvusMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float = 0.0,
with_embeddings: bool = False,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Find the nearest `limit` matches for an embedding.
Args:
@@ -429,7 +429,7 @@ class MilvusMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float = 0.0,
with_embedding: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Find the nearest match for an embedding.
Args:
@@ -1,9 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from collections.abc import Mapping
from importlib import metadata
from typing import Any, List, Mapping, Tuple
from typing import Any
from motor import core, motor_asyncio
from numpy import ndarray
@@ -104,7 +104,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
async def get_collections(
self,
) -> List[str]:
) -> list[str]:
"""Gets all collection names in the data store.
Returns:
@@ -156,7 +156,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
assert update_result.acknowledged
return record._id
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""Upserts a group of memory records into the data store. Does not guarantee that the collection exists.
If the record already exists, it will be updated.
If the record does not exist, it will be created.
@@ -169,7 +169,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
List[str] -- The unique identifiers for the memory records.
"""
upserts: List[UpdateOne] = []
upserts: list[UpdateOne] = []
for record in records:
document = memory_record_to_mongo_document(record)
upserts.append(UpdateOne(document, {"$set": document}, upsert=True))
@@ -201,7 +201,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
return document_to_memory_record(document, with_embedding) if document else None
async def get_batch(self, collection_name: str, keys: List[str], with_embeddings: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embeddings: bool) -> list[MemoryRecord]:
"""Gets a batch of memory records from the data store. Does not guarantee that the collection exists.
Arguments:
@@ -232,7 +232,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
raise ServiceResourceNotFoundError(f"collection {collection_name} not found")
await self.database[collection_name].delete_one({MONGODB_FIELD_ID: key})
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Removes a batch of memory records from the data store. Does not guarantee that the collection exists.
Arguments:
@@ -244,7 +244,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
"""
if not await self.does_collection_exist(collection_name):
raise ServiceResourceNotFoundError(f"collection {collection_name} not found")
deletes: List[DeleteOne] = [DeleteOne({MONGODB_FIELD_ID: key}) for key in keys]
deletes: list[DeleteOne] = [DeleteOne({MONGODB_FIELD_ID: key}) for key in keys]
bulk_write_result = await self.database[collection_name].bulk_write(deletes, ordered=False)
logger.debug("%s entries deleted", bulk_write_result.deleted_count)
@@ -255,7 +255,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
limit: int,
with_embeddings: bool,
min_relevance_score: float | None = None,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Gets the nearest matches to an embedding of type float. Does not guarantee that the collection exists.
Arguments:
@@ -269,7 +269,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
is its similarity score as a float.
"""
pipeline: list[dict[str, Any]] = []
vector_search_query: List[Mapping[str, Any]] = {
vector_search_query: list[Mapping[str, Any]] = {
"$vectorSearch": {
"queryVector": embedding.tolist(),
"limit": limit,
@@ -302,7 +302,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
embedding: ndarray,
with_embedding: bool,
min_relevance_score: float | None = None,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Gets the nearest match to an embedding of type float. Does not guarantee that the collection exists.
Arguments:
@@ -314,7 +314,7 @@ class MongoDBAtlasMemoryStore(MemoryStoreBase):
Returns:
Tuple[MemoryRecord, float] -- A tuple consisting of the MemoryRecord and the similarity score as a float.
"""
matches: List[Tuple[MemoryRecord, float]] = await self.get_nearest_matches(
matches: list[tuple[MemoryRecord, float]] = await self.get_nearest_matches(
collection_name=collection_name,
embedding=embedding,
limit=1,
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from numpy import array
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import List, NamedTuple, Optional, Tuple
from typing import NamedTuple
from numpy import ndarray
from pinecone import FetchResponse, IndexDescription, IndexList, Pinecone, ServerlessSpec
@@ -85,8 +85,8 @@ class PineconeMemoryStore(MemoryStoreBase):
async def create_collection(
self,
collection_name: str,
dimension_num: Optional[int] = None,
distance_type: Optional[str] = "cosine",
dimension_num: int | None = None,
distance_type: str | None = "cosine",
index_spec: NamedTuple = DEFAULT_INDEX_SPEC,
) -> None:
"""Creates a new collection in Pinecone if it does not exist.
@@ -114,7 +114,7 @@ class PineconeMemoryStore(MemoryStoreBase):
)
self.collection_names_cache.add(collection_name)
async def describe_collection(self, collection_name: str) -> Optional[IndexDescription]:
async def describe_collection(self, collection_name: str) -> IndexDescription | None:
"""Gets the description of the index.
Arguments:
collection_name {str} -- The name of the index to get.
@@ -190,7 +190,7 @@ class PineconeMemoryStore(MemoryStoreBase):
return record._id
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""Upserts a batch of records.
Arguments:
@@ -244,8 +244,8 @@ class PineconeMemoryStore(MemoryStoreBase):
return parse_payload(fetch_response.vectors[key], with_embedding)
async def get_batch(
self, collection_name: str, keys: List[str], with_embeddings: bool = False
) -> List[MemoryRecord]:
self, collection_name: str, keys: list[str], with_embeddings: bool = False
) -> list[MemoryRecord]:
"""Gets a batch of records.
Arguments:
@@ -278,7 +278,7 @@ class PineconeMemoryStore(MemoryStoreBase):
collection = self.pinecone.Index(collection_name)
collection.delete([key])
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Removes a batch of records.
Arguments:
@@ -302,7 +302,7 @@ class PineconeMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float = 0.0,
with_embedding: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Gets the nearest match to an embedding using cosine similarity.
Arguments:
@@ -330,7 +330,7 @@ class PineconeMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float = 0.0,
with_embeddings: bool = False,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Gets the nearest matches to an embedding using cosine similarity.
Arguments:
@@ -388,7 +388,7 @@ class PineconeMemoryStore(MemoryStoreBase):
)
async def __get_batch(
self, collection_name: str, keys: List[str], with_embeddings: bool = False
self, collection_name: str, keys: list[str], with_embeddings: bool = False
) -> "FetchResponse":
index = self.pinecone.Index(collection_name)
if len(keys) > MAX_FETCH_BATCH_SIZE:
@@ -3,7 +3,6 @@
import atexit
import json
import logging
from typing import List, Optional, Tuple
import numpy as np
from numpy import ndarray
@@ -83,7 +82,7 @@ class PostgresMemoryStore(MemoryStoreBase):
async def create_collection(
self,
collection_name: str,
dimension_num: Optional[int] = None,
dimension_num: int | None = None,
) -> None:
"""Creates a new collection.
@@ -119,7 +118,7 @@ class PostgresMemoryStore(MemoryStoreBase):
(),
)
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
"""Gets the list of collections.
Returns:
@@ -200,7 +199,7 @@ class PostgresMemoryStore(MemoryStoreBase):
raise ServiceResponseException("Upsert failed")
return result[0]
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""Upserts a batch of records.
Arguments:
@@ -293,8 +292,8 @@ class PostgresMemoryStore(MemoryStoreBase):
)
async def get_batch(
self, collection_name: str, keys: List[str], with_embeddings: bool = False
) -> List[MemoryRecord]:
self, collection_name: str, keys: list[str], with_embeddings: bool = False
) -> list[MemoryRecord]:
"""Gets a batch of records.
Arguments:
@@ -363,7 +362,7 @@ class PostgresMemoryStore(MemoryStoreBase):
(key,),
)
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Removes a batch of records.
Arguments:
@@ -394,7 +393,7 @@ class PostgresMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float = 0.0,
with_embeddings: bool = False,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Gets the nearest matches to an embedding using cosine similarity.
Arguments:
@@ -463,7 +462,7 @@ class PostgresMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float = 0.0,
with_embedding: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Gets the nearest match to an embedding using cosine similarity.
Arguments:
@@ -491,7 +490,7 @@ class PostgresMemoryStore(MemoryStoreBase):
results = await self.__get_collections(cur)
return collection_name in results
async def __get_collections(self, cur: Cursor) -> List[str]:
async def __get_collections(self, cur: Cursor) -> list[str]:
cur.execute(
"""
SELECT table_name
@@ -8,7 +8,6 @@ The QdrantMemoryStore inherits from MemoryStoreBase for persisting/retrieving da
import asyncio
import logging
import uuid
from typing import List, Optional, Tuple
from numpy import ndarray
from qdrant_client import QdrantClient
@@ -29,9 +28,9 @@ class QdrantMemoryStore(MemoryStoreBase):
def __init__(
self,
vector_size: int,
url: Optional[str] = None,
port: Optional[int] = 6333,
local: Optional[bool] = False,
url: str | None = None,
port: int | None = 6333,
local: bool | None = False,
**kwargs,
) -> None:
"""Initializes a new instance of the QdrantMemoryStore class."""
@@ -66,7 +65,7 @@ class QdrantMemoryStore(MemoryStoreBase):
async def get_collections(
self,
) -> List[str]:
) -> list[str]:
"""Gets the list of collections.
Returns:
@@ -136,7 +135,7 @@ class QdrantMemoryStore(MemoryStoreBase):
else:
raise ServiceResponseException("Upsert failed")
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
tasks = []
for record in records:
tasks.append(
@@ -158,7 +157,7 @@ class QdrantMemoryStore(MemoryStoreBase):
else:
raise ServiceResponseException("Batch upsert failed")
async def get(self, collection_name: str, key: str, with_embedding: bool = False) -> Optional[MemoryRecord]:
async def get(self, collection_name: str, key: str, with_embedding: bool = False) -> MemoryRecord | None:
result = await self._get_existing_record_by_payload_id(
collection_name=collection_name,
payload_id=key,
@@ -181,8 +180,8 @@ class QdrantMemoryStore(MemoryStoreBase):
return None
async def get_batch(
self, collection_name: str, keys: List[str], with_embeddings: bool = False
) -> List[MemoryRecord]:
self, collection_name: str, keys: list[str], with_embeddings: bool = False
) -> list[MemoryRecord]:
tasks = []
for key in keys:
tasks.append(
@@ -207,7 +206,7 @@ class QdrantMemoryStore(MemoryStoreBase):
if result.status != qdrant_models.UpdateStatus.COMPLETED:
raise ServiceResponseException("Delete failed")
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
tasks = []
for key in keys:
tasks.append(
@@ -235,7 +234,7 @@ class QdrantMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float,
with_embeddings: bool = False,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
match_results = self._qdrantclient.search(
collection_name=collection_name,
query_vector=embedding,
@@ -268,7 +267,7 @@ class QdrantMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float,
with_embedding: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
result = await self.get_nearest_matches(
collection_name=collection_name,
embedding=embedding,
@@ -283,7 +282,7 @@ class QdrantMemoryStore(MemoryStoreBase):
collection_name: str,
payload_id: str,
with_embedding: bool = False,
) -> Optional[qdrant_models.ScoredPoint]:
) -> qdrant_models.ScoredPoint | None:
"""Gets an existing record based upon payload id.
Arguments:
@@ -1,7 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import List, Tuple
import numpy as np
import redis
@@ -137,7 +136,7 @@ class RedisMemoryStore(MemoryStoreBase):
except Exception as e:
raise ServiceResponseException(f"Failed to create collection {collection_name}") from e
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
"""
Returns a list of names of all collection names present in the data store.
@@ -210,7 +209,7 @@ class RedisMemoryStore(MemoryStoreBase):
except Exception as e:
raise ServiceResponseException("Could not upsert messages.") from e
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
"""
Upserts a group of memory records into the data store. Does not guarantee that the collection exists.
* If the record already exists, it will be updated.
@@ -263,8 +262,8 @@ class RedisMemoryStore(MemoryStoreBase):
return record
async def get_batch(
self, collection_name: str, keys: List[str], with_embeddings: bool = False
) -> List[MemoryRecord]:
self, collection_name: str, keys: list[str], with_embeddings: bool = False
) -> list[MemoryRecord]:
"""
Gets a batch of memory records from the data store. Does not guarantee that the collection exists.
@@ -299,7 +298,7 @@ class RedisMemoryStore(MemoryStoreBase):
self._database.delete(get_redis_key(collection_name, key))
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""
Removes a batch of memory records from the data store. Does not guarantee that the collection exists.
@@ -319,7 +318,7 @@ class RedisMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float = 0.0,
with_embeddings: bool = False,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""
Get the nearest matches to an embedding using the configured similarity algorithm.
@@ -372,7 +371,7 @@ class RedisMemoryStore(MemoryStoreBase):
embedding: ndarray,
min_relevance_score: float = 0.0,
with_embedding: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""
Get the nearest match to an embedding using the configured similarity algorithm.
@@ -2,7 +2,7 @@
import json
from datetime import datetime
from typing import Any, Dict, Tuple
from typing import Any
import numpy as np
from redis import Redis
@@ -25,7 +25,7 @@ def get_redis_key(collection_name: str, record_id: str) -> str:
return f"{collection_name}:{record_id}"
def split_redis_key(redis_key: str) -> Tuple[str, str]:
def split_redis_key(redis_key: str) -> tuple[str, str]:
"""
Split a Redis key into its collection name and record ID
@@ -39,7 +39,7 @@ def split_redis_key(redis_key: str) -> Tuple[str, str]:
return collection, record_id
def serialize_record_to_redis(record: MemoryRecord, vector_type: np.dtype) -> Dict[str, Any]:
def serialize_record_to_redis(record: MemoryRecord, vector_type: np.dtype) -> dict[str, Any]:
all_metadata = {
"is_reference": record._is_reference,
"external_source_name": record._external_source_name or "",
@@ -58,7 +58,7 @@ def serialize_record_to_redis(record: MemoryRecord, vector_type: np.dtype) -> Di
return redis_mapping
def deserialize_redis_to_record(fields: Dict[str, Any], vector_type: np.dtype, with_embedding: bool) -> MemoryRecord:
def deserialize_redis_to_record(fields: dict[str, Any], vector_type: np.dtype, with_embedding: bool) -> MemoryRecord:
metadata = json.loads(fields[b"metadata"])
record = MemoryRecord(
id=metadata["id"],
@@ -6,7 +6,6 @@ import os
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union
import numpy as np
import pandas as pd
@@ -39,7 +38,7 @@ class _USearchCollection:
embeddings_index: Index
embeddings_data_table: pa.Table
embeddings_id_to_label: Dict[str, int]
embeddings_id_to_label: dict[str, int]
@staticmethod
def create_default(embeddings_index: Index) -> "_USearchCollection":
@@ -84,13 +83,13 @@ class _CollectionFileType(Enum):
# Mapping of collection file types to their file extensions.
_collection_file_extensions: Dict[_CollectionFileType, str] = {
_collection_file_extensions: dict[_CollectionFileType, str] = {
_CollectionFileType.USEARCH: ".usearch",
_CollectionFileType.PARQUET: ".parquet",
}
def memoryrecords_to_pyarrow_table(records: List[MemoryRecord]) -> pa.Table:
def memoryrecords_to_pyarrow_table(records: list[MemoryRecord]) -> pa.Table:
"""Convert a list of `MemoryRecord` to a PyArrow Table"""
records_pylist = [
{attr: getattr(record, "_" + attr) for attr in _embeddings_data_schema.names} for record in records
@@ -98,7 +97,7 @@ def memoryrecords_to_pyarrow_table(records: List[MemoryRecord]) -> pa.Table:
return pa.Table.from_pylist(records_pylist, schema=_embeddings_data_schema)
def pyarrow_table_to_memoryrecords(table: pa.Table, vectors: Optional[ndarray] = None) -> List[MemoryRecord]:
def pyarrow_table_to_memoryrecords(table: pa.Table, vectors: ndarray | None = None) -> list[MemoryRecord]:
"""Convert a PyArrow Table to a list of MemoryRecords.
Args:
@@ -121,7 +120,7 @@ def pyarrow_table_to_memoryrecords(table: pa.Table, vectors: Optional[ndarray] =
class USearchMemoryStore(MemoryStoreBase):
def __init__(
self,
persist_directory: Optional[os.PathLike] = None,
persist_directory: os.PathLike | None = None,
) -> None:
"""
Create a USearchMemoryStore instance.
@@ -140,7 +139,7 @@ class USearchMemoryStore(MemoryStoreBase):
"""
self._persist_directory = Path(persist_directory) if persist_directory is not None else None
self._collections: Dict[str, _USearchCollection] = {}
self._collections: dict[str, _USearchCollection] = {}
if self._persist_directory:
self._collections = self._read_collections_from_dir()
@@ -168,11 +167,11 @@ class USearchMemoryStore(MemoryStoreBase):
self,
collection_name: str,
ndim: int = 0,
metric: Union[str, MetricKind, CompiledMetric] = MetricKind.IP,
dtype: Optional[Union[str, ScalarKind]] = None,
connectivity: Optional[int] = None,
expansion_add: Optional[int] = None,
expansion_search: Optional[int] = None,
metric: str | MetricKind | CompiledMetric = MetricKind.IP,
dtype: str | ScalarKind | None = None,
connectivity: int | None = None,
expansion_add: int | None = None,
expansion_search: int | None = None,
view: bool = False,
) -> None:
"""Create a new collection.
@@ -219,7 +218,7 @@ class USearchMemoryStore(MemoryStoreBase):
return None
def _read_embeddings_table(self, path: os.PathLike) -> Tuple[pa.Table, Dict[str, int]]:
def _read_embeddings_table(self, path: os.PathLike) -> tuple[pa.Table, dict[str, int]]:
"""Read embeddings from the provided path and generate an ID to label mapping.
Args:
@@ -229,7 +228,7 @@ class USearchMemoryStore(MemoryStoreBase):
Tuple of embeddings table and a dictionary mapping from record ID to its label.
"""
embeddings_table = pq.read_table(path, schema=_embeddings_data_schema)
embeddings_id_to_label: Dict[str, int] = {
embeddings_id_to_label: dict[str, int] = {
record_id: idx for idx, record_id in enumerate(embeddings_table.column("id").to_pylist())
}
return embeddings_table, embeddings_id_to_label
@@ -239,7 +238,7 @@ class USearchMemoryStore(MemoryStoreBase):
# str cast is temporarily fix for https://github.com/unum-cloud/usearch/issues/196
return Index.restore(str(path), view=False)
def _read_collections_from_dir(self) -> Dict[str, _USearchCollection]:
def _read_collections_from_dir(self) -> dict[str, _USearchCollection]:
"""Read all collections from directory to memory.
Raises:
@@ -249,7 +248,7 @@ class USearchMemoryStore(MemoryStoreBase):
Dict[str, _USearchCollection]: Dictionary with collection names as keys and
their _USearchCollection as values.
"""
collections: Dict[str, _USearchCollection] = {}
collections: dict[str, _USearchCollection] = {}
for collection_name, collection_files in self._get_all_storage_files().items():
expected_storage_files = len(_CollectionFileType)
@@ -272,7 +271,7 @@ class USearchMemoryStore(MemoryStoreBase):
return collections
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
"""Get list of existing collections.
Returns:
@@ -300,14 +299,14 @@ class USearchMemoryStore(MemoryStoreBase):
async def upsert_batch(
self,
collection_name: str,
records: List[MemoryRecord],
records: list[MemoryRecord],
*,
compact: bool = False,
copy: bool = True,
threads: int = 0,
log: Union[str, bool] = False,
log: str | bool = False,
batch_size: int = 0,
) -> List[str]:
) -> list[str]:
"""Upsert a batch of MemoryRecords and return their IDs.
Args:
@@ -384,10 +383,10 @@ class USearchMemoryStore(MemoryStoreBase):
async def get_batch(
self,
collection_name: str,
keys: List[str],
keys: list[str],
with_embeddings: bool,
dtype: ScalarKind = ScalarKind.F32,
) -> List[MemoryRecord]:
) -> list[MemoryRecord]:
"""Retrieve a batch of MemoryRecords using their keys."""
collection_name = collection_name.lower()
if collection_name not in self._collections:
@@ -407,7 +406,7 @@ class USearchMemoryStore(MemoryStoreBase):
await self.remove_batch(collection_name=collection_name, keys=[key])
return None
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
"""Remove a batch of MemoryRecords using their keys."""
collection_name = collection_name.lower()
if collection_name not in self._collections:
@@ -429,7 +428,7 @@ class USearchMemoryStore(MemoryStoreBase):
min_relevance_score: float = 0.0,
with_embedding: bool = True,
exact: bool = False,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
"""Retrieve the nearest matching MemoryRecord for the provided embedding.
By default it is approximately search, see `exact` param description.
@@ -469,9 +468,9 @@ class USearchMemoryStore(MemoryStoreBase):
*,
threads: int = 0,
exact: bool = False,
log: Union[str, bool] = False,
log: str | bool = False,
batch_size: int = 0,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
"""Get the nearest matches to a given embedding.
By default it is approximately search, see `exact` param description.
@@ -500,7 +499,7 @@ class USearchMemoryStore(MemoryStoreBase):
collection_name = collection_name.lower()
ucollection = self._collections[collection_name]
result: Union[Matches, BatchMatches] = ucollection.embeddings_index.search(
result: Matches | BatchMatches = ucollection.embeddings_index.search(
vectors=embedding,
count=limit,
threads=threads,
@@ -513,7 +512,7 @@ class USearchMemoryStore(MemoryStoreBase):
relevance_score = 1 / (result.distances + 1)
filtered_labels = result.keys[np.where(relevance_score >= min_relevance_score)[0]]
filtered_vectors: Optional[np.ndarray] = None
filtered_vectors: np.ndarray | None = None
if with_embeddings:
filtered_vectors = ucollection.embeddings_index.get(filtered_labels)
@@ -527,7 +526,7 @@ class USearchMemoryStore(MemoryStoreBase):
)
]
def _get_all_storage_files(self) -> Dict[str, List[Path]]:
def _get_all_storage_files(self) -> dict[str, list[Path]]:
"""Return storage files for each collection in `self._persist_directory`.
Collection name is derived from file name and converted to lowercase. Files with extensions that
@@ -543,7 +542,7 @@ class USearchMemoryStore(MemoryStoreBase):
raise ServiceInitializationError("Persist directory is not set")
storage_exts = _collection_file_extensions.values()
collection_storage_files: Dict[str, List[Path]] = {}
collection_storage_files: dict[str, list[Path]] = {}
for path in self._persist_directory.iterdir():
if path.is_file() and (path.suffix in storage_exts):
collection_name = path.stem.lower()
@@ -3,7 +3,6 @@
import asyncio
import logging
from dataclasses import dataclass
from typing import List, Tuple
import numpy as np
import weaviate
@@ -176,7 +175,7 @@ class WeaviateMemoryStore(MemoryStoreBase):
schema["class"] = collection_name
await asyncio.get_running_loop().run_in_executor(None, self.client.schema.create_class, schema)
async def get_collections(self) -> List[str]:
async def get_collections(self) -> list[str]:
schemas = await asyncio.get_running_loop().run_in_executor(None, self.client.schema.get)
return [schema["class"] for schema in schemas["classes"]]
@@ -202,7 +201,7 @@ class WeaviateMemoryStore(MemoryStoreBase):
vector,
)
async def upsert_batch(self, collection_name: str, records: List[MemoryRecord]) -> List[str]:
async def upsert_batch(self, collection_name: str, records: list[MemoryRecord]) -> list[str]:
def _upsert_batch_inner():
results = []
with self.client.batch as batch:
@@ -227,7 +226,7 @@ class WeaviateMemoryStore(MemoryStoreBase):
results = await self.get_batch(collection_name, [key], with_embedding)
return results[0] if results else None
async def get_batch(self, collection_name: str, keys: List[str], with_embedding: bool) -> List[MemoryRecord]:
async def get_batch(self, collection_name: str, keys: list[str], with_embedding: bool) -> list[MemoryRecord]:
queries = self._build_multi_get_query(collection_name, keys, with_embedding)
results = await asyncio.get_running_loop().run_in_executor(None, self.client.query.multi_get(queries).do)
@@ -240,7 +239,7 @@ class WeaviateMemoryStore(MemoryStoreBase):
return memory_records
def _build_multi_get_query(self, collection_name: str, keys: List[str], with_embedding: bool):
def _build_multi_get_query(self, collection_name: str, keys: list[str], with_embedding: bool):
queries = []
for i, key in enumerate(keys):
query = self.client.query.get(collection_name, ALL_PROPERTIES).with_where(
@@ -270,7 +269,7 @@ class WeaviateMemoryStore(MemoryStoreBase):
async def remove(self, collection_name: str, key: str) -> None:
await self.remove_batch(collection_name, [key])
async def remove_batch(self, collection_name: str, keys: List[str]) -> None:
async def remove_batch(self, collection_name: str, keys: list[str]) -> None:
# TODO: Use In operator when it's available
# (https://github.com/weaviate/weaviate/issues/2387)
# and handle max delete objects
@@ -293,7 +292,7 @@ class WeaviateMemoryStore(MemoryStoreBase):
limit: int,
min_relevance_score: float,
with_embeddings: bool,
) -> List[Tuple[MemoryRecord, float]]:
) -> list[tuple[MemoryRecord, float]]:
nearVector = {
"vector": embedding,
"certainty": min_relevance_score,
@@ -332,7 +331,7 @@ class WeaviateMemoryStore(MemoryStoreBase):
embedding: np.ndarray,
min_relevance_score: float,
with_embedding: bool,
) -> Tuple[MemoryRecord, float]:
) -> tuple[MemoryRecord, float]:
results = await self.get_nearest_matches(
collection_name,
embedding,
@@ -1,6 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from enum import Enum
@@ -1,8 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import Any, Awaitable, Callable
from collections.abc import Awaitable, Callable
from typing import Any
from semantic_kernel.connectors.openapi_plugin.openapi_function_execution_parameters import (
OpenAPIFunctionExecutionParameters,
@@ -1,6 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from typing import Any
@@ -1,8 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import Any, Awaitable, Callable, List
from collections.abc import Awaitable, Callable
from typing import Any
from urllib.parse import urlparse
import httpx
@@ -25,7 +25,7 @@ class OpenAPIFunctionExecutionParameters(KernelBaseModel):
user_agent: str | None = None
enable_dynamic_payload: bool = True
enable_payload_namespacing: bool = False
operations_to_exclude: List[str] = Field(default_factory=list)
operations_to_exclude: list[str] = Field(default_factory=list)
def model_post_init(self, __context: Any) -> None:
from semantic_kernel.connectors.telemetry import HTTP_USER_AGENT
@@ -1,12 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import json
import logging
import re
from collections.abc import Callable, Mapping
from enum import Enum
from typing import TYPE_CHECKING, Any, Callable, Dict, Mapping, Tuple
from typing import TYPE_CHECKING, Any
from urllib.parse import urlencode, urljoin, urlparse, urlunparse
import httpx
@@ -43,7 +42,7 @@ class RestApiOperationPayloadProperty:
self,
name: str,
type: str,
properties: RestApiOperationPayloadProperty,
properties: "RestApiOperationPayloadProperty",
description: str | None = None,
is_required: bool = False,
default_value: Any | None = None,
@@ -63,7 +62,7 @@ class RestApiOperationPayload:
def __init__(
self,
media_type: str,
properties: list[RestApiOperationPayloadProperty],
properties: list["RestApiOperationPayloadProperty"],
description: str | None = None,
schema: str | None = None,
):
@@ -88,8 +87,8 @@ class RestApiOperation:
path: str,
summary: str | None = None,
description: str | None = None,
params: list[RestApiOperationParameter] | None = None,
request_body: RestApiOperationPayload | None = None,
params: list["RestApiOperationParameter"] | None = None,
request_body: "RestApiOperationPayload | None" = None,
):
self.id = id
self.method = method.upper()
@@ -110,7 +109,7 @@ class RestApiOperation:
full_path = urljoin(base_path, path.lstrip("/"))
return urlunparse(parsed_base._replace(path=full_path))
def build_headers(self, arguments: Dict[str, Any]) -> Dict[str, str]:
def build_headers(self, arguments: dict[str, Any]) -> dict[str, str]:
headers = {}
parameters = [p for p in self.parameters if p.location == RestApiOperationParameterLocation.HEADER]
@@ -151,7 +150,7 @@ class RestApiOperation:
return urlparse(server_url_string)
def build_path(self, path_template: str, arguments: Dict[str, Any]) -> str:
def build_path(self, path_template: str, arguments: dict[str, Any]) -> str:
parameters = [p for p in self.parameters if p.location == RestApiOperationParameterLocation.PATH]
for parameter in parameters:
argument = arguments.get(parameter.name)
@@ -165,7 +164,7 @@ class RestApiOperation:
path_template = path_template.replace(f"{{{parameter.name}}}", str(argument))
return path_template
def build_query_string(self, arguments: Dict[str, Any]) -> str:
def build_query_string(self, arguments: dict[str, Any]) -> str:
segments = []
parameters = [p for p in self.parameters if p.location == RestApiOperationParameterLocation.QUERY]
for parameter in parameters:
@@ -185,10 +184,10 @@ class RestApiOperation:
def get_parameters(
self,
operation: RestApiOperation,
operation: "RestApiOperation",
add_payload_params_from_metadata: bool = True,
enable_payload_spacing: bool = False,
) -> list[RestApiOperationParameter]:
) -> list["RestApiOperationParameter"]:
params = list(operation.parameters)
if operation.request_body is not None:
params.extend(
@@ -204,7 +203,7 @@ class RestApiOperation:
return params
def create_payload_artificial_parameter(self, operation: RestApiOperation) -> RestApiOperationParameter:
def create_payload_artificial_parameter(self, operation: "RestApiOperation") -> "RestApiOperationParameter":
return RestApiOperationParameter(
name=self.PAYLOAD_ARGUMENT_NAME,
type=(
@@ -220,7 +219,7 @@ class RestApiOperation:
schema=operation.request_body.schema if operation.request_body else None,
)
def create_content_type_artificial_parameter(self) -> RestApiOperationParameter:
def create_content_type_artificial_parameter(self) -> "RestApiOperationParameter":
return RestApiOperationParameter(
name=self.CONTENT_TYPE_ARGUMENT_NAME,
type="string",
@@ -239,10 +238,10 @@ class RestApiOperation:
def _get_parameters_from_payload_metadata(
self,
properties: list[RestApiOperationPayloadProperty],
properties: list["RestApiOperationPayloadProperty"],
enable_namespacing: bool = False,
root_property_name: bool = None,
) -> list[RestApiOperationParameter]:
) -> list["RestApiOperationParameter"]:
parameters: list[RestApiOperationParameter] = []
for property in properties:
parameter_name = self._get_property_name(property, root_property_name, enable_namespacing)
@@ -264,7 +263,7 @@ class RestApiOperation:
return parameters
def get_payload_parameters(
self, operation: RestApiOperation, use_parameters_from_metadata: bool, enable_namespacing: bool
self, operation: "RestApiOperation", use_parameters_from_metadata: bool, enable_namespacing: bool
):
if use_parameters_from_metadata:
if operation.request_body is None:
@@ -308,7 +307,6 @@ class RestApiOperationParameter:
default_value: Any | None = None,
schema: str | None = None,
):
self.name = name
self.type = type
self.location = location
@@ -424,7 +422,7 @@ class OpenApiParser:
self,
parsed_document: Any,
execution_settings: "OpenAIFunctionExecutionParameters | OpenAPIFunctionExecutionParameters | None" = None,
) -> Dict[str, RestApiOperation]:
) -> dict[str, RestApiOperation]:
"""Create the REST API Operations from the parsed OpenAPI document.
Args:
@@ -502,7 +500,7 @@ class OpenApiRunner:
def __init__(
self,
parsed_openapi_document: Mapping[str, str],
auth_callback: Callable[[Dict[str, str]], Dict[str, str]] | None = None,
auth_callback: Callable[[dict[str, str]], dict[str, str]] | None = None,
http_client: httpx.AsyncClient | None = None,
enable_dynamic_payload: bool = True,
enable_payload_namespacing: bool = False,
@@ -527,8 +525,8 @@ class OpenApiRunner:
return self.build_full_url(url, operation.build_query_string(arguments))
def build_json_payload(
self, payload_metadata: RestApiOperationPayload, arguments: Dict[str, Any]
) -> Tuple[str, str]:
self, payload_metadata: RestApiOperationPayload, arguments: dict[str, Any]
) -> tuple[str, str]:
"""Build the JSON payload."""
if self.enable_dynamic_payload:
if payload_metadata is None:
@@ -566,7 +564,7 @@ class OpenApiRunner:
)
return result
def build_operation_payload(self, operation: RestApiOperation, arguments: KernelArguments) -> Tuple[str, str]:
def build_operation_payload(self, operation: RestApiOperation, arguments: KernelArguments) -> tuple[str, str]:
if operation.request_body is None and self.payload_argument_name not in arguments:
return None, None
return self.build_json_payload(operation.request_body, arguments)
@@ -2,7 +2,6 @@
import logging
import urllib
from typing import List
import aiohttp
from pydantic import ValidationError
@@ -41,7 +40,7 @@ class BingConnector(ConnectorBase):
)
assert self._api_key, "API key cannot be 'None' or empty."
async def search(self, query: str, num_results: int = 1, offset: int = 0) -> List[str]:
async def search(self, query: str, num_results: int = 1, offset: int = 0) -> list[str]:
"""
Returns the search results of the query provided by pinging the Bing web search API.
Returns `num_results` results and ignores the first `offset`.
@@ -1,7 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
from abc import ABC, abstractmethod
from typing import List
class ConnectorBase(ABC):
@@ -10,5 +9,5 @@ class ConnectorBase(ABC):
"""
@abstractmethod
async def search(self, query: str, num_results: int = 1, offset: int = 0) -> List[str]:
async def search(self, query: str, num_results: int = 1, offset: int = 0) -> list[str]:
pass
@@ -2,7 +2,6 @@
import logging
import urllib
from typing import List
import aiohttp
@@ -30,7 +29,7 @@ class GoogleConnector(ConnectorBase):
if not self._search_engine_id:
raise ServiceInitializationError("Google search engine ID cannot be null.")
async def search(self, query: str, num_results: int = 1, offset: int = 0) -> List[str]:
async def search(self, query: str, num_results: int = 1, offset: int = 0) -> list[str]:
"""
Returns the search results of the query provided by pinging the Google Custom search API.
Returns `num_results` results and ignores the first `offset`.
@@ -2,7 +2,7 @@
import os
from importlib.metadata import PackageNotFoundError, version
from typing import Any, Dict
from typing import Any
from semantic_kernel.connectors.ai.open_ai.const import USER_AGENT
@@ -26,7 +26,7 @@ APP_INFO = (
)
def prepend_semantic_kernel_to_user_agent(headers: Dict[str, Any]):
def prepend_semantic_kernel_to_user_agent(headers: dict[str, Any]):
"""
Prepend "Semantic-Kernel" to the User-Agent in the headers.
@@ -1,7 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Any, Callable, Optional
from collections.abc import Callable
from typing import Any
import httpx
@@ -16,8 +17,8 @@ class DocumentLoader:
async def from_uri(
url: str,
http_client: httpx.AsyncClient,
auth_callback: Optional[Callable[[Any], None]],
user_agent: Optional[str] = HTTP_USER_AGENT,
auth_callback: Callable[[Any], None] | None,
user_agent: str | None = HTTP_USER_AGENT,
):
"""Load the manifest from the given URL"""
headers = {"User-Agent": user_agent}
@@ -1,10 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from collections.abc import Generator
from functools import singledispatchmethod
from html import unescape
from typing import Any, Generator
from typing import Any
from xml.etree.ElementTree import Element, tostring
from defusedxml.ElementTree import XML, ParseError
@@ -288,7 +288,7 @@ class ChatHistory(KernelBaseModel):
raise ContentSerializationError(f"Unable to serialize ChatHistory to JSON: {e}") from e
@classmethod
def restore_chat_history(cls, chat_history_json: str) -> ChatHistory:
def restore_chat_history(cls, chat_history_json: str) -> "ChatHistory":
"""
Restores a ChatHistory instance from a JSON string.
@@ -320,7 +320,7 @@ class ChatHistory(KernelBaseModel):
file.write(json_str)
@classmethod
def load_chat_history_from_file(cls, file_path: str) -> ChatHistory:
def load_chat_history_from_file(cls, file_path: str) -> "ChatHistory":
"""
Loads the ChatHistory from a file.
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from enum import Enum
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import json
import logging
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from functools import cached_property
from typing import TYPE_CHECKING, Any
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from enum import Enum
from typing import Any, Union, overload
@@ -163,7 +162,7 @@ class StreamingChatMessageContent(ChatMessageContent, StreamingContentMixin):
"""Return the content of the response encoded in the encoding."""
return self.content.encode(self.encoding if self.encoding else "utf-8") if self.content else b""
def __add__(self, other: StreamingChatMessageContent) -> StreamingChatMessageContent:
def __add__(self, other: "StreamingChatMessageContent") -> "StreamingChatMessageContent":
"""When combining two StreamingChatMessageContent instances, the content fields are combined.
The inner_content of the first one is used, ai_model_id and encoding should be the same,
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from html import unescape
from xml.etree.ElementTree import Element
@@ -1,12 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
import sys
from typing import TYPE_CHECKING
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from typing import TYPE_CHECKING, Annotated
if TYPE_CHECKING:
from semantic_kernel.functions.kernel_arguments import KernelArguments
@@ -1,16 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import sys
from typing import Any, Dict, Optional
from typing import Annotated, Any
import aiohttp
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from semantic_kernel.exceptions import FunctionExecutionException
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
@@ -52,7 +46,7 @@ class HttpPlugin(KernelBaseModel):
async def post(
self,
url: Annotated[str, "The URI to send the request to."],
body: Annotated[Optional[Dict[str, Any]], "The body of the request"] = {},
body: Annotated[dict[str, Any] | None, "The body of the request"] = {},
) -> str:
"""
Sends an HTTP POST request to the specified URI and returns
@@ -76,7 +70,7 @@ class HttpPlugin(KernelBaseModel):
async def put(
self,
url: Annotated[str, "The URI to send the request to."],
body: Annotated[Optional[Dict[str, Any]], "The body of the request"] = {},
body: Annotated[dict[str, Any] | None, "The body of the request"] = {},
) -> str:
"""
Sends an HTTP PUT request to the specified URI and returns
@@ -1,10 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
import sys
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from typing import Annotated
from semantic_kernel.functions.kernel_function_decorator import kernel_function
@@ -1,12 +1,12 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
import os
import re
from collections.abc import Awaitable, Callable
from io import BufferedReader, BytesIO
from typing import Annotated, Any, Awaitable, Callable
from typing import Annotated, Any
import httpx
from pydantic import ValidationError, field_validator
@@ -1,6 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import uuid
from enum import Enum
@@ -1,16 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import logging
import sys
from typing import Any, Dict, Final
from typing import Annotated, Any, Final
from pydantic import Field
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.memory.semantic_text_memory_base import SemanticTextMemoryBase
@@ -26,9 +20,9 @@ DEFAULT_LIMIT: Final[int] = 1
class TextMemoryPlugin(KernelBaseModel):
memory: SemanticTextMemoryBase
embeddings_kwargs: Dict[str, Any] = Field(default_factory=dict)
embeddings_kwargs: dict[str, Any] = Field(default_factory=dict)
def __init__(self, memory: SemanticTextMemoryBase, embeddings_kwargs: Dict[str, Any] = {}) -> None:
def __init__(self, memory: SemanticTextMemoryBase, embeddings_kwargs: dict[str, Any] = {}) -> None:
"""
Initialize a new instance of the TextMemoryPlugin
@@ -1,16 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import sys
from typing import Union
from typing import Annotated
from semantic_kernel.exceptions import FunctionExecutionException
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
@@ -27,9 +20,7 @@ class WaitPlugin(KernelBaseModel):
"""
@kernel_function(description="Wait for a certain number of seconds.")
async def wait(
self, input: Annotated[Union[float, str], "The number of seconds to wait, can be str or float."]
) -> None:
async def wait(self, input: Annotated[float | str, "The number of seconds to wait, can be str or float."]) -> None:
if isinstance(input, str):
try:
input = float(input)
@@ -1,10 +1,4 @@
import sys
from typing import TYPE_CHECKING, List, Optional
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from typing import TYPE_CHECKING, Annotated
from semantic_kernel.functions.kernel_function_decorator import kernel_function
@@ -35,9 +29,9 @@ class WebSearchEnginePlugin:
async def search(
self,
query: Annotated[str, "The search query"],
num_results: Annotated[Optional[int], "The number of search results to return"] = 1,
offset: Annotated[Optional[int], "The number of search results to skip"] = 0,
) -> List[str]:
num_results: Annotated[int | None, "The number of search results to return"] = 1,
offset: Annotated[int | None, "The number of search results to skip"] = 0,
) -> list[str]:
"""
Returns the search results of the query provided.
Returns `num_results` results and ignores the first `offset`.
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from typing import Any
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import TYPE_CHECKING, Any
@@ -11,7 +10,7 @@ class KernelArguments(dict):
def __init__(
self,
settings: (
"PromptExecutionSettings" | list["PromptExecutionSettings"] | dict[str, "PromptExecutionSettings"] | None
"PromptExecutionSettings | list[PromptExecutionSettings] | dict[str, PromptExecutionSettings] | None"
) = None,
**kwargs: Any,
):
@@ -1,12 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from abc import abstractmethod
from collections.abc import AsyncGenerator
from collections.abc import AsyncGenerator, Callable
from copy import copy, deepcopy
from inspect import isasyncgen, isgenerator
from typing import TYPE_CHECKING, Any, Callable
from typing import TYPE_CHECKING, Any
from semantic_kernel.filters.filter_types import FilterTypes
from semantic_kernel.filters.functions.function_invocation_context import FunctionInvocationContext
@@ -77,12 +76,12 @@ class KernelFunction(KernelBaseModel):
description: str | None = None,
prompt: str | None = None,
template_format: TEMPLATE_FORMAT_TYPES = KERNEL_TEMPLATE_FORMAT_NAME,
prompt_template: PromptTemplateBase | None = None,
prompt_template_config: PromptTemplateConfig | None = None,
prompt_template: "PromptTemplateBase | None " = None,
prompt_template_config: "PromptTemplateConfig | None" = None,
prompt_execution_settings: (
PromptExecutionSettings | list[PromptExecutionSettings] | dict[str, PromptExecutionSettings] | None
"PromptExecutionSettings | list[PromptExecutionSettings] | dict[str, PromptExecutionSettings] | None"
) = None,
) -> KernelFunctionFromPrompt:
) -> "KernelFunctionFromPrompt":
"""
Create a new instance of the KernelFunctionFromPrompt class.
"""
@@ -105,7 +104,7 @@ class KernelFunction(KernelBaseModel):
method: Callable[..., Any],
plugin_name: str | None = None,
stream_method: Callable[..., Any] | None = None,
) -> KernelFunctionFromMethod:
) -> "KernelFunctionFromMethod":
"""
Create a new instance of the KernelFunctionFromMethod class.
"""
@@ -138,17 +137,17 @@ class KernelFunction(KernelBaseModel):
return self.metadata.is_prompt
@property
def parameters(self) -> list[KernelParameterMetadata]:
def parameters(self) -> list["KernelParameterMetadata"]:
return self.metadata.parameters
@property
def return_parameter(self) -> KernelParameterMetadata | None:
def return_parameter(self) -> "KernelParameterMetadata | None":
return self.metadata.return_parameter
async def __call__(
self,
kernel: Kernel,
arguments: KernelArguments | None = None,
kernel: "Kernel",
arguments: "KernelArguments | None" = None,
metadata: dict[str, Any] = {},
**kwargs: Any,
) -> FunctionResult | None:
@@ -180,8 +179,8 @@ class KernelFunction(KernelBaseModel):
async def invoke(
self,
kernel: Kernel,
arguments: KernelArguments | None = None,
kernel: "Kernel",
arguments: "KernelArguments | None" = None,
metadata: dict[str, Any] = {},
**kwargs: Any,
) -> "FunctionResult | None":
@@ -220,11 +219,11 @@ class KernelFunction(KernelBaseModel):
async def invoke_stream(
self,
kernel: Kernel,
arguments: KernelArguments | None = None,
kernel: "Kernel",
arguments: "KernelArguments | None" = None,
metadata: dict[str, Any] = {},
**kwargs: Any,
) -> AsyncGenerator[FunctionResult | list[StreamingContentMixin | Any], Any]:
) -> "AsyncGenerator[FunctionResult | list[StreamingContentMixin | Any], Any]":
"""
Invoke a stream async function with the given arguments.
@@ -260,7 +259,7 @@ class KernelFunction(KernelBaseModel):
else:
yield function_context.result
def function_copy(self, plugin_name: str | None = None) -> KernelFunction:
def function_copy(self, plugin_name: str | None = None) -> "KernelFunction":
"""Copy the function, can also override the plugin_name.
Args:
@@ -1,9 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from collections.abc import Callable
from inspect import get_annotations, isasyncgenfunction, isclass, isgeneratorfunction, signature
from typing import Any, Callable, ForwardRef
from typing import Any, ForwardRef
NoneType = type(None)
logger = logging.getLogger(__name__)
@@ -1,9 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from collections.abc import Callable
from inspect import isasyncgen, isasyncgenfunction, isawaitable, iscoroutinefunction, isgenerator, isgeneratorfunction
from typing import Any, Callable
from typing import Any
from pydantic import ValidationError
@@ -1,10 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
import os
from collections.abc import AsyncGenerator
from html import unescape
from typing import Any, AsyncGenerator
from typing import Any
import yaml
from pydantic import Field, ValidationError, model_validator
@@ -274,7 +274,7 @@ through prompt_template_config or in the prompt_template."
arguments[parameter.name] = parameter.default
@classmethod
def from_yaml(cls, yaml_str: str, plugin_name: str | None = None) -> KernelFunctionFromPrompt:
def from_yaml(cls, yaml_str: str, plugin_name: str | None = None) -> "KernelFunctionFromPrompt":
"""Creates a new instance of the KernelFunctionFromPrompt class from a YAML string."""
try:
data = yaml.safe_load(yaml_str)
@@ -299,7 +299,7 @@ through prompt_template_config or in the prompt_template."
)
@classmethod
def from_directory(cls, path: str, plugin_name: str | None = None) -> KernelFunctionFromPrompt:
def from_directory(cls, path: str, plugin_name: str | None = None) -> "KernelFunctionFromPrompt":
"""Creates a new instance of the KernelFunctionFromPrompt class from a directory.
The directory needs to contain:
@@ -1,7 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import Any, List, Optional
from typing import Any
from pydantic import Field
@@ -12,13 +11,13 @@ from semantic_kernel.utils.validation import FUNCTION_NAME_REGEX, PLUGIN_NAME_RE
class KernelFunctionMetadata(KernelBaseModel):
name: str = Field(pattern=FUNCTION_NAME_REGEX)
plugin_name: Optional[str] = Field(None, pattern=PLUGIN_NAME_REGEX)
description: Optional[str] = Field(default=None)
parameters: List[KernelParameterMetadata] = Field(default_factory=list)
plugin_name: str | None = Field(None, pattern=PLUGIN_NAME_REGEX)
description: str | None = Field(default=None)
parameters: list[KernelParameterMetadata] = Field(default_factory=list)
is_prompt: bool
is_asynchronous: Optional[bool] = Field(default=True)
return_parameter: Optional[KernelParameterMetadata] = None
additional_properties: Optional[dict[str, Any]] = Field(default=None)
is_asynchronous: bool | None = Field(default=True)
return_parameter: KernelParameterMetadata | None = None
additional_properties: dict[str, Any] | None = Field(default=None)
@property
def fully_qualified_name(self) -> str:
@@ -1,8 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import Any, Type
from typing import Any
from pydantic import Field, model_validator
@@ -34,7 +33,7 @@ class KernelParameterMetadata(KernelBaseModel):
@classmethod
def infer_schema(
cls, type_object: Type | None, parameter_type: str | None, default_value: Any, description: str | None
cls, type_object: type | None, parameter_type: str | None, default_value: Any, description: str | None
) -> dict[str, Any] | None:
schema = None
@@ -1,22 +1,15 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import importlib
import inspect
import json
import logging
import os
import sys
from collections.abc import Generator
from collections.abc import Generator, ItemsView
from functools import singledispatchmethod
from glob import glob
from types import MethodType
from typing import TYPE_CHECKING, Any, ItemsView
if sys.version_info >= (3, 9):
from typing import Annotated # pragma: no cover
else:
from typing_extensions import Annotated # pragma: no cover
from typing import TYPE_CHECKING, Annotated, Any
import httpx
from pydantic import Field, StringConstraints
@@ -104,8 +97,8 @@ class KernelPlugin(KernelBaseModel):
description: str | None = None,
functions: (
KERNEL_FUNCTION_TYPE
| KernelPlugin
| list[KERNEL_FUNCTION_TYPE | KernelPlugin]
| "KernelPlugin"
| list[KERNEL_FUNCTION_TYPE | "KernelPlugin"]
| dict[str, KERNEL_FUNCTION_TYPE]
| None
) = None,
@@ -199,7 +192,7 @@ class KernelPlugin(KernelBaseModel):
raise TypeError(f"Unknown type being added, type was {type(functions)}")
@add.register(list)
def add_list(self, functions: list[KERNEL_FUNCTION_TYPE | KernelPlugin]) -> None:
def add_list(self, functions: list[KERNEL_FUNCTION_TYPE | "KernelPlugin"]) -> None:
"""Add a list of functions to the plugin."""
for function in functions:
if isinstance(function, KernelPlugin):
@@ -231,7 +224,7 @@ class KernelPlugin(KernelBaseModel):
# endregion
# region Properties
def get_functions_metadata(self) -> list[KernelFunctionMetadata]:
def get_functions_metadata(self) -> list["KernelFunctionMetadata"]:
"""
Get the metadata for the functions in the plugin.
@@ -246,7 +239,7 @@ class KernelPlugin(KernelBaseModel):
@classmethod
def from_object(
cls, plugin_name: str, plugin_instance: Any | dict[str, Any], description: str | None = None
) -> KernelPlugin:
) -> "KernelPlugin":
"""
Creates a plugin that wraps the specified target object and imports it into the kernel's plugin collection
@@ -281,7 +274,7 @@ class KernelPlugin(KernelBaseModel):
parent_directory: str,
description: str | None = None,
class_init_arguments: dict[str, dict[str, Any]] | None = None,
) -> KernelPlugin:
) -> "KernelPlugin":
"""Create a plugin from a specified directory.
This method does not recurse into subdirectories beyond one level deep from the specified plugin directory.
@@ -370,9 +363,9 @@ class KernelPlugin(KernelBaseModel):
cls,
plugin_name: str,
openapi_document_path: str,
execution_settings: OpenAPIFunctionExecutionParameters | None = None,
execution_settings: "OpenAPIFunctionExecutionParameters | None" = None,
description: str | None = None,
) -> KernelPlugin:
) -> "KernelPlugin":
"""Create a plugin from an OpenAPI document.
Args:
@@ -408,9 +401,9 @@ class KernelPlugin(KernelBaseModel):
plugin_name: str,
plugin_url: str | None = None,
plugin_str: str | None = None,
execution_parameters: OpenAIFunctionExecutionParameters | None = None,
execution_parameters: "OpenAIFunctionExecutionParameters | None" = None,
description: str | None = None,
) -> KernelPlugin:
) -> "KernelPlugin":
"""Create a plugin from the Open AI manifest.
Args:
@@ -474,7 +467,7 @@ class KernelPlugin(KernelBaseModel):
py_file: str,
description: str | None = None,
class_init_arguments: dict[str, dict[str, Any]] | None = None,
) -> KernelPlugin:
) -> "KernelPlugin":
module_name = os.path.basename(py_file).replace(".py", "")
spec = importlib.util.spec_from_file_location(module_name, py_file)
if not spec:
@@ -498,13 +491,13 @@ class KernelPlugin(KernelBaseModel):
def _validate_functions(
functions: (
KERNEL_FUNCTION_TYPE
| list[KERNEL_FUNCTION_TYPE | KernelPlugin]
| list[KERNEL_FUNCTION_TYPE | "KernelPlugin"]
| dict[str, KERNEL_FUNCTION_TYPE]
| KernelPlugin
| "KernelPlugin"
| None
),
plugin_name: str,
) -> dict[str, KernelFunction]:
) -> dict[str, "KernelFunction"]:
"""Validates the functions and returns a dictionary of functions."""
if not functions or not plugin_name:
# if the plugin_name is not present, the validation will fail, so no point in parsing.
@@ -542,7 +535,7 @@ class KernelPlugin(KernelBaseModel):
raise ValueError(f"Invalid type for supplied functions: {functions} (type: {type(functions)})")
@staticmethod
def _parse_or_copy(function: KERNEL_FUNCTION_TYPE, plugin_name: str) -> KernelFunction:
def _parse_or_copy(function: KERNEL_FUNCTION_TYPE, plugin_name: str) -> "KernelFunction":
"""Handle the function and return a KernelFunction instance."""
if isinstance(function, KernelFunction):
return function.function_copy(plugin_name=plugin_name)
@@ -1,5 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.functions.function_result import FunctionResult
+2 -2
View File
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
from typing import Any, Callable, Union
from collections.abc import Callable
from typing import Any, Union
from semantic_kernel.functions.kernel_function import KernelFunction
+8 -8
View File
@@ -1,10 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import logging
from collections.abc import AsyncGenerator, AsyncIterable
from copy import copy
from functools import singledispatchmethod
from typing import TYPE_CHECKING, Any, AsyncGenerator, AsyncIterable, Literal, Type, TypeVar, Union
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union
from pydantic import Field, field_validator
@@ -146,7 +146,7 @@ class Kernel(KernelFilterExtension):
async def invoke_stream(
self,
function: "KernelFunction" | None = None,
function: "KernelFunction | None" = None,
arguments: KernelArguments | None = None,
function_name: str | None = None,
plugin_name: str | None = None,
@@ -207,7 +207,7 @@ class Kernel(KernelFilterExtension):
async def invoke(
self,
function: "KernelFunction" | None = None,
function: "KernelFunction | None" = None,
arguments: KernelArguments | None = None,
function_name: str | None = None,
plugin_name: str | None = None,
@@ -435,7 +435,7 @@ class Kernel(KernelFilterExtension):
def add_function(
self,
plugin_name: str,
function: KERNEL_FUNCTION_TYPE | None = None,
function: "KERNEL_FUNCTION_TYPE | None" = None,
function_name: str | None = None,
description: str | None = None,
prompt: str | None = None,
@@ -505,7 +505,7 @@ class Kernel(KernelFilterExtension):
def add_functions(
self,
plugin_name: str,
functions: list[KERNEL_FUNCTION_TYPE] | dict[str, KERNEL_FUNCTION_TYPE],
functions: "list[KERNEL_FUNCTION_TYPE] | dict[str, KERNEL_FUNCTION_TYPE]",
) -> "KernelPlugin":
"""
Adds a list of functions to the specified plugin.
@@ -744,7 +744,7 @@ class Kernel(KernelFilterExtension):
def get_service(
self,
service_id: str | None = None,
type: Type[ALL_SERVICE_TYPES] | None = None,
type: type[ALL_SERVICE_TYPES] | None = None,
) -> "AIServiceClientBase":
"""Get a service by service_id and type.
@@ -792,7 +792,7 @@ class Kernel(KernelFilterExtension):
return {service.service_id: service for service in self.services.values() if isinstance(service, type)} # type: ignore
def get_prompt_execution_settings_from_service_id(
self, service_id: str, type: Type[ALL_SERVICE_TYPES] | None = None
self, service_id: str, type: type[ALL_SERVICE_TYPES] | None = None
) -> PromptExecutionSettings:
"""Get the specific request settings from the service, instantiated with the service_id and ai_model_id."""
service = self.get_service(service_id, type=type)
@@ -1,7 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.
from collections.abc import Callable, Coroutine
from functools import partial
from typing import Any, Callable, Coroutine, Literal, TypeVar
from typing import Any, Literal, TypeVar
from pydantic import Field

Some files were not shown because too many files have changed in this diff Show More