Python: major features for the Kernel Arguments, Function Result and new Prompt Templating (#5077)

### 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. -->
Major work to replace the context with Kernel Arguments on the front-end
and Function Results on the back.

Updated the function decorator to a new approach, in line with dotnet.

Revamps the way function are called, allowing native functions to be
completely ignorant of SK, other then the decorator.

This also moves things into the new folder structure in sync with
dotnet.

### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->
Adds:
- KernelArguments, a dict-like class that replaces the variables in
KernelContext. Closes #4565
- FunctionResult, a class to hold the results of function executions,
includes the function, the value and metadata, as well as two convenient
function to get the value out of it (str and get_inner_content) the
first is generic, the second specifically for KernelContent responses
(from AI services).
- AI Service Selector class, has a default, which is based on the order
in the arguments followed by the order in the functions, can be
overridden to implement your own strategy. Closes #4631
- Introduces ChatHistory and refactors the PromptTemplateConfig. Closes
#4856, #4630
- Improves parsing of templates, will now all validate during creation
and throw an error then, instead of some that do not check for validaty
until used.
- Introduces named_args block and thereby the ability to have multiple
arguments for a function call in a template. Closes #5003

Updates:
- kernel_function decorators, the parameter decorator was removed and
instead we now use Annotated to add a description of a field and we get
the type and required from the function definition.
- core plugins, use the new approach to kernel_function decorators.
- planners, template engines have all been updated to use the kernel and
kernelarguments instead of Context.
- Events have been updated, now use kernelarguments and function_result
- Tokenizers support for named_args and improvements on parsing and
checking.
- Kernel examples and notebooks to use the latest code.
- All unit and integration tests. There is more code coverage now than
before.

Removed:
- kernelContext
- kernel_function_parameter_decorator
- delegate handling code for native functions
- file_io_plugin and tests
- SemanticFunctionConfig
- ChatPromptTemplate

### 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
- [ ] I didn't break anyone 😄

---------

Co-authored-by: Evan Mattson <[email protected]>
Co-authored-by: Evan Mattson <[email protected]>
Co-authored-by: Evan Mattson <[email protected]>
This commit is contained in:
Eduard van Valkenburg
2024-02-24 20:05:40 +00:00
committed by GitHub
co-authored by Evan Mattson Evan Mattson Evan Mattson
parent 885873343e
commit 5817c2b927
276 changed files with 10594 additions and 9496 deletions
@@ -79,7 +79,10 @@ jobs:
AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }}
AzureOpenAIChat__DeploymentName: ${{ vars.AZUREOPENAI__CHAT__DEPLOYMENTNAME }}
AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDINGS__DEPLOYMENTNAME2 }}
AzureOpenAIEmbeddings_EastUS__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDINGS_EASTUS__DEPLOYMENTNAME}}
AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAI_EastUS__Endpoint: ${{ secrets.AZUREOPENAI_EASTUS__ENDPOINT }}
AzureOpenAI_EastUS__ApiKey: ${{ secrets.AZUREOPENAI_EASTUS__APIKEY }}
AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
AzureOpenAIEmbeddings__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
@@ -142,9 +145,12 @@ jobs:
AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }}
AzureOpenAIChat__DeploymentName: ${{ vars.AZUREOPENAI__CHAT__DEPLOYMENTNAME }}
AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDINGS__DEPLOYMENTNAME2 }}
AzureOpenAIEmbeddings_EastUS__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDINGS_EASTUS__DEPLOYMENTNAME}}
AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
AzureOpenAI_EastUS__Endpoint: ${{ secrets.AZUREOPENAI_EASTUS__ENDPOINT }}
AzureOpenAI_EastUS__ApiKey: ${{ secrets.AZUREOPENAI_EASTUS__APIKEY }}
AzureOpenAIEmbeddings__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
Bing__ApiKey: ${{ secrets.BING__APIKEY }}
OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
+4 -4
View File
@@ -2,10 +2,10 @@
source = semantic_kernel
omit =
semantic_kernel/connectors/memory/*
semantic_kernel/connectors/openapi/*
semantic_kernel/connectors/search_engine/*
semantic_kernel/connectors/ai/google_palm/*
semantic_kernel/connectors/ai/hugging_face/*
semantic_kernel/utils/settings.py
semantic_kernel/utils/null_logger.py
semantic_kernel/utils/logging.py
[report]
-5
View File
@@ -19,11 +19,6 @@
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"pythonTestExplorer.testFramework": "pytest",
"pythonTestExplorer.pytestPath": "poetry",
"pythonTestExplorer.pytestArguments": [
"run",
"pytest"
],
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
+49 -14
View File
@@ -33,14 +33,34 @@ kernel = sk.Kernel()
# Prepare OpenAI service using credentials stored in the `.env` file
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service("chat-gpt", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))
service_id="chat-gpt"
kernel.add_service(
OpenAIChatCompletion(
service_id=service_id,
ai_model_id="gpt-3.5-turbo",
api_key=api_key,
org_id=org_id
)
)
# Alternative using Azure:
# deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
# kernel.add_chat_service("dv", AzureChatCompletion(deployment, endpoint, api_key))
# kernel.add_service(
# AzureChatCompletion(
# service_id="dv",
# deployment_name=deployment,
# base_url=endpoint,
# api_key=api_key
# )
# )
# Wrap your prompt in a function
prompt = kernel.create_semantic_function("""
# Define the request settings
req_settings = kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
req_settings.max_tokens = 2000
req_settings.temperature = 0.7
req_settings.top_p = 0.8
prompt = """
1) A robot may not injure a human being or, through inaction,
allow a human being to come to harm.
@@ -50,37 +70,52 @@ such orders would conflict with the First Law.
3) A robot must protect its own existence as long as such protection
does not conflict with the First or Second Law.
Give me the TLDR in exactly 5 words.""")
Give me the TLDR in exactly 5 words."""
prompt_template_config = sk.PromptTemplateConfig(
template=prompt,
name="tldr",
template_format="semantic-kernel",
execution_settings=req_settings,
)
function = kernel.create_function_from_prompt(
prompt_template_config=prompt_template_config,
)
# Run your prompt
# Note: functions are run asynchronously
async def main():
print(await prompt()) # => Robots must not harm humans.
result = await kernel.invoke(function)
print(result) # => Robots must not harm humans.
if __name__ == "__main__":
asyncio.run(main())
```
# **Semantic functions** are Prompts with input parameters
# **Semantic Prompt Functions** are Prompts with input parameters
```python
# Create a reusable function with one input parameter
summarize = kernel.create_semantic_function("{{$input}}\n\nOne line TLDR with the fewest words.")
# Create a reusable function summarize function
summarize = kernel.create_function_from_prompt(
template="{{$input}}\n\nOne line TLDR with the fewest words."
execution_settings=req_settings,
)
# Summarize the laws of thermodynamics
print(await summarize("""
print(await kernel.invoke(summarize, input="""
1st Law of Thermodynamics - Energy cannot be created or destroyed.
2nd Law of Thermodynamics - For a spontaneous process, the entropy of the universe increases.
3rd Law of Thermodynamics - A perfect crystal at zero Kelvin has zero entropy."""))
# Summarize the laws of motion
print(await summarize("""
print(await kernel.invoke(summarize, input="""
1. An object at rest remains at rest, and an object in motion remains in motion at constant speed and in a straight line unless acted on by an unbalanced force.
2. The acceleration of an object depends on the mass of the object and the amount of force applied.
3. Whenever one object exerts a force on another object, the second object exerts an equal and opposite on the first."""))
# Summarize the law of universal gravitation
print(await summarize("""
print(await kernel.invoke(summarize, input="""
Every point mass attracts every single other point mass by a force acting along the line intersecting both points.
The force is proportional to the product of the two masses and inversely proportional to the square of the distance between them."""))
@@ -100,8 +135,8 @@ Python notebooks:
- [Getting started with Semantic Kernel](./notebooks/00-getting-started.ipynb)
- [Loading and configuring Semantic Kernel](./notebooks/01-basic-loading-the-kernel.ipynb)
- [Running AI prompts from file](./notebooks/02-running-prompts-from-file.ipynb)
- [Creating Semantic Functions at runtime (i.e. inline functions)](./notebooks/03-semantic-function-inline.ipynb)
- [Using Context Variables to Build a Chat Experience](./notebooks/04-context-variables-chat.ipynb)
- [Creating Prompt Functions at runtime (i.e. inline functions)](./notebooks/03-prompt-function-inline.ipynb)
- [Using Context Variables to Build a Chat Experience](./notebooks/04-kernel-arguments-chat.ipynb)
- [Introduction to planners](./notebooks/05-using-the-planner.ipynb)
- [Building Memory with Embeddings](./notebooks/06-memory-and-embeddings.ipynb)
- [Using Hugging Face for Plugins](./notebooks/07-hugging-face-for-plugins.ipynb)
+54 -31
View File
@@ -30,6 +30,25 @@
"kernel = sk.Kernel()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Configure the service you'd like to use via the `Service` Enum."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
"attachments": {},
"cell_type": "markdown",
@@ -44,30 +63,8 @@
"OPENAI_ORG_ID=\"\"\n",
"```\n",
"\n",
"Use \"keyword arguments\" to instantiate an OpenAI Chat Completion service and add it to the kernel:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"Use \"keyword arguments\" to instantiate an OpenAI Chat Completion service and add it to the kernel:\n",
"\n",
"api_key, org_id = sk.openai_settings_from_dot_env()\n",
"\n",
"kernel.add_chat_service(\n",
" \"chat-gpt\",\n",
" OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Option 2: using Azure OpenAI\n",
"\n",
"**Step 2**: Add your [Azure Open AI Service key](https://learn.microsoft.com/azure/cognitive-services/openai/quickstart?pivots=programming-language-studio) settings to a `.env` file in the same folder:\n",
@@ -87,14 +84,23 @@
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
"deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" service_id = \"chat-gpt\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(service_id=service_id, ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
"kernel.add_chat_service(\n",
" \"chat_completion\",\n",
" AzureChatCompletion(deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
")"
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"chat_completion\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )"
]
},
{
@@ -113,10 +119,27 @@
"metadata": {},
"outputs": [],
"source": [
"plugin = kernel.import_semantic_plugin_from_directory(\"../../samples/plugins\", \"FunPlugin\")\n",
"try:\n",
" plugin = kernel.import_plugin_from_prompt_directory(service_id, \"../../samples/plugins\", \"FunPlugin\")\n",
"except ValueError as e:\n",
" # Don't fail if we try to add the plug in again\n",
" # This is just for the sake of the example\n",
" # Once the plugin has been added to the kernel it will fail\n",
" # to add it again, if this cell is run multiple times without\n",
" # restarting the kernel\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"joke_function = plugin[\"Joke\"]\n",
"\n",
"print(await joke_function(\"time travel to dinosaur age\"))"
"joke = await kernel.invoke(joke_function, sk.KernelArguments(input=\"time travel to dinosaur age\", style=\"super silly\"))\n",
"print(joke)"
]
}
],
@@ -34,29 +34,7 @@
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureChatCompletion,\n",
" OpenAIChatCompletion,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"You can instantiate the kernel in a few ways, depending on your use case."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Simple instance\n",
"kernel_1 = sk.Kernel()"
"import semantic_kernel as sk"
]
},
{
@@ -66,7 +44,7 @@
"source": [
"When using the kernel for AI requests, the kernel needs some settings like URL and credentials to the AI models.\n",
"\n",
"The SDK currently supports OpenAI and Azure OpenAI, other services will be added over time.\n",
"The SDK currently supports OpenAI and Azure OpenAI, among other connectors.\n",
"\n",
"If you need an Azure OpenAI key, go [here](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/quickstart?pivots=rest-api)."
]
@@ -77,39 +55,10 @@
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"from services import Service\n",
"\n",
"kernel.add_chat_service( # We are adding a text service\n",
" \"Azure_curie\", # The alias we can use in prompt templates' config.json\n",
" AzureChatCompletion(\n",
" deployment_name=\"my-finetuned-Curie\", # Azure OpenAI *Deployment name*\n",
" endpoint=\"https://contoso.openai.azure.com/\", # Azure OpenAI *Endpoint*\n",
" api_key=\"...your Azure OpenAI Key...\", # Azure OpenAI *Key*\n",
" ),\n",
")\n",
"\n",
"kernel.add_chat_service( # We are adding a text service\n",
" \"OpenAI_chat_gpt\", # The alias we can use in prompt templates' config.json\n",
" OpenAIChatCompletion(\n",
" ai_model_id=\"gpt-3.5-turbo\", # OpenAI Model Name\n",
" api_key=\"...your OpenAI API Key...\", # OpenAI API key\n",
" org_id=\"...your OpenAI Org ID...\", # *optional* OpenAI Organization ID\n",
" ),\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"When working with multiple services and multiple models, the **first service** defined\n",
"is also the \"**default**\" used in these scenarios:\n",
"\n",
"* a prompt configuration doesn't specify which AI service to use\n",
"* a prompt configuration requires a service unknown to the kernel\n",
"\n",
"The default can be set and changed programmatically:"
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
@@ -118,7 +67,25 @@
"metadata": {},
"outputs": [],
"source": [
"kernel.set_default_text_completion_service(\"Azure_curie\")"
"kernel = sk.Kernel()\n",
"\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" service_id = \"oai_chat_gpt\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(service_id=service_id, ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat_completion\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )"
]
},
{
@@ -6,10 +6,10 @@
"id": "692e361b",
"metadata": {},
"source": [
"# How to run a semantic plugins from file\n",
"Now that you're familiar with Kernel basics, let's see how the kernel allows you to run Semantic Plugins and Semantic Functions stored on disk. \n",
"# How to run a prompt plugins from file\n",
"Now that you're familiar with Kernel basics, let's see how the kernel allows you to run Prompt Plugins and Prompt Functions stored on disk. \n",
"\n",
"A Semantic Plugin is a collection of Semantic Functions, where each function is defined with natural language that can be provided with a text file. \n",
"A Prompt Plugin is a collection of Semantic Functions, where each function is defined with natural language that can be provided with a text file. \n",
"\n",
"Refer to our [glossary](https://github.com/microsoft/semantic-kernel/blob/main/docs/GLOSSARY.md) for an in-depth guide to the terms.\n",
"\n",
@@ -60,14 +60,30 @@
"```\n",
"{\n",
" \"schema\": 1,\n",
" \"type\": \"completion\",\n",
" \"description\": \"Generate a funny joke\",\n",
" \"completion\": {\n",
" \"max_tokens\": 500,\n",
" \"temperature\": 0.5,\n",
" \"top_p\": 0.5\n",
" }\n",
" \"execution_settings\": {\n",
" \"default\": {\n",
" \"max_tokens\": 1000,\n",
" \"temperature\": 0.9,\n",
" \"top_p\": 0.0,\n",
" \"presence_penalty\": 0.0,\n",
" \"frequency_penalty\": 0.0\n",
" }\n",
" },\n",
" \"input_variables\": [\n",
" {\n",
" \"name\": \"input\",\n",
" \"description\": \"Joke subject\",\n",
" \"default\": \"\"\n",
" },\n",
" {\n",
" \"name\": \"style\",\n",
" \"description\": \"Give a hint about the desired joke style\",\n",
" \"default\": \"\"\n",
" }\n",
" ]\n",
"}\n",
"\n",
"```"
]
},
@@ -77,7 +93,7 @@
"id": "384ff07f",
"metadata": {},
"source": [
"Given a semantic function defined by these files, this is how to load and use a file based semantic function.\n",
"Given a prompt function defined by these files, this is how to load and use a file based prompt function.\n",
"\n",
"Load and configure the kernel, as usual, loading also the AI service settings defined in the [Setup notebook](00-getting-started.ipynb):"
]
@@ -92,6 +108,19 @@
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fdb865a7",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -100,26 +129,26 @@
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureChatCompletion,\n",
" OpenAIChatCompletion,\n",
")\n",
"\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
"# Configure AI service used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
" kernel.add_chat_service(\"chat_completion\", azure_chat_service)\n",
"else:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
" kernel.add_chat_service(\"chat-gpt\", oai_chat_service)"
" service_id = \"oai_chat_gpt\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(service_id=service_id, ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat_completion\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )"
]
},
{
@@ -141,7 +170,7 @@
"# note: using plugins from the samples folder\n",
"plugins_directory = \"../../samples/plugins\"\n",
"\n",
"funFunctions = kernel.import_semantic_plugin_from_directory(plugins_directory, \"FunPlugin\")\n",
"funFunctions = kernel.import_plugin_from_prompt_directory(service_id, plugins_directory, \"FunPlugin\")\n",
"\n",
"jokeFunction = funFunctions[\"Joke\"]"
]
@@ -162,7 +191,7 @@
"metadata": {},
"outputs": [],
"source": [
"result = await jokeFunction.invoke(\"travel to dinosaur age\")\n",
"result = await kernel.invoke(jokeFunction, sk.KernelArguments(input=\"travel to dinosaur age\", style=\"silly\"))\n",
"print(result)"
]
},
@@ -172,7 +201,7 @@
"id": "2281a1fc",
"metadata": {},
"source": [
"Great, now that you know how to load a plugin from disk, let's show how you can [create and run a semantic function inline.](./03-semantic-function-inline.ipynb)"
"Great, now that you know how to load a plugin from disk, let's show how you can [create and run a prompt function inline.](./03-prompt-function-inline.ipynb)"
]
}
],
@@ -192,7 +221,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.10.12"
}
},
"nbformat": 4,
@@ -6,16 +6,9 @@
"id": "3c93ac5b",
"metadata": {},
"source": [
"# Running Semantic Functions Inline"
"# Running Prompt Functions Inline"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "ebcabb91",
"metadata": {},
"source": []
},
{
"attachments": {},
"cell_type": "markdown",
@@ -58,6 +51,19 @@
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "68b770df",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -66,26 +72,30 @@
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
" OpenAITextCompletion,\n",
")\n",
"import semantic_kernel.connectors.ai.open_ai as sk_oai\n",
"from semantic_kernel.prompt_template.input_variable import InputVariable\n",
"\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion\n",
"\n",
"# Configure AI service used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_text_service = AzureTextCompletion(\n",
" deployment_name=\"text\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your text model\n",
" kernel.add_text_completion_service(\"dv\", azure_text_service)\n",
"else:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_text_service = OpenAITextCompletion(ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id)\n",
" kernel.add_text_completion_service(\"dv\", oai_text_service)"
" service_id = \"oai_text_completion\"\n",
" kernel.add_service(\n",
" OpenAITextCompletion(\n",
" service_id=service_id, ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id\n",
" ),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion\n",
"\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_text_completion\"\n",
" kernel.add_service(\n",
" AzureTextCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )"
]
},
{
@@ -110,7 +120,32 @@
"Summarize the content above.\n",
"\"\"\"\n",
"\n",
"summarize = kernel.create_semantic_function(prompt_template=prompt, max_tokens=2000, temperature=0.2, top_p=0.5)"
"if selectedService == Service.OpenAI:\n",
" execution_settings = sk_oai.OpenAITextPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=\"gpt-3.5-turbo-instruct\",\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" execution_settings = sk_oai.OpenAITextPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=deployment,\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=prompt,\n",
" name=\"summarize\",\n",
" template_format=\"semantic-kernel\",\n",
" input_variables=[\n",
" InputVariable(name=\"input\", description=\"The user input\", is_required=True),\n",
" ],\n",
" execution_settings=execution_settings,\n",
")\n",
"\n",
"summarize = kernel.create_function_from_prompt(prompt_template_config=prompt_template_config)"
]
},
{
@@ -158,7 +193,7 @@
"metadata": {},
"outputs": [],
"source": [
"summary = await summarize(input_text)\n",
"summary = await kernel.invoke(summarize, sk.KernelArguments(input=input_text))\n",
"\n",
"print(summary)"
]
@@ -178,7 +213,7 @@
"id": "29b59b28",
"metadata": {},
"source": [
"You can also use chat completion models (like `gpt-35-turbo` and `gpt4`) for creating plugins. Normally you would have to tweak the API to accommodate for a system and user role, but SK abstracts that away for you by using `kernel.add_chat_service` and `AzureChatCompletion` or `OpenAIChatCompletion`"
"You can also use chat completion models (like `gpt-35-turbo` and `gpt4`) for creating plugins. Normally you would have to tweak the API to accommodate for a system and user role, but SK abstracts that away for you by using `kernel.add_service` and `AzureChatCompletion` or `OpenAIChatCompletion`"
]
},
{
@@ -199,27 +234,25 @@
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureChatCompletion,\n",
" OpenAIChatCompletion,\n",
")\n",
"\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
"# Configure AI service used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
" kernel.add_chat_service(\"chat_completion\", azure_chat_service)\n",
"else:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
" kernel.add_chat_service(\"chat-gpt\", oai_chat_service)"
" service_id = \"oai_chat_gpt\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(service_id=service_id, ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat_completion\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )"
]
},
{
@@ -229,7 +262,7 @@
"metadata": {},
"outputs": [],
"source": [
"sk_prompt = \"\"\"\n",
"prompt = \"\"\"\n",
"{{$input}}\n",
"\n",
"Give me the TLDR in 5 words or less.\n",
@@ -246,11 +279,36 @@
" does not conflict with the First or Second Law.\n",
"\"\"\"\n",
"\n",
"tldr_function = kernel.create_semantic_function(prompt_template=sk_prompt, max_tokens=200, temperature=0, top_p=0.5)\n",
"if selectedService == Service.OpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=\"gpt-3.5-turbo-1106\",\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=deployment,\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"\n",
"summary = await tldr_function(text)\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=prompt,\n",
" name=\"tldr\",\n",
" template_format=\"semantic-kernel\",\n",
" input_variables=[\n",
" InputVariable(name=\"input\", description=\"The user input\", is_required=True),\n",
" ],\n",
" execution_settings=execution_settings,\n",
")\n",
"\n",
"print(f\"Output: {summary}\") # Output: Robots must not harm humans."
"tldr_function = kernel.create_function_from_prompt(prompt_template_config=prompt_template_config)\n",
"\n",
"summary = await kernel.invoke(tldr_function, sk.KernelArguments(input=text))\n",
"\n",
"print(f\"Output: {summary}\")"
]
}
],
@@ -270,7 +328,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.10.12"
}
},
"nbformat": 4,
@@ -6,17 +6,17 @@
"id": "fde98ddf",
"metadata": {},
"source": [
"# Creating a basic chat experience with context variables\n",
"# Creating a basic chat experience with kernel arguments\n",
"\n",
"In this example, we show how you can build a simple chat bot by sending and updating context with your requests. \n",
"In this example, we show how you can build a simple chat bot by sending and updating the kernel arguments with your requests. \n",
"\n",
"We introduce the Context Variables object which in this demo functions similarly as a key-value store that you can use when running the kernel.\n",
"We introduce the Kernel Arguments object which in this demo functions similarly as a key-value store that you can use when running the kernel.\n",
"\n",
"The context is local (i.e. in your computer's RAM) and not persisted anywhere beyond the life of this Jupyter session.\n",
"The chat history is local (i.e. in your computer's RAM) and not persisted anywhere beyond the life of this Jupyter session.\n",
"\n",
"In future examples, we will show how to persist the context on disk so that you can bring it into your applications. \n",
"In future examples, we will show how to persist the chat history on disk so that you can bring it into your applications. \n",
"\n",
"In this chat scenario, as the user talks back and forth with the bot, the context gets populated with the history of the conversation. During each new run of the kernel, the context can provide the AI with its variables' content. "
"In this chat scenario, as the user talks back and forth with the bot, the chat context gets populated with the history of the conversation. During each new run of the kernel, the kernel arguments and chat history can provide the AI with its variables' content. "
]
},
{
@@ -29,6 +29,19 @@
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0a235b31",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -37,27 +50,29 @@
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureChatCompletion,\n",
" OpenAIChatCompletion,\n",
")\n",
"import semantic_kernel.connectors.ai.open_ai as sk_oai\n",
"from semantic_kernel.prompt_template.input_variable import InputVariable\n",
"from semantic_kernel.contents.chat_history import ChatHistory\n",
"from semantic_kernel.functions.kernel_arguments import KernelArguments\n",
"\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
"# Configure AI service used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" kernel.add_chat_service(\n",
" \"chat_completion\",\n",
" AzureChatCompletion(deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )\n",
"else:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" kernel.add_chat_service(\n",
" \"gpt-3.5\",\n",
" OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id),\n",
" service_id = \"oai_chat_gpt\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(service_id=service_id, ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat_completion\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )"
]
},
@@ -77,7 +92,7 @@
"metadata": {},
"outputs": [],
"source": [
"sk_prompt = \"\"\"\n",
"prompt = \"\"\"\n",
"ChatBot can have a conversation with you about any topic.\n",
"It can give explicit instructions or say 'I don't know' if it does not have an answer.\n",
"\n",
@@ -102,13 +117,44 @@
"metadata": {},
"outputs": [],
"source": [
"chat_function = kernel.create_semantic_function(\n",
" prompt_template=sk_prompt,\n",
" function_name=\"ChatBot\",\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" top_p=0.5,\n",
")"
"if selectedService == Service.OpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=\"gpt-3.5-turbo-1106\",\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=deployment,\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=prompt,\n",
" name=\"chat\",\n",
" template_format=\"semantic-kernel\",\n",
" input_variables=[\n",
" InputVariable(name=\"input\", description=\"The user input\", is_required=True),\n",
" InputVariable(name=\"history\", description=\"The conversation history\", is_required=True),\n",
" ],\n",
" execution_settings=execution_settings,\n",
")\n",
"\n",
"chat_function = kernel.create_function_from_prompt(prompt_template_config=prompt_template_config)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6a0f7c01",
"metadata": {},
"outputs": [],
"source": [
"chat_history = ChatHistory()\n",
"chat_history.add_system_message(\"You are a helpful chatbot who is good about giving book recommendations.\")"
]
},
{
@@ -117,7 +163,7 @@
"id": "6e8a676f",
"metadata": {},
"source": [
"Initialize your context"
"Initialize the Kernel Arguments"
]
},
{
@@ -127,8 +173,7 @@
"metadata": {},
"outputs": [],
"source": [
"context = kernel.create_new_context()\n",
"context[\"history\"] = \"\""
"arguments = KernelArguments(user_input=\"Hi, I'm looking for book suggestions\", history=chat_history)"
]
},
{
@@ -147,9 +192,8 @@
"metadata": {},
"outputs": [],
"source": [
"context[\"user_input\"] = \"Hi, I'm looking for book suggestions\"\n",
"bot_answer = await chat_function.invoke(context=context)\n",
"print(bot_answer)"
"response = await kernel.invoke(chat_function, arguments)\n",
"print(response)"
]
},
{
@@ -168,8 +212,7 @@
"metadata": {},
"outputs": [],
"source": [
"context[\"history\"] += f\"\\nUser: {context['user_input']}\\nChatBot: {bot_answer}\\n\"\n",
"print(context[\"history\"])"
"chat_history.add_assistant_message(str(response))"
]
},
{
@@ -191,16 +234,15 @@
"async def chat(input_text: str) -> None:\n",
" # Save new message in the context variables\n",
" print(f\"User: {input_text}\")\n",
" context[\"user_input\"] = input_text\n",
" chat_history.add_user_message(input_text)\n",
"\n",
" # Process the user message and get an answer\n",
" answer = await chat_function.invoke(context=context)\n",
" answer = await kernel.invoke(chat_function, KernelArguments(user_input=input_text, history=chat_history))\n",
"\n",
" # Show the response\n",
" print(f\"ChatBot: {answer}\")\n",
"\n",
" # Append the new interaction to the chat history\n",
" context[\"history\"] += f\"\\nUser: {input_text}\\nChatBot: {answer}\\n\""
" chat_history.add_assistant_message(str(answer))"
]
},
{
@@ -259,7 +301,7 @@
"metadata": {},
"outputs": [],
"source": [
"print(context[\"history\"])"
"print(chat_history)"
]
}
],
@@ -279,7 +321,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.10.12"
}
},
"nbformat": 4,
+95 -76
View File
@@ -26,6 +26,31 @@
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7d548e40",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3852961c",
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.prompt_template.input_variable import InputVariable\n",
"from semantic_kernel.contents.chat_history import ChatHistory\n",
"from semantic_kernel.functions.kernel_arguments import KernelArguments"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -34,27 +59,26 @@
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" OpenAIChatCompletion,\n",
" AzureChatCompletion,\n",
")\n",
"import semantic_kernel.connectors.ai.open_ai as sk_oai\n",
"\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = True\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
"# Configure AI backend used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" kernel.add_chat_service(\n",
" \"chat_completion\",\n",
" AzureChatCompletion(deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )\n",
"else:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" kernel.add_chat_service(\n",
" \"gpt-4\",\n",
" OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" service_id = \"oai_chat_gpt\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(service_id=service_id, ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat_completion\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )"
]
},
@@ -100,8 +124,8 @@
"from semantic_kernel.core_plugins.text_plugin import TextPlugin\n",
"\n",
"plugins_directory = \"../../samples/plugins/\"\n",
"summarize_plugin = kernel.import_semantic_plugin_from_directory(plugins_directory, \"SummarizePlugin\")\n",
"writer_plugin = kernel.import_semantic_plugin_from_directory(plugins_directory, \"WriterPlugin\")\n",
"summarize_plugin = kernel.import_plugin_from_prompt_directory(service_id, plugins_directory, \"SummarizePlugin\")\n",
"writer_plugin = kernel.import_plugin_from_prompt_directory(service_id, plugins_directory, \"WriterPlugin\")\n",
"text_plugin = kernel.import_plugin(TextPlugin(), \"TextPlugin\")"
]
},
@@ -136,9 +160,9 @@
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.planning.basic_planner import BasicPlanner\n",
"from semantic_kernel.planners.basic_planner import BasicPlanner\n",
"\n",
"planner = BasicPlanner()"
"planner = BasicPlanner(service_id)"
]
},
{
@@ -186,18 +210,31 @@
"metadata": {},
"outputs": [],
"source": [
"sk_prompt = \"\"\"\n",
"prompt = \"\"\"\n",
"{{$input}}\n",
"\n",
"Rewrite the above in the style of Shakespeare.\n",
"\"\"\"\n",
"shakespeareFunction = kernel.create_semantic_function(\n",
" prompt_template=sk_prompt,\n",
" function_name=\"shakespeare\",\n",
" plugin_name=\"ShakespearePlugin\",\n",
"\n",
"exec_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" max_tokens=2000,\n",
" temperature=0.8,\n",
")"
")\n",
"\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=prompt,\n",
" function_name=\"shakespeare\",\n",
" plugin_name=\"ShakespearePlugin\",\n",
" name=\"planner\",\n",
" template_format=\"semantic-kernel\",\n",
" input_variables=[\n",
" InputVariable(name=\"input\", description=\"The user input\", is_required=True),\n",
" ],\n",
" execution_settings=exec_settings,\n",
")\n",
"\n",
"shakespeare_plugin = kernel.create_function_from_prompt(prompt_template_config=prompt_template_config)"
]
},
{
@@ -220,7 +257,33 @@
"She likes Shakespeare so write using his style. She speaks French so write it in French.\n",
"Convert the text to uppercase.\"\"\"\n",
"\n",
"new_plan = await planner.create_plan(ask, kernel)"
"# TODO: we cannot add an updated ask to a current plan because the underlying plugins already exist\n",
"kernel = sk.Kernel()\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" service_id = \"oai_chat_gpt\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(service_id=service_id, ai_model_id=\"gpt-3.5-turbo-1106\", api_key=api_key, org_id=org_id),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat_completion\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(service_id=service_id, deployment_name=deployment, endpoint=endpoint, api_key=api_key),\n",
" )\n",
"\n",
"plugins_directory = \"../../samples/plugins/\"\n",
"summarize_plugin = kernel.import_plugin_from_prompt_directory(service_id, plugins_directory, \"SummarizePlugin\")\n",
"writer_plugin = kernel.import_plugin_from_prompt_directory(service_id, plugins_directory, \"WriterPlugin\")\n",
"text_plugin = kernel.import_plugin(TextPlugin(), \"TextPlugin\")\n",
"\n",
"planner = BasicPlanner(service_id)\n",
"new_plan = await planner.create_plan(ask, kernel, prompt)"
]
},
{
@@ -310,7 +373,7 @@
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.planning import SequentialPlanner\n",
"from semantic_kernel.planners import SequentialPlanner\n",
"\n",
"planner = SequentialPlanner(kernel)"
]
@@ -395,7 +458,7 @@
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.planning import ActionPlanner\n",
"from semantic_kernel.planners import ActionPlanner\n",
"\n",
"planner = ActionPlanner(kernel)"
]
@@ -416,14 +479,12 @@
"outputs": [],
"source": [
"from semantic_kernel.core_plugins import (\n",
" FileIOPlugin,\n",
" MathPlugin,\n",
" TextPlugin,\n",
" TimePlugin,\n",
")\n",
"\n",
"kernel.import_plugin(MathPlugin(), \"math\")\n",
"kernel.import_plugin(FileIOPlugin(), \"fileIO\")\n",
"kernel.import_plugin(TimePlugin(), \"time\")\n",
"kernel.import_plugin(TextPlugin(), \"text\")"
]
@@ -486,19 +547,6 @@
"See a video walkthrough of Stepwise Planner [here.](https://youtu.be/DG_Ge1v0c4Q?si=T1CHaAm1vV0mWRHu)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "32839327",
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.planning import StepwisePlanner\n",
"from semantic_kernel.planning.stepwise_planner.stepwise_planner_config import (\n",
" StepwisePlannerConfig,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "e0a00bde",
@@ -511,38 +559,6 @@
"(https://www.microsoft.com/en-us/bing/apis/bing-web-search-api)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "92fc5de8",
"metadata": {},
"outputs": [],
"source": [
"class WebSearchEnginePlugin:\n",
" \"\"\"\n",
" A search engine plugin.\n",
" \"\"\"\n",
"\n",
" from semantic_kernel.orchestration.kernel_context import KernelContext\n",
" from semantic_kernel.plugin_definition import (\n",
" kernel_function,\n",
" kernel_function_context_parameter,\n",
" )\n",
"\n",
" def __init__(self, connector) -> None:\n",
" self._connector = connector\n",
"\n",
" @kernel_function(description=\"Performs a web search for a given query\", name=\"searchAsync\")\n",
" @kernel_function_context_parameter(\n",
" name=\"query\",\n",
" description=\"The search query\",\n",
" )\n",
" async def search(self, query: str, context: KernelContext) -> str:\n",
" query = query or context.variables.get(\"query\")[1]\n",
" result = await self._connector.search(query, num_results=5, offset=0)\n",
" return str(result)"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -551,6 +567,7 @@
"outputs": [],
"source": [
"from semantic_kernel.connectors.search_engine import BingConnector\n",
"from semantic_kernel.core_plugins import WebSearchEnginePlugin\n",
"\n",
"BING_API_KEY = sk.bing_search_settings_from_dot_env()\n",
"connector = BingConnector(BING_API_KEY)\n",
@@ -586,6 +603,8 @@
"metadata": {},
"outputs": [],
"source": [
"from semantic_kernel.planners.stepwise_planner import StepwisePlanner, StepwisePlannerConfig\n",
"\n",
"planner = StepwisePlanner(kernel, StepwisePlannerConfig(max_iterations=10, min_iteration_time_ms=1000))"
]
},
@@ -139,6 +139,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "5338d3ac",
"metadata": {},
"outputs": [],
"source": [
@@ -179,6 +180,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "24764c48",
"metadata": {},
"outputs": [],
"source": [
+229 -168
View File
@@ -1,171 +1,232 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Using Hugging Face With Plugins\n",
"\n",
"In this notebook, we demonstrate using Hugging Face models for Plugins using both SemanticMemory and text completions. \n",
"\n",
"SK supports downloading models from the Hugging Face that can perform the following tasks: text-generation, text2text-generation, summarization, and sentence-similarity. You can search for models by task at https://huggingface.co/models."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.5.1.dev0\n",
"\n",
"# Note that additional dependencies are required for the Hugging Face connectors:\n",
"!python -m pip install torch==2.0.0\n",
"!python -m pip install transformers==^4.28.1\n",
"!python -m pip install sentence-transformers==^2.2.2"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"import semantic_kernel.connectors.ai.hugging_face as sk_hf"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will create a kernel and add both text completion and embedding services. \n",
"\n",
"For text completion, we are choosing GPT2. This is a text-generation model. (Note: text-generation will repeat the input in the output, text2text-generation will not.)\n",
"For embeddings, we are using sentence-transformers/all-MiniLM-L6-v2. Vectors generated for this model are of length 384 (compared to a length of 1536 from OpenAI ADA).\n",
"\n",
"The following step may take a few minutes when run for the first time as the models will be downloaded to your local machine."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure LLM service\n",
"kernel.add_text_completion_service(\n",
" service_id=\"gpt2\",\n",
" service=sk_hf.HuggingFaceTextCompletion(ai_model_id=\"gpt2\", task=\"text-generation\"),\n",
")\n",
"kernel.add_text_embedding_generation_service(\n",
" service_id=\"sentence-transformers/all-MiniLM-L6-v2\",\n",
" service=sk_hf.HuggingFaceTextEmbedding(ai_model_id=\"sentence-transformers/all-MiniLM-L6-v2\"),\n",
")\n",
"kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())\n",
"kernel.import_plugin(sk.core_plugins.TextMemoryPlugin())"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "2a7e7ca4",
"metadata": {},
"source": [
"### Add Memories and Define a plugin to use them\n",
"\n",
"Most models available on huggingface.co are not as powerful as OpenAI GPT-3+. Your plugins will likely need to be simpler to accommodate this."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d096504c",
"metadata": {},
"outputs": [],
"source": [
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info1\", text=\"Sharks are fish.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info2\", text=\"Whales are mammals.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info3\", text=\"Penguins are birds.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info4\", text=\"Dolphins are mammals.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info5\", text=\"Flies are insects.\")\n",
"\n",
"# Define semantic function using SK prompt template language\n",
"my_prompt = \"\"\"I know these animal facts: {{recall $query1}} {{recall $query2}} {{recall $query3}} and \"\"\"\n",
"\n",
"# Create the semantic function\n",
"my_function = kernel.create_semantic_function(prompt_template=my_prompt, max_tokens=45, temperature=0.5, top_p=0.5)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "2calf857",
"metadata": {},
"source": [
"Let's now see what the completion looks like! Remember, \"gpt2\" is nowhere near as large as ChatGPT, so expect a much simpler answer."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "628c843e",
"metadata": {},
"outputs": [],
"source": [
"context = kernel.create_new_context()\n",
"context[sk.core_plugins.TextMemoryPlugin.COLLECTION_PARAM] = \"animal-facts\"\n",
"context[sk.core_plugins.TextMemoryPlugin.RELEVANCE_PARAM] = \"0.3\"\n",
"\n",
"context[\"query1\"] = \"animal that swims\"\n",
"context[\"query2\"] = \"animal that flies\"\n",
"context[\"query3\"] = \"penguins are?\"\n",
"output = await kernel.run(my_function, input_vars=context.variables)\n",
"\n",
"output = str(output).strip()\n",
"\n",
"query_result1 = await kernel.memory.search(\n",
" collection=\"animal-facts\", query=context[\"query1\"], limit=1, min_relevance_score=0.3\n",
")\n",
"query_result2 = await kernel.memory.search(\n",
" collection=\"animal-facts\", query=context[\"query2\"], limit=1, min_relevance_score=0.3\n",
")\n",
"query_result3 = await kernel.memory.search(\n",
" collection=\"animal-facts\", query=context[\"query3\"], limit=1, min_relevance_score=0.3\n",
")\n",
"\n",
"print(f\"gpt2 completed prompt with: '{output}'\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Using Hugging Face With Plugins\n",
"\n",
"In this notebook, we demonstrate using Hugging Face models for Plugins using both SemanticMemory and text completions. \n",
"\n",
"SK supports downloading models from the Hugging Face that can perform the following tasks: text-generation, text2text-generation, summarization, and sentence-similarity. You can search for models by task at https://huggingface.co/models."
]
},
"nbformat": 4,
"nbformat_minor": 5
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.5.1.dev0\n",
"\n",
"# Note that additional dependencies are required for the Hugging Face connectors:\n",
"!python -m pip install torch==2.0.0\n",
"!python -m pip install transformers==^4.28.1\n",
"!python -m pip install sentence-transformers==^2.2.2"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"import semantic_kernel.connectors.ai.hugging_face as sk_hf"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "753ab756",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.HuggingFace"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will create a kernel and add both text completion and embedding services. \n",
"\n",
"For text completion, we are choosing GPT2. This is a text-generation model. (Note: text-generation will repeat the input in the output, text2text-generation will not.)\n",
"For embeddings, we are using sentence-transformers/all-MiniLM-L6-v2. Vectors generated for this model are of length 384 (compared to a length of 1536 from OpenAI ADA).\n",
"\n",
"The following step may take a few minutes when run for the first time as the models will be downloaded to your local machine."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure LLM service\n",
"if selectedService == Service.HuggingFace:\n",
" text_service_id = \"gpt2\"\n",
" kernel.add_service(\n",
" service=sk_hf.HuggingFaceTextCompletion(\n",
" service_id=text_service_id, ai_model_id=text_service_id, task=\"text-generation\"\n",
" ),\n",
" )\n",
" embed_service_id = \"sentence-transformers/all-MiniLM-L6-v2\"\n",
" embedding_svc = sk_hf.HuggingFaceTextEmbedding(service_id=embed_service_id, ai_model_id=embed_service_id)\n",
" kernel.add_service(\n",
" service=embedding_svc,\n",
" )\n",
" kernel.use_memory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=embedding_svc)\n",
" kernel.import_plugin(sk.core_plugins.TextMemoryPlugin(), \"TextMemoryPlugin\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "2a7e7ca4",
"metadata": {},
"source": [
"### Add Memories and Define a plugin to use them\n",
"\n",
"Most models available on huggingface.co are not as powerful as OpenAI GPT-3+. Your plugins will likely need to be simpler to accommodate this."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d096504c",
"metadata": {},
"outputs": [],
"source": [
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info1\", text=\"Sharks are fish.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info2\", text=\"Whales are mammals.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info3\", text=\"Penguins are birds.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info4\", text=\"Dolphins are mammals.\")\n",
"await kernel.memory.save_information(collection=\"animal-facts\", id=\"info5\", text=\"Flies are insects.\")\n",
"\n",
"# Define prompt function using SK prompt template language\n",
"my_prompt = \"\"\"I know these animal facts: {{recall $query1}} {{recall $query2}} {{recall $query3}} and \"\"\"\n",
"\n",
"execution_settings = sk_hf.HuggingFacePromptExecutionSettings(\n",
" service_id=text_service_id,\n",
" ai_model_id=text_service_id,\n",
" max_tokens=45,\n",
" temperature=0.5,\n",
" top_p=0.5,\n",
")\n",
"\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=my_prompt,\n",
" name=\"text_complete\",\n",
" template_format=\"semantic-kernel\",\n",
" execution_settings=execution_settings,\n",
")\n",
"\n",
"my_function = kernel.create_function_from_prompt(prompt_template_config=prompt_template_config)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "2calf857",
"metadata": {},
"source": [
"Let's now see what the completion looks like! Remember, \"gpt2\" is nowhere near as large as ChatGPT, so expect a much simpler answer."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "628c843e",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/evmattso/.cache/pypoetry/virtualenvs/semantic-kernel-eoLGgW5m-py3.10/lib/python3.10/site-packages/transformers/generation/configuration_utils.py:389: UserWarning: `do_sample` is set to `False`. However, `temperature` is set to `0.5` -- this flag is only used in sample-based generation modes. You should set `do_sample=True` or unset `temperature`. This was detected when initializing the generation config instance, which means the corresponding file may hold incorrect parameterization and should be fixed.\n",
" warnings.warn(\n",
"/home/evmattso/.cache/pypoetry/virtualenvs/semantic-kernel-eoLGgW5m-py3.10/lib/python3.10/site-packages/transformers/generation/configuration_utils.py:394: UserWarning: `do_sample` is set to `False`. However, `top_p` is set to `0.5` -- this flag is only used in sample-based generation modes. You should set `do_sample=True` or unset `top_p`. This was detected when initializing the generation config instance, which means the corresponding file may hold incorrect parameterization and should be fixed.\n",
" warnings.warn(\n",
"/home/evmattso/.cache/pypoetry/virtualenvs/semantic-kernel-eoLGgW5m-py3.10/lib/python3.10/site-packages/transformers/generation/utils.py:1355: UserWarning: Using the model-agnostic default `max_length` (=20) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.\n",
" warnings.warn(\n",
"/home/evmattso/.cache/pypoetry/virtualenvs/semantic-kernel-eoLGgW5m-py3.10/lib/python3.10/site-packages/transformers/generation/utils.py:1363: UserWarning: Input length of input_ids is 21, but `max_length` is set to 20. This can lead to unexpected behavior. You should consider increasing `max_new_tokens`.\n",
" warnings.warn(\n",
"Error occurred while invoking function f_HSMshmxNdvxpFZSl: ('Hugging Face completion failed', ValueError('If `eos_token_id` is defined, make sure that `pad_token_id` is defined.'), None)\n",
"Error occurred while invoking function f_HSMshmxNdvxpFZSl: ('Hugging Face completion failed', ValueError('If `eos_token_id` is defined, make sure that `pad_token_id` is defined.'), None)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"gpt2 completed prompt with: ''\n"
]
}
],
"source": [
"from semantic_kernel.functions import KernelArguments\n",
"\n",
"arguments = KernelArguments(\n",
" query1=\"animal that swims\",\n",
" query2=\"animal that flies\",\n",
" query3=\"penguins are?\",\n",
" collection=\"animal-facts\",\n",
" relevance=0.3,\n",
")\n",
"\n",
"output = await kernel.invoke(\n",
" my_function,\n",
" arguments,\n",
")\n",
"\n",
"output = str(output).strip()\n",
"\n",
"query_result1 = await kernel.memory.search(\n",
" collection=\"animal-facts\", query=arguments[\"query1\"], limit=1, min_relevance_score=0.3\n",
")\n",
"query_result2 = await kernel.memory.search(\n",
" collection=\"animal-facts\", query=arguments[\"query2\"], limit=1, min_relevance_score=0.3\n",
")\n",
"query_result3 = await kernel.memory.search(\n",
" collection=\"animal-facts\", query=arguments[\"query3\"], limit=1, min_relevance_score=0.3\n",
")\n",
"\n",
"print(f\"{text_service_id} completed prompt with: '{output}'\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+199 -94
View File
@@ -49,6 +49,19 @@
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fddb5403",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -57,26 +70,31 @@
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureChatCompletion,\n",
" OpenAIChatCompletion,\n",
")\n",
"import semantic_kernel.connectors.ai.open_ai as sk_oai\n",
"from semantic_kernel.prompt_template.input_variable import InputVariable\n",
"\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"if selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
"# Configure AI service used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat\" # used later in the notebook\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"turbo\", endpoint=endpoint, api_key=api_key\n",
" service_id=service_id, deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
" kernel.add_chat_service(\"chat_completion\", azure_chat_service)\n",
"else:\n",
" kernel.add_service(azure_chat_service)\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
" kernel.add_chat_service(\"chat-gpt\", oai_chat_service)"
" service_id = \"oai_chat\" # used later in the notebook\n",
" oai_chat_service = OpenAIChatCompletion(\n",
" service_id=service_id, ai_model_id=\"gpt-4-turbo-1106\", api_key=api_key, org_id=org_id\n",
" )\n",
" kernel.add_service(oai_chat_service)"
]
},
{
@@ -105,7 +123,7 @@
"outputs": [],
"source": [
"import random\n",
"from semantic_kernel.plugin_definition import kernel_function\n",
"from semantic_kernel.functions import kernel_function\n",
"\n",
"\n",
"class GenerateNumberPlugin:\n",
@@ -150,23 +168,44 @@
"metadata": {},
"outputs": [],
"source": [
"sk_prompt = \"\"\"\n",
"prompt = \"\"\"\n",
"Write a short story about two Corgis on an adventure.\n",
"The story must be:\n",
"- G rated\n",
"- Have a positive message\n",
"- No sexism, racism or other bias/bigotry\n",
"- Be exactly {{$input}} paragraphs long\n",
"- Be exactly {{$input}} paragraphs long. It must be this length.\n",
"\"\"\"\n",
"\n",
"corgi_story = kernel.create_semantic_function(\n",
" prompt_template=sk_prompt,\n",
"if selectedService == Service.OpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=\"gpt-3.5-turbo-1106\",\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=deployment,\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=prompt,\n",
" name=\"story\",\n",
" template_format=\"semantic-kernel\",\n",
" input_variables=[\n",
" InputVariable(name=\"input\", description=\"The user input\", is_required=True),\n",
" ],\n",
" execution_settings=execution_settings,\n",
")\n",
"\n",
"corgi_story = kernel.create_function_from_prompt(\n",
" function_name=\"CorgiStory\",\n",
" plugin_name=\"CorgiPlugin\",\n",
" description=\"Write a short story about two Corgis on an adventure\",\n",
" max_tokens=500,\n",
" temperature=0.5,\n",
" top_p=0.5,\n",
" prompt_template_config=prompt_template_config,\n",
")\n",
"\n",
"generate_number_plugin = kernel.import_plugin(GenerateNumberPlugin(), \"GenerateNumberPlugin\")"
@@ -181,7 +220,7 @@
"source": [
"# Run the number generator\n",
"generate_number_three_or_higher = generate_number_plugin[\"GenerateNumberThreeOrHigher\"]\n",
"number_result = await generate_number_three_or_higher(6)\n",
"number_result = await generate_number_three_or_higher(kernel, input=6)\n",
"print(number_result)"
]
},
@@ -192,7 +231,15 @@
"metadata": {},
"outputs": [],
"source": [
"story = await corgi_story.invoke(input=number_result.result)"
"story = await corgi_story.invoke(kernel, input=number_result.value)"
]
},
{
"cell_type": "markdown",
"id": "7245e7a2",
"metadata": {},
"source": [
"_Note: depending on which model you're using, it may not respond with the proper number of paragraphs._"
]
},
{
@@ -202,7 +249,7 @@
"metadata": {},
"outputs": [],
"source": [
"print(\"Generating a corgi story exactly {} paragraphs long: \".format(number_result.result))\n",
"print(f\"Generating a corgi story exactly {number_result.value} paragraphs long.\")\n",
"print(\"=====================================================\")\n",
"print(story)"
]
@@ -213,13 +260,13 @@
"id": "8ef29d16",
"metadata": {},
"source": [
"## Context Variables\n",
"## Kernel Functions with Annotated Parameters\n",
"\n",
"That works! But let's expand on our example to make it more generic.\n",
"\n",
"For the native function, we'll introduce the lower limit variable. This means that a user will input two numbers and the number generator function will pick a number between the first and second input.\n",
"\n",
"We'll make use of the `semantic_kernel.ContextVariables` class to do hold these variables.\n"
"We'll make use of the Python's `Annotated` class to hold these variables.\n"
]
},
{
@@ -229,27 +276,40 @@
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"import semantic_kernel as sk\n",
"from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureChatCompletion,\n",
" OpenAIChatCompletion,\n",
")\n",
"\n",
"if sys.version_info >= (3, 9):\n",
" from typing import Annotated\n",
"else:\n",
" from typing_extensions import Annotated\n",
"\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"if selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
"# Configure AI service used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"aoai_chat\" # used later in the notebook\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"turbo\", endpoint=endpoint, api_key=api_key\n",
" service_id=service_id, deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
" kernel.add_chat_service(\"chat_completion\", azure_chat_service)\n",
"else:\n",
" kernel.add_service(azure_chat_service)\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
" kernel.add_chat_service(\"chat-gpt\", oai_chat_service)"
" service_id = \"oai_chat\" # used later in the notebook\n",
" oai_chat_service = OpenAIChatCompletion(\n",
" service_id=service_id, ai_model_id=\"gpt-4-turbo-1106\", api_key=api_key, org_id=org_id\n",
" )\n",
" kernel.add_service(oai_chat_service)"
]
},
{
@@ -258,7 +318,7 @@
"id": "091f45e4",
"metadata": {},
"source": [
"Let's start with the native function. Notice that we're also adding `@kernel_function_context_parameter` decorators to the function here to provide context about what variables need to be provided to the function, and any defaults for those inputs. Using the `@kernel_function_context_parameter` decorator provides the name, description and default values for a function's inputs to the [planner.](./05-using-the-planner.ipynb)\n"
"Let's start with the native function. Notice that we're add the `@kernel_function` decorator that holds the name of the function as well as an optional description. The input parameters are configured as part of the function's signature, and we use the `Annotated` type to specify the required input arguments.\n"
]
},
{
@@ -269,8 +329,7 @@
"outputs": [],
"source": [
"import random\n",
"from semantic_kernel.plugin_definition import kernel_function, kernel_function_context_parameter\n",
"from semantic_kernel import KernelContext\n",
"from semantic_kernel.functions import kernel_function\n",
"\n",
"\n",
"class GenerateNumberPlugin:\n",
@@ -279,12 +338,14 @@
" \"\"\"\n",
"\n",
" @kernel_function(\n",
" description=\"Generate a random number between min and max\",\n",
" name=\"GenerateNumber\",\n",
" description=\"Generate a random number between min and max\",\n",
" )\n",
" @kernel_function_context_parameter(name=\"min\", description=\"Minimum number of paragraphs.\")\n",
" @kernel_function_context_parameter(name=\"max\", description=\"Maximum number of paragraphs.\", default_value=\"10\")\n",
" def generate_number(self, context: KernelContext) -> str:\n",
" def generate_number(\n",
" self,\n",
" min: Annotated[int, \"the minimum number of paragraphs\"],\n",
" max: Annotated[int, \"the maximum number of paragraphs\"] = 10,\n",
" ) -> Annotated[int, \"the output is a number\"]:\n",
" \"\"\"\n",
" Generate a number between min-max\n",
" Example:\n",
@@ -296,9 +357,9 @@
" int value\n",
" \"\"\"\n",
" try:\n",
" return str(random.randint(int(context[\"min\"]), int(context[\"max\"])))\n",
" return str(random.randint(min, max))\n",
" except ValueError as e:\n",
" print(f\"Invalid input {context['min']} {context['max']}\")\n",
" print(f\"Invalid input {min} and {max}\")\n",
" raise e"
]
},
@@ -329,7 +390,7 @@
"metadata": {},
"outputs": [],
"source": [
"sk_prompt = \"\"\"\n",
"prompt = \"\"\"\n",
"Write a short story about two Corgis on an adventure.\n",
"The story must be:\n",
"- G rated\n",
@@ -339,43 +400,46 @@
"- Be written in this language: {{$language}}\n",
"\"\"\"\n",
"\n",
"corgi_story = kernel.create_semantic_function(\n",
" prompt_template=sk_prompt,\n",
"if selectedService == Service.OpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=\"gpt-3.5-turbo-1106\",\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=deployment,\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=prompt,\n",
" name=\"summarize\",\n",
" template_format=\"semantic-kernel\",\n",
" input_variables=[\n",
" InputVariable(name=\"paragraph_count\", description=\"The number of paragraphs\", is_required=True),\n",
" InputVariable(name=\"language\", description=\"The language of the story\", is_required=True),\n",
" ],\n",
" execution_settings=execution_settings,\n",
")\n",
"\n",
"corgi_story = kernel.create_function_from_prompt(\n",
" function_name=\"CorgiStory\",\n",
" plugin_name=\"CorgiPlugin\",\n",
" description=\"Write a short story about two Corgis on an adventure\",\n",
" max_tokens=500,\n",
" temperature=0.5,\n",
" top_p=0.5,\n",
" prompt_template_config=prompt_template_config,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "fdce1872",
"metadata": {},
"source": [
"Now we can call this using our `invoke` function by passing in our `context_variables` in the `variables` parameter.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "97d8d3c9",
"metadata": {},
"outputs": [],
"source": [
"context_variables = sk.ContextVariables(variables={\"min\": \"1\", \"max\": \"5\", \"language\": \"Spanish\"})"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "c8778bad",
"metadata": {},
"source": [
"Let's add a paragraph count to our context variables\n"
"Let's generate a paragraph count.\n"
]
},
{
@@ -385,8 +449,17 @@
"metadata": {},
"outputs": [],
"source": [
"num = await generate_number.invoke(variables=context_variables)\n",
"context_variables[\"paragraph_count\"] = num.result"
"result = await generate_number.invoke(kernel, min=1, max=5)\n",
"num_paragraphs = result.value\n",
"print(f\"Generating a corgi story {num_paragraphs} paragraphs long.\")"
]
},
{
"cell_type": "markdown",
"id": "225a9147",
"metadata": {},
"source": [
"We can now invoke our corgi_story function using the `kernel` and the keyword arguments `paragraph_count` and `language`."
]
},
{
@@ -397,7 +470,8 @@
"outputs": [],
"source": [
"# Pass the output to the semantic story function\n",
"story = await corgi_story.invoke(variables=context_variables)"
"desired_language = \"Spanish\"\n",
"story = await corgi_story.invoke(kernel, paragraph_count=num_paragraphs, language=desired_language)"
]
},
{
@@ -409,11 +483,7 @@
},
"outputs": [],
"source": [
"print(\n",
" \"Generating a corgi story exactly {} paragraphs long in {} language: \".format(\n",
" context_variables[\"paragraph_count\"], context_variables[\"language\"]\n",
" )\n",
")\n",
"print(f\"Generating a corgi story {num_paragraphs} paragraphs long in {desired_language}.\")\n",
"print(\"=====================================================\")\n",
"print(story)"
]
@@ -426,7 +496,7 @@
"source": [
"## Calling Native Functions within a Semantic Function\n",
"\n",
"One neat thing about the Semantic Kernel is that you can also call native functions from within Semantic Functions!\n",
"One neat thing about the Semantic Kernel is that you can also call native functions from within Prompt Functions!\n",
"\n",
"We will make our CorgiStory semantic function call a native function `GenerateNames` which will return names for our Corgi characters.\n",
"\n",
@@ -441,7 +511,7 @@
"outputs": [],
"source": [
"import random\n",
"from semantic_kernel.plugin_definition import kernel_function\n",
"from semantic_kernel.functions import kernel_function\n",
"\n",
"\n",
"class GenerateNamesPlugin:\n",
@@ -484,7 +554,7 @@
"metadata": {},
"outputs": [],
"source": [
"sk_prompt = \"\"\"\n",
"prompt = \"\"\"\n",
"Write a short story about two Corgis on an adventure.\n",
"The story must be:\n",
"- G rated\n",
@@ -496,6 +566,46 @@
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be72a503",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=\"gpt-3.5-turbo-1106\",\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(\n",
" service_id=service_id,\n",
" ai_model_id=deployment,\n",
" max_tokens=2000,\n",
" temperature=0.7,\n",
" )\n",
"\n",
"prompt_template_config = sk.PromptTemplateConfig(\n",
" template=prompt,\n",
" name=\"corgi-new\",\n",
" template_format=\"semantic-kernel\",\n",
" input_variables=[\n",
" InputVariable(name=\"paragraph_count\", description=\"The number of paragraphs\", is_required=True),\n",
" InputVariable(name=\"language\", description=\"The language of the story\", is_required=True),\n",
" ],\n",
" execution_settings=execution_settings,\n",
")\n",
"\n",
"corgi_story = kernel.create_function_from_prompt(\n",
" function_name=\"CorgiStoryUpdated\",\n",
" plugin_name=\"CorgiPluginUpdated\",\n",
" prompt_template_config=prompt_template_config,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -503,9 +613,8 @@
"metadata": {},
"outputs": [],
"source": [
"context_variables = sk.ContextVariables(variables={\"min\": \"1\", \"max\": \"5\", \"language\": \"Spanish\"})\n",
"num = await generate_number.invoke(variables=context_variables)\n",
"context_variables[\"paragraph_count\"] = str(num.result)"
"result = await generate_number.invoke(kernel, min=1, max=5)\n",
"num_paragraphs = result.value"
]
},
{
@@ -515,8 +624,8 @@
"metadata": {},
"outputs": [],
"source": [
"# Pass the output to the semantic story function\n",
"story = await corgi_story.invoke(variables=context_variables)"
"desired_language = \"French\"\n",
"story = await corgi_story.invoke(kernel, paragraph_count=num_paragraphs, language=desired_language)"
]
},
{
@@ -526,11 +635,7 @@
"metadata": {},
"outputs": [],
"source": [
"print(\n",
" \"Generating a corgi story exactly {} paragraphs long in {} language: \".format(\n",
" context_variables[\"paragraph_count\"], context_variables[\"language\"]\n",
" )\n",
")\n",
"print(f\"Generating a corgi story {num_paragraphs} paragraphs long in {desired_language}.\")\n",
"print(\"=====================================================\")\n",
"print(story)"
]
@@ -545,9 +650,9 @@
"\n",
"A quick review of what we've learned here:\n",
"\n",
"- We've learned how to create native and semantic functions and register them to the kernel\n",
"- We've seen how we can use context variables to pass in more custom variables into our prompt\n",
"- We've seen how we can call native functions within semantic function prompts.\n"
"- We've learned how to create native and prompt functions and register them to the kernel\n",
"- We've seen how we can use Kernel Arguments to pass in more custom variables into our prompt\n",
"- We've seen how we can call native functions within a prompt.\n"
]
}
],
+23 -54
View File
@@ -105,14 +105,18 @@
"# Configure AI service used by the kernel\n",
"if useAzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" service_id = \"chat_completion\"\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"turbo\", endpoint=endpoint, api_key=api_key\n",
" service_id=service_id, deployment_name=\"turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
" kernel.add_chat_service(\"chat_completion\", azure_chat_service)\n",
" kernel.add_service(azure_chat_service)\n",
"else:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
" kernel.add_chat_service(\"chat-gpt\", oai_chat_service)"
" service_id = \"chat-gpt\"\n",
" oai_chat_service = OpenAIChatCompletion(\n",
" service_id=service_id, ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id\n",
" )\n",
" kernel.add_service(oai_chat_service)"
]
},
{
@@ -135,7 +139,9 @@
"# note: using plugins from the samples folder\n",
"plugins_directory = \"../../samples/plugins\"\n",
"\n",
"groundingSemanticFunctions = kernel.import_semantic_plugin_from_directory(plugins_directory, \"GroundingPlugin\")"
"groundingSemanticFunctions = kernel.import_plugin_from_prompt_directory(\n",
" service_id, plugins_directory, \"GroundingPlugin\"\n",
")"
]
},
{
@@ -209,28 +215,6 @@
"Now, let us start calling individual semantic functions."
]
},
{
"cell_type": "markdown",
"id": "4e435b18",
"metadata": {},
"source": [
"### Preparing the Context\n",
"\n",
"Semantic functions operate in a context, which provides extra parameters for their operation. For the grounding plugin, the context is expected to supply the topic for the reference checking, and some particular examples of them:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "60aa6fa0",
"metadata": {},
"outputs": [],
"source": [
"context = kernel.create_new_context()\n",
"context[\"topic\"] = \"people and places\"\n",
"context[\"example_entities\"] = \"John, Jane, mother, brother, Paris, Rome\""
]
},
{
"cell_type": "markdown",
"id": "071c05e4",
@@ -248,9 +232,14 @@
"metadata": {},
"outputs": [],
"source": [
"extraction_result = await entity_extraction(summary_text, context=context)\n",
"extraction_result = await kernel.invoke(\n",
" entity_extraction,\n",
" input=summary_text,\n",
" topic=\"people and places\",\n",
" example_entities=\"John, Jane, mother, brother, Paris, Rome\",\n",
")\n",
"\n",
"print(extraction_result.result)"
"print(extraction_result)"
]
},
{
@@ -271,16 +260,6 @@
"We now use the grounding text to see if the entities we found are grounded. We start by adding the grounding text to our context:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "16cef37a",
"metadata": {},
"outputs": [],
"source": [
"context[\"reference_context\"] = grounding_text"
]
},
{
"cell_type": "markdown",
"id": "894e38d7",
@@ -296,9 +275,9 @@
"metadata": {},
"outputs": [],
"source": [
"grounding_result = await reference_check(extraction_result.result, context=context)\n",
"grounding_result = await kernel.invoke(reference_check, input=extraction_result.value, reference_context=grounding_text)\n",
"\n",
"print(grounding_result.result)"
"print(grounding_result)"
]
},
{
@@ -309,16 +288,6 @@
"So we now have a list of ungrounded entities (of course, this list may not be well grounded itself). Let us store this in the context:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a76aba58",
"metadata": {},
"outputs": [],
"source": [
"context[\"ungrounded_entities\"] = grounding_result.result"
]
},
{
"cell_type": "markdown",
"id": "35c1c329",
@@ -336,9 +305,9 @@
"metadata": {},
"outputs": [],
"source": [
"excision_result = await entity_excision(summary_text, context=context)\n",
"excision_result = await kernel.invoke(entity_excision, input=summary_text, ungrounded_entities=grounding_result.value)\n",
"\n",
"print(excision_result.result)"
"print(excision_result)"
]
}
],
@@ -358,7 +327,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.10.12"
}
},
"nbformat": 4,
@@ -1,404 +1,417 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Multiple Results\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "fb81bacd",
"metadata": {},
"source": [
"In this notebook we show how you can in a single request, have the LLM model return multiple results per prompt. This is useful for running experiments where you want to evaluate the robustness of your prompt and the parameters of your config against a particular large language model.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f4bfee4",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.AzureOpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"\n",
"if selectedService == Service.OpenAI or selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (\n",
" OpenAITextPromptExecutionSettings,\n",
" OpenAIChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (\n",
" AzureChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
" AzureChatCompletion,\n",
" OpenAITextCompletion,\n",
" OpenAIChatCompletion,\n",
" )\n",
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextCompletion"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will set up the text and chat services we will be submitting prompts to.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure Azure LLM service\n",
"if selectedService == Service.AzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_text_service = AzureTextCompletion(\n",
" deployment_name=\"gpt-35-turbo-instruct\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your text model (e.g. gpt-35-turbo-instruct or text-davinci-003)\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_text_service = OpenAITextCompletion(ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id)\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
"\n",
"# Configure Hugging Face service\n",
"if selectedService == Service.HuggingFace:\n",
" hf_text_service = HuggingFaceTextCompletion(ai_model_id=\"distilgpt2\", task=\"text-generation\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "50561d82",
"metadata": {},
"source": [
"Next, we'll set up the completion request settings for text completion services.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "628c843e",
"metadata": {},
"outputs": [],
"source": [
"oai_text_prompt_execution_settings = OpenAITextPromptExecutionSettings(\n",
" extension_data={\n",
" \"max_tokens\": 80,\n",
" \"temperature\": 0.7,\n",
" \"top_p\": 1,\n",
" \"frequency_penalty\": 0.5,\n",
" \"presence_penalty\": 0.5,\n",
" \"number_of_responses\": 3,\n",
" }\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "857a9c89",
"metadata": {},
"source": [
"## Multiple Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2979db8",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" results = await oai_text_service.complete(prompt=prompt, settings=oai_text_prompt_execution_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "4288d09f",
"metadata": {},
"source": [
"## Multiple Azure Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5319f14d",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" prompt = \"provide me a list of possible meanings for the acronym 'ORLD'\"\n",
" results = await azure_text_service.complete(prompt=prompt, settings=oai_text_prompt_execution_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "eb548f9c",
"metadata": {},
"source": [
"## Multiple Hugging Face Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a148709",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import (\n",
" HuggingFacePromptExecutionSettings,\n",
" )\n",
"\n",
" hf_prompt_execution_settings = HuggingFacePromptExecutionSettings(\n",
" extension_data={\"max_new_tokens\": 80, \"temperature\": 0.7, \"top_p\": 1}\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9525e4f3",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" prompt = \"The purpose of a rubber duck is\"\n",
" results = await hf_text_service.complete(prompt=prompt, prompt_execution_settings=hf_prompt_execution_settings)\n",
" print(\"\".join(results))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "da632e12",
"metadata": {},
"source": [
"Here, we're setting up the settings for Chat completions.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5f11e46",
"metadata": {},
"outputs": [],
"source": [
"oai_chat_prompt_execution_settings = OpenAIChatPromptExecutionSettings(\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d6bf238e",
"metadata": {},
"source": [
"## Multiple OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dabc6a4c",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" role = \"user\"\n",
" content = (\n",
" \"It's a beautiful day outside, birds are singing, flowers are blooming. On days like these, kids like you...\"\n",
" )\n",
" message = {\"role\": role, \"content\": content}\n",
" results = await oai_chat_service.complete_chat(messages=[message], settings=oai_chat_prompt_execution_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i}: {result[0]}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "cdb8f740",
"metadata": {},
"source": [
"## Multiple Azure OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "66ba4767",
"metadata": {},
"outputs": [],
"source": [
"az_oai_prompt_execution_settings = AzureChatPromptExecutionSettings(\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b74a64a9",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" role = \"user\"\n",
" content = \"Tomorow is going to be a great day, I can feel it. I'm going to wake up early, go for a run, and then...\"\n",
" message = {\"role\": role, \"content\": content}\n",
" results = await azure_chat_service.complete_chat(messages=[message], settings=az_oai_prompt_execution_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i}: {result[0]}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "98c8191d",
"metadata": {},
"source": [
"## Streaming Multiple Results\n",
"\n",
"Here is an example pattern if you want to stream your multiple results. Note that this is not supported for Hugging Face text completions at this time.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "26a37702",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" import os\n",
" from IPython.display import clear_output\n",
" import time\n",
"\n",
" # Determine the clear command based on OS\n",
" clear_command = \"cls\" if os.name == \"nt\" else \"clear\"\n",
"\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" stream = oai_text_service.complete_stream(prompt=prompt, settings=oai_text_prompt_execution_settings)\n",
" number_of_responses = oai_text_prompt_execution_settings.number_of_responses\n",
" texts = [\"\"] * number_of_responses\n",
"\n",
" last_clear_time = time.time()\n",
" clear_interval = 0.5 # seconds\n",
"\n",
" # Note: there are some quirks with displaying the output, which sometimes flashes and disappears.\n",
" # This could be influenced by a few factors specific to Jupyter notebooks and asynchronous processing.\n",
" # The following code attempts to buffer the results to avoid the output flashing on/off the screen.\n",
"\n",
" async for results in stream:\n",
" current_time = time.time()\n",
"\n",
" # Update texts with new results\n",
" for idx, result in enumerate(results):\n",
" if idx < number_of_responses:\n",
" texts[idx] += result\n",
"\n",
" # Clear and display output at intervals\n",
" if current_time - last_clear_time > clear_interval:\n",
" clear_output(wait=True)\n",
" for idx, text in enumerate(texts):\n",
" print(f\"Result {idx + 1}: {text}\")\n",
" last_clear_time = current_time\n",
"\n",
" print(\"----------------------------------------\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Multiple Results\n"
]
},
"nbformat": 4,
"nbformat_minor": 5
{
"attachments": {},
"cell_type": "markdown",
"id": "fb81bacd",
"metadata": {},
"source": [
"In this notebook we show how you can in a single request, have the LLM model return multiple results per prompt. This is useful for running experiments where you want to evaluate the robustness of your prompt and the parameters of your config against a particular large language model.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f4bfee4",
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.OpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.models.ai.chat_completion.chat_history import ChatHistory\n",
"\n",
"if selectedService == Service.OpenAI or selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (\n",
" OpenAITextPromptExecutionSettings,\n",
" OpenAIChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (\n",
" AzureChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
" AzureChatCompletion,\n",
" OpenAITextCompletion,\n",
" OpenAIChatCompletion,\n",
" )\n",
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextCompletion"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will set up the text and chat services we will be submitting prompts to.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure Azure LLM service\n",
"if selectedService == Service.AzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_text_service = AzureTextCompletion(\n",
" service_id=\"aoai_text\", deployment_name=\"gpt-35-turbo-instruct\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your text model (e.g. gpt-35-turbo-instruct or text-davinci-003)\n",
" azure_chat_service = AzureChatCompletion(\n",
" service_id=\"aoai_chat\", deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_text_service = OpenAITextCompletion(\n",
" service_id=\"oai_text\", ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id\n",
" )\n",
" oai_chat_service = OpenAIChatCompletion(\n",
" service_id=\"oai_chat\", ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id\n",
" )\n",
"\n",
"# Configure Hugging Face service\n",
"if selectedService == Service.HuggingFace:\n",
" hf_text_service = HuggingFaceTextCompletion(service_id=\"hf_text\", ai_model_id=\"distilgpt2\", task=\"text-generation\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "50561d82",
"metadata": {},
"source": [
"Next, we'll set up the completion request settings for text completion services.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "628c843e",
"metadata": {},
"outputs": [],
"source": [
"oai_text_prompt_execution_settings = OpenAITextPromptExecutionSettings(\n",
" service=\"oai_text\",\n",
" extension_data={\n",
" \"max_tokens\": 80,\n",
" \"temperature\": 0.7,\n",
" \"top_p\": 1,\n",
" \"frequency_penalty\": 0.5,\n",
" \"presence_penalty\": 0.5,\n",
" \"number_of_responses\": 3,\n",
" },\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "857a9c89",
"metadata": {},
"source": [
"## Multiple Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2979db8",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" chat = ChatHistory()\n",
" chat.add_user_message(\"What is the purpose of a rubber duck?\")\n",
" results = await oai_text_service.complete(chat_history=chat, settings=oai_text_prompt_execution_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "4288d09f",
"metadata": {},
"source": [
"## Multiple Azure Open AI Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5319f14d",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" chat = ChatHistory()\n",
" chat.add_user_message(\"provide me a list of possible meanings for the acronym 'ORLD'\")\n",
" results = await azure_text_service.complete(chat_history=chat, settings=oai_text_prompt_execution_settings)\n",
" i = 1\n",
" for result in results:\n",
" print(f\"Result {i}: {result}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "eb548f9c",
"metadata": {},
"source": [
"## Multiple Hugging Face Text Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a148709",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import (\n",
" HuggingFacePromptExecutionSettings,\n",
" )\n",
"\n",
" hf_prompt_execution_settings = HuggingFacePromptExecutionSettings(\n",
" service_id=\"hf_text\", extension_data={\"max_new_tokens\": 80, \"temperature\": 0.7, \"top_p\": 1}\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9525e4f3",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" prompt = \"The purpose of a rubber duck is\"\n",
" chat = ChatHistory()\n",
" chat.add_user_message(prompt)\n",
" results = await hf_text_service.complete(chat_history=chat, prompt_execution_settings=hf_prompt_execution_settings)\n",
" print(\"\".join(results))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "da632e12",
"metadata": {},
"source": [
"Here, we're setting up the settings for Chat completions.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5f11e46",
"metadata": {},
"outputs": [],
"source": [
"oai_chat_prompt_execution_settings = OpenAIChatPromptExecutionSettings(\n",
" service_id=\"oai_chat\",\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d6bf238e",
"metadata": {},
"source": [
"## Multiple OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dabc6a4c",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" chat = ChatHistory()\n",
" chat.add_user_message(\n",
" \"It's a beautiful day outside, birds are singing, flowers are blooming. On days like these, kids like you...\"\n",
" )\n",
" results = await oai_chat_service.complete_chat(chat_history=chat, settings=oai_chat_prompt_execution_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i+1}: {str(result)}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "cdb8f740",
"metadata": {},
"source": [
"## Multiple Azure OpenAI Chat Completions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "66ba4767",
"metadata": {},
"outputs": [],
"source": [
"az_oai_prompt_execution_settings = AzureChatPromptExecutionSettings(\n",
" service_id=\"aoai_chat\",\n",
" max_tokens=80,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
" number_of_responses=3,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b74a64a9",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" content = \"Tomorow is going to be a great day, I can feel it. I'm going to wake up early, go for a run, and then...\"\n",
" chat = ChatHistory()\n",
" chat.add_user_message(content)\n",
" results = await azure_chat_service.complete_chat(chat_history=chat, settings=az_oai_prompt_execution_settings)\n",
" i = 0\n",
" for result in results:\n",
" print(f\"Result {i+1}: {str(result)}\")\n",
" i += 1"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "98c8191d",
"metadata": {},
"source": [
"## Streaming Multiple Results\n",
"\n",
"Here is an example pattern if you want to stream your multiple results. Note that this is not supported for Hugging Face text completions at this time.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "26a37702",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" import os\n",
" from IPython.display import clear_output\n",
" import time\n",
"\n",
" # Determine the clear command based on OS\n",
" clear_command = \"cls\" if os.name == \"nt\" else \"clear\"\n",
"\n",
" chat = ChatHistory()\n",
" chat.add_user_message(\"what is the purpose of a rubber duck?\")\n",
"\n",
" stream = oai_text_service.complete_stream(chat_history=chat, settings=oai_text_prompt_execution_settings)\n",
" number_of_responses = oai_text_prompt_execution_settings.number_of_responses\n",
" texts = [\"\"] * number_of_responses\n",
"\n",
" last_clear_time = time.time()\n",
" clear_interval = 0.5 # seconds\n",
"\n",
" # Note: there are some quirks with displaying the output, which sometimes flashes and disappears.\n",
" # This could be influenced by a few factors specific to Jupyter notebooks and asynchronous processing.\n",
" # The following code attempts to buffer the results to avoid the output flashing on/off the screen.\n",
"\n",
" async for results in stream:\n",
" current_time = time.time()\n",
"\n",
" # Update texts with new results\n",
" for idx, result in enumerate(results):\n",
" if idx < number_of_responses:\n",
" texts[idx] += str(result)\n",
"\n",
" # Clear and display output at intervals\n",
" if current_time - last_clear_time > clear_interval:\n",
" clear_output(wait=True)\n",
" for idx, text in enumerate(texts):\n",
" print(f\"Result {idx + 1}: {text}\")\n",
" last_clear_time = current_time\n",
"\n",
" print(\"----------------------------------------\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+341 -328
View File
@@ -1,331 +1,344 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Streaming Results\n",
"\n",
"Here is an example pattern if you want to stream your multiple results. Note that this is not supported for Hugging Face text completions at this time.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.AzureOpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"\n",
"if selectedService == Service.OpenAI or selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (\n",
" OpenAITextPromptExecutionSettings,\n",
" OpenAIChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (\n",
" AzureChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
" AzureChatCompletion,\n",
" OpenAITextCompletion,\n",
" OpenAIChatCompletion,\n",
" )\n",
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextCompletion"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will set up the text and chat services we will be submitting prompts to.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure Azure LLM service\n",
"if selectedService == Service.AzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_text_service = AzureTextCompletion(\n",
" deployment_name=\"text-davinci-003\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your text model (e.g. gpt-35-turbo-instruct or text-davinci-003)\n",
" azure_chat_service = AzureChatCompletion(\n",
" deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_text_service = OpenAITextCompletion(ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id)\n",
" oai_chat_service = OpenAIChatCompletion(ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id)\n",
"\n",
"# Configure Hugging Face service\n",
"if selectedService == Service.HuggingFace:\n",
" hf_text_service = HuggingFaceTextCompletion(ai_model_id=\"distilgpt2\", task=\"text-generation\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "50561d82",
"metadata": {},
"source": [
"Next, we'll set up the completion request settings for text completion services.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "628c843e",
"metadata": {},
"outputs": [],
"source": [
"oai_prompt_execution_settings = OpenAITextPromptExecutionSettings(\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "857a9c89",
"metadata": {},
"source": [
"## Streaming Open AI Text Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2979db8",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" prompt = \"what is the purpose of a rubber duck?\"\n",
" stream = oai_text_service.complete_stream(prompt=prompt, settings=oai_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "4288d09f",
"metadata": {},
"source": [
"## Streaming Azure Open AI Text Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5319f14d",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" prompt = \"provide me a list of possible meanings for the acronym 'ORLD'\"\n",
" stream = azure_text_service.complete_stream(prompt=prompt, settings=oai_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "eb548f9c",
"metadata": {},
"source": [
"## Streaming Hugging Face Text Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be7b1c2e",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import (\n",
" HuggingFacePromptExecutionSettings,\n",
" )\n",
"\n",
" hf_prompt_execution_settings = HuggingFacePromptExecutionSettings(\n",
" extension_data={\n",
" \"max_new_tokens\": 80,\n",
" \"top_p\": 1,\n",
" \"eos_token_id\": 11,\n",
" \"pad_token_id\": 0,\n",
" }\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9525e4f3",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" prompt = \"The purpose of a rubber duck is\"\n",
" stream = hf_text_service.complete_stream(prompt=prompt, prompt_execution_settings=hf_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "da632e12",
"metadata": {},
"source": [
"Here, we're setting up the settings for Chat completions.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5f11e46",
"metadata": {},
"outputs": [],
"source": [
"oai_chat_prompt_execution_settings = OpenAIChatPromptExecutionSettings(\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d6bf238e",
"metadata": {},
"source": [
"## Streaming OpenAI Chat Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dabc6a4c",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" role = \"system\"\n",
" content = \"You are an AI assistant that helps people find information.\"\n",
" message = {\"role\": role, \"content\": content}\n",
" stream = oai_chat_service.complete_chat_stream(messages=[message], settings=oai_chat_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "cdb8f740",
"metadata": {},
"source": [
"## Streaming Azure OpenAI Chat Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da1e9f59",
"metadata": {},
"outputs": [],
"source": [
"az_oai_chat_prompt_execution_settings = AzureChatPromptExecutionSettings(\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b74a64a9",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" role = \"system\"\n",
" content = \"You are an AI assistant that helps people find information.\"\n",
" message = {\"role\": role, \"content\": content}\n",
" stream = azure_chat_service.complete_chat_stream(messages=[message], settings=az_oai_chat_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(text, end=\"\") # end = \"\" to avoid newlines"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "68e1c158",
"metadata": {},
"source": [
"# Streaming Results\n",
"\n",
"Here is an example pattern if you want to stream your multiple results. Note that this is not supported for Hugging Face text completions at this time.\n"
]
},
"nbformat": 4,
"nbformat_minor": 5
{
"cell_type": "code",
"execution_count": null,
"id": "a77bdf89",
"metadata": {},
"outputs": [],
"source": [
"!python -m pip install semantic-kernel==0.5.1.dev0"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from services import Service\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = Service.AzureOpenAI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "508ad44f",
"metadata": {},
"outputs": [],
"source": [
"import semantic_kernel as sk\n",
"from semantic_kernel.models.ai.chat_completion.chat_history import ChatHistory\n",
"\n",
"if selectedService == Service.OpenAI or selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (\n",
" OpenAITextPromptExecutionSettings,\n",
" OpenAIChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (\n",
" AzureChatPromptExecutionSettings,\n",
" )\n",
" from semantic_kernel.connectors.ai.open_ai import (\n",
" AzureTextCompletion,\n",
" AzureChatCompletion,\n",
" OpenAITextCompletion,\n",
" OpenAIChatCompletion,\n",
" )\n",
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face import HuggingFaceTextCompletion"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d8ddffc1",
"metadata": {},
"source": [
"First, we will set up the text and chat services we will be submitting prompts to.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f8dcbc6",
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.Kernel()\n",
"\n",
"# Configure Azure LLM service\n",
"if selectedService == Service.AzureOpenAI:\n",
" deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
" azure_text_service = AzureTextCompletion(\n",
" service_id=\"aoai_text\", deployment_name=\"text-davinci-003\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your text model (e.g. gpt-35-turbo-instruct or text-davinci-003)\n",
" azure_chat_service = AzureChatCompletion(\n",
" service_id=\"aoai_chat\", deployment_name=\"gpt-35-turbo\", endpoint=endpoint, api_key=api_key\n",
" ) # set the deployment name to the value of your chat model\n",
"\n",
"# Configure OpenAI service\n",
"if selectedService == Service.OpenAI:\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
" oai_text_service = OpenAITextCompletion(\n",
" service_id=\"oai_text\", ai_model_id=\"gpt-3.5-turbo-instruct\", api_key=api_key, org_id=org_id\n",
" )\n",
" oai_chat_service = OpenAIChatCompletion(\n",
" service_id=\"oai_chat\", ai_model_id=\"gpt-3.5-turbo\", api_key=api_key, org_id=org_id\n",
" )\n",
"\n",
"# Configure Hugging Face service\n",
"if selectedService == Service.HuggingFace:\n",
" hf_text_service = HuggingFaceTextCompletion(ai_model_id=\"distilgpt2\", task=\"text-generation\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "50561d82",
"metadata": {},
"source": [
"Next, we'll set up the completion request settings for text completion services.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "628c843e",
"metadata": {},
"outputs": [],
"source": [
"oai_prompt_execution_settings = OpenAITextPromptExecutionSettings(\n",
" service_id=\"oai_text\",\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "857a9c89",
"metadata": {},
"source": [
"## Streaming Open AI Text Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2979db8",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" chat = ChatHistory()\n",
" chat.add_user_message(\"What is the purpose of a rubber duck?\")\n",
" stream = oai_text_service.complete_stream(chat_history=chat, settings=oai_prompt_execution_settings)\n",
" async for message in stream:\n",
" print(str(message[0]), end=\"\") # end = \"\" to avoid newlines"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "4288d09f",
"metadata": {},
"source": [
"## Streaming Azure Open AI Text Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5319f14d",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" chat = ChatHistory()\n",
" chat.add_user_message(\"provide me a list of possible meanings for the acronym 'ORLD'\")\n",
" stream = azure_text_service.complete_stream(chat_history=chat, settings=oai_prompt_execution_settings)\n",
" async for message in stream:\n",
" print(str(message[0]), end=\"\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "eb548f9c",
"metadata": {},
"source": [
"## Streaming Hugging Face Text Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be7b1c2e",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import (\n",
" HuggingFacePromptExecutionSettings,\n",
" )\n",
"\n",
" hf_prompt_execution_settings = HuggingFacePromptExecutionSettings(\n",
" service_id=\"hf_text\",\n",
" extension_data={\n",
" \"max_new_tokens\": 80,\n",
" \"top_p\": 1,\n",
" \"eos_token_id\": 11,\n",
" \"pad_token_id\": 0,\n",
" },\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9525e4f3",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.HuggingFace:\n",
" chat = ChatHistory()\n",
" chat.add_user_message(\"The purpose of a rubber duck is\")\n",
" stream = hf_text_service.complete_stream(chat_history=chat, prompt_execution_settings=hf_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(str(text[0]), end=\"\") # end = \"\" to avoid newlines"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "da632e12",
"metadata": {},
"source": [
"Here, we're setting up the settings for Chat completions.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5f11e46",
"metadata": {},
"outputs": [],
"source": [
"oai_chat_prompt_execution_settings = OpenAIChatPromptExecutionSettings(\n",
" service_id=\"oai_chat\",\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "d6bf238e",
"metadata": {},
"source": [
"## Streaming OpenAI Chat Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dabc6a4c",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.OpenAI:\n",
" content = \"You are an AI assistant that helps people find information.\"\n",
" chat = ChatHistory()\n",
" chat.add_system_message(content)\n",
" stream = oai_chat_service.complete_chat_stream(chat_history=chat, settings=oai_chat_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(str(text[0]), end=\"\") # end = \"\" to avoid newlines"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "cdb8f740",
"metadata": {},
"source": [
"## Streaming Azure OpenAI Chat Completion\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da1e9f59",
"metadata": {},
"outputs": [],
"source": [
"az_oai_chat_prompt_execution_settings = AzureChatPromptExecutionSettings(\n",
" service_id=\"aoai_chat\",\n",
" max_tokens=150,\n",
" temperature=0.7,\n",
" top_p=1,\n",
" frequency_penalty=0.5,\n",
" presence_penalty=0.5,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b74a64a9",
"metadata": {},
"outputs": [],
"source": [
"if selectedService == Service.AzureOpenAI:\n",
" content = \"You are an AI assistant that helps people find information.\"\n",
" chat = ChatHistory()\n",
" chat.add_system_message(content)\n",
" chat.add_user_message(\"What is the purpose of a rubber duck?\")\n",
" stream = azure_chat_service.complete_chat_stream(chat_history=chat, settings=az_oai_chat_prompt_execution_settings)\n",
" async for text in stream:\n",
" print(str(text[0]), end=\"\") # end = \"\" to avoid newlines"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
-21
View File
@@ -2132,16 +2132,6 @@ files = [
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
@@ -4019,7 +4009,6 @@ files = [
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
@@ -4027,16 +4016,8 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
@@ -4053,7 +4034,6 @@ files = [
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
@@ -4061,7 +4041,6 @@ files = [
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
@@ -1,30 +1,29 @@
# Copyright (c) Microsoft. All rights reserved.
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import (
OpenAIChatCompletion,
)
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.core_plugins import (
FileIOPlugin,
MathPlugin,
TextPlugin,
TimePlugin,
)
from semantic_kernel.planning import ActionPlanner
from semantic_kernel.planners import ActionPlanner
async def main():
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
service_id = "chat-gpt"
kernel.add_service(
OpenAIChatCompletion(service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id)
)
kernel.add_chat_service("chat-gpt", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))
kernel.import_plugin(MathPlugin(), "math")
kernel.import_plugin(FileIOPlugin(), "fileIO")
kernel.import_plugin(TimePlugin(), "time")
kernel.import_plugin(TextPlugin(), "text")
# create an instance of action planner.
planner = ActionPlanner(kernel)
planner = ActionPlanner(kernel, service_id)
# the ask for which the action planner is going to find a relevant function.
ask = "What is the sum of 110 and 990?"
@@ -33,7 +32,7 @@ async def main():
plan = await planner.create_plan(goal=ask)
# ask the action planner to execute the identified function.
result = await plan.invoke()
result = await plan.invoke(kernel)
print(result)
"""
Output:
@@ -7,9 +7,8 @@ from dotenv import load_dotenv
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.chat_completion_client_base import (
ChatCompletionClientBase,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.utils.settings import azure_openai_settings_from_dot_env_as_dict
logging.basicConfig(level=logging.INFO)
@@ -27,10 +26,14 @@ flowery prose.
kernel = sk.Kernel()
chat_service = sk_oai.AzureChatCompletion(**azure_openai_settings_from_dot_env_as_dict(include_api_version=True))
kernel.add_chat_service("chat-gpt", chat_service)
service_id = "chat-gpt"
chat_service = sk_oai.AzureChatCompletion(
service_id=service_id, **azure_openai_settings_from_dot_env_as_dict(include_api_version=True)
)
kernel.add_service(chat_service)
## there are three ways to create the request settings in code: # noqa: E266
## Note: the prompt_execution_settings are a dictionary with the service_id as the key and the request settings as the value. # noqa: E501
## 1. create the request settings from the base class: # noqa: E266
# from semantic_kernel.connectors.ai.chat_completion_client_base import PromptExecutionSettings
@@ -39,36 +42,36 @@ kernel.add_chat_service("chat-gpt", chat_service)
## 2. create the request settings directly for the service you are using: # noqa: E266
# req_settings = sk_oai.AzureChatPromptExecutionSettings(max_tokens=2000, temperature=0.7, top_p=0.8)
## The second method is useful when you are using a single service, and you want to have type checking on the request settings or when you are using multiple instances of the same type of service, for instance gpt-35-turbo and gpt-4, both in openai and both for chat. # noqa: E501 E266
## The second method is useful when you are using a single service, and you want to have type checking on the request settings or when you are using multiple instances of the same type of service, for instance gpt-35-turbo and gpt-4, both in openai and both for chat. # noqa: E501 E266
## 3. create the request settings from the kernel based on the registered service class: # noqa: E266
req_settings = kernel.get_prompt_execution_settings_from_service(ChatCompletionClientBase, "chat-gpt")
req_settings = kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
req_settings.max_tokens = 2000
req_settings.temperature = 0.7
req_settings.top_p = 0.8
## The third method is the most specific as the returned request settings class is the one that is registered for the service and has some fields already filled in, like the service_id and ai_model_id. # noqa: E501 E266
prompt_template_config = sk.PromptTemplateConfig(
template=system_message
+ """ Summarize the on-going chat history: {{$chat_history}} and respond to this statement: {{$request}}""",
name="chat",
input_variables=[
InputVariable(name="request", description="The user input", is_required=True),
InputVariable(name="chat_history", description="The history of the conversation", is_required=True),
],
execution_settings=req_settings,
)
prompt_config = sk.PromptTemplateConfig(execution_settings=req_settings)
history = ChatHistory()
history.add_user_message("Hi there, who are you?")
history.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need.")
prompt_template = sk.ChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
prompt_template.add_system_message(system_message)
prompt_template.add_user_message("Hi there, who are you?")
prompt_template.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need.")
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
chat_function = kernel.create_function_from_prompt(prompt_template_config=prompt_template_config)
async def chat() -> bool:
context_vars = sk.ContextVariables()
try:
user_input = input("User:> ")
if user_input == "":
context_vars["user_input"] = "what is openai?"
else:
context_vars["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -80,16 +83,26 @@ async def chat() -> bool:
print("\n\nExiting chat...")
return False
stream = False
stream = True
if stream:
answer = kernel.run_stream(chat_function, input_vars=context_vars)
answer = kernel.invoke_stream(
chat_function,
request=user_input,
chat_history=history,
)
print("Mosscap:> ", end="")
async for message in answer:
print(str(message[0]), end="")
print("\n")
return True
answer = await kernel.run(chat_function, input_vars=context_vars)
answer = await kernel.invoke(
chat_function,
request=user_input,
chat_history=history,
)
print(f"Mosscap:> {answer}")
history.add_user_message(user_input)
history.add_assistant_message(str(answer))
return True
@@ -4,12 +4,19 @@ import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.open_ai.contents.azure_streaming_chat_message_content import (
AzureStreamingChatMessageContent,
)
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (
AzureAISearchDataSources,
AzureChatPromptExecutionSettings,
AzureDataSources,
ExtraBody,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
kernel = sk.Kernel()
@@ -37,35 +44,44 @@ azure_ai_search_settings["fieldsMapping"] = {
az_source = AzureAISearchDataSources(**azure_ai_search_settings)
az_data = AzureDataSources(type="AzureCognitiveSearch", parameters=az_source)
extra = ExtraBody(dataSources=[az_data])
req_settings = AzureChatPromptExecutionSettings(extra_body=extra)
prompt_config = sk.PromptTemplateConfig(execution_settings=req_settings)
req_settings = AzureChatPromptExecutionSettings(service_id="default", extra_body=extra)
# When using data, set use_extensions=True and use the 2023-12-01-preview API version.
chat_service = sk_oai.AzureChatCompletion(
service_id="chat-gpt",
deployment_name=deployment,
api_key=api_key,
endpoint=endpoint,
api_version="2023-12-01-preview",
use_extensions=True,
)
kernel.add_chat_service("chat-gpt", chat_service)
kernel.add_service(chat_service)
prompt_template = sk.ChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
prompt_template_config = PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="request", description="The user input", is_required=True),
],
execution_settings={"default": req_settings},
)
prompt_template.add_user_message("Hi there, who are you?")
prompt_template.add_assistant_message("I am an AI assistant here to answer your questions.")
chat = ChatHistory()
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
context = kernel.create_new_context()
chat.add_user_message("Hi there, who are you?")
chat.add_assistant_message("I am an AI assistant here to answer your questions.")
arguments = KernelArguments()
chat_function = kernel.create_function_from_prompt(
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
)
async def chat() -> bool:
context_vars = sk.ContextVariables()
try:
user_input = input("User:> ")
context_vars["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -80,14 +96,20 @@ async def chat() -> bool:
# Non streaming
# answer = await kernel.run(chat_function, input_vars=context_vars)
# print(f"Assistant:> {answer}")
arguments = KernelArguments(user_input=user_input, execution_settings=req_settings)
answer = kernel.run_stream(chat_function, input_vars=context_vars, input_context=context)
full_message = None
print("Assistant:> ", end="")
async for message in answer:
print(message, end="")
async for message in kernel.invoke_stream(chat_function, arguments=arguments):
print(str(message[0]), end="")
full_message = message[0] if not full_message else full_message + message[0]
chat.add_assistant_message(str(full_message))
print("\n")
# The tool message containing cited sources is available in the context
print(f"Tool:> {context.objects.get('tool_message')}")
if isinstance(full_message, AzureStreamingChatMessageContent):
chat.add_function_response_message(name="tool", content=full_message.tool_message)
print(f"Tool:> {full_message.tool_message}")
return True
@@ -2,7 +2,6 @@
import asyncio
import os
from typing import Tuple
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
@@ -12,14 +11,15 @@ from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_
AzureDataSources,
ExtraBody,
)
from semantic_kernel.connectors.ai.open_ai.semantic_functions.open_ai_chat_prompt_template import (
OpenAIChatPromptTemplate,
)
from semantic_kernel.connectors.ai.open_ai.utils import (
chat_completion_with_function_call,
chat_completion_with_tool_call,
get_tool_call_object,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.core_plugins.time_plugin import TimePlugin
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
kernel = sk.Kernel()
@@ -31,7 +31,7 @@ azure_ai_search_settings = sk.azure_aisearch_settings_from_dot_env_as_dict()
az_source = AzureAISearchDataSources(**azure_ai_search_settings)
az_data = AzureDataSources(type="AzureCognitiveSearch", parameters=az_source)
extra = ExtraBody(dataSources=[az_data])
req_settings = AzureChatPromptExecutionSettings(extra_body=extra)
req_settings = AzureChatPromptExecutionSettings(service_id="chat-gpt", extra_body=extra, tool_choice="auto")
# For example, AI Search index may contain the following document:
@@ -40,21 +40,21 @@ req_settings = AzureChatPromptExecutionSettings(extra_body=extra)
# groundbreaking phenomenon in glaciology that could potentially reshape our understanding of climate change.
chat_service = sk_oai.AzureChatCompletion(
service_id="chat-gpt",
deployment_name=deployment,
api_key=api_key,
endpoint=endpoint,
api_version="2023-12-01-preview",
use_extensions=True,
)
kernel.add_chat_service(
"chat-gpt",
kernel.add_service(
chat_service,
)
plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
# adding plugins to the kernel
# the joke plugin in the FunPlugins is a semantic plugin and has the function calling disabled.
kernel.import_semantic_plugin_from_directory(plugins_directory, "FunPlugin")
kernel.import_plugin_from_prompt_directory("chat-gpt", plugins_directory, "FunPlugin")
# the math plugin is a core plugin and has the function calling enabled.
kernel.import_plugin(TimePlugin(), plugin_name="time")
@@ -63,58 +63,70 @@ kernel.import_plugin(TimePlugin(), plugin_name="time")
# if you only want to use a specific tool, set the name of that tool in this parameter,
# the format for that is 'PluginName-FunctionName', (i.e. 'math-Add').
# if the model or api version do not support this you will get an error.
req_settings.tool_choice = "auto"
prompt_config = sk.PromptTemplateConfig(execution_settings=req_settings)
prompt_template = OpenAIChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
prompt_template.add_user_message("Hi there, who are you?")
prompt_template.add_assistant_message("I am an AI assistant here to answer your questions.")
prompt_template_config = PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="history", description="The history of the conversation", is_required=True),
InputVariable(name="request", description="The user input", is_required=True),
],
execution_settings=req_settings,
)
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
history = ChatHistory()
history.add_user_message("Hi there, who are you?")
history.add_assistant_message("I am an AI assistant here to answer your questions.")
arguments = KernelArguments()
chat_function = kernel.create_function_from_prompt(
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
)
# calling the chat, you could add a overloaded version of the settings here,
# to enable or disable function calling or set the function calling to a specific plugin.
# see the openai_function_calling example for how to use this with a unrelated function definition
filter = {"exclude_plugin": ["ChatBot"]}
functions = get_tool_call_object(kernel, filter)
req_settings.tools = get_tool_call_object(kernel, filter)
async def chat(context: sk.KernelContext) -> Tuple[bool, sk.KernelContext]:
async def chat() -> bool:
try:
user_input = input("User:> ")
context.variables["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False, None
return False
except EOFError:
print("\n\nExiting chat...")
return False, None
return False
if user_input == "exit":
print("\n\nExiting chat...")
return False, None
return False
context = await chat_completion_with_function_call(
kernel,
chat_plugin_name="ChatBot",
chat_function_name="Chat",
context=context,
functions=functions,
arguments = KernelArguments(request=user_input, execution_settings=req_settings)
answer = await chat_completion_with_tool_call(
kernel=kernel,
arguments=arguments,
chat_function=chat_function,
)
print(f"Assistant:> {context.result}")
return True, context
print(f"Mosscap:> {answer}")
history.add_user_message(user_input)
history.add_assistant_message(str(answer))
return True
async def main() -> None:
chatting = True
context = kernel.create_new_context()
print(
"Welcome to the chat bot!\
\n Type 'exit' to exit.\
\n Try a time question to see the function calling in action (i.e. what day is it?)."
)
chatting = True
while chatting:
chatting, context = await chat(context)
chatting = await chat()
if __name__ == "__main__":
@@ -4,12 +4,22 @@ import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.open_ai.contents.azure_streaming_chat_message_content import (
AzureStreamingChatMessageContent,
)
from semantic_kernel.connectors.ai.open_ai.contents.open_ai_chat_message_content import OpenAIChatMessageContent
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.tool_calls import ToolCall
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (
AzureAISearchDataSources,
AzureChatPromptExecutionSettings,
AzureDataSources,
ExtraBody,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.chat_role import ChatRole
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
kernel = sk.Kernel()
@@ -42,36 +52,45 @@ azure_ai_search_settings["queryType"] = "vector"
az_source = AzureAISearchDataSources(**azure_ai_search_settings)
az_data = AzureDataSources(type="AzureCognitiveSearch", parameters=az_source)
extra = ExtraBody(dataSources=[az_data])
req_settings = AzureChatPromptExecutionSettings(extra_body=extra)
prompt_config = sk.PromptTemplateConfig(execution_settings=req_settings)
req_settings = AzureChatPromptExecutionSettings(service_id="default", extra_body=extra)
# When using data, set use_extensions=True and use the 2023-12-01-preview API version.
chat_service = sk_oai.AzureChatCompletion(
service_id="chat-gpt",
deployment_name=deployment,
api_key=api_key,
endpoint=endpoint,
api_version="2023-12-01-preview",
use_extensions=True,
)
kernel.add_chat_service("chat-gpt", chat_service)
kernel.add_service(chat_service)
prompt_template_config = PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="chat", description="The history of the conversation", is_required=True, default=""),
InputVariable(name="request", description="The user input", is_required=True),
],
execution_settings={"default": req_settings},
)
prompt_template = sk.ChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
chat = ChatHistory()
prompt_template.add_user_message("Hi there, who are you?")
prompt_template.add_assistant_message("I am an AI assistant here to answer your questions.")
chat.add_user_message("Hi there, who are you?")
chat.add_assistant_message("I am an AI assistant here to answer your questions.")
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
context = kernel.create_new_context()
arguments = KernelArguments()
chat_function = kernel.create_function_from_prompt(
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
)
async def chat() -> bool:
context_vars = sk.ContextVariables()
try:
user_input = input("User:> ")
context_vars["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -83,17 +102,28 @@ async def chat() -> bool:
print("\n\nExiting chat...")
return False
arguments = KernelArguments(user_input=user_input, execution_settings=req_settings)
# Non streaming
# answer = await kernel.run(chat_function, input_vars=context_vars)
# answer = await kernel.invoke(chat_function, input_vars=context_vars)
# print(f"Assistant:> {answer}")
arguments = KernelArguments(user_input=user_input, execution_settings=req_settings)
answer = kernel.run_stream(chat_function, input_vars=context_vars, input_context=context)
full_message = None
print("Assistant:> ", end="")
async for message in answer:
print(message, end="")
async for message in kernel.invoke_stream(chat_function, arguments=arguments):
print(str(message[0]), end="")
full_message = message[0] if not full_message else full_message + message[0]
chat.add_assistant_message(str(full_message))
print("\n")
# The tool message containing cited sources is available in the context
print(f"Tool:> {context.objects.get('tool_message')}")
if isinstance(full_message, AzureStreamingChatMessageContent):
tool_call = ToolCall(full_message.tool_message)
chat.add_message(
role=ChatRole.TOOL,
content=full_message,
metadata={OpenAIChatMessageContent.ToolIdProperty: tool_call.function.name},
)
print(f"Tool:> {full_message.tool_message}")
return True
@@ -0,0 +1,119 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.search_engine import BingConnector
from semantic_kernel.core_plugins import WebSearchEnginePlugin
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.kernel_prompt_template import KernelPromptTemplate
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
async def example1(kernel: sk.Kernel, search_plugin_name: str):
print("======== Bing and Google Search Plugins ========")
question = "What's the largest building in the world?"
function = kernel.plugins[search_plugin_name]["search"]
result = await kernel.invoke(function, query=question)
print(question)
print(f"----{search_plugin_name}----")
print(result)
async def example2(kernel: sk.Kernel, service_id: str):
print("======== Use the Search Plugin to Answer User Questions ========")
prompt = """
Answer questions only when you know the facts or the information is provided.
When you don't have sufficient information you reply with a list of commands to find the information needed.
When answering multiple questions, use a bullet point list.
Note: make sure single and double quotes are escaped using a backslash char.
[COMMANDS AVAILABLE]
- bing.search
[INFORMATION PROVIDED]
{{ $externalInformation }}
[EXAMPLE 1]
Question: what's the biggest lake in Italy?
Answer: Lake Garda, also known as Lago di Garda.
[EXAMPLE 2]
Question: what's the biggest lake in Italy? What's the smallest positive number?
Answer:
* Lake Garda, also known as Lago di Garda.
* The smallest positive number is 1.
[EXAMPLE 3]
Question: what's Ferrari stock price? Who is the current number one female tennis player in the world?
Answer:
{{ '{{' }} bing.search ""what\\'s Ferrari stock price?"" {{ '}}' }}.
{{ '{{' }} bing.search ""Who is the current number one female tennis player in the world?"" {{ '}}' }}.
[END OF EXAMPLES]
[TASK]
Question: {{ $question }}.
Answer:
"""
question = "Who is the most followed person on TikTok right now? What's the exchange rate EUR:USD?"
print(question)
oracle = kernel.create_function_from_prompt(
template=prompt,
execution_settings=sk_oai.OpenAIChatPromptExecutionSettings(
service_id=service_id, max_tokens=150, temperature=0, top_p=1
),
)
answer = await kernel.invoke(
oracle,
question=question,
externalInformation="",
)
result = str(answer)
if "bing.search" in result:
prompt_template = KernelPromptTemplate(PromptTemplateConfig(template=result))
print("--- Fetching information from Bing... ---")
information = await prompt_template.render(kernel, KernelArguments())
print("Information found:\n")
print(information)
answer = await kernel.invoke(oracle, question=question, externalInformation=information)
print("\n---- Oracle's Answer ----:\n")
print(answer)
else:
print("AI had all of the information, there was no need to query Bing.")
async def main():
kernel = sk.Kernel()
model = "gpt-3.5-turbo-1106"
service_id = model
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_service(
sk_oai.OpenAIChatCompletion(service_id=service_id, ai_model_id=model, api_key=api_key, org_id=org_id),
)
bing_api_key = sk.bing_search_settings_from_dot_env()
assert bing_api_key is not None
bing_connector = BingConnector(api_key=bing_api_key)
bing = WebSearchEnginePlugin(bing_connector)
kernel.import_plugin(bing, "bing")
await example1(kernel, "bing")
await example2(kernel, service_id)
if __name__ == "__main__":
asyncio.run(main())
@@ -1,3 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from dotenv import load_dotenv
@@ -13,9 +15,10 @@ load_dotenv()
async def main():
kernel = sk.Kernel()
deployment, key, endpoint, api_version = sk.azure_openai_settings_from_dot_env(include_api_version=True)
kernel.add_chat_service(
"chat-gpt",
service_id = "chat-gpt"
kernel.add_service(
AzureChatCompletion(
service_id=service_id,
deployment_name=deployment,
api_key=key,
endpoint=endpoint,
@@ -25,31 +28,40 @@ async def main():
connector = BingConnector(api_key=os.getenv("BING_API_KEY"))
web_plugin = kernel.import_plugin(WebSearchEnginePlugin(connector), "WebSearch")
prompt = "Who is Leonardo DiCaprio's current girlfriend?"
search = web_plugin["searchAsync"]
result = await search.invoke(prompt)
print(result)
print("---------------- Question 1 -----------------\n")
"""
Output:
["Celebrity Celebrity News Everything You Need to Know About Leonardo DiCaprio and Camila Morrone's
Relationship From the beginning of their romance to today, we track their relationship here. By..."]
"""
query = "Which country receives the most rain per year?"
search = web_plugin["search"]
result = await kernel.invoke(search, query=query)
print(f"Question: {query}\n")
print(f"Answer: {result}\n")
print("---------------- Question 2 -----------------\n")
prompt = """
Answer the question using only the data that is provided in the data section.
Do not use any prior knowledge to answer the question.
Data: {{WebSearch.SearchAsync "What is semantic kernel?"}}
Question: What is semantic kernel?
Data: {{WebSearch.search "What is semantic kernel?"}}
Question: {{$question}}?
Answer:
"""
qna = kernel.create_semantic_function(prompt, temperature=0.2)
context = kernel.create_new_context()
context["num_results"] = "10"
context["offset"] = "0"
result = await qna.invoke(context=context)
print(result)
req_settings = kernel.get_service("chat-gpt").get_prompt_execution_settings_class()(service_id=service_id)
req_settings.temperature = 0.2
prompt_template_config = sk.PromptTemplateConfig(
template=prompt,
name="qna",
template_format="semantic-kernel",
execution_settings=req_settings,
)
question = "What is Semantic Kernel?"
qna = kernel.create_function_from_prompt(prompt_template_config=prompt_template_config)
result = await qna.invoke(kernel, question=question, num_results=10, offset=0)
print(f"Question: {question}\n")
print(f"Answer: {result}\n")
"""
Output:
+44 -14
View File
@@ -4,13 +4,17 @@ import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
sk_prompt = """
prompt = """
ChatBot can have a conversation with you about any topic.
It can give explicit instructions or say 'I don't know'
when it doesn't know the answer.
{{$chat_history}}
User:> {{$user_input}}
ChatBot:>
"""
@@ -18,20 +22,48 @@ ChatBot:>
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service("chat-gpt", sk_oai.OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))
service_id = "chat-gpt"
kernel.add_service(
sk_oai.OpenAIChatCompletion(service_id=service_id, ai_model_id="gpt-3.5-turbo-1106", api_key=api_key, org_id=org_id)
)
prompt_config = sk.PromptTemplateConfig.from_execution_settings(max_tokens=2000, temperature=0.7, top_p=0.4)
settings = kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
settings.max_tokens = 2000
settings.temperature = 0.7
settings.top_p = 0.8
prompt_template = sk.PromptTemplate(sk_prompt, kernel.prompt_template_engine, prompt_config)
prompt_template_config = PromptTemplateConfig(
template=prompt,
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(
name="user_input",
description="The user input",
is_required=True,
default="",
),
InputVariable(
name="chat_history",
description="The history of the conversation",
is_required=True,
),
],
execution_settings=settings,
)
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
chat_history = ChatHistory()
chat_history.add_user_message("Hi there, who are you?")
chat_history.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need")
chat_function = kernel.create_function_from_prompt(
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
)
async def chat(context_vars: sk.ContextVariables) -> bool:
async def chat(chat_history: ChatHistory) -> bool:
try:
user_input = input("User:> ")
context_vars["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -43,20 +75,18 @@ async def chat(context_vars: sk.ContextVariables) -> bool:
print("\n\nExiting chat...")
return False
answer = await kernel.run(chat_function, input_vars=context_vars)
context_vars["chat_history"] += f"\nUser:> {user_input}\nChatBot:> {answer}\n"
answer = await kernel.invoke(chat_function, user_input=user_input, chat_history=chat_history)
chat_history.add_user_message(user_input)
chat_history.add_assistant_message(str(answer))
print(f"ChatBot:> {answer}")
return True
async def main() -> None:
context = sk.ContextVariables()
context["chat_history"] = ""
chatting = True
while chatting:
chatting = await chat(context)
chatting = await chat(chat_history)
if __name__ == "__main__":
@@ -4,6 +4,13 @@ import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.chat_completion_client_base import (
ChatCompletionClientBase,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
system_message = """
You are a chat bot. Your name is Mosscap and
@@ -17,26 +24,49 @@ flowery prose.
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service("chat-gpt", sk_oai.OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))
kernel.add_service(
sk_oai.OpenAIChatCompletion(service_id="chat-gpt", ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id)
)
prompt_config = sk.PromptTemplateConfig.from_execution_settings(max_tokens=2000, temperature=0.7, top_p=0.8)
settings = kernel.get_prompt_execution_settings_from_service(ChatCompletionClientBase, "chat-gpt")
settings.max_tokens = 2000
settings.temperature = 0.7
settings.top_p = 0.8
prompt_template = sk.ChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
prompt_template_config = PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(
name="user_input",
description="The user input",
is_required=True,
default="",
),
InputVariable(
name="chat_history",
description="The history of the conversation",
is_required=True,
),
],
execution_settings=settings,
)
prompt_template.add_system_message(system_message)
prompt_template.add_user_message("Hi there, who are you?")
prompt_template.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need.")
chat = ChatHistory(system_message=system_message)
chat.add_user_message("Hi there, who are you?")
chat.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need")
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
chat_function = kernel.create_function_from_prompt(
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
)
chat.add_user_message("I want to find a hotel in Seattle with free wifi and a pool.")
async def chat() -> bool:
context_vars = sk.ContextVariables()
try:
user_input = input("User:> ")
context_vars["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -48,7 +78,9 @@ async def chat() -> bool:
print("\n\nExiting chat...")
return False
answer = await kernel.run(chat_function, input_vars=context_vars)
answer = await kernel.invoke(chat_function, KernelArguments(user_input=user_input, chat_history=chat))
chat.add_user_message(user_input)
chat.add_assistant_message(str(answer))
print(f"Mosscap:> {answer}")
return True
@@ -2,18 +2,17 @@
import asyncio
import os
from typing import Tuple
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.open_ai.semantic_functions.open_ai_chat_prompt_template import (
OpenAIChatPromptTemplate,
)
from semantic_kernel.connectors.ai.open_ai.utils import (
chat_completion_with_function_call,
chat_completion_with_tool_call,
get_tool_call_object,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.core_plugins import MathPlugin
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
system_message = """
You are a chat bot. Your name is Mosscap and
@@ -33,11 +32,11 @@ kernel = sk.Kernel()
# Note: the underlying gpt-35/gpt-4 model version needs to be at least version 0613 to support tools.
deployment_name, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
api_version = "2023-12-01-preview"
kernel.add_chat_service(
"chat-gpt",
kernel.add_service(
sk_oai.AzureChatCompletion(
deployment_name,
endpoint,
service_id="chat",
deployment_name=deployment_name,
base_url=endpoint,
api_key=api_key,
api_version=api_version,
),
@@ -46,7 +45,7 @@ kernel.add_chat_service(
plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
# adding plugins to the kernel
# the joke plugin in the FunPlugins is a semantic plugin and has the function calling disabled.
kernel.import_semantic_plugin_from_directory(plugins_directory, "FunPlugin")
# kernel.import_plugin_from_prompt_directory("chat", plugins_directory, "FunPlugin")
# the math plugin is a core plugin and has the function calling enabled.
kernel.import_plugin(MathPlugin(), plugin_name="math")
@@ -55,62 +54,77 @@ kernel.import_plugin(MathPlugin(), plugin_name="math")
# if you only want to use a specific function, set the name of that function in this parameter,
# the format for that is 'PluginName-FunctionName', (i.e. 'math-Add').
# if the model or api version do not support this you will get an error.
prompt_config = sk.PromptTemplateConfig(
execution_settings=sk_oai.AzureChatPromptExecutionSettings(
service_id="chat-gpt",
ai_model_id=deployment_name,
max_tokens=2000,
temperature=0.7,
top_p=0.8,
tool_choice="auto",
tools=get_tool_call_object(kernel, {"exclude_plugin": ["ChatBot"]}),
)
execution_settings = sk_oai.AzureChatPromptExecutionSettings(
service_id="chat",
ai_model_id=deployment_name,
max_tokens=2000,
temperature=0.7,
top_p=0.8,
tool_choice="auto",
tools=get_tool_call_object(kernel, {"exclude_plugin": ["ChatBot"]}),
)
prompt_template = OpenAIChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
prompt_template.add_system_message(system_message)
prompt_template.add_user_message("Hi there, who are you?")
prompt_template.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need.")
prompt_template_config = sk.PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="user_input", description="The user input", is_required=True),
InputVariable(name="history", description="The history of the conversation", is_required=True, default=""),
],
execution_settings={"default": execution_settings},
)
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
history = ChatHistory()
history.add_system_message(system_message)
history.add_user_message("Hi there, who are you?")
history.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need.")
arguments = KernelArguments()
chat_function = kernel.create_function_from_prompt(
prompt_template_config=prompt_template_config,
plugin_name="ChatBot",
function_name="Chat",
)
async def chat(context: sk.KernelContext) -> Tuple[bool, sk.KernelContext]:
async def chat() -> bool:
try:
user_input = input("User:> ")
context.variables["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False, None
return False
except EOFError:
print("\n\nExiting chat...")
return False, None
return False
if user_input == "exit":
print("\n\nExiting chat...")
return False, None
context = await chat_completion_with_function_call(
kernel,
return False
arguments = KernelArguments(
user_input=user_input, history=("\n").join([f"{msg.role}: {msg.content}" for msg in history])
)
result = await chat_completion_with_tool_call(
kernel=kernel,
arguments=arguments,
chat_plugin_name="ChatBot",
chat_function_name="Chat",
context=context,
)
print(f"Mosscap:> {context.result}")
return True, context
print(f"Mosscap:> {result}")
return True
async def main() -> None:
chatting = True
context = kernel.create_new_context()
print(
"Welcome to the chat bot!\
\n Type 'exit' to exit.\
\n Try a math question to see the function calling in action (i.e. what is 3+3?)."
)
while chatting:
chatting, context = await chat(context)
chatting = await chat()
if __name__ == "__main__":
@@ -0,0 +1,71 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
async def main():
kernel = sk.Kernel()
useAzureOpenAI = False
model = "gpt-35-turbo" if useAzureOpenAI else "gpt-3.5-turbo-1106"
service_id = model
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_service(
sk_oai.OpenAIChatCompletion(service_id=service_id, ai_model_id=model, api_key=api_key, org_id=org_id),
)
template = """
Previous information from chat:
{{$chat_history}}
User: {{$request}}
Assistant:
"""
print("--- Rendered Prompt ---")
prompt_template_config = PromptTemplateConfig(
template=template,
name="chat",
description="Chat with the assistant",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="chat_history", description="The conversation history", is_required=False, default=""),
InputVariable(name="request", description="The user's request", is_required=True),
],
execution_settings=sk_oai.OpenAIChatPromptExecutionSettings(
service_id=service_id, max_tokens=4000, temperature=0.2
),
)
chat = kernel.create_function_from_prompt(
prompt_template_config=prompt_template_config,
)
chat_history = ChatHistory()
print("User > ")
while (user_input := input()) != "exit":
result = await kernel.invoke(
chat,
KernelArguments(
request=user_input,
chat_history=chat_history,
),
)
result = str(result)
print(result)
chat_history.add_user_message(user_input)
chat_history.add_assistant_message(result)
if __name__ == "__main__":
asyncio.run(main())
@@ -5,14 +5,17 @@ from typing import Tuple
import semantic_kernel as sk
import semantic_kernel.connectors.ai.google_palm as sk_gp
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.prompt_template.input_variable import InputVariable
kernel = sk.Kernel()
apikey = sk.google_palm_settings_from_dot_env()
palm_text_embed = sk_gp.GooglePalmTextEmbedding("models/embedding-gecko-001", apikey)
kernel.add_text_embedding_generation_service("gecko", palm_text_embed)
palm_chat_completion = sk_gp.GooglePalmChatCompletion("models/chat-bison-001", apikey)
kernel.add_chat_service("models/chat-bison-001", palm_chat_completion)
kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())
kernel.add_service(palm_text_embed)
chat_service_id = "models/chat-bison-001"
palm_chat_completion = sk_gp.GooglePalmChatCompletion(chat_service_id, apikey)
kernel.add_service(palm_chat_completion)
kernel.use_memory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=palm_text_embed)
kernel.import_plugin(sk.core_plugins.TextMemoryPlugin(), "TextMemoryPlugin")
@@ -42,7 +45,7 @@ async def search_memory_examples(kernel: sk.Kernel) -> None:
async def setup_chat_with_memory(
kernel: sk.Kernel,
) -> Tuple[sk.KernelFunction, sk.KernelContext]:
) -> Tuple[sk.KernelFunction, sk.KernelArguments]:
"""
When using Google PaLM to chat with memories, a chat prompt template is
essential; otherwise, the kernel will send text prompts to the Google PaLM
@@ -53,7 +56,7 @@ async def setup_chat_with_memory(
Note that this is only an issue for the chat service; the text service
does not require a chat prompt template.
"""
sk_prompt = """
prompt = """
ChatBot can have a conversation with you about any topic.
It can give explicit instructions or say 'I don't know' if
it does not have an answer.
@@ -67,33 +70,46 @@ async def setup_chat_with_memory(
""".strip()
prompt_config = sk.PromptTemplateConfig.from_execution_settings(max_tokens=2000, temperature=0.7, top_p=0.8)
prompt_template = sk.ChatPromptTemplate( # Create the chat prompt template
"{{$user_input}}", kernel.prompt_template_engine, prompt_config
req_settings = kernel.get_service(chat_service_id).get_prompt_execution_settings_class()(service_id=chat_service_id)
req_settings.max_tokens = 2000
req_settings.temperature = 0.7
req_settings.top_p = 0.8
prompt_template_config = sk.PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="user_input", description="The user input", is_required=True),
InputVariable(name="chat_history", description="The history of the conversation", is_required=True),
],
execution_settings=req_settings,
)
prompt_template.add_system_message(sk_prompt) # Add the memory as a system message
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_func = kernel.register_semantic_function(None, "ChatWithMemory", function_config)
context = kernel.create_new_context()
context["fact1"] = "what is my name?"
context["fact2"] = "what is my favorite hobby?"
context["fact3"] = "where's my family from?"
context["fact4"] = "where did I travel last year?"
context["fact5"] = "what do I do for work?"
chat_func = kernel.create_function_from_prompt(
plugin_name="chat_memory", function_name="ChatWithMemory", prompt_template_config=prompt_template_config
)
context[sk.core_plugins.TextMemoryPlugin.COLLECTION_PARAM] = "aboutMe"
context[sk.core_plugins.TextMemoryPlugin.RELEVANCE_PARAM] = 0.6
chat_history = ChatHistory()
chat_history.add_system_message(prompt)
context["chat_history"] = ""
arguments = sk.KernelArguments(
fact1="what is my name?",
fact2="what is my favorite hobby?",
fact3="where's my family from?",
fact4="where did I travel last year?",
fact5="what do I do for work?",
collection="aboutMe",
relevance=0.6,
chat_history=chat_history,
)
return chat_func, context
return chat_func, arguments
async def chat(kernel: sk.Kernel, chat_func: sk.KernelFunction, context: sk.KernelContext) -> bool:
async def chat(kernel: sk.Kernel, chat_func: sk.KernelFunction, arguments: sk.KernelArguments) -> bool:
try:
user_input = input("User:> ")
context["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -105,8 +121,10 @@ async def chat(kernel: sk.Kernel, chat_func: sk.KernelFunction, context: sk.Kern
print("\n\nExiting chat...")
return False
answer = await kernel.run(chat_func, input_vars=context.variables)
context["chat_history"] += f"\nUser:> {user_input}\nChatBot:> {answer}\n"
arguments["user_input"] = user_input
answer = await kernel.invoke(chat_func, arguments)
arguments["chat_history"].add_user_message(user_input)
arguments["chat_history"].add_assistant_message(str(answer))
print(f"ChatBot:> {answer}")
return True
@@ -115,11 +133,11 @@ async def chat(kernel: sk.Kernel, chat_func: sk.KernelFunction, context: sk.Kern
async def main() -> None:
await populate_memory(kernel)
await search_memory_examples(kernel)
chat_func, context = await setup_chat_with_memory(kernel)
chat_func, arguments = await setup_chat_with_memory(kernel)
print("Begin chatting (type 'exit' to exit):\n")
chatting = True
while chatting:
chatting = await chat(kernel, chat_func, context)
chatting = await chat(kernel, chat_func, arguments)
if __name__ == "__main__":
@@ -4,6 +4,8 @@ import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.google_palm as sk_gp
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.prompt_template.input_variable import InputVariable
"""
System messages prime the assistant with different personalities or behaviors.
@@ -28,23 +30,39 @@ Bartholomew "Blackbeard" Thorne.
kernel = sk.Kernel()
api_key = sk.google_palm_settings_from_dot_env()
palm_chat_completion = sk_gp.GooglePalmChatCompletion("models/chat-bison-001", api_key)
kernel.add_chat_service("models/chat-bison-001", palm_chat_completion)
prompt_config = sk.PromptTemplateConfig.from_execution_settings(max_tokens=2000, temperature=0.7, top_p=0.8)
prompt_template = sk.ChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
prompt_template.add_system_message(system_message) # Add the system message for context
prompt_template.add_user_message("Hi there, my name is Andrea, who are you?") # Include a chat history
prompt_template.add_assistant_message("I am Blackbeard.")
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("PiratePlugin", "Chat", function_config)
service_id = "models/chat-bison-001"
palm_chat_completion = sk_gp.GooglePalmChatCompletion(service_id, api_key)
kernel.add_service(palm_chat_completion)
req_settings = kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
req_settings.max_tokens = 2000
req_settings.temperature = 0.7
req_settings.top_p = 0.8
prompt_template_config = sk.PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="user_input", description="The user input", is_required=True),
InputVariable(name="chat_history", description="The history of the conversation", is_required=True),
],
execution_settings=req_settings,
)
chat_func = kernel.create_function_from_prompt(
plugin_name="PiratePlugin", function_name="Chat", prompt_template_config=prompt_template_config
)
chat_history = ChatHistory()
chat_history.add_system_message(system_message)
chat_history.add_user_message("Hi there, who are you?")
chat_history.add_assistant_message("I am Blackbeard.")
async def chat() -> bool:
context_vars = sk.ContextVariables()
try:
user_input = input("User:> ")
context_vars["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -56,8 +74,10 @@ async def chat() -> bool:
print("\n\nExiting chat...")
return False
answer = await kernel.run(chat_function, input_vars=context_vars)
answer = await kernel.invoke(chat_func, user_input=user_input, chat_history=chat_history)
print(f"Blackbeard:> {answer}")
chat_history.add_user_message(user_input)
chat_history.add_assistant_message(str(answer))
return True
@@ -14,7 +14,7 @@ async def text_completion_example_complete(kernel, api_key, user_mssg, settings)
Complete a text prompt using the Google PaLM model and print the results.
"""
palm_text_completion = sk_gp.GooglePalmTextCompletion("models/text-bison-001", api_key)
kernel.add_text_completion_service("models/text-bison-001", palm_text_completion)
kernel.add_service(palm_text_completion)
answer = await palm_text_completion.complete(user_mssg, settings)
return answer
@@ -15,7 +15,9 @@ load_dotenv()
async def main():
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service("chat-gpt", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))
kernel.add_service(
OpenAIChatCompletion(service_id="chat-gpt", ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id)
)
"""
Instantiate a Google Connector
@@ -58,9 +58,10 @@ def setup(use_azure: bool = False):
# Configure AI service used by the kernel
if useAzureOpenAI:
deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
kernel.add_chat_service(
"chat_completion",
service_id = ("chat_completion",)
kernel.add_service(
AzureChatCompletion(
service_id=service_id,
deployment_name=deployment,
endpoint=endpoint,
api_key=api_key,
@@ -70,19 +71,18 @@ def setup(use_azure: bool = False):
)
else:
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service(
"chat-gpt",
OpenAIChatCompletion(ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id),
service_id = "chat-gpt"
kernel.add_service(
OpenAIChatCompletion(service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id),
)
# note: using plugins from the samples folder
plugins_directory = "../samples/plugins/"
grounding_semantic_functions = kernel.import_semantic_plugin_from_directory(plugins_directory, "GroundingPlugin")
grounding_semantic_functions = kernel.import_plugin_from_prompt_directory(
service_id, plugins_directory, "GroundingPlugin"
)
# entity_extraction = grounding_semantic_functions["ExtractEntities"]
# reference_check = grounding_semantic_functions["ReferenceCheckEntities"]
# entity_excision = grounding_semantic_functions["ExciseEntities"]
return kernel, grounding_semantic_functions
@@ -5,6 +5,8 @@ from typing import Tuple
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments
async def populate_memory(kernel: sk.Kernel) -> None:
@@ -31,6 +33,7 @@ async def search_memory_examples(kernel: sk.Kernel) -> None:
print(f"Answer: {result[0].text}\n")
# TODO fix this ASAP
async def setup_chat_with_memory(
kernel: sk.Kernel,
) -> Tuple[sk.KernelFunction, sk.KernelContext]:
@@ -68,10 +71,9 @@ async def setup_chat_with_memory(
return chat_func, context
async def chat(kernel: sk.Kernel, chat_func: sk.KernelFunction, context: sk.KernelContext) -> bool:
async def chat(kernel: sk.Kernel, chat_func: sk.KernelFunction, chat_history: ChatHistory) -> bool:
try:
user_input = input("User:> ")
context["user_input"] = user_input
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
@@ -83,8 +85,9 @@ async def chat(kernel: sk.Kernel, chat_func: sk.KernelFunction, context: sk.Kern
print("\n\nExiting chat...")
return False
answer = await kernel.run(chat_func, input_vars=context.variables)
context["chat_history"] += f"\nUser:> {user_input}\nChatBot:> {answer}\n"
answer = await kernel.invoke(chat_func, KernelArguments(user_input=user_input, chat_history=chat_history))
chat_history.add_user_message(user_input)
chat_history.add_assistant_message(str(answer))
print(f"ChatBot:> {answer}")
return True
@@ -94,12 +97,16 @@ async def main() -> None:
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service("chat-gpt", sk_oai.OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))
kernel.add_text_embedding_generation_service(
"ada", sk_oai.OpenAITextEmbedding("text-embedding-ada-002", api_key, org_id)
service_id = "chat-gpt"
kernel.add_service(
sk_oai.OpenAIChatCompletion(service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id)
)
embedding_gen = sk_oai.OpenAITextEmbedding(
service_id="ada", ai_model_id="text-embedding-ada-002", api_key=api_key, org_id=org_id
)
kernel.add_service(embedding_gen)
kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())
kernel.use_memory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=embedding_gen)
kernel.import_plugin(sk.core_plugins.TextMemoryPlugin(), "TextMemoryPlugin")
print("Populating memory...")
@@ -0,0 +1,155 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Tuple
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import (
OpenAIChatCompletion,
OpenAITextEmbedding,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions import KernelFunction
from semantic_kernel.prompt_template.input_variable import InputVariable
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
oai_text_embedding = OpenAITextEmbedding(
service_id="oai_text_embed", ai_model_id="text-embedding-ada-002", api_key=api_key, org_id=org_id
)
service_id = "oai_chat"
oai_chat_service = OpenAIChatCompletion(
service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id
)
kernel.use_memory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=oai_text_embedding)
kernel.add_service(oai_chat_service)
kernel.add_service(oai_text_embedding)
kernel.import_plugin(sk.core_plugins.TextMemoryPlugin(), "text_memory")
async def populate_memory(kernel: sk.Kernel) -> None:
await kernel.memory.save_information(collection="aboutMe", id="info1", text="My name is Andrea")
await kernel.memory.save_information(collection="aboutMe", id="info2", text="I currently work as a tour guide")
await kernel.memory.save_information(
collection="aboutMe", id="info3", text="I've been living in Seattle since 2005"
)
await kernel.memory.save_information(
collection="aboutMe",
id="info4",
text="I visited France and Italy five times since 2015",
)
await kernel.memory.save_information(collection="aboutMe", id="info5", text="My family is from New York")
async def search_memory_examples(kernel: sk.Kernel) -> None:
questions = [
"what's my name",
"where do I live?",
"where's my family from?",
"where have I traveled?",
"what do I do for work",
]
for question in questions:
print(f"Question: {question}")
result = await kernel.memory.search("aboutMe", question)
print(f"Answer: {result[0].text}\n")
async def setup_chat_with_memory(
kernel: sk.Kernel,
) -> Tuple[KernelFunction, sk.KernelArguments]:
prompt = """
ChatBot can have a conversation with you about any topic.
It can give explicit instructions or say 'I don't know' if
it does not have an answer.
Information about me, from previous conversations:
{{$fact1}} {{recall $fact1}}
{{$fact2}} {{recall $fact2}}
{{$fact3}} {{recall $fact3}}
{{$fact4}} {{recall $fact4}}
{{$fact5}} {{recall $fact5}}
{{$user_input}}
""".strip()
req_settings = kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
req_settings.max_tokens = 2000
req_settings.temperature = 0.7
req_settings.top_p = 0.8
prompt_template_config = sk.PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="user_input", description="The user input", is_required=True),
InputVariable(name="chat_history", description="The history of the conversation", is_required=True),
],
execution_settings=req_settings,
)
chat_func = kernel.create_function_from_prompt(
plugin_name="chat_memory", function_name="ChatWithMemory", prompt_template_config=prompt_template_config
)
chat_history = ChatHistory()
chat_history.add_system_message(prompt)
arguments = sk.KernelArguments(
fact1="what is my name?",
fact2="what is my favorite hobby?",
fact3="where's my family from?",
fact4="where did I travel last year?",
fact5="what do I do for work?",
collection="aboutMe",
relevance=0.6,
chat_history=chat_history,
)
return chat_func, arguments
async def chat(kernel: sk.Kernel, chat_func: KernelFunction, arguments: sk.KernelArguments) -> bool:
try:
user_input = input("User:> ")
print(f"User:> {user_input}")
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
except EOFError:
print("\n\nExiting chat...")
return False
if user_input == "exit":
print("\n\nExiting chat...")
return False
arguments["user_input"] = user_input
answer = await kernel.invoke(chat_func, arguments)
arguments["chat_history"].add_user_message(user_input)
arguments["chat_history"].add_system_message(str(answer))
print(f"ChatBot:> {answer}")
return True
async def main():
print("Populating memory...")
await populate_memory(kernel)
print("Asking questions... (manually)")
await search_memory_examples(kernel)
print("Setting up a chat (with memory!)")
chat_func, arguments = await setup_chat_with_memory(kernel)
result = await chat(kernel=kernel, chat_func=chat_func, arguments=arguments)
print(result)
if __name__ == "__main__":
asyncio.run(main())
@@ -5,10 +5,15 @@ import os
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.open_ai.models.chat.open_ai_chat_message import (
OpenAIChatMessage,
from semantic_kernel.connectors.ai.chat_completion_client_base import (
ChatCompletionClientBase,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.core_plugins import MathPlugin
from semantic_kernel.functions.function_result import FunctionResult
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
system_message = """
You are a chat bot. Your name is Mosscap and
@@ -26,9 +31,9 @@ you will return a full answer to me as soon as possible.
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service(
"gpt-3.5-turbo",
kernel.add_service(
sk_oai.OpenAIChatCompletion(
service_id="chat",
ai_model_id="gpt-3.5-turbo-1106",
api_key=api_key,
),
@@ -37,7 +42,7 @@ kernel.add_chat_service(
plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
# adding plugins to the kernel
# the joke plugin in the FunPlugins is a semantic plugin and has the function calling disabled.
kernel.import_semantic_plugin_from_directory(plugins_directory, "FunPlugin")
kernel.import_plugin_from_prompt_directory("chat", plugins_directory, "FunPlugin")
# the math plugin is a core plugin and has the function calling enabled.
kernel.import_plugin(MathPlugin(), plugin_name="math")
@@ -74,46 +79,69 @@ tools = [
}
]
prompt_config = sk.PromptTemplateConfig.from_execution_settings(
max_tokens=2000,
temperature=0.7,
top_p=0.8,
tool_choice="auto",
tools=tools,
)
prompt_template = sk.ChatPromptTemplate[OpenAIChatMessage](
"{{$user_input}}", kernel.prompt_template_engine, prompt_config
)
prompt_template.add_system_message(system_message)
prompt_template.add_user_message("Hi there, who are you?")
prompt_template.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need.")
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
chat_function = kernel.register_semantic_function("ChatBot", "Chat", function_config)
# define the functions available
async def main():
settings = kernel.get_prompt_execution_settings_from_service(ChatCompletionClientBase, "chat")
settings.service_id = "chat"
settings.tools = tools
settings.tool_choice = "auto"
settings.max_tokens = 2000
settings.temperature = 0.7
settings.top_p = 0.8
prompt_template_config = PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(
name="user_input",
description="The history of the conversation",
is_required=True,
default="",
),
InputVariable(
name="chat_history",
description="The history of the conversation",
is_required=True,
),
],
execution_settings=settings,
)
async def main() -> None:
context = kernel.create_new_context()
context.variables["user_input"] = "I want to find a hotel in Seattle with free wifi and a pool."
chat = ChatHistory(system_message=system_message)
chat.add_user_message("Hi there, who are you?")
chat.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure out what people need")
chat_function = kernel.create_function_from_prompt(
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
)
response = kernel.invoke_stream(
chat_function,
KernelArguments(user_input="I want to find a hotel in Seattle with free wifi and a pool.", chat_history=chat),
)
messages = []
tool_call = None
response = chat_function.invoke_stream_async(context=context)
async for message in response:
if isinstance(message, FunctionResult):
# There's been an error, so print it
print(message)
return
current = message[0]
messages.append(current)
messages.append(str(current))
if current.tool_calls:
if tool_call is None:
tool_call = current.tool_calls[0]
else:
tool_call += current.tool_calls[0]
if tool_call:
print(f"Function to be called: {tool_call.function.name}")
print(f"Function parameters: \n{tool_call.function.parse_arguments()}")
return
print("No function was called")
print(f"Output was: {str(context)}")
output = "".join([msg for msg in messages])
print(f"Output was: {output}")
if __name__ == "__main__":
@@ -5,13 +5,11 @@ from typing import Any, Dict
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.connectors.ai.chat_completion_client_base import (
ChatCompletionClientBase,
)
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.connectors.ai.text_completion_client_base import (
TextCompletionClientBase,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
"""
Logit bias enables prioritizing certain tokens within a given output.
@@ -29,9 +27,16 @@ def _config_ban_tokens(settings: PromptExecutionSettings, keys: Dict[Any, Any]):
return settings
def _prepare_input_chat(chat: ChatHistory):
return "".join([f"{msg.role}: {msg.content}\n" for msg in chat])
async def chat_request_example(kernel, api_key, org_id):
openai_chat_completion = sk_oai.OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id)
kernel.add_chat_service("chat_service", openai_chat_completion)
service_id = "chat_service"
openai_chat_completion = sk_oai.OpenAIChatCompletion(
service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id
)
kernel.add_service(openai_chat_completion)
# Spaces and capitalization affect the token ids.
# The following is the token ids of basketball related words.
@@ -65,49 +70,53 @@ async def chat_request_example(kernel, api_key, org_id):
]
# Model will try its best to avoid using any of the above words
settings = kernel.get_prompt_execution_settings_from_service(ChatCompletionClientBase, "chat_service")
settings = kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
settings = _config_ban_tokens(settings, keys)
prompt_config = sk.PromptTemplateConfig.from_execution_settings(max_tokens=2000, temperature=0.7, top_p=0.8)
prompt_template = sk.ChatPromptTemplate("{{$user_input}}", kernel.prompt_template_engine, prompt_config)
prompt_template_config = PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(
name="user_input", description="The history of the conversation", is_required=True, default=""
),
],
execution_settings=settings,
)
# Setup chat with prompt
prompt_template.add_system_message("You are a basketball expert")
user_mssg = "I love the LA Lakers, tell me an interesting fact about LeBron James."
prompt_template.add_user_message(user_mssg)
function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
kernel.register_semantic_function("ChatBot", "Chat", function_config)
chat = ChatHistory()
chat_messages = []
messages = [{"role": "user", "content": user_mssg}]
chat.add_user_message("Hi there, who are you?")
chat.add_assistant_message("I am an AI assistant here to answer your questions.")
chat_messages.append(("user", user_mssg))
answer = await openai_chat_completion.complete_chat(messages=messages, settings=settings)
chat_messages.append(("assistant", str(answer[0])))
chat_function = kernel.create_function_from_prompt(
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
)
user_mssg = "What are his best all-time stats?"
messages = [{"role": "user", "content": user_mssg}]
chat_messages.append(("user", user_mssg))
answer = await openai_chat_completion.complete_chat(messages=messages, settings=settings)
chat_messages.append(("assistant", str(answer[0])))
chat.add_system_message("You are a basketball expert")
chat.add_user_message("I love the LA Lakers, tell me an interesting fact about LeBron James.")
context_vars = sk.ContextVariables()
context_vars["chat_history"] = ""
context_vars["chat_bot_ans"] = ""
for role, mssg in chat_messages:
if role == "user":
context_vars["chat_history"] += f"User:> {mssg}\n"
elif role == "assistant":
context_vars["chat_history"] += f"ChatBot:> {mssg}\n"
context_vars["chat_bot_ans"] += f"{mssg}\n"
answer = await kernel.invoke(chat_function, KernelArguments(user_input=_prepare_input_chat(chat)))
chat.add_assistant_message(str(answer))
kernel.remove_chat_service("chat_service")
return context_vars, banned_words
chat.add_user_message("What are his best all-time stats?")
answer = await kernel.invoke(chat_function, KernelArguments(user_input=_prepare_input_chat(chat)))
chat.add_assistant_message(str(answer))
print(chat)
kernel.remove_all_services()
return chat, banned_words
async def text_complete_request_example(kernel, api_key, org_id):
openai_text_completion = sk_oai.OpenAITextCompletion("gpt-3.5-turbo-instruct", api_key, org_id)
kernel.add_text_completion_service("text_service", openai_text_completion)
service_id = "text_service"
openai_text_completion = sk_oai.OpenAITextCompletion(
service_id=service_id, ai_model_id="gpt-3.5-turbo-instruct", api_key=api_key, org_id=org_id
)
kernel.add_service(openai_text_completion)
# Spaces and capitalization affect the token ids.
# The following is the token ids of pie related words.
@@ -150,18 +159,37 @@ async def text_complete_request_example(kernel, api_key, org_id):
]
# Model will try its best to avoid using any of the above words
settings = kernel.get_prompt_execution_settings_from_service(TextCompletionClientBase, "text_service")
settings = kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
settings = _config_ban_tokens(settings, keys)
user_mssg = "The best pie flavor to have in autumn is"
answer = await openai_text_completion.complete(user_mssg, settings)
prompt_template_config = PromptTemplateConfig(
template="{{$user_input}}",
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(
name="user_input", description="The history of the conversation", is_required=True, default=""
),
],
execution_settings=settings,
)
context_vars = sk.ContextVariables()
context_vars["chat_history"] = f"User:> {user_mssg}\nChatBot:> {answer}\n"
context_vars["chat_bot_ans"] = str(answer)
chat = ChatHistory()
kernel.remove_text_completion_service("text_service")
return context_vars, banned_words
chat.add_user_message("The best pie flavor to have in autumn is")
text_function = kernel.create_function_from_prompt(
plugin_name="TextBot", function_name="TextCompletion", prompt_template_config=prompt_template_config
)
answer = await kernel.invoke(text_function, KernelArguments(user_input=_prepare_input_chat(chat)))
chat.add_assistant_message(str(answer))
print(chat)
kernel.remove_all_services()
return chat, banned_words
def _check_banned_words(banned_list, actual_list) -> bool:
@@ -173,9 +201,9 @@ def _check_banned_words(banned_list, actual_list) -> bool:
return passed
def _format_output(context, banned_words) -> None:
print(context["chat_history"])
chat_bot_ans_words = context["chat_bot_ans"].split()
def _format_output(chat, banned_words) -> None:
print("--- Checking for banned words ---")
chat_bot_ans_words = [word for msg in chat.messages if msg.role == "assistant" for word in msg.content.split()]
if _check_banned_words(banned_words, chat_bot_ans_words):
print("None of the banned words were found in the answer")
@@ -5,30 +5,39 @@ import os
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.functions.kernel_arguments import KernelArguments
kernel = sk.Kernel()
useAzureOpenAI = False
model = "gpt-35-turbo-instruct" if useAzureOpenAI else "gpt-3.5-turbo-instruct"
service_id = model
async def main():
kernel = sk.Kernel()
# Configure AI service used by the kernel
if useAzureOpenAI:
deployment_name, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
kernel.add_text_completion_service(
service_id,
sk_oai.AzureTextCompletion(deployment_name=model, api_key=api_key, endpoint=endpoint),
)
else:
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_text_completion_service(
service_id,
sk_oai.OpenAITextCompletion(ai_model_id=model, api_key=api_key, org_id=org_id),
)
useAzureOpenAI = False
model = "gpt-35-turbo-instruct" if useAzureOpenAI else "gpt-3.5-turbo-instruct"
service_id = model
# note: using plugins from the samples folder
plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
plugin = kernel.import_semantic_plugin_from_directory(plugins_directory, "FunPlugin")
# Configure AI service used by the kernel
if useAzureOpenAI:
deployment_name, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
kernel.add_service(
sk_oai.AzureTextCompletion(
service_id=service_id, deployment_name=model, api_key=api_key, endpoint=endpoint
),
)
else:
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_service(
sk_oai.OpenAITextCompletion(service_id=service_id, ai_model_id=model, api_key=api_key, org_id=org_id),
)
result = asyncio.run(kernel.run(plugin["Joke"], input_str="time travel to dinosaur age"))
print(result)
# note: using plugins from the samples folder
plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
plugin = kernel.import_plugin_from_prompt_directory(service_id, plugins_directory, "FunPlugin")
arguments = KernelArguments(input="time travel to dinosaur age", style="super silly")
result = await kernel.invoke(plugin["Joke"], arguments)
print(result)
if __name__ == "__main__":
asyncio.run(main())
@@ -13,7 +13,6 @@ from semantic_kernel.connectors.memory.azure_cognitive_search import (
AzureCognitiveSearchMemoryStore,
)
from semantic_kernel.core_plugins.text_memory_plugin import TextMemoryPlugin
from semantic_kernel.orchestration.context_variables import ContextVariables
COLLECTION_NAME = "generic"
@@ -45,19 +44,19 @@ async def main() -> None:
vector_size = 1536
# Setting up OpenAI services for text completion and text embedding
kernel.add_text_completion_service(
"dv",
kernel.add_service(
AzureTextCompletion(
# Note: text-davinci-003 is deprecated and will be replaced by
# AzureOpenAI's gpt-35-turbo-instruct model.
service_id="dv",
deployment_name="gpt-35-turbo-instruct",
endpoint=AZURE_OPENAI_ENDPOINT,
api_key=AZURE_OPENAI_API_KEY,
),
)
kernel.add_text_embedding_generation_service(
"ada",
kernel.add_service(
AzureTextEmbedding(
service_id="ada",
deployment_name="text-embedding-ada-002",
endpoint=AZURE_OPENAI_ENDPOINT,
api_key=AZURE_OPENAI_API_KEY,
@@ -99,15 +98,8 @@ Remember, just answer Grounded or Ungrounded or Unclear: """.strip()
chat_func = kernel.create_semantic_function(sk_prompt_rag, max_tokens=1000, temperature=0.5)
self_critique_func = kernel.create_semantic_function(sk_prompt_rag_sc, max_tokens=4, temperature=0.0)
answer = await kernel.run(
answer = await kernel.invoke(
chat_func,
input_vars=ContextVariables(
variables={
"user_input": user_input,
"collection": COLLECTION_NAME,
"limit": "2",
}
),
)
print(f"Answer: {str(answer).strip()}")
check = await kernel.run(self_critique_func, input_context=answer)
@@ -3,26 +3,27 @@
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.core_plugins import (
FileIOPlugin,
MathPlugin,
TextPlugin,
TimePlugin,
)
from semantic_kernel.planning import SequentialPlanner
from semantic_kernel.planners import SequentialPlanner
async def main():
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service("gpt-3.5", OpenAIChatCompletion("gpt-3.5-turbo", api_key=api_key, org_id=org_id))
service_id = "gpt-3.5"
kernel.add_service(
OpenAIChatCompletion(service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id)
)
kernel.import_plugin(MathPlugin(), "math")
kernel.import_plugin(FileIOPlugin(), "fileIO")
kernel.import_plugin(TimePlugin(), "time")
kernel.import_plugin(TextPlugin(), "text")
# create an instance of sequential planner.
planner = SequentialPlanner(kernel)
planner = SequentialPlanner(service_id=service_id, kernel=kernel)
# the ask for which the sequential planner is going to find a relevant function.
ask = "What day of the week is today, all uppercase?"
@@ -1,5 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
import os
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
@@ -16,13 +18,19 @@ async def main():
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_chat_service("chat-gpt", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))
service_id = "chat-gpt"
kernel.add_service(
OpenAIChatCompletion(service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id)
)
plugin = kernel.import_semantic_plugin_from_directory("../../samples/plugins", "FunPlugin")
plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
plugin = kernel.import_plugin_from_prompt_directory(service_id, plugins_directory, "FunPlugin")
joke_function = plugin["Joke"]
print(joke_function("time travel to dinosaur age"))
result = await kernel.invoke(joke_function, input="time travel to dinosaur age", style="silly")
print(result)
if __name__ == "__main__":
@@ -0,0 +1,55 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai
from semantic_kernel.core_plugins import (
TimePlugin,
)
from semantic_kernel.prompt_template.kernel_prompt_template import KernelPromptTemplate
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
async def main():
kernel = sk.Kernel()
useAzureOpenAI = False
model = "gpt-35-turbo" if useAzureOpenAI else "gpt-3.5-turbo-1106"
service_id = model
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_service(
sk_oai.OpenAIChatCompletion(service_id=service_id, ai_model_id=model, api_key=api_key, org_id=org_id),
)
kernel.import_plugin(TimePlugin(), "time")
function_definition = """
Today is: {{time.Date}}
Current time is: {{time.Time}}
Answer to the following questions using JSON syntax, including the data used.
Is it morning, afternoon, evening, or night (morning/afternoon/evening/night)?
Is it weekend time (weekend/not weekend)?
"""
print("--- Rendered Prompt ---")
prompt_template_config = PromptTemplateConfig(template=function_definition)
prompt_template = KernelPromptTemplate(prompt_template_config)
rendered_prompt = await prompt_template.render(kernel, arguments=None)
print(rendered_prompt)
kind_of_day = kernel.create_function_from_prompt(
template=function_definition,
execution_settings=sk_oai.OpenAIChatPromptExecutionSettings(service_id=service_id, max_tokens=100),
function_name="kind_of_day",
)
print("--- Prompt Function Result ---")
result = await kernel.invoke(kind_of_day)
print(result)
if __name__ == "__main__":
asyncio.run(main())
+3 -15
View File
@@ -1,18 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
from semantic_kernel import core_plugins, memory
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel import Kernel
from semantic_kernel.orchestration.context_variables import ContextVariables
from semantic_kernel.orchestration.kernel_context import KernelContext
from semantic_kernel.orchestration.kernel_function import KernelFunction
from semantic_kernel.semantic_functions.chat_prompt_template import ChatPromptTemplate
from semantic_kernel.semantic_functions.prompt_template import PromptTemplate
from semantic_kernel.semantic_functions.prompt_template_config import (
from semantic_kernel.prompt_template.prompt_template_config import (
PromptTemplateConfig,
)
from semantic_kernel.semantic_functions.semantic_function_config import (
SemanticFunctionConfig,
)
from semantic_kernel.utils.logging import setup_logging
from semantic_kernel.utils.null_logger import NullLogger
from semantic_kernel.utils.settings import (
@@ -46,12 +39,7 @@ __all__ = [
"google_palm_settings_from_dot_env",
"redis_settings_from_dot_env",
"PromptTemplateConfig",
"PromptTemplate",
"ChatPromptTemplate",
"SemanticFunctionConfig",
"ContextVariables",
"KernelFunction",
"KernelContext",
"KernelArguments",
"memory",
"core_plugins",
"setup_logging",
@@ -30,6 +30,10 @@ class AIException(Exception):
FunctionTypeNotSupported = 9
# The LLM raised an error due to improper content.
BadContentError = 10
# No service was found.
NoServiceFound = 11
# The prompt is not valid
InvalidPrompt = 12
# The error code.
_error_code: ErrorCodes
@@ -1,30 +1,35 @@
# Copyright (c) Microsoft. All rights reserved.
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, AsyncIterable, List, Optional
from typing import TYPE_CHECKING, AsyncIterable, Dict, List, Optional, Type
from semantic_kernel.contents import ChatMessageContent
from semantic_kernel.services.ai_service_client_base import AIServiceClientBase
if TYPE_CHECKING:
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.models.chat.chat_message import ChatMessage
from semantic_kernel.models.contents import ChatMessageContent, StreamingChatMessageContent
from semantic_kernel.contents import StreamingChatMessageContent
from semantic_kernel.contents.chat_history import ChatHistory
class ChatCompletionClientBase(ABC):
class ChatCompletionClientBase(AIServiceClientBase, ABC):
def get_chat_message_content_class(self) -> Type[ChatMessageContent]:
"""Get the chat message content types used by a class, default is ChatMessageContent."""
return ChatMessageContent
@abstractmethod
async def complete_chat(
self,
messages: List["ChatMessage"],
chat_history: "ChatHistory",
settings: "PromptExecutionSettings",
logger: Optional[Any] = None,
) -> List["ChatMessageContent"]:
"""
This is the method that is called from the kernel to get a response from a chat-optimized LLM.
Arguments:
messages {List[ChatMessage]} -- A list of chat messages, that can be rendered into a
set of messages, from system, user, assistant and function.
chat_history {ChatHistory} -- A list of chats in a chat_history object, that can be
rendered into messages from system, user, assistant and tools.
settings {PromptExecutionSettings} -- Settings for the request.
logger {Logger} -- A logger to use for logging. (Deprecated)
Returns:
Union[str, List[str]] -- A string or list of strings representing the response(s) from the LLM.
@@ -34,20 +39,30 @@ class ChatCompletionClientBase(ABC):
@abstractmethod
async def complete_chat_stream(
self,
messages: List["ChatMessage"],
chat_history: "ChatHistory",
settings: "PromptExecutionSettings",
logger: Optional[Any] = None,
) -> AsyncIterable[List["StreamingChatMessageContent"]]:
"""
This is the method that is called from the kernel to get a stream response from a chat-optimized LLM.
Arguments:
messages {List[ChatMessage]} -- A list of chat messages, that can be rendered into a
set of messages, from system, user, assistant and function.
chat_history {ChatHistory} -- A list of chat chat_history, that can be rendered into a
set of chat_history, from system, user, assistant and function.
settings {PromptExecutionSettings} -- Settings for the request.
logger {Logger} -- A logger to use for logging. (Deprecated)
Yields:
A stream representing the response(s) from the LLM.
"""
pass
def _prepare_chat_history_for_request(
self,
chat_history: "ChatHistory",
) -> List[Dict[str, Optional[str]]]:
"""
Prepare the chat history for a request, allowing customization of the key names for role/author,
and optionally overriding the role.
"""
return [
message.model_dump(exclude_none=True, exclude=["metadata", "encoding"]) for message in chat_history.messages
]
@@ -3,11 +3,13 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, List
from semantic_kernel.services.ai_service_client_base import AIServiceClientBase
if TYPE_CHECKING:
from numpy import ndarray
class EmbeddingGeneratorBase(ABC):
class EmbeddingGeneratorBase(AIServiceClientBase, ABC):
@abstractmethod
async def generate_embeddings(self, texts: List[str]) -> "ndarray":
pass
@@ -2,10 +2,10 @@
import logging
import sys
from typing import Any, Dict, List, Optional, Tuple
from typing import Dict, List, Optional, Tuple
from semantic_kernel.models.contents.chat_message_content import ChatMessageContent
from semantic_kernel.models.contents.text_content import TextContent
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.text_content import TextContent
if sys.version_info >= (3, 9):
from typing import Annotated
@@ -17,7 +17,6 @@ from google.generativeai.types import ChatResponse, MessageDict
from pydantic import PrivateAttr, StringConstraints
from semantic_kernel.connectors.ai.ai_exception import AIException
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.chat_completion_client_base import (
ChatCompletionClientBase,
)
@@ -29,20 +28,24 @@ from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecut
from semantic_kernel.connectors.ai.text_completion_client_base import (
TextCompletionClientBase,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.chat_role import ChatRole
logger: logging.Logger = logging.getLogger(__name__)
int_to_role = {1: ChatRole.USER, 2: ChatRole.SYSTEM, 3: ChatRole.ASSISTANT, 4: ChatRole.TOOL}
class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBase, AIServiceClientBase):
class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBase):
api_key: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]
_message_history: Optional[ChatResponse] = PrivateAttr()
_message_history: Optional[ChatHistory] = PrivateAttr()
service_id: Optional[str] = None
def __init__(
self,
ai_model_id: str,
api_key: str,
message_history: Optional[ChatResponse] = None,
log: Optional[Any] = None,
message_history: Optional[ChatHistory] = None,
):
"""
Initializes a new instance of the GooglePalmChatCompletion class.
@@ -52,20 +55,17 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
https://developers.generativeai.google/models/language
api_key {str} -- GooglePalm API key, see
https://developers.generativeai.google/products/palm
message_history {Optional[ChatResponse]} -- The message history to use for context. (Optional)
log {Optional[Any]} -- A logger to use for logging. (Optional)
message_history {Optional[ChatHistory]} -- The message history to use for context. (Optional)
"""
super().__init__(
ai_model_id=ai_model_id,
api_key=api_key,
)
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
self._message_history = message_history
async def complete_chat(
self,
messages: List[Dict[str, str]],
messages: ChatHistory,
settings: GooglePalmPromptExecutionSettings,
) -> List[ChatMessageContent]:
"""
@@ -79,7 +79,7 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
Returns:
List[ChatMessageContent] -- A list of ChatMessageContent objects representing the response(s) from the LLM.
"""
settings.messages = [{"author": message["role"], "content": message["content"]} for message in messages]
settings.messages = self._prepare_chat_history_for_request(messages)
if not settings.ai_model_id:
settings.ai_model_id = self.ai_model_id
response = await self._send_chat_request(settings)
@@ -99,13 +99,16 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
Returns:
ChatMessageContent -- The created chat message content.
"""
metadata = {"citation_metadata": candidate.get("citation_metadata"), "filters": response.filters}
metadata = {
"citation_metadata": candidate.get("citation_metadata"),
"filters": response.filters,
"choice_index": index,
}
return ChatMessageContent(
choice_index=index,
inner_content=response,
ai_model_id=self.ai_model_id,
metadata=metadata,
role=candidate.get("author"),
role=int_to_role[int(candidate.get("author"))], # TODO: why is author coming back as '1'?
content=candidate.get("content"),
)
@@ -120,7 +123,6 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
self,
prompt: str,
settings: GooglePalmPromptExecutionSettings,
**kwargs,
) -> List[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -132,8 +134,6 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
Returns:
List[TextContent] -- A list of TextContent objects representing the response(s) from the LLM.
"""
if kwargs.get("logger"):
logger.warning("The `logger` parameter is deprecated. Please use the `logging` module instead.")
settings.messages = [{"author": "user", "content": prompt}]
if not settings.ai_model_id:
settings.ai_model_id = self.ai_model_id
@@ -162,10 +162,7 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
self,
prompt: str,
settings: GooglePalmPromptExecutionSettings,
**kwargs,
):
if kwargs.get("logger"):
logger.warning("The `logger` parameter is deprecated. Please use the `logging` module instead.")
raise NotImplementedError("Google Palm API does not currently support streaming")
async def _send_chat_request(
@@ -236,3 +233,18 @@ class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBas
def get_prompt_execution_settings_class(self) -> "PromptExecutionSettings":
"""Create a request settings object."""
return GooglePalmChatPromptExecutionSettings
def _prepare_chat_history_for_request(
self,
chat_history: ChatHistory,
) -> List[Dict[str, Optional[str]]]:
"""
Prepare the chat history for a request, allowing customization of the key names for role/author,
and optionally overriding the role.
"""
standard_out = super()._prepare_chat_history_for_request(chat_history)
for message in standard_out:
message["author"] = message.pop("role")
# The last message should always be from the user
standard_out[-1]["author"] = "user"
return standard_out
@@ -2,9 +2,9 @@
import logging
import sys
from typing import Any, List, Optional
from typing import List
from semantic_kernel.models.contents.text_content import TextContent
from semantic_kernel.contents.text_content import TextContent
if sys.version_info >= (3, 9):
from typing import Annotated
@@ -16,7 +16,6 @@ from google.generativeai.types.text_types import TextCompletion
from pydantic import StringConstraints
from semantic_kernel.connectors.ai.ai_exception import AIException
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.google_palm.gp_prompt_execution_settings import (
GooglePalmTextPromptExecutionSettings,
)
@@ -28,10 +27,10 @@ from semantic_kernel.connectors.ai.text_completion_client_base import (
logger: logging.Logger = logging.getLogger(__name__)
class GooglePalmTextCompletion(TextCompletionClientBase, AIServiceClientBase):
class GooglePalmTextCompletion(TextCompletionClientBase):
api_key: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]
def __init__(self, ai_model_id: str, api_key: str, log: Optional[Any] = None):
def __init__(self, ai_model_id: str, api_key: str):
"""
Initializes a new instance of the GooglePalmTextCompletion class.
@@ -40,15 +39,10 @@ class GooglePalmTextCompletion(TextCompletionClientBase, AIServiceClientBase):
https://developers.generativeai.google/models/language
api_key {str} -- GooglePalm API key, see
https://developers.generativeai.google/products/palm
log {Optional[Any]} -- The logger instance to use. (Optional) (Deprecated)
"""
super().__init__(ai_model_id=ai_model_id, api_key=api_key)
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
async def complete(
self, prompt: str, settings: GooglePalmTextPromptExecutionSettings, **kwargs
) -> List[TextContent]:
async def complete(self, prompt: str, settings: GooglePalmTextPromptExecutionSettings) -> List[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -97,7 +91,6 @@ class GooglePalmTextCompletion(TextCompletionClientBase, AIServiceClientBase):
self,
prompt: str,
settings: GooglePalmTextPromptExecutionSettings,
logger: Optional[Any] = None,
):
raise NotImplementedError("Google Palm API does not currently support streaming")
@@ -13,13 +13,12 @@ from numpy import array, ndarray
from pydantic import StringConstraints
from semantic_kernel.connectors.ai.ai_exception import AIException
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import (
EmbeddingGeneratorBase,
)
class GooglePalmTextEmbedding(EmbeddingGeneratorBase, AIServiceClientBase):
class GooglePalmTextEmbedding(EmbeddingGeneratorBase):
api_key: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]
def __init__(self, ai_model_id: str, api_key: str) -> None:
@@ -8,15 +8,14 @@ import torch
from transformers import AutoTokenizer, TextIteratorStreamer, pipeline
from semantic_kernel.connectors.ai.ai_exception import AIException
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.hugging_face.hf_prompt_execution_settings import (
HuggingFacePromptExecutionSettings,
)
from semantic_kernel.connectors.ai.text_completion_client_base import (
TextCompletionClientBase,
)
from semantic_kernel.models.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.models.contents.text_content import TextContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
if TYPE_CHECKING:
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
@@ -24,7 +23,7 @@ if TYPE_CHECKING:
logger: logging.Logger = logging.getLogger(__name__)
class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
class HuggingFaceTextCompletion(TextCompletionClientBase):
task: Literal["summarization", "text-generation", "text2text-generation"]
device: str
generator: Any
@@ -34,6 +33,7 @@ class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
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,
) -> None:
@@ -47,6 +47,7 @@ class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
-- None if using device_map instead. (If both device and device_map
are specified, device overrides device_map. If unintended,
it can lead to unexpected behavior.)
service_id {Optional[str]} -- Service ID for the AI service.
task {Optional[str]} -- Model completion task type, options are:
- summarization: takes a long text and returns a shorter summary.
- text-generation: takes incomplete text and returns a set of completion candidates.
@@ -69,6 +70,7 @@ class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
**pipeline_kwargs or {},
)
super().__init__(
service_id=service_id,
ai_model_id=ai_model_id,
task=task,
device=(f"cuda:{device}" if device >= 0 and torch.cuda.is_available() else "cpu"),
@@ -79,7 +81,6 @@ class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
self,
prompt: str,
settings: HuggingFacePromptExecutionSettings,
**kwargs,
) -> List[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -91,8 +92,6 @@ class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
Returns:
List[TextContent] -- A list of TextContent objects representing the response(s) from the LLM.
"""
if kwargs.get("logger"):
logger.warning("The `logger` parameter is deprecated. Please use the `logging` module instead.")
try:
results = self.generator(prompt, **settings.prepare_settings_dict())
except Exception as e:
@@ -112,7 +111,6 @@ class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
self,
prompt: str,
settings: HuggingFacePromptExecutionSettings,
**kwargs,
) -> AsyncIterable[List[StreamingTextContent]]:
"""
Streams a text completion using a Hugging Face model.
@@ -125,8 +123,6 @@ class HuggingFaceTextCompletion(TextCompletionClientBase, AIServiceClientBase):
Yields:
List[StreamingTextContent] -- List of StreamingTextContent objects.
"""
if kwargs.get("logger"):
logger.warning("The `logger` parameter is deprecated. Please use the `logging` module instead.")
if settings.num_return_sequences > 1:
raise AIException(
AIException.ErrorCodes.InvalidConfiguration,
@@ -8,7 +8,6 @@ import torch
from numpy import array, ndarray
from semantic_kernel.connectors.ai.ai_exception import AIException
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import (
EmbeddingGeneratorBase,
)
@@ -16,7 +15,7 @@ from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import (
logger: logging.Logger = logging.getLogger(__name__)
class HuggingFaceTextEmbedding(EmbeddingGeneratorBase, AIServiceClientBase):
class HuggingFaceTextEmbedding(EmbeddingGeneratorBase):
device: str
generator: Any
@@ -24,7 +23,7 @@ class HuggingFaceTextEmbedding(EmbeddingGeneratorBase, AIServiceClientBase):
self,
ai_model_id: str,
device: Optional[int] = -1,
log: Optional[Any] = None,
service_id: Optional[str] = None,
) -> None:
"""
Initializes a new instance of the HuggingFaceTextEmbedding class.
@@ -37,11 +36,10 @@ class HuggingFaceTextEmbedding(EmbeddingGeneratorBase, AIServiceClientBase):
Note that this model will be downloaded from the Hugging Face model hub.
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
resolved_device = f"cuda:{device}" if device >= 0 and torch.cuda.is_available() else "cpu"
super().__init__(
ai_model_id=ai_model_id,
service_id=service_id,
device=resolved_device,
generator=sentence_transformers.SentenceTransformer(model_name_or_path=ai_model_id, device=resolved_device),
)
@@ -1,3 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any, Dict, List, Literal, Optional
from pydantic import Field
@@ -6,7 +8,7 @@ from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecut
class OllamaPromptExecutionSettings(PromptExecutionSettings):
ai_model_id: str = Field("", alias="model")
ai_model_id: str = Field("", serialization_alias="model")
format: Optional[Literal["json"]] = None
options: Optional[Dict[str, Any]] = None
stream: bool = False
@@ -2,12 +2,11 @@
import json
import logging
from typing import AsyncIterable, Dict, List, Optional
from typing import AsyncIterable, List, Optional
import aiohttp
from pydantic import HttpUrl
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.chat_completion_client_base import (
ChatCompletionClientBase,
)
@@ -18,15 +17,16 @@ from semantic_kernel.connectors.ai.ollama.utils import AsyncSession
from semantic_kernel.connectors.ai.text_completion_client_base import (
TextCompletionClientBase,
)
from semantic_kernel.models.contents.chat_message_content import ChatMessageContent
from semantic_kernel.models.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.models.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.models.contents.text_content import TextContent
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
logger: logging.Logger = logging.getLogger(__name__)
class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase, AIServiceClientBase):
class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase):
"""
Initializes a new instance of the OllamaChatCompletion class.
@@ -43,23 +43,21 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase, A
async def complete_chat(
self,
messages: List[Dict[str, str]],
chat_history: ChatHistory,
settings: OllamaChatPromptExecutionSettings,
**kwargs,
) -> List[ChatMessageContent]:
"""
This is the method that is called from the kernel to get a response from a chat-optimized LLM.
Arguments:
messages {List[ChatMessage]} -- A list of chat messages, that can be rendered into a
set of messages, from system, user, assistant and function.
chat_history {ChatHistory} -- A chat history that contains a list of chat messages,
that can be rendered into a set of messages, from system, user, assistant and function.
settings {PromptExecutionSettings} -- Settings for the request.
logger {Logger} -- A logger to use for logging. (Deprecated)
Returns:
List[ChatMessageContent] -- A list of ChatMessageContent objects representing the response(s) from the LLM.
"""
settings.messages = messages
settings.messages = self._prepare_chat_history_for_request(chat_history)
settings.stream = False
async with AsyncSession(self.session) as session:
async with session.post(str(self.url), json=settings.prepare_settings_dict()) as response:
@@ -76,22 +74,22 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase, A
async def complete_chat_stream(
self,
messages: List[Dict[str, str]],
chat_history: ChatHistory,
settings: OllamaChatPromptExecutionSettings,
**kwargs,
) -> AsyncIterable[List[StreamingChatMessageContent]]:
"""
Streams a text completion using a Ollama model.
Note that this method does not support multiple responses.
Arguments:
prompt {str} -- Prompt to complete.
chat_history {ChatHistory} -- A chat history that contains a list of chat messages,
that can be rendered into a set of messages, from system, user, assistant and function.
settings {OllamaChatPromptExecutionSettings} -- Request settings.
Yields:
List[StreamingChatMessageContent] -- Stream of StreamingChatMessageContent objects.
"""
settings.messages = messages
settings.messages = self._prepare_chat_history_for_request(chat_history)
settings.stream = True
async with AsyncSession(self.session) as session:
async with session.post(str(self.url), json=settings.prepare_settings_dict()) as response:
@@ -115,15 +113,13 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase, A
self,
prompt: str,
settings: OllamaChatPromptExecutionSettings,
**kwargs,
) -> List[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
Arguments:
prompt {str} -- The prompt to send to the LLM.
chat_history {ChatHistory} -- A chat history that contains the prompt to complete.
settings {OllamaChatPromptExecutionSettings} -- Settings for the request.
logger {Logger} -- A logger to use for logging (deprecated).
Returns:
List["TextContent"] -- The completion result(s).
@@ -146,14 +142,13 @@ class OllamaChatCompletion(TextCompletionClientBase, ChatCompletionClientBase, A
self,
prompt: str,
settings: OllamaChatPromptExecutionSettings,
**kwargs,
) -> AsyncIterable[List[StreamingTextContent]]:
"""
Streams a text completion using a Ollama model.
Note that this method does not support multiple responses.
Arguments:
prompt {str} -- Prompt to complete.
prompt {str} -- A chat history that contains the prompt to complete.
settings {OllamaChatPromptExecutionSettings} -- Request settings.
Yields:
@@ -7,7 +7,6 @@ from typing import AsyncIterable, List, Optional
import aiohttp
from pydantic import HttpUrl
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.ollama.ollama_prompt_execution_settings import (
OllamaTextPromptExecutionSettings,
)
@@ -15,13 +14,13 @@ from semantic_kernel.connectors.ai.ollama.utils import AsyncSession
from semantic_kernel.connectors.ai.text_completion_client_base import (
TextCompletionClientBase,
)
from semantic_kernel.models.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.models.contents.text_content import TextContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
logger: logging.Logger = logging.getLogger(__name__)
class OllamaTextCompletion(TextCompletionClientBase, AIServiceClientBase):
class OllamaTextCompletion(TextCompletionClientBase):
"""
Initializes a new instance of the OllamaTextCompletion class.
@@ -39,7 +38,6 @@ class OllamaTextCompletion(TextCompletionClientBase, AIServiceClientBase):
self,
prompt: str,
settings: OllamaTextPromptExecutionSettings,
**kwargs,
) -> List[TextContent]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -63,7 +61,6 @@ class OllamaTextCompletion(TextCompletionClientBase, AIServiceClientBase):
self,
prompt: str,
settings: OllamaTextPromptExecutionSettings,
**kwargs,
) -> AsyncIterable[List[StreamingTextContent]]:
"""
Streams a text completion using a Ollama model.
@@ -7,7 +7,6 @@ import aiohttp
from numpy import array, ndarray
from pydantic import HttpUrl
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import (
EmbeddingGeneratorBase,
)
@@ -16,7 +15,7 @@ from semantic_kernel.connectors.ai.ollama.utils import AsyncSession
logger: logging.Logger = logging.getLogger(__name__)
class OllamaTextEmbedding(EmbeddingGeneratorBase, AIServiceClientBase):
class OllamaTextEmbedding(EmbeddingGeneratorBase):
"""Ollama embeddings client.
Make sure to have the ollama service running either locally or remotely.
@@ -1,3 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
import aiohttp
@@ -1,11 +1,14 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import List, Optional
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
from openai.types.chat import ChatCompletion
from semantic_kernel.connectors.ai.open_ai.models.chat.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat.tool_calls import ToolCall
from semantic_kernel.models.contents import ChatMessageContent
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.tool_calls import ToolCall
from semantic_kernel.contents import ChatMessageContent
from semantic_kernel.contents.chat_role import ChatRole
class OpenAIChatMessageContent(ChatMessageContent):
@@ -27,6 +30,44 @@ class OpenAIChatMessageContent(ChatMessageContent):
__str__: Returns the content of the response.
"""
inner_content: ChatCompletion
inner_content: Optional[ChatCompletion] = None
function_call: Optional[FunctionCall] = None
tool_calls: Optional[List[ToolCall]] = None
@staticmethod
def ToolIdProperty():
# Directly using the class name and the attribute name as strings
return f"{ToolCall.__name__}.{ToolCall.id.__name__}"
def to_prompt(self, root_key: str) -> str:
"""Convert the OpenAIChatMessageContent to a prompt.
Returns:
str - The prompt from the ChatMessageContent.
"""
root = Element(root_key)
root.set("role", self.role.value)
if self.function_call:
root.set("function_call", self.function_call.model_dump_json(exclude_none=True))
if self.tool_calls:
root.set("tool_calls", ",".join([call.model_dump_json(exclude_none=True) for call in self.tool_calls]))
root.text = self.content or ""
return ElementTree.tostring(root, encoding=self.encoding or "unicode")
@classmethod
def from_element(cls, element: Element) -> "ChatMessageContent":
"""Create a new instance of OpenAIChatMessageContent from a prompt.
Args:
prompt: str - The prompt to create the ChatMessageContent from.
Returns:
ChatMessageContent - The new instance of ChatMessageContent.
"""
args = {"role": element.get("role", ChatRole.USER.value), "content": element.text}
if function_call := element.get("function_call"):
args["function_call"] = FunctionCall.model_validate_json(function_call)
if tool_calls := element.get("tool_calls"):
args["tool_calls"] = [ToolCall.model_validate_json(call) for call in tool_calls.split(",")]
return cls(**args)
@@ -3,9 +3,9 @@ from typing import List, Optional
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
from semantic_kernel.connectors.ai.open_ai.models.chat.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat.tool_calls import ToolCall
from semantic_kernel.models.contents import StreamingChatMessageContent
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.tool_calls import ToolCall
from semantic_kernel.contents import StreamingChatMessageContent
class OpenAIStreamingChatMessageContent(StreamingChatMessageContent):
@@ -1,9 +1,9 @@
"""Class to hold chat messages."""
import json
from typing import Dict, Optional, Tuple
from typing import Any, Dict, List, Optional
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.orchestration.context_variables import ContextVariables
class FunctionCall(KernelBaseModel):
@@ -24,23 +24,29 @@ class FunctionCall(KernelBaseModel):
id=self.id or other.id,
)
def parse_arguments(self) -> Dict[str, str]:
def parse_arguments(self) -> Optional[Dict[str, Any]]:
"""Parse the arguments into a dictionary."""
if not self.arguments:
return None
try:
return json.loads(self.arguments)
except json.JSONDecodeError:
return None
def to_context_variables(self) -> ContextVariables:
"""Return the arguments as a ContextVariables instance."""
def to_kernel_arguments(self) -> KernelArguments:
"""Return the arguments as a KernelArguments instance."""
args = self.parse_arguments()
return ContextVariables(variables={k.lower(): str(v) for k, v in args.items()})
if not args:
return KernelArguments()
return KernelArguments(**args)
def split_name(self) -> Tuple[str, str]:
def split_name(self) -> List[str]:
"""Split the name into a plugin and function name."""
if not self.name:
raise ValueError("Name is not set.")
if "-" not in self.name:
return None, self.name
return self.name.split("-")
return ["", self.name]
return self.name.split("-", maxsplit=1)
def split_name_dict(self) -> dict:
"""Split the name into a plugin and function name."""
@@ -1,18 +1,17 @@
"""Class to hold chat messages."""
from typing import Optional
from semantic_kernel.connectors.ai.open_ai.models.chat.function_call import (
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.function_call import (
FunctionCall,
)
from semantic_kernel.connectors.ai.open_ai.models.chat.tool_calls import ToolCall
from semantic_kernel.models.chat.chat_message import ChatMessage
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.tool_calls import ToolCall
from semantic_kernel.models.ai.chat_completion.chat_message import ChatMessage
class OpenAIChatMessage(ChatMessage):
"""Class to hold openai chat messages, which might include name and function_call fields."""
"""Class to hold openai chat messages, which might include name, function_call and tool_calls fields."""
name: Optional[str] = None
# TODO: handle tool_calls
function_call: Optional[FunctionCall] = None
tool_calls: Optional[ToolCall] = None
tool_call_id: Optional[str] = None
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Literal, Optional
from semantic_kernel.connectors.ai.open_ai.models.chat.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.function_call import FunctionCall
from semantic_kernel.kernel_pydantic import KernelBaseModel
@@ -0,0 +1,93 @@
# # Copyright (c) Microsoft. All rights reserved.
# import logging
# from typing import Any, Optional
# from semantic_kernel.connectors.ai.open_ai.models.chat_completion.open_ai_chat_message import (
# OpenAIChatMessage,
# )
# from semantic_kernel.prompt_template.chat_prompt_template import ChatPromptTemplate
# from semantic_kernel.prompt_template.prompt_template import PromptTemplate
# logger: logging.Logger = logging.getLogger(__name__)
# class OpenAIChatPromptTemplate(ChatPromptTemplate[OpenAIChatMessage]):
# def add_function_response_message(self, name: str, content: Any, tool_call_id: Optional[str] = None) -> None:
# """Add a function response message to the chat template."""
# self.messages.append(
# OpenAIChatMessage(role="function", name=name, fixed_content=str(content), tool_call_id=tool_call_id)
# )
# def add_tool_call_response_message(self, tool_call_id: str, content: Any) -> None:
# """Add a tool call response message to the chat template."""
# self.messages.append(OpenAIChatMessage(role="tool", tool_call_id=tool_call_id, fixed_content=str(content)))
# def add_message(self, role: str, message: Optional[str] = None, **kwargs: Any) -> None:
# """Add a message to the chat template.
# Arguments:
# role: The role of the message, one of "user", "assistant", "system", "function"
# message: The message to add, can include templating components.
# kwargs: can be used by inherited classes.
# name: the name of the function that was used, to be used with role: function
# function_call: the function call that is specified, to be used with role: assistant
# """
# name = kwargs.get("name")
# if name is not None and role != "function":
# logger.warning("name is only used with role: function, ignoring")
# name = None
# function_call = kwargs.get("function_call")
# if function_call is not None:
# if role == "assistant":
# self.messages.append(
# OpenAIChatMessage(
# role=role,
# fixed_content=message,
# name=name,
# function_call=function_call,
# )
# )
# return
# logger.warning("function_call is only used with role: assistant, ignoring")
# function_call = None
# tool_calls = kwargs.get("tool_calls")
# if tool_calls is not None:
# # TODO: update this when tool_calls is implemented
# # and allow for multiple tool calls
# ids = [tool_call.id for tool_call in tool_calls]
# if role == "assistant":
# self.messages.append(
# OpenAIChatMessage(
# role=role,
# fixed_content=message,
# name=name,
# tool_calls=tool_calls[0],
# tool_call_id=ids[0],
# )
# )
# return
# self._log.warning("tool_calls is only used with role: assistant, ignoring")
# tool_calls = None
# tool_call_id = kwargs.get("tool_call_id")
# if tool_call_id is not None:
# if role == "tool":
# self.messages.append(
# OpenAIChatMessage(
# role=role,
# fixed_content=message,
# name=name,
# tool_call_id=tool_call_id,
# )
# )
# return
# self._log.warning("tool_call_id is only used with role: tool, ignoring")
# tool_call_id = None
# self.messages.append(
# OpenAIChatMessage(
# role=role,
# content_template=PromptTemplate(message, self.template_engine, self.prompt_config),
# name=name,
# function_call=function_call,
# )
# )
@@ -1,93 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Any, Optional
from semantic_kernel.connectors.ai.open_ai.models.chat.open_ai_chat_message import (
OpenAIChatMessage,
)
from semantic_kernel.semantic_functions.chat_prompt_template import ChatPromptTemplate
from semantic_kernel.semantic_functions.prompt_template import PromptTemplate
logger: logging.Logger = logging.getLogger(__name__)
class OpenAIChatPromptTemplate(ChatPromptTemplate[OpenAIChatMessage]):
def add_function_response_message(self, name: str, content: Any, tool_call_id: Optional[str] = None) -> None:
"""Add a function response message to the chat template."""
self.messages.append(
OpenAIChatMessage(role="function", name=name, fixed_content=str(content), tool_call_id=tool_call_id)
)
def add_tool_call_response_message(self, tool_call_id: str, content: Any) -> None:
"""Add a tool call response message to the chat template."""
self.messages.append(OpenAIChatMessage(role="tool", tool_call_id=tool_call_id, fixed_content=str(content)))
def add_message(self, role: str, message: Optional[str] = None, **kwargs: Any) -> None:
"""Add a message to the chat template.
Arguments:
role: The role of the message, one of "user", "assistant", "system", "function"
message: The message to add, can include templating components.
kwargs: can be used by inherited classes.
name: the name of the function that was used, to be used with role: function
function_call: the function call that is specified, to be used with role: assistant
"""
name = kwargs.get("name")
if name is not None and role != "function":
logger.warning("name is only used with role: function, ignoring")
name = None
function_call = kwargs.get("function_call")
if function_call is not None:
if role == "assistant":
self.messages.append(
OpenAIChatMessage(
role=role,
fixed_content=message,
name=name,
function_call=function_call,
)
)
return
logger.warning("function_call is only used with role: assistant, ignoring")
function_call = None
tool_calls = kwargs.get("tool_calls")
if tool_calls is not None:
# TODO: update this when tool_calls is implemented
# and allow for multiple tool calls
ids = [tool_call.id for tool_call in tool_calls]
if role == "assistant":
self.messages.append(
OpenAIChatMessage(
role=role,
fixed_content=message,
name=name,
tool_calls=tool_calls[0],
tool_call_id=ids[0],
)
)
return
self._log.warning("tool_calls is only used with role: assistant, ignoring")
tool_calls = None
tool_call_id = kwargs.get("tool_call_id")
if tool_call_id is not None:
if role == "tool":
self.messages.append(
OpenAIChatMessage(
role=role,
fixed_content=message,
name=name,
tool_call_id=tool_call_id,
)
)
return
self._log.warning("tool_call_id is only used with role: tool, ignoring")
tool_call_id = None
self.messages.append(
OpenAIChatMessage(
role=role,
content_template=PromptTemplate(message, self.template_engine, self.prompt_config),
name=name,
function_call=function_call,
)
)
@@ -35,9 +35,9 @@ from semantic_kernel.connectors.ai.open_ai.services.open_ai_text_completion_base
OpenAITextCompletionBase,
)
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.contents.chat_role import ChatRole
from semantic_kernel.contents.finish_reason import FinishReason
from semantic_kernel.kernel_pydantic import HttpsUrl
from semantic_kernel.models.chat.chat_role import ChatRole
from semantic_kernel.models.chat.finish_reason import FinishReason
logger: logging.Logger = logging.getLogger(__name__)
@@ -50,12 +50,12 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
self,
deployment_name: str,
base_url: Union[HttpsUrl, str],
service_id: Optional[str] = None,
api_version: str = DEFAULT_AZURE_API_VERSION,
api_key: Optional[str] = None,
ad_token: Optional[str] = None,
ad_token_provider: Optional[AsyncAzureADTokenProvider] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an AzureChatCompletion service.
@@ -80,8 +80,6 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
The default value is False.
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log: The logger instance to use. (Optional) (Deprecated)
logger: deprecated, use 'log' instead.
"""
@overload
@@ -90,11 +88,11 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
deployment_name: str,
endpoint: Union[HttpsUrl, str],
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[AsyncAzureADTokenProvider] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an AzureChatCompletion service.
@@ -117,8 +115,6 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
The default value is False.
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log: The logger instance to use. (Optional) (Deprecated)
logger: deprecated, use 'log' instead.
"""
@overload
@@ -126,7 +122,7 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
self,
deployment_name: str,
async_client: AsyncAzureOpenAI,
log: Optional[Any] = None,
service_id: Optional[str] = None,
) -> None:
"""
Initialize an AzureChatCompletion service.
@@ -138,7 +134,6 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure OpenAI Studio.
async_client {AsyncAzureOpenAI} -- An existing client to use.
log: The logger instance to use. (Optional) (Deprecated)
"""
@overload
@@ -147,11 +142,11 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
deployment_name: str,
endpoint: Union[HttpsUrl, str],
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[AsyncAzureADTokenProvider] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
use_extensions: bool = False,
) -> None:
"""
@@ -187,14 +182,13 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
endpoint: Optional[Union[HttpsUrl, str]] = None,
base_url: Optional[Union[HttpsUrl, str]] = 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[AsyncAzureADTokenProvider] = None,
default_headers: Optional[Mapping[str, str]] = None,
async_client: Optional[AsyncAzureOpenAI] = None,
use_extensions: bool = False,
log: Optional[Any] = None,
**kwargs,
) -> None:
"""
Initialize an AzureChatCompletion service.
@@ -223,18 +217,11 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
The default value is False.
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log: The logger instance to use. (Optional) (Deprecated)
logger: deprecated.
async_client {Optional[AsyncAzureOpenAI]} -- An existing client to use. (Optional)
use_extensions: Whether to use extensions, for example when chatting with data. (Optional)
When True, base_url is overwritten to '{endpoint}/openai/deployments/{deployment_name}/extensions'.
The default value is False.
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
if kwargs.get("logger"):
logger.warning("The 'logger' argument is deprecated. Please use the `logging` module instead.")
if base_url and isinstance(base_url, str):
base_url = HttpsUrl(base_url)
if use_extensions and endpoint and deployment_name:
@@ -244,6 +231,7 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
endpoint=endpoint if not isinstance(endpoint, str) else HttpsUrl(endpoint),
base_url=base_url,
api_version=api_version,
service_id=service_id,
api_key=api_key,
ad_token=ad_token,
ad_token_provider=ad_token_provider,
@@ -267,6 +255,7 @@ class AzureChatCompletion(AzureOpenAIConfigBase, OpenAIChatCompletionBase, OpenA
endpoint=settings.get("endpoint"),
base_url=settings.get("base_url"),
api_version=settings.get("api_version", DEFAULT_AZURE_API_VERSION),
service_id=settings.get("service_id"),
api_key=settings.get("api_key"),
ad_token=settings.get("ad_token"),
ad_token_provider=settings.get("ad_token_provider"),
@@ -2,7 +2,7 @@
import json
import logging
from typing import Any, Awaitable, Callable, Dict, Mapping, Optional, Union
from typing import Awaitable, Callable, Dict, Mapping, Optional, Union
from openai import AsyncAzureOpenAI
from pydantic import validate_call
@@ -33,11 +33,11 @@ class AzureOpenAIConfigBase(OpenAIHandler):
endpoint: Optional[HttpsUrl] = None,
base_url: Optional[HttpsUrl] = 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,
log: Optional[Any] = None,
async_client: Optional[AsyncAzureOpenAI] = None,
) -> None:
"""Internal class for configuring a connection to an Azure OpenAI service.
@@ -53,14 +53,11 @@ class AzureOpenAIConfigBase(OpenAIHandler):
ad_token_provider {Optional[Callable[[], Union[str, Awaitable[str]]]]} -- A callable
or coroutine function providing Azure AD tokens. (Optional)
default_headers {Union[Mapping[str, str], None]} -- Default headers for HTTP requests. (Optional)
log -- Logger instance for logging purposes. (Optional) (Deprecated)
async_client {Optional[AsyncAzureOpenAI]} -- An existing client to use. (Optional)
The `validate_call` decorator is used with a configuration that allows arbitrary types.
This is necessary for types like `HttpsUrl` and `OpenAIModelTypes`.
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
# Merge APP_INFO into the headers if it exists
merged_headers = default_headers.copy() if default_headers else {}
if APP_INFO:
@@ -96,12 +93,14 @@ class AzureOpenAIConfigBase(OpenAIHandler):
azure_ad_token_provider=ad_token_provider,
default_headers=merged_headers,
)
super().__init__(
ai_model_id=deployment_name,
client=async_client,
ai_model_type=ai_model_type,
)
args = {
"ai_model_id": deployment_name,
"client": async_client,
"ai_model_type": ai_model_type,
}
if service_id:
args["service_id"] = service_id
super().__init__(**args)
def to_dict(self) -> Dict[str, str]:
client_settings = {
@@ -120,6 +119,7 @@ class AzureOpenAIConfigBase(OpenAIHandler):
"api_type",
"org_id",
"ai_model_type",
"service_id",
"client",
},
by_alias=True,
@@ -28,11 +28,11 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
self,
base_url: str,
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[AsyncAzureADTokenProvider] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an AzureTextCompletion service.
@@ -55,8 +55,6 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
The default value is False.
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log: The logger instance to use. (Optional) (Deprecated)
logger: deprecated.
"""
@overload
@@ -65,6 +63,7 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
deployment_name: str,
endpoint: str,
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[AsyncAzureADTokenProvider] = None,
@@ -92,8 +91,6 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
The default value is False.
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log: The logger instance to use. (Optional) (Deprecated)
logger: deprecated, use 'log' instead.
"""
@overload
@@ -101,7 +98,7 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
self,
deployment_name: str,
async_client: AsyncAzureOpenAI,
log: Optional[Any] = None,
service_id: Optional[str] = None,
) -> None:
"""
Initialize an AzureChatCompletion service.
@@ -113,7 +110,6 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure OpenAI Studio.
async_client {AsyncAzureOpenAI} -- An existing client to use.
log: The logger instance to use. (Optional) (Deprecated)
"""
def __init__(
@@ -122,13 +118,12 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
endpoint: Optional[str] = None,
base_url: Optional[str] = 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[AsyncAzureADTokenProvider] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
async_client: Optional[AsyncAzureOpenAI] = None,
**kwargs,
) -> None:
"""
Initialize an AzureTextCompletion service.
@@ -151,19 +146,14 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
The default value is False.
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log: The logger instance to use. (Optional) (Deprecated)
logger: deprecated, use 'log' instead.
async_client {Optional[AsyncAzureOpenAI]} -- An existing client to use.
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
if kwargs.get("logger"):
logger.warning("The 'logger' argument is deprecated.")
super().__init__(
deployment_name=deployment_name,
endpoint=endpoint,
base_url=base_url,
api_version=api_version,
service_id=service_id,
api_key=api_key,
ad_token=ad_token,
ad_token_provider=ad_token_provider,
@@ -188,6 +178,7 @@ class AzureTextCompletion(AzureOpenAIConfigBase, OpenAITextCompletionBase):
endpoint=settings.get("endpoint"),
base_url=settings.get("base_url"),
api_version=settings.get("api_version", DEFAULT_AZURE_API_VERSION),
service_id=settings.get("service_id"),
api_key=settings["api_key"],
ad_token=settings.get("ad_token"),
ad_token_provider=settings.get("ad_token_provider"),
@@ -2,7 +2,7 @@
import logging
from typing import Any, Dict, Mapping, Optional, overload
from typing import Dict, Mapping, Optional, overload
from openai import AsyncAzureOpenAI
from openai.lib.azure import AsyncAzureADTokenProvider
@@ -29,7 +29,7 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
self,
deployment_name: str,
async_client: AsyncAzureOpenAI,
log: Optional[Any] = None,
service_id: Optional[str] = None,
) -> None:
"""
Initialize an AzureChatCompletion service.
@@ -41,7 +41,6 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure OpenAI Studio.
async_client {AsyncAzureOpenAI} -- An existing client to use.
log: The logger instance to use. (Optional) (Deprecated)
"""
def __init__(
@@ -49,13 +48,12 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
deployment_name: str,
endpoint: Optional[str] = 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[AsyncAzureADTokenProvider] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
async_client: Optional[AsyncAzureOpenAI] = None,
**kwargs,
) -> None:
"""
Initialize an AzureTextEmbedding service.
@@ -81,19 +79,14 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
(Optional) The default value is False.
:param default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
:param log: The logger instance to use. (Optional) (Deprecated)
:param logger: Deprecated, please use log instead. (Optional)
:param async_client: An existing client to use. (Optional)
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
if kwargs.get("logger"):
logger.warning("The 'logger' argument is deprecated.")
super().__init__(
deployment_name=deployment_name,
endpoint=endpoint,
api_version=api_version,
service_id=service_id,
api_key=api_key,
ad_token=ad_token,
ad_token_provider=ad_token_provider,
@@ -117,6 +110,7 @@ class AzureTextEmbedding(AzureOpenAIConfigBase, OpenAITextEmbeddingBase):
endpoint=settings["endpoint"],
api_key=settings["api_key"],
api_version=settings.get("api_version", DEFAULT_AZURE_API_VERSION),
service_id=settings.get("service_id"),
ad_token=settings.get("ad_token"),
ad_token_provider=settings.get("ad_token_provider"),
default_headers=settings.get("default_headers"),
@@ -2,7 +2,6 @@
import logging
from typing import (
Any,
Dict,
Mapping,
Optional,
@@ -31,7 +30,7 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
self,
ai_model_id: str,
async_client: AsyncOpenAI,
log: Optional[Any] = None,
service_id: Optional[str] = None,
) -> None:
"""
Initialize an OpenAIChatCompletion service.
@@ -40,7 +39,6 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
ai_model_id {str} -- OpenAI model name, see
https://platform.openai.com/docs/models
async_client {AsyncOpenAI} -- An existing client to use.
log: The logger instance to use. (Optional) (Deprecated)
"""
@overload
@@ -49,8 +47,8 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
ai_model_id: str,
api_key: Optional[str] = None,
org_id: Optional[str] = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an OpenAIChatCompletion service.
@@ -65,7 +63,6 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
account belongs to multiple organizations.
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log -- The logger instance to use. (Optional) (Deprecated)
"""
@overload
@@ -73,8 +70,8 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
self,
ai_model_id: str,
api_key: Optional[str] = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an OpenAIChatCompletion service.
@@ -86,7 +83,6 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
https://platform.openai.com/account/api-keys
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
log -- The logger instance to use. (Optional) (Deprecated)
"""
def __init__(
@@ -94,9 +90,9 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
ai_model_id: str,
api_key: Optional[str] = None,
org_id: Optional[str] = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
async_client: Optional[AsyncOpenAI] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an OpenAIChatCompletion service.
@@ -112,14 +108,12 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
default_headers: The default headers mapping of string keys to
string values for HTTP requests. (Optional)
async_client {Optional[AsyncOpenAI]} -- An existing client to use. (Optional)
log -- The logger instance to use. (Optional) (Deprecated)
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
super().__init__(
ai_model_id=ai_model_id,
api_key=api_key,
org_id=org_id,
service_id=service_id,
ai_model_type=OpenAIModelTypes.CHAT,
default_headers=default_headers,
async_client=async_client,
@@ -138,5 +132,6 @@ class OpenAIChatCompletion(OpenAIConfigBase, OpenAIChatCompletionBase, OpenAITex
ai_model_id=settings["ai_model_id"],
api_key=settings["api_key"],
org_id=settings.get("org_id"),
service_id=settings.get("service_id"),
default_headers=settings.get("default_headers"),
)
@@ -6,6 +6,7 @@ from typing import (
Dict,
List,
Optional,
Type,
Union,
)
@@ -18,16 +19,18 @@ from semantic_kernel.connectors.ai.chat_completion_client_base import (
ChatCompletionClientBase,
)
from semantic_kernel.connectors.ai.open_ai.contents import OpenAIChatMessageContent, OpenAIStreamingChatMessageContent
from semantic_kernel.connectors.ai.open_ai.models.chat.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat.tool_calls import ToolCall
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.tool_calls import ToolCall
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (
OpenAIChatPromptExecutionSettings,
OpenAIPromptExecutionSettings,
)
from semantic_kernel.connectors.ai.open_ai.services.open_ai_handler import OpenAIHandler
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.models.chat.chat_role import ChatRole
from semantic_kernel.models.chat.finish_reason import FinishReason
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.chat_role import ChatRole
from semantic_kernel.contents.finish_reason import FinishReason
logger: logging.Logger = logging.getLogger(__name__)
@@ -39,24 +42,26 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
"""Create a request settings object."""
return OpenAIChatPromptExecutionSettings
def get_chat_message_content_class(self) -> Type[ChatMessageContent]:
"""Get the chat message content types used by a class, default is ChatMessageContent."""
return OpenAIChatMessageContent
async def complete_chat(
self,
messages: List[Dict[str, str]],
chat_history: ChatHistory,
settings: OpenAIPromptExecutionSettings,
**kwargs,
) -> List[OpenAIChatMessageContent]:
"""Executes a chat completion request and returns the result.
Arguments:
messages {List[Dict[str,str]]} -- The messages to use for the chat completion.
chat_history {ChatHistory} -- The chat history to use for the chat completion.
settings {OpenAIChatPromptExecutionSettings | AzureChatPromptExecutionSettings} -- The settings to use
for the chat completion request.
Returns:
List[OpenAIChatMessageContent | AzureChatMessageContent] -- The completion result(s).
"""
# TODO: replace messages with ChatHistory object with ChatMessageContent objects
settings.messages = messages
settings.messages = self._prepare_chat_history_for_request(chat_history)
settings.stream = False
if not settings.ai_model_id:
settings.ai_model_id = self.ai_model_id
@@ -66,14 +71,13 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
async def complete_chat_stream(
self,
messages: List[Dict[str, str]],
chat_history: ChatHistory,
settings: OpenAIPromptExecutionSettings,
**kwargs,
) -> AsyncIterable[List[OpenAIStreamingChatMessageContent]]:
"""Executes a streaming chat completion request and returns the result.
Arguments:
messages {List[Tuple[str,str]]} -- The messages to use for the chat completion.
chat_history {ChatHistory} -- The chat history to use for the chat completion.
settings {OpenAIChatPromptExecutionSettings | AzureChatPromptExecutionSettings} -- The settings to use
for the chat completion request.
@@ -81,7 +85,7 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
List[OpenAIStreamingChatMessageContent | AzureStreamingChatMessageContent] -- A stream of
OpenAIStreamingChatMessages or AzureStreamingChatMessageContent when using Azure.
"""
settings.messages = messages
settings.messages = self._prepare_chat_history_for_request(chat_history)
settings.stream = True
if not settings.ai_model_id:
settings.ai_model_id = self.ai_model_id
@@ -131,9 +135,9 @@ class OpenAIChatCompletionBase(OpenAIHandler, ChatCompletionClientBase):
inner_content=chunk,
ai_model_id=self.ai_model_id,
metadata=metadata,
role=ChatRole(choice.delta.role),
role=ChatRole(choice.delta.role) if choice.delta.role else None,
content=choice.delta.content,
finish_reason=FinishReason(choice.finish_reason),
finish_reason=FinishReason(choice.finish_reason) if choice.finish_reason else None,
function_call=self._get_function_call_from_chat_choice(choice),
tool_calls=self._get_tool_calls_from_chat_choice(choice),
)
@@ -2,7 +2,7 @@
import json
import logging
from typing import Any, Dict, Mapping, Optional
from typing import Dict, Mapping, Optional
from openai import AsyncOpenAI
from pydantic import Field, validate_call
@@ -30,9 +30,9 @@ class OpenAIConfigBase(OpenAIHandler):
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,
log: Optional[Any] = None,
) -> None:
"""Initialize a client for OpenAI services.
@@ -52,8 +52,6 @@ class OpenAIConfigBase(OpenAIHandler):
for HTTP requests. (Optional)
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
# Merge APP_INFO into the headers if it exists
merged_headers = default_headers.copy() if default_headers else {}
if APP_INFO:
@@ -70,12 +68,14 @@ class OpenAIConfigBase(OpenAIHandler):
organization=org_id,
default_headers=merged_headers,
)
super().__init__(
ai_model_id=ai_model_id,
client=async_client,
ai_model_type=ai_model_type,
)
args = {
"ai_model_id": ai_model_id,
"client": async_client,
"ai_model_type": ai_model_type,
}
if service_id:
args["service_id"] = service_id
super().__init__(**args)
def to_dict(self) -> Dict[str, str]:
"""
@@ -94,6 +94,7 @@ class OpenAIConfigBase(OpenAIHandler):
"total_tokens",
"api_type",
"ai_model_type",
"service_id",
"client",
},
by_alias=True,
@@ -8,10 +8,8 @@ from numpy import array, ndarray
from openai import AsyncOpenAI, AsyncStream, BadRequestError
from openai.types import Completion
from openai.types.chat import ChatCompletion, ChatCompletionChunk
from pydantic import Field
from semantic_kernel.connectors.ai.ai_exception import AIException
from semantic_kernel.connectors.ai.ai_service_client_base import AIServiceClientBase
from semantic_kernel.connectors.ai.open_ai.exceptions.content_filter_ai_exception import (
ContentFilterAIException,
)
@@ -22,19 +20,19 @@ from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_pro
from semantic_kernel.connectors.ai.open_ai.services.open_ai_model_types import (
OpenAIModelTypes,
)
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.kernel_pydantic import KernelBaseModel
logger: logging.Logger = logging.getLogger(__name__)
class OpenAIHandler(AIServiceClientBase, ABC):
class OpenAIHandler(KernelBaseModel, ABC):
"""Internal class for calls to OpenAI API's."""
client: AsyncOpenAI
ai_model_type: OpenAIModelTypes = OpenAIModelTypes.CHAT
prompt_tokens: int = Field(0, init_var=False)
completion_tokens: int = Field(0, init_var=False)
total_tokens: int = Field(0, init_var=False)
prompt_tokens: int = 0
completion_tokens: int = 0
total_tokens: int = 0
async def _send_request(
self,
@@ -100,7 +98,3 @@ class OpenAIHandler(AIServiceClientBase, ABC):
self.total_tokens += response.usage.total_tokens
if hasattr(response.usage, "completion_tokens"):
self.completion_tokens += response.usage.completion_tokens
def get_prompt_execution_settings_class(self) -> "PromptExecutionSettings":
"""Return the class with the applicable request settings."""
return OpenAIPromptExecutionSettings
@@ -2,7 +2,7 @@
import json
import logging
from typing import Any, Dict, Mapping, Optional, overload
from typing import Dict, Mapping, Optional, overload
from openai import AsyncOpenAI
@@ -27,7 +27,7 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
self,
ai_model_id: str,
async_client: AsyncOpenAI,
log: Optional[Any] = None,
service_id: Optional[str] = None,
) -> None:
"""
Initialize an OpenAITextCompletion service.
@@ -44,8 +44,8 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
ai_model_id: str,
api_key: Optional[str] = None,
org_id: Optional[str] = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an OpenAITextCompletion service.
@@ -67,8 +67,8 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
self,
ai_model_id: str,
api_key: Optional[str] = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
) -> None:
"""
Initialize an OpenAITextCompletion service.
@@ -87,8 +87,8 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
ai_model_id: str,
api_key: Optional[str] = None,
org_id: Optional[str] = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
log: Optional[Any] = None,
async_client: Optional[AsyncOpenAI] = None,
) -> None:
"""
@@ -106,12 +106,11 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
string values for HTTP requests. (Optional)
async_client {Optional[AsyncOpenAI]} -- An existing client to use. (Optional)
"""
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
super().__init__(
ai_model_id=ai_model_id,
api_key=api_key,
org_id=org_id,
service_id=service_id,
ai_model_type=OpenAIModelTypes.TEXT,
default_headers=default_headers,
async_client=async_client,
@@ -131,5 +130,6 @@ class OpenAITextCompletion(OpenAITextCompletionBase, OpenAIConfigBase):
ai_model_id=settings["ai_model_id"],
api_key=settings["api_key"],
org_id=settings.get("org_id"),
service_id=settings.get("service_id"),
default_headers=settings.get("default_headers"),
)
@@ -16,7 +16,7 @@ from semantic_kernel.connectors.ai.open_ai.services.open_ai_handler import (
OpenAIHandler,
)
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.models.contents import StreamingTextContent, TextContent
from semantic_kernel.contents import StreamingTextContent, TextContent
if TYPE_CHECKING:
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (
@@ -35,7 +35,6 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
self,
prompt: str,
settings: "OpenAIPromptExecutionSettings",
**kwargs,
) -> List["TextContent"]:
"""Executes a completion request and returns the result.
@@ -77,7 +76,6 @@ class OpenAITextCompletionBase(OpenAIHandler, TextCompletionClientBase):
self,
prompt: str,
settings: "OpenAIPromptExecutionSettings",
**kwargs,
) -> AsyncIterable[List["StreamingTextContent"]]:
"""
Executes a completion request and streams the result.
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import logging
from typing import Any, Dict, Mapping, Optional, overload
from typing import Dict, Mapping, Optional, overload
from openai import AsyncOpenAI
@@ -26,7 +26,7 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
self,
ai_model_id: str,
async_client: AsyncOpenAI,
log: Optional[Any] = None,
service_id: Optional[str] = None,
) -> None:
"""
Initialize an OpenAITextEmbedding service.
@@ -42,9 +42,9 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
ai_model_id: str,
api_key: Optional[str] = None,
org_id: Optional[str] = None,
service_id: Optional[str] = None,
default_headers: Optional[Mapping[str, str]] = None,
async_client: Optional[AsyncOpenAI] = None,
log: Optional[Any] = None,
) -> None:
"""
Initializes a new instance of the OpenAITextCompletion class.
@@ -66,11 +66,10 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
api_key=api_key,
ai_model_type=OpenAIModelTypes.EMBEDDING,
org_id=org_id,
service_id=service_id,
default_headers=default_headers,
async_client=async_client,
)
if log:
logger.warning("The `log` parameter is deprecated. Please use the `logging` module instead.")
@classmethod
def from_dict(cls, settings: Dict[str, str]) -> "OpenAITextEmbedding":
@@ -85,5 +84,6 @@ class OpenAITextEmbedding(OpenAIConfigBase, OpenAITextEmbeddingBase):
ai_model_id=settings["ai_model_id"],
api_key=settings["api_key"],
org_id=settings.get("org_id"),
service_id=settings.get("service_id"),
default_headers=settings.get("default_headers"),
)
@@ -2,28 +2,39 @@
import json
import logging
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Tuple, Union
from openai.types.chat import ChatCompletion
from semantic_kernel import Kernel, KernelContext
from semantic_kernel.connectors.ai.open_ai.models.chat.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat.tool_calls import ToolCall
from semantic_kernel.connectors.ai.open_ai.semantic_functions.open_ai_chat_prompt_template import (
OpenAIChatPromptTemplate,
)
from semantic_kernel.orchestration.context_variables import ContextVariables
from semantic_kernel.orchestration.kernel_function import KernelFunction
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.function_call import FunctionCall
from semantic_kernel.connectors.ai.open_ai.models.chat_completion.tool_calls import ToolCall
from semantic_kernel.connectors.ai.text_completion_client_base import TextCompletionClientBase
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.function_result import FunctionResult
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.functions.kernel_function import KernelFunction
logger: logging.Logger = logging.getLogger(__name__)
TYPE_MAPPER = {
"str": "string",
"int": "number",
"float": "number",
"bool": "boolean",
"list": "array",
"dict": "object",
}
def _describe_tool_call(function: KernelFunction) -> Dict[str, str]:
"""Create the object used for the tool call.
Assumes that arguments for semantic functions are optional, for native functions required.
"""
func_view = function.describe()
func_view = function.metadata
return {
"type": "function",
"function": {
@@ -34,29 +45,41 @@ def _describe_tool_call(function: KernelFunction) -> Dict[str, str]:
"properties": {
param.name: {
"description": param.description,
"type": param.type_ if param.type_ else "string",
"type": parse_param(param.type_),
**({"enum": param.enum} if hasattr(param, "enum") else {}), # Added support for enum
}
for param in func_view.parameters
if param.expose
},
"required": [p.name for p in func_view.parameters if p.required],
"required": [p.name for p in func_view.parameters if p.required and p.expose],
},
},
}
def parse_param(param_type: Optional[str]) -> str:
"""Parse the parameter type."""
if not param_type:
return "string"
if "," in param_type:
param_type = param_type.split(",", maxsplit=1)[0]
return TYPE_MAPPER.get(param_type, "string")
def _describe_function(function: KernelFunction) -> Dict[str, str]:
"""Create the object used for function_calling.
Assumes that arguments for semantic functions are optional, for native functions required.
"""
func_view = function.describe()
func_view = function.metadata
return {
"name": f"{func_view.plugin_name}-{func_view.name}",
"description": func_view.description,
"parameters": {
"type": "object",
"properties": {
param.name: {"description": param.description, "type": param.type_} for param in func_view.parameters
param.name: {"description": param.description, "type": param.type_}
for param in func_view.parameters
if param.expose
},
"required": [p.name for p in func_view.parameters if p.required],
},
@@ -132,13 +155,13 @@ def get_function_calling_object(
if include_function and exclude_function:
raise ValueError("Cannot use both include_function and exclude_function at the same time.")
if include_plugin:
include_plugin = [plugin.lower() for plugin in include_plugin]
include_plugin = [plugin for plugin in include_plugin]
if exclude_plugin:
exclude_plugin = [plugin.lower() for plugin in exclude_plugin]
exclude_plugin = [plugin for plugin in exclude_plugin]
if include_function:
include_function = [function.lower() for function in include_function]
include_function = [function for function in include_function]
if exclude_function:
exclude_function = [function.lower() for function in exclude_function]
exclude_function = [function for function in exclude_function]
result = []
for (
plugin_name,
@@ -154,44 +177,57 @@ def get_function_calling_object(
return result
async def execute(kernel: Kernel, func: KernelFunction, input_vars: ContextVariables) -> str:
async def execute(kernel: Kernel, func: KernelFunction, arguments: KernelArguments) -> FunctionResult:
"""Execute a function and return the result.
Args:
kernel (Kernel): the kernel to use.
func (KernelFunction): the function to execute.
input_vars (ContextVariables): the input variables.
kernel: the kernel to use.
func: the function to execute.
arguments: the arguments to pass to the function.
Returns:
str: the result of the execution.
"""
result = await kernel.run(func, input_vars=input_vars)
result = await kernel.invoke(functions=func, arguments=arguments)
logger.info(f"Execution result: {result}")
return str(result)
return result
async def execute_function_call(kernel: Kernel, function_call: FunctionCall) -> str:
async def execute_function_call(kernel: Kernel, function_call: FunctionCall) -> FunctionResult:
"""Execute a function call and return the result."""
func = kernel.func(**function_call.split_name_dict())
input_vars = function_call.to_context_variables()
return await execute(kernel, func, input_vars)
arguments = function_call.to_kernel_arguments()
return await execute(kernel, func, arguments)
async def execute_tool_call(kernel: Kernel, tool_call: ToolCall) -> str:
async def execute_and_store_tool_call(
kernel: Kernel,
tool_call: ToolCall,
chat_function: KernelFunction,
):
"""Execute a tool call and store the result in the chat prompt template."""
tool_call_result = await execute_tool_call(kernel, tool_call)
chat_function.chat_prompt_template.add_function_response_message(
name=tool_call.function.name, content=str(tool_call_result), tool_call_id=tool_call.id
)
async def execute_tool_call(kernel: Kernel, tool_call: ToolCall) -> FunctionResult:
"""Execute a tool call and return the result."""
func = kernel.func(**tool_call.function.split_name_dict())
input_vars = tool_call.function.to_context_variables()
return await execute(kernel, func, input_vars)
arguments = tool_call.function.to_kernel_arguments()
return await execute(kernel, func, arguments)
async def chat_completion_with_function_call(
async def chat_completion_with_tool_call(
kernel: Kernel,
context: KernelContext,
arguments: Optional[KernelArguments] = None,
chat_plugin_name: Optional[str] = None,
chat_function_name: Optional[str] = None,
chat_function: Optional[KernelFunction] = None,
chat_history: Optional[ChatHistory] = None,
**kwargs: Dict[str, Any],
) -> KernelContext:
) -> FunctionResult:
"""Perform a chat completion with auto-executing function calling.
This is a recursive function that will execute the chat function multiple times,
@@ -204,44 +240,55 @@ async def chat_completion_with_function_call(
args:
kernel: the kernel to use.
context: the context to use.
functions: the function calling object,
make sure to use get_tool_call_object method to create it.
Optional arguments:
chat_plugin_name: the plugin name of the chat function.
chat_function_name: the function name of the chat function.
chat_function: the chat function, if not provided, it will be retrieved from the kernel.
make sure to provide either the chat_function or the chat_plugin_name and chat_function_name.
arguments: the arguments to pass to the chat function.
Optional arguments:
chat_plugin_name: the plugin name of the chat function.
chat_function_name: the function name of the chat function.
chat_function: the chat function, if not provided, it will be retrieved from the kernel.
make sure to provide either the chat_function or the chat_plugin_name and chat_function_name.
chat_history: the chat history to use, if not provided, will attempt to retrieve from arguments
with key "chat_history".
max_function_calls: the maximum number of function calls to execute, defaults to 5.
current_call_count: the current number of function calls executed.
max_function_calls: the maximum number of function calls to execute, defaults to 5.
current_call_count: the current number of function calls executed.
returns:
the context with the result of the chat completion, just like a regular invoke/run_async.
the FunctionResult with the result of the chat completion, just like a regular invoke/run_async.
"""
chat_history or arguments["chat_history"]
# check the number of function calls
max_function_calls = kwargs.get("max_function_calls", 5)
current_call_count = kwargs.get("current_call_count", 0)
# get the chat function
if chat_function is None:
if chat_plugin_name is None or chat_function_name is None:
raise ValueError("Please provide either the chat_function or the chat_plugin_name and chat_function_name.")
chat_function = kernel.func(plugin_name=chat_plugin_name, function_name=chat_function_name)
assert isinstance(
chat_function.chat_prompt_template, OpenAIChatPromptTemplate
assert issubclass(
type(chat_function.ai_service), Union[ChatCompletionClientBase, TextCompletionClientBase]
), "Please make sure to initialize your chat function with the OpenAIChatPromptTemplate class."
settings = chat_function.chat_prompt_template.prompt_config.execution_settings
settings = chat_function.prompt_execution_settings[chat_function.ai_service.service_id]
if not arguments:
arguments = KernelArguments()
arguments.execution_settings[settings.service_id] = settings
arguments["user_input"] = ("\n").join([f"{msg.role}: {msg.content}" for msg in chat_history])
if current_call_count >= max_function_calls:
settings.functions = []
context = await chat_function.invoke(
context=context,
result = await chat_function.invoke(
kernel=kernel,
arguments=arguments,
# when the maximum number of function calls is reached, execute the chat function without Functions.
settings=settings,
)
if not (results := context.objects.pop("results", None)):
return context
if not isinstance(result, FunctionResult) and result.value[0].tool_call is None:
return result
function_call = next(
(
fc
for fc in (results[0].function_call or results[0].tool_calls or [None])
for fc in (result.value[0].function_call or result.value[0].tool_calls or [None])
if isinstance(fc, (FunctionCall, ToolCall))
),
None,
@@ -251,16 +298,17 @@ async def chat_completion_with_function_call(
result = await execute_call(kernel, function_call)
tool_call_id = function_call.id
else:
return context
return result
# add the result to the chat prompt template
chat_history.add_tool_message()
chat_function.chat_prompt_template.add_function_response_message(
name=function_call.function.name, content=str(result), tool_call_id=tool_call_id
)
# request another completion
return await chat_completion_with_function_call(
return await chat_completion_with_tool_call(
kernel,
arguments=arguments,
chat_function=chat_function,
context=context,
max_function_calls=max_function_calls,
current_call_count=current_call_count + 1,
)
@@ -14,7 +14,7 @@ class PromptExecutionSettings(KernelBaseModel):
prompt execution settings that each services returns by using the service.get_prompt_execution_settings() method.
Parameters:
service_id (str, optional): The service ID to use for the request. Defaults to None.
service_id (str): The service ID to use for the request.
extension_data (Dict[str, Any], optional): Any additional data to send with the request. Defaults to None.
kwargs (Any): Additional keyword arguments,
these are attempted to parse into the keys of the specific prompt execution settings.
@@ -2,14 +2,16 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, AsyncIterable, List, Optional
from typing import TYPE_CHECKING, AsyncIterable, List
from semantic_kernel.services.ai_service_client_base import AIServiceClientBase
if TYPE_CHECKING:
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.models.contents import StreamingTextContent, TextContent
from semantic_kernel.contents import StreamingTextContent, TextContent
class TextCompletionClientBase(ABC):
class TextCompletionClientBase(AIServiceClientBase, ABC):
"""Base class for text completion AI services."""
@abstractmethod
@@ -17,7 +19,6 @@ class TextCompletionClientBase(ABC):
self,
prompt: str,
settings: "PromptExecutionSettings",
logger: Optional[Any] = None,
) -> List["TextContent"]:
"""
This is the method that is called from the kernel to get a response from a text-optimized LLM.
@@ -25,7 +26,6 @@ class TextCompletionClientBase(ABC):
Arguments:
prompt {str} -- The prompt to send to the LLM.
settings {PromptExecutionSettings} -- Settings for the request.
logger {Logger} -- A logger to use for logging (deprecated).
Returns:
Union[str, List[str]] -- A string or list of strings representing the response(s) from the LLM.
@@ -36,7 +36,6 @@ class TextCompletionClientBase(ABC):
self,
prompt: str,
settings: "PromptExecutionSettings",
logger: Optional[Any] = None,
) -> AsyncIterable[List["StreamingTextContent"]]:
"""
This is the method that is called from the kernel to get a stream response from a text-optimized LLM.
@@ -44,7 +43,6 @@ class TextCompletionClientBase(ABC):
Arguments:
prompt {str} -- The prompt to send to the LLM.
settings {PromptExecutionSettings} -- Settings for the request.
logger {Logger} -- A logger to use for logging (deprecated).
Yields:
A stream representing the response(s) from the LLM.
@@ -1,6 +1,6 @@
# semantic_kernel.connectors.memory.redis
This connector uses Redis to implement Semantic Memory. It requires the [RediSearch](https://redis.io/docs/interact/search-and-query/) module to be enabled on Redis to implement vector similarity search.
This connector uses Redis to implement Semantic Memory. It requires the [RediSearch](https://redis.io/docs/interact/search-and-query/) module to be enabled on Redis to implement vector similarity search.
See the [.net README](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Connectors/Connectors.Memory.Redis/README.md) for more information.
@@ -22,10 +22,10 @@ docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:la
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.add_text_completion_service("dv", sk_oai.OpenAITextCompletion("text-davinci-003", api_key, org_id))
kernel.add_text_embedding_generation_service("ada", sk_oai.OpenAITextEmbedding("text-embedding-ada-002", api_key, org_id))
kernel.add_service(sk_oai.OpenAITextCompletion(service_id="dv", ai_model_id="text-davinci-003", api_key=api_key, org_id=org_id))
embedding_generator = sk_oai.OpenAITextEmbedding(service_id="ada", ai_model_id="text-embedding-ada-002", api_key=api_key, org_id=org_id)
kernel.add_service(embedding_generator)
redis_connection_string = sk.redis_settings_from_dot_env()
kernel.register_memory_store(memory_store=RedisMemoryStore(connection_string=redis_connection_string))
kernel.use_memory(storage=RedisMemoryStore(connection_string=redis_connection_string), embeddings_generator=embedding_generator)
```
@@ -1,6 +1,12 @@
import json
import logging
import sys
from typing import Dict, Mapping, Optional, Union
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
from urllib.parse import urljoin
import aiohttp
@@ -10,13 +16,13 @@ from openapi_core.contrib.requests import RequestsOpenAPIRequest
from openapi_core.exceptions import OpenAPIError
from prance import ResolvingParser
from semantic_kernel import Kernel, KernelContext
from semantic_kernel.connectors.ai.open_ai.const import (
USER_AGENT,
)
from semantic_kernel.connectors.telemetry import HTTP_USER_AGENT
from semantic_kernel.orchestration.kernel_function import KernelFunction
from semantic_kernel.plugin_definition import kernel_function, kernel_function_context_parameter
from semantic_kernel.functions.kernel_function import KernelFunction
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel import Kernel
logger: logging.Logger = logging.getLogger(__name__)
@@ -216,7 +222,7 @@ class OpenApiRunner:
query_params: Optional[Dict[str, str]] = None,
headers: Optional[Dict[str, str]] = None,
request_body: Optional[Union[str, Dict[str, str]]] = None,
) -> aiohttp.ClientResponse:
) -> str:
prepared_request = operation.prepare_request(
path_params=path_params,
query_params=query_params,
@@ -264,22 +270,30 @@ def register_openapi_plugin(
description=operation.summary if operation.summary else operation.description,
name=operation_id,
)
@kernel_function_context_parameter(name="path_params", description="A dictionary of path parameters")
@kernel_function_context_parameter(name="query_params", description="A dictionary of query parameters")
@kernel_function_context_parameter(name="headers", description="A dictionary of headers")
@kernel_function_context_parameter(name="request_body", description="A dictionary of the request body")
async def run_openapi_operation(kernel_context: KernelContext) -> str:
path_params = kernel_context.variables.get("path_params")
query_params = kernel_context.variables.get("query_params")
headers = kernel_context.variables.get("headers")
request_body = kernel_context.variables.get("request_body")
async def run_openapi_operation(
path_params: Annotated[Optional[Union[Dict, str]], "A dictionary of path parameters"] = None,
query_params: Annotated[Optional[Union[Dict, str]], "A dictionary of query parameters"] = None,
headers: Annotated[Optional[Union[Dict, str]], "A dictionary of headers"] = None,
request_body: Annotated[Optional[Union[Dict, str]], "A dictionary of the request body"] = None,
) -> str:
response = await runner.run_operation(
operation,
path_params=json.loads(path_params) if path_params else None,
query_params=json.loads(query_params) if query_params else None,
headers=json.loads(headers) if headers else None,
request_body=json.loads(request_body) if request_body else None,
path_params=json.loads(path_params)
if isinstance(path_params, str)
else path_params
if path_params
else None,
query_params=json.loads(query_params)
if isinstance(query_params, str)
else query_params
if query_params
else None,
headers=json.loads(headers) if isinstance(headers, str) else headers if headers else None,
request_body=json.loads(request_body)
if isinstance(request_body, str)
else request_body
if request_body
else None,
)
return response
@@ -0,0 +1,16 @@
# Copyright (c) Microsoft. All rights reserved.
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.kernel_content import KernelContent
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.contents.streaming_kernel_content import StreamingKernelContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
__all__ = [
"ChatMessageContent",
"KernelContent",
"TextContent",
"StreamingKernelContent",
"StreamingChatMessageContent",
"StreamingTextContent",
]
@@ -0,0 +1,291 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import logging
import xml.etree.ElementTree as ET
from typing import Any, Dict, Final, Iterator, List, Optional, Tuple, Type, Union
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.chat_role import ChatRole
from semantic_kernel.kernel_pydantic import KernelBaseModel
logger = logging.getLogger(__name__)
ROOT_KEY_MESSAGE: Final[str] = "message"
class ChatHistory(KernelBaseModel):
"""
This class holds the history of chat messages from a chat conversation.
Note: the constructor takes a system_message parameter, which is not part
of the class definition. This is to allow the system_message to be passed in
as a keyword argument, but not be part of the class definition.
Attributes:
messages (List[ChatMessageContent]): The list of chat messages in the history.
"""
messages: List[ChatMessageContent]
def __init__(self, **data: Any):
"""
Initializes a new instance of the ChatHistory class, optionally incorporating a message and/or
a system message at the beginning of the chat history.
This constructor allows for flexible initialization with chat messages and an optional messages or a
system message. If both 'messages' (a list of ChatMessageContent instances) and 'system_message' are
provided, the 'system_message' is prepended to the list of messages, ensuring it appears as the first
message in the history. If only 'system_message' is provided without any 'messages', the chat history is
initialized with the 'system_message' as its first item. If 'messages' are provided without a
'system_message', the chat history is initialized with the provided messages as is.
Parameters:
- **data: Arbitrary keyword arguments. The constructor looks for two optional keys:
- 'messages': Optional[List[ChatMessageContent]], a list of chat messages to include in the history.
- 'system_message' Optional[str]: An optional string representing a system-generated message to be
included at the start of the chat history.
Note: The 'system_message' is not retained as part of the class's attributes; it's used during
initialization and then discarded. The rest of the keyword arguments are passed to the superclass
constructor and handled according to the Pydantic model's behavior.
"""
system_message_content = data.pop("system_message", None)
if system_message_content:
system_message = ChatMessageContent(role=ChatRole.SYSTEM, content=system_message_content)
if "messages" in data:
data["messages"] = [system_message] + data["messages"]
else:
data["messages"] = [system_message]
if "messages" not in data:
data["messages"] = []
super().__init__(**data)
def add_system_message(self, content: str) -> None:
"""Add a system message to the chat history."""
self.add_message(message=self._prepare_for_add(ChatRole.SYSTEM, content))
def add_user_message(self, content: str) -> None:
"""Add a user message to the chat history."""
self.add_message(message=self._prepare_for_add(ChatRole.USER, content))
def add_assistant_message(self, content: str) -> None:
"""Add an assistant message to the chat history."""
self.add_message(message=self._prepare_for_add(ChatRole.ASSISTANT, content))
def add_tool_message(self, content: str, metadata: Optional[Dict[str, Any]] = None) -> None:
"""Add a tool message to the chat history."""
self.add_message(message=self._prepare_for_add(ChatRole.TOOL, content), metadata=metadata)
def add_message(
self,
message: Union[ChatMessageContent, Dict[str, Any]],
encoding: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a message to the history.
This method accepts either a ChatMessageContent instance or a
dictionary with the necessary information to construct a ChatMessageContent instance.
Args:
message (Union[ChatMessageContent, dict]): The message to add, either as
a pre-constructed ChatMessageContent instance or a dictionary specifying 'role' and 'content'.
encoding (Optional[str]): The encoding of the message. Required if 'message' is a dict.
metadata (Optional[dict[str, Any]]): Any metadata to attach to the message. Required if 'message' is a dict.
"""
if isinstance(message, ChatMessageContent):
self.messages.append(message)
return
if "role" not in message:
raise ValueError(f"Dictionary must contain at least the role. Got: {message}")
if encoding:
message["encoding"] = encoding
if metadata:
message["metadata"] = metadata
self.messages.append(ChatMessageContent(**message))
def _prepare_for_add(self, role: ChatRole, content: str) -> Dict[str, str]:
"""Prepare a message to be added to the history."""
return {"role": role, "content": content}
def remove_message(self, message: ChatMessageContent) -> bool:
"""Remove a message from the history.
Args:
message (ChatMessageContent): The message to remove.
Returns:
bool: True if the message was removed, False if the message was not found.
"""
try:
self.messages.remove(message)
return True
except ValueError:
return False
def __len__(self) -> int:
"""Return the number of messages in the history."""
return len(self.messages)
def __getitem__(self, index: int) -> ChatMessageContent:
"""Get a message from the history using the [] operator.
Args:
index (int): The index of the message to get.
Returns:
ChatMessageContent: The message at the specified index.
"""
return self.messages[index]
def __contains__(self, item: ChatMessageContent) -> bool:
"""Check if a message is in the history.
Args:
item (ChatMessageContent): The message to check for.
Returns:
bool: True if the message is in the history, False otherwise.
"""
return item in self.messages
def __str__(self) -> str:
"""Return a string representation of the history."""
if not self.messages:
return ""
return "\n".join([msg.to_prompt(root_key=ROOT_KEY_MESSAGE) for msg in self.messages])
def __iter__(self) -> Iterator[ChatMessageContent]:
"""Return an iterator over the messages in the history."""
return iter(self.messages)
def __eq__(self, other: "ChatHistory") -> bool:
"""Check if two ChatHistory instances are equal."""
if not isinstance(other, ChatHistory):
return False
return self.messages == other.messages
@classmethod
def from_rendered_prompt(
cls, rendered_prompt: str, chat_message_content_type: Type[ChatMessageContent] = ChatMessageContent
) -> "ChatHistory":
"""
Create a ChatHistory instance from a rendered prompt.
Args:
rendered_prompt (str): The rendered prompt to convert to a ChatHistory instance.
Returns:
ChatHistory: The ChatHistory instance created from the rendered prompt.
"""
messages: List[chat_message_content_type] = []
result, remainder = cls._render_remaining(rendered_prompt, chat_message_content_type, True)
if result:
messages.append(result)
while remainder:
result, remainder = cls._render_remaining(remainder, chat_message_content_type)
if result:
messages.append(result)
return cls(messages=messages)
@staticmethod
def _render_remaining(
prompt: Optional[str],
chat_message_content_type: Type[ChatMessageContent] = ChatMessageContent,
first: bool = False,
) -> Tuple[Optional[ChatMessageContent], Optional[str]]:
"""Render the remaining messages in the history."""
if not prompt:
return None, None
prompt = prompt.strip()
start = prompt.find(f"<{ROOT_KEY_MESSAGE}")
end_tag = f"</{ROOT_KEY_MESSAGE}>"
single_item_end_tag = "/>"
end = prompt.find(end_tag)
end_of_tag = end + len(end_tag)
if end == -1:
end = prompt.find(single_item_end_tag)
end_of_tag = end + len(single_item_end_tag)
if start == -1 or end == -1:
return chat_message_content_type(role=ChatRole.SYSTEM if first else ChatRole.USER, content=prompt), None
if start > 0 and end > 0:
return (
chat_message_content_type(role=ChatRole.SYSTEM if first else ChatRole.USER, content=prompt[:start]),
prompt[start:],
)
try:
return chat_message_content_type.from_element(ET.fromstring(prompt[start:end_of_tag])), prompt[end_of_tag:]
except ET.ParseError:
logger.warning(f"Unable to parse prompt: {prompt[start:end_of_tag]}, returning as content")
return (
chat_message_content_type(
role=ChatRole.SYSTEM if first else ChatRole.USER, content=prompt[start:end_of_tag]
),
prompt[end_of_tag:],
)
def serialize(self) -> str:
"""
Serializes the ChatHistory instance to a JSON string.
Returns:
str: A JSON string representation of the ChatHistory instance.
Raises:
ValueError: If the ChatHistory instance cannot be serialized to JSON.
"""
try:
return self.model_dump_json(indent=4)
except TypeError as e:
raise ValueError(f"Unable to serialize ChatHistory to JSON: {e}")
@classmethod
def restore_chat_history(cls, chat_history_json: str) -> "ChatHistory":
"""
Restores a ChatHistory instance from a JSON string.
Args:
chat_history_json (str): The JSON string to deserialize
into a ChatHistory instance.
Returns:
ChatHistory: The deserialized ChatHistory instance.
Raises:
ValueError: If the JSON string is invalid or the deserialized data
fails validation.
"""
try:
return ChatHistory.model_validate_json(chat_history_json)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON format: {e}")
def store_chat_history_to_file(chat_history: "ChatHistory", file_path: str) -> None:
"""
Stores the serialized ChatHistory to a file.
Args:
chat_history (ChatHistory): The ChatHistory instance to serialize and store.
file_path (str): The path to the file where the serialized data will be stored.
"""
json_str = chat_history.serialize()
with open(file_path, "w") as file:
file.write(json_str)
def load_chat_history_from_file(file_path: str) -> "ChatHistory":
"""
Loads the ChatHistory from a file.
Args:
file_path (str): The path to the file from which to load the ChatHistory.
Returns:
ChatHistory: The deserialized ChatHistory instance.
"""
with open(file_path, "r") as file:
json_str = file.read()
return ChatHistory.restore_chat_history(json_str)
@@ -1,8 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Optional
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
from semantic_kernel.models.chat.chat_role import ChatRole
from semantic_kernel.models.contents.kernel_content import KernelContent
from semantic_kernel.contents.chat_role import ChatRole
from semantic_kernel.contents.kernel_content import KernelContent
class ChatMessageContent(KernelContent):
@@ -30,4 +32,29 @@ class ChatMessageContent(KernelContent):
encoding: Optional[str] = None
def __str__(self) -> str:
return self.content
return self.content or ""
def to_prompt(self, root_key: str) -> str:
"""Convert the ChatMessageContent to a prompt.
Returns:
str - The prompt from the ChatMessageContent.
"""
root = Element(root_key)
root.set("role", self.role.value)
root.text = self.content or ""
return ElementTree.tostring(root, encoding=self.encoding or "unicode")
@classmethod
def from_element(cls, element: Element) -> "ChatMessageContent":
"""Create a new instance of ChatMessageContent from a prompt.
Args:
prompt: str - The prompt to create the ChatMessageContent from.
Returns:
ChatMessageContent - The new instance of ChatMessageContent.
"""
args = {"role": element.get("role", ChatRole.USER.value), "content": element.text}
return cls(**args)
@@ -12,7 +12,7 @@ class KernelContent(KernelBaseModel, ABC):
inner_content: Optional[Any] = None
ai_model_id: Optional[str] = None
metadata: Dict[str, Any] = Field(default_factory=dict)
metadata: Optional[Dict[str, Any]] = Field(default_factory=dict)
@abstractmethod
def __str__(self) -> str:
@@ -1,9 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Optional
from semantic_kernel.models.chat.chat_role import ChatRole
from semantic_kernel.models.chat.finish_reason import FinishReason
from semantic_kernel.models.contents.streaming_kernel_content import StreamingKernelContent
from semantic_kernel.contents.chat_role import ChatRole
from semantic_kernel.contents.finish_reason import FinishReason
from semantic_kernel.contents.streaming_kernel_content import StreamingKernelContent
class StreamingChatMessageContent(StreamingKernelContent):
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Optional
from semantic_kernel.models.contents.streaming_kernel_content import StreamingKernelContent
from semantic_kernel.contents.streaming_kernel_content import StreamingKernelContent
class StreamingTextContent(StreamingKernelContent):
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Optional
from semantic_kernel.models.contents.kernel_content import KernelContent
from semantic_kernel.contents.kernel_content import KernelContent
class TextContent(KernelContent):
@@ -3,7 +3,6 @@
from semantic_kernel.core_plugins.conversation_summary_plugin import (
ConversationSummaryPlugin,
)
from semantic_kernel.core_plugins.file_io_plugin import FileIOPlugin
from semantic_kernel.core_plugins.http_plugin import HttpPlugin
from semantic_kernel.core_plugins.math_plugin import MathPlugin
from semantic_kernel.core_plugins.text_memory_plugin import TextMemoryPlugin
@@ -14,7 +13,6 @@ from semantic_kernel.core_plugins.web_search_engine_plugin import WebSearchEngin
__all__ = [
"TextMemoryPlugin",
"TextPlugin",
"FileIOPlugin",
"TimePlugin",
"HttpPlugin",
"ConversationSummaryPlugin",
@@ -1,9 +1,17 @@
# 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
if TYPE_CHECKING:
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel import Kernel
from semantic_kernel.orchestration.kernel_context import KernelContext
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
class ConversationSummaryPlugin:
@@ -11,14 +19,14 @@ class ConversationSummaryPlugin:
Semantic plugin that enables conversations summarization.
"""
from semantic_kernel.plugin_definition import kernel_function
from semantic_kernel.functions.kernel_function_decorator import kernel_function
# The max tokens to process in a single semantic function call.
_max_tokens = 1024
_summarize_conversation_prompt_template = (
"BEGIN CONTENT TO SUMMARIZE:\n{{"
+ "$INPUT"
+ "$input"
+ "}}\nEND CONTENT TO SUMMARIZE.\nSummarize the conversation in 'CONTENT TO"
" SUMMARIZE', identifying main points of discussion and any"
" conclusions that were reached.\nDo not incorporate other general"
@@ -26,28 +34,43 @@ class ConversationSummaryPlugin:
" or tags.\n\nBEGIN SUMMARY:\n"
)
def __init__(self, kernel: "Kernel"):
self._summarizeConversationFunction = kernel.create_semantic_function(
def __init__(
self, kernel: "Kernel", prompt_template_config: "PromptTemplateConfig", return_key: str = "summary"
) -> None:
"""
Initializes a new instance of the ConversationSummaryPlugin class.
:param kernel: The kernel instance.
:param prompt_template_config: The prompt template configuration.
:param return_key: The key to use for the return value.
"""
self.return_key = return_key
self._summarizeConversationFunction = kernel.create_function_from_prompt(
ConversationSummaryPlugin._summarize_conversation_prompt_template,
plugin_name=ConversationSummaryPlugin.__name__,
description=("Given a section of a conversation transcript, summarize the part of" " the conversation."),
max_tokens=ConversationSummaryPlugin._max_tokens,
temperature=0.1,
top_p=0.5,
function_name="SummarizeConversation",
prompt_template_config=prompt_template_config,
)
@kernel_function(
description="Given a long conversation transcript, summarize the conversation.",
name="SummarizeConversation",
input_description="A long conversation transcript.",
)
async def summarize_conversation(self, input: str, context: "KernelContext") -> "KernelContext":
async def summarize_conversation(
self,
input: Annotated[str, "A long conversation transcript."],
kernel: Annotated["Kernel", "The kernel instance."],
arguments: Annotated["KernelArguments", "Arguments used by the kernel."],
) -> Annotated[
"KernelArguments", "KernelArguments with the summarized conversation result in key self.return_key."
]:
"""
Given a long conversation transcript, summarize the conversation.
:param input: A long conversation transcript.
:param context: The KernelContext for function execution.
:return: KernelContext with the summarized conversation result.
:param kernel: The kernel for function execution.
:param arguments: Arguments used by the kernel.
:return: KernelArguments with the summarized conversation result in key self.return_key.
"""
from semantic_kernel.text import text_chunker
from semantic_kernel.text.function_extension import (
@@ -57,4 +80,7 @@ class ConversationSummaryPlugin:
lines = text_chunker._split_text_lines(input, ConversationSummaryPlugin._max_tokens, True)
paragraphs = text_chunker._split_text_paragraph(lines, ConversationSummaryPlugin._max_tokens)
return await aggregate_chunked_results(self._summarizeConversationFunction, paragraphs, context)
arguments[self.return_key] = await aggregate_chunked_results(
self._summarizeConversationFunction, paragraphs, kernel, arguments
)
return arguments
@@ -1,75 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import os
import typing as t
import aiofiles
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.plugin_definition import kernel_function, kernel_function_context_parameter
if t.TYPE_CHECKING:
from semantic_kernel.orchestration.kernel_context import KernelContext
class FileIOPlugin(KernelBaseModel):
"""
Description: Read and write from a file.
Usage:
kernel.import_plugin(FileIOPlugin(), plugin_name="file")
Examples:
{{file.readAsync $path }} => "hello world"
{{file.writeAsync}}
"""
@kernel_function(
description="Read a file",
name="readAsync",
input_description="Path of the source file",
)
async def read(self, path: str) -> str:
"""
Read a file
Example:
{{file.readAsync $path }} => "hello world"
Args:
path -- The path to the file to read
Returns:
The contents of the file
"""
assert os.path.exists(path), f"File {path} does not exist"
async with aiofiles.open(path, "r", encoding="UTF-8") as fp:
content = await fp.read()
return content
@kernel_function(
description="Write a file",
name="writeAsync",
)
@kernel_function_context_parameter(name="path", description="Destination path")
@kernel_function_context_parameter(name="content", description="File content")
async def write(self, context: "KernelContext") -> None:
"""
Write a file
Example:
{{file.writeAsync}}
Args:
Contains the 'path' for the Destination file and
the 'content' of the file to write.
"""
path = context.variables.get("path")
content = context.variables.get("content")
assert path, "Path is required"
assert content, "Content is required"
async with aiofiles.open(path, "w") as fp:
await fp.write(content)
@@ -1,15 +1,18 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import typing as t
import sys
from typing import Any, Dict, Optional
import aiohttp
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.plugin_definition import kernel_function, kernel_function_context_parameter
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
if t.TYPE_CHECKING:
from semantic_kernel.orchestration.kernel_context import KernelContext
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
class HttpPlugin(KernelBaseModel):
@@ -28,7 +31,7 @@ class HttpPlugin(KernelBaseModel):
"""
@kernel_function(description="Makes a GET request to a uri", name="getAsync")
async def get(self, url: str) -> str:
async def get(self, url: Annotated[str, "The URI to send the request to."]) -> str:
"""
Sends an HTTP GET request to the specified URI and returns
the response body as a string.
@@ -45,22 +48,23 @@ class HttpPlugin(KernelBaseModel):
return await response.text()
@kernel_function(description="Makes a POST request to a uri", name="postAsync")
@kernel_function_context_parameter(name="body", description="The body of the request")
async def post(self, url: str, context: "KernelContext") -> str:
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"] = {},
) -> str:
"""
Sends an HTTP POST request to the specified URI and returns
the response body as a string.
params:
url: The URI to send the request to.
context: Contains the body of the request
body: Contains the body of the request
returns:
The response body as a string.
"""
if not url:
raise ValueError("url cannot be `None` or empty")
body = context.variables.get("body")
headers = {"Content-Type": "application/json"}
data = json.dumps(body)
async with aiohttp.ClientSession() as session:
@@ -68,8 +72,11 @@ class HttpPlugin(KernelBaseModel):
return await response.text()
@kernel_function(description="Makes a PUT request to a uri", name="putAsync")
@kernel_function_context_parameter(name="body", description="The body of the request")
async def put(self, url: str, context: "KernelContext") -> str:
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"] = {},
) -> str:
"""
Sends an HTTP PUT request to the specified URI and returns
the response body as a string.
@@ -81,8 +88,6 @@ class HttpPlugin(KernelBaseModel):
if not url:
raise ValueError("url cannot be `None` or empty")
body = context.variables.get("body")
headers = {"Content-Type": "application/json"}
data = json.dumps(body)
async with aiohttp.ClientSession() as session:
@@ -90,7 +95,7 @@ class HttpPlugin(KernelBaseModel):
return await response.text()
@kernel_function(description="Makes a DELETE request to a uri", name="deleteAsync")
async def delete(self, url: str) -> str:
async def delete(self, url: Annotated[str, "The URI to send the request to."]) -> str:
"""
Sends an HTTP DELETE request to the specified URI and returns
the response body as a string.
@@ -1,14 +1,15 @@
# Copyright (c) Microsoft. All rights reserved.
import typing as t
import sys
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.plugin_definition import kernel_function, kernel_function_context_parameter
if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated
if t.TYPE_CHECKING:
from semantic_kernel.orchestration.kernel_context import KernelContext
from semantic_kernel.functions.kernel_function_decorator import kernel_function
class MathPlugin(KernelBaseModel):
class MathPlugin:
"""
Description: MathPlugin provides a set of functions to make Math calculations.
@@ -16,42 +17,32 @@ class MathPlugin(KernelBaseModel):
kernel.import_plugin(MathPlugin(), plugin_name="math")
Examples:
{{math.Add}} => Returns the sum of initial_value_text and Amount (provided in the KernelContext)
{{math.Add}} => Returns the sum of input and amount (provided in the KernelArguments)
{{math.Subtract}} => Returns the difference of input and amount (provided in the KernelArguments)
"""
@kernel_function(
description="Adds value to a value",
name="Add",
input_description="The value to add",
)
@kernel_function_context_parameter(
name="Amount",
description="Amount to add",
type="number",
required=True,
)
def add(self, initial_value_text: str, context: "KernelContext") -> str:
"""
Returns the Addition result of initial and amount values provided.
:param initial_value_text: Initial value as string to add the specified amount
:param context: Contains the context to get the numbers from
:return: The resulting sum as a string
"""
return MathPlugin.add_or_subtract(initial_value_text, context, add=True)
@kernel_function(name="Add")
def add(
self,
input: Annotated[int, "the first number to add"],
amount: Annotated[int, "the second number to add"],
) -> Annotated[int, "the output is a number"]:
"""Returns the Addition result of the values provided."""
if isinstance(input, str):
input = int(input)
if isinstance(amount, str):
amount = int(amount)
return MathPlugin.add_or_subtract(input, amount, add=True)
@kernel_function(
description="Subtracts value to a value",
name="Subtract",
input_description="The value to subtract",
)
@kernel_function_context_parameter(
name="Amount",
description="Amount to subtract",
type="number",
required=True,
)
def subtract(self, initial_value_text: str, context: "KernelContext") -> str:
def subtract(
self,
input: Annotated[int, "the first number"],
amount: Annotated[int, "the number to subtract"],
) -> int:
"""
Returns the difference of numbers provided.
@@ -59,10 +50,14 @@ class MathPlugin(KernelBaseModel):
:param context: Contains the context to get the numbers from
:return: The resulting subtraction as a string
"""
return MathPlugin.add_or_subtract(initial_value_text, context, add=False)
if isinstance(input, str):
input = int(input)
if isinstance(amount, str):
amount = int(amount)
return MathPlugin.add_or_subtract(input, amount, add=False)
@staticmethod
def add_or_subtract(initial_value_text: str, context: "KernelContext", add: bool) -> str:
def add_or_subtract(input: int, amount: int, add: bool) -> int:
"""
Helper function to perform addition or subtraction based on the add flag.
@@ -71,19 +66,4 @@ class MathPlugin(KernelBaseModel):
:param add: If True, performs addition, otherwise performs subtraction
:return: The resulting sum or subtraction as a string
"""
try:
initial_value = int(initial_value_text)
except ValueError:
raise ValueError(f"Initial value provided is not in numeric format: {initial_value_text}")
context_amount = context["Amount"]
if context_amount is not None:
try:
amount = int(context_amount)
except ValueError:
raise ValueError("Context amount provided is not in numeric format:" f" {context_amount}")
result = initial_value + amount if add else initial_value - amount
return str(result)
else:
raise ValueError("Context amount should not be None.")
return input + amount if add else input - amount
@@ -1,88 +1,65 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import logging
import sys
import typing as t
from typing import ClassVar
from typing import Final, Optional
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.plugin_definition import kernel_function, kernel_function_context_parameter
if t.TYPE_CHECKING:
from semantic_kernel.orchestration.kernel_context import KernelContext
from semantic_kernel.kernel import Kernel
logger: logging.Logger = logging.getLogger(__name__)
DEFAULT_COLLECTION: Final[str] = "generic"
DEFAULT_RELEVANCE: Final[float] = 0.75
DEFAULT_LIMIT: Final[int] = 1
class TextMemoryPlugin(KernelBaseModel):
COLLECTION_PARAM: ClassVar[str] = "collection"
RELEVANCE_PARAM: ClassVar[str] = "relevance"
KEY_PARAM: ClassVar[str] = "key"
LIMIT_PARAM: ClassVar[str] = "limit"
DEFAULT_COLLECTION: ClassVar[str] = "generic"
DEFAULT_RELEVANCE: ClassVar[float] = "0.75"
DEFAULT_LIMIT: ClassVar[int] = "1"
# @staticmethod
@kernel_function(
description="Recall a fact from the long term memory",
name="recall",
input_description="The information to retrieve",
)
@kernel_function_context_parameter(
name=COLLECTION_PARAM,
description="The collection to search for information",
default_value=DEFAULT_COLLECTION,
)
@kernel_function_context_parameter(
name=RELEVANCE_PARAM,
description="The relevance score, from 0.0 to 1.0; 1.0 means perfect match",
default_value=DEFAULT_RELEVANCE,
)
@kernel_function_context_parameter(
name=LIMIT_PARAM,
description="The maximum number of relevant memories to recall.",
default_value=DEFAULT_LIMIT,
)
async def recall(self, ask: str, context: "KernelContext") -> str:
async def recall(
self,
ask: Annotated[str, "The information to retrieve"],
kernel: Annotated["Kernel", "The kernel"],
collection: Annotated[Optional[str], "The collection to search for information."] = DEFAULT_COLLECTION,
relevance: Annotated[
Optional[float], "The relevance score, from 0.0 to 1.0; 1.0 means perfect match"
] = DEFAULT_RELEVANCE,
limit: Annotated[Optional[int], "The maximum number of relevant memories to recall."] = DEFAULT_LIMIT,
) -> str:
"""
Recall a fact from the long term memory.
Example:
context["input"] = "what is the capital of France?"
{{memory.recall $input}} => "Paris"
{{memory.recall $ask}} => "Paris"
Args:
ask -- The question to ask the memory
context -- Contains the 'collection' to search for information
, the 'relevance' score to use when searching
and the 'limit' of relevant memories to retrieve.
collection -- The collection to search for information
relevance -- The relevance score, from 0.0 to 1.0; 1.0 means perfect match
limit -- The maximum number of relevant memories to recall
Returns:
The nearest item from the memory store as a string or empty string if not found.
"""
if context.variables is None:
raise ValueError("The context doesn't have the variables required to know how to recall memory")
if context.memory is None:
if kernel.memory is None:
raise ValueError("The context doesn't have a memory instance to search")
collection = context.variables.get(TextMemoryPlugin.COLLECTION_PARAM, TextMemoryPlugin.DEFAULT_COLLECTION)
if not collection:
raise ValueError("Memory collection not defined for TextMemoryPlugin")
relevance = context.variables.get(TextMemoryPlugin.RELEVANCE_PARAM, TextMemoryPlugin.DEFAULT_RELEVANCE)
if not relevance:
raise ValueError("Relevance value not defined for TextMemoryPlugin")
limit = context.variables.get(TextMemoryPlugin.LIMIT_PARAM, TextMemoryPlugin.DEFAULT_LIMIT)
if limit is None or str(limit).strip() == "":
raise ValueError("Limit value not defined for TextMemoryPlugin")
results = await context.memory.search(
results = await kernel.memory.search(
collection=collection,
query=ask,
limit=int(limit),
min_relevance_score=float(relevance),
limit=limit,
min_relevance_score=relevance,
)
if results is None or len(results) == 0:
logger.warning(f"Memory not found in collection: {collection}")
@@ -93,43 +70,26 @@ class TextMemoryPlugin(KernelBaseModel):
@kernel_function(
description="Save information to semantic memory",
name="save",
input_description="The information to save",
)
@kernel_function_context_parameter(
name=COLLECTION_PARAM,
description="The collection to save the information",
default_value=DEFAULT_COLLECTION,
)
@kernel_function_context_parameter(
name=KEY_PARAM,
description="The unique key to associate with the information",
)
async def save(self, text: str, context: "KernelContext") -> None:
async def save(
self,
text: Annotated[str, "The information to save."],
kernel: Annotated["Kernel", "The kernel"],
key: Annotated[str, "The unique key to associate with the information."],
collection: Annotated[Optional[str], "The collection to save the information."] = DEFAULT_COLLECTION,
) -> None:
"""
Save a fact to the long term memory.
Example:
context["input"] = "the capital of France is Paris"
context[TextMemoryPlugin.KEY_PARAM] = "countryInfo1"
{{memory.save $input}}
Args:
text -- The text to save to the memory
context -- Contains the 'collection' to save the information
and unique 'key' to associate with the information
kernel -- The kernel instance, that has a memory store
collection -- The collection to save the information
key -- The unique key to associate with the information
"""
if context.variables is None:
raise ValueError("The context doesn't have the variables required to know how to recall memory")
if context.memory is None:
if kernel.memory is None:
raise ValueError("The context doesn't have a memory instance to search")
collection = context.variables.get(TextMemoryPlugin.COLLECTION_PARAM, TextMemoryPlugin.DEFAULT_COLLECTION)
if not collection:
raise ValueError("Memory collection not defined for TextMemoryPlugin")
key = context.variables.get(TextMemoryPlugin.KEY_PARAM, None)
if not key:
raise ValueError("Memory key not defined for TextMemoryPlugin")
await context.memory.save_information(collection, text=text, id=key)
await kernel.memory.save_information(collection, text=text, id=key)
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.plugin_definition import kernel_function
class TextPlugin(KernelBaseModel):
@@ -12,73 +12,73 @@ class TextPlugin(KernelBaseModel):
kernel.import_plugin(TextPlugin(), plugin_name="text")
Examples:
KernelContext["input"] = " hello world "
KernelArguments["input"] = " hello world "
{{text.trim $input}} => "hello world"
KernelContext["input"] = " hello world "
KernelArguments["input"] = " hello world "
{{text.trimStart $input} => "hello world "
KernelContext["input"] = " hello world "
KernelArguments["input"] = " hello world "
{{text.trimEnd $input} => " hello world"
KernelContext["input"] = "hello world"
KernelArguments["input"] = "hello world"
{{text.uppercase $input}} => "HELLO WORLD"
KernelContext["input"] = "HELLO WORLD"
KernelArguments["input"] = "HELLO WORLD"
{{text.lowercase $input}} => "hello world"
"""
@kernel_function(description="Trim whitespace from the start and end of a string.")
def trim(self, text: str) -> str:
def trim(self, input: str) -> str:
"""
Trim whitespace from the start and end of a string.
Example:
KernelContext["input"] = " hello world "
KernelArguments["input"] = " hello world "
{{text.trim $input}} => "hello world"
"""
return text.strip()
return input.strip()
@kernel_function(description="Trim whitespace from the start of a string.")
def trim_start(self, text: str) -> str:
def trim_start(self, input: str) -> str:
"""
Trim whitespace from the start of a string.
Example:
KernelContext["input"] = " hello world "
{{text.trim $input}} => "hello world "
KernelArguments["input"] = " hello world "
{{input.trim $input}} => "hello world "
"""
return text.lstrip()
return input.lstrip()
@kernel_function(description="Trim whitespace from the end of a string.")
def trim_end(self, text: str) -> str:
def trim_end(self, input: str) -> str:
"""
Trim whitespace from the end of a string.
Example:
KernelContext["input"] = " hello world "
{{text.trim $input}} => " hello world"
KernelArguments["input"] = " hello world "
{{input.trim $input}} => " hello world"
"""
return text.rstrip()
return input.rstrip()
@kernel_function(description="Convert a string to uppercase.")
def uppercase(self, text: str) -> str:
def uppercase(self, input: str) -> str:
"""
Convert a string to uppercase.
Example:
KernelContext["input"] = "hello world"
{{text.uppercase $input}} => "HELLO WORLD"
KernelArguments["input"] = "hello world"
{{input.uppercase $input}} => "HELLO WORLD"
"""
return text.upper()
return input.upper()
@kernel_function(description="Convert a string to lowercase.")
def lowercase(self, text: str) -> str:
def lowercase(self, input: str) -> str:
"""
Convert a string to lowercase.
Example:
KernelContext["input"] = "HELLO WORLD"
{{text.lowercase $input}} => "hello world"
KernelArguments["input"] = "HELLO WORLD"
{{input.lowercase $input}} => "hello world"
"""
return text.lower()
return input.lower()
@@ -2,8 +2,8 @@
import datetime
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.plugin_definition import kernel_function
class TimePlugin(KernelBaseModel):
@@ -1,9 +1,16 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import sys
from typing import Union
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.plugin_definition import kernel_function
class WaitPlugin(KernelBaseModel):
@@ -14,13 +21,16 @@ class WaitPlugin(KernelBaseModel):
kernel.import_plugin(WaitPlugin(), plugin_name="wait")
Examples:
{{wait.seconds 5}} => Wait for 5 seconds
{{wait.wait 5}} => Wait for 5 seconds
"""
@kernel_function(description="Wait for a certain number of seconds.")
async def wait(self, seconds_text: str) -> None:
try:
seconds = max(float(seconds_text), 0)
except ValueError:
raise ValueError("seconds text must be a number")
await asyncio.sleep(seconds)
async def wait(
self, input: Annotated[Union[float, str], "The number of seconds to wait, can be str or float."]
) -> None:
if isinstance(input, str):
try:
input = float(input)
except ValueError as exc:
raise ValueError("seconds text must be a number") from exc
await asyncio.sleep(abs(input))
@@ -1,10 +1,14 @@
import typing as t
import sys
from typing import TYPE_CHECKING, Optional
from semantic_kernel.connectors.search_engine.connector import ConnectorBase
from semantic_kernel.plugin_definition import kernel_function, kernel_function_context_parameter
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
if t.TYPE_CHECKING:
from semantic_kernel.orchestration.kernel_context import KernelContext
if TYPE_CHECKING:
from semantic_kernel.connectors.search_engine.connector import ConnectorBase
class WebSearchEnginePlugin:
@@ -27,28 +31,21 @@ class WebSearchEnginePlugin:
def __init__(self, connector: "ConnectorBase") -> None:
self._connector = connector
@kernel_function(description="Performs a web search for a given query", name="searchAsync")
@kernel_function_context_parameter(
name="num_results",
description="The number of search results to return",
default_value="1",
)
@kernel_function_context_parameter(
name="offset",
description="The number of search results to skip",
default_value="0",
)
async def search(self, query: str, context: "KernelContext") -> str:
@kernel_function(description="Performs a web search for a given query", name="search")
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,
) -> str:
"""
Returns the search results of the query provided.
Returns `num_results` results and ignores the first `offset`.
:param query: search query
:param context: contains the context of count and offset parameters
:param num_results: number of search results to return, default is 1
:param offset: number of search results to skip, default is 0
:return: stringified list of search results
"""
_num_results = context.variables.get("num_results")
_offset = context.variables.get("offset")
result = await self._connector.search(query, _num_results, _offset)
result = await self._connector.search(query, num_results, offset)
return str(result)
@@ -1,16 +1,45 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Optional
from pydantic import Field
from semantic_kernel.events.kernel_events_args import KernelEventArgs
from semantic_kernel.functions.function_result import FunctionResult
class FunctionInvokedEventArgs(KernelEventArgs):
def __init__(self, function_view, context):
super().__init__(function_view, context)
self._repeat_requested = False
"""Function Invoked Event Args.
@property
def is_repeat_requested(self):
return self._repeat_requested
Receives relevant parts of the the execution, after (invoked) the function is executed.
When a handler changes the arguments in the invoking event,
the new arguments are passed to the invoked event,
make sure to use the update_arguments function, since that also raises the flag that the arguments were updated.
If exception is not None, the function execution failed,
if you want the execution of the pipeline to continue, you need to clear the exception.
You can then also set the repeat flag to True, to repeat the function execution, possible with updated arguments.
Args:
kernel_function_metadata (FunctionView): The function that is being executed.
arguments (KernelArguments): The arguments that are being passed to the function.
function_result (FunctionResult): The result of the function execution.
exception (Optional: Exception): The exception that was raised during the function execution.
Flags:
updated_arguments (bool): Whether the arguments were updated, default False.
is_cancel_requested (bool): Whether the function execution has to be canceled, default False.
is_repeat_requested (bool): Whether the function execution has to be repeated, default False.
Methods:
cancel: Sets the is_cancel_requested flag to True.
update_arguments: Updates the arguments and raises the updated_arguments flag.
repeat: Sets the is_repeat_requested flag to True.
"""
function_result: Optional[FunctionResult] = None
exception: Optional[Exception] = None
is_repeat_requested: bool = Field(default=False, init_var=False)
def repeat(self):
self._repeat_requested = True
self.is_repeat_requested = True

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