Files
microsoft--semantic-kernel/python/samples/concepts/auto_function_calling/parallel_function_calling.py
T
wehub-resource-sync b957a53def
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:21:23 +08:00

103 lines
4.1 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
import asyncio
import logging
import sys
import time
from typing import Annotated
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion import OpenAIChatCompletion
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel import Kernel
# This sample demonstrates how the kernel will execute functions in parallel.
# The output of this sample should look similar to the following:
#
# [2024-09-11 10:15:35.070 INFO] processing 2 tool calls in parallel.
# The employee with ID 123 is named John Doe and they are 30 years old.
# Time elapsed: 11.96s
#
# The mock plugin simulates a long-running operation to fetch the employee's name and age.
# When you run the sample, you should see the total execution time is less than the sum
# of the two function calls because the kernel executes the functions in parallel.
# This concept example shows how to handle both streaming and non-streaming responses
# To toggle the behavior, set the following flag accordingly:
stream = True
def set_up_logging():
"""Set up logging to verify the kernel execute the functions in parallel"""
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
handler.setFormatter(
logging.Formatter("[%(asctime)s.%(msecs)03d %(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S"),
)
# Print only the logs from the chat completion client to reduce the output of the sample
handler.addFilter(lambda record: record.name == "semantic_kernel.connectors.ai.chat_completion_client_base")
root_logger.addHandler(handler)
class EmployeePlugin:
"""A mock plugin to simulate a plugin that fetches employee information"""
@kernel_function(name="get_name", description="Find the name of the employee by the id")
async def get_name(
self, id: Annotated[str, "The ID of the employee"]
) -> Annotated[str, "The name of the employee"]:
# Simulate a long-running operation
await asyncio.sleep(10)
return "John Doe"
@kernel_function(name="get_age", description="Get the age of the employee by the id")
async def get_age(self, id: Annotated[str, "The ID of the employee"]) -> Annotated[int, "The age of the employee"]:
# Simulate a long-running operation
await asyncio.sleep(10)
return 30
async def main():
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(service_id="open_ai"))
kernel.add_plugin(EmployeePlugin(), "EmployeePlugin")
# With this query, the model will call the get_name and get_age functions in parallel.
# Note that for certain queries, the model may choose to call the functions sequentially.
# For example, if the available functions are `get_email_by_id` and `get_name_by_email`,
# the model will not be able to call them in parallel because the second function depends
# on the result of the first function.
query = "What is the name and age of the employee of ID 123?"
arguments = KernelArguments(
settings=PromptExecutionSettings(
# Set the function_choice_behavior to auto to let the model
# decide which function to use, and let the kernel automatically
# execute the functions.
function_choice_behavior=FunctionChoiceBehavior.Auto(),
)
)
start = time.perf_counter()
if stream:
async for result in kernel.invoke_prompt_stream(query, arguments=arguments):
print(str(result[0]), end="")
print()
else:
result = await kernel.invoke_prompt(query, arguments=arguments)
print(result)
print(f"Time elapsed: {time.perf_counter() - start:.2f}s")
if __name__ == "__main__":
set_up_logging()
asyncio.run(main())