-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Python: Azure AI Inference Function Calling (#7035)
### 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 the Function Choice abstraction has been implemented in Python, it is time to extend this feature to other connectors. The first (OAI and AOAI are not included) connector to be granted this honor is the Azure AI Inference connector. ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> 1. Add function calling to Azure AI Inference. ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄
- Loading branch information
1 parent
4e99b76
commit 0ad7f54
Showing
8 changed files
with
455 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
346 changes: 246 additions & 100 deletions
346
...ic_kernel/connectors/ai/azure_ai_inference/services/azure_ai_inference_chat_completion.py
Large diffs are not rendered by default.
Oops, something went wrong.
135 changes: 135 additions & 0 deletions
135
python/semantic_kernel/connectors/ai/azure_ai_inference/services/utils.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
# Copyright (c) Microsoft. All rights reserved. | ||
|
||
import logging | ||
from collections.abc import Callable | ||
|
||
from azure.ai.inference.models import ( | ||
AssistantMessage, | ||
ChatCompletionsFunctionToolCall, | ||
ChatRequestMessage, | ||
FunctionCall, | ||
ImageContentItem, | ||
ImageDetailLevel, | ||
ImageUrl, | ||
SystemMessage, | ||
TextContentItem, | ||
ToolMessage, | ||
UserMessage, | ||
) | ||
|
||
from semantic_kernel.contents.chat_message_content import ChatMessageContent | ||
from semantic_kernel.contents.function_call_content import FunctionCallContent | ||
from semantic_kernel.contents.function_result_content import FunctionResultContent | ||
from semantic_kernel.contents.image_content import ImageContent | ||
from semantic_kernel.contents.text_content import TextContent | ||
from semantic_kernel.contents.utils.author_role import AuthorRole | ||
|
||
logger: logging.Logger = logging.getLogger(__name__) | ||
|
||
|
||
def _format_system_message(message: ChatMessageContent) -> SystemMessage: | ||
"""Format a system message to the expected object for the client. | ||
Args: | ||
message: The system message. | ||
Returns: | ||
The formatted system message. | ||
""" | ||
return SystemMessage(content=message.content) | ||
|
||
|
||
def _format_user_message(message: ChatMessageContent) -> UserMessage: | ||
"""Format a user message to the expected object for the client. | ||
If there are any image items in the message, we need to create a list of content items, | ||
otherwise we need to just pass in the content as a string or it will error. | ||
Args: | ||
message: The user message. | ||
Returns: | ||
The formatted user message. | ||
""" | ||
if not any(isinstance(item, (ImageContent)) for item in message.items): | ||
return UserMessage(content=message.content) | ||
|
||
contentItems = [] | ||
for item in message.items: | ||
if isinstance(item, TextContent): | ||
contentItems.append(TextContentItem(text=item.text)) | ||
elif isinstance(item, ImageContent) and (item.data_uri or item.uri): | ||
contentItems.append( | ||
ImageContentItem(image_url=ImageUrl(url=item.data_uri or str(item.uri), detail=ImageDetailLevel.Auto)) | ||
) | ||
else: | ||
logger.warning( | ||
"Unsupported item type in User message while formatting chat history for Azure AI" | ||
f" Inference: {type(item)}" | ||
) | ||
|
||
return UserMessage(content=contentItems) | ||
|
||
|
||
def _format_assistant_message(message: ChatMessageContent) -> AssistantMessage: | ||
"""Format an assistant message to the expected object for the client. | ||
Args: | ||
message: The assistant message. | ||
Returns: | ||
The formatted assistant message. | ||
""" | ||
contentItems = [] | ||
toolCalls = [] | ||
|
||
for item in message.items: | ||
if isinstance(item, TextContent): | ||
contentItems.append(TextContentItem(text=item.text)) | ||
elif isinstance(item, FunctionCallContent): | ||
toolCalls.append( | ||
ChatCompletionsFunctionToolCall( | ||
id=item.id, function=FunctionCall(name=item.name, arguments=item.arguments) | ||
) | ||
) | ||
else: | ||
logger.warning( | ||
"Unsupported item type in Assistant message while formatting chat history for Azure AI" | ||
f" Inference: {type(item)}" | ||
) | ||
|
||
# tollCalls cannot be an empty list, so we need to set it to None if it is empty | ||
return AssistantMessage(content=contentItems, tool_calls=toolCalls if toolCalls else None) | ||
|
||
|
||
def _format_tool_message(message: ChatMessageContent) -> ToolMessage: | ||
"""Format a tool message to the expected object for the client. | ||
Args: | ||
message: The tool message. | ||
Returns: | ||
The formatted tool message. | ||
""" | ||
if len(message.items) != 1: | ||
logger.warning( | ||
"Unsupported number of items in Tool message while formatting chat history for Azure AI" | ||
f" Inference: {len(message.items)}" | ||
) | ||
|
||
if not isinstance(message.items[0], FunctionResultContent): | ||
logger.warning( | ||
"Unsupported item type in Tool message while formatting chat history for Azure AI" | ||
f" Inference: {type(message.items[0])}" | ||
) | ||
|
||
# The API expects the result to be a string, so we need to convert it to a string | ||
return ToolMessage(content=str(message.items[0].result), tool_call_id=message.items[0].id) | ||
|
||
|
||
MESSAGE_CONVERTERS: dict[AuthorRole, Callable[[ChatMessageContent], ChatRequestMessage]] = { | ||
AuthorRole.SYSTEM: _format_system_message, | ||
AuthorRole.USER: _format_user_message, | ||
AuthorRole.ASSISTANT: _format_assistant_message, | ||
AuthorRole.TOOL: _format_tool_message, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 10 additions & 18 deletions
28
python/semantic_kernel/connectors/ai/function_calling_utils.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters